From 2258d927a691ddd2ab585adb17ea9f96e89d0638 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 27 Sep 2016 17:53:58 +0200 Subject: xfrm: remove unused helper Not used anymore since 2009 (9e0d57fd6dad37, 'xfrm: SAD entries do not expire correctly after suspend-resume'). Signed-off-by: Florian Westphal Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 419bf5d..45cb7c6 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -388,14 +388,6 @@ static void xfrm_state_gc_task(struct work_struct *work) xfrm_state_gc_destroy(x); } -static inline unsigned long make_jiffies(long secs) -{ - if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) - return MAX_SCHEDULE_TIMEOUT-1; - else - return secs*HZ; -} - static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me) { struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer); -- cgit v1.1 From ac86aa5755dbd8b127f03c5a0bf641caf73378f2 Mon Sep 17 00:00:00 2001 From: Maharaja Kennadyrajan Date: Wed, 28 Sep 2016 15:11:53 +0300 Subject: ath10k: provide provision to get Transmit Power Control stats for 10.4 This patch helps to get the TPC stats for 10.4 fw variants. Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 54df425..4829513 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -5397,6 +5397,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_10_4_PDEV_BSS_CHAN_INFO_EVENTID: ath10k_wmi_event_pdev_bss_chan_info(ar, skb); break; + case WMI_10_4_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; default: ath10k_warn(ar, "Unknown eventid: %d\n", id); break; @@ -8153,6 +8156,7 @@ static const struct wmi_ops wmi_10_4_ops = { .get_vdev_subtype = ath10k_wmi_10_4_op_get_vdev_subtype, .gen_pdev_bss_chan_info_req = ath10k_wmi_10_2_op_gen_pdev_bss_chan_info, .gen_echo = ath10k_wmi_op_gen_echo, + .gen_pdev_get_tpc_config = ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config, }; int ath10k_wmi_attach(struct ath10k *ar) -- cgit v1.1 From bafe492610d58ec66cc149b19c51774efd95344a Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 28 Sep 2016 15:11:53 +0300 Subject: ath10k: add cc_wraparound type for QCA9888 and QCA9884 During offchannel scan, iw survey dump shows wrong values. Fix this by assigning cycle counter wranarround type for QCA9888 and QCA9884, they share same config with QCA4019. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 21ae8d6..867a840 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -198,6 +198,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9984/qca9994 hw1.0", .patch_load_addr = QCA9984_HW_1_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH, .otp_exe_param = 0x00000700, .continuous_frag_desc = true, .cck_rate_map_rev2 = true, @@ -223,6 +224,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .name = "qca9888 hw2.0", .patch_load_addr = QCA9888_HW_2_0_PATCH_LOAD_ADDR, .uart_pin = 7, + .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH, .otp_exe_param = 0x00000700, .continuous_frag_desc = true, .channel_counters_freq_hz = 150000, -- cgit v1.1 From d291d8e0592ad5effaaa1af7de95dc9bfecc4f9e Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 28 Sep 2016 15:11:57 +0300 Subject: ath: export alpha2 helper This will be helpful for drivers that can acquire alpha2 regulatory codes. Signed-off-by: Michal Kazior Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/regd.c | 3 ++- drivers/net/wireless/ath/regd.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index f850603..43afa83 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -449,7 +449,7 @@ static void ath_reg_apply_world_flags(struct wiphy *wiphy, } } -static u16 ath_regd_find_country_by_name(char *alpha2) +u16 ath_regd_find_country_by_name(char *alpha2) { unsigned int i; @@ -460,6 +460,7 @@ static u16 ath_regd_find_country_by_name(char *alpha2) return -1; } +EXPORT_SYMBOL(ath_regd_find_country_by_name); static int __ath_reg_dyn_country(struct wiphy *wiphy, struct ath_regulatory *reg, diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h index 565d307..5d80be2 100644 --- a/drivers/net/wireless/ath/regd.h +++ b/drivers/net/wireless/ath/regd.h @@ -251,6 +251,7 @@ enum CountryCode { bool ath_is_world_regd(struct ath_regulatory *reg); bool ath_is_49ghz_allowed(u16 redomain); +u16 ath_regd_find_country_by_name(char *alpha2); int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)); -- cgit v1.1 From 209b2a68de76c888002a4d054ef27b78ae9d2de2 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 28 Sep 2016 15:11:58 +0300 Subject: ath10k: add platform regulatory domain support This overrides whatever regulatory the device EEPROM contains and uses what the platform says instead - in this implementation the ACPI driver. In case the hint is not programmed or corrupted (0xffff) the device falls back to the eeprom programmed settings. Signed-off-by: Bartosz Markowski [kvalo@qca.qualcomm.com: remove ifdef, change error handling, change info messages to dbg] Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/mac.c | 113 ++++++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index dda49af..c091306 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -713,6 +713,7 @@ struct ath10k { u32 phy_capability; u32 hw_min_tx_power; u32 hw_max_tx_power; + u32 hw_eeprom_rd; u32 ht_cap_info; u32 vht_cap_info; u32 num_rf_chains; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 76297d6..ed71b95 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -19,6 +19,7 @@ #include #include +#include #include "hif.h" #include "core.h" @@ -3179,7 +3180,8 @@ static void ath10k_mac_vif_handle_tx_pause(struct ath10k_vif *arvif, ath10k_mac_vif_tx_unlock(arvif, pause_id); break; default: - ath10k_warn(ar, "received unknown tx pause action %d on vdev %i, ignoring\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "received unknown tx pause action %d on vdev %i, ignoring\n", action, arvif->vdev_id); break; } @@ -7789,6 +7791,109 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id) return arvif_iter.arvif; } +#define WRD_METHOD "WRDD" +#define WRDD_WIFI (0x07) + +static u32 ath10k_mac_wrdd_get_mcc(struct ath10k *ar, union acpi_object *wrdd) +{ + union acpi_object *mcc_pkg; + union acpi_object *domain_type; + union acpi_object *mcc_value; + u32 i; + + if (wrdd->type != ACPI_TYPE_PACKAGE || + wrdd->package.count < 2 || + wrdd->package.elements[0].type != ACPI_TYPE_INTEGER || + wrdd->package.elements[0].integer.value != 0) { + ath10k_warn(ar, "ignoring malformed/unsupported wrdd structure\n"); + return 0; + } + + for (i = 1; i < wrdd->package.count; ++i) { + mcc_pkg = &wrdd->package.elements[i]; + + if (mcc_pkg->type != ACPI_TYPE_PACKAGE) + continue; + if (mcc_pkg->package.count < 2) + continue; + if (mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || + mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) + continue; + + domain_type = &mcc_pkg->package.elements[0]; + if (domain_type->integer.value != WRDD_WIFI) + continue; + + mcc_value = &mcc_pkg->package.elements[1]; + return mcc_value->integer.value; + } + return 0; +} + +static int ath10k_mac_get_wrdd_regulatory(struct ath10k *ar, u16 *rd) +{ + struct pci_dev __maybe_unused *pdev = to_pci_dev(ar->dev); + acpi_handle root_handle; + acpi_handle handle; + struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + u32 alpha2_code; + char alpha2[3]; + + root_handle = ACPI_HANDLE(&pdev->dev); + if (!root_handle) + return -EOPNOTSUPP; + + status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle); + if (ACPI_FAILURE(status)) { + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "failed to get wrd method %d\n", status); + return -EIO; + } + + status = acpi_evaluate_object(handle, NULL, NULL, &wrdd); + if (ACPI_FAILURE(status)) { + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "failed to call wrdc %d\n", status); + return -EIO; + } + + alpha2_code = ath10k_mac_wrdd_get_mcc(ar, wrdd.pointer); + kfree(wrdd.pointer); + if (!alpha2_code) + return -EIO; + + alpha2[0] = (alpha2_code >> 8) & 0xff; + alpha2[1] = (alpha2_code >> 0) & 0xff; + alpha2[2] = '\0'; + + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "regulatory hint from WRDD (alpha2-code): %s\n", alpha2); + + *rd = ath_regd_find_country_by_name(alpha2); + if (*rd == 0xffff) + return -EIO; + + *rd |= COUNTRY_ERD_FLAG; + return 0; +} + +static int ath10k_mac_init_rd(struct ath10k *ar) +{ + int ret; + u16 rd; + + ret = ath10k_mac_get_wrdd_regulatory(ar, &rd); + if (ret) { + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "fallback to eeprom programmed regulatory settings\n"); + rd = ar->hw_eeprom_rd; + } + + ar->ath_common.regulatory.current_rd = rd; + return 0; +} + int ath10k_mac_register(struct ath10k *ar) { static const u32 cipher_suites[] = { @@ -8012,6 +8117,12 @@ int ath10k_mac_register(struct ath10k *ar) ar->running_fw->fw_file.fw_features)) ar->ops->wake_tx_queue = NULL; + ret = ath10k_mac_init_rd(ar); + if (ret) { + ath10k_err(ar, "failed to derive regdom: %d\n", ret); + goto err_dfs_detector_exit; + } + ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, ath10k_reg_notifier); if (ret) { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4829513..b4cfaf9 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4676,7 +4676,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) ar->fw_version_build = (__le32_to_cpu(arg.sw_ver1) & 0x0000ffff); ar->phy_capability = __le32_to_cpu(arg.phy_capab); ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains); - ar->ath_common.regulatory.current_rd = __le32_to_cpu(arg.eeprom_rd); + ar->hw_eeprom_rd = __le32_to_cpu(arg.eeprom_rd); ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ", arg.service_map, arg.service_map_len); -- cgit v1.1 From ebee76f7fa46851775942624fe6c7b80559a408a Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 28 Sep 2016 15:11:58 +0300 Subject: ath10k: allow setting coverage class Unfortunately ath10k does not generally allow modifying the coverage class with the stock firmware and Qualcomm has so far refused to implement this feature so that it can be properly supported in ath10k. If we however know the registers that need to be modified for proper operation with a higher coverage class, then we can do these modifications from the driver. This is a hack and might cause subtle problems but as it's not enabled by default (only when user space changes the coverage class explicitly) it should not cause new problems for existing setups. But still this should be considered as an experimental feature and used with caution. This patch implements the support for first generation cards (QCA9880, QCA9887 and so on) which are based on a core that is similar to ath9k. The registers are modified in place and need to be re-written every time the firmware sets them. To achieve this the register status is verified after certain WMI events from the firmware. The coverage class may not be modified temporarily right after the card re-initializes the registers. This is for example the case during scanning. Thanks to Sebastian Gottschall for initially working on a userspace support for this. This patch wouldn't have been possible without this documentation. Signed-off-by: Benjamin Berg Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 11 +++ drivers/net/wireless/ath/ath10k/core.h | 13 +++ drivers/net/wireless/ath/ath10k/debug.h | 22 +++++ drivers/net/wireless/ath/ath10k/hw.c | 142 ++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 28 ++++++- drivers/net/wireless/ath/ath10k/mac.c | 19 +++++ drivers/net/wireless/ath/ath10k/wmi.c | 48 +++++++++++ 7 files changed, 282 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 867a840..7005e2a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1562,6 +1562,15 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); } +static void ath10k_core_set_coverage_class_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, + set_coverage_class_work); + + if (ar->hw_params.hw_ops->set_coverage_class) + ar->hw_params.hw_ops->set_coverage_class(ar, -1); +} + static int ath10k_core_init_firmware_features(struct ath10k *ar) { struct ath10k_fw_file *fw_file = &ar->normal_mode_fw.fw_file; @@ -2344,6 +2353,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, INIT_WORK(&ar->register_work, ath10k_core_register_work); INIT_WORK(&ar->restart_work, ath10k_core_restart); + INIT_WORK(&ar->set_coverage_class_work, + ath10k_core_set_coverage_class_work); init_dummy_netdev(&ar->napi_dev); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index c091306..6e5aa2d 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -912,6 +912,19 @@ struct ath10k { struct net_device napi_dev; struct napi_struct napi; + struct work_struct set_coverage_class_work; + /* protected by conf_mutex */ + struct { + /* writing also protected by data_lock */ + s16 coverage_class; + + u32 reg_phyclk; + u32 reg_slottime_conf; + u32 reg_slottime_orig; + u32 reg_ack_cts_timeout_conf; + u32 reg_ack_cts_timeout_orig; + } fw_coverage; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index c458fa9..335512b 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -94,7 +94,19 @@ int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw, void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ethtool_stats *stats, u64 *data); + +static inline u64 ath10k_debug_get_fw_dbglog_mask(struct ath10k *ar) +{ + return ar->debug.fw_dbglog_mask; +} + +static inline u32 ath10k_debug_get_fw_dbglog_level(struct ath10k *ar) +{ + return ar->debug.fw_dbglog_level; +} + #else + static inline int ath10k_debug_start(struct ath10k *ar) { return 0; @@ -144,6 +156,16 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) return NULL; } +static inline u64 ath10k_debug_get_fw_dbglog_mask(struct ath10k *ar) +{ + return 0; +} + +static inline u32 ath10k_debug_get_fw_dbglog_level(struct ath10k *ar) +{ + return 0; +} + #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0) #define ath10k_debug_get_et_strings NULL diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 675e75d..33fb268 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -17,11 +17,14 @@ #include #include "core.h" #include "hw.h" +#include "hif.h" +#include "wmi-ops.h" const struct ath10k_hw_regs qca988x_regs = { .rtc_soc_base_address = 0x00004000, .rtc_wmac_base_address = 0x00005000, .soc_core_base_address = 0x00009000, + .wlan_mac_base_address = 0x00020000, .ce_wrapper_base_address = 0x00057000, .ce0_base_address = 0x00057400, .ce1_base_address = 0x00057800, @@ -48,6 +51,7 @@ const struct ath10k_hw_regs qca6174_regs = { .rtc_soc_base_address = 0x00000800, .rtc_wmac_base_address = 0x00001000, .soc_core_base_address = 0x0003a000, + .wlan_mac_base_address = 0x00020000, .ce_wrapper_base_address = 0x00034000, .ce0_base_address = 0x00034400, .ce1_base_address = 0x00034800, @@ -74,6 +78,7 @@ const struct ath10k_hw_regs qca99x0_regs = { .rtc_soc_base_address = 0x00080000, .rtc_wmac_base_address = 0x00000000, .soc_core_base_address = 0x00082000, + .wlan_mac_base_address = 0x00030000, .ce_wrapper_base_address = 0x0004d000, .ce0_base_address = 0x0004a000, .ce1_base_address = 0x0004a400, @@ -109,6 +114,7 @@ const struct ath10k_hw_regs qca99x0_regs = { const struct ath10k_hw_regs qca4019_regs = { .rtc_soc_base_address = 0x00080000, .soc_core_base_address = 0x00082000, + .wlan_mac_base_address = 0x00030000, .ce_wrapper_base_address = 0x0004d000, .ce0_base_address = 0x0004a000, .ce1_base_address = 0x0004a400, @@ -220,7 +226,143 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, survey->time_busy = CCNT_TO_MSEC(ar, rcc); } +/* The firmware does not support setting the coverage class. Instead this + * function monitors and modifies the corresponding MAC registers. + */ +static void ath10k_hw_qca988x_set_coverage_class(struct ath10k *ar, + s16 value) +{ + u32 slottime_reg; + u32 slottime; + u32 timeout_reg; + u32 ack_timeout; + u32 cts_timeout; + u32 phyclk_reg; + u32 phyclk; + u64 fw_dbglog_mask; + u32 fw_dbglog_level; + + mutex_lock(&ar->conf_mutex); + + /* Only modify registers if the core is started. */ + if ((ar->state != ATH10K_STATE_ON) && + (ar->state != ATH10K_STATE_RESTARTED)) + goto unlock; + + /* Retrieve the current values of the two registers that need to be + * adjusted. + */ + slottime_reg = ath10k_hif_read32(ar, WLAN_MAC_BASE_ADDRESS + + WAVE1_PCU_GBL_IFS_SLOT); + timeout_reg = ath10k_hif_read32(ar, WLAN_MAC_BASE_ADDRESS + + WAVE1_PCU_ACK_CTS_TIMEOUT); + phyclk_reg = ath10k_hif_read32(ar, WLAN_MAC_BASE_ADDRESS + + WAVE1_PHYCLK); + phyclk = MS(phyclk_reg, WAVE1_PHYCLK_USEC) + 1; + + if (value < 0) + value = ar->fw_coverage.coverage_class; + + /* Break out if the coverage class and registers have the expected + * value. + */ + if (value == ar->fw_coverage.coverage_class && + slottime_reg == ar->fw_coverage.reg_slottime_conf && + timeout_reg == ar->fw_coverage.reg_ack_cts_timeout_conf && + phyclk_reg == ar->fw_coverage.reg_phyclk) + goto unlock; + + /* Store new initial register values from the firmware. */ + if (slottime_reg != ar->fw_coverage.reg_slottime_conf) + ar->fw_coverage.reg_slottime_orig = slottime_reg; + if (timeout_reg != ar->fw_coverage.reg_ack_cts_timeout_conf) + ar->fw_coverage.reg_ack_cts_timeout_orig = timeout_reg; + ar->fw_coverage.reg_phyclk = phyclk_reg; + + /* Calculat new value based on the (original) firmware calculation. */ + slottime_reg = ar->fw_coverage.reg_slottime_orig; + timeout_reg = ar->fw_coverage.reg_ack_cts_timeout_orig; + + /* Do some sanity checks on the slottime register. */ + if (slottime_reg % phyclk) { + ath10k_warn(ar, + "failed to set coverage class: expected integer microsecond value in register\n"); + + goto store_regs; + } + + slottime = MS(slottime_reg, WAVE1_PCU_GBL_IFS_SLOT); + slottime = slottime / phyclk; + if (slottime != 9 && slottime != 20) { + ath10k_warn(ar, + "failed to set coverage class: expected slot time of 9 or 20us in HW register. It is %uus.\n", + slottime); + + goto store_regs; + } + + /* Recalculate the register values by adding the additional propagation + * delay (3us per coverage class). + */ + + slottime = MS(slottime_reg, WAVE1_PCU_GBL_IFS_SLOT); + slottime += value * 3 * phyclk; + slottime = min_t(u32, slottime, WAVE1_PCU_GBL_IFS_SLOT_MAX); + slottime = SM(slottime, WAVE1_PCU_GBL_IFS_SLOT); + slottime_reg = (slottime_reg & ~WAVE1_PCU_GBL_IFS_SLOT_MASK) | slottime; + + /* Update ack timeout (lower halfword). */ + ack_timeout = MS(timeout_reg, WAVE1_PCU_ACK_CTS_TIMEOUT_ACK); + ack_timeout += 3 * value * phyclk; + ack_timeout = min_t(u32, ack_timeout, WAVE1_PCU_ACK_CTS_TIMEOUT_MAX); + ack_timeout = SM(ack_timeout, WAVE1_PCU_ACK_CTS_TIMEOUT_ACK); + + /* Update cts timeout (upper halfword). */ + cts_timeout = MS(timeout_reg, WAVE1_PCU_ACK_CTS_TIMEOUT_CTS); + cts_timeout += 3 * value * phyclk; + cts_timeout = min_t(u32, cts_timeout, WAVE1_PCU_ACK_CTS_TIMEOUT_MAX); + cts_timeout = SM(cts_timeout, WAVE1_PCU_ACK_CTS_TIMEOUT_CTS); + + timeout_reg = ack_timeout | cts_timeout; + + ath10k_hif_write32(ar, + WLAN_MAC_BASE_ADDRESS + WAVE1_PCU_GBL_IFS_SLOT, + slottime_reg); + ath10k_hif_write32(ar, + WLAN_MAC_BASE_ADDRESS + WAVE1_PCU_ACK_CTS_TIMEOUT, + timeout_reg); + + /* Ensure we have a debug level of WARN set for the case that the + * coverage class is larger than 0. This is important as we need to + * set the registers again if the firmware does an internal reset and + * this way we will be notified of the event. + */ + fw_dbglog_mask = ath10k_debug_get_fw_dbglog_mask(ar); + fw_dbglog_level = ath10k_debug_get_fw_dbglog_level(ar); + + if (value > 0) { + if (fw_dbglog_level > ATH10K_DBGLOG_LEVEL_WARN) + fw_dbglog_level = ATH10K_DBGLOG_LEVEL_WARN; + fw_dbglog_mask = ~0; + } + + ath10k_wmi_dbglog_cfg(ar, fw_dbglog_mask, fw_dbglog_level); + +store_regs: + /* After an error we will not retry setting the coverage class. */ + spin_lock_bh(&ar->data_lock); + ar->fw_coverage.coverage_class = value; + spin_unlock_bh(&ar->data_lock); + + ar->fw_coverage.reg_slottime_conf = slottime_reg; + ar->fw_coverage.reg_ack_cts_timeout_conf = timeout_reg; + +unlock: + mutex_unlock(&ar->conf_mutex); +} + const struct ath10k_hw_ops qca988x_ops = { + .set_coverage_class = ath10k_hw_qca988x_set_coverage_class, }; static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd) diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 6038b74..883547f 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -230,6 +230,7 @@ struct ath10k_hw_regs { u32 rtc_soc_base_address; u32 rtc_wmac_base_address; u32 soc_core_base_address; + u32 wlan_mac_base_address; u32 ce_wrapper_base_address; u32 ce0_base_address; u32 ce1_base_address; @@ -418,6 +419,7 @@ struct htt_rx_desc; /* Defines needed for Rx descriptor abstraction */ struct ath10k_hw_ops { int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd); + void (*set_coverage_class)(struct ath10k *ar, s16 value); }; extern const struct ath10k_hw_ops qca988x_ops; @@ -614,7 +616,7 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define WLAN_SI_BASE_ADDRESS 0x00010000 #define WLAN_GPIO_BASE_ADDRESS 0x00014000 #define WLAN_ANALOG_INTF_BASE_ADDRESS 0x0001c000 -#define WLAN_MAC_BASE_ADDRESS 0x00020000 +#define WLAN_MAC_BASE_ADDRESS ar->regs->wlan_mac_base_address #define EFUSE_BASE_ADDRESS 0x00030000 #define FPGA_REG_BASE_ADDRESS 0x00039000 #define WLAN_UART2_BASE_ADDRESS 0x00054c00 @@ -814,4 +816,28 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB) +/* Register definitions for first generation ath10k cards. These cards include + * a mac thich has a register allocation similar to ath9k and at least some + * registers including the ones relevant for modifying the coverage class are + * identical to the ath9k definitions. + * These registers are usually managed by the ath10k firmware. However by + * overriding them it is possible to support coverage class modifications. + */ +#define WAVE1_PCU_ACK_CTS_TIMEOUT 0x8014 +#define WAVE1_PCU_ACK_CTS_TIMEOUT_MAX 0x00003FFF +#define WAVE1_PCU_ACK_CTS_TIMEOUT_ACK_MASK 0x00003FFF +#define WAVE1_PCU_ACK_CTS_TIMEOUT_ACK_LSB 0 +#define WAVE1_PCU_ACK_CTS_TIMEOUT_CTS_MASK 0x3FFF0000 +#define WAVE1_PCU_ACK_CTS_TIMEOUT_CTS_LSB 16 + +#define WAVE1_PCU_GBL_IFS_SLOT 0x1070 +#define WAVE1_PCU_GBL_IFS_SLOT_MASK 0x0000FFFF +#define WAVE1_PCU_GBL_IFS_SLOT_MAX 0x0000FFFF +#define WAVE1_PCU_GBL_IFS_SLOT_LSB 0 +#define WAVE1_PCU_GBL_IFS_SLOT_RESV0 0xFFFF0000 + +#define WAVE1_PHYCLK 0x801C +#define WAVE1_PHYCLK_USEC_MASK 0x0000007F +#define WAVE1_PHYCLK_USEC_LSB 0 + #endif /* _HW_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ed71b95..2e5d2ca 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -5412,6 +5412,20 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); } +static void ath10k_mac_op_set_coverage_class(struct ieee80211_hw *hw, s16 value) +{ + struct ath10k *ar = hw->priv; + + /* This function should never be called if setting the coverage class + * is not supported on this hardware. + */ + if (!ar->hw_params.hw_ops->set_coverage_class) { + WARN_ON_ONCE(1); + return; + } + ar->hw_params.hw_ops->set_coverage_class(ar, value); +} + static int ath10k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req) @@ -7437,6 +7451,7 @@ static const struct ieee80211_ops ath10k_ops = { .remove_interface = ath10k_remove_interface, .configure_filter = ath10k_configure_filter, .bss_info_changed = ath10k_bss_info_changed, + .set_coverage_class = ath10k_mac_op_set_coverage_class, .hw_scan = ath10k_hw_scan, .cancel_hw_scan = ath10k_cancel_hw_scan, .set_key = ath10k_set_key, @@ -8123,6 +8138,10 @@ int ath10k_mac_register(struct ath10k *ar) goto err_dfs_detector_exit; } + /* Disable set_coverage_class for chipsets that do not support it. */ + if (!ar->hw_params.hw_ops->set_coverage_class) + ar->ops->set_coverage_class = NULL; + ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, ath10k_reg_notifier); if (ret) { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index b4cfaf9..387c4ee 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4931,6 +4931,23 @@ exit: return 0; } +static inline void ath10k_wmi_queue_set_coverage_class_work(struct ath10k *ar) +{ + if (ar->hw_params.hw_ops->set_coverage_class) { + spin_lock_bh(&ar->data_lock); + + /* This call only ensures that the modified coverage class + * persists in case the firmware sets the registers back to + * their default value. So calling it is only necessary if the + * coverage class has a non-zero value. + */ + if (ar->fw_coverage.coverage_class) + queue_work(ar->workqueue, &ar->set_coverage_class_work); + + spin_unlock_bh(&ar->data_lock); + } +} + static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -4951,6 +4968,7 @@ static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) return; case WMI_SCAN_EVENTID: ath10k_wmi_event_scan(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_CHAN_INFO_EVENTID: ath10k_wmi_event_chan_info(ar, skb); @@ -4960,15 +4978,18 @@ static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_DEBUG_MESG_EVENTID: ath10k_wmi_event_debug_mesg(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_UPDATE_STATS_EVENTID: ath10k_wmi_event_update_stats(ar, skb); break; case WMI_VDEV_START_RESP_EVENTID: ath10k_wmi_event_vdev_start_resp(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_VDEV_STOPPED_EVENTID: ath10k_wmi_event_vdev_stopped(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_PEER_STA_KICKOUT_EVENTID: ath10k_wmi_event_peer_sta_kickout(ar, skb); @@ -4984,12 +5005,14 @@ static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_ROAM_EVENTID: ath10k_wmi_event_roam(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_PROFILE_MATCH: ath10k_wmi_event_profile_match(ar, skb); break; case WMI_DEBUG_PRINT_EVENTID: ath10k_wmi_event_debug_print(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_PDEV_QVIT_EVENTID: ath10k_wmi_event_pdev_qvit(ar, skb); @@ -5038,6 +5061,7 @@ static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) return; case WMI_READY_EVENTID: ath10k_wmi_event_ready(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; default: ath10k_warn(ar, "Unknown eventid: %d\n", id); @@ -5081,6 +5105,7 @@ static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb) return; case WMI_10X_SCAN_EVENTID: ath10k_wmi_event_scan(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10X_CHAN_INFO_EVENTID: ath10k_wmi_event_chan_info(ar, skb); @@ -5090,15 +5115,18 @@ static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10X_DEBUG_MESG_EVENTID: ath10k_wmi_event_debug_mesg(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10X_UPDATE_STATS_EVENTID: ath10k_wmi_event_update_stats(ar, skb); break; case WMI_10X_VDEV_START_RESP_EVENTID: ath10k_wmi_event_vdev_start_resp(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10X_VDEV_STOPPED_EVENTID: ath10k_wmi_event_vdev_stopped(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10X_PEER_STA_KICKOUT_EVENTID: ath10k_wmi_event_peer_sta_kickout(ar, skb); @@ -5114,12 +5142,14 @@ static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10X_ROAM_EVENTID: ath10k_wmi_event_roam(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10X_PROFILE_MATCH: ath10k_wmi_event_profile_match(ar, skb); break; case WMI_10X_DEBUG_PRINT_EVENTID: ath10k_wmi_event_debug_print(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10X_PDEV_QVIT_EVENTID: ath10k_wmi_event_pdev_qvit(ar, skb); @@ -5159,6 +5189,7 @@ static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb) return; case WMI_10X_READY_EVENTID: ath10k_wmi_event_ready(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10X_PDEV_UTF_EVENTID: /* ignore utf events */ @@ -5205,6 +5236,7 @@ static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) return; case WMI_10_2_SCAN_EVENTID: ath10k_wmi_event_scan(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_CHAN_INFO_EVENTID: ath10k_wmi_event_chan_info(ar, skb); @@ -5214,15 +5246,18 @@ static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10_2_DEBUG_MESG_EVENTID: ath10k_wmi_event_debug_mesg(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_UPDATE_STATS_EVENTID: ath10k_wmi_event_update_stats(ar, skb); break; case WMI_10_2_VDEV_START_RESP_EVENTID: ath10k_wmi_event_vdev_start_resp(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_VDEV_STOPPED_EVENTID: ath10k_wmi_event_vdev_stopped(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_PEER_STA_KICKOUT_EVENTID: ath10k_wmi_event_peer_sta_kickout(ar, skb); @@ -5238,12 +5273,14 @@ static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10_2_ROAM_EVENTID: ath10k_wmi_event_roam(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_PROFILE_MATCH: ath10k_wmi_event_profile_match(ar, skb); break; case WMI_10_2_DEBUG_PRINT_EVENTID: ath10k_wmi_event_debug_print(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_PDEV_QVIT_EVENTID: ath10k_wmi_event_pdev_qvit(ar, skb); @@ -5274,15 +5311,18 @@ static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10_2_VDEV_STANDBY_REQ_EVENTID: ath10k_wmi_event_vdev_standby_req(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_VDEV_RESUME_REQ_EVENTID: ath10k_wmi_event_vdev_resume_req(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_SERVICE_READY_EVENTID: ath10k_wmi_event_service_ready(ar, skb); return; case WMI_10_2_READY_EVENTID: ath10k_wmi_event_ready(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_2_PDEV_TEMPERATURE_EVENTID: ath10k_wmi_event_temperature(ar, skb); @@ -5345,12 +5385,14 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10_4_DEBUG_MESG_EVENTID: ath10k_wmi_event_debug_mesg(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_4_SERVICE_READY_EVENTID: ath10k_wmi_event_service_ready(ar, skb); return; case WMI_10_4_SCAN_EVENTID: ath10k_wmi_event_scan(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_4_CHAN_INFO_EVENTID: ath10k_wmi_event_chan_info(ar, skb); @@ -5360,12 +5402,14 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10_4_READY_EVENTID: ath10k_wmi_event_ready(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_4_PEER_STA_KICKOUT_EVENTID: ath10k_wmi_event_peer_sta_kickout(ar, skb); break; case WMI_10_4_ROAM_EVENTID: ath10k_wmi_event_roam(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_4_HOST_SWBA_EVENTID: ath10k_wmi_event_host_swba(ar, skb); @@ -5375,12 +5419,15 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) break; case WMI_10_4_DEBUG_PRINT_EVENTID: ath10k_wmi_event_debug_print(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_4_VDEV_START_RESP_EVENTID: ath10k_wmi_event_vdev_start_resp(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_4_VDEV_STOPPED_EVENTID: ath10k_wmi_event_vdev_stopped(ar, skb); + ath10k_wmi_queue_set_coverage_class_work(ar); break; case WMI_10_4_WOW_WAKEUP_HOST_EVENTID: case WMI_10_4_PEER_RATECODE_LIST_EVENTID: @@ -6099,6 +6146,7 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar, | WMI_SCAN_EVENT_COMPLETED | WMI_SCAN_EVENT_BSS_CHANNEL | WMI_SCAN_EVENT_FOREIGN_CHANNEL + | WMI_SCAN_EVENT_FOREIGN_CHANNEL_EXIT | WMI_SCAN_EVENT_DEQUEUED; arg->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT; arg->n_bssids = 1; -- cgit v1.1 From 3605d751d5dd82ad1586bed72a0ebc84d81e20c7 Mon Sep 17 00:00:00 2001 From: James Minor Date: Mon, 3 Oct 2016 13:00:02 -0500 Subject: ath6kl: fix busreqs so they can be reused when sg is cleaned up To reuse the busreqs in case of hardware restart, they must be properly reinitialized. If the scat_req pointer isn't reset to 0, __ath6kl_sdio_write_async() will assume there is sg work to be done (causing a kernel OOPS). Signed-off-by: James Minor Reviewed-by: Steve deRosier Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/sdio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index eab0ab9..96ed060 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -703,8 +703,10 @@ static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar) * ath6kl_hif_rw_comp_handler() with status -ECANCELED so * that the packet is properly freed? */ - if (s_req->busrequest) + if (s_req->busrequest) { + s_req->busrequest->scat_req = 0; ath6kl_sdio_free_bus_req(ar_sdio, s_req->busrequest); + } kfree(s_req->virt_dma_buf); kfree(s_req->sgentries); kfree(s_req); -- cgit v1.1 From db14b18a73a1ceaac96124e7918aa9e484f80dbb Mon Sep 17 00:00:00 2001 From: James Minor Date: Mon, 3 Oct 2016 13:00:03 -0500 Subject: ath6kl: after cleanup properly reflect that sg is disabled This allows the hardware to be restarted, as it will cause the sg to be reinitialized. Signed-off-by: James Minor Reviewed-by: Steve deRosier Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/sdio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 96ed060..8261e24 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -714,6 +714,8 @@ static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar) spin_lock_bh(&ar_sdio->scat_lock); } spin_unlock_bh(&ar_sdio->scat_lock); + + ar_sdio->scatter_enabled = false; } /* setup of HIF scatter resources */ -- cgit v1.1 From fdb6e4839e3ad98b9b51be00e1f99d63db402030 Mon Sep 17 00:00:00 2001 From: James Minor Date: Mon, 3 Oct 2016 13:00:04 -0500 Subject: ath6kl: configure SDIO when power is reapplied When power is removed from the device, all of the SDIO settings return to default. Fix that by reconfiguring after power is applied. Signed-off-by: James Minor Reviewed-by: Steve deRosier Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/sdio.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 8261e24..c2df075 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -75,6 +75,8 @@ struct ath6kl_sdio { #define CMD53_ARG_FIXED_ADDRESS 0 #define CMD53_ARG_INCR_ADDRESS 1 +static int ath6kl_sdio_config(struct ath6kl *ar); + static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar) { return ar->hif_priv; @@ -526,8 +528,15 @@ static int ath6kl_sdio_power_on(struct ath6kl *ar) */ msleep(10); + ret = ath6kl_sdio_config(ar); + if (ret) { + ath6kl_err("Failed to config sdio: %d\n", ret); + goto out; + } + ar_sdio->is_disabled = false; +out: return ret; } -- cgit v1.1 From 1e8a655db2f4dd8777eda08c2af7d1381b9eecca Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 2 Oct 2016 12:06:48 +0200 Subject: net: mv643xx_eth: use phydev from struct net_device The private structure contain a pointer to phydev, but the structure net_device already contain such pointer. So we can remove the pointer phydev in the private structure, and update the driver to use the one contained in struct net_device. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 88 +++++++++++++++--------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 5583118..ae7370d 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -384,8 +384,6 @@ struct mv643xx_eth_private { struct net_device *dev; - struct phy_device *phy; - struct timer_list mib_counters_timer; spinlock_t mib_counters_lock; struct mib_counters mib_counters; @@ -1236,7 +1234,7 @@ static void mv643xx_eth_adjust_link(struct net_device *dev) DISABLE_AUTO_NEG_FOR_FLOW_CTRL | DISABLE_AUTO_NEG_FOR_DUPLEX; - if (mp->phy->autoneg == AUTONEG_ENABLE) { + if (dev->phydev->autoneg == AUTONEG_ENABLE) { /* enable auto negotiation */ pscr &= ~autoneg_disable; goto out_write; @@ -1244,7 +1242,7 @@ static void mv643xx_eth_adjust_link(struct net_device *dev) pscr |= autoneg_disable; - if (mp->phy->speed == SPEED_1000) { + if (dev->phydev->speed == SPEED_1000) { /* force gigabit, half duplex not supported */ pscr |= SET_GMII_SPEED_TO_1000; pscr |= SET_FULL_DUPLEX_MODE; @@ -1253,12 +1251,12 @@ static void mv643xx_eth_adjust_link(struct net_device *dev) pscr &= ~SET_GMII_SPEED_TO_1000; - if (mp->phy->speed == SPEED_100) + if (dev->phydev->speed == SPEED_100) pscr |= SET_MII_SPEED_TO_100; else pscr &= ~SET_MII_SPEED_TO_100; - if (mp->phy->duplex == DUPLEX_FULL) + if (dev->phydev->duplex == DUPLEX_FULL) pscr |= SET_FULL_DUPLEX_MODE; else pscr &= ~SET_FULL_DUPLEX_MODE; @@ -1500,11 +1498,12 @@ static int mv643xx_eth_get_settings_phy(struct mv643xx_eth_private *mp, struct ethtool_cmd *cmd) { + struct net_device *dev = mp->dev; int err; - err = phy_read_status(mp->phy); + err = phy_read_status(dev->phydev); if (err == 0) - err = phy_ethtool_gset(mp->phy, cmd); + err = phy_ethtool_gset(dev->phydev, cmd); /* * The MAC does not support 1000baseT_Half. @@ -1553,23 +1552,21 @@ mv643xx_eth_get_settings_phyless(struct mv643xx_eth_private *mp, static void mv643xx_eth_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { - struct mv643xx_eth_private *mp = netdev_priv(dev); wol->supported = 0; wol->wolopts = 0; - if (mp->phy) - phy_ethtool_get_wol(mp->phy, wol); + if (dev->phydev) + phy_ethtool_get_wol(dev->phydev, wol); } static int mv643xx_eth_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { - struct mv643xx_eth_private *mp = netdev_priv(dev); int err; - if (mp->phy == NULL) + if (!dev->phydev) return -EOPNOTSUPP; - err = phy_ethtool_set_wol(mp->phy, wol); + err = phy_ethtool_set_wol(dev->phydev, wol); /* Given that mv643xx_eth works without the marvell-specific PHY driver, * this debugging hint is useful to have. */ @@ -1583,7 +1580,7 @@ mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct mv643xx_eth_private *mp = netdev_priv(dev); - if (mp->phy != NULL) + if (dev->phydev) return mv643xx_eth_get_settings_phy(mp, cmd); else return mv643xx_eth_get_settings_phyless(mp, cmd); @@ -1592,10 +1589,9 @@ mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct mv643xx_eth_private *mp = netdev_priv(dev); int ret; - if (mp->phy == NULL) + if (!dev->phydev) return -EINVAL; /* @@ -1603,7 +1599,7 @@ mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) */ cmd->advertising &= ~ADVERTISED_1000baseT_Half; - ret = phy_ethtool_sset(mp->phy, cmd); + ret = phy_ethtool_sset(dev->phydev, cmd); if (!ret) mv643xx_eth_adjust_link(dev); return ret; @@ -1622,12 +1618,10 @@ static void mv643xx_eth_get_drvinfo(struct net_device *dev, static int mv643xx_eth_nway_reset(struct net_device *dev) { - struct mv643xx_eth_private *mp = netdev_priv(dev); - - if (mp->phy == NULL) + if (!dev->phydev) return -EINVAL; - return genphy_restart_aneg(mp->phy); + return genphy_restart_aneg(dev->phydev); } static int @@ -2326,19 +2320,20 @@ static inline void oom_timer_wrapper(unsigned long data) static void port_start(struct mv643xx_eth_private *mp) { + struct net_device *dev = mp->dev; u32 pscr; int i; /* * Perform PHY reset, if there is a PHY. */ - if (mp->phy != NULL) { + if (dev->phydev) { struct ethtool_cmd cmd; - mv643xx_eth_get_settings(mp->dev, &cmd); - phy_init_hw(mp->phy); - mv643xx_eth_set_settings(mp->dev, &cmd); - phy_start(mp->phy); + mv643xx_eth_get_settings(dev, &cmd); + phy_init_hw(dev->phydev); + mv643xx_eth_set_settings(dev, &cmd); + phy_start(dev->phydev); } /* @@ -2350,7 +2345,7 @@ static void port_start(struct mv643xx_eth_private *mp) wrlp(mp, PORT_SERIAL_CONTROL, pscr); pscr |= DO_NOT_FORCE_LINK_FAIL; - if (mp->phy == NULL) + if (!dev->phydev) pscr |= FORCE_LINK_PASS; wrlp(mp, PORT_SERIAL_CONTROL, pscr); @@ -2534,8 +2529,8 @@ static int mv643xx_eth_stop(struct net_device *dev) del_timer_sync(&mp->rx_oom); netif_carrier_off(dev); - if (mp->phy) - phy_stop(mp->phy); + if (dev->phydev) + phy_stop(dev->phydev); free_irq(dev->irq, dev); port_reset(mp); @@ -2553,13 +2548,12 @@ static int mv643xx_eth_stop(struct net_device *dev) static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct mv643xx_eth_private *mp = netdev_priv(dev); int ret; - if (mp->phy == NULL) + if (!dev->phydev) return -ENOTSUPP; - ret = phy_mii_ioctl(mp->phy, ifr, cmd); + ret = phy_mii_ioctl(dev->phydev, ifr, cmd); if (!ret) mv643xx_eth_adjust_link(dev); return ret; @@ -3006,7 +3000,8 @@ static struct phy_device *phy_scan(struct mv643xx_eth_private *mp, static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) { - struct phy_device *phy = mp->phy; + struct net_device *dev = mp->dev; + struct phy_device *phy = dev->phydev; if (speed == 0) { phy->autoneg = AUTONEG_ENABLE; @@ -3024,6 +3019,7 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) { + struct net_device *dev = mp->dev; u32 pscr; pscr = rdlp(mp, PORT_SERIAL_CONTROL); @@ -3033,7 +3029,7 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) } pscr = MAX_RX_PACKET_9700BYTE | SERIAL_PORT_CONTROL_RESERVED; - if (mp->phy == NULL) { + if (!dev->phydev) { pscr |= DISABLE_AUTO_NEG_SPEED_GMII; if (speed == SPEED_1000) pscr |= SET_GMII_SPEED_TO_1000; @@ -3072,6 +3068,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) struct mv643xx_eth_platform_data *pd; struct mv643xx_eth_private *mp; struct net_device *dev; + struct phy_device *phydev = NULL; struct resource *res; int err; @@ -3127,18 +3124,18 @@ static int mv643xx_eth_probe(struct platform_device *pdev) err = 0; if (pd->phy_node) { - mp->phy = of_phy_connect(mp->dev, pd->phy_node, - mv643xx_eth_adjust_link, 0, - PHY_INTERFACE_MODE_GMII); - if (!mp->phy) + phydev = of_phy_connect(mp->dev, pd->phy_node, + mv643xx_eth_adjust_link, 0, + PHY_INTERFACE_MODE_GMII); + if (!phydev) err = -ENODEV; else - phy_addr_set(mp, mp->phy->mdio.addr); + phy_addr_set(mp, phydev->mdio.addr); } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { - mp->phy = phy_scan(mp, pd->phy_addr); + phydev = phy_scan(mp, pd->phy_addr); - if (IS_ERR(mp->phy)) - err = PTR_ERR(mp->phy); + if (IS_ERR(phydev)) + err = PTR_ERR(phydev); else phy_init(mp, pd->speed, pd->duplex); } @@ -3222,10 +3219,11 @@ out: static int mv643xx_eth_remove(struct platform_device *pdev) { struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); + struct net_device *dev = mp->dev; unregister_netdev(mp->dev); - if (mp->phy != NULL) - phy_disconnect(mp->phy); + if (dev->phydev) + phy_disconnect(dev->phydev); cancel_work_sync(&mp->tx_timeout_task); if (!IS_ERR(mp->clk)) -- cgit v1.1 From a54e1612bc3dd4a3974b69a6043dd4ddbab14e6d Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 2 Oct 2016 12:06:49 +0200 Subject: net: mv643xx_eth: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 84 +++++++++++++++++++----------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index ae7370d..18e6bb6 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1495,56 +1495,69 @@ static const struct mv643xx_eth_stats mv643xx_eth_stats[] = { }; static int -mv643xx_eth_get_settings_phy(struct mv643xx_eth_private *mp, - struct ethtool_cmd *cmd) +mv643xx_eth_get_link_ksettings_phy(struct mv643xx_eth_private *mp, + struct ethtool_link_ksettings *cmd) { struct net_device *dev = mp->dev; int err; + u32 supported, advertising; err = phy_read_status(dev->phydev); if (err == 0) - err = phy_ethtool_gset(dev->phydev, cmd); + err = phy_ethtool_ksettings_get(dev->phydev, cmd); /* * The MAC does not support 1000baseT_Half. */ - cmd->supported &= ~SUPPORTED_1000baseT_Half; - cmd->advertising &= ~ADVERTISED_1000baseT_Half; + ethtool_convert_link_mode_to_legacy_u32(&supported, + cmd->link_modes.supported); + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); + supported &= ~SUPPORTED_1000baseT_Half; + advertising &= ~ADVERTISED_1000baseT_Half; + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return err; } static int -mv643xx_eth_get_settings_phyless(struct mv643xx_eth_private *mp, - struct ethtool_cmd *cmd) +mv643xx_eth_get_link_ksettings_phyless(struct mv643xx_eth_private *mp, + struct ethtool_link_ksettings *cmd) { u32 port_status; + u32 supported, advertising; port_status = rdlp(mp, PORT_STATUS); - cmd->supported = SUPPORTED_MII; - cmd->advertising = ADVERTISED_MII; + supported = SUPPORTED_MII; + advertising = ADVERTISED_MII; switch (port_status & PORT_SPEED_MASK) { case PORT_SPEED_10: - ethtool_cmd_speed_set(cmd, SPEED_10); + cmd->base.speed = SPEED_10; break; case PORT_SPEED_100: - ethtool_cmd_speed_set(cmd, SPEED_100); + cmd->base.speed = SPEED_100; break; case PORT_SPEED_1000: - ethtool_cmd_speed_set(cmd, SPEED_1000); + cmd->base.speed = SPEED_1000; break; default: - cmd->speed = -1; + cmd->base.speed = -1; break; } - cmd->duplex = (port_status & FULL_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; - cmd->port = PORT_MII; - cmd->phy_address = 0; - cmd->transceiver = XCVR_INTERNAL; - cmd->autoneg = AUTONEG_DISABLE; - cmd->maxtxpkt = 1; - cmd->maxrxpkt = 1; + cmd->base.duplex = (port_status & FULL_DUPLEX) ? + DUPLEX_FULL : DUPLEX_HALF; + cmd->base.port = PORT_MII; + cmd->base.phy_address = 0; + cmd->base.autoneg = AUTONEG_DISABLE; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } @@ -1576,19 +1589,23 @@ mv643xx_eth_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) } static int -mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +mv643xx_eth_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct mv643xx_eth_private *mp = netdev_priv(dev); if (dev->phydev) - return mv643xx_eth_get_settings_phy(mp, cmd); + return mv643xx_eth_get_link_ksettings_phy(mp, cmd); else - return mv643xx_eth_get_settings_phyless(mp, cmd); + return mv643xx_eth_get_link_ksettings_phyless(mp, cmd); } static int -mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +mv643xx_eth_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { + struct ethtool_link_ksettings c = *cmd; + u32 advertising; int ret; if (!dev->phydev) @@ -1597,9 +1614,13 @@ mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) /* * The MAC does not support 1000baseT_Half. */ - cmd->advertising &= ~ADVERTISED_1000baseT_Half; + ethtool_convert_link_mode_to_legacy_u32(&advertising, + c.link_modes.advertising); + advertising &= ~ADVERTISED_1000baseT_Half; + ethtool_convert_legacy_u32_to_link_mode(c.link_modes.advertising, + advertising); - ret = phy_ethtool_sset(dev->phydev, cmd); + ret = phy_ethtool_ksettings_set(dev->phydev, &c); if (!ret) mv643xx_eth_adjust_link(dev); return ret; @@ -1746,8 +1767,6 @@ static int mv643xx_eth_get_sset_count(struct net_device *dev, int sset) } static const struct ethtool_ops mv643xx_eth_ethtool_ops = { - .get_settings = mv643xx_eth_get_settings, - .set_settings = mv643xx_eth_set_settings, .get_drvinfo = mv643xx_eth_get_drvinfo, .nway_reset = mv643xx_eth_nway_reset, .get_link = ethtool_op_get_link, @@ -1761,6 +1780,8 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops = { .get_ts_info = ethtool_op_get_ts_info, .get_wol = mv643xx_eth_get_wol, .set_wol = mv643xx_eth_set_wol, + .get_link_ksettings = mv643xx_eth_get_link_ksettings, + .set_link_ksettings = mv643xx_eth_set_link_ksettings, }; @@ -2328,11 +2349,12 @@ static void port_start(struct mv643xx_eth_private *mp) * Perform PHY reset, if there is a PHY. */ if (dev->phydev) { - struct ethtool_cmd cmd; + struct ethtool_link_ksettings cmd; - mv643xx_eth_get_settings(dev, &cmd); + mv643xx_eth_get_link_ksettings(dev, &cmd); phy_init_hw(dev->phydev); - mv643xx_eth_set_settings(dev, &cmd); + mv643xx_eth_set_link_ksettings( + dev, (const struct ethtool_link_ksettings *)&cmd); phy_start(dev->phydev); } -- cgit v1.1 From d6d50c7ea42d3659782695bfebf4ae6548d00db5 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 3 Oct 2016 08:28:19 +0200 Subject: net: stmmac: use phydev from struct net_device The private structure contain a pointer to phydev, but the structure net_device already contain such pointer. So we can remove the pointer phydev in the private structure, and update the driver to use the one contained in struct net_device. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 4 +-- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 - .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 18 +++++----- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 40 ++++++++++------------ 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index bec6963..5ad1dfb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -367,8 +367,8 @@ static int socfpga_dwmac_resume(struct device *dev) * control register 0, and can be modified by the phy driver * framework. */ - if (priv->phydev) - phy_resume(priv->phydev); + if (ndev->phydev) + phy_resume(ndev->phydev); return stmmac_resume(dev); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 8dc9056..f94e028 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -90,7 +90,6 @@ struct stmmac_priv { struct mac_device_info *hw; spinlock_t lock; - struct phy_device *phydev ____cacheline_aligned_in_smp; int oldlink; int speed; int oldduplex; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 1e06173..ac823bf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -273,7 +273,7 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct stmmac_priv *priv = netdev_priv(dev); - struct phy_device *phy = priv->phydev; + struct phy_device *phy = dev->phydev; int rc; if (priv->hw->pcs & STMMAC_PCS_RGMII || @@ -359,7 +359,7 @@ static int stmmac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct stmmac_priv *priv = netdev_priv(dev); - struct phy_device *phy = priv->phydev; + struct phy_device *phy = dev->phydev; int rc; if (priv->hw->pcs & STMMAC_PCS_RGMII || @@ -468,12 +468,12 @@ stmmac_get_pauseparam(struct net_device *netdev, if (!adv_lp.pause) return; } else { - if (!(priv->phydev->supported & SUPPORTED_Pause) || - !(priv->phydev->supported & SUPPORTED_Asym_Pause)) + if (!(netdev->phydev->supported & SUPPORTED_Pause) || + !(netdev->phydev->supported & SUPPORTED_Asym_Pause)) return; } - pause->autoneg = priv->phydev->autoneg; + pause->autoneg = netdev->phydev->autoneg; if (priv->flow_ctrl & FLOW_RX) pause->rx_pause = 1; @@ -487,7 +487,7 @@ stmmac_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct stmmac_priv *priv = netdev_priv(netdev); - struct phy_device *phy = priv->phydev; + struct phy_device *phy = netdev->phydev; int new_pause = FLOW_OFF; if (priv->hw->pcs && priv->hw->mac->pcs_get_adv_lp) { @@ -547,7 +547,7 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, } } if (priv->eee_enabled) { - int val = phy_get_eee_err(priv->phydev); + int val = phy_get_eee_err(dev->phydev); if (val) priv->xstats.phy_eee_wakeup_error_n = val; } @@ -666,7 +666,7 @@ static int stmmac_ethtool_op_get_eee(struct net_device *dev, edata->eee_active = priv->eee_active; edata->tx_lpi_timer = priv->tx_lpi_timer; - return phy_ethtool_get_eee(priv->phydev, edata); + return phy_ethtool_get_eee(dev->phydev, edata); } static int stmmac_ethtool_op_set_eee(struct net_device *dev, @@ -691,7 +691,7 @@ static int stmmac_ethtool_op_set_eee(struct net_device *dev, priv->tx_lpi_timer = edata->tx_lpi_timer; } - return phy_ethtool_set_eee(priv->phydev, edata); + return phy_ethtool_set_eee(dev->phydev, edata); } static u32 stmmac_usec2riwt(u32 usec, struct stmmac_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 4c8c60a..1a06c87 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -221,7 +221,8 @@ static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv) */ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv) { - struct phy_device *phydev = priv->phydev; + struct net_device *ndev = priv->dev; + struct phy_device *phydev = ndev->phydev; if (likely(priv->plat->fix_mac_speed)) priv->plat->fix_mac_speed(priv->plat->bsp_priv, phydev->speed); @@ -279,6 +280,7 @@ static void stmmac_eee_ctrl_timer(unsigned long arg) */ bool stmmac_eee_init(struct stmmac_priv *priv) { + struct net_device *ndev = priv->dev; unsigned long flags; bool ret = false; @@ -295,7 +297,7 @@ bool stmmac_eee_init(struct stmmac_priv *priv) int tx_lpi_timer = priv->tx_lpi_timer; /* Check if the PHY supports EEE */ - if (phy_init_eee(priv->phydev, 1)) { + if (phy_init_eee(ndev->phydev, 1)) { /* To manage at run-time if the EEE cannot be supported * anymore (for example because the lp caps have been * changed). @@ -327,7 +329,7 @@ bool stmmac_eee_init(struct stmmac_priv *priv) tx_lpi_timer); } /* Set HW EEE according to the speed */ - priv->hw->mac->set_eee_pls(priv->hw, priv->phydev->link); + priv->hw->mac->set_eee_pls(priv->hw, ndev->phydev->link); ret = true; spin_unlock_irqrestore(&priv->lock, flags); @@ -691,7 +693,7 @@ static void stmmac_release_ptp(struct stmmac_priv *priv) static void stmmac_adjust_link(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - struct phy_device *phydev = priv->phydev; + struct phy_device *phydev = dev->phydev; unsigned long flags; int new_state = 0; unsigned int fc = priv->flow_ctrl, pause_time = priv->pause; @@ -874,8 +876,6 @@ static int stmmac_init_phy(struct net_device *dev) pr_debug("stmmac_init_phy: %s: attached to PHY (UID 0x%x)" " Link = %d\n", dev->name, phydev->phy_id, phydev->link); - priv->phydev = phydev; - return 0; } @@ -1800,8 +1800,8 @@ static int stmmac_open(struct net_device *dev) stmmac_init_tx_coalesce(priv); - if (priv->phydev) - phy_start(priv->phydev); + if (dev->phydev) + phy_start(dev->phydev); /* Request the IRQ lines */ ret = request_irq(dev->irq, stmmac_interrupt, @@ -1848,8 +1848,8 @@ wolirq_error: init_error: free_dma_desc_resources(priv); dma_desc_error: - if (priv->phydev) - phy_disconnect(priv->phydev); + if (dev->phydev) + phy_disconnect(dev->phydev); return ret; } @@ -1868,10 +1868,9 @@ static int stmmac_release(struct net_device *dev) del_timer_sync(&priv->eee_ctrl_timer); /* Stop and disconnect the PHY */ - if (priv->phydev) { - phy_stop(priv->phydev); - phy_disconnect(priv->phydev); - priv->phydev = NULL; + if (dev->phydev) { + phy_stop(dev->phydev); + phy_disconnect(dev->phydev); } netif_stop_queue(dev); @@ -2863,7 +2862,6 @@ static void stmmac_poll_controller(struct net_device *dev) */ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct stmmac_priv *priv = netdev_priv(dev); int ret = -EOPNOTSUPP; if (!netif_running(dev)) @@ -2873,9 +2871,9 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: - if (!priv->phydev) + if (!dev->phydev) return -EINVAL; - ret = phy_mii_ioctl(priv->phydev, rq, cmd); + ret = phy_mii_ioctl(dev->phydev, rq, cmd); break; case SIOCSHWTSTAMP: ret = stmmac_hwtstamp_ioctl(dev, rq); @@ -3428,8 +3426,8 @@ int stmmac_suspend(struct device *dev) if (!ndev || !netif_running(ndev)) return 0; - if (priv->phydev) - phy_stop(priv->phydev); + if (ndev->phydev) + phy_stop(ndev->phydev); spin_lock_irqsave(&priv->lock, flags); @@ -3523,8 +3521,8 @@ int stmmac_resume(struct device *dev) spin_unlock_irqrestore(&priv->lock, flags); - if (priv->phydev) - phy_start(priv->phydev); + if (ndev->phydev) + phy_start(ndev->phydev); return 0; } -- cgit v1.1 From 1685e785cc8812fe3069cd4a7ac6c0a346c6e0aa Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 3 Oct 2016 08:28:20 +0200 Subject: net: stmmac: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 97 +++++++++++++--------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index ac823bf..3fe9340 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -269,8 +269,8 @@ static void stmmac_ethtool_getdrvinfo(struct net_device *dev, strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } -static int stmmac_ethtool_getsettings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int stmmac_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct stmmac_priv *priv = netdev_priv(dev); struct phy_device *phy = dev->phydev; @@ -279,15 +279,16 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, if (priv->hw->pcs & STMMAC_PCS_RGMII || priv->hw->pcs & STMMAC_PCS_SGMII) { struct rgmii_adv adv; + u32 supported, advertising, lp_advertising; if (!priv->xstats.pcs_link) { - ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); - cmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; return 0; } - cmd->duplex = priv->xstats.pcs_duplex; + cmd->base.duplex = priv->xstats.pcs_duplex; - ethtool_cmd_speed_set(cmd, priv->xstats.pcs_speed); + cmd->base.speed = priv->xstats.pcs_speed; /* Get and convert ADV/LP_ADV from the HW AN registers */ if (!priv->hw->mac->pcs_get_adv_lp) @@ -297,45 +298,59 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, /* Encoding of PSE bits is defined in 802.3z, 37.2.1.4 */ + ethtool_convert_link_mode_to_legacy_u32( + &supported, cmd->link_modes.supported); + ethtool_convert_link_mode_to_legacy_u32( + &advertising, cmd->link_modes.advertising); + ethtool_convert_link_mode_to_legacy_u32( + &lp_advertising, cmd->link_modes.lp_advertising); + if (adv.pause & STMMAC_PCS_PAUSE) - cmd->advertising |= ADVERTISED_Pause; + advertising |= ADVERTISED_Pause; if (adv.pause & STMMAC_PCS_ASYM_PAUSE) - cmd->advertising |= ADVERTISED_Asym_Pause; + advertising |= ADVERTISED_Asym_Pause; if (adv.lp_pause & STMMAC_PCS_PAUSE) - cmd->lp_advertising |= ADVERTISED_Pause; + lp_advertising |= ADVERTISED_Pause; if (adv.lp_pause & STMMAC_PCS_ASYM_PAUSE) - cmd->lp_advertising |= ADVERTISED_Asym_Pause; + lp_advertising |= ADVERTISED_Asym_Pause; /* Reg49[3] always set because ANE is always supported */ - cmd->autoneg = ADVERTISED_Autoneg; - cmd->supported |= SUPPORTED_Autoneg; - cmd->advertising |= ADVERTISED_Autoneg; - cmd->lp_advertising |= ADVERTISED_Autoneg; + cmd->base.autoneg = ADVERTISED_Autoneg; + supported |= SUPPORTED_Autoneg; + advertising |= ADVERTISED_Autoneg; + lp_advertising |= ADVERTISED_Autoneg; if (adv.duplex) { - cmd->supported |= (SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full | - SUPPORTED_10baseT_Full); - cmd->advertising |= (ADVERTISED_1000baseT_Full | - ADVERTISED_100baseT_Full | - ADVERTISED_10baseT_Full); + supported |= (SUPPORTED_1000baseT_Full | + SUPPORTED_100baseT_Full | + SUPPORTED_10baseT_Full); + advertising |= (ADVERTISED_1000baseT_Full | + ADVERTISED_100baseT_Full | + ADVERTISED_10baseT_Full); } else { - cmd->supported |= (SUPPORTED_1000baseT_Half | - SUPPORTED_100baseT_Half | - SUPPORTED_10baseT_Half); - cmd->advertising |= (ADVERTISED_1000baseT_Half | - ADVERTISED_100baseT_Half | - ADVERTISED_10baseT_Half); + supported |= (SUPPORTED_1000baseT_Half | + SUPPORTED_100baseT_Half | + SUPPORTED_10baseT_Half); + advertising |= (ADVERTISED_1000baseT_Half | + ADVERTISED_100baseT_Half | + ADVERTISED_10baseT_Half); } if (adv.lp_duplex) - cmd->lp_advertising |= (ADVERTISED_1000baseT_Full | - ADVERTISED_100baseT_Full | - ADVERTISED_10baseT_Full); + lp_advertising |= (ADVERTISED_1000baseT_Full | + ADVERTISED_100baseT_Full | + ADVERTISED_10baseT_Full); else - cmd->lp_advertising |= (ADVERTISED_1000baseT_Half | - ADVERTISED_100baseT_Half | - ADVERTISED_10baseT_Half); - cmd->port = PORT_OTHER; + lp_advertising |= (ADVERTISED_1000baseT_Half | + ADVERTISED_100baseT_Half | + ADVERTISED_10baseT_Half); + cmd->base.port = PORT_OTHER; + + ethtool_convert_legacy_u32_to_link_mode( + cmd->link_modes.supported, supported); + ethtool_convert_legacy_u32_to_link_mode( + cmd->link_modes.advertising, advertising); + ethtool_convert_legacy_u32_to_link_mode( + cmd->link_modes.lp_advertising, lp_advertising); return 0; } @@ -350,13 +365,13 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, "link speed / duplex setting\n", dev->name); return -EBUSY; } - cmd->transceiver = XCVR_INTERNAL; - rc = phy_ethtool_gset(phy, cmd); + rc = phy_ethtool_ksettings_get(phy, cmd); return rc; } -static int stmmac_ethtool_setsettings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int +stmmac_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct stmmac_priv *priv = netdev_priv(dev); struct phy_device *phy = dev->phydev; @@ -367,7 +382,7 @@ static int stmmac_ethtool_setsettings(struct net_device *dev, u32 mask = ADVERTISED_Autoneg | ADVERTISED_Pause; /* Only support ANE */ - if (cmd->autoneg != AUTONEG_ENABLE) + if (cmd->base.autoneg != AUTONEG_ENABLE) return -EINVAL; mask &= (ADVERTISED_1000baseT_Half | @@ -389,7 +404,7 @@ static int stmmac_ethtool_setsettings(struct net_device *dev, } spin_lock(&priv->lock); - rc = phy_ethtool_sset(phy, cmd); + rc = phy_ethtool_ksettings_set(phy, cmd); spin_unlock(&priv->lock); return rc; @@ -850,8 +865,6 @@ static int stmmac_set_tunable(struct net_device *dev, static const struct ethtool_ops stmmac_ethtool_ops = { .begin = stmmac_check_if_running, .get_drvinfo = stmmac_ethtool_getdrvinfo, - .get_settings = stmmac_ethtool_getsettings, - .set_settings = stmmac_ethtool_setsettings, .get_msglevel = stmmac_ethtool_getmsglevel, .set_msglevel = stmmac_ethtool_setmsglevel, .get_regs = stmmac_ethtool_gregs, @@ -871,6 +884,8 @@ static const struct ethtool_ops stmmac_ethtool_ops = { .set_coalesce = stmmac_set_coalesce, .get_tunable = stmmac_get_tunable, .set_tunable = stmmac_set_tunable, + .get_link_ksettings = stmmac_ethtool_get_link_ksettings, + .set_link_ksettings = stmmac_ethtool_set_link_ksettings, }; void stmmac_set_ethtool_ops(struct net_device *netdev) -- cgit v1.1 From 2479876d460f9e7730c4ebc4e591325d6e5a5a0e Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 8 Oct 2016 17:46:15 +0200 Subject: net: ti: cpsw: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c6cff3d..6d4b8a2 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1967,27 +1967,30 @@ static int cpsw_get_ts_info(struct net_device *ndev, return 0; } -static int cpsw_get_settings(struct net_device *ndev, - struct ethtool_cmd *ecmd) +static int cpsw_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *ecmd) { struct cpsw_priv *priv = netdev_priv(ndev); struct cpsw_common *cpsw = priv->cpsw; int slave_no = cpsw_slave_index(cpsw, priv); if (cpsw->slaves[slave_no].phy) - return phy_ethtool_gset(cpsw->slaves[slave_no].phy, ecmd); + return phy_ethtool_ksettings_get(cpsw->slaves[slave_no].phy, + ecmd); else return -EOPNOTSUPP; } -static int cpsw_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) +static int cpsw_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *ecmd) { struct cpsw_priv *priv = netdev_priv(ndev); struct cpsw_common *cpsw = priv->cpsw; int slave_no = cpsw_slave_index(cpsw, priv); if (cpsw->slaves[slave_no].phy) - return phy_ethtool_sset(cpsw->slaves[slave_no].phy, ecmd); + return phy_ethtool_ksettings_set(cpsw->slaves[slave_no].phy, + ecmd); else return -EOPNOTSUPP; } @@ -2245,8 +2248,6 @@ static const struct ethtool_ops cpsw_ethtool_ops = { .set_msglevel = cpsw_set_msglevel, .get_link = ethtool_op_get_link, .get_ts_info = cpsw_get_ts_info, - .get_settings = cpsw_get_settings, - .set_settings = cpsw_set_settings, .get_coalesce = cpsw_get_coalesce, .set_coalesce = cpsw_set_coalesce, .get_sset_count = cpsw_get_sset_count, @@ -2262,6 +2263,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = { .complete = cpsw_ethtool_op_complete, .get_channels = cpsw_get_channels, .set_channels = cpsw_set_channels, + .get_link_ksettings = cpsw_get_link_ksettings, + .set_link_ksettings = cpsw_set_link_ksettings, }; static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_common *cpsw, -- cgit v1.1 From 86e3a04002378610d77d3941f45ebbf531cf605e Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 8 Oct 2016 19:48:15 +0200 Subject: net: ti: netcp_ethss: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_ethss.c | 42 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index d543298..48cb04f 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -1840,8 +1840,8 @@ static void keystone_get_ethtool_stats(struct net_device *ndev, spin_unlock_bh(&gbe_dev->hw_stats_lock); } -static int keystone_get_settings(struct net_device *ndev, - struct ethtool_cmd *cmd) +static int keystone_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *cmd) { struct netcp_intf *netcp = netdev_priv(ndev); struct phy_device *phy = ndev->phydev; @@ -1858,20 +1858,28 @@ static int keystone_get_settings(struct net_device *ndev, if (!gbe_intf->slave) return -EINVAL; - ret = phy_ethtool_gset(phy, cmd); + ret = phy_ethtool_ksettings_get(phy, cmd); if (!ret) - cmd->port = gbe_intf->slave->phy_port_t; + cmd->base.port = gbe_intf->slave->phy_port_t; return ret; } -static int keystone_set_settings(struct net_device *ndev, - struct ethtool_cmd *cmd) +static int keystone_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) { struct netcp_intf *netcp = netdev_priv(ndev); struct phy_device *phy = ndev->phydev; struct gbe_intf *gbe_intf; - u32 features = cmd->advertising & cmd->supported; + u8 port = cmd->base.port; + u32 advertising, supported; + u32 features; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); + ethtool_convert_link_mode_to_legacy_u32(&supported, + cmd->link_modes.supported); + features = advertising & supported; if (!phy) return -EINVAL; @@ -1883,25 +1891,25 @@ static int keystone_set_settings(struct net_device *ndev, if (!gbe_intf->slave) return -EINVAL; - if (cmd->port != gbe_intf->slave->phy_port_t) { - if ((cmd->port == PORT_TP) && !(features & ADVERTISED_TP)) + if (port != gbe_intf->slave->phy_port_t) { + if ((port == PORT_TP) && !(features & ADVERTISED_TP)) return -EINVAL; - if ((cmd->port == PORT_AUI) && !(features & ADVERTISED_AUI)) + if ((port == PORT_AUI) && !(features & ADVERTISED_AUI)) return -EINVAL; - if ((cmd->port == PORT_BNC) && !(features & ADVERTISED_BNC)) + if ((port == PORT_BNC) && !(features & ADVERTISED_BNC)) return -EINVAL; - if ((cmd->port == PORT_MII) && !(features & ADVERTISED_MII)) + if ((port == PORT_MII) && !(features & ADVERTISED_MII)) return -EINVAL; - if ((cmd->port == PORT_FIBRE) && !(features & ADVERTISED_FIBRE)) + if ((port == PORT_FIBRE) && !(features & ADVERTISED_FIBRE)) return -EINVAL; } - gbe_intf->slave->phy_port_t = cmd->port; - return phy_ethtool_sset(phy, cmd); + gbe_intf->slave->phy_port_t = port; + return phy_ethtool_ksettings_set(phy, cmd); } static const struct ethtool_ops keystone_ethtool_ops = { @@ -1912,8 +1920,8 @@ static const struct ethtool_ops keystone_ethtool_ops = { .get_strings = keystone_get_stat_strings, .get_sset_count = keystone_get_sset_count, .get_ethtool_stats = keystone_get_ethtool_stats, - .get_settings = keystone_get_settings, - .set_settings = keystone_set_settings, + .get_link_ksettings = keystone_get_link_ksettings, + .set_link_ksettings = keystone_set_link_ksettings, }; #define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ -- cgit v1.1 From 6e76510e7e19bfdca65042ccd52543011ed92047 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 9 Oct 2016 12:07:04 +0200 Subject: net: usb: lan78xx: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/lan78xx.c | 70 +++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index db558b8..13f033c 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1092,7 +1092,7 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, static int lan78xx_link_reset(struct lan78xx_net *dev) { struct phy_device *phydev = dev->net->phydev; - struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; + struct ethtool_link_ksettings ecmd; int ladv, radv, ret; u32 buf; @@ -1126,12 +1126,12 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) } else if (phydev->link && !dev->link_on) { dev->link_on = true; - phy_ethtool_gset(phydev, &ecmd); + phy_ethtool_ksettings_get(phydev, &ecmd); ret = phy_read(phydev, LAN88XX_INT_STS); if (dev->udev->speed == USB_SPEED_SUPER) { - if (ethtool_cmd_speed(&ecmd) == 1000) { + if (ecmd.base.speed == 1000) { /* disable U2 */ ret = lan78xx_read_reg(dev, USB_CFG1, &buf); buf &= ~USB_CFG1_DEV_U2_INIT_EN_; @@ -1159,9 +1159,10 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) netif_dbg(dev, link, dev->net, "speed: %u duplex: %d anadv: 0x%04x anlpa: 0x%04x", - ethtool_cmd_speed(&ecmd), ecmd.duplex, ladv, radv); + ecmd.base.speed, ecmd.base.duplex, ladv, radv); - ret = lan78xx_update_flowcontrol(dev, ecmd.duplex, ladv, radv); + ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv, + radv); phy_mac_interrupt(phydev, 1); if (!timer_pending(&dev->stat_monitor)) { @@ -1484,7 +1485,8 @@ static void lan78xx_set_mdix_status(struct net_device *net, __u8 mdix_ctrl) dev->mdix_ctrl = mdix_ctrl; } -static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd) +static int lan78xx_get_link_ksettings(struct net_device *net, + struct ethtool_link_ksettings *cmd) { struct lan78xx_net *dev = netdev_priv(net); struct phy_device *phydev = net->phydev; @@ -1495,20 +1497,20 @@ static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd) if (ret < 0) return ret; - ret = phy_ethtool_gset(phydev, cmd); + ret = phy_ethtool_ksettings_get(phydev, cmd); buf = lan78xx_get_mdix_status(net); buf &= LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; if (buf == LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_) { - cmd->eth_tp_mdix = ETH_TP_MDI_AUTO; - cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + cmd->base.eth_tp_mdix = ETH_TP_MDI_AUTO; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_) { - cmd->eth_tp_mdix = ETH_TP_MDI; - cmd->eth_tp_mdix_ctrl = ETH_TP_MDI; + cmd->base.eth_tp_mdix = ETH_TP_MDI; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI; } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_X_) { - cmd->eth_tp_mdix = ETH_TP_MDI_X; - cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X; + cmd->base.eth_tp_mdix = ETH_TP_MDI_X; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_X; } usb_autopm_put_interface(dev->intf); @@ -1516,7 +1518,8 @@ static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd) return ret; } -static int lan78xx_set_settings(struct net_device *net, struct ethtool_cmd *cmd) +static int lan78xx_set_link_ksettings(struct net_device *net, + const struct ethtool_link_ksettings *cmd) { struct lan78xx_net *dev = netdev_priv(net); struct phy_device *phydev = net->phydev; @@ -1527,14 +1530,13 @@ static int lan78xx_set_settings(struct net_device *net, struct ethtool_cmd *cmd) if (ret < 0) return ret; - if (dev->mdix_ctrl != cmd->eth_tp_mdix_ctrl) { - lan78xx_set_mdix_status(net, cmd->eth_tp_mdix_ctrl); - } + if (dev->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl) + lan78xx_set_mdix_status(net, cmd->base.eth_tp_mdix_ctrl); /* change speed & duplex */ - ret = phy_ethtool_sset(phydev, cmd); + ret = phy_ethtool_ksettings_set(phydev, cmd); - if (!cmd->autoneg) { + if (!cmd->base.autoneg) { /* force link down */ temp = phy_read(phydev, MII_BMCR); phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK); @@ -1552,9 +1554,9 @@ static void lan78xx_get_pause(struct net_device *net, { struct lan78xx_net *dev = netdev_priv(net); struct phy_device *phydev = net->phydev; - struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; + struct ethtool_link_ksettings ecmd; - phy_ethtool_gset(phydev, &ecmd); + phy_ethtool_ksettings_get(phydev, &ecmd); pause->autoneg = dev->fc_autoneg; @@ -1570,12 +1572,12 @@ static int lan78xx_set_pause(struct net_device *net, { struct lan78xx_net *dev = netdev_priv(net); struct phy_device *phydev = net->phydev; - struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; + struct ethtool_link_ksettings ecmd; int ret; - phy_ethtool_gset(phydev, &ecmd); + phy_ethtool_ksettings_get(phydev, &ecmd); - if (pause->autoneg && !ecmd.autoneg) { + if (pause->autoneg && !ecmd.base.autoneg) { ret = -EINVAL; goto exit; } @@ -1587,13 +1589,21 @@ static int lan78xx_set_pause(struct net_device *net, if (pause->tx_pause) dev->fc_request_control |= FLOW_CTRL_TX; - if (ecmd.autoneg) { + if (ecmd.base.autoneg) { u32 mii_adv; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32( + &advertising, ecmd.link_modes.advertising); - ecmd.advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); + advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control); - ecmd.advertising |= mii_adv_to_ethtool_adv_t(mii_adv); - phy_ethtool_sset(phydev, &ecmd); + advertising |= mii_adv_to_ethtool_adv_t(mii_adv); + + ethtool_convert_legacy_u32_to_link_mode( + ecmd.link_modes.advertising, advertising); + + phy_ethtool_ksettings_set(phydev, &ecmd); } dev->fc_autoneg = pause->autoneg; @@ -1609,8 +1619,6 @@ static const struct ethtool_ops lan78xx_ethtool_ops = { .get_drvinfo = lan78xx_get_drvinfo, .get_msglevel = lan78xx_get_msglevel, .set_msglevel = lan78xx_set_msglevel, - .get_settings = lan78xx_get_settings, - .set_settings = lan78xx_set_settings, .get_eeprom_len = lan78xx_ethtool_get_eeprom_len, .get_eeprom = lan78xx_ethtool_get_eeprom, .set_eeprom = lan78xx_ethtool_set_eeprom, @@ -1623,6 +1631,8 @@ static const struct ethtool_ops lan78xx_ethtool_ops = { .set_eee = lan78xx_set_eee, .get_pauseparam = lan78xx_get_pause, .set_pauseparam = lan78xx_set_pause, + .get_link_ksettings = lan78xx_get_link_ksettings, + .set_link_ksettings = lan78xx_set_link_ksettings, }; static int lan78xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -- cgit v1.1 From bb10bb3ea8d6417d89ff1dd4df75d44f8c97ff56 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 9 Oct 2016 17:00:53 +0200 Subject: net: dsa: slave: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- net/dsa/slave.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 6b1282c..68714a5 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -641,7 +641,8 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) /* ethtool operations *******************************************************/ static int -dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +dsa_slave_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct dsa_slave_priv *p = netdev_priv(dev); int err; @@ -650,19 +651,20 @@ dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) if (p->phy != NULL) { err = phy_read_status(p->phy); if (err == 0) - err = phy_ethtool_gset(p->phy, cmd); + err = phy_ethtool_ksettings_get(p->phy, cmd); } return err; } static int -dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +dsa_slave_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct dsa_slave_priv *p = netdev_priv(dev); if (p->phy != NULL) - return phy_ethtool_sset(p->phy, cmd); + return phy_ethtool_ksettings_set(p->phy, cmd); return -EOPNOTSUPP; } @@ -990,8 +992,6 @@ void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops) } static const struct ethtool_ops dsa_slave_ethtool_ops = { - .get_settings = dsa_slave_get_settings, - .set_settings = dsa_slave_set_settings, .get_drvinfo = dsa_slave_get_drvinfo, .get_regs_len = dsa_slave_get_regs_len, .get_regs = dsa_slave_get_regs, @@ -1007,6 +1007,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { .get_wol = dsa_slave_get_wol, .set_eee = dsa_slave_set_eee, .get_eee = dsa_slave_get_eee, + .get_link_ksettings = dsa_slave_get_link_ksettings, + .set_link_ksettings = dsa_slave_set_link_ksettings, }; static const struct net_device_ops dsa_slave_netdev_ops = { -- cgit v1.1 From c66549ffd666605831abf6cf19ce0571ad868e39 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Wed, 5 Oct 2016 15:01:57 +0200 Subject: openvswitch: correctly fragment packet with mpls headers If mpls headers were pushed to a defragmented packet, the refragmentation no longer works correctly after 48d2ab609b6b ("net: mpls: Fixups for GSO"). The network header has to be shifted after the mpls headers for the fragmentation and restored afterwards. Fixes: 48d2ab609b6b ("net: mpls: Fixups for GSO") Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 4e03f64..1105c4e 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -62,7 +62,8 @@ struct ovs_frag_data { struct vport *vport; struct ovs_skb_cb cb; __be16 inner_protocol; - __u16 vlan_tci; + u16 network_offset; /* valid only for MPLS */ + u16 vlan_tci; __be16 vlan_proto; unsigned int l2_len; u8 l2_data[MAX_L2_LEN]; @@ -666,6 +667,12 @@ static int ovs_vport_output(struct net *net, struct sock *sk, struct sk_buff *sk skb_postpush_rcsum(skb, skb->data, data->l2_len); skb_reset_mac_header(skb); + if (eth_p_mpls(skb->protocol)) { + skb->inner_network_header = skb->network_header; + skb_set_network_header(skb, data->network_offset); + skb_reset_mac_len(skb); + } + ovs_vport_send(vport, skb); return 0; } @@ -684,7 +691,8 @@ static struct dst_ops ovs_dst_ops = { /* prepare_frag() is called once per (larger-than-MTU) frame; its inverse is * ovs_vport_output(), which is called once per fragmented packet. */ -static void prepare_frag(struct vport *vport, struct sk_buff *skb) +static void prepare_frag(struct vport *vport, struct sk_buff *skb, + u16 orig_network_offset) { unsigned int hlen = skb_network_offset(skb); struct ovs_frag_data *data; @@ -694,6 +702,7 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb) data->vport = vport; data->cb = *OVS_CB(skb); data->inner_protocol = skb->inner_protocol; + data->network_offset = orig_network_offset; data->vlan_tci = skb->vlan_tci; data->vlan_proto = skb->vlan_proto; data->l2_len = hlen; @@ -706,6 +715,13 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb) static void ovs_fragment(struct net *net, struct vport *vport, struct sk_buff *skb, u16 mru, __be16 ethertype) { + u16 orig_network_offset = 0; + + if (eth_p_mpls(skb->protocol)) { + orig_network_offset = skb_network_offset(skb); + skb->network_header = skb->inner_network_header; + } + if (skb_network_offset(skb) > MAX_L2_LEN) { OVS_NLERR(1, "L2 header too long to fragment"); goto err; @@ -715,7 +731,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, struct dst_entry ovs_dst; unsigned long orig_dst; - prepare_frag(vport, skb); + prepare_frag(vport, skb, orig_network_offset); dst_init(&ovs_dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); ovs_dst.dev = vport->dev; @@ -735,7 +751,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, goto err; } - prepare_frag(vport, skb); + prepare_frag(vport, skb, orig_network_offset); memset(&ovs_rt, 0, sizeof(ovs_rt)); dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); -- cgit v1.1 From 751d6fd18fb3c6cc123dce844200ddbf5a8ad1a8 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 6 Oct 2016 14:35:57 -0700 Subject: drivers: net: xgene: fix: Use GPIO to get link status The link value reported by the link status register is not reliable when no SPF module inserted. This patchset fixes this issue by using GPIO to determine the link status. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 6 +++++- drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 1 + drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 19 +++++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 429f18f..f75d955 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1381,9 +1381,13 @@ static void xgene_enet_gpiod_get(struct xgene_enet_pdata *pdata) { struct device *dev = &pdata->pdev->dev; - if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) + pdata->sfp_gpio_en = false; + if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII || + (!device_property_present(dev, "sfp-gpios") && + !device_property_present(dev, "rxlos-gpios"))) return; + pdata->sfp_gpio_en = true; pdata->sfp_rdy = gpiod_get(dev, "rxlos", GPIOD_IN); if (IS_ERR(pdata->sfp_rdy)) pdata->sfp_rdy = gpiod_get(dev, "sfp", GPIOD_IN); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 0cda58f..011965b 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -219,6 +219,7 @@ struct xgene_enet_pdata { u8 rx_delay; bool mdio_driver; struct gpio_desc *sfp_rdy; + bool sfp_gpio_en; }; struct xgene_indirect_ctl { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index 6475f38..d1758b0 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -415,16 +415,31 @@ static void xgene_enet_clear(struct xgene_enet_pdata *pdata, xgene_enet_wr_ring_if(pdata, addr, data); } +static int xgene_enet_gpio_lookup(struct xgene_enet_pdata *pdata) +{ + struct device *dev = &pdata->pdev->dev; + + pdata->sfp_rdy = gpiod_get(dev, "rxlos", GPIOD_IN); + if (IS_ERR(pdata->sfp_rdy)) + pdata->sfp_rdy = gpiod_get(dev, "sfp", GPIOD_IN); + + if (IS_ERR(pdata->sfp_rdy)) + return -ENODEV; + + return 0; +} + static void xgene_enet_link_state(struct work_struct *work) { struct xgene_enet_pdata *pdata = container_of(to_delayed_work(work), struct xgene_enet_pdata, link_work); - struct gpio_desc *sfp_rdy = pdata->sfp_rdy; struct net_device *ndev = pdata->ndev; u32 link_status, poll_interval; link_status = xgene_enet_link_status(pdata); - if (link_status && !IS_ERR(sfp_rdy) && !gpiod_get_value(sfp_rdy)) + if (pdata->sfp_gpio_en && link_status && + (!IS_ERR(pdata->sfp_rdy) || !xgene_enet_gpio_lookup(pdata)) && + !gpiod_get_value(pdata->sfp_rdy)) link_status = 0; if (link_status) { -- cgit v1.1 From d2561ed389579e02249449365323113b1c075921 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 6 Oct 2016 14:35:58 -0700 Subject: arm64: xgene: defconfig: Enable Standby GPIO Enable CONFIG_GPIO_XGENE_SB. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index dab2cb0..6be0811 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -257,6 +257,7 @@ CONFIG_GPIO_DWAPB=y CONFIG_GPIO_PL061=y CONFIG_GPIO_RCAR=y CONFIG_GPIO_XGENE=y +CONFIG_GPIO_XGENE_SB=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y CONFIG_GPIO_MAX77620=y -- cgit v1.1 From 4e0ad32216910905d00a1228379163c6e5057dc4 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 7 Oct 2016 15:51:47 +0200 Subject: Subject: [PATCH] af_iucv: enable control sends in case of SEND_SHUTDOWN If a socket program has shut down the socket for sending, it can still receive an undetermined number of packets. The AF_IUCV protocol for HIPER transport requires sending of a WIN flag from time to time from the receiver to the sender, otherwise the peer cannot continue sending. That means sending of control flags must still work, even though the AF_IUCV socket is shutdown for sending data. sock_alloc_send_skb() returns with error EPIPE, if socket sk_shutdown is SEND_SHUTDOWN. Thus this patch temporarily removes the send shutdown attribute from the socket to enable transfer of control flags. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/iucv/af_iucv.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 02b45a8..88d0eaa 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -453,19 +453,27 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) } } -/* Send FIN through an IUCV socket for HIPER transport */ +/* Send controlling flags through an IUCV socket for HIPER transport */ static int iucv_send_ctrl(struct sock *sk, u8 flags) { int err = 0; int blen; struct sk_buff *skb; + u8 shutdown = 0; blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; + if (sk->sk_shutdown & SEND_SHUTDOWN) { + /* controlling flags should be sent anyway */ + shutdown = sk->sk_shutdown; + sk->sk_shutdown &= RCV_SHUTDOWN; + } skb = sock_alloc_send_skb(sk, blen, 1, &err); if (skb) { skb_reserve(skb, blen); err = afiucv_hs_send(NULL, sk, skb, flags); } + if (shutdown) + sk->sk_shutdown = shutdown; return err; } -- cgit v1.1 From 8c68b1a0e9fa288015f1492e1b342db159734da9 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 7 Oct 2016 15:51:48 +0200 Subject: Subject: [PATCH] af_iucv: drop skbs rejected by filter A packet filter might be installed for instance with setsockopt SO_ATTACH_FILTER. af_iucv currently queues skbs rejected by filter into the backlog queue. This does not make sense, since packets rejected by filter can be dropped immediately. This patch adds separate sk_filter return code checking, and dropping of packets if applicable. Signed-off-by: Ursula Braun Reported-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/iucv/af_iucv.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 88d0eaa..cfb9e5f 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1323,8 +1323,13 @@ static void iucv_process_message(struct sock *sk, struct sk_buff *skb, } IUCV_SKB_CB(skb)->offset = 0; - if (sock_queue_rcv_skb(sk, skb)) - skb_queue_head(&iucv_sk(sk)->backlog_skb_q, skb); + if (sk_filter(sk, skb)) { + atomic_inc(&sk->sk_drops); /* skb rejected by filter */ + kfree_skb(skb); + return; + } + if (__sock_queue_rcv_skb(sk, skb)) /* handle rcv queue full */ + skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb); } /* iucv_process_message_q() - Process outstanding IUCV messages @@ -1438,13 +1443,13 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, rskb = skb_dequeue(&iucv->backlog_skb_q); while (rskb) { IUCV_SKB_CB(rskb)->offset = 0; - if (sock_queue_rcv_skb(sk, rskb)) { + if (__sock_queue_rcv_skb(sk, rskb)) { + /* handle rcv queue full */ skb_queue_head(&iucv->backlog_skb_q, rskb); break; - } else { - rskb = skb_dequeue(&iucv->backlog_skb_q); } + rskb = skb_dequeue(&iucv->backlog_skb_q); } if (skb_queue_empty(&iucv->backlog_skb_q)) { if (!list_empty(&iucv->message_q.list)) @@ -2124,12 +2129,17 @@ static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb) skb_reset_transport_header(skb); skb_reset_network_header(skb); IUCV_SKB_CB(skb)->offset = 0; + if (sk_filter(sk, skb)) { + atomic_inc(&sk->sk_drops); /* skb rejected by filter */ + kfree_skb(skb); + return NET_RX_SUCCESS; + } + spin_lock(&iucv->message_q.lock); if (skb_queue_empty(&iucv->backlog_skb_q)) { - if (sock_queue_rcv_skb(sk, skb)) { + if (__sock_queue_rcv_skb(sk, skb)) /* handle rcv queue full */ skb_queue_tail(&iucv->backlog_skb_q, skb); - } } else skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb); spin_unlock(&iucv->message_q.lock); -- cgit v1.1 From 850092db5acfd9cd2c9c2e80ca974843c4f1283b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Oct 2016 15:32:16 +0200 Subject: mac80211: remove unnecessary mesh check sta_info_get_bss() is equivalent to sta_info_get() in the mesh case, since sta->sdata->bss will be NULL (it's only set for AP/AP_VLAN interfaces.) Thus, the mesh check here isn't actually needed - remove it. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fd6541f..7d01723 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -357,10 +357,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, mutex_lock(&local->sta_mtx); if (mac_addr) { - if (ieee80211_vif_is_mesh(&sdata->vif)) - sta = sta_info_get(sdata, mac_addr); - else - sta = sta_info_get_bss(sdata, mac_addr); + sta = sta_info_get_bss(sdata, mac_addr); /* * The ASSOC test makes sure the driver is ready to * receive the key. When wpa_supplicant has roamed -- cgit v1.1 From 246ad56e25b4005648cd54e0f0ab8f0357f8a42a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Oct 2016 12:09:52 +0200 Subject: mac80211_hwsim: make multi-channel ops const Instead of building the multi-channel ops at runtime, declare the common ops with a macro and build both that way, so that the multi-channel ops can also be const. As a side effect, due to the removed code, this decreases the size of the module (while shifting data from .bss to .text due to the newly added const). Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 79 ++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 431f13b..f9ba0772 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2256,35 +2256,51 @@ static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw, WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN); } +#define HWSIM_COMMON_OPS \ + .tx = mac80211_hwsim_tx, \ + .start = mac80211_hwsim_start, \ + .stop = mac80211_hwsim_stop, \ + .add_interface = mac80211_hwsim_add_interface, \ + .change_interface = mac80211_hwsim_change_interface, \ + .remove_interface = mac80211_hwsim_remove_interface, \ + .config = mac80211_hwsim_config, \ + .configure_filter = mac80211_hwsim_configure_filter, \ + .bss_info_changed = mac80211_hwsim_bss_info_changed, \ + .sta_add = mac80211_hwsim_sta_add, \ + .sta_remove = mac80211_hwsim_sta_remove, \ + .sta_notify = mac80211_hwsim_sta_notify, \ + .set_tim = mac80211_hwsim_set_tim, \ + .conf_tx = mac80211_hwsim_conf_tx, \ + .get_survey = mac80211_hwsim_get_survey, \ + CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) \ + .ampdu_action = mac80211_hwsim_ampdu_action, \ + .flush = mac80211_hwsim_flush, \ + .get_tsf = mac80211_hwsim_get_tsf, \ + .set_tsf = mac80211_hwsim_set_tsf, \ + .get_et_sset_count = mac80211_hwsim_get_et_sset_count, \ + .get_et_stats = mac80211_hwsim_get_et_stats, \ + .get_et_strings = mac80211_hwsim_get_et_strings, + static const struct ieee80211_ops mac80211_hwsim_ops = { - .tx = mac80211_hwsim_tx, - .start = mac80211_hwsim_start, - .stop = mac80211_hwsim_stop, - .add_interface = mac80211_hwsim_add_interface, - .change_interface = mac80211_hwsim_change_interface, - .remove_interface = mac80211_hwsim_remove_interface, - .config = mac80211_hwsim_config, - .configure_filter = mac80211_hwsim_configure_filter, - .bss_info_changed = mac80211_hwsim_bss_info_changed, - .sta_add = mac80211_hwsim_sta_add, - .sta_remove = mac80211_hwsim_sta_remove, - .sta_notify = mac80211_hwsim_sta_notify, - .set_tim = mac80211_hwsim_set_tim, - .conf_tx = mac80211_hwsim_conf_tx, - .get_survey = mac80211_hwsim_get_survey, - CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) - .ampdu_action = mac80211_hwsim_ampdu_action, + HWSIM_COMMON_OPS .sw_scan_start = mac80211_hwsim_sw_scan, .sw_scan_complete = mac80211_hwsim_sw_scan_complete, - .flush = mac80211_hwsim_flush, - .get_tsf = mac80211_hwsim_get_tsf, - .set_tsf = mac80211_hwsim_set_tsf, - .get_et_sset_count = mac80211_hwsim_get_et_sset_count, - .get_et_stats = mac80211_hwsim_get_et_stats, - .get_et_strings = mac80211_hwsim_get_et_strings, }; -static struct ieee80211_ops mac80211_hwsim_mchan_ops; +static const struct ieee80211_ops mac80211_hwsim_mchan_ops = { + HWSIM_COMMON_OPS + .hw_scan = mac80211_hwsim_hw_scan, + .cancel_hw_scan = mac80211_hwsim_cancel_hw_scan, + .sw_scan_start = NULL, + .sw_scan_complete = NULL, + .remain_on_channel = mac80211_hwsim_roc, + .cancel_remain_on_channel = mac80211_hwsim_croc, + .add_chanctx = mac80211_hwsim_add_chanctx, + .remove_chanctx = mac80211_hwsim_remove_chanctx, + .change_chanctx = mac80211_hwsim_change_chanctx, + .assign_vif_chanctx = mac80211_hwsim_assign_vif_chanctx, + .unassign_vif_chanctx = mac80211_hwsim_unassign_vif_chanctx, +}; struct hwsim_new_radio_params { unsigned int channels; @@ -3360,21 +3376,6 @@ static int __init init_mac80211_hwsim(void) if (channels < 1) return -EINVAL; - mac80211_hwsim_mchan_ops = mac80211_hwsim_ops; - mac80211_hwsim_mchan_ops.hw_scan = mac80211_hwsim_hw_scan; - mac80211_hwsim_mchan_ops.cancel_hw_scan = mac80211_hwsim_cancel_hw_scan; - mac80211_hwsim_mchan_ops.sw_scan_start = NULL; - mac80211_hwsim_mchan_ops.sw_scan_complete = NULL; - mac80211_hwsim_mchan_ops.remain_on_channel = mac80211_hwsim_roc; - mac80211_hwsim_mchan_ops.cancel_remain_on_channel = mac80211_hwsim_croc; - mac80211_hwsim_mchan_ops.add_chanctx = mac80211_hwsim_add_chanctx; - mac80211_hwsim_mchan_ops.remove_chanctx = mac80211_hwsim_remove_chanctx; - mac80211_hwsim_mchan_ops.change_chanctx = mac80211_hwsim_change_chanctx; - mac80211_hwsim_mchan_ops.assign_vif_chanctx = - mac80211_hwsim_assign_vif_chanctx; - mac80211_hwsim_mchan_ops.unassign_vif_chanctx = - mac80211_hwsim_unassign_vif_chanctx; - spin_lock_init(&hwsim_radio_lock); err = register_pernet_device(&hwsim_net_ops); -- cgit v1.1 From 5f9994bd4a58f72a4312fb3e09c7fe2c4ca95b82 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Mon, 10 Oct 2016 19:12:20 +0200 Subject: mac80211: remove unnecessary num_mcast_sta check Checking for num_mcast_sta in __ieee80211_request_smps_ap() is unnecessary as sta list will be empty in this case anyway, so the list iteration will just exit immediately. Since this isn't a "hot" code path, it doesn't really matter, and the next patch will redefine num_mcast_sta to make this check invalid. Signed-off-by: Michael Braun [change commit message] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7d01723..7de34a3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2477,13 +2477,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, smps_mode == IEEE80211_SMPS_AUTOMATIC) return 0; - /* If no associated stations, there's no need to do anything */ - if (!atomic_read(&sdata->u.ap.num_mcast_sta)) { - sdata->smps_mode = smps_mode; - ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); - return 0; - } - ht_dbg(sdata, "SMPS %d requested in AP mode, sending Action frame to %d stations\n", smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); -- cgit v1.1 From 72f15d53f3e0ca19bb5755b9cbdde000ee2f702e Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Mon, 10 Oct 2016 19:12:21 +0200 Subject: mac80211: filter multicast data packets on AP / AP_VLAN This patch adds filtering for multicast data packets on AP_VLAN interfaces that have no authorized station connected and changes filtering on AP interfaces to not count stations assigned to AP_VLAN interfaces. This saves airtime and avoids waking up other stations currently authorized in this BSS. When using WPA, the packets dropped could not be decrypted by any station. The behaviour when there are no AP_VLAN interfaces is left unchanged. When there are AP_VLAN interfaces, this patch 1. adds filtering multicast data packets sent on AP_VLAN interfaces that have no authorized station connected. No filtering happens on 4addr AP_VLAN interfaces. 2. makes filtering of multicast data packets sent on AP interfaces depend on the number of authorized stations in this bss not assigned to an AP_VLAN interface. Therefore, a new num_mcast_sta counter is added for AP_VLAN interfaces. The existing one for AP interfaces is altered to not track stations assigned to an AP_VLAN interface. The new counter is exposed in debugfs. Signed-off-by: Michael Braun [reformat commit message a bit, unline ieee80211_vif_{inc,dec}_num_mcast] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 20 ++++++-------------- net/mac80211/debugfs_netdev.c | 11 +++++++++++ net/mac80211/ieee80211_i.h | 18 ++++++++++++++++++ net/mac80211/iface.c | 16 ++++++++++++++++ net/mac80211/rx.c | 5 +++-- net/mac80211/sta_info.c | 10 ++-------- net/mac80211/tx.c | 5 ++--- 7 files changed, 58 insertions(+), 27 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7de34a3..eb68add 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1520,9 +1520,6 @@ static int ieee80211_change_station(struct wiphy *wiphy, goto out_err; if (params->vlan && params->vlan != sta->sdata->dev) { - bool prev_4addr = false; - bool new_4addr = false; - vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (params->vlan->ieee80211_ptr->use_4addr) { @@ -1532,26 +1529,21 @@ static int ieee80211_change_station(struct wiphy *wiphy, } rcu_assign_pointer(vlansdata->u.vlan.sta, sta); - new_4addr = true; __ieee80211_check_fast_rx_iface(vlansdata); } if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && - sta->sdata->u.vlan.sta) { + sta->sdata->u.vlan.sta) RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL); - prev_4addr = true; - } + + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + ieee80211_vif_dec_num_mcast(sta->sdata); sta->sdata = vlansdata; ieee80211_check_fast_xmit(sta); - if (sta->sta_state == IEEE80211_STA_AUTHORIZED && - prev_4addr != new_4addr) { - if (new_4addr) - atomic_dec(&sta->sdata->bss->num_mcast_sta); - else - atomic_inc(&sta->sdata->bss->num_mcast_sta); - } + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + ieee80211_vif_inc_num_mcast(sta->sdata); ieee80211_send_layer2_update(sta); } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index bcec124..1a05f85 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -477,6 +477,7 @@ IEEE80211_IF_FILE_RW(tdls_wider_bw); IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC); IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC); IEEE80211_IF_FILE(dtim_count, u.ap.ps.dtim_count, DEC); +IEEE80211_IF_FILE(num_mcast_sta_vlan, u.vlan.num_mcast_sta, ATOMIC); static ssize_t ieee80211_if_fmt_num_buffered_multicast( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) @@ -684,6 +685,13 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } +static void add_vlan_files(struct ieee80211_sub_if_data *sdata) +{ + /* add num_mcast_sta_vlan using name num_mcast_sta */ + debugfs_create_file("num_mcast_sta", 0400, sdata->vif.debugfs_dir, + sdata, &num_mcast_sta_vlan_ops); +} + static void add_ibss_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD_MODE(tsf, 0600); @@ -787,6 +795,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata) case NL80211_IFTYPE_AP: add_ap_files(sdata); break; + case NL80211_IFTYPE_AP_VLAN: + add_vlan_files(sdata); + break; case NL80211_IFTYPE_WDS: add_wds_files(sdata); break; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 34c2add..103187c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -307,6 +307,7 @@ struct ieee80211_if_vlan { /* used for all tx if the VLAN is configured to 4-addr mode */ struct sta_info __rcu *sta; + atomic_t num_mcast_sta; /* number of stations receiving multicast */ }; struct mesh_stats { @@ -1527,6 +1528,23 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) return false; } +void ieee80211_vif_inc_num_mcast(struct ieee80211_sub_if_data *sdata); +void ieee80211_vif_dec_num_mcast(struct ieee80211_sub_if_data *sdata); + +/* This function returns the number of multicast stations connected to this + * interface. It returns -1 if that number is not tracked, that is for netdevs + * not in AP or AP_VLAN mode or when using 4addr. + */ +static inline int +ieee80211_vif_get_num_mcast_if(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->vif.type == NL80211_IFTYPE_AP) + return atomic_read(&sdata->u.ap.num_mcast_sta); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta) + return atomic_read(&sdata->u.vlan.num_mcast_sta); + return -1; +} + u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, struct ieee80211_rx_status *status, unsigned int mpdu_len, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 638ec07..a8245d9b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -2005,3 +2005,19 @@ void ieee80211_iface_exit(void) { unregister_netdevice_notifier(&mac80211_netdev_notifier); } + +void ieee80211_vif_inc_num_mcast(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->vif.type == NL80211_IFTYPE_AP) + atomic_inc(&sdata->u.ap.num_mcast_sta); + else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + atomic_inc(&sdata->u.vlan.num_mcast_sta); +} + +void ieee80211_vif_dec_num_mcast(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->vif.type == NL80211_IFTYPE_AP) + atomic_dec(&sdata->u.ap.num_mcast_sta); + else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + atomic_dec(&sdata->u.vlan.num_mcast_sta); +} diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6175db3..fc08a50 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2215,7 +2215,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) { - if (is_multicast_ether_addr(ehdr->h_dest)) { + if (is_multicast_ether_addr(ehdr->h_dest) && + ieee80211_vif_get_num_mcast_if(sdata) != 0) { /* * send multicast frames both to higher layers in * local net stack and back to the wireless medium @@ -2224,7 +2225,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) if (!xmit_skb) net_info_ratelimited("%s: failed to clone multicast frame\n", dev->name); - } else { + } else if (!is_multicast_ether_addr(ehdr->h_dest)) { dsta = sta_info_get(sdata, skb->data); if (dsta) { /* diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 78e9ecb..ab91e1e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1871,10 +1871,7 @@ int sta_info_move_state(struct sta_info *sta, if (!sta->sta.support_p2p_ps) ieee80211_recalc_p2p_go_ps_allowed(sta->sdata); } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { - if (sta->sdata->vif.type == NL80211_IFTYPE_AP || - (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && - !sta->sdata->u.vlan.sta)) - atomic_dec(&sta->sdata->bss->num_mcast_sta); + ieee80211_vif_dec_num_mcast(sta->sdata); clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); ieee80211_clear_fast_xmit(sta); ieee80211_clear_fast_rx(sta); @@ -1882,10 +1879,7 @@ int sta_info_move_state(struct sta_info *sta, break; case IEEE80211_STA_AUTHORIZED: if (sta->sta_state == IEEE80211_STA_ASSOC) { - if (sta->sdata->vif.type == NL80211_IFTYPE_AP || - (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && - !sta->sdata->u.vlan.sta)) - atomic_inc(&sta->sdata->bss->num_mcast_sta); + ieee80211_vif_inc_num_mcast(sta->sdata); set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); ieee80211_check_fast_xmit(sta); ieee80211_check_fast_rx(sta); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1c56abc..f5d45e4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -331,9 +331,8 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc); return TX_DROP; } - } else if (unlikely(tx->sdata->vif.type == NL80211_IFTYPE_AP && - ieee80211_is_data(hdr->frame_control) && - !atomic_read(&tx->sdata->u.ap.num_mcast_sta))) { + } else if (unlikely(ieee80211_is_data(hdr->frame_control) && + ieee80211_vif_get_num_mcast_if(tx->sdata) == 0)) { /* * No associated STAs - no need to send multicast * frames. -- cgit v1.1 From 32910bb951a2a0332d70b23f6b3f83fa988dfcde Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Oct 2016 14:17:13 +0200 Subject: mac80211: preserve more bits when building QoS header Michael Braun reported that when trying to inject A-MSDUs over monitor interfaces, the frame doesn't come out right since the QoS header A-MSDU bit is overwritten. Rather than adding that bit specifically simply preserve those bits that we don't set here, since we typically get here with a zeroed-out QoS header anyway. Signed-off-by: Johannes Berg --- net/mac80211/wme.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 9eb0aee..3e3d301 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -236,26 +236,35 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, { struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + u8 flags; u8 *p; - u8 ack_policy, tid; if (!ieee80211_is_data_qos(hdr->frame_control)) return; p = ieee80211_get_qos_ctl(hdr); - tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - /* preserve EOSP bit */ - ack_policy = *p & IEEE80211_QOS_CTL_EOSP; + /* set up the first byte */ + + /* + * preserve everything but the TID and ACK policy + * (which we both write here) + */ + flags = *p & ~(IEEE80211_QOS_CTL_TID_MASK | + IEEE80211_QOS_CTL_ACK_POLICY_MASK); if (is_multicast_ether_addr(hdr->addr1) || sdata->noack_map & BIT(tid)) { - ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; + flags |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; info->flags |= IEEE80211_TX_CTL_NO_ACK; } - /* qos header is 2 bytes */ - *p++ = ack_policy | tid; + *p = flags | tid; + + /* set up the second byte */ + p++; + if (ieee80211_vif_is_mesh(&sdata->vif)) { /* preserve RSPI and Mesh PS Level bit */ *p &= ((IEEE80211_QOS_CTL_RSPI | -- cgit v1.1 From e227300c8395dffaa7614ce7c7666a82180ebc60 Mon Sep 17 00:00:00 2001 From: Purushottam Kushwaha Date: Wed, 12 Oct 2016 18:25:35 +0530 Subject: cfg80211: pass struct to interface combination check/iter Move the growing parameter list to a structure for the interface combination check and iteration functions in cfg80211 and mac80211 to make the code easier to understand. Signed-off-by: Purushottam Kushwaha [edit commit message] Signed-off-by: Johannes Berg --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 22 ++++++----- include/net/cfg80211.h | 46 +++++++++++----------- net/mac80211/util.c | 44 ++++++++++----------- net/wireless/util.c | 28 ++++++------- 4 files changed, 68 insertions(+), 72 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index b777e1b..2295336 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -414,23 +414,24 @@ static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_vif *vif, enum nl80211_iftype new_type) { - int iftype_num[NUM_NL80211_IFTYPES]; struct brcmf_cfg80211_vif *pos; bool check_combos = false; int ret = 0; + struct iface_combination_params params = { + .num_different_channels = 1, + }; - memset(&iftype_num[0], 0, sizeof(iftype_num)); list_for_each_entry(pos, &cfg->vif_list, list) if (pos == vif) { - iftype_num[new_type]++; + params.iftype_num[new_type]++; } else { /* concurrent interfaces so need check combinations */ check_combos = true; - iftype_num[pos->wdev.iftype]++; + params.iftype_num[pos->wdev.iftype]++; } if (check_combos) - ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); + ret = cfg80211_check_combinations(cfg->wiphy, ¶ms); return ret; } @@ -438,15 +439,16 @@ static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, enum nl80211_iftype new_type) { - int iftype_num[NUM_NL80211_IFTYPES]; struct brcmf_cfg80211_vif *pos; + struct iface_combination_params params = { + .num_different_channels = 1, + }; - memset(&iftype_num[0], 0, sizeof(iftype_num)); list_for_each_entry(pos, &cfg->vif_list, list) - iftype_num[pos->wdev.iftype]++; + params.iftype_num[pos->wdev.iftype]++; - iftype_num[new_type]++; - return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); + params.iftype_num[new_type]++; + return cfg80211_check_combinations(cfg->wiphy, ¶ms); } static void convert_key_from_CPU(struct brcmf_wsec_key *key, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fe78f02..ea10854 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -772,6 +772,26 @@ struct cfg80211_csa_settings { }; /** + * struct iface_combination_params - input parameters for interface combinations + * + * Used to pass interface combination parameters + * + * @num_different_channels: the number of different channels we want + * to use for verification + * @radar_detect: a bitmap where each bit corresponds to a channel + * width where radar detection is needed, as in the definition of + * &struct ieee80211_iface_combination.@radar_detect_widths + * @iftype_num: array with the number of interfaces of each interface + * type. The index is the interface type as specified in &enum + * nl80211_iftype. + */ +struct iface_combination_params { + int num_different_channels; + u8 radar_detect; + int iftype_num[NUM_NL80211_IFTYPES]; +}; + +/** * enum station_parameters_apply_mask - station parameter values to apply * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) * @STATION_PARAM_APPLY_CAPABILITY: apply new capability @@ -5575,36 +5595,20 @@ unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy); * cfg80211_check_combinations - check interface combinations * * @wiphy: the wiphy - * @num_different_channels: the number of different channels we want - * to use for verification - * @radar_detect: a bitmap where each bit corresponds to a channel - * width where radar detection is needed, as in the definition of - * &struct ieee80211_iface_combination.@radar_detect_widths - * @iftype_num: array with the numbers of interfaces of each interface - * type. The index is the interface type as specified in &enum - * nl80211_iftype. + * @params: the interface combinations parameter * * This function can be called by the driver to check whether a * combination of interfaces and their types are allowed according to * the interface combinations. */ int cfg80211_check_combinations(struct wiphy *wiphy, - const int num_different_channels, - const u8 radar_detect, - const int iftype_num[NUM_NL80211_IFTYPES]); + struct iface_combination_params *params); /** * cfg80211_iter_combinations - iterate over matching combinations * * @wiphy: the wiphy - * @num_different_channels: the number of different channels we want - * to use for verification - * @radar_detect: a bitmap where each bit corresponds to a channel - * width where radar detection is needed, as in the definition of - * &struct ieee80211_iface_combination.@radar_detect_widths - * @iftype_num: array with the numbers of interfaces of each interface - * type. The index is the interface type as specified in &enum - * nl80211_iftype. + * @params: the interface combinations parameter * @iter: function to call for each matching combination * @data: pointer to pass to iter function * @@ -5613,9 +5617,7 @@ int cfg80211_check_combinations(struct wiphy *wiphy, * purposes. */ int cfg80211_iter_combinations(struct wiphy *wiphy, - const int num_different_channels, - const u8 radar_detect, - const int iftype_num[NUM_NL80211_IFTYPES], + struct iface_combination_params *params, void (*iter)(const struct ieee80211_iface_combination *c, void *data), void *data); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 545c79a..031273a 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3308,10 +3308,11 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_sub_if_data *sdata_iter; enum nl80211_iftype iftype = sdata->wdev.iftype; - int num[NUM_NL80211_IFTYPES]; struct ieee80211_chanctx *ctx; - int num_different_channels = 0; int total = 1; + struct iface_combination_params params = { + .radar_detect = radar_detect, + }; lockdep_assert_held(&local->chanctx_mtx); @@ -3322,9 +3323,6 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, !chandef->chan)) return -EINVAL; - if (chandef) - num_different_channels = 1; - if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) return -EINVAL; @@ -3335,24 +3333,26 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, return 0; } - memset(num, 0, sizeof(num)); + if (chandef) + params.num_different_channels = 1; if (iftype != NL80211_IFTYPE_UNSPECIFIED) - num[iftype] = 1; + params.iftype_num[iftype] = 1; list_for_each_entry(ctx, &local->chanctx_list, list) { if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) continue; - radar_detect |= ieee80211_chanctx_radar_detect(local, ctx); + params.radar_detect |= + ieee80211_chanctx_radar_detect(local, ctx); if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { - num_different_channels++; + params.num_different_channels++; continue; } if (chandef && chanmode == IEEE80211_CHANCTX_SHARED && cfg80211_chandef_compatible(chandef, &ctx->conf.def)) continue; - num_different_channels++; + params.num_different_channels++; } list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) { @@ -3365,16 +3365,14 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype)) continue; - num[wdev_iter->iftype]++; + params.iftype_num[wdev_iter->iftype]++; total++; } - if (total == 1 && !radar_detect) + if (total == 1 && !params.radar_detect) return 0; - return cfg80211_check_combinations(local->hw.wiphy, - num_different_channels, - radar_detect, num); + return cfg80211_check_combinations(local->hw.wiphy, ¶ms); } static void @@ -3390,12 +3388,10 @@ ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c, int ieee80211_max_num_channels(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; - int num[NUM_NL80211_IFTYPES] = {}; struct ieee80211_chanctx *ctx; - int num_different_channels = 0; - u8 radar_detect = 0; u32 max_num_different_channels = 1; int err; + struct iface_combination_params params = {0}; lockdep_assert_held(&local->chanctx_mtx); @@ -3403,17 +3399,17 @@ int ieee80211_max_num_channels(struct ieee80211_local *local) if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) continue; - num_different_channels++; + params.num_different_channels++; - radar_detect |= ieee80211_chanctx_radar_detect(local, ctx); + params.radar_detect |= + ieee80211_chanctx_radar_detect(local, ctx); } list_for_each_entry_rcu(sdata, &local->interfaces, list) - num[sdata->wdev.iftype]++; + params.iftype_num[sdata->wdev.iftype]++; - err = cfg80211_iter_combinations(local->hw.wiphy, - num_different_channels, radar_detect, - num, ieee80211_iter_max_chans, + err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms, + ieee80211_iter_max_chans, &max_num_different_channels); if (err < 0) return err; diff --git a/net/wireless/util.c b/net/wireless/util.c index 8edce22..0d69b25 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1580,9 +1580,7 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, } int cfg80211_iter_combinations(struct wiphy *wiphy, - const int num_different_channels, - const u8 radar_detect, - const int iftype_num[NUM_NL80211_IFTYPES], + struct iface_combination_params *params, void (*iter)(const struct ieee80211_iface_combination *c, void *data), void *data) @@ -1593,7 +1591,7 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, int num_interfaces = 0; u32 used_iftypes = 0; - if (radar_detect) { + if (params->radar_detect) { rcu_read_lock(); regdom = rcu_dereference(cfg80211_regdomain); if (regdom) @@ -1602,8 +1600,8 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, } for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { - num_interfaces += iftype_num[iftype]; - if (iftype_num[iftype] > 0 && + num_interfaces += params->iftype_num[iftype]; + if (params->iftype_num[iftype] > 0 && !(wiphy->software_iftypes & BIT(iftype))) used_iftypes |= BIT(iftype); } @@ -1617,7 +1615,7 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if (num_interfaces > c->max_interfaces) continue; - if (num_different_channels > c->num_different_channels) + if (params->num_different_channels > c->num_different_channels) continue; limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, @@ -1632,16 +1630,17 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, all_iftypes |= limits[j].types; if (!(limits[j].types & BIT(iftype))) continue; - if (limits[j].max < iftype_num[iftype]) + if (limits[j].max < params->iftype_num[iftype]) goto cont; - limits[j].max -= iftype_num[iftype]; + limits[j].max -= params->iftype_num[iftype]; } } - if (radar_detect != (c->radar_detect_widths & radar_detect)) + if (params->radar_detect != + (c->radar_detect_widths & params->radar_detect)) goto cont; - if (radar_detect && c->radar_detect_regions && + if (params->radar_detect && c->radar_detect_regions && !(c->radar_detect_regions & BIT(region))) goto cont; @@ -1675,14 +1674,11 @@ cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c, } int cfg80211_check_combinations(struct wiphy *wiphy, - const int num_different_channels, - const u8 radar_detect, - const int iftype_num[NUM_NL80211_IFTYPES]) + struct iface_combination_params *params) { int err, num = 0; - err = cfg80211_iter_combinations(wiphy, num_different_channels, - radar_detect, iftype_num, + err = cfg80211_iter_combinations(wiphy, params, cfg80211_iter_sum_ifcombs, &num); if (err) return err; -- cgit v1.1 From 0c317a02ca982ca093e71bf07cb562265ba40032 Mon Sep 17 00:00:00 2001 From: Purushottam Kushwaha Date: Wed, 12 Oct 2016 18:26:51 +0530 Subject: cfg80211: support virtual interfaces with different beacon intervals This commit provides a mechanism for the host drivers to advertise the support for different beacon intervals among the respective interface combinations in a group, through NL80211_IFACE_COMB_BI_MIN_GCD (u32). This value will be compared against GCD of all beaconing interfaces of matching combinations. If the driver doesn't advertise this value, the old behaviour where all beacon intervals must be identical is retained. If it is specified, then any beacon interval for an interface in the interface combination as well as the GCD of all active beacon intervals in the combination must be greater or equal to this value. Signed-off-by: Purushottam Kushwaha [change commit message, some variable names, small other things] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 15 +++++++++++++++ include/uapi/linux/nl80211.h | 8 ++++++-- net/wireless/core.h | 2 +- net/wireless/nl80211.c | 14 +++++++++++--- net/wireless/util.c | 43 +++++++++++++++++++++++++++++++++++++------ 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ea10854..5000ec75 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -784,11 +784,19 @@ struct cfg80211_csa_settings { * @iftype_num: array with the number of interfaces of each interface * type. The index is the interface type as specified in &enum * nl80211_iftype. + * @beacon_int_gcd: a value specifying GCD of all beaconing interfaces, + * the GCD of a single value is considered the value itself, so for + * a single interface this should be set to that interface's beacon + * interval + * @beacon_int_different: a flag indicating whether or not all beacon + * intervals (of beaconing interfaces) are different or not. */ struct iface_combination_params { int num_different_channels; u8 radar_detect; int iftype_num[NUM_NL80211_IFTYPES]; + u32 beacon_int_gcd; + bool beacon_int_different; }; /** @@ -3100,6 +3108,12 @@ struct ieee80211_iface_limit { * only in special cases. * @radar_detect_widths: bitmap of channel widths supported for radar detection * @radar_detect_regions: bitmap of regions supported for radar detection + * @beacon_int_min_gcd: This interface combination supports different + * beacon intervals. + * = 0 - all beacon intervals for different interface must be same. + * > 0 - any beacon interval for the interface part of this combination AND + * *GCD* of all beacon intervals from beaconing interfaces of this + * combination must be greater or equal to this value. * * With this structure the driver can describe which interface * combinations it supports concurrently. @@ -3158,6 +3172,7 @@ struct ieee80211_iface_combination { bool beacon_int_infra_match; u8 radar_detect_widths; u8 radar_detect_regions; + u32 beacon_int_min_gcd; }; struct ieee80211_txrx_stypes { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 56368e9..1362d24 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4280,6 +4280,9 @@ enum nl80211_iface_limit_attrs { * of supported channel widths for radar detection. * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap * of supported regulatory regions for radar detection. + * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of + * different beacon intervals supported by all the interface combinations + * in this group (if not present, all beacon intervals be identical). * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -4287,8 +4290,8 @@ enum nl80211_iface_limit_attrs { * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 * => allows an AP and a STA that must match BIs * - * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 - * => allows 8 of AP/GO + * numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8, + * => allows 8 of AP/GO that can have BI gcd >= min gcd * * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 * => allows two STAs on different channels @@ -4314,6 +4317,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_NUM_CHANNELS, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, + NL80211_IFACE_COMB_BI_MIN_GCD, /* keep last */ NUM_NL80211_IFACE_COMB, diff --git a/net/wireless/core.h b/net/wireless/core.h index 08d2e94..21e3188 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -475,7 +475,7 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, u32 *mask); int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int); + enum nl80211_iftype iftype, u32 beacon_int); void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c510810..903cd5a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1075,6 +1075,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, c->radar_detect_regions))) goto nla_put_failure; + if (c->beacon_int_min_gcd && + nla_put_u32(msg, NL80211_IFACE_COMB_BI_MIN_GCD, + c->beacon_int_min_gcd)) + goto nla_put_failure; nla_nest_end(msg, nl_combi); } @@ -3803,7 +3807,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params.dtim_period = nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - err = cfg80211_validate_beacon_int(rdev, params.beacon_interval); + err = cfg80211_validate_beacon_int(rdev, dev->ieee80211_ptr->iftype, + params.beacon_interval); if (err) return err; @@ -8152,7 +8157,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - err = cfg80211_validate_beacon_int(rdev, ibss.beacon_interval); + err = cfg80211_validate_beacon_int(rdev, NL80211_IFTYPE_ADHOC, + ibss.beacon_interval); if (err) return err; @@ -9417,7 +9423,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) setup.beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - err = cfg80211_validate_beacon_int(rdev, setup.beacon_interval); + err = cfg80211_validate_beacon_int(rdev, + NL80211_IFTYPE_MESH_POINT, + setup.beacon_interval); if (err) return err; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 0d69b25..d2ea1f1 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1559,24 +1559,46 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, EXPORT_SYMBOL(ieee80211_chandef_to_operating_class); int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int) + enum nl80211_iftype iftype, u32 beacon_int) { struct wireless_dev *wdev; - int res = 0; + struct iface_combination_params params = { + .beacon_int_gcd = beacon_int, /* GCD(n) = n */ + }; if (beacon_int < 10 || beacon_int > 10000) return -EINVAL; + params.iftype_num[iftype] = 1; list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (!wdev->beacon_interval) continue; - if (wdev->beacon_interval != beacon_int) { - res = -EINVAL; - break; + + params.iftype_num[wdev->iftype]++; + } + + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + u32 bi_prev = wdev->beacon_interval; + + if (!wdev->beacon_interval) + continue; + + /* slight optimisation - skip identical BIs */ + if (wdev->beacon_interval == beacon_int) + continue; + + params.beacon_int_different = true; + + /* Get the GCD */ + while (bi_prev != 0) { + u32 tmp_bi = bi_prev; + + bi_prev = params.beacon_int_gcd % bi_prev; + params.beacon_int_gcd = tmp_bi; } } - return res; + return cfg80211_check_combinations(&rdev->wiphy, ¶ms); } int cfg80211_iter_combinations(struct wiphy *wiphy, @@ -1652,6 +1674,15 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if ((all_iftypes & used_iftypes) != used_iftypes) goto cont; + if (params->beacon_int_gcd) { + if (c->beacon_int_min_gcd && + params->beacon_int_gcd < c->beacon_int_min_gcd) + return -EINVAL; + if (!c->beacon_int_min_gcd && + params->beacon_int_different) + goto cont; + } + /* This combination covered all interface types and * supported the requested numbers, so we're good. */ -- cgit v1.1 From 61e84623ace35ce48975e8f90bbbac7557c43d61 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 7 Oct 2016 22:04:33 -0400 Subject: net: centralize net_device min/max MTU checking While looking into an MTU issue with sfc, I started noticing that almost every NIC driver with an ndo_change_mtu function implemented almost exactly the same range checks, and in many cases, that was the only practical thing their ndo_change_mtu function was doing. Quite a few drivers have either 68, 64, 60 or 46 as their minimum MTU value checked, and then various sizes from 1500 to 65535 for their maximum MTU value. We can remove a whole lot of redundant code here if we simple store min_mtu and max_mtu in net_device, and check against those in net/core/dev.c's dev_set_mtu(). In theory, there should be zero functional change with this patch, it just puts the infrastructure in place. Subsequent patches will attempt to start using said infrastructure, with theoretically zero change in functionality. CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++++ net/core/dev.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 136ae6bb..fbdf923 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1506,6 +1506,8 @@ enum netdev_priv_flags { * @if_port: Selectable AUI, TP, ... * @dma: DMA channel * @mtu: Interface MTU value + * @min_mtu: Interface Minimum MTU value + * @max_mtu: Interface Maximum MTU value * @type: Interface hardware type * @hard_header_len: Maximum hardware header length. * @@ -1726,6 +1728,8 @@ struct net_device { unsigned char dma; unsigned int mtu; + unsigned int min_mtu; + unsigned int max_mtu; unsigned short type; unsigned short hard_header_len; diff --git a/net/core/dev.c b/net/core/dev.c index f1fe26f..f376639 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6499,9 +6499,18 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) if (new_mtu == dev->mtu) return 0; - /* MTU must be positive. */ - if (new_mtu < 0) + /* MTU must be positive, and in range */ + if (new_mtu < 0 || new_mtu < dev->min_mtu) { + net_err_ratelimited("%s: Invalid MTU %d requested, hw min %d\n", + dev->name, new_mtu, dev->min_mtu); return -EINVAL; + } + + if (dev->max_mtu > 0 && new_mtu > dev->max_mtu) { + net_err_ratelimited("%s: Invalid MTU %d requested, hw max %d\n", + dev->name, new_mtu, dev->min_mtu); + return -EINVAL; + } if (!netif_device_present(dev)) return -ENODEV; -- cgit v1.1 From a52ad514fdf3b8a57ca4322c92d2d8d5c6182485 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 7 Oct 2016 22:04:34 -0400 Subject: net: deprecate eth_change_mtu, remove usage With centralized MTU checking, there's nothing productive done by eth_change_mtu that isn't already done in dev_set_mtu, so mark it as deprecated and remove all usage of it in the kernel. All callers have been audited for calls to alloc_etherdev* or ether_setup directly, which means they all have a valid dev->min_mtu and dev->max_mtu. Now eth_change_mtu prints out a netdev_warn about being deprecated, for the benefit of out-of-tree drivers that might be utilizing it. Of note, dvb_net.c actually had dev->mtu = 4096, while using eth_change_mtu, meaning that if you ever tried changing it's mtu, you couldn't set it above 1500 anymore. It's now getting dev->max_mtu also set to 4096 to remedy that. v2: fix up lantiq_etop, missed breakage due to drive not compiling on x86 CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- arch/m68k/emu/nfeth.c | 1 - drivers/isdn/hysdn/hysdn_net.c | 1 - drivers/media/dvb-core/dvb_net.c | 2 +- drivers/net/appletalk/ipddp.c | 1 - drivers/net/cris/eth_v10.c | 1 - drivers/net/ethernet/3com/3c509.c | 1 - drivers/net/ethernet/3com/3c515.c | 1 - drivers/net/ethernet/3com/3c574_cs.c | 1 - drivers/net/ethernet/3com/3c589_cs.c | 1 - drivers/net/ethernet/3com/3c59x.c | 2 -- drivers/net/ethernet/3com/typhoon.c | 1 - drivers/net/ethernet/8390/8390.c | 1 - drivers/net/ethernet/8390/8390p.c | 1 - drivers/net/ethernet/8390/ax88796.c | 1 - drivers/net/ethernet/8390/axnet_cs.c | 1 - drivers/net/ethernet/8390/etherh.c | 1 - drivers/net/ethernet/8390/hydra.c | 1 - drivers/net/ethernet/8390/mac8390.c | 1 - drivers/net/ethernet/8390/mcf8390.c | 1 - drivers/net/ethernet/8390/ne2k-pci.c | 1 - drivers/net/ethernet/8390/pcnet_cs.c | 1 - drivers/net/ethernet/8390/smc-ultra.c | 1 - drivers/net/ethernet/8390/wd.c | 1 - drivers/net/ethernet/8390/zorro8390.c | 1 - drivers/net/ethernet/adaptec/starfire.c | 1 - drivers/net/ethernet/adi/bfin_mac.c | 1 - drivers/net/ethernet/allwinner/sun4i-emac.c | 1 - drivers/net/ethernet/amd/a2065.c | 1 - drivers/net/ethernet/amd/am79c961a.c | 1 - drivers/net/ethernet/amd/ariadne.c | 1 - drivers/net/ethernet/amd/atarilance.c | 1 - drivers/net/ethernet/amd/au1000_eth.c | 1 - drivers/net/ethernet/amd/declance.c | 1 - drivers/net/ethernet/amd/hplance.c | 1 - drivers/net/ethernet/amd/lance.c | 1 - drivers/net/ethernet/amd/mvme147.c | 1 - drivers/net/ethernet/amd/ni65.c | 1 - drivers/net/ethernet/amd/nmclan_cs.c | 1 - drivers/net/ethernet/amd/pcnet32.c | 1 - drivers/net/ethernet/amd/sun3lance.c | 1 - drivers/net/ethernet/amd/sunlance.c | 1 - drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 1 - drivers/net/ethernet/apple/bmac.c | 1 - drivers/net/ethernet/apple/mace.c | 1 - drivers/net/ethernet/apple/macmace.c | 1 - drivers/net/ethernet/aurora/nb8800.c | 1 - drivers/net/ethernet/cadence/macb.c | 1 - drivers/net/ethernet/cirrus/cs89x0.c | 1 - drivers/net/ethernet/cirrus/ep93xx_eth.c | 1 - drivers/net/ethernet/cirrus/mac89x0.c | 1 - drivers/net/ethernet/davicom/dm9000.c | 1 - drivers/net/ethernet/dec/tulip/de2104x.c | 1 - drivers/net/ethernet/dec/tulip/de4x5.c | 1 - drivers/net/ethernet/dec/tulip/dmfe.c | 1 - drivers/net/ethernet/dec/tulip/tulip_core.c | 1 - drivers/net/ethernet/dec/tulip/uli526x.c | 1 - drivers/net/ethernet/dec/tulip/winbond-840.c | 1 - drivers/net/ethernet/dec/tulip/xircom_cb.c | 1 - drivers/net/ethernet/dnet.c | 1 - drivers/net/ethernet/ec_bhf.c | 1 - drivers/net/ethernet/fealnx.c | 1 - drivers/net/ethernet/freescale/fec_main.c | 1 - drivers/net/ethernet/freescale/fec_mpc52xx.c | 1 - drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c | 1 - drivers/net/ethernet/freescale/ucc_geth.c | 1 - drivers/net/ethernet/fujitsu/fmvj18x_cs.c | 1 - drivers/net/ethernet/hisilicon/hip04_eth.c | 1 - drivers/net/ethernet/hisilicon/hisi_femac.c | 1 - drivers/net/ethernet/hp/hp100.c | 2 -- drivers/net/ethernet/i825xx/82596.c | 1 - drivers/net/ethernet/i825xx/ether1.c | 1 - drivers/net/ethernet/i825xx/lib82596.c | 1 - drivers/net/ethernet/i825xx/sun3_82586.c | 1 - drivers/net/ethernet/ibm/emac/core.c | 1 - drivers/net/ethernet/korina.c | 1 - drivers/net/ethernet/lantiq_etop.c | 18 ++++++++---------- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 - drivers/net/ethernet/micrel/ks8851.c | 1 - drivers/net/ethernet/micrel/ks8851_mll.c | 1 - drivers/net/ethernet/microchip/enc28j60.c | 1 - drivers/net/ethernet/moxa/moxart_ether.c | 1 - drivers/net/ethernet/natsemi/jazzsonic.c | 1 - drivers/net/ethernet/natsemi/macsonic.c | 1 - drivers/net/ethernet/natsemi/xtsonic.c | 1 - drivers/net/ethernet/netx-eth.c | 1 - drivers/net/ethernet/nuvoton/w90p910_ether.c | 1 - drivers/net/ethernet/nxp/lpc_eth.c | 1 - drivers/net/ethernet/packetengines/hamachi.c | 1 - drivers/net/ethernet/packetengines/yellowfin.c | 1 - drivers/net/ethernet/qlogic/qla3xxx.c | 1 - drivers/net/ethernet/rdc/r6040.c | 1 - drivers/net/ethernet/realtek/atp.c | 1 - drivers/net/ethernet/renesas/ravb_main.c | 1 - drivers/net/ethernet/renesas/sh_eth.c | 2 -- drivers/net/ethernet/seeq/ether3.c | 1 - drivers/net/ethernet/seeq/sgiseeq.c | 1 - drivers/net/ethernet/sgi/ioc3-eth.c | 1 - drivers/net/ethernet/sgi/meth.c | 1 - drivers/net/ethernet/silan/sc92031.c | 1 - drivers/net/ethernet/sis/sis190.c | 1 - drivers/net/ethernet/sis/sis900.c | 1 - drivers/net/ethernet/smsc/epic100.c | 1 - drivers/net/ethernet/smsc/smc911x.c | 1 - drivers/net/ethernet/smsc/smc9194.c | 1 - drivers/net/ethernet/smsc/smc91c92_cs.c | 1 - drivers/net/ethernet/smsc/smc91x.c | 1 - drivers/net/ethernet/smsc/smsc911x.c | 1 - drivers/net/ethernet/sun/sunbmac.c | 1 - drivers/net/ethernet/sun/sunhme.c | 1 - drivers/net/ethernet/sun/sunqe.c | 1 - drivers/net/ethernet/ti/cpmac.c | 1 - drivers/net/ethernet/ti/cpsw.c | 1 - drivers/net/ethernet/ti/tlan.c | 1 - drivers/net/ethernet/toshiba/tc35815.c | 1 - drivers/net/ethernet/tundra/tsi108_eth.c | 1 - drivers/net/ethernet/via/via-rhine.c | 1 - drivers/net/ethernet/wiznet/w5100.c | 1 - drivers/net/ethernet/wiznet/w5300.c | 1 - drivers/net/ethernet/xircom/xirc2ps_cs.c | 1 - drivers/net/ethernet/xscale/ixp4xx_eth.c | 1 - drivers/net/plip/plip.c | 1 - drivers/net/sb1000.c | 1 - drivers/net/usb/catc.c | 1 - drivers/net/usb/kaweth.c | 1 - drivers/net/usb/pegasus.c | 1 - drivers/net/usb/r8152.c | 3 ++- drivers/net/usb/rtl8150.c | 1 - drivers/net/wan/sbni.c | 1 - drivers/net/wireless/intersil/prism54/islpci_dev.c | 1 - drivers/net/wireless/mac80211_hwsim.c | 1 - drivers/net/wireless/marvell/libertas/main.c | 1 - drivers/net/wireless/ray_cs.c | 1 - drivers/net/wireless/wl3501_cs.c | 1 - drivers/net/wireless/zydas/zd1201.c | 1 - drivers/staging/rtl8188eu/os_dep/mon.c | 1 - drivers/staging/rtl8192e/rtl8192e/rtl_core.c | 1 - drivers/staging/rtl8192u/r8192U_core.c | 1 - drivers/staging/slicoss/slicoss.c | 1 - include/uapi/linux/if_ether.h | 2 ++ net/atm/br2684.c | 2 -- net/bluetooth/bnep/netdev.c | 1 - net/ethernet/eth.c | 5 +++-- net/irda/irlan/irlan_eth.c | 1 - 143 files changed, 16 insertions(+), 156 deletions(-) diff --git a/arch/m68k/emu/nfeth.c b/arch/m68k/emu/nfeth.c index a0985fd..fc4be02 100644 --- a/arch/m68k/emu/nfeth.c +++ b/arch/m68k/emu/nfeth.c @@ -184,7 +184,6 @@ static const struct net_device_ops nfeth_netdev_ops = { .ndo_start_xmit = nfeth_xmit, .ndo_tx_timeout = nfeth_tx_timeout, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c index 5609dee..b93a4e9a 100644 --- a/drivers/isdn/hysdn/hysdn_net.c +++ b/drivers/isdn/hysdn/hysdn_net.c @@ -232,7 +232,6 @@ static const struct net_device_ops hysdn_netdev_ops = { .ndo_open = net_open, .ndo_stop = net_close, .ndo_start_xmit = net_send_packet, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index 9914f69a..0da622f 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1198,7 +1198,6 @@ static const struct net_device_ops dvb_netdev_ops = { .ndo_start_xmit = dvb_net_tx, .ndo_set_rx_mode = dvb_net_set_multicast_list, .ndo_set_mac_address = dvb_net_set_mac, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -1209,6 +1208,7 @@ static void dvb_net_setup(struct net_device *dev) dev->header_ops = &dvb_header_ops; dev->netdev_ops = &dvb_netdev_ops; dev->mtu = 4096; + dev->max_mtu = 4096; dev->flags |= IFF_NOARP; } diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index e90c6a7..31f89f1 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -59,7 +59,6 @@ static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); static const struct net_device_ops ipddp_netdev_ops = { .ndo_start_xmit = ipddp_xmit, .ndo_do_ioctl = ipddp_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index 221f5f0..e128826 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -264,7 +264,6 @@ static const struct net_device_ops e100_netdev_ops = { .ndo_do_ioctl = e100_ioctl, .ndo_set_mac_address = e100_set_mac_address, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_config = e100_set_config, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = e100_netpoll, diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 91ada52..9f9a5f4 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -508,7 +508,6 @@ static const struct net_device_ops netdev_ops = { .ndo_get_stats = el3_get_stats, .ndo_set_rx_mode = set_multicast_list, .ndo_tx_timeout = el3_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c index b26e038..b9f4c46 100644 --- a/drivers/net/ethernet/3com/3c515.c +++ b/drivers/net/ethernet/3com/3c515.c @@ -570,7 +570,6 @@ static const struct net_device_ops netdev_ops = { .ndo_tx_timeout = corkscrew_timeout, .ndo_get_stats = corkscrew_get_stats, .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c index b88afd7..9359a37 100644 --- a/drivers/net/ethernet/3com/3c574_cs.c +++ b/drivers/net/ethernet/3com/3c574_cs.c @@ -254,7 +254,6 @@ static const struct net_device_ops el3_netdev_ops = { .ndo_get_stats = el3_get_stats, .ndo_do_ioctl = el3_ioctl, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index 71396e4..e28254a 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -188,7 +188,6 @@ static const struct net_device_ops el3_netdev_ops = { .ndo_set_config = el3_config, .ndo_get_stats = el3_get_stats, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 9133e79..3ecf613 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -1062,7 +1062,6 @@ static const struct net_device_ops boomrang_netdev_ops = { .ndo_do_ioctl = vortex_ioctl, #endif .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -1080,7 +1079,6 @@ static const struct net_device_ops vortex_netdev_ops = { .ndo_do_ioctl = vortex_ioctl, #endif .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 8f8418d..506b507 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -2255,7 +2255,6 @@ static const struct net_device_ops typhoon_netdev_ops = { .ndo_get_stats = typhoon_get_stats, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, }; static int diff --git a/drivers/net/ethernet/8390/8390.c b/drivers/net/ethernet/8390/8390.c index 5db1f55..a43544a 100644 --- a/drivers/net/ethernet/8390/8390.c +++ b/drivers/net/ethernet/8390/8390.c @@ -64,7 +64,6 @@ const struct net_device_ops ei_netdev_ops = { .ndo_set_rx_mode = ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ei_poll, #endif diff --git a/drivers/net/ethernet/8390/8390p.c b/drivers/net/ethernet/8390/8390p.c index e8fc2e8..46d2257 100644 --- a/drivers/net/ethernet/8390/8390p.c +++ b/drivers/net/ethernet/8390/8390p.c @@ -69,7 +69,6 @@ const struct net_device_ops eip_netdev_ops = { .ndo_set_rx_mode = eip_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = eip_poll, #endif diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 39ca935..b0a3b85 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -536,7 +536,6 @@ static const struct net_device_ops ax_netdev_ops = { .ndo_set_rx_mode = ax_ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ax_ei_poll, #endif diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c index 4ea717d..1d84a05 100644 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ b/drivers/net/ethernet/8390/axnet_cs.c @@ -134,7 +134,6 @@ static const struct net_device_ops axnet_netdev_ops = { .ndo_tx_timeout = axnet_tx_timeout, .ndo_get_stats = get_stats, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/8390/etherh.c b/drivers/net/ethernet/8390/etherh.c index d686b9c..11cbf22 100644 --- a/drivers/net/ethernet/8390/etherh.c +++ b/drivers/net/ethernet/8390/etherh.c @@ -654,7 +654,6 @@ static const struct net_device_ops etherh_netdev_ops = { .ndo_set_rx_mode = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = __ei_poll, #endif diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c index 0fe19d6..8ae2491 100644 --- a/drivers/net/ethernet/8390/hydra.c +++ b/drivers/net/ethernet/8390/hydra.c @@ -105,7 +105,6 @@ static const struct net_device_ops hydra_netdev_ops = { .ndo_set_rx_mode = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = __ei_poll, #endif diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c index b928390..9497f18 100644 --- a/drivers/net/ethernet/8390/mac8390.c +++ b/drivers/net/ethernet/8390/mac8390.c @@ -483,7 +483,6 @@ static const struct net_device_ops mac8390_netdev_ops = { .ndo_set_rx_mode = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = __ei_poll, #endif diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c index e1c0555..4bb967b 100644 --- a/drivers/net/ethernet/8390/mcf8390.c +++ b/drivers/net/ethernet/8390/mcf8390.c @@ -308,7 +308,6 @@ static const struct net_device_ops mcf8390_netdev_ops = { .ndo_set_rx_mode = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = __ei_poll, #endif diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c index 57e9791..0735530 100644 --- a/drivers/net/ethernet/8390/ne2k-pci.c +++ b/drivers/net/ethernet/8390/ne2k-pci.c @@ -209,7 +209,6 @@ static const struct net_device_ops ne2k_netdev_ops = { .ndo_set_rx_mode = ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ei_poll, #endif diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c index 2f79d29..63079a6 100644 --- a/drivers/net/ethernet/8390/pcnet_cs.c +++ b/drivers/net/ethernet/8390/pcnet_cs.c @@ -227,7 +227,6 @@ static const struct net_device_ops pcnet_netdev_ops = { .ndo_do_ioctl = ei_ioctl, .ndo_set_rx_mode = ei_set_multicast_list, .ndo_tx_timeout = ei_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c index 139385d..364b651 100644 --- a/drivers/net/ethernet/8390/smc-ultra.c +++ b/drivers/net/ethernet/8390/smc-ultra.c @@ -195,7 +195,6 @@ static const struct net_device_ops ultra_netdev_ops = { .ndo_set_rx_mode = ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ultra_poll, #endif diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c index dd7d816..ad019cb 100644 --- a/drivers/net/ethernet/8390/wd.c +++ b/drivers/net/ethernet/8390/wd.c @@ -156,7 +156,6 @@ static const struct net_device_ops wd_netdev_ops = { .ndo_set_rx_mode = ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ei_poll, #endif diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c index 8308728..6d93956 100644 --- a/drivers/net/ethernet/8390/zorro8390.c +++ b/drivers/net/ethernet/8390/zorro8390.c @@ -284,7 +284,6 @@ static const struct net_device_ops zorro8390_netdev_ops = { .ndo_set_rx_mode = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = __ei_poll, #endif diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index 8af2c88..4a9a16e 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -634,7 +634,6 @@ static const struct net_device_ops netdev_ops = { .ndo_get_stats = get_stats, .ndo_set_rx_mode = set_rx_mode, .ndo_do_ioctl = netdev_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef VLAN_SUPPORT diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 00f9ee3..8816452 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1571,7 +1571,6 @@ static const struct net_device_ops bfin_mac_netdev_ops = { .ndo_set_rx_mode = bfin_mac_set_multicast_list, .ndo_do_ioctl = bfin_mac_ioctl, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = bfin_mac_poll_controller, #endif diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 6ffdff6..af27f9d 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -773,7 +773,6 @@ static const struct net_device_ops emac_netdev_ops = { .ndo_tx_timeout = emac_timeout, .ndo_set_rx_mode = emac_set_rx_mode, .ndo_do_ioctl = emac_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = emac_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/amd/a2065.c b/drivers/net/ethernet/amd/a2065.c index a83cd1c..ee4b94e 100644 --- a/drivers/net/ethernet/amd/a2065.c +++ b/drivers/net/ethernet/amd/a2065.c @@ -665,7 +665,6 @@ static const struct net_device_ops lance_netdev_ops = { .ndo_tx_timeout = lance_tx_timeout, .ndo_set_rx_mode = lance_set_multicast, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/amd/am79c961a.c b/drivers/net/ethernet/amd/am79c961a.c index fcdf5dd..b11e910 100644 --- a/drivers/net/ethernet/amd/am79c961a.c +++ b/drivers/net/ethernet/amd/am79c961a.c @@ -663,7 +663,6 @@ static const struct net_device_ops am79c961_netdev_ops = { .ndo_set_rx_mode = am79c961_setmulticastlist, .ndo_tx_timeout = am79c961_timeout, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = am79c961_poll_controller, diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c index 968b7bf..5fd7b15 100644 --- a/drivers/net/ethernet/amd/ariadne.c +++ b/drivers/net/ethernet/amd/ariadne.c @@ -706,7 +706,6 @@ static const struct net_device_ops ariadne_netdev_ops = { .ndo_get_stats = ariadne_get_stats, .ndo_set_rx_mode = set_multicast_list, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c index d2bc8e5..e53ccc3 100644 --- a/drivers/net/ethernet/amd/atarilance.c +++ b/drivers/net/ethernet/amd/atarilance.c @@ -460,7 +460,6 @@ static const struct net_device_ops lance_netdev_ops = { .ndo_set_mac_address = lance_set_mac_address, .ndo_tx_timeout = lance_tx_timeout, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static unsigned long __init lance_probe1( struct net_device *dev, diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index df66418..a3c90fe 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1103,7 +1103,6 @@ static const struct net_device_ops au1000_netdev_ops = { .ndo_tx_timeout = au1000_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int au1000_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/amd/declance.c b/drivers/net/ethernet/amd/declance.c index b799c7a..76e5fc7 100644 --- a/drivers/net/ethernet/amd/declance.c +++ b/drivers/net/ethernet/amd/declance.c @@ -1013,7 +1013,6 @@ static const struct net_device_ops lance_netdev_ops = { .ndo_start_xmit = lance_start_xmit, .ndo_tx_timeout = lance_tx_timeout, .ndo_set_rx_mode = lance_set_multicast, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/amd/hplance.c b/drivers/net/ethernet/amd/hplance.c index 6c9de11..c3dbf1c 100644 --- a/drivers/net/ethernet/amd/hplance.c +++ b/drivers/net/ethernet/amd/hplance.c @@ -72,7 +72,6 @@ static const struct net_device_ops hplance_netdev_ops = { .ndo_stop = hplance_close, .ndo_start_xmit = lance_start_xmit, .ndo_set_rx_mode = lance_set_multicast, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c index abb1ba2..61a641f 100644 --- a/drivers/net/ethernet/amd/lance.c +++ b/drivers/net/ethernet/amd/lance.c @@ -461,7 +461,6 @@ static const struct net_device_ops lance_netdev_ops = { .ndo_get_stats = lance_get_stats, .ndo_set_rx_mode = set_multicast_list, .ndo_tx_timeout = lance_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/amd/mvme147.c b/drivers/net/ethernet/amd/mvme147.c index 0660ac5..0a92044 100644 --- a/drivers/net/ethernet/amd/mvme147.c +++ b/drivers/net/ethernet/amd/mvme147.c @@ -62,7 +62,6 @@ static const struct net_device_ops lance_netdev_ops = { .ndo_start_xmit = lance_start_xmit, .ndo_set_rx_mode = lance_set_multicast, .ndo_tx_timeout = lance_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/amd/ni65.c b/drivers/net/ethernet/amd/ni65.c index cda53db..5985bf2 100644 --- a/drivers/net/ethernet/amd/ni65.c +++ b/drivers/net/ethernet/amd/ni65.c @@ -407,7 +407,6 @@ static const struct net_device_ops ni65_netdev_ops = { .ndo_start_xmit = ni65_send_packet, .ndo_tx_timeout = ni65_timeout, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index 2807e18..113a3b3 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -427,7 +427,6 @@ static const struct net_device_ops mace_netdev_ops = { .ndo_set_config = mace_config, .ndo_get_stats = mace_get_stats, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index c22bf52..adc7ab9 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -1527,7 +1527,6 @@ static const struct net_device_ops pcnet32_netdev_ops = { .ndo_get_stats = pcnet32_get_stats, .ndo_set_rx_mode = pcnet32_set_multicast_list, .ndo_do_ioctl = pcnet32_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/amd/sun3lance.c b/drivers/net/ethernet/amd/sun3lance.c index 3d8c6b2..12bb4f1 100644 --- a/drivers/net/ethernet/amd/sun3lance.c +++ b/drivers/net/ethernet/amd/sun3lance.c @@ -299,7 +299,6 @@ static const struct net_device_ops lance_netdev_ops = { .ndo_start_xmit = lance_start_xmit, .ndo_set_rx_mode = set_multicast_list, .ndo_set_mac_address = NULL, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c index 9b56b40..291ca51 100644 --- a/drivers/net/ethernet/amd/sunlance.c +++ b/drivers/net/ethernet/amd/sunlance.c @@ -1294,7 +1294,6 @@ static const struct net_device_ops sparc_lance_ops = { .ndo_start_xmit = lance_start_xmit, .ndo_set_rx_mode = lance_set_multicast, .ndo_tx_timeout = lance_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index f75d955..3fc7b0d 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1252,7 +1252,6 @@ static const struct net_device_ops xgene_ndev_ops = { .ndo_start_xmit = xgene_enet_start_xmit, .ndo_tx_timeout = xgene_enet_timeout, .ndo_get_stats64 = xgene_enet_get_stats64, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = xgene_enet_set_mac_address, }; diff --git a/drivers/net/ethernet/apple/bmac.c b/drivers/net/ethernet/apple/bmac.c index a65d7a6..2b2d870 100644 --- a/drivers/net/ethernet/apple/bmac.c +++ b/drivers/net/ethernet/apple/bmac.c @@ -1237,7 +1237,6 @@ static const struct net_device_ops bmac_netdev_ops = { .ndo_start_xmit = bmac_output, .ndo_set_rx_mode = bmac_set_multicast, .ndo_set_mac_address = bmac_set_address, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/apple/mace.c b/drivers/net/ethernet/apple/mace.c index e58a7c7..96dd530 100644 --- a/drivers/net/ethernet/apple/mace.c +++ b/drivers/net/ethernet/apple/mace.c @@ -102,7 +102,6 @@ static const struct net_device_ops mace_netdev_ops = { .ndo_start_xmit = mace_xmit_start, .ndo_set_rx_mode = mace_set_multicast, .ndo_set_mac_address = mace_set_address, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/apple/macmace.c b/drivers/net/ethernet/apple/macmace.c index 89914ca..857df9c 100644 --- a/drivers/net/ethernet/apple/macmace.c +++ b/drivers/net/ethernet/apple/macmace.c @@ -186,7 +186,6 @@ static const struct net_device_ops mace_netdev_ops = { .ndo_tx_timeout = mace_tx_timeout, .ndo_set_rx_mode = mace_set_multicast, .ndo_set_mac_address = mace_set_address, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c index b047fd6..453dc09 100644 --- a/drivers/net/ethernet/aurora/nb8800.c +++ b/drivers/net/ethernet/aurora/nb8800.c @@ -1032,7 +1032,6 @@ static const struct net_device_ops nb8800_netdev_ops = { .ndo_set_mac_address = nb8800_set_mac_address, .ndo_set_rx_mode = nb8800_set_rx_mode, .ndo_do_ioctl = nb8800_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index b32444a..20e7204 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -2793,7 +2793,6 @@ static const struct net_device_ops at91ether_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, .ndo_do_ioctl = macb_ioctl, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = at91ether_poll_controller, #endif diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index c363b58..3647b28 100644 --- a/drivers/net/ethernet/cirrus/cs89x0.c +++ b/drivers/net/ethernet/cirrus/cs89x0.c @@ -1266,7 +1266,6 @@ static const struct net_device_ops net_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = net_poll_controller, #endif - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c index de9f7c9..9119af0 100644 --- a/drivers/net/ethernet/cirrus/ep93xx_eth.c +++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c @@ -749,7 +749,6 @@ static const struct net_device_ops ep93xx_netdev_ops = { .ndo_start_xmit = ep93xx_xmit, .ndo_do_ioctl = ep93xx_ioctl, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/cirrus/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c index 0771967..b600fbb 100644 --- a/drivers/net/ethernet/cirrus/mac89x0.c +++ b/drivers/net/ethernet/cirrus/mac89x0.c @@ -172,7 +172,6 @@ static const struct net_device_ops mac89x0_netdev_ops = { .ndo_set_rx_mode = set_multicast_list, .ndo_set_mac_address = set_mac_address, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; /* Probe for the CS8900 card in slot E. We won't bother looking diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index f45385f..f1a81c5 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -1382,7 +1382,6 @@ static const struct net_device_ops dm9000_netdev_ops = { .ndo_tx_timeout = dm9000_timeout, .ndo_set_rx_mode = dm9000_hash_table, .ndo_do_ioctl = dm9000_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_features = dm9000_set_features, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index cadcee6..90c573b 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -1956,7 +1956,6 @@ static const struct net_device_ops de_netdev_ops = { .ndo_start_xmit = de_start_xmit, .ndo_get_stats = de_get_stats, .ndo_tx_timeout = de_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index 6620fc8..51fda3a 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -1085,7 +1085,6 @@ static const struct net_device_ops de4x5_netdev_ops = { .ndo_get_stats = de4x5_get_stats, .ndo_set_rx_mode = set_multicast_list, .ndo_do_ioctl = de4x5_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address= eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index 8ed0fd8..df49949 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -352,7 +352,6 @@ static const struct net_device_ops netdev_ops = { .ndo_stop = dmfe_stop, .ndo_start_xmit = dmfe_start_xmit, .ndo_set_rx_mode = dmfe_set_filter_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index bbde90b..5f13774 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1282,7 +1282,6 @@ static const struct net_device_ops tulip_netdev_ops = { .ndo_get_stats = tulip_get_stats, .ndo_do_ioctl = private_ioctl, .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index e750b5d..e1c4133 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -269,7 +269,6 @@ static const struct net_device_ops netdev_ops = { .ndo_stop = uli526x_stop, .ndo_start_xmit = uli526x_start_xmit, .ndo_set_rx_mode = uli526x_set_filter_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index 1f62b94..feda96d 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -353,7 +353,6 @@ static const struct net_device_ops netdev_ops = { .ndo_set_rx_mode = set_rx_mode, .ndo_do_ioctl = netdev_ioctl, .ndo_tx_timeout = tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/dec/tulip/xircom_cb.c b/drivers/net/ethernet/dec/tulip/xircom_cb.c index 0e721ce..19e4ea1 100644 --- a/drivers/net/ethernet/dec/tulip/xircom_cb.c +++ b/drivers/net/ethernet/dec/tulip/xircom_cb.c @@ -174,7 +174,6 @@ static const struct net_device_ops netdev_ops = { .ndo_open = xircom_open, .ndo_stop = xircom_close, .ndo_start_xmit = xircom_start_xmit, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index c3b64cd..2a17c59 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -767,7 +767,6 @@ static const struct net_device_ops dnet_netdev_ops = { .ndo_do_ioctl = dnet_ioctl, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int dnet_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/ec_bhf.c b/drivers/net/ethernet/ec_bhf.c index f7b4248..5765095 100644 --- a/drivers/net/ethernet/ec_bhf.c +++ b/drivers/net/ethernet/ec_bhf.c @@ -482,7 +482,6 @@ static const struct net_device_ops ec_bhf_netdev_ops = { .ndo_open = ec_bhf_open, .ndo_stop = ec_bhf_stop, .ndo_get_stats64 = ec_bhf_get_stats, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr }; diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c index c08bd76..6967b28 100644 --- a/drivers/net/ethernet/fealnx.c +++ b/drivers/net/ethernet/fealnx.c @@ -472,7 +472,6 @@ static const struct net_device_ops netdev_ops = { .ndo_set_rx_mode = set_rx_mode, .ndo_do_ioctl = mii_ioctl, .ndo_tx_timeout = fealnx_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 48a033e..4ce8179 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3055,7 +3055,6 @@ static const struct net_device_ops fec_netdev_ops = { .ndo_stop = fec_enet_close, .ndo_start_xmit = fec_enet_start_xmit, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = fec_timeout, .ndo_set_mac_address = fec_set_mac_address, diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index 446ae9d..aa8cf5d2 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -802,7 +802,6 @@ static const struct net_device_ops mpc52xx_fec_netdev_ops = { .ndo_set_mac_address = mpc52xx_fec_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = mpc52xx_fec_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_tx_timeout = mpc52xx_fec_tx_timeout, .ndo_get_stats = mpc52xx_fec_get_stats, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index dc120c1..925d1bc 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -912,7 +912,6 @@ static const struct net_device_ops fs_enet_netdev_ops = { .ndo_do_ioctl = fs_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = fs_enet_netpoll, #endif diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 186ef8f..7861824 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -3681,7 +3681,6 @@ static const struct net_device_ops ucc_geth_netdev_ops = { .ndo_start_xmit = ucc_geth_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = ucc_geth_set_mac_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_rx_mode = ucc_geth_set_multi, .ndo_tx_timeout = ucc_geth_timeout, .ndo_do_ioctl = ucc_geth_ioctl, diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c index 399cfd2..51c4abc 100644 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -225,7 +225,6 @@ static const struct net_device_ops fjn_netdev_ops = { .ndo_tx_timeout = fjn_tx_timeout, .ndo_set_config = fjn_config, .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index 3977889..3c99ca4 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -769,7 +769,6 @@ static const struct net_device_ops hip04_netdev_ops = { .ndo_set_mac_address = hip04_set_mac_address, .ndo_tx_timeout = hip04_timeout, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int hip04_alloc_ring(struct net_device *ndev, struct device *d) diff --git a/drivers/net/ethernet/hisilicon/hisi_femac.c b/drivers/net/ethernet/hisilicon/hisi_femac.c index ced1859..4986306 100644 --- a/drivers/net/ethernet/hisilicon/hisi_femac.c +++ b/drivers/net/ethernet/hisilicon/hisi_femac.c @@ -712,7 +712,6 @@ static const struct net_device_ops hisi_femac_netdev_ops = { .ndo_do_ioctl = hisi_femac_net_ioctl, .ndo_set_mac_address = hisi_femac_set_mac_address, .ndo_set_rx_mode = hisi_femac_net_set_rx_mode, - .ndo_change_mtu = eth_change_mtu, }; static void hisi_femac_core_reset(struct hisi_femac_priv *priv) diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c index 631dbc7..1a31bee 100644 --- a/drivers/net/ethernet/hp/hp100.c +++ b/drivers/net/ethernet/hp/hp100.c @@ -427,7 +427,6 @@ static const struct net_device_ops hp100_bm_netdev_ops = { .ndo_start_xmit = hp100_start_xmit_bm, .ndo_get_stats = hp100_get_stats, .ndo_set_rx_mode = hp100_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -438,7 +437,6 @@ static const struct net_device_ops hp100_netdev_ops = { .ndo_start_xmit = hp100_start_xmit, .ndo_get_stats = hp100_get_stats, .ndo_set_rx_mode = hp100_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/i825xx/82596.c b/drivers/net/ethernet/i825xx/82596.c index ce235b7..9458838 100644 --- a/drivers/net/ethernet/i825xx/82596.c +++ b/drivers/net/ethernet/i825xx/82596.c @@ -1118,7 +1118,6 @@ static const struct net_device_ops i596_netdev_ops = { .ndo_start_xmit = i596_start_xmit, .ndo_set_rx_mode = set_multicast_list, .ndo_tx_timeout = i596_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/i825xx/ether1.c b/drivers/net/ethernet/i825xx/ether1.c index 5d353c6..dc98345 100644 --- a/drivers/net/ethernet/i825xx/ether1.c +++ b/drivers/net/ethernet/i825xx/ether1.c @@ -981,7 +981,6 @@ static const struct net_device_ops ether1_netdev_ops = { .ndo_set_rx_mode = ether1_setmulticastlist, .ndo_tx_timeout = ether1_timeout, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c index 3dbc53c2..e867733 100644 --- a/drivers/net/ethernet/i825xx/lib82596.c +++ b/drivers/net/ethernet/i825xx/lib82596.c @@ -1037,7 +1037,6 @@ static const struct net_device_ops i596_netdev_ops = { .ndo_start_xmit = i596_start_xmit, .ndo_set_rx_mode = set_multicast_list, .ndo_tx_timeout = i596_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/i825xx/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c index 21c84cc..8bb15a8 100644 --- a/drivers/net/ethernet/i825xx/sun3_82586.c +++ b/drivers/net/ethernet/i825xx/sun3_82586.c @@ -337,7 +337,6 @@ static const struct net_device_ops sun3_82586_netdev_ops = { .ndo_get_stats = sun3_82586_get_stats, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, }; static int __init sun3_82586_probe1(struct net_device *dev,int ioaddr) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 8f13919..5d804a54 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2718,7 +2718,6 @@ static const struct net_device_ops emac_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = emac_set_mac_address, .ndo_start_xmit = emac_start_xmit, - .ndo_change_mtu = eth_change_mtu, }; static const struct net_device_ops emac_gige_netdev_ops = { diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index 1799fe1..cbeea91 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -1085,7 +1085,6 @@ static const struct net_device_ops korina_netdev_ops = { .ndo_set_rx_mode = korina_multicast_list, .ndo_tx_timeout = korina_tx_timeout, .ndo_do_ioctl = korina_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 91e09d6..1a739d7 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -519,18 +519,16 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) static int ltq_etop_change_mtu(struct net_device *dev, int new_mtu) { - int ret = eth_change_mtu(dev, new_mtu); + struct ltq_etop_priv *priv = netdev_priv(dev); + unsigned long flags; - if (!ret) { - struct ltq_etop_priv *priv = netdev_priv(dev); - unsigned long flags; + dev->mtu = new_mtu; - spin_lock_irqsave(&priv->lock, flags); - ltq_etop_w32((ETOP_PLEN_UNDER << 16) | new_mtu, - LTQ_ETOP_IGPLEN); - spin_unlock_irqrestore(&priv->lock, flags); - } - return ret; + spin_lock_irqsave(&priv->lock, flags); + ltq_etop_w32((ETOP_PLEN_UNDER << 16) | new_mtu, LTQ_ETOP_IGPLEN); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; } static int diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 4a62ffd..8f80e61 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -2243,7 +2243,6 @@ static const struct net_device_ops mtk_netdev_ops = { .ndo_set_mac_address = mtk_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = mtk_do_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_tx_timeout = mtk_tx_timeout, .ndo_get_stats64 = mtk_get_stats64, .ndo_fix_features = mtk_fix_features, diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 1edc973..e7e1aff 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -1063,7 +1063,6 @@ static const struct net_device_ops ks8851_netdev_ops = { .ndo_start_xmit = ks8851_start_xmit, .ndo_set_mac_address = ks8851_set_mac_address, .ndo_set_rx_mode = ks8851_set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index 2fc5cd5..db62807 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -1285,7 +1285,6 @@ static const struct net_device_ops ks_netdev_ops = { .ndo_start_xmit = ks_start_xmit, .ndo_set_mac_address = ks_set_mac_address, .ndo_set_rx_mode = ks_set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c index 0a26b11..045b910 100644 --- a/drivers/net/ethernet/microchip/enc28j60.c +++ b/drivers/net/ethernet/microchip/enc28j60.c @@ -1544,7 +1544,6 @@ static const struct net_device_ops enc28j60_netdev_ops = { .ndo_set_rx_mode = enc28j60_set_multicast_list, .ndo_set_mac_address = enc28j60_set_mac_address, .ndo_tx_timeout = enc28j60_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 4367dd6..9774b50 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -444,7 +444,6 @@ static struct net_device_ops moxart_netdev_ops = { .ndo_set_rx_mode = moxart_mac_set_rx_mode, .ndo_set_mac_address = moxart_set_mac_address, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int moxart_mac_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/natsemi/jazzsonic.c b/drivers/net/ethernet/natsemi/jazzsonic.c index acf3f11..a6caeb5 100644 --- a/drivers/net/ethernet/natsemi/jazzsonic.c +++ b/drivers/net/ethernet/natsemi/jazzsonic.c @@ -110,7 +110,6 @@ static const struct net_device_ops sonic_netdev_ops = { .ndo_get_stats = sonic_get_stats, .ndo_set_rx_mode = sonic_multicast_list, .ndo_tx_timeout = sonic_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/natsemi/macsonic.c b/drivers/net/ethernet/natsemi/macsonic.c index d98f5b8..3ca6ae7 100644 --- a/drivers/net/ethernet/natsemi/macsonic.c +++ b/drivers/net/ethernet/natsemi/macsonic.c @@ -190,7 +190,6 @@ static const struct net_device_ops macsonic_netdev_ops = { .ndo_tx_timeout = sonic_tx_timeout, .ndo_get_stats = sonic_get_stats, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/natsemi/xtsonic.c b/drivers/net/ethernet/natsemi/xtsonic.c index 7007d21..9ee0f69 100644 --- a/drivers/net/ethernet/natsemi/xtsonic.c +++ b/drivers/net/ethernet/natsemi/xtsonic.c @@ -124,7 +124,6 @@ static const struct net_device_ops xtsonic_netdev_ops = { .ndo_set_rx_mode = sonic_multicast_list, .ndo_tx_timeout = sonic_tx_timeout, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/netx-eth.c b/drivers/net/ethernet/netx-eth.c index adbc47f..df4188cb 100644 --- a/drivers/net/ethernet/netx-eth.c +++ b/drivers/net/ethernet/netx-eth.c @@ -304,7 +304,6 @@ static const struct net_device_ops netx_eth_netdev_ops = { .ndo_start_xmit = netx_eth_hard_start_xmit, .ndo_tx_timeout = netx_eth_timeout, .ndo_set_rx_mode = netx_eth_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c index 712d8bc..119f6dc 100644 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c @@ -915,7 +915,6 @@ static const struct net_device_ops w90p910_ether_netdev_ops = { .ndo_set_mac_address = w90p910_set_mac_address, .ndo_do_ioctl = w90p910_ether_ioctl, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static void __init get_mac_address(struct net_device *dev) diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 8e13ec8..dd6b0d0 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -1256,7 +1256,6 @@ static const struct net_device_ops lpc_netdev_ops = { .ndo_do_ioctl = lpc_eth_ioctl, .ndo_set_mac_address = lpc_set_mac_address, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int lpc_eth_drv_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c index 91be2f0..2d04679 100644 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ b/drivers/net/ethernet/packetengines/hamachi.c @@ -568,7 +568,6 @@ static const struct net_device_ops hamachi_netdev_ops = { .ndo_start_xmit = hamachi_start_xmit, .ndo_get_stats = hamachi_get_stats, .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_tx_timeout = hamachi_tx_timeout, diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c index fb1d103..2a2ca5f 100644 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ b/drivers/net/ethernet/packetengines/yellowfin.c @@ -360,7 +360,6 @@ static const struct net_device_ops netdev_ops = { .ndo_stop = yellowfin_close, .ndo_start_xmit = yellowfin_start_xmit, .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_do_ioctl = netdev_ioctl, diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index b09a6b8..5c100ab 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -3755,7 +3755,6 @@ static const struct net_device_ops ql3xxx_netdev_ops = { .ndo_open = ql3xxx_open, .ndo_start_xmit = ql3xxx_send, .ndo_stop = ql3xxx_close, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = ql3xxx_set_mac_address, .ndo_tx_timeout = ql3xxx_tx_timeout, diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index 5ef5d72..4ff4e04 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -969,7 +969,6 @@ static const struct net_device_ops r6040_netdev_ops = { .ndo_start_xmit = r6040_start_xmit, .ndo_get_stats = r6040_get_stats, .ndo_set_rx_mode = r6040_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_do_ioctl = r6040_ioctl, diff --git a/drivers/net/ethernet/realtek/atp.c b/drivers/net/ethernet/realtek/atp.c index 5cb9678..570ed3b 100644 --- a/drivers/net/ethernet/realtek/atp.c +++ b/drivers/net/ethernet/realtek/atp.c @@ -245,7 +245,6 @@ static const struct net_device_ops atp_netdev_ops = { .ndo_start_xmit = atp_send_packet, .ndo_set_rx_mode = set_rx_mode, .ndo_tx_timeout = tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 630536b..27cfec3 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1780,7 +1780,6 @@ static const struct net_device_ops ravb_netdev_ops = { .ndo_do_ioctl = ravb_do_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, }; /* MDIO bus init function */ diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 05b0dc5..e443695 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2914,7 +2914,6 @@ static const struct net_device_ops sh_eth_netdev_ops = { .ndo_do_ioctl = sh_eth_do_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, }; static const struct net_device_ops sh_eth_netdev_ops_tsu = { @@ -2929,7 +2928,6 @@ static const struct net_device_ops sh_eth_netdev_ops_tsu = { .ndo_do_ioctl = sh_eth_do_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = eth_change_mtu, }; #ifdef CONFIG_OF diff --git a/drivers/net/ethernet/seeq/ether3.c b/drivers/net/ethernet/seeq/ether3.c index bdac936..244c1e1 100644 --- a/drivers/net/ethernet/seeq/ether3.c +++ b/drivers/net/ethernet/seeq/ether3.c @@ -745,7 +745,6 @@ static const struct net_device_ops ether3_netdev_ops = { .ndo_set_rx_mode = ether3_setmulticastlist, .ndo_tx_timeout = ether3_timeout, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/seeq/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c index c2bd537..ed34196 100644 --- a/drivers/net/ethernet/seeq/sgiseeq.c +++ b/drivers/net/ethernet/seeq/sgiseeq.c @@ -714,7 +714,6 @@ static const struct net_device_ops sgiseeq_netdev_ops = { .ndo_tx_timeout = timeout, .ndo_set_rx_mode = sgiseeq_set_multicast, .ndo_set_mac_address = sgiseeq_set_mac_address, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index 7a254da..42051ab 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -1225,7 +1225,6 @@ static const struct net_device_ops ioc3_netdev_ops = { .ndo_do_ioctl = ioc3_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = ioc3_set_mac_address, - .ndo_change_mtu = eth_change_mtu, }; static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/net/ethernet/sgi/meth.c b/drivers/net/ethernet/sgi/meth.c index aaa80f1..69d2d30 100644 --- a/drivers/net/ethernet/sgi/meth.c +++ b/drivers/net/ethernet/sgi/meth.c @@ -815,7 +815,6 @@ static const struct net_device_ops meth_netdev_ops = { .ndo_start_xmit = meth_tx, .ndo_do_ioctl = meth_ioctl, .ndo_tx_timeout = meth_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_set_rx_mode = meth_set_rx_mode, diff --git a/drivers/net/ethernet/silan/sc92031.c b/drivers/net/ethernet/silan/sc92031.c index 7426f8b..6c2e2b3 100644 --- a/drivers/net/ethernet/silan/sc92031.c +++ b/drivers/net/ethernet/silan/sc92031.c @@ -1386,7 +1386,6 @@ static const struct net_device_ops sc92031_netdev_ops = { .ndo_open = sc92031_open, .ndo_stop = sc92031_stop, .ndo_set_rx_mode = sc92031_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_tx_timeout = sc92031_tx_timeout, diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index 27be6c8..210e35d 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -1833,7 +1833,6 @@ static const struct net_device_ops sis190_netdev_ops = { .ndo_start_xmit = sis190_start_xmit, .ndo_tx_timeout = sis190_tx_timeout, .ndo_set_rx_mode = sis190_set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = sis190_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c index 6f85276..39fca6c 100644 --- a/drivers/net/ethernet/sis/sis900.c +++ b/drivers/net/ethernet/sis/sis900.c @@ -400,7 +400,6 @@ static const struct net_device_ops sis900_netdev_ops = { .ndo_start_xmit = sis900_start_xmit, .ndo_set_config = sis900_set_config, .ndo_set_rx_mode = set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_do_ioctl = mii_ioctl, diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c index 7186b89..fe9760f 100644 --- a/drivers/net/ethernet/smsc/epic100.c +++ b/drivers/net/ethernet/smsc/epic100.c @@ -313,7 +313,6 @@ static const struct net_device_ops epic_netdev_ops = { .ndo_get_stats = epic_get_stats, .ndo_set_rx_mode = set_rx_mode, .ndo_do_ioctl = netdev_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index cb49c96..4f19c61 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -1753,7 +1753,6 @@ static const struct net_device_ops smc911x_netdev_ops = { .ndo_start_xmit = smc911x_hard_start_xmit, .ndo_tx_timeout = smc911x_timeout, .ndo_set_rx_mode = smc911x_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c index d496888..c8d8467 100644 --- a/drivers/net/ethernet/smsc/smc9194.c +++ b/drivers/net/ethernet/smsc/smc9194.c @@ -809,7 +809,6 @@ static const struct net_device_ops smc_netdev_ops = { .ndo_start_xmit = smc_wait_to_send_packet, .ndo_tx_timeout = smc_timeout, .ndo_set_rx_mode = smc_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index db3c696..f1c75e2 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -294,7 +294,6 @@ static const struct net_device_ops smc_netdev_ops = { .ndo_set_config = s9k_config, .ndo_set_rx_mode = set_rx_mode, .ndo_do_ioctl = smc_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 7321259..9b4780f 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -1762,7 +1762,6 @@ static const struct net_device_ops smc_netdev_ops = { .ndo_start_xmit = smc_hard_start_xmit, .ndo_tx_timeout = smc_timeout, .ndo_set_rx_mode = smc_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index e9b8579..cdb343f 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -2152,7 +2152,6 @@ static const struct net_device_ops smsc911x_netdev_ops = { .ndo_get_stats = smsc911x_get_stats, .ndo_set_rx_mode = smsc911x_set_multicast_list, .ndo_do_ioctl = smsc911x_do_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = smsc911x_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c index aa4f9d2..ea89ef3 100644 --- a/drivers/net/ethernet/sun/sunbmac.c +++ b/drivers/net/ethernet/sun/sunbmac.c @@ -1064,7 +1064,6 @@ static const struct net_device_ops bigmac_ops = { .ndo_get_stats = bigmac_get_stats, .ndo_set_rx_mode = bigmac_set_multicast, .ndo_tx_timeout = bigmac_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index cf4dcff..ca96408 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -2669,7 +2669,6 @@ static const struct net_device_ops hme_netdev_ops = { .ndo_tx_timeout = happy_meal_tx_timeout, .ndo_get_stats = happy_meal_get_stats, .ndo_set_rx_mode = happy_meal_set_multicast, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c index 9b825780..c5ef711 100644 --- a/drivers/net/ethernet/sun/sunqe.c +++ b/drivers/net/ethernet/sun/sunqe.c @@ -823,7 +823,6 @@ static const struct net_device_ops qec_ops = { .ndo_start_xmit = qe_start_xmit, .ndo_set_rx_mode = qe_set_multicast, .ndo_tx_timeout = qe_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index fa0cfda..c56e703 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -1068,7 +1068,6 @@ static const struct net_device_ops cpmac_netdev_ops = { .ndo_tx_timeout = cpmac_tx_timeout, .ndo_set_rx_mode = cpmac_set_multicast_list, .ndo_do_ioctl = cpmac_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 6d4b8a2..b1ddf89 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1883,7 +1883,6 @@ static const struct net_device_ops cpsw_netdev_ops = { .ndo_set_mac_address = cpsw_ndo_set_mac_address, .ndo_do_ioctl = cpsw_ndo_ioctl, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_tx_timeout = cpsw_ndo_tx_timeout, .ndo_set_rx_mode = cpsw_ndo_set_rx_mode, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index ece0ea0..4a3eeb1 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -772,7 +772,6 @@ static const struct net_device_ops tlan_netdev_ops = { .ndo_get_stats = tlan_get_stats, .ndo_set_rx_mode = tlan_set_multicast_list, .ndo_do_ioctl = tlan_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 5b01b3f..3be61ed 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -747,7 +747,6 @@ static const struct net_device_ops tc35815_netdev_ops = { .ndo_tx_timeout = tc35815_tx_timeout, .ndo_do_ioctl = tc35815_ioctl, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = tc35815_poll_controller, diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c index 8fd1312..f153ad7 100644 --- a/drivers/net/ethernet/tundra/tsi108_eth.c +++ b/drivers/net/ethernet/tundra/tsi108_eth.c @@ -1548,7 +1548,6 @@ static const struct net_device_ops tsi108_netdev_ops = { .ndo_do_ioctl = tsi108_do_ioctl, .ndo_set_mac_address = tsi108_set_mac, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index 9d14731..ba5c542 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -890,7 +890,6 @@ static const struct net_device_ops rhine_netdev_ops = { .ndo_start_xmit = rhine_start_tx, .ndo_get_stats64 = rhine_get_stats64, .ndo_set_rx_mode = rhine_set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_do_ioctl = netdev_ioctl, diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index d2349a1..e1296ef 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -1045,7 +1045,6 @@ static const struct net_device_ops w5100_netdev_ops = { .ndo_set_rx_mode = w5100_set_rx_mode, .ndo_set_mac_address = w5100_set_macaddr, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int w5100_mmio_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index ca31a57..724fabd 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -536,7 +536,6 @@ static const struct net_device_ops w5300_netdev_ops = { .ndo_set_rx_mode = w5300_set_rx_mode, .ndo_set_mac_address = w5300_set_macaddr, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static int w5300_hw_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index ddced28..3b08ec7 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -466,7 +466,6 @@ static const struct net_device_ops netdev_ops = { .ndo_set_config = do_config, .ndo_do_ioctl = do_ioctl, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 7f127dc..46cc33b 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1379,7 +1379,6 @@ static const struct net_device_ops ixp4xx_netdev_ops = { .ndo_start_xmit = eth_xmit, .ndo_set_rx_mode = eth_set_mcast_list, .ndo_do_ioctl = eth_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index 9c4b41a..3c55ea3 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -270,7 +270,6 @@ static const struct net_device_ops plip_netdev_ops = { .ndo_stop = plip_close, .ndo_start_xmit = plip_tx_packet, .ndo_do_ioctl = plip_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index aad0b59..8b8b532 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -141,7 +141,6 @@ static const struct net_device_ops sb1000_netdev_ops = { .ndo_start_xmit = sb1000_start_xmit, .ndo_do_ioctl = sb1000_dev_ioctl, .ndo_stop = sb1000_close, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index d9ca05d..a1f2f6f 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -761,7 +761,6 @@ static const struct net_device_ops catc_netdev_ops = { .ndo_tx_timeout = catc_tx_timeout, .ndo_set_rx_mode = catc_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index 66b34dd..338aed5 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -982,7 +982,6 @@ static const struct net_device_ops kaweth_netdev_ops = { .ndo_tx_timeout = kaweth_tx_timeout, .ndo_set_rx_mode = kaweth_set_rx_mode, .ndo_get_stats = kaweth_netdev_stats, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 1434e5d..399f7ee 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1273,7 +1273,6 @@ static const struct net_device_ops pegasus_netdev_ops = { .ndo_set_rx_mode = pegasus_set_multicast, .ndo_get_stats = pegasus_netdev_stats, .ndo_tx_timeout = pegasus_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 44d439f..2886946 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -4113,7 +4113,8 @@ static int rtl8152_change_mtu(struct net_device *dev, int new_mtu) switch (tp->version) { case RTL_VER_01: case RTL_VER_02: - return eth_change_mtu(dev, new_mtu); + dev->mtu = new_mtu; + return 0; default: break; } diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 7c72bfa..93a1bda 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -847,7 +847,6 @@ static const struct net_device_ops rtl8150_netdev_ops = { .ndo_set_rx_mode = rtl8150_set_multicast, .ndo_set_mac_address = rtl8150_set_mac_address, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 3a421ca..3f83be9 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -211,7 +211,6 @@ static const struct net_device_ops sbni_netdev_ops = { .ndo_start_xmit = sbni_start_xmit, .ndo_set_rx_mode = set_multicast_list, .ndo_do_ioctl = sbni_ioctl, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/intersil/prism54/islpci_dev.c b/drivers/net/wireless/intersil/prism54/islpci_dev.c index 84a4201..325176d 100644 --- a/drivers/net/wireless/intersil/prism54/islpci_dev.c +++ b/drivers/net/wireless/intersil/prism54/islpci_dev.c @@ -808,7 +808,6 @@ static const struct net_device_ops islpci_netdev_ops = { .ndo_start_xmit = islpci_eth_transmit, .ndo_tx_timeout = islpci_eth_tx_timeout, .ndo_set_mac_address = prism54_set_mac_address, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 431f13b..e95b79b 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2791,7 +2791,6 @@ static void mac80211_hwsim_free(void) static const struct net_device_ops hwsim_netdev_ops = { .ndo_start_xmit = hwsim_mon_xmit, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c index 8541cbe..e350020 100644 --- a/drivers/net/wireless/marvell/libertas/main.c +++ b/drivers/net/wireless/marvell/libertas/main.c @@ -945,7 +945,6 @@ static const struct net_device_ops lbs_netdev_ops = { .ndo_start_xmit = lbs_hard_start_xmit, .ndo_set_mac_address = lbs_set_mac_address, .ndo_set_rx_mode = lbs_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 0881ba8..4fdc722 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -272,7 +272,6 @@ static const struct net_device_ops ray_netdev_ops = { .ndo_set_config = ray_dev_config, .ndo_get_stats = ray_get_stats, .ndo_set_rx_mode = set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 932f3f81..d9d29ab 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1853,7 +1853,6 @@ static const struct net_device_ops wl3501_netdev_ops = { .ndo_stop = wl3501_close, .ndo_start_xmit = wl3501_hard_start_xmit, .ndo_tx_timeout = wl3501_tx_timeout, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/wireless/zydas/zd1201.c b/drivers/net/wireless/zydas/zd1201.c index dea049b..de7ff39 100644 --- a/drivers/net/wireless/zydas/zd1201.c +++ b/drivers/net/wireless/zydas/zd1201.c @@ -1724,7 +1724,6 @@ static const struct net_device_ops zd1201_netdev_ops = { .ndo_tx_timeout = zd1201_tx_timeout, .ndo_set_rx_mode = zd1201_set_multicast, .ndo_set_mac_address = zd1201_set_mac_address, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/staging/rtl8188eu/os_dep/mon.c b/drivers/staging/rtl8188eu/os_dep/mon.c index d976e5e..c9c9821 100644 --- a/drivers/staging/rtl8188eu/os_dep/mon.c +++ b/drivers/staging/rtl8188eu/os_dep/mon.c @@ -145,7 +145,6 @@ static netdev_tx_t mon_xmit(struct sk_buff *skb, struct net_device *dev) static const struct net_device_ops mon_netdev_ops = { .ndo_start_xmit = mon_xmit, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index 4c30eea..5f53fbd 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -2545,7 +2545,6 @@ static const struct net_device_ops rtl8192_netdev_ops = { .ndo_set_rx_mode = _rtl92e_set_multicast, .ndo_set_mac_address = _rtl92e_set_mac_adr, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_start_xmit = rtllib_xmit, }; diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c index 457eeb5..fdb03dc 100644 --- a/drivers/staging/rtl8192u/r8192U_core.c +++ b/drivers/staging/rtl8192u/r8192U_core.c @@ -4930,7 +4930,6 @@ static const struct net_device_ops rtl8192_netdev_ops = { .ndo_set_rx_mode = r8192_set_multicast, .ndo_set_mac_address = r8192_set_mac_adr, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_start_xmit = ieee80211_xmit, }; diff --git a/drivers/staging/slicoss/slicoss.c b/drivers/staging/slicoss/slicoss.c index 062307a..2802b90 100644 --- a/drivers/staging/slicoss/slicoss.c +++ b/drivers/staging/slicoss/slicoss.c @@ -2880,7 +2880,6 @@ static const struct net_device_ops slic_netdev_ops = { .ndo_get_stats = slic_get_stats, .ndo_set_rx_mode = slic_mcast_set_list, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, }; static u32 slic_card_locate(struct adapter *adapter) diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 117d02e..864d6f2 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -35,6 +35,8 @@ #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ #define ETH_FCS_LEN 4 /* Octets in the FCS */ +#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ + /* * These are the defined Ethernet Protocol ID's. */ diff --git a/net/atm/br2684.c b/net/atm/br2684.c index aa0047c..c7d82f4 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -620,14 +620,12 @@ error: static const struct net_device_ops br2684_netdev_ops = { .ndo_start_xmit = br2684_start_xmit, .ndo_set_mac_address = br2684_mac_addr, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; static const struct net_device_ops br2684_netdev_ops_routed = { .ndo_start_xmit = br2684_start_xmit, .ndo_set_mac_address = br2684_mac_addr, - .ndo_change_mtu = eth_change_mtu }; static void br2684_setup(struct net_device *netdev) diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index f4fcb4a..0f25ddc 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -211,7 +211,6 @@ static const struct net_device_ops bnep_netdev_ops = { .ndo_set_rx_mode = bnep_net_set_mc_list, .ndo_set_mac_address = bnep_net_set_mac_addr, .ndo_tx_timeout = bnep_net_timeout, - .ndo_change_mtu = eth_change_mtu, }; diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 66dff5e..f983c10 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -322,8 +322,7 @@ EXPORT_SYMBOL(eth_mac_addr); */ int eth_change_mtu(struct net_device *dev, int new_mtu) { - if (new_mtu < 68 || new_mtu > ETH_DATA_LEN) - return -EINVAL; + netdev_warn(dev, "%s is deprecated\n", __func__); dev->mtu = new_mtu; return 0; } @@ -357,6 +356,8 @@ void ether_setup(struct net_device *dev) dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; dev->mtu = ETH_DATA_LEN; + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = ETH_DATA_LEN; dev->addr_len = ETH_ALEN; dev->tx_queue_len = 1000; /* Ethernet wants good queues */ dev->flags = IFF_BROADCAST|IFF_MULTICAST; diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index d8b7267..8192eae 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -51,7 +51,6 @@ static const struct net_device_ops irlan_eth_netdev_ops = { .ndo_stop = irlan_eth_close, .ndo_start_xmit = irlan_eth_xmit, .ndo_set_rx_mode = irlan_eth_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; -- cgit v1.1 From cc6ac9bccf6b9814d37932e86a92f8e6a92960dc Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 8 Oct 2016 11:36:05 +0800 Subject: sctp: reuse sent_count to avoid retransmitted chunks for RTT measurements Now sctp uses chunk->resent to record if a chunk is retransmitted, for RTT measurements with retransmitted DATA chunks. chunk->sent_count was introduced to record how many times one chunk has been sent for prsctp RTX policy before. We actually can know if one chunk is retransmitted by checking chunk->sent_count is greater than 1. This patch is to remove resent from sctp_chunk and reuse sent_count to avoid retransmitted chunks for RTT measurements. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 +- net/sctp/output.c | 3 ++- net/sctp/outqueue.c | 4 +--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 11c3bf2..27a933e 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -641,7 +641,6 @@ struct sctp_chunk { #define SCTP_NEED_FRTX 0x1 #define SCTP_DONT_FRTX 0x2 __u16 rtt_in_progress:1, /* This chunk used for RTT calc? */ - resent:1, /* Has this chunk ever been resent. */ has_tsn:1, /* Does this chunk have a TSN yet? */ has_ssn:1, /* Does this chunk have a SSN yet? */ singleton:1, /* Only chunk in the packet? */ @@ -656,6 +655,7 @@ struct sctp_chunk { fast_retransmit:2; /* Is this chunk fast retransmitted? */ }; +#define sctp_chunk_retransmitted(chunk) (chunk->sent_count > 1) void sctp_chunk_hold(struct sctp_chunk *); void sctp_chunk_put(struct sctp_chunk *); int sctp_user_addto_chunk(struct sctp_chunk *chunk, int len, diff --git a/net/sctp/output.c b/net/sctp/output.c index 2a5c189..84f66d5 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -552,7 +552,8 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) * for a given destination transport address. */ - if (!chunk->resent && !tp->rto_pending) { + if (!sctp_chunk_retransmitted(chunk) && + !tp->rto_pending) { chunk->rtt_in_progress = 1; tp->rto_pending = 1; } diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 5825853..e540826 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -507,8 +507,6 @@ void sctp_retransmit_mark(struct sctp_outq *q, transport->rto_pending = 0; } - chunk->resent = 1; - /* Move the chunk to the retransmit queue. The chunks * on the retransmit queue are always kept in order. */ @@ -1439,7 +1437,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, * instance). */ if (!tchunk->tsn_gap_acked && - !tchunk->resent && + !sctp_chunk_retransmitted(tchunk) && tchunk->rtt_in_progress) { tchunk->rtt_in_progress = 0; rtt = jiffies - tchunk->sent_at; -- cgit v1.1 From 8ae808eb853e3789b81b8a502cdf22bb01b76880 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 8 Oct 2016 11:40:16 +0800 Subject: sctp: remove the old ttl expires policy The prsctp polices include ttl expires policy already, we should remove the old ttl expires codes, and just adjust the new polices' codes to be compatible with the old one for users. This patch is to remove all the old expires codes, and if prsctp polices are not set, it will still set msg's expires_at and check the expires in sctp_check_abandoned. Note that asoc->prsctp_enable is set by default, so users can't feel any difference even if they use the old expires api in userspace. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 - net/sctp/chunk.c | 32 ++++++++------------------------ net/sctp/output.c | 3 --- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 27a933e..bd4a3de 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -530,7 +530,6 @@ struct sctp_datamsg { /* Did the messenge fail to send? */ int send_error; u8 send_failed:1, - can_abandon:1, /* can chunks from this message can be abandoned. */ can_delay; /* should this message be Nagle delayed */ }; diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 7a1cdf4..615f0dd 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -52,7 +52,6 @@ static void sctp_datamsg_init(struct sctp_datamsg *msg) atomic_set(&msg->refcnt, 1); msg->send_failed = 0; msg->send_error = 0; - msg->can_abandon = 0; msg->can_delay = 1; msg->expires_at = 0; INIT_LIST_HEAD(&msg->chunks); @@ -182,20 +181,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, /* Note: Calculate this outside of the loop, so that all fragments * have the same expiration. */ - if (sinfo->sinfo_timetolive) { - /* sinfo_timetolive is in milliseconds */ + if (asoc->peer.prsctp_capable && sinfo->sinfo_timetolive && + (SCTP_PR_TTL_ENABLED(sinfo->sinfo_flags) || + !SCTP_PR_POLICY(sinfo->sinfo_flags))) msg->expires_at = jiffies + msecs_to_jiffies(sinfo->sinfo_timetolive); - msg->can_abandon = 1; - - pr_debug("%s: msg:%p expires_at:%ld jiffies:%ld\n", __func__, - msg, msg->expires_at, jiffies); - } - - if (asoc->peer.prsctp_capable && - SCTP_PR_TTL_ENABLED(sinfo->sinfo_flags)) - msg->expires_at = - jiffies + msecs_to_jiffies(sinfo->sinfo_timetolive); /* This is the biggest possible DATA chunk that can fit into * the packet @@ -354,18 +344,8 @@ errout: /* Check whether this message has expired. */ int sctp_chunk_abandoned(struct sctp_chunk *chunk) { - if (!chunk->asoc->peer.prsctp_capable || - !SCTP_PR_POLICY(chunk->sinfo.sinfo_flags)) { - struct sctp_datamsg *msg = chunk->msg; - - if (!msg->can_abandon) - return 0; - - if (time_after(jiffies, msg->expires_at)) - return 1; - + if (!chunk->asoc->peer.prsctp_capable) return 0; - } if (SCTP_PR_TTL_ENABLED(chunk->sinfo.sinfo_flags) && time_after(jiffies, chunk->msg->expires_at)) { @@ -378,6 +358,10 @@ int sctp_chunk_abandoned(struct sctp_chunk *chunk) chunk->sent_count > chunk->sinfo.sinfo_timetolive) { chunk->asoc->abandoned_sent[SCTP_PR_INDEX(RTX)]++; return 1; + } else if (!SCTP_PR_POLICY(chunk->sinfo.sinfo_flags) && + chunk->msg->expires_at && + time_after(jiffies, chunk->msg->expires_at)) { + return 1; } /* PRIO policy is processed by sendmsg, not here */ diff --git a/net/sctp/output.c b/net/sctp/output.c index 84f66d5..4282b48 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -866,9 +866,6 @@ static void sctp_packet_append_data(struct sctp_packet *packet, rwnd = 0; asoc->peer.rwnd = rwnd; - /* Has been accepted for transmission. */ - if (!asoc->peer.prsctp_capable) - chunk->msg->can_abandon = 0; sctp_chunk_assign_tsn(chunk); sctp_chunk_assign_ssn(chunk); } -- cgit v1.1 From 7543d11682ef4ddc13d289b423c07356cc0d3672 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Tue, 4 Oct 2016 21:19:59 +0530 Subject: ath10k: cleanup calling ath10k_htt_rx_h_unchain 'ath10k_htt_rx_h_unchain' needs to be called only if the return value from 'ath10k_htt_rx_amsdu_pop' is 1('chained msdu's'), this change makes it more explicit and avoids doing a skb_peek, fetching rx descriptor pointer, checking rx msdu decap format for the case of ret = 0 (unchained msdus). Found this change during code walk through, not sure if this addresses any issue. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 0b4c156..285b235 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1463,8 +1463,7 @@ static int ath10k_unchain_msdu(struct sk_buff_head *amsdu) } static void ath10k_htt_rx_h_unchain(struct ath10k *ar, - struct sk_buff_head *amsdu, - bool chained) + struct sk_buff_head *amsdu) { struct sk_buff *first; struct htt_rx_desc *rxd; @@ -1475,9 +1474,6 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, decap = MS(__le32_to_cpu(rxd->msdu_start.common.info1), RX_MSDU_START_INFO1_DECAP_FORMAT); - if (!chained) - return; - /* FIXME: Current unchaining logic can only handle simple case of raw * msdu chaining. If decapping is other than raw the chaining may be * more complex and this isn't handled by the current code. Don't even @@ -1555,7 +1551,11 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt) num_msdus = skb_queue_len(&amsdu); ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff); - ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0); + + /* only for ret = 1 indicates chained msdus */ + if (ret > 0) + ath10k_htt_rx_h_unchain(ar, &amsdu); + ath10k_htt_rx_h_filter(ar, &amsdu, rx_status); ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status); ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status); -- cgit v1.1 From ebaa4b1620bf69f2bc43cb45ea85fbafdaec23c3 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 10 Oct 2016 19:51:18 +0530 Subject: ath10k: fix kernel panic due to race in accessing arvif list arvifs list is traversed within data_lock spin_lock in tasklet context to fill channel information from the corresponding vif. This means any access to arvifs list for add/del operations should also be protected with the same spin_lock to avoid the race. Fix this by performing list add/del on arvfis within the data_lock. This could fix kernel panic something like the below. LR is at ath10k_htt_rx_pktlog_completion_handler+0x100/0xb6c [ath10k_core] PC is at ath10k_htt_rx_pktlog_completion_handler+0x1c0/0xb6c [ath10k_core] Internal error: Oops: 17 [#1] PREEMPT SMP ARM [] (ath10k_htt_rx_pktlog_completion_handler+0x2f4/0xb6c [ath10k_core]) [] (ath10k_htt_txrx_compl_task+0x8b4/0x1188 [ath10k_core]) [] (tasklet_action+0x8c/0xec) [] (__do_softirq+0xdc/0x208) [] (irq_exit+0x84/0xe0) [] (__handle_domain_irq+0x80/0xa0) [] (gic_handle_irq+0x38/0x5c) [] (__irq_svc+0x40/0x74) (gdb) list *(ath10k_htt_rx_pktlog_completion_handler+0x1c0) 0x136c0 is in ath10k_htt_rx_h_channel (drivers/net/wireless/ath/ath10k/htt_rx.c:769) 764 struct cfg80211_chan_def def; 765 766 lockdep_assert_held(&ar->data_lock); 767 768 list_for_each_entry(arvif, &ar->arvifs, list) { 769 if (arvif->vdev_id == vdev_id && 770 ath10k_mac_vif_chan(arvif->vif, &def) == 0) 771 return def.chan; 772 } 773 Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2e5d2ca..691b7b5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4931,7 +4931,9 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, } ar->free_vdev_map &= ~(1LL << arvif->vdev_id); + spin_lock_bh(&ar->data_lock); list_add(&arvif->list, &ar->arvifs); + spin_unlock_bh(&ar->data_lock); /* It makes no sense to have firmware do keepalives. mac80211 already * takes care of this with idle connection polling. @@ -5082,7 +5084,9 @@ err_peer_delete: err_vdev_delete: ath10k_wmi_vdev_delete(ar, arvif->vdev_id); ar->free_vdev_map |= 1LL << arvif->vdev_id; + spin_lock_bh(&ar->data_lock); list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); err: if (arvif->beacon_buf) { @@ -5128,7 +5132,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, arvif->vdev_id, ret); ar->free_vdev_map |= 1LL << arvif->vdev_id; + spin_lock_bh(&ar->data_lock); list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); if (arvif->vdev_type == WMI_VDEV_TYPE_AP || arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { -- cgit v1.1 From e463139a726276cd2995ed21c809504d6470f061 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 9 Aug 2016 15:02:26 +0800 Subject: ath9k: change entropy formula for easier understanding The quality of ADC entropy is 10 bits of min-entropy for a 32-bit value, change '(((x) * 8 * 320) >> 10)' to '(((x) * 8 * 10) >> 5)' for easier understanding. Signed-off-by: Miaoqing Pan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/rng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/rng.c b/drivers/net/wireless/ath/ath9k/rng.c index d38e50f..568b1c6 100644 --- a/drivers/net/wireless/ath/ath9k/rng.c +++ b/drivers/net/wireless/ath/ath9k/rng.c @@ -22,7 +22,7 @@ #include "ar9003_phy.h" #define ATH9K_RNG_BUF_SIZE 320 -#define ATH9K_RNG_ENTROPY(x) (((x) * 8 * 320) >> 10) /* quality: 320/1024 */ +#define ATH9K_RNG_ENTROPY(x) (((x) * 8 * 10) >> 5) /* quality: 10/32 */ static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size) { -- cgit v1.1 From de1dfeefefde51e781df49c80f90f1f306fd0cf5 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Tue, 11 Oct 2016 16:12:56 -0700 Subject: bridge: add address and vlan to fdb warning messages This patch adds vlan and address to warning messages printed in the bridge fdb code for debuggability. Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- net/bridge/br_fdb.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 6b43c8c..e4a4176 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -535,9 +535,8 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, */ if (fdb->is_local) return 0; - br_warn(br, "adding interface %s with same address " - "as a received packet\n", - source ? source->dev->name : br->dev->name); + br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n", + source ? source->dev->name : br->dev->name, addr, vid); fdb_delete(br, fdb); } @@ -583,9 +582,8 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, /* attempt to update an entry for a local interface */ if (unlikely(fdb->is_local)) { if (net_ratelimit()) - br_warn(br, "received packet on %s with " - "own address as source address\n", - source->dev->name); + br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n", + source->dev->name, addr, vid); } else { /* fastpath: update of existing entry */ if (unlikely(source != fdb->dst)) { -- cgit v1.1 From baac789832ac91d477dc3c69f8d7bf84babd74cf Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Wed, 12 Oct 2016 12:38:49 +0200 Subject: s390/netiucv: get rid of one memcpy in netiucv_printuser Save a memcpy in netiucv_printuser(). Signed-off-by: Ursula Braun Reported-by: David Binderman Signed-off-by: David S. Miller --- drivers/s390/net/netiucv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index b0e8ffd..88b6e9c 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -302,8 +302,7 @@ static char *netiucv_printuser(struct iucv_connection *conn) if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) { tmp_uid[8] = '\0'; tmp_udat[16] = '\0'; - memcpy(tmp_uid, conn->userid, 8); - memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8); + memcpy(tmp_uid, netiucv_printname(conn->userid, 8), 8); memcpy(tmp_udat, conn->userdata, 16); EBCASC(tmp_udat, 16); memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16); -- cgit v1.1 From 9edebf11a4aa6349fecaedd673516fcf1999956a Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Wed, 12 Oct 2016 12:38:50 +0200 Subject: s390/netiucv: improve checking of sysfs attribute buffer High values are always wrong for netiucv's sysfs attribute "buffer". But the current code does not detect values between 2**31 and 2**32 as invalid. Choosing type "unsigned int" for variable "bs1" and making use of "kstrtouint()" improves the syntax checking for "buffer". Signed-off-by: Ursula Braun Reported-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/s390/net/netiucv.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 88b6e9c..2f0f391 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1563,21 +1563,21 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr, { struct netiucv_priv *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->conn->netdev; - char *e; - int bs1; + unsigned int bs1; + int rc; IUCV_DBF_TEXT(trace, 3, __func__); if (count >= 39) return -EINVAL; - bs1 = simple_strtoul(buf, &e, 0); + rc = kstrtouint(buf, 0, &bs1); - if (e && (!isspace(*e))) { - IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n", - *e); + if (rc == -EINVAL) { + IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %s\n", + buf); return -EINVAL; } - if (bs1 > NETIUCV_BUFSIZE_MAX) { + if ((rc == -ERANGE) || (bs1 > NETIUCV_BUFSIZE_MAX)) { IUCV_DBF_TEXT_(setup, 2, "buffer_write: buffer size %d too large\n", bs1); -- cgit v1.1 From f7e3a16c4a80add7ab965fb5d50dbe5f29ec1628 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 12 Oct 2016 12:38:51 +0200 Subject: s390/lcs: remove trailing space at end of dev_err message There is a trailing white space at the end of a dev_err message that does nothing useful - remove it. Signed-off-by: Colin Ian King Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/lcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 251db0a..211b31d 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1888,7 +1888,7 @@ lcs_stop_device(struct net_device *dev) rc = lcs_stopcard(card); if (rc) dev_err(&card->dev->dev, - " Shutting down the LCS device failed\n "); + " Shutting down the LCS device failed\n"); return rc; } -- cgit v1.1 From cf53b1da73bdf940f1523ec5a7d375d7056c759c Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 11 Oct 2016 13:04:09 -0700 Subject: Revert "net: Add driver helper functions to determine checksum offloadability" This reverts commit 6ae23ad36253a8033c5714c52b691b84456487c5. The code has been in kernel since 4.4 but there are no in tree code that uses. Unused code is broken code, remove it. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 78 -------------------------- net/core/dev.c | 136 ---------------------------------------------- 2 files changed, 214 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fbdf923..bf341b6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2653,71 +2653,6 @@ static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb, remcsum_unadjust((__sum16 *)ptr, grc->delta); } -struct skb_csum_offl_spec { - __u16 ipv4_okay:1, - ipv6_okay:1, - encap_okay:1, - ip_options_okay:1, - ext_hdrs_okay:1, - tcp_okay:1, - udp_okay:1, - sctp_okay:1, - vlan_okay:1, - no_encapped_ipv6:1, - no_not_encapped:1; -}; - -bool __skb_csum_offload_chk(struct sk_buff *skb, - const struct skb_csum_offl_spec *spec, - bool *csum_encapped, - bool csum_help); - -static inline bool skb_csum_offload_chk(struct sk_buff *skb, - const struct skb_csum_offl_spec *spec, - bool *csum_encapped, - bool csum_help) -{ - if (skb->ip_summed != CHECKSUM_PARTIAL) - return false; - - return __skb_csum_offload_chk(skb, spec, csum_encapped, csum_help); -} - -static inline bool skb_csum_offload_chk_help(struct sk_buff *skb, - const struct skb_csum_offl_spec *spec) -{ - bool csum_encapped; - - return skb_csum_offload_chk(skb, spec, &csum_encapped, true); -} - -static inline bool skb_csum_off_chk_help_cmn(struct sk_buff *skb) -{ - static const struct skb_csum_offl_spec csum_offl_spec = { - .ipv4_okay = 1, - .ip_options_okay = 1, - .ipv6_okay = 1, - .vlan_okay = 1, - .tcp_okay = 1, - .udp_okay = 1, - }; - - return skb_csum_offload_chk_help(skb, &csum_offl_spec); -} - -static inline bool skb_csum_off_chk_help_cmn_v4_only(struct sk_buff *skb) -{ - static const struct skb_csum_offl_spec csum_offl_spec = { - .ipv4_okay = 1, - .ip_options_okay = 1, - .tcp_okay = 1, - .udp_okay = 1, - .vlan_okay = 1, - }; - - return skb_csum_offload_chk_help(skb, &csum_offl_spec); -} - static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, @@ -3961,19 +3896,6 @@ static inline bool can_checksum_protocol(netdev_features_t features, } } -/* Map an ethertype into IP protocol if possible */ -static inline int eproto_to_ipproto(int eproto) -{ - switch (eproto) { - case htons(ETH_P_IP): - return IPPROTO_IP; - case htons(ETH_P_IPV6): - return IPPROTO_IPV6; - default: - return -1; - } -} - #ifdef CONFIG_BUG void netdev_rx_csum_fault(struct net_device *dev); #else diff --git a/net/core/dev.c b/net/core/dev.c index f376639..6498cc2 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -139,7 +139,6 @@ #include #include #include -#include #include #include "net-sysfs.h" @@ -2492,141 +2491,6 @@ out: } EXPORT_SYMBOL(skb_checksum_help); -/* skb_csum_offload_check - Driver helper function to determine if a device - * with limited checksum offload capabilities is able to offload the checksum - * for a given packet. - * - * Arguments: - * skb - sk_buff for the packet in question - * spec - contains the description of what device can offload - * csum_encapped - returns true if the checksum being offloaded is - * encpasulated. That is it is checksum for the transport header - * in the inner headers. - * checksum_help - when set indicates that helper function should - * call skb_checksum_help if offload checks fail - * - * Returns: - * true: Packet has passed the checksum checks and should be offloadable to - * the device (a driver may still need to check for additional - * restrictions of its device) - * false: Checksum is not offloadable. If checksum_help was set then - * skb_checksum_help was called to resolve checksum for non-GSO - * packets and when IP protocol is not SCTP - */ -bool __skb_csum_offload_chk(struct sk_buff *skb, - const struct skb_csum_offl_spec *spec, - bool *csum_encapped, - bool csum_help) -{ - struct iphdr *iph; - struct ipv6hdr *ipv6; - void *nhdr; - int protocol; - u8 ip_proto; - - if (skb->protocol == htons(ETH_P_8021Q) || - skb->protocol == htons(ETH_P_8021AD)) { - if (!spec->vlan_okay) - goto need_help; - } - - /* We check whether the checksum refers to a transport layer checksum in - * the outermost header or an encapsulated transport layer checksum that - * corresponds to the inner headers of the skb. If the checksum is for - * something else in the packet we need help. - */ - if (skb_checksum_start_offset(skb) == skb_transport_offset(skb)) { - /* Non-encapsulated checksum */ - protocol = eproto_to_ipproto(vlan_get_protocol(skb)); - nhdr = skb_network_header(skb); - *csum_encapped = false; - if (spec->no_not_encapped) - goto need_help; - } else if (skb->encapsulation && spec->encap_okay && - skb_checksum_start_offset(skb) == - skb_inner_transport_offset(skb)) { - /* Encapsulated checksum */ - *csum_encapped = true; - switch (skb->inner_protocol_type) { - case ENCAP_TYPE_ETHER: - protocol = eproto_to_ipproto(skb->inner_protocol); - break; - case ENCAP_TYPE_IPPROTO: - protocol = skb->inner_protocol; - break; - } - nhdr = skb_inner_network_header(skb); - } else { - goto need_help; - } - - switch (protocol) { - case IPPROTO_IP: - if (!spec->ipv4_okay) - goto need_help; - iph = nhdr; - ip_proto = iph->protocol; - if (iph->ihl != 5 && !spec->ip_options_okay) - goto need_help; - break; - case IPPROTO_IPV6: - if (!spec->ipv6_okay) - goto need_help; - if (spec->no_encapped_ipv6 && *csum_encapped) - goto need_help; - ipv6 = nhdr; - nhdr += sizeof(*ipv6); - ip_proto = ipv6->nexthdr; - break; - default: - goto need_help; - } - -ip_proto_again: - switch (ip_proto) { - case IPPROTO_TCP: - if (!spec->tcp_okay || - skb->csum_offset != offsetof(struct tcphdr, check)) - goto need_help; - break; - case IPPROTO_UDP: - if (!spec->udp_okay || - skb->csum_offset != offsetof(struct udphdr, check)) - goto need_help; - break; - case IPPROTO_SCTP: - if (!spec->sctp_okay || - skb->csum_offset != offsetof(struct sctphdr, checksum)) - goto cant_help; - break; - case NEXTHDR_HOP: - case NEXTHDR_ROUTING: - case NEXTHDR_DEST: { - u8 *opthdr = nhdr; - - if (protocol != IPPROTO_IPV6 || !spec->ext_hdrs_okay) - goto need_help; - - ip_proto = opthdr[0]; - nhdr += (opthdr[1] + 1) << 3; - - goto ip_proto_again; - } - default: - goto need_help; - } - - /* Passed the tests for offloading checksum */ - return true; - -need_help: - if (csum_help && !skb_shinfo(skb)->gso_size) - skb_checksum_help(skb); -cant_help: - return false; -} -EXPORT_SYMBOL(__skb_csum_offload_chk); - __be16 skb_network_protocol(struct sk_buff *skb, int *depth) { __be16 type = skb->protocol; -- cgit v1.1 From 4f58e6dceb0e44ca8f21568ed81e1df24e55964c Mon Sep 17 00:00:00 2001 From: "Allan W. Nielsen" Date: Wed, 12 Oct 2016 15:47:51 +0200 Subject: net: phy: Cleanup the Edge-Rate feature in Microsemi PHYs. Edge-Rate cleanup include the following: - Updated device tree bindings documentation for edge-rate - The edge-rate is now specified as a "slowdown", meaning that it is now being specified as positive values instead of negative (both documentation and implementation wise). - Only explicitly documented values for "vsc8531,vddmac" and "vsc8531,edge-slowdown" are accepted by the device driver. - Deleted include/dt-bindings/net/mscc-phy-vsc8531.h as it was not needed. - Read/validate devicetree settings in probe instead of init Signed-off-by: Allan W. Nielsen Signed-off-by: Raju Lakkaraju Signed-off-by: David S. Miller --- .../devicetree/bindings/net/mscc-phy-vsc8531.txt | 51 ++++---- drivers/net/phy/mscc.c | 135 ++++++++++----------- include/dt-bindings/net/mscc-phy-vsc8531.h | 21 ---- 3 files changed, 90 insertions(+), 117 deletions(-) delete mode 100644 include/dt-bindings/net/mscc-phy-vsc8531.h diff --git a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt index 99c7eb0..bdefefc6 100644 --- a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt +++ b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt @@ -6,22 +6,27 @@ Required properties: Documentation/devicetree/bindings/net/phy.txt Optional properties: -- vsc8531,vddmac : The vddmac in mV. +- vsc8531,vddmac : The vddmac in mV. Allowed values is listed + in the first row of Table 1 (below). + This property is only used in combination + with the 'edge-slowdown' property. + Default value is 3300. - vsc8531,edge-slowdown : % the edge should be slowed down relative to - the fastest possible edge time. Native sign - need not enter. + the fastest possible edge time. Edge rate sets the drive strength of the MAC - interface output signals. Changing the drive - strength will affect the edge rate of the output - signal. The goal of this setting is to help - reduce electrical emission (EMI) by being able - to reprogram drive strength and in effect slow - down the edge rate if desired. Table 1 shows the - impact to the edge rate per VDDMAC supply for each - drive strength setting. - Ref: Table:1 - Edge rate change below. - -Note: see dt-bindings/net/mscc-phy-vsc8531.h for applicable values + interface output signals. Changing the + drive strength will affect the edge rate of + the output signal. The goal of this setting + is to help reduce electrical emission (EMI) + by being able to reprogram drive strength + and in effect slow down the edge rate if + desired. + To adjust the edge-slowdown, the 'vddmac' + must be specified. Table 1 lists the + supported edge-slowdown values for a given + 'vddmac'. + Default value is 0%. + Ref: Table:1 - Edge rate change (below). Table: 1 - Edge rate change ----------------------------------------------------------------| @@ -29,23 +34,23 @@ Table: 1 - Edge rate change | | | 3300 mV 2500 mV 1800 mV 1500 mV | |---------------------------------------------------------------| -| Default Deafult Default Default | +| 0% 0% 0% 0% | | (Fastest) (recommended) (recommended) | |---------------------------------------------------------------| -| -2% -3% -5% -6% | +| 2% 3% 5% 6% | |---------------------------------------------------------------| -| -4% -6% -9% -14% | +| 4% 6% 9% 14% | |---------------------------------------------------------------| -| -7% -10% -16% -21% | +| 7% 10% 16% 21% | |(recommended) (recommended) | |---------------------------------------------------------------| -| -10% -14% -23% -29% | +| 10% 14% 23% 29% | |---------------------------------------------------------------| -| -17% -23% -35% -42% | +| 17% 23% 35% 42% | |---------------------------------------------------------------| -| -29% -37% -52% -58% | +| 29% 37% 52% 58% | |---------------------------------------------------------------| -| -53% -63% -76% -77% | +| 53% 63% 76% 77% | | (slowest) | |---------------------------------------------------------------| @@ -54,5 +59,5 @@ Example: vsc8531_0: ethernet-phy@0 { compatible = "ethernet-phy-id0007.0570"; vsc8531,vddmac = <3300>; - vsc8531,edge-slowdown = <21>; + vsc8531,edge-slowdown = <7>; }; diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index 77a6671..113616b 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -12,7 +12,6 @@ #include #include #include -#include #include enum rgmii_rx_clock_delay { @@ -69,23 +68,29 @@ enum rgmii_rx_clock_delay { #define PHY_ID_VSC8531 0x00070570 #define PHY_ID_VSC8541 0x00070770 -struct edge_rate_table { - u16 vddmac; - int slowdown[MSCC_SLOWDOWN_MAX]; -}; +#define MSCC_VDDMAC_1500 1500 +#define MSCC_VDDMAC_1800 1800 +#define MSCC_VDDMAC_2500 2500 +#define MSCC_VDDMAC_3300 3300 -struct edge_rate_table edge_table[MSCC_VDDMAC_MAX] = { - {3300, { 0, -2, -4, -7, -10, -17, -29, -53} }, - {2500, { 0, -3, -6, -10, -14, -23, -37, -63} }, - {1800, { 0, -5, -9, -16, -23, -35, -52, -76} }, - {1500, { 0, -6, -14, -21, -29, -42, -58, -77} }, +struct vsc8531_private { + int rate_magic; }; -struct vsc8531_private { - u8 edge_slowdown; +#ifdef CONFIG_OF_MDIO +struct vsc8531_edge_rate_table { u16 vddmac; + u8 slowdown[8]; }; +static const struct vsc8531_edge_rate_table edge_table[] = { + {MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} }, + {MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} }, + {MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} }, + {MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} }, +}; +#endif /* CONFIG_OF_MDIO */ + static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page) { int rc; @@ -205,29 +210,43 @@ out_unlock: mutex_unlock(&phydev->lock); } -static u8 edge_rate_magic_get(u16 vddmac, - int slowdown) +#ifdef CONFIG_OF_MDIO +static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev) { - int rc = (MSCC_SLOWDOWN_MAX - 1); - u8 vdd; u8 sd; + u16 vdd; + int rc, i, j; + struct device *dev = &phydev->mdio.dev; + struct device_node *of_node = dev->of_node; + u8 sd_array_size = ARRAY_SIZE(edge_table[0].slowdown); - for (vdd = 0; vdd < MSCC_VDDMAC_MAX; vdd++) { - if (edge_table[vdd].vddmac == vddmac) { - for (sd = 0; sd < MSCC_SLOWDOWN_MAX; sd++) { - if (edge_table[vdd].slowdown[sd] <= slowdown) { - rc = (MSCC_SLOWDOWN_MAX - sd - 1); - break; - } - } - } - } + if (!of_node) + return -ENODEV; - return rc; + rc = of_property_read_u16(of_node, "vsc8531,vddmac", &vdd); + if (rc != 0) + vdd = MSCC_VDDMAC_3300; + + rc = of_property_read_u8(of_node, "vsc8531,edge-slowdown", &sd); + if (rc != 0) + sd = 0; + + for (i = 0; i < ARRAY_SIZE(edge_table); i++) + if (edge_table[i].vddmac == vdd) + for (j = 0; j < sd_array_size; j++) + if (edge_table[i].slowdown[j] == sd) + return (sd_array_size - j - 1); + + return -EINVAL; +} +#else +static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev) +{ + return 0; } +#endif /* CONFIG_OF_MDIO */ -static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, - u8 edge_rate) +static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate) { int rc; u16 reg_val; @@ -308,45 +327,10 @@ out_unlock: return rc; } -#ifdef CONFIG_OF_MDIO -static int vsc8531_of_init(struct phy_device *phydev) -{ - int rc; - struct vsc8531_private *vsc8531 = phydev->priv; - struct device *dev = &phydev->mdio.dev; - struct device_node *of_node = dev->of_node; - - if (!of_node) - return -ENODEV; - - rc = of_property_read_u16(of_node, "vsc8531,vddmac", - &vsc8531->vddmac); - if (rc == -EINVAL) - vsc8531->vddmac = MSCC_VDDMAC_3300; - rc = of_property_read_u8(of_node, "vsc8531,edge-slowdown", - &vsc8531->edge_slowdown); - if (rc == -EINVAL) - vsc8531->edge_slowdown = 0; - - rc = 0; - return rc; -} -#else -static int vsc8531_of_init(struct phy_device *phydev) -{ - return 0; -} -#endif /* CONFIG_OF_MDIO */ - static int vsc85xx_config_init(struct phy_device *phydev) { int rc; struct vsc8531_private *vsc8531 = phydev->priv; - u8 edge_rate; - - rc = vsc8531_of_init(phydev); - if (rc) - return rc; rc = vsc85xx_default_config(phydev); if (rc) @@ -356,9 +340,7 @@ static int vsc85xx_config_init(struct phy_device *phydev) if (rc) return rc; - edge_rate = edge_rate_magic_get(vsc8531->vddmac, - -(int)vsc8531->edge_slowdown); - rc = vsc85xx_edge_rate_cntl_set(phydev, edge_rate); + rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic); if (rc) return rc; @@ -396,14 +378,21 @@ static int vsc85xx_config_intr(struct phy_device *phydev) static int vsc85xx_probe(struct phy_device *phydev) { + int rate_magic; struct vsc8531_private *vsc8531; + rate_magic = vsc85xx_edge_rate_magic_get(phydev); + if (rate_magic < 0) + return rate_magic; + vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL); if (!vsc8531) return -ENOMEM; phydev->priv = vsc8531; + vsc8531->rate_magic = rate_magic; + return 0; } @@ -424,9 +413,9 @@ static struct phy_driver vsc85xx_driver[] = { .config_intr = &vsc85xx_config_intr, .suspend = &genphy_suspend, .resume = &genphy_resume, - .probe = &vsc85xx_probe, - .set_wol = &vsc85xx_wol_set, - .get_wol = &vsc85xx_wol_get, + .probe = &vsc85xx_probe, + .set_wol = &vsc85xx_wol_set, + .get_wol = &vsc85xx_wol_get, }, { .phy_id = PHY_ID_VSC8541, @@ -443,9 +432,9 @@ static struct phy_driver vsc85xx_driver[] = { .config_intr = &vsc85xx_config_intr, .suspend = &genphy_suspend, .resume = &genphy_resume, - .probe = &vsc85xx_probe, - .set_wol = &vsc85xx_wol_set, - .get_wol = &vsc85xx_wol_get, + .probe = &vsc85xx_probe, + .set_wol = &vsc85xx_wol_set, + .get_wol = &vsc85xx_wol_get, } }; diff --git a/include/dt-bindings/net/mscc-phy-vsc8531.h b/include/dt-bindings/net/mscc-phy-vsc8531.h deleted file mode 100644 index 2383dd2..0000000 --- a/include/dt-bindings/net/mscc-phy-vsc8531.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Device Tree constants for Microsemi VSC8531 PHY - * - * Author: Nagaraju Lakkaraju - * - * License: Dual MIT/GPL - * Copyright (c) 2016 Microsemi Corporation - */ - -#ifndef _DT_BINDINGS_MSCC_VSC8531_H -#define _DT_BINDINGS_MSCC_VSC8531_H - -/* MAC interface Edge rate control VDDMAC in milli Volts */ -#define MSCC_VDDMAC_3300 3300 -#define MSCC_VDDMAC_2500 2500 -#define MSCC_VDDMAC_1800 1800 -#define MSCC_VDDMAC_1500 1500 -#define MSCC_VDDMAC_MAX 4 -#define MSCC_SLOWDOWN_MAX 8 - -#endif -- cgit v1.1 From 165779231ff9e9c4ac7baaee84eff91d589f3e22 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Thu, 13 Oct 2016 09:06:41 +0300 Subject: net/sched: act_mirred: Rename tcfm_ok_push to tcfm_mac_header_xmit and make it a bool 'tcfm_ok_push' specifies whether a mac_len sized push is needed upon egress to the target device (if action is performed at ingress). Rename it to 'tcfm_mac_header_xmit' as this is actually an attribute of the target device (and use a bool instead of int). This allows to decouple the attribute from the action to be taken. Signed-off-by: Shmulik Ladkani Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/tc_act/tc_mirred.h | 2 +- net/sched/act_mirred.c | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h index 62770ad..9543109 100644 --- a/include/net/tc_act/tc_mirred.h +++ b/include/net/tc_act/tc_mirred.h @@ -8,7 +8,7 @@ struct tcf_mirred { struct tc_action common; int tcfm_eaction; int tcfm_ifindex; - int tcfm_ok_push; + bool tcfm_mac_header_xmit; struct net_device __rcu *tcfm_dev; struct list_head tcfm_list; }; diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 667dc38..16e17a8 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -60,11 +60,12 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, { struct tc_action_net *tn = net_generic(net, mirred_net_id); struct nlattr *tb[TCA_MIRRED_MAX + 1]; + bool mac_header_xmit = false; struct tc_mirred *parm; struct tcf_mirred *m; struct net_device *dev; - int ret, ok_push = 0; bool exists = false; + int ret; if (nla == NULL) return -EINVAL; @@ -102,10 +103,10 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, case ARPHRD_IPGRE: case ARPHRD_VOID: case ARPHRD_NONE: - ok_push = 0; + mac_header_xmit = false; break; default: - ok_push = 1; + mac_header_xmit = true; break; } } else { @@ -136,7 +137,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, dev_put(rcu_dereference_protected(m->tcfm_dev, 1)); dev_hold(dev); rcu_assign_pointer(m->tcfm_dev, dev); - m->tcfm_ok_push = ok_push; + m->tcfm_mac_header_xmit = mac_header_xmit; } if (ret == ACT_P_CREATED) { @@ -181,7 +182,7 @@ static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, goto out; if (!(at & AT_EGRESS)) { - if (m->tcfm_ok_push) + if (m->tcfm_mac_header_xmit) skb_push_rcsum(skb2, skb->mac_len); } -- cgit v1.1 From dcf800344a91173802f2ad9c1c6b642f4b302df6 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Thu, 13 Oct 2016 09:06:42 +0300 Subject: net/sched: act_mirred: Refactor detection whether dev needs xmit at mac header Move detection logic that tests whether device expects skb data to point at mac_header upon xmit into a function. Signed-off-by: Shmulik Ladkani Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/act_mirred.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 16e17a8..69dcce8 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -54,6 +54,20 @@ static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { static int mirred_net_id; static struct tc_action_ops act_mirred_ops; +static bool dev_is_mac_header_xmit(const struct net_device *dev) +{ + switch (dev->type) { + case ARPHRD_TUNNEL: + case ARPHRD_TUNNEL6: + case ARPHRD_SIT: + case ARPHRD_IPGRE: + case ARPHRD_VOID: + case ARPHRD_NONE: + return false; + } + return true; +} + static int tcf_mirred_init(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_action **a, int ovr, int bind) @@ -96,19 +110,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, tcf_hash_release(*a, bind); return -ENODEV; } - switch (dev->type) { - case ARPHRD_TUNNEL: - case ARPHRD_TUNNEL6: - case ARPHRD_SIT: - case ARPHRD_IPGRE: - case ARPHRD_VOID: - case ARPHRD_NONE: - mac_header_xmit = false; - break; - default: - mac_header_xmit = true; - break; - } + mac_header_xmit = dev_is_mac_header_xmit(dev); } else { dev = NULL; } -- cgit v1.1 From 5724b8b5694794829a071c6da7dd0bc146df0756 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Thu, 13 Oct 2016 09:06:43 +0300 Subject: net/sched: tc_mirred: Rename public predicates 'is_tcf_mirred_redirect' and 'is_tcf_mirred_mirror' These accessors are used in various drivers that support tc offloading, to detect properties of a given 'tc_action'. 'is_tcf_mirred_redirect' tests that the action is TCA_EGRESS_REDIR. 'is_tcf_mirred_mirror' tests that the action is TCA_EGRESS_MIRROR. As a prep towards supporting INGRESS redir/mirror, rename these predicates to reflect their true meaning: s/is_tcf_mirred_redirect/is_tcf_mirred_egress_redirect/ s/is_tcf_mirred_mirror/is_tcf_mirred_egress_mirror/ Signed-off-by: Shmulik Ladkani Cc: Hariprasad S Cc: Jeff Kirsher Cc: Saeed Mahameed Cc: Jiri Pirko Cc: Ido Schimmel Cc: Jakub Kicinski Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 4 +++- drivers/net/ethernet/netronome/nfp/nfp_net_offload.c | 2 +- include/net/tc_act/tc_mirred.h | 4 ++-- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index 49d2deb..52af62e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -113,7 +113,7 @@ static int fill_action_fields(struct adapter *adap, } /* Re-direct to specified port in hardware. */ - if (is_tcf_mirred_redirect(a)) { + if (is_tcf_mirred_egress_redirect(a)) { struct net_device *n_dev; unsigned int i, index; bool found = false; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index a244d9a..784b0b9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8410,7 +8410,7 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, } /* Redirect to a VF or a offloaded macvlan */ - if (is_tcf_mirred_redirect(a)) { + if (is_tcf_mirred_egress_redirect(a)) { int ifindex = tcf_mirred_ifindex(a); err = handle_redirect_action(adapter, ifindex, queue, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index ce8c54d..135a95b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -404,7 +404,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, continue; } - if (is_tcf_mirred_redirect(a)) { + if (is_tcf_mirred_egress_redirect(a)) { int ifindex = tcf_mirred_ifindex(a); struct net_device *out_dev; struct mlx5e_priv *out_priv; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 1ec0a4c..43a5edd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1237,8 +1237,10 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, tcf_exts_to_list(cls->exts, &actions); list_for_each_entry(a, &actions, list) { - if (!is_tcf_mirred_mirror(a) || protocol != htons(ETH_P_ALL)) + if (!is_tcf_mirred_egress_mirror(a) || + protocol != htons(ETH_P_ALL)) { return -ENOTSUPP; + } err = mlxsw_sp_port_add_cls_matchall_mirror(mlxsw_sp_port, cls, a, ingress); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c index 8acfb63..cfed40c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c @@ -128,7 +128,7 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) if (is_tcf_gact_shot(a)) return NN_ACT_TC_DROP; - if (is_tcf_mirred_redirect(a) && + if (is_tcf_mirred_egress_redirect(a) && tcf_mirred_ifindex(a) == nn->netdev->ifindex) return NN_ACT_TC_REDIR; } diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h index 9543109..604bc31 100644 --- a/include/net/tc_act/tc_mirred.h +++ b/include/net/tc_act/tc_mirred.h @@ -14,7 +14,7 @@ struct tcf_mirred { }; #define to_mirred(a) ((struct tcf_mirred *)a) -static inline bool is_tcf_mirred_redirect(const struct tc_action *a) +static inline bool is_tcf_mirred_egress_redirect(const struct tc_action *a) { #ifdef CONFIG_NET_CLS_ACT if (a->ops && a->ops->type == TCA_ACT_MIRRED) @@ -23,7 +23,7 @@ static inline bool is_tcf_mirred_redirect(const struct tc_action *a) return false; } -static inline bool is_tcf_mirred_mirror(const struct tc_action *a) +static inline bool is_tcf_mirred_egress_mirror(const struct tc_action *a) { #ifdef CONFIG_NET_CLS_ACT if (a->ops && a->ops->type == TCA_ACT_MIRRED) -- cgit v1.1 From 53592b3640019f2834701093e38272fdfd367ad8 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Thu, 13 Oct 2016 09:06:44 +0300 Subject: net/sched: act_mirred: Implement ingress actions Up until now, 'action mirred' supported only egress actions (either TCA_EGRESS_REDIR or TCA_EGRESS_MIRROR). This patch implements the corresponding ingress actions TCA_INGRESS_REDIR and TCA_INGRESS_MIRROR. This allows attaching filters whose target is to hand matching skbs into the rx processing of a specified device. Signed-off-by: Shmulik Ladkani Cc: Jamal Hadi Salim Cc: Eric Dumazet Cc: Cong Wang Tested-by: Jamal Hadi Salim Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/act_mirred.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 69dcce8..2d93be6 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -33,6 +33,25 @@ static LIST_HEAD(mirred_list); static DEFINE_SPINLOCK(mirred_list_lock); +static bool tcf_mirred_is_act_redirect(int action) +{ + return action == TCA_EGRESS_REDIR || action == TCA_INGRESS_REDIR; +} + +static u32 tcf_mirred_act_direction(int action) +{ + switch (action) { + case TCA_EGRESS_REDIR: + case TCA_EGRESS_MIRROR: + return AT_EGRESS; + case TCA_INGRESS_REDIR: + case TCA_INGRESS_MIRROR: + return AT_INGRESS; + default: + BUG(); + } +} + static void tcf_mirred_release(struct tc_action *a, int bind) { struct tcf_mirred *m = to_mirred(a); @@ -97,6 +116,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, switch (parm->eaction) { case TCA_EGRESS_MIRROR: case TCA_EGRESS_REDIR: + case TCA_INGRESS_REDIR: + case TCA_INGRESS_MIRROR: break; default: if (exists) @@ -156,15 +177,20 @@ static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { struct tcf_mirred *m = to_mirred(a); + bool m_mac_header_xmit; struct net_device *dev; struct sk_buff *skb2; - int retval, err; + int retval, err = 0; + int m_eaction; + int mac_len; u32 at; tcf_lastuse_update(&m->tcf_tm); bstats_cpu_update(this_cpu_ptr(m->common.cpu_bstats), skb); rcu_read_lock(); + m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit); + m_eaction = READ_ONCE(m->tcfm_eaction); retval = READ_ONCE(m->tcf_action); dev = rcu_dereference(m->tcfm_dev); if (unlikely(!dev)) { @@ -183,23 +209,36 @@ static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, if (!skb2) goto out; - if (!(at & AT_EGRESS)) { - if (m->tcfm_mac_header_xmit) + /* If action's target direction differs than filter's direction, + * and devices expect a mac header on xmit, then mac push/pull is + * needed. + */ + if (at != tcf_mirred_act_direction(m_eaction) && m_mac_header_xmit) { + if (at & AT_EGRESS) { + /* caught at egress, act ingress: pull mac */ + mac_len = skb_network_header(skb) - skb_mac_header(skb); + skb_pull_rcsum(skb2, mac_len); + } else { + /* caught at ingress, act egress: push mac */ skb_push_rcsum(skb2, skb->mac_len); + } } /* mirror is always swallowed */ - if (m->tcfm_eaction != TCA_EGRESS_MIRROR) + if (tcf_mirred_is_act_redirect(m_eaction)) skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); skb2->skb_iif = skb->dev->ifindex; skb2->dev = dev; - err = dev_queue_xmit(skb2); + if (tcf_mirred_act_direction(m_eaction) & AT_EGRESS) + err = dev_queue_xmit(skb2); + else + err = netif_receive_skb(skb2); if (err) { out: qstats_overlimit_inc(this_cpu_ptr(m->common.cpu_qstats)); - if (m->tcfm_eaction != TCA_EGRESS_MIRROR) + if (tcf_mirred_is_act_redirect(m_eaction)) retval = TC_ACT_SHOT; } rcu_read_unlock(); -- cgit v1.1 From c3aaa403840a5ccd305fb5e73f3cbfac6453b5e5 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Fri, 14 Oct 2016 05:19:17 -0400 Subject: qed: Pass MAC hints to VFs Some hypervisors can support MAC hints to their VFs. Even though we don't have such a hypervisor API in linux, we add sufficient logic for the VF to be able to receive such hints and set the mac accordingly - as long as the VF has not been set with a MAC already. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_vf.c | 4 ++-- drivers/net/ethernet/qlogic/qede/qede_main.c | 6 +++++- include/linux/qed/qed_eth_if.h | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index abf5bf1..f580bf4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -1230,8 +1230,8 @@ static void qed_handle_bulletin_change(struct qed_hwfn *hwfn) is_mac_exist = qed_vf_bulletin_get_forced_mac(hwfn, mac, &is_mac_forced); - if (is_mac_exist && is_mac_forced && cookie) - ops->force_mac(cookie, mac); + if (is_mac_exist && cookie) + ops->force_mac(cookie, mac, !!is_mac_forced); /* Always update link configuration according to bulletin */ qed_link_update(hwfn); diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 343038c..9866d95 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -171,10 +171,14 @@ static struct pci_driver qede_pci_driver = { #endif }; -static void qede_force_mac(void *dev, u8 *mac) +static void qede_force_mac(void *dev, u8 *mac, bool forced) { struct qede_dev *edev = dev; + /* MAC hints take effect only if we haven't set one already */ + if (is_valid_ether_addr(edev->ndev->dev_addr) && !forced) + return; + ether_addr_copy(edev->ndev->dev_addr, mac); ether_addr_copy(edev->primary_mac, mac); } diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 33c24eb..1c77948 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -129,7 +129,7 @@ struct qed_tunn_params { struct qed_eth_cb_ops { struct qed_common_cb_ops common; - void (*force_mac) (void *dev, u8 *mac); + void (*force_mac) (void *dev, u8 *mac, bool forced); }; #ifdef CONFIG_DCB -- cgit v1.1 From a150241ccf31f8606f5ceb2208fd421066c17e07 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Fri, 14 Oct 2016 05:19:18 -0400 Subject: qede: GSO support for tunnels with outer csum This patch adds GSO support for GRE and UDP tunnels where outer checksums are enabled. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 1 + drivers/net/ethernet/qlogic/qede/qede_main.c | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 28c0e9f..f50e527 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -320,6 +320,7 @@ struct qede_fastpath { #define XMIT_L4_CSUM BIT(0) #define XMIT_LSO BIT(1) #define XMIT_ENC BIT(2) +#define XMIT_ENC_GSO_L4_CSUM BIT(3) #define QEDE_CSUM_ERROR BIT(0) #define QEDE_CSUM_UNNECESSARY BIT(1) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 9866d95..7d5dc1e 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -400,8 +400,19 @@ static u32 qede_xmit_type(struct qede_dev *edev, (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPV6)) *ipv6_ext = 1; - if (skb->encapsulation) + if (skb->encapsulation) { rc |= XMIT_ENC; + if (skb_is_gso(skb)) { + unsigned short gso_type = skb_shinfo(skb)->gso_type; + + if ((gso_type & SKB_GSO_UDP_TUNNEL_CSUM) || + (gso_type & SKB_GSO_GRE_CSUM)) + rc |= XMIT_ENC_GSO_L4_CSUM; + + rc |= XMIT_LSO; + return rc; + } + } if (skb_is_gso(skb)) rc |= XMIT_LSO; @@ -637,6 +648,12 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, if (unlikely(xmit_type & XMIT_ENC)) { first_bd->data.bd_flags.bitfields |= 1 << ETH_TX_1ST_BD_FLAGS_TUNN_IP_CSUM_SHIFT; + + if (xmit_type & XMIT_ENC_GSO_L4_CSUM) { + u8 tmp = ETH_TX_1ST_BD_FLAGS_TUNN_L4_CSUM_SHIFT; + + first_bd->data.bd_flags.bitfields |= 1 << tmp; + } hlen = qede_get_skb_hlen(skb, true); } else { first_bd->data.bd_flags.bitfields |= @@ -2320,11 +2337,14 @@ static void qede_init_ndev(struct qede_dev *edev) /* Encap features*/ hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_TSO_ECN; + NETIF_F_TSO_ECN | NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_GRE_CSUM; ndev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GSO_GRE | - NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RXCSUM; + NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RXCSUM | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_GRE_CSUM; ndev->vlan_features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM | NETIF_F_HIGHDMA; -- cgit v1.1 From 256958538ae2616e4aa14efab8c0d11df1e188db Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Fri, 14 Oct 2016 05:19:19 -0400 Subject: qede: Prevent GSO on long Geneve headers Due to hardware limitation, when transmitting a geneve-encapsulated packet with more than 32 bytes worth of geneve options the hardware would not be able to crack the packet and consider it a regular UDP packet. This implements the ndo_features_check() in qede in order to prevent GSO on said transmitted packets. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_main.c | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 7d5dc1e..6c2b09c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -2240,6 +2240,40 @@ static void qede_udp_tunnel_del(struct net_device *dev, schedule_delayed_work(&edev->sp_task, 0); } +/* 8B udp header + 8B base tunnel header + 32B option length */ +#define QEDE_MAX_TUN_HDR_LEN 48 + +static netdev_features_t qede_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + if (skb->encapsulation) { + u8 l4_proto = 0; + + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + l4_proto = ip_hdr(skb)->protocol; + break; + case htons(ETH_P_IPV6): + l4_proto = ipv6_hdr(skb)->nexthdr; + break; + default: + return features; + } + + /* Disable offloads for geneve tunnels, as HW can't parse + * the geneve header which has option length greater than 32B. + */ + if ((l4_proto == IPPROTO_UDP) && + ((skb_inner_mac_header(skb) - + skb_transport_header(skb)) > QEDE_MAX_TUN_HDR_LEN)) + return features & ~(NETIF_F_CSUM_MASK | + NETIF_F_GSO_MASK); + } + + return features; +} + static const struct net_device_ops qede_netdev_ops = { .ndo_open = qede_open, .ndo_stop = qede_close, @@ -2264,6 +2298,7 @@ static const struct net_device_ops qede_netdev_ops = { #endif .ndo_udp_tunnel_add = qede_udp_tunnel_add, .ndo_udp_tunnel_del = qede_udp_tunnel_del, + .ndo_features_check = qede_features_check, }; /* ------------------------------------------------------------------------- -- cgit v1.1 From 7b7e70f979e34ed84d725eab8ea42921ab6f42e3 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Fri, 14 Oct 2016 05:19:20 -0400 Subject: qed*: Allow unicast filtering Apparently qede fails to set IFF_UNICAST_FLT, and as a result is not actually performing unicast MAC filtering. While we're at it - relax a hard-coded limitation that limits each interface into using at most 15 unicast MAC addresses before turning promiscuous. Instead utilize the HW resources to their limit. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_l2.c | 12 ++++++++++-- drivers/net/ethernet/qlogic/qede/qede_main.c | 4 +++- include/linux/qed/qed_eth_if.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index ddd410a..6b0e22d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1652,6 +1652,7 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev, if (IS_PF(cdev)) { int max_vf_vlan_filters = 0; + int max_vf_mac_filters = 0; if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) { for_each_hwfn(cdev, i) @@ -1665,11 +1666,18 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev, info->num_queues = cdev->num_hwfns; } - if (IS_QED_SRIOV(cdev)) + if (IS_QED_SRIOV(cdev)) { max_vf_vlan_filters = cdev->p_iov_info->total_vfs * QED_ETH_VF_NUM_VLAN_FILTERS; - info->num_vlan_filters = RESC_NUM(&cdev->hwfns[0], QED_VLAN) - + max_vf_mac_filters = cdev->p_iov_info->total_vfs * + QED_ETH_VF_NUM_MAC_FILTERS; + } + info->num_vlan_filters = RESC_NUM(QED_LEADING_HWFN(cdev), + QED_VLAN) - max_vf_vlan_filters; + info->num_mac_filters = RESC_NUM(QED_LEADING_HWFN(cdev), + QED_MAC) - + max_vf_mac_filters; ether_addr_copy(info->port_mac, cdev->hwfns[0].hw_info.hw_mac_addr); diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 6c2b09c..0e483af 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -2365,6 +2365,8 @@ static void qede_init_ndev(struct qede_dev *edev) qede_set_ethtool_ops(ndev); + ndev->priv_flags = IFF_UNICAST_FLT; + /* user-changeble features */ hw_features = NETIF_F_GRO | NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | @@ -3937,7 +3939,7 @@ static void qede_config_rx_mode(struct net_device *ndev) /* Check for promiscuous */ if ((ndev->flags & IFF_PROMISC) || - (uc_count > 15)) { /* @@@TBD resource allocation - 1 */ + (uc_count > edev->dev_info.num_mac_filters - 1)) { accept_flags = QED_FILTER_RX_MODE_TYPE_PROMISC; } else { /* Add MAC filters according to the unicast secondary macs */ diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 1c77948..1513080 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -23,6 +23,7 @@ struct qed_dev_eth_info { u8 port_mac[ETH_ALEN]; u8 num_vlan_filters; + u16 num_mac_filters; /* Legacy VF - this affects the datapath, so qede has to know */ bool is_legacy; -- cgit v1.1 From c59f5291fd63f9892c8f1d4b94247aeea92305c5 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Fri, 14 Oct 2016 05:19:21 -0400 Subject: qed: Allow chance for fast ramrod completions Whenever a ramrod is being sent for some device configuration, the driver is going to sleep at least 5ms between each iteration of polling on the completion of the ramrod. However, in almost every configuration scenario the firmware would be able to comply and complete the ramrod in a manner of several usecs. This is especially important in cases where there might be a lot of sequential configurations applying to the hardware [e.g., RoCE], in which case the existing scheme might cause some visible user delays. This patch changes the completion scheme - instead of immediately starting to sleep for a 'long' period, allow the device to quickly poll on the first iteration after a couple of usecs. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_spq.c | 85 +++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index caff415..259a615 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -37,7 +37,11 @@ ***************************************************************************/ #define SPQ_HIGH_PRI_RESERVE_DEFAULT (1) -#define SPQ_BLOCK_SLEEP_LENGTH (1000) + +#define SPQ_BLOCK_DELAY_MAX_ITER (10) +#define SPQ_BLOCK_DELAY_US (10) +#define SPQ_BLOCK_SLEEP_MAX_ITER (1000) +#define SPQ_BLOCK_SLEEP_MS (5) /*************************************************************************** * Blocking Imp. (BLOCK/EBLOCK mode) @@ -57,53 +61,81 @@ static void qed_spq_blocking_cb(struct qed_hwfn *p_hwfn, smp_wmb(); } -static int qed_spq_block(struct qed_hwfn *p_hwfn, - struct qed_spq_entry *p_ent, - u8 *p_fw_ret) +static int __qed_spq_block(struct qed_hwfn *p_hwfn, + struct qed_spq_entry *p_ent, + u8 *p_fw_ret, bool sleep_between_iter) { - int sleep_count = SPQ_BLOCK_SLEEP_LENGTH; struct qed_spq_comp_done *comp_done; - int rc; + u32 iter_cnt; comp_done = (struct qed_spq_comp_done *)p_ent->comp_cb.cookie; - while (sleep_count) { - /* validate we receive completion update */ + iter_cnt = sleep_between_iter ? SPQ_BLOCK_SLEEP_MAX_ITER + : SPQ_BLOCK_DELAY_MAX_ITER; + + while (iter_cnt--) { + /* Validate we receive completion update */ smp_rmb(); if (comp_done->done == 1) { if (p_fw_ret) *p_fw_ret = comp_done->fw_return_code; return 0; } - usleep_range(5000, 10000); - sleep_count--; + + if (sleep_between_iter) + msleep(SPQ_BLOCK_SLEEP_MS); + else + udelay(SPQ_BLOCK_DELAY_US); } + return -EBUSY; +} + +static int qed_spq_block(struct qed_hwfn *p_hwfn, + struct qed_spq_entry *p_ent, + u8 *p_fw_ret, bool skip_quick_poll) +{ + struct qed_spq_comp_done *comp_done; + int rc; + + /* A relatively short polling period w/o sleeping, to allow the FW to + * complete the ramrod and thus possibly to avoid the following sleeps. + */ + if (!skip_quick_poll) { + rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, false); + if (!rc) + return 0; + } + + /* Move to polling with a sleeping period between iterations */ + rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, true); + if (!rc) + return 0; + DP_INFO(p_hwfn, "Ramrod is stuck, requesting MCP drain\n"); rc = qed_mcp_drain(p_hwfn, p_hwfn->p_main_ptt); - if (rc != 0) + if (rc) { DP_NOTICE(p_hwfn, "MCP drain failed\n"); + goto err; + } /* Retry after drain */ - sleep_count = SPQ_BLOCK_SLEEP_LENGTH; - while (sleep_count) { - /* validate we receive completion update */ - smp_rmb(); - if (comp_done->done == 1) { - if (p_fw_ret) - *p_fw_ret = comp_done->fw_return_code; - return 0; - } - usleep_range(5000, 10000); - sleep_count--; - } + rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, true); + if (!rc) + return 0; + comp_done = (struct qed_spq_comp_done *)p_ent->comp_cb.cookie; if (comp_done->done == 1) { if (p_fw_ret) *p_fw_ret = comp_done->fw_return_code; return 0; } - - DP_NOTICE(p_hwfn, "Ramrod is stuck, MCP drain failed\n"); +err: + DP_NOTICE(p_hwfn, + "Ramrod is stuck [CID %08x cmd %02x protocol %02x echo %04x]\n", + le32_to_cpu(p_ent->elem.hdr.cid), + p_ent->elem.hdr.cmd_id, + p_ent->elem.hdr.protocol_id, + le16_to_cpu(p_ent->elem.hdr.echo)); return -EBUSY; } @@ -729,7 +761,8 @@ int qed_spq_post(struct qed_hwfn *p_hwfn, * access p_ent here to see whether it's successful or not. * Thus, after gaining the answer perform the cleanup here. */ - rc = qed_spq_block(p_hwfn, p_ent, fw_return_code); + rc = qed_spq_block(p_hwfn, p_ent, fw_return_code, + p_ent->queue == &p_spq->unlimited_pending); if (p_ent->queue == &p_spq->unlimited_pending) { /* This is an allocated p_ent which does not need to -- cgit v1.1 From 7eff82b0fc26937d23e59d3de8a1930b25d4738c Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Fri, 14 Oct 2016 05:19:22 -0400 Subject: qed: Handle malicious VFs events Malicious VFs might be caught in several different methods: - Misusing their bar permission and being blocked by hardware. - Misusing their fastpath logic and being blocked by firmware. - Misusing their interaction with their PF via hw-channel, and being blocked by PF driver. On the first two items, firmware would indicate to driver that the VF is to be considered malicious, but would sometime still allow the VF to communicate with the PF [depending on the exact nature of the malicious activity done by the VF]. The current existing logic on the PF side lacks handling of such events, and might allow the PF to perform some incorrect configuration on behalf of a VF that was previously indicated as malicious. The new scheme is simple - Once the PF determines a VF is malicious it would: a. Ignore any further requests on behalf of the VF-driver. b. Prevent any configurations initiated by the hyperuser for the malicious VF, as firmware isn't willing to serve such. The malicious indication would be cleared upon the VF flr, after which it would become usable once again. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 114 +++++++++++++++++++++++----- drivers/net/ethernet/qlogic/qed/qed_sriov.h | 1 + drivers/net/ethernet/qlogic/qed/qed_vf.h | 1 + 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index d2d6621..6f029f9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -109,7 +109,8 @@ static int qed_sp_vf_stop(struct qed_hwfn *p_hwfn, } static bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn, - int rel_vf_id, bool b_enabled_only) + int rel_vf_id, + bool b_enabled_only, bool b_non_malicious) { if (!p_hwfn->pf_iov_info) { DP_NOTICE(p_hwfn->cdev, "No iov info\n"); @@ -124,6 +125,10 @@ static bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn, b_enabled_only) return false; + if ((p_hwfn->pf_iov_info->vfs_array[rel_vf_id].b_malicious) && + b_non_malicious) + return false; + return true; } @@ -138,7 +143,8 @@ static struct qed_vf_info *qed_iov_get_vf_info(struct qed_hwfn *p_hwfn, return NULL; } - if (qed_iov_is_valid_vfid(p_hwfn, relative_vf_id, b_enabled_only)) + if (qed_iov_is_valid_vfid(p_hwfn, relative_vf_id, + b_enabled_only, false)) vf = &p_hwfn->pf_iov_info->vfs_array[relative_vf_id]; else DP_ERR(p_hwfn, "qed_iov_get_vf_info: VF[%d] is not enabled\n", @@ -542,7 +548,8 @@ int qed_iov_hw_info(struct qed_hwfn *p_hwfn) return 0; } -static bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid) +bool _qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, + int vfid, bool b_fail_malicious) { /* Check PF supports sriov */ if (IS_VF(p_hwfn->cdev) || !IS_QED_SRIOV(p_hwfn->cdev) || @@ -550,12 +557,17 @@ static bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid) return false; /* Check VF validity */ - if (!qed_iov_is_valid_vfid(p_hwfn, vfid, true)) + if (!qed_iov_is_valid_vfid(p_hwfn, vfid, true, b_fail_malicious)) return false; return true; } +bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid) +{ + return _qed_iov_pf_sanity_check(p_hwfn, vfid, true); +} + static void qed_iov_set_vf_to_disable(struct qed_dev *cdev, u16 rel_vf_id, u8 to_disable) { @@ -652,6 +664,9 @@ static int qed_iov_enable_vf_access(struct qed_hwfn *p_hwfn, qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf); + /* It's possible VF was previously considered malicious */ + vf->b_malicious = false; + rc = qed_mcp_config_vf_msix(p_hwfn, p_ptt, vf->abs_vf_id, vf->num_sbs); if (rc) return rc; @@ -2804,6 +2819,13 @@ qed_iov_execute_vf_flr_cleanup(struct qed_hwfn *p_hwfn, return rc; } + /* Workaround to make VF-PF channel ready, as FW + * doesn't do that as a part of FLR. + */ + REG_WR(p_hwfn, + GTT_BAR0_MAP_REG_USDM_RAM + + USTORM_VF_PF_CHANNEL_READY_OFFSET(vfid), 1); + /* VF_STOPPED has to be set only after final cleanup * but prior to re-enabling the VF. */ @@ -2942,7 +2964,8 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, mbx->first_tlv = mbx->req_virt->first_tlv; /* check if tlv type is known */ - if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { + if (qed_iov_tlv_supported(mbx->first_tlv.tl.type) && + !p_vf->b_malicious) { switch (mbx->first_tlv.tl.type) { case CHANNEL_TLV_ACQUIRE: qed_iov_vf_mbx_acquire(p_hwfn, p_ptt, p_vf); @@ -2984,6 +3007,15 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, qed_iov_vf_mbx_release(p_hwfn, p_ptt, p_vf); break; } + } else if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF [%02x] - considered malicious; Ignoring TLV [%04x]\n", + p_vf->abs_vf_id, mbx->first_tlv.tl.type); + + qed_iov_prepare_resp(p_hwfn, p_ptt, p_vf, + mbx->first_tlv.tl.type, + sizeof(struct pfvf_def_resp_tlv), + PFVF_STATUS_MALICIOUS); } else { /* unknown TLV - this may belong to a VF driver from the future * - a version written after this PF driver was written, which @@ -3033,20 +3065,30 @@ static void qed_iov_pf_get_and_clear_pending_events(struct qed_hwfn *p_hwfn, memset(p_pending_events, 0, sizeof(u64) * QED_VF_ARRAY_LENGTH); } -static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn, - u16 abs_vfid, struct regpair *vf_msg) +static struct qed_vf_info *qed_sriov_get_vf_from_absid(struct qed_hwfn *p_hwfn, + u16 abs_vfid) { - u8 min = (u8)p_hwfn->cdev->p_iov_info->first_vf_in_pf; - struct qed_vf_info *p_vf; + u8 min = (u8) p_hwfn->cdev->p_iov_info->first_vf_in_pf; - if (!qed_iov_pf_sanity_check(p_hwfn, (int)abs_vfid - min)) { + if (!_qed_iov_pf_sanity_check(p_hwfn, (int)abs_vfid - min, false)) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, - "Got a message from VF [abs 0x%08x] that cannot be handled by PF\n", + "Got indication for VF [abs 0x%08x] that cannot be handled by PF\n", abs_vfid); - return 0; + return NULL; } - p_vf = &p_hwfn->pf_iov_info->vfs_array[(u8)abs_vfid - min]; + + return &p_hwfn->pf_iov_info->vfs_array[(u8) abs_vfid - min]; +} + +static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn, + u16 abs_vfid, struct regpair *vf_msg) +{ + struct qed_vf_info *p_vf = qed_sriov_get_vf_from_absid(p_hwfn, + abs_vfid); + + if (!p_vf) + return 0; /* List the physical address of the request so that handler * could later on copy the message from it. @@ -3060,6 +3102,23 @@ static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn, return 0; } +static void qed_sriov_vfpf_malicious(struct qed_hwfn *p_hwfn, + struct malicious_vf_eqe_data *p_data) +{ + struct qed_vf_info *p_vf; + + p_vf = qed_sriov_get_vf_from_absid(p_hwfn, p_data->vf_id); + + if (!p_vf) + return; + + DP_INFO(p_hwfn, + "VF [%d] - Malicious behavior [%02x]\n", + p_vf->abs_vf_id, p_data->err_id); + + p_vf->b_malicious = true; +} + int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, u8 opcode, __le16 echo, union event_ring_data *data) { @@ -3067,6 +3126,9 @@ int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, case COMMON_EVENT_VF_PF_CHANNEL: return qed_sriov_vfpf_msg(p_hwfn, le16_to_cpu(echo), &data->vf_pf_channel.msg_addr); + case COMMON_EVENT_MALICIOUS_VF: + qed_sriov_vfpf_malicious(p_hwfn, &data->malicious_vf); + return 0; default: DP_INFO(p_hwfn->cdev, "Unknown sriov eqe event 0x%02x\n", opcode); @@ -3083,7 +3145,7 @@ u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, u16 rel_vf_id) goto out; for (i = rel_vf_id; i < p_iov->total_vfs; i++) - if (qed_iov_is_valid_vfid(p_hwfn, rel_vf_id, true)) + if (qed_iov_is_valid_vfid(p_hwfn, rel_vf_id, true, false)) return i; out: @@ -3130,6 +3192,12 @@ static void qed_iov_bulletin_set_forced_mac(struct qed_hwfn *p_hwfn, return; } + if (vf_info->b_malicious) { + DP_NOTICE(p_hwfn->cdev, + "Can't set forced MAC to malicious VF [%d]\n", vfid); + return; + } + feature = 1 << MAC_ADDR_FORCED; memcpy(vf_info->bulletin.p_virt->mac, mac, ETH_ALEN); @@ -3153,6 +3221,12 @@ static void qed_iov_bulletin_set_forced_vlan(struct qed_hwfn *p_hwfn, return; } + if (vf_info->b_malicious) { + DP_NOTICE(p_hwfn->cdev, + "Can't set forced vlan to malicious VF [%d]\n", vfid); + return; + } + feature = 1 << VLAN_ADDR_FORCED; vf_info->bulletin.p_virt->pvid = pvid; if (pvid) @@ -3367,7 +3441,7 @@ int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled) qed_for_each_vf(hwfn, j) { int k; - if (!qed_iov_is_valid_vfid(hwfn, j, true)) + if (!qed_iov_is_valid_vfid(hwfn, j, true, false)) continue; /* Wait until VF is disabled before releasing */ @@ -3425,7 +3499,7 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num) num_sbs = min_t(int, sb_cnt_info.sb_free_blk, limit); for (i = 0; i < num; i++) { - if (!qed_iov_is_valid_vfid(hwfn, i, false)) + if (!qed_iov_is_valid_vfid(hwfn, i, false, true)) continue; rc = qed_iov_init_hw_for_vf(hwfn, @@ -3477,7 +3551,7 @@ static int qed_sriov_pf_set_mac(struct qed_dev *cdev, u8 *mac, int vfid) return -EINVAL; } - if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true)) { + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true, true)) { DP_VERBOSE(cdev, QED_MSG_IOV, "Cannot set VF[%d] MAC (VF is not active)\n", vfid); return -EINVAL; @@ -3509,7 +3583,7 @@ static int qed_sriov_pf_set_vlan(struct qed_dev *cdev, u16 vid, int vfid) return -EINVAL; } - if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true)) { + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true, true)) { DP_VERBOSE(cdev, QED_MSG_IOV, "Cannot set VF[%d] MAC (VF is not active)\n", vfid); return -EINVAL; @@ -3543,7 +3617,7 @@ static int qed_get_vf_config(struct qed_dev *cdev, if (IS_VF(cdev)) return -EINVAL; - if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true)) { + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true, false)) { DP_VERBOSE(cdev, QED_MSG_IOV, "VF index [%d] isn't active\n", vf_id); return -EINVAL; @@ -3647,7 +3721,7 @@ static int qed_set_vf_link_state(struct qed_dev *cdev, if (IS_VF(cdev)) return -EINVAL; - if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true)) { + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true, true)) { DP_VERBOSE(cdev, QED_MSG_IOV, "VF index [%d] isn't active\n", vf_id); return -EINVAL; diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index 0dd23e4..3cf515b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -132,6 +132,7 @@ struct qed_vf_info { struct qed_iov_vf_mbx vf_mbx; enum vf_state state; bool b_init; + bool b_malicious; u8 to_disable; struct qed_bulletin bulletin; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 35db7a28..944745b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -40,6 +40,7 @@ enum { PFVF_STATUS_NOT_SUPPORTED, PFVF_STATUS_NO_RESOURCE, PFVF_STATUS_FORCED, + PFVF_STATUS_MALICIOUS, }; /* vf pf channel tlvs */ -- cgit v1.1 From d5df7688b6a36dbb5611a58229a5e294cd978999 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Fri, 14 Oct 2016 05:19:23 -0400 Subject: qed: Fix possible race when reading firmware return code. While handling SPQ ramrod completion, there is a possible race where driver might not read updated fw return code based on ramrod completion done. This patch ensures that fw return code is written first and then completion done flag is updated using appropriate memory barriers. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sp.h | 4 ++-- drivers/net/ethernet/qlogic/qed/qed_spq.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp.h b/drivers/net/ethernet/qlogic/qed/qed_sp.h index 652c908..27c450f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sp.h @@ -111,8 +111,8 @@ union qed_spq_req_comp { }; struct qed_spq_comp_done { - u64 done; - u8 fw_return_code; + unsigned int done; + u8 fw_return_code; }; struct qed_spq_entry { diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index 259a615..6c05402 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -54,11 +54,10 @@ static void qed_spq_blocking_cb(struct qed_hwfn *p_hwfn, comp_done = (struct qed_spq_comp_done *)cookie; - comp_done->done = 0x1; - comp_done->fw_return_code = fw_return_code; + comp_done->fw_return_code = fw_return_code; - /* make update visible to waiting thread */ - smp_wmb(); + /* Make sure completion done is visible on waiting thread */ + smp_store_release(&comp_done->done, 0x1); } static int __qed_spq_block(struct qed_hwfn *p_hwfn, @@ -74,8 +73,9 @@ static int __qed_spq_block(struct qed_hwfn *p_hwfn, while (iter_cnt--) { /* Validate we receive completion update */ - smp_rmb(); - if (comp_done->done == 1) { + if (READ_ONCE(comp_done->done) == 1) { + /* Read updated FW return value */ + smp_read_barrier_depends(); if (p_fw_ret) *p_fw_ret = comp_done->fw_return_code; return 0; -- cgit v1.1 From 462d807474783dfc7d0c412f320749aebcc5f8fc Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Fri, 14 Oct 2016 20:27:25 +0900 Subject: fjes: ethtool -d support for fjes driver This patch adds implementation of supporting ethtool -d for fjes driver. By using ethtool -d, you can get registers dump of Exetnded socket device. # ethtool -d es0 Offset Values ------ ------ 0x0000: 01 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 0x0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0020: 02 00 00 80 02 00 00 80 64 a6 58 08 07 00 00 00 0x0030: 00 00 00 00 28 80 00 00 00 00 f9 e3 06 00 00 00 0x0040: 00 00 00 00 18 00 00 00 80 a4 58 08 07 00 00 00 0x0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0080: 00 00 00 00 00 00 e0 7f 00 00 01 00 00 00 01 00 0x0090: 00 00 00 00 Signed-off-by: Taku Izumi Signed-off-by: David S. Miller --- drivers/net/fjes/fjes_ethtool.c | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/net/fjes/fjes_ethtool.c b/drivers/net/fjes/fjes_ethtool.c index 9c218e1..8397634 100644 --- a/drivers/net/fjes/fjes_ethtool.c +++ b/drivers/net/fjes/fjes_ethtool.c @@ -121,12 +121,60 @@ static int fjes_get_settings(struct net_device *netdev, return 0; } +static int fjes_get_regs_len(struct net_device *netdev) +{ +#define FJES_REGS_LEN 37 + return FJES_REGS_LEN * sizeof(u32); +} + +static void fjes_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct fjes_adapter *adapter = netdev_priv(netdev); + struct fjes_hw *hw = &adapter->hw; + u32 *regs_buff = p; + + memset(p, 0, FJES_REGS_LEN * sizeof(u32)); + + regs->version = 1; + + /* Information registers */ + regs_buff[0] = rd32(XSCT_OWNER_EPID); + regs_buff[1] = rd32(XSCT_MAX_EP); + + /* Device Control registers */ + regs_buff[4] = rd32(XSCT_DCTL); + + /* Command Control registers */ + regs_buff[8] = rd32(XSCT_CR); + regs_buff[9] = rd32(XSCT_CS); + regs_buff[10] = rd32(XSCT_SHSTSAL); + regs_buff[11] = rd32(XSCT_SHSTSAH); + + regs_buff[13] = rd32(XSCT_REQBL); + regs_buff[14] = rd32(XSCT_REQBAL); + regs_buff[15] = rd32(XSCT_REQBAH); + + regs_buff[17] = rd32(XSCT_RESPBL); + regs_buff[18] = rd32(XSCT_RESPBAL); + regs_buff[19] = rd32(XSCT_RESPBAH); + + /* Interrupt Control registers */ + regs_buff[32] = rd32(XSCT_IS); + regs_buff[33] = rd32(XSCT_IMS); + regs_buff[34] = rd32(XSCT_IMC); + regs_buff[35] = rd32(XSCT_IG); + regs_buff[36] = rd32(XSCT_ICTL); +} + static const struct ethtool_ops fjes_ethtool_ops = { .get_settings = fjes_get_settings, .get_drvinfo = fjes_get_drvinfo, .get_ethtool_stats = fjes_get_ethtool_stats, .get_strings = fjes_get_strings, .get_sset_count = fjes_get_sset_count, + .get_regs = fjes_get_regs, + .get_regs_len = fjes_get_regs_len, }; void fjes_set_ethtool_ops(struct net_device *netdev) -- cgit v1.1 From 21b7efbc5e910487c86f93ac88194dadbacac6e8 Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Fri, 14 Oct 2016 20:27:32 +0900 Subject: fjes: Enhance ethtool -S for fjes driver This patch enhances ethtool -S for fjes driver so that EP related statistics can be retrieved. The following statistics can be displayed via ethtool -S: ep%d_com_regist_buf_exec ep%d_com_unregist_buf_exec ep%d_send_intr_rx ep%d_send_intr_unshare ep%d_send_intr_zoneupdate ep%d_recv_intr_rx ep%d_recv_intr_unshare ep%d_recv_intr_stop ep%d_recv_intr_zoneupdate ep%d_tx_buffer_full ep%d_tx_dropped_not_shared ep%d_tx_dropped_ver_mismatch ep%d_tx_dropped_buf_size_mismatch ep%d_tx_dropped_vlanid_mismatch Signed-off-by: Taku Izumi Signed-off-by: David S. Miller --- drivers/net/fjes/fjes_ethtool.c | 70 ++++++++++++++++++++++++++++++++++++++++- drivers/net/fjes/fjes_hw.c | 9 ++++++ drivers/net/fjes/fjes_hw.h | 19 +++++++++++ drivers/net/fjes/fjes_main.c | 44 +++++++++++++++++++++++--- 4 files changed, 137 insertions(+), 5 deletions(-) diff --git a/drivers/net/fjes/fjes_ethtool.c b/drivers/net/fjes/fjes_ethtool.c index 8397634..68ef287 100644 --- a/drivers/net/fjes/fjes_ethtool.c +++ b/drivers/net/fjes/fjes_ethtool.c @@ -49,10 +49,18 @@ static const struct fjes_stats fjes_gstrings_stats[] = { FJES_STAT("tx_dropped", stats64.tx_dropped), }; +#define FJES_EP_STATS_LEN 14 +#define FJES_STATS_LEN \ + (ARRAY_SIZE(fjes_gstrings_stats) + \ + ((&((struct fjes_adapter *)netdev_priv(netdev))->hw)->max_epid - 1) * \ + FJES_EP_STATS_LEN) + static void fjes_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct fjes_adapter *adapter = netdev_priv(netdev); + struct fjes_hw *hw = &adapter->hw; + int epidx; char *p; int i; @@ -61,11 +69,39 @@ static void fjes_get_ethtool_stats(struct net_device *netdev, data[i] = (fjes_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } + for (epidx = 0; epidx < hw->max_epid; epidx++) { + if (epidx == hw->my_epid) + continue; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .com_regist_buf_exec; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .com_unregist_buf_exec; + data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_rx; + data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_unshare; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .send_intr_zoneupdate; + data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_rx; + data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_unshare; + data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_stop; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .recv_intr_zoneupdate; + data[i++] = hw->ep_shm_info[epidx].ep_stats.tx_buffer_full; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .tx_dropped_not_shared; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .tx_dropped_ver_mismatch; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .tx_dropped_buf_size_mismatch; + data[i++] = hw->ep_shm_info[epidx].ep_stats + .tx_dropped_vlanid_mismatch; + } } static void fjes_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { + struct fjes_adapter *adapter = netdev_priv(netdev); + struct fjes_hw *hw = &adapter->hw; u8 *p = data; int i; @@ -76,6 +112,38 @@ static void fjes_get_strings(struct net_device *netdev, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } + for (i = 0; i < hw->max_epid; i++) { + if (i == hw->my_epid) + continue; + sprintf(p, "ep%u_com_regist_buf_exec", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_com_unregist_buf_exec", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_send_intr_rx", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_send_intr_unshare", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_send_intr_zoneupdate", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_recv_intr_rx", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_recv_intr_unshare", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_recv_intr_stop", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_recv_intr_zoneupdate", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_tx_buffer_full", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_tx_dropped_not_shared", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_tx_dropped_ver_mismatch", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_tx_dropped_buf_size_mismatch", i); + p += ETH_GSTRING_LEN; + sprintf(p, "ep%u_tx_dropped_vlanid_mismatch", i); + p += ETH_GSTRING_LEN; + } break; } } @@ -84,7 +152,7 @@ static int fjes_get_sset_count(struct net_device *netdev, int sset) { switch (sset) { case ETH_SS_STATS: - return ARRAY_SIZE(fjes_gstrings_stats); + return FJES_STATS_LEN; default: return -EOPNOTSUPP; } diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c index 0dbafed..82b56e8 100644 --- a/drivers/net/fjes/fjes_hw.c +++ b/drivers/net/fjes/fjes_hw.c @@ -752,6 +752,7 @@ void fjes_hw_raise_epstop(struct fjes_hw *hw) case EP_PARTNER_SHARED: fjes_hw_raise_interrupt(hw, epidx, REG_ICTL_MASK_TXRX_STOP_REQ); + hw->ep_shm_info[epidx].ep_stats.send_intr_unshare += 1; break; default: break; @@ -1062,6 +1063,9 @@ static void fjes_hw_update_zone_task(struct work_struct *work) break; } mutex_unlock(&hw->hw_info.lock); + + hw->ep_shm_info[epidx].ep_stats + .com_regist_buf_exec += 1; } if (test_bit(epidx, &unshare_bit)) { @@ -1085,6 +1089,9 @@ static void fjes_hw_update_zone_task(struct work_struct *work) mutex_unlock(&hw->hw_info.lock); + hw->ep_shm_info[epidx].ep_stats + .com_unregist_buf_exec += 1; + if (ret == 0) { spin_lock_irqsave(&hw->rx_status_lock, flags); fjes_hw_setup_epbuf( @@ -1099,6 +1106,8 @@ static void fjes_hw_update_zone_task(struct work_struct *work) fjes_hw_raise_interrupt(hw, epidx, REG_ICTL_MASK_TXRX_STOP_REQ); + hw->ep_shm_info[epidx].ep_stats.send_intr_unshare += 1; + set_bit(epidx, &hw->txrx_stop_req_bit); spin_lock_irqsave(&hw->rx_status_lock, flags); hw->ep_shm_info[epidx].tx. diff --git a/drivers/net/fjes/fjes_hw.h b/drivers/net/fjes/fjes_hw.h index 1445ac9..205182e 100644 --- a/drivers/net/fjes/fjes_hw.h +++ b/drivers/net/fjes/fjes_hw.h @@ -228,6 +228,24 @@ union ep_buffer_info { }; +/* statistics of EP */ +struct fjes_drv_ep_stats { + u64 com_regist_buf_exec; + u64 com_unregist_buf_exec; + u64 send_intr_rx; + u64 send_intr_unshare; + u64 send_intr_zoneupdate; + u64 recv_intr_rx; + u64 recv_intr_unshare; + u64 recv_intr_stop; + u64 recv_intr_zoneupdate; + u64 tx_buffer_full; + u64 tx_dropped_not_shared; + u64 tx_dropped_ver_mismatch; + u64 tx_dropped_buf_size_mismatch; + u64 tx_dropped_vlanid_mismatch; +}; + /* buffer pair for Extended Partition */ struct ep_share_mem_info { struct epbuf_handler { @@ -238,6 +256,7 @@ struct ep_share_mem_info { } tx, rx; struct rtnl_link_stats64 net_stats; + struct fjes_drv_ep_stats ep_stats; u16 tx_status_work; diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index e46b1eb..03e2383 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -366,6 +366,8 @@ static int fjes_setup_resources(struct fjes_adapter *adapter) FJES_ZONING_STATUS_ENABLE)) { fjes_hw_raise_interrupt(hw, epidx, REG_ICTL_MASK_INFO_UPDATE); + hw->ep_shm_info[epidx].ep_stats + .send_intr_zoneupdate += 1; } } @@ -397,6 +399,9 @@ static int fjes_setup_resources(struct fjes_adapter *adapter) adapter->force_reset = true; return result; } + + hw->ep_shm_info[epidx].ep_stats + .com_regist_buf_exec += 1; } } @@ -422,6 +427,8 @@ static void fjes_free_resources(struct fjes_adapter *adapter) result = fjes_hw_unregister_buff_addr(hw, epidx); mutex_unlock(&hw->hw_info.lock); + hw->ep_shm_info[epidx].ep_stats.com_unregist_buf_exec += 1; + if (result) reset_flag = true; @@ -567,6 +574,7 @@ static void fjes_raise_intr_rxdata_task(struct work_struct *work) FJES_RX_POLL_WORK)) { fjes_hw_raise_interrupt(hw, epid, REG_ICTL_MASK_RX_DATA); + hw->ep_shm_info[epid].ep_stats.send_intr_rx += 1; } } @@ -663,6 +671,9 @@ fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev) pstatus = fjes_hw_get_partner_ep_status(hw, dest_epid); if (pstatus != EP_PARTNER_SHARED) { + if (!is_multi) + hw->ep_shm_info[dest_epid].ep_stats + .tx_dropped_not_shared += 1; ret = NETDEV_TX_OK; } else if (!fjes_hw_check_epbuf_version( &adapter->hw.ep_shm_info[dest_epid].rx, 0)) { @@ -670,6 +681,8 @@ fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev) adapter->stats64.tx_carrier_errors += 1; hw->ep_shm_info[dest_epid].net_stats .tx_carrier_errors += 1; + hw->ep_shm_info[dest_epid].ep_stats + .tx_dropped_ver_mismatch += 1; ret = NETDEV_TX_OK; } else if (!fjes_hw_check_mtu( @@ -679,12 +692,16 @@ fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev) hw->ep_shm_info[dest_epid].net_stats.tx_dropped += 1; adapter->stats64.tx_errors += 1; hw->ep_shm_info[dest_epid].net_stats.tx_errors += 1; + hw->ep_shm_info[dest_epid].ep_stats + .tx_dropped_buf_size_mismatch += 1; ret = NETDEV_TX_OK; } else if (vlan && !fjes_hw_check_vlan_id( &adapter->hw.ep_shm_info[dest_epid].rx, vlan_id)) { + hw->ep_shm_info[dest_epid].ep_stats + .tx_dropped_vlanid_mismatch += 1; ret = NETDEV_TX_OK; } else { if (len < VLAN_ETH_HLEN) { @@ -718,6 +735,8 @@ fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev) ret = NETDEV_TX_OK; } else { netif_trans_update(netdev); + hw->ep_shm_info[dest_epid].ep_stats + .tx_buffer_full += 1; netif_tx_stop_queue(cur_queue); if (!work_pending(&adapter->tx_stall_task)) @@ -970,21 +989,33 @@ static irqreturn_t fjes_intr(int irq, void *data) icr = fjes_hw_capture_interrupt_status(hw); if (icr & REG_IS_MASK_IS_ASSERT) { - if (icr & REG_ICTL_MASK_RX_DATA) + if (icr & REG_ICTL_MASK_RX_DATA) { fjes_rx_irq(adapter, icr & REG_IS_MASK_EPID); + hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats + .recv_intr_rx += 1; + } - if (icr & REG_ICTL_MASK_DEV_STOP_REQ) + if (icr & REG_ICTL_MASK_DEV_STOP_REQ) { fjes_stop_req_irq(adapter, icr & REG_IS_MASK_EPID); + hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats + .recv_intr_stop += 1; + } - if (icr & REG_ICTL_MASK_TXRX_STOP_REQ) + if (icr & REG_ICTL_MASK_TXRX_STOP_REQ) { fjes_txrx_stop_req_irq(adapter, icr & REG_IS_MASK_EPID); + hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats + .recv_intr_unshare += 1; + } if (icr & REG_ICTL_MASK_TXRX_STOP_DONE) fjes_hw_set_irqmask(hw, REG_ICTL_MASK_TXRX_STOP_DONE, true); - if (icr & REG_ICTL_MASK_INFO_UPDATE) + if (icr & REG_ICTL_MASK_INFO_UPDATE) { fjes_update_zone_irq(adapter, icr & REG_IS_MASK_EPID); + hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats + .recv_intr_zoneupdate += 1; + } ret = IRQ_HANDLED; } else { @@ -1364,6 +1395,8 @@ static void fjes_watch_unshare_task(struct work_struct *work) break; } mutex_unlock(&hw->hw_info.lock); + hw->ep_shm_info[epidx].ep_stats + .com_unregist_buf_exec += 1; spin_lock_irqsave(&hw->rx_status_lock, flags); fjes_hw_setup_epbuf(&hw->ep_shm_info[epidx].tx, @@ -1406,6 +1439,9 @@ static void fjes_watch_unshare_task(struct work_struct *work) } mutex_unlock(&hw->hw_info.lock); + hw->ep_shm_info[epidx].ep_stats + .com_unregist_buf_exec += 1; + spin_lock_irqsave(&hw->rx_status_lock, flags); fjes_hw_setup_epbuf( &hw->ep_shm_info[epidx].tx, -- cgit v1.1 From 82f6aea801e3732fe4bf41af46ebe352bf24fde8 Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Fri, 14 Oct 2016 20:27:38 +0900 Subject: fjes: Add tracepoints in fjes driver This patch adds tracepoints in fjes driver. This is useful for debugging purpose. Signed-off-by: Taku Izumi Signed-off-by: David S. Miller --- drivers/net/fjes/Makefile | 2 +- drivers/net/fjes/fjes_hw.c | 25 +++- drivers/net/fjes/fjes_main.c | 5 + drivers/net/fjes/fjes_trace.c | 30 ++++ drivers/net/fjes/fjes_trace.h | 311 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 369 insertions(+), 4 deletions(-) create mode 100644 drivers/net/fjes/fjes_trace.c create mode 100644 drivers/net/fjes/fjes_trace.h diff --git a/drivers/net/fjes/Makefile b/drivers/net/fjes/Makefile index 523e3d7..6705d1b 100644 --- a/drivers/net/fjes/Makefile +++ b/drivers/net/fjes/Makefile @@ -27,4 +27,4 @@ obj-$(CONFIG_FUJITSU_ES) += fjes.o -fjes-objs := fjes_main.o fjes_hw.o fjes_ethtool.o +fjes-objs := fjes_main.o fjes_hw.o fjes_ethtool.o fjes_trace.o diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c index 82b56e8..dba59dc 100644 --- a/drivers/net/fjes/fjes_hw.c +++ b/drivers/net/fjes/fjes_hw.c @@ -21,6 +21,7 @@ #include "fjes_hw.h" #include "fjes.h" +#include "fjes_trace.h" static void fjes_hw_update_zone_task(struct work_struct *); static void fjes_hw_epstop_task(struct work_struct *); @@ -371,7 +372,7 @@ fjes_hw_issue_request_command(struct fjes_hw *hw, enum fjes_dev_command_response_e ret = FJES_CMD_STATUS_UNKNOWN; union REG_CR cr; union REG_CS cs; - int timeout; + int timeout = FJES_COMMAND_REQ_TIMEOUT * 1000; cr.reg = 0; cr.bits.req_start = 1; @@ -408,6 +409,8 @@ fjes_hw_issue_request_command(struct fjes_hw *hw, } } + trace_fjes_hw_issue_request_command(&cr, &cs, timeout, ret); + return ret; } @@ -427,11 +430,13 @@ int fjes_hw_request_info(struct fjes_hw *hw) res_buf->info.code = 0; ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_INFO); + trace_fjes_hw_request_info(hw, res_buf); result = 0; if (FJES_DEV_COMMAND_INFO_RES_LEN((*hw->hw_info.max_epid)) != res_buf->info.length) { + trace_fjes_hw_request_info_err("Invalid res_buf"); result = -ENOMSG; } else if (ret == FJES_CMD_STATUS_NORMAL) { switch (res_buf->info.code) { @@ -448,6 +453,7 @@ int fjes_hw_request_info(struct fjes_hw *hw) result = -EPERM; break; case FJES_CMD_STATUS_TIMEOUT: + trace_fjes_hw_request_info_err("Timeout"); result = -EBUSY; break; case FJES_CMD_STATUS_ERROR_PARAM: @@ -512,6 +518,8 @@ int fjes_hw_register_buff_addr(struct fjes_hw *hw, int dest_epid, res_buf->share_buffer.length = 0; res_buf->share_buffer.code = 0; + trace_fjes_hw_register_buff_addr_req(req_buf, buf_pair); + ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_SHARE_BUFFER); timeout = FJES_COMMAND_REQ_BUFF_TIMEOUT * 1000; @@ -532,16 +540,20 @@ int fjes_hw_register_buff_addr(struct fjes_hw *hw, int dest_epid, result = 0; + trace_fjes_hw_register_buff_addr(res_buf, timeout); + if (res_buf->share_buffer.length != - FJES_DEV_COMMAND_SHARE_BUFFER_RES_LEN) + FJES_DEV_COMMAND_SHARE_BUFFER_RES_LEN) { + trace_fjes_hw_register_buff_addr_err("Invalid res_buf"); result = -ENOMSG; - else if (ret == FJES_CMD_STATUS_NORMAL) { + } else if (ret == FJES_CMD_STATUS_NORMAL) { switch (res_buf->share_buffer.code) { case FJES_CMD_REQ_RES_CODE_NORMAL: result = 0; set_bit(dest_epid, &hw->hw_info.buffer_share_bit); break; case FJES_CMD_REQ_RES_CODE_BUSY: + trace_fjes_hw_register_buff_addr_err("Busy Timeout"); result = -EBUSY; break; default: @@ -554,6 +566,7 @@ int fjes_hw_register_buff_addr(struct fjes_hw *hw, int dest_epid, result = -EPERM; break; case FJES_CMD_STATUS_TIMEOUT: + trace_fjes_hw_register_buff_addr_err("Timeout"); result = -EBUSY; break; case FJES_CMD_STATUS_ERROR_PARAM: @@ -595,6 +608,7 @@ int fjes_hw_unregister_buff_addr(struct fjes_hw *hw, int dest_epid) res_buf->unshare_buffer.length = 0; res_buf->unshare_buffer.code = 0; + trace_fjes_hw_unregister_buff_addr_req(req_buf); ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_UNSHARE_BUFFER); timeout = FJES_COMMAND_REQ_BUFF_TIMEOUT * 1000; @@ -616,8 +630,11 @@ int fjes_hw_unregister_buff_addr(struct fjes_hw *hw, int dest_epid) result = 0; + trace_fjes_hw_unregister_buff_addr(res_buf, timeout); + if (res_buf->unshare_buffer.length != FJES_DEV_COMMAND_UNSHARE_BUFFER_RES_LEN) { + trace_fjes_hw_unregister_buff_addr_err("Invalid res_buf"); result = -ENOMSG; } else if (ret == FJES_CMD_STATUS_NORMAL) { switch (res_buf->unshare_buffer.code) { @@ -626,6 +643,7 @@ int fjes_hw_unregister_buff_addr(struct fjes_hw *hw, int dest_epid) clear_bit(dest_epid, &hw->hw_info.buffer_share_bit); break; case FJES_CMD_REQ_RES_CODE_BUSY: + trace_fjes_hw_unregister_buff_addr_err("Busy Timeout"); result = -EBUSY; break; default: @@ -638,6 +656,7 @@ int fjes_hw_unregister_buff_addr(struct fjes_hw *hw, int dest_epid) result = -EPERM; break; case FJES_CMD_STATUS_TIMEOUT: + trace_fjes_hw_unregister_buff_addr_err("Timeout"); result = -EBUSY; break; case FJES_CMD_STATUS_ERROR_PARAM: diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index 03e2383..4c135ae 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -27,6 +27,7 @@ #include #include "fjes.h" +#include "fjes_trace.h" #define MAJ 1 #define MIN 1 @@ -904,6 +905,7 @@ static void fjes_txrx_stop_req_irq(struct fjes_adapter *adapter, unsigned long flags; status = fjes_hw_get_partner_ep_status(hw, src_epid); + trace_fjes_txrx_stop_req_irq_pre(hw, src_epid, status); switch (status) { case EP_PARTNER_UNSHARE: case EP_PARTNER_COMPLETE: @@ -934,6 +936,7 @@ static void fjes_txrx_stop_req_irq(struct fjes_adapter *adapter, } break; } + trace_fjes_txrx_stop_req_irq_post(hw, src_epid); } static void fjes_stop_req_irq(struct fjes_adapter *adapter, int src_epid) @@ -945,6 +948,7 @@ static void fjes_stop_req_irq(struct fjes_adapter *adapter, int src_epid) set_bit(src_epid, &hw->hw_info.buffer_unshare_reserve_bit); status = fjes_hw_get_partner_ep_status(hw, src_epid); + trace_fjes_stop_req_irq_pre(hw, src_epid, status); switch (status) { case EP_PARTNER_WAITING: spin_lock_irqsave(&hw->rx_status_lock, flags); @@ -968,6 +972,7 @@ static void fjes_stop_req_irq(struct fjes_adapter *adapter, int src_epid) queue_work(adapter->control_wq, &hw->epstop_task); break; } + trace_fjes_stop_req_irq_post(hw, src_epid); } static void fjes_update_zone_irq(struct fjes_adapter *adapter, diff --git a/drivers/net/fjes/fjes_trace.c b/drivers/net/fjes/fjes_trace.c new file mode 100644 index 0000000..066fa76 --- /dev/null +++ b/drivers/net/fjes/fjes_trace.c @@ -0,0 +1,30 @@ +/* + * FUJITSU Extended Socket Network Device driver + * Copyright (c) 2015-2016 FUJITSU LIMITED + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +#include + +#ifndef __CHECKER__ +#include "fjes_hw.h" + +#define CREATE_TRACE_POINTS +#include "fjes_trace.h" + +#endif /* __CHECKER__ */ diff --git a/drivers/net/fjes/fjes_trace.h b/drivers/net/fjes/fjes_trace.h new file mode 100644 index 0000000..1f0a489 --- /dev/null +++ b/drivers/net/fjes/fjes_trace.h @@ -0,0 +1,311 @@ +/* + * FUJITSU Extended Socket Network Device driver + * Copyright (c) 2015-2016 FUJITSU LIMITED + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +#if !defined(FJES_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define FJES_TRACE_H_ + +#include +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fjes + +/* tracepoints for fjes_hw.c */ + +TRACE_EVENT(fjes_hw_issue_request_command, + TP_PROTO(union REG_CR *cr, union REG_CS *cs, int timeout, + enum fjes_dev_command_response_e ret), + TP_ARGS(cr, cs, timeout, ret), + TP_STRUCT__entry( + __field(u16, cr_req) + __field(u8, cr_error) + __field(u16, cr_err_info) + __field(u8, cr_req_start) + __field(u16, cs_req) + __field(u8, cs_busy) + __field(u8, cs_complete) + __field(int, timeout) + __field(int, ret); + ), + TP_fast_assign( + __entry->cr_req = cr->bits.req_code; + __entry->cr_error = cr->bits.error; + __entry->cr_err_info = cr->bits.err_info; + __entry->cr_req_start = cr->bits.req_start; + __entry->cs_req = cs->bits.req_code; + __entry->cs_busy = cs->bits.busy; + __entry->cs_complete = cs->bits.complete; + __entry->timeout = timeout; + __entry->ret = ret; + ), + TP_printk("CR=[req=%04x, error=%u, err_info=%04x, req_start=%u], CS=[req=%04x, busy=%u, complete=%u], timeout=%d, ret=%d", + __entry->cr_req, __entry->cr_error, __entry->cr_err_info, + __entry->cr_req_start, __entry->cs_req, __entry->cs_busy, + __entry->cs_complete, __entry->timeout, __entry->ret) +); + +TRACE_EVENT(fjes_hw_request_info, + TP_PROTO(struct fjes_hw *hw, union fjes_device_command_res *res_buf), + TP_ARGS(hw, res_buf), + TP_STRUCT__entry( + __field(int, length) + __field(int, code) + __dynamic_array(u8, zone, hw->max_epid) + __dynamic_array(u8, status, hw->max_epid) + ), + TP_fast_assign( + int x; + + __entry->length = res_buf->info.length; + __entry->code = res_buf->info.code; + for (x = 0; x < hw->max_epid; x++) { + *((u8 *)__get_dynamic_array(zone) + x) = + res_buf->info.info[x].zone; + *((u8 *)__get_dynamic_array(status) + x) = + res_buf->info.info[x].es_status; + } + ), + TP_printk("res_buf=[length=%d, code=%d, es_zones=%s, es_status=%s]", + __entry->length, __entry->code, + __print_array(__get_dynamic_array(zone), + __get_dynamic_array_len(zone) / sizeof(u8), + sizeof(u8)), + __print_array(__get_dynamic_array(status), + __get_dynamic_array_len(status) / sizeof(u8), + sizeof(u8))) +); + +TRACE_EVENT(fjes_hw_request_info_err, + TP_PROTO(char *err), + TP_ARGS(err), + TP_STRUCT__entry( + __string(err, err) + ), + TP_fast_assign( + __assign_str(err, err); + ), + TP_printk("%s", __get_str(err)) +); + +TRACE_EVENT(fjes_hw_register_buff_addr_req, + TP_PROTO(union fjes_device_command_req *req_buf, + struct ep_share_mem_info *buf_pair), + TP_ARGS(req_buf, buf_pair), + TP_STRUCT__entry( + __field(int, length) + __field(int, epid) + __field(u64, tx) + __field(size_t, tx_size) + __field(u64, rx) + __field(size_t, rx_size) + ), + TP_fast_assign( + void *tx, *rx; + + tx = (void *)buf_pair->tx.buffer; + rx = (void *)buf_pair->rx.buffer; + __entry->length = req_buf->share_buffer.length; + __entry->epid = req_buf->share_buffer.epid; + __entry->tx_size = buf_pair->tx.size; + __entry->rx_size = buf_pair->rx.size; + __entry->tx = page_to_phys(vmalloc_to_page(tx)) + + offset_in_page(tx); + __entry->rx = page_to_phys(vmalloc_to_page(rx)) + + offset_in_page(rx); + ), + TP_printk("req_buf=[length=%d, epid=%d], TX=[phy=0x%016llx, size=%zu], RX=[phy=0x%016llx, size=%zu]", + __entry->length, __entry->epid, __entry->tx, __entry->tx_size, + __entry->rx, __entry->rx_size) +); + +TRACE_EVENT(fjes_hw_register_buff_addr, + TP_PROTO(union fjes_device_command_res *res_buf, int timeout), + TP_ARGS(res_buf, timeout), + TP_STRUCT__entry( + __field(int, length) + __field(int, code) + __field(int, timeout) + ), + TP_fast_assign( + __entry->length = res_buf->share_buffer.length; + __entry->code = res_buf->share_buffer.code; + __entry->timeout = timeout; + ), + TP_printk("res_buf=[length=%d, code=%d], timeout=%d", + __entry->length, __entry->code, __entry->timeout) +); + +TRACE_EVENT(fjes_hw_register_buff_addr_err, + TP_PROTO(char *err), + TP_ARGS(err), + TP_STRUCT__entry( + __string(err, err) + ), + TP_fast_assign( + __assign_str(err, err); + ), + TP_printk("%s", __get_str(err)) +); + +TRACE_EVENT(fjes_hw_unregister_buff_addr_req, + TP_PROTO(union fjes_device_command_req *req_buf), + TP_ARGS(req_buf), + TP_STRUCT__entry( + __field(int, length) + __field(int, epid) + ), + TP_fast_assign( + __entry->length = req_buf->unshare_buffer.length; + __entry->epid = req_buf->unshare_buffer.epid; + ), + TP_printk("req_buf=[length=%d, epid=%d]", + __entry->length, __entry->epid) +); + +TRACE_EVENT(fjes_hw_unregister_buff_addr, + TP_PROTO(union fjes_device_command_res *res_buf, int timeout), + TP_ARGS(res_buf, timeout), + TP_STRUCT__entry( + __field(int, length) + __field(int, code) + __field(int, timeout) + ), + TP_fast_assign( + __entry->length = res_buf->unshare_buffer.length; + __entry->code = res_buf->unshare_buffer.code; + __entry->timeout = timeout; + ), + TP_printk("res_buf=[length=%d, code=%d], timeout=%d", + __entry->length, __entry->code, __entry->timeout) +); + +TRACE_EVENT(fjes_hw_unregister_buff_addr_err, + TP_PROTO(char *err), + TP_ARGS(err), + TP_STRUCT__entry( + __string(err, err) + ), + TP_fast_assign( + __assign_str(err, err); + ), + TP_printk("%s", __get_str(err)) +); + +/* tracepoints for fjes_main.c */ + +TRACE_EVENT(fjes_txrx_stop_req_irq_pre, + TP_PROTO(struct fjes_hw *hw, int src_epid, + enum ep_partner_status status), + TP_ARGS(hw, src_epid, status), + TP_STRUCT__entry( + __field(int, src_epid) + __field(enum ep_partner_status, status) + __field(u8, ep_status) + __field(unsigned long, txrx_stop_req_bit) + __field(u16, rx_status) + ), + TP_fast_assign( + __entry->src_epid = src_epid; + __entry->status = status; + __entry->ep_status = hw->hw_info.share->ep_status[src_epid]; + __entry->txrx_stop_req_bit = hw->txrx_stop_req_bit; + __entry->rx_status = + hw->ep_shm_info[src_epid].tx.info->v1i.rx_status; + ), + TP_printk("epid=%d, partner_status=%d, ep_status=%x, txrx_stop_req_bit=%016lx, tx.rx_status=%08x", + __entry->src_epid, __entry->status, __entry->ep_status, + __entry->txrx_stop_req_bit, __entry->rx_status) +); + +TRACE_EVENT(fjes_txrx_stop_req_irq_post, + TP_PROTO(struct fjes_hw *hw, int src_epid), + TP_ARGS(hw, src_epid), + TP_STRUCT__entry( + __field(int, src_epid) + __field(u8, ep_status) + __field(unsigned long, txrx_stop_req_bit) + __field(u16, rx_status) + ), + TP_fast_assign( + __entry->src_epid = src_epid; + __entry->ep_status = hw->hw_info.share->ep_status[src_epid]; + __entry->txrx_stop_req_bit = hw->txrx_stop_req_bit; + __entry->rx_status = hw->ep_shm_info[src_epid].tx.info->v1i.rx_status; + ), + TP_printk("epid=%d, ep_status=%x, txrx_stop_req_bit=%016lx, tx.rx_status=%08x", + __entry->src_epid, __entry->ep_status, + __entry->txrx_stop_req_bit, __entry->rx_status) +); + +TRACE_EVENT(fjes_stop_req_irq_pre, + TP_PROTO(struct fjes_hw *hw, int src_epid, + enum ep_partner_status status), + TP_ARGS(hw, src_epid, status), + TP_STRUCT__entry( + __field(int, src_epid) + __field(enum ep_partner_status, status) + __field(u8, ep_status) + __field(unsigned long, txrx_stop_req_bit) + __field(u16, rx_status) + ), + TP_fast_assign( + __entry->src_epid = src_epid; + __entry->status = status; + __entry->ep_status = hw->hw_info.share->ep_status[src_epid]; + __entry->txrx_stop_req_bit = hw->txrx_stop_req_bit; + __entry->rx_status = + hw->ep_shm_info[src_epid].tx.info->v1i.rx_status; + ), + TP_printk("epid=%d, partner_status=%d, ep_status=%x, txrx_stop_req_bit=%016lx, tx.rx_status=%08x", + __entry->src_epid, __entry->status, __entry->ep_status, + __entry->txrx_stop_req_bit, __entry->rx_status) +); + +TRACE_EVENT(fjes_stop_req_irq_post, + TP_PROTO(struct fjes_hw *hw, int src_epid), + TP_ARGS(hw, src_epid), + TP_STRUCT__entry( + __field(int, src_epid) + __field(u8, ep_status) + __field(unsigned long, txrx_stop_req_bit) + __field(u16, rx_status) + ), + TP_fast_assign( + __entry->src_epid = src_epid; + __entry->ep_status = hw->hw_info.share->ep_status[src_epid]; + __entry->txrx_stop_req_bit = hw->txrx_stop_req_bit; + __entry->rx_status = + hw->ep_shm_info[src_epid].tx.info->v1i.rx_status; + ), + TP_printk("epid=%d, ep_status=%x, txrx_stop_req_bit=%016lx, tx.rx_status=%08x", + __entry->src_epid, __entry->ep_status, + __entry->txrx_stop_req_bit, __entry->rx_status) +); + +#endif /* FJES_TRACE_H_ */ + +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH ../../../drivers/net/fjes +#define TRACE_INCLUDE_FILE fjes_trace + +/* This part must be outside protection */ +#include -- cgit v1.1 From b6ba737d0b290b272452a2362088222a248fc8da Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Fri, 14 Oct 2016 20:27:45 +0900 Subject: fjes: ethtool -w and -W support for fjes driver This patch adds implementation of supporting ethtool -w and -W for fjes driver. You can enable and disable firmware debug mode by using ethtool -W, and also retrieve firmware activity information by using ethtool -w. This is useful for debugging. Signed-off-by: Taku Izumi Signed-off-by: David S. Miller --- drivers/net/fjes/fjes_ethtool.c | 63 ++++++++++++++++++ drivers/net/fjes/fjes_hw.c | 137 ++++++++++++++++++++++++++++++++++++++++ drivers/net/fjes/fjes_hw.h | 15 +++++ drivers/net/fjes/fjes_trace.h | 69 ++++++++++++++++++++ 4 files changed, 284 insertions(+) diff --git a/drivers/net/fjes/fjes_ethtool.c b/drivers/net/fjes/fjes_ethtool.c index 68ef287..6575f88 100644 --- a/drivers/net/fjes/fjes_ethtool.c +++ b/drivers/net/fjes/fjes_ethtool.c @@ -235,6 +235,66 @@ static void fjes_get_regs(struct net_device *netdev, regs_buff[36] = rd32(XSCT_ICTL); } +static int fjes_set_dump(struct net_device *netdev, struct ethtool_dump *dump) +{ + struct fjes_adapter *adapter = netdev_priv(netdev); + struct fjes_hw *hw = &adapter->hw; + int ret = 0; + + if (dump->flag) { + if (hw->debug_mode) + return -EPERM; + + hw->debug_mode = dump->flag; + + /* enable debug mode */ + mutex_lock(&hw->hw_info.lock); + ret = fjes_hw_start_debug(hw); + mutex_unlock(&hw->hw_info.lock); + + if (ret) + hw->debug_mode = 0; + } else { + if (!hw->debug_mode) + return -EPERM; + + /* disable debug mode */ + mutex_lock(&hw->hw_info.lock); + ret = fjes_hw_stop_debug(hw); + mutex_unlock(&hw->hw_info.lock); + } + + return ret; +} + +static int fjes_get_dump_flag(struct net_device *netdev, + struct ethtool_dump *dump) +{ + struct fjes_adapter *adapter = netdev_priv(netdev); + struct fjes_hw *hw = &adapter->hw; + + dump->len = hw->hw_info.trace_size; + dump->version = 1; + dump->flag = hw->debug_mode; + + return 0; +} + +static int fjes_get_dump_data(struct net_device *netdev, + struct ethtool_dump *dump, void *buf) +{ + struct fjes_adapter *adapter = netdev_priv(netdev); + struct fjes_hw *hw = &adapter->hw; + int ret = 0; + + if (hw->hw_info.trace) + memcpy(buf, hw->hw_info.trace, hw->hw_info.trace_size); + else + ret = -EPERM; + + return ret; +} + static const struct ethtool_ops fjes_ethtool_ops = { .get_settings = fjes_get_settings, .get_drvinfo = fjes_get_drvinfo, @@ -243,6 +303,9 @@ static const struct ethtool_ops fjes_ethtool_ops = { .get_sset_count = fjes_get_sset_count, .get_regs = fjes_get_regs, .get_regs_len = fjes_get_regs_len, + .set_dump = fjes_set_dump, + .get_dump_flag = fjes_get_dump_flag, + .get_dump_data = fjes_get_dump_data, }; void fjes_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c index dba59dc..9c652c0 100644 --- a/drivers/net/fjes/fjes_hw.c +++ b/drivers/net/fjes/fjes_hw.c @@ -343,6 +343,9 @@ int fjes_hw_init(struct fjes_hw *hw) ret = fjes_hw_setup(hw); + hw->hw_info.trace = vzalloc(FJES_DEBUG_BUFFER_SIZE); + hw->hw_info.trace_size = FJES_DEBUG_BUFFER_SIZE; + return ret; } @@ -351,6 +354,18 @@ void fjes_hw_exit(struct fjes_hw *hw) int ret; if (hw->base) { + + if (hw->debug_mode) { + /* disable debug mode */ + mutex_lock(&hw->hw_info.lock); + fjes_hw_stop_debug(hw); + mutex_unlock(&hw->hw_info.lock); + } + vfree(hw->hw_info.trace); + hw->hw_info.trace = NULL; + hw->hw_info.trace_size = 0; + hw->debug_mode = 0; + ret = fjes_hw_reset(hw); if (ret) pr_err("%s: reset error", __func__); @@ -1175,3 +1190,125 @@ static void fjes_hw_epstop_task(struct work_struct *work) } } } + +int fjes_hw_start_debug(struct fjes_hw *hw) +{ + union fjes_device_command_req *req_buf = hw->hw_info.req_buf; + union fjes_device_command_res *res_buf = hw->hw_info.res_buf; + enum fjes_dev_command_response_e ret; + int page_count; + int result = 0; + void *addr; + int i; + + if (!hw->hw_info.trace) + return -EPERM; + memset(hw->hw_info.trace, 0, FJES_DEBUG_BUFFER_SIZE); + + memset(req_buf, 0, hw->hw_info.req_buf_size); + memset(res_buf, 0, hw->hw_info.res_buf_size); + + req_buf->start_trace.length = + FJES_DEV_COMMAND_START_DBG_REQ_LEN(hw->hw_info.trace_size); + req_buf->start_trace.mode = hw->debug_mode; + req_buf->start_trace.buffer_len = hw->hw_info.trace_size; + page_count = hw->hw_info.trace_size / FJES_DEBUG_PAGE_SIZE; + for (i = 0; i < page_count; i++) { + addr = ((u8 *)hw->hw_info.trace) + i * FJES_DEBUG_PAGE_SIZE; + req_buf->start_trace.buffer[i] = + (__le64)(page_to_phys(vmalloc_to_page(addr)) + + offset_in_page(addr)); + } + + res_buf->start_trace.length = 0; + res_buf->start_trace.code = 0; + + trace_fjes_hw_start_debug_req(req_buf); + ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_START_DEBUG); + trace_fjes_hw_start_debug(res_buf); + + if (res_buf->start_trace.length != + FJES_DEV_COMMAND_START_DBG_RES_LEN) { + result = -ENOMSG; + trace_fjes_hw_start_debug_err("Invalid res_buf"); + } else if (ret == FJES_CMD_STATUS_NORMAL) { + switch (res_buf->start_trace.code) { + case FJES_CMD_REQ_RES_CODE_NORMAL: + result = 0; + break; + default: + result = -EPERM; + break; + } + } else { + switch (ret) { + case FJES_CMD_STATUS_UNKNOWN: + result = -EPERM; + break; + case FJES_CMD_STATUS_TIMEOUT: + trace_fjes_hw_start_debug_err("Busy Timeout"); + result = -EBUSY; + break; + case FJES_CMD_STATUS_ERROR_PARAM: + case FJES_CMD_STATUS_ERROR_STATUS: + default: + result = -EPERM; + break; + } + } + + return result; +} + +int fjes_hw_stop_debug(struct fjes_hw *hw) +{ + union fjes_device_command_req *req_buf = hw->hw_info.req_buf; + union fjes_device_command_res *res_buf = hw->hw_info.res_buf; + enum fjes_dev_command_response_e ret; + int result = 0; + + if (!hw->hw_info.trace) + return -EPERM; + + memset(req_buf, 0, hw->hw_info.req_buf_size); + memset(res_buf, 0, hw->hw_info.res_buf_size); + req_buf->stop_trace.length = FJES_DEV_COMMAND_STOP_DBG_REQ_LEN; + + res_buf->stop_trace.length = 0; + res_buf->stop_trace.code = 0; + + ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_STOP_DEBUG); + trace_fjes_hw_stop_debug(res_buf); + + if (res_buf->stop_trace.length != FJES_DEV_COMMAND_STOP_DBG_RES_LEN) { + trace_fjes_hw_stop_debug_err("Invalid res_buf"); + result = -ENOMSG; + } else if (ret == FJES_CMD_STATUS_NORMAL) { + switch (res_buf->stop_trace.code) { + case FJES_CMD_REQ_RES_CODE_NORMAL: + result = 0; + hw->debug_mode = 0; + break; + default: + result = -EPERM; + break; + } + } else { + switch (ret) { + case FJES_CMD_STATUS_UNKNOWN: + result = -EPERM; + break; + case FJES_CMD_STATUS_TIMEOUT: + result = -EBUSY; + trace_fjes_hw_stop_debug_err("Busy Timeout"); + break; + case FJES_CMD_STATUS_ERROR_PARAM: + case FJES_CMD_STATUS_ERROR_STATUS: + default: + result = -EPERM; + break; + } + } + + return result; +} diff --git a/drivers/net/fjes/fjes_hw.h b/drivers/net/fjes/fjes_hw.h index 205182e..3a6da09 100644 --- a/drivers/net/fjes/fjes_hw.h +++ b/drivers/net/fjes/fjes_hw.h @@ -33,6 +33,9 @@ struct fjes_hw; #define EP_BUFFER_SUPPORT_VLAN_MAX 4 #define EP_BUFFER_INFO_SIZE 4096 +#define FJES_DEBUG_PAGE_SIZE 4096 +#define FJES_DEBUG_BUFFER_SIZE (16 * FJES_DEBUG_PAGE_SIZE) + #define FJES_DEVICE_RESET_TIMEOUT ((17 + 1) * 3 * 8) /* sec */ #define FJES_COMMAND_REQ_TIMEOUT ((5 + 1) * 3 * 8) /* sec */ #define FJES_COMMAND_REQ_BUFF_TIMEOUT (60 * 3) /* sec */ @@ -94,6 +97,12 @@ struct fjes_hw; #define FJES_DEV_RES_BUF_SIZE(maxep) \ FJES_DEV_COMMAND_INFO_RES_LEN(maxep) +#define FJES_DEV_COMMAND_START_DBG_REQ_LEN(byte) \ + (16 + (8 * (byte) / FJES_DEBUG_PAGE_SIZE)) +#define FJES_DEV_COMMAND_START_DBG_RES_LEN (8) +#define FJES_DEV_COMMAND_STOP_DBG_REQ_LEN (4) +#define FJES_DEV_COMMAND_STOP_DBG_RES_LEN (8) + /* Frame & MTU */ struct esmem_frame { __le32 frame_size; @@ -173,6 +182,8 @@ enum fjes_dev_command_request_type { FJES_CMD_REQ_INFO = 0x0001, FJES_CMD_REQ_SHARE_BUFFER = 0x0002, FJES_CMD_REQ_UNSHARE_BUFFER = 0x0004, + FJES_CMD_REQ_START_DEBUG = 0x0100, + FJES_CMD_REQ_STOP_DEBUG = 0x0200, }; /* parameter for command control */ @@ -321,6 +332,8 @@ struct fjes_hw { struct fjes_hw_info hw_info; spinlock_t rx_status_lock; /* spinlock for rx_status */ + + u32 debug_mode; }; int fjes_hw_init(struct fjes_hw *); @@ -353,4 +366,6 @@ void *fjes_hw_epbuf_rx_curpkt_get_addr(struct epbuf_handler *, size_t *); void fjes_hw_epbuf_rx_curpkt_drop(struct epbuf_handler *); int fjes_hw_epbuf_tx_pkt_send(struct epbuf_handler *, void *, size_t); +int fjes_hw_start_debug(struct fjes_hw *); +int fjes_hw_stop_debug(struct fjes_hw *); #endif /* FJES_HW_H_ */ diff --git a/drivers/net/fjes/fjes_trace.h b/drivers/net/fjes/fjes_trace.h index 1f0a489..22e8f1a 100644 --- a/drivers/net/fjes/fjes_trace.h +++ b/drivers/net/fjes/fjes_trace.h @@ -209,6 +209,75 @@ TRACE_EVENT(fjes_hw_unregister_buff_addr_err, TP_printk("%s", __get_str(err)) ); +TRACE_EVENT(fjes_hw_start_debug_req, + TP_PROTO(union fjes_device_command_req *req_buf), + TP_ARGS(req_buf), + TP_STRUCT__entry( + __field(int, length) + __field(int, mode) + __field(phys_addr_t, buffer) + ), + TP_fast_assign( + __entry->length = req_buf->start_trace.length; + __entry->mode = req_buf->start_trace.mode; + __entry->buffer = req_buf->start_trace.buffer[0]; + ), + TP_printk("req_buf=[length=%d, mode=%d, buffer=%p]", + __entry->length, __entry->mode, (void *)__entry->buffer) +); + +TRACE_EVENT(fjes_hw_start_debug, + TP_PROTO(union fjes_device_command_res *res_buf), + TP_ARGS(res_buf), + TP_STRUCT__entry( + __field(int, length) + __field(int, code) + ), + TP_fast_assign( + __entry->length = res_buf->start_trace.length; + __entry->code = res_buf->start_trace.code; + ), + TP_printk("res_buf=[length=%d, code=%d]", __entry->length, __entry->code) +); + +TRACE_EVENT(fjes_hw_start_debug_err, + TP_PROTO(char *err), + TP_ARGS(err), + TP_STRUCT__entry( + __string(err, err) + ), + TP_fast_assign( + __assign_str(err, err) + ), + TP_printk("%s", __get_str(err)) +); + +TRACE_EVENT(fjes_hw_stop_debug, + TP_PROTO(union fjes_device_command_res *res_buf), + TP_ARGS(res_buf), + TP_STRUCT__entry( + __field(int, length) + __field(int, code) + ), + TP_fast_assign( + __entry->length = res_buf->stop_trace.length; + __entry->code = res_buf->stop_trace.code; + ), + TP_printk("res_buf=[length=%d, code=%d]", __entry->length, __entry->code) +); + +TRACE_EVENT(fjes_hw_stop_debug_err, + TP_PROTO(char *err), + TP_ARGS(err), + TP_STRUCT__entry( + __string(err, err) + ), + TP_fast_assign( + __assign_str(err, err) + ), + TP_printk("%s", __get_str(err)) +); + /* tracepoints for fjes_main.c */ TRACE_EVENT(fjes_txrx_stop_req_irq_pre, -- cgit v1.1 From c753119e6d1a26a3df04d6fe69d75049082e3f32 Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Fri, 14 Oct 2016 20:28:07 +0900 Subject: fjes: Add debugfs entry for EP status information in fjes driver This patch adds debugfs entry to show EP status information. You can get each EP's status information like the following: # cat /sys/kernel/debug/fjes/fjes.0/status EPID STATUS SAME_ZONE CONNECTED ep0 shared Y Y ep1 - - - ep2 unshared N N ep3 unshared N N ep4 unshared N N ep5 unshared N N ep6 unshared N N ep7 unshared N N Signed-off-by: Taku Izumi Signed-off-by: David S. Miller --- drivers/net/fjes/Makefile | 2 +- drivers/net/fjes/fjes.h | 16 ++++++ drivers/net/fjes/fjes_debugfs.c | 117 ++++++++++++++++++++++++++++++++++++++++ drivers/net/fjes/fjes_main.c | 12 ++++- 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 drivers/net/fjes/fjes_debugfs.c diff --git a/drivers/net/fjes/Makefile b/drivers/net/fjes/Makefile index 6705d1b..bc47b35 100644 --- a/drivers/net/fjes/Makefile +++ b/drivers/net/fjes/Makefile @@ -27,4 +27,4 @@ obj-$(CONFIG_FUJITSU_ES) += fjes.o -fjes-objs := fjes_main.o fjes_hw.o fjes_ethtool.o fjes_trace.o +fjes-objs := fjes_main.o fjes_hw.o fjes_ethtool.o fjes_trace.o fjes_debugfs.o diff --git a/drivers/net/fjes/fjes.h b/drivers/net/fjes/fjes.h index a592fe2..0372be3 100644 --- a/drivers/net/fjes/fjes.h +++ b/drivers/net/fjes/fjes.h @@ -66,6 +66,10 @@ struct fjes_adapter { bool interrupt_watch_enable; struct fjes_hw hw; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_adapter; +#endif }; extern char fjes_driver_name[]; @@ -74,4 +78,16 @@ extern const u32 fjes_support_mtu[]; void fjes_set_ethtool_ops(struct net_device *); +#ifdef CONFIG_DEBUG_FS +void fjes_dbg_adapter_init(struct fjes_adapter *adapter); +void fjes_dbg_adapter_exit(struct fjes_adapter *adapter); +void fjes_dbg_init(void); +void fjes_dbg_exit(void); +#else +static inline void fjes_dbg_adapter_init(struct fjes_adapter *adapter) {} +static inline void fjes_dbg_adapter_exit(struct fjes_adapter *adapter) {} +static inline void fjes_dbg_init(void) {} +static inline void fjes_dbg_exit(void) {} +#endif /* CONFIG_DEBUG_FS */ + #endif /* FJES_H_ */ diff --git a/drivers/net/fjes/fjes_debugfs.c b/drivers/net/fjes/fjes_debugfs.c new file mode 100644 index 0000000..30052eb --- /dev/null +++ b/drivers/net/fjes/fjes_debugfs.c @@ -0,0 +1,117 @@ +/* + * FUJITSU Extended Socket Network Device driver + * Copyright (c) 2015-2016 FUJITSU LIMITED + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +/* debugfs support for fjes driver */ + +#ifdef CONFIG_DEBUG_FS + +#include +#include +#include + +#include "fjes.h" + +static struct dentry *fjes_debug_root; + +static const char * const ep_status_string[] = { + "unshared", + "shared", + "waiting", + "complete", +}; + +static int fjes_dbg_status_show(struct seq_file *m, void *v) +{ + struct fjes_adapter *adapter = m->private; + struct fjes_hw *hw = &adapter->hw; + int max_epid = hw->max_epid; + int my_epid = hw->my_epid; + int epidx; + + seq_puts(m, "EPID\tSTATUS SAME_ZONE CONNECTED\n"); + for (epidx = 0; epidx < max_epid; epidx++) { + if (epidx == my_epid) { + seq_printf(m, "ep%d\t%-16c %-16c %-16c\n", + epidx, '-', '-', '-'); + } else { + seq_printf(m, "ep%d\t%-16s %-16c %-16c\n", + epidx, + ep_status_string[fjes_hw_get_partner_ep_status(hw, epidx)], + fjes_hw_epid_is_same_zone(hw, epidx) ? 'Y' : 'N', + fjes_hw_epid_is_shared(hw->hw_info.share, epidx) ? 'Y' : 'N'); + } + } + + return 0; +} + +static int fjes_dbg_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, fjes_dbg_status_show, inode->i_private); +} + +static const struct file_operations fjes_dbg_status_fops = { + .owner = THIS_MODULE, + .open = fjes_dbg_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void fjes_dbg_adapter_init(struct fjes_adapter *adapter) +{ + const char *name = dev_name(&adapter->plat_dev->dev); + struct dentry *pfile; + + adapter->dbg_adapter = debugfs_create_dir(name, fjes_debug_root); + if (!adapter->dbg_adapter) { + dev_err(&adapter->plat_dev->dev, + "debugfs entry for %s failed\n", name); + return; + } + + pfile = debugfs_create_file("status", 0444, adapter->dbg_adapter, + adapter, &fjes_dbg_status_fops); + if (!pfile) + dev_err(&adapter->plat_dev->dev, + "debugfs status for %s failed\n", name); +} + +void fjes_dbg_adapter_exit(struct fjes_adapter *adapter) +{ + debugfs_remove_recursive(adapter->dbg_adapter); + adapter->dbg_adapter = NULL; +} + +void fjes_dbg_init(void) +{ + fjes_debug_root = debugfs_create_dir(fjes_driver_name, NULL); + if (!fjes_debug_root) + pr_info("init of debugfs failed\n"); +} + +void fjes_dbg_exit(void) +{ + debugfs_remove_recursive(fjes_debug_root); + fjes_debug_root = NULL; +} + +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index 4c135ae..359e7a5 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -1257,6 +1257,8 @@ static int fjes_probe(struct platform_device *plat_dev) netif_carrier_off(netdev); + fjes_dbg_adapter_init(adapter); + return 0; err_hw_exit: @@ -1274,6 +1276,8 @@ static int fjes_remove(struct platform_device *plat_dev) struct fjes_adapter *adapter = netdev_priv(netdev); struct fjes_hw *hw = &adapter->hw; + fjes_dbg_adapter_exit(adapter); + cancel_delayed_work_sync(&adapter->interrupt_watch_task); cancel_work_sync(&adapter->unshare_watch_task); cancel_work_sync(&adapter->raise_intr_rxdata_task); @@ -1478,9 +1482,13 @@ static int __init fjes_init_module(void) pr_info("%s - version %s - %s\n", fjes_driver_string, fjes_driver_version, fjes_copyright); + fjes_dbg_init(); + result = platform_driver_register(&fjes_driver); - if (result < 0) + if (result < 0) { + fjes_dbg_exit(); return result; + } result = acpi_bus_register_driver(&fjes_acpi_driver); if (result < 0) @@ -1490,6 +1498,7 @@ static int __init fjes_init_module(void) fail_acpi_driver: platform_driver_unregister(&fjes_driver); + fjes_dbg_exit(); return result; } @@ -1500,6 +1509,7 @@ static void __exit fjes_exit_module(void) { acpi_bus_unregister_driver(&fjes_acpi_driver); platform_driver_unregister(&fjes_driver); + fjes_dbg_exit(); } module_exit(fjes_exit_module); -- cgit v1.1 From 8f87d7750515a4b6f92947ebe012bf6a503ed48e Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Fri, 14 Oct 2016 20:28:15 +0900 Subject: fjes: Update fjes driver version : 1.2 Signed-off-by: Taku Izumi Signed-off-by: David S. Miller --- drivers/net/fjes/fjes_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index 359e7a5..f36eb4a 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -30,7 +30,7 @@ #include "fjes_trace.h" #define MAJ 1 -#define MIN 1 +#define MIN 2 #define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) #define DRV_NAME "fjes" char fjes_driver_name[] = DRV_NAME; -- cgit v1.1 From 1104d9ba443a3972052ea4eaa01e51f9ee084652 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 14 Oct 2016 11:25:36 -0700 Subject: lwtunnel: Add destroy state operation Users of lwt tunnels may set up some secondary state in build_state function. Add a corresponding destroy_state function to allow users to clean up state. This destroy state function is called from lwstate_free. Also, we now free lwstate using kfree_rcu so user can assume structure is not freed before rcu. Acked-by: Roopa Prabhu Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/lwtunnel.h | 7 +++---- net/core/lwtunnel.c | 13 +++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h index ea3f80f..67d235f 100644 --- a/include/net/lwtunnel.h +++ b/include/net/lwtunnel.h @@ -29,6 +29,7 @@ struct lwtunnel_state { int (*orig_input)(struct sk_buff *); int len; __u16 headroom; + struct rcu_head rcu; __u8 data[0]; }; @@ -36,6 +37,7 @@ struct lwtunnel_encap_ops { int (*build_state)(struct net_device *dev, struct nlattr *encap, unsigned int family, const void *cfg, struct lwtunnel_state **ts); + void (*destroy_state)(struct lwtunnel_state *lws); int (*output)(struct net *net, struct sock *sk, struct sk_buff *skb); int (*input)(struct sk_buff *skb); int (*fill_encap)(struct sk_buff *skb, @@ -46,10 +48,7 @@ struct lwtunnel_encap_ops { }; #ifdef CONFIG_LWTUNNEL -static inline void lwtstate_free(struct lwtunnel_state *lws) -{ - kfree(lws); -} +void lwtstate_free(struct lwtunnel_state *lws); static inline struct lwtunnel_state * lwtstate_get(struct lwtunnel_state *lws) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index e5f84c2..88fd642 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -130,6 +130,19 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type, } EXPORT_SYMBOL(lwtunnel_build_state); +void lwtstate_free(struct lwtunnel_state *lws) +{ + const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type]; + + if (ops->destroy_state) { + ops->destroy_state(lws); + kfree_rcu(lws, rcu); + } else { + kfree(lws); + } +} +EXPORT_SYMBOL(lwtstate_free); + int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate) { const struct lwtunnel_encap_ops *ops; -- cgit v1.1 From 79ff2fc31e0f6a52eeb67fb89fba87e822b9b7b5 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 14 Oct 2016 11:25:37 -0700 Subject: ila: Cache a route to translated address Add a dst_cache to ila_lwt structure. This holds a cached route for the translated address. In ila_output we now perform a route lookup after translation and if possible (destination in original route is full 128 bits) we set the dst_cache. Subsequent calls to ila_output can then use the cache to avoid the route lookup. This eliminates the need to set the gateway on ILA routes as previously was being done. Now we can do something like: ./ip route add 3333::2000:0:0:2/128 encap ila 2222:0:0:2 \ csum-mode neutral-map dev eth0 ## No via needed! Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- net/ipv6/ila/ila_lwt.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index e50c27a..d0a98d9 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -6,29 +6,80 @@ #include #include #include +#include #include #include +#include #include #include #include #include "ila.h" +struct ila_lwt { + struct ila_params p; + struct dst_cache dst_cache; + u32 connected : 1; +}; + +static inline struct ila_lwt *ila_lwt_lwtunnel( + struct lwtunnel_state *lwt) +{ + return (struct ila_lwt *)lwt->data; +} + static inline struct ila_params *ila_params_lwtunnel( - struct lwtunnel_state *lwstate) + struct lwtunnel_state *lwt) { - return (struct ila_params *)lwstate->data; + return &ila_lwt_lwtunnel(lwt)->p; } static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct dst_entry *dst = skb_dst(skb); + struct dst_entry *orig_dst = skb_dst(skb); + struct ila_lwt *ilwt = ila_lwt_lwtunnel(orig_dst->lwtstate); + struct dst_entry *dst; + int err = -EINVAL; if (skb->protocol != htons(ETH_P_IPV6)) goto drop; - ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate), true); + ila_update_ipv6_locator(skb, ila_params_lwtunnel(orig_dst->lwtstate), + true); + + dst = dst_cache_get(&ilwt->dst_cache); + if (unlikely(!dst)) { + struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct flowi6 fl6; + + /* Lookup a route for the new destination. Take into + * account that the base route may already have a gateway. + */ + + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_oif = orig_dst->dev->ifindex; + fl6.flowi6_iif = LOOPBACK_IFINDEX; + fl6.daddr = *rt6_nexthop((struct rt6_info *)orig_dst, + &ip6h->daddr); + + dst = ip6_route_output(net, NULL, &fl6); + if (dst->error) { + err = -EHOSTUNREACH; + dst_release(dst); + goto drop; + } + + dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + goto drop; + } + + if (ilwt->connected) + dst_cache_set_ip6(&ilwt->dst_cache, dst, &fl6.saddr); + } - return dst->lwtstate->orig_output(net, sk, skb); + skb_dst_set(skb, dst); + return dst_output(net, sk, skb); drop: kfree_skb(skb); @@ -60,6 +111,7 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, unsigned int family, const void *cfg, struct lwtunnel_state **ts) { + struct ila_lwt *ilwt; struct ila_params *p; struct nlattr *tb[ILA_ATTR_MAX + 1]; size_t encap_len = sizeof(*p); @@ -71,7 +123,7 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, if (family != AF_INET6) return -EINVAL; - if (cfg6->fc_dst_len < sizeof(struct ila_locator) + 1) { + if (cfg6->fc_dst_len < 8 * sizeof(struct ila_locator) + 3) { /* Need to have full locator and at least type field * included in destination */ @@ -99,6 +151,13 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, if (!newts) return -ENOMEM; + ilwt = ila_lwt_lwtunnel(newts); + ret = dst_cache_init(&ilwt->dst_cache, GFP_ATOMIC); + if (ret) { + kfree(newts); + return ret; + } + newts->len = encap_len; p = ila_params_lwtunnel(newts); @@ -120,11 +179,19 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | LWTUNNEL_STATE_INPUT_REDIRECT; + if (cfg6->fc_dst_len == 8 * sizeof(struct in6_addr)) + ilwt->connected = 1; + *ts = newts; return 0; } +static void ila_destroy_state(struct lwtunnel_state *lwt) +{ + dst_cache_destroy(&ila_lwt_lwtunnel(lwt)->dst_cache); +} + static int ila_fill_encap_info(struct sk_buff *skb, struct lwtunnel_state *lwtstate) { @@ -159,6 +226,7 @@ static int ila_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) static const struct lwtunnel_encap_ops ila_encap_ops = { .build_state = ila_build_state, + .destroy_state = ila_destroy_state, .output = ila_output, .input = ila_input, .fill_encap = ila_fill_encap_info, -- cgit v1.1 From ab530f63e7fbac2b0a233da9d31e21a86d1863f5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 15 Oct 2016 17:40:30 +0200 Subject: ipvlan: constify l3mdev_ops structure This l3mdev_ops structure is only stored in the l3mdev_ops field of a net_device structure. This field is declared const, so the l3mdev_ops structure can be declared as const also. Additionally drop the __read_mostly annotation. The semantic patch that adds const is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct l3mdev_ops i@p = { ... }; @ok@ identifier r.i; struct net_device *e; position p; @@ e->l3mdev_ops = &i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct l3mdev_ops e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct l3mdev_ops i = { ... }; // The effect on the layout of the .o file is shown by the following output of the size command, first before then after the transformation: text data bss dec hex filename 7364 466 52 7882 1eca drivers/net/ipvlan/ipvlan_main.o 7412 434 52 7898 1eda drivers/net/ipvlan/ipvlan_main.o Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index f442eb3..ab90b22 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -26,7 +26,7 @@ static struct nf_hook_ops ipvl_nfops[] __read_mostly = { }, }; -static struct l3mdev_ops ipvl_l3mdev_ops __read_mostly = { +static const struct l3mdev_ops ipvl_l3mdev_ops = { .l3mdev_l3_rcv = ipvlan_l3_rcv, }; -- cgit v1.1 From 1609d18de655e243d2db0f8fa21a3830ff0ab89f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Oct 2016 08:02:48 +0200 Subject: nl80211: correctly use nl80211_nan_srf_policy This was clearly intended to be used in the attribute parsing, so do that instead of leaving the attribute policy unused. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 903cd5a..65c0a17 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10786,7 +10786,8 @@ static int nl80211_nan_add_func(struct sk_buff *skb, err = nla_parse(srf_tb, NL80211_NAN_SRF_ATTR_MAX, nla_data(tb[NL80211_NAN_FUNC_SRF]), - nla_len(tb[NL80211_NAN_FUNC_SRF]), NULL); + nla_len(tb[NL80211_NAN_FUNC_SRF]), + nl80211_nan_srf_policy); if (err) goto out; -- cgit v1.1 From f83ace3b1ed4c3032c963f9f1a46985fc797633c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Oct 2016 08:04:07 +0200 Subject: nl80211: ifdef WoWLAN related policies To avoid unused variable warnings when CONFIG_PM isn't set, add the appropriate ifdef to the policies that are only used for WoWLAN, which can only be invoked when CONFIG_PM is set. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 65c0a17..e48b9c3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -435,6 +435,7 @@ nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, }; +#ifdef CONFIG_PM /* policy for WoWLAN attributes */ static const struct nla_policy nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { @@ -468,6 +469,7 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 }, [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, }; +#endif /* CONFIG_PM */ /* policy for coalesce rule attributes */ static const struct nla_policy -- cgit v1.1 From 4b82a004ec1e89251998bfc30c228b73cfdf6129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Mon, 17 Oct 2016 00:39:33 +0200 Subject: mac80211_hwsim: suggest nl80211 instead of wext driver in documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For mac80211_hwsim interfaces, suggest to use wpa_supplicant with the more modern, netlink based driver instead of wext. Signed-off-by: Linus Lüssing Signed-off-by: Johannes Berg --- Documentation/networking/mac80211_hwsim/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/networking/mac80211_hwsim/README b/Documentation/networking/mac80211_hwsim/README index 24ac91d..3566a72 100644 --- a/Documentation/networking/mac80211_hwsim/README +++ b/Documentation/networking/mac80211_hwsim/README @@ -60,7 +60,7 @@ modprobe mac80211_hwsim hostapd hostapd.conf # Run wpa_supplicant (station) for wlan1 -wpa_supplicant -Dwext -iwlan1 -c wpa_supplicant.conf +wpa_supplicant -Dnl80211 -iwlan1 -c wpa_supplicant.conf More test cases are available in hostap.git: -- cgit v1.1 From 06f2bb1e017166c6413debc28cf72087679242fb Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Sat, 15 Oct 2016 13:28:18 +0200 Subject: mac80211: avoid extra memcpy in A-MSDU head creation Signed-off-by: Michael Braun Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f5d45e4..9661f54 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3059,11 +3059,11 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr; - struct ethhdr amsdu_hdr; + struct ethhdr *amsdu_hdr; int hdr_len = fast_tx->hdr_len - sizeof(rfc1042_header); int subframe_len = skb->len - hdr_len; void *data; - u8 *qc; + u8 *qc, *h_80211_src, *h_80211_dst; if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) return false; @@ -3071,19 +3071,22 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata, if (info->control.flags & IEEE80211_TX_CTRL_AMSDU) return true; - if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(amsdu_hdr), + if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr), &subframe_len)) return false; - amsdu_hdr.h_proto = cpu_to_be16(subframe_len); - memcpy(amsdu_hdr.h_source, skb->data + fast_tx->sa_offs, ETH_ALEN); - memcpy(amsdu_hdr.h_dest, skb->data + fast_tx->da_offs, ETH_ALEN); + data = skb_push(skb, sizeof(*amsdu_hdr)); + memmove(data, data + sizeof(*amsdu_hdr), hdr_len); + hdr = data; + amsdu_hdr = data + hdr_len; + /* h_80211_src/dst is addr* field within hdr */ + h_80211_src = data + fast_tx->sa_offs; + h_80211_dst = data + fast_tx->da_offs; - data = skb_push(skb, sizeof(amsdu_hdr)); - memmove(data, data + sizeof(amsdu_hdr), hdr_len); - memcpy(data + hdr_len, &amsdu_hdr, sizeof(amsdu_hdr)); + amsdu_hdr->h_proto = cpu_to_be16(subframe_len); + ether_addr_copy(amsdu_hdr->h_source, h_80211_src); + ether_addr_copy(amsdu_hdr->h_dest, h_80211_dst); - hdr = data; qc = ieee80211_get_qos_ctl(hdr); *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; -- cgit v1.1 From a3e2f4b6ed9de85086850fe49801f9b00adb6ae1 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Sat, 15 Oct 2016 13:28:19 +0200 Subject: mac80211: fix A-MSDU outer SA/DA According to IEEE 802.11-2012 section 8.3.2 table 8-19, the outer SA/DA of A-MSDU frames need to be changed depending on FromDS/ToDS values. Signed-off-by: Michael Braun [use ether_addr_copy and add alignment annotations] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 +- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/tx.c | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a810dfc..e50c9e0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1438,7 +1438,7 @@ enum ieee80211_vif_flags { struct ieee80211_vif { enum nl80211_iftype type; struct ieee80211_bss_conf bss_conf; - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN] __aligned(2); bool p2p; bool csa_active; bool mu_mimo_owner; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 103187c..42d194a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -443,7 +443,7 @@ struct ieee80211_if_managed { struct ieee80211_mgd_auth_data *auth_data; struct ieee80211_mgd_assoc_data *assoc_data; - u8 bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN] __aligned(2); u16 aid; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9661f54..772e369 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3064,6 +3064,7 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata, int subframe_len = skb->len - hdr_len; void *data; u8 *qc, *h_80211_src, *h_80211_dst; + const u8 *bssid; if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) return false; @@ -3087,6 +3088,28 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata, ether_addr_copy(amsdu_hdr->h_source, h_80211_src); ether_addr_copy(amsdu_hdr->h_dest, h_80211_dst); + /* according to IEEE 802.11-2012 8.3.2 table 8-19, the outer SA/DA + * fields needs to be changed to BSSID for A-MSDU frames depending + * on FromDS/ToDS values. + */ + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + bssid = sdata->u.mgd.bssid; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + bssid = sdata->vif.addr; + break; + default: + bssid = NULL; + } + + if (bssid && ieee80211_has_fromds(hdr->frame_control)) + ether_addr_copy(h_80211_src, bssid); + + if (bssid && ieee80211_has_tods(hdr->frame_control)) + ether_addr_copy(h_80211_dst, bssid); + qc = ieee80211_get_qos_ctl(hdr); *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; -- cgit v1.1 From 4a2d09e31d4b4c9306177390de8911e7a56539c2 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 17 Oct 2016 14:08:36 +0200 Subject: batman-adv: Start new development cycle Signed-off-by: Simon Wunderlich --- net/batman-adv/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 09af21e..8b0979b 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -24,7 +24,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2016.4" +#define BATADV_SOURCE_VERSION "2016.5" #endif /* B.A.T.M.A.N. parameters */ -- cgit v1.1 From c2059d8582191304308c8573ead6585d25d085a6 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 16 Jul 2016 21:30:21 +0200 Subject: batman-adv: Add B.A.T.M.A.N. V sysfs files to README Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- Documentation/networking/batman-adv.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt index 8a8d3d9..d414e60 100644 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@ -32,7 +32,7 @@ compatible interfaces. Once found, it will create subfolders in the /sys directories of each supported interface, e.g. # ls /sys/class/net/eth0/batman_adv/ -# iface_status mesh_iface +# elp_interval iface_status mesh_iface throughput_override If an interface does not have the "batman_adv" subfolder it prob- ably is not supported. Not supported interfaces are: loopback, -- cgit v1.1 From 1ed0359f1556e4c57778933f1e1104ca2cff4994 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 16 Jul 2016 21:30:22 +0200 Subject: batman-adv: Add network_coding and mcast sysfs files to README Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- Documentation/networking/batman-adv.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt index d414e60..8afa991 100644 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@ -71,10 +71,11 @@ All mesh wide settings can be found in batman's own interface folder: # ls /sys/class/net/bat0/mesh/ -#aggregated_ogms distributed_arp_table gw_sel_class orig_interval -#ap_isolation fragmentation hop_penalty routing_algo -#bonding gw_bandwidth isolation_mark vlan0 -#bridge_loop_avoidance gw_mode log_level +# aggregated_ogms fragmentation isolation_mark routing_algo +# ap_isolation gw_bandwidth log_level vlan0 +# bonding gw_mode multicast_mode +# bridge_loop_avoidance gw_sel_class network_coding +# distributed_arp_table hop_penalty orig_interval There is a special folder for debugging information: -- cgit v1.1 From 204fa42c6fc9f20e4142bd1309e0a5065966d64e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 16 Jul 2016 21:32:01 +0200 Subject: batman-adv: Add dat, mcast, nc and neighbor debugfs files to README Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- Documentation/networking/batman-adv.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt index 8afa991..a918131 100644 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@ -80,9 +80,10 @@ folder: There is a special folder for debugging information: # ls /sys/kernel/debug/batman_adv/bat0/ -# bla_backbone_table log transtable_global -# bla_claim_table originators transtable_local -# gateways socket +# bla_backbone_table log neighbors transtable_local +# bla_claim_table mcast_flags originators +# dat_cache nc socket +# gateways nc_nodes transtable_global Some of the files contain all sort of status information regard- ing the mesh network. For example, you can view the table of -- cgit v1.1 From 0de939bac21e3031e06d9e9dc8007fb096e708a0 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 16 Jul 2016 21:32:02 +0200 Subject: batman-adv: Document new nc, mcast and tpmeter log levels Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- Documentation/networking/batman-adv.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt index a918131..ccf9467 100644 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@ -161,13 +161,16 @@ file in debugfs The additional debug output is by default disabled. It can be en- abled during run time. Following log_levels are defined: -0 - All debug output disabled -1 - Enable messages related to routing / flooding / broadcasting -2 - Enable messages related to route added / changed / deleted -4 - Enable messages related to translation table operations -8 - Enable messages related to bridge loop avoidance -16 - Enable messaged related to DAT, ARP snooping and parsing -31 - Enable all messages + 0 - All debug output disabled + 1 - Enable messages related to routing / flooding / broadcasting + 2 - Enable messages related to route added / changed / deleted + 4 - Enable messages related to translation table operations + 8 - Enable messages related to bridge loop avoidance + 16 - Enable messages related to DAT, ARP snooping and parsing + 32 - Enable messages related to network coding + 64 - Enable messages related to multicast +128 - Enable messages related to throughput meter +255 - Enable all messages The debug output can be changed at runtime using the file /sys/class/net/bat0/mesh/log_level. e.g. -- cgit v1.1 From 739ae86cd2b34ba078cee7492d22283f5500ed19 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 22 Jul 2016 01:30:09 +0200 Subject: batman-adv: Remove unused function batadv_hash_delete Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/hash.h | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index cbbf870..557a704 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -61,36 +61,6 @@ void batadv_hash_set_lock_class(struct batadv_hashtable *hash, /* free only the hashtable and the hash itself. */ void batadv_hash_destroy(struct batadv_hashtable *hash); -/* remove the hash structure. if hashdata_free_cb != NULL, this function will be - * called to remove the elements inside of the hash. if you don't remove the - * elements, memory might be leaked. - */ -static inline void batadv_hash_delete(struct batadv_hashtable *hash, - batadv_hashdata_free_cb free_cb, - void *arg) -{ - struct hlist_head *head; - struct hlist_node *node, *node_tmp; - spinlock_t *list_lock; /* spinlock to protect write access */ - u32 i; - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - list_lock = &hash->list_locks[i]; - - spin_lock_bh(list_lock); - hlist_for_each_safe(node, node_tmp, head) { - hlist_del_rcu(node); - - if (free_cb) - free_cb(node, arg); - } - spin_unlock_bh(list_lock); - } - - batadv_hash_destroy(hash); -} - /** * batadv_hash_add - adds data to the hashtable * @hash: storage hash table -- cgit v1.1 From c408c1b9d49077bd6f0e267292fd35624bac76fb Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 22 Jul 2016 01:30:10 +0200 Subject: batman-adv: Move batadv_sum_counter to soft-interface.c The function batadv_sum_counter is only used in soft-interface.c and has no special relevance for main.h. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/main.h | 21 --------------------- net/batman-adv/soft-interface.c | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 8b0979b..6a2328d 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -185,7 +185,6 @@ enum batadv_uev_type { #include /* for packet.h */ #include -#include #include #include /* for packet.h */ #include @@ -284,26 +283,6 @@ static inline void batadv_add_counter(struct batadv_priv *bat_priv, size_t idx, #define batadv_inc_counter(b, i) batadv_add_counter(b, i, 1) -/** - * batadv_sum_counter - Sum the cpu-local counters for index 'idx' - * @bat_priv: the bat priv with all the soft interface information - * @idx: index of counter to sum up - * - * Return: sum of all cpu-local counters - */ -static inline u64 batadv_sum_counter(struct batadv_priv *bat_priv, size_t idx) -{ - u64 *counters, sum = 0; - int cpu; - - for_each_possible_cpu(cpu) { - counters = per_cpu_ptr(bat_priv->bat_counters, cpu); - sum += counters[idx]; - } - - return sum; -} - /* Define a macro to reach the control buffer of the skb. The members of the * control buffer are defined in struct batadv_skb_cb in types.h. * The macro is inspired by the similar macro TCP_SKB_CB() in tcp.h. diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 49e16b6..f37c1c7 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,26 @@ static int batadv_interface_release(struct net_device *dev) return 0; } +/** + * batadv_sum_counter - Sum the cpu-local counters for index 'idx' + * @bat_priv: the bat priv with all the soft interface information + * @idx: index of counter to sum up + * + * Return: sum of all cpu-local counters + */ +static u64 batadv_sum_counter(struct batadv_priv *bat_priv, size_t idx) +{ + u64 *counters, sum = 0; + int cpu; + + for_each_possible_cpu(cpu) { + counters = per_cpu_ptr(bat_priv->bat_counters, cpu); + sum += counters[idx]; + } + + return sum; +} + static struct net_device_stats *batadv_interface_stats(struct net_device *dev) { struct batadv_priv *bat_priv = netdev_priv(dev); -- cgit v1.1 From bf093191db672b2ba6b769f8f7e21519348ab7fd Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 22 Jul 2016 01:30:11 +0200 Subject: batman-adv: Remove unused batadv_icmp_user_cmd_type Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/packet.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 6afc0b8..d2e9bbd 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -252,16 +252,6 @@ struct batadv_elp_packet { #define BATADV_ELP_HLEN sizeof(struct batadv_elp_packet) /** - * enum batadv_icmp_user_cmd_type - types for batman-adv icmp cmd modes - * @BATADV_TP_START: start a throughput meter run - * @BATADV_TP_STOP: stop a throughput meter run - */ -enum batadv_icmp_user_cmd_type { - BATADV_TP_START = 0, - BATADV_TP_STOP = 2, -}; - -/** * struct batadv_icmp_header - common members among all the ICMP packets * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header -- cgit v1.1 From 6020a86a4a208485640f7e17742835755c557676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 23 Aug 2016 03:13:16 +0200 Subject: batman-adv: fix batadv_forw_packet kerneldoc for list attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The forw_packet list node is wrongly attributed to the icmp socket code. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index b3dd1a3..97336ec 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1363,7 +1363,7 @@ struct batadv_skb_cb { /** * struct batadv_forw_packet - structure for bcast packets to be sent/forwarded - * @list: list node for batadv_socket_client::queue_list + * @list: list node for batadv_priv::forw_{bat,bcast}_list * @send_time: execution time for delayed_work (packet sending) * @own: bool for locally generated packets (local OGMs are re-scheduled after * sending) -- cgit v1.1 From 843314c9775cf4e4dc171866b3376f52c85fbffb Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Sun, 25 Sep 2016 08:46:36 +0800 Subject: batman-adv: remove unsed argument from batadv_dbg_arp() function The argument "type" passed to the batadv_dbg_arp() function is never used. Remove it. Signed-off-by: Antonio Quartulli Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/distributed-arp-table.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index e257efd..cbb4f32 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -369,12 +369,11 @@ out: * batadv_dbg_arp - print a debug message containing all the ARP packet details * @bat_priv: the bat priv with all the soft interface information * @skb: ARP packet - * @type: ARP type * @hdr_size: size of the possible header before the ARP packet * @msg: message to print together with the debugging information */ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, - u16 type, int hdr_size, char *msg) + int hdr_size, char *msg) { struct batadv_unicast_4addr_packet *unicast_4addr_packet; struct batadv_bcast_packet *bcast_pkt; @@ -441,7 +440,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, #else static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, - u16 type, int hdr_size, char *msg) + int hdr_size, char *msg) { } @@ -983,8 +982,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, if (type != ARPOP_REQUEST) goto out; - batadv_dbg_arp(bat_priv, skb, type, hdr_size, - "Parsing outgoing ARP REQUEST"); + batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing outgoing ARP REQUEST"); ip_src = batadv_arp_ip_src(skb, hdr_size); hw_src = batadv_arp_hw_src(skb, hdr_size); @@ -1075,8 +1073,7 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, ip_src = batadv_arp_ip_src(skb, hdr_size); ip_dst = batadv_arp_ip_dst(skb, hdr_size); - batadv_dbg_arp(bat_priv, skb, type, hdr_size, - "Parsing incoming ARP REQUEST"); + batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing incoming ARP REQUEST"); batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); @@ -1149,8 +1146,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, if (type != ARPOP_REPLY) return; - batadv_dbg_arp(bat_priv, skb, type, hdr_size, - "Parsing outgoing ARP REPLY"); + batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing outgoing ARP REPLY"); hw_src = batadv_arp_hw_src(skb, hdr_size); ip_src = batadv_arp_ip_src(skb, hdr_size); @@ -1195,8 +1191,7 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, if (type != ARPOP_REPLY) goto out; - batadv_dbg_arp(bat_priv, skb, type, hdr_size, - "Parsing incoming ARP REPLY"); + batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing incoming ARP REPLY"); hw_src = batadv_arp_hw_src(skb, hdr_size); ip_src = batadv_arp_ip_src(skb, hdr_size); -- cgit v1.1 From 0566df307bc882bfca4128521f01a48f4579d27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 20 Sep 2016 15:59:17 +0200 Subject: batman-adv: Allow selecting BATMAN V if CFG80211 is not built MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the new stub for cfg80211_get_station(), we can now build the BATMAN V protocol even with a kernel that was built without any wireless support. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index f20742c..b73b96a 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -17,7 +17,7 @@ config BATMAN_ADV config BATMAN_ADV_BATMAN_V bool "B.A.T.M.A.N. V protocol (experimental)" - depends on BATMAN_ADV && CFG80211=y || (CFG80211=m && BATMAN_ADV=m) + depends on BATMAN_ADV && !(CFG80211=m && BATMAN_ADV=y) default n help This option enables the B.A.T.M.A.N. V protocol, the successor -- cgit v1.1 From ab3a70bef8f36b3884c7bee1f95c09c888ceb43e Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sun, 16 Oct 2016 21:25:35 -0700 Subject: ila: Don't use dest cache when gateway is set If the gateway is set on an ILA route we don't need to bother with using the destination cache in the ILA route. Translation does not change the routing in this case so we can stick with orig_output in the lwstate output function. Tested: Ran netperf with and without gateway for LWT route. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- net/ipv6/ila/ila_lwt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index d0a98d9..9fafba6 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -36,6 +36,7 @@ static inline struct ila_params *ila_params_lwtunnel( static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *orig_dst = skb_dst(skb); + struct rt6_info *rt = (struct rt6_info *)orig_dst; struct ila_lwt *ilwt = ila_lwt_lwtunnel(orig_dst->lwtstate); struct dst_entry *dst; int err = -EINVAL; @@ -46,6 +47,13 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) ila_update_ipv6_locator(skb, ila_params_lwtunnel(orig_dst->lwtstate), true); + if (rt->rt6i_flags & (RTF_GATEWAY | RTF_CACHE)) { + /* Already have a next hop address in route, no need for + * dest cache route. + */ + return orig_dst->lwtstate->orig_output(net, sk, skb); + } + dst = dst_cache_get(&ilwt->dst_cache); if (unlikely(!dst)) { struct ipv6hdr *ip6h = ipv6_hdr(skb); -- cgit v1.1 From a7f7b44beadbec60e78d360944e926d5cba7f2d4 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 17 Oct 2016 09:22:04 +0200 Subject: net: hip04: Remove superfluous ether_setup after alloc_etherdev There is no need to call ether_setup after alloc_ethdev since it was already called there. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hip04_eth.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index 3c99ca4..854befd 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -897,7 +897,6 @@ static int hip04_mac_probe(struct platform_device *pdev) INIT_WORK(&priv->tx_timeout_task, hip04_tx_timeout_task); - ether_setup(ndev); ndev->netdev_ops = &hip04_netdev_ops; ndev->ethtool_ops = &hip04_ethtool_ops; ndev->watchdog_timeo = TX_TIMEOUT; -- cgit v1.1 From e81c7b69c87297ead2d8a6bdd34a008522204824 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 15 Oct 2016 11:53:21 -0700 Subject: rds: Remove unused rds_conn_error This macro's last use was removed in commit d769ef81d5b59 ("RDS: Update rds_conn_shutdown to work with rds_conn_path") so make the macro and the __rds_conn_error function definition and declaration disappear. Signed-off-by: Joe Perches Acked-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/connection.c | 15 --------------- net/rds/rds.h | 4 ---- 2 files changed, 19 deletions(-) diff --git a/net/rds/connection.c b/net/rds/connection.c index f505855..13f459d 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -689,21 +689,6 @@ void rds_conn_connect_if_down(struct rds_connection *conn) } EXPORT_SYMBOL_GPL(rds_conn_connect_if_down); -/* - * An error occurred on the connection - */ -void -__rds_conn_error(struct rds_connection *conn, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprintk(fmt, ap); - va_end(ap); - - rds_conn_drop(conn); -} - void __rds_conn_path_error(struct rds_conn_path *cp, const char *fmt, ...) { diff --git a/net/rds/rds.h b/net/rds/rds.h index fd0bccb..25532a4 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -683,10 +683,6 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, struct rds_info_lengths *lens, int (*visitor)(struct rds_connection *, void *), size_t item_len); -__printf(2, 3) -void __rds_conn_error(struct rds_connection *conn, const char *, ...); -#define rds_conn_error(conn, fmt...) \ - __rds_conn_error(conn, KERN_WARNING "RDS: " fmt) __printf(2, 3) void __rds_conn_path_error(struct rds_conn_path *cp, const char *, ...); -- cgit v1.1 From 9c7cbcf5a87076c210bec87458424be7841755b5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 15 Oct 2016 11:53:22 -0700 Subject: rds: Remove duplicate prefix from rds_conn_path_error use rds_conn_path_error already prefixes "RDS:" to the output. Signed-off-by: Joe Perches Acked-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/threads.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/rds/threads.c b/net/rds/threads.c index e42df11b..e36e333 100644 --- a/net/rds/threads.c +++ b/net/rds/threads.c @@ -171,8 +171,7 @@ void rds_connect_worker(struct work_struct *work) RDS_CONN_DOWN)) rds_queue_reconnect(cp); else - rds_conn_path_error(cp, - "RDS: connect failed\n"); + rds_conn_path_error(cp, "connect failed\n"); } } } -- cgit v1.1 From dc30c35be720aa7c62bd9fa4d506be2d686e9165 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Oct 2016 19:56:49 +0200 Subject: net: dsa: mv88e6xxx: Implement interrupt support. The switch can have up to two interrupt controllers. One of these contains the interrupts from the integrated PHYs, so is useful to export. The Marvell PHY driver can then be used in interrupt mode, rather than polling, speeding up PHY handling and reducing load on the MDIO bus. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- .../devicetree/bindings/net/dsa/marvell.txt | 21 +- drivers/net/dsa/mv88e6xxx/chip.c | 248 ++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/global2.c | 139 +++++++++++- drivers/net/dsa/mv88e6xxx/global2.h | 11 + drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 31 +++ 5 files changed, 438 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/net/dsa/marvell.txt b/Documentation/devicetree/bindings/net/dsa/marvell.txt index 7629189..32025eb 100644 --- a/Documentation/devicetree/bindings/net/dsa/marvell.txt +++ b/Documentation/devicetree/bindings/net/dsa/marvell.txt @@ -20,16 +20,35 @@ Required properties: Optional properties: - reset-gpios : Should be a gpio specifier for a reset line - +- interrupt-parent : Parent interrupt controller +- interrupts : Interrupt from the switch +- interrupt-controller : Indicates the switch is itself an interrupt + controller. This is used for the PHY interrupts. +#interrupt-cells = <2> : Controller uses two cells, number and flag +- mdio : container of PHY and devices on the switches MDIO + bus Example: mdio { #address-cells = <1>; #size-cells = <0>; + interrupt-parent = <&gpio0>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <2>; switch0: switch@0 { compatible = "marvell,mv88e6085"; reg = <0>; reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>; }; + mdio { + #address-cells = <1>; + #size-cells = <0>; + switch1phy0: switch1phy0@0 { + reg = <0>; + interrupt-parent = <&switch0>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + }; + }; }; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 883fd98..ac03297 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -18,11 +18,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -323,6 +327,164 @@ static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val) reg, val); } +static void mv88e6xxx_g1_irq_mask(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + chip->g1_irq.masked |= (1 << n); +} + +static void mv88e6xxx_g1_irq_unmask(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + chip->g1_irq.masked &= ~(1 << n); +} + +static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_chip *chip = dev_id; + unsigned int nhandled = 0; + unsigned int sub_irq; + unsigned int n; + u16 reg; + int err; + + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); + mutex_unlock(&chip->reg_lock); + + if (err) + goto out; + + for (n = 0; n < chip->g1_irq.nirqs; ++n) { + if (reg & (1 << n)) { + sub_irq = irq_find_mapping(chip->g1_irq.domain, n); + handle_nested_irq(sub_irq); + ++nhandled; + } + } +out: + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + + mutex_lock(&chip->reg_lock); +} + +static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + u16 mask = GENMASK(chip->g1_irq.nirqs, 0); + u16 reg; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); + if (err) + goto out; + + reg &= ~mask; + reg |= (~chip->g1_irq.masked & mask); + + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); + if (err) + goto out; + +out: + mutex_unlock(&chip->reg_lock); +} + +static struct irq_chip mv88e6xxx_g1_irq_chip = { + .name = "mv88e6xxx-g1", + .irq_mask = mv88e6xxx_g1_irq_mask, + .irq_unmask = mv88e6xxx_g1_irq_unmask, + .irq_bus_lock = mv88e6xxx_g1_irq_bus_lock, + .irq_bus_sync_unlock = mv88e6xxx_g1_irq_bus_sync_unlock, +}; + +static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d, + unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mv88e6xxx_chip *chip = d->host_data; + + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = { + .map = mv88e6xxx_g1_irq_domain_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip) +{ + int irq, virq; + + for (irq = 0; irq < 16; irq++) { + virq = irq_find_mapping(chip->g2_irq.domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(chip->g2_irq.domain); +} + +static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) +{ + int err, irq; + u16 reg; + + chip->g1_irq.nirqs = chip->info->g1_irqs; + chip->g1_irq.domain = irq_domain_add_simple( + NULL, chip->g1_irq.nirqs, 0, + &mv88e6xxx_g1_irq_domain_ops, chip); + if (!chip->g1_irq.domain) + return -ENOMEM; + + for (irq = 0; irq < chip->g1_irq.nirqs; irq++) + irq_create_mapping(chip->g1_irq.domain, irq); + + chip->g1_irq.chip = mv88e6xxx_g1_irq_chip; + chip->g1_irq.masked = ~0; + + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); + if (err) + goto out; + + reg &= ~GENMASK(chip->g1_irq.nirqs, 0); + + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); + if (err) + goto out; + + /* Reading the interrupt status clears (most of) them */ + err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); + if (err) + goto out; + + err = request_threaded_irq(chip->irq, NULL, + mv88e6xxx_g1_irq_thread_fn, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + dev_name(chip->dev), chip); + if (err) + goto out; + + return 0; + +out: + mv88e6xxx_g1_irq_free(chip); + + return err; +} + int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask) { int i; @@ -2770,7 +2932,11 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) /* Enable the PHY Polling Unit if present, don't discard any packets, * and mask all interrupt sources. */ - reg = 0; + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); + if (err < 0) + return err; + + reg &= ~GLOBAL_CONTROL_PPU_ENABLE; if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) || mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE)) reg |= GLOBAL_CONTROL_PPU_ENABLE; @@ -2875,10 +3041,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) mutex_lock(&chip->reg_lock); - err = mv88e6xxx_switch_reset(chip); - if (err) - goto unlock; - /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { err = mv88e6xxx_setup_port(chip, i); @@ -3326,6 +3488,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 8, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6085_ops, }, @@ -3339,6 +3502,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 8, .flags = MV88E6XXX_FLAGS_FAMILY_6095, .ops = &mv88e6095_ops, }, @@ -3352,6 +3516,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6123_ops, }, @@ -3365,6 +3530,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6131_ops, }, @@ -3378,6 +3544,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6161_ops, }, @@ -3391,6 +3558,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6165_ops, }, @@ -3404,6 +3572,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6171_ops, }, @@ -3417,6 +3586,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6172_ops, }, @@ -3430,6 +3600,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6175_ops, }, @@ -3443,6 +3614,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6176_ops, }, @@ -3456,6 +3628,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 8, .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6185_ops, }, @@ -3469,6 +3642,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6240_ops, }, @@ -3482,6 +3656,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 8, .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6320_ops, }, @@ -3495,6 +3670,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 8, .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6321_ops, }, @@ -3508,6 +3684,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6350_ops, }, @@ -3521,6 +3698,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6351_ops, }, @@ -3534,6 +3712,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6352_ops, }, @@ -3667,6 +3846,12 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, if (err) goto free; + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_switch_reset(chip); + mutex_unlock(&chip->reg_lock); + if (err) + goto free; + mv88e6xxx_phy_init(chip); err = mv88e6xxx_mdio_register(chip, NULL); @@ -3837,17 +4022,56 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) !of_property_read_u32(np, "eeprom-length", &eeprom_len)) chip->eeprom_len = eeprom_len; + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_switch_reset(chip); + mutex_unlock(&chip->reg_lock); + if (err) + goto out; + + chip->irq = of_irq_get(np, 0); + if (chip->irq == -EPROBE_DEFER) { + err = chip->irq; + goto out; + } + + if (chip->irq > 0) { + /* Has to be performed before the MDIO bus is created, + * because the PHYs will link there interrupts to these + * interrupt controllers + */ + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_g1_irq_setup(chip); + mutex_unlock(&chip->reg_lock); + + if (err) + goto out; + + if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) { + err = mv88e6xxx_g2_irq_setup(chip); + if (err) + goto out_g1_irq; + } + } + err = mv88e6xxx_mdio_register(chip, np); if (err) - return err; + goto out_g2_irq; err = mv88e6xxx_register_switch(chip, np); - if (err) { - mv88e6xxx_mdio_unregister(chip); - return err; - } + if (err) + goto out_mdio; return 0; + +out_mdio: + mv88e6xxx_mdio_unregister(chip); +out_g2_irq: + if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) + mv88e6xxx_g2_irq_free(chip); +out_g1_irq: + mv88e6xxx_g1_irq_free(chip); +out: + return err; } static void mv88e6xxx_remove(struct mdio_device *mdiodev) @@ -3858,6 +4082,10 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) mv88e6xxx_phy_destroy(chip); mv88e6xxx_unregister_switch(chip); mv88e6xxx_mdio_unregister(chip); + + if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) + mv88e6xxx_g2_irq_free(chip); + mv88e6xxx_g1_irq_free(chip); } static const struct of_device_id mv88e6xxx_of_match[] = { diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index cf686e7..1a0b135 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -1,5 +1,6 @@ /* - * Marvell 88E6xxx Switch Global 2 Registers support (device address 0x1C) + * Marvell 88E6xxx Switch Global 2 Registers support (device address + * 0x1C) * * Copyright (c) 2008 Marvell Semiconductor * @@ -11,6 +12,7 @@ * (at your option) any later version. */ +#include #include "mv88e6xxx.h" #include "global2.h" @@ -417,6 +419,141 @@ int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr, int reg, return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); } +static void mv88e6xxx_g2_irq_mask(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + chip->g2_irq.masked |= (1 << n); +} + +static void mv88e6xxx_g2_irq_unmask(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + chip->g2_irq.masked &= ~(1 << n); +} + +static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_chip *chip = dev_id; + unsigned int nhandled = 0; + unsigned int sub_irq; + unsigned int n; + int err; + u16 reg; + + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, ®); + mutex_unlock(&chip->reg_lock); + if (err) + goto out; + + for (n = 0; n < 16; ++n) { + if (reg & (1 << n)) { + sub_irq = irq_find_mapping(chip->g2_irq.domain, n); + handle_nested_irq(sub_irq); + ++nhandled; + } + } +out: + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + + mutex_lock(&chip->reg_lock); +} + +static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) +{ + struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + + mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked); + + mutex_unlock(&chip->reg_lock); +} + +static struct irq_chip mv88e6xxx_g2_irq_chip = { + .name = "mv88e6xxx-g2", + .irq_mask = mv88e6xxx_g2_irq_mask, + .irq_unmask = mv88e6xxx_g2_irq_unmask, + .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock, + .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock, +}; + +static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d, + unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mv88e6xxx_chip *chip = d->host_data; + + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = { + .map = mv88e6xxx_g2_irq_domain_map, + .xlate = irq_domain_xlate_twocell, +}; + +void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) +{ + int irq, virq; + + for (irq = 0; irq < 16; irq++) { + virq = irq_find_mapping(chip->g2_irq.domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(chip->g2_irq.domain); +} + +int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) +{ + int device_irq; + int err, irq; + + if (!chip->dev->of_node) + return -EINVAL; + + chip->g2_irq.domain = irq_domain_add_simple( + chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip); + if (!chip->g2_irq.domain) + return -ENOMEM; + + for (irq = 0; irq < 16; irq++) + irq_create_mapping(chip->g2_irq.domain, irq); + + chip->g2_irq.chip = mv88e6xxx_g2_irq_chip; + chip->g2_irq.masked = ~0; + + device_irq = irq_find_mapping(chip->g1_irq.domain, + GLOBAL_STATUS_IRQ_DEVICE); + if (device_irq < 0) { + err = device_irq; + goto out; + } + + err = devm_request_threaded_irq(chip->dev, device_irq, NULL, + mv88e6xxx_g2_irq_thread_fn, + IRQF_ONESHOT, "mv88e6xxx-g1", chip); + if (err) + goto out; + + return 0; +out: + mv88e6xxx_g2_irq_free(chip); + + return err; +} + int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) { u16 reg; diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index c4bb903..1eb3ddd 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -33,6 +33,8 @@ int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, struct ethtool_eeprom *eeprom, u8 *data); int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip); +void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip); #else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ @@ -83,6 +85,15 @@ static inline int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) return -EOPNOTSUPP; } +static inline int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) +{ + return -EOPNOTSUPP; +} + +static inline void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) +{ +} + #endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ #endif /* _MV88E6XXX_GLOBAL2_H */ diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index e572121..6f48e58 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -13,6 +13,7 @@ #define __MV88E6XXX_H #include +#include #include #ifndef UINT64_MAX @@ -167,6 +168,15 @@ #define GLOBAL_STATUS_PPU_INITIALIZING (0x1 << 14) #define GLOBAL_STATUS_PPU_DISABLED (0x2 << 14) #define GLOBAL_STATUS_PPU_POLLING (0x3 << 14) +#define GLOBAL_STATUS_IRQ_AVB 8 +#define GLOBAL_STATUS_IRQ_DEVICE 7 +#define GLOBAL_STATUS_IRQ_STATS 6 +#define GLOBAL_STATUS_IRQ_VTU_PROBLEM 5 +#define GLOBAL_STATUS_IRQ_VTU_DONE 4 +#define GLOBAL_STATUS_IRQ_ATU_PROBLEM 3 +#define GLOBAL_STATUS_IRQ_ATU_DONE 2 +#define GLOBAL_STATUS_IRQ_TCAM_DONE 1 +#define GLOBAL_STATUS_IRQ_EEPROM_DONE 0 #define GLOBAL_MAC_01 0x01 #define GLOBAL_MAC_23 0x02 #define GLOBAL_MAC_45 0x03 @@ -417,6 +427,7 @@ enum mv88e6xxx_cap { * The device contains a second set of global 16-bit registers. */ MV88E6XXX_CAP_GLOBAL2, + MV88E6XXX_CAP_G2_INT, /* (0x00) Interrupt Status */ MV88E6XXX_CAP_G2_MGMT_EN_2X, /* (0x02) MGMT Enable Register 2x */ MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */ MV88E6XXX_CAP_G2_IRL_CMD, /* (0x09) Ingress Rate Command */ @@ -464,6 +475,7 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_G1_VTU_FID BIT_ULL(MV88E6XXX_CAP_G1_VTU_FID) #define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2) +#define MV88E6XXX_FLAG_G2_INT BIT_ULL(MV88E6XXX_CAP_G2_INT) #define MV88E6XXX_FLAG_G2_MGMT_EN_2X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_2X) #define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X) #define MV88E6XXX_FLAG_G2_IRL_CMD BIT_ULL(MV88E6XXX_CAP_G2_IRL_CMD) @@ -524,6 +536,7 @@ enum mv88e6xxx_cap { (MV88E6XXX_FLAG_G1_ATU_FID | \ MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ @@ -536,6 +549,7 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6185 \ (MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAG_PPU | \ @@ -561,6 +575,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G1_ATU_FID | \ MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ @@ -578,6 +593,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G1_ATU_FID | \ MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ @@ -602,6 +618,7 @@ struct mv88e6xxx_info { unsigned int port_base_addr; unsigned int global1_addr; unsigned int age_time_coeff; + unsigned int g1_irqs; unsigned long long flags; const struct mv88e6xxx_ops *ops; }; @@ -628,6 +645,13 @@ struct mv88e6xxx_priv_port { struct net_device *bridge_dev; }; +struct mv88e6xxx_irq { + u16 masked; + struct irq_chip chip; + struct irq_domain *domain; + unsigned int nirqs; +}; + struct mv88e6xxx_chip { const struct mv88e6xxx_info *info; @@ -677,6 +701,13 @@ struct mv88e6xxx_chip { /* And the MDIO bus itself */ struct mii_bus *mdio_bus; + + /* There can be two interrupt controllers, which are chained + * off a GPIO as interrupt source + */ + struct mv88e6xxx_irq g1_irq; + struct mv88e6xxx_irq g2_irq; + int irq; }; struct mv88e6xxx_bus_ops { -- cgit v1.1 From c974bdbc3e7734e72d6347afc49f806342578e75 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Oct 2016 19:56:50 +0200 Subject: net: phy: Use threaded IRQ, to allow IRQ from sleeping devices The interrupt lines from PHYs maybe connected to I2C bus expanders, or from switches on MDIO busses. Such interrupts are sourced from devices which sleep, so use threaded interrupts. Threaded interrupts require that the interrupt requester also uses the threaded API. Change the phylib to use the threaded API, which is backwards compatible with none-threaded IRQs. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index f424b86..25f2b29 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -739,10 +739,10 @@ phy_err: int phy_start_interrupts(struct phy_device *phydev) { atomic_set(&phydev->irq_disable, 0); - if (request_irq(phydev->irq, phy_interrupt, - IRQF_SHARED, - "phy_interrupt", - phydev) < 0) { + if (request_threaded_irq(phydev->irq, NULL, phy_interrupt, + IRQF_ONESHOT | IRQF_SHARED, + "phy_interrupt", + phydev) < 0) { pr_warn("%s: Can't get IRQ %d (PHY)\n", phydev->mdio.bus->name, phydev->irq); phydev->irq = PHY_POLL; -- cgit v1.1 From 664fcf123a30edf16b47d2ce1f610d654ba917b2 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Oct 2016 19:56:51 +0200 Subject: net: phy: Threaded interrupts allow some simplification The PHY interrupts are now handled in a threaded interrupt handler, which can sleep. The work queue is no longer needed, phy_change() can be called directly. phy_mac_interrupt() still needs to be safe to call in interrupt context, so keep the work queue, and use a helper to call phy_change(). Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 45 +++++++++++++++++++++++++------------------- drivers/net/phy/phy_device.c | 2 +- include/linux/phy.h | 5 +++-- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 25f2b29..bb673c6 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -664,7 +664,7 @@ static void phy_error(struct phy_device *phydev) * @phy_dat: phy_device pointer * * Description: When a PHY interrupt occurs, the handler disables - * interrupts, and schedules a work task to clear the interrupt. + * interrupts, and uses phy_change to handle the interrupt. */ static irqreturn_t phy_interrupt(int irq, void *phy_dat) { @@ -673,15 +673,10 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) if (PHY_HALTED == phydev->state) return IRQ_NONE; /* It can't be ours. */ - /* The MDIO bus is not allowed to be written in interrupt - * context, so we need to disable the irq here. A work - * queue will write the PHY to disable and clear the - * interrupt, and then reenable the irq line. - */ disable_irq_nosync(irq); atomic_inc(&phydev->irq_disable); - queue_work(system_power_efficient_wq, &phydev->phy_queue); + phy_change(phydev); return IRQ_HANDLED; } @@ -766,12 +761,6 @@ int phy_stop_interrupts(struct phy_device *phydev) free_irq(phydev->irq, phydev); - /* Cannot call flush_scheduled_work() here as desired because - * of rtnl_lock(), but we do not really care about what would - * be done, except from enable_irq(), so cancel any work - * possibly pending and take care of the matter below. - */ - cancel_work_sync(&phydev->phy_queue); /* If work indeed has been cancelled, disable_irq() will have * been left unbalanced from phy_interrupt() and enable_irq() * has to be called so that other devices on the line work. @@ -784,14 +773,11 @@ int phy_stop_interrupts(struct phy_device *phydev) EXPORT_SYMBOL(phy_stop_interrupts); /** - * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes - * @work: work_struct that describes the work to be done + * phy_change - Called by the phy_interrupt to handle PHY changes + * @phydev: phy_device struct that interrupted */ -void phy_change(struct work_struct *work) +void phy_change(struct phy_device *phydev) { - struct phy_device *phydev = - container_of(work, struct phy_device, phy_queue); - if (phy_interrupt_is_valid(phydev)) { if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev)) @@ -833,6 +819,18 @@ phy_err: } /** + * phy_change_work - Scheduled by the phy_mac_interrupt to handle PHY changes + * @work: work_struct that describes the work to be done + */ +void phy_change_work(struct work_struct *work) +{ + struct phy_device *phydev = + container_of(work, struct phy_device, phy_queue); + + phy_change(phydev); +} + +/** * phy_stop - Bring down the PHY link, and stop checking the status * @phydev: target phy_device struct */ @@ -1116,6 +1114,15 @@ void phy_state_machine(struct work_struct *work) PHY_STATE_TIME * HZ); } +/** + * phy_mac_interrupt - MAC says the link has changed + * @phydev: phy_device struct with changed link + * @new_link: Link is Up/Down. + * + * Description: The MAC layer is able indicate there has been a change + * in the PHY link status. Set the new link status, and trigger the + * state machine, work a work queue. + */ void phy_mac_interrupt(struct phy_device *phydev, int new_link) { phydev->link = new_link; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index e977ba9..ac440a8 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -347,7 +347,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); - INIT_WORK(&dev->phy_queue, phy_change); + INIT_WORK(&dev->phy_queue, phy_change_work); /* Request the appropriate module unconditionally; don't * bother trying to do so only if it isn't already loaded, diff --git a/include/linux/phy.h b/include/linux/phy.h index e25f183..c47378c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -343,7 +343,7 @@ struct phy_c45_device_ids { * giving up on the current attempt at acquiring a link * irq: IRQ number of the PHY's interrupt (-1 if none) * phy_timer: The timer for handling the state machine - * phy_queue: A work_queue for the interrupt + * phy_queue: A work_queue for the phy_mac_interrupt * attached_dev: The attached enet driver's device instance ptr * adjust_link: Callback for the enet controller to respond to * changes in the link state. @@ -802,7 +802,8 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner); int phy_drivers_register(struct phy_driver *new_driver, int n, struct module *owner); void phy_state_machine(struct work_struct *work); -void phy_change(struct work_struct *work); +void phy_change(struct phy_device *phydev); +void phy_change_work(struct work_struct *work); void phy_mac_interrupt(struct phy_device *phydev, int new_link); void phy_start_machine(struct phy_device *phydev); void phy_stop_machine(struct phy_device *phydev); -- cgit v1.1 From ae0219cb441cf5f471feb90961b1e93a91ba6db6 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Oct 2016 19:56:52 +0200 Subject: net: phy: Use phy name when requesting the interrupt Using the fixed name "phy_interrupt" is not very informative in /proc/interrupts when there are a lot of phys, e.g. a device with an Ethernet switch. So when requesting the interrupt, use the name of the phy. Signed-off-by: Andrew Lunn Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index bb673c6..96bf033 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -736,8 +736,7 @@ int phy_start_interrupts(struct phy_device *phydev) atomic_set(&phydev->irq_disable, 0); if (request_threaded_irq(phydev->irq, NULL, phy_interrupt, IRQF_ONESHOT | IRQF_SHARED, - "phy_interrupt", - phydev) < 0) { + phydev_name(phydev), phydev) < 0) { pr_warn("%s: Can't get IRQ %d (PHY)\n", phydev->mdio.bus->name, phydev->irq); phydev->irq = PHY_POLL; -- cgit v1.1 From f283745b3caffcbc69ca71b7b22a9d3c2054284f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 16 Oct 2016 19:56:53 +0200 Subject: arm: vf610: zii devel b: Add support for switch interrupts The Switches use GPIO lines to indicate interrupts from two of the switches. With these interrupts in place, we can make use of the interrupt controllers within the switch to indicate when the internal PHYs generate an interrupt. Use standard PHY properties to do this. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- arch/arm/boot/dts/vf610-zii-dev-rev-b.dts | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts b/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts index 5c1fcab..1552db0 100644 --- a/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts +++ b/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts @@ -88,10 +88,16 @@ switch0: switch0@0 { compatible = "marvell,mv88e6085"; + pinctrl-0 = <&pinctrl_gpio_switch0>; + pinctrl-names = "default"; #address-cells = <1>; #size-cells = <0>; reg = <0>; dsa,member = <0 0>; + interrupt-parent = <&gpio0>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <2>; ports { #address-cells = <1>; @@ -99,16 +105,19 @@ port@0 { reg = <0>; label = "lan0"; + phy-handle = <&switch0phy0>; }; port@1 { reg = <1>; label = "lan1"; + phy-handle = <&switch0phy1>; }; port@2 { reg = <2>; label = "lan2"; + phy-handle = <&switch0phy2>; }; switch0port5: port@5 { @@ -133,6 +142,24 @@ }; }; }; + mdio { + #address-cells = <1>; + #size-cells = <0>; + switch0phy0: switch0phy0@0 { + reg = <0>; + interrupt-parent = <&switch0>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + }; + switch0phy1: switch1phy0@1 { + reg = <1>; + interrupt-parent = <&switch0>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; }; + switch0phy2: switch1phy0@2 { + reg = <2>; + interrupt-parent = <&switch0>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + }; + }; }; }; @@ -143,10 +170,16 @@ switch1: switch1@0 { compatible = "marvell,mv88e6085"; + pinctrl-0 = <&pinctrl_gpio_switch1>; + pinctrl-names = "default"; #address-cells = <1>; #size-cells = <0>; reg = <0>; dsa,member = <0 1>; + interrupt-parent = <&gpio0>; + interrupts = <26 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <2>; ports { #address-cells = <1>; @@ -196,12 +229,18 @@ #size-cells = <0>; switch1phy0: switch1phy0@0 { reg = <0>; + interrupt-parent = <&switch1>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; }; switch1phy1: switch1phy0@1 { reg = <1>; + interrupt-parent = <&switch1>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; }; switch1phy2: switch1phy0@2 { reg = <2>; + interrupt-parent = <&switch1>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; }; }; }; @@ -636,6 +675,18 @@ >; }; + pinctrl_gpio_switch0: pinctrl-gpio-switch0 { + fsl,pins = < + VF610_PAD_PTB5__GPIO_27 0x219d + >; + }; + + pinctrl_gpio_switch1: pinctrl-gpio-switch1 { + fsl,pins = < + VF610_PAD_PTB4__GPIO_26 0x219d + >; + }; + pinctrl_i2c_mux_reset: pinctrl-i2c-mux-reset { fsl,pins = < VF610_PAD_PTE14__GPIO_119 0x31c2 -- cgit v1.1 From ea939008c2d7cade2cbedea5dcbd689e4415f3ed Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 17 Oct 2016 16:30:57 +0200 Subject: fjes: fix format string for trace output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit phys_addr_t may be wider than a pointer and has to be printed using the special %pap format string, as pointed out by this new warning. arch/x86/include/../../../drivers/net/fjes/fjes_trace.h: In function ‘trace_raw_output_fjes_hw_start_debug_req’: arch/x86/include/../../../drivers/net/fjes/fjes_trace.h:212:563: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] Note that this has to pass the address by reference instead of casting it to a different type. Fixes: b6ba737d0b29 ("fjes: ethtool -w and -W support for fjes driver") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/fjes/fjes_trace.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/fjes/fjes_trace.h b/drivers/net/fjes/fjes_trace.h index 22e8f1a..cca01a1 100644 --- a/drivers/net/fjes/fjes_trace.h +++ b/drivers/net/fjes/fjes_trace.h @@ -222,8 +222,8 @@ TRACE_EVENT(fjes_hw_start_debug_req, __entry->mode = req_buf->start_trace.mode; __entry->buffer = req_buf->start_trace.buffer[0]; ), - TP_printk("req_buf=[length=%d, mode=%d, buffer=%p]", - __entry->length, __entry->mode, (void *)__entry->buffer) + TP_printk("req_buf=[length=%d, mode=%d, buffer=%pap]", + __entry->length, __entry->mode, &__entry->buffer) ); TRACE_EVENT(fjes_hw_start_debug, -- cgit v1.1 From ca8eddc2759c949940720b5dc347e93bc981f5f2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 17 Oct 2016 15:31:58 +0000 Subject: net: ethernet: nb8800: fix error return code in nb8800_open() Fix to return error code -ENODEV from the of_phy_connect() error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Acked-by: Mans Rullgard Signed-off-by: David S. Miller --- drivers/net/ethernet/aurora/nb8800.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c index 453dc09..99c4055 100644 --- a/drivers/net/ethernet/aurora/nb8800.c +++ b/drivers/net/ethernet/aurora/nb8800.c @@ -975,8 +975,10 @@ static int nb8800_open(struct net_device *dev) phydev = of_phy_connect(dev, priv->phy_node, nb8800_link_reconfigure, 0, priv->phy_mode); - if (!phydev) + if (!phydev) { + err = -ENODEV; goto err_free_irq; + } nb8800_pause_adv(dev); -- cgit v1.1 From a0e65de71527477b100afe1e9ab2667d70f94d9e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 17 Oct 2016 18:02:22 +0100 Subject: net: report right mtu value in error message Check is for max_mtu but message reports min_mtu. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index 6498cc2..352e981 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6372,7 +6372,7 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) if (dev->max_mtu > 0 && new_mtu > dev->max_mtu) { net_err_ratelimited("%s: Invalid MTU %d requested, hw max %d\n", - dev->name, new_mtu, dev->min_mtu); + dev->name, new_mtu, dev->max_mtu); return -EINVAL; } -- cgit v1.1 From 3f3177bb680fc08300e6f2341f44755ff90a358a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 17 Oct 2016 15:19:56 +0000 Subject: fsl/fman: fix error return code in mac_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: 3933961682a3 ("fsl/fman: Add FMan MAC driver") Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/mac.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c index 8fe6b3e..cc5d07c 100644 --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -879,13 +879,17 @@ static int mac_probe(struct platform_device *_of_dev) priv->fixed_link = kzalloc(sizeof(*priv->fixed_link), GFP_KERNEL); - if (!priv->fixed_link) + if (!priv->fixed_link) { + err = -ENOMEM; goto _return_dev_set_drvdata; + } priv->phy_node = of_node_get(mac_node); phy = of_phy_find_device(priv->phy_node); - if (!phy) + if (!phy) { + err = -EINVAL; goto _return_dev_set_drvdata; + } priv->fixed_link->link = phy->link; priv->fixed_link->speed = phy->speed; -- cgit v1.1 From 320c975f180b19296f0fd6c5bf2144e633aaba5e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 17 Oct 2016 15:25:16 +0000 Subject: cfg80211: fix possible memory leak in cfg80211_iter_combinations() 'limits' is malloced in cfg80211_iter_combinations() and should be freed before leaving from the error handling cases, otherwise it will cause memory leak. Fixes: 0c317a02ca98 ("cfg80211: support virtual interfaces with different beacon intervals") Signed-off-by: Wei Yongjun Signed-off-by: Johannes Berg --- net/wireless/util.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index d2ea1f1..ec17c28 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1676,8 +1676,10 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if (params->beacon_int_gcd) { if (c->beacon_int_min_gcd && - params->beacon_int_gcd < c->beacon_int_min_gcd) + params->beacon_int_gcd < c->beacon_int_min_gcd) { + kfree(limits); return -EINVAL; + } if (!c->beacon_int_min_gcd && params->beacon_int_different) goto cont; -- cgit v1.1 From 393b299d2cb804f09e05aafbdce9e3d3f61438cf Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 11 Mar 2016 13:10:20 +0100 Subject: batman-adv: Less function calls in batadv_is_ap_isolated() after error detection The variables "tt_local_entry" and "tt_global_entry" were eventually checked again despite of a corresponding null pointer test before. * Avoid this double check by reordering a function call sequence and the better selection of jump targets. * Omit the initialisation for these variables at the beginning then. Signed-off-by: Markus Elfring Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/translation-table.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 7f66309..ad1e3bc 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -3835,8 +3835,8 @@ void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv) bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst, unsigned short vid) { - struct batadv_tt_local_entry *tt_local_entry = NULL; - struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_tt_local_entry *tt_local_entry; + struct batadv_tt_global_entry *tt_global_entry; struct batadv_softif_vlan *vlan; bool ret = false; @@ -3845,27 +3845,24 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst, return false; if (!atomic_read(&vlan->ap_isolation)) - goto out; + goto vlan_put; tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid); if (!tt_local_entry) - goto out; + goto vlan_put; tt_global_entry = batadv_tt_global_hash_find(bat_priv, src, vid); if (!tt_global_entry) - goto out; - - if (!_batadv_is_ap_isolated(tt_local_entry, tt_global_entry)) - goto out; + goto local_entry_put; - ret = true; + if (_batadv_is_ap_isolated(tt_local_entry, tt_global_entry)) + ret = true; -out: + batadv_tt_global_entry_put(tt_global_entry); +local_entry_put: + batadv_tt_local_entry_put(tt_local_entry); +vlan_put: batadv_softif_vlan_put(vlan); - if (tt_global_entry) - batadv_tt_global_entry_put(tt_global_entry); - if (tt_local_entry) - batadv_tt_local_entry_put(tt_local_entry); return ret; } -- cgit v1.1 From 403f0727cb35d3ce82166340d792e20c7d5adb30 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 18 Oct 2016 08:44:17 +0200 Subject: vlan: Remove unnecessary comparison of unsigned against 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit args.u.name_type is of type unsigned int and is always >= 0. This fixes the following GCC warning: net/8021q/vlan.c: In function ‘vlan_ioctl_handler’: net/8021q/vlan.c:574:14: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- net/8021q/vlan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 8de138d..f8903c1 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -571,8 +571,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) err = -EPERM; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) break; - if ((args.u.name_type >= 0) && - (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) { + if (args.u.name_type < VLAN_NAME_TYPE_HIGHEST) { struct vlan_net *vn; vn = net_generic(net, vlan_net_id); -- cgit v1.1 From d6081de7e011327af089475bb60593423963526a Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Tue, 18 Oct 2016 16:50:17 +0530 Subject: net: phy: dp83867: Add documentation for optional impedance control Add documention of ti,min-output-impedance and ti,max-output-impedance which can be used to correct MAC impedance mismatch using phy extended registers. Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/ti,dp83867.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/net/ti,dp83867.txt b/Documentation/devicetree/bindings/net/ti,dp83867.txt index 5d21141..85bf945 100644 --- a/Documentation/devicetree/bindings/net/ti,dp83867.txt +++ b/Documentation/devicetree/bindings/net/ti,dp83867.txt @@ -9,6 +9,18 @@ Required properties: - ti,fifo-depth - Transmitt FIFO depth- see dt-bindings/net/ti-dp83867.h for applicable values +Optional property: + - ti,min-output-impedance - MAC Interface Impedance control to set + the programmable output impedance to + minimum value (35 ohms). + - ti,max-output-impedance - MAC Interface Impedance control to set + the programmable output impedance to + maximum value (70 ohms). + +Note: ti,min-output-impedance and ti,max-output-impedance are mutually + exclusive. When both properties are present ti,max-output-impedance + takes precedence. + Default child nodes are standard Ethernet PHY device nodes as described in Documentation/devicetree/bindings/net/phy.txt -- cgit v1.1 From ed838fe937dbcdcf7c0444a5b62edfec6ecd753c Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Tue, 18 Oct 2016 16:50:18 +0530 Subject: net: phy: dp83867: add support for MAC impedance configuration Add support for programmable MAC impedance configuration Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- drivers/net/phy/dp83867.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 91177a4..1b63924 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -33,6 +33,7 @@ /* Extended Registers */ #define DP83867_RGMIICTL 0x0032 #define DP83867_RGMIIDCTL 0x0086 +#define DP83867_IO_MUX_CFG 0x0170 #define DP83867_SW_RESET BIT(15) #define DP83867_SW_RESTART BIT(14) @@ -62,10 +63,17 @@ /* RGMIIDCTL bits */ #define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4 +/* IO_MUX_CFG bits */ +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f + +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f + struct dp83867_private { int rx_id_delay; int tx_id_delay; int fifo_depth; + int io_impedance; }; static int dp83867_ack_interrupt(struct phy_device *phydev) @@ -111,6 +119,14 @@ static int dp83867_of_init(struct phy_device *phydev) if (!of_node) return -ENODEV; + dp83867->io_impedance = -EINVAL; + + /* Optional configuration */ + if (of_property_read_bool(of_node, "ti,max-output-impedance")) + dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX; + else if (of_property_read_bool(of_node, "ti,min-output-impedance")) + dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN; + ret = of_property_read_u32(of_node, "ti,rx-internal-delay", &dp83867->rx_id_delay); if (ret) @@ -184,6 +200,18 @@ static int dp83867_config_init(struct phy_device *phydev) phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, DP83867_DEVADDR, delay); + + if (dp83867->io_impedance >= 0) { + val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG, + DP83867_DEVADDR); + + val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + val |= dp83867->io_impedance & + DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + + phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, + DP83867_DEVADDR, val); + } } return 0; -- cgit v1.1 From b76db38cd8aee9f048c849a8f646695e42c762ba Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Tue, 18 Oct 2016 16:50:19 +0530 Subject: ARM: dts: dra72-evm-revc: add phy impedance settings The default impedance settings of the phy is not the optimal value, due to this the second ethernet is not working. Fix it with correct values which makes the second ethernet port to work. Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- arch/arm/boot/dts/dra72-evm-revc.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/dra72-evm-revc.dts b/arch/arm/boot/dts/dra72-evm-revc.dts index 064b322..5450367 100644 --- a/arch/arm/boot/dts/dra72-evm-revc.dts +++ b/arch/arm/boot/dts/dra72-evm-revc.dts @@ -62,6 +62,7 @@ ti,rx-internal-delay = ; ti,tx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-impedance; }; dp83867_1: ethernet-phy@3 { @@ -69,5 +70,6 @@ ti,rx-internal-delay = ; ti,tx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-imepdance; }; }; -- cgit v1.1 From 1f0655444201ff770efb66078808ecefbc54dfe9 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Tue, 18 Oct 2016 16:50:20 +0530 Subject: ARM: dts: dra72-evm-revc: fix correct phy delay The current delay settings of the phy are not the optimal value, fix it with correct values. Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- arch/arm/boot/dts/dra72-evm-revc.dts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/dra72-evm-revc.dts b/arch/arm/boot/dts/dra72-evm-revc.dts index 5450367..3b23b32 100644 --- a/arch/arm/boot/dts/dra72-evm-revc.dts +++ b/arch/arm/boot/dts/dra72-evm-revc.dts @@ -59,16 +59,16 @@ &davinci_mdio { dp83867_0: ethernet-phy@2 { reg = <2>; - ti,rx-internal-delay = ; - ti,tx-internal-delay = ; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; ti,fifo-depth = ; ti,min-output-impedance; }; dp83867_1: ethernet-phy@3 { reg = <3>; - ti,rx-internal-delay = ; - ti,tx-internal-delay = ; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; ti,fifo-depth = ; ti,min-output-imepdance; }; -- cgit v1.1 From 67bef942801842ce7486a23dd3940e320f217319 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:03 -0400 Subject: ethernet/atheros: use core min/max MTU checking atl2: min_mtu 40, max_mtu 1504 - Remove a few redundant defines that already have equivalents in if_ether.h. atl1: min_mtu 42, max_mtu 10218 atl1e: min_mtu 42, max_mtu 8170 atl1c: min_mtu 42, max_mtu 6122/1500 - GbE hardware gets a max_mtu of 6122, slower hardware gets 1500. alx: min_mtu 34, max_mtu 9256 - Not so sure that minimum MTU number is really what was intended, but that's what the math actually makes it out to be, due to max_frame manipulations and comparison in alx_change_mtu, rather than just comparing new_mtu. (I think 68 was the intended min_mtu value). CC: netdev@vger.kernel.org CC: Jay Cliburn CC: Chris Snook Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/hw.h | 1 - drivers/net/ethernet/atheros/alx/main.c | 10 ++---- drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 41 ++++++++++++++++--------- drivers/net/ethernet/atheros/atl1e/atl1e_main.c | 12 +++----- drivers/net/ethernet/atheros/atlx/atl1.c | 15 ++++----- drivers/net/ethernet/atheros/atlx/atl2.c | 16 +++++----- drivers/net/ethernet/atheros/atlx/atl2.h | 3 -- 7 files changed, 47 insertions(+), 51 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h index 0191477..e42d7e0 100644 --- a/drivers/net/ethernet/atheros/alx/hw.h +++ b/drivers/net/ethernet/atheros/alx/hw.h @@ -351,7 +351,6 @@ struct alx_rrd { #define ALX_MAX_JUMBO_PKT_SIZE (9*1024) #define ALX_MAX_TSO_PKT_SIZE (7*1024) #define ALX_MAX_FRAME_SIZE ALX_MAX_JUMBO_PKT_SIZE -#define ALX_MIN_FRAME_SIZE (ETH_ZLEN + ETH_FCS_LEN + VLAN_HLEN) #define ALX_MAX_RX_QUEUES 8 #define ALX_MAX_TX_QUEUES 4 diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index c0f84b7..eccbacd 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -892,6 +892,9 @@ static int alx_init_sw(struct alx_priv *alx) hw->smb_timer = 400; hw->mtu = alx->dev->mtu; alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu); + /* MTU range: 34 - 9256 */ + alx->dev->min_mtu = 34; + alx->dev->max_mtu = ALX_MAX_FRAME_LEN(ALX_MAX_FRAME_SIZE); alx->tx_ringsz = 256; alx->rx_ringsz = 512; hw->imt = 200; @@ -994,13 +997,6 @@ static int alx_change_mtu(struct net_device *netdev, int mtu) struct alx_priv *alx = netdev_priv(netdev); int max_frame = ALX_MAX_FRAME_LEN(mtu); - if ((max_frame < ALX_MIN_FRAME_SIZE) || - (max_frame > ALX_MAX_FRAME_SIZE)) - return -EINVAL; - - if (netdev->mtu == mtu) - return 0; - netdev->mtu = mtu; alx->hw.mtu = mtu; alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE); diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index a3200ea..773d3b7 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -519,6 +519,26 @@ static int atl1c_set_features(struct net_device *netdev, return 0; } +static void atl1c_set_max_mtu(struct net_device *netdev) +{ + struct atl1c_adapter *adapter = netdev_priv(netdev); + struct atl1c_hw *hw = &adapter->hw; + + switch (hw->nic_type) { + /* These (GbE) devices support jumbo packets, max_mtu 6122 */ + case athr_l1c: + case athr_l1d: + case athr_l1d_2: + netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + break; + /* The 10/100 devices don't support jumbo packets, max_mtu 1500 */ + default: + netdev->max_mtu = ETH_DATA_LEN; + break; + } +} + /** * atl1c_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure @@ -529,22 +549,9 @@ static int atl1c_set_features(struct net_device *netdev, static int atl1c_change_mtu(struct net_device *netdev, int new_mtu) { struct atl1c_adapter *adapter = netdev_priv(netdev); - struct atl1c_hw *hw = &adapter->hw; - int old_mtu = netdev->mtu; - int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; - - /* Fast Ethernet controller doesn't support jumbo packet */ - if (((hw->nic_type == athr_l2c || - hw->nic_type == athr_l2c_b || - hw->nic_type == athr_l2c_b2) && new_mtu > ETH_DATA_LEN) || - max_frame < ETH_ZLEN + ETH_FCS_LEN || - max_frame > MAX_JUMBO_FRAME_SIZE) { - if (netif_msg_link(adapter)) - dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); - return -EINVAL; - } + /* set MTU */ - if (old_mtu != new_mtu && netif_running(netdev)) { + if (netif_running(netdev)) { while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); netdev->mtu = new_mtu; @@ -2511,6 +2518,7 @@ static int atl1c_init_netdev(struct net_device *netdev, struct pci_dev *pdev) netdev->netdev_ops = &atl1c_netdev_ops; netdev->watchdog_timeo = AT_TX_WATCHDOG; + netdev->min_mtu = ETH_ZLEN - (ETH_HLEN + VLAN_HLEN); atl1c_set_ethtool_ops(netdev); /* TODO: add when ready */ @@ -2613,6 +2621,9 @@ static int atl1c_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_err(&pdev->dev, "net device private data init failed\n"); goto err_sw_init; } + /* set max MTU */ + atl1c_set_max_mtu(netdev); + atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE); /* Init GPHY as early as possible due to power saving issue */ diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 974713b..e96091b6 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -439,16 +439,10 @@ static int atl1e_set_features(struct net_device *netdev, static int atl1e_change_mtu(struct net_device *netdev, int new_mtu) { struct atl1e_adapter *adapter = netdev_priv(netdev); - int old_mtu = netdev->mtu; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; - if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || - (max_frame > MAX_JUMBO_FRAME_SIZE)) { - netdev_warn(adapter->netdev, "invalid MTU setting\n"); - return -EINVAL; - } /* set MTU */ - if (old_mtu != new_mtu && netif_running(netdev)) { + if (netif_running(netdev)) { while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); netdev->mtu = new_mtu; @@ -2272,6 +2266,10 @@ static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev) netdev->netdev_ops = &atl1e_netdev_ops; netdev->watchdog_timeo = AT_TX_WATCHDOG; + /* MTU range: 42 - 8170 */ + netdev->min_mtu = ETH_ZLEN - (ETH_HLEN + VLAN_HLEN); + netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); atl1e_set_ethtool_ops(netdev); netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index 529bca7..9aede18 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -2701,23 +2701,15 @@ static void atl1_reset_dev_task(struct work_struct *work) static int atl1_change_mtu(struct net_device *netdev, int new_mtu) { struct atl1_adapter *adapter = netdev_priv(netdev); - int old_mtu = netdev->mtu; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; - if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || - (max_frame > MAX_JUMBO_FRAME_SIZE)) { - if (netif_msg_link(adapter)) - dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); - return -EINVAL; - } - adapter->hw.max_frame_size = max_frame; adapter->hw.tx_jumbo_task_th = (max_frame + 7) >> 3; adapter->rx_buffer_len = (max_frame + 7) & ~7; adapter->hw.rx_jumbo_th = adapter->rx_buffer_len / 8; netdev->mtu = new_mtu; - if ((old_mtu != new_mtu) && netif_running(netdev)) { + if (netif_running(netdev)) { atl1_down(adapter); atl1_up(adapter); } @@ -3031,6 +3023,11 @@ static int atl1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* is this valid? see atl1_setup_mac_ctrl() */ netdev->features |= NETIF_F_RXCSUM; + /* MTU range: 42 - 10218 */ + netdev->min_mtu = ETH_ZLEN - (ETH_HLEN + VLAN_HLEN); + netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + /* * patch for some L1 of old version, * the final version of L1 may not need these diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 2ff4658..6911394 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -253,7 +253,7 @@ static int atl2_configure(struct atl2_adapter *adapter) /* set MTU */ ATL2_WRITE_REG(hw, REG_MTU, adapter->netdev->mtu + - ENET_HEADER_SIZE + VLAN_SIZE + ETHERNET_FCS_SIZE); + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN); /* 1590 */ ATL2_WRITE_REG(hw, REG_TX_CUT_THRESH, 0x177); @@ -925,15 +925,11 @@ static int atl2_change_mtu(struct net_device *netdev, int new_mtu) struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; - if ((new_mtu < 40) || (new_mtu > (ETH_DATA_LEN + VLAN_SIZE))) - return -EINVAL; - /* set MTU */ - if (hw->max_frame_size != new_mtu) { - netdev->mtu = new_mtu; - ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ENET_HEADER_SIZE + - VLAN_SIZE + ETHERNET_FCS_SIZE); - } + netdev->mtu = new_mtu; + hw->max_frame_size = new_mtu; + ATL2_WRITE_REG(hw, REG_MTU, new_mtu + ETH_HLEN + + VLAN_HLEN + ETH_FCS_LEN); return 0; } @@ -1398,6 +1394,8 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &atl2_netdev_ops; netdev->ethtool_ops = &atl2_ethtool_ops; netdev->watchdog_timeo = 5 * HZ; + netdev->min_mtu = 40; + netdev->max_mtu = ETH_DATA_LEN + VLAN_HLEN; strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); netdev->mem_start = mmio_start; diff --git a/drivers/net/ethernet/atheros/atlx/atl2.h b/drivers/net/ethernet/atheros/atlx/atl2.h index 2f27d4c..c64a6bd 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.h +++ b/drivers/net/ethernet/atheros/atlx/atl2.h @@ -228,12 +228,9 @@ static void atl2_force_ps(struct atl2_hw *hw); #define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x000F /* Everything */ /* The size (in bytes) of a ethernet packet */ -#define ENET_HEADER_SIZE 14 #define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* with FCS */ #define MINIMUM_ETHERNET_FRAME_SIZE 64 /* with FCS */ -#define ETHERNET_FCS_SIZE 4 #define MAX_JUMBO_FRAME_SIZE 0x2000 -#define VLAN_SIZE 4 struct tx_pkt_header { unsigned pkt_size:11; -- cgit v1.1 From e1c6dccaf3af291488fbad155d7ee6bc29db262a Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:04 -0400 Subject: ethernet/broadcom: use core min/max MTU checking tg3: min_mtu 60, max_mtu 9000/1500 bnxt: min_mtu 60, max_mtu 9000 bnx2x: min_mtu 46, max_mtu 9600 - Fix up ETH_OVREHEAD -> ETH_OVERHEAD while we're in here, remove duplicated defines from bnx2x_link.c. bnx2: min_mtu 46, max_mtu 9000 - Use more standard ETH_* defines while we're at it. bcm63xx_enet: min_mtu 46, max_mtu 2028 - compute_hw_mtu was made largely pointless, and thus merged back into bcm_enet_change_mtu. b44: min_mtu 60, max_mtu 1500 CC: netdev@vger.kernel.org CC: Michael Chan CC: Sony Chacko CC: Ariel Elior CC: Dept-HSGLinuxNICDev@qlogic.com CC: Siva Reddy Kallam CC: Prashant Sreedharan Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/b44.c | 9 +++--- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 35 ++++++++---------------- drivers/net/ethernet/broadcom/bnx2.c | 16 +++++------ drivers/net/ethernet/broadcom/bnx2.h | 6 ++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 6 ++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 8 +----- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 22 ++++++--------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 7 +++-- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 7 +++-- drivers/net/ethernet/broadcom/tg3.c | 9 +++--- 10 files changed, 51 insertions(+), 74 deletions(-) diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 17aa33c..1df3048 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -59,8 +59,8 @@ #define B44_TX_TIMEOUT (5 * HZ) /* hardware minimum and maximum for a single frame's data payload */ -#define B44_MIN_MTU 60 -#define B44_MAX_MTU 1500 +#define B44_MIN_MTU ETH_ZLEN +#define B44_MAX_MTU ETH_DATA_LEN #define B44_RX_RING_SIZE 512 #define B44_DEF_RX_RING_PENDING 200 @@ -1064,9 +1064,6 @@ static int b44_change_mtu(struct net_device *dev, int new_mtu) { struct b44 *bp = netdev_priv(dev); - if (new_mtu < B44_MIN_MTU || new_mtu > B44_MAX_MTU) - return -EINVAL; - if (!netif_running(dev)) { /* We'll just catch it later when the * device is up'd. @@ -2377,6 +2374,8 @@ static int b44_init_one(struct ssb_device *sdev, dev->netdev_ops = &b44_netdev_ops; netif_napi_add(dev, &bp->napi, b44_poll, 64); dev->watchdog_timeo = B44_TX_TIMEOUT; + dev->min_mtu = B44_MIN_MTU; + dev->max_mtu = B44_MAX_MTU; dev->irq = sdev->irq; dev->ethtool_ops = &b44_ethtool_ops; diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index ae364c7..7e513ca 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1622,20 +1622,19 @@ static int bcm_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } /* - * calculate actual hardware mtu + * adjust mtu, can't be called while device is running */ -static int compute_hw_mtu(struct bcm_enet_priv *priv, int mtu) +static int bcm_enet_change_mtu(struct net_device *dev, int new_mtu) { - int actual_mtu; + struct bcm_enet_priv *priv = netdev_priv(dev); + int actual_mtu = new_mtu; - actual_mtu = mtu; + if (netif_running(dev)) + return -EBUSY; /* add ethernet header + vlan tag size */ actual_mtu += VLAN_ETH_HLEN; - if (actual_mtu < 64 || actual_mtu > BCMENET_MAX_MTU) - return -EINVAL; - /* * setup maximum size before we get overflow mark in * descriptor, note that this will not prevent reception of @@ -1650,22 +1649,7 @@ static int compute_hw_mtu(struct bcm_enet_priv *priv, int mtu) */ priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN, priv->dma_maxburst * 4); - return 0; -} -/* - * adjust mtu, can't be called while device is running - */ -static int bcm_enet_change_mtu(struct net_device *dev, int new_mtu) -{ - int ret; - - if (netif_running(dev)) - return -EBUSY; - - ret = compute_hw_mtu(netdev_priv(dev), new_mtu); - if (ret) - return ret; dev->mtu = new_mtu; return 0; } @@ -1755,7 +1739,7 @@ static int bcm_enet_probe(struct platform_device *pdev) priv->enet_is_sw = false; priv->dma_maxburst = BCMENET_DMA_MAXBURST; - ret = compute_hw_mtu(priv, dev->mtu); + ret = bcm_enet_change_mtu(dev, dev->mtu); if (ret) goto out; @@ -1888,6 +1872,9 @@ static int bcm_enet_probe(struct platform_device *pdev) netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16); dev->ethtool_ops = &bcm_enet_ethtool_ops; + /* MTU range: 46 - 2028 */ + dev->min_mtu = ETH_ZLEN - ETH_HLEN; + dev->max_mtu = BCMENET_MAX_MTU - VLAN_ETH_HLEN; SET_NETDEV_DEV(dev, &pdev->dev); ret = register_netdev(dev); @@ -2742,7 +2729,7 @@ static int bcm_enetsw_probe(struct platform_device *pdev) priv->dma_chan_width = pd->dma_chan_width; } - ret = compute_hw_mtu(priv, dev->mtu); + ret = bcm_enet_change_mtu(dev, dev->mtu); if (ret) goto out; diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 27f11a5..2a5df3f 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -2298,7 +2298,7 @@ bnx2_init_5706s_phy(struct bnx2 *bp, int reset_phy) if (BNX2_CHIP(bp) == BNX2_CHIP_5706) BNX2_WR(bp, BNX2_MISC_GP_HW_CTL0, 0x300); - if (bp->dev->mtu > 1500) { + if (bp->dev->mtu > ETH_DATA_LEN) { u32 val; /* Set extended packet length bit */ @@ -2352,7 +2352,7 @@ bnx2_init_copper_phy(struct bnx2 *bp, int reset_phy) bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val); } - if (bp->dev->mtu > 1500) { + if (bp->dev->mtu > ETH_DATA_LEN) { /* Set extended packet length bit */ bnx2_write_phy(bp, 0x18, 0x7); bnx2_read_phy(bp, 0x18, &val); @@ -4985,12 +4985,12 @@ bnx2_init_chip(struct bnx2 *bp) /* Program the MTU. Also include 4 bytes for CRC32. */ mtu = bp->dev->mtu; val = mtu + ETH_HLEN + ETH_FCS_LEN; - if (val > (MAX_ETHERNET_PACKET_SIZE + 4)) + if (val > (MAX_ETHERNET_PACKET_SIZE + ETH_HLEN + 4)) val |= BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA; BNX2_WR(bp, BNX2_EMAC_RX_MTU_SIZE, val); - if (mtu < 1500) - mtu = 1500; + if (mtu < ETH_DATA_LEN) + mtu = ETH_DATA_LEN; bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG, BNX2_RBUF_CONFIG_VAL(mtu)); bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG2, BNX2_RBUF_CONFIG2_VAL(mtu)); @@ -7896,10 +7896,6 @@ bnx2_change_mtu(struct net_device *dev, int new_mtu) { struct bnx2 *bp = netdev_priv(dev); - if (((new_mtu + ETH_HLEN) > MAX_ETHERNET_JUMBO_PACKET_SIZE) || - ((new_mtu + ETH_HLEN) < MIN_ETHERNET_PACKET_SIZE)) - return -EINVAL; - dev->mtu = new_mtu; return bnx2_change_ring_size(bp, bp->rx_ring_size, bp->tx_ring_size, false); @@ -8589,6 +8585,8 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; dev->features |= dev->hw_features; dev->priv_flags |= IFF_UNICAST_FLT; + dev->min_mtu = MIN_ETHERNET_PACKET_SIZE; + dev->max_mtu = MAX_ETHERNET_JUMBO_PACKET_SIZE; if (!(bp->flags & BNX2_FLAG_CAN_KEEP_VLAN)) dev->hw_features &= ~NETIF_F_HW_VLAN_CTAG_RX; diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index 380234d..a09ec47 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -6530,9 +6530,9 @@ struct l2_fhdr { #define MII_BNX2_AER_AER_AN_MMD 0x3800 #define MII_BNX2_BLK_ADDR_COMBO_IEEEB0 0xffe0 -#define MIN_ETHERNET_PACKET_SIZE 60 -#define MAX_ETHERNET_PACKET_SIZE 1514 -#define MAX_ETHERNET_JUMBO_PACKET_SIZE 9014 +#define MIN_ETHERNET_PACKET_SIZE (ETH_ZLEN - ETH_HLEN) +#define MAX_ETHERNET_PACKET_SIZE ETH_DATA_LEN +#define MAX_ETHERNET_JUMBO_PACKET_SIZE 9000 #define BNX2_RX_COPY_THRESH 128 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 7dd7490..0a23034 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1396,9 +1396,9 @@ struct bnx2x { int tx_ring_size; /* L2 header size + 2*VLANs (8 bytes) + LLC SNAP (8 bytes) */ -#define ETH_OVREHEAD (ETH_HLEN + 8 + 8) -#define ETH_MIN_PACKET_SIZE 60 -#define ETH_MAX_PACKET_SIZE 1500 +#define ETH_OVERHEAD (ETH_HLEN + 8 + 8) +#define ETH_MIN_PACKET_SIZE (ETH_ZLEN - ETH_HLEN) +#define ETH_MAX_PACKET_SIZE ETH_DATA_LEN #define ETH_MAX_JUMBO_PACKET_SIZE 9600 /* TCP with Timestamp Option (32) + IPv6 (40) */ #define ETH_MAX_TPA_HEADER_SIZE 72 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 0a9108c..ed42c10 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2023,7 +2023,7 @@ static void bnx2x_set_rx_buf_size(struct bnx2x *bp) mtu = bp->dev->mtu; fp->rx_buf_size = BNX2X_FW_RX_ALIGN_START + IP_HEADER_ALIGNMENT_PADDING + - ETH_OVREHEAD + + ETH_OVERHEAD + mtu + BNX2X_FW_RX_ALIGN_END; /* Note : rx_buf_size doesn't take into account NET_SKB_PAD */ @@ -4855,12 +4855,6 @@ int bnx2x_change_mtu(struct net_device *dev, int new_mtu) return -EAGAIN; } - if ((new_mtu > ETH_MAX_JUMBO_PACKET_SIZE) || - ((new_mtu + ETH_HLEN) < ETH_MIN_PACKET_SIZE)) { - BNX2X_ERR("Can't support requested MTU size\n"); - return -EINVAL; - } - /* This does not race with packet allocation * because the actual alloc size is * only updated as part of load diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 1fb8010..05356ef 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -34,12 +34,6 @@ typedef int (*read_sfp_module_eeprom_func_p)(struct bnx2x_phy *phy, u8 dev_addr, u16 addr, u8 byte_cnt, u8 *o_buf, u8); /********************************************************/ -#define ETH_HLEN 14 -/* L2 header size + 2*VLANs (8 bytes) + LLC SNAP (8 bytes) */ -#define ETH_OVREHEAD (ETH_HLEN + 8 + 8) -#define ETH_MIN_PACKET_SIZE 60 -#define ETH_MAX_PACKET_SIZE 1500 -#define ETH_MAX_JUMBO_PACKET_SIZE 9600 #define MDIO_ACCESS_TIMEOUT 1000 #define WC_LANE_MAX 4 #define I2C_SWITCH_WIDTH 2 @@ -1917,7 +1911,7 @@ static int bnx2x_emac_enable(struct link_params *params, /* Enable emac for jumbo packets */ EMAC_WR(bp, EMAC_REG_EMAC_RX_MTU_SIZE, (EMAC_RX_MTU_SIZE_JUMBO_ENA | - (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD))); + (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVERHEAD))); /* Strip CRC */ REG_WR(bp, NIG_REG_NIG_INGRESS_EMAC0_NO_CRC + port*4, 0x1); @@ -2314,19 +2308,19 @@ static int bnx2x_bmac1_enable(struct link_params *params, REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); /* Set rx mtu */ - wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; + wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVERHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_MAX_SIZE, wb_data, 2); bnx2x_update_pfc_bmac1(params, vars); /* Set tx mtu */ - wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; + wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVERHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_MAX_SIZE, wb_data, 2); /* Set cnt max size */ - wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; + wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVERHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_CNT_MAX_SIZE, wb_data, 2); @@ -2384,18 +2378,18 @@ static int bnx2x_bmac2_enable(struct link_params *params, udelay(30); /* Set RX MTU */ - wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; + wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVERHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_RX_MAX_SIZE, wb_data, 2); udelay(30); /* Set TX MTU */ - wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; + wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVERHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_TX_MAX_SIZE, wb_data, 2); udelay(30); /* Set cnt max size */ - wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD - 2; + wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVERHEAD - 2; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_CNT_MAX_SIZE, wb_data, 2); udelay(30); @@ -2516,7 +2510,7 @@ static int bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl, } else { u32 thresh = (ETH_MAX_JUMBO_PACKET_SIZE + - ETH_OVREHEAD)/16; + ETH_OVERHEAD)/16; REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0); /* Update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, thresh); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 20fe6a8..67b6180 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12080,8 +12080,7 @@ static int bnx2x_get_hwinfo(struct bnx2x *bp) mtu_size, mtu); /* if valid: update device mtu */ - if (((mtu_size + ETH_HLEN) >= - ETH_MIN_PACKET_SIZE) && + if ((mtu_size >= ETH_MIN_PACKET_SIZE) && (mtu_size <= ETH_MAX_JUMBO_PACKET_SIZE)) bp->dev->mtu = mtu_size; @@ -13315,6 +13314,10 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, dev->dcbnl_ops = &bnx2x_dcbnl_ops; #endif + /* MTU range, 46 - 9600 */ + dev->min_mtu = ETH_MIN_PACKET_SIZE; + dev->max_mtu = ETH_MAX_JUMBO_PACKET_SIZE; + /* get_port_hwinfo() will set prtad and mmds properly */ bp->mdio.prtad = MDIO_PRTAD_NONE; bp->mdio.mmds = 0; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index a9f9f37..a042da1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6290,9 +6290,6 @@ static int bnxt_change_mtu(struct net_device *dev, int new_mtu) { struct bnxt *bp = netdev_priv(dev); - if (new_mtu < 60 || new_mtu > 9500) - return -EINVAL; - if (netif_running(dev)) bnxt_close_nic(bp, false, false); @@ -6870,6 +6867,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->features |= dev->hw_features | NETIF_F_HIGHDMA; dev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 60 - 9500 */ + dev->min_mtu = ETH_ZLEN; + dev->max_mtu = 9500; + #ifdef CONFIG_BNXT_SRIOV init_waitqueue_head(&bp->sriov_cfg_wait); #endif diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index a927a73..185e9e0 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -124,7 +124,7 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits) #define TG3_TX_TIMEOUT (5 * HZ) /* hardware minimum and maximum for a single frame's data payload */ -#define TG3_MIN_MTU 60 +#define TG3_MIN_MTU ETH_ZLEN #define TG3_MAX_MTU(tp) \ (tg3_flag(tp, JUMBO_CAPABLE) ? 9000 : 1500) @@ -14199,9 +14199,6 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu) int err; bool reset_phy = false; - if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU(tp)) - return -EINVAL; - if (!netif_running(dev)) { /* We'll just catch it later when the * device is up'd. @@ -17799,6 +17796,10 @@ static int tg3_init_one(struct pci_dev *pdev, dev->hw_features |= features; dev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 60 - 9000 or 1500, depending on hardware */ + dev->min_mtu = TG3_MIN_MTU; + dev->max_mtu = TG3_MAX_MTU(tp); + if (tg3_chip_rev_id(tp) == CHIPREV_ID_5705_A1 && !tg3_flag(tp, TSO_CAPABLE) && !(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH)) { -- cgit v1.1 From 91c527a55664ddf4bee26673a35f91748dae4142 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:05 -0400 Subject: ethernet/intel: use core min/max MTU checking e100: min_mtu 68, max_mtu 1500 - remove e100_change_mtu entirely, is identical to old eth_change_mtu, and no longer serves a purpose. No need to set min_mtu or max_mtu explicitly, as ether_setup() will already set them to 68 and 1500. e1000: min_mtu 46, max_mtu 16110 e1000e: min_mtu 68, max_mtu varies based on adapter fm10k: min_mtu 68, max_mtu 15342 - remove fm10k_change_mtu entirely, does nothing now i40e: min_mtu 68, max_mtu 9706 i40evf: min_mtu 68, max_mtu 9706 igb: min_mtu 68, max_mtu 9216 - There are two different "max" frame sizes claimed and both checked in the driver, the larger value wasn't relevant though, so I've set max_mtu to the smaller of the two values here to retain identical behavior. igbvf: min_mtu 68, max_mtu 9216 - Same issue as igb duplicated ixgb: min_mtu 68, max_mtu 16114 - Also remove pointless old == new check, as that's done in dev_set_mtu ixgbe: min_mtu 68, max_mtu 9710 ixgbevf: min_mtu 68, max_mtu dependent on hardware/firmware - Some hw can only handle up to max_mtu 1504 on a vf, others 9710 CC: netdev@vger.kernel.org CC: intel-wired-lan@lists.osuosl.org CC: Jeff Kirsher Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/e100.c | 9 ------- drivers/net/ethernet/intel/e1000/e1000_main.c | 12 ++++----- drivers/net/ethernet/intel/e1000e/netdev.c | 14 +++++----- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 15 +++-------- drivers/net/ethernet/intel/i40e/i40e_main.c | 10 +++---- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 8 +++--- drivers/net/ethernet/intel/igb/e1000_defines.h | 3 ++- drivers/net/ethernet/intel/igb/igb_main.c | 15 +++-------- drivers/net/ethernet/intel/igbvf/defines.h | 3 ++- drivers/net/ethernet/intel/igbvf/netdev.c | 14 +++------- drivers/net/ethernet/intel/ixgb/ixgb_main.c | 16 +++-------- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 11 ++++---- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 33 ++++++++++++----------- 13 files changed, 62 insertions(+), 101 deletions(-) diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index 068789e..25c6dfd 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -2286,14 +2286,6 @@ static int e100_set_mac_address(struct net_device *netdev, void *p) return 0; } -static int e100_change_mtu(struct net_device *netdev, int new_mtu) -{ - if (new_mtu < ETH_ZLEN || new_mtu > ETH_DATA_LEN) - return -EINVAL; - netdev->mtu = new_mtu; - return 0; -} - static int e100_asf(struct nic *nic) { /* ASF can be enabled from eeprom */ @@ -2834,7 +2826,6 @@ static const struct net_device_ops e100_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = e100_set_multicast_list, .ndo_set_mac_address = e100_set_mac_address, - .ndo_change_mtu = e100_change_mtu, .ndo_do_ioctl = e100_do_ioctl, .ndo_tx_timeout = e100_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index f42129d..33076fa 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -1085,6 +1085,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->subsystem_vendor_id != PCI_VENDOR_ID_VMWARE) netdev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 46 - 16110 */ + netdev->min_mtu = ETH_ZLEN - ETH_HLEN; + netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); + adapter->en_mng_pt = e1000_enable_mng_pass_thru(hw); /* initialize eeprom parameters */ @@ -3549,13 +3553,7 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - int max_frame = new_mtu + ENET_HEADER_SIZE + ETHERNET_FCS_SIZE; - - if ((max_frame < MINIMUM_ETHERNET_FRAME_SIZE) || - (max_frame > MAX_JUMBO_FRAME_SIZE)) { - e_err(probe, "Invalid MTU setting\n"); - return -EINVAL; - } + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; /* Adapter-specific max frame size limits. */ switch (hw->mac_type) { diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 7017281..8759d92 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -5974,19 +5974,12 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) int max_frame = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; /* Jumbo frame support */ - if ((max_frame > (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)) && + if ((new_mtu > ETH_DATA_LEN) && !(adapter->flags & FLAG_HAS_JUMBO_FRAMES)) { e_err("Jumbo Frames not supported.\n"); return -EINVAL; } - /* Supported frame sizes */ - if ((new_mtu < (VLAN_ETH_ZLEN + ETH_FCS_LEN)) || - (max_frame > adapter->max_hw_frame_size)) { - e_err("Unsupported MTU setting\n"); - return -EINVAL; - } - /* Jumbo frame workaround on 82579 and newer requires CRC be stripped */ if ((adapter->hw.mac.type >= e1000_pch2lan) && !(adapter->flags2 & FLAG2_CRC_STRIPPING) && @@ -7187,6 +7180,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->vlan_features |= NETIF_F_HIGHDMA; } + /* MTU range: 68 - max_hw_frame_size */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = adapter->max_hw_frame_size - + (VLAN_ETH_HLEN + ETH_FCS_LEN); + if (e1000e_enable_mng_pass_thru(&adapter->hw)) adapter->flags |= FLAG_MNG_PT_ENABLED; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 0562938..bc5ef6e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -706,16 +706,6 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) return err; } -static int fm10k_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < 68 || new_mtu > FM10K_MAX_JUMBO_FRAME_SIZE) - return -EINVAL; - - dev->mtu = new_mtu; - - return 0; -} - /** * fm10k_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure @@ -1405,7 +1395,6 @@ static const struct net_device_ops fm10k_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_start_xmit = fm10k_xmit_frame, .ndo_set_mac_address = fm10k_set_mac, - .ndo_change_mtu = fm10k_change_mtu, .ndo_tx_timeout = fm10k_tx_timeout, .ndo_vlan_rx_add_vid = fm10k_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = fm10k_vlan_rx_kill_vid, @@ -1490,5 +1479,9 @@ struct net_device *fm10k_alloc_netdev(const struct fm10k_info *info) dev->hw_features |= hw_features; + /* MTU range: 68 - 15342 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = FM10K_MAX_JUMBO_FRAME_SIZE; + return dev; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index ac1faee..83edbe8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2239,13 +2239,8 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) static int i40e_change_mtu(struct net_device *netdev, int new_mtu) { struct i40e_netdev_priv *np = netdev_priv(netdev); - int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; struct i40e_vsi *vsi = np->vsi; - /* MTU < 68 is an error and causes problems on some kernels */ - if ((new_mtu < 68) || (max_frame > I40E_MAX_RXBUFFER)) - return -EINVAL; - netdev_info(netdev, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); netdev->mtu = new_mtu; @@ -9220,6 +9215,11 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) i40e_fcoe_config_netdev(netdev, vsi); #endif + /* MTU range: 68 - 9706 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = I40E_MAX_RXBUFFER - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + return 0; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 1437281..777eb29 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2133,10 +2133,6 @@ static struct net_device_stats *i40evf_get_stats(struct net_device *netdev) static int i40evf_change_mtu(struct net_device *netdev, int new_mtu) { struct i40evf_adapter *adapter = netdev_priv(netdev); - int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - - if ((new_mtu < 68) || (max_frame > I40E_MAX_RXBUFFER)) - return -EINVAL; netdev->mtu = new_mtu; adapter->flags |= I40EVF_FLAG_RESET_NEEDED; @@ -2424,6 +2420,10 @@ static void i40evf_init_task(struct work_struct *work) i40evf_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; + /* MTU range: 68 - 9710 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = I40E_MAX_RXBUFFER - (ETH_HLEN + ETH_FCS_LEN); + if (!is_valid_ether_addr(adapter->hw.mac.addr)) { dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n", adapter->hw.mac.addr); diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index 2688180..8aee314 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -357,7 +357,8 @@ #define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ /* As per the EAS the maximum supported size is 9.5KB (9728 bytes) */ -#define MAX_JUMBO_FRAME_SIZE 0x2600 +#define MAX_JUMBO_FRAME_SIZE 0x2600 +#define MAX_STD_JUMBO_FRAME_SIZE 9216 /* PBA constants */ #define E1000_PBA_34K 0x0022 diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index edc9a6a..4feca69 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2468,6 +2468,10 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 68 - 9216 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; + adapter->en_mng_pt = igb_enable_mng_pass_thru(hw); /* before reading the NVM, reset the controller to put the device in a @@ -5408,17 +5412,6 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu) struct pci_dev *pdev = adapter->pdev; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; - if ((new_mtu < 68) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { - dev_err(&pdev->dev, "Invalid MTU setting\n"); - return -EINVAL; - } - -#define MAX_STD_JUMBO_FRAME_SIZE 9238 - if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) { - dev_err(&pdev->dev, "MTU > 9216 not supported.\n"); - return -EINVAL; - } - /* adjust max frame to be at least the size of a standard frame */ if (max_frame < (ETH_FRAME_LEN + ETH_FCS_LEN)) max_frame = ETH_FRAME_LEN + ETH_FCS_LEN; diff --git a/drivers/net/ethernet/intel/igbvf/defines.h b/drivers/net/ethernet/intel/igbvf/defines.h index ee1ef08..f1789d1 100644 --- a/drivers/net/ethernet/intel/igbvf/defines.h +++ b/drivers/net/ethernet/intel/igbvf/defines.h @@ -85,7 +85,8 @@ #define E1000_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ #define E1000_TXD_STAT_DD 0x00000001 /* Desc Done */ -#define MAX_JUMBO_FRAME_SIZE 0x3F00 +#define MAX_JUMBO_FRAME_SIZE 0x3F00 +#define MAX_STD_JUMBO_FRAME_SIZE 9216 /* 802.1q VLAN Packet Size */ #define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */ diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 12bb877..810fcf7 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2356,16 +2356,6 @@ static int igbvf_change_mtu(struct net_device *netdev, int new_mtu) struct igbvf_adapter *adapter = netdev_priv(netdev); int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - if (new_mtu < 68 || new_mtu > INT_MAX - ETH_HLEN - ETH_FCS_LEN || - max_frame > MAX_JUMBO_FRAME_SIZE) - return -EINVAL; - -#define MAX_STD_JUMBO_FRAME_SIZE 9234 - if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) { - dev_err(&adapter->pdev->dev, "MTU > 9216 not supported.\n"); - return -EINVAL; - } - while (test_and_set_bit(__IGBVF_RESETTING, &adapter->state)) usleep_range(1000, 2000); /* igbvf_down has a dependency on max_frame_size */ @@ -2786,6 +2776,10 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; + /* MTU range: 68 - 9216 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; + /*reset the controller to put the device in a known good state */ err = hw->mac.ops.reset_hw(hw); if (err) { diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index 31f9145..5826b1d 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -487,6 +487,10 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->vlan_features |= NETIF_F_HIGHDMA; } + /* MTU range: 68 - 16114 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = IXGB_MAX_JUMBO_FRAME_SIZE - ETH_HLEN; + /* make sure the EEPROM is good */ if (!ixgb_validate_eeprom_checksum(&adapter->hw)) { @@ -1619,18 +1623,6 @@ ixgb_change_mtu(struct net_device *netdev, int new_mtu) { struct ixgb_adapter *adapter = netdev_priv(netdev); int max_frame = new_mtu + ENET_HEADER_SIZE + ENET_FCS_LENGTH; - int old_max_frame = netdev->mtu + ENET_HEADER_SIZE + ENET_FCS_LENGTH; - - /* MTU < 68 is an error for IPv4 traffic, just don't allow it */ - if ((new_mtu < 68) || - (max_frame > IXGB_MAX_JUMBO_FRAME_SIZE + ENET_FCS_LENGTH)) { - netif_err(adapter, probe, adapter->netdev, - "Invalid MTU setting %d\n", new_mtu); - return -EINVAL; - } - - if (old_max_frame == max_frame) - return 0; if (netif_running(netdev)) ixgb_down(adapter, true); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 784b0b9..7dfde20 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -6049,11 +6049,6 @@ static void ixgbe_free_all_rx_resources(struct ixgbe_adapter *adapter) static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - - /* MTU < 68 is an error and causes problems on some kernels */ - if ((new_mtu < 68) || (max_frame > IXGBE_MAX_JUMBO_FRAME_SIZE)) - return -EINVAL; /* * For 82599EB we cannot allow legacy VFs to enable their receive @@ -6062,7 +6057,7 @@ static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu) */ if ((adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) && (adapter->hw.mac.type == ixgbe_mac_82599EB) && - (max_frame > (ETH_FRAME_LEN + ETH_FCS_LEN))) + (new_mtu > ETH_DATA_LEN)) e_warn(probe, "Setting MTU > 1500 will disable legacy VFs\n"); e_info(probe, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); @@ -9608,6 +9603,10 @@ skip_sriov: netdev->priv_flags |= IFF_UNICAST_FLT; netdev->priv_flags |= IFF_SUPP_NOFCS; + /* MTU range: 68 - 9710 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = IXGBE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); + #ifdef CONFIG_IXGBE_DCB if (adapter->flags & IXGBE_FLAG_DCB_CAPABLE) netdev->dcbnl_ops = &dcbnl_ops; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 7eaac32..d2775f0 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -3742,24 +3742,8 @@ static int ixgbevf_change_mtu(struct net_device *netdev, int new_mtu) struct ixgbevf_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - int max_possible_frame = MAXIMUM_ETHERNET_VLAN_SIZE; int ret; - switch (adapter->hw.api_version) { - case ixgbe_mbox_api_11: - case ixgbe_mbox_api_12: - max_possible_frame = IXGBE_MAX_JUMBO_FRAME_SIZE; - break; - default: - if (adapter->hw.mac.type != ixgbe_mac_82599_vf) - max_possible_frame = IXGBE_MAX_JUMBO_FRAME_SIZE; - break; - } - - /* MTU < 68 is an error and causes problems on some kernels */ - if ((new_mtu < 68) || (max_frame > max_possible_frame)) - return -EINVAL; - spin_lock_bh(&adapter->mbx_lock); /* notify the PF of our intent to use this size of frame */ ret = hw->mac.ops.set_rlpml(hw, max_frame); @@ -4104,6 +4088,23 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 68 - 1504 or 9710 */ + netdev->min_mtu = ETH_MIN_MTU; + switch (adapter->hw.api_version) { + case ixgbe_mbox_api_11: + case ixgbe_mbox_api_12: + netdev->max_mtu = IXGBE_MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN); + break; + default: + if (adapter->hw.mac.type != ixgbe_mac_82599_vf) + netdev->max_mtu = IXGBE_MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN); + else + netdev->max_mtu = ETH_DATA_LEN + ETH_FCS_LEN; + break; + } + if (IXGBE_REMOVED(hw->hw_addr)) { err = -EIO; goto err_sw_init; -- cgit v1.1 From 5777987e0f4bf83c874b538d6be4029f4597878a Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:06 -0400 Subject: ethernet/marvell: use core min/max MTU checking mvneta: min_mtu 68, max_mtu 9676 - mtu validation routine mostly did range check, merge back into mvneta_change_mtu for simplicity mvpp2: min_mtu 68, max_mtu 9676 - mtu validation routine mostly did range check, merge back into mvpp2_change_mtu for simplicity pxa168_eth: min_mtu 68, max_mtu 9500 skge: min_mtu 60, max_mtu 9000 sky2: min_mtu 68, max_mtu 1500 or 9000, depending on hw CC: netdev@vger.kernel.org CC: Mirko Lindner CC: Stephen Hemminger CC: Thomas Petazzoni Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 36 +++++++++---------------------- drivers/net/ethernet/marvell/mvpp2.c | 36 ++++++++----------------------- drivers/net/ethernet/marvell/pxa168_eth.c | 7 +++--- drivers/net/ethernet/marvell/skge.c | 7 +++--- drivers/net/ethernet/marvell/sky2.c | 18 +++++++--------- 5 files changed, 35 insertions(+), 69 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 5cb07c2..b85819e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3024,29 +3024,6 @@ static void mvneta_stop_dev(struct mvneta_port *pp) mvneta_rx_reset(pp); } -/* Return positive if MTU is valid */ -static int mvneta_check_mtu_valid(struct net_device *dev, int mtu) -{ - if (mtu < 68) { - netdev_err(dev, "cannot change mtu to less than 68\n"); - return -EINVAL; - } - - /* 9676 == 9700 - 20 and rounding to 8 */ - if (mtu > 9676) { - netdev_info(dev, "Illegal MTU value %d, round to 9676\n", mtu); - mtu = 9676; - } - - if (!IS_ALIGNED(MVNETA_RX_PKT_SIZE(mtu), 8)) { - netdev_info(dev, "Illegal MTU value %d, rounding to %d\n", - mtu, ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8)); - mtu = ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8); - } - - return mtu; -} - static void mvneta_percpu_enable(void *arg) { struct mvneta_port *pp = arg; @@ -3067,9 +3044,11 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu) struct mvneta_port *pp = netdev_priv(dev); int ret; - mtu = mvneta_check_mtu_valid(dev, mtu); - if (mtu < 0) - return -EINVAL; + if (!IS_ALIGNED(MVNETA_RX_PKT_SIZE(mtu), 8)) { + netdev_info(dev, "Illegal MTU value %d, rounding to %d\n", + mtu, ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8)); + mtu = ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8); + } dev->mtu = mtu; @@ -4154,6 +4133,11 @@ static int mvneta_probe(struct platform_device *pdev) dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; dev->gso_max_segs = MVNETA_MAX_TSO_SEGS; + /* MTU range: 68 - 9676 */ + dev->min_mtu = ETH_MIN_MTU; + /* 9676 == 9700 - 20 and rounding to 8 */ + dev->max_mtu = 9676; + err = register_netdev(dev); if (err < 0) { dev_err(&pdev->dev, "failed to register\n"); diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 60227a3..c8bf155 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5453,29 +5453,6 @@ static void mvpp2_stop_dev(struct mvpp2_port *port) phy_stop(ndev->phydev); } -/* Return positive if MTU is valid */ -static inline int mvpp2_check_mtu_valid(struct net_device *dev, int mtu) -{ - if (mtu < 68) { - netdev_err(dev, "cannot change mtu to less than 68\n"); - return -EINVAL; - } - - /* 9676 == 9700 - 20 and rounding to 8 */ - if (mtu > 9676) { - netdev_info(dev, "illegal MTU value %d, round to 9676\n", mtu); - mtu = 9676; - } - - if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) { - netdev_info(dev, "illegal MTU value %d, round to %d\n", mtu, - ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8)); - mtu = ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8); - } - - return mtu; -} - static int mvpp2_check_ringparam_valid(struct net_device *dev, struct ethtool_ringparam *ring) { @@ -5717,10 +5694,10 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) struct mvpp2_port *port = netdev_priv(dev); int err; - mtu = mvpp2_check_mtu_valid(dev, mtu); - if (mtu < 0) { - err = mtu; - goto error; + if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) { + netdev_info(dev, "illegal MTU value %d, round to %d\n", mtu, + ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8)); + mtu = ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8); } if (!netif_running(dev)) { @@ -6212,6 +6189,11 @@ static int mvpp2_port_probe(struct platform_device *pdev, dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO; dev->vlan_features |= features; + /* MTU range: 68 - 9676 */ + dev->min_mtu = ETH_MIN_MTU; + /* 9676 == 9700 - 20 and rounding to 8 */ + dev->max_mtu = 9676; + err = register_netdev(dev); if (err < 0) { dev_err(&pdev->dev, "failed to register netdev\n"); diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 5d5000c..b78a838 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1209,9 +1209,6 @@ static int pxa168_eth_change_mtu(struct net_device *dev, int mtu) int retval; struct pxa168_eth_private *pep = netdev_priv(dev); - if ((mtu > 9500) || (mtu < 68)) - return -EINVAL; - dev->mtu = mtu; retval = set_port_config_ext(pep); @@ -1459,6 +1456,10 @@ static int pxa168_eth_probe(struct platform_device *pdev) dev->base_addr = 0; dev->ethtool_ops = &pxa168_ethtool_ops; + /* MTU range: 68 - 9500 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = 9500; + INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task); if (pdev->dev.of_node) diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 7173836..9c4b722 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -2900,9 +2900,6 @@ static int skge_change_mtu(struct net_device *dev, int new_mtu) { int err; - if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) - return -EINVAL; - if (!netif_running(dev)) { dev->mtu = new_mtu; return 0; @@ -3857,6 +3854,10 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, dev->watchdog_timeo = TX_WATCHDOG; dev->irq = hw->pdev->irq; + /* MTU range: 60 - 9000 */ + dev->min_mtu = ETH_ZLEN; + dev->max_mtu = ETH_JUMBO_MTU; + if (highmem) dev->features |= NETIF_F_HIGHDMA; diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index f05ea56..aa60f4d 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -2398,16 +2398,6 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) u16 ctl, mode; u32 imask; - /* MTU size outside the spec */ - if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) - return -EINVAL; - - /* MTU > 1500 on yukon FE and FE+ not allowed */ - if (new_mtu > ETH_DATA_LEN && - (hw->chip_id == CHIP_ID_YUKON_FE || - hw->chip_id == CHIP_ID_YUKON_FE_P)) - return -EINVAL; - if (!netif_running(dev)) { dev->mtu = new_mtu; netdev_update_features(dev); @@ -4808,6 +4798,14 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port, dev->features |= dev->hw_features; + /* MTU range: 60 - 1500 or 9000 */ + dev->min_mtu = ETH_ZLEN; + if (hw->chip_id == CHIP_ID_YUKON_FE || + hw->chip_id == CHIP_ID_YUKON_FE_P) + dev->max_mtu = ETH_DATA_LEN; + else + dev->max_mtu = ETH_JUMBO_MTU; + /* try to get mac address in the following order: * 1) from device tree data * 2) from internal registers set by bootloader -- cgit v1.1 From b80f71f5816fce2da8a0df22719f106c0a3d8dc7 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:07 -0400 Subject: ethernet/mellanox: use core min/max MTU checking mlx4: min_mtu 46, max_mtu depends on hardware mlx5: min_mtu 68, max_mtu depends on hardware CC: netdev@vger.kernel.org CC: Tariq Toukan CC: Saeed Mahameed Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 8 ++++---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 24 ++++++----------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 7e703be..bf35ac4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2205,10 +2205,6 @@ static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu) en_dbg(DRV, priv, "Change MTU called - current:%d new:%d\n", dev->mtu, new_mtu); - if ((new_mtu < MLX4_EN_MIN_MTU) || (new_mtu > priv->max_mtu)) { - en_err(priv, "Bad MTU size:%d.\n", new_mtu); - return -EPERM; - } if (priv->xdp_ring_num && MLX4_EN_EFF_MTU(new_mtu) > FRAG_SZ0) { en_err(priv, "MTU size:%d requires frags but XDP running\n", new_mtu); @@ -3288,6 +3284,10 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, dev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM; } + /* MTU range: 46 - hw-specific max */ + dev->min_mtu = MLX4_EN_MIN_MTU; + dev->max_mtu = priv->max_mtu; + mdev->pndev[port] = dev; mdev->upper[port] = NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 7eaf380..03183eb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2851,31 +2851,13 @@ static int mlx5e_set_features(struct net_device *netdev, return err ? -EINVAL : 0; } -#define MXL5_HW_MIN_MTU 64 -#define MXL5E_MIN_MTU (MXL5_HW_MIN_MTU + ETH_FCS_LEN) - static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu) { struct mlx5e_priv *priv = netdev_priv(netdev); - struct mlx5_core_dev *mdev = priv->mdev; bool was_opened; - u16 max_mtu; - u16 min_mtu; int err = 0; bool reset; - mlx5_query_port_max_mtu(mdev, &max_mtu, 1); - - max_mtu = MLX5E_HW2SW_MTU(max_mtu); - min_mtu = MLX5E_HW2SW_MTU(MXL5E_MIN_MTU); - - if (new_mtu > max_mtu || new_mtu < min_mtu) { - netdev_err(netdev, - "%s: Bad MTU (%d), valid range is: [%d..%d]\n", - __func__, new_mtu, min_mtu, max_mtu); - return -EINVAL; - } - mutex_lock(&priv->state_lock); reset = !priv->params.lro_en && @@ -3835,6 +3817,7 @@ int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) { const struct mlx5e_profile *profile; struct mlx5e_priv *priv; + u16 max_mtu; int err; priv = netdev_priv(netdev); @@ -3865,6 +3848,11 @@ int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) mlx5e_init_l2_addr(priv); + /* MTU range: 68 - hw-specific max */ + netdev->min_mtu = ETH_MIN_MTU; + mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1); + netdev->max_mtu = MLX5E_HW2SW_MTU(max_mtu); + mlx5e_set_dev_port_mtu(netdev); if (profile->enable) -- cgit v1.1 From caff2a87f60ab15ce872e65d577d7577c9121026 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:08 -0400 Subject: ethernet/qlogic: use core min/max MTU checking qede: min_mtu 46, max_mtu 9600 - Put define for max in qede.h qlcnic: min_mtu 68, max_mtu 9600 CC: netdev@vger.kernel.org CC Dept-GELinuxNICDev@qlogic.com CC: Yuval Mintz CC: Ariel Elior Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 5 +++-- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 8 -------- drivers/net/ethernet/qlogic/qede/qede_main.c | 4 ++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 6 ------ drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 4 ++++ 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index f50e527..9135b9d 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -362,8 +362,9 @@ void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, struct qede_dev *edev, #define NUM_TX_BDS_MIN 128 #define NUM_TX_BDS_DEF NUM_TX_BDS_MAX -#define QEDE_MIN_PKT_LEN 64 -#define QEDE_RX_HDR_SIZE 256 +#define QEDE_MIN_PKT_LEN 64 +#define QEDE_RX_HDR_SIZE 256 +#define QEDE_MAX_JUMBO_PACKET_SIZE 9600 #define for_each_queue(i) for (i = 0; i < edev->num_queues; i++) #endif /* _QEDE_H_ */ diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 25a9b29..b7dbb44 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -723,19 +723,11 @@ static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args) } /* Netdevice NDOs */ -#define ETH_MAX_JUMBO_PACKET_SIZE 9600 -#define ETH_MIN_PACKET_SIZE 60 int qede_change_mtu(struct net_device *ndev, int new_mtu) { struct qede_dev *edev = netdev_priv(ndev); union qede_reload_args args; - if ((new_mtu > ETH_MAX_JUMBO_PACKET_SIZE) || - ((new_mtu + ETH_HLEN) < ETH_MIN_PACKET_SIZE)) { - DP_ERR(edev, "Can't support requested MTU size\n"); - return -EINVAL; - } - DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), "Configuring MTU size of %d\n", new_mtu); diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 0e483af..4f29865 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -2391,6 +2391,10 @@ static void qede_init_ndev(struct qede_dev *edev) ndev->hw_features = hw_features; + /* MTU range: 46 - 9600 */ + ndev->min_mtu = ETH_ZLEN - ETH_HLEN; + ndev->max_mtu = QEDE_MAX_JUMBO_PACKET_SIZE; + /* Set network device HW mac */ ether_addr_copy(edev->ndev->dev_addr, edev->dev_info.common.hw_mac); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 509b596..838cc0c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -1024,12 +1024,6 @@ int qlcnic_change_mtu(struct net_device *netdev, int mtu) struct qlcnic_adapter *adapter = netdev_priv(netdev); int rc = 0; - if (mtu < P3P_MIN_MTU || mtu > P3P_MAX_MTU) { - dev_err(&adapter->netdev->dev, "%d bytes < mtu < %d bytes" - " not supported\n", P3P_MAX_MTU, P3P_MIN_MTU); - return -EINVAL; - } - rc = qlcnic_fw_cmd_set_mtu(adapter, mtu); if (!rc) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 3ae3968..4c0cce96 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -2342,6 +2342,10 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, netdev->priv_flags |= IFF_UNICAST_FLT; netdev->irq = adapter->msix_entries[0].vector; + /* MTU range: 68 - 9600 */ + netdev->min_mtu = P3P_MIN_MTU; + netdev->max_mtu = P3P_MAX_MTU; + err = qlcnic_set_real_num_queues(adapter, adapter->drv_tx_rings, adapter->drv_sds_rings); if (err) -- cgit v1.1 From c7315a95c2a3c8a5a5e31c72755d7505e92f0872 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:09 -0400 Subject: ethernet/realtek: use core min/max MTU checking 8139cp: min_mtu 60, max_mtu 4096 8139too: min_mtu 68, max_mtu 1770 r8169: min_mtu 60, max_mtu depends on chipset, 1500 to 9k-ish CC: netdev@vger.kernel.org CC: Realtek linux nic maintainers Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/8139cp.c | 8 ++++---- drivers/net/ethernet/realtek/8139too.c | 13 ++++--------- drivers/net/ethernet/realtek/r8169.c | 8 ++++---- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 5297bf7..b7c89eb 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -1277,10 +1277,6 @@ static int cp_change_mtu(struct net_device *dev, int new_mtu) { struct cp_private *cp = netdev_priv(dev); - /* check for invalid MTU, according to hardware limits */ - if (new_mtu < CP_MIN_MTU || new_mtu > CP_MAX_MTU) - return -EINVAL; - /* if network interface not up, no need for complexity */ if (!netif_running(dev)) { dev->mtu = new_mtu; @@ -2010,6 +2006,10 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_HIGHDMA; + /* MTU range: 60 - 4096 */ + dev->min_mtu = CP_MIN_MTU; + dev->max_mtu = CP_MAX_MTU; + rc = register_netdev(dev); if (rc) goto err_out_iomap; diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index da4c2d8..9bc047a 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -924,19 +924,10 @@ static int rtl8139_set_features(struct net_device *dev, netdev_features_t featur return 0; } -static int rtl8139_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < 68 || new_mtu > MAX_ETH_DATA_SIZE) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static const struct net_device_ops rtl8139_netdev_ops = { .ndo_open = rtl8139_open, .ndo_stop = rtl8139_close, .ndo_get_stats64 = rtl8139_get_stats64, - .ndo_change_mtu = rtl8139_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = rtl8139_set_mac_address, .ndo_start_xmit = rtl8139_start_xmit, @@ -1022,6 +1013,10 @@ static int rtl8139_init_one(struct pci_dev *pdev, dev->hw_features |= NETIF_F_RXALL; dev->hw_features |= NETIF_F_RXFCS; + /* MTU range: 68 - 1770 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = MAX_ETH_DATA_SIZE; + /* tp zeroed and aligned in alloc_etherdev */ tp = netdev_priv(dev); diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index e55638c..b698ea5 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -6673,10 +6673,6 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu) { struct rtl8169_private *tp = netdev_priv(dev); - if (new_mtu < ETH_ZLEN || - new_mtu > rtl_chip_infos[tp->mac_version].jumbo_max) - return -EINVAL; - if (new_mtu > ETH_DATA_LEN) rtl_hw_jumbo_enable(tp); else @@ -8430,6 +8426,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->hw_features |= NETIF_F_RXALL; dev->hw_features |= NETIF_F_RXFCS; + /* MTU range: 60 - hw-specific max */ + dev->min_mtu = ETH_ZLEN; + dev->max_mtu = rtl_chip_infos[chipset].jumbo_max; + tp->hw_start = cfg->hw_start; tp->event_slow = cfg->event_slow; -- cgit v1.1 From 540bfe30dc3f0b365c3a859eecad66ef468e167c Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:10 -0400 Subject: ethernet/sun: use core min/max MTU checking cassini: min_mtu 60, max_mtu 9000 niu: min_mtu 68, max_mtu 9216 sungem: min_mtu 68, max_mtu 1500 (comments say jumbo mode is broken) sunvnet: min_mtu 68, max_mtu 65535 - removed sunvnet_change_mut_common as it does nothing now CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/cassini.c | 7 ++++--- drivers/net/ethernet/sun/ldmvsw.c | 5 ++++- drivers/net/ethernet/sun/niu.c | 7 ++++--- drivers/net/ethernet/sun/sungem.c | 11 ++++++----- drivers/net/ethernet/sun/sunvnet.c | 5 ++++- drivers/net/ethernet/sun/sunvnet_common.c | 10 ---------- drivers/net/ethernet/sun/sunvnet_common.h | 3 ++- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 062bce9..e9e5ef2 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -3863,9 +3863,6 @@ static int cas_change_mtu(struct net_device *dev, int new_mtu) { struct cas *cp = netdev_priv(dev); - if (new_mtu < CAS_MIN_MTU || new_mtu > CAS_MAX_MTU) - return -EINVAL; - dev->mtu = new_mtu; if (!netif_running(dev) || !netif_device_present(dev)) return 0; @@ -5115,6 +5112,10 @@ static int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; + /* MTU range: 60 - varies or 9000 */ + dev->min_mtu = CAS_MIN_MTU; + dev->max_mtu = CAS_MAX_MTU; + if (register_netdev(dev)) { dev_err(&pdev->dev, "Cannot register net device, aborting\n"); goto err_out_free_consistent; diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index 0ac449a..335b876 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -139,7 +139,6 @@ static const struct net_device_ops vsw_ops = { .ndo_set_mac_address = sunvnet_set_mac_addr_common, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = sunvnet_tx_timeout_common, - .ndo_change_mtu = sunvnet_change_mtu_common, .ndo_start_xmit = vsw_start_xmit, .ndo_select_queue = vsw_select_queue, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -239,6 +238,10 @@ static struct net_device *vsw_alloc_netdev(u8 hwaddr[], NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; + /* MTU range: 68 - 65535 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = VNET_MAX_MTU; + SET_NETDEV_DEV(dev, &vdev->dev); return dev; diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index a2371aa..f90d1af 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -6754,9 +6754,6 @@ static int niu_change_mtu(struct net_device *dev, int new_mtu) struct niu *np = netdev_priv(dev); int err, orig_jumbo, new_jumbo; - if (new_mtu < 68 || new_mtu > NIU_MAX_MTU) - return -EINVAL; - orig_jumbo = (dev->mtu > ETH_DATA_LEN); new_jumbo = (new_mtu > ETH_DATA_LEN); @@ -9823,6 +9820,10 @@ static int niu_pci_init_one(struct pci_dev *pdev, dev->irq = pdev->irq; + /* MTU range: 68 - 9216 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = NIU_MAX_MTU; + niu_assign_netdev_ops(dev); err = niu_get_invariants(np); diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index d6ad0fb..66ecf0f 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -2476,9 +2476,9 @@ static void gem_set_multicast(struct net_device *dev) } /* Jumbo-grams don't seem to work :-( */ -#define GEM_MIN_MTU 68 +#define GEM_MIN_MTU ETH_MIN_MTU #if 1 -#define GEM_MAX_MTU 1500 +#define GEM_MAX_MTU ETH_DATA_LEN #else #define GEM_MAX_MTU 9000 #endif @@ -2487,9 +2487,6 @@ static int gem_change_mtu(struct net_device *dev, int new_mtu) { struct gem *gp = netdev_priv(dev); - if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU) - return -EINVAL; - dev->mtu = new_mtu; /* We'll just catch it later when the device is up'd or resumed */ @@ -2977,6 +2974,10 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; + /* MTU range: 68 - 1500 (Jumbo mode is broken) */ + dev->min_mtu = GEM_MIN_MTU; + dev->max_mtu = GEM_MAX_MTU; + /* Register with kernel */ if (register_netdev(dev)) { pr_err("Cannot register net device, aborting\n"); diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index a2f9b47..5356a70 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -159,7 +159,6 @@ static const struct net_device_ops vnet_ops = { .ndo_set_mac_address = sunvnet_set_mac_addr_common, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = sunvnet_tx_timeout_common, - .ndo_change_mtu = sunvnet_change_mtu_common, .ndo_start_xmit = vnet_start_xmit, .ndo_select_queue = vnet_select_queue, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -202,6 +201,10 @@ static struct vnet *vnet_new(const u64 *local_mac, NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; + /* MTU range: 68 - 65535 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = VNET_MAX_MTU; + SET_NETDEV_DEV(dev, &vdev->dev); err = register_netdev(dev); diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 904a5a1..58efe69 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -1583,16 +1583,6 @@ void sunvnet_set_rx_mode_common(struct net_device *dev, struct vnet *vp) } EXPORT_SYMBOL_GPL(sunvnet_set_rx_mode_common); -int sunvnet_change_mtu_common(struct net_device *dev, int new_mtu) -{ - if (new_mtu < 68 || new_mtu > 65535) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL_GPL(sunvnet_change_mtu_common); - int sunvnet_set_mac_addr_common(struct net_device *dev, void *p) { return -EINVAL; diff --git a/drivers/net/ethernet/sun/sunvnet_common.h b/drivers/net/ethernet/sun/sunvnet_common.h index bd36528..ce5c824 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.h +++ b/drivers/net/ethernet/sun/sunvnet_common.h @@ -15,6 +15,8 @@ #define VNET_MINTSO 2048 /* VIO protocol's minimum TSO len */ #define VNET_MAXTSO 65535 /* VIO protocol's maximum TSO len */ +#define VNET_MAX_MTU 65535 + /* VNET packets are sent in buffers with the first 6 bytes skipped * so that after the ethernet header the IPv4/IPv6 headers are aligned * properly. @@ -125,7 +127,6 @@ int sunvnet_close_common(struct net_device *dev); void sunvnet_set_rx_mode_common(struct net_device *dev, struct vnet *vp); int sunvnet_set_mac_addr_common(struct net_device *dev, void *p); void sunvnet_tx_timeout_common(struct net_device *dev); -int sunvnet_change_mtu_common(struct net_device *dev, int new_mtu); int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev, struct vnet_port *(*vnet_tx_port) (struct sk_buff *, struct net_device *)); -- cgit v1.1 From f7ad72bfceb95e6607e0f0d7b7dbfb551865610a Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:11 -0400 Subject: ethernet/dlink: use core min/max MTU checking dl2k: min_mtu 68, max_mtu 1536 or 8000, depending on hardware - Removed change_mtu, does nothing productive anymore sundance: min_mtu 68, max_mtu 8191 CC: netdev@vger.kernel.org CC: Denis Kirjanov Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/dlink/dl2k.c | 22 ++++------------------ drivers/net/ethernet/dlink/sundance.c | 6 ++++-- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index 78f1446..8c95a8a 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -76,7 +76,6 @@ static void rio_free_tx (struct net_device *dev, int irq); static void tx_error (struct net_device *dev, int tx_status); static int receive_packet (struct net_device *dev); static void rio_error (struct net_device *dev, int int_status); -static int change_mtu (struct net_device *dev, int new_mtu); static void set_multicast (struct net_device *dev); static struct net_device_stats *get_stats (struct net_device *dev); static int clear_stats (struct net_device *dev); @@ -106,7 +105,6 @@ static const struct net_device_ops netdev_ops = { .ndo_set_rx_mode = set_multicast, .ndo_do_ioctl = rio_ioctl, .ndo_tx_timeout = rio_tx_timeout, - .ndo_change_mtu = change_mtu, }; static int @@ -230,6 +228,10 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) #if 0 dev->features = NETIF_F_IP_CSUM; #endif + /* MTU range: 68 - 1536 or 8000 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = np->jumbo ? MAX_JUMBO : PACKET_SIZE; + pci_set_drvdata (pdev, dev); ring_space = pci_alloc_consistent (pdev, TX_TOTAL_SIZE, &ring_dma); @@ -1198,22 +1200,6 @@ clear_stats (struct net_device *dev) return 0; } - -static int -change_mtu (struct net_device *dev, int new_mtu) -{ - struct netdev_private *np = netdev_priv(dev); - int max = (np->jumbo) ? MAX_JUMBO : 1536; - - if ((new_mtu < 68) || (new_mtu > max)) { - return -EINVAL; - } - - dev->mtu = new_mtu; - - return 0; -} - static void set_multicast (struct net_device *dev) { diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index 79d8009..eab36ac 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -580,6 +580,10 @@ static int sundance_probe1(struct pci_dev *pdev, dev->ethtool_ops = ðtool_ops; dev->watchdog_timeo = TX_TIMEOUT; + /* MTU range: 68 - 8191 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = 8191; + pci_set_drvdata(pdev, dev); i = register_netdev(dev); @@ -713,8 +717,6 @@ err_out_netdev: static int change_mtu(struct net_device *dev, int new_mtu) { - if ((new_mtu < 68) || (new_mtu > 8191)) /* Set by RxDMAFrameLen */ - return -EINVAL; if (netif_running(dev)) return -EBUSY; dev->mtu = new_mtu; -- cgit v1.1 From 18c310fb9583a9c69b0316b7907759017f2e152a Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:12 -0400 Subject: ethernet/neterion: use core min/max MTU checking s2io: min_mtu 46, max_mtu 9600 vxge: min_mtu 68, max_mtu 9600 CC: netdev@vger.kernel.org CC: Jon Mason Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/neterion/s2io.c | 9 ++++----- drivers/net/ethernet/neterion/vxge/vxge-config.h | 2 +- drivers/net/ethernet/neterion/vxge/vxge-main.c | 9 ++++----- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index eaa37c0..564f682 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -6678,11 +6678,6 @@ static int s2io_change_mtu(struct net_device *dev, int new_mtu) struct s2io_nic *sp = netdev_priv(dev); int ret = 0; - if ((new_mtu < MIN_MTU) || (new_mtu > S2IO_JUMBO_SIZE)) { - DBG_PRINT(ERR_DBG, "%s: MTU size is invalid.\n", dev->name); - return -EPERM; - } - dev->mtu = new_mtu; if (netif_running(dev)) { s2io_stop_all_tx_queue(sp); @@ -8019,6 +8014,10 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) config->mc_start_offset = S2IO_HERC_MC_ADDR_START_OFFSET; } + /* MTU range: 46 - 9600 */ + dev->min_mtu = MIN_MTU; + dev->max_mtu = S2IO_JUMBO_SIZE; + /* store mac addresses from CAM to s2io_nic structure */ do_s2io_store_unicast_mc(sp); diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.h b/drivers/net/ethernet/neterion/vxge/vxge-config.h index 6ce4412..cfa9704 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-config.h +++ b/drivers/net/ethernet/neterion/vxge/vxge-config.h @@ -27,7 +27,7 @@ (((size) - (((u64)adrs) & ((size)-1))) & ((size)-1)) #endif -#define VXGE_HW_MIN_MTU 68 +#define VXGE_HW_MIN_MTU ETH_MIN_MTU #define VXGE_HW_MAX_MTU 9600 #define VXGE_HW_DEFAULT_MTU 1500 diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index e0993eb..e07b936 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -3074,11 +3074,6 @@ static int vxge_change_mtu(struct net_device *dev, int new_mtu) vxge_debug_entryexit(vdev->level_trace, "%s:%d", __func__, __LINE__); - if ((new_mtu < VXGE_HW_MIN_MTU) || (new_mtu > VXGE_HW_MAX_MTU)) { - vxge_debug_init(vdev->level_err, - "%s: mtu size is invalid", dev->name); - return -EPERM; - } /* check if device is down already */ if (unlikely(!is_vxge_card_up(vdev))) { @@ -3462,6 +3457,10 @@ static int vxge_device_register(struct __vxge_hw_device *hldev, "%s : using High DMA", __func__); } + /* MTU range: 68 - 9600 */ + ndev->min_mtu = VXGE_HW_MIN_MTU; + ndev->max_mtu = VXGE_HW_MAX_MTU; + ret = register_netdev(ndev); if (ret) { vxge_debug_init(vxge_hw_device_trace_level_get(hldev), -- cgit v1.1 From 109cc16526c6d5551f7735683f7985b2b0300bb5 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:13 -0400 Subject: ethernet/cavium: use core min/max MTU checking liquidio: min_mtu 68, max_mtu 16000 thunder: min_mtu 64, max_mtu 9200 CC: netdev@vger.kernel.org CC: Sunil Goutham CC: Robert Richter CC: Derek Chickles CC: Satanand Burla CC: Felix Manlunas CC: Raghu Vatsavayi Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 15 ++++----------- drivers/net/ethernet/cavium/liquidio/octeon_network.h | 2 +- drivers/net/ethernet/cavium/octeon/octeon_mgmt.c | 13 +++---------- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 10 ++++------ 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index afc6f9d..71d01a7 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2868,17 +2868,6 @@ static int liquidio_change_mtu(struct net_device *netdev, int new_mtu) struct octnic_ctrl_pkt nctrl; int ret = 0; - /* Limit the MTU to make sure the ethernet packets are between 68 bytes - * and 16000 bytes - */ - if ((new_mtu < LIO_MIN_MTU_SIZE) || - (new_mtu > LIO_MAX_MTU_SIZE)) { - dev_err(&oct->pci_dev->dev, "Invalid MTU: %d\n", new_mtu); - dev_err(&oct->pci_dev->dev, "Valid range %d and %d\n", - LIO_MIN_MTU_SIZE, LIO_MAX_MTU_SIZE); - return -EINVAL; - } - memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); nctrl.ncmd.u64 = 0; @@ -3891,6 +3880,10 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) netdev->hw_features = netdev->hw_features & ~NETIF_F_HW_VLAN_CTAG_RX; + /* MTU range: 68 - 16000 */ + netdev->min_mtu = LIO_MIN_MTU_SIZE; + netdev->max_mtu = LIO_MAX_MTU_SIZE; + /* Point to the properties for octeon device to which this * interface belongs. */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index e5d1deb..54b9665 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -29,7 +29,7 @@ #include #define LIO_MAX_MTU_SIZE (OCTNET_MAX_FRM_SIZE - OCTNET_FRM_HEADER_SIZE) -#define LIO_MIN_MTU_SIZE 68 +#define LIO_MIN_MTU_SIZE ETH_MIN_MTU struct oct_nic_stats_resp { u64 rh; diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c index 4ab404f..16e12c4 100644 --- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c @@ -645,16 +645,6 @@ static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu) struct octeon_mgmt *p = netdev_priv(netdev); int size_without_fcs = new_mtu + OCTEON_MGMT_RX_HEADROOM; - /* Limit the MTU to make sure the ethernet packets are between - * 64 bytes and 16383 bytes. - */ - if (size_without_fcs < 64 || size_without_fcs > 16383) { - dev_warn(p->dev, "MTU must be between %d and %d.\n", - 64 - OCTEON_MGMT_RX_HEADROOM, - 16383 - OCTEON_MGMT_RX_HEADROOM); - return -EINVAL; - } - netdev->mtu = new_mtu; cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_MAX, size_without_fcs); @@ -1491,6 +1481,9 @@ static int octeon_mgmt_probe(struct platform_device *pdev) netdev->netdev_ops = &octeon_mgmt_ops; netdev->ethtool_ops = &octeon_mgmt_ethtool_ops; + netdev->min_mtu = 64 - OCTEON_MGMT_RX_HEADROOM; + netdev->max_mtu = 16383 - OCTEON_MGMT_RX_HEADROOM; + mac = of_get_mac_address(pdev->dev.of_node); if (mac) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 45a13f7..b192712 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -1312,12 +1312,6 @@ static int nicvf_change_mtu(struct net_device *netdev, int new_mtu) { struct nicvf *nic = netdev_priv(netdev); - if (new_mtu > NIC_HW_MAX_FRS) - return -EINVAL; - - if (new_mtu < NIC_HW_MIN_FRS) - return -EINVAL; - if (nicvf_update_hw_max_frs(nic, new_mtu)) return -EINVAL; netdev->mtu = new_mtu; @@ -1630,6 +1624,10 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &nicvf_netdev_ops; netdev->watchdog_timeo = NICVF_TX_TIMEOUT; + /* MTU range: 64 - 9200 */ + netdev->min_mtu = NIC_HW_MIN_FRS; + netdev->max_mtu = NIC_HW_MAX_FRS; + INIT_WORK(&nic->reset_task, nicvf_reset_task); err = register_netdev(netdev); -- cgit v1.1 From 3d5d96acfeb9ddab99e0216109cbc034a65fd490 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:14 -0400 Subject: ethernet/ibm: use core min/max MTU checking ehea: min_mtu 68, max_mtu 9022 - remove ehea_change_mtu, it's now redundant emac: min_mtu 46, max_mtu 1500 or whatever gets read from OF CC: netdev@vger.kernel.org CC: Douglas Miller Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ehea/ehea_main.c | 13 ++++--------- drivers/net/ethernet/ibm/emac/core.c | 9 +++++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 54efa9a..e9719ba 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -1981,14 +1981,6 @@ out: ehea_update_bcmc_registrations(); } -static int ehea_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > EHEA_MAX_PACKET_SIZE)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static void xmit_common(struct sk_buff *skb, struct ehea_swqe *swqe) { swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT | EHEA_SWQE_CRC; @@ -2968,7 +2960,6 @@ static const struct net_device_ops ehea_netdev_ops = { .ndo_set_mac_address = ehea_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = ehea_set_multicast_list, - .ndo_change_mtu = ehea_change_mtu, .ndo_vlan_rx_add_vid = ehea_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = ehea_vlan_rx_kill_vid, .ndo_tx_timeout = ehea_tx_watchdog, @@ -3041,6 +3032,10 @@ static struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, NETIF_F_IP_CSUM; dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT; + /* MTU range: 68 - 9022 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = EHEA_MAX_PACKET_SIZE; + INIT_WORK(&port->reset_task, ehea_reset_port); INIT_DELAYED_WORK(&port->stats_work, ehea_update_stats); diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 5d804a54..52a69c9 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -1099,9 +1099,6 @@ static int emac_change_mtu(struct net_device *ndev, int new_mtu) struct emac_instance *dev = netdev_priv(ndev); int ret = 0; - if (new_mtu < EMAC_MIN_MTU || new_mtu > dev->max_mtu) - return -EINVAL; - DBG(dev, "change_mtu(%d)" NL, new_mtu); if (netif_running(ndev)) { @@ -2564,7 +2561,7 @@ static int emac_init_config(struct emac_instance *dev) if (emac_read_uint_prop(np, "cell-index", &dev->cell_index, 1)) return -ENXIO; if (emac_read_uint_prop(np, "max-frame-size", &dev->max_mtu, 0)) - dev->max_mtu = 1500; + dev->max_mtu = ETH_DATA_LEN; if (emac_read_uint_prop(np, "rx-fifo-size", &dev->rx_fifo_size, 0)) dev->rx_fifo_size = 2048; if (emac_read_uint_prop(np, "tx-fifo-size", &dev->tx_fifo_size, 0)) @@ -2890,6 +2887,10 @@ static int emac_probe(struct platform_device *ofdev) ndev->netdev_ops = &emac_netdev_ops; ndev->ethtool_ops = &emac_ethtool_ops; + /* MTU range: 46 - 1500 or whatever is in OF */ + ndev->min_mtu = EMAC_MIN_MTU; + ndev->max_mtu = dev->max_mtu; + netif_carrier_off(ndev); err = register_netdev(ndev); -- cgit v1.1 From f58f0817d78a8418f1f33a30be09fcb34184cc94 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:15 -0400 Subject: ethernet/tile: use core min/max MTU checking tilegx: min_mtu 68, max_mtu 1500 or 9000, depending on modparam - remove tile_net_change_mtu now that it is fully redundant tilepro: min_mtu 68, max_mtu 1500 - hardware supports jumbo packets up to 10226, but it's not implemented or tested yet, according to code comments CC: netdev@vger.kernel.org CC: Chris Metcalf Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/tile/tilegx.c | 21 ++++++++------------- drivers/net/ethernet/tile/tilepro.c | 27 +++++---------------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c index 11213a3..0aaf975 100644 --- a/drivers/net/ethernet/tile/tilegx.c +++ b/drivers/net/ethernet/tile/tilegx.c @@ -59,6 +59,9 @@ /* Maximum number of packets to handle per "poll". */ #define TILE_NET_WEIGHT 64 +/* Maximum Jumbo Packet MTU */ +#define TILE_JUMBO_MAX_MTU 9000 + /* Number of entries in each iqueue. */ #define IQUEUE_ENTRIES 512 @@ -2101,17 +2104,6 @@ static int tile_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) return -EOPNOTSUPP; } -/* Change the MTU. */ -static int tile_net_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < 68) - return -EINVAL; - if (new_mtu > ((jumbo_num != 0) ? 9000 : 1500)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - /* Change the Ethernet address of the NIC. * * The hypervisor driver does not support changing MAC address. However, @@ -2154,7 +2146,6 @@ static const struct net_device_ops tile_net_ops = { .ndo_start_xmit = tile_net_tx, .ndo_select_queue = tile_net_select_queue, .ndo_do_ioctl = tile_net_ioctl, - .ndo_change_mtu = tile_net_change_mtu, .ndo_tx_timeout = tile_net_tx_timeout, .ndo_set_mac_address = tile_net_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -2174,7 +2165,11 @@ static void tile_net_setup(struct net_device *dev) ether_setup(dev); dev->netdev_ops = &tile_net_ops; dev->watchdog_timeo = TILE_NET_TIMEOUT; - dev->mtu = 1500; + + /* MTU range: 68 - 1500 or 9000 */ + dev->mtu = ETH_DATA_LEN; + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = jumbo_num ? TILE_JUMBO_MAX_MTU : ETH_DATA_LEN; features |= NETIF_F_HW_CSUM; features |= NETIF_F_SG; diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c index 4ef605a..0a3b7da 100644 --- a/drivers/net/ethernet/tile/tilepro.c +++ b/drivers/net/ethernet/tile/tilepro.c @@ -87,7 +87,7 @@ /* This should be 1500 if "jumbo" is not set in LIPP. */ /* This should be at most 10226 (10240 - 14) if "jumbo" is set in LIPP. */ /* ISSUE: This has not been thoroughly tested (except at 1500). */ -#define TILE_NET_MTU 1500 +#define TILE_NET_MTU ETH_DATA_LEN /* HACK: Define this to verify incoming packets. */ /* #define TILE_NET_VERIFY_INGRESS */ @@ -2095,26 +2095,6 @@ static struct rtnl_link_stats64 *tile_net_get_stats64(struct net_device *dev, } -/* - * Change the "mtu". - * - * The "change_mtu" method is usually not needed. - * If you need it, it must be like this. - */ -static int tile_net_change_mtu(struct net_device *dev, int new_mtu) -{ - PDEBUG("tile_net_change_mtu()\n"); - - /* Check ranges. */ - if ((new_mtu < 68) || (new_mtu > 1500)) - return -EINVAL; - - /* Accept the value. */ - dev->mtu = new_mtu; - - return 0; -} - /* * Change the Ethernet Address of the NIC. @@ -2229,7 +2209,6 @@ static const struct net_device_ops tile_net_ops = { .ndo_start_xmit = tile_net_tx, .ndo_do_ioctl = tile_net_ioctl, .ndo_get_stats64 = tile_net_get_stats64, - .ndo_change_mtu = tile_net_change_mtu, .ndo_tx_timeout = tile_net_tx_timeout, .ndo_set_mac_address = tile_net_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -2252,7 +2231,11 @@ static void tile_net_setup(struct net_device *dev) dev->netdev_ops = &tile_net_ops; dev->watchdog_timeo = TILE_NET_TIMEOUT; dev->tx_queue_len = TILE_NET_TX_QUEUE_LEN; + + /* MTU range: 68 - 1500 */ dev->mtu = TILE_NET_MTU; + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = TILE_NET_MTU; features |= NETIF_F_HW_CSUM; features |= NETIF_F_SG; -- cgit v1.1 From 1281a2c7b814cf5ccdafa49c1410dd5092da0542 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:16 -0400 Subject: ethernet/toshiba: use core min/max MTU checking gelic_net: min_mtu 64, max_mtu 1518 - remove gelic_net_change_mtu now that it is redundant spidernet: min_Mtu 64, max_mtu 2294 - remove spiter_net_change_mtu now that it is redundant CC: netdev@vger.kernel.org CC: Geoff Levand CC: Ishizaki Kou Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/toshiba/ps3_gelic_net.c | 23 ++++------------------ drivers/net/ethernet/toshiba/ps3_gelic_net.h | 1 - drivers/net/ethernet/toshiba/ps3_gelic_wireless.c | 1 - drivers/net/ethernet/toshiba/spider_net.c | 24 ++++------------------- 4 files changed, 8 insertions(+), 41 deletions(-) diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c index 272f2b1..345316c 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -1114,24 +1114,6 @@ static int gelic_net_poll(struct napi_struct *napi, int budget) } return packets_done; } -/** - * gelic_net_change_mtu - changes the MTU of an interface - * @netdev: interface device structure - * @new_mtu: new MTU value - * - * returns 0 on success, <0 on failure - */ -int gelic_net_change_mtu(struct net_device *netdev, int new_mtu) -{ - /* no need to re-alloc skbs or so -- the max mtu is about 2.3k - * and mtu is outbound only anyway */ - if ((new_mtu < GELIC_NET_MIN_MTU) || - (new_mtu > GELIC_NET_MAX_MTU)) { - return -EINVAL; - } - netdev->mtu = new_mtu; - return 0; -} /** * gelic_card_interrupt - event handler for gelic_net @@ -1446,7 +1428,6 @@ static const struct net_device_ops gelic_netdevice_ops = { .ndo_stop = gelic_net_stop, .ndo_start_xmit = gelic_net_xmit, .ndo_set_rx_mode = gelic_net_set_multi, - .ndo_change_mtu = gelic_net_change_mtu, .ndo_tx_timeout = gelic_net_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, @@ -1513,6 +1494,10 @@ int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card) netdev->features |= NETIF_F_VLAN_CHALLENGED; } + /* MTU range: 64 - 1518 */ + netdev->min_mtu = GELIC_NET_MIN_MTU; + netdev->max_mtu = GELIC_NET_MAX_MTU; + status = register_netdev(netdev); if (status) { dev_err(ctodev(card), "%s:Couldn't register %s %d\n", diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.h b/drivers/net/ethernet/toshiba/ps3_gelic_net.h index 8505196..003d045 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.h +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h @@ -373,7 +373,6 @@ int gelic_net_stop(struct net_device *netdev); int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev); void gelic_net_set_multi(struct net_device *netdev); void gelic_net_tx_timeout(struct net_device *netdev); -int gelic_net_change_mtu(struct net_device *netdev, int new_mtu); int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card); /* shared ethtool ops */ diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c index 446ea58..b3abd02 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c @@ -2558,7 +2558,6 @@ static const struct net_device_ops gelic_wl_netdevice_ops = { .ndo_stop = gelic_wl_stop, .ndo_start_xmit = gelic_net_xmit, .ndo_set_rx_mode = gelic_net_set_multi, - .ndo_change_mtu = gelic_net_change_mtu, .ndo_tx_timeout = gelic_net_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c index 36a6e8b..cb341df 100644 --- a/drivers/net/ethernet/toshiba/spider_net.c +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -1279,25 +1279,6 @@ static int spider_net_poll(struct napi_struct *napi, int budget) } /** - * spider_net_change_mtu - changes the MTU of an interface - * @netdev: interface device structure - * @new_mtu: new MTU value - * - * returns 0 on success, <0 on failure - */ -static int -spider_net_change_mtu(struct net_device *netdev, int new_mtu) -{ - /* no need to re-alloc skbs or so -- the max mtu is about 2.3k - * and mtu is outbound only anyway */ - if ( (new_mtu < SPIDER_NET_MIN_MTU ) || - (new_mtu > SPIDER_NET_MAX_MTU) ) - return -EINVAL; - netdev->mtu = new_mtu; - return 0; -} - -/** * spider_net_set_mac - sets the MAC of an interface * @netdev: interface device structure * @ptr: pointer to new MAC address @@ -2229,7 +2210,6 @@ static const struct net_device_ops spider_net_ops = { .ndo_start_xmit = spider_net_xmit, .ndo_set_rx_mode = spider_net_set_multi, .ndo_set_mac_address = spider_net_set_mac, - .ndo_change_mtu = spider_net_change_mtu, .ndo_do_ioctl = spider_net_do_ioctl, .ndo_tx_timeout = spider_net_tx_timeout, .ndo_validate_addr = eth_validate_addr, @@ -2299,6 +2279,10 @@ spider_net_setup_netdev(struct spider_net_card *card) /* some time: NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | * NETIF_F_HW_VLAN_CTAG_FILTER */ + /* MTU range: 64 - 2294 */ + netdev->min_mtu = SPIDER_NET_MIN_MTU; + netdev->max_mtu = SPIDER_NET_MAX_MTU; + netdev->irq = card->pdev->irq; card->num_rx_ints = 0; card->ignore_rx_ramfull = 0; -- cgit v1.1 From 44770e1180decf9b483d9b537fdad385a4fbf462 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Oct 2016 15:54:17 -0400 Subject: ethernet: use core min/max MTU checking et131x: min_mtu 64, max_mtu 9216 altera_tse: min_mtu 64, max_mtu 1500 amd8111e: min_mtu 60, max_mtu 9000 bnad: min_mtu 46, max_mtu 9000 macb: min_mtu 68, max_mtu 1500 or 10240 depending on hardware capability xgmac: min_mtu 46, max_mtu 9000 cxgb2: min_mtu 68, max_mtu 9582 (pm3393) or 9600 (vsc7326) enic: min_mtu 68, max_mtu 9000 gianfar: min_mtu 50, max_mu 9586 hns_enet: min_mtu 68, max_mtu 9578 (v1) or 9706 (v2) ksz884x: min_mtu 60, max_mtu 1894 myri10ge: min_mtu 68, max_mtu 9000 natsemi: min_mtu 64, max_mtu 2024 nfp: min_mtu 68, max_mtu hardware-specific forcedeth: min_mtu 64, max_mtu 1500 or 9100, depending on hardware pch_gbe: min_mtu 46, max_mtu 10300 pasemi_mac: min_mtu 64, max_mtu 9000 qcaspi: min_mtu 46, max_mtu 1500 - remove qcaspi_netdev_change_mtu as it is now redundant rocker: min_mtu 68, max_mtu 9000 sxgbe: min_mtu 68, max_mtu 9000 stmmac: min_mtu 46, max_mtu depends on hardware tehuti: min_mtu 60, max_mtu 16384 - driver had no max mtu checking, but product docs say 16k jumbo packets are supported by the hardware netcp: min_mtu 68, max_mtu 9486 - remove netcp_ndo_change_mtu as it is now redundant via-velocity: min_mtu 64, max_mtu 9000 octeon: min_mtu 46, max_mtu 65370 CC: netdev@vger.kernel.org CC: Mark Einon CC: Vince Bridgers CC: Rasesh Mody CC: Nicolas Ferre CC: Santosh Raspatur CC: Hariprasad S CC: Christian Benvenuti CC: Sujith Sankar CC: Govindarajulu Varadarajan <_govind@gmx.com> CC: Neel Patel CC: Claudiu Manoil CC: Yisen Zhuang CC: Salil Mehta CC: Hyong-Youb Kim CC: Jakub Kicinski CC: Olof Johansson CC: Jiri Pirko CC: Byungho An CC: Girish K S CC: Vipul Pandya CC: Giuseppe Cavallaro CC: Alexandre Torgue CC: Andy Gospodarek CC: Wingman Kwok CC: Murali Karicheri CC: Francois Romieu Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/agere/et131x.c | 7 +++-- drivers/net/ethernet/altera/altera_tse.h | 1 - drivers/net/ethernet/altera/altera_tse_main.c | 14 ++------- drivers/net/ethernet/amd/amd8111e.c | 5 ++-- drivers/net/ethernet/brocade/bna/bnad.c | 7 +++-- drivers/net/ethernet/cadence/macb.c | 19 ++++++------- drivers/net/ethernet/calxeda/xgmac.c | 20 ++++--------- drivers/net/ethernet/chelsio/cxgb/common.h | 5 ++++ drivers/net/ethernet/chelsio/cxgb/cxgb2.c | 18 ++++++++++-- drivers/net/ethernet/chelsio/cxgb/pm3393.c | 8 +----- drivers/net/ethernet/chelsio/cxgb/vsc7326.c | 5 ---- drivers/net/ethernet/cisco/enic/enic_main.c | 7 +++-- drivers/net/ethernet/cisco/enic/enic_res.h | 2 +- drivers/net/ethernet/freescale/gianfar.c | 9 ++---- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 3 +- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 11 +++++--- drivers/net/ethernet/micrel/ksz884x.c | 33 +++++++++++----------- drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 20 +++++++------ drivers/net/ethernet/natsemi/natsemi.c | 7 +++-- .../net/ethernet/netronome/nfp/nfp_net_common.c | 10 +++---- drivers/net/ethernet/nvidia/forcedeth.c | 9 +++--- .../net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 13 ++++----- drivers/net/ethernet/pasemi/pasemi_mac.c | 12 ++++---- drivers/net/ethernet/qualcomm/qca_framing.h | 6 ++-- drivers/net/ethernet/qualcomm/qca_spi.c | 16 +++-------- drivers/net/ethernet/rocker/rocker_main.c | 12 ++++---- drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 17 +++-------- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 25 ++++++---------- drivers/net/ethernet/tehuti/tehuti.c | 14 +++------ drivers/net/ethernet/tehuti/tehuti.h | 3 ++ drivers/net/ethernet/ti/netcp_core.c | 20 +++---------- drivers/net/ethernet/via/via-velocity.c | 11 +++----- drivers/staging/octeon/ethernet.c | 22 +++++++-------- 33 files changed, 169 insertions(+), 222 deletions(-) diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index 9066838..831bab3 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -176,6 +176,8 @@ MODULE_DESCRIPTION("10/100/1000 Base-T Ethernet Driver for the ET1310 by Agere S #define NUM_FBRS 2 #define MAX_PACKETS_HANDLED 256 +#define ET131X_MIN_MTU 64 +#define ET131X_MAX_MTU 9216 #define ALCATEL_MULTICAST_PKT 0x01000000 #define ALCATEL_BROADCAST_PKT 0x02000000 @@ -3869,9 +3871,6 @@ static int et131x_change_mtu(struct net_device *netdev, int new_mtu) int result = 0; struct et131x_adapter *adapter = netdev_priv(netdev); - if (new_mtu < 64 || new_mtu > 9216) - return -EINVAL; - et131x_disable_txrx(netdev); netdev->mtu = new_mtu; @@ -3958,6 +3957,8 @@ static int et131x_pci_setup(struct pci_dev *pdev, netdev->watchdog_timeo = ET131X_TX_TIMEOUT; netdev->netdev_ops = &et131x_netdev_ops; + netdev->min_mtu = ET131X_MIN_MTU; + netdev->max_mtu = ET131X_MAX_MTU; SET_NETDEV_DEV(netdev, &pdev->dev); netdev->ethtool_ops = &et131x_ethtool_ops; diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h index e005200..9b640c8 100644 --- a/drivers/net/ethernet/altera/altera_tse.h +++ b/drivers/net/ethernet/altera/altera_tse.h @@ -443,7 +443,6 @@ struct altera_tse_private { /* RX/TX MAC FIFO configs */ u32 tx_fifo_depth; u32 rx_fifo_depth; - u32 max_mtu; /* Hash filter settings */ u32 hash_filter; diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index bda31f3..cc9787e 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -994,20 +994,11 @@ static void tse_set_mac(struct altera_tse_private *priv, bool enable) */ static int tse_change_mtu(struct net_device *dev, int new_mtu) { - struct altera_tse_private *priv = netdev_priv(dev); - unsigned int max_mtu = priv->max_mtu; - unsigned int min_mtu = ETH_ZLEN + ETH_FCS_LEN; - if (netif_running(dev)) { netdev_err(dev, "must be stopped to change its MTU\n"); return -EBUSY; } - if ((new_mtu < min_mtu) || (new_mtu > max_mtu)) { - netdev_err(dev, "invalid MTU, max MTU is: %u\n", max_mtu); - return -EINVAL; - } - dev->mtu = new_mtu; netdev_update_features(dev); @@ -1446,15 +1437,16 @@ static int altera_tse_probe(struct platform_device *pdev) of_property_read_bool(pdev->dev.of_node, "altr,has-supplementary-unicast"); + priv->dev->min_mtu = ETH_ZLEN + ETH_FCS_LEN; /* Max MTU is 1500, ETH_DATA_LEN */ - priv->max_mtu = ETH_DATA_LEN; + priv->dev->max_mtu = ETH_DATA_LEN; /* Get the max mtu from the device tree. Note that the * "max-frame-size" parameter is actually max mtu. Definition * in the ePAPR v1.1 spec and usage differ, so go with usage. */ of_property_read_u32(pdev->dev.of_node, "max-frame-size", - &priv->max_mtu); + &priv->dev->max_mtu); /* The DMA buffer size already accounts for an alignment bias * to avoid unaligned access exceptions for the NIOS processor, diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index f92cc971..84b4ffb 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1556,9 +1556,6 @@ static int amd8111e_change_mtu(struct net_device *dev, int new_mtu) struct amd8111e_priv *lp = netdev_priv(dev); int err; - if ((new_mtu < AMD8111E_MIN_MTU) || (new_mtu > AMD8111E_MAX_MTU)) - return -EINVAL; - if (!netif_running(dev)) { /* new_mtu will be used * when device starts netxt time @@ -1874,6 +1871,8 @@ static int amd8111e_probe_one(struct pci_dev *pdev, dev->ethtool_ops = &ops; dev->irq =pdev->irq; dev->watchdog_timeo = AMD8111E_TX_TIMEOUT; + dev->min_mtu = AMD8111E_MIN_MTU; + dev->max_mtu = AMD8111E_MAX_MTU; netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32); #if AMD8111E_VLAN_TAG_USED diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index f9df4b5a..b200a78 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -3296,9 +3296,6 @@ bnad_change_mtu(struct net_device *netdev, int new_mtu) struct bnad *bnad = netdev_priv(netdev); u32 rx_count = 0, frame, new_frame; - if (new_mtu + ETH_HLEN < ETH_ZLEN || new_mtu > BNAD_JUMBO_MTU) - return -EINVAL; - mutex_lock(&bnad->conf_mutex); mtu = netdev->mtu; @@ -3465,6 +3462,10 @@ bnad_netdev_init(struct bnad *bnad, bool using_dac) netdev->mem_start = bnad->mmio_start; netdev->mem_end = bnad->mmio_start + bnad->mmio_len - 1; + /* MTU range: 46 - 9000 */ + netdev->min_mtu = ETH_ZLEN - ETH_HLEN; + netdev->max_mtu = BNAD_JUMBO_MTU; + netdev->netdev_ops = &bnad_netdev_ops; bnad_set_ethtool_ops(netdev); } diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 20e7204..e83781a 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -56,7 +56,7 @@ #define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1)) #define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1)) -#define GEM_MTU_MIN_SIZE 68 +#define GEM_MTU_MIN_SIZE ETH_MIN_MTU #define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0) #define MACB_WOL_ENABLED (0x1 << 1) @@ -1986,19 +1986,9 @@ static int macb_close(struct net_device *dev) static int macb_change_mtu(struct net_device *dev, int new_mtu) { - struct macb *bp = netdev_priv(dev); - u32 max_mtu; - if (netif_running(dev)) return -EBUSY; - max_mtu = ETH_DATA_LEN; - if (bp->caps & MACB_CAPS_JUMBO) - max_mtu = gem_readl(bp, JML) - ETH_HLEN - ETH_FCS_LEN; - - if ((new_mtu > max_mtu) || (new_mtu < GEM_MTU_MIN_SIZE)) - return -EINVAL; - dev->mtu = new_mtu; return 0; @@ -3027,6 +3017,13 @@ static int macb_probe(struct platform_device *pdev) goto err_out_free_netdev; } + /* MTU range: 68 - 1500 or 10240 */ + dev->min_mtu = GEM_MTU_MIN_SIZE; + if (bp->caps & MACB_CAPS_JUMBO) + dev->max_mtu = gem_readl(bp, JML) - ETH_HLEN - ETH_FCS_LEN; + else + dev->max_mtu = ETH_DATA_LEN; + mac = of_get_mac_address(np); if (mac) ether_addr_copy(bp->dev->dev_addr, mac); diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 63efa0d..6e72366 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -394,7 +394,7 @@ struct xgmac_priv { }; /* XGMAC Configuration Settings */ -#define MAX_MTU 9000 +#define XGMAC_MAX_MTU 9000 #define PAUSE_TIME 0x400 #define DMA_RX_RING_SZ 256 @@ -1360,20 +1360,6 @@ out: */ static int xgmac_change_mtu(struct net_device *dev, int new_mtu) { - struct xgmac_priv *priv = netdev_priv(dev); - int old_mtu; - - if ((new_mtu < 46) || (new_mtu > MAX_MTU)) { - netdev_err(priv->dev, "invalid MTU, max MTU is: %d\n", MAX_MTU); - return -EINVAL; - } - - old_mtu = dev->mtu; - - /* return early if the buffer sizes will not change */ - if (old_mtu == new_mtu) - return 0; - /* Stop everything, get ready to change the MTU */ if (!netif_running(dev)) return 0; @@ -1804,6 +1790,10 @@ static int xgmac_probe(struct platform_device *pdev) ndev->features |= ndev->hw_features; ndev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 46 - 9000 */ + ndev->min_mtu = ETH_ZLEN - ETH_HLEN; + ndev->max_mtu = XGMAC_MAX_MTU; + /* Get the MAC address */ xgmac_get_mac_addr(priv->base, ndev->dev_addr, 0); if (!is_valid_ether_addr(ndev->dev_addr)) diff --git a/drivers/net/ethernet/chelsio/cxgb/common.h b/drivers/net/ethernet/chelsio/cxgb/common.h index 53b1f94..6916c62 100644 --- a/drivers/net/ethernet/chelsio/cxgb/common.h +++ b/drivers/net/ethernet/chelsio/cxgb/common.h @@ -85,6 +85,11 @@ struct t1_rx_mode { #define SPEED_INVALID 0xffff #define DUPLEX_INVALID 0xff +/* Max frame size PM3393 can handle. Includes Ethernet header and CRC. */ +#define PM3393_MAX_FRAME_SIZE 9600 + +#define VSC7326_MAX_MTU 9600 + enum { CHBT_BOARD_N110, CHBT_BOARD_N210, diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index f5f1b0b..81d1d0b 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -825,8 +825,6 @@ static int t1_change_mtu(struct net_device *dev, int new_mtu) if (!mac->ops->set_mtu) return -EOPNOTSUPP; - if (new_mtu < 68) - return -EINVAL; if ((ret = mac->ops->set_mtu(mac, new_mtu))) return ret; dev->mtu = new_mtu; @@ -1101,6 +1099,22 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netif_napi_add(netdev, &adapter->napi, t1_poll, 64); netdev->ethtool_ops = &t1_ethtool_ops; + + switch (bi->board) { + case CHBT_BOARD_CHT110: + case CHBT_BOARD_N110: + case CHBT_BOARD_N210: + case CHBT_BOARD_CHT210: + netdev->max_mtu = PM3393_MAX_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN); + break; + case CHBT_BOARD_CHN204: + netdev->max_mtu = VSC7326_MAX_MTU; + break; + default: + netdev->max_mtu = ETH_DATA_LEN; + break; + } } if (t1_init_sw_modules(adapter, bi) < 0) { diff --git a/drivers/net/ethernet/chelsio/cxgb/pm3393.c b/drivers/net/ethernet/chelsio/cxgb/pm3393.c index eb462d7..c27908e 100644 --- a/drivers/net/ethernet/chelsio/cxgb/pm3393.c +++ b/drivers/net/ethernet/chelsio/cxgb/pm3393.c @@ -47,9 +47,6 @@ #define OFFSET(REG_ADDR) ((REG_ADDR) << 2) -/* Max frame size PM3393 can handle. Includes Ethernet header and CRC. */ -#define MAX_FRAME_SIZE 9600 - #define IPG 12 #define TXXG_CONF1_VAL ((IPG << SUNI1x10GEXP_BITOFF_TXXG_IPGT) | \ SUNI1x10GEXP_BITMSK_TXXG_32BIT_ALIGN | SUNI1x10GEXP_BITMSK_TXXG_CRCEN | \ @@ -331,10 +328,7 @@ static int pm3393_set_mtu(struct cmac *cmac, int mtu) { int enabled = cmac->instance->enabled; - /* MAX_FRAME_SIZE includes header + FCS, mtu doesn't */ - mtu += 14 + 4; - if (mtu > MAX_FRAME_SIZE) - return -EINVAL; + mtu += ETH_HLEN + ETH_FCS_LEN; /* Disable Rx/Tx MAC before configuring it. */ if (enabled) diff --git a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c index 6f30b6f..bdc895b 100644 --- a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c +++ b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c @@ -11,8 +11,6 @@ /* 30 minutes for full statistics update */ #define MAJOR_UPDATE_TICKS (1800 / STATS_TICK_SECS) -#define MAX_MTU 9600 - /* The egress WM value 0x01a01fff should be used only when the * interface is down (MAC port disabled). This is a workaround * for disabling the T2/MAC flow-control. When the interface is @@ -452,9 +450,6 @@ static int mac_set_mtu(struct cmac *mac, int mtu) { int port = mac->instance->index; - if (mtu > MAX_MTU) - return -EINVAL; - /* max_len includes header and FCS */ vsc_write(mac->adapter, REG_MAX_LEN(port), mtu + 14 + 4); return 0; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 48f82ab..f514faf 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1843,9 +1843,6 @@ static int enic_change_mtu(struct net_device *netdev, int new_mtu) struct enic *enic = netdev_priv(netdev); int running = netif_running(netdev); - if (new_mtu < ENIC_MIN_MTU || new_mtu > ENIC_MAX_MTU) - return -EINVAL; - if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) return -EOPNOTSUPP; @@ -2751,6 +2748,10 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 68 - 9000 */ + netdev->min_mtu = ENIC_MIN_MTU; + netdev->max_mtu = ENIC_MAX_MTU; + err = register_netdev(netdev); if (err) { dev_err(dev, "Cannot register net device, aborting\n"); diff --git a/drivers/net/ethernet/cisco/enic/enic_res.h b/drivers/net/ethernet/cisco/enic/enic_res.h index 69f60af..81f98a8 100644 --- a/drivers/net/ethernet/cisco/enic/enic_res.h +++ b/drivers/net/ethernet/cisco/enic/enic_res.h @@ -30,7 +30,7 @@ #define ENIC_MIN_RQ_DESCS 64 #define ENIC_MAX_RQ_DESCS 4096 -#define ENIC_MIN_MTU 68 +#define ENIC_MIN_MTU ETH_MIN_MTU #define ENIC_MAX_MTU 9000 #define ENIC_MULTICAST_PERFECT_FILTERS 32 diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 4b4f5bc..4092104 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1338,7 +1338,10 @@ static int gfar_probe(struct platform_device *ofdev) /* Fill in the dev structure */ dev->watchdog_timeo = TX_TIMEOUT; + /* MTU range: 50 - 9586 */ dev->mtu = 1500; + dev->min_mtu = 50; + dev->max_mtu = GFAR_JUMBO_FRAME_SIZE - ETH_HLEN; dev->netdev_ops = &gfar_netdev_ops; dev->ethtool_ops = &gfar_ethtool_ops; @@ -2592,12 +2595,6 @@ static int gfar_set_mac_address(struct net_device *dev) static int gfar_change_mtu(struct net_device *dev, int new_mtu) { struct gfar_private *priv = netdev_priv(dev); - int frame_size = new_mtu + ETH_HLEN; - - if ((frame_size < 64) || (frame_size > GFAR_JUMBO_FRAME_SIZE)) { - netif_err(priv, drv, dev, "Invalid MTU setting\n"); - return -EINVAL; - } while (test_and_set_bit_lock(GFAR_RESETTING, &priv->state)) cpu_relax(); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 22e1410..d8e9941 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -446,8 +446,7 @@ int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu) if (mac_cb->mac_type == HNAE_PORT_DEBUG) max_frm = MAC_MAX_MTU_DBG; - if ((new_mtu < MAC_MIN_MTU) || (new_frm > max_frm) || - (new_frm > HNS_RCB_RING_MAX_BD_PER_PKT * buf_size)) + if (new_frm > HNS_RCB_RING_MAX_BD_PER_PKT * buf_size) return -EINVAL; if (!drv->config_max_frame_length) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 059aaed..a720867 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -22,6 +22,7 @@ #include "hnae.h" #include "hns_enet.h" +#include "hns_dsaf_mac.h" #define NIC_MAX_Q_PER_VF 16 #define HNS_NIC_TX_TIMEOUT (5 * HZ) @@ -1405,10 +1406,6 @@ static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu) struct hnae_handle *h = priv->ae_handle; int ret; - /* MTU < 68 is an error and causes problems on some kernels */ - if (new_mtu < 68) - return -EINVAL; - if (!h->dev->ops->set_mtu) return -ENOTSUPP; @@ -1953,14 +1950,20 @@ static int hns_nic_dev_probe(struct platform_device *pdev) NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO; + /* MTU range: 68 - 9578 (v1) or 9706 (v2) */ + ndev->min_mtu = MAC_MIN_MTU; switch (priv->enet_ver) { case AE_VERSION_2: ndev->features |= NETIF_F_TSO | NETIF_F_TSO6; ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6; + ndev->max_mtu = MAC_MAX_MTU_V2 - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); break; default: + ndev->max_mtu = MAC_MAX_MTU - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); break; } diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 280e761..97f6ef1 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -5807,24 +5807,19 @@ static int netdev_change_mtu(struct net_device *dev, int new_mtu) if (hw->dev_count > 1) if (dev != hw_priv->dev) return 0; - if (new_mtu < 60) - return -EINVAL; - if (dev->mtu != new_mtu) { - hw_mtu = new_mtu + ETHERNET_HEADER_SIZE + 4; - if (hw_mtu > MAX_RX_BUF_SIZE) - return -EINVAL; - if (hw_mtu > REGULAR_RX_BUF_SIZE) { - hw->features |= RX_HUGE_FRAME; - hw_mtu = MAX_RX_BUF_SIZE; - } else { - hw->features &= ~RX_HUGE_FRAME; - hw_mtu = REGULAR_RX_BUF_SIZE; - } - hw_mtu = (hw_mtu + 3) & ~3; - hw_priv->mtu = hw_mtu; - dev->mtu = new_mtu; + hw_mtu = new_mtu + ETHERNET_HEADER_SIZE + 4; + if (hw_mtu > REGULAR_RX_BUF_SIZE) { + hw->features |= RX_HUGE_FRAME; + hw_mtu = MAX_RX_BUF_SIZE; + } else { + hw->features &= ~RX_HUGE_FRAME; + hw_mtu = REGULAR_RX_BUF_SIZE; } + hw_mtu = (hw_mtu + 3) & ~3; + hw_priv->mtu = hw_mtu; + dev->mtu = new_mtu; + return 0; } @@ -7099,6 +7094,12 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id) dev->netdev_ops = &netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; + + /* MTU range: 60 - 1894 */ + dev->min_mtu = ETH_ZLEN; + dev->max_mtu = MAX_RX_BUF_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + if (register_netdev(dev)) goto pcidev_init_reg_err; port_set_power_saving(port, true); diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 6d1a956..5ae3f17 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -3232,10 +3232,6 @@ static int myri10ge_change_mtu(struct net_device *dev, int new_mtu) struct myri10ge_priv *mgp = netdev_priv(dev); int error = 0; - if ((new_mtu < 68) || (ETH_HLEN + new_mtu > MYRI10GE_MAX_ETHER_MTU)) { - netdev_err(dev, "new mtu (%d) is not valid\n", new_mtu); - return -EINVAL; - } netdev_info(dev, "changing mtu from %d to %d\n", dev->mtu, new_mtu); if (mgp->running) { /* if we change the mtu on an active device, we must @@ -4086,13 +4082,19 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) myri10ge_setup_dca(mgp); #endif pci_set_drvdata(pdev, mgp); - if ((myri10ge_initial_mtu + ETH_HLEN) > MYRI10GE_MAX_ETHER_MTU) - myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN; - if ((myri10ge_initial_mtu + ETH_HLEN) < 68) - myri10ge_initial_mtu = 68; - netdev->netdev_ops = &myri10ge_netdev_ops; + /* MTU range: 68 - 9000 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN; + + if (myri10ge_initial_mtu > netdev->max_mtu) + myri10ge_initial_mtu = netdev->max_mtu; + if (myri10ge_initial_mtu < netdev->min_mtu) + myri10ge_initial_mtu = netdev->min_mtu; + netdev->mtu = myri10ge_initial_mtu; + + netdev->netdev_ops = &myri10ge_netdev_ops; netdev->hw_features = mgp->features | NETIF_F_RXCSUM; /* fake NETIF_F_HW_VLAN_CTAG_RX for good GRO performance */ diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c index ed89029..22b0821 100644 --- a/drivers/net/ethernet/natsemi/natsemi.c +++ b/drivers/net/ethernet/natsemi/natsemi.c @@ -929,6 +929,10 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent) dev->ethtool_ops = ðtool_ops; + /* MTU range: 64 - 2024 */ + dev->min_mtu = ETH_ZLEN + ETH_FCS_LEN; + dev->max_mtu = NATSEMI_RX_LIMIT - NATSEMI_HEADERS; + if (mtu) dev->mtu = mtu; @@ -2526,9 +2530,6 @@ static void __set_rx_mode(struct net_device *dev) static int natsemi_change_mtu(struct net_device *dev, int new_mtu) { - if (new_mtu < 64 || new_mtu > NATSEMI_RX_LIMIT-NATSEMI_HEADERS) - return -EINVAL; - dev->mtu = new_mtu; /* synchronized against open : rtnl_lock() held by caller */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index aee3fd2..d365760 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2278,11 +2278,6 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) struct nfp_net_rx_ring *tmp_rings; int err; - if (new_mtu < 68 || new_mtu > nn->max_mtu) { - nn_err(nn, "New MTU (%d) is not valid\n", new_mtu); - return -EINVAL; - } - old_mtu = netdev->mtu; old_fl_bufsz = nn->fl_bufsz; new_fl_bufsz = NFP_NET_MAX_PREPEND + ETH_HLEN + VLAN_HLEN * 2 + new_mtu; @@ -2930,6 +2925,11 @@ int nfp_net_netdev_init(struct net_device *netdev) ether_setup(netdev); netdev->netdev_ops = &nfp_net_netdev_ops; netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000); + + /* MTU range: 68 - hw-specific max */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = nn->max_mtu; + netif_carrier_off(netdev); nfp_net_set_ethtool_ops(netdev); diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 9b0d7f4..3913f07 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -3008,17 +3008,12 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu) struct fe_priv *np = netdev_priv(dev); int old_mtu; - if (new_mtu < 64 || new_mtu > np->pkt_limit) - return -EINVAL; - old_mtu = dev->mtu; dev->mtu = new_mtu; /* return early if the buffer sizes will not change */ if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN) return 0; - if (old_mtu == new_mtu) - return 0; /* synchronized against open : rtnl_lock() held by caller */ if (netif_running(dev)) { @@ -5719,6 +5714,10 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) /* Add loopback capability to the device. */ dev->hw_features |= NETIF_F_LOOPBACK; + /* MTU range: 64 - 1500 or 9100 */ + dev->min_mtu = ETH_ZLEN + ETH_FCS_LEN; + dev->max_mtu = np->pkt_limit; + np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG; if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) || (id->driver_data & DEV_HAS_PAUSEFRAME_TX_V2) || diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 3cd87a4..d461f41 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -2260,16 +2260,10 @@ static int pch_gbe_set_mac(struct net_device *netdev, void *addr) static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu) { struct pch_gbe_adapter *adapter = netdev_priv(netdev); - int max_frame; + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; unsigned long old_rx_buffer_len = adapter->rx_buffer_len; int err; - max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || - (max_frame > PCH_GBE_MAX_JUMBO_FRAME_SIZE)) { - netdev_err(netdev, "Invalid MTU setting\n"); - return -EINVAL; - } if (max_frame <= PCH_GBE_FRAME_SIZE_2048) adapter->rx_buffer_len = PCH_GBE_FRAME_SIZE_2048; else if (max_frame <= PCH_GBE_FRAME_SIZE_4096) @@ -2633,6 +2627,11 @@ static int pch_gbe_probe(struct pci_dev *pdev, netdev->features = netdev->hw_features; pch_gbe_set_ethtool_ops(netdev); + /* MTU range: 46 - 10300 */ + netdev->min_mtu = ETH_ZLEN - ETH_HLEN; + netdev->max_mtu = PCH_GBE_MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN); + pch_gbe_mac_load_mac_addr(&adapter->hw); pch_gbe_mac_reset_hw(&adapter->hw); diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index 2f4a837..badfa1d 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -53,7 +53,7 @@ * - Multiqueue RX/TX */ -#define PE_MIN_MTU 64 +#define PE_MIN_MTU (ETH_ZLEN + ETH_HLEN) #define PE_MAX_MTU 9000 #define PE_DEF_MTU ETH_DATA_LEN @@ -1611,9 +1611,6 @@ static int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu) int running; int ret = 0; - if (new_mtu < PE_MIN_MTU || new_mtu > PE_MAX_MTU) - return -EINVAL; - running = netif_running(dev); if (running) { @@ -1635,7 +1632,7 @@ static int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu) } /* Setup checksum channels if large MTU and none already allocated */ - if (new_mtu > 1500 && !mac->num_cs) { + if (new_mtu > PE_DEF_MTU && !mac->num_cs) { pasemi_mac_setup_csrings(mac); if (!mac->num_cs) { ret = -ENOMEM; @@ -1757,6 +1754,11 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev->netdev_ops = &pasemi_netdev_ops; dev->mtu = PE_DEF_MTU; + + /* MTU range: 64 - 9000 */ + dev->min_mtu = PE_MIN_MTU; + dev->max_mtu = PE_MAX_MTU; + /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ mac->bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128; diff --git a/drivers/net/ethernet/qualcomm/qca_framing.h b/drivers/net/ethernet/qualcomm/qca_framing.h index 5d96595..d5e795d 100644 --- a/drivers/net/ethernet/qualcomm/qca_framing.h +++ b/drivers/net/ethernet/qualcomm/qca_framing.h @@ -43,9 +43,9 @@ /* Frame length is invalid */ #define QCAFRM_INVFRAME (QCAFRM_ERR_BASE - 4) -/* Min/Max Ethernet MTU */ -#define QCAFRM_ETHMINMTU 46 -#define QCAFRM_ETHMAXMTU 1500 +/* Min/Max Ethernet MTU: 46/1500 */ +#define QCAFRM_ETHMINMTU (ETH_ZLEN - ETH_HLEN) +#define QCAFRM_ETHMAXMTU ETH_DATA_LEN /* Min/Max frame lengths */ #define QCAFRM_ETHMINLEN (QCAFRM_ETHMINMTU + ETH_HLEN) diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 6e2add9..513e6c7 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -780,24 +780,12 @@ qcaspi_netdev_uninit(struct net_device *dev) dev_kfree_skb(qca->rx_skb); } -static int -qcaspi_netdev_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < QCAFRM_ETHMINMTU) || (new_mtu > QCAFRM_ETHMAXMTU)) - return -EINVAL; - - dev->mtu = new_mtu; - - return 0; -} - static const struct net_device_ops qcaspi_netdev_ops = { .ndo_init = qcaspi_netdev_init, .ndo_uninit = qcaspi_netdev_uninit, .ndo_open = qcaspi_netdev_open, .ndo_stop = qcaspi_netdev_close, .ndo_start_xmit = qcaspi_netdev_xmit, - .ndo_change_mtu = qcaspi_netdev_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_tx_timeout = qcaspi_netdev_tx_timeout, .ndo_validate_addr = eth_validate_addr, @@ -814,6 +802,10 @@ qcaspi_netdev_setup(struct net_device *dev) dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->tx_queue_len = 100; + /* MTU range: 46 - 1500 */ + dev->min_mtu = QCAFRM_ETHMINMTU; + dev->max_mtu = QCAFRM_ETHMAXMTU; + qca = netdev_priv(dev); memset(qca, 0, sizeof(struct qcaspi)); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 5424fb3..9b64f36 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1953,12 +1953,6 @@ static int rocker_port_change_mtu(struct net_device *dev, int new_mtu) int running = netif_running(dev); int err; -#define ROCKER_PORT_MIN_MTU 68 -#define ROCKER_PORT_MAX_MTU 9000 - - if (new_mtu < ROCKER_PORT_MIN_MTU || new_mtu > ROCKER_PORT_MAX_MTU) - return -EINVAL; - if (running) rocker_port_stop(dev); @@ -2536,6 +2530,8 @@ static void rocker_port_dev_addr_init(struct rocker_port *rocker_port) } } +#define ROCKER_PORT_MIN_MTU ETH_MIN_MTU +#define ROCKER_PORT_MAX_MTU 9000 static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) { const struct pci_dev *pdev = rocker->pdev; @@ -2570,6 +2566,10 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_SG; + /* MTU range: 68 - 9000 */ + dev->min_mtu = ROCKER_PORT_MIN_MTU; + dev->max_mtu = ROCKER_PORT_MAX_MTU; + err = rocker_world_port_pre_init(rocker_port); if (err) { dev_err(&pdev->dev, "port world pre-init failed\n"); diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index ea44a24..5dbe406 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -1820,19 +1820,6 @@ static int sxgbe_set_features(struct net_device *dev, */ static int sxgbe_change_mtu(struct net_device *dev, int new_mtu) { - /* RFC 791, page 25, "Every internet module must be able to forward - * a datagram of 68 octets without further fragmentation." - */ - if (new_mtu < MIN_MTU || (new_mtu > MAX_MTU)) { - netdev_err(dev, "invalid MTU, MTU should be in between %d and %d\n", - MIN_MTU, MAX_MTU); - return -EINVAL; - } - - /* Return if the buffer sizes will not change */ - if (dev->mtu == new_mtu) - return 0; - dev->mtu = new_mtu; if (!netif_running(dev)) @@ -2144,6 +2131,10 @@ struct sxgbe_priv_data *sxgbe_drv_probe(struct device *device, /* assign filtering support */ ndev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 68 - 9000 */ + ndev->min_mtu = MIN_MTU; + ndev->max_mtu = MAX_MTU; + priv->msg_enable = netif_msg_init(debug, default_msg_level); /* Enable TCP segmentation offload for all DMA channels */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index da0b80a..fa4a82f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2715,27 +2715,11 @@ static void stmmac_set_rx_mode(struct net_device *dev) */ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) { - struct stmmac_priv *priv = netdev_priv(dev); - int max_mtu; - if (netif_running(dev)) { pr_err("%s: must be stopped to change its MTU\n", dev->name); return -EBUSY; } - if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00)) - max_mtu = JUMBO_LEN; - else - max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); - - if (priv->plat->maxmtu < max_mtu) - max_mtu = priv->plat->maxmtu; - - if ((new_mtu < 46) || (new_mtu > max_mtu)) { - pr_err("%s: invalid MTU, max MTU is: %d\n", dev->name, max_mtu); - return -EINVAL; - } - dev->mtu = new_mtu; netdev_update_features(dev); @@ -3317,6 +3301,15 @@ int stmmac_dvr_probe(struct device *device, #endif priv->msg_enable = netif_msg_init(debug, default_msg_level); + /* MTU range: 46 - hw-specific max */ + ndev->min_mtu = ETH_ZLEN - ETH_HLEN; + if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00)) + ndev->max_mtu = JUMBO_LEN; + else + ndev->max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); + if (priv->plat->maxmtu < ndev->max_mtu) + ndev->max_mtu = priv->plat->maxmtu; + if (flow_ctrl) priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */ diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 7108c68..baa3e4a 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -761,16 +761,6 @@ static int bdx_change_mtu(struct net_device *ndev, int new_mtu) { ENTER; - if (new_mtu == ndev->mtu) - RET(0); - - /* enforce minimum frame size */ - if (new_mtu < ETH_ZLEN) { - netdev_err(ndev, "mtu %d is less then minimal %d\n", - new_mtu, ETH_ZLEN); - RET(-EINVAL); - } - ndev->mtu = new_mtu; if (netif_running(ndev)) { bdx_close(ndev); @@ -2057,6 +2047,10 @@ bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef BDX_LLTX ndev->features |= NETIF_F_LLTX; #endif + /* MTU range: 60 - 16384 */ + ndev->min_mtu = ETH_ZLEN; + ndev->max_mtu = BDX_MAX_MTU; + spin_lock_init(&priv->tx_lock); /*bdx_hw_reset(priv); */ diff --git a/drivers/net/ethernet/tehuti/tehuti.h b/drivers/net/ethernet/tehuti/tehuti.h index 709ebd6..8e7b4c9 100644 --- a/drivers/net/ethernet/tehuti/tehuti.h +++ b/drivers/net/ethernet/tehuti/tehuti.h @@ -74,6 +74,9 @@ * ifcontig eth1 txqueuelen 3000 - to change it at runtime */ #define BDX_NDEV_TXQ_LEN 3000 +/* Max MTU for Jumbo Frame mode, per tehutinetworks.net Features FAQ is 16k */ +#define BDX_MAX_MTU (16 * 1024) + #define FIFO_SIZE 4096 #define FIFO_EXTRA_SPACE 1024 diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 3251666..11609d5 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1766,21 +1766,6 @@ out: return (ret == 0) ? 0 : err; } -static int netcp_ndo_change_mtu(struct net_device *ndev, int new_mtu) -{ - struct netcp_intf *netcp = netdev_priv(ndev); - - /* MTU < 68 is an error for IPv4 traffic */ - if ((new_mtu < 68) || - (new_mtu > (NETCP_MAX_FRAME_SIZE - ETH_HLEN - ETH_FCS_LEN))) { - dev_err(netcp->ndev_dev, "Invalid mtu size = %d\n", new_mtu); - return -EINVAL; - } - - ndev->mtu = new_mtu; - return 0; -} - static void netcp_ndo_tx_timeout(struct net_device *ndev) { struct netcp_intf *netcp = netdev_priv(ndev); @@ -1886,7 +1871,6 @@ static const struct net_device_ops netcp_netdev_ops = { .ndo_start_xmit = netcp_ndo_start_xmit, .ndo_set_rx_mode = netcp_set_rx_mode, .ndo_do_ioctl = netcp_ndo_ioctl, - .ndo_change_mtu = netcp_ndo_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = netcp_rx_add_vid, @@ -1923,6 +1907,10 @@ static int netcp_create_interface(struct netcp_device *netcp_device, ndev->hw_features = ndev->features; ndev->vlan_features |= NETIF_F_SG; + /* MTU range: 68 - 9486 */ + ndev->min_mtu = ETH_MIN_MTU; + ndev->max_mtu = NETCP_MAX_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); + netcp = netdev_priv(ndev); spin_lock_init(&netcp->lock); INIT_LIST_HEAD(&netcp->module_head); diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index 908e72e..4716e60 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -2284,13 +2284,6 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) struct velocity_info *vptr = netdev_priv(dev); int ret = 0; - if ((new_mtu < VELOCITY_MIN_MTU) || new_mtu > (VELOCITY_MAX_MTU)) { - VELOCITY_PRT(MSG_LEVEL_ERR, KERN_NOTICE "%s: Invalid MTU.\n", - vptr->netdev->name); - ret = -EINVAL; - goto out_0; - } - if (!netif_running(dev)) { dev->mtu = new_mtu; goto out_0; @@ -2864,6 +2857,10 @@ static int velocity_probe(struct device *dev, int irq, NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_IP_CSUM; + /* MTU range: 64 - 9000 */ + netdev->min_mtu = VELOCITY_MIN_MTU; + netdev->max_mtu = VELOCITY_MAX_MTU; + ret = register_netdev(netdev); if (ret < 0) goto err_iounmap; diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index d02e3e3..8130dfe 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -259,17 +259,6 @@ static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu) #endif int mtu_overhead = ETH_HLEN + ETH_FCS_LEN + vlan_bytes; - /* - * Limit the MTU to make sure the ethernet packets are between - * 64 bytes and 65535 bytes. - */ - if ((new_mtu + mtu_overhead < VLAN_ETH_ZLEN) || - (new_mtu + mtu_overhead > OCTEON_MAX_MTU)) { - pr_err("MTU must be between %d and %d.\n", - VLAN_ETH_ZLEN - mtu_overhead, - OCTEON_MAX_MTU - mtu_overhead); - return -EINVAL; - } dev->mtu = new_mtu; if ((interface < 2) && @@ -457,7 +446,7 @@ int cvm_oct_common_init(struct net_device *dev) dev->ethtool_ops = &cvm_oct_ethtool_ops; cvm_oct_set_mac_filter(dev); - dev->netdev_ops->ndo_change_mtu(dev, dev->mtu); + dev_set_mtu(dev, dev->mtu); /* * Zero out stats for port so we won't mistakenly show @@ -685,6 +674,11 @@ static int cvm_oct_probe(struct platform_device *pdev) int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; int qos; struct device_node *pip; + int mtu_overhead = ETH_HLEN + ETH_FCS_LEN; + +#if IS_ENABLED(CONFIG_VLAN_8021Q) + mtu_overhead += VLAN_HLEN; +#endif octeon_mdiobus_force_mod_depencency(); @@ -783,6 +777,8 @@ static int cvm_oct_probe(struct platform_device *pdev) strcpy(dev->name, "pow%d"); for (qos = 0; qos < 16; qos++) skb_queue_head_init(&priv->tx_free_list[qos]); + dev->min_mtu = VLAN_ETH_ZLEN - mtu_overhead; + dev->max_mtu = OCTEON_MAX_MTU - mtu_overhead; if (register_netdev(dev) < 0) { pr_err("Failed to register ethernet device for POW\n"); @@ -836,6 +832,8 @@ static int cvm_oct_probe(struct platform_device *pdev) for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++) cvmx_fau_atomic_write32(priv->fau + qos * 4, 0); + dev->min_mtu = VLAN_ETH_ZLEN - mtu_overhead; + dev->max_mtu = OCTEON_MAX_MTU - mtu_overhead; switch (priv->imode) { /* These types don't support ports to IPD/PKO */ -- cgit v1.1 From 1a776b9ce82d2df9c7ba140588a49721ffb0dc8f Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 17 Oct 2016 14:28:35 +0200 Subject: bpf: add various tests around spill/fill of regs Add several spill/fill tests. Besides others, one that performs xadd on the spilled register, one ldx/stx test where different types are spilled from two branches and read out from common path. Verfier does handle all correctly. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/test_verifier.c | 116 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c index 369ffaa..45cf740 100644 --- a/samples/bpf/test_verifier.c +++ b/samples/bpf/test_verifier.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1128,6 +1129,108 @@ static struct bpf_test tests[] = { .result = REJECT, }, { + "unpriv: spill/fill of ctx", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "unpriv: spill/fill of ctx 2", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_hash_recalc), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of ctx 3", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_hash_recalc), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "R1 type=fp expected=ctx", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of ctx 4", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_10, BPF_REG_0, -8, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_hash_recalc), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "R1 type=inv expected=ctx", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of different pointers stx", + .insns = { + BPF_MOV64_IMM(BPF_REG_3, 42), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, + offsetof(struct __sk_buff, mark)), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "same insn cannot be used with different pointers", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of different pointers ldx", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, + -(__s32)offsetof(struct bpf_perf_event_data, + sample_period) - 8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, + offsetof(struct bpf_perf_event_data, + sample_period)), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "same insn cannot be used with different pointers", + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { "unpriv: write pointer into map elem value", .insns = { BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), @@ -1194,6 +1297,19 @@ static struct bpf_test tests[] = { .result = REJECT, }, { + "unpriv: spill/fill frame pointer", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "frame pointer is read only", + .result = REJECT, + }, + { "unpriv: cmp of frame pointer", .insns = { BPF_JMP_IMM(BPF_JEQ, BPF_REG_10, 0, 0), -- cgit v1.1 From 5aa5bd14c5f8660c64ceedf14a549781be47e53d Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 17 Oct 2016 14:28:36 +0200 Subject: bpf: add initial suite for selftests Add a start of a test suite for kernel selftests. This moves test_verifier and test_maps over to tools/testing/selftests/bpf/ along with various code improvements and also adds a script for invoking test_bpf module. The test suite can simply be run via selftest framework, f.e.: # cd tools/testing/selftests/bpf/ # make # make run_tests Both test_verifier and test_maps were kind of misplaced in samples/bpf/ directory and we were looking into adding them to selftests for a while now, so it can be picked up by kbuild bot et al and hopefully also get more exposure and thus new test case additions. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- MAINTAINERS | 3 +- samples/bpf/Makefile | 3 - samples/bpf/test_maps.c | 503 ----- samples/bpf/test_verifier.c | 2645 ------------------------- tools/include/linux/filter.h | 24 + tools/testing/selftests/Makefile | 3 +- tools/testing/selftests/bpf/.gitignore | 2 + tools/testing/selftests/bpf/Makefile | 13 + tools/testing/selftests/bpf/bpf_sys.h | 108 ++ tools/testing/selftests/bpf/config | 5 + tools/testing/selftests/bpf/test_kmod.sh | 39 + tools/testing/selftests/bpf/test_maps.c | 525 +++++ tools/testing/selftests/bpf/test_verifier.c | 2764 +++++++++++++++++++++++++++ 13 files changed, 3484 insertions(+), 3153 deletions(-) delete mode 100644 samples/bpf/test_maps.c delete mode 100644 samples/bpf/test_verifier.c create mode 100644 tools/testing/selftests/bpf/.gitignore create mode 100644 tools/testing/selftests/bpf/Makefile create mode 100644 tools/testing/selftests/bpf/bpf_sys.h create mode 100644 tools/testing/selftests/bpf/config create mode 100755 tools/testing/selftests/bpf/test_kmod.sh create mode 100644 tools/testing/selftests/bpf/test_maps.c create mode 100644 tools/testing/selftests/bpf/test_verifier.c diff --git a/MAINTAINERS b/MAINTAINERS index 1fc66f0a..f18b546 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2521,6 +2521,8 @@ L: netdev@vger.kernel.org L: linux-kernel@vger.kernel.org S: Supported F: kernel/bpf/ +F: tools/testing/selftests/bpf/ +F: lib/test_bpf.c BROADCOM B44 10/100 ETHERNET DRIVER M: Michael Chan @@ -8413,7 +8415,6 @@ F: include/uapi/linux/net_namespace.h F: tools/net/ F: tools/testing/selftests/net/ F: lib/random32.c -F: lib/test_bpf.c NETWORKING [IPv4/IPv6] M: "David S. Miller" diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 12b7304..5c53fdb 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -2,7 +2,6 @@ obj- := dummy.o # List of programs to build -hostprogs-y := test_verifier test_maps hostprogs-y += sock_example hostprogs-y += fds_example hostprogs-y += sockex1 @@ -28,8 +27,6 @@ hostprogs-y += test_current_task_under_cgroup hostprogs-y += trace_event hostprogs-y += sampleip -test_verifier-objs := test_verifier.o libbpf.o -test_maps-objs := test_maps.o libbpf.o sock_example-objs := sock_example.o libbpf.o fds_example-objs := bpf_load.o libbpf.o fds_example.o sockex1-objs := bpf_load.o libbpf.o sockex1_user.o diff --git a/samples/bpf/test_maps.c b/samples/bpf/test_maps.c deleted file mode 100644 index cce2b59..0000000 --- a/samples/bpf/test_maps.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Testsuite for eBPF maps - * - * Copyright (c) 2014 PLUMgrid, http://plumgrid.com - * Copyright (c) 2016 Facebook - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "libbpf.h" - -static int map_flags; - -/* sanity tests for map API */ -static void test_hashmap_sanity(int i, void *data) -{ - long long key, next_key, value; - int map_fd; - - map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), - 2, map_flags); - if (map_fd < 0) { - printf("failed to create hashmap '%s'\n", strerror(errno)); - exit(1); - } - - key = 1; - value = 1234; - /* insert key=1 element */ - assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0); - - value = 0; - /* BPF_NOEXIST means: add new element if it doesn't exist */ - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && - /* key=1 already exists */ - errno == EEXIST); - - assert(bpf_update_elem(map_fd, &key, &value, -1) == -1 && errno == EINVAL); - - /* check that key=1 can be found */ - assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234); - - key = 2; - /* check that key=2 is not found */ - assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT); - - /* BPF_EXIST means: update existing element */ - assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 && - /* key=2 is not there */ - errno == ENOENT); - - /* insert key=2 element */ - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0); - - /* key=1 and key=2 were inserted, check that key=0 cannot be inserted - * due to max_entries limit - */ - key = 0; - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && - errno == E2BIG); - - /* update existing element, thought the map is full */ - key = 1; - assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == 0); - key = 2; - assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0); - key = 1; - assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0); - - /* check that key = 0 doesn't exist */ - key = 0; - assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT); - - /* iterate over two elements */ - assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 && - (next_key == 1 || next_key == 2)); - assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 && - (next_key == 1 || next_key == 2)); - assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 && - errno == ENOENT); - - /* delete both elements */ - key = 1; - assert(bpf_delete_elem(map_fd, &key) == 0); - key = 2; - assert(bpf_delete_elem(map_fd, &key) == 0); - assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT); - - key = 0; - /* check that map is empty */ - assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 && - errno == ENOENT); - close(map_fd); -} - -/* sanity tests for percpu map API */ -static void test_percpu_hashmap_sanity(int task, void *data) -{ - long long key, next_key; - int expected_key_mask = 0; - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - long long value[nr_cpus]; - int map_fd, i; - - map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key), - sizeof(value[0]), 2, map_flags); - if (map_fd < 0) { - printf("failed to create hashmap '%s'\n", strerror(errno)); - exit(1); - } - - for (i = 0; i < nr_cpus; i++) - value[i] = i + 100; - key = 1; - /* insert key=1 element */ - assert(!(expected_key_mask & key)); - assert(bpf_update_elem(map_fd, &key, value, BPF_ANY) == 0); - expected_key_mask |= key; - - /* BPF_NOEXIST means: add new element if it doesn't exist */ - assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 && - /* key=1 already exists */ - errno == EEXIST); - - /* -1 is an invalid flag */ - assert(bpf_update_elem(map_fd, &key, value, -1) == -1 && - errno == EINVAL); - - /* check that key=1 can be found. value could be 0 if the lookup - * was run from a different cpu. - */ - value[0] = 1; - assert(bpf_lookup_elem(map_fd, &key, value) == 0 && value[0] == 100); - - key = 2; - /* check that key=2 is not found */ - assert(bpf_lookup_elem(map_fd, &key, value) == -1 && errno == ENOENT); - - /* BPF_EXIST means: update existing element */ - assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == -1 && - /* key=2 is not there */ - errno == ENOENT); - - /* insert key=2 element */ - assert(!(expected_key_mask & key)); - assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0); - expected_key_mask |= key; - - /* key=1 and key=2 were inserted, check that key=0 cannot be inserted - * due to max_entries limit - */ - key = 0; - assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 && - errno == E2BIG); - - /* check that key = 0 doesn't exist */ - assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT); - - /* iterate over two elements */ - while (!bpf_get_next_key(map_fd, &key, &next_key)) { - assert((expected_key_mask & next_key) == next_key); - expected_key_mask &= ~next_key; - - assert(bpf_lookup_elem(map_fd, &next_key, value) == 0); - for (i = 0; i < nr_cpus; i++) - assert(value[i] == i + 100); - - key = next_key; - } - assert(errno == ENOENT); - - /* Update with BPF_EXIST */ - key = 1; - assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == 0); - - /* delete both elements */ - key = 1; - assert(bpf_delete_elem(map_fd, &key) == 0); - key = 2; - assert(bpf_delete_elem(map_fd, &key) == 0); - assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT); - - key = 0; - /* check that map is empty */ - assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 && - errno == ENOENT); - close(map_fd); -} - -static void test_arraymap_sanity(int i, void *data) -{ - int key, next_key, map_fd; - long long value; - - map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), - 2, 0); - if (map_fd < 0) { - printf("failed to create arraymap '%s'\n", strerror(errno)); - exit(1); - } - - key = 1; - value = 1234; - /* insert key=1 element */ - assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0); - - value = 0; - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && - errno == EEXIST); - - /* check that key=1 can be found */ - assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234); - - key = 0; - /* check that key=0 is also found and zero initialized */ - assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0); - - - /* key=0 and key=1 were inserted, check that key=2 cannot be inserted - * due to max_entries limit - */ - key = 2; - assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 && - errno == E2BIG); - - /* check that key = 2 doesn't exist */ - assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT); - - /* iterate over two elements */ - assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 && - next_key == 0); - assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 && - next_key == 1); - assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 && - errno == ENOENT); - - /* delete shouldn't succeed */ - key = 1; - assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL); - - close(map_fd); -} - -static void test_percpu_arraymap_many_keys(void) -{ - unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - unsigned nr_keys = 20000; - long values[nr_cpus]; - int key, map_fd, i; - - map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), - sizeof(values[0]), nr_keys, 0); - if (map_fd < 0) { - printf("failed to create per-cpu arraymap '%s'\n", - strerror(errno)); - exit(1); - } - - for (i = 0; i < nr_cpus; i++) - values[i] = i + 10; - - for (key = 0; key < nr_keys; key++) - assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0); - - for (key = 0; key < nr_keys; key++) { - for (i = 0; i < nr_cpus; i++) - values[i] = 0; - assert(bpf_lookup_elem(map_fd, &key, values) == 0); - for (i = 0; i < nr_cpus; i++) - assert(values[i] == i + 10); - } - - close(map_fd); -} - -static void test_percpu_arraymap_sanity(int i, void *data) -{ - unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - long values[nr_cpus]; - int key, next_key, map_fd; - - map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), - sizeof(values[0]), 2, 0); - if (map_fd < 0) { - printf("failed to create arraymap '%s'\n", strerror(errno)); - exit(1); - } - - for (i = 0; i < nr_cpus; i++) - values[i] = i + 100; - - key = 1; - /* insert key=1 element */ - assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0); - - values[0] = 0; - assert(bpf_update_elem(map_fd, &key, values, BPF_NOEXIST) == -1 && - errno == EEXIST); - - /* check that key=1 can be found */ - assert(bpf_lookup_elem(map_fd, &key, values) == 0 && values[0] == 100); - - key = 0; - /* check that key=0 is also found and zero initialized */ - assert(bpf_lookup_elem(map_fd, &key, values) == 0 && - values[0] == 0 && values[nr_cpus - 1] == 0); - - - /* check that key=2 cannot be inserted due to max_entries limit */ - key = 2; - assert(bpf_update_elem(map_fd, &key, values, BPF_EXIST) == -1 && - errno == E2BIG); - - /* check that key = 2 doesn't exist */ - assert(bpf_lookup_elem(map_fd, &key, values) == -1 && errno == ENOENT); - - /* iterate over two elements */ - assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 && - next_key == 0); - assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 && - next_key == 1); - assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 && - errno == ENOENT); - - /* delete shouldn't succeed */ - key = 1; - assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL); - - close(map_fd); -} - -#define MAP_SIZE (32 * 1024) -static void test_map_large(void) -{ - struct bigkey { - int a; - char b[116]; - long long c; - } key; - int map_fd, i, value; - - /* allocate 4Mbyte of memory */ - map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), - MAP_SIZE, map_flags); - if (map_fd < 0) { - printf("failed to create large map '%s'\n", strerror(errno)); - exit(1); - } - - for (i = 0; i < MAP_SIZE; i++) { - key = (struct bigkey) {.c = i}; - value = i; - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0); - } - key.c = -1; - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && - errno == E2BIG); - - /* iterate through all elements */ - for (i = 0; i < MAP_SIZE; i++) - assert(bpf_get_next_key(map_fd, &key, &key) == 0); - assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT); - - key.c = 0; - assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0); - key.a = 1; - assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT); - - close(map_fd); -} - -/* fork N children and wait for them to complete */ -static void run_parallel(int tasks, void (*fn)(int i, void *data), void *data) -{ - pid_t pid[tasks]; - int i; - - for (i = 0; i < tasks; i++) { - pid[i] = fork(); - if (pid[i] == 0) { - fn(i, data); - exit(0); - } else if (pid[i] == -1) { - printf("couldn't spawn #%d process\n", i); - exit(1); - } - } - for (i = 0; i < tasks; i++) { - int status; - - assert(waitpid(pid[i], &status, 0) == pid[i]); - assert(status == 0); - } -} - -static void test_map_stress(void) -{ - run_parallel(100, test_hashmap_sanity, NULL); - run_parallel(100, test_percpu_hashmap_sanity, NULL); - run_parallel(100, test_arraymap_sanity, NULL); - run_parallel(100, test_percpu_arraymap_sanity, NULL); -} - -#define TASKS 1024 -#define DO_UPDATE 1 -#define DO_DELETE 0 -static void do_work(int fn, void *data) -{ - int map_fd = ((int *)data)[0]; - int do_update = ((int *)data)[1]; - int i; - int key, value; - - for (i = fn; i < MAP_SIZE; i += TASKS) { - key = value = i; - if (do_update) { - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0); - assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == 0); - } else { - assert(bpf_delete_elem(map_fd, &key) == 0); - } - } -} - -static void test_map_parallel(void) -{ - int i, map_fd, key = 0, value = 0; - int data[2]; - - map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), - MAP_SIZE, map_flags); - if (map_fd < 0) { - printf("failed to create map for parallel test '%s'\n", - strerror(errno)); - exit(1); - } - - data[0] = map_fd; - data[1] = DO_UPDATE; - /* use the same map_fd in children to add elements to this map - * child_0 adds key=0, key=1024, key=2048, ... - * child_1 adds key=1, key=1025, key=2049, ... - * child_1023 adds key=1023, ... - */ - run_parallel(TASKS, do_work, data); - - /* check that key=0 is already there */ - assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && - errno == EEXIST); - - /* check that all elements were inserted */ - key = -1; - for (i = 0; i < MAP_SIZE; i++) - assert(bpf_get_next_key(map_fd, &key, &key) == 0); - assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT); - - /* another check for all elements */ - for (i = 0; i < MAP_SIZE; i++) { - key = MAP_SIZE - i - 1; - assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && - value == key); - } - - /* now let's delete all elemenets in parallel */ - data[1] = DO_DELETE; - run_parallel(TASKS, do_work, data); - - /* nothing should be left */ - key = -1; - assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT); -} - -static void run_all_tests(void) -{ - test_hashmap_sanity(0, NULL); - test_percpu_hashmap_sanity(0, NULL); - test_arraymap_sanity(0, NULL); - test_percpu_arraymap_sanity(0, NULL); - test_percpu_arraymap_many_keys(); - - test_map_large(); - test_map_parallel(); - test_map_stress(); -} - -int main(void) -{ - map_flags = 0; - run_all_tests(); - map_flags = BPF_F_NO_PREALLOC; - run_all_tests(); - printf("test_maps: OK\n"); - return 0; -} diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c deleted file mode 100644 index 45cf740..0000000 --- a/samples/bpf/test_verifier.c +++ /dev/null @@ -1,2645 +0,0 @@ -/* - * Testsuite for eBPF verifier - * - * Copyright (c) 2014 PLUMgrid, http://plumgrid.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "libbpf.h" - -#define MAX_INSNS 512 -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) - -#define MAX_FIXUPS 8 - -struct bpf_test { - const char *descr; - struct bpf_insn insns[MAX_INSNS]; - int fixup[MAX_FIXUPS]; - int prog_array_fixup[MAX_FIXUPS]; - int test_val_map_fixup[MAX_FIXUPS]; - const char *errstr; - const char *errstr_unpriv; - enum { - UNDEF, - ACCEPT, - REJECT - } result, result_unpriv; - enum bpf_prog_type prog_type; -}; - -/* Note we want this to be 64 bit aligned so that the end of our array is - * actually the end of the structure. - */ -#define MAX_ENTRIES 11 -struct test_val { - unsigned index; - int foo[MAX_ENTRIES]; -}; - -struct other_val { - unsigned int action[32]; -}; - -static struct bpf_test tests[] = { - { - "add+sub+mul", - .insns = { - BPF_MOV64_IMM(BPF_REG_1, 1), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 2), - BPF_MOV64_IMM(BPF_REG_2, 3), - BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -1), - BPF_ALU64_IMM(BPF_MUL, BPF_REG_1, 3), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - }, - { - "unreachable", - .insns = { - BPF_EXIT_INSN(), - BPF_EXIT_INSN(), - }, - .errstr = "unreachable", - .result = REJECT, - }, - { - "unreachable2", - .insns = { - BPF_JMP_IMM(BPF_JA, 0, 0, 1), - BPF_JMP_IMM(BPF_JA, 0, 0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "unreachable", - .result = REJECT, - }, - { - "out of range jump", - .insns = { - BPF_JMP_IMM(BPF_JA, 0, 0, 1), - BPF_EXIT_INSN(), - }, - .errstr = "jump out of range", - .result = REJECT, - }, - { - "out of range jump2", - .insns = { - BPF_JMP_IMM(BPF_JA, 0, 0, -2), - BPF_EXIT_INSN(), - }, - .errstr = "jump out of range", - .result = REJECT, - }, - { - "test1 ld_imm64", - .insns = { - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), - BPF_LD_IMM64(BPF_REG_0, 0), - BPF_LD_IMM64(BPF_REG_0, 0), - BPF_LD_IMM64(BPF_REG_0, 1), - BPF_LD_IMM64(BPF_REG_0, 1), - BPF_MOV64_IMM(BPF_REG_0, 2), - BPF_EXIT_INSN(), - }, - .errstr = "invalid BPF_LD_IMM insn", - .errstr_unpriv = "R1 pointer comparison", - .result = REJECT, - }, - { - "test2 ld_imm64", - .insns = { - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), - BPF_LD_IMM64(BPF_REG_0, 0), - BPF_LD_IMM64(BPF_REG_0, 0), - BPF_LD_IMM64(BPF_REG_0, 1), - BPF_LD_IMM64(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .errstr = "invalid BPF_LD_IMM insn", - .errstr_unpriv = "R1 pointer comparison", - .result = REJECT, - }, - { - "test3 ld_imm64", - .insns = { - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), - BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0), - BPF_LD_IMM64(BPF_REG_0, 0), - BPF_LD_IMM64(BPF_REG_0, 0), - BPF_LD_IMM64(BPF_REG_0, 1), - BPF_LD_IMM64(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_ld_imm64 insn", - .result = REJECT, - }, - { - "test4 ld_imm64", - .insns = { - BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_ld_imm64 insn", - .result = REJECT, - }, - { - "test5 ld_imm64", - .insns = { - BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0), - }, - .errstr = "invalid bpf_ld_imm64 insn", - .result = REJECT, - }, - { - "no bpf_exit", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_0, BPF_REG_2), - }, - .errstr = "jump out of range", - .result = REJECT, - }, - { - "loop (back-edge)", - .insns = { - BPF_JMP_IMM(BPF_JA, 0, 0, -1), - BPF_EXIT_INSN(), - }, - .errstr = "back-edge", - .result = REJECT, - }, - { - "loop2 (back-edge)", - .insns = { - BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), - BPF_JMP_IMM(BPF_JA, 0, 0, -4), - BPF_EXIT_INSN(), - }, - .errstr = "back-edge", - .result = REJECT, - }, - { - "conditional loop", - .insns = { - BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -3), - BPF_EXIT_INSN(), - }, - .errstr = "back-edge", - .result = REJECT, - }, - { - "read uninitialized register", - .insns = { - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_EXIT_INSN(), - }, - .errstr = "R2 !read_ok", - .result = REJECT, - }, - { - "read invalid register", - .insns = { - BPF_MOV64_REG(BPF_REG_0, -1), - BPF_EXIT_INSN(), - }, - .errstr = "R15 is invalid", - .result = REJECT, - }, - { - "program doesn't init R0 before exit", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1), - BPF_EXIT_INSN(), - }, - .errstr = "R0 !read_ok", - .result = REJECT, - }, - { - "program doesn't init R0 before exit in all branches", - .insns = { - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2), - BPF_EXIT_INSN(), - }, - .errstr = "R0 !read_ok", - .errstr_unpriv = "R1 pointer comparison", - .result = REJECT, - }, - { - "stack out of bounds", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid stack", - .result = REJECT, - }, - { - "invalid call insn1", - .insns = { - BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, 0, 0, 0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "BPF_CALL uses reserved", - .result = REJECT, - }, - { - "invalid call insn2", - .insns = { - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 1, 0), - BPF_EXIT_INSN(), - }, - .errstr = "BPF_CALL uses reserved", - .result = REJECT, - }, - { - "invalid function call", - .insns = { - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 1234567), - BPF_EXIT_INSN(), - }, - .errstr = "invalid func 1234567", - .result = REJECT, - }, - { - "uninitialized stack1", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_EXIT_INSN(), - }, - .fixup = {2}, - .errstr = "invalid indirect read from stack", - .result = REJECT, - }, - { - "uninitialized stack2", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -8), - BPF_EXIT_INSN(), - }, - .errstr = "invalid read from stack", - .result = REJECT, - }, - { - "invalid argument register", - .insns = { - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_cgroup_classid), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_cgroup_classid), - BPF_EXIT_INSN(), - }, - .errstr = "R1 !read_ok", - .result = REJECT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "non-invalid argument register", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_1), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_cgroup_classid), - BPF_ALU64_REG(BPF_MOV, BPF_REG_1, BPF_REG_6), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_cgroup_classid), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "check valid spill/fill", - .insns = { - /* spill R1(ctx) into stack */ - BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), - - /* fill it back into R2 */ - BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8), - - /* should be able to access R0 = *(R2 + 8) */ - /* BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 8), */ - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R0 leaks addr", - .result = ACCEPT, - .result_unpriv = REJECT, - }, - { - "check valid spill/fill, skb mark", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_1), - BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, - offsetof(struct __sk_buff, mark)), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .result_unpriv = ACCEPT, - }, - { - "check corrupted spill/fill", - .insns = { - /* spill R1(ctx) into stack */ - BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), - - /* mess up with R1 pointer on stack */ - BPF_ST_MEM(BPF_B, BPF_REG_10, -7, 0x23), - - /* fill back into R0 should fail */ - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), - - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "attempt to corrupt spilled", - .errstr = "corrupted spill", - .result = REJECT, - }, - { - "invalid src register in STX", - .insns = { - BPF_STX_MEM(BPF_B, BPF_REG_10, -1, -1), - BPF_EXIT_INSN(), - }, - .errstr = "R15 is invalid", - .result = REJECT, - }, - { - "invalid dst register in STX", - .insns = { - BPF_STX_MEM(BPF_B, 14, BPF_REG_10, -1), - BPF_EXIT_INSN(), - }, - .errstr = "R14 is invalid", - .result = REJECT, - }, - { - "invalid dst register in ST", - .insns = { - BPF_ST_MEM(BPF_B, 14, -1, -1), - BPF_EXIT_INSN(), - }, - .errstr = "R14 is invalid", - .result = REJECT, - }, - { - "invalid src register in LDX", - .insns = { - BPF_LDX_MEM(BPF_B, BPF_REG_0, 12, 0), - BPF_EXIT_INSN(), - }, - .errstr = "R12 is invalid", - .result = REJECT, - }, - { - "invalid dst register in LDX", - .insns = { - BPF_LDX_MEM(BPF_B, 11, BPF_REG_1, 0), - BPF_EXIT_INSN(), - }, - .errstr = "R11 is invalid", - .result = REJECT, - }, - { - "junk insn", - .insns = { - BPF_RAW_INSN(0, 0, 0, 0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid BPF_LD_IMM", - .result = REJECT, - }, - { - "junk insn2", - .insns = { - BPF_RAW_INSN(1, 0, 0, 0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "BPF_LDX uses reserved fields", - .result = REJECT, - }, - { - "junk insn3", - .insns = { - BPF_RAW_INSN(-1, 0, 0, 0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid BPF_ALU opcode f0", - .result = REJECT, - }, - { - "junk insn4", - .insns = { - BPF_RAW_INSN(-1, -1, -1, -1, -1), - BPF_EXIT_INSN(), - }, - .errstr = "invalid BPF_ALU opcode f0", - .result = REJECT, - }, - { - "junk insn5", - .insns = { - BPF_RAW_INSN(0x7f, -1, -1, -1, -1), - BPF_EXIT_INSN(), - }, - .errstr = "BPF_ALU uses reserved fields", - .result = REJECT, - }, - { - "misaligned read from stack", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -4), - BPF_EXIT_INSN(), - }, - .errstr = "misaligned access", - .result = REJECT, - }, - { - "invalid map_fd for function call", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_delete_elem), - BPF_EXIT_INSN(), - }, - .errstr = "fd 0 is not pointing to valid bpf_map", - .result = REJECT, - }, - { - "don't check return value before access", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {3}, - .errstr = "R0 invalid mem access 'map_value_or_null'", - .result = REJECT, - }, - { - "access memory with incorrect alignment", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0), - BPF_EXIT_INSN(), - }, - .fixup = {3}, - .errstr = "misaligned access", - .result = REJECT, - }, - { - "sometimes access memory with incorrect alignment", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), - BPF_EXIT_INSN(), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1), - BPF_EXIT_INSN(), - }, - .fixup = {3}, - .errstr = "R0 invalid mem access", - .errstr_unpriv = "R0 leaks addr", - .result = REJECT, - }, - { - "jump test 1", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -8), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 5), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R1 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "jump test 2", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 2), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0), - BPF_JMP_IMM(BPF_JA, 0, 0, 14), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 2), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0), - BPF_JMP_IMM(BPF_JA, 0, 0, 11), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 2), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0), - BPF_JMP_IMM(BPF_JA, 0, 0, 8), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0), - BPF_JMP_IMM(BPF_JA, 0, 0, 5), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 2), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0), - BPF_JMP_IMM(BPF_JA, 0, 0, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R1 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "jump test 3", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 19), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 3), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), - BPF_JMP_IMM(BPF_JA, 0, 0, 15), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 3), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -32), - BPF_JMP_IMM(BPF_JA, 0, 0, 11), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 3), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -40), - BPF_JMP_IMM(BPF_JA, 0, 0, 7), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 3), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -48), - BPF_JMP_IMM(BPF_JA, 0, 0, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 0), - BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -56), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_delete_elem), - BPF_EXIT_INSN(), - }, - .fixup = {24}, - .errstr_unpriv = "R1 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "jump test 4", - .insns = { - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R1 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "jump test 5", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 2), - BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), - BPF_JMP_IMM(BPF_JA, 0, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R1 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "access skb fields ok", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, len)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, mark)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, pkt_type)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, queue_mapping)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, protocol)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, vlan_present)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, vlan_tci)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - }, - { - "access skb fields bad1", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, -4), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .result = REJECT, - }, - { - "access skb fields bad2", - .insns = { - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), - BPF_EXIT_INSN(), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, pkt_type)), - BPF_EXIT_INSN(), - }, - .fixup = {4}, - .errstr = "different pointers", - .errstr_unpriv = "R1 pointer comparison", - .result = REJECT, - }, - { - "access skb fields bad3", - .insns = { - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, pkt_type)), - BPF_EXIT_INSN(), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), - BPF_EXIT_INSN(), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), - BPF_JMP_IMM(BPF_JA, 0, 0, -12), - }, - .fixup = {6}, - .errstr = "different pointers", - .errstr_unpriv = "R1 pointer comparison", - .result = REJECT, - }, - { - "access skb fields bad4", - .insns = { - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 3), - BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, - offsetof(struct __sk_buff, len)), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), - BPF_EXIT_INSN(), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), - BPF_JMP_IMM(BPF_JA, 0, 0, -13), - }, - .fixup = {7}, - .errstr = "different pointers", - .errstr_unpriv = "R1 pointer comparison", - .result = REJECT, - }, - { - "check skb->mark is not writeable by sockets", - .insns = { - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, - offsetof(struct __sk_buff, mark)), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .errstr_unpriv = "R1 leaks addr", - .result = REJECT, - }, - { - "check skb->tc_index is not writeable by sockets", - .insns = { - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, - offsetof(struct __sk_buff, tc_index)), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .errstr_unpriv = "R1 leaks addr", - .result = REJECT, - }, - { - "check non-u32 access to cb", - .insns = { - BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_1, - offsetof(struct __sk_buff, cb[0])), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .errstr_unpriv = "R1 leaks addr", - .result = REJECT, - }, - { - "check out of range skb->cb access", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0]) + 256), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .errstr_unpriv = "", - .result = REJECT, - .prog_type = BPF_PROG_TYPE_SCHED_ACT, - }, - { - "write skb fields from socket prog", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[4])), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, mark)), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, tc_index)), - BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, - offsetof(struct __sk_buff, cb[0])), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, - offsetof(struct __sk_buff, cb[2])), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .errstr_unpriv = "R1 leaks addr", - .result_unpriv = REJECT, - }, - { - "write skb fields from tc_cls_act prog", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0])), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, mark)), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, tc_index)), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, tc_index)), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[3])), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "", - .result_unpriv = REJECT, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "PTR_TO_STACK store/load", - .insns = { - BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -10), - BPF_ST_MEM(BPF_DW, BPF_REG_1, 2, 0xfaceb00c), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 2), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - }, - { - "PTR_TO_STACK store/load - bad alignment on off", - .insns = { - BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), - BPF_ST_MEM(BPF_DW, BPF_REG_1, 2, 0xfaceb00c), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 2), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "misaligned access off -6 size 8", - }, - { - "PTR_TO_STACK store/load - bad alignment on reg", - .insns = { - BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -10), - BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "misaligned access off -2 size 8", - }, - { - "PTR_TO_STACK store/load - out of bounds low", - .insns = { - BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -80000), - BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack off=-79992 size=8", - }, - { - "PTR_TO_STACK store/load - out of bounds high", - .insns = { - BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), - BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack off=0 size=8", - }, - { - "unpriv: return pointer", - .insns = { - BPF_MOV64_REG(BPF_REG_0, BPF_REG_10), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R0 leaks addr", - }, - { - "unpriv: add const to pointer", - .insns = { - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R1 pointer arithmetic", - }, - { - "unpriv: add pointer to pointer", - .insns = { - BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_10), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R1 pointer arithmetic", - }, - { - "unpriv: neg pointer", - .insns = { - BPF_ALU64_IMM(BPF_NEG, BPF_REG_1, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R1 pointer arithmetic", - }, - { - "unpriv: cmp pointer with const", - .insns = { - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R1 pointer comparison", - }, - { - "unpriv: cmp pointer with pointer", - .insns = { - BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .result_unpriv = REJECT, - .errstr_unpriv = "R10 pointer comparison", - }, - { - "unpriv: check that printk is disallowed", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), - BPF_MOV64_IMM(BPF_REG_2, 8), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_1), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_trace_printk), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "unknown func 6", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: pass pointer to helper function", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_update_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {3}, - .errstr_unpriv = "R4 leaks addr", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: indirectly pass pointer on stack to helper function", - .insns = { - BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {3}, - .errstr = "invalid indirect read from stack off -8+0 size 8", - .result = REJECT, - }, - { - "unpriv: mangle pointer on stack 1", - .insns = { - BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), - BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "attempt to corrupt spilled", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: mangle pointer on stack 2", - .insns = { - BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), - BPF_ST_MEM(BPF_B, BPF_REG_10, -1, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "attempt to corrupt spilled", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: read pointer from stack in small chunks", - .insns = { - BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid size", - .result = REJECT, - }, - { - "unpriv: write pointer into ctx", - .insns = { - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R1 leaks addr", - .result_unpriv = REJECT, - .errstr = "invalid bpf_context access", - .result = REJECT, - }, - { - "unpriv: spill/fill of ctx", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - }, - { - "unpriv: spill/fill of ctx 2", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_hash_recalc), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "unpriv: spill/fill of ctx 3", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_hash_recalc), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "R1 type=fp expected=ctx", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "unpriv: spill/fill of ctx 4", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_10, BPF_REG_0, -8, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_hash_recalc), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "R1 type=inv expected=ctx", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "unpriv: spill/fill of different pointers stx", - .insns = { - BPF_MOV64_IMM(BPF_REG_3, 42), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_2, 0), - BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, - offsetof(struct __sk_buff, mark)), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "same insn cannot be used with different pointers", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "unpriv: spill/fill of different pointers ldx", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, - -(__s32)offsetof(struct bpf_perf_event_data, - sample_period) - 8), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_2, 0), - BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, - offsetof(struct bpf_perf_event_data, - sample_period)), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "same insn cannot be used with different pointers", - .prog_type = BPF_PROG_TYPE_PERF_EVENT, - }, - { - "unpriv: write pointer into map elem value", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), - BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {3}, - .errstr_unpriv = "R0 leaks addr", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: partial copy of pointer", - .insns = { - BPF_MOV32_REG(BPF_REG_1, BPF_REG_10), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R10 partial copy", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: pass pointer to tail_call", - .insns = { - BPF_MOV64_REG(BPF_REG_3, BPF_REG_1), - BPF_LD_MAP_FD(BPF_REG_2, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .prog_array_fixup = {1}, - .errstr_unpriv = "R3 leaks addr into helper", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: cmp map pointer with zero", - .insns = { - BPF_MOV64_IMM(BPF_REG_1, 0), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {1}, - .errstr_unpriv = "R1 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: write into frame pointer", - .insns = { - BPF_MOV64_REG(BPF_REG_10, BPF_REG_1), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "frame pointer is read only", - .result = REJECT, - }, - { - "unpriv: spill/fill frame pointer", - .insns = { - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "frame pointer is read only", - .result = REJECT, - }, - { - "unpriv: cmp of frame pointer", - .insns = { - BPF_JMP_IMM(BPF_JEQ, BPF_REG_10, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R10 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: cmp of stack pointer", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R2 pointer comparison", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "unpriv: obfuscate stack pointer", - .insns = { - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr_unpriv = "R2 pointer arithmetic", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "raw_stack: no skb_load_bytes", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - /* Call to skb_load_bytes() omitted. */ - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid read from stack off -8+0 size 8", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, negative len", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, -8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, negative len 2", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, ~0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, zero len", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, no init", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, init", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_ST_MEM(BPF_DW, BPF_REG_6, 0, 0xcafe), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, spilled regs around bounds", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -16), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, -8), /* spill ctx from R1 */ - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8), /* spill ctx from R1 */ - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, -8), /* fill ctx into R0 */ - BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 8), /* fill ctx into R2 */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, - offsetof(struct __sk_buff, mark)), - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_2, - offsetof(struct __sk_buff, priority)), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, spilled regs corruption", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), /* spill ctx from R1 */ - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), /* fill ctx into R0 */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, - offsetof(struct __sk_buff, mark)), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "R0 invalid mem access 'inv'", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, spilled regs corruption 2", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -16), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, -8), /* spill ctx from R1 */ - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), /* spill ctx from R1 */ - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8), /* spill ctx from R1 */ - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, -8), /* fill ctx into R0 */ - BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 8), /* fill ctx into R2 */ - BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, 0), /* fill ctx into R3 */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, - offsetof(struct __sk_buff, mark)), - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_2, - offsetof(struct __sk_buff, priority)), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_3, - offsetof(struct __sk_buff, pkt_type)), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_3), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "R3 invalid mem access 'inv'", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, spilled regs + data", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -16), - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, -8), /* spill ctx from R1 */ - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), /* spill ctx from R1 */ - BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8), /* spill ctx from R1 */ - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, -8), /* fill ctx into R0 */ - BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 8), /* fill ctx into R2 */ - BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, 0), /* fill data into R3 */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, - offsetof(struct __sk_buff, mark)), - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_2, - offsetof(struct __sk_buff, priority)), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_3), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, invalid access 1", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -513), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3 off=-513 access_size=8", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, invalid access 2", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -1), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 8), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3 off=-1 access_size=8", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, invalid access 3", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 0xffffffff), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 0xffffffff), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3 off=-1 access_size=-1", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, invalid access 4", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -1), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 0x7fffffff), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3 off=-1 access_size=2147483647", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, invalid access 5", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -512), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 0x7fffffff), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3 off=-512 access_size=2147483647", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, invalid access 6", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -512), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid stack type R3 off=-512 access_size=0", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "raw_stack: skb_load_bytes, large access", - .insns = { - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -512), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_4, 512), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test1", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test2", - .insns = { - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_MOV64_REG(BPF_REG_5, BPF_REG_3), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14), - BPF_JMP_REG(BPF_JGT, BPF_REG_5, BPF_REG_4, 15), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_3, 7), - BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_3, 12), - BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 14), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_4), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), - BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 48), - BPF_ALU64_IMM(BPF_RSH, BPF_REG_2, 48), - BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_2), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_3), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8), - BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1), - BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_3, 4), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test3", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access off=76", - .result = REJECT, - .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, - }, - { - "direct packet access: test4 (write)", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test5 (pkt_end >= reg, good access)", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 2), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test6 (pkt_end >= reg, bad access)", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 3), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid access to packet", - .result = REJECT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test7 (pkt_end >= reg, both accesses)", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 3), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid access to packet", - .result = REJECT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test8 (double test, variant 1)", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 4), - BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test9 (double test, variant 2)", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 2), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "direct packet access: test10 (write invalid)", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .errstr = "invalid access to packet", - .result = REJECT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test1, valid packet_ptr range", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct xdp_md, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct xdp_md, data_end)), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 5), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_update_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {5}, - .result_unpriv = ACCEPT, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_XDP, - }, - { - "helper access to packet: test2, unchecked packet_ptr", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct xdp_md, data)), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {1}, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_XDP, - }, - { - "helper access to packet: test3, variable add", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct xdp_md, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct xdp_md, data_end)), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 10), - BPF_LDX_MEM(BPF_B, BPF_REG_5, BPF_REG_2, 0), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_5), - BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_5, BPF_REG_3, 4), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_4), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {11}, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_XDP, - }, - { - "helper access to packet: test4, packet_ptr with bad range", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct xdp_md, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct xdp_md, data_end)), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), - BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 2), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {7}, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_XDP, - }, - { - "helper access to packet: test5, packet_ptr with too short range", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct xdp_md, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct xdp_md, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 3), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {6}, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_XDP, - }, - { - "helper access to packet: test6, cls valid packet_ptr range", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 5), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_update_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {5}, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test7, cls unchecked packet_ptr", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {1}, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test8, cls variable add", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 10), - BPF_LDX_MEM(BPF_B, BPF_REG_5, BPF_REG_2, 0), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_5), - BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_5, BPF_REG_3, 4), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_4), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {11}, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test9, cls packet_ptr with bad range", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), - BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 2), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {7}, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test10, cls packet_ptr with too short range", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), - BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 3), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .fixup = {6}, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test11, cls unsuitable helper 1", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_7, 4), - BPF_MOV64_IMM(BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_4, 42), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_store_bytes), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "helper access to the packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test12, cls unsuitable helper 2", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 8), - BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 3), - BPF_MOV64_IMM(BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_4, 4), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "helper access to the packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test13, cls helper ok", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = ACCEPT, - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test14, cls helper fail sub", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 4), - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "type=inv expected=fp", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test15, cls helper fail range 1", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_2, 8), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test16, cls helper fail range 2", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_2, -9), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test17, cls helper fail range 3", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_2, ~0), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test18, cls helper fail range zero", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_MOV64_IMM(BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test19, pkt end as input", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "R1 type=pkt_end expected=fp", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "helper access to packet: test20, wrong reg", - .insns = { - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct __sk_buff, data)), - BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, - offsetof(struct __sk_buff, data_end)), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), - BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), - BPF_MOV64_IMM(BPF_REG_2, 4), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_csum_diff), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .result = REJECT, - .errstr = "invalid access to packet", - .prog_type = BPF_PROG_TYPE_SCHED_CLS, - }, - { - "valid map access into an array with a constant", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr_unpriv = "R0 leaks addr", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "valid map access into an array with a register", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), - BPF_MOV64_IMM(BPF_REG_1, 4), - BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr_unpriv = "R0 leaks addr", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "valid map access into an array with a variable", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), - BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), - BPF_JMP_IMM(BPF_JGE, BPF_REG_1, MAX_ENTRIES, 3), - BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr_unpriv = "R0 leaks addr", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "valid map access into an array with a signed variable", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), - BPF_JMP_IMM(BPF_JSGT, BPF_REG_1, 0xffffffff, 1), - BPF_MOV32_IMM(BPF_REG_1, 0), - BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES), - BPF_JMP_REG(BPF_JSGT, BPF_REG_2, BPF_REG_1, 1), - BPF_MOV32_IMM(BPF_REG_1, 0), - BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr_unpriv = "R0 leaks addr", - .result_unpriv = REJECT, - .result = ACCEPT, - }, - { - "invalid map access into an array with a constant", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, (MAX_ENTRIES + 1) << 2, - offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr = "invalid access to map value, value_size=48 off=48 size=8", - .result = REJECT, - }, - { - "invalid map access into an array with a register", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), - BPF_MOV64_IMM(BPF_REG_1, MAX_ENTRIES + 1), - BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr = "R0 min value is outside of the array range", - .result = REJECT, - }, - { - "invalid map access into an array with a variable", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), - BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), - BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", - .result = REJECT, - }, - { - "invalid map access into an array with no floor check", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), - BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES), - BPF_JMP_REG(BPF_JSGT, BPF_REG_2, BPF_REG_1, 1), - BPF_MOV32_IMM(BPF_REG_1, 0), - BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", - .result = REJECT, - }, - { - "invalid map access into an array with a invalid max check", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), - BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES + 1), - BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1), - BPF_MOV32_IMM(BPF_REG_1, 0), - BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), - BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3}, - .errstr = "invalid access to map value, value_size=48 off=44 size=8", - .result = REJECT, - }, - { - "invalid map access into an array with a invalid max check", - .insns = { - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10), - BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), - BPF_LD_MAP_FD(BPF_REG_1, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), - BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_8), - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct test_val, foo)), - BPF_EXIT_INSN(), - }, - .test_val_map_fixup = {3, 11}, - .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", - .result = REJECT, - }, -}; - -static int probe_filter_length(struct bpf_insn *fp) -{ - int len = 0; - - for (len = MAX_INSNS - 1; len > 0; --len) - if (fp[len].code != 0 || fp[len].imm != 0) - break; - - return len + 1; -} - -static int create_map(size_t val_size, int num) -{ - int map_fd; - - map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, - sizeof(long long), val_size, num, 0); - if (map_fd < 0) - printf("failed to create map '%s'\n", strerror(errno)); - - return map_fd; -} - -static int create_prog_array(void) -{ - int map_fd; - - map_fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, - sizeof(int), sizeof(int), 4, 0); - if (map_fd < 0) - printf("failed to create prog_array '%s'\n", strerror(errno)); - - return map_fd; -} - -static int test(void) -{ - int prog_fd, i, pass_cnt = 0, err_cnt = 0; - bool unpriv = geteuid() != 0; - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - struct bpf_insn *prog = tests[i].insns; - int prog_type = tests[i].prog_type; - int prog_len = probe_filter_length(prog); - int *fixup = tests[i].fixup; - int *prog_array_fixup = tests[i].prog_array_fixup; - int *test_val_map_fixup = tests[i].test_val_map_fixup; - int expected_result; - const char *expected_errstr; - int map_fd = -1, prog_array_fd = -1, test_val_map_fd = -1; - - if (*fixup) { - map_fd = create_map(sizeof(long long), 1024); - - do { - prog[*fixup].imm = map_fd; - fixup++; - } while (*fixup); - } - if (*prog_array_fixup) { - prog_array_fd = create_prog_array(); - - do { - prog[*prog_array_fixup].imm = prog_array_fd; - prog_array_fixup++; - } while (*prog_array_fixup); - } - if (*test_val_map_fixup) { - /* Unprivileged can't create a hash map.*/ - if (unpriv) - continue; - test_val_map_fd = create_map(sizeof(struct test_val), - 256); - do { - prog[*test_val_map_fixup].imm = test_val_map_fd; - test_val_map_fixup++; - } while (*test_val_map_fixup); - } - - printf("#%d %s ", i, tests[i].descr); - - prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER, - prog, prog_len * sizeof(struct bpf_insn), - "GPL", 0); - - if (unpriv && tests[i].result_unpriv != UNDEF) - expected_result = tests[i].result_unpriv; - else - expected_result = tests[i].result; - - if (unpriv && tests[i].errstr_unpriv) - expected_errstr = tests[i].errstr_unpriv; - else - expected_errstr = tests[i].errstr; - - if (expected_result == ACCEPT) { - if (prog_fd < 0) { - printf("FAIL\nfailed to load prog '%s'\n", - strerror(errno)); - printf("%s", bpf_log_buf); - err_cnt++; - goto fail; - } - } else { - if (prog_fd >= 0) { - printf("FAIL\nunexpected success to load\n"); - printf("%s", bpf_log_buf); - err_cnt++; - goto fail; - } - if (strstr(bpf_log_buf, expected_errstr) == 0) { - printf("FAIL\nunexpected error message: %s", - bpf_log_buf); - err_cnt++; - goto fail; - } - } - - pass_cnt++; - printf("OK\n"); -fail: - if (map_fd >= 0) - close(map_fd); - if (prog_array_fd >= 0) - close(prog_array_fd); - if (test_val_map_fd >= 0) - close(test_val_map_fd); - close(prog_fd); - - } - printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, err_cnt); - - return 0; -} - -int main(void) -{ - struct rlimit r = {1 << 20, 1 << 20}; - - setrlimit(RLIMIT_MEMLOCK, &r); - return test(); -} diff --git a/tools/include/linux/filter.h b/tools/include/linux/filter.h index 3276625..122153b 100644 --- a/tools/include/linux/filter.h +++ b/tools/include/linux/filter.h @@ -218,6 +218,30 @@ .off = OFF, \ .imm = IMM }) +/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ + +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, IMM) + +#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_DW | BPF_IMM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = (__u32) (IMM) }), \ + ((struct bpf_insn) { \ + .code = 0, /* zero is reserved opcode */ \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = ((__u64) (IMM)) >> 32 }) + +/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ + +#define BPF_LD_MAP_FD(DST, MAP_FD) \ + BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) + /* Program exit */ #define BPF_EXIT_INSN() \ diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index f770dba..a3144a3 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,5 @@ -TARGETS = breakpoints +TARGETS = bpf +TARGETS += breakpoints TARGETS += capabilities TARGETS += cpu-hotplug TARGETS += efivarfs diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore new file mode 100644 index 0000000..3c59f96e --- /dev/null +++ b/tools/testing/selftests/bpf/.gitignore @@ -0,0 +1,2 @@ +test_verifier +test_maps diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile new file mode 100644 index 0000000..4761e2d6 --- /dev/null +++ b/tools/testing/selftests/bpf/Makefile @@ -0,0 +1,13 @@ +CFLAGS += -Wall -O2 + +test_objs = test_verifier test_maps + +TEST_PROGS := test_verifier test_maps test_kmod.sh +TEST_FILES := $(test_objs) + +all: $(test_objs) + +include ../lib.mk + +clean: + $(RM) $(test_objs) diff --git a/tools/testing/selftests/bpf/bpf_sys.h b/tools/testing/selftests/bpf/bpf_sys.h new file mode 100644 index 0000000..6b4565f --- /dev/null +++ b/tools/testing/selftests/bpf/bpf_sys.h @@ -0,0 +1,108 @@ +#ifndef __BPF_SYS__ +#define __BPF_SYS__ + +#include +#include + +#include + +#include + +static inline __u64 bpf_ptr_to_u64(const void *ptr) +{ + return (__u64)(unsigned long) ptr; +} + +static inline int bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ +#ifdef __NR_bpf + return syscall(__NR_bpf, cmd, attr, size); +#else + fprintf(stderr, "No bpf syscall, kernel headers too old?\n"); + errno = ENOSYS; + return -1; +#endif +} + +static inline int bpf_map_lookup(int fd, const void *key, void *value) +{ + union bpf_attr attr = {}; + + attr.map_fd = fd; + attr.key = bpf_ptr_to_u64(key); + attr.value = bpf_ptr_to_u64(value); + + return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); +} + +static inline int bpf_map_update(int fd, const void *key, const void *value, + uint64_t flags) +{ + union bpf_attr attr = {}; + + attr.map_fd = fd; + attr.key = bpf_ptr_to_u64(key); + attr.value = bpf_ptr_to_u64(value); + attr.flags = flags; + + return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); +} + +static inline int bpf_map_delete(int fd, const void *key) +{ + union bpf_attr attr = {}; + + attr.map_fd = fd; + attr.key = bpf_ptr_to_u64(key); + + return bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); +} + +static inline int bpf_map_next_key(int fd, const void *key, void *next_key) +{ + union bpf_attr attr = {}; + + attr.map_fd = fd; + attr.key = bpf_ptr_to_u64(key); + attr.next_key = bpf_ptr_to_u64(next_key); + + return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); +} + +static inline int bpf_map_create(enum bpf_map_type type, uint32_t size_key, + uint32_t size_value, uint32_t max_elem, + uint32_t flags) +{ + union bpf_attr attr = {}; + + attr.map_type = type; + attr.key_size = size_key; + attr.value_size = size_value; + attr.max_entries = max_elem; + attr.map_flags = flags; + + return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); +} + +static inline int bpf_prog_load(enum bpf_prog_type type, + const struct bpf_insn *insns, size_t size_insns, + const char *license, char *log, size_t size_log) +{ + union bpf_attr attr = {}; + + attr.prog_type = type; + attr.insns = bpf_ptr_to_u64(insns); + attr.insn_cnt = size_insns / sizeof(struct bpf_insn); + attr.license = bpf_ptr_to_u64(license); + + if (size_log > 0) { + attr.log_buf = bpf_ptr_to_u64(log); + attr.log_size = size_log; + attr.log_level = 1; + log[0] = 0; + } + + return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); +} + +#endif /* __BPF_SYS__ */ diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config new file mode 100644 index 0000000..52d53ed --- /dev/null +++ b/tools/testing/selftests/bpf/config @@ -0,0 +1,5 @@ +CONFIG_BPF=y +CONFIG_BPF_SYSCALL=y +CONFIG_NET_CLS_BPF=m +CONFIG_BPF_EVENTS=y +CONFIG_TEST_BPF=m diff --git a/tools/testing/selftests/bpf/test_kmod.sh b/tools/testing/selftests/bpf/test_kmod.sh new file mode 100755 index 0000000..92e627a --- /dev/null +++ b/tools/testing/selftests/bpf/test_kmod.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +SRC_TREE=../../../../ + +test_run() +{ + sysctl -w net.core.bpf_jit_enable=$1 2>&1 > /dev/null + sysctl -w net.core.bpf_jit_harden=$2 2>&1 > /dev/null + + echo "[ JIT enabled:$1 hardened:$2 ]" + dmesg -C + insmod $SRC_TREE/lib/test_bpf.ko 2> /dev/null + if [ $? -ne 0 ]; then + rc=1 + fi + rmmod test_bpf 2> /dev/null + dmesg | grep FAIL +} + +test_save() +{ + JE=`sysctl -n net.core.bpf_jit_enable` + JH=`sysctl -n net.core.bpf_jit_harden` +} + +test_restore() +{ + sysctl -w net.core.bpf_jit_enable=$JE 2>&1 > /dev/null + sysctl -w net.core.bpf_jit_harden=$JH 2>&1 > /dev/null +} + +rc=0 +test_save +test_run 0 0 +test_run 1 0 +test_run 1 1 +test_run 1 2 +test_restore +exit $rc diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c new file mode 100644 index 0000000..ee384f0 --- /dev/null +++ b/tools/testing/selftests/bpf/test_maps.c @@ -0,0 +1,525 @@ +/* + * Testsuite for eBPF maps + * + * Copyright (c) 2014 PLUMgrid, http://plumgrid.com + * Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "bpf_sys.h" + +static int map_flags; + +static void test_hashmap(int task, void *data) +{ + long long key, next_key, value; + int fd; + + fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + 2, map_flags); + if (fd < 0) { + printf("Failed to create hashmap '%s'!\n", strerror(errno)); + exit(1); + } + + key = 1; + value = 1234; + /* Insert key=1 element. */ + assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); + + value = 0; + /* BPF_NOEXIST means add new element if it doesn't exist. */ + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + /* key=1 already exists. */ + errno == EEXIST); + + /* -1 is an invalid flag. */ + assert(bpf_map_update(fd, &key, &value, -1) == -1 && errno == EINVAL); + + /* Check that key=1 can be found. */ + assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234); + + key = 2; + /* Check that key=2 is not found. */ + assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT); + + /* BPF_EXIST means update existing element. */ + assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == -1 && + /* key=2 is not there. */ + errno == ENOENT); + + /* Insert key=2 element. */ + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0); + + /* key=1 and key=2 were inserted, check that key=0 cannot be + * inserted due to max_entries limit. + */ + key = 0; + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + errno == E2BIG); + + /* Update existing element, though the map is full. */ + key = 1; + assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0); + key = 2; + assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); + key = 1; + assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); + + /* Check that key = 0 doesn't exist. */ + key = 0; + assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + + /* Iterate over two elements. */ + assert(bpf_map_next_key(fd, &key, &next_key) == 0 && + (next_key == 1 || next_key == 2)); + assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 && + (next_key == 1 || next_key == 2)); + assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 && + errno == ENOENT); + + /* Delete both elements. */ + key = 1; + assert(bpf_map_delete(fd, &key) == 0); + key = 2; + assert(bpf_map_delete(fd, &key) == 0); + assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + + key = 0; + /* Check that map is empty. */ + assert(bpf_map_next_key(fd, &key, &next_key) == -1 && + errno == ENOENT); + + close(fd); +} + +static void test_hashmap_percpu(int task, void *data) +{ + unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + long long value[nr_cpus]; + long long key, next_key; + int expected_key_mask = 0; + int fd, i; + + fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key), + sizeof(value[0]), 2, map_flags); + if (fd < 0) { + printf("Failed to create hashmap '%s'!\n", strerror(errno)); + exit(1); + } + + for (i = 0; i < nr_cpus; i++) + value[i] = i + 100; + + key = 1; + /* Insert key=1 element. */ + assert(!(expected_key_mask & key)); + assert(bpf_map_update(fd, &key, value, BPF_ANY) == 0); + expected_key_mask |= key; + + /* BPF_NOEXIST means add new element if it doesn't exist. */ + assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 && + /* key=1 already exists. */ + errno == EEXIST); + + /* -1 is an invalid flag. */ + assert(bpf_map_update(fd, &key, value, -1) == -1 && errno == EINVAL); + + /* Check that key=1 can be found. Value could be 0 if the lookup + * was run from a different CPU. + */ + value[0] = 1; + assert(bpf_map_lookup(fd, &key, value) == 0 && value[0] == 100); + + key = 2; + /* Check that key=2 is not found. */ + assert(bpf_map_lookup(fd, &key, value) == -1 && errno == ENOENT); + + /* BPF_EXIST means update existing element. */ + assert(bpf_map_update(fd, &key, value, BPF_EXIST) == -1 && + /* key=2 is not there. */ + errno == ENOENT); + + /* Insert key=2 element. */ + assert(!(expected_key_mask & key)); + assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == 0); + expected_key_mask |= key; + + /* key=1 and key=2 were inserted, check that key=0 cannot be + * inserted due to max_entries limit. + */ + key = 0; + assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 && + errno == E2BIG); + + /* Check that key = 0 doesn't exist. */ + assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + + /* Iterate over two elements. */ + while (!bpf_map_next_key(fd, &key, &next_key)) { + assert((expected_key_mask & next_key) == next_key); + expected_key_mask &= ~next_key; + + assert(bpf_map_lookup(fd, &next_key, value) == 0); + + for (i = 0; i < nr_cpus; i++) + assert(value[i] == i + 100); + + key = next_key; + } + assert(errno == ENOENT); + + /* Update with BPF_EXIST. */ + key = 1; + assert(bpf_map_update(fd, &key, value, BPF_EXIST) == 0); + + /* Delete both elements. */ + key = 1; + assert(bpf_map_delete(fd, &key) == 0); + key = 2; + assert(bpf_map_delete(fd, &key) == 0); + assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + + key = 0; + /* Check that map is empty. */ + assert(bpf_map_next_key(fd, &key, &next_key) == -1 && + errno == ENOENT); + + close(fd); +} + +static void test_arraymap(int task, void *data) +{ + int key, next_key, fd; + long long value; + + fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), + 2, 0); + if (fd < 0) { + printf("Failed to create arraymap '%s'!\n", strerror(errno)); + exit(1); + } + + key = 1; + value = 1234; + /* Insert key=1 element. */ + assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); + + value = 0; + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + errno == EEXIST); + + /* Check that key=1 can be found. */ + assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234); + + key = 0; + /* Check that key=0 is also found and zero initialized. */ + assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 0); + + /* key=0 and key=1 were inserted, check that key=2 cannot be inserted + * due to max_entries limit. + */ + key = 2; + assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == -1 && + errno == E2BIG); + + /* Check that key = 2 doesn't exist. */ + assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT); + + /* Iterate over two elements. */ + assert(bpf_map_next_key(fd, &key, &next_key) == 0 && + next_key == 0); + assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 && + next_key == 1); + assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 && + errno == ENOENT); + + /* Delete shouldn't succeed. */ + key = 1; + assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL); + + close(fd); +} + +static void test_arraymap_percpu(int task, void *data) +{ + unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + int key, next_key, fd, i; + long values[nr_cpus]; + + fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), + sizeof(values[0]), 2, 0); + if (fd < 0) { + printf("Failed to create arraymap '%s'!\n", strerror(errno)); + exit(1); + } + + for (i = 0; i < nr_cpus; i++) + values[i] = i + 100; + + key = 1; + /* Insert key=1 element. */ + assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0); + + values[0] = 0; + assert(bpf_map_update(fd, &key, values, BPF_NOEXIST) == -1 && + errno == EEXIST); + + /* Check that key=1 can be found. */ + assert(bpf_map_lookup(fd, &key, values) == 0 && values[0] == 100); + + key = 0; + /* Check that key=0 is also found and zero initialized. */ + assert(bpf_map_lookup(fd, &key, values) == 0 && + values[0] == 0 && values[nr_cpus - 1] == 0); + + /* Check that key=2 cannot be inserted due to max_entries limit. */ + key = 2; + assert(bpf_map_update(fd, &key, values, BPF_EXIST) == -1 && + errno == E2BIG); + + /* Check that key = 2 doesn't exist. */ + assert(bpf_map_lookup(fd, &key, values) == -1 && errno == ENOENT); + + /* Iterate over two elements. */ + assert(bpf_map_next_key(fd, &key, &next_key) == 0 && + next_key == 0); + assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 && + next_key == 1); + assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 && + errno == ENOENT); + + /* Delete shouldn't succeed. */ + key = 1; + assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL); + + close(fd); +} + +static void test_arraymap_percpu_many_keys(void) +{ + unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + unsigned int nr_keys = 20000; + long values[nr_cpus]; + int key, fd, i; + + fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), + sizeof(values[0]), nr_keys, 0); + if (fd < 0) { + printf("Failed to create per-cpu arraymap '%s'!\n", + strerror(errno)); + exit(1); + } + + for (i = 0; i < nr_cpus; i++) + values[i] = i + 10; + + for (key = 0; key < nr_keys; key++) + assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0); + + for (key = 0; key < nr_keys; key++) { + for (i = 0; i < nr_cpus; i++) + values[i] = 0; + + assert(bpf_map_lookup(fd, &key, values) == 0); + + for (i = 0; i < nr_cpus; i++) + assert(values[i] == i + 10); + } + + close(fd); +} + +#define MAP_SIZE (32 * 1024) + +static void test_map_large(void) +{ + struct bigkey { + int a; + char b[116]; + long long c; + } key; + int fd, i, value; + + fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + MAP_SIZE, map_flags); + if (fd < 0) { + printf("Failed to create large map '%s'!\n", strerror(errno)); + exit(1); + } + + for (i = 0; i < MAP_SIZE; i++) { + key = (struct bigkey) { .c = i }; + value = i; + + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0); + } + + key.c = -1; + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + errno == E2BIG); + + /* Iterate through all elements. */ + for (i = 0; i < MAP_SIZE; i++) + assert(bpf_map_next_key(fd, &key, &key) == 0); + assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT); + + key.c = 0; + assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 0); + key.a = 1; + assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT); + + close(fd); +} + +static void run_parallel(int tasks, void (*fn)(int task, void *data), + void *data) +{ + pid_t pid[tasks]; + int i; + + for (i = 0; i < tasks; i++) { + pid[i] = fork(); + if (pid[i] == 0) { + fn(i, data); + exit(0); + } else if (pid[i] == -1) { + printf("Couldn't spawn #%d process!\n", i); + exit(1); + } + } + + for (i = 0; i < tasks; i++) { + int status; + + assert(waitpid(pid[i], &status, 0) == pid[i]); + assert(status == 0); + } +} + +static void test_map_stress(void) +{ + run_parallel(100, test_hashmap, NULL); + run_parallel(100, test_hashmap_percpu, NULL); + + run_parallel(100, test_arraymap, NULL); + run_parallel(100, test_arraymap_percpu, NULL); +} + +#define TASKS 1024 + +#define DO_UPDATE 1 +#define DO_DELETE 0 + +static void do_work(int fn, void *data) +{ + int do_update = ((int *)data)[1]; + int fd = ((int *)data)[0]; + int i, key, value; + + for (i = fn; i < MAP_SIZE; i += TASKS) { + key = value = i; + + if (do_update) { + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0); + assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0); + } else { + assert(bpf_map_delete(fd, &key) == 0); + } + } +} + +static void test_map_parallel(void) +{ + int i, fd, key = 0, value = 0; + int data[2]; + + fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + MAP_SIZE, map_flags); + if (fd < 0) { + printf("Failed to create map for parallel test '%s'!\n", + strerror(errno)); + exit(1); + } + + /* Use the same fd in children to add elements to this map: + * child_0 adds key=0, key=1024, key=2048, ... + * child_1 adds key=1, key=1025, key=2049, ... + * child_1023 adds key=1023, ... + */ + data[0] = fd; + data[1] = DO_UPDATE; + run_parallel(TASKS, do_work, data); + + /* Check that key=0 is already there. */ + assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + errno == EEXIST); + + /* Check that all elements were inserted. */ + key = -1; + for (i = 0; i < MAP_SIZE; i++) + assert(bpf_map_next_key(fd, &key, &key) == 0); + assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT); + + /* Another check for all elements */ + for (i = 0; i < MAP_SIZE; i++) { + key = MAP_SIZE - i - 1; + + assert(bpf_map_lookup(fd, &key, &value) == 0 && + value == key); + } + + /* Now let's delete all elemenets in parallel. */ + data[1] = DO_DELETE; + run_parallel(TASKS, do_work, data); + + /* Nothing should be left. */ + key = -1; + assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT); +} + +static void run_all_tests(void) +{ + test_hashmap(0, NULL); + test_hashmap_percpu(0, NULL); + + test_arraymap(0, NULL); + test_arraymap_percpu(0, NULL); + + test_arraymap_percpu_many_keys(); + + test_map_large(); + test_map_parallel(); + test_map_stress(); +} + +int main(void) +{ + struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; + + setrlimit(RLIMIT_MEMLOCK, &rinf); + + map_flags = 0; + run_all_tests(); + + map_flags = BPF_F_NO_PREALLOC; + run_all_tests(); + + printf("test_maps: OK\n"); + return 0; +} diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c new file mode 100644 index 0000000..ff5df12 --- /dev/null +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -0,0 +1,2764 @@ +/* + * Testsuite for eBPF verifier + * + * Copyright (c) 2014 PLUMgrid, http://plumgrid.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "../../../include/linux/filter.h" + +#include "bpf_sys.h" + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#define MAX_INSNS 512 +#define MAX_FIXUPS 8 + +struct bpf_test { + const char *descr; + struct bpf_insn insns[MAX_INSNS]; + int fixup_map1[MAX_FIXUPS]; + int fixup_map2[MAX_FIXUPS]; + int fixup_prog[MAX_FIXUPS]; + const char *errstr; + const char *errstr_unpriv; + enum { + UNDEF, + ACCEPT, + REJECT + } result, result_unpriv; + enum bpf_prog_type prog_type; +}; + +/* Note we want this to be 64 bit aligned so that the end of our array is + * actually the end of the structure. + */ +#define MAX_ENTRIES 11 + +struct test_val { + unsigned int index; + int foo[MAX_ENTRIES]; +}; + +static struct bpf_test tests[] = { + { + "add+sub+mul", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 1), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 2), + BPF_MOV64_IMM(BPF_REG_2, 3), + BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -1), + BPF_ALU64_IMM(BPF_MUL, BPF_REG_1, 3), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_1), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "unreachable", + .insns = { + BPF_EXIT_INSN(), + BPF_EXIT_INSN(), + }, + .errstr = "unreachable", + .result = REJECT, + }, + { + "unreachable2", + .insns = { + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "unreachable", + .result = REJECT, + }, + { + "out of range jump", + .insns = { + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "jump out of range", + .result = REJECT, + }, + { + "out of range jump2", + .insns = { + BPF_JMP_IMM(BPF_JA, 0, 0, -2), + BPF_EXIT_INSN(), + }, + .errstr = "jump out of range", + .result = REJECT, + }, + { + "test1 ld_imm64", + .insns = { + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), + BPF_LD_IMM64(BPF_REG_0, 0), + BPF_LD_IMM64(BPF_REG_0, 0), + BPF_LD_IMM64(BPF_REG_0, 1), + BPF_LD_IMM64(BPF_REG_0, 1), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .errstr = "invalid BPF_LD_IMM insn", + .errstr_unpriv = "R1 pointer comparison", + .result = REJECT, + }, + { + "test2 ld_imm64", + .insns = { + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), + BPF_LD_IMM64(BPF_REG_0, 0), + BPF_LD_IMM64(BPF_REG_0, 0), + BPF_LD_IMM64(BPF_REG_0, 1), + BPF_LD_IMM64(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid BPF_LD_IMM insn", + .errstr_unpriv = "R1 pointer comparison", + .result = REJECT, + }, + { + "test3 ld_imm64", + .insns = { + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0), + BPF_LD_IMM64(BPF_REG_0, 0), + BPF_LD_IMM64(BPF_REG_0, 0), + BPF_LD_IMM64(BPF_REG_0, 1), + BPF_LD_IMM64(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_ld_imm64 insn", + .result = REJECT, + }, + { + "test4 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_ld_imm64 insn", + .result = REJECT, + }, + { + "test5 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0), + }, + .errstr = "invalid bpf_ld_imm64 insn", + .result = REJECT, + }, + { + "no bpf_exit", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_0, BPF_REG_2), + }, + .errstr = "jump out of range", + .result = REJECT, + }, + { + "loop (back-edge)", + .insns = { + BPF_JMP_IMM(BPF_JA, 0, 0, -1), + BPF_EXIT_INSN(), + }, + .errstr = "back-edge", + .result = REJECT, + }, + { + "loop2 (back-edge)", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), + BPF_JMP_IMM(BPF_JA, 0, 0, -4), + BPF_EXIT_INSN(), + }, + .errstr = "back-edge", + .result = REJECT, + }, + { + "conditional loop", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -3), + BPF_EXIT_INSN(), + }, + .errstr = "back-edge", + .result = REJECT, + }, + { + "read uninitialized register", + .insns = { + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_EXIT_INSN(), + }, + .errstr = "R2 !read_ok", + .result = REJECT, + }, + { + "read invalid register", + .insns = { + BPF_MOV64_REG(BPF_REG_0, -1), + BPF_EXIT_INSN(), + }, + .errstr = "R15 is invalid", + .result = REJECT, + }, + { + "program doesn't init R0 before exit", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1), + BPF_EXIT_INSN(), + }, + .errstr = "R0 !read_ok", + .result = REJECT, + }, + { + "program doesn't init R0 before exit in all branches", + .insns = { + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .errstr = "R0 !read_ok", + .errstr_unpriv = "R1 pointer comparison", + .result = REJECT, + }, + { + "stack out of bounds", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack", + .result = REJECT, + }, + { + "invalid call insn1", + .insns = { + BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, 0, 0, 0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "BPF_CALL uses reserved", + .result = REJECT, + }, + { + "invalid call insn2", + .insns = { + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 1, 0), + BPF_EXIT_INSN(), + }, + .errstr = "BPF_CALL uses reserved", + .result = REJECT, + }, + { + "invalid function call", + .insns = { + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 1234567), + BPF_EXIT_INSN(), + }, + .errstr = "invalid func 1234567", + .result = REJECT, + }, + { + "uninitialized stack1", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 2 }, + .errstr = "invalid indirect read from stack", + .result = REJECT, + }, + { + "uninitialized stack2", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -8), + BPF_EXIT_INSN(), + }, + .errstr = "invalid read from stack", + .result = REJECT, + }, + { + "invalid argument register", + .insns = { + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_cgroup_classid), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_cgroup_classid), + BPF_EXIT_INSN(), + }, + .errstr = "R1 !read_ok", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "non-invalid argument register", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_1), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_cgroup_classid), + BPF_ALU64_REG(BPF_MOV, BPF_REG_1, BPF_REG_6), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_cgroup_classid), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "check valid spill/fill", + .insns = { + /* spill R1(ctx) into stack */ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), + /* fill it back into R2 */ + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8), + /* should be able to access R0 = *(R2 + 8) */ + /* BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 8), */ + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R0 leaks addr", + .result = ACCEPT, + .result_unpriv = REJECT, + }, + { + "check valid spill/fill, skb mark", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_1), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, + offsetof(struct __sk_buff, mark)), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .result_unpriv = ACCEPT, + }, + { + "check corrupted spill/fill", + .insns = { + /* spill R1(ctx) into stack */ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), + /* mess up with R1 pointer on stack */ + BPF_ST_MEM(BPF_B, BPF_REG_10, -7, 0x23), + /* fill back into R0 should fail */ + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "attempt to corrupt spilled", + .errstr = "corrupted spill", + .result = REJECT, + }, + { + "invalid src register in STX", + .insns = { + BPF_STX_MEM(BPF_B, BPF_REG_10, -1, -1), + BPF_EXIT_INSN(), + }, + .errstr = "R15 is invalid", + .result = REJECT, + }, + { + "invalid dst register in STX", + .insns = { + BPF_STX_MEM(BPF_B, 14, BPF_REG_10, -1), + BPF_EXIT_INSN(), + }, + .errstr = "R14 is invalid", + .result = REJECT, + }, + { + "invalid dst register in ST", + .insns = { + BPF_ST_MEM(BPF_B, 14, -1, -1), + BPF_EXIT_INSN(), + }, + .errstr = "R14 is invalid", + .result = REJECT, + }, + { + "invalid src register in LDX", + .insns = { + BPF_LDX_MEM(BPF_B, BPF_REG_0, 12, 0), + BPF_EXIT_INSN(), + }, + .errstr = "R12 is invalid", + .result = REJECT, + }, + { + "invalid dst register in LDX", + .insns = { + BPF_LDX_MEM(BPF_B, 11, BPF_REG_1, 0), + BPF_EXIT_INSN(), + }, + .errstr = "R11 is invalid", + .result = REJECT, + }, + { + "junk insn", + .insns = { + BPF_RAW_INSN(0, 0, 0, 0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid BPF_LD_IMM", + .result = REJECT, + }, + { + "junk insn2", + .insns = { + BPF_RAW_INSN(1, 0, 0, 0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "BPF_LDX uses reserved fields", + .result = REJECT, + }, + { + "junk insn3", + .insns = { + BPF_RAW_INSN(-1, 0, 0, 0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid BPF_ALU opcode f0", + .result = REJECT, + }, + { + "junk insn4", + .insns = { + BPF_RAW_INSN(-1, -1, -1, -1, -1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid BPF_ALU opcode f0", + .result = REJECT, + }, + { + "junk insn5", + .insns = { + BPF_RAW_INSN(0x7f, -1, -1, -1, -1), + BPF_EXIT_INSN(), + }, + .errstr = "BPF_ALU uses reserved fields", + .result = REJECT, + }, + { + "misaligned read from stack", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -4), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "invalid map_fd for function call", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_delete_elem), + BPF_EXIT_INSN(), + }, + .errstr = "fd 0 is not pointing to valid bpf_map", + .result = REJECT, + }, + { + "don't check return value before access", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr = "R0 invalid mem access 'map_value_or_null'", + .result = REJECT, + }, + { + "access memory with incorrect alignment", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "sometimes access memory with incorrect alignment", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0), + BPF_EXIT_INSN(), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr = "R0 invalid mem access", + .errstr_unpriv = "R0 leaks addr", + .result = REJECT, + }, + { + "jump test 1", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 5), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "jump test 2", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 14), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 11), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 5), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "jump test 3", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 19), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 3), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), + BPF_JMP_IMM(BPF_JA, 0, 0, 15), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 3), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -32), + BPF_JMP_IMM(BPF_JA, 0, 0, 11), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 3), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -40), + BPF_JMP_IMM(BPF_JA, 0, 0, 7), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 3), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -48), + BPF_JMP_IMM(BPF_JA, 0, 0, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 0), + BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -56), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_delete_elem), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 24 }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "jump test 4", + .insns = { + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "jump test 5", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "access skb fields ok", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, len)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, mark)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, pkt_type)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, queue_mapping)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, protocol)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, vlan_present)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, vlan_tci)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "access skb fields bad1", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, -4), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "access skb fields bad2", + .insns = { + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 9), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, pkt_type)), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 4 }, + .errstr = "different pointers", + .errstr_unpriv = "R1 pointer comparison", + .result = REJECT, + }, + { + "access skb fields bad3", + .insns = { + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, pkt_type)), + BPF_EXIT_INSN(), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_JMP_IMM(BPF_JA, 0, 0, -12), + }, + .fixup_map1 = { 6 }, + .errstr = "different pointers", + .errstr_unpriv = "R1 pointer comparison", + .result = REJECT, + }, + { + "access skb fields bad4", + .insns = { + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 3), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, + offsetof(struct __sk_buff, len)), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_JMP_IMM(BPF_JA, 0, 0, -13), + }, + .fixup_map1 = { 7 }, + .errstr = "different pointers", + .errstr_unpriv = "R1 pointer comparison", + .result = REJECT, + }, + { + "check skb->mark is not writeable by sockets", + .insns = { + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, + offsetof(struct __sk_buff, mark)), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .errstr_unpriv = "R1 leaks addr", + .result = REJECT, + }, + { + "check skb->tc_index is not writeable by sockets", + .insns = { + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, + offsetof(struct __sk_buff, tc_index)), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .errstr_unpriv = "R1 leaks addr", + .result = REJECT, + }, + { + "check non-u32 access to cb", + .insns = { + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .errstr_unpriv = "R1 leaks addr", + .result = REJECT, + }, + { + "check out of range skb->cb access", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) + 256), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .errstr_unpriv = "", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_ACT, + }, + { + "write skb fields from socket prog", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4])), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, mark)), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_index)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, + offsetof(struct __sk_buff, cb[2])), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .errstr_unpriv = "R1 leaks addr", + .result_unpriv = REJECT, + }, + { + "write skb fields from tc_cls_act prog", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, mark)), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_index)), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, tc_index)), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3])), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "", + .result_unpriv = REJECT, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "PTR_TO_STACK store/load", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -10), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 2, 0xfaceb00c), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 2), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "PTR_TO_STACK store/load - bad alignment on off", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 2, 0xfaceb00c), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 2), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "misaligned access off -6 size 8", + }, + { + "PTR_TO_STACK store/load - bad alignment on reg", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -10), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "misaligned access off -2 size 8", + }, + { + "PTR_TO_STACK store/load - out of bounds low", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -80000), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack off=-79992 size=8", + }, + { + "PTR_TO_STACK store/load - out of bounds high", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack off=0 size=8", + }, + { + "unpriv: return pointer", + .insns = { + BPF_MOV64_REG(BPF_REG_0, BPF_REG_10), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R0 leaks addr", + }, + { + "unpriv: add const to pointer", + .insns = { + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R1 pointer arithmetic", + }, + { + "unpriv: add pointer to pointer", + .insns = { + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_10), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R1 pointer arithmetic", + }, + { + "unpriv: neg pointer", + .insns = { + BPF_ALU64_IMM(BPF_NEG, BPF_REG_1, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R1 pointer arithmetic", + }, + { + "unpriv: cmp pointer with const", + .insns = { + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R1 pointer comparison", + }, + { + "unpriv: cmp pointer with pointer", + .insns = { + BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .result_unpriv = REJECT, + .errstr_unpriv = "R10 pointer comparison", + }, + { + "unpriv: check that printk is disallowed", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_MOV64_IMM(BPF_REG_2, 8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_1), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_trace_printk), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "unknown func 6", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: pass pointer to helper function", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_update_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr_unpriv = "R4 leaks addr", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: indirectly pass pointer on stack to helper function", + .insns = { + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr = "invalid indirect read from stack off -8+0 size 8", + .result = REJECT, + }, + { + "unpriv: mangle pointer on stack 1", + .insns = { + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), + BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "attempt to corrupt spilled", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: mangle pointer on stack 2", + .insns = { + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), + BPF_ST_MEM(BPF_B, BPF_REG_10, -1, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "attempt to corrupt spilled", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: read pointer from stack in small chunks", + .insns = { + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid size", + .result = REJECT, + }, + { + "unpriv: write pointer into ctx", + .insns = { + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R1 leaks addr", + .result_unpriv = REJECT, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "unpriv: spill/fill of ctx", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "unpriv: spill/fill of ctx 2", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_hash_recalc), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of ctx 3", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_hash_recalc), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "R1 type=fp expected=ctx", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of ctx 4", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_10, + BPF_REG_0, -8, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_hash_recalc), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "R1 type=inv expected=ctx", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of different pointers stx", + .insns = { + BPF_MOV64_IMM(BPF_REG_3, 42), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, + offsetof(struct __sk_buff, mark)), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "same insn cannot be used with different pointers", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "unpriv: spill/fill of different pointers ldx", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, + -(__s32)offsetof(struct bpf_perf_event_data, + sample_period) - 8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, + offsetof(struct bpf_perf_event_data, + sample_period)), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "same insn cannot be used with different pointers", + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "unpriv: write pointer into map elem value", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr_unpriv = "R0 leaks addr", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: partial copy of pointer", + .insns = { + BPF_MOV32_REG(BPF_REG_1, BPF_REG_10), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R10 partial copy", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: pass pointer to tail_call", + .insns = { + BPF_MOV64_REG(BPF_REG_3, BPF_REG_1), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_prog = { 1 }, + .errstr_unpriv = "R3 leaks addr into helper", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: cmp map pointer with zero", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 1 }, + .errstr_unpriv = "R1 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: write into frame pointer", + .insns = { + BPF_MOV64_REG(BPF_REG_10, BPF_REG_1), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "frame pointer is read only", + .result = REJECT, + }, + { + "unpriv: spill/fill frame pointer", + .insns = { + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "frame pointer is read only", + .result = REJECT, + }, + { + "unpriv: cmp of frame pointer", + .insns = { + BPF_JMP_IMM(BPF_JEQ, BPF_REG_10, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R10 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: cmp of stack pointer", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R2 pointer comparison", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "unpriv: obfuscate stack pointer", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R2 pointer arithmetic", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "raw_stack: no skb_load_bytes", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + /* Call to skb_load_bytes() omitted. */ + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid read from stack off -8+0 size 8", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, negative len", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, negative len 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, ~0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, zero len", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, no init", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, init", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_6, 0, 0xcafe), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, spilled regs around bounds", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 8), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, + offsetof(struct __sk_buff, mark)), + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_2, + offsetof(struct __sk_buff, priority)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, spilled regs corruption", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, + offsetof(struct __sk_buff, mark)), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "R0 invalid mem access 'inv'", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, spilled regs corruption 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 8), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, + offsetof(struct __sk_buff, mark)), + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_2, + offsetof(struct __sk_buff, priority)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_3, + offsetof(struct __sk_buff, pkt_type)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_3), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "R3 invalid mem access 'inv'", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, spilled regs + data", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 8), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, + offsetof(struct __sk_buff, mark)), + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_2, + offsetof(struct __sk_buff, priority)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_3), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, invalid access 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -513), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3 off=-513 access_size=8", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, invalid access 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -1), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3 off=-1 access_size=8", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, invalid access 3", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 0xffffffff), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 0xffffffff), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3 off=-1 access_size=-1", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, invalid access 4", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -1), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 0x7fffffff), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3 off=-1 access_size=2147483647", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, invalid access 5", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -512), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 0x7fffffff), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3 off=-512 access_size=2147483647", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, invalid access 6", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -512), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid stack type R3 off=-512 access_size=0", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "raw_stack: skb_load_bytes, large access", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -512), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_4, 512), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test1", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test2", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_3), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14), + BPF_JMP_REG(BPF_JGT, BPF_REG_5, BPF_REG_4, 15), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_3, 7), + BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_3, 12), + BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 14), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_4), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 48), + BPF_ALU64_IMM(BPF_RSH, BPF_REG_2, 48), + BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_2), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_3), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_3, 4), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test3", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access off=76", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, + }, + { + "direct packet access: test4 (write)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test5 (pkt_end >= reg, good access)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test6 (pkt_end >= reg, bad access)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 3), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid access to packet", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test7 (pkt_end >= reg, both accesses)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 3), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid access to packet", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test8 (double test, variant 1)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 4), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test9 (double test, variant 2)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test10 (write invalid)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid access to packet", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test1, valid packet_ptr range", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct xdp_md, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct xdp_md, data_end)), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 5), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_update_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 5 }, + .result_unpriv = ACCEPT, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_XDP, + }, + { + "helper access to packet: test2, unchecked packet_ptr", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct xdp_md, data)), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 1 }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_XDP, + }, + { + "helper access to packet: test3, variable add", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct xdp_md, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct xdp_md, data_end)), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 10), + BPF_LDX_MEM(BPF_B, BPF_REG_5, BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_5), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_5, BPF_REG_3, 4), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 11 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_XDP, + }, + { + "helper access to packet: test4, packet_ptr with bad range", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct xdp_md, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct xdp_md, data_end)), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 7 }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_XDP, + }, + { + "helper access to packet: test5, packet_ptr with too short range", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct xdp_md, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct xdp_md, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 3), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 6 }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_XDP, + }, + { + "helper access to packet: test6, cls valid packet_ptr range", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 5), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_update_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 5 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test7, cls unchecked packet_ptr", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 1 }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test8, cls variable add", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 10), + BPF_LDX_MEM(BPF_B, BPF_REG_5, BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_5), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_5, BPF_REG_3, 4), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 11 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test9, cls packet_ptr with bad range", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 7 }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test10, cls packet_ptr with too short range", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 3), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 6 }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test11, cls unsuitable helper 1", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_7, 4), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_4, 42), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_store_bytes), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "helper access to the packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test12, cls unsuitable helper 2", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 3), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_4, 4), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "helper access to the packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test13, cls helper ok", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test14, cls helper fail sub", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 4), + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "type=inv expected=fp", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test15, cls helper fail range 1", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_2, 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test16, cls helper fail range 2", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_2, -9), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test17, cls helper fail range 3", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_2, ~0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test18, cls helper fail range zero", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test19, pkt end as input", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "R1 type=pkt_end expected=fp", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to packet: test20, wrong reg", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), + BPF_MOV64_IMM(BPF_REG_2, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_csum_diff), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "valid map access into an array with a constant", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 leaks addr", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "valid map access into an array with a register", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_IMM(BPF_REG_1, 4), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "valid map access into an array with a variable", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, MAX_ENTRIES, 3), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "valid map access into an array with a signed variable", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_1, 0xffffffff, 1), + BPF_MOV32_IMM(BPF_REG_1, 0), + BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES), + BPF_JMP_REG(BPF_JSGT, BPF_REG_2, BPF_REG_1, 1), + BPF_MOV32_IMM(BPF_REG_1, 0), + BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .result_unpriv = REJECT, + .result = ACCEPT, + }, + { + "invalid map access into an array with a constant", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, (MAX_ENTRIES + 1) << 2, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=48 size=8", + .result = REJECT, + }, + { + "invalid map access into an array with a register", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_IMM(BPF_REG_1, MAX_ENTRIES + 1), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "R0 min value is outside of the array range", + .result_unpriv = REJECT, + .result = REJECT, + }, + { + "invalid map access into an array with a variable", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", + .result_unpriv = REJECT, + .result = REJECT, + }, + { + "invalid map access into an array with no floor check", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES), + BPF_JMP_REG(BPF_JSGT, BPF_REG_2, BPF_REG_1, 1), + BPF_MOV32_IMM(BPF_REG_1, 0), + BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", + .result_unpriv = REJECT, + .result = REJECT, + }, + { + "invalid map access into an array with a invalid max check", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES + 1), + BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1), + BPF_MOV32_IMM(BPF_REG_1, 0), + BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "invalid access to map value, value_size=48 off=44 size=8", + .result_unpriv = REJECT, + .result = REJECT, + }, + { + "invalid map access into an array with a invalid max check", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_8), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3, 11 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", + .result_unpriv = REJECT, + .result = REJECT, + }, +}; + +static int probe_filter_length(const struct bpf_insn *fp) +{ + int len; + + for (len = MAX_INSNS - 1; len > 0; --len) + if (fp[len].code != 0 || fp[len].imm != 0) + break; + return len + 1; +} + +static int create_map(uint32_t size_value, uint32_t max_elem) +{ + int fd; + + fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(long long), + size_value, max_elem, BPF_F_NO_PREALLOC); + if (fd < 0) + printf("Failed to create hash map '%s'!\n", strerror(errno)); + + return fd; +} + +static int create_prog_array(void) +{ + int fd; + + fd = bpf_map_create(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), + sizeof(int), 4, 0); + if (fd < 0) + printf("Failed to create prog array '%s'!\n", strerror(errno)); + + return fd; +} + +static char bpf_vlog[32768]; + +static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, + int *fd_f1, int *fd_f2, int *fd_f3) +{ + int *fixup_map1 = test->fixup_map1; + int *fixup_map2 = test->fixup_map2; + int *fixup_prog = test->fixup_prog; + + /* Allocating HTs with 1 elem is fine here, since we only test + * for verifier and not do a runtime lookup, so the only thing + * that really matters is value size in this case. + */ + if (*fixup_map1) { + *fd_f1 = create_map(sizeof(long long), 1); + do { + prog[*fixup_map1].imm = *fd_f1; + fixup_map1++; + } while (*fixup_map1); + } + + if (*fixup_map2) { + *fd_f2 = create_map(sizeof(struct test_val), 1); + do { + prog[*fixup_map2].imm = *fd_f2; + fixup_map2++; + } while (*fixup_map2); + } + + if (*fixup_prog) { + *fd_f3 = create_prog_array(); + do { + prog[*fixup_prog].imm = *fd_f3; + fixup_prog++; + } while (*fixup_prog); + } +} + +static void do_test_single(struct bpf_test *test, bool unpriv, + int *passes, int *errors) +{ + struct bpf_insn *prog = test->insns; + int prog_len = probe_filter_length(prog); + int prog_type = test->prog_type; + int fd_f1 = -1, fd_f2 = -1, fd_f3 = -1; + int fd_prog, expected_ret; + const char *expected_err; + + do_test_fixup(test, prog, &fd_f1, &fd_f2, &fd_f3); + + fd_prog = bpf_prog_load(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, + prog, prog_len * sizeof(struct bpf_insn), + "GPL", bpf_vlog, sizeof(bpf_vlog)); + + expected_ret = unpriv && test->result_unpriv != UNDEF ? + test->result_unpriv : test->result; + expected_err = unpriv && test->errstr_unpriv ? + test->errstr_unpriv : test->errstr; + if (expected_ret == ACCEPT) { + if (fd_prog < 0) { + printf("FAIL\nFailed to load prog '%s'!\n", + strerror(errno)); + goto fail_log; + } + } else { + if (fd_prog >= 0) { + printf("FAIL\nUnexpected success to load!\n"); + goto fail_log; + } + if (!strstr(bpf_vlog, expected_err)) { + printf("FAIL\nUnexpected error message!\n"); + goto fail_log; + } + } + + (*passes)++; + printf("OK\n"); +close_fds: + close(fd_prog); + close(fd_f1); + close(fd_f2); + close(fd_f3); + sched_yield(); + return; +fail_log: + (*errors)++; + printf("%s", bpf_vlog); + goto close_fds; +} + +static int do_test(bool unpriv, unsigned int from, unsigned int to) +{ + int i, passes = 0, errors = 0; + + for (i = from; i < to; i++) { + struct bpf_test *test = &tests[i]; + + /* Program types that are not supported by non-root we + * skip right away. + */ + if (unpriv && test->prog_type) + continue; + + printf("#%d %s ", i, test->descr); + do_test_single(test, unpriv, &passes, &errors); + } + + printf("Summary: %d PASSED, %d FAILED\n", passes, errors); + return errors ? -errors : 0; +} + +int main(int argc, char **argv) +{ + struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; + struct rlimit rlim = { 1 << 20, 1 << 20 }; + unsigned int from = 0, to = ARRAY_SIZE(tests); + bool unpriv = geteuid() != 0; + + if (argc == 3) { + unsigned int l = atoi(argv[argc - 2]); + unsigned int u = atoi(argv[argc - 1]); + + if (l < to && u < to) { + from = l; + to = u + 1; + } + } else if (argc == 2) { + unsigned int t = atoi(argv[argc - 1]); + + if (t < to) { + from = t; + to = t + 1; + } + } + + setrlimit(RLIMIT_MEMLOCK, unpriv ? &rlim : &rinf); + return do_test(unpriv, from, to); +} -- cgit v1.1 From 790510d99f39cee7f275d001aa5024032ed9bb48 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:43 -0700 Subject: net: Remove refnr arg when inserting link adjacencies Commit 93409033ae65 ("net: Add netdev all_adj_list refcnt propagation to fix panic") propagated the refnr to insert and remove functions tracking the netdev adjacency graph. However, for the insert path the refnr can only be 1. Accordingly, remove the refnr argument to make that clear. ie., the refnr arg in 93409033ae65 was only needed for the remove path. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/dev.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 352e981..f67fd16 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5453,7 +5453,6 @@ static inline bool netdev_adjacent_is_neigh_list(struct net_device *dev, static int __netdev_adjacent_dev_insert(struct net_device *dev, struct net_device *adj_dev, - u16 ref_nr, struct list_head *dev_list, void *private, bool master) { @@ -5463,7 +5462,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev, adj = __netdev_find_adj(adj_dev, dev_list); if (adj) { - adj->ref_nr += ref_nr; + adj->ref_nr += 1; return 0; } @@ -5473,7 +5472,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev, adj->dev = adj_dev; adj->master = master; - adj->ref_nr = ref_nr; + adj->ref_nr = 1; adj->private = private; dev_hold(adj_dev); @@ -5547,22 +5546,21 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev, static int __netdev_adjacent_dev_link_lists(struct net_device *dev, struct net_device *upper_dev, - u16 ref_nr, struct list_head *up_list, struct list_head *down_list, void *private, bool master) { int ret; - ret = __netdev_adjacent_dev_insert(dev, upper_dev, ref_nr, up_list, + ret = __netdev_adjacent_dev_insert(dev, upper_dev, up_list, private, master); if (ret) return ret; - ret = __netdev_adjacent_dev_insert(upper_dev, dev, ref_nr, down_list, + ret = __netdev_adjacent_dev_insert(upper_dev, dev, down_list, private, false); if (ret) { - __netdev_adjacent_dev_remove(dev, upper_dev, ref_nr, up_list); + __netdev_adjacent_dev_remove(dev, upper_dev, 1, up_list); return ret; } @@ -5570,10 +5568,9 @@ static int __netdev_adjacent_dev_link_lists(struct net_device *dev, } static int __netdev_adjacent_dev_link(struct net_device *dev, - struct net_device *upper_dev, - u16 ref_nr) + struct net_device *upper_dev) { - return __netdev_adjacent_dev_link_lists(dev, upper_dev, ref_nr, + return __netdev_adjacent_dev_link_lists(dev, upper_dev, &dev->all_adj_list.upper, &upper_dev->all_adj_list.lower, NULL, false); @@ -5602,12 +5599,12 @@ static int __netdev_adjacent_dev_link_neighbour(struct net_device *dev, struct net_device *upper_dev, void *private, bool master) { - int ret = __netdev_adjacent_dev_link(dev, upper_dev, 1); + int ret = __netdev_adjacent_dev_link(dev, upper_dev); if (ret) return ret; - ret = __netdev_adjacent_dev_link_lists(dev, upper_dev, 1, + ret = __netdev_adjacent_dev_link_lists(dev, upper_dev, &dev->adj_list.upper, &upper_dev->adj_list.lower, private, master); @@ -5676,7 +5673,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) { pr_debug("Interlinking %s with %s, non-neighbour\n", i->dev->name, j->dev->name); - ret = __netdev_adjacent_dev_link(i->dev, j->dev, i->ref_nr); + ret = __netdev_adjacent_dev_link(i->dev, j->dev); if (ret) goto rollback_mesh; } @@ -5686,7 +5683,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) { pr_debug("linking %s's upper device %s with %s\n", upper_dev->name, i->dev->name, dev->name); - ret = __netdev_adjacent_dev_link(dev, i->dev, i->ref_nr); + ret = __netdev_adjacent_dev_link(dev, i->dev); if (ret) goto rollback_upper_mesh; } @@ -5695,7 +5692,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, list_for_each_entry(i, &dev->all_adj_list.lower, list) { pr_debug("linking %s's lower device %s with %s\n", dev->name, i->dev->name, upper_dev->name); - ret = __netdev_adjacent_dev_link(i->dev, upper_dev, i->ref_nr); + ret = __netdev_adjacent_dev_link(i->dev, upper_dev); if (ret) goto rollback_lower_mesh; } -- cgit v1.1 From 1a3f060c1a47dba4e12ac21ce62b57666b9c4e95 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:44 -0700 Subject: net: Introduce new api for walking upper and lower devices This patch introduces netdev_walk_all_upper_dev_rcu, netdev_walk_all_lower_dev and netdev_walk_all_lower_dev_rcu. These functions recursively walk the adj_list of devices to determine all upper and lower devices. The functions take a callback function that is invoked for each device in the list. If the callback returns non-0, the walk is terminated and the functions return that code back to callers. v3 - simplified netdev_has_upper_dev_all_rcu and __netdev_has_upper_dev and removed typecast as suggested by Stephen v2 - fixed definition of netdev_next_lower_dev_rcu to mirror the upper_dev version. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/linux/netdevice.h | 17 +++++ net/core/dev.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bf341b6..a5902d99 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3778,6 +3778,14 @@ struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, updev; \ updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter))) +int netdev_walk_all_upper_dev_rcu(struct net_device *dev, + int (*fn)(struct net_device *upper_dev, + void *data), + void *data); + +bool netdev_has_upper_dev_all_rcu(struct net_device *dev, + struct net_device *upper_dev); + void *netdev_lower_get_next_private(struct net_device *dev, struct list_head **iter); void *netdev_lower_get_next_private_rcu(struct net_device *dev, @@ -3821,6 +3829,15 @@ struct net_device *netdev_all_lower_get_next_rcu(struct net_device *dev, ldev; \ ldev = netdev_all_lower_get_next_rcu(dev, &(iter))) +int netdev_walk_all_lower_dev(struct net_device *dev, + int (*fn)(struct net_device *lower_dev, + void *data), + void *data); +int netdev_walk_all_lower_dev_rcu(struct net_device *dev, + int (*fn)(struct net_device *lower_dev, + void *data), + void *data); + void *netdev_adjacent_get_private(struct list_head *adj_list); void *netdev_lower_get_first_private_rcu(struct net_device *dev); struct net_device *netdev_master_upper_dev_get(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index f67fd16..fc48337 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5156,6 +5156,31 @@ bool netdev_has_upper_dev(struct net_device *dev, EXPORT_SYMBOL(netdev_has_upper_dev); /** + * netdev_has_upper_dev_all - Check if device is linked to an upper device + * @dev: device + * @upper_dev: upper device to check + * + * Find out if a device is linked to specified upper device and return true + * in case it is. Note that this checks the entire upper device chain. + * The caller must hold rcu lock. + */ + +static int __netdev_has_upper_dev(struct net_device *upper_dev, void *data) +{ + struct net_device *dev = data; + + return upper_dev == dev; +} + +bool netdev_has_upper_dev_all_rcu(struct net_device *dev, + struct net_device *upper_dev) +{ + return !!netdev_walk_all_upper_dev_rcu(dev, __netdev_has_upper_dev, + upper_dev); +} +EXPORT_SYMBOL(netdev_has_upper_dev_all_rcu); + +/** * netdev_has_any_upper_dev - Check if device is linked to some device * @dev: device * @@ -5255,6 +5280,51 @@ struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, } EXPORT_SYMBOL(netdev_all_upper_get_next_dev_rcu); +static struct net_device *netdev_next_upper_dev_rcu(struct net_device *dev, + struct list_head **iter) +{ + struct netdev_adjacent *upper; + + WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held()); + + upper = list_entry_rcu((*iter)->next, struct netdev_adjacent, list); + + if (&upper->list == &dev->adj_list.upper) + return NULL; + + *iter = &upper->list; + + return upper->dev; +} + +int netdev_walk_all_upper_dev_rcu(struct net_device *dev, + int (*fn)(struct net_device *dev, + void *data), + void *data) +{ + struct net_device *udev; + struct list_head *iter; + int ret; + + for (iter = &dev->adj_list.upper, + udev = netdev_next_upper_dev_rcu(dev, &iter); + udev; + udev = netdev_next_upper_dev_rcu(dev, &iter)) { + /* first is the upper device itself */ + ret = fn(udev, data); + if (ret) + return ret; + + /* then look at all of its upper devices */ + ret = netdev_walk_all_upper_dev_rcu(udev, fn, data); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(netdev_walk_all_upper_dev_rcu); + /** * netdev_lower_get_next_private - Get the next ->private from the * lower neighbour list @@ -5361,6 +5431,49 @@ struct net_device *netdev_all_lower_get_next(struct net_device *dev, struct list } EXPORT_SYMBOL(netdev_all_lower_get_next); +static struct net_device *netdev_next_lower_dev(struct net_device *dev, + struct list_head **iter) +{ + struct netdev_adjacent *lower; + + lower = list_entry(*iter, struct netdev_adjacent, list); + + if (&lower->list == &dev->adj_list.lower) + return NULL; + + *iter = lower->list.next; + + return lower->dev; +} + +int netdev_walk_all_lower_dev(struct net_device *dev, + int (*fn)(struct net_device *dev, + void *data), + void *data) +{ + struct net_device *ldev; + struct list_head *iter; + int ret; + + for (iter = &dev->adj_list.lower, + ldev = netdev_next_lower_dev(dev, &iter); + ldev; + ldev = netdev_next_lower_dev(dev, &iter)) { + /* first is the lower device itself */ + ret = fn(ldev, data); + if (ret) + return ret; + + /* then look at all of its lower devices */ + ret = netdev_walk_all_lower_dev(ldev, fn, data); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(netdev_walk_all_lower_dev); + /** * netdev_all_lower_get_next_rcu - Get the next device from all * lower neighbour list, RCU variant @@ -5382,6 +5495,48 @@ struct net_device *netdev_all_lower_get_next_rcu(struct net_device *dev, } EXPORT_SYMBOL(netdev_all_lower_get_next_rcu); +static struct net_device *netdev_next_lower_dev_rcu(struct net_device *dev, + struct list_head **iter) +{ + struct netdev_adjacent *lower; + + lower = list_entry_rcu((*iter)->next, struct netdev_adjacent, list); + if (&lower->list == &dev->adj_list.lower) + return NULL; + + *iter = &lower->list; + + return lower->dev; +} + +int netdev_walk_all_lower_dev_rcu(struct net_device *dev, + int (*fn)(struct net_device *dev, + void *data), + void *data) +{ + struct net_device *ldev; + struct list_head *iter; + int ret; + + for (iter = &dev->adj_list.lower, + ldev = netdev_next_lower_dev_rcu(dev, &iter); + ldev; + ldev = netdev_next_lower_dev_rcu(dev, &iter)) { + /* first is the lower device itself */ + ret = fn(ldev, data); + if (ret) + return ret; + + /* then look at all of its lower devices */ + ret = netdev_walk_all_lower_dev_rcu(ldev, fn, data); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(netdev_walk_all_lower_dev_rcu); + /** * netdev_lower_get_first_private_rcu - Get the first ->private from the * lower neighbour list, RCU -- cgit v1.1 From b3208b2024c9089106df52ae25ebf39068d6f9fc Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:45 -0700 Subject: net: bonding: Flip to the new dev walk API Convert alb_send_learning_packets and bond_has_this_ip to use the new netdev_walk_all_upper_dev_rcu API. In both cases this is just a code conversion; no functional change is intended. v2 - removed typecast of data and simplified bond_upper_dev_walk Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/bonding/bond_alb.c | 82 ++++++++++++++++++++++++++--------------- drivers/net/bonding/bond_main.c | 17 +++++---- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 551f0f8..c80b023 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -950,13 +950,61 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[], dev_queue_xmit(skb); } +struct alb_walk_data { + struct bonding *bond; + struct slave *slave; + u8 *mac_addr; + bool strict_match; +}; + +static int alb_upper_dev_walk(struct net_device *upper, void *_data) +{ + struct alb_walk_data *data = _data; + bool strict_match = data->strict_match; + struct bonding *bond = data->bond; + struct slave *slave = data->slave; + u8 *mac_addr = data->mac_addr; + struct bond_vlan_tag *tags; + + if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) { + if (strict_match && + ether_addr_equal_64bits(mac_addr, + upper->dev_addr)) { + alb_send_lp_vid(slave, mac_addr, + vlan_dev_vlan_proto(upper), + vlan_dev_vlan_id(upper)); + } else if (!strict_match) { + alb_send_lp_vid(slave, upper->dev_addr, + vlan_dev_vlan_proto(upper), + vlan_dev_vlan_id(upper)); + } + } + + /* If this is a macvlan device, then only send updates + * when strict_match is turned off. + */ + if (netif_is_macvlan(upper) && !strict_match) { + tags = bond_verify_device_path(bond->dev, upper, 0); + if (IS_ERR_OR_NULL(tags)) + BUG(); + alb_send_lp_vid(slave, upper->dev_addr, + tags[0].vlan_proto, tags[0].vlan_id); + kfree(tags); + } + + return 0; +} + static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], bool strict_match) { struct bonding *bond = bond_get_bond_by_slave(slave); - struct net_device *upper; - struct list_head *iter; - struct bond_vlan_tag *tags; + struct alb_walk_data data = { + .strict_match = strict_match, + .mac_addr = mac_addr, + .slave = slave, + .bond = bond, + }; /* send untagged */ alb_send_lp_vid(slave, mac_addr, 0, 0); @@ -965,33 +1013,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], * for that device. */ rcu_read_lock(); - netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { - if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) { - if (strict_match && - ether_addr_equal_64bits(mac_addr, - upper->dev_addr)) { - alb_send_lp_vid(slave, mac_addr, - vlan_dev_vlan_proto(upper), - vlan_dev_vlan_id(upper)); - } else if (!strict_match) { - alb_send_lp_vid(slave, upper->dev_addr, - vlan_dev_vlan_proto(upper), - vlan_dev_vlan_id(upper)); - } - } - - /* If this is a macvlan device, then only send updates - * when strict_match is turned off. - */ - if (netif_is_macvlan(upper) && !strict_match) { - tags = bond_verify_device_path(bond->dev, upper, 0); - if (IS_ERR_OR_NULL(tags)) - BUG(); - alb_send_lp_vid(slave, upper->dev_addr, - tags[0].vlan_proto, tags[0].vlan_id); - kfree(tags); - } - } + netdev_walk_all_upper_dev_rcu(bond->dev, alb_upper_dev_walk, &data); rcu_read_unlock(); } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 5fa36eb..c9944d8 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2270,22 +2270,23 @@ re_arm: } } +static int bond_upper_dev_walk(struct net_device *upper, void *data) +{ + __be32 ip = *((__be32 *)data); + + return ip == bond_confirm_addr(upper, 0, ip); +} + static bool bond_has_this_ip(struct bonding *bond, __be32 ip) { - struct net_device *upper; - struct list_head *iter; bool ret = false; if (ip == bond_confirm_addr(bond->dev, 0, ip)) return true; rcu_read_lock(); - netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { - if (ip == bond_confirm_addr(upper, 0, ip)) { - ret = true; - break; - } - } + if (netdev_walk_all_upper_dev_rcu(bond->dev, bond_upper_dev_walk, &ip)) + ret = true; rcu_read_unlock(); return ret; -- cgit v1.1 From 453d39329ad03f9f6d93ec6d0d57bf7a1e2a20c7 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:46 -0700 Subject: IB/core: Flip to the new dev walk API Convert rdma_is_upper_dev_rcu, handle_netdev_upper and ipoib_get_net_dev_match_addr to the new upper device walk API. This is just a code conversion; no functional change is intended. v2 - removed typecast of data Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/infiniband/core/core_priv.h | 9 +------ drivers/infiniband/core/roce_gid_mgmt.c | 42 ++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index 19d499d..0c0bea0 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -127,14 +127,7 @@ void ib_cache_release_one(struct ib_device *device); static inline bool rdma_is_upper_dev_rcu(struct net_device *dev, struct net_device *upper) { - struct net_device *_upper = NULL; - struct list_head *iter; - - netdev_for_each_all_upper_dev_rcu(dev, _upper, iter) - if (_upper == upper) - break; - - return _upper == upper; + return netdev_has_upper_dev_all_rcu(dev, upper); } int addr_init(void); diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c index 06556c3..3a64a08 100644 --- a/drivers/infiniband/core/roce_gid_mgmt.c +++ b/drivers/infiniband/core/roce_gid_mgmt.c @@ -437,6 +437,28 @@ static void callback_for_addr_gid_device_scan(struct ib_device *device, &parsed->gid_attr); } +struct upper_list { + struct list_head list; + struct net_device *upper; +}; + +static int netdev_upper_walk(struct net_device *upper, void *data) +{ + struct upper_list *entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + struct list_head *upper_list = data; + + if (!entry) { + pr_info("roce_gid_mgmt: couldn't allocate entry to delete ndev\n"); + return 0; + } + + list_add_tail(&entry->list, upper_list); + dev_hold(upper); + entry->upper = upper; + + return 0; +} + static void handle_netdev_upper(struct ib_device *ib_dev, u8 port, void *cookie, void (*handle_netdev)(struct ib_device *ib_dev, @@ -444,30 +466,12 @@ static void handle_netdev_upper(struct ib_device *ib_dev, u8 port, struct net_device *ndev)) { struct net_device *ndev = (struct net_device *)cookie; - struct upper_list { - struct list_head list; - struct net_device *upper; - }; - struct net_device *upper; - struct list_head *iter; struct upper_list *upper_iter; struct upper_list *upper_temp; LIST_HEAD(upper_list); rcu_read_lock(); - netdev_for_each_all_upper_dev_rcu(ndev, upper, iter) { - struct upper_list *entry = kmalloc(sizeof(*entry), - GFP_ATOMIC); - - if (!entry) { - pr_info("roce_gid_mgmt: couldn't allocate entry to delete ndev\n"); - continue; - } - - list_add_tail(&entry->list, &upper_list); - dev_hold(upper); - entry->upper = upper; - } + netdev_walk_all_upper_dev_rcu(ndev, netdev_upper_walk, &upper_list); rcu_read_unlock(); handle_netdev(ib_dev, port, ndev); -- cgit v1.1 From e0e79c8e74b08976d9b45e52b704b9228c6965c7 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:47 -0700 Subject: IB/ipoib: Flip to new dev walk API Convert ipoib_get_net_dev_match_addr to the new upper device walk API. This is just a code conversion; no functional change is intended. v2 - removed typecast of data Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/infiniband/ulp/ipoib/ipoib_main.c | 37 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 5636fc3d..cc05921 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -292,6 +292,25 @@ static struct net_device *ipoib_get_master_net_dev(struct net_device *dev) return dev; } +struct ipoib_walk_data { + const struct sockaddr *addr; + struct net_device *result; +}; + +static int ipoib_upper_walk(struct net_device *upper, void *_data) +{ + struct ipoib_walk_data *data = _data; + int ret = 0; + + if (ipoib_is_dev_match_addr_rcu(data->addr, upper)) { + dev_hold(upper); + data->result = upper; + ret = 1; + } + + return ret; +} + /** * Find a net_device matching the given address, which is an upper device of * the given net_device. @@ -304,27 +323,21 @@ static struct net_device *ipoib_get_master_net_dev(struct net_device *dev) static struct net_device *ipoib_get_net_dev_match_addr( const struct sockaddr *addr, struct net_device *dev) { - struct net_device *upper, - *result = NULL; - struct list_head *iter; + struct ipoib_walk_data data = { + .addr = addr, + }; rcu_read_lock(); if (ipoib_is_dev_match_addr_rcu(addr, dev)) { dev_hold(dev); - result = dev; + data.result = dev; goto out; } - netdev_for_each_all_upper_dev_rcu(dev, upper, iter) { - if (ipoib_is_dev_match_addr_rcu(addr, upper)) { - dev_hold(upper); - result = upper; - break; - } - } + netdev_walk_all_upper_dev_rcu(dev, ipoib_upper_walk, &data); out: rcu_read_unlock(); - return result; + return data.result; } /* returns the number of IPoIB netdevs on top a given ipoib device matching a -- cgit v1.1 From 1cd127fc7d3a6d6a0fc9f9cca47ca3a16ee79679 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:48 -0700 Subject: ixgbe: Flip to the new dev walk API Convert ixgbe users to new dev walk API. This is just a code conversion; no functional change is intended. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 132 ++++++++++++++++---------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 7dfde20..cbd2cfa 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5012,24 +5012,23 @@ fwd_queue_err: return err; } -static void ixgbe_configure_dfwd(struct ixgbe_adapter *adapter) +static int ixgbe_upper_dev_walk(struct net_device *upper, void *data) { - struct net_device *upper; - struct list_head *iter; - int err; - - netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { - if (netif_is_macvlan(upper)) { - struct macvlan_dev *dfwd = netdev_priv(upper); - struct ixgbe_fwd_adapter *vadapter = dfwd->fwd_priv; + if (netif_is_macvlan(upper)) { + struct macvlan_dev *dfwd = netdev_priv(upper); + struct ixgbe_fwd_adapter *vadapter = dfwd->fwd_priv; - if (dfwd->fwd_priv) { - err = ixgbe_fwd_ring_up(upper, vadapter); - if (err) - continue; - } - } + if (dfwd->fwd_priv) + ixgbe_fwd_ring_up(upper, vadapter); } + + return 0; +} + +static void ixgbe_configure_dfwd(struct ixgbe_adapter *adapter) +{ + netdev_walk_all_upper_dev_rcu(adapter->netdev, + ixgbe_upper_dev_walk, NULL); } static void ixgbe_configure(struct ixgbe_adapter *adapter) @@ -5448,12 +5447,25 @@ static void ixgbe_fdir_filter_exit(struct ixgbe_adapter *adapter) spin_unlock(&adapter->fdir_perfect_lock); } +static int ixgbe_disable_macvlan(struct net_device *upper, void *data) +{ + if (netif_is_macvlan(upper)) { + struct macvlan_dev *vlan = netdev_priv(upper); + + if (vlan->fwd_priv) { + netif_tx_stop_all_queues(upper); + netif_carrier_off(upper); + netif_tx_disable(upper); + } + } + + return 0; +} + void ixgbe_down(struct ixgbe_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct ixgbe_hw *hw = &adapter->hw; - struct net_device *upper; - struct list_head *iter; int i; /* signal that we are down to the interrupt handler */ @@ -5477,17 +5489,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter) netif_tx_disable(netdev); /* disable any upper devices */ - netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { - if (netif_is_macvlan(upper)) { - struct macvlan_dev *vlan = netdev_priv(upper); - - if (vlan->fwd_priv) { - netif_tx_stop_all_queues(upper); - netif_carrier_off(upper); - netif_tx_disable(upper); - } - } - } + netdev_walk_all_upper_dev_rcu(adapter->netdev, + ixgbe_disable_macvlan, NULL); ixgbe_irq_disable(adapter); @@ -6723,6 +6726,18 @@ static void ixgbe_update_default_up(struct ixgbe_adapter *adapter) #endif } +static int ixgbe_enable_macvlan(struct net_device *upper, void *data) +{ + if (netif_is_macvlan(upper)) { + struct macvlan_dev *vlan = netdev_priv(upper); + + if (vlan->fwd_priv) + netif_tx_wake_all_queues(upper); + } + + return 0; +} + /** * ixgbe_watchdog_link_is_up - update netif_carrier status and * print link up message @@ -6732,8 +6747,6 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct ixgbe_hw *hw = &adapter->hw; - struct net_device *upper; - struct list_head *iter; u32 link_speed = adapter->link_speed; const char *speed_str; bool flow_rx, flow_tx; @@ -6804,14 +6817,8 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter) /* enable any upper devices */ rtnl_lock(); - netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { - if (netif_is_macvlan(upper)) { - struct macvlan_dev *vlan = netdev_priv(upper); - - if (vlan->fwd_priv) - netif_tx_wake_all_queues(upper); - } - } + netdev_walk_all_upper_dev_rcu(adapter->netdev, + ixgbe_enable_macvlan, NULL); rtnl_unlock(); /* update the default user priority for VFs */ @@ -8345,12 +8352,38 @@ static int ixgbe_configure_clsu32_del_hnode(struct ixgbe_adapter *adapter, } #ifdef CONFIG_NET_CLS_ACT +struct upper_walk_data { + struct ixgbe_adapter *adapter; + u64 action; + int ifindex; + u8 queue; +}; + +static int get_macvlan_queue(struct net_device *upper, void *_data) +{ + if (netif_is_macvlan(upper)) { + struct macvlan_dev *dfwd = netdev_priv(upper); + struct ixgbe_fwd_adapter *vadapter = dfwd->fwd_priv; + struct upper_walk_data *data = _data; + struct ixgbe_adapter *adapter = data->adapter; + int ifindex = data->ifindex; + + if (vadapter && vadapter->netdev->ifindex == ifindex) { + data->queue = adapter->rx_ring[vadapter->rx_base_queue]->reg_idx; + data->action = data->queue; + return 1; + } + } + + return 0; +} + static int handle_redirect_action(struct ixgbe_adapter *adapter, int ifindex, u8 *queue, u64 *action) { unsigned int num_vfs = adapter->num_vfs, vf; + struct upper_walk_data data; struct net_device *upper; - struct list_head *iter; /* redirect to a SRIOV VF */ for (vf = 0; vf < num_vfs; ++vf) { @@ -8368,17 +8401,16 @@ static int handle_redirect_action(struct ixgbe_adapter *adapter, int ifindex, } /* redirect to a offloaded macvlan netdev */ - netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { - if (netif_is_macvlan(upper)) { - struct macvlan_dev *dfwd = netdev_priv(upper); - struct ixgbe_fwd_adapter *vadapter = dfwd->fwd_priv; - - if (vadapter && vadapter->netdev->ifindex == ifindex) { - *queue = adapter->rx_ring[vadapter->rx_base_queue]->reg_idx; - *action = *queue; - return 0; - } - } + data.adapter = adapter; + data.ifindex = ifindex; + data.action = 0; + data.queue = 0; + if (netdev_walk_all_upper_dev_rcu(adapter->netdev, + get_macvlan_queue, &data)) { + *action = data.action; + *queue = data.queue; + + return 0; } return -EINVAL; -- cgit v1.1 From dd82364c3ab93a96bb1e45d22106a4b1ea4bef39 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:49 -0700 Subject: mlxsw: Flip to the new dev walk API Convert mlxsw users to new dev walk API. This is just a code conversion; no functional change is intended. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 37 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 43a5edd..99805fd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3092,19 +3092,30 @@ static bool mlxsw_sp_port_dev_check(const struct net_device *dev) return dev->netdev_ops == &mlxsw_sp_port_netdev_ops; } +static int mlxsw_lower_dev_walk(struct net_device *lower_dev, void *data) +{ + struct mlxsw_sp_port **port = data; + int ret = 0; + + if (mlxsw_sp_port_dev_check(lower_dev)) { + *port = netdev_priv(lower_dev); + ret = 1; + } + + return ret; +} + static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev) { - struct net_device *lower_dev; - struct list_head *iter; + struct mlxsw_sp_port *port; if (mlxsw_sp_port_dev_check(dev)) return netdev_priv(dev); - netdev_for_each_all_lower_dev(dev, lower_dev, iter) { - if (mlxsw_sp_port_dev_check(lower_dev)) - return netdev_priv(lower_dev); - } - return NULL; + port = NULL; + netdev_walk_all_lower_dev(dev, mlxsw_lower_dev_walk, &port); + + return port; } static struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) @@ -3117,17 +3128,15 @@ static struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev) { - struct net_device *lower_dev; - struct list_head *iter; + struct mlxsw_sp_port *port; if (mlxsw_sp_port_dev_check(dev)) return netdev_priv(dev); - netdev_for_each_all_lower_dev_rcu(dev, lower_dev, iter) { - if (mlxsw_sp_port_dev_check(lower_dev)) - return netdev_priv(lower_dev); - } - return NULL; + port = NULL; + netdev_walk_all_lower_dev_rcu(dev, mlxsw_lower_dev_walk, &port); + + return port; } struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev) -- cgit v1.1 From cf2d67408b2f9a840f27d25a0219070b5b5deec4 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:50 -0700 Subject: rocker: Flip to the new dev walk API Convert rocker to the new dev walk API. This is just a code conversion; no functional change is intended. v2 - removed typecast of data Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_main.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 9b64f36..2e81b70 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2839,20 +2839,37 @@ static bool rocker_port_dev_check_under(const struct net_device *dev, return true; } +struct rocker_walk_data { + struct rocker *rocker; + struct rocker_port *port; +}; + +static int rocker_lower_dev_walk(struct net_device *lower_dev, void *_data) +{ + struct rocker_walk_data *data = _data; + int ret = 0; + + if (rocker_port_dev_check_under(lower_dev, data->rocker)) { + data->port = netdev_priv(lower_dev); + ret = 1; + } + + return ret; +} + struct rocker_port *rocker_port_dev_lower_find(struct net_device *dev, struct rocker *rocker) { - struct net_device *lower_dev; - struct list_head *iter; + struct rocker_walk_data data; if (rocker_port_dev_check_under(dev, rocker)) return netdev_priv(dev); - netdev_for_each_all_lower_dev(dev, lower_dev, iter) { - if (rocker_port_dev_check_under(lower_dev, rocker)) - return netdev_priv(lower_dev); - } - return NULL; + data.rocker = rocker; + data.port = NULL; + netdev_walk_all_lower_dev(dev, rocker_lower_dev_walk, &data); + + return data.port; } static int rocker_netdevice_event(struct notifier_block *unused, -- cgit v1.1 From f1170fd462c67c4ae2f20734566d94e0f8f62f69 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:51 -0700 Subject: net: Remove all_adj_list and its references Only direct adjacencies are maintained. All upper or lower devices can be learned via the new walk API which recursively walks the adj_list for upper devices or lower devices. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/linux/netdevice.h | 25 ------ net/core/dev.c | 223 ++++------------------------------------------ 2 files changed, 18 insertions(+), 230 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a5902d99..458c876 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1456,7 +1456,6 @@ enum netdev_priv_flags { * @ptype_specific: Device-specific, protocol-specific packet handlers * * @adj_list: Directly linked devices, like slaves for bonding - * @all_adj_list: All linked devices, *including* neighbours * @features: Currently active device features * @hw_features: User-changeable features * @@ -1675,11 +1674,6 @@ struct net_device { struct list_head lower; } adj_list; - struct { - struct list_head upper; - struct list_head lower; - } all_adj_list; - netdev_features_t features; netdev_features_t hw_features; netdev_features_t wanted_features; @@ -3771,13 +3765,6 @@ struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, updev; \ updev = netdev_upper_get_next_dev_rcu(dev, &(iter))) -/* iterate through upper list, must be called under RCU read lock */ -#define netdev_for_each_all_upper_dev_rcu(dev, updev, iter) \ - for (iter = &(dev)->all_adj_list.upper, \ - updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)); \ - updev; \ - updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter))) - int netdev_walk_all_upper_dev_rcu(struct net_device *dev, int (*fn)(struct net_device *upper_dev, void *data), @@ -3817,18 +3804,6 @@ struct net_device *netdev_all_lower_get_next(struct net_device *dev, struct net_device *netdev_all_lower_get_next_rcu(struct net_device *dev, struct list_head **iter); -#define netdev_for_each_all_lower_dev(dev, ldev, iter) \ - for (iter = (dev)->all_adj_list.lower.next, \ - ldev = netdev_all_lower_get_next(dev, &(iter)); \ - ldev; \ - ldev = netdev_all_lower_get_next(dev, &(iter))) - -#define netdev_for_each_all_lower_dev_rcu(dev, ldev, iter) \ - for (iter = (dev)->all_adj_list.lower.next, \ - ldev = netdev_all_lower_get_next_rcu(dev, &(iter)); \ - ldev; \ - ldev = netdev_all_lower_get_next_rcu(dev, &(iter))) - int netdev_walk_all_lower_dev(struct net_device *dev, int (*fn)(struct net_device *lower_dev, void *data), diff --git a/net/core/dev.c b/net/core/dev.c index fc48337..a9fe149 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5137,6 +5137,13 @@ static struct netdev_adjacent *__netdev_find_adj(struct net_device *adj_dev, return NULL; } +static int __netdev_has_upper_dev(struct net_device *upper_dev, void *data) +{ + struct net_device *dev = data; + + return upper_dev == dev; +} + /** * netdev_has_upper_dev - Check if device is linked to an upper device * @dev: device @@ -5151,7 +5158,8 @@ bool netdev_has_upper_dev(struct net_device *dev, { ASSERT_RTNL(); - return __netdev_find_adj(upper_dev, &dev->all_adj_list.upper); + return netdev_walk_all_upper_dev_rcu(dev, __netdev_has_upper_dev, + upper_dev); } EXPORT_SYMBOL(netdev_has_upper_dev); @@ -5165,13 +5173,6 @@ EXPORT_SYMBOL(netdev_has_upper_dev); * The caller must hold rcu lock. */ -static int __netdev_has_upper_dev(struct net_device *upper_dev, void *data) -{ - struct net_device *dev = data; - - return upper_dev == dev; -} - bool netdev_has_upper_dev_all_rcu(struct net_device *dev, struct net_device *upper_dev) { @@ -5191,7 +5192,7 @@ static bool netdev_has_any_upper_dev(struct net_device *dev) { ASSERT_RTNL(); - return !list_empty(&dev->all_adj_list.upper); + return !list_empty(&dev->adj_list.upper); } /** @@ -5254,32 +5255,6 @@ struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, } EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu); -/** - * netdev_all_upper_get_next_dev_rcu - Get the next dev from upper list - * @dev: device - * @iter: list_head ** of the current position - * - * Gets the next device from the dev's upper list, starting from iter - * position. The caller must hold RCU read lock. - */ -struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, - struct list_head **iter) -{ - struct netdev_adjacent *upper; - - WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held()); - - upper = list_entry_rcu((*iter)->next, struct netdev_adjacent, list); - - if (&upper->list == &dev->all_adj_list.upper) - return NULL; - - *iter = &upper->list; - - return upper->dev; -} -EXPORT_SYMBOL(netdev_all_upper_get_next_dev_rcu); - static struct net_device *netdev_next_upper_dev_rcu(struct net_device *dev, struct list_head **iter) { @@ -5406,31 +5381,6 @@ void *netdev_lower_get_next(struct net_device *dev, struct list_head **iter) } EXPORT_SYMBOL(netdev_lower_get_next); -/** - * netdev_all_lower_get_next - Get the next device from all lower neighbour list - * @dev: device - * @iter: list_head ** of the current position - * - * Gets the next netdev_adjacent from the dev's all lower neighbour - * list, starting from iter position. The caller must hold RTNL lock or - * its own locking that guarantees that the neighbour all lower - * list will remain unchanged. - */ -struct net_device *netdev_all_lower_get_next(struct net_device *dev, struct list_head **iter) -{ - struct netdev_adjacent *lower; - - lower = list_entry(*iter, struct netdev_adjacent, list); - - if (&lower->list == &dev->all_adj_list.lower) - return NULL; - - *iter = lower->list.next; - - return lower->dev; -} -EXPORT_SYMBOL(netdev_all_lower_get_next); - static struct net_device *netdev_next_lower_dev(struct net_device *dev, struct list_head **iter) { @@ -5474,27 +5424,6 @@ int netdev_walk_all_lower_dev(struct net_device *dev, } EXPORT_SYMBOL_GPL(netdev_walk_all_lower_dev); -/** - * netdev_all_lower_get_next_rcu - Get the next device from all - * lower neighbour list, RCU variant - * @dev: device - * @iter: list_head ** of the current position - * - * Gets the next netdev_adjacent from the dev's all lower neighbour - * list, starting from iter position. The caller must hold RCU read lock. - */ -struct net_device *netdev_all_lower_get_next_rcu(struct net_device *dev, - struct list_head **iter) -{ - struct netdev_adjacent *lower; - - lower = list_first_or_null_rcu(&dev->all_adj_list.lower, - struct netdev_adjacent, list); - - return lower ? lower->dev : NULL; -} -EXPORT_SYMBOL(netdev_all_lower_get_next_rcu); - static struct net_device *netdev_next_lower_dev_rcu(struct net_device *dev, struct list_head **iter) { @@ -5722,15 +5651,6 @@ static int __netdev_adjacent_dev_link_lists(struct net_device *dev, return 0; } -static int __netdev_adjacent_dev_link(struct net_device *dev, - struct net_device *upper_dev) -{ - return __netdev_adjacent_dev_link_lists(dev, upper_dev, - &dev->all_adj_list.upper, - &upper_dev->all_adj_list.lower, - NULL, false); -} - static void __netdev_adjacent_dev_unlink_lists(struct net_device *dev, struct net_device *upper_dev, u16 ref_nr, @@ -5741,40 +5661,19 @@ static void __netdev_adjacent_dev_unlink_lists(struct net_device *dev, __netdev_adjacent_dev_remove(upper_dev, dev, ref_nr, down_list); } -static void __netdev_adjacent_dev_unlink(struct net_device *dev, - struct net_device *upper_dev, - u16 ref_nr) -{ - __netdev_adjacent_dev_unlink_lists(dev, upper_dev, ref_nr, - &dev->all_adj_list.upper, - &upper_dev->all_adj_list.lower); -} - static int __netdev_adjacent_dev_link_neighbour(struct net_device *dev, struct net_device *upper_dev, void *private, bool master) { - int ret = __netdev_adjacent_dev_link(dev, upper_dev); - - if (ret) - return ret; - - ret = __netdev_adjacent_dev_link_lists(dev, upper_dev, - &dev->adj_list.upper, - &upper_dev->adj_list.lower, - private, master); - if (ret) { - __netdev_adjacent_dev_unlink(dev, upper_dev, 1); - return ret; - } - - return 0; + return __netdev_adjacent_dev_link_lists(dev, upper_dev, + &dev->adj_list.upper, + &upper_dev->adj_list.lower, + private, master); } static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev, struct net_device *upper_dev) { - __netdev_adjacent_dev_unlink(dev, upper_dev, 1); __netdev_adjacent_dev_unlink_lists(dev, upper_dev, 1, &dev->adj_list.upper, &upper_dev->adj_list.lower); @@ -5785,7 +5684,6 @@ static int __netdev_upper_dev_link(struct net_device *dev, void *upper_priv, void *upper_info) { struct netdev_notifier_changeupper_info changeupper_info; - struct netdev_adjacent *i, *j, *to_i, *to_j; int ret = 0; ASSERT_RTNL(); @@ -5794,10 +5692,10 @@ static int __netdev_upper_dev_link(struct net_device *dev, return -EBUSY; /* To prevent loops, check if dev is not upper device to upper_dev. */ - if (__netdev_find_adj(dev, &upper_dev->all_adj_list.upper)) + if (netdev_has_upper_dev(upper_dev, dev)) return -EBUSY; - if (__netdev_find_adj(upper_dev, &dev->adj_list.upper)) + if (netdev_has_upper_dev(dev, upper_dev)) return -EEXIST; if (master && netdev_master_upper_dev_get(dev)) @@ -5819,80 +5717,15 @@ static int __netdev_upper_dev_link(struct net_device *dev, if (ret) return ret; - /* Now that we linked these devs, make all the upper_dev's - * all_adj_list.upper visible to every dev's all_adj_list.lower an - * versa, and don't forget the devices itself. All of these - * links are non-neighbours. - */ - list_for_each_entry(i, &dev->all_adj_list.lower, list) { - list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) { - pr_debug("Interlinking %s with %s, non-neighbour\n", - i->dev->name, j->dev->name); - ret = __netdev_adjacent_dev_link(i->dev, j->dev); - if (ret) - goto rollback_mesh; - } - } - - /* add dev to every upper_dev's upper device */ - list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) { - pr_debug("linking %s's upper device %s with %s\n", - upper_dev->name, i->dev->name, dev->name); - ret = __netdev_adjacent_dev_link(dev, i->dev); - if (ret) - goto rollback_upper_mesh; - } - - /* add upper_dev to every dev's lower device */ - list_for_each_entry(i, &dev->all_adj_list.lower, list) { - pr_debug("linking %s's lower device %s with %s\n", dev->name, - i->dev->name, upper_dev->name); - ret = __netdev_adjacent_dev_link(i->dev, upper_dev); - if (ret) - goto rollback_lower_mesh; - } - ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev, &changeupper_info.info); ret = notifier_to_errno(ret); if (ret) - goto rollback_lower_mesh; + goto rollback; return 0; -rollback_lower_mesh: - to_i = i; - list_for_each_entry(i, &dev->all_adj_list.lower, list) { - if (i == to_i) - break; - __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr); - } - - i = NULL; - -rollback_upper_mesh: - to_i = i; - list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) { - if (i == to_i) - break; - __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr); - } - - i = j = NULL; - -rollback_mesh: - to_i = i; - to_j = j; - list_for_each_entry(i, &dev->all_adj_list.lower, list) { - list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) { - if (i == to_i && j == to_j) - break; - __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr); - } - if (i == to_i) - break; - } - +rollback: __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); return ret; @@ -5949,7 +5782,6 @@ void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev) { struct netdev_notifier_changeupper_info changeupper_info; - struct netdev_adjacent *i, *j; ASSERT_RTNL(); changeupper_info.upper_dev = upper_dev; @@ -5961,23 +5793,6 @@ void netdev_upper_dev_unlink(struct net_device *dev, __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); - /* Here is the tricky part. We must remove all dev's lower - * devices from all upper_dev's upper devices and vice - * versa, to maintain the graph relationship. - */ - list_for_each_entry(i, &dev->all_adj_list.lower, list) - list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) - __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr); - - /* remove also the devices itself from lower/upper device - * list - */ - list_for_each_entry(i, &dev->all_adj_list.lower, list) - __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr); - - list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) - __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr); - call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev, &changeupper_info.info); } @@ -7679,8 +7494,6 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, INIT_LIST_HEAD(&dev->link_watch_list); INIT_LIST_HEAD(&dev->adj_list.upper); INIT_LIST_HEAD(&dev->adj_list.lower); - INIT_LIST_HEAD(&dev->all_adj_list.upper); - INIT_LIST_HEAD(&dev->all_adj_list.lower); INIT_LIST_HEAD(&dev->ptype_all); INIT_LIST_HEAD(&dev->ptype_specific); #ifdef CONFIG_NET_SCHED -- cgit v1.1 From 0f524a80ff35af8a7664d7661d948107da142e04 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:52 -0700 Subject: net: Add warning if any lower device is still in adjacency list Lower list should be empty just like upper. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/dev.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index a9fe149..c6bbf31 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5219,6 +5219,20 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev) } EXPORT_SYMBOL(netdev_master_upper_dev_get); +/** + * netdev_has_any_lower_dev - Check if device is linked to some device + * @dev: device + * + * Find out if a device is linked to a lower device and return true in case + * it is. The caller must hold the RTNL lock. + */ +static bool netdev_has_any_lower_dev(struct net_device *dev) +{ + ASSERT_RTNL(); + + return !list_empty(&dev->adj_list.lower); +} + void *netdev_adjacent_get_private(struct list_head *adj_list) { struct netdev_adjacent *adj; @@ -6616,6 +6630,7 @@ static void rollback_registered_many(struct list_head *head) /* Notifier chain MUST detach us all upper devices. */ WARN_ON(netdev_has_any_upper_dev(dev)); + WARN_ON(netdev_has_any_lower_dev(dev)); /* Remove entries from kobject tree */ netdev_unregister_kobject(dev); -- cgit v1.1 From 67b62f98a1de962277b60d77c0c208b76867dbae Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 17 Oct 2016 19:15:53 -0700 Subject: net: dev: Improve debug statements for adjacency tracking Adjacency code only has debugs for the insert case. Add debugs for the remove path and make both consistently worded to make it easier to follow the insert and removal with reference counts. In addition, change the BUG to a WARN_ON. A missing adjacency at removal time is not cause for a panic. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/dev.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index c6bbf31..f55fb45 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5561,6 +5561,9 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev, if (adj) { adj->ref_nr += 1; + pr_debug("Insert adjacency: dev %s adj_dev %s adj->ref_nr %d\n", + dev->name, adj_dev->name, adj->ref_nr); + return 0; } @@ -5574,8 +5577,8 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev, adj->private = private; dev_hold(adj_dev); - pr_debug("dev_hold for %s, because of link added from %s to %s\n", - adj_dev->name, dev->name, adj_dev->name); + pr_debug("Insert adjacency: dev %s adj_dev %s adj->ref_nr %d; dev_hold on %s\n", + dev->name, adj_dev->name, adj->ref_nr, adj_dev->name); if (netdev_adjacent_is_neigh_list(dev, adj_dev, dev_list)) { ret = netdev_adjacent_sysfs_add(dev, adj_dev, dev_list); @@ -5614,17 +5617,22 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev, { struct netdev_adjacent *adj; + pr_debug("Remove adjacency: dev %s adj_dev %s ref_nr %d\n", + dev->name, adj_dev->name, ref_nr); + adj = __netdev_find_adj(adj_dev, dev_list); if (!adj) { - pr_err("tried to remove device %s from %s\n", + pr_err("Adjacency does not exist for device %s from %s\n", dev->name, adj_dev->name); - BUG(); + WARN_ON(1); + return; } if (adj->ref_nr > ref_nr) { - pr_debug("%s to %s ref_nr-%d = %d\n", dev->name, adj_dev->name, - ref_nr, adj->ref_nr-ref_nr); + pr_debug("adjacency: %s to %s ref_nr - %d = %d\n", + dev->name, adj_dev->name, ref_nr, + adj->ref_nr - ref_nr); adj->ref_nr -= ref_nr; return; } @@ -5636,7 +5644,7 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev, netdev_adjacent_sysfs_del(dev, adj_dev->name, dev_list); list_del_rcu(&adj->list); - pr_debug("dev_put for %s, because link removed from %s to %s\n", + pr_debug("adjacency: dev_put for %s, because link removed from %s to %s\n", adj_dev->name, dev->name, adj_dev->name); dev_put(adj_dev); kfree_rcu(adj, rcu); -- cgit v1.1 From 0e0f27dd65dd7403d41d72a8efafe388bbb36f84 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 17 Oct 2016 10:49:52 -0500 Subject: skge: Rename LED_OFF and LED_ON in marvel skge driver to avoid conflicts with leds namespace Adding led support for phy causes namespace conflicts for some phy drivers. The marvel skge driver declared an enum for representing the states of Link LED Register. The enum contained constant LED_OFF which conflicted with declartation found in linux/leds.h. LED_OFF changed to LED_REG_OFF Also changed LED_ON to LED_REG_ON to avoid possible future conflict and for consistency. Signed-off-by: Zach Brown Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/skge.c | 6 +++--- drivers/net/ethernet/marvell/skge.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 9c4b722..9146a51 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -1048,7 +1048,7 @@ static const char *skge_pause(enum pause_status status) static void skge_link_up(struct skge_port *skge) { skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), - LED_BLK_OFF|LED_SYNC_OFF|LED_ON); + LED_BLK_OFF|LED_SYNC_OFF|LED_REG_ON); netif_carrier_on(skge->netdev); netif_wake_queue(skge->netdev); @@ -1062,7 +1062,7 @@ static void skge_link_up(struct skge_port *skge) static void skge_link_down(struct skge_port *skge) { - skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF); + skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_REG_OFF); netif_carrier_off(skge->netdev); netif_stop_queue(skge->netdev); @@ -2668,7 +2668,7 @@ static int skge_down(struct net_device *dev) if (hw->ports == 1) free_irq(hw->pdev->irq, hw); - skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF); + skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_REG_OFF); if (is_genesis(hw)) genesis_stop(skge); else diff --git a/drivers/net/ethernet/marvell/skge.h b/drivers/net/ethernet/marvell/skge.h index a2eb341..3ea151f 100644 --- a/drivers/net/ethernet/marvell/skge.h +++ b/drivers/net/ethernet/marvell/skge.h @@ -662,8 +662,8 @@ enum { LED_BLK_OFF = 1<<4, /* Link LED Blinking Off */ LED_SYNC_ON = 1<<3, /* Use Sync Wire to switch LED */ LED_SYNC_OFF = 1<<2, /* Disable Sync Wire Input */ - LED_ON = 1<<1, /* switch LED on */ - LED_OFF = 1<<0, /* switch LED off */ + LED_REG_ON = 1<<1, /* switch LED on */ + LED_REG_OFF = 1<<0, /* switch LED off */ }; /* Receive GMAC FIFO (YUKON) */ -- cgit v1.1 From 61a179657f1d00e45697de20067d5d7b3724eb91 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 17 Oct 2016 10:49:53 -0500 Subject: net: phy: Encapsulate actions performed during link state changes into function phy_adjust_link During phy state machine state transitions some set of actions should occur whenever the link state changes. These actions should be encapsulated into a single function This patch adds the phy_adjust_link function, which is called whenever phydev->adjust_link would have been called before. Actions that should occur whenever the phy link is adjusted can now be added to the phy_adjust_link function. Signed-off-by: Zach Brown Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 96bf033..8b7659e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -908,6 +908,11 @@ void phy_start(struct phy_device *phydev) } EXPORT_SYMBOL(phy_start); +static void phy_adjust_link(struct phy_device *phydev) +{ + phydev->adjust_link(phydev->attached_dev); +} + /** * phy_state_machine - Handle the state machine * @work: work_struct that describes the work to be done @@ -950,7 +955,7 @@ void phy_state_machine(struct work_struct *work) if (!phydev->link) { phydev->state = PHY_NOLINK; netif_carrier_off(phydev->attached_dev); - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); break; } @@ -963,7 +968,7 @@ void phy_state_machine(struct work_struct *work) if (err > 0) { phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); } else if (0 == phydev->link_timeout--) needs_aneg = true; @@ -990,7 +995,7 @@ void phy_state_machine(struct work_struct *work) } phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); } break; case PHY_FORCING: @@ -1006,7 +1011,7 @@ void phy_state_machine(struct work_struct *work) needs_aneg = true; } - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); break; case PHY_RUNNING: /* Only register a CHANGE if we are polling and link changed @@ -1035,7 +1040,7 @@ void phy_state_machine(struct work_struct *work) netif_carrier_off(phydev->attached_dev); } - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); if (phy_interrupt_is_valid(phydev)) err = phy_config_interrupt(phydev, @@ -1045,7 +1050,7 @@ void phy_state_machine(struct work_struct *work) if (phydev->link) { phydev->link = 0; netif_carrier_off(phydev->attached_dev); - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); do_suspend = true; } break; @@ -1069,7 +1074,7 @@ void phy_state_machine(struct work_struct *work) } else { phydev->state = PHY_NOLINK; } - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); } else { phydev->state = PHY_AN; phydev->link_timeout = PHY_AN_TIMEOUT; @@ -1085,7 +1090,7 @@ void phy_state_machine(struct work_struct *work) } else { phydev->state = PHY_NOLINK; } - phydev->adjust_link(phydev->attached_dev); + phy_adjust_link(phydev); } break; } -- cgit v1.1 From 1f9127caece42514a47011326b83ad93d95cd5d7 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 17 Oct 2016 10:49:54 -0500 Subject: net: phy: Create phy_supported_speeds function which lists speeds currently supported by a phydevice phy_supported_speeds provides a means to get a list of all the speeds a phy device currently supports. Signed-off-by: Zach Brown Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/phy.h | 15 +++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 8b7659e..ee3c793 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -261,6 +261,41 @@ static inline unsigned int phy_find_valid(unsigned int idx, u32 features) } /** + * phy_supported_speeds - return all speeds currently supported by a phy device + * @phy: The phy device to return supported speeds of. + * @speeds: buffer to store supported speeds in. + * @size: size of speeds buffer. + * + * Description: Returns the number of supported speeds, and fills the speeds + * buffer with the supported speeds. If speeds buffer is too small to contain + * all currently supported speeds, will return as many speeds as can fit. + */ +unsigned int phy_supported_speeds(struct phy_device *phy, + unsigned int *speeds, + unsigned int size) +{ + unsigned int count = 0; + unsigned int idx = 0; + + while (idx < MAX_NUM_SETTINGS && count < size) { + idx = phy_find_valid(idx, phy->supported); + + if (!(settings[idx].setting & phy->supported)) + break; + + /* Assumes settings are grouped by speed */ + if ((count == 0) || + (speeds[count - 1] != settings[idx].speed)) { + speeds[count] = settings[idx].speed; + count++; + } + idx++; + } + + return count; +} + +/** * phy_check_valid - check if there is a valid PHY setting which matches * speed, duplex, and feature mask * @speed: speed to match diff --git a/include/linux/phy.h b/include/linux/phy.h index c47378c..4b6c246 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -85,6 +85,21 @@ typedef enum { } phy_interface_t; /** + * phy_supported_speeds - return all speeds currently supported by a phy device + * @phy: The phy device to return supported speeds of. + * @speeds: buffer to store supported speeds in. + * @size: size of speeds buffer. + * + * Description: Returns the number of supported speeds, and + * fills the speeds * buffer with the supported speeds. If speeds buffer is + * too small to contain * all currently supported speeds, will return as + * many speeds as can fit. + */ +unsigned int phy_supported_speeds(struct phy_device *phy, + unsigned int *speeds, + unsigned int size); + +/** * It maps 'enum phy_interface_t' found in include/linux/phy.h * into the device tree binding of 'phy-mode', so that Ethernet * device driver can get phy interface from device tree. -- cgit v1.1 From 2e0bc452f4721520502575362a9cd3c1248d2337 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 17 Oct 2016 10:49:55 -0500 Subject: net: phy: leds: add support for led triggers on phy link state change Create an option CONFIG_LED_TRIGGER_PHY (default n), which will create a set of led triggers for each instantiated PHY device. There is one LED trigger per link-speed, per-phy. The triggers are registered during phy_attach and unregistered during phy_detach. This allows for a user to configure their system to allow a set of LEDs not controlled by the phy to represent link state changes on the phy. LEDS controlled by the phy are unaffected. For example, we have a board where some of the leds in the RJ45 socket are controlled by the phy, but others are not. Using the triggers provided by this patch the leds not controlled by the phy can be configured to show the current speed of the ethernet connection. The leds controlled by the phy are unaffected. Signed-off-by: Josh Cartwright Signed-off-by: Nathan Sullivan Signed-off-by: Zach Brown Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 13 ++++ drivers/net/phy/Makefile | 1 + drivers/net/phy/phy.c | 1 + drivers/net/phy/phy_device.c | 5 ++ drivers/net/phy/phy_led_triggers.c | 136 +++++++++++++++++++++++++++++++++++++ include/linux/phy.h | 7 ++ include/linux/phy_led_triggers.h | 51 ++++++++++++++ 7 files changed, 214 insertions(+) create mode 100644 drivers/net/phy/phy_led_triggers.c create mode 100644 include/linux/phy_led_triggers.h diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 2651c8d..45f68ea 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -15,6 +15,19 @@ if PHYLIB config SWPHY bool +config LED_TRIGGER_PHY + bool "Support LED triggers for tracking link state" + depends on LEDS_TRIGGERS + ---help--- + Adds support for a set of LED trigger events per-PHY. Link + state change will trigger the events, for consumption by an + LED class driver. There are triggers for each link speed currently + supported by the phy, and are of the form: + :: + + Where speed is in the form: + Mbps or Gbps + comment "MDIO bus device drivers" config MDIO_BCM_IPROC diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index e58667d..86d12cd 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -2,6 +2,7 @@ libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o libphy-$(CONFIG_SWPHY) += swphy.o +libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o obj-$(CONFIG_PHYLIB) += libphy.o diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ee3c793..2f94c60 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -946,6 +946,7 @@ EXPORT_SYMBOL(phy_start); static void phy_adjust_link(struct phy_device *phydev) { phydev->adjust_link(phydev->attached_dev); + phy_led_trigger_change_speed(phydev); } /** diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index ac440a8..49a1c98 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -916,6 +917,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, else phy_resume(phydev); + phy_led_triggers_register(phydev); + return err; error: @@ -989,6 +992,8 @@ void phy_detach(struct phy_device *phydev) } } + phy_led_triggers_unregister(phydev); + /* * The phydev might go away on the put_device() below, so avoid * a use-after-free bug by reading the underlying bus first. diff --git a/drivers/net/phy/phy_led_triggers.c b/drivers/net/phy/phy_led_triggers.c new file mode 100644 index 0000000..cda600a --- /dev/null +++ b/drivers/net/phy/phy_led_triggers.c @@ -0,0 +1,136 @@ +/* Copyright (C) 2016 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include + +static struct phy_led_trigger *phy_speed_to_led_trigger(struct phy_device *phy, + unsigned int speed) +{ + unsigned int i; + + for (i = 0; i < phy->phy_num_led_triggers; i++) { + if (phy->phy_led_triggers[i].speed == speed) + return &phy->phy_led_triggers[i]; + } + return NULL; +} + +void phy_led_trigger_change_speed(struct phy_device *phy) +{ + struct phy_led_trigger *plt; + + if (!phy->link) + goto out_change_speed; + + if (phy->speed == 0) + return; + + plt = phy_speed_to_led_trigger(phy, phy->speed); + if (!plt) { + netdev_alert(phy->attached_dev, + "No phy led trigger registered for speed(%d)\n", + phy->speed); + goto out_change_speed; + } + + if (plt != phy->last_triggered) { + led_trigger_event(&phy->last_triggered->trigger, LED_OFF); + led_trigger_event(&plt->trigger, LED_FULL); + phy->last_triggered = plt; + } + return; + +out_change_speed: + if (phy->last_triggered) { + led_trigger_event(&phy->last_triggered->trigger, + LED_OFF); + phy->last_triggered = NULL; + } +} +EXPORT_SYMBOL_GPL(phy_led_trigger_change_speed); + +static int phy_led_trigger_register(struct phy_device *phy, + struct phy_led_trigger *plt, + unsigned int speed) +{ + char name_suffix[PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE]; + + plt->speed = speed; + + if (speed < SPEED_1000) + snprintf(name_suffix, sizeof(name_suffix), "%dMbps", speed); + else if (speed == SPEED_2500) + snprintf(name_suffix, sizeof(name_suffix), "2.5Gbps"); + else + snprintf(name_suffix, sizeof(name_suffix), "%dGbps", + DIV_ROUND_CLOSEST(speed, 1000)); + + snprintf(plt->name, sizeof(plt->name), PHY_ID_FMT ":%s", + phy->mdio.bus->id, phy->mdio.addr, name_suffix); + plt->trigger.name = plt->name; + + return led_trigger_register(&plt->trigger); +} + +static void phy_led_trigger_unregister(struct phy_led_trigger *plt) +{ + led_trigger_unregister(&plt->trigger); +} + +int phy_led_triggers_register(struct phy_device *phy) +{ + int i, err; + unsigned int speeds[50]; + + phy->phy_num_led_triggers = phy_supported_speeds(phy, speeds, + ARRAY_SIZE(speeds)); + if (!phy->phy_num_led_triggers) + return 0; + + phy->phy_led_triggers = devm_kzalloc(&phy->mdio.dev, + sizeof(struct phy_led_trigger) * + phy->phy_num_led_triggers, + GFP_KERNEL); + if (!phy->phy_led_triggers) + return -ENOMEM; + + for (i = 0; i < phy->phy_num_led_triggers; i++) { + err = phy_led_trigger_register(phy, &phy->phy_led_triggers[i], + speeds[i]); + if (err) + goto out_unreg; + } + + phy->last_triggered = NULL; + phy_led_trigger_change_speed(phy); + + return 0; +out_unreg: + while (i--) + phy_led_trigger_unregister(&phy->phy_led_triggers[i]); + devm_kfree(&phy->mdio.dev, phy->phy_led_triggers); + return err; +} +EXPORT_SYMBOL_GPL(phy_led_triggers_register); + +void phy_led_triggers_unregister(struct phy_device *phy) +{ + int i; + + for (i = 0; i < phy->phy_num_led_triggers; i++) + phy_led_trigger_unregister(&phy->phy_led_triggers[i]); + + devm_kfree(&phy->mdio.dev, phy->phy_led_triggers); +} +EXPORT_SYMBOL_GPL(phy_led_triggers_unregister); diff --git a/include/linux/phy.h b/include/linux/phy.h index 4b6c246..e7e1fd3 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -420,6 +421,12 @@ struct phy_device { int link_timeout; +#ifdef CONFIG_LED_TRIGGER_PHY + struct phy_led_trigger *phy_led_triggers; + unsigned int phy_num_led_triggers; + struct phy_led_trigger *last_triggered; +#endif + /* * Interrupt number for this PHY * -1 means no interrupt diff --git a/include/linux/phy_led_triggers.h b/include/linux/phy_led_triggers.h new file mode 100644 index 0000000..a2daea0 --- /dev/null +++ b/include/linux/phy_led_triggers.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2016 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __PHY_LED_TRIGGERS +#define __PHY_LED_TRIGGERS + +struct phy_device; + +#ifdef CONFIG_LED_TRIGGER_PHY + +#include + +#define PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE 10 +#define PHY_MII_BUS_ID_SIZE (20 - 3) + +#define PHY_LINK_LED_TRIGGER_NAME_SIZE (PHY_MII_BUS_ID_SIZE + \ + FIELD_SIZEOF(struct mdio_device, addr)+\ + PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE) + +struct phy_led_trigger { + struct led_trigger trigger; + char name[PHY_LINK_LED_TRIGGER_NAME_SIZE]; + unsigned int speed; +}; + + +extern int phy_led_triggers_register(struct phy_device *phy); +extern void phy_led_triggers_unregister(struct phy_device *phy); +extern void phy_led_trigger_change_speed(struct phy_device *phy); + +#else + +static inline int phy_led_triggers_register(struct phy_device *phy) +{ + return 0; +} +static inline void phy_led_triggers_unregister(struct phy_device *phy) { } +static inline void phy_led_trigger_change_speed(struct phy_device *phy) { } + +#endif + +#endif -- cgit v1.1 From cd94e519dc5d82e2b8a0c93ebf09152727af5150 Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Tue, 18 Oct 2016 17:47:45 +0100 Subject: ethernet/sfc: use core min/max MTU checking Fixes: 61e84623ace3 ("net: centralize net_device min/max MTU checking") Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 3cf3557..b626da6 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -2263,18 +2263,6 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu) rc = efx_check_disabled(efx); if (rc) return rc; - if (new_mtu > EFX_MAX_MTU) { - netif_err(efx, drv, efx->net_dev, - "Requested MTU of %d too big (max: %d)\n", - new_mtu, EFX_MAX_MTU); - return -EINVAL; - } - if (new_mtu < EFX_MIN_MTU) { - netif_err(efx, drv, efx->net_dev, - "Requested MTU of %d too small (min: %d)\n", - new_mtu, EFX_MIN_MTU); - return -EINVAL; - } netif_dbg(efx, drv, efx->net_dev, "changing MTU to %d\n", new_mtu); @@ -2478,6 +2466,8 @@ static int efx_register_netdev(struct efx_nic *efx) net_dev->priv_flags |= IFF_UNICAST_FLT; net_dev->ethtool_ops = &efx_ethtool_ops; net_dev->gso_max_segs = EFX_TSO_MAX_SEGS; + net_dev->min_mtu = EFX_MIN_MTU; + net_dev->max_mtu = EFX_MAX_MTU; rtnl_lock(); -- cgit v1.1 From 9a97434215819872b054c3d0c067e5e4fa768b0e Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 17 Oct 2016 21:45:29 +0200 Subject: ARM: pxa: enhance smc91x platform data Instead of having the smc91x driver relying on machine_is_*() calls, provide this data through platform data, ie. idp, mainstone and stargate. This way, the driver doesn't need anymore machine_is_*() calls, which wouldn't work anymore with a device-tree build. Signed-off-by: Robert Jarzmik Signed-off-by: David S. Miller --- arch/arm/mach-pxa/idp.c | 1 + arch/arm/mach-pxa/mainstone.c | 1 + arch/arm/mach-pxa/stargate2.c | 1 + include/linux/smc91x.h | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c index 66070ac..d1db32b 100644 --- a/arch/arm/mach-pxa/idp.c +++ b/arch/arm/mach-pxa/idp.c @@ -85,6 +85,7 @@ static struct resource smc91x_resources[] = { static struct smc91x_platdata smc91x_platdata = { .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | SMC91X_USE_DMA | SMC91X_NOWAIT, + .pxa_u16_align4 = true, }; static struct platform_device smc91x_device = { diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c index 4096406..a2d851a 100644 --- a/arch/arm/mach-pxa/mainstone.c +++ b/arch/arm/mach-pxa/mainstone.c @@ -140,6 +140,7 @@ static struct resource smc91x_resources[] = { static struct smc91x_platdata mainstone_smc91x_info = { .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | SMC91X_NOWAIT | SMC91X_USE_DMA, + .pxa_u16_align4 = true, }; static struct platform_device smc91x_device = { diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c index 702f4f1..7b6610e 100644 --- a/arch/arm/mach-pxa/stargate2.c +++ b/arch/arm/mach-pxa/stargate2.c @@ -673,6 +673,7 @@ static struct resource smc91x_resources[] = { static struct smc91x_platdata stargate2_smc91x_info = { .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | SMC91X_NOWAIT | SMC91X_USE_DMA, + .pxa_u16_align4 = true, }; static struct platform_device smc91x_device = { diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index e302c44..129bc67 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -39,6 +39,7 @@ struct smc91x_platdata { unsigned long flags; unsigned char leda; unsigned char ledb; + bool pxa_u16_align4; /* PXA buggy u16 writes on 4*n+2 addresses */ }; #endif /* __SMC91X_H__ */ -- cgit v1.1 From d09d747ae4c25a89aa04c0881ea52e90a09bedfd Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 17 Oct 2016 21:45:30 +0200 Subject: net: smc91x: isolate u16 writes alignment workaround Writes to u16 has a special handling on 3 PXA platforms, where the hardware wiring forces these writes to be u32 aligned. This patch isolates this handling for PXA platforms as before, but enables this "workaround" to be set up dynamically, which will be the case in device-tree build types. This patch was tested on 2 PXA platforms : mainstone, which relies on the workaround, and lubbock, which doesn't. Signed-off-by: Robert Jarzmik Signed-off-by: David S. Miller --- arch/mn10300/unit-asb2303/include/unit/smc91111.h | 2 +- drivers/net/ethernet/smsc/smc91x.c | 3 +- drivers/net/ethernet/smsc/smc91x.h | 82 ++++++++++++----------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/arch/mn10300/unit-asb2303/include/unit/smc91111.h b/arch/mn10300/unit-asb2303/include/unit/smc91111.h index dd456e9..dd4e294 100644 --- a/arch/mn10300/unit-asb2303/include/unit/smc91111.h +++ b/arch/mn10300/unit-asb2303/include/unit/smc91111.h @@ -30,7 +30,7 @@ #if SMC_CAN_USE_16BIT #define SMC_inw(a, r) inw((unsigned long) ((a) + (r))) -#define SMC_outw(v, a, r) outw(v, (unsigned long) ((a) + (r))) +#define SMC_outw(lp, v, a, r) outw(v, (unsigned long) ((a) + (r))) #define SMC_insw(a, r, p, l) insw((unsigned long) ((a) + (r)), (p), (l)) #define SMC_outsw(a, r, p, l) outsw((unsigned long) ((a) + (r)), (p), (l)) #endif diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 9b4780f..705d99b 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -602,7 +602,8 @@ static void smc_hardware_send_pkt(unsigned long data) SMC_PUSH_DATA(lp, buf, len & ~1); /* Send final ctl word with the last byte if there is one */ - SMC_outw(((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG(lp)); + SMC_outw(lp, ((len & 1) ? (0x2000 | buf[len - 1]) : 0), ioaddr, + DATA_REG(lp)); /* * If THROTTLE_TX_PKTS is set, we stop the queue here. This will diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h index ea84654..45e6b81 100644 --- a/drivers/net/ethernet/smsc/smc91x.h +++ b/drivers/net/ethernet/smsc/smc91x.h @@ -63,8 +63,6 @@ #if defined(CONFIG_ARM) -#include - /* Now the bus width is specified in the platform data * pretend here to support all I/O access types */ @@ -86,11 +84,11 @@ #define SMC_inl(a, r) readl((a) + (r)) #define SMC_outb(v, a, r) writeb(v, (a) + (r)) -#define SMC_outw(v, a, r) \ +#define SMC_outw(lp, v, a, r) \ do { \ unsigned int __v = v, __smc_r = r; \ if (SMC_16BIT(lp)) \ - __SMC_outw(__v, a, __smc_r); \ + __SMC_outw(lp, __v, a, __smc_r); \ else if (SMC_8BIT(lp)) \ SMC_outw_b(__v, a, __smc_r); \ else \ @@ -107,10 +105,10 @@ #define SMC_IRQ_FLAGS (-1) /* from resource */ /* We actually can't write halfwords properly if not word aligned */ -static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg) +static inline void _SMC_outw_align4(u16 val, void __iomem *ioaddr, int reg, + bool use_align4_workaround) { - if ((machine_is_mainstone() || machine_is_stargate2() || - machine_is_pxa_idp()) && reg & 2) { + if (use_align4_workaround) { unsigned int v = val << 16; v |= readl(ioaddr + (reg & ~2)) & 0xffff; writel(v, ioaddr + (reg & ~2)); @@ -119,6 +117,12 @@ static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg) } } +#define __SMC_outw(lp, v, a, r) \ + _SMC_outw_align4((v), (a), (r), \ + IS_BUILTIN(CONFIG_ARCH_PXA) && ((r) & 2) && \ + (lp)->cfg.pxa_u16_align4) + + #elif defined(CONFIG_SH_SH4202_MICRODEV) #define SMC_CAN_USE_8BIT 0 @@ -129,7 +133,7 @@ static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_inw(a, r) inw((a) + (r) - 0xa0000000) #define SMC_inl(a, r) inl((a) + (r) - 0xa0000000) #define SMC_outb(v, a, r) outb(v, (a) + (r) - 0xa0000000) -#define SMC_outw(v, a, r) outw(v, (a) + (r) - 0xa0000000) +#define SMC_outw(lp, v, a, r) outw(v, (a) + (r) - 0xa0000000) #define SMC_outl(v, a, r) outl(v, (a) + (r) - 0xa0000000) #define SMC_insl(a, r, p, l) insl((a) + (r) - 0xa0000000, p, l) #define SMC_outsl(a, r, p, l) outsl((a) + (r) - 0xa0000000, p, l) @@ -147,7 +151,7 @@ static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_inb(a, r) inb(((u32)a) + (r)) #define SMC_inw(a, r) inw(((u32)a) + (r)) #define SMC_outb(v, a, r) outb(v, ((u32)a) + (r)) -#define SMC_outw(v, a, r) outw(v, ((u32)a) + (r)) +#define SMC_outw(lp, v, a, r) outw(v, ((u32)a) + (r)) #define SMC_insw(a, r, p, l) insw(((u32)a) + (r), p, l) #define SMC_outsw(a, r, p, l) outsw(((u32)a) + (r), p, l) @@ -175,7 +179,7 @@ static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_inw(a, r) readw((a) + (r)) #define SMC_inl(a, r) readl((a) + (r)) #define SMC_outb(v, a, r) writeb(v, (a) + (r)) -#define SMC_outw(v, a, r) writew(v, (a) + (r)) +#define SMC_outw(lp, v, a, r) writew(v, (a) + (r)) #define SMC_outl(v, a, r) writel(v, (a) + (r)) #define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) #define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) @@ -207,7 +211,7 @@ static inline void mcf_outsw(void *a, unsigned char *p, int l) } #define SMC_inw(a, r) _swapw(readw((a) + (r))) -#define SMC_outw(v, a, r) writew(_swapw(v), (a) + (r)) +#define SMC_outw(lp, v, a, r) writew(_swapw(v), (a) + (r)) #define SMC_insw(a, r, p, l) mcf_insw(a + r, p, l) #define SMC_outsw(a, r, p, l) mcf_outsw(a + r, p, l) @@ -241,7 +245,7 @@ static inline void mcf_outsw(void *a, unsigned char *p, int l) #define SMC_inw(a, r) ioread16((a) + (r)) #define SMC_inl(a, r) ioread32((a) + (r)) #define SMC_outb(v, a, r) iowrite8(v, (a) + (r)) -#define SMC_outw(v, a, r) iowrite16(v, (a) + (r)) +#define SMC_outw(lp, v, a, r) iowrite16(v, (a) + (r)) #define SMC_outl(v, a, r) iowrite32(v, (a) + (r)) #define SMC_insw(a, r, p, l) ioread16_rep((a) + (r), p, l) #define SMC_outsw(a, r, p, l) iowrite16_rep((a) + (r), p, l) @@ -303,6 +307,8 @@ struct smc_local { /* the low address lines on some platforms aren't connected... */ int io_shift; + /* on some platforms a u16 write must be 4-bytes aligned */ + bool half_word_align4; struct smc91x_platdata cfg; }; @@ -457,7 +463,7 @@ smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma, #if ! SMC_CAN_USE_16BIT -#define SMC_outw(x, ioaddr, reg) SMC_outw_b(x, ioaddr, reg) +#define SMC_outw(lp, x, ioaddr, reg) SMC_outw_b(x, ioaddr, reg) #define SMC_inw(ioaddr, reg) SMC_inw_b(ioaddr, reg) #define SMC_insw(a, r, p, l) BUG() #define SMC_outsw(a, r, p, l) BUG() @@ -909,7 +915,7 @@ static const char * chip_ids[ 16 ] = { else if (SMC_8BIT(lp)) \ SMC_outb(x, ioaddr, PN_REG(lp)); \ else \ - SMC_outw(x, ioaddr, PN_REG(lp)); \ + SMC_outw(lp, x, ioaddr, PN_REG(lp)); \ } while (0) #define SMC_GET_AR(lp) \ @@ -937,7 +943,7 @@ static const char * chip_ids[ 16 ] = { int __mask; \ local_irq_save(__flags); \ __mask = SMC_inw(ioaddr, INT_REG(lp)) & ~0xff; \ - SMC_outw(__mask | (x), ioaddr, INT_REG(lp)); \ + SMC_outw(lp, __mask | (x), ioaddr, INT_REG(lp)); \ local_irq_restore(__flags); \ } \ } while (0) @@ -951,7 +957,7 @@ static const char * chip_ids[ 16 ] = { if (SMC_8BIT(lp)) \ SMC_outb(x, ioaddr, IM_REG(lp)); \ else \ - SMC_outw((x) << 8, ioaddr, INT_REG(lp)); \ + SMC_outw(lp, (x) << 8, ioaddr, INT_REG(lp)); \ } while (0) #define SMC_CURRENT_BANK(lp) SMC_inw(ioaddr, BANK_SELECT) @@ -961,22 +967,22 @@ static const char * chip_ids[ 16 ] = { if (SMC_MUST_ALIGN_WRITE(lp)) \ SMC_outl((x)<<16, ioaddr, 12< Date: Mon, 17 Oct 2016 21:45:31 +0200 Subject: net: smc91x: take into account half-word workaround For device-tree builds, platforms such as mainstone, idp and stargate2 must have their u16 writes all aligned on 32 bit boundaries. This is already enabled in platform data builds, and this patch adds it to device-tree builds. Signed-off-by: Robert Jarzmik Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smc91x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 705d99b..65077c7 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2326,6 +2326,8 @@ static int smc_drv_probe(struct platform_device *pdev) if (!device_property_read_u32(&pdev->dev, "reg-shift", &val)) lp->io_shift = val; + lp->cfg.pxa_u16_align4 = + device_property_read_bool(&pdev->dev, "pxa-u16-align4"); } #endif -- cgit v1.1 From c4055a8cf7ba0042947309d1eada4d842bd3d8b7 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 17 Oct 2016 21:45:32 +0200 Subject: net: smsc91x: add u16 workaround for pxa platforms Add a workaround for mainstone, idp and stargate2 boards, for u16 writes which must be aligned on 32 bits addresses. Signed-off-by: Robert Jarzmik Cc: Jeremy Linton Acked-by: Rob Herring Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/smsc-lan91c111.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/net/smsc-lan91c111.txt b/Documentation/devicetree/bindings/net/smsc-lan91c111.txt index e77e167..309e37e 100644 --- a/Documentation/devicetree/bindings/net/smsc-lan91c111.txt +++ b/Documentation/devicetree/bindings/net/smsc-lan91c111.txt @@ -13,3 +13,5 @@ Optional properties: 16-bit access only. - power-gpios: GPIO to control the PWRDWN pin - reset-gpios: GPIO to control the RESET pin +- pxa-u16-align4 : Boolean, put in place the workaround the force all + u16 writes to be 32 bits aligned -- cgit v1.1 From 9c403b6beeeb6cccebb745804820fdccf38125a7 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Tue, 18 Oct 2016 08:57:50 +0800 Subject: net: vlan: Use sizeof instead of literal number Use sizeof variable instead of literal number to enhance the readability. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- net/8021q/vlan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index f8903c1..9accde3 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -515,8 +515,8 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) return -EFAULT; /* Null terminate this sucker, just in case. */ - args.device1[23] = 0; - args.u.device2[23] = 0; + args.device1[sizeof(args.device1) - 1] = 0; + args.u.device2[sizeof(args.u.device2) - 1] = 0; rtnl_lock(); -- cgit v1.1 From d248cafc892e5d5a44a84553dada18fcc470bf27 Mon Sep 17 00:00:00 2001 From: hayeswang Date: Tue, 18 Oct 2016 11:41:48 +0800 Subject: r8152: add new products of Lenovo Add the following four products of Lenovo and sort the order of the list. VID PID 0x17ef 0x3062 0x17ef 0x3069 0x17ef 0x720c 0x17ef 0x7214 Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ether.c | 28 ++++++++++++++++++++++++++++ drivers/net/usb/r8152.c | 6 +++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index c47ec0a..45e5e43 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -687,6 +687,20 @@ static const struct usb_device_id products[] = { .driver_info = 0, }, +/* ThinkPad USB-C Dock (based on Realtek RTL8153) */ +{ + USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3062, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + +/* ThinkPad Thunderbolt 3 Dock (based on Realtek RTL8153) */ +{ + USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3069, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */ { USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM, @@ -694,6 +708,20 @@ static const struct usb_device_id products[] = { .driver_info = 0, }, +/* Lenovo USB C to Ethernet Adapter (based on Realtek RTL8153) */ +{ + USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x720c, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + +/* Lenovo USB-C Travel Hub (based on Realtek RTL8153) */ +{ + USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7214, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* NVIDIA Tegra USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */ { USB_DEVICE_AND_INTERFACE_INFO(NVIDIA_VENDOR_ID, 0x09ff, USB_CLASS_COMM, diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2886946..8d6e13c 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -4411,8 +4411,12 @@ static struct usb_device_id rtl8152_table[] = { {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, {REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)}, - {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)}, {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x3062)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x3069)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x720c)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7214)}, {REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA, 0x09ff)}, {} }; -- cgit v1.1 From aa0938c6d77d84d9ab72c5674c9624dddc345095 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 18 Oct 2016 15:53:37 +0000 Subject: net: dsa: mv88e6xxx: fix non static symbol warning Fixes the following sparse warning: drivers/net/dsa/mv88e6xxx/chip.c:2866:5: warning: symbol 'mv88e6xxx_g1_set_switch_mac' was not declared. Should it be static? Signed-off-by: Wei Yongjun Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ac03297..157360f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2863,7 +2863,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000); } -int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) +static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) { int err; -- cgit v1.1 From 4251e7475d4b2ac0d9ad039439ad0650921b1d27 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 18 Oct 2016 15:54:50 +0000 Subject: qed: Remove useless set memory to zero use memset() The memory return by kzalloc() has already be set to zero, so remove useless memset(0). Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_roce.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 76831a3..b11beb5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -2747,7 +2747,6 @@ static int qed_roce_ll2_start(struct qed_dev *cdev, DP_ERR(cdev, "qed roce ll2 start: failed memory allocation\n"); return -ENOMEM; } - memset(roce_ll2, 0, sizeof(*roce_ll2)); roce_ll2->handle = QED_LL2_UNUSED_HANDLE; roce_ll2->cbs = params->cbs; roce_ll2->cb_cookie = params->cb_cookie; -- cgit v1.1 From 422d2f77803d85477df72f3153e85fd2b8c6b9e9 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Mon, 25 Jul 2016 00:42:44 +0200 Subject: batman-adv: Remove needless init of variables on stack Some variables are overwritten immediatelly in a functions. These don't have to be initialized to a specific value on the stack because the value will be overwritten before they will be used anywhere. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_v.c | 2 +- net/batman-adv/bat_v_ogm.c | 4 ++-- net/batman-adv/fragmentation.c | 4 ++-- net/batman-adv/gateway_client.c | 2 +- net/batman-adv/originator.c | 8 ++++---- net/batman-adv/send.c | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index e79f6f0..57c795c 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -787,7 +787,7 @@ static bool batadv_v_gw_is_eligible(struct batadv_priv *bat_priv, struct batadv_orig_node *curr_gw_orig, struct batadv_orig_node *orig_node) { - struct batadv_gw_node *curr_gw = NULL, *orig_gw = NULL; + struct batadv_gw_node *curr_gw, *orig_gw = NULL; u32 gw_throughput, orig_throughput, threshold; bool ret = false; diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 1aeeadca..61ff5f8 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -401,7 +401,7 @@ static int batadv_v_ogm_metric_update(struct batadv_priv *bat_priv, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing) { - struct batadv_orig_ifinfo *orig_ifinfo = NULL; + struct batadv_orig_ifinfo *orig_ifinfo; struct batadv_neigh_ifinfo *neigh_ifinfo = NULL; bool protection_started = false; int ret = -EINVAL; @@ -486,7 +486,7 @@ static bool batadv_v_ogm_route_update(struct batadv_priv *bat_priv, struct batadv_hard_iface *if_outgoing) { struct batadv_neigh_node *router = NULL; - struct batadv_orig_node *orig_neigh_node = NULL; + struct batadv_orig_node *orig_neigh_node; struct batadv_neigh_node *orig_neigh_router = NULL; struct batadv_neigh_ifinfo *router_ifinfo = NULL, *neigh_ifinfo = NULL; u32 router_throughput, neigh_throughput; diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 0934730..1b2f5b9 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -252,7 +252,7 @@ batadv_frag_merge_packets(struct hlist_head *chain) { struct batadv_frag_packet *packet; struct batadv_frag_list_entry *entry; - struct sk_buff *skb_out = NULL; + struct sk_buff *skb_out; int size, hdr_size = sizeof(struct batadv_frag_packet); /* Remove first entry, as this is the destination for the rest of the @@ -352,7 +352,7 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_orig_node *orig_node_src) { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct batadv_orig_node *orig_node_dst = NULL; + struct batadv_orig_node *orig_node_dst; struct batadv_neigh_node *neigh_node = NULL; struct batadv_frag_packet *packet; u16 total_size; diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index de055d6..69bfe98 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -704,7 +704,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, { struct batadv_neigh_node *neigh_curr = NULL; struct batadv_neigh_node *neigh_old = NULL; - struct batadv_orig_node *orig_dst_node = NULL; + struct batadv_orig_node *orig_dst_node; struct batadv_gw_node *gw_node = NULL; struct batadv_gw_node *curr_gw = NULL; struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 5f3bfc4..6af87c9 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -364,7 +364,7 @@ struct batadv_orig_ifinfo * batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node, struct batadv_hard_iface *if_outgoing) { - struct batadv_orig_ifinfo *orig_ifinfo = NULL; + struct batadv_orig_ifinfo *orig_ifinfo; unsigned long reset_time; spin_lock_bh(&orig_node->neigh_list_lock); @@ -520,7 +520,7 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, const u8 *neigh_addr) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); - struct batadv_hardif_neigh_node *hardif_neigh = NULL; + struct batadv_hardif_neigh_node *hardif_neigh; spin_lock_bh(&hard_iface->neigh_list_lock); @@ -563,7 +563,7 @@ static struct batadv_hardif_neigh_node * batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface, const u8 *neigh_addr) { - struct batadv_hardif_neigh_node *hardif_neigh = NULL; + struct batadv_hardif_neigh_node *hardif_neigh; /* first check without locking to avoid the overhead */ hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr); @@ -683,7 +683,7 @@ batadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node, struct batadv_hard_iface *hard_iface, const u8 *neigh_addr) { - struct batadv_neigh_node *neigh_node = NULL; + struct batadv_neigh_node *neigh_node; /* first check without locking to avoid the overhead */ neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr); diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 8d4e1f5..e1e9136 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -562,7 +562,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, unsigned long delay) { - struct batadv_hard_iface *primary_if = NULL; + struct batadv_hard_iface *primary_if; struct batadv_forw_packet *forw_packet; struct batadv_bcast_packet *bcast_packet; struct sk_buff *newskb; -- cgit v1.1 From 176e5b772b113f9ab013d574e13d8a433d47bbcb Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 27 Jul 2016 12:31:07 +0200 Subject: batman-adv: Use proper name for fragments list head The batman-adv codebase is using "list" for the list node (prev/next) and +"_list" for the head of a list. Not using this naming scheme can up in confusions because list_head is used for both the head of the list and the list node (prev/next) in each item of the list. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/fragmentation.c | 14 +++++++------- net/batman-adv/fragmentation.h | 2 +- net/batman-adv/originator.c | 2 +- net/batman-adv/types.h | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 1b2f5b9..2b967a3 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -73,7 +73,7 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig_node, spin_lock_bh(&chain->lock); if (!check_cb || check_cb(chain)) { - batadv_frag_clear_chain(&chain->head); + batadv_frag_clear_chain(&chain->fragment_list); chain->size = 0; } @@ -117,8 +117,8 @@ static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain, if (chain->seqno == seqno) return false; - if (!hlist_empty(&chain->head)) - batadv_frag_clear_chain(&chain->head); + if (!hlist_empty(&chain->fragment_list)) + batadv_frag_clear_chain(&chain->fragment_list); chain->size = 0; chain->seqno = seqno; @@ -176,7 +176,7 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, chain = &orig_node->fragments[bucket]; spin_lock_bh(&chain->lock); if (batadv_frag_init_chain(chain, seqno)) { - hlist_add_head(&frag_entry_new->list, &chain->head); + hlist_add_head(&frag_entry_new->list, &chain->fragment_list); chain->size = skb->len - hdr_size; chain->timestamp = jiffies; chain->total_size = ntohs(frag_packet->total_size); @@ -185,7 +185,7 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, } /* Find the position for the new fragment. */ - hlist_for_each_entry(frag_entry_curr, &chain->head, list) { + hlist_for_each_entry(frag_entry_curr, &chain->fragment_list, list) { /* Drop packet if fragment already exists. */ if (frag_entry_curr->no == frag_entry_new->no) goto err_unlock; @@ -220,11 +220,11 @@ out: * exceeds the maximum size of one merged packet. Don't allow * packets to have different total_size. */ - batadv_frag_clear_chain(&chain->head); + batadv_frag_clear_chain(&chain->fragment_list); chain->size = 0; } else if (ntohs(frag_packet->total_size) == chain->size) { /* All fragments received. Hand over chain to caller. */ - hlist_move_list(&chain->head, chain_out); + hlist_move_list(&chain->fragment_list, chain_out); chain->size = 0; } diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h index 3202fe3..b95f619 100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@ -47,7 +47,7 @@ int batadv_frag_send_packet(struct sk_buff *skb, static inline bool batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry) { - if (!hlist_empty(&frags_entry->head) && + if (!hlist_empty(&frags_entry->fragment_list) && batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT)) return true; return false; diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 6af87c9..518b1ed 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -1021,7 +1021,7 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, batadv_orig_node_vlan_put(vlan); for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { - INIT_HLIST_HEAD(&orig_node->fragments[i].head); + INIT_HLIST_HEAD(&orig_node->fragments[i].fragment_list); spin_lock_init(&orig_node->fragments[i].lock); orig_node->fragments[i].size = 0; } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 97336ec..d31e7ec 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -184,7 +184,7 @@ struct batadv_orig_ifinfo { /** * struct batadv_frag_table_entry - head in the fragment buffer table - * @head: head of list with fragments + * @fragment_list: head of list with fragments * @lock: lock to protect the list of fragments * @timestamp: time (jiffie) of last received fragment * @seqno: sequence number of the fragments in the list @@ -192,8 +192,8 @@ struct batadv_orig_ifinfo { * @total_size: expected size of the assembled packet */ struct batadv_frag_table_entry { - struct hlist_head head; - spinlock_t lock; /* protects head */ + struct hlist_head fragment_list; + spinlock_t lock; /* protects fragment_list */ unsigned long timestamp; u16 seqno; u16 size; -- cgit v1.1 From 70ea5cee95479695a5a84c0ef665604fdf1f8362 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 27 Jul 2016 12:31:08 +0200 Subject: batman-adv: Use proper name for gateway list head The batman-adv codebase is using "list" for the list node (prev/next) and +"_list" for the head of a list. Not using this naming scheme can up in confusions when reading the code. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 6 +++--- net/batman-adv/bat_v.c | 6 +++--- net/batman-adv/gateway_client.c | 7 ++++--- net/batman-adv/main.c | 2 +- net/batman-adv/types.h | 8 ++++---- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index e2d18d0..0b9be62 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -2486,7 +2486,7 @@ batadv_iv_gw_get_best_gw_node(struct batadv_priv *bat_priv) struct batadv_orig_node *orig_node; rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { + hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) { orig_node = gw_node->orig_node; router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT); if (!router) @@ -2674,7 +2674,7 @@ static void batadv_iv_gw_print(struct batadv_priv *bat_priv, " Gateway (#/255) Nexthop [outgoingIF]: advertised uplink bandwidth\n"); rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { + hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) { /* fails if orig_node has no router */ if (batadv_iv_gw_write_buffer_text(bat_priv, seq, gw_node) < 0) continue; @@ -2774,7 +2774,7 @@ static void batadv_iv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb, int idx = 0; rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { + hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) { if (idx++ < idx_skip) continue; diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index 57c795c..2ac612d7 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -750,7 +750,7 @@ batadv_v_gw_get_best_gw_node(struct batadv_priv *bat_priv) u32 max_bw = 0, bw; rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { + hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) { if (!kref_get_unless_zero(&gw_node->refcount)) continue; @@ -889,7 +889,7 @@ static void batadv_v_gw_print(struct batadv_priv *bat_priv, " Gateway ( throughput) Nexthop [outgoingIF]: advertised uplink bandwidth\n"); rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { + hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) { /* fails if orig_node has no router */ if (batadv_v_gw_write_buffer_text(bat_priv, seq, gw_node) < 0) continue; @@ -1009,7 +1009,7 @@ static void batadv_v_gw_dump(struct sk_buff *msg, struct netlink_callback *cb, int idx = 0; rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { + hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) { if (idx++ < idx_skip) continue; diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 69bfe98..52b8bd6 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -348,7 +348,7 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv, spin_lock_bh(&bat_priv->gw.list_lock); kref_get(&gw_node->refcount); - hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list); + hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.gateway_list); spin_unlock_bh(&bat_priv->gw.list_lock); batadv_dbg(BATADV_DBG_BATMAN, bat_priv, @@ -376,7 +376,8 @@ struct batadv_gw_node *batadv_gw_node_get(struct batadv_priv *bat_priv, struct batadv_gw_node *gw_node_tmp, *gw_node = NULL; rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) { + hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.gateway_list, + list) { if (gw_node_tmp->orig_node != orig_node) continue; @@ -475,7 +476,7 @@ void batadv_gw_node_free(struct batadv_priv *bat_priv) spin_lock_bh(&bat_priv->gw.list_lock); hlist_for_each_entry_safe(gw_node, node_tmp, - &bat_priv->gw.list, list) { + &bat_priv->gw.gateway_list, list) { hlist_del_init_rcu(&gw_node->list); batadv_gw_node_put(gw_node); } diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 2c017ab..5e4e818 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -160,7 +160,7 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); - INIT_HLIST_HEAD(&bat_priv->gw.list); + INIT_HLIST_HEAD(&bat_priv->gw.gateway_list); #ifdef CONFIG_BATMAN_ADV_MCAST INIT_HLIST_HEAD(&bat_priv->mcast.want_all_unsnoopables_list); INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv4_list); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index d31e7ec..673a22e 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -706,8 +706,8 @@ struct batadv_priv_debug_log { /** * struct batadv_priv_gw - per mesh interface gateway data - * @list: list of available gateway nodes - * @list_lock: lock protecting gw_list & curr_gw + * @gateway_list: list of available gateway nodes + * @list_lock: lock protecting gateway_list & curr_gw * @curr_gw: pointer to currently selected gateway node * @mode: gateway operation: off, client or server (see batadv_gw_modes) * @sel_class: gateway selection class (applies if gw_mode client) @@ -716,8 +716,8 @@ struct batadv_priv_debug_log { * @reselect: bool indicating a gateway re-selection is in progress */ struct batadv_priv_gw { - struct hlist_head list; - spinlock_t list_lock; /* protects gw_list & curr_gw */ + struct hlist_head gateway_list; + spinlock_t list_lock; /* protects gateway_list & curr_gw */ struct batadv_gw_node __rcu *curr_gw; /* rcu protected pointer */ atomic_t mode; atomic_t sel_class; -- cgit v1.1 From 507b37cf71c86b7ceaebf333b8ae488a600f5afd Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 1 Sep 2016 10:25:12 +0200 Subject: batman-adv: Use octal permissions instead of macros Linus prefers to have octal permission numbers instead of combinations of macro names ("random line noise"). Also old existing "bad symbolic permission bit macro use" should be converted to octal numbers. (http://lkml.kernel.org/r/CA+55aFw5v23T-zvDZp-MmD_EYxF8WbafwwB59934FV7g21uMGQ@mail.gmail.com) Also remove the S_IFREG bit from the octal representation because it is filtered out by debugfs_create. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/debugfs.c | 26 ++++++++++---------- net/batman-adv/icmp_socket.c | 5 ++-- net/batman-adv/log.c | 4 +--- net/batman-adv/network-coding.c | 8 +++---- net/batman-adv/sysfs.c | 53 ++++++++++++++++++----------------------- 5 files changed, 41 insertions(+), 55 deletions(-) diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index b4ffba7..7792550 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -186,7 +186,7 @@ struct batadv_debuginfo batadv_debuginfo_##_name = { \ /* the following attributes are general and therefore they will be directly * placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs */ -static BATADV_DEBUGINFO(routing_algos, S_IRUGO, batadv_algorithms_open); +static BATADV_DEBUGINFO(routing_algos, 0444, batadv_algorithms_open); static struct batadv_debuginfo *batadv_general_debuginfos[] = { &batadv_debuginfo_routing_algos, @@ -194,26 +194,24 @@ static struct batadv_debuginfo *batadv_general_debuginfos[] = { }; /* The following attributes are per soft interface */ -static BATADV_DEBUGINFO(neighbors, S_IRUGO, neighbors_open); -static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open); -static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open); -static BATADV_DEBUGINFO(transtable_global, S_IRUGO, - batadv_transtable_global_open); +static BATADV_DEBUGINFO(neighbors, 0444, neighbors_open); +static BATADV_DEBUGINFO(originators, 0444, batadv_originators_open); +static BATADV_DEBUGINFO(gateways, 0444, batadv_gateways_open); +static BATADV_DEBUGINFO(transtable_global, 0444, batadv_transtable_global_open); #ifdef CONFIG_BATMAN_ADV_BLA -static BATADV_DEBUGINFO(bla_claim_table, S_IRUGO, batadv_bla_claim_table_open); -static BATADV_DEBUGINFO(bla_backbone_table, S_IRUGO, +static BATADV_DEBUGINFO(bla_claim_table, 0444, batadv_bla_claim_table_open); +static BATADV_DEBUGINFO(bla_backbone_table, 0444, batadv_bla_backbone_table_open); #endif #ifdef CONFIG_BATMAN_ADV_DAT -static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open); +static BATADV_DEBUGINFO(dat_cache, 0444, batadv_dat_cache_open); #endif -static BATADV_DEBUGINFO(transtable_local, S_IRUGO, - batadv_transtable_local_open); +static BATADV_DEBUGINFO(transtable_local, 0444, batadv_transtable_local_open); #ifdef CONFIG_BATMAN_ADV_NC -static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open); +static BATADV_DEBUGINFO(nc_nodes, 0444, batadv_nc_nodes_open); #endif #ifdef CONFIG_BATMAN_ADV_MCAST -static BATADV_DEBUGINFO(mcast_flags, S_IRUGO, batadv_mcast_flags_open); +static BATADV_DEBUGINFO(mcast_flags, 0444, batadv_mcast_flags_open); #endif static struct batadv_debuginfo *batadv_mesh_debuginfos[] = { @@ -253,7 +251,7 @@ struct batadv_debuginfo batadv_hardif_debuginfo_##_name = { \ }, \ } -static BATADV_HARDIF_DEBUGINFO(originators, S_IRUGO, +static BATADV_HARDIF_DEBUGINFO(originators, 0444, batadv_originators_hardif_open); static struct batadv_debuginfo *batadv_hardif_debuginfos[] = { diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 378cc11..b310f381a 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -322,8 +321,8 @@ int batadv_socket_setup(struct batadv_priv *bat_priv) if (!bat_priv->debug_dir) goto err; - d = debugfs_create_file(BATADV_ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR, - bat_priv->debug_dir, bat_priv, &batadv_fops); + d = debugfs_create_file(BATADV_ICMP_SOCKET, 0600, bat_priv->debug_dir, + bat_priv, &batadv_fops); if (!d) goto err; diff --git a/net/batman-adv/log.c b/net/batman-adv/log.c index 56dc532..c73c317 100644 --- a/net/batman-adv/log.c +++ b/net/batman-adv/log.c @@ -31,7 +31,6 @@ #include /* for linux/wait.h */ #include #include -#include #include #include #include @@ -212,8 +211,7 @@ int batadv_debug_log_setup(struct batadv_priv *bat_priv) spin_lock_init(&bat_priv->debug_log->lock); init_waitqueue_head(&bat_priv->debug_log->queue_wait); - d = debugfs_create_file("log", S_IFREG | S_IRUSR, - bat_priv->debug_dir, bat_priv, + d = debugfs_create_file("log", 0400, bat_priv->debug_dir, bat_priv, &batadv_log_fops); if (!d) goto err; diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index e3baf69..c213dde 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -1961,17 +1960,16 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv) if (!nc_dir) goto out; - file = debugfs_create_u8("min_tq", S_IRUGO | S_IWUSR, nc_dir, - &bat_priv->nc.min_tq); + file = debugfs_create_u8("min_tq", 0644, nc_dir, &bat_priv->nc.min_tq); if (!file) goto out; - file = debugfs_create_u32("max_fwd_delay", S_IRUGO | S_IWUSR, nc_dir, + file = debugfs_create_u32("max_fwd_delay", 0644, nc_dir, &bat_priv->nc.max_fwd_delay); if (!file) goto out; - file = debugfs_create_u32("max_buffer_time", S_IRUGO | S_IWUSR, nc_dir, + file = debugfs_create_u32("max_buffer_time", 0644, nc_dir, &bat_priv->nc.max_buffer_time); if (!file) goto out; diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 02d96f2..17c8441 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -666,41 +665,36 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj, return count; } -BATADV_ATTR_SIF_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL); -BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); +BATADV_ATTR_SIF_BOOL(aggregated_ogms, 0644, NULL); +BATADV_ATTR_SIF_BOOL(bonding, 0644, NULL); #ifdef CONFIG_BATMAN_ADV_BLA -BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, - batadv_bla_status_update); +BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, 0644, batadv_bla_status_update); #endif #ifdef CONFIG_BATMAN_ADV_DAT -BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, - batadv_dat_status_update); +BATADV_ATTR_SIF_BOOL(distributed_arp_table, 0644, batadv_dat_status_update); #endif -BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu); -static BATADV_ATTR(routing_algo, S_IRUGO, batadv_show_bat_algo, NULL); -static BATADV_ATTR(gw_mode, S_IRUGO | S_IWUSR, batadv_show_gw_mode, - batadv_store_gw_mode); -BATADV_ATTR_SIF_UINT(orig_interval, orig_interval, S_IRUGO | S_IWUSR, - 2 * BATADV_JITTER, INT_MAX, NULL); -BATADV_ATTR_SIF_UINT(hop_penalty, hop_penalty, S_IRUGO | S_IWUSR, 0, - BATADV_TQ_MAX_VALUE, NULL); -static BATADV_ATTR(gw_sel_class, S_IRUGO | S_IWUSR, batadv_show_gw_sel_class, +BATADV_ATTR_SIF_BOOL(fragmentation, 0644, batadv_update_min_mtu); +static BATADV_ATTR(routing_algo, 0444, batadv_show_bat_algo, NULL); +static BATADV_ATTR(gw_mode, 0644, batadv_show_gw_mode, batadv_store_gw_mode); +BATADV_ATTR_SIF_UINT(orig_interval, orig_interval, 0644, 2 * BATADV_JITTER, + INT_MAX, NULL); +BATADV_ATTR_SIF_UINT(hop_penalty, hop_penalty, 0644, 0, BATADV_TQ_MAX_VALUE, + NULL); +static BATADV_ATTR(gw_sel_class, 0644, batadv_show_gw_sel_class, batadv_store_gw_sel_class); -static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth, +static BATADV_ATTR(gw_bandwidth, 0644, batadv_show_gw_bwidth, batadv_store_gw_bwidth); #ifdef CONFIG_BATMAN_ADV_MCAST -BATADV_ATTR_SIF_BOOL(multicast_mode, S_IRUGO | S_IWUSR, NULL); +BATADV_ATTR_SIF_BOOL(multicast_mode, 0644, NULL); #endif #ifdef CONFIG_BATMAN_ADV_DEBUG -BATADV_ATTR_SIF_UINT(log_level, log_level, S_IRUGO | S_IWUSR, 0, - BATADV_DBG_ALL, NULL); +BATADV_ATTR_SIF_UINT(log_level, log_level, 0644, 0, BATADV_DBG_ALL, NULL); #endif #ifdef CONFIG_BATMAN_ADV_NC -BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, - batadv_nc_status_update); +BATADV_ATTR_SIF_BOOL(network_coding, 0644, batadv_nc_status_update); #endif -static BATADV_ATTR(isolation_mark, S_IRUGO | S_IWUSR, - batadv_show_isolation_mark, batadv_store_isolation_mark); +static BATADV_ATTR(isolation_mark, 0644, batadv_show_isolation_mark, + batadv_store_isolation_mark); static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_aggregated_ogms, @@ -731,7 +725,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { NULL, }; -BATADV_ATTR_VLAN_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL); +BATADV_ATTR_VLAN_BOOL(ap_isolation, 0644, NULL); /* array of vlan specific sysfs attributes */ static struct batadv_attribute *batadv_vlan_attrs[] = { @@ -1116,14 +1110,13 @@ static ssize_t batadv_show_throughput_override(struct kobject *kobj, #endif -static BATADV_ATTR(mesh_iface, S_IRUGO | S_IWUSR, batadv_show_mesh_iface, +static BATADV_ATTR(mesh_iface, 0644, batadv_show_mesh_iface, batadv_store_mesh_iface); -static BATADV_ATTR(iface_status, S_IRUGO, batadv_show_iface_status, NULL); +static BATADV_ATTR(iface_status, 0444, batadv_show_iface_status, NULL); #ifdef CONFIG_BATMAN_ADV_BATMAN_V -BATADV_ATTR_HIF_UINT(elp_interval, bat_v.elp_interval, S_IRUGO | S_IWUSR, +BATADV_ATTR_HIF_UINT(elp_interval, bat_v.elp_interval, 0644, 2 * BATADV_JITTER, INT_MAX, NULL); -static BATADV_ATTR(throughput_override, S_IRUGO | S_IWUSR, - batadv_show_throughput_override, +static BATADV_ATTR(throughput_override, 0644, batadv_show_throughput_override, batadv_store_throughput_override); #endif -- cgit v1.1 From 4c7da0f6dbcde2431d773ce03cde5e7abede54e0 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 21 Sep 2016 09:23:50 +0200 Subject: batman-adv: Avoid precedence issues in macros It must be avoided that arguments to a macro are evaluated ungrouped (which enforces normal operator precendence). Otherwise the result of the macro is not well defined. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/log.h | 12 ++++++------ net/batman-adv/main.h | 4 ++-- net/batman-adv/packet.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/net/batman-adv/log.h b/net/batman-adv/log.h index e0e1a88..ab47acf 100644 --- a/net/batman-adv/log.h +++ b/net/batman-adv/log.h @@ -71,12 +71,12 @@ int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...) __printf(2, 3); /* possibly ratelimited debug output */ -#define _batadv_dbg(type, bat_priv, ratelimited, fmt, arg...) \ - do { \ - if (atomic_read(&bat_priv->log_level) & type && \ - (!ratelimited || net_ratelimit())) \ - batadv_debug_log(bat_priv, fmt, ## arg);\ - } \ +#define _batadv_dbg(type, bat_priv, ratelimited, fmt, arg...) \ + do { \ + if (atomic_read(&(bat_priv)->log_level) & (type) && \ + (!(ratelimited) || net_ratelimit())) \ + batadv_debug_log(bat_priv, fmt, ## arg); \ + } \ while (0) #else /* !CONFIG_BATMAN_ADV_DEBUG */ __printf(4, 5) diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 6a2328d..daddca9 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -199,8 +199,8 @@ struct packet_type; struct seq_file; struct sk_buff; -#define BATADV_PRINT_VID(vid) ((vid & BATADV_VLAN_HAS_TAG) ? \ - (int)(vid & VLAN_VID_MASK) : -1) +#define BATADV_PRINT_VID(vid) (((vid) & BATADV_VLAN_HAS_TAG) ? \ + (int)((vid) & VLAN_VID_MASK) : -1) extern struct list_head batadv_hardif_list; diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index d2e9bbd..7a36bcf 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -21,7 +21,7 @@ #include #include -#define batadv_tp_is_error(n) ((u8)n > 127 ? 1 : 0) +#define batadv_tp_is_error(n) ((u8)(n) > 127 ? 1 : 0) /** * enum batadv_packettype - types for batman-adv encapsulated packets -- cgit v1.1 From 341ca402e42b3c4a8b34341a35f82ff9f7bf8664 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 18 Sep 2016 14:51:59 +0300 Subject: iwlwifi: mvm: update txq metadata to current owner When a TXQ's owner is changed, the FW is indeed notified, but the driver doesn't update the current metadata to reflect the owner change. Fix that. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index fc77188..a65030f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -875,12 +875,17 @@ static void iwl_mvm_change_queue_owner(struct iwl_mvm *mvm, int queue) cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd); - if (ret) + if (ret) { IWL_ERR(mvm, "Failed to update owner of TXQ %d (ret=%d)\n", queue, ret); - else - IWL_DEBUG_TX_QUEUES(mvm, "Changed TXQ %d ownership to tid %d\n", - queue, tid); + return; + } + + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[queue].txq_tid = tid; + spin_unlock_bh(&mvm->queue_info_lock); + IWL_DEBUG_TX_QUEUES(mvm, "Changed TXQ %d ownership to tid %d\n", + queue, tid); } static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue) -- cgit v1.1 From 56214749e0b46eeac6795551f187c0f9443918f2 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Thu, 22 Sep 2016 15:14:08 +0300 Subject: iwlwifi: mvm: fix reserved txq freeing If a TXQ's marking as a reserved queue is removed, when removing the STA the driver might try to access out of bounds memory. Make sure the reserved queue is freed only if it is still reserved. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index a65030f..c9dcb70 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1494,12 +1494,15 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); /* If DQA is supported - the queues can be disabled now */ - if (iwl_mvm_is_dqa_supported(mvm)) { + if (iwl_mvm_is_dqa_supported(mvm)) + iwl_mvm_disable_sta_queues(mvm, vif, mvm_sta); + + /* If there is a TXQ still marked as reserved - free it */ + if (iwl_mvm_is_dqa_supported(mvm) && + mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { u8 reserved_txq = mvm_sta->reserved_queue; enum iwl_mvm_queue_status *status; - iwl_mvm_disable_sta_queues(mvm, vif, mvm_sta); - /* * If no traffic has gone through the reserved TXQ - it * is still marked as IWL_MVM_QUEUE_RESERVED, and -- cgit v1.1 From 737847aa2c22ea41ce38950bff800d2f7dd05a03 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 27 Sep 2016 15:17:29 +0300 Subject: iwlwifi: mvm: support MONITOR vif in DQA mode In DQA mode the TXQs are allocated on demand, so make sure the sniffer STA tfd_queue_msk isn't set. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 6b962d6..9a91203 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -899,9 +899,11 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - for (i = 0; i < IEEE80211_NUM_ACS; i++) - if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) - tfd_queue_msk |= BIT(vif->hw_queue[i]); + if (!iwl_mvm_is_dqa_supported(mvm)) { + for (i = 0; i < IEEE80211_NUM_ACS; i++) + if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) + tfd_queue_msk |= BIT(vif->hw_queue[i]); + } cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | MAC_FILTER_IN_CONTROL_AND_MGMT | -- cgit v1.1 From ad5de73734031c07eb8c7cdb7c7d63d3630bb202 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 27 Sep 2016 16:01:10 +0300 Subject: iwlwifi: mvm: fix dqa deferred frames marking When a STA has deferred traffic to TX, an appropriate bit is turned on in %deferred_tx_frames to indicate deferred traffic. This marking is never turned off, resulting in iterating over TIDs with no deferred traffic. Although this didn't cause any failures/errors/bugs, there is still no point of iterating over these TIDs when not needed. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index c9dcb70..5ec1b96 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1015,6 +1015,7 @@ static void iwl_mvm_tx_deferred_stream(struct iwl_mvm *mvm, local_bh_disable(); spin_lock(&mvmsta->lock); skb_queue_splice_init(&tid_data->deferred_tx_frames, &deferred_tx); + mvmsta->deferred_traffic_tid_map &= ~BIT(tid); spin_unlock(&mvmsta->lock); while ((skb = __skb_dequeue(&deferred_tx))) -- cgit v1.1 From 65e254821cee1a63f3f6d23270e90b33432b2bef Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Apr 2016 14:24:22 +0200 Subject: iwlwifi: mvm: use firmware station PM notification for AP_LINK_PS When using RSS on 9000 series devices, we can't rely on processing the received frames for station powersave handling, since they could be processed on different CPUs and out of order. In order to still manage the powersave of stations, the firmware sends a notification on sleep->wake, wake->sleep and - for U-APSD - frames received with PM while already sleeping (with the TID.) With this, the driver can set AP_LINK_PS, which is required for real parallel RX. In addition, this requires checking for PS-Poll frames and calling ieee80211_sta_pspoll() appropriately. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 2 + drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 26 ++++++++ .../net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 9 ++- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 70 ++++++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 3 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 13 ++++ drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 1 + 9 files changed, 119 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index ceec5ca..1ad0ec1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -293,6 +293,7 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * is supported. * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC * @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan + * @IWL_UCODE_TLV_CAPA_STA_PM_NOTIF: firmware will send STA PM notification * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT @@ -342,6 +343,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = (__force iwl_ucode_tlv_capa_t)29, IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31, + IWL_UCODE_TLV_CAPA_STA_PM_NOTIF = (__force iwl_ucode_tlv_capa_t)38, IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index acc5cd5..b530fa4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -474,4 +474,30 @@ struct iwl_mvm_internal_rxq_notif { u8 data[]; } __packed; +/** + * enum iwl_mvm_pm_event - type of station PM event + * @IWL_MVM_PM_EVENT_AWAKE: station woke up + * @IWL_MVM_PM_EVENT_ASLEEP: station went to sleep + * @IWL_MVM_PM_EVENT_UAPSD: station sent uAPSD trigger + * @IWL_MVM_PM_EVENT_PS_POLL: station sent PS-Poll + */ +enum iwl_mvm_pm_event { + IWL_MVM_PM_EVENT_AWAKE, + IWL_MVM_PM_EVENT_ASLEEP, + IWL_MVM_PM_EVENT_UAPSD, + IWL_MVM_PM_EVENT_PS_POLL, +}; /* PEER_PM_NTFY_API_E_VER_1 */ + +/** + * struct iwl_mvm_pm_state_notification - station PM state notification + * @sta_id: station ID of the station changing state + * @type: the new powersave state, see IWL_MVM_PM_EVENT_ above + */ +struct iwl_mvm_pm_state_notification { + u8 sta_id; + u8 type; + /* private: */ + u16 reserved; +} __packed; /* PEER_PM_NTFY_API_S_VER_1 */ + #endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index 6c8e3ca..3b5150e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -179,7 +179,7 @@ enum iwl_sta_key_flag { * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed * @STA_MODIFY_QUEUE_REMOVAL: this command removes a queue * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx - * @STA_MODIFY_TX_RATE: unused + * @STA_MODIFY_UAPSD_ACS: this command modifies %uapsd_trigger_acs * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count @@ -189,7 +189,7 @@ enum iwl_sta_key_flag { enum iwl_sta_modify_flag { STA_MODIFY_QUEUE_REMOVAL = BIT(0), STA_MODIFY_TID_DISABLE_TX = BIT(1), - STA_MODIFY_TX_RATE = BIT(2), + STA_MODIFY_UAPSD_ACS = BIT(2), STA_MODIFY_ADD_BA_TID = BIT(3), STA_MODIFY_REMOVE_BA_TID = BIT(4), STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5), @@ -353,6 +353,8 @@ struct iwl_mvm_add_sta_cmd_v7 { * @beamform_flags: beam forming controls * @tfd_queue_msk: tfd queues used by this station * @rx_ba_window: aggregation window size + * @scd_queue_bank: queue bank in used. Each bank contains 32 queues. 0 means + * that the queues used by this station are in the first 32. * * The device contains an internal table of per-station information, with info * on security keys, aggregation parameters, and Tx rates for initial Tx @@ -382,7 +384,8 @@ struct iwl_mvm_add_sta_cmd { __le16 beamform_flags; __le32 tfd_queue_msk; __le16 rx_ba_window; - __le16 reserved; + u8 scd_queue_bank; + u8 uapsd_trigger_acs; } __packed; /* ADD_STA_CMD_API_S_VER_8 */ /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 9763369..ae12bad 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -332,6 +332,7 @@ enum iwl_data_path_subcmd_ids { DQA_ENABLE_CMD = 0x0, UPDATE_MU_GROUPS_CMD = 0x1, TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2, + STA_PM_NOTIF = 0xFD, MU_GROUP_MGMT_NOTIF = 0xFE, RX_QUEUES_NOTIFICATION = 0xFF, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 318efd8..9eeb2c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -445,6 +445,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); if (iwl_mvm_has_new_rx_api(mvm)) ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_STA_PM_NOTIF)) + ieee80211_hw_set(hw, AP_LINK_PS); if (mvm->trans->num_rx_queues > 1) ieee80211_hw_set(hw, USES_RSS); @@ -2318,10 +2320,9 @@ iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, tids, more_data, true); } -static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) +static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); @@ -2374,6 +2375,67 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, spin_unlock_bh(&mvmsta->lock); } +static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + __iwl_mvm_mac_sta_notify(hw, cmd, sta); +} + +void iwl_mvm_sta_pm_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm_pm_state_notification *notif = (void *)pkt->data; + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + bool sleeping = (notif->type != IWL_MVM_PM_EVENT_AWAKE); + + if (WARN_ON(notif->sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))) + return; + + rcu_read_lock(); + sta = mvm->fw_id_to_mac_id[notif->sta_id]; + if (WARN_ON(IS_ERR_OR_NULL(sta))) { + rcu_read_unlock(); + return; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + if (!mvmsta->vif || + mvmsta->vif->type != NL80211_IFTYPE_AP) { + rcu_read_unlock(); + return; + } + + if (mvmsta->sleeping != sleeping) { + mvmsta->sleeping = sleeping; + __iwl_mvm_mac_sta_notify(mvm->hw, + sleeping ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, + sta); + ieee80211_sta_ps_transition(sta, sleeping); + } + + if (sleeping) { + switch (notif->type) { + case IWL_MVM_PM_EVENT_AWAKE: + case IWL_MVM_PM_EVENT_ASLEEP: + break; + case IWL_MVM_PM_EVENT_UAPSD: + ieee80211_sta_uapsd_trigger(sta, IEEE80211_NUM_TIDS); + break; + case IWL_MVM_PM_EVENT_PS_POLL: + ieee80211_sta_pspoll(sta); + break; + default: + break; + } + } + + rcu_read_unlock(); +} + static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index d17cbf6..726ba48 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1418,6 +1418,7 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_mu_mimo_grp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_sta_pm_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_window_status_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 05fe6dd..e86986f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -306,6 +306,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { iwl_mvm_rx_stored_beacon_notif, RX_HANDLER_SYNC), RX_HANDLER_GRP(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF, iwl_mvm_mu_mimo_grp_notif, RX_HANDLER_SYNC), + RX_HANDLER_GRP(DATA_PATH_GROUP, STA_PM_NOTIF, + iwl_mvm_sta_pm_notif, RX_HANDLER_SYNC), }; #undef RX_HANDLER #undef RX_HANDLER_GRP @@ -452,6 +454,7 @@ static const struct iwl_hcmd_names iwl_mvm_phy_names[] = { static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { HCMD_NAME(UPDATE_MU_GROUPS_CMD), HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD), + HCMD_NAME(STA_PM_NOTIF), HCMD_NAME(MU_GROUP_MGMT_NOTIF), HCMD_NAME(RX_QUEUES_NOTIFICATION), }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 5ec1b96..82ee786 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -203,6 +203,19 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, add_sta_cmd.station_flags |= cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); + if (sta->wme) { + add_sta_cmd.modify_mask |= STA_MODIFY_UAPSD_ACS; + + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + add_sta_cmd.uapsd_trigger_acs |= BIT(AC_BK); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + add_sta_cmd.uapsd_trigger_acs |= BIT(AC_BE); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + add_sta_cmd.uapsd_trigger_acs |= BIT(AC_VI); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + add_sta_cmd.uapsd_trigger_acs |= BIT(AC_VO); + } + status = ADD_STA_SUCCESS; ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, iwl_mvm_add_sta_cmd_size(mvm), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index e068d53..b45c7b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -436,6 +436,7 @@ struct iwl_mvm_sta { bool disable_tx; bool tlc_amsdu; + bool sleeping; u8 agg_tids; u8 sleep_tx_count; u8 avg_energy; -- cgit v1.1 From 64fa3aff89785b5a924ce3934f6595c35b4dffee Mon Sep 17 00:00:00 2001 From: Sharon Dvir Date: Wed, 17 Aug 2016 15:35:09 +0300 Subject: iwlwifi: pcie: give a meaningful name to interrupt request Instead of passing DRV_NAME pass a string that represents the reason for the interrupt. Signed-off-by: Sharon Dvir Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 29 ++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index ae95533..b10e363 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1598,6 +1598,29 @@ static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans) } } +static const char *queue_name(struct device *dev, + struct iwl_trans_pcie *trans_p, int i) +{ + if (trans_p->shared_vec_mask) { + int vec = trans_p->shared_vec_mask & + IWL_SHARED_IRQ_FIRST_RSS ? 1 : 0; + + if (i == 0) + return DRV_NAME ": shared IRQ"; + + return devm_kasprintf(dev, GFP_KERNEL, + DRV_NAME ": queue %d", i + vec); + } + if (i == 0) + return DRV_NAME ": default queue"; + + if (i == trans_p->alloc_vecs - 1) + return DRV_NAME ": exception"; + + return devm_kasprintf(dev, GFP_KERNEL, + DRV_NAME ": queue %d", i); +} + static int iwl_pcie_init_msix_handler(struct pci_dev *pdev, struct iwl_trans_pcie *trans_pcie) { @@ -1606,6 +1629,10 @@ static int iwl_pcie_init_msix_handler(struct pci_dev *pdev, for (i = 0; i < trans_pcie->alloc_vecs; i++) { int ret; struct msix_entry *msix_entry; + const char *qname = queue_name(&pdev->dev, trans_pcie, i); + + if (!qname) + return -ENOMEM; msix_entry = &trans_pcie->msix_entries[i]; ret = devm_request_threaded_irq(&pdev->dev, @@ -1615,7 +1642,7 @@ static int iwl_pcie_init_msix_handler(struct pci_dev *pdev, iwl_pcie_irq_msix_handler : iwl_pcie_irq_rx_msix_handler, IRQF_SHARED, - DRV_NAME, + qname, msix_entry); if (ret) { IWL_ERR(trans_pcie->trans, -- cgit v1.1 From 19c52f460f6d84af8225d8a769d66871dc5ed65b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 9 Oct 2016 15:43:24 +0300 Subject: iwlwifi: mvm: tell the firmware about the AID of the peer When a peer associates, the firmware will soon want to know its AID to be able to compute the TIM IE by itself. In DQA, the firmware has all the information it needs to update the TIM internally. The only missing part is the AID. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 82ee786..636c8b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -202,6 +202,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT); add_sta_cmd.station_flags |= cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); + add_sta_cmd.assoc_id = cpu_to_le16(sta->aid); if (sta->wme) { add_sta_cmd.modify_mask |= STA_MODIFY_UAPSD_ACS; -- cgit v1.1 From 8236f7db272484093fb02ce72a40e7224a5c29d4 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 6 Oct 2016 16:42:32 +0300 Subject: iwlwifi: mvm: assign cab queue to the correct station Currently when configuring the cab queue the scheduler is configured without station id - which results in station id 0. In DQA mode this causes firmware to assert later on when the actual station 0 is added with an empty tfd_queue_mask. Fix that by configuring the queue to the broadcast station. This is a bit trickier since the queue should not be included in the tfd_queue_mask of the ADD_STA since it is a multicast queue, and the tfd_queue_mask is only unicast queue. As a result the queue should be enabled only after the broadcast station is added. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 16 +++++++--------- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 9a91203..4a0874e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -499,23 +499,21 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (ret) return ret; + /* If DQA is supported - queues will be enabled when needed */ + if (iwl_mvm_is_dqa_supported(mvm)) + return 0; + switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE: - if (!iwl_mvm_is_dqa_supported(mvm)) - iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_TX_FIFO_VO, 0, - wdg_timeout); + iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, + IWL_MVM_OFFCHANNEL_QUEUE, + IWL_MVM_TX_FIFO_VO, 0, wdg_timeout); break; case NL80211_IFTYPE_AP: iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue, IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); /* fall through */ default: - /* If DQA is supported - queues will be enabled when needed */ - if (iwl_mvm_is_dqa_supported(mvm)) - break; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], vif->hw_queue[ac], diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 9eeb2c3..4f8c134 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2099,6 +2099,22 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, if (ret) goto out_unbind; + /* enable the multicast queue, now that we have a station for it */ + if (iwl_mvm_is_dqa_supported(mvm)) { + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, vif, false, false); + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = IWL_MVM_TX_FIFO_MCAST, + .sta_id = mvmvif->bcast_sta.sta_id, + .tid = IWL_MAX_TID_COUNT, + .aggregate = false, + .frame_limit = IWL_FRAME_LIMIT, + }; + + iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, + &cfg, wdg_timeout); + } + /* must be set before quota calculations */ mvmvif->ap_ibss_active = true; -- cgit v1.1 From a1264c3d6c04f0e4e9d447caaa249d6288b01520 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Oct 2016 23:12:07 +0300 Subject: wireless: radiotap: fix timestamp sampling position values The values don't match the radiotap spec, corrected that. Reported-by: Oz Shalev Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index ba07b9d..d0e7e3f 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -333,9 +333,9 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_TIMESTAMP_UNIT_NS 0x0003 #define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_MASK 0x00F0 #define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU 0x0000 -#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU 0x0010 +#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ 0x0010 #define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_PPDU 0x0020 -#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ 0x0030 +#define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU 0x0030 #define IEEE80211_RADIOTAP_TIMESTAMP_SPOS_UNKNOWN 0x00F0 #define IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT 0x00 -- cgit v1.1 From 1c3d185a9a0b136a58e73b02912d593d0303d1da Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Oct 2016 23:12:08 +0300 Subject: mac80211: fix tid_agg_rx NULL dereference On drivers setting the SUPPORTS_REORDERING_BUFFER hardware flag, we crash when the peer sends an AddBA request while we already have a session open on the seame TID; this is because on those drivers, the tid_agg_rx is left NULL even though the session is valid, and the agg_session_valid bit is set. To fix this, store the dialog tokens outside the tid_agg_rx to be able to compare them to the received AddBA request. Fixes: f89e07d4cf26 ("mac80211: agg-rx: refuse ADDBA Request with timeout update") Reported-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 8 ++------ net/mac80211/debugfs_sta.c | 2 +- net/mac80211/sta_info.h | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index f6749dc..3b5fd41 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -315,11 +315,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, mutex_lock(&sta->ampdu_mlme.mtx); if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { - tid_agg_rx = rcu_dereference_protected( - sta->ampdu_mlme.tid_rx[tid], - lockdep_is_held(&sta->ampdu_mlme.mtx)); - - if (tid_agg_rx->dialog_token == dialog_token) { + if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) { ht_dbg_ratelimited(sta->sdata, "updated AddBA Req from %pM on tid %u\n", sta->sta.addr, tid); @@ -396,7 +392,6 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, } /* update data */ - tid_agg_rx->dialog_token = dialog_token; tid_agg_rx->ssn = start_seq_num; tid_agg_rx->head_seq_num = start_seq_num; tid_agg_rx->buf_size = buf_size; @@ -418,6 +413,7 @@ end: if (status == WLAN_STATUS_SUCCESS) { __set_bit(tid, sta->ampdu_mlme.agg_session_valid); __clear_bit(tid, sta->ampdu_mlme.unexpected_agg); + sta->ampdu_mlme.tid_rx_token[tid] = dialog_token; } mutex_unlock(&sta->ampdu_mlme.mtx); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index a2fcdb4..14ec63a 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -205,7 +205,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_rx); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", - tid_rx ? tid_rx->dialog_token : 0); + tid_rx ? sta->ampdu_mlme.tid_rx_token[i] : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", tid_rx ? tid_rx->ssn : 0); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index ed5fcb9..dd06ef0 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -184,7 +184,6 @@ struct tid_ampdu_tx { * @ssn: Starting Sequence Number expected to be aggregated. * @buf_size: buffer size for incoming A-MPDUs * @timeout: reset timer value (in TUs). - * @dialog_token: dialog token for aggregation session * @rcu_head: RCU head used for freeing this struct * @reorder_lock: serializes access to reorder buffer, see below. * @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and @@ -213,7 +212,6 @@ struct tid_ampdu_rx { u16 ssn; u16 buf_size; u16 timeout; - u8 dialog_token; bool auto_seq; bool removed; }; @@ -225,6 +223,7 @@ struct tid_ampdu_rx { * to tid_tx[idx], which are protected by the sta spinlock) * tid_start_tx is also protected by sta->lock. * @tid_rx: aggregation info for Rx per TID -- RCU protected + * @tid_rx_token: dialog tokens for valid aggregation sessions * @tid_rx_timer_expired: bitmap indicating on which TIDs the * RX timer expired until the work for it runs * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the @@ -243,6 +242,7 @@ struct sta_ampdu_mlme { struct mutex mtx; /* rx */ struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; + u8 tid_rx_token[IEEE80211_NUM_TIDS]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; -- cgit v1.1 From b473b8f12a6e187c1348a84556b8db1e2a82f981 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Oct 2016 23:12:09 +0300 Subject: mac80211: improve RX aggregation data in debugfs When the driver sets the SUPPORTS_REORDERING_BUFFER hardware flag, the debugfs data for RX aggregation sessions won't even indicate that a session is open. Since the previous fix to store the dialog token separately, we can indicate that it's open and add the token so that there's at least some data (ssn is not available.) Signed-off-by: Johannes Berg --- net/mac80211/debugfs_sta.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 14ec63a..f6003b8 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -199,13 +199,18 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, "TID\t\tRX\tDTKN\tSSN\t\tTX\tDTKN\tpending\n"); for (i = 0; i < IEEE80211_NUM_TIDS; i++) { + bool tid_rx_valid; + tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]); tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]); + tid_rx_valid = test_bit(i, sta->ampdu_mlme.agg_session_valid); p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); - p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_rx); + p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", + tid_rx_valid); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", - tid_rx ? sta->ampdu_mlme.tid_rx_token[i] : 0); + tid_rx_valid ? + sta->ampdu_mlme.tid_rx_token[i] : 0); p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", tid_rx ? tid_rx->ssn : 0); -- cgit v1.1 From 0aa419ec6e7b98d485f6c66a62a90965eda3c1bb Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 18 Oct 2016 23:12:10 +0300 Subject: mac80211: allow the driver not to pass the tid to ieee80211_sta_uapsd_trigger iwlwifi will check internally that the tid maps to an AC that is trigger enabled, but can't know what tid exactly. Allow the driver to pass a generic tid and make mac80211 assume that a trigger frame was received. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ net/mac80211/rx.c | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e50c9e0..f3dbada 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4087,6 +4087,10 @@ void ieee80211_sta_pspoll(struct ieee80211_sta *sta); * This must be used in conjunction with ieee80211_sta_ps_transition() * and possibly ieee80211_sta_pspoll(); calls to all three must be * serialized. + * %IEEE80211_NUM_TIDS can be passed as the tid if the tid is unknown. + * In this case, mac80211 will not check that this tid maps to an AC + * that is trigger enabled and assume that the caller did the proper + * checks. */ void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *sta, u8 tid); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fc08a50..837d562 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1394,13 +1394,14 @@ void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *pubsta, u8 tid) u8 ac = ieee802_1d_to_ac[tid & 7]; /* - * If this AC is not trigger-enabled do nothing. + * If this AC is not trigger-enabled do nothing unless the + * driver is calling us after it already checked. * * NB: This could/should check a separate bitmap of trigger- * enabled queues, but for now we only implement uAPSD w/o * TSPEC changes to the ACs, so they're always the same. */ - if (!(sta->sta.uapsd_queues & BIT(ac))) + if (!(sta->sta.uapsd_queues & BIT(ac)) && tid != IEEE80211_NUM_TIDS) return; /* if we are in a service period, do nothing */ -- cgit v1.1 From f3fe4e93dd6346c01fd4070ae02ec746fbae73bb Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 18 Oct 2016 23:12:11 +0300 Subject: mac80211: add a HW flag for supporting HW TX fragmentation Currently mac80211 determines whether HW does fragmentation by checking whether the set_frag_threshold callback is set or not. However, some drivers may want to set the HW fragmentation capability depending on HW generation. Allow this by checking a HW flag instead of checking the callback. Signed-off-by: Sara Sharon [added the flag to ath10k and wlcore] Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 1 + drivers/net/wireless/ti/wlcore/main.c | 1 + include/net/mac80211.h | 10 ++++++++-- net/mac80211/debugfs.c | 1 + net/mac80211/main.c | 4 ++++ net/mac80211/tx.c | 4 ++-- net/mac80211/wpa.c | 2 +- 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 76297d6..e322b6d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -7881,6 +7881,7 @@ int ath10k_mac_register(struct ath10k *ar) ieee80211_hw_set(ar->hw, WANT_MONITOR_VIF); ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA); ieee80211_hw_set(ar->hw, QUEUE_CONTROL); + ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG); if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 471521a..9f39c6c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -6086,6 +6086,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) ieee80211_hw_set(wl->hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(wl->hw, SIGNAL_DBM); ieee80211_hw_set(wl->hw, SUPPORTS_PS); + ieee80211_hw_set(wl->hw, SUPPORTS_TX_FRAG); wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f3dbada..a1a2702 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2025,6 +2025,10 @@ struct ieee80211_txq { * drivers, mac80211 packet loss mechanism will not be triggered and driver * is completely depending on firmware event for station kickout. * + * @IEEE80211_HW_SUPPORTS_TX_FRAG: Hardware does fragmentation by itself. + * The stack will not do fragmentation. + * The callback for @set_frag_threshold should be set as well. + * * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays */ enum ieee80211_hw_flags { @@ -2066,6 +2070,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_TX_AMSDU, IEEE80211_HW_TX_FRAG_LIST, IEEE80211_HW_REPORTS_LOW_ACK, + IEEE80211_HW_SUPPORTS_TX_FRAG, /* keep last, obviously */ NUM_IEEE80211_HW_FLAGS @@ -3093,8 +3098,9 @@ enum ieee80211_reconfig_type { * The callback must be atomic. * * @set_frag_threshold: Configuration of fragmentation threshold. Assign this - * if the device does fragmentation by itself; if this callback is - * implemented then the stack will not do fragmentation. + * if the device does fragmentation by itself. Note that to prevent the + * stack from doing fragmentation IEEE80211_HW_SUPPORTS_TX_FRAG + * should be set as well. * The callback can sleep. * * @set_rts_threshold: Configuration of RTS threshold (if device needs it) diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index f56e2f4..e02ba42 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -210,6 +210,7 @@ static const char *hw_flag_names[] = { FLAG(TX_AMSDU), FLAG(TX_FRAG_LIST), FLAG(REPORTS_LOW_ACK), + FLAG(SUPPORTS_TX_FRAG), #undef FLAG }; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 1075ac2..0d9163c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -821,6 +821,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) !local->ops->tdls_recv_channel_switch)) return -EOPNOTSUPP; + if (WARN_ON(ieee80211_hw_check(hw, SUPPORTS_TX_FRAG) && + !local->ops->set_frag_threshold)) + return -EINVAL; + if (WARN_ON(local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN) && (!local->ops->start_nan || !local->ops->stop_nan))) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 772e369..62ccaf6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -934,7 +934,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) if (info->flags & IEEE80211_TX_CTL_DONTFRAG) return TX_CONTINUE; - if (tx->local->ops->set_frag_threshold) + if (ieee80211_hw_check(&tx->local->hw, SUPPORTS_TX_FRAG)) return TX_CONTINUE; /* @@ -2800,7 +2800,7 @@ void ieee80211_check_fast_xmit(struct sta_info *sta) /* fast-xmit doesn't handle fragmentation at all */ if (local->hw.wiphy->frag_threshold != (u32)-1 && - !local->ops->set_frag_threshold) + !ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG)) goto out; rcu_read_lock(); diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index b48c1e1..c249345 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -57,7 +57,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) if (info->control.hw_key && (info->flags & IEEE80211_TX_CTL_DONTFRAG || - tx->local->ops->set_frag_threshold) && + ieee80211_hw_check(&tx->local->hw, SUPPORTS_TX_FRAG)) && !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { /* hwaccel - with no need for SW-generated MMIC */ return TX_CONTINUE; -- cgit v1.1 From f438ceb81d424cb90a5a1aad569056bd7c2ab4c5 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 18 Oct 2016 23:12:12 +0300 Subject: mac80211: uapsd_queues is in QoS IE order The uapsd_queue field is in QoS IE order and not in IEEE80211_AC_*'s order. This means that mac80211 would get confused between BK and BE which is certainly not such a big deal but needs to be fixed. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 ++- include/net/mac80211.h | 3 ++- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 2 +- net/mac80211/rx.c | 3 ++- net/mac80211/sta_info.c | 13 ++++++++----- net/mac80211/util.c | 7 +++++++ 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5000ec75..10a26f0 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4574,7 +4574,8 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr); * moves to cfg80211 in this call * @buf: authentication frame (header + body) * @len: length of the frame data - * @uapsd_queues: bitmap of ACs configured to uapsd. -1 if n/a. + * @uapsd_queues: bitmap of queues configured for uapsd. Same format + * as the AC bitmap in the QoS info field * * After being asked to associate via cfg80211_ops::assoc() the driver must * call either this function or cfg80211_auth_timeout(). diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a1a2702..b9b24ab 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1745,7 +1745,8 @@ struct ieee80211_sta_rates { * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *), size is determined in hw information. * @uapsd_queues: bitmap of queues configured for uapsd. Only valid - * if wme is supported. + * if wme is supported. The bits order is like in + * IEEE80211_WMM_IE_STA_QOSINFO_AC_*. * @max_sp: max Service Period. Only valid if wme is supported. * @bandwidth: current bandwidth the station can receive with * @rx_nss: in HT/VHT, the maximum number of spatial streams the diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 42d194a..b4e2b6c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -84,6 +84,8 @@ struct ieee80211_local; #define IEEE80211_DEFAULT_MAX_SP_LEN \ IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL +extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS]; + #define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */) #define IEEE80211_MAX_NAN_INSTANCE_ID 255 diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7486f2d..c8d3a9b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3193,7 +3193,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, uapsd_queues = 0; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) if (sdata->tx_conf[ac].uapsd) - uapsd_queues |= BIT(ac); + uapsd_queues |= ieee80211_ac_to_qos_mask[ac]; } cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 837d562..21a8947 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1401,7 +1401,8 @@ void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *pubsta, u8 tid) * enabled queues, but for now we only implement uAPSD w/o * TSPEC changes to the ACs, so they're always the same. */ - if (!(sta->sta.uapsd_queues & BIT(ac)) && tid != IEEE80211_NUM_TIDS) + if (!(sta->sta.uapsd_queues & ieee80211_ac_to_qos_mask[ac]) && + tid != IEEE80211_NUM_TIDS) return; /* if we are in a service period, do nothing */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ab91e1e..236d47e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -709,7 +709,7 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending) for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; - if (ignore_for_tim & BIT(ac)) + if (ignore_for_tim & ieee80211_ac_to_qos_mask[ac]) continue; indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) || @@ -1389,7 +1389,7 @@ ieee80211_sta_ps_more_data(struct sta_info *sta, u8 ignored_acs, return true; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - if (ignored_acs & BIT(ac)) + if (ignored_acs & ieee80211_ac_to_qos_mask[ac]) continue; if (!skb_queue_empty(&sta->tx_filtered[ac]) || @@ -1414,7 +1414,7 @@ ieee80211_sta_ps_get_frames(struct sta_info *sta, int n_frames, u8 ignored_acs, for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; - if (ignored_acs & BIT(ac)) + if (ignored_acs & ieee80211_ac_to_qos_mask[ac]) continue; tids = ieee80211_tids_for_ac(ac); @@ -1482,7 +1482,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, BIT(find_highest_prio_tid(driver_release_tids)); if (skb_queue_empty(&frames) && !driver_release_tids) { - int tid; + int tid, ac; /* * For PS-Poll, this can only happen due to a race condition @@ -1500,7 +1500,10 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, */ /* This will evaluate to 1, 3, 5 or 7. */ - tid = 7 - ((ffs(~ignored_acs) - 1) << 1); + for (ac = IEEE80211_AC_VO; ac < IEEE80211_NUM_ACS; ac++) + if (ignored_acs & BIT(ac)) + continue; + tid = 7 - 2 * ac; ieee80211_send_null_response(sta, tid, reason, true, false); } else if (!driver_release_tids) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 031273a..7f24bdc 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3452,3 +3452,10 @@ void ieee80211_txq_get_depth(struct ieee80211_txq *txq, *byte_cnt = txqi->tin.backlog_bytes + frag_bytes; } EXPORT_SYMBOL(ieee80211_txq_get_depth); + +const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = { + IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI, + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE, + IEEE80211_WMM_IE_STA_QOSINFO_AC_BK +}; -- cgit v1.1 From 0711d638786941ec02551dd9b4aa0d8341f7db5b Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 18 Oct 2016 23:12:13 +0300 Subject: cfg80211: allow aborting in-progress connection atttempts On a disconnect request from userspace, cfg80211 currently calls called rdev_disconnect() only in case that 'current_bss' was set, i.e. connection had been established. Change this to allow the userspace call to succeed and call the driver's disconnect() method also while the connection attempt is in progress, to be able to abort attempts. Signed-off-by: Ilan Peer Signed-off-by: Luca Coelho [change commit subject/message] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 ++++--- net/wireless/sme.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 10a26f0..2bbbcc3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2564,9 +2564,10 @@ struct cfg80211_nan_func { * cases, the result of roaming is indicated with a call to * cfg80211_roamed() or cfg80211_roamed_bss(). * (invoked with the wireless_dev mutex held) - * @disconnect: Disconnect from the BSS/ESS. Once done, call - * cfg80211_disconnected(). - * (invoked with the wireless_dev mutex held) + * @disconnect: Disconnect from the BSS/ESS or stop connection attempts if + * connection is in progress. Once done, call cfg80211_disconnected() in + * case connection was already established (invoked with the + * wireless_dev mutex held), otherwise call cfg80211_connect_timeout(). * * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call * cfg80211_ibss_joined(), also call that function when changing BSSID due diff --git a/net/wireless/sme.c b/net/wireless/sme.c index a77db33..2b5bb38 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -1088,7 +1088,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, err = cfg80211_sme_disconnect(wdev, reason); else if (!rdev->ops->disconnect) cfg80211_mlme_down(rdev, dev); - else if (wdev->current_bss) + else if (wdev->ssid_len) err = rdev_disconnect(rdev, dev, reason); return err; -- cgit v1.1 From 0ea2a2ee8de0501b132cc4454a31bbadbdb5dbbe Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Wed, 19 Oct 2016 07:47:47 +0300 Subject: cfg80211: allow vendor commands to be sent to nan interface Allow vendor commands that require WIPHY_VENDOR_CMD_NEED_RUNNING flag to be sent to NAN interface. Signed-off-by: Andrei Otcheretianski Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e48b9c3..fcf5b4f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -11258,7 +11258,8 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) if (wdev->netdev && !netif_running(wdev->netdev)) return -ENETDOWN; - if (!wdev->netdev && !wdev->p2p_started) + if (!wdev->netdev && !wdev->p2p_started && + !wdev->nan_started) return -ENETDOWN; } -- cgit v1.1 From 3134e9bdbe9c81a943b2c027a78433d4b0cf50dc Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 19 Oct 2016 11:24:57 +0200 Subject: net: fs_enet: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct fs_enet_private, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- .../net/ethernet/freescale/fs_enet/fs_enet-main.c | 43 +++++++++------------- drivers/net/ethernet/freescale/fs_enet/fs_enet.h | 1 - 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index 925d1bc..44f50e1 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -118,22 +118,22 @@ static int fs_enet_napi(struct napi_struct *napi, int budget) BD_ENET_TX_RL | BD_ENET_TX_UN | BD_ENET_TX_CSL)) { if (sc & BD_ENET_TX_HB) /* No heartbeat */ - fep->stats.tx_heartbeat_errors++; + dev->stats.tx_heartbeat_errors++; if (sc & BD_ENET_TX_LC) /* Late collision */ - fep->stats.tx_window_errors++; + dev->stats.tx_window_errors++; if (sc & BD_ENET_TX_RL) /* Retrans limit */ - fep->stats.tx_aborted_errors++; + dev->stats.tx_aborted_errors++; if (sc & BD_ENET_TX_UN) /* Underrun */ - fep->stats.tx_fifo_errors++; + dev->stats.tx_fifo_errors++; if (sc & BD_ENET_TX_CSL) /* Carrier lost */ - fep->stats.tx_carrier_errors++; + dev->stats.tx_carrier_errors++; if (sc & (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) { - fep->stats.tx_errors++; + dev->stats.tx_errors++; do_restart = 1; } } else - fep->stats.tx_packets++; + dev->stats.tx_packets++; if (sc & BD_ENET_TX_READY) { dev_warn(fep->dev, @@ -145,7 +145,7 @@ static int fs_enet_napi(struct napi_struct *napi, int budget) * but we eventually sent the packet OK. */ if (sc & BD_ENET_TX_DEF) - fep->stats.collisions++; + dev->stats.collisions++; /* unmap */ if (fep->mapped_as_page[dirtyidx]) @@ -212,19 +212,19 @@ static int fs_enet_napi(struct napi_struct *napi, int budget) */ if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_CL | BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) { - fep->stats.rx_errors++; + dev->stats.rx_errors++; /* Frame too long or too short. */ if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) - fep->stats.rx_length_errors++; + dev->stats.rx_length_errors++; /* Frame alignment */ if (sc & (BD_ENET_RX_NO | BD_ENET_RX_CL)) - fep->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; /* CRC Error */ if (sc & BD_ENET_RX_CR) - fep->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; /* FIFO overrun */ if (sc & BD_ENET_RX_OV) - fep->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; skbn = fep->rx_skbuff[curidx]; } else { @@ -233,9 +233,9 @@ static int fs_enet_napi(struct napi_struct *napi, int budget) /* * Process the incoming frame. */ - fep->stats.rx_packets++; + dev->stats.rx_packets++; pkt_len = CBDR_DATLEN(bdp) - 4; /* remove CRC */ - fep->stats.rx_bytes += pkt_len + 4; + dev->stats.rx_bytes += pkt_len + 4; if (pkt_len <= fpi->rx_copybreak) { /* +2 to make IP header L1 cache aligned */ @@ -277,7 +277,7 @@ static int fs_enet_napi(struct napi_struct *napi, int budget) received++; netif_receive_skb(skb); } else { - fep->stats.rx_dropped++; + dev->stats.rx_dropped++; skbn = skb; } } @@ -543,7 +543,7 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) curidx = bdp - fep->tx_bd_base; len = skb->len; - fep->stats.tx_bytes += len; + dev->stats.tx_bytes += len; if (nr_frags) len -= skb->data_len; fep->tx_free -= nr_frags + 1; @@ -619,7 +619,7 @@ static void fs_timeout(struct net_device *dev) unsigned long flags; int wake = 0; - fep->stats.tx_errors++; + dev->stats.tx_errors++; spin_lock_irqsave(&fep->lock, flags); @@ -774,12 +774,6 @@ static int fs_enet_close(struct net_device *dev) return 0; } -static struct net_device_stats *fs_enet_get_stats(struct net_device *dev) -{ - struct fs_enet_private *fep = netdev_priv(dev); - return &fep->stats; -} - /*************************************************************************/ static void fs_get_drvinfo(struct net_device *dev, @@ -905,7 +899,6 @@ extern void fs_mii_disconnect(struct net_device *dev); static const struct net_device_ops fs_enet_netdev_ops = { .ndo_open = fs_enet_open, .ndo_stop = fs_enet_close, - .ndo_get_stats = fs_enet_get_stats, .ndo_start_xmit = fs_enet_start_xmit, .ndo_tx_timeout = fs_timeout, .ndo_set_rx_mode = fs_set_multicast_list, diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h index fee24c8..5ce516c 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h @@ -137,7 +137,6 @@ struct fs_enet_private { cbd_t __iomem *cur_rx; cbd_t __iomem *cur_tx; int tx_free; - struct net_device_stats stats; struct timer_list phy_timer_list; const struct phy_info *phy; u32 msg_enable; -- cgit v1.1 From 57a09bf0a416700676e77102c28f9cfcb48267e0 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 18 Oct 2016 19:51:19 +0200 Subject: bpf: Detect identical PTR_TO_MAP_VALUE_OR_NULL registers A BPF program is required to check the return register of a map_elem_lookup() call before accessing memory. The verifier keeps track of this by converting the type of the result register from PTR_TO_MAP_VALUE_OR_NULL to PTR_TO_MAP_VALUE after a conditional jump ensures safety. This check is currently exclusively performed for the result register 0. In the event the compiler reorders instructions, BPF_MOV64_REG instructions may be moved before the conditional jump which causes them to keep their type PTR_TO_MAP_VALUE_OR_NULL to which the verifier objects when the register is accessed: 0: (b7) r1 = 10 1: (7b) *(u64 *)(r10 -8) = r1 2: (bf) r2 = r10 3: (07) r2 += -8 4: (18) r1 = 0x59c00000 6: (85) call 1 7: (bf) r4 = r0 8: (15) if r0 == 0x0 goto pc+1 R0=map_value(ks=8,vs=8) R4=map_value_or_null(ks=8,vs=8) R10=fp 9: (7a) *(u64 *)(r4 +0) = 0 R4 invalid mem access 'map_value_or_null' This commit extends the verifier to keep track of all identical PTR_TO_MAP_VALUE_OR_NULL registers after a map_elem_lookup() by assigning them an ID and then marking them all when the conditional jump is observed. Signed-off-by: Thomas Graf Reviewed-by: Josef Bacik Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf_verifier.h | 2 +- kernel/bpf/verifier.c | 61 +++++++++++++++++------- tools/testing/selftests/bpf/test_verifier.c | 72 +++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 17 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 7035b99..ac5b393 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -23,13 +23,13 @@ struct bpf_reg_state { * result in a bad access. */ u64 min_value, max_value; + u32 id; union { /* valid when type == CONST_IMM | PTR_TO_STACK | UNKNOWN_VALUE */ s64 imm; /* valid when type == PTR_TO_PACKET* */ struct { - u32 id; u16 off; u16 range; }; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 99a7e5b..846d7ce 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -212,9 +212,10 @@ static void print_verifier_state(struct bpf_verifier_state *state) else if (t == CONST_PTR_TO_MAP || t == PTR_TO_MAP_VALUE || t == PTR_TO_MAP_VALUE_OR_NULL || t == PTR_TO_MAP_VALUE_ADJ) - verbose("(ks=%d,vs=%d)", + verbose("(ks=%d,vs=%d,id=%u)", reg->map_ptr->key_size, - reg->map_ptr->value_size); + reg->map_ptr->value_size, + reg->id); if (reg->min_value != BPF_REGISTER_MIN_RANGE) verbose(",min_value=%llu", (unsigned long long)reg->min_value); @@ -447,6 +448,7 @@ static void mark_reg_unknown_value(struct bpf_reg_state *regs, u32 regno) { BUG_ON(regno >= MAX_BPF_REG); regs[regno].type = UNKNOWN_VALUE; + regs[regno].id = 0; regs[regno].imm = 0; } @@ -1252,6 +1254,7 @@ static int check_call(struct bpf_verifier_env *env, int func_id) return -EINVAL; } regs[BPF_REG_0].map_ptr = meta.map_ptr; + regs[BPF_REG_0].id = ++env->id_gen; } else { verbose("unknown return type %d of func %d\n", fn->ret_type, func_id); @@ -1644,8 +1647,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) insn->src_reg); return -EACCES; } - regs[insn->dst_reg].type = UNKNOWN_VALUE; - regs[insn->dst_reg].map_ptr = NULL; + mark_reg_unknown_value(regs, insn->dst_reg); } } else { /* case: R = imm @@ -1907,6 +1909,38 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg, check_reg_overflow(true_reg); } +static void mark_map_reg(struct bpf_reg_state *regs, u32 regno, u32 id, + enum bpf_reg_type type) +{ + struct bpf_reg_state *reg = ®s[regno]; + + if (reg->type == PTR_TO_MAP_VALUE_OR_NULL && reg->id == id) { + reg->type = type; + if (type == UNKNOWN_VALUE) + mark_reg_unknown_value(regs, regno); + } +} + +/* The logic is similar to find_good_pkt_pointers(), both could eventually + * be folded together at some point. + */ +static void mark_map_regs(struct bpf_verifier_state *state, u32 regno, + enum bpf_reg_type type) +{ + struct bpf_reg_state *regs = state->regs; + int i; + + for (i = 0; i < MAX_BPF_REG; i++) + mark_map_reg(regs, i, regs[regno].id, type); + + for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { + if (state->stack_slot_type[i] != STACK_SPILL) + continue; + mark_map_reg(state->spilled_regs, i / BPF_REG_SIZE, + regs[regno].id, type); + } +} + static int check_cond_jmp_op(struct bpf_verifier_env *env, struct bpf_insn *insn, int *insn_idx) { @@ -1994,18 +2028,13 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, if (BPF_SRC(insn->code) == BPF_K && insn->imm == 0 && (opcode == BPF_JEQ || opcode == BPF_JNE) && dst_reg->type == PTR_TO_MAP_VALUE_OR_NULL) { - if (opcode == BPF_JEQ) { - /* next fallthrough insn can access memory via - * this register - */ - regs[insn->dst_reg].type = PTR_TO_MAP_VALUE; - /* branch targer cannot access it, since reg == 0 */ - mark_reg_unknown_value(other_branch->regs, - insn->dst_reg); - } else { - other_branch->regs[insn->dst_reg].type = PTR_TO_MAP_VALUE; - mark_reg_unknown_value(regs, insn->dst_reg); - } + /* Mark all identical map registers in each branch as either + * safe or unknown depending R == 0 or R != 0 conditional. + */ + mark_map_regs(this_branch, insn->dst_reg, + opcode == BPF_JEQ ? PTR_TO_MAP_VALUE : UNKNOWN_VALUE); + mark_map_regs(other_branch, insn->dst_reg, + opcode == BPF_JEQ ? UNKNOWN_VALUE : PTR_TO_MAP_VALUE); } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && dst_reg->type == PTR_TO_PACKET && regs[insn->src_reg].type == PTR_TO_PACKET_END) { diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index ff5df12..0ef8eaf 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -2588,6 +2588,78 @@ static struct bpf_test tests[] = { .result_unpriv = REJECT, .result = REJECT, }, + { + "multiple registers share map_lookup_elem result", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 10), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_4, 0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 4 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS + }, + { + "invalid memory access with multiple map_lookup_elem calls", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 10), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_1), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_4, 0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 4 }, + .result = REJECT, + .errstr = "R4 !read_ok", + .prog_type = BPF_PROG_TYPE_SCHED_CLS + }, + { + "valid indirect map_lookup_elem access with 2nd lookup in branch", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 10), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_1), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_2), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_IMM(BPF_REG_2, 10), + BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0, 3), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_ST_MEM(BPF_DW, BPF_REG_4, 0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 4 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS + }, }; static int probe_filter_length(const struct bpf_insn *fp) -- cgit v1.1 From f33eb0cf9984f79e8643eaac888e4b6a06a8e221 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Wed, 19 Oct 2016 11:26:36 +0200 Subject: openvswitch: remove unused functions ovs_vport_deferred_free is not used anywhere. It's the only caller of free_vport_rcu thus this one can be removed, too. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/vport.c | 16 ---------------- net/openvswitch/vport.h | 1 - 2 files changed, 17 deletions(-) diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 7387418..9d0a878 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -465,22 +465,6 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, } EXPORT_SYMBOL_GPL(ovs_vport_receive); -static void free_vport_rcu(struct rcu_head *rcu) -{ - struct vport *vport = container_of(rcu, struct vport, rcu); - - ovs_vport_free(vport); -} - -void ovs_vport_deferred_free(struct vport *vport) -{ - if (!vport) - return; - - call_rcu(&vport->rcu, free_vport_rcu); -} -EXPORT_SYMBOL_GPL(ovs_vport_deferred_free); - static unsigned int packet_length(const struct sk_buff *skb) { unsigned int length = skb->len - ETH_HLEN; diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index f01f28a..46e5b69 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -149,7 +149,6 @@ struct vport_ops { struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *, const struct vport_parms *); void ovs_vport_free(struct vport *); -void ovs_vport_deferred_free(struct vport *vport); #define VPORT_ALIGN 8 -- cgit v1.1 From 76e4cc7731a1e0c07e202999b9834f9d9be66de4 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Wed, 19 Oct 2016 11:26:37 +0200 Subject: openvswitch: remove unnecessary EXPORT_SYMBOLs Some symbols exported to other modules are really used only by openvswitch.ko. Remove the exports. Tested by loading all 4 openvswitch modules, nothing breaks. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/datapath.c | 2 -- net/openvswitch/vport-netdev.c | 1 - net/openvswitch/vport.c | 1 - 3 files changed, 4 deletions(-) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 4d67ea8..194435a 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -59,7 +59,6 @@ #include "vport-netdev.h" int ovs_net_id __read_mostly; -EXPORT_SYMBOL_GPL(ovs_net_id); static struct genl_family dp_packet_genl_family; static struct genl_family dp_flow_genl_family; @@ -131,7 +130,6 @@ int lockdep_ovsl_is_held(void) else return 1; } -EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held); #endif static struct vport *new_vport(const struct vport_parms *); diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 4e39723..e825753 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -162,7 +162,6 @@ void ovs_netdev_detach_dev(struct vport *vport) netdev_master_upper_dev_get(vport->dev)); dev_set_promiscuity(vport->dev, -1); } -EXPORT_SYMBOL_GPL(ovs_netdev_detach_dev); static void netdev_destroy(struct vport *vport) { diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 9d0a878..9bb85b3 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -463,7 +463,6 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, ovs_dp_process_packet(skb, &key); return 0; } -EXPORT_SYMBOL_GPL(ovs_vport_receive); static unsigned int packet_length(const struct sk_buff *skb) { -- cgit v1.1 From 0f6e87613c3234c6350aaa971bd9434d29405302 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 19 Oct 2016 13:47:52 +0000 Subject: net: arc_emac: use dev_kfree_skb_any instead of dev_kfree_skb Replace dev_kfree_skb with dev_kfree_skb_any in arc_emac_tx() which can be called from hard irq context (netpoll) and from other contexts. arc_emac_tx() only frees skbs that it has dropped. This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/arc/emac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index b0da969..95d8b3e 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -633,7 +633,7 @@ static int arc_emac_tx(struct sk_buff *skb, struct net_device *ndev) if (unlikely(dma_mapping_error(&ndev->dev, addr))) { stats->tx_dropped++; stats->tx_errors++; - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); return NETDEV_TX_OK; } dma_unmap_addr_set(&priv->tx_buff[*txbd_curr], addr, addr); -- cgit v1.1 From b410d13e10db86832026f6ef1a9a7b4ae1c48c19 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 19 Oct 2016 09:56:57 -0500 Subject: net: macb: Use variables with defaults for tx/rx ring sizes instead of hardcoded values The macb driver hardcoded the tx/rx ring sizes. This made it impossible to change the sizes at run time. Add tx_ring_size, and rx_ring_size variables to macb object, which are initilized with default vales during macb_init. Change all references to RX_RING_SIZE and TX_RING_SIZE to their respective replacements. Signed-off-by: Zach Brown Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 114 ++++++++++++++++++++---------------- drivers/net/ethernet/cadence/macb.h | 3 + 2 files changed, 65 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index e83781a..644975d 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -37,14 +37,16 @@ #define MACB_RX_BUFFER_SIZE 128 #define RX_BUFFER_MULTIPLE 64 /* bytes */ -#define RX_RING_SIZE 512 /* must be power of 2 */ -#define RX_RING_BYTES (sizeof(struct macb_dma_desc) * RX_RING_SIZE) +#define DEFAULT_RX_RING_SIZE 512 /* must be power of 2 */ +#define RX_RING_BYTES(bp) (sizeof(struct macb_dma_desc) \ + * (bp)->rx_ring_size) -#define TX_RING_SIZE 128 /* must be power of 2 */ -#define TX_RING_BYTES (sizeof(struct macb_dma_desc) * TX_RING_SIZE) +#define DEFAULT_TX_RING_SIZE 512 /* must be power of 2 */ +#define TX_RING_BYTES(bp) (sizeof(struct macb_dma_desc) \ + * (bp)->tx_ring_size) /* level of occupied TX descriptors under which we wake up TX process */ -#define MACB_TX_WAKEUP_THRESH (3 * TX_RING_SIZE / 4) +#define MACB_TX_WAKEUP_THRESH(bp) (3 * (bp)->tx_ring_size / 4) #define MACB_RX_INT_FLAGS (MACB_BIT(RCOMP) | MACB_BIT(RXUBR) \ | MACB_BIT(ISR_ROVR)) @@ -67,45 +69,47 @@ #define MACB_HALT_TIMEOUT 1230 /* Ring buffer accessors */ -static unsigned int macb_tx_ring_wrap(unsigned int index) +static unsigned int macb_tx_ring_wrap(struct macb *bp, unsigned int index) { - return index & (TX_RING_SIZE - 1); + return index & (bp->tx_ring_size - 1); } static struct macb_dma_desc *macb_tx_desc(struct macb_queue *queue, unsigned int index) { - return &queue->tx_ring[macb_tx_ring_wrap(index)]; + return &queue->tx_ring[macb_tx_ring_wrap(queue->bp, index)]; } static struct macb_tx_skb *macb_tx_skb(struct macb_queue *queue, unsigned int index) { - return &queue->tx_skb[macb_tx_ring_wrap(index)]; + return &queue->tx_skb[macb_tx_ring_wrap(queue->bp, index)]; } static dma_addr_t macb_tx_dma(struct macb_queue *queue, unsigned int index) { dma_addr_t offset; - offset = macb_tx_ring_wrap(index) * sizeof(struct macb_dma_desc); + offset = macb_tx_ring_wrap(queue->bp, index) * + sizeof(struct macb_dma_desc); return queue->tx_ring_dma + offset; } -static unsigned int macb_rx_ring_wrap(unsigned int index) +static unsigned int macb_rx_ring_wrap(struct macb *bp, unsigned int index) { - return index & (RX_RING_SIZE - 1); + return index & (bp->rx_ring_size - 1); } static struct macb_dma_desc *macb_rx_desc(struct macb *bp, unsigned int index) { - return &bp->rx_ring[macb_rx_ring_wrap(index)]; + return &bp->rx_ring[macb_rx_ring_wrap(bp, index)]; } static void *macb_rx_buffer(struct macb *bp, unsigned int index) { - return bp->rx_buffers + bp->rx_buffer_size * macb_rx_ring_wrap(index); + return bp->rx_buffers + bp->rx_buffer_size * + macb_rx_ring_wrap(bp, index); } /* I/O accessors */ @@ -608,7 +612,8 @@ static void macb_tx_error_task(struct work_struct *work) */ if (!(ctrl & MACB_BIT(TX_BUF_EXHAUSTED))) { netdev_vdbg(bp->dev, "txerr skb %u (data %p) TX complete\n", - macb_tx_ring_wrap(tail), skb->data); + macb_tx_ring_wrap(bp, tail), + skb->data); bp->stats.tx_packets++; bp->stats.tx_bytes += skb->len; } @@ -700,7 +705,8 @@ static void macb_tx_interrupt(struct macb_queue *queue) /* First, update TX stats if needed */ if (skb) { netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n", - macb_tx_ring_wrap(tail), skb->data); + macb_tx_ring_wrap(bp, tail), + skb->data); bp->stats.tx_packets++; bp->stats.tx_bytes += skb->len; } @@ -720,7 +726,7 @@ static void macb_tx_interrupt(struct macb_queue *queue) queue->tx_tail = tail; if (__netif_subqueue_stopped(bp->dev, queue_index) && CIRC_CNT(queue->tx_head, queue->tx_tail, - TX_RING_SIZE) <= MACB_TX_WAKEUP_THRESH) + bp->tx_ring_size) <= MACB_TX_WAKEUP_THRESH(bp)) netif_wake_subqueue(bp->dev, queue_index); } @@ -731,8 +737,8 @@ static void gem_rx_refill(struct macb *bp) dma_addr_t paddr; while (CIRC_SPACE(bp->rx_prepared_head, bp->rx_tail, - RX_RING_SIZE) > 0) { - entry = macb_rx_ring_wrap(bp->rx_prepared_head); + bp->rx_ring_size) > 0) { + entry = macb_rx_ring_wrap(bp, bp->rx_prepared_head); /* Make hw descriptor updates visible to CPU */ rmb(); @@ -759,7 +765,7 @@ static void gem_rx_refill(struct macb *bp) bp->rx_skbuff[entry] = skb; - if (entry == RX_RING_SIZE - 1) + if (entry == bp->rx_ring_size - 1) paddr |= MACB_BIT(RX_WRAP); macb_set_addr(&(bp->rx_ring[entry]), paddr); bp->rx_ring[entry].ctrl = 0; @@ -813,7 +819,7 @@ static int gem_rx(struct macb *bp, int budget) dma_addr_t addr; bool rxused; - entry = macb_rx_ring_wrap(bp->rx_tail); + entry = macb_rx_ring_wrap(bp, bp->rx_tail); desc = &bp->rx_ring[entry]; /* Make hw descriptor updates visible to CPU */ @@ -895,8 +901,8 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, len = desc->ctrl & bp->rx_frm_len_mask; netdev_vdbg(bp->dev, "macb_rx_frame frags %u - %u (len %u)\n", - macb_rx_ring_wrap(first_frag), - macb_rx_ring_wrap(last_frag), len); + macb_rx_ring_wrap(bp, first_frag), + macb_rx_ring_wrap(bp, last_frag), len); /* The ethernet header starts NET_IP_ALIGN bytes into the * first buffer. Since the header is 14 bytes, this makes the @@ -969,12 +975,12 @@ static inline void macb_init_rx_ring(struct macb *bp) int i; addr = bp->rx_buffers_dma; - for (i = 0; i < RX_RING_SIZE; i++) { + for (i = 0; i < bp->rx_ring_size; i++) { bp->rx_ring[i].addr = addr; bp->rx_ring[i].ctrl = 0; addr += bp->rx_buffer_size; } - bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP); + bp->rx_ring[bp->rx_ring_size - 1].addr |= MACB_BIT(RX_WRAP); } static int macb_rx(struct macb *bp, int budget) @@ -1228,7 +1234,7 @@ static unsigned int macb_tx_map(struct macb *bp, offset = 0; while (len) { size = min(len, bp->max_tx_length); - entry = macb_tx_ring_wrap(tx_head); + entry = macb_tx_ring_wrap(bp, tx_head); tx_skb = &queue->tx_skb[entry]; mapping = dma_map_single(&bp->pdev->dev, @@ -1257,7 +1263,7 @@ static unsigned int macb_tx_map(struct macb *bp, offset = 0; while (len) { size = min(len, bp->max_tx_length); - entry = macb_tx_ring_wrap(tx_head); + entry = macb_tx_ring_wrap(bp, tx_head); tx_skb = &queue->tx_skb[entry]; mapping = skb_frag_dma_map(&bp->pdev->dev, frag, @@ -1295,14 +1301,14 @@ static unsigned int macb_tx_map(struct macb *bp, * to set the end of TX queue */ i = tx_head; - entry = macb_tx_ring_wrap(i); + entry = macb_tx_ring_wrap(bp, i); ctrl = MACB_BIT(TX_USED); desc = &queue->tx_ring[entry]; desc->ctrl = ctrl; do { i--; - entry = macb_tx_ring_wrap(i); + entry = macb_tx_ring_wrap(bp, i); tx_skb = &queue->tx_skb[entry]; desc = &queue->tx_ring[entry]; @@ -1311,7 +1317,7 @@ static unsigned int macb_tx_map(struct macb *bp, ctrl |= MACB_BIT(TX_LAST); eof = 0; } - if (unlikely(entry == (TX_RING_SIZE - 1))) + if (unlikely(entry == (bp->tx_ring_size - 1))) ctrl |= MACB_BIT(TX_WRAP); /* Set TX buffer descriptor */ @@ -1388,7 +1394,8 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) spin_lock_irqsave(&bp->lock, flags); /* This is a hard error, log it. */ - if (CIRC_SPACE(queue->tx_head, queue->tx_tail, TX_RING_SIZE) < count) { + if (CIRC_SPACE(queue->tx_head, queue->tx_tail, + bp->tx_ring_size) < count) { netif_stop_subqueue(dev, queue_index); spin_unlock_irqrestore(&bp->lock, flags); netdev_dbg(bp->dev, "tx_head = %u, tx_tail = %u\n", @@ -1414,7 +1421,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); - if (CIRC_SPACE(queue->tx_head, queue->tx_tail, TX_RING_SIZE) < 1) + if (CIRC_SPACE(queue->tx_head, queue->tx_tail, bp->tx_ring_size) < 1) netif_stop_subqueue(dev, queue_index); unlock: @@ -1453,7 +1460,7 @@ static void gem_free_rx_buffers(struct macb *bp) if (!bp->rx_skbuff) return; - for (i = 0; i < RX_RING_SIZE; i++) { + for (i = 0; i < bp->rx_ring_size; i++) { skb = bp->rx_skbuff[i]; if (!skb) @@ -1478,7 +1485,7 @@ static void macb_free_rx_buffers(struct macb *bp) { if (bp->rx_buffers) { dma_free_coherent(&bp->pdev->dev, - RX_RING_SIZE * bp->rx_buffer_size, + bp->rx_ring_size * bp->rx_buffer_size, bp->rx_buffers, bp->rx_buffers_dma); bp->rx_buffers = NULL; } @@ -1491,7 +1498,7 @@ static void macb_free_consistent(struct macb *bp) bp->macbgem_ops.mog_free_rx_buffers(bp); if (bp->rx_ring) { - dma_free_coherent(&bp->pdev->dev, RX_RING_BYTES, + dma_free_coherent(&bp->pdev->dev, RX_RING_BYTES(bp), bp->rx_ring, bp->rx_ring_dma); bp->rx_ring = NULL; } @@ -1500,7 +1507,7 @@ static void macb_free_consistent(struct macb *bp) kfree(queue->tx_skb); queue->tx_skb = NULL; if (queue->tx_ring) { - dma_free_coherent(&bp->pdev->dev, TX_RING_BYTES, + dma_free_coherent(&bp->pdev->dev, TX_RING_BYTES(bp), queue->tx_ring, queue->tx_ring_dma); queue->tx_ring = NULL; } @@ -1511,14 +1518,14 @@ static int gem_alloc_rx_buffers(struct macb *bp) { int size; - size = RX_RING_SIZE * sizeof(struct sk_buff *); + size = bp->rx_ring_size * sizeof(struct sk_buff *); bp->rx_skbuff = kzalloc(size, GFP_KERNEL); if (!bp->rx_skbuff) return -ENOMEM; - - netdev_dbg(bp->dev, - "Allocated %d RX struct sk_buff entries at %p\n", - RX_RING_SIZE, bp->rx_skbuff); + else + netdev_dbg(bp->dev, + "Allocated %d RX struct sk_buff entries at %p\n", + bp->rx_ring_size, bp->rx_skbuff); return 0; } @@ -1526,7 +1533,7 @@ static int macb_alloc_rx_buffers(struct macb *bp) { int size; - size = RX_RING_SIZE * bp->rx_buffer_size; + size = bp->rx_ring_size * bp->rx_buffer_size; bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, &bp->rx_buffers_dma, GFP_KERNEL); if (!bp->rx_buffers) @@ -1545,7 +1552,7 @@ static int macb_alloc_consistent(struct macb *bp) int size; for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - size = TX_RING_BYTES; + size = TX_RING_BYTES(bp); queue->tx_ring = dma_alloc_coherent(&bp->pdev->dev, size, &queue->tx_ring_dma, GFP_KERNEL); @@ -1556,13 +1563,13 @@ static int macb_alloc_consistent(struct macb *bp) q, size, (unsigned long)queue->tx_ring_dma, queue->tx_ring); - size = TX_RING_SIZE * sizeof(struct macb_tx_skb); + size = bp->tx_ring_size * sizeof(struct macb_tx_skb); queue->tx_skb = kmalloc(size, GFP_KERNEL); if (!queue->tx_skb) goto out_err; } - size = RX_RING_BYTES; + size = RX_RING_BYTES(bp); bp->rx_ring = dma_alloc_coherent(&bp->pdev->dev, size, &bp->rx_ring_dma, GFP_KERNEL); if (!bp->rx_ring) @@ -1588,11 +1595,11 @@ static void gem_init_rings(struct macb *bp) int i; for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - for (i = 0; i < TX_RING_SIZE; i++) { - macb_set_addr(&(queue->tx_ring[i]), 0); + for (i = 0; i < bp->tx_ring_size; i++) { + queue->tx_ring[i].addr = 0; queue->tx_ring[i].ctrl = MACB_BIT(TX_USED); } - queue->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP); + queue->tx_ring[bp->tx_ring_size - 1].ctrl |= MACB_BIT(TX_WRAP); queue->tx_head = 0; queue->tx_tail = 0; } @@ -1609,13 +1616,13 @@ static void macb_init_rings(struct macb *bp) macb_init_rx_ring(bp); - for (i = 0; i < TX_RING_SIZE; i++) { + for (i = 0; i < bp->tx_ring_size; i++) { bp->queues[0].tx_ring[i].addr = 0; bp->queues[0].tx_ring[i].ctrl = MACB_BIT(TX_USED); } bp->queues[0].tx_head = 0; bp->queues[0].tx_tail = 0; - bp->queues[0].tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP); + bp->queues[0].tx_ring[bp->tx_ring_size - 1].ctrl |= MACB_BIT(TX_WRAP); bp->rx_tail = 0; } @@ -2148,8 +2155,8 @@ static void macb_get_regs(struct net_device *dev, struct ethtool_regs *regs, regs->version = (macb_readl(bp, MID) & ((1 << MACB_REV_SIZE) - 1)) | MACB_GREGS_VERSION; - tail = macb_tx_ring_wrap(bp->queues[0].tx_tail); - head = macb_tx_ring_wrap(bp->queues[0].tx_head); + tail = macb_tx_ring_wrap(bp, bp->queues[0].tx_tail); + head = macb_tx_ring_wrap(bp, bp->queues[0].tx_head); regs_buff[0] = macb_readl(bp, NCR); regs_buff[1] = macb_or_gem_readl(bp, NCFGR); @@ -2419,6 +2426,9 @@ static int macb_init(struct platform_device *pdev) int err; u32 val; + bp->tx_ring_size = DEFAULT_TX_RING_SIZE; + bp->rx_ring_size = DEFAULT_RX_RING_SIZE; + /* set the queue register mapping once for all: queue0 has a special * register mapping but we don't want to test the queue index then * compute the corresponding register offset at run time. diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 8bed4b5..1216950 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -811,6 +811,9 @@ struct macb { void *rx_buffers; size_t rx_buffer_size; + unsigned int rx_ring_size; + unsigned int tx_ring_size; + unsigned int num_queues; unsigned int queue_mask; struct macb_queue queues[MACB_MAX_QUEUES]; -- cgit v1.1 From 8441bb33beaf58a67a7664dddede68c5860acf74 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 19 Oct 2016 09:56:58 -0500 Subject: net: macb: Add ethtool get_ringparam and set_ringparam functionality Some applications want to tune the size of the macb rx/tx ring buffers. The ethtool set_ringparam function is the standard way of doing it. Signed-off-by: Zach Brown Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 644975d..e1847ce 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -37,11 +37,16 @@ #define MACB_RX_BUFFER_SIZE 128 #define RX_BUFFER_MULTIPLE 64 /* bytes */ + #define DEFAULT_RX_RING_SIZE 512 /* must be power of 2 */ +#define MIN_RX_RING_SIZE 64 +#define MAX_RX_RING_SIZE 8192 #define RX_RING_BYTES(bp) (sizeof(struct macb_dma_desc) \ * (bp)->rx_ring_size) #define DEFAULT_TX_RING_SIZE 512 /* must be power of 2 */ +#define MIN_TX_RING_SIZE 64 +#define MAX_TX_RING_SIZE 4096 #define TX_RING_BYTES(bp) (sizeof(struct macb_dma_desc) \ * (bp)->tx_ring_size) @@ -2211,6 +2216,56 @@ static int macb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) return 0; } +static void macb_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct macb *bp = netdev_priv(netdev); + + ring->rx_max_pending = MAX_RX_RING_SIZE; + ring->tx_max_pending = MAX_TX_RING_SIZE; + + ring->rx_pending = bp->rx_ring_size; + ring->tx_pending = bp->tx_ring_size; +} + +static int macb_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct macb *bp = netdev_priv(netdev); + u32 new_rx_size, new_tx_size; + unsigned int reset = 0; + + if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) + return -EINVAL; + + new_rx_size = clamp_t(u32, ring->rx_pending, + MIN_RX_RING_SIZE, MAX_RX_RING_SIZE); + new_rx_size = roundup_pow_of_two(new_rx_size); + + new_tx_size = clamp_t(u32, ring->tx_pending, + MIN_TX_RING_SIZE, MAX_TX_RING_SIZE); + new_tx_size = roundup_pow_of_two(new_tx_size); + + if ((new_tx_size == bp->tx_ring_size) && + (new_rx_size == bp->rx_ring_size)) { + /* nothing to do */ + return 0; + } + + if (netif_running(bp->dev)) { + reset = 1; + macb_close(bp->dev); + } + + bp->rx_ring_size = new_rx_size; + bp->tx_ring_size = new_tx_size; + + if (reset) + macb_open(bp->dev); + + return 0; +} + static const struct ethtool_ops macb_ethtool_ops = { .get_regs_len = macb_get_regs_len, .get_regs = macb_get_regs, @@ -2220,6 +2275,8 @@ static const struct ethtool_ops macb_ethtool_ops = { .set_wol = macb_set_wol, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_ringparam = macb_get_ringparam, + .set_ringparam = macb_set_ringparam, }; static const struct ethtool_ops gem_ethtool_ops = { @@ -2232,6 +2289,8 @@ static const struct ethtool_ops gem_ethtool_ops = { .get_sset_count = gem_get_sset_count, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_ringparam = macb_get_ringparam, + .set_ringparam = macb_set_ringparam, }; static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -- cgit v1.1 From c5098ebbd6605e7ba1fd1dbfe4c1297ac2ab7362 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 19 Oct 2016 23:16:39 +0200 Subject: ila: Fix tailroom allocation of lwtstate Tailroom is supposed to be of length sizeof(struct ila_lwt) but sizeof(struct ila_params) is currently allocated. This leads to the dst_cache and connected member of ila_lwt being referenced out of bounds. struct ila_lwt { struct ila_params p; struct dst_cache dst_cache; u32 connected : 1; }; Fixes: 65d7ab8de582 ("net: Identifier Locator Addressing module") Signed-off-by: Thomas Graf Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/ipv6/ila/ila_lwt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 9fafba6..c7a39d0 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -122,7 +122,7 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, struct ila_lwt *ilwt; struct ila_params *p; struct nlattr *tb[ILA_ATTR_MAX + 1]; - size_t encap_len = sizeof(*p); + size_t encap_len = sizeof(*ilwt); struct lwtunnel_state *newts; const struct fib6_config *cfg6 = cfg; struct ila_addr *iaddr; -- cgit v1.1 From 25c07e2c5ec80a1c7390b41e2b03ca3d0e31fc01 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Wed, 19 Oct 2016 23:23:50 +0200 Subject: net: smc91x: fix neponset breakage by pxa u16 writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The patch isolating the u16 writes for pxa assumed all machine_is_*() calls were removed, and therefore removed the mach-types.h include which provided them. Unfortunately 2 machine_is_*() remained in smc91x.c file including smc91x.h from which the include was removed, triggering the error: drivers/net/ethernet/smsc/smc91x.c: In function ‘smc_drv_probe’: drivers/net/ethernet/smsc/smc91x.c:2380:2: error: implicit declaration of function ‘machine_is_assabet’ [-Werror=implicit-function-declaration] if (machine_is_assabet() && machine_has_neponset()) This adds back the wrongly removed include. Fixes: d09d747ae4c2 ("net: smc91x: isolate u16 writes alignment workaround") Signed-off-by: Robert Jarzmik Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smc91x.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h index 45e6b81..08b17ad 100644 --- a/drivers/net/ethernet/smsc/smc91x.h +++ b/drivers/net/ethernet/smsc/smc91x.h @@ -63,6 +63,8 @@ #if defined(CONFIG_ARM) +#include + /* Now the bus width is specified in the platform data * pretend here to support all I/O access types */ -- cgit v1.1 From 9652dc2eb9e40d100e0a3abfdcc0ea852c8be254 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 19 Oct 2016 21:24:58 -0700 Subject: tcp: relax listening_hash operations softirq handlers use RCU protection to lookup listeners, and write operations all happen from process context. We do not need to block BH for dump operations. Also SYN_RECV since request sockets are stored in the ehash table : 1) inet_diag_dump_icsk() no longer need to clear cb->args[3] and cb->args[4] that were used as cursors while iterating the old per listener hash table. 2) Also factorize a test : No need to scan listening_hash[] if r->id.idiag_dport is not zero. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/inet_diag.c | 16 ++++------------ net/ipv4/tcp_ipv4.c | 6 +++--- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index e4d16fc..0a1d4a8 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -863,7 +863,7 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, s_num = num = cb->args[2]; if (cb->args[0] == 0) { - if (!(idiag_states & TCPF_LISTEN)) + if (!(idiag_states & TCPF_LISTEN) || r->id.idiag_dport) goto skip_listen_ht; for (i = s_i; i < INET_LHTABLE_SIZE; i++) { @@ -872,7 +872,7 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, num = 0; ilb = &hashinfo->listening_hash[i]; - spin_lock_bh(&ilb->lock); + spin_lock(&ilb->lock); sk_for_each(sk, &ilb->head) { struct inet_sock *inet = inet_sk(sk); @@ -892,26 +892,18 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, r->id.idiag_sport) goto next_listen; - if (r->id.idiag_dport || - cb->args[3] > 0) - goto next_listen; - if (inet_csk_diag_dump(sk, skb, cb, r, bc, net_admin) < 0) { - spin_unlock_bh(&ilb->lock); + spin_unlock(&ilb->lock); goto done; } next_listen: - cb->args[3] = 0; - cb->args[4] = 0; ++num; } - spin_unlock_bh(&ilb->lock); + spin_unlock(&ilb->lock); s_num = 0; - cb->args[3] = 0; - cb->args[4] = 0; } skip_listen_ht: cb->args[0] = 1; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index bd5e8d1..83b3d0b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1893,7 +1893,7 @@ static void *listening_get_next(struct seq_file *seq, void *cur) if (!sk) { get_head: ilb = &tcp_hashinfo.listening_hash[st->bucket]; - spin_lock_bh(&ilb->lock); + spin_lock(&ilb->lock); sk = sk_head(&ilb->head); st->offset = 0; goto get_sk; @@ -1911,7 +1911,7 @@ get_sk: return sk; icsk = inet_csk(sk); } - spin_unlock_bh(&ilb->lock); + spin_unlock(&ilb->lock); st->offset = 0; if (++st->bucket < INET_LHTABLE_SIZE) goto get_head; @@ -2119,7 +2119,7 @@ static void tcp_seq_stop(struct seq_file *seq, void *v) switch (st->state) { case TCP_SEQ_STATE_LISTENING: if (v != SEQ_START_TOKEN) - spin_unlock_bh(&tcp_hashinfo.listening_hash[st->bucket].lock); + spin_unlock(&tcp_hashinfo.listening_hash[st->bucket].lock); break; case TCP_SEQ_STATE_ESTABLISHED: if (v) -- cgit v1.1 From 01e5943ac16b1529821cceea36e929f7ea51c854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 20 Oct 2016 10:28:27 +0200 Subject: net: fec: drop check for clk==NULL before calling clk_* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clk_prepare, clk_enable and their counterparts (at least the common clk ones, but also most others) do check for the clk being NULL anyhow (and return 0 then), so there is no gain when the caller checks, too. Signed-off-by: Uwe Kleine-König Acked-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 4ce8179..43b2839 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1841,11 +1841,11 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) ret = clk_prepare_enable(fep->clk_ahb); if (ret) return ret; - if (fep->clk_enet_out) { - ret = clk_prepare_enable(fep->clk_enet_out); - if (ret) - goto failed_clk_enet_out; - } + + ret = clk_prepare_enable(fep->clk_enet_out); + if (ret) + goto failed_clk_enet_out; + if (fep->clk_ptp) { mutex_lock(&fep->ptp_clk_mutex); ret = clk_prepare_enable(fep->clk_ptp); @@ -1857,23 +1857,20 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) } mutex_unlock(&fep->ptp_clk_mutex); } - if (fep->clk_ref) { - ret = clk_prepare_enable(fep->clk_ref); - if (ret) - goto failed_clk_ref; - } + + ret = clk_prepare_enable(fep->clk_ref); + if (ret) + goto failed_clk_ref; } else { clk_disable_unprepare(fep->clk_ahb); - if (fep->clk_enet_out) - clk_disable_unprepare(fep->clk_enet_out); + clk_disable_unprepare(fep->clk_enet_out); if (fep->clk_ptp) { mutex_lock(&fep->ptp_clk_mutex); clk_disable_unprepare(fep->clk_ptp); fep->ptp_clk_on = false; mutex_unlock(&fep->ptp_clk_mutex); } - if (fep->clk_ref) - clk_disable_unprepare(fep->clk_ref); + clk_disable_unprepare(fep->clk_ref); } return 0; -- cgit v1.1 From 547412fe08239366f867aea4cc9c8e6816302c1d Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Thu, 20 Oct 2016 16:30:31 +0800 Subject: net: phy: aquantia: add PHY ID of AQR106 and AQR107 The AQR106 and AQR107 can use the existing driver. Signed-off-by: Shaohui Xie Signed-off-by: David S. Miller --- drivers/net/phy/aquantia.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index 09b0b0a..e8ae50e 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -21,6 +21,8 @@ #define PHY_ID_AQ1202 0x03a1b445 #define PHY_ID_AQ2104 0x03a1b460 #define PHY_ID_AQR105 0x03a1b4a2 +#define PHY_ID_AQR106 0x03a1b4d0 +#define PHY_ID_AQR107 0x03a1b4e0 #define PHY_ID_AQR405 0x03a1b4b0 #define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \ @@ -154,6 +156,30 @@ static struct phy_driver aquantia_driver[] = { .read_status = aquantia_read_status, }, { + .phy_id = PHY_ID_AQR106, + .phy_id_mask = 0xfffffff0, + .name = "Aquantia AQR106", + .features = PHY_AQUANTIA_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .aneg_done = aquantia_aneg_done, + .config_aneg = aquantia_config_aneg, + .config_intr = aquantia_config_intr, + .ack_interrupt = aquantia_ack_interrupt, + .read_status = aquantia_read_status, +}, +{ + .phy_id = PHY_ID_AQR107, + .phy_id_mask = 0xfffffff0, + .name = "Aquantia AQR107", + .features = PHY_AQUANTIA_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .aneg_done = aquantia_aneg_done, + .config_aneg = aquantia_config_aneg, + .config_intr = aquantia_config_intr, + .ack_interrupt = aquantia_ack_interrupt, + .read_status = aquantia_read_status, +}, +{ .phy_id = PHY_ID_AQR405, .phy_id_mask = 0xfffffff0, .name = "Aquantia AQR405", @@ -173,6 +199,8 @@ static struct mdio_device_id __maybe_unused aquantia_tbl[] = { { PHY_ID_AQ1202, 0xfffffff0 }, { PHY_ID_AQ2104, 0xfffffff0 }, { PHY_ID_AQR105, 0xfffffff0 }, + { PHY_ID_AQR106, 0xfffffff0 }, + { PHY_ID_AQR107, 0xfffffff0 }, { PHY_ID_AQR405, 0xfffffff0 }, { } }; -- cgit v1.1 From d80f45ffd8847891554f00e19fc9bc95ee720976 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 20 Oct 2016 16:59:49 +0000 Subject: dwc_eth_qos: use dev_kfree_skb_any instead of dev_kfree_skb Replace dev_kfree_skb with dev_kfree_skb_any in dwceqos_start_xmit() which can be called from hard irq context (netpoll) and from other contexts. dwceqos_start_xmit() only frees skbs that it has dropped. This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc_eth_qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c index 0d00531..eaa51ce 100644 --- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c +++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c @@ -2211,7 +2211,7 @@ static int dwceqos_start_xmit(struct sk_buff *skb, struct net_device *ndev) tx_error: dwceqos_tx_rollback(lp, &trans); - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); return 0; } -- cgit v1.1 From 81ad2b7de6dd352802f9979910343d4867e1ac94 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 20 Oct 2016 17:00:32 +0000 Subject: net: ethernet: mediatek: use dev_kfree_skb_any instead of dev_kfree_skb Replace dev_kfree_skb with dev_kfree_skb_any in mtk_start_xmit() which can be called from hard irq context (netpoll) and from other contexts. mtk_start_xmit() only frees skbs that it has dropped. This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 8f80e61..d716274 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -843,7 +843,7 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) drop: spin_unlock(ð->page_lock); stats->tx_dropped++; - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); return NETDEV_TX_OK; } -- cgit v1.1 From 1a61a8f1776758acb5111785c8a1688394fa3a60 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 20 Oct 2016 17:01:56 +0000 Subject: myri10ge: fix typo in parameter description Fix typo in parameter description. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 5ae3f17..e506ca8 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -289,7 +289,7 @@ static char *myri10ge_fw_names[MYRI10GE_MAX_BOARDS] = {[0 ... (MYRI10GE_MAX_BOARDS - 1)] = NULL }; module_param_array_named(myri10ge_fw_names, myri10ge_fw_names, charp, NULL, 0444); -MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image names per board"); +MODULE_PARM_DESC(myri10ge_fw_names, "Firmware image names per board"); static int myri10ge_ecrc_enable = 1; module_param(myri10ge_ecrc_enable, int, S_IRUGO); -- cgit v1.1 From d894be57ca92c8a8819ab544d550809e8731137b Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:16 -0400 Subject: ethernet: use net core MTU range checking in more drivers Somehow, I missed a healthy number of ethernet drivers in the last pass. Most of these drivers either were in need of an updated max_mtu to make jumbo frames possible to enable again. In a few cases, also setting a different min_mtu to match previous lower bounds. There are also a few drivers that had no upper bounds checking, so they're getting a brand new ETH_MAX_MTU that is identical to IP_MAX_MTU, but accessible by includes all ethernet and ethernet-like drivers all have already. acenic: - min_mtu = 0, max_mtu = 9000 amazon/ena: - min_mtu = 128, max_mtu = adapter->max_mtu amd/xgbe: - min_mtu = 0, max_mtu = 9000 sb1250: - min_mtu = 0, max_mtu = 1518 cxgb3: - min_mtu = 81, max_mtu = 65535 cxgb4: - min_mtu = 81, max_mtu = 9600 cxgb4vf: - min_mtu = 81, max_mtu = 65535 benet: - min_mtu = 256, max_mtu = 9000 ibmveth: - min_mtu = 68, max_mtu = 65535 ibmvnic: - min_mtu = adapter->min_mtu, max_mtu = adapter->max_mtu - remove now redundant ibmvnic_change_mtu jme: - min_mtu = 1280, max_mtu = 9202 mv643xx_eth: - min_mtu = 64, max_mtu = 9500 mlxsw: - min_mtu = 0, max_mtu = 65535 - Basically bypassing the core checks, and instead relying on dynamic checks in the respective switch drivers' ndo_change_mtu functions ns83820: - min_mtu = 0 - remove redundant ns83820_change_mtu, only checked for mtu > 1500 netxen: - min_mtu = 0, max_mtu = 8000 (P2), max_mtu = 9600 (P3) qlge: - min_mtu = 1500, max_mtu = 9000 - driver only supports setting mtu to 1500 or 9000, so the core check only rules out < 1500 and > 9000, qlge_change_mtu still needs to check that the value is 1500 or 9000 qualcomm/emac: - min_mtu = 46, max_mtu = 9194 xilinx_axienet: - min_mtu = 64, max_mtu = 9000 Fixes: 61e84623ace3 ("net: centralize net_device min/max MTU checking") CC: netdev@vger.kernel.org CC: Jes Sorensen CC: Netanel Belgazal CC: Tom Lendacky CC: Santosh Raspatur CC: Hariprasad S CC: Sathya Perla CC: Ajit Khaparde CC: Sriharsha Basavapatna CC: Somnath Kotur CC: Thomas Falcon CC: John Allen CC: Guo-Fu Tseng CC: Sebastian Hesselbarth CC: Jiri Pirko CC: Ido Schimmel CC: Manish Chopra CC: Sony Chacko CC: Rajesh Borundia CC: Timur Tabi CC: Anirudha Sarangi CC: John Linn Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/alteon/acenic.c | 5 ++--- drivers/net/ethernet/amazon/ena/ena_netdev.c | 9 ++------- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 5 ----- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 2 ++ drivers/net/ethernet/broadcom/sb1250-mac.c | 12 ++---------- drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 4 ++-- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 6 ++++-- .../net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 6 ++---- drivers/net/ethernet/emulex/benet/be_main.c | 22 ++++------------------ drivers/net/ethernet/ibm/ibmveth.c | 6 +++--- drivers/net/ethernet/ibm/ibmvnic.c | 16 ++++------------ drivers/net/ethernet/jme.c | 12 ++++-------- drivers/net/ethernet/marvell/mv643xx_eth.c | 7 ++++--- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 3 +++ drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 3 +++ drivers/net/ethernet/natsemi/ns83820.c | 11 ++--------- drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c | 12 ------------ .../net/ethernet/qlogic/netxen/netxen_nic_main.c | 7 +++++++ drivers/net/ethernet/qlogic/qlge/qlge_main.c | 7 +++++++ drivers/net/ethernet/qualcomm/emac/emac.c | 13 ++++++------- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 7 ++++--- include/uapi/linux/if_ether.h | 1 + 22 files changed, 68 insertions(+), 108 deletions(-) diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c index b90a26b..a5c1e29 100644 --- a/drivers/net/ethernet/alteon/acenic.c +++ b/drivers/net/ethernet/alteon/acenic.c @@ -474,6 +474,8 @@ static int acenic_probe_one(struct pci_dev *pdev, dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; dev->watchdog_timeo = 5*HZ; + dev->min_mtu = 0; + dev->max_mtu = ACE_JUMBO_MTU; dev->netdev_ops = &ace_netdev_ops; dev->ethtool_ops = &ace_ethtool_ops; @@ -2548,9 +2550,6 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu) struct ace_private *ap = netdev_priv(dev); struct ace_regs __iomem *regs = ap->regs; - if (new_mtu > ACE_JUMBO_MTU) - return -EINVAL; - writel(new_mtu + ETH_HLEN + 4, ®s->IfMtu); dev->mtu = new_mtu; diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index bfeaec5..2a55ab0 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -103,13 +103,6 @@ static int ena_change_mtu(struct net_device *dev, int new_mtu) struct ena_adapter *adapter = netdev_priv(dev); int ret; - if ((new_mtu > adapter->max_mtu) || (new_mtu < ENA_MIN_MTU)) { - netif_err(adapter, drv, dev, - "Invalid MTU setting. new_mtu: %d\n", new_mtu); - - return -EINVAL; - } - ret = ena_com_set_dev_mtu(adapter->ena_dev, new_mtu); if (!ret) { netif_dbg(adapter, drv, dev, "set MTU to %d\n", new_mtu); @@ -2755,6 +2748,8 @@ static void ena_set_conf_feat_params(struct ena_adapter *adapter, ena_set_dev_offloads(feat, netdev); adapter->max_mtu = feat->dev_attr.max_mtu; + netdev->max_mtu = adapter->max_mtu; + netdev->min_mtu = ENA_MIN_MTU; } static int ena_rss_init_default(struct ena_adapter *adapter) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 7f9216d..c4e6682 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -257,11 +257,6 @@ static int xgbe_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu) { unsigned int rx_buf_size; - if (mtu > XGMAC_JUMBO_PACKET_MTU) { - netdev_alert(netdev, "MTU exceeds maximum supported value\n"); - return -EINVAL; - } - rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; rx_buf_size = clamp_val(rx_buf_size, XGBE_RX_MIN_BUF_SIZE, PAGE_SIZE); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 9de0788..667e120 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -738,6 +738,8 @@ static int xgbe_probe(struct platform_device *pdev) pdata->netdev_features = netdev->features; netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->min_mtu = 0; + netdev->max_mtu = XGMAC_JUMBO_PACKET_MTU; /* Use default watchdog timeout */ netdev->watchdog_timeo = 0; diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index f1b8118..cb312e4 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2147,15 +2147,6 @@ static void sbmac_setmulti(struct sbmac_softc *sc) } } -static int sb1250_change_mtu(struct net_device *_dev, int new_mtu) -{ - if (new_mtu > ENET_PACKET_SIZE) - return -EINVAL; - _dev->mtu = new_mtu; - pr_info("changing the mtu to %d\n", new_mtu); - return 0; -} - static const struct net_device_ops sbmac_netdev_ops = { .ndo_open = sbmac_open, .ndo_stop = sbmac_close, @@ -2163,7 +2154,6 @@ static const struct net_device_ops sbmac_netdev_ops = { .ndo_set_rx_mode = sbmac_set_rx_mode, .ndo_tx_timeout = sbmac_tx_timeout, .ndo_do_ioctl = sbmac_mii_ioctl, - .ndo_change_mtu = sb1250_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -2229,6 +2219,8 @@ static int sbmac_init(struct platform_device *pldev, long long base) dev->netdev_ops = &sbmac_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; + dev->max_mtu = 0; + dev->max_mtu = ENET_PACKET_SIZE; netif_napi_add(dev, &sc->napi, sbmac_poll, 16); diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 43da891..092b3c1 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -2531,8 +2531,6 @@ static int cxgb_change_mtu(struct net_device *dev, int new_mtu) struct adapter *adapter = pi->adapter; int ret; - if (new_mtu < 81) /* accommodate SACK */ - return -EINVAL; if ((ret = t3_mac_set_mtu(&pi->mac, new_mtu))) return ret; dev->mtu = new_mtu; @@ -3295,6 +3293,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &cxgb_netdev_ops; netdev->ethtool_ops = &cxgb_ethtool_ops; + netdev->min_mtu = 81; + netdev->max_mtu = ETH_MAX_MTU; } pci_set_drvdata(pdev, adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index f320497..b0bb23f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2502,8 +2502,6 @@ static int cxgb_change_mtu(struct net_device *dev, int new_mtu) int ret; struct port_info *pi = netdev_priv(dev); - if (new_mtu < 81 || new_mtu > MAX_MTU) /* accommodate SACK */ - return -EINVAL; ret = t4_set_rxmode(pi->adapter, pi->adapter->pf, pi->viid, new_mtu, -1, -1, -1, -1, true); if (!ret) @@ -4803,6 +4801,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->priv_flags |= IFF_UNICAST_FLT; + /* MTU range: 81 - 9600 */ + netdev->min_mtu = 81; + netdev->max_mtu = MAX_MTU; + netdev->netdev_ops = &cxgb4_netdev_ops; #ifdef CONFIG_CHELSIO_T4_DCB netdev->dcbnl_ops = &cxgb4_dcb_ops; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 100b2cc..5d4da0e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -1108,10 +1108,6 @@ static int cxgb4vf_change_mtu(struct net_device *dev, int new_mtu) int ret; struct port_info *pi = netdev_priv(dev); - /* accommodate SACK */ - if (new_mtu < 81) - return -EINVAL; - ret = t4vf_set_rxmode(pi->adapter, pi->viid, new_mtu, -1, -1, -1, -1, true); if (!ret) @@ -2966,6 +2962,8 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_HIGHDMA; netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->min_mtu = 81; + netdev->max_mtu = ETH_MAX_MTU; netdev->netdev_ops = &cxgb4vf_netdev_ops; netdev->ethtool_ops = &cxgb4vf_ethtool_ops; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index cece8a0..3f6152c 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1406,23 +1406,6 @@ drop: return NETDEV_TX_OK; } -static int be_change_mtu(struct net_device *netdev, int new_mtu) -{ - struct be_adapter *adapter = netdev_priv(netdev); - struct device *dev = &adapter->pdev->dev; - - if (new_mtu < BE_MIN_MTU || new_mtu > BE_MAX_MTU) { - dev_info(dev, "MTU must be between %d and %d bytes\n", - BE_MIN_MTU, BE_MAX_MTU); - return -EINVAL; - } - - dev_info(dev, "MTU changed from %d to %d bytes\n", - netdev->mtu, new_mtu); - netdev->mtu = new_mtu; - return 0; -} - static inline bool be_in_all_promisc(struct be_adapter *adapter) { return (adapter->if_flags & BE_IF_FLAGS_ALL_PROMISCUOUS) == @@ -5216,7 +5199,6 @@ static const struct net_device_ops be_netdev_ops = { .ndo_start_xmit = be_xmit, .ndo_set_rx_mode = be_set_rx_mode, .ndo_set_mac_address = be_mac_addr_set, - .ndo_change_mtu = be_change_mtu, .ndo_get_stats64 = be_get_stats64, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = be_vlan_add_vid, @@ -5266,6 +5248,10 @@ static void be_netdev_init(struct net_device *netdev) netdev->netdev_ops = &be_netdev_ops; netdev->ethtool_ops = &be_ethtool_ops; + + /* MTU range: 256 - 9000 */ + netdev->min_mtu = BE_MIN_MTU; + netdev->max_mtu = BE_MAX_MTU; } static void be_cleanup(struct be_adapter *adapter) diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index ebe6071..29c05d0 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1349,9 +1349,6 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu) int i, rc; int need_restart = 0; - if (new_mtu < IBMVETH_MIN_MTU) - return -EINVAL; - for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) if (new_mtu_oh <= adapter->rx_buff_pool[i].buff_size) break; @@ -1551,6 +1548,9 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) netdev->hw_features |= NETIF_F_TSO; } + netdev->min_mtu = IBMVETH_MIN_MTU; + netdev->min_mtu = ETH_MAX_MTU; + memcpy(netdev->dev_addr, mac_addr_p, ETH_ALEN); if (firmware_has_feature(FW_FEATURE_CMO)) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index bfe17d9..657206b 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -902,17 +902,6 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p) return 0; } -static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu) -{ - struct ibmvnic_adapter *adapter = netdev_priv(netdev); - - if (new_mtu > adapter->req_mtu || new_mtu < adapter->min_mtu) - return -EINVAL; - - netdev->mtu = new_mtu; - return 0; -} - static void ibmvnic_tx_timeout(struct net_device *dev) { struct ibmvnic_adapter *adapter = netdev_priv(dev); @@ -1029,7 +1018,6 @@ static const struct net_device_ops ibmvnic_netdev_ops = { .ndo_set_rx_mode = ibmvnic_set_multi, .ndo_set_mac_address = ibmvnic_set_mac, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = ibmvnic_change_mtu, .ndo_tx_timeout = ibmvnic_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ibmvnic_netpoll_controller, @@ -2638,10 +2626,12 @@ static void handle_query_cap_rsp(union ibmvnic_crq *crq, break; case MIN_MTU: adapter->min_mtu = be64_to_cpu(crq->query_capability.number); + netdev->min_mtu = adapter->min_mtu; netdev_dbg(netdev, "min_mtu = %lld\n", adapter->min_mtu); break; case MAX_MTU: adapter->max_mtu = be64_to_cpu(crq->query_capability.number); + netdev->max_mtu = adapter->max_mtu; netdev_dbg(netdev, "max_mtu = %lld\n", adapter->max_mtu); break; case MAX_MULTICAST_FILTERS: @@ -3654,6 +3644,8 @@ static void handle_crq_init_rsp(struct work_struct *work) goto task_failed; netdev->real_num_tx_queues = adapter->req_tx_queues; + netdev->min_mtu = adapter->min_mtu; + netdev->max_mtu = adapter->max_mtu; if (adapter->failover) { adapter->failover = false; diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 836ebd8..f9fcab5 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -2357,14 +2357,6 @@ jme_change_mtu(struct net_device *netdev, int new_mtu) { struct jme_adapter *jme = netdev_priv(netdev); - if (new_mtu == jme->old_mtu) - return 0; - - if (((new_mtu + ETH_HLEN) > MAX_ETHERNET_JUMBO_PACKET_SIZE) || - ((new_mtu) < IPV6_MIN_MTU)) - return -EINVAL; - - netdev->mtu = new_mtu; netdev_update_features(netdev); @@ -3063,6 +3055,10 @@ jme_init_one(struct pci_dev *pdev, if (using_dac) netdev->features |= NETIF_F_HIGHDMA; + /* MTU range: 1280 - 9202*/ + netdev->min_mtu = IPV6_MIN_MTU; + netdev->max_mtu = MAX_ETHERNET_JUMBO_PACKET_SIZE - ETH_HLEN; + SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 18e6bb6..68675d8 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2585,9 +2585,6 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) { struct mv643xx_eth_private *mp = netdev_priv(dev); - if (new_mtu < 64 || new_mtu > 9500) - return -EINVAL; - dev->mtu = new_mtu; mv643xx_eth_recalc_skb_size(mp); tx_set_rate(mp, 1000000000, 16777216); @@ -3206,6 +3203,10 @@ static int mv643xx_eth_probe(struct platform_device *pdev) dev->priv_flags |= IFF_UNICAST_FLT; dev->gso_max_segs = MV643XX_MAX_TSO_SEGS; + /* MTU range: 64 - 9500 */ + dev->min_mtu = 64; + dev->max_mtu = 9500; + SET_NETDEV_DEV(dev, &pdev->dev); if (mp->shared->win_protect) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 99805fd..6d8cb22 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2284,6 +2284,9 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC; dev->hw_features |= NETIF_F_HW_TC; + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; + /* Each packet needs to have a Tx header (metadata) on top all other * headers. */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index c0c23e2..66af63d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -994,6 +994,9 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port) dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG | NETIF_F_VLAN_CHALLENGED; + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; + /* Each packet needs to have a Tx header (metadata) on top all other * headers. */ diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c index 569ade6..a34631e 100644 --- a/drivers/net/ethernet/natsemi/ns83820.c +++ b/drivers/net/ethernet/natsemi/ns83820.c @@ -1679,14 +1679,6 @@ static void ns83820_getmac(struct ns83820 *dev, u8 *mac) } } -static int ns83820_change_mtu(struct net_device *ndev, int new_mtu) -{ - if (new_mtu > RX_BUF_SIZE) - return -EINVAL; - ndev->mtu = new_mtu; - return 0; -} - static void ns83820_set_multicast(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); @@ -1933,7 +1925,6 @@ static const struct net_device_ops netdev_ops = { .ndo_stop = ns83820_stop, .ndo_start_xmit = ns83820_hard_start_xmit, .ndo_get_stats = ns83820_get_stats, - .ndo_change_mtu = ns83820_change_mtu, .ndo_set_rx_mode = ns83820_set_multicast, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, @@ -2190,6 +2181,8 @@ static int ns83820_init_one(struct pci_dev *pci_dev, ndev->features |= NETIF_F_SG; ndev->features |= NETIF_F_IP_CSUM; + ndev->min_mtu = 0; + #ifdef NS83820_VLAN_ACCEL_SUPPORT /* We also support hardware vlan acceleration */ ndev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c index 2b10f1b..a996801 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c @@ -987,20 +987,8 @@ int netxen_send_lro_cleanup(struct netxen_adapter *adapter) int netxen_nic_change_mtu(struct net_device *netdev, int mtu) { struct netxen_adapter *adapter = netdev_priv(netdev); - int max_mtu; int rc = 0; - if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) - max_mtu = P3_MAX_MTU; - else - max_mtu = P2_MAX_MTU; - - if (mtu > max_mtu) { - printk(KERN_ERR "%s: mtu > %d bytes unsupported\n", - netdev->name, max_mtu); - return -EINVAL; - } - if (adapter->set_mtu) rc = adapter->set_mtu(adapter, mtu); diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 7a0281a..561fb94 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -1572,6 +1572,13 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->physical_port = i; } + /* MTU range: 0 - 8000 (P2) or 9600 (P3) */ + netdev->min_mtu = 0; + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + netdev->max_mtu = P3_MAX_MTU; + else + netdev->max_mtu = P2_MAX_MTU; + netxen_nic_clear_stats(adapter); err = netxen_setup_intr(adapter); diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index fd4a8e4..1409412 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4788,6 +4788,13 @@ static int qlge_probe(struct pci_dev *pdev, ndev->ethtool_ops = &qlge_ethtool_ops; ndev->watchdog_timeo = 10 * HZ; + /* MTU range: this driver only supports 1500 or 9000, so this only + * filters out values above or below, and we'll rely on + * qlge_change_mtu to make sure only 1500 or 9000 are allowed + */ + ndev->min_mtu = ETH_DATA_LEN; + ndev->max_mtu = 9000; + err = register_netdev(ndev); if (err) { dev_err(&pdev->dev, "net device registration failed.\n"); diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c index 9bf3b2b..e4e1925 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac.c +++ b/drivers/net/ethernet/qualcomm/emac/emac.c @@ -239,15 +239,8 @@ static void emac_rx_mode_set(struct net_device *netdev) /* Change the Maximum Transfer Unit (MTU) */ static int emac_change_mtu(struct net_device *netdev, int new_mtu) { - unsigned int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; struct emac_adapter *adpt = netdev_priv(netdev); - if ((max_frame < EMAC_MIN_ETH_FRAME_SIZE) || - (max_frame > EMAC_MAX_ETH_FRAME_SIZE)) { - netdev_err(adpt->netdev, "error: invalid MTU setting\n"); - return -EINVAL; - } - netif_info(adpt, hw, adpt->netdev, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); @@ -679,6 +672,12 @@ static int emac_probe(struct platform_device *pdev) netdev->vlan_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6; + /* MTU range: 46 - 9194 */ + netdev->min_mtu = EMAC_MIN_ETH_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + netdev->max_mtu = EMAC_MAX_ETH_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + INIT_WORK(&adpt->work_thread, emac_work_thread); /* Initialize queues */ diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index c688d68..c9c8a3b 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1034,9 +1034,6 @@ static int axienet_change_mtu(struct net_device *ndev, int new_mtu) XAE_TRL_SIZE) > lp->rxmem) return -EINVAL; - if ((new_mtu > XAE_JUMBO_MTU) || (new_mtu < 64)) - return -EINVAL; - ndev->mtu = new_mtu; return 0; @@ -1475,6 +1472,10 @@ static int axienet_probe(struct platform_device *pdev) ndev->netdev_ops = &axienet_netdev_ops; ndev->ethtool_ops = &axienet_ethtool_ops; + /* MTU range: 64 - 9000 */ + ndev->min_mtu = 64; + ndev->max_mtu = XAE_JUMBO_MTU; + lp = netdev_priv(ndev); lp->ndev = ndev; lp->dev = &pdev->dev; diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 864d6f2..3e5185e 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -36,6 +36,7 @@ #define ETH_FCS_LEN 4 /* Octets in the FCS */ #define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ +#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU */ /* * These are the defined Ethernet Protocol ID's. -- cgit v1.1 From f77f0aee4da4beff5042b49913e856539f23adff Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:17 -0400 Subject: net: use core MTU range checking in USB NIC drivers usbnet: - Remove stale new_mtu <= 0 check in usbnet.c - Set min_mtu = 0, max_mtu = 65535 (sub-drivers must set their own max_mtu and/or min_mtu as needed) r8152: - Set appropriate max_mtu for different variants (1500 or 9194) lan78xx: - Set max_mtu = 9000 asix_driver: - max_mtu = 16384 for ax88178 variant ax88179: - max_mtu = 4088 cdc_ncm: - max_mtu from hardware cdc-phonet: - min_mtu = 6, max_mtu = 65541 sierra_net: - max_mtu = 1500, call usbnet_change_mtu directly - sierra_net_change_mtu checked for MTU > 1500, then called usbnet_change_mtu, but if we set max_mtu to let the network core handle the range check, then we can simply call usbnet_change_mtu directly smsc75xx: - max_mtu = 9000 CC: netdev@vger.kernel.org CC: Woojung Huh CC: Microchip Linux Driver Support CC: Hayes Wang CC: Oliver Neukum CC: Steve Glendinning Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/usb/asix_devices.c | 4 +--- drivers/net/usb/ax88179_178a.c | 4 +--- drivers/net/usb/cdc-phonet.c | 12 ++---------- drivers/net/usb/cdc_ncm.c | 5 +---- drivers/net/usb/lan78xx.c | 8 +++----- drivers/net/usb/r8152.c | 15 ++++++++++++--- drivers/net/usb/sierra_net.c | 13 ++----------- drivers/net/usb/smsc75xx.c | 4 +--- drivers/net/usb/usbnet.c | 4 ++-- 9 files changed, 25 insertions(+), 44 deletions(-) diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index cce2495..7363cc5 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -1026,9 +1026,6 @@ static int ax88178_change_mtu(struct net_device *net, int new_mtu) netdev_dbg(dev->net, "ax88178_change_mtu() new_mtu=%d\n", new_mtu); - if (new_mtu <= 0 || ll_mtu > 16384) - return -EINVAL; - if ((ll_mtu % dev->maxpacket) == 0) return -EDOM; @@ -1081,6 +1078,7 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->netdev_ops = &ax88178_netdev_ops; dev->net->ethtool_ops = &ax88178_ethtool_ops; + dev->net->max_mtu = 16384 - (dev->net->hard_header_len + 4); /* Blink LEDS so users know driver saw dongle */ asix_sw_reset(dev, 0, 0); diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index e6338c1..36c70d6 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -907,9 +907,6 @@ static int ax88179_change_mtu(struct net_device *net, int new_mtu) struct usbnet *dev = netdev_priv(net); u16 tmp16; - if (new_mtu <= 0 || new_mtu > 4088) - return -EINVAL; - net->mtu = new_mtu; dev->hard_mtu = net->mtu + net->hard_header_len; @@ -1266,6 +1263,7 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->netdev_ops = &ax88179_netdev_ops; dev->net->ethtool_ops = &ax88179_ethtool_ops; dev->net->needed_headroom = 8; + dev->net->max_mtu = 4088; /* Initialize MII structure */ dev->mii.dev = dev->net; diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index ff2270e..eb52de8 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -276,21 +276,11 @@ static int usbpn_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -ENOIOCTLCMD; } -static int usbpn_set_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - static const struct net_device_ops usbpn_ops = { .ndo_open = usbpn_open, .ndo_stop = usbpn_close, .ndo_start_xmit = usbpn_xmit, .ndo_do_ioctl = usbpn_ioctl, - .ndo_change_mtu = usbpn_set_mtu, }; static void usbpn_setup(struct net_device *dev) @@ -301,6 +291,8 @@ static void usbpn_setup(struct net_device *dev) dev->type = ARPHRD_PHONET; dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->mtu = PHONET_MAX_MTU; + dev->min_mtu = PHONET_MIN_MTU; + dev->max_mtu = PHONET_MAX_MTU; dev->hard_header_len = 1; dev->dev_addr[0] = PN_MEDIA_USB; dev->addr_len = 1; diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 877c951..7141817 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -740,10 +740,6 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) int cdc_ncm_change_mtu(struct net_device *net, int new_mtu) { struct usbnet *dev = netdev_priv(net); - int maxmtu = cdc_ncm_max_dgram_size(dev) - cdc_ncm_eth_hlen(dev); - - if (new_mtu <= 0 || new_mtu > maxmtu) - return -EINVAL; net->mtu = new_mtu; cdc_ncm_set_dgram_size(dev, new_mtu + cdc_ncm_eth_hlen(dev)); @@ -909,6 +905,7 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ /* must handle MTU changes */ dev->net->netdev_ops = &cdc_ncm_netdev_ops; + dev->net->max_mtu = cdc_ncm_max_dgram_size(dev) - cdc_ncm_eth_hlen(dev); return 0; diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 13f033c..c4e748e 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1980,11 +1980,6 @@ static int lan78xx_change_mtu(struct net_device *netdev, int new_mtu) int old_rx_urb_size = dev->rx_urb_size; int ret; - if (new_mtu > MAX_SINGLE_PACKET_SIZE) - return -EINVAL; - - if (new_mtu <= 0) - return -EINVAL; /* no second zero-length packet read wanted after mtu-sized packets */ if ((ll_mtu % dev->maxpacket) == 0) return -EDOM; @@ -3388,6 +3383,9 @@ static int lan78xx_probe(struct usb_interface *intf, if (netdev->mtu > (dev->hard_mtu - netdev->hard_header_len)) netdev->mtu = dev->hard_mtu - netdev->hard_header_len; + /* MTU range: 68 - 9000 */ + netdev->max_mtu = MAX_SINGLE_PACKET_SIZE; + dev->ep_blkin = (intf->cur_altsetting)->endpoint + 0; dev->ep_blkout = (intf->cur_altsetting)->endpoint + 1; dev->ep_intr = (intf->cur_altsetting)->endpoint + 2; diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 8d6e13c..4213c28 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -4119,9 +4119,6 @@ static int rtl8152_change_mtu(struct net_device *dev, int new_mtu) break; } - if (new_mtu < 68 || new_mtu > RTL8153_MAX_MTU) - return -EINVAL; - ret = usb_autopm_get_interface(tp->intf); if (ret < 0) return ret; @@ -4311,6 +4308,18 @@ static int rtl8152_probe(struct usb_interface *intf, netdev->ethtool_ops = &ops; netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); + /* MTU range: 68 - 1500 or 9194 */ + netdev->min_mtu = ETH_MIN_MTU; + switch (tp->version) { + case RTL_VER_01: + case RTL_VER_02: + netdev->max_mtu = ETH_DATA_LEN; + break; + default: + netdev->max_mtu = RTL8153_MAX_MTU; + break; + } + tp->mii.dev = netdev; tp->mii.mdio_read = read_mii_word; tp->mii.mdio_write = write_mii_word; diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index a2515887..12071f1 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -165,7 +165,6 @@ struct lsi_umts { /* Forward definitions */ static void sierra_sync_timer(unsigned long syncdata); -static int sierra_net_change_mtu(struct net_device *net, int new_mtu); /* Our own net device operations structure */ static const struct net_device_ops sierra_net_device_ops = { @@ -173,7 +172,7 @@ static const struct net_device_ops sierra_net_device_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, - .ndo_change_mtu = sierra_net_change_mtu, + .ndo_change_mtu = usbnet_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -622,15 +621,6 @@ static const struct ethtool_ops sierra_net_ethtool_ops = { .nway_reset = usbnet_nway_reset, }; -/* MTU can not be more than 1500 bytes, enforce it. */ -static int sierra_net_change_mtu(struct net_device *net, int new_mtu) -{ - if (new_mtu > SIERRA_NET_MAX_SUPPORTED_MTU) - return -EINVAL; - - return usbnet_change_mtu(net, new_mtu); -} - static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap) { int result = 0; @@ -720,6 +710,7 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->hard_header_len += SIERRA_NET_HIP_EXT_HDR_LEN; dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; + dev->net->max_mtu = SIERRA_NET_MAX_SUPPORTED_MTU; /* Set up the netdev */ dev->net->flags |= IFF_NOARP; diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 9af9799..0b17b40 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -925,9 +925,6 @@ static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu) struct usbnet *dev = netdev_priv(netdev); int ret; - if (new_mtu > MAX_SINGLE_PACKET_SIZE) - return -EINVAL; - ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu + ETH_HLEN); if (ret < 0) { netdev_warn(dev->net, "Failed to set mac rx frame length\n"); @@ -1448,6 +1445,7 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->flags |= IFF_MULTICAST; dev->net->hard_header_len += SMSC75XX_TX_OVERHEAD; dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; + dev->net->max_mtu = MAX_SINGLE_PACKET_SIZE; return 0; } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index d5071e3..3de65ea 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -384,8 +384,6 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu) int old_hard_mtu = dev->hard_mtu; int old_rx_urb_size = dev->rx_urb_size; - if (new_mtu <= 0) - return -EINVAL; // no second zero-length packet read wanted after mtu-sized packets if ((ll_mtu % dev->maxpacket) == 0) return -EDOM; @@ -1669,6 +1667,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) * bind() should set rx_urb_size in that case. */ dev->hard_mtu = net->mtu + net->hard_header_len; + net->min_mtu = 0; + net->max_mtu = ETH_MAX_MTU; net->netdev_ops = &usbnet_netdev_ops; net->watchdog_timeo = TX_TIMEOUT_JIFFIES; -- cgit v1.1 From 9c22b4a34eddbaa5b5243c8cd27e31aa36e676e1 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:18 -0400 Subject: net: use core MTU range checking in wireless drivers - set max_mtu in wil6210 driver - set max_mtu in atmel driver - set min/max_mtu in cisco airo driver, remove airo_change_mtu - set min/max_mtu in ipw2100/ipw2200 drivers, remove libipw_change_mtu - set min/max_mtu in p80211netdev, remove wlan_change_mtu - set min/max_mtu in net/mac80211/iface.c and remove ieee80211_change_mtu - set min/max_mtu in wimax/i2400m and remove i2400m_change_mtu - set min/max_mtu in intersil/hostap and remove prism2_change_mtu - set min/max_mtu in intersil/orinoco - set min/max_mtu in tty/n_gsm and remove gsm_change_mtu CC: netdev@vger.kernel.org CC: linux-wireless@vger.kernel.org CC: Maya Erez CC: Simon Kelley CC: Stanislav Yakovlev CC: Johannes Berg CC: Inaky Perez-Gonzalez Signed-off-by: Jarod Wilson Acked-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/wimax/i2400m/netdev.c | 22 ++-------------------- drivers/net/wireless/ath/wil6210/netdev.c | 17 +---------------- drivers/net/wireless/atmel/atmel.c | 13 ++++--------- drivers/net/wireless/cisco/airo.c | 14 +++----------- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 3 ++- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 8 ++++++-- drivers/net/wireless/intel/ipw2x00/libipw.h | 1 - drivers/net/wireless/intel/ipw2x00/libipw_module.c | 9 --------- drivers/net/wireless/intersil/hostap/hostap_main.c | 15 ++------------- drivers/net/wireless/intersil/orinoco/main.c | 6 +++--- drivers/staging/wlan-ng/p80211netdev.c | 18 +++++------------- drivers/tty/n_gsm.c | 12 ++---------- net/mac80211/iface.c | 15 ++++----------- 13 files changed, 34 insertions(+), 119 deletions(-) diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index bb74f4b..7f64e74 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c @@ -395,25 +395,6 @@ drop: static -int i2400m_change_mtu(struct net_device *net_dev, int new_mtu) -{ - int result; - struct i2400m *i2400m = net_dev_to_i2400m(net_dev); - struct device *dev = i2400m_dev(i2400m); - - if (new_mtu >= I2400M_MAX_MTU) { - dev_err(dev, "Cannot change MTU to %d (max is %d)\n", - new_mtu, I2400M_MAX_MTU); - result = -EINVAL; - } else { - net_dev->mtu = new_mtu; - result = 0; - } - return result; -} - - -static void i2400m_tx_timeout(struct net_device *net_dev) { /* @@ -590,7 +571,6 @@ static const struct net_device_ops i2400m_netdev_ops = { .ndo_stop = i2400m_stop, .ndo_start_xmit = i2400m_hard_start_xmit, .ndo_tx_timeout = i2400m_tx_timeout, - .ndo_change_mtu = i2400m_change_mtu, }; static void i2400m_get_drvinfo(struct net_device *net_dev, @@ -621,6 +601,8 @@ void i2400m_netdev_setup(struct net_device *net_dev) d_fnstart(3, NULL, "(net_dev %p)\n", net_dev); ether_setup(net_dev); net_dev->mtu = I2400M_MAX_MTU; + net_dev->min_mtu = 0; + net_dev->max_mtu = I2400M_MAX_MTU; net_dev->tx_queue_len = I2400M_TX_QLEN; net_dev->features = NETIF_F_VLAN_CHALLENGED diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 61de5e9..d18372c 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -41,21 +41,6 @@ static int wil_stop(struct net_device *ndev) return wil_down(wil); } -static int wil_change_mtu(struct net_device *ndev, int new_mtu) -{ - struct wil6210_priv *wil = ndev_to_wil(ndev); - - if (new_mtu < 68 || new_mtu > mtu_max) { - wil_err(wil, "invalid MTU %d\n", new_mtu); - return -EINVAL; - } - - wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu); - ndev->mtu = new_mtu; - - return 0; -} - static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) { struct wil6210_priv *wil = ndev_to_wil(ndev); @@ -69,7 +54,6 @@ static const struct net_device_ops wil_netdev_ops = { .ndo_start_xmit = wil_start_xmit, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = wil_change_mtu, .ndo_do_ioctl = wil_do_ioctl, }; @@ -126,6 +110,7 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) static void wil_dev_setup(struct net_device *dev) { ether_setup(dev); + dev->max_mtu = mtu_max; dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT; } diff --git a/drivers/net/wireless/atmel/atmel.c b/drivers/net/wireless/atmel/atmel.c index bf2e9a0..eb92d5a 100644 --- a/drivers/net/wireless/atmel/atmel.c +++ b/drivers/net/wireless/atmel/atmel.c @@ -1295,14 +1295,6 @@ static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) return &priv->wstats; } -static int atmel_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > 2312)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static int atmel_set_mac_address(struct net_device *dev, void *p) { struct sockaddr *addr = p; @@ -1506,7 +1498,6 @@ static const struct file_operations atmel_proc_fops = { static const struct net_device_ops atmel_netdev_ops = { .ndo_open = atmel_open, .ndo_stop = atmel_close, - .ndo_change_mtu = atmel_change_mtu, .ndo_set_mac_address = atmel_set_mac_address, .ndo_start_xmit = start_tx, .ndo_do_ioctl = atmel_ioctl, @@ -1600,6 +1591,10 @@ struct net_device *init_atmel_card(unsigned short irq, unsigned long port, dev->irq = irq; dev->base_addr = port; + /* MTU range: 68 - 2312 */ + dev->min_mtu = 68; + dev->max_mtu = MAX_WIRELESS_BODY - ETH_FCS_LEN; + SET_NETDEV_DEV(dev, sys_dev); if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 69b826d..4b04045 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -2329,14 +2329,6 @@ static int airo_set_mac_address(struct net_device *dev, void *p) return 0; } -static int airo_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > 2400)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static LIST_HEAD(airo_devices); static void add_airo_dev(struct airo_info *ai) @@ -2656,7 +2648,6 @@ static const struct net_device_ops airo11_netdev_ops = { .ndo_get_stats = airo_get_stats, .ndo_set_mac_address = airo_set_mac_address, .ndo_do_ioctl = airo_ioctl, - .ndo_change_mtu = airo_change_mtu, }; static void wifi_setup(struct net_device *dev) @@ -2668,6 +2659,8 @@ static void wifi_setup(struct net_device *dev) dev->type = ARPHRD_IEEE80211; dev->hard_header_len = ETH_HLEN; dev->mtu = AIRO_DEF_MTU; + dev->min_mtu = 68; + dev->max_mtu = MIC_MSGLEN_MAX; dev->addr_len = ETH_ALEN; dev->tx_queue_len = 100; @@ -2754,7 +2747,6 @@ static const struct net_device_ops airo_netdev_ops = { .ndo_set_rx_mode = airo_set_multicast_list, .ndo_set_mac_address = airo_set_mac_address, .ndo_do_ioctl = airo_ioctl, - .ndo_change_mtu = airo_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -2766,7 +2758,6 @@ static const struct net_device_ops mpi_netdev_ops = { .ndo_set_rx_mode = airo_set_multicast_list, .ndo_set_mac_address = airo_set_mac_address, .ndo_do_ioctl = airo_ioctl, - .ndo_change_mtu = airo_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -2822,6 +2813,7 @@ static struct net_device *_init_airo_card( unsigned short irq, int port, dev->irq = irq; dev->base_addr = port; dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->max_mtu = MIC_MSGLEN_MAX; SET_NETDEV_DEV(dev, dmdev); diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index bfa542c..6417609 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -6035,7 +6035,6 @@ static const struct net_device_ops ipw2100_netdev_ops = { .ndo_open = ipw2100_open, .ndo_stop = ipw2100_close, .ndo_start_xmit = libipw_xmit, - .ndo_change_mtu = libipw_change_mtu, .ndo_tx_timeout = ipw2100_tx_timeout, .ndo_set_mac_address = ipw2100_set_address, .ndo_validate_addr = eth_validate_addr, @@ -6071,6 +6070,8 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, dev->wireless_data = &priv->wireless_data; dev->watchdog_timeo = 3 * HZ; dev->irq = 0; + dev->min_mtu = 68; + dev->max_mtu = LIBIPW_DATA_LEN; /* NOTE: We don't use the wireless_handlers hook * in dev as the system will start throwing WX requests diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index bfd6861..ef9af8a 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -11561,7 +11561,6 @@ static const struct net_device_ops ipw_prom_netdev_ops = { .ndo_open = ipw_prom_open, .ndo_stop = ipw_prom_stop, .ndo_start_xmit = ipw_prom_hard_start_xmit, - .ndo_change_mtu = libipw_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -11587,6 +11586,9 @@ static int ipw_prom_alloc(struct ipw_priv *priv) priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP; priv->prom_net_dev->netdev_ops = &ipw_prom_netdev_ops; + priv->prom_net_dev->min_mtu = 68; + priv->prom_net_dev->max_mtu = LIBIPW_DATA_LEN; + priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR; SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev); @@ -11619,7 +11621,6 @@ static const struct net_device_ops ipw_netdev_ops = { .ndo_set_rx_mode = ipw_net_set_multicast_list, .ndo_set_mac_address = ipw_net_set_mac_address, .ndo_start_xmit = libipw_xmit, - .ndo_change_mtu = libipw_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -11729,6 +11730,9 @@ static int ipw_pci_probe(struct pci_dev *pdev, net_dev->wireless_handlers = &ipw_wx_handler_def; net_dev->ethtool_ops = &ipw_ethtool_ops; + net_dev->min_mtu = 68; + net_dev->max_mtu = LIBIPW_DATA_LEN; + err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group); if (err) { IPW_ERROR("failed to create sysfs device attributes\n"); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw.h b/drivers/net/wireless/intel/ipw2x00/libipw.h index b057161..b513551 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw.h +++ b/drivers/net/wireless/intel/ipw2x00/libipw.h @@ -948,7 +948,6 @@ static inline int libipw_is_cck_rate(u8 rate) /* libipw.c */ void free_libipw(struct net_device *dev, int monitor); struct net_device *alloc_libipw(int sizeof_priv, int monitor); -int libipw_change_mtu(struct net_device *dev, int new_mtu); void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c index 60f2874..2332075 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw_module.c +++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c @@ -118,15 +118,6 @@ static void libipw_networks_initialize(struct libipw_device *ieee) &ieee->network_free_list); } -int libipw_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > LIBIPW_DATA_LEN)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL(libipw_change_mtu); - struct net_device *alloc_libipw(int sizeof_priv, int monitor) { struct libipw_device *ieee; diff --git a/drivers/net/wireless/intersil/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c index 80d4228..1a16b8c 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_main.c +++ b/drivers/net/wireless/intersil/hostap/hostap_main.c @@ -765,16 +765,6 @@ static void hostap_set_multicast_list(struct net_device *dev) } -static int prism2_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - - static void prism2_tx_timeout(struct net_device *dev) { struct hostap_interface *iface; @@ -813,7 +803,6 @@ static const struct net_device_ops hostap_netdev_ops = { .ndo_do_ioctl = hostap_ioctl, .ndo_set_mac_address = prism2_set_mac_address, .ndo_set_rx_mode = hostap_set_multicast_list, - .ndo_change_mtu = prism2_change_mtu, .ndo_tx_timeout = prism2_tx_timeout, .ndo_validate_addr = eth_validate_addr, }; @@ -826,7 +815,6 @@ static const struct net_device_ops hostap_mgmt_netdev_ops = { .ndo_do_ioctl = hostap_ioctl, .ndo_set_mac_address = prism2_set_mac_address, .ndo_set_rx_mode = hostap_set_multicast_list, - .ndo_change_mtu = prism2_change_mtu, .ndo_tx_timeout = prism2_tx_timeout, .ndo_validate_addr = eth_validate_addr, }; @@ -839,7 +827,6 @@ static const struct net_device_ops hostap_master_ops = { .ndo_do_ioctl = hostap_ioctl, .ndo_set_mac_address = prism2_set_mac_address, .ndo_set_rx_mode = hostap_set_multicast_list, - .ndo_change_mtu = prism2_change_mtu, .ndo_tx_timeout = prism2_tx_timeout, .ndo_validate_addr = eth_validate_addr, }; @@ -851,6 +838,8 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local, iface = netdev_priv(dev); ether_setup(dev); + dev->min_mtu = PRISM2_MIN_MTU; + dev->max_mtu = PRISM2_MAX_MTU; dev->priv_flags &= ~IFF_TX_SKB_SHARING; /* kernel callbacks */ diff --git a/drivers/net/wireless/intersil/orinoco/main.c b/drivers/net/wireless/intersil/orinoco/main.c index 7afe200..9d96b7c 100644 --- a/drivers/net/wireless/intersil/orinoco/main.c +++ b/drivers/net/wireless/intersil/orinoco/main.c @@ -322,9 +322,6 @@ int orinoco_change_mtu(struct net_device *dev, int new_mtu) { struct orinoco_private *priv = ndev_priv(dev); - if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU)) - return -EINVAL; - /* MTU + encapsulation + header length */ if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) > (priv->nicbuf_size - ETH_HLEN)) @@ -2288,6 +2285,9 @@ int orinoco_if_add(struct orinoco_private *priv, dev->base_addr = base_addr; dev->irq = irq; + dev->min_mtu = ORINOCO_MIN_MTU; + dev->max_mtu = ORINOCO_MAX_MTU; + SET_NETDEV_DEV(dev, priv->dev); ret = register_netdev(dev); if (ret) diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c index 825a63a..4762d38 100644 --- a/drivers/staging/wlan-ng/p80211netdev.c +++ b/drivers/staging/wlan-ng/p80211netdev.c @@ -669,18 +669,6 @@ static int p80211knetdev_set_mac_address(struct net_device *dev, void *addr) return result; } -static int wlan_change_mtu(struct net_device *dev, int new_mtu) -{ - /* 2312 is max 802.11 payload, 20 is overhead, (ether + llc +snap) - and another 8 for wep. */ - if ((new_mtu < 68) || (new_mtu > (2312 - 20 - 8))) - return -EINVAL; - - dev->mtu = new_mtu; - - return 0; -} - static const struct net_device_ops p80211_netdev_ops = { .ndo_init = p80211knetdev_init, .ndo_open = p80211knetdev_open, @@ -690,7 +678,6 @@ static const struct net_device_ops p80211_netdev_ops = { .ndo_do_ioctl = p80211knetdev_do_ioctl, .ndo_set_mac_address = p80211knetdev_set_mac_address, .ndo_tx_timeout = p80211knetdev_tx_timeout, - .ndo_change_mtu = wlan_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -756,6 +743,11 @@ int wlan_setup(struct wlandevice *wlandev, struct device *physdev) wdev->wiphy = wiphy; wdev->iftype = NL80211_IFTYPE_STATION; netdev->ieee80211_ptr = wdev; + netdev->min_mtu = 68; + /* 2312 is max 802.11 payload, 20 is overhead, + * (ether + llc + snap) and another 8 for wep. + */ + netdev->max_mtu = (2312 - 20 - 8); netif_stop_queue(netdev); netif_carrier_off(netdev); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 54cab59..f3932ba 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2711,15 +2711,6 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, return; } -static int gsm_change_mtu(struct net_device *net, int new_mtu) -{ - struct gsm_mux_net *mux_net = netdev_priv(net); - if ((new_mtu < 8) || (new_mtu > mux_net->dlci->gsm->mtu)) - return -EINVAL; - net->mtu = new_mtu; - return 0; -} - static void gsm_mux_net_init(struct net_device *net) { static const struct net_device_ops gsm_netdev_ops = { @@ -2728,7 +2719,6 @@ static void gsm_mux_net_init(struct net_device *net) .ndo_start_xmit = gsm_mux_net_start_xmit, .ndo_tx_timeout = gsm_mux_net_tx_timeout, .ndo_get_stats = gsm_mux_net_get_stats, - .ndo_change_mtu = gsm_change_mtu, }; net->netdev_ops = &gsm_netdev_ops; @@ -2787,6 +2777,8 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) return -ENOMEM; } net->mtu = dlci->gsm->mtu; + net->min_mtu = 8; + net->max_mtu = dlci->gsm->mtu; mux_net = netdev_priv(net); mux_net->dlci = dlci; kref_init(&mux_net->ref); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 638ec07..73e6a8f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -150,15 +150,6 @@ void ieee80211_recalc_idle(struct ieee80211_local *local) ieee80211_hw_config(local, change); } -static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr, bool check_dup) { @@ -1166,7 +1157,6 @@ static const struct net_device_ops ieee80211_dataif_ops = { .ndo_uninit = ieee80211_uninit, .ndo_start_xmit = ieee80211_subif_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, - .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = ieee80211_change_mac, .ndo_select_queue = ieee80211_netdev_select_queue, .ndo_get_stats64 = ieee80211_get_stats64, @@ -1200,7 +1190,6 @@ static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_uninit = ieee80211_uninit, .ndo_start_xmit = ieee80211_monitor_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, - .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = ieee80211_change_mac, .ndo_select_queue = ieee80211_monitor_select_queue, .ndo_get_stats64 = ieee80211_get_stats64, @@ -1884,6 +1873,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); + /* MTU range: 256 - 2304 */ + ndev->min_mtu = 256; + ndev->max_mtu = IEEE80211_MAX_DATA_LEN; + ret = register_netdevice(ndev); if (ret) { ieee80211_if_free(ndev); -- cgit v1.1 From 8b6b4135e4fb2b537f33b811c13f77bee25ca8d3 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:19 -0400 Subject: net: use core MTU range checking in WAN drivers - set min/max_mtu in all hdlc drivers, remove hdlc_change_mtu - sent max_mtu in lec driver, remove lec_change_mtu - set min/max_mtu in x25_asy driver CC: netdev@vger.kernel.org CC: Krzysztof Halasa CC: Krzysztof Halasa CC: Jan "Yenya" Kasprzak CC: Francois Romieu CC: Kevin Curtis CC: Zhao Qiang Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/char/pcmcia/synclink_cs.c | 1 - drivers/net/wan/c101.c | 1 - drivers/net/wan/cosa.c | 1 - drivers/net/wan/dscc4.c | 1 - drivers/net/wan/farsync.c | 1 - drivers/net/wan/fsl_ucc_hdlc.c | 1 - drivers/net/wan/hdlc.c | 11 ++--------- drivers/net/wan/hdlc_fr.c | 3 ++- drivers/net/wan/hostess_sv11.c | 1 - drivers/net/wan/ixp4xx_hss.c | 1 - drivers/net/wan/lmc/lmc_main.c | 1 - drivers/net/wan/n2.c | 1 - drivers/net/wan/pc300too.c | 1 - drivers/net/wan/pci200syn.c | 1 - drivers/net/wan/sealevel.c | 1 - drivers/net/wan/wanxl.c | 1 - drivers/net/wan/x25_asy.c | 5 ++--- drivers/tty/synclink.c | 1 - drivers/tty/synclink_gt.c | 1 - drivers/tty/synclinkmp.c | 1 - include/linux/hdlc.h | 2 -- net/atm/lec.c | 11 +---------- 22 files changed, 7 insertions(+), 42 deletions(-) diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index d28922d..a7dd5f4 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -4248,7 +4248,6 @@ static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size) static const struct net_device_ops hdlcdev_ops = { .ndo_open = hdlcdev_open, .ndo_stop = hdlcdev_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = hdlcdev_ioctl, .ndo_tx_timeout = hdlcdev_tx_timeout, diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index 09a5075..2371e07 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -302,7 +302,6 @@ static void c101_destroy_card(card_t *card) static const struct net_device_ops c101_ops = { .ndo_open = c101_open, .ndo_stop = c101_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = c101_ioctl, }; diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index b87fe0a..087eb26 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -432,7 +432,6 @@ module_exit(cosa_exit); static const struct net_device_ops cosa_ops = { .ndo_open = cosa_net_open, .ndo_stop = cosa_net_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = cosa_net_ioctl, .ndo_tx_timeout = cosa_net_timeout, diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 6292259..7351e54 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -887,7 +887,6 @@ static inline int dscc4_set_quartz(struct dscc4_dev_priv *dpriv, int hz) static const struct net_device_ops dscc4_ops = { .ndo_open = dscc4_open, .ndo_stop = dscc4_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = dscc4_ioctl, .ndo_tx_timeout = dscc4_tx_timeout, diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 3c9cbf9..03696d3 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -2394,7 +2394,6 @@ fst_init_card(struct fst_card_info *card) static const struct net_device_ops fst_ops = { .ndo_open = fst_open, .ndo_stop = fst_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = fst_ioctl, .ndo_tx_timeout = fst_tx_timeout, diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 6564753..e38ce4d 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -992,7 +992,6 @@ static const struct dev_pm_ops uhdlc_pm_ops = { static const struct net_device_ops uhdlc_ops = { .ndo_open = uhdlc_open, .ndo_stop = uhdlc_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = uhdlc_ioctl, }; diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 9bd4aa8..7221a53 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -46,14 +46,6 @@ static const char* version = "HDLC support module revision 1.22"; static struct hdlc_proto *first_proto; -int hdlc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p, struct net_device *orig_dev) { @@ -237,6 +229,8 @@ static void hdlc_setup_dev(struct net_device *dev) dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->priv_flags = IFF_WAN_HDLC; dev->mtu = HDLC_MAX_MTU; + dev->min_mtu = 68; + dev->max_mtu = HDLC_MAX_MTU; dev->type = ARPHRD_RAWHDLC; dev->hard_header_len = 16; dev->addr_len = 0; @@ -353,7 +347,6 @@ MODULE_AUTHOR("Krzysztof Halasa "); MODULE_DESCRIPTION("HDLC support module"); MODULE_LICENSE("GPL v2"); -EXPORT_SYMBOL(hdlc_change_mtu); EXPORT_SYMBOL(hdlc_start_xmit); EXPORT_SYMBOL(hdlc_open); EXPORT_SYMBOL(hdlc_close); diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index b6e0cfb..eb91528 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -1053,7 +1053,6 @@ static void pvc_setup(struct net_device *dev) static const struct net_device_ops pvc_ops = { .ndo_open = pvc_open, .ndo_stop = pvc_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = pvc_xmit, .ndo_do_ioctl = pvc_ioctl, }; @@ -1096,6 +1095,8 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) } dev->netdev_ops = &pvc_ops; dev->mtu = HDLC_MAX_MTU; + dev->min_mtu = 68; + dev->max_mtu = HDLC_MAX_MTU; dev->priv_flags |= IFF_NO_QUEUE; dev->ml_priv = pvc; diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index 3d74166..dd6bb33 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -180,7 +180,6 @@ static int hostess_attach(struct net_device *dev, unsigned short encoding, static const struct net_device_ops hostess_ops = { .ndo_open = hostess_open, .ndo_stop = hostess_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = hostess_ioctl, }; diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index e7bbdb7..6a505c2 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -1321,7 +1321,6 @@ static int hss_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static const struct net_device_ops hss_hdlc_ops = { .ndo_open = hss_hdlc_open, .ndo_stop = hss_hdlc_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = hss_hdlc_ioctl, }; diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index 299140c..001b779 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -808,7 +808,6 @@ static int lmc_attach(struct net_device *dev, unsigned short encoding, static const struct net_device_ops lmc_ops = { .ndo_open = lmc_open, .ndo_stop = lmc_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = lmc_ioctl, .ndo_tx_timeout = lmc_driver_timeout, diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c index 315bf09..c8f4517 100644 --- a/drivers/net/wan/n2.c +++ b/drivers/net/wan/n2.c @@ -330,7 +330,6 @@ static void n2_destroy_card(card_t *card) static const struct net_device_ops n2_ops = { .ndo_open = n2_open, .ndo_stop = n2_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = n2_ioctl, }; diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c index db36385..e1dd1ec 100644 --- a/drivers/net/wan/pc300too.c +++ b/drivers/net/wan/pc300too.c @@ -291,7 +291,6 @@ static void pc300_pci_remove_one(struct pci_dev *pdev) static const struct net_device_ops pc300_ops = { .ndo_open = pc300_open, .ndo_stop = pc300_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = pc300_ioctl, }; diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c index e845562..4e437c5 100644 --- a/drivers/net/wan/pci200syn.c +++ b/drivers/net/wan/pci200syn.c @@ -270,7 +270,6 @@ static void pci200_pci_remove_one(struct pci_dev *pdev) static const struct net_device_ops pci200_ops = { .ndo_open = pci200_open, .ndo_stop = pci200_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = pci200_ioctl, }; diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c index 27860b4..fbb5aa2 100644 --- a/drivers/net/wan/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -174,7 +174,6 @@ static int sealevel_attach(struct net_device *dev, unsigned short encoding, static const struct net_device_ops sealevel_ops = { .ndo_open = sealevel_open, .ndo_stop = sealevel_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = sealevel_ioctl, }; diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index a20d688..0c73175 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -551,7 +551,6 @@ static void wanxl_pci_remove_one(struct pci_dev *pdev) static const struct net_device_ops wanxl_ops = { .ndo_open = wanxl_open, .ndo_stop = wanxl_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = wanxl_ioctl, .ndo_get_stats = wanxl_get_stats, diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 1bc5e93..878b05d 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -124,9 +124,6 @@ static int x25_asy_change_mtu(struct net_device *dev, int newmtu) unsigned char *xbuff, *rbuff; int len; - if (newmtu > 65534) - return -EINVAL; - len = 2 * newmtu; xbuff = kmalloc(len + 4, GFP_ATOMIC); rbuff = kmalloc(len + 4, GFP_ATOMIC); @@ -751,6 +748,8 @@ static void x25_asy_setup(struct net_device *dev) */ dev->mtu = SL_MTU; + dev->min_mtu = 0; + dev->max_mtu = 65534; dev->netdev_ops = &x25_asy_netdev_ops; dev->watchdog_timeo = HZ*20; dev->hard_header_len = 0; diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index c13e27e..415885c 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -7973,7 +7973,6 @@ static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) static const struct net_device_ops hdlcdev_ops = { .ndo_open = hdlcdev_open, .ndo_stop = hdlcdev_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = hdlcdev_ioctl, .ndo_tx_timeout = hdlcdev_tx_timeout, diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 7aca2d4..8267bcf 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -1768,7 +1768,6 @@ static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) static const struct net_device_ops hdlcdev_ops = { .ndo_open = hdlcdev_open, .ndo_stop = hdlcdev_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = hdlcdev_ioctl, .ndo_tx_timeout = hdlcdev_tx_timeout, diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index dec1565..d66620f 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -1887,7 +1887,6 @@ static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size) static const struct net_device_ops hdlcdev_ops = { .ndo_open = hdlcdev_open, .ndo_stop = hdlcdev_close, - .ndo_change_mtu = hdlc_change_mtu, .ndo_start_xmit = hdlc_start_xmit, .ndo_do_ioctl = hdlcdev_ioctl, .ndo_tx_timeout = hdlcdev_tx_timeout, diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index e31bcd4..97585d9 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -93,8 +93,6 @@ static __inline__ void debug_frame(const struct sk_buff *skb) int hdlc_open(struct net_device *dev); /* Must be called by hardware driver when HDLC device is being closed */ void hdlc_close(struct net_device *dev); -/* May be used by hardware driver */ -int hdlc_change_mtu(struct net_device *dev, int new_mtu); /* Must be pointed to by hw driver's dev->netdev_ops->ndo_start_xmit */ netdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev); diff --git a/net/atm/lec.c b/net/atm/lec.c index 5d26938..779b3fa 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -544,15 +544,6 @@ send_to_lecd(struct lec_priv *priv, atmlec_msg_type type, return 0; } -/* shamelessly stolen from drivers/net/net_init.c */ -static int lec_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > 18190)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static void lec_set_multicast_list(struct net_device *dev) { /* @@ -565,7 +556,6 @@ static const struct net_device_ops lec_netdev_ops = { .ndo_open = lec_open, .ndo_stop = lec_close, .ndo_start_xmit = lec_start_xmit, - .ndo_change_mtu = lec_change_mtu, .ndo_tx_timeout = lec_tx_timeout, .ndo_set_rx_mode = lec_set_multicast_list, }; @@ -742,6 +732,7 @@ static int lecd_attach(struct atm_vcc *vcc, int arg) if (!dev_lec[i]) return -ENOMEM; dev_lec[i]->netdev_ops = &lec_netdev_ops; + dev_lec[i]->max_mtu = 18190; snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i); if (register_netdev(dev_lec[i])) { free_netdev(dev_lec[i]); -- cgit v1.1 From 91572088e3fdbf4fe31cf397926d8b890fdb3237 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:20 -0400 Subject: net: use core MTU range checking in core net infra geneve: - Merge __geneve_change_mtu back into geneve_change_mtu, set max_mtu - This one isn't quite as straight-forward as others, could use some closer inspection and testing macvlan: - set min/max_mtu tun: - set min/max_mtu, remove tun_net_change_mtu vxlan: - Merge __vxlan_change_mtu back into vxlan_change_mtu - Set max_mtu to IP_MAX_MTU and retain dynamic MTU range checks in change_mtu function - This one is also not as straight-forward and could use closer inspection and testing from vxlan folks bridge: - set max_mtu of IP_MAX_MTU and retain dynamic MTU range checks in change_mtu function openvswitch: - set min/max_mtu, remove internal_dev_change_mtu - note: max_mtu wasn't checked previously, it's been set to 65535, which is the largest possible size supported sch_teql: - set min/max_mtu (note: max_mtu previously unchecked, used max of 65535) macsec: - min_mtu = 0, max_mtu = 65535 macvlan: - min_mtu = 0, max_mtu = 65535 ntb_netdev: - min_mtu = 0, max_mtu = 65535 veth: - min_mtu = 68, max_mtu = 65535 8021q: - min_mtu = 0, max_mtu = 65535 CC: netdev@vger.kernel.org CC: Nicolas Dichtel CC: Hannes Frederic Sowa CC: Tom Herbert CC: Daniel Borkmann CC: Alexander Duyck CC: Paolo Abeni CC: Jiri Benc CC: WANG Cong CC: Roopa Prabhu CC: Pravin B Shelar CC: Sabrina Dubroca CC: Patrick McHardy CC: Stephen Hemminger CC: Pravin Shelar CC: Maxim Krasnyansky Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/geneve.c | 48 +++++++++++---------------- drivers/net/macsec.c | 2 ++ drivers/net/macvlan.c | 8 ++++- drivers/net/ntb_netdev.c | 3 ++ drivers/net/tun.c | 20 ++++------- drivers/net/veth.c | 17 ++-------- drivers/net/vxlan.c | 64 +++++++++++++++++++----------------- net/8021q/vlan_dev.c | 3 ++ net/bridge/br_device.c | 3 +- net/openvswitch/vport-internal_dev.c | 10 ------ net/sched/sch_teql.c | 5 ++- 11 files changed, 81 insertions(+), 102 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 3c20e87..752bcaa 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1034,39 +1034,18 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev) return geneve_xmit_skb(skb, dev, info); } -static int __geneve_change_mtu(struct net_device *dev, int new_mtu, bool strict) +static int geneve_change_mtu(struct net_device *dev, int new_mtu) { - struct geneve_dev *geneve = netdev_priv(dev); - /* The max_mtu calculation does not take account of GENEVE - * options, to avoid excluding potentially valid - * configurations. + /* Only possible if called internally, ndo_change_mtu path's new_mtu + * is guaranteed to be between dev->min_mtu and dev->max_mtu. */ - int max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - dev->hard_header_len; - - if (geneve->remote.sa.sa_family == AF_INET6) - max_mtu -= sizeof(struct ipv6hdr); - else - max_mtu -= sizeof(struct iphdr); - - if (new_mtu < 68) - return -EINVAL; - - if (new_mtu > max_mtu) { - if (strict) - return -EINVAL; - - new_mtu = max_mtu; - } + if (new_mtu > dev->max_mtu) + new_mtu = dev->max_mtu; dev->mtu = new_mtu; return 0; } -static int geneve_change_mtu(struct net_device *dev, int new_mtu) -{ - return __geneve_change_mtu(dev, new_mtu, true); -} - static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) { struct ip_tunnel_info *info = skb_tunnel_info(skb); @@ -1170,6 +1149,14 @@ static void geneve_setup(struct net_device *dev) dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; + /* MTU range: 68 - (something less than 65535) */ + dev->min_mtu = ETH_MIN_MTU; + /* The max_mtu calculation does not take account of GENEVE + * options, to avoid excluding potentially valid + * configurations. This will be further reduced by IPvX hdr size. + */ + dev->max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - dev->hard_header_len; + netif_keep_dst(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; @@ -1285,10 +1272,13 @@ static int geneve_configure(struct net *net, struct net_device *dev, /* make enough headroom for basic scenario */ encap_len = GENEVE_BASE_HLEN + ETH_HLEN; - if (remote->sa.sa_family == AF_INET) + if (remote->sa.sa_family == AF_INET) { encap_len += sizeof(struct iphdr); - else + dev->max_mtu -= sizeof(struct iphdr); + } else { encap_len += sizeof(struct ipv6hdr); + dev->max_mtu -= sizeof(struct ipv6hdr); + } dev->needed_headroom = encap_len + ETH_HLEN; if (metadata) { @@ -1488,7 +1478,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, /* openvswitch users expect packet sizes to be unrestricted, * so set the largest MTU we can. */ - err = __geneve_change_mtu(dev, IP_MAX_MTU, false); + err = geneve_change_mtu(dev, IP_MAX_MTU); if (err) goto err; diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 3ea47f2..1a134cb 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -2970,6 +2970,8 @@ static void macsec_free_netdev(struct net_device *dev) static void macsec_setup(struct net_device *dev) { ether_setup(dev); + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; dev->priv_flags |= IFF_NO_QUEUE; dev->netdev_ops = &macsec_netdev_ops; dev->destructor = macsec_free_netdev; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 3234fcd..a064415 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -777,7 +777,7 @@ static int macvlan_change_mtu(struct net_device *dev, int new_mtu) { struct macvlan_dev *vlan = netdev_priv(dev); - if (new_mtu < 68 || vlan->lowerdev->mtu < new_mtu) + if (vlan->lowerdev->mtu < new_mtu) return -EINVAL; dev->mtu = new_mtu; return 0; @@ -1085,6 +1085,8 @@ void macvlan_common_setup(struct net_device *dev) { ether_setup(dev); + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; dev->priv_flags &= ~IFF_TX_SKB_SHARING; netif_keep_dst(dev); dev->priv_flags |= IFF_UNICAST_FLT; @@ -1297,6 +1299,10 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, else if (dev->mtu > lowerdev->mtu) return -EINVAL; + /* MTU range: 68 - lowerdev->max_mtu */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = lowerdev->max_mtu; + if (!tb[IFLA_ADDRESS]) eth_hw_addr_random(dev); diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index a9acf71..36877ba 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -433,6 +433,9 @@ static int ntb_netdev_probe(struct device *client_dev) ndev->netdev_ops = &ntb_netdev_ops; ndev->ethtool_ops = &ntb_ethtool_ops; + ndev->min_mtu = 0; + ndev->max_mtu = ETH_MAX_MTU; + dev->qp = ntb_transport_create_queue(ndev, client_dev, &ntb_netdev_handlers); if (!dev->qp) { diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8093e39..9328568 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -925,18 +925,6 @@ static void tun_net_mclist(struct net_device *dev) */ } -#define MIN_MTU 68 -#define MAX_MTU 65535 - -static int -tun_net_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < MIN_MTU || new_mtu + dev->hard_header_len > MAX_MTU) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static netdev_features_t tun_net_fix_features(struct net_device *dev, netdev_features_t features) { @@ -1014,7 +1002,6 @@ static const struct net_device_ops tun_netdev_ops = { .ndo_open = tun_net_open, .ndo_stop = tun_net_close, .ndo_start_xmit = tun_net_xmit, - .ndo_change_mtu = tun_net_change_mtu, .ndo_fix_features = tun_net_fix_features, .ndo_select_queue = tun_select_queue, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -1029,7 +1016,6 @@ static const struct net_device_ops tap_netdev_ops = { .ndo_open = tun_net_open, .ndo_stop = tun_net_close, .ndo_start_xmit = tun_net_xmit, - .ndo_change_mtu = tun_net_change_mtu, .ndo_fix_features = tun_net_fix_features, .ndo_set_rx_mode = tun_net_mclist, .ndo_set_mac_address = eth_mac_addr, @@ -1062,6 +1048,9 @@ static void tun_flow_uninit(struct tun_struct *tun) tun_flow_flush(tun); } +#define MIN_MTU 68 +#define MAX_MTU 65535 + /* Initialize net device. */ static void tun_net_init(struct net_device *dev) { @@ -1092,6 +1081,9 @@ static void tun_net_init(struct net_device *dev) break; } + + dev->min_mtu = MIN_MTU; + dev->max_mtu = MAX_MTU - dev->hard_header_len; } /* Character device part */ diff --git a/drivers/net/veth.c b/drivers/net/veth.c index fbc853e..0520952a 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -23,9 +23,6 @@ #define DRV_NAME "veth" #define DRV_VERSION "1.0" -#define MIN_MTU 68 /* Min L3 MTU */ -#define MAX_MTU 65535 /* Max L3 MTU (arbitrary) */ - struct pcpu_vstats { u64 packets; u64 bytes; @@ -216,17 +213,9 @@ static int veth_close(struct net_device *dev) return 0; } -static int is_valid_veth_mtu(int new_mtu) +static int is_valid_veth_mtu(int mtu) { - return new_mtu >= MIN_MTU && new_mtu <= MAX_MTU; -} - -static int veth_change_mtu(struct net_device *dev, int new_mtu) -{ - if (!is_valid_veth_mtu(new_mtu)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; + return mtu >= ETH_MIN_MTU && mtu <= ETH_MAX_MTU; } static int veth_dev_init(struct net_device *dev) @@ -300,7 +289,6 @@ static const struct net_device_ops veth_netdev_ops = { .ndo_open = veth_open, .ndo_stop = veth_close, .ndo_start_xmit = veth_xmit, - .ndo_change_mtu = veth_change_mtu, .ndo_get_stats64 = veth_get_stats64, .ndo_set_rx_mode = veth_set_multicast_list, .ndo_set_mac_address = eth_mac_addr, @@ -337,6 +325,7 @@ static void veth_setup(struct net_device *dev) NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX); dev->destructor = veth_dev_free; + dev->max_mtu = ETH_MAX_MTU; dev->hw_features = VETH_FEATURES; dev->hw_enc_features = VETH_FEATURES; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index e7d1668..c0170b6 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2367,43 +2367,31 @@ static void vxlan_set_multicast_list(struct net_device *dev) { } -static int __vxlan_change_mtu(struct net_device *dev, - struct net_device *lowerdev, - struct vxlan_rdst *dst, int new_mtu, bool strict) +static int vxlan_change_mtu(struct net_device *dev, int new_mtu) { - int max_mtu = IP_MAX_MTU; - - if (lowerdev) - max_mtu = lowerdev->mtu; + struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_rdst *dst = &vxlan->default_dst; + struct net_device *lowerdev = __dev_get_by_index(vxlan->net, + dst->remote_ifindex); + bool use_ipv6 = false; if (dst->remote_ip.sa.sa_family == AF_INET6) - max_mtu -= VXLAN6_HEADROOM; - else - max_mtu -= VXLAN_HEADROOM; - - if (new_mtu < 68) - return -EINVAL; + use_ipv6 = true; - if (new_mtu > max_mtu) { - if (strict) + /* This check is different than dev->max_mtu, because it looks at + * the lowerdev->mtu, rather than the static dev->max_mtu + */ + if (lowerdev) { + int max_mtu = lowerdev->mtu - + (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); + if (new_mtu > max_mtu) return -EINVAL; - - new_mtu = max_mtu; } dev->mtu = new_mtu; return 0; } -static int vxlan_change_mtu(struct net_device *dev, int new_mtu) -{ - struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_rdst *dst = &vxlan->default_dst; - struct net_device *lowerdev = __dev_get_by_index(vxlan->net, - dst->remote_ifindex); - return __vxlan_change_mtu(dev, lowerdev, dst, new_mtu, true); -} - static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) { struct vxlan_dev *vxlan = netdev_priv(dev); @@ -2795,6 +2783,10 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, vxlan_ether_setup(dev); } + /* MTU range: 68 - 65535 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = ETH_MAX_MTU; + vxlan->net = src_net; dst->remote_vni = conf->vni; @@ -2838,7 +2830,8 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, #endif if (!conf->mtu) - dev->mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); + dev->mtu = lowerdev->mtu - + (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); needed_headroom = lowerdev->hard_header_len; } else if (vxlan_addr_multicast(&dst->remote_ip)) { @@ -2847,9 +2840,20 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, } if (conf->mtu) { - err = __vxlan_change_mtu(dev, lowerdev, dst, conf->mtu, false); - if (err) - return err; + int max_mtu = ETH_MAX_MTU; + + if (lowerdev) + max_mtu = lowerdev->mtu; + + max_mtu -= (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); + + if (conf->mtu < dev->min_mtu || conf->mtu > dev->max_mtu) + return -EINVAL; + + dev->mtu = conf->mtu; + + if (conf->mtu > max_mtu) + dev->mtu = max_mtu; } if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index fbfacd5..10da6c5 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -826,5 +826,8 @@ void vlan_setup(struct net_device *dev) dev->destructor = vlan_dev_free; dev->ethtool_ops = &vlan_ethtool_ops; + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; + eth_zero_addr(dev->broadcast); } diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 89a687f..c08e02b 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -185,7 +185,7 @@ static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev, static int br_change_mtu(struct net_device *dev, int new_mtu) { struct net_bridge *br = netdev_priv(dev); - if (new_mtu < 68 || new_mtu > br_min_mtu(br)) + if (new_mtu > br_min_mtu(br)) return -EINVAL; dev->mtu = new_mtu; @@ -410,6 +410,7 @@ void br_dev_setup(struct net_device *dev) br->bridge_hello_time = br->hello_time = 2 * HZ; br->bridge_forward_delay = br->forward_delay = 15 * HZ; br->ageing_time = BR_DEFAULT_AGEING_TIME; + dev->max_mtu = ETH_MAX_MTU; br_netfilter_rtable_init(br); br_stp_timer_init(br); diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index e7da290..d5d6cae 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -89,15 +89,6 @@ static const struct ethtool_ops internal_dev_ethtool_ops = { .get_link = ethtool_op_get_link, }; -static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu) -{ - if (new_mtu < 68) - return -EINVAL; - - netdev->mtu = new_mtu; - return 0; -} - static void internal_dev_destructor(struct net_device *dev) { struct vport *vport = ovs_internal_dev_get_vport(dev); @@ -148,7 +139,6 @@ static const struct net_device_ops internal_dev_netdev_ops = { .ndo_stop = internal_dev_stop, .ndo_start_xmit = internal_dev_xmit, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = internal_dev_change_mtu, .ndo_get_stats64 = internal_get_stats, .ndo_set_rx_headroom = internal_set_rx_headroom, }; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 2cd9b44..b019636 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -418,9 +418,6 @@ static int teql_master_mtu(struct net_device *dev, int new_mtu) struct teql_master *m = netdev_priv(dev); struct Qdisc *q; - if (new_mtu < 68) - return -EINVAL; - q = m->slaves; if (q) { do { @@ -460,6 +457,8 @@ static __init void teql_master_setup(struct net_device *dev) dev->netdev_ops = &teql_netdev_ops; dev->type = ARPHRD_VOID; dev->mtu = 1500; + dev->min_mtu = 68; + dev->max_mtu = 65535; dev->tx_queue_len = 100; dev->flags = IFF_NOARP; dev->hard_header_len = LL_MAX_HEADER; -- cgit v1.1 From d0c2c9973ecd267307543526ae93855e3a3e4b23 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:21 -0400 Subject: net: use core MTU range checking in virt drivers hyperv_net: - set min/max_mtu, per Haiyang, after rndis_filter_device_add virtio_net: - set min/max_mtu - remove virtnet_change_mtu vmxnet3: - set min/max_mtu xen-netback: - min_mtu = 0, max_mtu = 65517 xen-netfront: - min_mtu = 0, max_mtu = 65535 unisys/visor: - clean up defines a little to not clash with network core or add redundat definitions CC: netdev@vger.kernel.org CC: virtualization@lists.linux-foundation.org CC: "K. Y. Srinivasan" CC: Haiyang Zhang CC: "Michael S. Tsirkin" CC: Shrikrishna Khare CC: "VMware, Inc." CC: Wei Liu CC: Paul Durrant CC: David Kershner Signed-off-by: Jarod Wilson Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 4 ++-- drivers/net/hyperv/netvsc_drv.c | 14 +++++++------- drivers/net/virtio_net.c | 23 ++++++++++------------- drivers/net/vmxnet3/vmxnet3_drv.c | 7 ++++--- drivers/net/xen-netback/interface.c | 5 ++++- drivers/net/xen-netfront.c | 2 ++ drivers/staging/unisys/include/iochannel.h | 10 ++++------ drivers/staging/unisys/visornic/visornic_main.c | 4 ++-- 8 files changed, 35 insertions(+), 34 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index f4fbcb5..3958ada 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -606,8 +606,8 @@ struct nvsp_message { } __packed; -#define NETVSC_MTU 65536 -#define NETVSC_MTU_MIN 68 +#define NETVSC_MTU 65535 +#define NETVSC_MTU_MIN ETH_MIN_MTU #define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024*16) /* 16MB */ #define NETVSC_RECEIVE_BUFFER_SIZE_LEGACY (1024*1024*15) /* 15MB */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f0919bd..3b28cf1 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -872,19 +872,12 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) struct netvsc_device *nvdev = ndevctx->nvdev; struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; - int limit = ETH_DATA_LEN; u32 num_chn; int ret = 0; if (ndevctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; - if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) - limit = NETVSC_MTU - ETH_HLEN; - - if (mtu < NETVSC_MTU_MIN || mtu > limit) - return -EINVAL; - ret = netvsc_close(ndev); if (ret) goto out; @@ -1402,6 +1395,13 @@ static int netvsc_probe(struct hv_device *dev, netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); + /* MTU range: 68 - 1500 or 65521 */ + net->min_mtu = NETVSC_MTU_MIN; + if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) + net->max_mtu = NETVSC_MTU - ETH_HLEN; + else + net->max_mtu = ETH_DATA_LEN; + ret = register_netdev(net); if (ret != 0) { pr_err("Unable to register netdev.\n"); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index fad84f3..720809f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1419,17 +1419,6 @@ static const struct ethtool_ops virtnet_ethtool_ops = { .set_settings = virtnet_set_settings, }; -#define MIN_MTU 68 -#define MAX_MTU 65535 - -static int virtnet_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < MIN_MTU || new_mtu > MAX_MTU) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static const struct net_device_ops virtnet_netdev = { .ndo_open = virtnet_open, .ndo_stop = virtnet_close, @@ -1437,7 +1426,6 @@ static const struct net_device_ops virtnet_netdev = { .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = virtnet_set_mac_address, .ndo_set_rx_mode = virtnet_set_rx_mode, - .ndo_change_mtu = virtnet_change_mtu, .ndo_get_stats64 = virtnet_stats, .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid, @@ -1748,6 +1736,9 @@ static bool virtnet_validate_features(struct virtio_device *vdev) return true; } +#define MIN_MTU ETH_MIN_MTU +#define MAX_MTU ETH_MAX_MTU + static int virtnet_probe(struct virtio_device *vdev) { int i, err; @@ -1821,6 +1812,10 @@ static int virtnet_probe(struct virtio_device *vdev) dev->vlan_features = dev->features; + /* MTU range: 68 - 65535 */ + dev->min_mtu = MIN_MTU; + dev->max_mtu = MAX_MTU; + /* Configuration may specify what MAC to use. Otherwise random. */ if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) virtio_cread_bytes(vdev, @@ -1875,8 +1870,10 @@ static int virtnet_probe(struct virtio_device *vdev) mtu = virtio_cread16(vdev, offsetof(struct virtio_net_config, mtu)); - if (virtnet_change_mtu(dev, mtu)) + if (mtu < dev->min_mtu || mtu > dev->max_mtu) __virtio_clear_bit(vdev, VIRTIO_NET_F_MTU); + else + dev->mtu = mtu; } if (vi->any_header_sg) diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index b5554f2..0c36de1 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -2969,9 +2969,6 @@ vmxnet3_change_mtu(struct net_device *netdev, int new_mtu) struct vmxnet3_adapter *adapter = netdev_priv(netdev); int err = 0; - if (new_mtu < VMXNET3_MIN_MTU || new_mtu > VMXNET3_MAX_MTU) - return -EINVAL; - netdev->mtu = new_mtu; /* @@ -3428,6 +3425,10 @@ vmxnet3_probe_device(struct pci_dev *pdev, vmxnet3_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; + /* MTU range: 60 - 9000 */ + netdev->min_mtu = VMXNET3_MIN_MTU; + netdev->max_mtu = VMXNET3_MAX_MTU; + INIT_WORK(&adapter->work, vmxnet3_reset_work); set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 74dc2bf..e30ffd2 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -302,7 +302,7 @@ static int xenvif_close(struct net_device *dev) static int xenvif_change_mtu(struct net_device *dev, int mtu) { struct xenvif *vif = netdev_priv(dev); - int max = vif->can_sg ? 65535 - VLAN_ETH_HLEN : ETH_DATA_LEN; + int max = vif->can_sg ? ETH_MAX_MTU - VLAN_ETH_HLEN : ETH_DATA_LEN; if (mtu > max) return -EINVAL; @@ -471,6 +471,9 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, dev->tx_queue_len = XENVIF_QUEUE_LENGTH; + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU - VLAN_ETH_HLEN; + /* * Initialise a dummy MAC address. We choose the numerically * largest non-broadcast address to prevent the address getting diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index e17879d..7d616b0 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1329,6 +1329,8 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) netdev->features |= netdev->hw_features; netdev->ethtool_ops = &xennet_ethtool_ops; + netdev->min_mtu = 0; + netdev->max_mtu = XEN_NETIF_MAX_TX_SIZE; SET_NETDEV_DEV(netdev, &dev->dev); np->netdev = netdev; diff --git a/drivers/staging/unisys/include/iochannel.h b/drivers/staging/unisys/include/iochannel.h index cba4433..9081b3f 100644 --- a/drivers/staging/unisys/include/iochannel.h +++ b/drivers/staging/unisys/include/iochannel.h @@ -113,12 +113,10 @@ enum net_types { }; -#define ETH_HEADER_SIZE 14 /* size of ethernet header */ - #define ETH_MIN_DATA_SIZE 46 /* minimum eth data size */ -#define ETH_MIN_PACKET_SIZE (ETH_HEADER_SIZE + ETH_MIN_DATA_SIZE) +#define ETH_MIN_PACKET_SIZE (ETH_HLEN + ETH_MIN_DATA_SIZE) -#define ETH_MAX_MTU 16384 /* maximum data size */ +#define VISOR_ETH_MAX_MTU 16384 /* maximum data size */ #ifndef MAX_MACADDR_LEN #define MAX_MACADDR_LEN 6 /* number of bytes in MAC address */ @@ -288,7 +286,7 @@ struct net_pkt_xmt { int len; /* full length of data in the packet */ int num_frags; /* number of fragments in frags containing data */ struct phys_info frags[MAX_PHYS_INFO]; /* physical page information */ - char ethhdr[ETH_HEADER_SIZE]; /* the ethernet header */ + char ethhdr[ETH_HLEN]; /* the ethernet header */ struct { /* these are needed for csum at uisnic end */ u8 valid; /* 1 = struct is valid - else ignore */ @@ -323,7 +321,7 @@ struct net_pkt_xmtdone { */ #define RCVPOST_BUF_SIZE 4032 #define MAX_NET_RCV_CHAIN \ - ((ETH_MAX_MTU + ETH_HEADER_SIZE + RCVPOST_BUF_SIZE - 1) \ + ((VISOR_ETH_MAX_MTU + ETH_HLEN + RCVPOST_BUF_SIZE - 1) \ / RCVPOST_BUF_SIZE) struct net_pkt_rcvpost { diff --git a/drivers/staging/unisys/visornic/visornic_main.c b/drivers/staging/unisys/visornic/visornic_main.c index 1367007..f8a584b 100644 --- a/drivers/staging/unisys/visornic/visornic_main.c +++ b/drivers/staging/unisys/visornic/visornic_main.c @@ -791,7 +791,7 @@ visornic_xmit(struct sk_buff *skb, struct net_device *netdev) * pointing to */ firstfraglen = skb->len - skb->data_len; - if (firstfraglen < ETH_HEADER_SIZE) { + if (firstfraglen < ETH_HLEN) { spin_unlock_irqrestore(&devdata->priv_lock, flags); devdata->busy_cnt++; dev_err(&netdev->dev, @@ -864,7 +864,7 @@ visornic_xmit(struct sk_buff *skb, struct net_device *netdev) /* copy ethernet header from first frag into ocmdrsp * - everything else will be pass in frags & DMA'ed */ - memcpy(cmdrsp->net.xmt.ethhdr, skb->data, ETH_HEADER_SIZE); + memcpy(cmdrsp->net.xmt.ethhdr, skb->data, ETH_HLEN); /* copy frags info - from skb->data we need to only provide access * beyond eth header */ -- cgit v1.1 From b3e3893e1253692c3d2b8e8ebd5a26183ed30443 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:22 -0400 Subject: net: use core MTU range checking in misc drivers firewire-net: - set min/max_mtu - remove fwnet_change_mtu nes: - set max_mtu - clean up nes_netdev_change_mtu xpnet: - set min/max_mtu - remove xpnet_dev_change_mtu hippi: - set min/max_mtu - remove hippi_change_mtu batman-adv: - set max_mtu - remove batadv_interface_change_mtu - initialization is a little async, not 100% certain that max_mtu is set in the optimal place, don't have hardware to test with rionet: - set min/max_mtu - remove rionet_change_mtu slip: - set min/max_mtu - streamline sl_change_mtu um/net_kern: - remove pointless ndo_change_mtu hsi/clients/ssi_protocol: - use core MTU range checking - remove now redundant ssip_pn_set_mtu ipoib: - set a default max MTU value - Note: ipoib's actual max MTU can vary, depending on if the device is in connected mode or not, so we'll just set the max_mtu value to the max possible, and let the ndo_change_mtu function continue to validate any new MTU change requests with checks for CM or not. Note that ipoib has no min_mtu set, and thus, the network core's mtu > 0 check is the only lower bounds here. mptlan: - use net core MTU range checking - remove now redundant mpt_lan_change_mtu fddi: - min_mtu = 21, max_mtu = 4470 - remove now redundant fddi_change_mtu (including export) fjes: - min_mtu = 8192, max_mtu = 65536 - The max_mtu value is actually one over IP_MAX_MTU here, but the idea is to get past the core net MTU range checks so fjes_change_mtu can validate a new MTU against what it supports (see fjes_support_mtu in fjes_hw.c) hsr: - min_mtu = 0 (calls ether_setup, max_mtu is 1500) f_phonet: - min_mtu = 6, max_mtu = 65541 u_ether: - min_mtu = 14, max_mtu = 15412 phonet/pep-gprs: - min_mtu = 576, max_mtu = 65530 - remove redundant gprs_set_mtu CC: netdev@vger.kernel.org CC: linux-rdma@vger.kernel.org CC: Stefan Richter CC: Faisal Latif CC: linux-rdma@vger.kernel.org CC: Cliff Whickman CC: Robin Holt CC: Jes Sorensen CC: Marek Lindner CC: Simon Wunderlich CC: Antonio Quartulli CC: Sathya Prakash CC: Chaitra P B CC: Suganath Prabu Subramani CC: MPT-FusionLinux.pdl@broadcom.com CC: Sebastian Reichel CC: Felipe Balbi CC: Arvid Brodin CC: Remi Denis-Courmont Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- arch/um/drivers/net_kern.c | 8 -------- drivers/firewire/net.c | 18 ++++-------------- drivers/hsi/clients/ssi_protocol.c | 14 ++++---------- drivers/infiniband/hw/nes/nes.c | 1 - drivers/infiniband/hw/nes/nes.h | 4 ++-- drivers/infiniband/hw/nes/nes_nic.c | 10 +++------- drivers/infiniband/ulp/ipoib/ipoib_main.c | 1 + drivers/message/fusion/mptlan.c | 15 ++++----------- drivers/misc/sgi-xp/xpnet.c | 21 ++++----------------- drivers/net/fddi/skfp/skfddi.c | 1 - drivers/net/fjes/fjes_main.c | 2 ++ drivers/net/hippi/rrunner.c | 1 - drivers/net/rionet.c | 15 +++------------ drivers/net/slip/slip.c | 11 +++++------ drivers/usb/gadget/function/f_phonet.c | 11 ++--------- drivers/usb/gadget/function/u_ether.c | 14 ++++---------- include/linux/fddidevice.h | 1 - include/linux/hippidevice.h | 1 - net/802/fddi.c | 11 ++--------- net/802/hippi.c | 14 ++------------ net/batman-adv/soft-interface.c | 13 +------------ net/hsr/hsr_device.c | 1 + net/phonet/pep-gprs.c | 12 ++---------- 23 files changed, 46 insertions(+), 154 deletions(-) diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index 2cd5b68..1669240 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -256,13 +256,6 @@ static void uml_net_tx_timeout(struct net_device *dev) netif_wake_queue(dev); } -static int uml_net_change_mtu(struct net_device *dev, int new_mtu) -{ - dev->mtu = new_mtu; - - return 0; -} - #ifdef CONFIG_NET_POLL_CONTROLLER static void uml_net_poll_controller(struct net_device *dev) { @@ -374,7 +367,6 @@ static const struct net_device_ops uml_netdev_ops = { .ndo_set_rx_mode = uml_net_set_multicast_list, .ndo_tx_timeout = uml_net_tx_timeout, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = uml_net_change_mtu, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = uml_net_poll_controller, diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 309311b..8430222 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -1349,15 +1349,6 @@ static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net) return NETDEV_TX_OK; } -static int fwnet_change_mtu(struct net_device *net, int new_mtu) -{ - if (new_mtu < 68) - return -EINVAL; - - net->mtu = new_mtu; - return 0; -} - static const struct ethtool_ops fwnet_ethtool_ops = { .get_link = ethtool_op_get_link, }; @@ -1366,7 +1357,6 @@ static const struct net_device_ops fwnet_netdev_ops = { .ndo_open = fwnet_open, .ndo_stop = fwnet_stop, .ndo_start_xmit = fwnet_tx, - .ndo_change_mtu = fwnet_change_mtu, }; static void fwnet_init_dev(struct net_device *net) @@ -1435,7 +1425,6 @@ static int fwnet_probe(struct fw_unit *unit, struct net_device *net; bool allocated_netdev = false; struct fwnet_device *dev; - unsigned max_mtu; int ret; union fwnet_hwaddr *ha; @@ -1478,9 +1467,10 @@ static int fwnet_probe(struct fw_unit *unit, * Use the RFC 2734 default 1500 octets or the maximum payload * as initial MTU */ - max_mtu = (1 << (card->max_receive + 1)) - - sizeof(struct rfc2734_header) - IEEE1394_GASP_HDR_SIZE; - net->mtu = min(1500U, max_mtu); + net->max_mtu = (1 << (card->max_receive + 1)) + - sizeof(struct rfc2734_header) - IEEE1394_GASP_HDR_SIZE; + net->mtu = min(1500U, net->max_mtu); + net->min_mtu = ETH_MIN_MTU; /* Set our hardware address while we're at it */ ha = (union fwnet_hwaddr *)net->dev_addr; diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c index 6031cd1..7ef8196 100644 --- a/drivers/hsi/clients/ssi_protocol.c +++ b/drivers/hsi/clients/ssi_protocol.c @@ -960,15 +960,6 @@ static int ssip_pn_stop(struct net_device *dev) return 0; } -static int ssip_pn_set_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu > SSIP_MAX_MTU || new_mtu < PHONET_MIN_MTU) - return -EINVAL; - dev->mtu = new_mtu; - - return 0; -} - static void ssip_xmit_work(struct work_struct *work) { struct ssi_protocol *ssi = @@ -1060,7 +1051,6 @@ static const struct net_device_ops ssip_pn_ops = { .ndo_open = ssip_pn_open, .ndo_stop = ssip_pn_stop, .ndo_start_xmit = ssip_pn_xmit, - .ndo_change_mtu = ssip_pn_set_mtu, }; static void ssip_pn_setup(struct net_device *dev) @@ -1136,6 +1126,10 @@ static int ssi_protocol_probe(struct device *dev) goto out1; } + /* MTU range: 6 - 65535 */ + ssi->netdev->min_mtu = PHONET_MIN_MTU; + ssi->netdev->max_mtu = SSIP_MAX_MTU; + SET_NETDEV_DEV(ssi->netdev, dev); netif_carrier_off(ssi->netdev); err = register_netdev(ssi->netdev); diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index 35cbb17..2baa45a 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -65,7 +65,6 @@ MODULE_DESCRIPTION("NetEffect RNIC Low-level iWARP Driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(DRV_VERSION); -int max_mtu = 9000; int interrupt_mod_interval = 0; /* Interoperability */ diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h index e7430c9..85acd08 100644 --- a/drivers/infiniband/hw/nes/nes.h +++ b/drivers/infiniband/hw/nes/nes.h @@ -83,6 +83,8 @@ #define NES_FIRST_QPN 64 #define NES_SW_CONTEXT_ALIGN 1024 +#define NES_MAX_MTU 9000 + #define NES_NIC_MAX_NICS 16 #define NES_MAX_ARP_TABLE_SIZE 4096 @@ -169,8 +171,6 @@ do { \ #include "nes_cm.h" #include "nes_mgt.h" -extern int max_mtu; -#define max_frame_len (max_mtu+ETH_HLEN) extern int interrupt_mod_interval; extern int nes_if_count; extern int mpa_version; diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 2b27d13..7f8597d 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -981,20 +981,16 @@ static int nes_netdev_change_mtu(struct net_device *netdev, int new_mtu) { struct nes_vnic *nesvnic = netdev_priv(netdev); struct nes_device *nesdev = nesvnic->nesdev; - int ret = 0; u8 jumbomode = 0; u32 nic_active; u32 nic_active_bit; u32 uc_all_active; u32 mc_all_active; - if ((new_mtu < ETH_ZLEN) || (new_mtu > max_mtu)) - return -EINVAL; - netdev->mtu = new_mtu; nesvnic->max_frame_size = new_mtu + VLAN_ETH_HLEN; - if (netdev->mtu > 1500) { + if (netdev->mtu > ETH_DATA_LEN) { jumbomode=1; } nes_nic_init_timer_defaults(nesdev, jumbomode); @@ -1020,7 +1016,7 @@ static int nes_netdev_change_mtu(struct net_device *netdev, int new_mtu) nes_write_indexed(nesdev, NES_IDX_NIC_UNICAST_ALL, nic_active); } - return ret; + return 0; } @@ -1658,7 +1654,7 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev, netdev->watchdog_timeo = NES_TX_TIMEOUT; netdev->irq = nesdev->pcidev->irq; - netdev->mtu = ETH_DATA_LEN; + netdev->max_mtu = NES_MAX_MTU; netdev->hard_header_len = ETH_HLEN; netdev->addr_len = ETH_ALEN; netdev->type = ARPHRD_ETHER; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index cc05921..ae5d7cd 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -2017,6 +2017,7 @@ static struct net_device *ipoib_add_port(const char *format, /* MTU will be reset when mcast join happens */ priv->dev->mtu = IPOIB_UD_MTU(priv->max_ib_mtu); priv->mcast_mtu = priv->admin_mtu = priv->dev->mtu; + priv->dev->max_mtu = IPOIB_CM_MTU; priv->dev->neigh_priv_len = sizeof(struct ipoib_neigh); diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index 6955c9e..55dd71b 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -549,16 +549,6 @@ mpt_lan_close(struct net_device *dev) } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static int -mpt_lan_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < MPT_LAN_MIN_MTU) || (new_mtu > MPT_LAN_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* Tx timeout handler. */ static void mpt_lan_tx_timeout(struct net_device *dev) @@ -1304,7 +1294,6 @@ static const struct net_device_ops mpt_netdev_ops = { .ndo_open = mpt_lan_open, .ndo_stop = mpt_lan_close, .ndo_start_xmit = mpt_lan_sdu_send, - .ndo_change_mtu = mpt_lan_change_mtu, .ndo_tx_timeout = mpt_lan_tx_timeout, }; @@ -1375,6 +1364,10 @@ mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum) dev->netdev_ops = &mpt_netdev_ops; dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT; + /* MTU range: 96 - 65280 */ + dev->min_mtu = MPT_LAN_MIN_MTU; + dev->max_mtu = MPT_LAN_MAX_MTU; + dlprintk((KERN_INFO MYNAM ": Finished registering dev " "and setting initial values\n")); diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 557f978..0c26eaf 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -118,6 +118,8 @@ static DEFINE_SPINLOCK(xpnet_broadcast_lock); * now, the default is 64KB. */ #define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES) +/* 68 comes from min TCP+IP+MAC header */ +#define XPNET_MIN_MTU 68 /* 32KB has been determined to be the ideal */ #define XPNET_DEF_MTU (0x8000UL) @@ -330,22 +332,6 @@ xpnet_dev_stop(struct net_device *dev) return 0; } -static int -xpnet_dev_change_mtu(struct net_device *dev, int new_mtu) -{ - /* 68 comes from min TCP+IP+MAC header */ - if ((new_mtu < 68) || (new_mtu > XPNET_MAX_MTU)) { - dev_err(xpnet, "ifconfig %s mtu %d failed; value must be " - "between 68 and %ld\n", dev->name, new_mtu, - XPNET_MAX_MTU); - return -EINVAL; - } - - dev->mtu = new_mtu; - dev_dbg(xpnet, "ifconfig %s mtu set to %d\n", dev->name, new_mtu); - return 0; -} - /* * Notification that the other end has received the message and * DMA'd the skb information. At this point, they are done with @@ -519,7 +505,6 @@ static const struct net_device_ops xpnet_netdev_ops = { .ndo_open = xpnet_dev_open, .ndo_stop = xpnet_dev_stop, .ndo_start_xmit = xpnet_dev_hard_start_xmit, - .ndo_change_mtu = xpnet_dev_change_mtu, .ndo_tx_timeout = xpnet_dev_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, @@ -555,6 +540,8 @@ xpnet_init(void) xpnet_device->netdev_ops = &xpnet_netdev_ops; xpnet_device->mtu = XPNET_DEF_MTU; + xpnet_device->min_mtu = XPNET_MIN_MTU; + xpnet_device->max_mtu = XPNET_MAX_MTU; /* * Multicast assumes the LSB of the first octet is set for multicast diff --git a/drivers/net/fddi/skfp/skfddi.c b/drivers/net/fddi/skfp/skfddi.c index 51acc6d..3a63918 100644 --- a/drivers/net/fddi/skfp/skfddi.c +++ b/drivers/net/fddi/skfp/skfddi.c @@ -166,7 +166,6 @@ static const struct net_device_ops skfp_netdev_ops = { .ndo_stop = skfp_close, .ndo_start_xmit = skfp_send_pkt, .ndo_get_stats = skfp_ctl_get_stats, - .ndo_change_mtu = fddi_change_mtu, .ndo_set_rx_mode = skfp_ctl_set_multicast_list, .ndo_set_mac_address = skfp_ctl_set_mac_address, .ndo_do_ioctl = skfp_ioctl, diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index f36eb4a..b77e4ecf 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -1316,6 +1316,8 @@ static void fjes_netdev_setup(struct net_device *netdev) netdev->netdev_ops = &fjes_netdev_ops; fjes_set_ethtool_ops(netdev); netdev->mtu = fjes_support_mtu[3]; + netdev->min_mtu = fjes_support_mtu[0]; + netdev->max_mtu = fjes_support_mtu[3]; netdev->flags |= IFF_BROADCAST; netdev->features |= NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_FILTER; } diff --git a/drivers/net/hippi/rrunner.c b/drivers/net/hippi/rrunner.c index 95c0b45..f5a9728 100644 --- a/drivers/net/hippi/rrunner.c +++ b/drivers/net/hippi/rrunner.c @@ -68,7 +68,6 @@ static const struct net_device_ops rr_netdev_ops = { .ndo_stop = rr_close, .ndo_do_ioctl = rr_ioctl, .ndo_start_xmit = rr_start_xmit, - .ndo_change_mtu = hippi_change_mtu, .ndo_set_mac_address = hippi_mac_addr, }; diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index a31f461..300bb14 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -466,17 +466,6 @@ static void rionet_set_msglevel(struct net_device *ndev, u32 value) rnet->msg_enable = value; } -static int rionet_change_mtu(struct net_device *ndev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > RIONET_MAX_MTU)) { - printk(KERN_ERR "%s: Invalid MTU size %d\n", - ndev->name, new_mtu); - return -EINVAL; - } - ndev->mtu = new_mtu; - return 0; -} - static const struct ethtool_ops rionet_ethtool_ops = { .get_drvinfo = rionet_get_drvinfo, .get_msglevel = rionet_get_msglevel, @@ -488,7 +477,6 @@ static const struct net_device_ops rionet_netdev_ops = { .ndo_open = rionet_open, .ndo_stop = rionet_close, .ndo_start_xmit = rionet_start_xmit, - .ndo_change_mtu = rionet_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; @@ -525,6 +513,9 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) ndev->netdev_ops = &rionet_netdev_ops; ndev->mtu = RIONET_MAX_MTU; + /* MTU range: 68 - 4082 */ + ndev->min_mtu = ETH_MIN_MTU; + ndev->max_mtu = RIONET_MAX_MTU; ndev->features = NETIF_F_LLTX; SET_NETDEV_DEV(ndev, &mport->dev); ndev->ethtool_ops = &rionet_ethtool_ops; diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 9ed6d1c..7e933d8 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -561,12 +561,7 @@ static int sl_change_mtu(struct net_device *dev, int new_mtu) { struct slip *sl = netdev_priv(dev); - if (new_mtu < 68 || new_mtu > 65534) - return -EINVAL; - - if (new_mtu != dev->mtu) - return sl_realloc_bufs(sl, new_mtu); - return 0; + return sl_realloc_bufs(sl, new_mtu); } /* Netdevice get statistics request */ @@ -663,6 +658,10 @@ static void sl_setup(struct net_device *dev) dev->addr_len = 0; dev->tx_queue_len = 10; + /* MTU range: 68 - 65534 */ + dev->min_mtu = 68; + dev->max_mtu = 65534; + /* New-style flags. */ dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST; } diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 0473d61..b4058f0 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -261,19 +261,10 @@ out: return NETDEV_TX_OK; } -static int pn_net_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static const struct net_device_ops pn_netdev_ops = { .ndo_open = pn_net_open, .ndo_stop = pn_net_close, .ndo_start_xmit = pn_net_xmit, - .ndo_change_mtu = pn_net_mtu, }; static void pn_net_setup(struct net_device *dev) @@ -282,6 +273,8 @@ static void pn_net_setup(struct net_device *dev) dev->type = ARPHRD_PHONET; dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->mtu = PHONET_DEV_MTU; + dev->min_mtu = PHONET_MIN_MTU; + dev->max_mtu = PHONET_MAX_MTU; dev->hard_header_len = 1; dev->dev_addr[0] = PN_MEDIA_USB; dev->addr_len = 1; diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 9c8c9ed..39a6df1 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -142,15 +142,6 @@ static inline int qlen(struct usb_gadget *gadget, unsigned qmult) /* NETWORK DRIVER HOOKUP (to the layer above this driver) */ -static int ueth_change_mtu(struct net_device *net, int new_mtu) -{ - if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN) - return -ERANGE; - net->mtu = new_mtu; - - return 0; -} - static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) { struct eth_dev *dev = netdev_priv(net); @@ -736,7 +727,6 @@ static const struct net_device_ops eth_netdev_ops = { .ndo_open = eth_open, .ndo_stop = eth_stop, .ndo_start_xmit = eth_start_xmit, - .ndo_change_mtu = ueth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -799,6 +789,10 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, net->ethtool_ops = &ops; + /* MTU range: 14 - 15412 */ + net->min_mtu = ETH_HLEN; + net->max_mtu = GETHER_MAX_ETH_FRAME_LEN; + dev->gadget = g; SET_NETDEV_DEV(net, &g->dev); SET_NETDEV_DEVTYPE(net, &gadget_type); diff --git a/include/linux/fddidevice.h b/include/linux/fddidevice.h index 9a79f01..32c22cf 100644 --- a/include/linux/fddidevice.h +++ b/include/linux/fddidevice.h @@ -26,7 +26,6 @@ #ifdef __KERNEL__ __be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev); -int fddi_change_mtu(struct net_device *dev, int new_mtu); struct net_device *alloc_fddidev(int sizeof_priv); #endif diff --git a/include/linux/hippidevice.h b/include/linux/hippidevice.h index 8ec23fb..402f99e 100644 --- a/include/linux/hippidevice.h +++ b/include/linux/hippidevice.h @@ -32,7 +32,6 @@ struct hippi_cb { }; __be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev); -int hippi_change_mtu(struct net_device *dev, int new_mtu); int hippi_mac_addr(struct net_device *dev, void *p); int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p); struct net_device *alloc_hippi_dev(int sizeof_priv); diff --git a/net/802/fddi.c b/net/802/fddi.c index 7d3a0af..6356623 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -141,15 +141,6 @@ __be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev) EXPORT_SYMBOL(fddi_type_trans); -int fddi_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL(fddi_change_mtu); - static const struct header_ops fddi_header_ops = { .create = fddi_header, }; @@ -161,6 +152,8 @@ static void fddi_setup(struct net_device *dev) dev->type = ARPHRD_FDDI; dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */ dev->mtu = FDDI_K_SNAP_DLEN; /* Assume max payload of 802.2 SNAP frame */ + dev->min_mtu = FDDI_K_SNAP_HLEN; + dev->max_mtu = FDDI_K_SNAP_DLEN; dev->addr_len = FDDI_K_ALEN; dev->tx_queue_len = 100; /* Long queues on FDDI */ dev->flags = IFF_BROADCAST | IFF_MULTICAST; diff --git a/net/802/hippi.c b/net/802/hippi.c index ade1a52..5e4427b 100644 --- a/net/802/hippi.c +++ b/net/802/hippi.c @@ -116,18 +116,6 @@ __be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev) EXPORT_SYMBOL(hippi_type_trans); -int hippi_change_mtu(struct net_device *dev, int new_mtu) -{ - /* - * HIPPI's got these nice large MTUs. - */ - if ((new_mtu < 68) || (new_mtu > 65280)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL(hippi_change_mtu); - /* * For HIPPI we will actually use the lower 4 bytes of the hardware * address as the I-FIELD rather than the actual hardware address. @@ -174,6 +162,8 @@ static void hippi_setup(struct net_device *dev) dev->type = ARPHRD_HIPPI; dev->hard_header_len = HIPPI_HLEN; dev->mtu = 65280; + dev->min_mtu = 68; + dev->max_mtu = 65280; dev->addr_len = HIPPI_ALEN; dev->tx_queue_len = 25 /* 5 */; memset(dev->broadcast, 0xFF, HIPPI_ALEN); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 49e16b6..112679d 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -158,17 +158,6 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) return 0; } -static int batadv_interface_change_mtu(struct net_device *dev, int new_mtu) -{ - /* check ranges */ - if ((new_mtu < 68) || (new_mtu > batadv_hardif_min_mtu(dev))) - return -EINVAL; - - dev->mtu = new_mtu; - - return 0; -} - /** * batadv_interface_set_rx_mode - set the rx mode of a device * @dev: registered network device to modify @@ -920,7 +909,6 @@ static const struct net_device_ops batadv_netdev_ops = { .ndo_vlan_rx_add_vid = batadv_interface_add_vid, .ndo_vlan_rx_kill_vid = batadv_interface_kill_vid, .ndo_set_mac_address = batadv_interface_set_mac_addr, - .ndo_change_mtu = batadv_interface_change_mtu, .ndo_set_rx_mode = batadv_interface_set_rx_mode, .ndo_start_xmit = batadv_interface_tx, .ndo_validate_addr = eth_validate_addr, @@ -987,6 +975,7 @@ struct net_device *batadv_softif_create(struct net *net, const char *name) dev_net_set(soft_iface, net); soft_iface->rtnl_link_ops = &batadv_link_ops; + soft_iface->max_mtu = batadv_hardif_min_mtu(soft_iface); ret = register_netdevice(soft_iface); if (ret < 0) { diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 16737cd..fc65b14 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -398,6 +398,7 @@ void hsr_dev_setup(struct net_device *dev) random_ether_addr(dev->dev_addr); ether_setup(dev); + dev->min_mtu = 0; dev->header_ops = &hsr_header_ops; dev->netdev_ops = &hsr_device_ops; SET_NETDEV_DEVTYPE(dev, &hsr_type); diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c index fa8237f..21c28b5 100644 --- a/net/phonet/pep-gprs.c +++ b/net/phonet/pep-gprs.c @@ -217,20 +217,10 @@ static netdev_tx_t gprs_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static int gprs_set_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11))) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - static const struct net_device_ops gprs_netdev_ops = { .ndo_open = gprs_open, .ndo_stop = gprs_close, .ndo_start_xmit = gprs_xmit, - .ndo_change_mtu = gprs_set_mtu, }; static void gprs_setup(struct net_device *dev) @@ -239,6 +229,8 @@ static void gprs_setup(struct net_device *dev) dev->type = ARPHRD_PHONET_PIPE; dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->mtu = GPRS_DEFAULT_MTU; + dev->min_mtu = 576; + dev->max_mtu = (PHONET_MAX_MTU - 11); dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = 10; -- cgit v1.1 From 46b3ef4cdf6f84c35eeacfd4c19839e0067fcfdd Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:23 -0400 Subject: s390/net: use net core MTU range checking ctcm: - min_mtu = 576, max_mtu = 65527 netiucv: - min_mtu = 576, max_mtu = 65535 qeth: - min_mtu = 64, max_mtu = 65535 CC: netdev@vger.kernel.org CC: linux-s390@vger.kernel.org CC: Ursula Braun Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/s390/net/ctcm_main.c | 5 ++--- drivers/s390/net/netiucv.c | 24 ++---------------------- drivers/s390/net/qeth_core_main.c | 4 ---- drivers/s390/net/qeth_l2_main.c | 2 ++ drivers/s390/net/qeth_l3_main.c | 2 ++ 5 files changed, 8 insertions(+), 29 deletions(-) diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index ad17fc5..ac65f12 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1032,9 +1032,6 @@ static int ctcm_change_mtu(struct net_device *dev, int new_mtu) struct ctcm_priv *priv; int max_bufsize; - if (new_mtu < 576 || new_mtu > 65527) - return -EINVAL; - priv = dev->ml_priv; max_bufsize = priv->channel[CTCM_READ]->max_bufsize; @@ -1123,6 +1120,8 @@ void static ctcm_dev_setup(struct net_device *dev) dev->type = ARPHRD_SLIP; dev->tx_queue_len = 100; dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->min_mtu = 576; + dev->max_mtu = 65527; } /* diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 2f0f391..2981024 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1428,27 +1428,6 @@ static struct net_device_stats *netiucv_stats (struct net_device * dev) return &priv->stats; } -/** - * netiucv_change_mtu - * @dev: Pointer to interface struct. - * @new_mtu: The new MTU to use for this interface. - * - * Sets MTU of an interface. - * - * Returns 0 on success, -EINVAL if MTU is out of valid range. - * (valid range is 576 .. NETIUCV_MTU_MAX). - */ -static int netiucv_change_mtu(struct net_device * dev, int new_mtu) -{ - IUCV_DBF_TEXT(trace, 3, __func__); - if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) { - IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n"); - return -EINVAL; - } - dev->mtu = new_mtu; - return 0; -} - /* * attributes in sysfs */ @@ -1986,12 +1965,13 @@ static const struct net_device_ops netiucv_netdev_ops = { .ndo_stop = netiucv_close, .ndo_get_stats = netiucv_stats, .ndo_start_xmit = netiucv_tx, - .ndo_change_mtu = netiucv_change_mtu, }; static void netiucv_setup_netdevice(struct net_device *dev) { dev->mtu = NETIUCV_MTU_DEFAULT; + dev->min_mtu = 576; + dev->max_mtu = NETIUCV_MTU_MAX; dev->destructor = netiucv_free_netdevice; dev->hard_header_len = NETIUCV_HDRLEN; dev->addr_len = 0; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 20cf296..e335583 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4202,10 +4202,6 @@ int qeth_change_mtu(struct net_device *dev, int new_mtu) sprintf(dbf_text, "%8x", new_mtu); QETH_CARD_TEXT(card, 4, dbf_text); - if (new_mtu < 64) - return -EINVAL; - if (new_mtu > 65535) - return -EINVAL; if ((!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) && (!qeth_mtu_is_valid(card, new_mtu))) return -EINVAL; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index bb27058..9c921c28 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1107,6 +1107,8 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->ml_priv = card; card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->mtu = card->info.initial_mtu; + card->dev->min_mtu = 64; + card->dev->max_mtu = ETH_MAX_MTU; card->dev->netdev_ops = &qeth_l2_netdev_ops; card->dev->ethtool_ops = (card->info.type != QETH_CARD_TYPE_OSN) ? diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 272d9e7..ac37d05 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3140,6 +3140,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) card->dev->ml_priv = card; card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->mtu = card->info.initial_mtu; + card->dev->min_mtu = 64; + card->dev->max_mtu = ETH_MAX_MTU; card->dev->ethtool_ops = &qeth_l3_ethtool_ops; card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | -- cgit v1.1 From b96f9afee4eb5229c80afa3d97ccf725fb1b89a6 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 13:55:24 -0400 Subject: ipv4/6: use core net MTU range checking ipv4/ip_tunnel: - min_mtu = 68, max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen - preserve all ndo_change_mtu checks for now to prevent regressions ipv6/ip6_tunnel: - min_mtu = 68, max_mtu = 0xFFF8 - dev->hard_header_len - preserve all ndo_change_mtu checks for now to prevent regressions ipv6/ip6_vti: - min_mtu = 1280, max_mtu = 65535 - remove redundant vti6_change_mtu ipv6/sit: - min_mtu = 1280, max_mtu = 0xFFF8 - t_hlen - remove redundant ipip6_tunnel_change_mtu CC: netdev@vger.kernel.org CC: "David S. Miller" CC: Alexey Kuznetsov CC: James Morris CC: Hideaki YOSHIFUJI CC: Patrick McHardy Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- net/ipv4/ip_tunnel.c | 6 +++++- net/ipv6/ip6_tunnel.c | 4 +++- net/ipv6/ip6_vti.c | 21 ++------------------- net/ipv6/sit.c | 14 ++------------ 4 files changed, 12 insertions(+), 33 deletions(-) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 5719d6b..12a92e3 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -358,6 +358,7 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net, { struct ip_tunnel *nt; struct net_device *dev; + int t_hlen; BUG_ON(!itn->fb_tunnel_dev); dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms); @@ -367,6 +368,9 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net, dev->mtu = ip_tunnel_bind_dev(dev); nt = netdev_priv(dev); + t_hlen = nt->hlen + sizeof(struct iphdr); + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen; ip_tunnel_add(itn, nt); return nt; } @@ -929,7 +933,7 @@ int __ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict) int t_hlen = tunnel->hlen + sizeof(struct iphdr); int max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen; - if (new_mtu < 68) + if (new_mtu < ETH_MIN_MTU) return -EINVAL; if (new_mtu > max_mtu) { diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 6a66adb..3a70567 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1634,7 +1634,7 @@ int ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) struct ip6_tnl *tnl = netdev_priv(dev); if (tnl->parms.proto == IPPROTO_IPIP) { - if (new_mtu < 68) + if (new_mtu < ETH_MIN_MTU) return -EINVAL; } else { if (new_mtu < IPV6_MIN_MTU) @@ -1787,6 +1787,8 @@ ip6_tnl_dev_init_gen(struct net_device *dev) dev->mtu = ETH_DATA_LEN - t_hlen; if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) dev->mtu -= 8; + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = 0xFFF8 - dev->hard_header_len; return 0; diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 8a02ca8..35c5b2d 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -812,30 +812,11 @@ vti6_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return err; } -/** - * vti6_tnl_change_mtu - change mtu manually for tunnel device - * @dev: virtual device associated with tunnel - * @new_mtu: the new mtu - * - * Return: - * 0 on success, - * %-EINVAL if mtu too small - **/ -static int vti6_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < IPV6_MIN_MTU) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - static const struct net_device_ops vti6_netdev_ops = { .ndo_init = vti6_dev_init, .ndo_uninit = vti6_dev_uninit, .ndo_start_xmit = vti6_tnl_xmit, .ndo_do_ioctl = vti6_ioctl, - .ndo_change_mtu = vti6_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, .ndo_get_iflink = ip6_tnl_get_iflink, }; @@ -855,6 +836,8 @@ static void vti6_dev_setup(struct net_device *dev) dev->type = ARPHRD_TUNNEL6; dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr); dev->mtu = ETH_DATA_LEN; + dev->min_mtu = IPV6_MIN_MTU; + dev->max_mtu = IP_MAX_MTU; dev->flags |= IFF_NOARP; dev->addr_len = sizeof(struct in6_addr); netif_keep_dst(dev); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index b1cdf80..dc7a344 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1318,23 +1318,11 @@ done: return err; } -static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu) -{ - struct ip_tunnel *tunnel = netdev_priv(dev); - int t_hlen = tunnel->hlen + sizeof(struct iphdr); - - if (new_mtu < IPV6_MIN_MTU || new_mtu > 0xFFF8 - t_hlen) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - static const struct net_device_ops ipip6_netdev_ops = { .ndo_init = ipip6_tunnel_init, .ndo_uninit = ipip6_tunnel_uninit, .ndo_start_xmit = sit_tunnel_xmit, .ndo_do_ioctl = ipip6_tunnel_ioctl, - .ndo_change_mtu = ipip6_tunnel_change_mtu, .ndo_get_stats64 = ip_tunnel_get_stats64, .ndo_get_iflink = ip_tunnel_get_iflink, }; @@ -1365,6 +1353,8 @@ static void ipip6_tunnel_setup(struct net_device *dev) dev->type = ARPHRD_SIT; dev->hard_header_len = LL_MAX_HEADER + t_hlen; dev->mtu = ETH_DATA_LEN - t_hlen; + dev->min_mtu = IPV6_MIN_MTU; + dev->max_mtu = 0xFFF8 - t_hlen; dev->flags = IFF_NOARP; netif_keep_dst(dev); dev->addr_len = 4; -- cgit v1.1 From e8f0a89cd7591c5d4eb7c77833af865b4db356b7 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Wed, 19 Oct 2016 15:53:01 +0200 Subject: hv_netvsc: fix a race between netvsc_send() and netvsc_init_buf() Fix in commit 880988348270 ("hv_netvsc: set nvdev link after populating chn_table") turns out to be incomplete. A crash in netvsc_get_next_send_section() is observed on mtu change when the device is under load. The race I identified is: if we get to netvsc_send() after we set net_device_ctx->nvdev link in netvsc_device_add() but before we finish netvsc_connect_vsp()->netvsc_init_buf() send_section_map is not allocated and we crash. Unfortunately we can't set net_device_ctx->nvdev link after the netvsc_init_buf() call as during the negotiation we need to receive packets and on the receive path we check for it. It would probably be possible to split nvdev into a pair of nvdev_in and nvdev_out links and check them accordingly in get_outbound_net_device()/ get_inbound_net_device() but this looks like an overkill. Check that send_section_map is allocated in netvsc_send(). Signed-off-by: Vitaly Kuznetsov Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 720b5fa..e2bfaac 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -888,6 +888,13 @@ int netvsc_send(struct hv_device *device, if (!net_device) return -ENODEV; + /* We may race with netvsc_connect_vsp()/netvsc_init_buf() and get + * here before the negotiation with the host is finished and + * send_section_map may not be allocated yet. + */ + if (!net_device->send_section_map) + return -EAGAIN; + out_channel = net_device->chn_table[q_idx]; packet->send_buf_index = NETVSC_INVALID_INDEX; -- cgit v1.1 From 8b1efc0f83f1f75b8f85c70d2211007de8fd7633 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 20 Oct 2016 23:25:27 -0400 Subject: net: remove MTU limits on a few ether_setup callers These few drivers call ether_setup(), but have no ndo_change_mtu, and thus were overlooked for changes to MTU range checking behavior. They previously had no range checks, so for feature-parity, set their min_mtu to 0 and max_mtu to ETH_MAX_MTU (65535), instead of the 68 and 1500 inherited from the ether_setup() changes. Fine-tuning can come after we get back to full feature-parity here. CC: netdev@vger.kernel.org Reported-by: Asbjoern Sloth Toennesen CC: Asbjoern Sloth Toennesen CC: R Parameswaran Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- net/atm/br2684.c | 4 +++- net/bluetooth/bnep/netdev.c | 2 ++ net/dsa/slave.c | 2 ++ net/irda/irlan/irlan_eth.c | 3 ++- net/l2tp/l2tp_eth.c | 2 ++ 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/net/atm/br2684.c b/net/atm/br2684.c index c7d82f4..fca84e1 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -649,7 +649,9 @@ static void br2684_setup_routed(struct net_device *netdev) netdev->hard_header_len = sizeof(llc_oui_ipv4); /* worst case */ netdev->netdev_ops = &br2684_netdev_ops_routed; netdev->addr_len = 0; - netdev->mtu = 1500; + netdev->mtu = ETH_DATA_LEN; + netdev->min_mtu = 0; + netdev->max_mtu = ETH_MAX_MTU; netdev->type = ARPHRD_PPP; netdev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; netdev->tx_queue_len = 100; diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 0f25ddc..2b875ed 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -221,6 +221,8 @@ void bnep_net_setup(struct net_device *dev) dev->addr_len = ETH_ALEN; ether_setup(dev); + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->netdev_ops = &bnep_netdev_ops; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 68714a5..d0c7bce 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1247,6 +1247,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, slave_dev->priv_flags |= IFF_NO_QUEUE; slave_dev->netdev_ops = &dsa_slave_netdev_ops; slave_dev->switchdev_ops = &dsa_slave_switchdev_ops; + slave_dev->min_mtu = 0; + slave_dev->max_mtu = ETH_MAX_MTU; SET_NETDEV_DEVTYPE(slave_dev, &dsa_type); netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one, diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 8192eae..74d09f9 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -66,7 +66,8 @@ static void irlan_eth_setup(struct net_device *dev) dev->netdev_ops = &irlan_eth_netdev_ops; dev->destructor = free_netdev; - + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; /* * Lets do all queueing in IrTTP instead of this device driver. diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 965f7e3..e2c6ae0 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -259,6 +259,8 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p session->mtu = dev->mtu - session->hdr_len; dev->mtu = session->mtu; dev->needed_headroom += session->hdr_len; + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; priv = netdev_priv(dev); priv->dev = dev; -- cgit v1.1 From f8c3bf00d440df2bc2c3f669d460868d9ba67226 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 21 Oct 2016 13:55:45 +0200 Subject: net/socket: factor out helpers for memory and queue manipulation Basic sock operations that udp code can use with its own memory accounting schema. No functional change is introduced in the existing APIs. v4 -> v5: - avoid whitespace changes v2 -> v4: - avoid exporting __sock_enqueue_skb v1 -> v2: - avoid export sock_rmem_free Acked-by: Hannes Frederic Sowa Signed-off-by: Paolo Abeni Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 4 ++++ net/core/datagram.c | 36 ++++++++++++++++++------------ net/core/sock.c | 64 +++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index ebf75db..2764895 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1274,7 +1274,9 @@ static inline struct inode *SOCK_INODE(struct socket *socket) /* * Functions for memory accounting */ +int __sk_mem_raise_allocated(struct sock *sk, int size, int amt, int kind); int __sk_mem_schedule(struct sock *sk, int size, int kind); +void __sk_mem_reduce_allocated(struct sock *sk, int amount); void __sk_mem_reclaim(struct sock *sk, int amount); #define SK_MEM_QUANTUM ((int)PAGE_SIZE) @@ -1950,6 +1952,8 @@ void sk_reset_timer(struct sock *sk, struct timer_list *timer, void sk_stop_timer(struct sock *sk, struct timer_list *timer); +int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb, + unsigned int flags); int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); diff --git a/net/core/datagram.c b/net/core/datagram.c index b7de71f..bfb973a 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -323,6 +323,27 @@ void __skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb, int len) } EXPORT_SYMBOL(__skb_free_datagram_locked); +int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb, + unsigned int flags) +{ + int err = 0; + + if (flags & MSG_PEEK) { + err = -ENOENT; + spin_lock_bh(&sk->sk_receive_queue.lock); + if (skb == skb_peek(&sk->sk_receive_queue)) { + __skb_unlink(skb, &sk->sk_receive_queue); + atomic_dec(&skb->users); + err = 0; + } + spin_unlock_bh(&sk->sk_receive_queue.lock); + } + + atomic_inc(&sk->sk_drops); + return err; +} +EXPORT_SYMBOL(__sk_queue_drop_skb); + /** * skb_kill_datagram - Free a datagram skbuff forcibly * @sk: socket @@ -346,23 +367,10 @@ EXPORT_SYMBOL(__skb_free_datagram_locked); int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags) { - int err = 0; - - if (flags & MSG_PEEK) { - err = -ENOENT; - spin_lock_bh(&sk->sk_receive_queue.lock); - if (skb == skb_peek(&sk->sk_receive_queue)) { - __skb_unlink(skb, &sk->sk_receive_queue); - atomic_dec(&skb->users); - err = 0; - } - spin_unlock_bh(&sk->sk_receive_queue.lock); - } + int err = __sk_queue_drop_skb(sk, skb, flags); kfree_skb(skb); - atomic_inc(&sk->sk_drops); sk_mem_reclaim_partial(sk); - return err; } EXPORT_SYMBOL(skb_kill_datagram); diff --git a/net/core/sock.c b/net/core/sock.c index c73e28f..d8e4532e 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2091,24 +2091,18 @@ int sk_wait_data(struct sock *sk, long *timeo, const struct sk_buff *skb) EXPORT_SYMBOL(sk_wait_data); /** - * __sk_mem_schedule - increase sk_forward_alloc and memory_allocated + * __sk_mem_raise_allocated - increase memory_allocated * @sk: socket * @size: memory size to allocate + * @amt: pages to allocate * @kind: allocation type * - * If kind is SK_MEM_SEND, it means wmem allocation. Otherwise it means - * rmem allocation. This function assumes that protocols which have - * memory_pressure use sk_wmem_queued as write buffer accounting. + * Similar to __sk_mem_schedule(), but does not update sk_forward_alloc */ -int __sk_mem_schedule(struct sock *sk, int size, int kind) +int __sk_mem_raise_allocated(struct sock *sk, int size, int amt, int kind) { struct proto *prot = sk->sk_prot; - int amt = sk_mem_pages(size); - long allocated; - - sk->sk_forward_alloc += amt * SK_MEM_QUANTUM; - - allocated = sk_memory_allocated_add(sk, amt); + long allocated = sk_memory_allocated_add(sk, amt); if (mem_cgroup_sockets_enabled && sk->sk_memcg && !mem_cgroup_charge_skmem(sk->sk_memcg, amt)) @@ -2169,9 +2163,6 @@ suppress_allocation: trace_sock_exceed_buf_limit(sk, prot, allocated); - /* Alas. Undo changes. */ - sk->sk_forward_alloc -= amt * SK_MEM_QUANTUM; - sk_memory_allocated_sub(sk, amt); if (mem_cgroup_sockets_enabled && sk->sk_memcg) @@ -2179,18 +2170,40 @@ suppress_allocation: return 0; } +EXPORT_SYMBOL(__sk_mem_raise_allocated); + +/** + * __sk_mem_schedule - increase sk_forward_alloc and memory_allocated + * @sk: socket + * @size: memory size to allocate + * @kind: allocation type + * + * If kind is SK_MEM_SEND, it means wmem allocation. Otherwise it means + * rmem allocation. This function assumes that protocols which have + * memory_pressure use sk_wmem_queued as write buffer accounting. + */ +int __sk_mem_schedule(struct sock *sk, int size, int kind) +{ + int ret, amt = sk_mem_pages(size); + + sk->sk_forward_alloc += amt << SK_MEM_QUANTUM_SHIFT; + ret = __sk_mem_raise_allocated(sk, size, amt, kind); + if (!ret) + sk->sk_forward_alloc -= amt << SK_MEM_QUANTUM_SHIFT; + return ret; +} EXPORT_SYMBOL(__sk_mem_schedule); /** - * __sk_mem_reclaim - reclaim memory_allocated + * __sk_mem_reduce_allocated - reclaim memory_allocated * @sk: socket - * @amount: number of bytes (rounded down to a SK_MEM_QUANTUM multiple) + * @amount: number of quanta + * + * Similar to __sk_mem_reclaim(), but does not update sk_forward_alloc */ -void __sk_mem_reclaim(struct sock *sk, int amount) +void __sk_mem_reduce_allocated(struct sock *sk, int amount) { - amount >>= SK_MEM_QUANTUM_SHIFT; sk_memory_allocated_sub(sk, amount); - sk->sk_forward_alloc -= amount << SK_MEM_QUANTUM_SHIFT; if (mem_cgroup_sockets_enabled && sk->sk_memcg) mem_cgroup_uncharge_skmem(sk->sk_memcg, amount); @@ -2199,6 +2212,19 @@ void __sk_mem_reclaim(struct sock *sk, int amount) (sk_memory_allocated(sk) < sk_prot_mem_limits(sk, 0))) sk_leave_memory_pressure(sk); } +EXPORT_SYMBOL(__sk_mem_reduce_allocated); + +/** + * __sk_mem_reclaim - reclaim sk_forward_alloc and memory_allocated + * @sk: socket + * @amount: number of bytes (rounded down to a SK_MEM_QUANTUM multiple) + */ +void __sk_mem_reclaim(struct sock *sk, int amount) +{ + amount >>= SK_MEM_QUANTUM_SHIFT; + sk->sk_forward_alloc -= amount << SK_MEM_QUANTUM_SHIFT; + __sk_mem_reduce_allocated(sk, amount); +} EXPORT_SYMBOL(__sk_mem_reclaim); int sk_set_peek_off(struct sock *sk, int val) -- cgit v1.1 From f970bd9e3a06f06df8d8ecf1f8ad2c8615cc17eb Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 21 Oct 2016 13:55:46 +0200 Subject: udp: implement memory accounting helpers Avoid using the generic helpers. Use the receive queue spin lock to protect the memory accounting operation, both on enqueue and on dequeue. On dequeue perform partial memory reclaiming, trying to leave a quantum of forward allocated memory. On enqueue use a custom helper, to allow some optimizations: - use a plain spin_lock() variant instead of the slightly costly spin_lock_irqsave(), - avoid dst_force check, since the calling code has already dropped the skb dst - avoid orphaning the skb, since skb_steal_sock() already did the work for us The above needs custom memory reclaiming on shutdown, provided by the udp_destruct_sock(). v5 -> v6: - don't orphan the skb on enqueue v4 -> v5: - replace the mem_lock with the receive queue spin lock - ensure that the bh is always allowed to enqueue at least a skb, even if sk_rcvbuf is exceeded v3 -> v4: - reworked memory accunting, simplifying the schema - provide an helper for both memory scheduling and enqueuing v1 -> v2: - use a udp specific destrctor to perform memory reclaiming - remove a couple of helpers, unneeded after the above cleanup - do not reclaim memory on dequeue if not under memory pressure - reworked the fwd accounting schema to avoid potential integer overflow Acked-by: Hannes Frederic Sowa Signed-off-by: Paolo Abeni Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/udp.h | 4 +++ net/ipv4/udp.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/include/net/udp.h b/include/net/udp.h index ea53a87..18f1e6b 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -246,6 +246,9 @@ static inline __be16 udp_flow_src_port(struct net *net, struct sk_buff *skb, } /* net/ipv4/udp.c */ +void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len); +int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb); + void udp_v4_early_demux(struct sk_buff *skb); int udp_get_port(struct sock *sk, unsigned short snum, int (*saddr_cmp)(const struct sock *, @@ -258,6 +261,7 @@ void udp_flush_pending_frames(struct sock *sk); void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); int udp_rcv(struct sk_buff *skb); int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); +int udp_init_sock(struct sock *sk); int udp_disconnect(struct sock *sk, int flags); unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait); struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 7d96dc2..be3c331 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1172,6 +1172,112 @@ out: return ret; } +static void udp_rmem_release(struct sock *sk, int size, int partial) +{ + int amt; + + atomic_sub(size, &sk->sk_rmem_alloc); + + spin_lock_bh(&sk->sk_receive_queue.lock); + sk->sk_forward_alloc += size; + amt = (sk->sk_forward_alloc - partial) & ~(SK_MEM_QUANTUM - 1); + sk->sk_forward_alloc -= amt; + spin_unlock_bh(&sk->sk_receive_queue.lock); + + if (amt) + __sk_mem_reduce_allocated(sk, amt >> SK_MEM_QUANTUM_SHIFT); +} + +static void udp_rmem_free(struct sk_buff *skb) +{ + udp_rmem_release(skb->sk, skb->truesize, 1); +} + +int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) +{ + struct sk_buff_head *list = &sk->sk_receive_queue; + int rmem, delta, amt, err = -ENOMEM; + int size = skb->truesize; + + /* try to avoid the costly atomic add/sub pair when the receive + * queue is full; always allow at least a packet + */ + rmem = atomic_read(&sk->sk_rmem_alloc); + if (rmem && (rmem + size > sk->sk_rcvbuf)) + goto drop; + + /* we drop only if the receive buf is full and the receive + * queue contains some other skb + */ + rmem = atomic_add_return(size, &sk->sk_rmem_alloc); + if ((rmem > sk->sk_rcvbuf) && (rmem > size)) + goto uncharge_drop; + + spin_lock(&list->lock); + if (size >= sk->sk_forward_alloc) { + amt = sk_mem_pages(size); + delta = amt << SK_MEM_QUANTUM_SHIFT; + if (!__sk_mem_raise_allocated(sk, delta, amt, SK_MEM_RECV)) { + err = -ENOBUFS; + spin_unlock(&list->lock); + goto uncharge_drop; + } + + sk->sk_forward_alloc += delta; + } + + sk->sk_forward_alloc -= size; + + /* the skb owner in now the udp socket */ + skb->sk = sk; + skb->destructor = udp_rmem_free; + skb->dev = NULL; + sock_skb_set_dropcount(sk, skb); + + __skb_queue_tail(list, skb); + spin_unlock(&list->lock); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk); + + return 0; + +uncharge_drop: + atomic_sub(skb->truesize, &sk->sk_rmem_alloc); + +drop: + atomic_inc(&sk->sk_drops); + return err; +} +EXPORT_SYMBOL_GPL(__udp_enqueue_schedule_skb); + +static void udp_destruct_sock(struct sock *sk) +{ + /* reclaim completely the forward allocated memory */ + __skb_queue_purge(&sk->sk_receive_queue); + udp_rmem_release(sk, 0, 0); + inet_sock_destruct(sk); +} + +int udp_init_sock(struct sock *sk) +{ + sk->sk_destruct = udp_destruct_sock; + return 0; +} +EXPORT_SYMBOL_GPL(udp_init_sock); + +void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len) +{ + if (unlikely(READ_ONCE(sk->sk_peek_off) >= 0)) { + bool slow = lock_sock_fast(sk); + + sk_peek_offset_bwd(sk, len); + unlock_sock_fast(sk, slow); + } + consume_skb(skb); +} +EXPORT_SYMBOL_GPL(skb_consume_udp); + /** * first_packet_length - return length of first packet in receive queue * @sk: socket -- cgit v1.1 From 850cbaddb52dfd4e0c7cabe2c168dd34b44ae0b9 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 21 Oct 2016 13:55:47 +0200 Subject: udp: use it's own memory accounting schema Completely avoid default sock memory accounting and replace it with udp-specific accounting. Since the new memory accounting model encapsulates completely the required locking, remove the socket lock on both enqueue and dequeue, and avoid using the backlog on enqueue. Be sure to clean-up rx queue memory on socket destruction, using udp its own sk_destruct. Tested using pktgen with random src port, 64 bytes packet, wire-speed on a 10G link as sender and udp_sink as the receiver, using an l4 tuple rxhash to stress the contention, and one or more udp_sink instances with reuseport. nr readers Kpps (vanilla) Kpps (patched) 1 170 440 3 1250 2150 6 3000 3650 9 4200 4450 12 5700 6250 v4 -> v5: - avoid unneeded test in first_packet_length v3 -> v4: - remove useless sk_rcvqueues_full() call v2 -> v3: - do not set the now unsed backlog_rcv callback v1 -> v2: - add memory pressure support - fixed dropwatch accounting for ipv6 Acked-by: Hannes Frederic Sowa Signed-off-by: Paolo Abeni Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/udp.c | 43 ++++++++----------------------------------- net/ipv6/udp.c | 34 ++++++++-------------------------- net/sunrpc/svcsock.c | 20 ++++++++++++++++---- net/sunrpc/xprtsock.c | 2 +- 4 files changed, 33 insertions(+), 66 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index be3c331..c833271 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1307,13 +1307,7 @@ static int first_packet_length(struct sock *sk) res = skb ? skb->len : -1; spin_unlock_bh(&rcvq->lock); - if (!skb_queue_empty(&list_kill)) { - bool slow = lock_sock_fast(sk); - - __skb_queue_purge(&list_kill); - sk_mem_reclaim_partial(sk); - unlock_sock_fast(sk, slow); - } + __skb_queue_purge(&list_kill); return res; } @@ -1362,7 +1356,6 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, int err; int is_udplite = IS_UDPLITE(sk); bool checksum_valid = false; - bool slow; if (flags & MSG_ERRQUEUE) return ip_recv_error(sk, msg, len, addr_len); @@ -1403,13 +1396,12 @@ try_again: } if (unlikely(err)) { - trace_kfree_skb(skb, udp_recvmsg); if (!peeked) { atomic_inc(&sk->sk_drops); UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); } - skb_free_datagram_locked(sk, skb); + kfree_skb(skb); return err; } @@ -1434,16 +1426,15 @@ try_again: if (flags & MSG_TRUNC) err = ulen; - __skb_free_datagram_locked(sk, skb, peeking ? -err : err); + skb_consume_udp(sk, skb, peeking ? -err : err); return err; csum_copy_err: - slow = lock_sock_fast(sk); - if (!skb_kill_datagram(sk, skb, flags)) { + if (!__sk_queue_drop_skb(sk, skb, flags)) { UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite); UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); } - unlock_sock_fast(sk, slow); + kfree_skb(skb); /* starting over for a new packet, but check if we need to yield */ cond_resched(); @@ -1562,7 +1553,7 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) sk_incoming_cpu_update(sk); } - rc = __sock_queue_rcv_skb(sk, skb); + rc = __udp_enqueue_schedule_skb(sk, skb); if (rc < 0) { int is_udplite = IS_UDPLITE(sk); @@ -1577,7 +1568,6 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) } return 0; - } static struct static_key udp_encap_needed __read_mostly; @@ -1599,7 +1589,6 @@ EXPORT_SYMBOL(udp_encap_enable); int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct udp_sock *up = udp_sk(sk); - int rc; int is_udplite = IS_UDPLITE(sk); /* @@ -1686,25 +1675,9 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) goto drop; udp_csum_pull_header(skb); - if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) { - __UDP_INC_STATS(sock_net(sk), UDP_MIB_RCVBUFERRORS, - is_udplite); - goto drop; - } - - rc = 0; ipv4_pktinfo_prepare(sk, skb); - bh_lock_sock(sk); - if (!sock_owned_by_user(sk)) - rc = __udp_queue_rcv_skb(sk, skb); - else if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) { - bh_unlock_sock(sk); - goto drop; - } - bh_unlock_sock(sk); - - return rc; + return __udp_queue_rcv_skb(sk, skb); csum_error: __UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite); @@ -2314,13 +2287,13 @@ struct proto udp_prot = { .connect = ip4_datagram_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, + .init = udp_init_sock, .destroy = udp_destroy_sock, .setsockopt = udp_setsockopt, .getsockopt = udp_getsockopt, .sendmsg = udp_sendmsg, .recvmsg = udp_recvmsg, .sendpage = udp_sendpage, - .backlog_rcv = __udp_queue_rcv_skb, .release_cb = ip4_datagram_release_cb, .hash = udp_lib_hash, .unhash = udp_lib_unhash, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9aa7c1c..71963b2 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -334,7 +334,6 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int is_udplite = IS_UDPLITE(sk); bool checksum_valid = false; int is_udp4; - bool slow; if (flags & MSG_ERRQUEUE) return ipv6_recv_error(sk, msg, len, addr_len); @@ -378,7 +377,6 @@ try_again: goto csum_copy_err; } if (unlikely(err)) { - trace_kfree_skb(skb, udpv6_recvmsg); if (!peeked) { atomic_inc(&sk->sk_drops); if (is_udp4) @@ -388,7 +386,7 @@ try_again: UDP6_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); } - skb_free_datagram_locked(sk, skb); + kfree_skb(skb); return err; } if (!peeked) { @@ -437,12 +435,11 @@ try_again: if (flags & MSG_TRUNC) err = ulen; - __skb_free_datagram_locked(sk, skb, peeking ? -err : err); + skb_consume_udp(sk, skb, peeking ? -err : err); return err; csum_copy_err: - slow = lock_sock_fast(sk); - if (!skb_kill_datagram(sk, skb, flags)) { + if (!__sk_queue_drop_skb(sk, skb, flags)) { if (is_udp4) { UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite); @@ -455,7 +452,7 @@ csum_copy_err: UDP_MIB_INERRORS, is_udplite); } } - unlock_sock_fast(sk, slow); + kfree_skb(skb); /* starting over for a new packet, but check if we need to yield */ cond_resched(); @@ -523,7 +520,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) sk_incoming_cpu_update(sk); } - rc = __sock_queue_rcv_skb(sk, skb); + rc = __udp_enqueue_schedule_skb(sk, skb); if (rc < 0) { int is_udplite = IS_UDPLITE(sk); @@ -535,6 +532,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) kfree_skb(skb); return -1; } + return 0; } @@ -556,7 +554,6 @@ EXPORT_SYMBOL(udpv6_encap_enable); int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct udp_sock *up = udp_sk(sk); - int rc; int is_udplite = IS_UDPLITE(sk); if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) @@ -622,25 +619,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) goto drop; udp_csum_pull_header(skb); - if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) { - __UDP6_INC_STATS(sock_net(sk), - UDP_MIB_RCVBUFERRORS, is_udplite); - goto drop; - } skb_dst_drop(skb); - bh_lock_sock(sk); - rc = 0; - if (!sock_owned_by_user(sk)) - rc = __udpv6_queue_rcv_skb(sk, skb); - else if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) { - bh_unlock_sock(sk); - goto drop; - } - bh_unlock_sock(sk); - - return rc; + return __udpv6_queue_rcv_skb(sk, skb); csum_error: __UDP6_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite); @@ -1433,12 +1415,12 @@ struct proto udpv6_prot = { .connect = ip6_datagram_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, + .init = udp_init_sock, .destroy = udpv6_destroy_sock, .setsockopt = udpv6_setsockopt, .getsockopt = udpv6_getsockopt, .sendmsg = udpv6_sendmsg, .recvmsg = udpv6_recvmsg, - .backlog_rcv = __udpv6_queue_rcv_skb, .release_cb = ip6_datagram_release_cb, .hash = udp_lib_hash, .unhash = udp_lib_unhash, diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 57625f6..e2a55dc 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,18 @@ static void svc_release_skb(struct svc_rqst *rqstp) } } +static void svc_release_udp_skb(struct svc_rqst *rqstp) +{ + struct sk_buff *skb = rqstp->rq_xprt_ctxt; + + if (skb) { + rqstp->rq_xprt_ctxt = NULL; + + dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); + consume_skb(skb); + } +} + union svc_pktinfo_u { struct in_pktinfo pkti; struct in6_pktinfo pkti6; @@ -575,7 +588,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) goto out_free; } local_bh_enable(); - skb_free_datagram_locked(svsk->sk_sk, skb); + consume_skb(skb); } else { /* we can use it in-place */ rqstp->rq_arg.head[0].iov_base = skb->data; @@ -602,8 +615,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) return len; out_free: - trace_kfree_skb(skb, svc_udp_recvfrom); - skb_free_datagram_locked(svsk->sk_sk, skb); + kfree_skb(skb); return 0; } @@ -660,7 +672,7 @@ static struct svc_xprt_ops svc_udp_ops = { .xpo_create = svc_udp_create, .xpo_recvfrom = svc_udp_recvfrom, .xpo_sendto = svc_udp_sendto, - .xpo_release_rqst = svc_release_skb, + .xpo_release_rqst = svc_release_udp_skb, .xpo_detach = svc_sock_detach, .xpo_free = svc_sock_free, .xpo_prep_reply_hdr = svc_udp_prep_reply_hdr, diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 0137af1..1758665 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1083,7 +1083,7 @@ static void xs_udp_data_receive(struct sock_xprt *transport) skb = skb_recv_datagram(sk, 0, 1, &err); if (skb != NULL) { xs_udp_data_read_skb(&transport->xprt, sk, skb); - skb_free_datagram_locked(sk, skb); + consume_skb(skb); continue; } if (!test_and_clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state)) -- cgit v1.1 From 2d0e30c30f84d08dc16f0f2af41f1b8a85f0755e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 21 Oct 2016 12:46:33 +0200 Subject: bpf: add helper for retrieving current numa node id Use case is mainly for soreuseport to select sockets for the local numa node, but since generic, lets also add this for other networking and tracing program types. Suggested-by: Eric Dumazet Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 6 ++++++ kernel/bpf/core.c | 1 + kernel/bpf/helpers.c | 12 ++++++++++++ kernel/trace/bpf_trace.c | 2 ++ net/core/filter.c | 2 ++ 6 files changed, 24 insertions(+) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index c201017..edcd96d 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -319,6 +319,7 @@ extern const struct bpf_func_proto bpf_map_delete_elem_proto; extern const struct bpf_func_proto bpf_get_prandom_u32_proto; extern const struct bpf_func_proto bpf_get_smp_processor_id_proto; +extern const struct bpf_func_proto bpf_get_numa_node_id_proto; extern const struct bpf_func_proto bpf_tail_call_proto; extern const struct bpf_func_proto bpf_ktime_get_ns_proto; extern const struct bpf_func_proto bpf_get_current_pid_tgid_proto; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index f09c70b..374ef58 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -426,6 +426,12 @@ enum bpf_func_id { */ BPF_FUNC_set_hash_invalid, + /** + * bpf_get_numa_node_id() + * Returns the id of the current NUMA node. + */ + BPF_FUNC_get_numa_node_id, + __BPF_FUNC_MAX_ID, }; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index aa6d981..82a0414 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1043,6 +1043,7 @@ const struct bpf_func_proto bpf_map_delete_elem_proto __weak; const struct bpf_func_proto bpf_get_prandom_u32_proto __weak; const struct bpf_func_proto bpf_get_smp_processor_id_proto __weak; +const struct bpf_func_proto bpf_get_numa_node_id_proto __weak; const struct bpf_func_proto bpf_ktime_get_ns_proto __weak; const struct bpf_func_proto bpf_get_current_pid_tgid_proto __weak; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 3991840..045cbe6 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,17 @@ const struct bpf_func_proto bpf_get_smp_processor_id_proto = { .ret_type = RET_INTEGER, }; +BPF_CALL_0(bpf_get_numa_node_id) +{ + return numa_node_id(); +} + +const struct bpf_func_proto bpf_get_numa_node_id_proto = { + .func = bpf_get_numa_node_id, + .gpl_only = false, + .ret_type = RET_INTEGER, +}; + BPF_CALL_0(bpf_ktime_get_ns) { /* NMI safe access to clock monotonic */ diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 5dcb992..fa77311 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -422,6 +422,8 @@ static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id) return bpf_get_trace_printk_proto(); case BPF_FUNC_get_smp_processor_id: return &bpf_get_smp_processor_id_proto; + case BPF_FUNC_get_numa_node_id: + return &bpf_get_numa_node_id_proto; case BPF_FUNC_perf_event_read: return &bpf_perf_event_read_proto; case BPF_FUNC_probe_write_user: diff --git a/net/core/filter.c b/net/core/filter.c index 00351cd..cd9e2ba 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2492,6 +2492,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) return &bpf_get_prandom_u32_proto; case BPF_FUNC_get_smp_processor_id: return &bpf_get_raw_smp_processor_id_proto; + case BPF_FUNC_get_numa_node_id: + return &bpf_get_numa_node_id_proto; case BPF_FUNC_tail_call: return &bpf_tail_call_proto; case BPF_FUNC_ktime_get_ns: -- cgit v1.1 From 3c2c3c16aaf6a83902beb6063488e2c907de62e9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 21 Oct 2016 12:46:34 +0200 Subject: reuseport, bpf: add test case for bpf_get_numa_node_id The test case is very similar to reuseport_bpf_cpu, only that here we select socket members based on current numa node id. # numactl -H available: 2 nodes (0-1) node 0 cpus: 0 1 2 3 4 5 12 13 14 15 16 17 node 0 size: 128867 MB node 0 free: 120080 MB node 1 cpus: 6 7 8 9 10 11 18 19 20 21 22 23 node 1 size: 96765 MB node 1 free: 87504 MB node distances: node 0 1 0: 10 20 1: 20 10 # ./reuseport_bpf_numa ---- IPv4 UDP ---- send node 0, receive socket 0 send node 1, receive socket 1 send node 1, receive socket 1 send node 0, receive socket 0 ---- IPv6 UDP ---- send node 0, receive socket 0 send node 1, receive socket 1 send node 1, receive socket 1 send node 0, receive socket 0 ---- IPv4 TCP ---- send node 0, receive socket 0 send node 1, receive socket 1 send node 1, receive socket 1 send node 0, receive socket 0 ---- IPv6 TCP ---- send node 0, receive socket 0 send node 1, receive socket 1 send node 1, receive socket 1 send node 0, receive socket 0 SUCCESS Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/testing/selftests/net/.gitignore | 1 + tools/testing/selftests/net/Makefile | 11 +- tools/testing/selftests/net/reuseport_bpf_numa.c | 255 +++++++++++++++++++++++ 3 files changed, 263 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/net/reuseport_bpf_numa.c diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 0840684..afe109e 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -3,4 +3,5 @@ psock_fanout psock_tpacket reuseport_bpf reuseport_bpf_cpu +reuseport_bpf_numa reuseport_dualstack diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 0e53407..e24e4c8 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -1,14 +1,17 @@ # Makefile for net selftests -CFLAGS = -Wall -O2 -g - +CFLAGS = -Wall -Wl,--no-as-needed -O2 -g CFLAGS += -I../../../../usr/include/ -NET_PROGS = socket psock_fanout psock_tpacket reuseport_bpf reuseport_bpf_cpu reuseport_dualstack +NET_PROGS = socket +NET_PROGS += psock_fanout psock_tpacket +NET_PROGS += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa +NET_PROGS += reuseport_dualstack all: $(NET_PROGS) +reuseport_bpf_numa: LDFLAGS += -lnuma %: %.c - $(CC) $(CFLAGS) -o $@ $^ + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh TEST_FILES := $(NET_PROGS) diff --git a/tools/testing/selftests/net/reuseport_bpf_numa.c b/tools/testing/selftests/net/reuseport_bpf_numa.c new file mode 100644 index 0000000..6f20bc9 --- /dev/null +++ b/tools/testing/selftests/net/reuseport_bpf_numa.c @@ -0,0 +1,255 @@ +/* + * Test functionality of BPF filters with SO_REUSEPORT. Same test as + * in reuseport_bpf_cpu, only as one socket per NUMA node. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const int PORT = 8888; + +static void build_rcv_group(int *rcv_fd, size_t len, int family, int proto) +{ + struct sockaddr_storage addr; + struct sockaddr_in *addr4; + struct sockaddr_in6 *addr6; + size_t i; + int opt; + + switch (family) { + case AF_INET: + addr4 = (struct sockaddr_in *)&addr; + addr4->sin_family = AF_INET; + addr4->sin_addr.s_addr = htonl(INADDR_ANY); + addr4->sin_port = htons(PORT); + break; + case AF_INET6: + addr6 = (struct sockaddr_in6 *)&addr; + addr6->sin6_family = AF_INET6; + addr6->sin6_addr = in6addr_any; + addr6->sin6_port = htons(PORT); + break; + default: + error(1, 0, "Unsupported family %d", family); + } + + for (i = 0; i < len; ++i) { + rcv_fd[i] = socket(family, proto, 0); + if (rcv_fd[i] < 0) + error(1, errno, "failed to create receive socket"); + + opt = 1; + if (setsockopt(rcv_fd[i], SOL_SOCKET, SO_REUSEPORT, &opt, + sizeof(opt))) + error(1, errno, "failed to set SO_REUSEPORT"); + + if (bind(rcv_fd[i], (struct sockaddr *)&addr, sizeof(addr))) + error(1, errno, "failed to bind receive socket"); + + if (proto == SOCK_STREAM && listen(rcv_fd[i], len * 10)) + error(1, errno, "failed to listen on receive port"); + } +} + +static void attach_bpf(int fd) +{ + static char bpf_log_buf[65536]; + static const char bpf_license[] = ""; + + int bpf_fd; + const struct bpf_insn prog[] = { + /* R0 = bpf_get_numa_node_id() */ + { BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_numa_node_id }, + /* return R0 */ + { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 } + }; + union bpf_attr attr; + + memset(&attr, 0, sizeof(attr)); + attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; + attr.insn_cnt = sizeof(prog) / sizeof(prog[0]); + attr.insns = (unsigned long) &prog; + attr.license = (unsigned long) &bpf_license; + attr.log_buf = (unsigned long) &bpf_log_buf; + attr.log_size = sizeof(bpf_log_buf); + attr.log_level = 1; + + bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); + if (bpf_fd < 0) + error(1, errno, "ebpf error. log:\n%s\n", bpf_log_buf); + + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd, + sizeof(bpf_fd))) + error(1, errno, "failed to set SO_ATTACH_REUSEPORT_EBPF"); + + close(bpf_fd); +} + +static void send_from_node(int node_id, int family, int proto) +{ + struct sockaddr_storage saddr, daddr; + struct sockaddr_in *saddr4, *daddr4; + struct sockaddr_in6 *saddr6, *daddr6; + int fd; + + switch (family) { + case AF_INET: + saddr4 = (struct sockaddr_in *)&saddr; + saddr4->sin_family = AF_INET; + saddr4->sin_addr.s_addr = htonl(INADDR_ANY); + saddr4->sin_port = 0; + + daddr4 = (struct sockaddr_in *)&daddr; + daddr4->sin_family = AF_INET; + daddr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + daddr4->sin_port = htons(PORT); + break; + case AF_INET6: + saddr6 = (struct sockaddr_in6 *)&saddr; + saddr6->sin6_family = AF_INET6; + saddr6->sin6_addr = in6addr_any; + saddr6->sin6_port = 0; + + daddr6 = (struct sockaddr_in6 *)&daddr; + daddr6->sin6_family = AF_INET6; + daddr6->sin6_addr = in6addr_loopback; + daddr6->sin6_port = htons(PORT); + break; + default: + error(1, 0, "Unsupported family %d", family); + } + + if (numa_run_on_node(node_id) < 0) + error(1, errno, "failed to pin to node"); + + fd = socket(family, proto, 0); + if (fd < 0) + error(1, errno, "failed to create send socket"); + + if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) + error(1, errno, "failed to bind send socket"); + + if (connect(fd, (struct sockaddr *)&daddr, sizeof(daddr))) + error(1, errno, "failed to connect send socket"); + + if (send(fd, "a", 1, 0) < 0) + error(1, errno, "failed to send message"); + + close(fd); +} + +static +void receive_on_node(int *rcv_fd, int len, int epfd, int node_id, int proto) +{ + struct epoll_event ev; + int i, fd; + char buf[8]; + + i = epoll_wait(epfd, &ev, 1, -1); + if (i < 0) + error(1, errno, "epoll_wait failed"); + + if (proto == SOCK_STREAM) { + fd = accept(ev.data.fd, NULL, NULL); + if (fd < 0) + error(1, errno, "failed to accept"); + i = recv(fd, buf, sizeof(buf), 0); + close(fd); + } else { + i = recv(ev.data.fd, buf, sizeof(buf), 0); + } + + if (i < 0) + error(1, errno, "failed to recv"); + + for (i = 0; i < len; ++i) + if (ev.data.fd == rcv_fd[i]) + break; + if (i == len) + error(1, 0, "failed to find socket"); + fprintf(stderr, "send node %d, receive socket %d\n", node_id, i); + if (node_id != i) + error(1, 0, "node id/receive socket mismatch"); +} + +static void test(int *rcv_fd, int len, int family, int proto) +{ + struct epoll_event ev; + int epfd, node; + + build_rcv_group(rcv_fd, len, family, proto); + attach_bpf(rcv_fd[0]); + + epfd = epoll_create(1); + if (epfd < 0) + error(1, errno, "failed to create epoll"); + for (node = 0; node < len; ++node) { + ev.events = EPOLLIN; + ev.data.fd = rcv_fd[node]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fd[node], &ev)) + error(1, errno, "failed to register sock epoll"); + } + + /* Forward iterate */ + for (node = 0; node < len; ++node) { + send_from_node(node, family, proto); + receive_on_node(rcv_fd, len, epfd, node, proto); + } + + /* Reverse iterate */ + for (node = len - 1; node >= 0; --node) { + send_from_node(node, family, proto); + receive_on_node(rcv_fd, len, epfd, node, proto); + } + + close(epfd); + for (node = 0; node < len; ++node) + close(rcv_fd[node]); +} + +int main(void) +{ + int *rcv_fd, nodes; + + if (numa_available() < 0) + error(1, errno, "no numa api support"); + + nodes = numa_max_node() + 1; + + rcv_fd = calloc(nodes, sizeof(int)); + if (!rcv_fd) + error(1, 0, "failed to allocate array"); + + fprintf(stderr, "---- IPv4 UDP ----\n"); + test(rcv_fd, nodes, AF_INET, SOCK_DGRAM); + + fprintf(stderr, "---- IPv6 UDP ----\n"); + test(rcv_fd, nodes, AF_INET6, SOCK_DGRAM); + + fprintf(stderr, "---- IPv4 TCP ----\n"); + test(rcv_fd, nodes, AF_INET, SOCK_STREAM); + + fprintf(stderr, "---- IPv6 TCP ----\n"); + test(rcv_fd, nodes, AF_INET6, SOCK_STREAM); + + free(rcv_fd); + + fprintf(stderr, "SUCCESS\n"); + return 0; +} -- cgit v1.1 From fe0612dc902a299c78f46c776038b91ecf64e8d8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 21 Oct 2016 16:07:18 +0200 Subject: mlxsw: item: Make struct mlxsw_item args const These should be const, so enforce it. Signed-off-by: Jiri Pirko Acked-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/item.h | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/item.h b/drivers/net/ethernet/mellanox/mlxsw/item.h index a94dbda6..1c55a34 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/item.h +++ b/drivers/net/ethernet/mellanox/mlxsw/item.h @@ -55,7 +55,7 @@ struct mlxsw_item { }; static inline unsigned int -__mlxsw_item_offset(struct mlxsw_item *item, unsigned short index, +__mlxsw_item_offset(const struct mlxsw_item *item, unsigned short index, size_t typesize) { BUG_ON(index && !item->step); @@ -72,7 +72,7 @@ __mlxsw_item_offset(struct mlxsw_item *item, unsigned short index, typesize); } -static inline u16 __mlxsw_item_get16(char *buf, struct mlxsw_item *item, +static inline u16 __mlxsw_item_get16(char *buf, const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(u16)); @@ -87,7 +87,7 @@ static inline u16 __mlxsw_item_get16(char *buf, struct mlxsw_item *item, return tmp; } -static inline void __mlxsw_item_set16(char *buf, struct mlxsw_item *item, +static inline void __mlxsw_item_set16(char *buf, const struct mlxsw_item *item, unsigned short index, u16 val) { unsigned int offset = __mlxsw_item_offset(item, index, @@ -105,7 +105,7 @@ static inline void __mlxsw_item_set16(char *buf, struct mlxsw_item *item, b[offset] = cpu_to_be16(tmp); } -static inline u32 __mlxsw_item_get32(char *buf, struct mlxsw_item *item, +static inline u32 __mlxsw_item_get32(char *buf, const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(u32)); @@ -120,7 +120,7 @@ static inline u32 __mlxsw_item_get32(char *buf, struct mlxsw_item *item, return tmp; } -static inline void __mlxsw_item_set32(char *buf, struct mlxsw_item *item, +static inline void __mlxsw_item_set32(char *buf, const struct mlxsw_item *item, unsigned short index, u32 val) { unsigned int offset = __mlxsw_item_offset(item, index, @@ -138,7 +138,7 @@ static inline void __mlxsw_item_set32(char *buf, struct mlxsw_item *item, b[offset] = cpu_to_be32(tmp); } -static inline u64 __mlxsw_item_get64(char *buf, struct mlxsw_item *item, +static inline u64 __mlxsw_item_get64(char *buf, const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(u64)); @@ -153,7 +153,7 @@ static inline u64 __mlxsw_item_get64(char *buf, struct mlxsw_item *item, return tmp; } -static inline void __mlxsw_item_set64(char *buf, struct mlxsw_item *item, +static inline void __mlxsw_item_set64(char *buf, const struct mlxsw_item *item, unsigned short index, u64 val) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(u64)); @@ -171,7 +171,7 @@ static inline void __mlxsw_item_set64(char *buf, struct mlxsw_item *item, } static inline void __mlxsw_item_memcpy_from(char *buf, char *dst, - struct mlxsw_item *item, + const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char)); @@ -180,7 +180,7 @@ static inline void __mlxsw_item_memcpy_from(char *buf, char *dst, } static inline void __mlxsw_item_memcpy_to(char *buf, const char *src, - struct mlxsw_item *item, + const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char)); @@ -189,7 +189,8 @@ static inline void __mlxsw_item_memcpy_to(char *buf, const char *src, } static inline u16 -__mlxsw_item_bit_array_offset(struct mlxsw_item *item, u16 index, u8 *shift) +__mlxsw_item_bit_array_offset(const struct mlxsw_item *item, + u16 index, u8 *shift) { u16 max_index, be_index; u16 offset; /* byte offset inside the array */ @@ -212,7 +213,8 @@ __mlxsw_item_bit_array_offset(struct mlxsw_item *item, u16 index, u8 *shift) return item->offset + offset; } -static inline u8 __mlxsw_item_bit_array_get(char *buf, struct mlxsw_item *item, +static inline u8 __mlxsw_item_bit_array_get(char *buf, + const struct mlxsw_item *item, u16 index) { u8 shift, tmp; @@ -224,7 +226,8 @@ static inline u8 __mlxsw_item_bit_array_get(char *buf, struct mlxsw_item *item, return tmp; } -static inline void __mlxsw_item_bit_array_set(char *buf, struct mlxsw_item *item, +static inline void __mlxsw_item_bit_array_set(char *buf, + const struct mlxsw_item *item, u16 index, u8 val) { u8 shift, tmp; -- cgit v1.1 From 412791df394bbf3547204b9875c7f5648e3dcf91 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 21 Oct 2016 16:07:19 +0200 Subject: mlxsw: item: Make char *buf arg constant for getters Enforce const for getter buf args. Signed-off-by: Jiri Pirko Acked-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/item.h | 31 ++++++++++++++------------ drivers/net/ethernet/mellanox/mlxsw/pci.c | 5 +++-- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 4 ++-- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 2 +- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/item.h b/drivers/net/ethernet/mellanox/mlxsw/item.h index 1c55a34..3c95e3d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/item.h +++ b/drivers/net/ethernet/mellanox/mlxsw/item.h @@ -72,7 +72,8 @@ __mlxsw_item_offset(const struct mlxsw_item *item, unsigned short index, typesize); } -static inline u16 __mlxsw_item_get16(char *buf, const struct mlxsw_item *item, +static inline u16 __mlxsw_item_get16(const char *buf, + const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(u16)); @@ -105,7 +106,8 @@ static inline void __mlxsw_item_set16(char *buf, const struct mlxsw_item *item, b[offset] = cpu_to_be16(tmp); } -static inline u32 __mlxsw_item_get32(char *buf, const struct mlxsw_item *item, +static inline u32 __mlxsw_item_get32(const char *buf, + const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(u32)); @@ -138,7 +140,8 @@ static inline void __mlxsw_item_set32(char *buf, const struct mlxsw_item *item, b[offset] = cpu_to_be32(tmp); } -static inline u64 __mlxsw_item_get64(char *buf, const struct mlxsw_item *item, +static inline u64 __mlxsw_item_get64(const char *buf, + const struct mlxsw_item *item, unsigned short index) { unsigned int offset = __mlxsw_item_offset(item, index, sizeof(u64)); @@ -170,7 +173,7 @@ static inline void __mlxsw_item_set64(char *buf, const struct mlxsw_item *item, b[offset] = cpu_to_be64(tmp); } -static inline void __mlxsw_item_memcpy_from(char *buf, char *dst, +static inline void __mlxsw_item_memcpy_from(const char *buf, char *dst, const struct mlxsw_item *item, unsigned short index) { @@ -213,7 +216,7 @@ __mlxsw_item_bit_array_offset(const struct mlxsw_item *item, return item->offset + offset; } -static inline u8 __mlxsw_item_bit_array_get(char *buf, +static inline u8 __mlxsw_item_bit_array_get(const char *buf, const struct mlxsw_item *item, u16 index) { @@ -257,7 +260,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u16 mlxsw_##_type##_##_cname##_##_iname##_get(char *buf) \ +static inline u16 mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ { \ return __mlxsw_item_get16(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ @@ -278,7 +281,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .name = #_type "_" #_cname "_" #_iname, \ }; \ static inline u16 \ -mlxsw_##_type##_##_cname##_##_iname##_get(char *buf, unsigned short index) \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, unsigned short index)\ { \ return __mlxsw_item_get16(buf, &__ITEM_NAME(_type, _cname, _iname), \ index); \ @@ -298,7 +301,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u32 mlxsw_##_type##_##_cname##_##_iname##_get(char *buf) \ +static inline u32 mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ { \ return __mlxsw_item_get32(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ @@ -319,7 +322,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .name = #_type "_" #_cname "_" #_iname, \ }; \ static inline u32 \ -mlxsw_##_type##_##_cname##_##_iname##_get(char *buf, unsigned short index) \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, unsigned short index)\ { \ return __mlxsw_item_get32(buf, &__ITEM_NAME(_type, _cname, _iname), \ index); \ @@ -339,7 +342,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u64 mlxsw_##_type##_##_cname##_##_iname##_get(char *buf) \ +static inline u64 mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ { \ return __mlxsw_item_get64(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ @@ -360,7 +363,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .name = #_type "_" #_cname "_" #_iname, \ }; \ static inline u64 \ -mlxsw_##_type##_##_cname##_##_iname##_get(char *buf, unsigned short index) \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, unsigned short index)\ { \ return __mlxsw_item_get64(buf, &__ITEM_NAME(_type, _cname, _iname), \ index); \ @@ -380,7 +383,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .name = #_type "_" #_cname "_" #_iname, \ }; \ static inline void \ -mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, char *dst) \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(const char *buf, char *dst) \ { \ __mlxsw_item_memcpy_from(buf, dst, \ &__ITEM_NAME(_type, _cname, _iname), 0); \ @@ -402,7 +405,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .name = #_type "_" #_cname "_" #_iname, \ }; \ static inline void \ -mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(const char *buf, \ unsigned short index, \ char *dst) \ { \ @@ -427,7 +430,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .name = #_type "_" #_cname "_" #_iname, \ }; \ static inline u8 \ -mlxsw_##_type##_##_cname##_##_iname##_get(char *buf, u16 index) \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, u16 index) \ { \ return __mlxsw_item_bit_array_get(buf, \ &__ITEM_NAME(_type, _cname, _iname), \ diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index e742bd4..6f36806 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -238,8 +238,9 @@ static bool mlxsw_pci_elem_hw_owned(struct mlxsw_pci_queue *q, bool owner_bit) return owner_bit != !!(q->consumer_counter & q->count); } -static char *mlxsw_pci_queue_sw_elem_get(struct mlxsw_pci_queue *q, - u32 (*get_elem_owner_func)(char *)) +static char * +mlxsw_pci_queue_sw_elem_get(struct mlxsw_pci_queue *q, + u32 (*get_elem_owner_func)(const char *)) { struct mlxsw_pci_queue_elem_info *elem_info; char *elem; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 6d8cb22..991a320 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1413,7 +1413,7 @@ err_port_pause_configure: struct mlxsw_sp_port_hw_stats { char str[ETH_GSTRING_LEN]; - u64 (*getter)(char *payload); + u64 (*getter)(const char *payload); }; static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = { @@ -1534,7 +1534,7 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = { #define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats) -static u64 mlxsw_reg_ppcnt_tc_transmit_queue_bytes_get(char *ppcnt_pl) +static u64 mlxsw_reg_ppcnt_tc_transmit_queue_bytes_get(const char *ppcnt_pl) { u64 transmit_queue = mlxsw_reg_ppcnt_tc_transmit_queue_get(ppcnt_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 66af63d..963618d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -410,7 +410,7 @@ static void mlxsw_sx_port_get_drvinfo(struct net_device *dev, struct mlxsw_sx_port_hw_stats { char str[ETH_GSTRING_LEN]; - u64 (*getter)(char *payload); + u64 (*getter)(const char *payload); }; static const struct mlxsw_sx_port_hw_stats mlxsw_sx_port_hw_stats[] = { -- cgit v1.1 From 21978dcfc8d847db3e280427547241a59d58ac01 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 21 Oct 2016 16:07:20 +0200 Subject: mlxsw: reg: Use helper macro to define registers Save some code and also prepare to easily carry name in string form. Signed-off-by: Jiri Pirko Acked-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 306 +++++++----------------------- 1 file changed, 66 insertions(+), 240 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 6460c72..770dbb8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -50,6 +50,12 @@ struct mlxsw_reg_info { u16 len; /* In u8 */ }; +#define MLXSW_REG_DEFINE(_name, _id, _len) \ +static const struct mlxsw_reg_info mlxsw_reg_##_name = { \ + .id = _id, \ + .len = _len, \ +} + #define MLXSW_REG(type) (&mlxsw_reg_##type) #define MLXSW_REG_LEN(type) MLXSW_REG(type)->len #define MLXSW_REG_ZERO(type, payload) memset(payload, 0, MLXSW_REG(type)->len) @@ -61,10 +67,7 @@ struct mlxsw_reg_info { #define MLXSW_REG_SGCR_ID 0x2000 #define MLXSW_REG_SGCR_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_sgcr = { - .id = MLXSW_REG_SGCR_ID, - .len = MLXSW_REG_SGCR_LEN, -}; +MLXSW_REG_DEFINE(sgcr, MLXSW_REG_SGCR_ID, MLXSW_REG_SGCR_LEN); /* reg_sgcr_llb * Link Local Broadcast (Default=0) @@ -87,10 +90,7 @@ static inline void mlxsw_reg_sgcr_pack(char *payload, bool llb) #define MLXSW_REG_SPAD_ID 0x2002 #define MLXSW_REG_SPAD_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_spad = { - .id = MLXSW_REG_SPAD_ID, - .len = MLXSW_REG_SPAD_LEN, -}; +MLXSW_REG_DEFINE(spad, MLXSW_REG_SPAD_ID, MLXSW_REG_SPAD_LEN); /* reg_spad_base_mac * Base MAC address for the switch partitions. @@ -109,10 +109,7 @@ MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6); #define MLXSW_REG_SMID_ID 0x2007 #define MLXSW_REG_SMID_LEN 0x240 -static const struct mlxsw_reg_info mlxsw_reg_smid = { - .id = MLXSW_REG_SMID_ID, - .len = MLXSW_REG_SMID_LEN, -}; +MLXSW_REG_DEFINE(smid, MLXSW_REG_SMID_ID, MLXSW_REG_SMID_LEN); /* reg_smid_swid * Switch partition ID. @@ -156,10 +153,7 @@ static inline void mlxsw_reg_smid_pack(char *payload, u16 mid, #define MLXSW_REG_SSPR_ID 0x2008 #define MLXSW_REG_SSPR_LEN 0x8 -static const struct mlxsw_reg_info mlxsw_reg_sspr = { - .id = MLXSW_REG_SSPR_ID, - .len = MLXSW_REG_SSPR_LEN, -}; +MLXSW_REG_DEFINE(sspr, MLXSW_REG_SSPR_ID, MLXSW_REG_SSPR_LEN); /* reg_sspr_m * Master - if set, then the record describes the master system port. @@ -215,10 +209,7 @@ static inline void mlxsw_reg_sspr_pack(char *payload, u8 local_port) #define MLXSW_REG_SFDAT_ID 0x2009 #define MLXSW_REG_SFDAT_LEN 0x8 -static const struct mlxsw_reg_info mlxsw_reg_sfdat = { - .id = MLXSW_REG_SFDAT_ID, - .len = MLXSW_REG_SFDAT_LEN, -}; +MLXSW_REG_DEFINE(sfdat, MLXSW_REG_SFDAT_ID, MLXSW_REG_SFDAT_LEN); /* reg_sfdat_swid * Switch partition ID. @@ -256,10 +247,7 @@ static inline void mlxsw_reg_sfdat_pack(char *payload, u32 age_time) #define MLXSW_REG_SFD_LEN (MLXSW_REG_SFD_BASE_LEN + \ MLXSW_REG_SFD_REC_LEN * MLXSW_REG_SFD_REC_MAX_COUNT) -static const struct mlxsw_reg_info mlxsw_reg_sfd = { - .id = MLXSW_REG_SFD_ID, - .len = MLXSW_REG_SFD_LEN, -}; +MLXSW_REG_DEFINE(sfd, MLXSW_REG_SFD_ID, MLXSW_REG_SFD_LEN); /* reg_sfd_swid * Switch partition ID for queries. Reserved on Write. @@ -580,10 +568,7 @@ mlxsw_reg_sfd_mc_pack(char *payload, int rec_index, #define MLXSW_REG_SFN_LEN (MLXSW_REG_SFN_BASE_LEN + \ MLXSW_REG_SFN_REC_LEN * MLXSW_REG_SFN_REC_MAX_COUNT) -static const struct mlxsw_reg_info mlxsw_reg_sfn = { - .id = MLXSW_REG_SFN_ID, - .len = MLXSW_REG_SFN_LEN, -}; +MLXSW_REG_DEFINE(sfn, MLXSW_REG_SFN_ID, MLXSW_REG_SFN_LEN); /* reg_sfn_swid * Switch partition ID. @@ -701,10 +686,7 @@ static inline void mlxsw_reg_sfn_mac_lag_unpack(char *payload, int rec_index, #define MLXSW_REG_SPMS_ID 0x200D #define MLXSW_REG_SPMS_LEN 0x404 -static const struct mlxsw_reg_info mlxsw_reg_spms = { - .id = MLXSW_REG_SPMS_ID, - .len = MLXSW_REG_SPMS_LEN, -}; +MLXSW_REG_DEFINE(spms, MLXSW_REG_SPMS_ID, MLXSW_REG_SPMS_LEN); /* reg_spms_local_port * Local port number. @@ -748,10 +730,7 @@ static inline void mlxsw_reg_spms_vid_pack(char *payload, u16 vid, #define MLXSW_REG_SPVID_ID 0x200E #define MLXSW_REG_SPVID_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_spvid = { - .id = MLXSW_REG_SPVID_ID, - .len = MLXSW_REG_SPVID_LEN, -}; +MLXSW_REG_DEFINE(spvid, MLXSW_REG_SPVID_ID, MLXSW_REG_SPVID_LEN); /* reg_spvid_local_port * Local port number. @@ -792,10 +771,7 @@ static inline void mlxsw_reg_spvid_pack(char *payload, u8 local_port, u16 pvid) #define MLXSW_REG_SPVM_LEN (MLXSW_REG_SPVM_BASE_LEN + \ MLXSW_REG_SPVM_REC_LEN * MLXSW_REG_SPVM_REC_MAX_COUNT) -static const struct mlxsw_reg_info mlxsw_reg_spvm = { - .id = MLXSW_REG_SPVM_ID, - .len = MLXSW_REG_SPVM_LEN, -}; +MLXSW_REG_DEFINE(spvm, MLXSW_REG_SPVM_ID, MLXSW_REG_SPVM_LEN); /* reg_spvm_pt * Priority tagged. If this bit is set, packets forwarded to the port with @@ -891,10 +867,7 @@ static inline void mlxsw_reg_spvm_pack(char *payload, u8 local_port, #define MLXSW_REG_SPAFT_ID 0x2010 #define MLXSW_REG_SPAFT_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_spaft = { - .id = MLXSW_REG_SPAFT_ID, - .len = MLXSW_REG_SPAFT_LEN, -}; +MLXSW_REG_DEFINE(spaft, MLXSW_REG_SPAFT_ID, MLXSW_REG_SPAFT_LEN); /* reg_spaft_local_port * Local port number. @@ -947,10 +920,7 @@ static inline void mlxsw_reg_spaft_pack(char *payload, u8 local_port, #define MLXSW_REG_SFGC_ID 0x2011 #define MLXSW_REG_SFGC_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_sfgc = { - .id = MLXSW_REG_SFGC_ID, - .len = MLXSW_REG_SFGC_LEN, -}; +MLXSW_REG_DEFINE(sfgc, MLXSW_REG_SFGC_ID, MLXSW_REG_SFGC_LEN); enum mlxsw_reg_sfgc_type { MLXSW_REG_SFGC_TYPE_BROADCAST, @@ -1045,10 +1015,7 @@ mlxsw_reg_sfgc_pack(char *payload, enum mlxsw_reg_sfgc_type type, #define MLXSW_REG_SFTR_ID 0x2012 #define MLXSW_REG_SFTR_LEN 0x420 -static const struct mlxsw_reg_info mlxsw_reg_sftr = { - .id = MLXSW_REG_SFTR_ID, - .len = MLXSW_REG_SFTR_LEN, -}; +MLXSW_REG_DEFINE(sftr, MLXSW_REG_SFTR_ID, MLXSW_REG_SFTR_LEN); /* reg_sftr_swid * Switch partition ID with which to associate the port. @@ -1118,10 +1085,7 @@ static inline void mlxsw_reg_sftr_pack(char *payload, #define MLXSW_REG_SFDF_ID 0x2013 #define MLXSW_REG_SFDF_LEN 0x14 -static const struct mlxsw_reg_info mlxsw_reg_sfdf = { - .id = MLXSW_REG_SFDF_ID, - .len = MLXSW_REG_SFDF_LEN, -}; +MLXSW_REG_DEFINE(sfdf, MLXSW_REG_SFDF_ID, MLXSW_REG_SFDF_LEN); /* reg_sfdf_swid * Switch partition ID. @@ -1205,10 +1169,7 @@ MLXSW_ITEM32(reg, sfdf, lag_fid_lag_id, 0x08, 0, 10); #define MLXSW_REG_SLDR_ID 0x2014 #define MLXSW_REG_SLDR_LEN 0x0C /* counting in only one port in list */ -static const struct mlxsw_reg_info mlxsw_reg_sldr = { - .id = MLXSW_REG_SLDR_ID, - .len = MLXSW_REG_SLDR_LEN, -}; +MLXSW_REG_DEFINE(sldr, MLXSW_REG_SLDR_ID, MLXSW_REG_SLDR_LEN); enum mlxsw_reg_sldr_op { /* Indicates a creation of a new LAG-ID, lag_id must be valid */ @@ -1288,10 +1249,7 @@ static inline void mlxsw_reg_sldr_lag_remove_port_pack(char *payload, u8 lag_id, #define MLXSW_REG_SLCR_ID 0x2015 #define MLXSW_REG_SLCR_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_slcr = { - .id = MLXSW_REG_SLCR_ID, - .len = MLXSW_REG_SLCR_LEN, -}; +MLXSW_REG_DEFINE(slcr, MLXSW_REG_SLCR_ID, MLXSW_REG_SLCR_LEN); enum mlxsw_reg_slcr_pp { /* Global Configuration (for all ports) */ @@ -1404,10 +1362,7 @@ static inline void mlxsw_reg_slcr_pack(char *payload, u16 lag_hash) #define MLXSW_REG_SLCOR_ID 0x2016 #define MLXSW_REG_SLCOR_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_slcor = { - .id = MLXSW_REG_SLCOR_ID, - .len = MLXSW_REG_SLCOR_LEN, -}; +MLXSW_REG_DEFINE(slcor, MLXSW_REG_SLCOR_ID, MLXSW_REG_SLCOR_LEN); enum mlxsw_reg_slcor_col { /* Port is added with collector disabled */ @@ -1490,10 +1445,7 @@ static inline void mlxsw_reg_slcor_col_disable_pack(char *payload, #define MLXSW_REG_SPMLR_ID 0x2018 #define MLXSW_REG_SPMLR_LEN 0x8 -static const struct mlxsw_reg_info mlxsw_reg_spmlr = { - .id = MLXSW_REG_SPMLR_ID, - .len = MLXSW_REG_SPMLR_LEN, -}; +MLXSW_REG_DEFINE(spmlr, MLXSW_REG_SPMLR_ID, MLXSW_REG_SPMLR_LEN); /* reg_spmlr_local_port * Local port number. @@ -1544,10 +1496,7 @@ static inline void mlxsw_reg_spmlr_pack(char *payload, u8 local_port, #define MLXSW_REG_SVFA_ID 0x201C #define MLXSW_REG_SVFA_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_svfa = { - .id = MLXSW_REG_SVFA_ID, - .len = MLXSW_REG_SVFA_LEN, -}; +MLXSW_REG_DEFINE(svfa, MLXSW_REG_SVFA_ID, MLXSW_REG_SVFA_LEN); /* reg_svfa_swid * Switch partition ID. @@ -1636,10 +1585,7 @@ static inline void mlxsw_reg_svfa_pack(char *payload, u8 local_port, #define MLXSW_REG_SVPE_ID 0x201E #define MLXSW_REG_SVPE_LEN 0x4 -static const struct mlxsw_reg_info mlxsw_reg_svpe = { - .id = MLXSW_REG_SVPE_ID, - .len = MLXSW_REG_SVPE_LEN, -}; +MLXSW_REG_DEFINE(svpe, MLXSW_REG_SVPE_ID, MLXSW_REG_SVPE_LEN); /* reg_svpe_local_port * Local port number @@ -1672,10 +1618,7 @@ static inline void mlxsw_reg_svpe_pack(char *payload, u8 local_port, #define MLXSW_REG_SFMR_ID 0x201F #define MLXSW_REG_SFMR_LEN 0x18 -static const struct mlxsw_reg_info mlxsw_reg_sfmr = { - .id = MLXSW_REG_SFMR_ID, - .len = MLXSW_REG_SFMR_LEN, -}; +MLXSW_REG_DEFINE(sfmr, MLXSW_REG_SFMR_ID, MLXSW_REG_SFMR_LEN); enum mlxsw_reg_sfmr_op { MLXSW_REG_SFMR_OP_CREATE_FID, @@ -1762,10 +1705,7 @@ static inline void mlxsw_reg_sfmr_pack(char *payload, MLXSW_REG_SPVMLR_REC_LEN * \ MLXSW_REG_SPVMLR_REC_MAX_COUNT) -static const struct mlxsw_reg_info mlxsw_reg_spvmlr = { - .id = MLXSW_REG_SPVMLR_ID, - .len = MLXSW_REG_SPVMLR_LEN, -}; +MLXSW_REG_DEFINE(spvmlr, MLXSW_REG_SPVMLR_ID, MLXSW_REG_SPVMLR_LEN); /* reg_spvmlr_local_port * Local ingress port. @@ -1823,10 +1763,7 @@ static inline void mlxsw_reg_spvmlr_pack(char *payload, u8 local_port, #define MLXSW_REG_QTCT_ID 0x400A #define MLXSW_REG_QTCT_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_qtct = { - .id = MLXSW_REG_QTCT_ID, - .len = MLXSW_REG_QTCT_LEN, -}; +MLXSW_REG_DEFINE(qtct, MLXSW_REG_QTCT_ID, MLXSW_REG_QTCT_LEN); /* reg_qtct_local_port * Local port number. @@ -1875,10 +1812,7 @@ static inline void mlxsw_reg_qtct_pack(char *payload, u8 local_port, #define MLXSW_REG_QEEC_ID 0x400D #define MLXSW_REG_QEEC_LEN 0x1C -static const struct mlxsw_reg_info mlxsw_reg_qeec = { - .id = MLXSW_REG_QEEC_ID, - .len = MLXSW_REG_QEEC_LEN, -}; +MLXSW_REG_DEFINE(qeec, MLXSW_REG_QEEC_ID, MLXSW_REG_QEEC_LEN); /* reg_qeec_local_port * Local port number. @@ -2000,10 +1934,7 @@ static inline void mlxsw_reg_qeec_pack(char *payload, u8 local_port, #define MLXSW_REG_PMLP_ID 0x5002 #define MLXSW_REG_PMLP_LEN 0x40 -static const struct mlxsw_reg_info mlxsw_reg_pmlp = { - .id = MLXSW_REG_PMLP_ID, - .len = MLXSW_REG_PMLP_LEN, -}; +MLXSW_REG_DEFINE(pmlp, MLXSW_REG_PMLP_ID, MLXSW_REG_PMLP_LEN); /* reg_pmlp_rxtx * 0 - Tx value is used for both Tx and Rx. @@ -2059,10 +1990,7 @@ static inline void mlxsw_reg_pmlp_pack(char *payload, u8 local_port) #define MLXSW_REG_PMTU_ID 0x5003 #define MLXSW_REG_PMTU_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_pmtu = { - .id = MLXSW_REG_PMTU_ID, - .len = MLXSW_REG_PMTU_LEN, -}; +MLXSW_REG_DEFINE(pmtu, MLXSW_REG_PMTU_ID, MLXSW_REG_PMTU_LEN); /* reg_pmtu_local_port * Local port number. @@ -2116,10 +2044,7 @@ static inline void mlxsw_reg_pmtu_pack(char *payload, u8 local_port, #define MLXSW_REG_PTYS_ID 0x5004 #define MLXSW_REG_PTYS_LEN 0x40 -static const struct mlxsw_reg_info mlxsw_reg_ptys = { - .id = MLXSW_REG_PTYS_ID, - .len = MLXSW_REG_PTYS_LEN, -}; +MLXSW_REG_DEFINE(ptys, MLXSW_REG_PTYS_ID, MLXSW_REG_PTYS_LEN); /* reg_ptys_local_port * Local port number. @@ -2232,10 +2157,7 @@ static inline void mlxsw_reg_ptys_unpack(char *payload, u32 *p_eth_proto_cap, #define MLXSW_REG_PPAD_ID 0x5005 #define MLXSW_REG_PPAD_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_ppad = { - .id = MLXSW_REG_PPAD_ID, - .len = MLXSW_REG_PPAD_LEN, -}; +MLXSW_REG_DEFINE(ppad, MLXSW_REG_PPAD_ID, MLXSW_REG_PPAD_LEN); /* reg_ppad_single_base_mac * 0: base_mac, local port should be 0 and mac[7:0] is @@ -2273,10 +2195,7 @@ static inline void mlxsw_reg_ppad_pack(char *payload, bool single_base_mac, #define MLXSW_REG_PAOS_ID 0x5006 #define MLXSW_REG_PAOS_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_paos = { - .id = MLXSW_REG_PAOS_ID, - .len = MLXSW_REG_PAOS_LEN, -}; +MLXSW_REG_DEFINE(paos, MLXSW_REG_PAOS_ID, MLXSW_REG_PAOS_LEN); /* reg_paos_swid * Switch partition ID with which to associate the port. @@ -2356,10 +2275,7 @@ static inline void mlxsw_reg_paos_pack(char *payload, u8 local_port, #define MLXSW_REG_PFCC_ID 0x5007 #define MLXSW_REG_PFCC_LEN 0x20 -static const struct mlxsw_reg_info mlxsw_reg_pfcc = { - .id = MLXSW_REG_PFCC_ID, - .len = MLXSW_REG_PFCC_LEN, -}; +MLXSW_REG_DEFINE(pfcc, MLXSW_REG_PFCC_ID, MLXSW_REG_PFCC_LEN); /* reg_pfcc_local_port * Local port number. @@ -2495,10 +2411,7 @@ static inline void mlxsw_reg_pfcc_pack(char *payload, u8 local_port) #define MLXSW_REG_PPCNT_ID 0x5008 #define MLXSW_REG_PPCNT_LEN 0x100 -static const struct mlxsw_reg_info mlxsw_reg_ppcnt = { - .id = MLXSW_REG_PPCNT_ID, - .len = MLXSW_REG_PPCNT_LEN, -}; +MLXSW_REG_DEFINE(ppcnt, MLXSW_REG_PPCNT_ID, MLXSW_REG_PPCNT_LEN); /* reg_ppcnt_swid * For HCA: must be always 0. @@ -2768,10 +2681,7 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port, #define MLXSW_REG_PPTB_ID 0x500B #define MLXSW_REG_PPTB_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_pptb = { - .id = MLXSW_REG_PPTB_ID, - .len = MLXSW_REG_PPTB_LEN, -}; +MLXSW_REG_DEFINE(pptb, MLXSW_REG_PPTB_ID, MLXSW_REG_PPTB_LEN); enum { MLXSW_REG_PPTB_MM_UM, @@ -2865,10 +2775,7 @@ static inline void mlxsw_reg_pptb_prio_to_buff_pack(char *payload, u8 prio, #define MLXSW_REG_PBMC_ID 0x500C #define MLXSW_REG_PBMC_LEN 0x6C -static const struct mlxsw_reg_info mlxsw_reg_pbmc = { - .id = MLXSW_REG_PBMC_ID, - .len = MLXSW_REG_PBMC_LEN, -}; +MLXSW_REG_DEFINE(pbmc, MLXSW_REG_PBMC_ID, MLXSW_REG_PBMC_LEN); /* reg_pbmc_local_port * Local port number. @@ -2978,10 +2885,7 @@ static inline void mlxsw_reg_pbmc_lossless_buffer_pack(char *payload, #define MLXSW_REG_PSPA_ID 0x500D #define MLXSW_REG_PSPA_LEN 0x8 -static const struct mlxsw_reg_info mlxsw_reg_pspa = { - .id = MLXSW_REG_PSPA_ID, - .len = MLXSW_REG_PSPA_LEN, -}; +MLXSW_REG_DEFINE(pspa, MLXSW_REG_PSPA_ID, MLXSW_REG_PSPA_LEN); /* reg_pspa_swid * Switch partition ID. @@ -3017,10 +2921,7 @@ static inline void mlxsw_reg_pspa_pack(char *payload, u8 swid, u8 local_port) #define MLXSW_REG_HTGT_ID 0x7002 #define MLXSW_REG_HTGT_LEN 0x100 -static const struct mlxsw_reg_info mlxsw_reg_htgt = { - .id = MLXSW_REG_HTGT_ID, - .len = MLXSW_REG_HTGT_LEN, -}; +MLXSW_REG_DEFINE(htgt, MLXSW_REG_HTGT_ID, MLXSW_REG_HTGT_LEN); /* reg_htgt_swid * Switch partition ID. @@ -3154,10 +3055,7 @@ static inline void mlxsw_reg_htgt_pack(char *payload, #define MLXSW_REG_HPKT_ID 0x7003 #define MLXSW_REG_HPKT_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_hpkt = { - .id = MLXSW_REG_HPKT_ID, - .len = MLXSW_REG_HPKT_LEN, -}; +MLXSW_REG_DEFINE(hpkt, MLXSW_REG_HPKT_ID, MLXSW_REG_HPKT_LEN); enum { MLXSW_REG_HPKT_ACK_NOT_REQUIRED, @@ -3256,10 +3154,7 @@ static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) #define MLXSW_REG_RGCR_ID 0x8001 #define MLXSW_REG_RGCR_LEN 0x28 -static const struct mlxsw_reg_info mlxsw_reg_rgcr = { - .id = MLXSW_REG_RGCR_ID, - .len = MLXSW_REG_RGCR_LEN, -}; +MLXSW_REG_DEFINE(rgcr, MLXSW_REG_RGCR_ID, MLXSW_REG_RGCR_LEN); /* reg_rgcr_ipv4_en * IPv4 router enable. @@ -3330,10 +3225,7 @@ static inline void mlxsw_reg_rgcr_pack(char *payload, bool ipv4_en) #define MLXSW_REG_RITR_ID 0x8002 #define MLXSW_REG_RITR_LEN 0x40 -static const struct mlxsw_reg_info mlxsw_reg_ritr = { - .id = MLXSW_REG_RITR_ID, - .len = MLXSW_REG_RITR_LEN, -}; +MLXSW_REG_DEFINE(ritr, MLXSW_REG_RITR_ID, MLXSW_REG_RITR_LEN); /* reg_ritr_enable * Enables routing on the router interface. @@ -3533,10 +3425,7 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, #define MLXSW_REG_RATR_ID 0x8008 #define MLXSW_REG_RATR_LEN 0x2C -static const struct mlxsw_reg_info mlxsw_reg_ratr = { - .id = MLXSW_REG_RATR_ID, - .len = MLXSW_REG_RATR_LEN, -}; +MLXSW_REG_DEFINE(ratr, MLXSW_REG_RATR_ID, MLXSW_REG_RATR_LEN); enum mlxsw_reg_ratr_op { /* Read */ @@ -3663,10 +3552,7 @@ static inline void mlxsw_reg_ratr_eth_entry_pack(char *payload, #define MLXSW_REG_RALTA_ID 0x8010 #define MLXSW_REG_RALTA_LEN 0x04 -static const struct mlxsw_reg_info mlxsw_reg_ralta = { - .id = MLXSW_REG_RALTA_ID, - .len = MLXSW_REG_RALTA_LEN, -}; +MLXSW_REG_DEFINE(ralta, MLXSW_REG_RALTA_ID, MLXSW_REG_RALTA_LEN); /* reg_ralta_op * opcode (valid for Write, must be 0 on Read) @@ -3718,10 +3604,7 @@ static inline void mlxsw_reg_ralta_pack(char *payload, bool alloc, #define MLXSW_REG_RALST_ID 0x8011 #define MLXSW_REG_RALST_LEN 0x104 -static const struct mlxsw_reg_info mlxsw_reg_ralst = { - .id = MLXSW_REG_RALST_ID, - .len = MLXSW_REG_RALST_LEN, -}; +MLXSW_REG_DEFINE(ralst, MLXSW_REG_RALST_ID, MLXSW_REG_RALST_LEN); /* reg_ralst_root_bin * The bin number of the root bin. @@ -3788,10 +3671,7 @@ static inline void mlxsw_reg_ralst_bin_pack(char *payload, u8 bin_number, #define MLXSW_REG_RALTB_ID 0x8012 #define MLXSW_REG_RALTB_LEN 0x04 -static const struct mlxsw_reg_info mlxsw_reg_raltb = { - .id = MLXSW_REG_RALTB_ID, - .len = MLXSW_REG_RALTB_LEN, -}; +MLXSW_REG_DEFINE(raltb, MLXSW_REG_RALTB_ID, MLXSW_REG_RALTB_LEN); /* reg_raltb_virtual_router * Virtual Router ID @@ -3832,10 +3712,7 @@ static inline void mlxsw_reg_raltb_pack(char *payload, u16 virtual_router, #define MLXSW_REG_RALUE_ID 0x8013 #define MLXSW_REG_RALUE_LEN 0x38 -static const struct mlxsw_reg_info mlxsw_reg_ralue = { - .id = MLXSW_REG_RALUE_ID, - .len = MLXSW_REG_RALUE_LEN, -}; +MLXSW_REG_DEFINE(ralue, MLXSW_REG_RALUE_ID, MLXSW_REG_RALUE_LEN); /* reg_ralue_protocol * Protocol. @@ -4095,10 +3972,7 @@ mlxsw_reg_ralue_act_ip2me_pack(char *payload) #define MLXSW_REG_RAUHT_ID 0x8014 #define MLXSW_REG_RAUHT_LEN 0x74 -static const struct mlxsw_reg_info mlxsw_reg_rauht = { - .id = MLXSW_REG_RAUHT_ID, - .len = MLXSW_REG_RAUHT_LEN, -}; +MLXSW_REG_DEFINE(rauht, MLXSW_REG_RAUHT_ID, MLXSW_REG_RAUHT_LEN); enum mlxsw_reg_rauht_type { MLXSW_REG_RAUHT_TYPE_IPV4, @@ -4234,10 +4108,7 @@ static inline void mlxsw_reg_rauht_pack4(char *payload, #define MLXSW_REG_RALEU_ID 0x8015 #define MLXSW_REG_RALEU_LEN 0x28 -static const struct mlxsw_reg_info mlxsw_reg_raleu = { - .id = MLXSW_REG_RALEU_ID, - .len = MLXSW_REG_RALEU_LEN, -}; +MLXSW_REG_DEFINE(raleu, MLXSW_REG_RALEU_ID, MLXSW_REG_RALEU_LEN); /* reg_raleu_protocol * Protocol. @@ -4309,10 +4180,7 @@ static inline void mlxsw_reg_raleu_pack(char *payload, MLXSW_REG_RAUHTD_REC_MAX_NUM * MLXSW_REG_RAUHTD_REC_LEN) #define MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC 4 -static const struct mlxsw_reg_info mlxsw_reg_rauhtd = { - .id = MLXSW_REG_RAUHTD_ID, - .len = MLXSW_REG_RAUHTD_LEN, -}; +MLXSW_REG_DEFINE(rauhtd, MLXSW_REG_RAUHTD_ID, MLXSW_REG_RAUHTD_LEN); #define MLXSW_REG_RAUHTD_FILTER_A BIT(0) #define MLXSW_REG_RAUHTD_FILTER_RIF BIT(3) @@ -4444,10 +4312,7 @@ static inline void mlxsw_reg_rauhtd_ent_ipv4_unpack(char *payload, #define MLXSW_REG_MFCR_ID 0x9001 #define MLXSW_REG_MFCR_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_mfcr = { - .id = MLXSW_REG_MFCR_ID, - .len = MLXSW_REG_MFCR_LEN, -}; +MLXSW_REG_DEFINE(mfcr, MLXSW_REG_MFCR_ID, MLXSW_REG_MFCR_LEN); enum mlxsw_reg_mfcr_pwm_frequency { MLXSW_REG_MFCR_PWM_FEQ_11HZ = 0x00, @@ -4507,10 +4372,7 @@ mlxsw_reg_mfcr_unpack(char *payload, #define MLXSW_REG_MFSC_ID 0x9002 #define MLXSW_REG_MFSC_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_mfsc = { - .id = MLXSW_REG_MFSC_ID, - .len = MLXSW_REG_MFSC_LEN, -}; +MLXSW_REG_DEFINE(mfsc, MLXSW_REG_MFSC_ID, MLXSW_REG_MFSC_LEN); /* reg_mfsc_pwm * Fan pwm to control / monitor. @@ -4541,10 +4403,7 @@ static inline void mlxsw_reg_mfsc_pack(char *payload, u8 pwm, #define MLXSW_REG_MFSM_ID 0x9003 #define MLXSW_REG_MFSM_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_mfsm = { - .id = MLXSW_REG_MFSM_ID, - .len = MLXSW_REG_MFSM_LEN, -}; +MLXSW_REG_DEFINE(mfsm, MLXSW_REG_MFSM_ID, MLXSW_REG_MFSM_LEN); /* reg_mfsm_tacho * Fan tachometer index. @@ -4572,10 +4431,7 @@ static inline void mlxsw_reg_mfsm_pack(char *payload, u8 tacho) #define MLXSW_REG_MTCAP_ID 0x9009 #define MLXSW_REG_MTCAP_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_mtcap = { - .id = MLXSW_REG_MTCAP_ID, - .len = MLXSW_REG_MTCAP_LEN, -}; +MLXSW_REG_DEFINE(mtcap, MLXSW_REG_MTCAP_ID, MLXSW_REG_MTCAP_LEN); /* reg_mtcap_sensor_count * Number of sensors supported by the device. @@ -4593,10 +4449,7 @@ MLXSW_ITEM32(reg, mtcap, sensor_count, 0x00, 0, 7); #define MLXSW_REG_MTMP_ID 0x900A #define MLXSW_REG_MTMP_LEN 0x20 -static const struct mlxsw_reg_info mlxsw_reg_mtmp = { - .id = MLXSW_REG_MTMP_ID, - .len = MLXSW_REG_MTMP_LEN, -}; +MLXSW_REG_DEFINE(mtmp, MLXSW_REG_MTMP_ID, MLXSW_REG_MTMP_LEN); /* reg_mtmp_sensor_index * Sensors index to access. @@ -4679,10 +4532,7 @@ static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp, #define MLXSW_REG_MPAT_ID 0x901A #define MLXSW_REG_MPAT_LEN 0x78 -static const struct mlxsw_reg_info mlxsw_reg_mpat = { - .id = MLXSW_REG_MPAT_ID, - .len = MLXSW_REG_MPAT_LEN, -}; +MLXSW_REG_DEFINE(mpat, MLXSW_REG_MPAT_ID, MLXSW_REG_MPAT_LEN); /* reg_mpat_pa_id * Port Analyzer ID. @@ -4742,10 +4592,7 @@ static inline void mlxsw_reg_mpat_pack(char *payload, u8 pa_id, #define MLXSW_REG_MPAR_ID 0x901B #define MLXSW_REG_MPAR_LEN 0x08 -static const struct mlxsw_reg_info mlxsw_reg_mpar = { - .id = MLXSW_REG_MPAR_ID, - .len = MLXSW_REG_MPAR_LEN, -}; +MLXSW_REG_DEFINE(mpar, MLXSW_REG_MPAR_ID, MLXSW_REG_MPAR_LEN); /* reg_mpar_local_port * The local port to mirror the packets from. @@ -4795,10 +4642,7 @@ static inline void mlxsw_reg_mpar_pack(char *payload, u8 local_port, #define MLXSW_REG_MLCR_ID 0x902B #define MLXSW_REG_MLCR_LEN 0x0C -static const struct mlxsw_reg_info mlxsw_reg_mlcr = { - .id = MLXSW_REG_MLCR_ID, - .len = MLXSW_REG_MLCR_LEN, -}; +MLXSW_REG_DEFINE(mlcr, MLXSW_REG_MLCR_ID, MLXSW_REG_MLCR_LEN); /* reg_mlcr_local_port * Local port number. @@ -4839,10 +4683,7 @@ static inline void mlxsw_reg_mlcr_pack(char *payload, u8 local_port, #define MLXSW_REG_SBPR_ID 0xB001 #define MLXSW_REG_SBPR_LEN 0x14 -static const struct mlxsw_reg_info mlxsw_reg_sbpr = { - .id = MLXSW_REG_SBPR_ID, - .len = MLXSW_REG_SBPR_LEN, -}; +MLXSW_REG_DEFINE(sbpr, MLXSW_REG_SBPR_ID, MLXSW_REG_SBPR_LEN); /* shared direstion enum for SBPR, SBCM, SBPM */ enum mlxsw_reg_sbxx_dir { @@ -4899,10 +4740,7 @@ static inline void mlxsw_reg_sbpr_pack(char *payload, u8 pool, #define MLXSW_REG_SBCM_ID 0xB002 #define MLXSW_REG_SBCM_LEN 0x28 -static const struct mlxsw_reg_info mlxsw_reg_sbcm = { - .id = MLXSW_REG_SBCM_ID, - .len = MLXSW_REG_SBCM_LEN, -}; +MLXSW_REG_DEFINE(sbcm, MLXSW_REG_SBCM_ID, MLXSW_REG_SBCM_LEN); /* reg_sbcm_local_port * Local port number. @@ -4979,10 +4817,7 @@ static inline void mlxsw_reg_sbcm_pack(char *payload, u8 local_port, u8 pg_buff, #define MLXSW_REG_SBPM_ID 0xB003 #define MLXSW_REG_SBPM_LEN 0x28 -static const struct mlxsw_reg_info mlxsw_reg_sbpm = { - .id = MLXSW_REG_SBPM_ID, - .len = MLXSW_REG_SBPM_LEN, -}; +MLXSW_REG_DEFINE(sbpm, MLXSW_REG_SBPM_ID, MLXSW_REG_SBPM_LEN); /* reg_sbpm_local_port * Local port number. @@ -5073,10 +4908,7 @@ static inline void mlxsw_reg_sbpm_unpack(char *payload, u32 *p_buff_occupancy, #define MLXSW_REG_SBMM_ID 0xB004 #define MLXSW_REG_SBMM_LEN 0x28 -static const struct mlxsw_reg_info mlxsw_reg_sbmm = { - .id = MLXSW_REG_SBMM_ID, - .len = MLXSW_REG_SBMM_LEN, -}; +MLXSW_REG_DEFINE(sbmm, MLXSW_REG_SBMM_ID, MLXSW_REG_SBMM_LEN); /* reg_sbmm_prio * Switch Priority. @@ -5135,10 +4967,7 @@ static inline void mlxsw_reg_sbmm_pack(char *payload, u8 prio, u32 min_buff, MLXSW_REG_SBSR_REC_LEN * \ MLXSW_REG_SBSR_REC_MAX_COUNT) -static const struct mlxsw_reg_info mlxsw_reg_sbsr = { - .id = MLXSW_REG_SBSR_ID, - .len = MLXSW_REG_SBSR_LEN, -}; +MLXSW_REG_DEFINE(sbsr, MLXSW_REG_SBSR_ID, MLXSW_REG_SBSR_LEN); /* reg_sbsr_clr * Clear Max Buffer Occupancy. When this bit is set, the max_buff_occupancy @@ -5228,10 +5057,7 @@ static inline void mlxsw_reg_sbsr_rec_unpack(char *payload, int rec_index, #define MLXSW_REG_SBIB_ID 0xB006 #define MLXSW_REG_SBIB_LEN 0x10 -static const struct mlxsw_reg_info mlxsw_reg_sbib = { - .id = MLXSW_REG_SBIB_ID, - .len = MLXSW_REG_SBIB_LEN, -}; +MLXSW_REG_DEFINE(sbib, MLXSW_REG_SBIB_ID, MLXSW_REG_SBIB_LEN); /* reg_sbib_local_port * Local port number -- cgit v1.1 From 8e9658d56718a62d0586f4dbb61b68b07d3c6cc0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 21 Oct 2016 16:07:21 +0200 Subject: mlxsw: reg: Generare register names automatically Extend the MLXSW_REG_DEFINE macro to store register name in string form. Use this string later on instead of hard coded string values. Signed-off-by: Jiri Pirko Acked-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 196 +++++++++++------------------- 1 file changed, 73 insertions(+), 123 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 770dbb8..debcf26 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -48,12 +48,14 @@ struct mlxsw_reg_info { u16 id; u16 len; /* In u8 */ + const char *name; }; #define MLXSW_REG_DEFINE(_name, _id, _len) \ static const struct mlxsw_reg_info mlxsw_reg_##_name = { \ .id = _id, \ .len = _len, \ + .name = #_name, \ } #define MLXSW_REG(type) (&mlxsw_reg_##type) @@ -5082,132 +5084,80 @@ static inline void mlxsw_reg_sbib_pack(char *payload, u8 local_port, mlxsw_reg_sbib_buff_size_set(payload, buff_size); } +static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { + MLXSW_REG(sgcr), + MLXSW_REG(spad), + MLXSW_REG(smid), + MLXSW_REG(sspr), + MLXSW_REG(sfdat), + MLXSW_REG(sfd), + MLXSW_REG(sfn), + MLXSW_REG(spms), + MLXSW_REG(spvid), + MLXSW_REG(spvm), + MLXSW_REG(spaft), + MLXSW_REG(sfgc), + MLXSW_REG(sftr), + MLXSW_REG(sfdf), + MLXSW_REG(sldr), + MLXSW_REG(slcr), + MLXSW_REG(slcor), + MLXSW_REG(spmlr), + MLXSW_REG(svfa), + MLXSW_REG(svpe), + MLXSW_REG(sfmr), + MLXSW_REG(spvmlr), + MLXSW_REG(qtct), + MLXSW_REG(qeec), + MLXSW_REG(pmlp), + MLXSW_REG(pmtu), + MLXSW_REG(ptys), + MLXSW_REG(ppad), + MLXSW_REG(paos), + MLXSW_REG(pfcc), + MLXSW_REG(ppcnt), + MLXSW_REG(pptb), + MLXSW_REG(pbmc), + MLXSW_REG(pspa), + MLXSW_REG(htgt), + MLXSW_REG(hpkt), + MLXSW_REG(rgcr), + MLXSW_REG(ritr), + MLXSW_REG(ratr), + MLXSW_REG(ralta), + MLXSW_REG(ralst), + MLXSW_REG(raltb), + MLXSW_REG(ralue), + MLXSW_REG(rauht), + MLXSW_REG(raleu), + MLXSW_REG(rauhtd), + MLXSW_REG(mfcr), + MLXSW_REG(mfsc), + MLXSW_REG(mfsm), + MLXSW_REG(mtcap), + MLXSW_REG(mtmp), + MLXSW_REG(mpat), + MLXSW_REG(mpar), + MLXSW_REG(mlcr), + MLXSW_REG(sbpr), + MLXSW_REG(sbcm), + MLXSW_REG(sbpm), + MLXSW_REG(sbmm), + MLXSW_REG(sbsr), + MLXSW_REG(sbib), +}; + static inline const char *mlxsw_reg_id_str(u16 reg_id) { - switch (reg_id) { - case MLXSW_REG_SGCR_ID: - return "SGCR"; - case MLXSW_REG_SPAD_ID: - return "SPAD"; - case MLXSW_REG_SMID_ID: - return "SMID"; - case MLXSW_REG_SSPR_ID: - return "SSPR"; - case MLXSW_REG_SFDAT_ID: - return "SFDAT"; - case MLXSW_REG_SFD_ID: - return "SFD"; - case MLXSW_REG_SFN_ID: - return "SFN"; - case MLXSW_REG_SPMS_ID: - return "SPMS"; - case MLXSW_REG_SPVID_ID: - return "SPVID"; - case MLXSW_REG_SPVM_ID: - return "SPVM"; - case MLXSW_REG_SPAFT_ID: - return "SPAFT"; - case MLXSW_REG_SFGC_ID: - return "SFGC"; - case MLXSW_REG_SFTR_ID: - return "SFTR"; - case MLXSW_REG_SFDF_ID: - return "SFDF"; - case MLXSW_REG_SLDR_ID: - return "SLDR"; - case MLXSW_REG_SLCR_ID: - return "SLCR"; - case MLXSW_REG_SLCOR_ID: - return "SLCOR"; - case MLXSW_REG_SPMLR_ID: - return "SPMLR"; - case MLXSW_REG_SVFA_ID: - return "SVFA"; - case MLXSW_REG_SVPE_ID: - return "SVPE"; - case MLXSW_REG_SFMR_ID: - return "SFMR"; - case MLXSW_REG_SPVMLR_ID: - return "SPVMLR"; - case MLXSW_REG_QTCT_ID: - return "QTCT"; - case MLXSW_REG_QEEC_ID: - return "QEEC"; - case MLXSW_REG_PMLP_ID: - return "PMLP"; - case MLXSW_REG_PMTU_ID: - return "PMTU"; - case MLXSW_REG_PTYS_ID: - return "PTYS"; - case MLXSW_REG_PPAD_ID: - return "PPAD"; - case MLXSW_REG_PAOS_ID: - return "PAOS"; - case MLXSW_REG_PFCC_ID: - return "PFCC"; - case MLXSW_REG_PPCNT_ID: - return "PPCNT"; - case MLXSW_REG_PPTB_ID: - return "PPTB"; - case MLXSW_REG_PBMC_ID: - return "PBMC"; - case MLXSW_REG_PSPA_ID: - return "PSPA"; - case MLXSW_REG_HTGT_ID: - return "HTGT"; - case MLXSW_REG_HPKT_ID: - return "HPKT"; - case MLXSW_REG_RGCR_ID: - return "RGCR"; - case MLXSW_REG_RITR_ID: - return "RITR"; - case MLXSW_REG_RATR_ID: - return "RATR"; - case MLXSW_REG_RALTA_ID: - return "RALTA"; - case MLXSW_REG_RALST_ID: - return "RALST"; - case MLXSW_REG_RALTB_ID: - return "RALTB"; - case MLXSW_REG_RALUE_ID: - return "RALUE"; - case MLXSW_REG_RAUHT_ID: - return "RAUHT"; - case MLXSW_REG_RALEU_ID: - return "RALEU"; - case MLXSW_REG_RAUHTD_ID: - return "RAUHTD"; - case MLXSW_REG_MFCR_ID: - return "MFCR"; - case MLXSW_REG_MFSC_ID: - return "MFSC"; - case MLXSW_REG_MFSM_ID: - return "MFSM"; - case MLXSW_REG_MTCAP_ID: - return "MTCAP"; - case MLXSW_REG_MPAT_ID: - return "MPAT"; - case MLXSW_REG_MPAR_ID: - return "MPAR"; - case MLXSW_REG_MTMP_ID: - return "MTMP"; - case MLXSW_REG_MLCR_ID: - return "MLCR"; - case MLXSW_REG_SBPR_ID: - return "SBPR"; - case MLXSW_REG_SBCM_ID: - return "SBCM"; - case MLXSW_REG_SBPM_ID: - return "SBPM"; - case MLXSW_REG_SBMM_ID: - return "SBMM"; - case MLXSW_REG_SBSR_ID: - return "SBSR"; - case MLXSW_REG_SBIB_ID: - return "SBIB"; - default: - return "*UNKNOWN*"; + const struct mlxsw_reg_info *reg_info; + int i; + + for (i = 0; i < ARRAY_SIZE(mlxsw_reg_infos); i++) { + reg_info = mlxsw_reg_infos[i]; + if (reg_info->id == reg_id) + return reg_info->name; } + return "*UNKNOWN*"; } /* PUDE - Port Up / Down Event -- cgit v1.1 From f38a2314e55978e7b5c128175db27244665d0765 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 21 Oct 2016 16:07:22 +0200 Subject: mlxsw: cmd: Push resource query defines to cmd.h Push cmd resource query related defines to cmd.h where they belong. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/cmd.h | 5 +++++ drivers/net/ethernet/mellanox/mlxsw/pci.c | 10 ++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index 28271be..56e19b0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -513,6 +513,11 @@ static inline int mlxsw_cmd_unmap_fa(struct mlxsw_core *mlxsw_core) * are no more sources in the table, will return resource id 0xFFF to indicate * it. */ + +#define MLXSW_CMD_QUERY_RESOURCES_TABLE_END_ID 0xffff +#define MLXSW_CMD_QUERY_RESOURCES_MAX_QUERIES 100 +#define MLXSW_CMD_QUERY_RESOURCES_PER_QUERY 32 + static inline int mlxsw_cmd_query_resources(struct mlxsw_core *mlxsw_core, char *out_mbox, int index) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 6f36806..c3b1663 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -1155,7 +1155,6 @@ mlxsw_pci_config_profile_swid_config(struct mlxsw_pci *mlxsw_pci, mlxsw_cmd_mbox_config_profile_swid_config_mask_set(mbox, index, mask); } -#define MLXSW_RESOURCES_TABLE_END_ID 0xffff #define MLXSW_MAX_SPAN_ID 0x2420 #define MLXSW_MAX_LAG_ID 0x2520 #define MLXSW_MAX_PORTS_IN_LAG_ID 0x2521 @@ -1167,8 +1166,6 @@ mlxsw_pci_config_profile_swid_config(struct mlxsw_pci *mlxsw_pci, #define MLXSW_MAX_VLAN_GROUPS_ID 0x2906 #define MLXSW_MAX_REGIONS_ID 0x2901 #define MLXSW_MAX_RIF_ID 0x2C02 -#define MLXSW_RESOURCES_QUERY_MAX_QUERIES 100 -#define MLXSW_RESOURCES_PER_QUERY 32 static void mlxsw_pci_resources_query_parse(int id, u64 val, struct mlxsw_resources *resources) @@ -1238,16 +1235,17 @@ static int mlxsw_pci_resources_query(struct mlxsw_pci *mlxsw_pci, char *mbox, mlxsw_cmd_mbox_zero(mbox); - for (index = 0; index < MLXSW_RESOURCES_QUERY_MAX_QUERIES; index++) { + for (index = 0; index < MLXSW_CMD_QUERY_RESOURCES_MAX_QUERIES; + index++) { err = mlxsw_cmd_query_resources(mlxsw_pci->core, mbox, index); if (err) return err; - for (i = 0; i < MLXSW_RESOURCES_PER_QUERY; i++) { + for (i = 0; i < MLXSW_CMD_QUERY_RESOURCES_PER_QUERY; i++) { id = mlxsw_cmd_mbox_query_resource_id_get(mbox, i); data = mlxsw_cmd_mbox_query_resource_data_get(mbox, i); - if (id == MLXSW_RESOURCES_TABLE_END_ID) + if (id == MLXSW_CMD_QUERY_RESOURCES_TABLE_END_ID) return 0; mlxsw_pci_resources_query_parse(id, data, resources); -- cgit v1.1 From c1a3831121f60f614fe92314d8c271d4d89d0a9a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 21 Oct 2016 16:07:23 +0200 Subject: mlxsw: Convert resources into array Since the number of resources is going to get much bigger, ease up the addition by simly defining IDs. Convert the existing structure members to a set array, one for validity, one for values. Introduce a set of getters and setters for easy access. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 32 ++++-- drivers/net/ethernet/mellanox/mlxsw/core.h | 44 ++----- drivers/net/ethernet/mellanox/mlxsw/pci.c | 127 ++++++--------------- drivers/net/ethernet/mellanox/mlxsw/resources.h | 121 ++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 38 +++--- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 5 +- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 39 +++---- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 7 +- 8 files changed, 223 insertions(+), 190 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/resources.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index aa33d58..a37d471 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -67,6 +67,7 @@ #include "trap.h" #include "emad.h" #include "reg.h" +#include "resources.h" static LIST_HEAD(mlxsw_core_driver_list); static DEFINE_SPINLOCK(mlxsw_core_driver_list_lock); @@ -111,7 +112,7 @@ struct mlxsw_core { struct { u8 *mapping; /* lag_id+port_index to local_port mapping */ } lag; - struct mlxsw_resources resources; + struct mlxsw_res res; struct mlxsw_hwmon *hwmon; unsigned long driver_priv[0]; /* driver_priv has to be always the last item */ @@ -1101,14 +1102,15 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, } err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, - &mlxsw_core->resources); + &mlxsw_core->res); if (err) goto err_bus_init; - if (mlxsw_core->resources.max_lag_valid && - mlxsw_core->resources.max_ports_in_lag_valid) { - alloc_size = sizeof(u8) * mlxsw_core->resources.max_lag * - mlxsw_core->resources.max_ports_in_lag; + if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG) && + MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG_MEMBERS)) { + alloc_size = sizeof(u8) * + MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG) * + MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS); mlxsw_core->lag.mapping = kzalloc(alloc_size, GFP_KERNEL); if (!mlxsw_core->lag.mapping) { err = -ENOMEM; @@ -1615,7 +1617,7 @@ EXPORT_SYMBOL(mlxsw_core_skb_receive); static int mlxsw_core_lag_mapping_index(struct mlxsw_core *mlxsw_core, u16 lag_id, u8 port_index) { - return mlxsw_core->resources.max_ports_in_lag * lag_id + + return MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS) * lag_id + port_index; } @@ -1644,7 +1646,7 @@ void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core, { int i; - for (i = 0; i < mlxsw_core->resources.max_ports_in_lag; i++) { + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS); i++) { int index = mlxsw_core_lag_mapping_index(mlxsw_core, lag_id, i); @@ -1654,11 +1656,19 @@ void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_lag_mapping_clear); -struct mlxsw_resources *mlxsw_core_resources_get(struct mlxsw_core *mlxsw_core) +bool mlxsw_core_res_valid(struct mlxsw_core *mlxsw_core, + enum mlxsw_res_id res_id) { - return &mlxsw_core->resources; + return mlxsw_res_valid(&mlxsw_core->res, res_id); } -EXPORT_SYMBOL(mlxsw_core_resources_get); +EXPORT_SYMBOL(mlxsw_core_res_valid); + +u64 mlxsw_core_res_get(struct mlxsw_core *mlxsw_core, + enum mlxsw_res_id res_id) +{ + return mlxsw_res_get(&mlxsw_core->res, res_id); +} +EXPORT_SYMBOL(mlxsw_core_res_get); int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, struct mlxsw_core_port *mlxsw_core_port, u8 local_port, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index c4f550b..94a846d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -48,8 +48,8 @@ #include "trap.h" #include "reg.h" - #include "cmd.h" +#include "resources.h" #define MLXSW_MODULE_ALIAS_PREFIX "mlxsw-driver-" #define MODULE_MLXSW_DRIVER_ALIAS(kind) \ @@ -266,45 +266,23 @@ struct mlxsw_driver { const struct mlxsw_config_profile *profile; }; -struct mlxsw_resources { - u32 max_span_valid:1, - max_lag_valid:1, - max_ports_in_lag_valid:1, - kvd_size_valid:1, - kvd_single_min_size_valid:1, - kvd_double_min_size_valid:1, - max_virtual_routers_valid:1, - max_system_ports_valid:1, - max_vlan_groups_valid:1, - max_regions_valid:1, - max_rif_valid:1; - u8 max_span; - u8 max_lag; - u8 max_ports_in_lag; - u32 kvd_size; - u32 kvd_single_min_size; - u32 kvd_double_min_size; - u16 max_virtual_routers; - u16 max_system_ports; - u16 max_vlan_groups; - u16 max_regions; - u16 max_rif; +bool mlxsw_core_res_valid(struct mlxsw_core *mlxsw_core, + enum mlxsw_res_id res_id); - /* Internal resources. - * Determined by the SW, not queried from the HW. - */ - u32 kvd_single_size; - u32 kvd_double_size; - u32 kvd_linear_size; -}; +#define MLXSW_CORE_RES_VALID(res, short_res_id) \ + mlxsw_core_res_valid(res, MLXSW_RES_ID_##short_res_id) + +u64 mlxsw_core_res_get(struct mlxsw_core *mlxsw_core, + enum mlxsw_res_id res_id); -struct mlxsw_resources *mlxsw_core_resources_get(struct mlxsw_core *mlxsw_core); +#define MLXSW_CORE_RES_GET(res, short_res_id) \ + mlxsw_core_res_get(res, MLXSW_RES_ID_##short_res_id) struct mlxsw_bus { const char *kind; int (*init)(void *bus_priv, struct mlxsw_core *mlxsw_core, const struct mlxsw_config_profile *profile, - struct mlxsw_resources *resources); + struct mlxsw_res *res); void (*fini)(void *bus_priv); bool (*skb_transmit_busy)(void *bus_priv, const struct mlxsw_tx_info *tx_info); diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index c3b1663..c88f2ab 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -52,6 +52,7 @@ #include "core.h" #include "cmd.h" #include "port.h" +#include "resources.h" static const char mlxsw_pci_driver_name[] = "mlxsw_pci"; @@ -1155,73 +1156,8 @@ mlxsw_pci_config_profile_swid_config(struct mlxsw_pci *mlxsw_pci, mlxsw_cmd_mbox_config_profile_swid_config_mask_set(mbox, index, mask); } -#define MLXSW_MAX_SPAN_ID 0x2420 -#define MLXSW_MAX_LAG_ID 0x2520 -#define MLXSW_MAX_PORTS_IN_LAG_ID 0x2521 -#define MLXSW_KVD_SIZE_ID 0x1001 -#define MLXSW_KVD_SINGLE_MIN_SIZE_ID 0x1002 -#define MLXSW_KVD_DOUBLE_MIN_SIZE_ID 0x1003 -#define MLXSW_MAX_VIRTUAL_ROUTERS_ID 0x2C01 -#define MLXSW_MAX_SYSTEM_PORT_ID 0x2502 -#define MLXSW_MAX_VLAN_GROUPS_ID 0x2906 -#define MLXSW_MAX_REGIONS_ID 0x2901 -#define MLXSW_MAX_RIF_ID 0x2C02 - -static void mlxsw_pci_resources_query_parse(int id, u64 val, - struct mlxsw_resources *resources) -{ - switch (id) { - case MLXSW_MAX_SPAN_ID: - resources->max_span = val; - resources->max_span_valid = 1; - break; - case MLXSW_MAX_LAG_ID: - resources->max_lag = val; - resources->max_lag_valid = 1; - break; - case MLXSW_MAX_PORTS_IN_LAG_ID: - resources->max_ports_in_lag = val; - resources->max_ports_in_lag_valid = 1; - break; - case MLXSW_KVD_SIZE_ID: - resources->kvd_size = val; - resources->kvd_size_valid = 1; - break; - case MLXSW_KVD_SINGLE_MIN_SIZE_ID: - resources->kvd_single_min_size = val; - resources->kvd_single_min_size_valid = 1; - break; - case MLXSW_KVD_DOUBLE_MIN_SIZE_ID: - resources->kvd_double_min_size = val; - resources->kvd_double_min_size_valid = 1; - break; - case MLXSW_MAX_VIRTUAL_ROUTERS_ID: - resources->max_virtual_routers = val; - resources->max_virtual_routers_valid = 1; - break; - case MLXSW_MAX_SYSTEM_PORT_ID: - resources->max_system_ports = val; - resources->max_system_ports_valid = 1; - break; - case MLXSW_MAX_VLAN_GROUPS_ID: - resources->max_vlan_groups = val; - resources->max_vlan_groups_valid = 1; - break; - case MLXSW_MAX_REGIONS_ID: - resources->max_regions = val; - resources->max_regions_valid = 1; - break; - case MLXSW_MAX_RIF_ID: - resources->max_rif = val; - resources->max_rif_valid = 1; - break; - default: - break; - } -} - static int mlxsw_pci_resources_query(struct mlxsw_pci *mlxsw_pci, char *mbox, - struct mlxsw_resources *resources, + struct mlxsw_res *res, u8 query_enabled) { int index, i; @@ -1248,7 +1184,7 @@ static int mlxsw_pci_resources_query(struct mlxsw_pci *mlxsw_pci, char *mbox, if (id == MLXSW_CMD_QUERY_RESOURCES_TABLE_END_ID) return 0; - mlxsw_pci_resources_query_parse(id, data, resources); + mlxsw_res_parse(res, id, data); } } @@ -1258,13 +1194,14 @@ static int mlxsw_pci_resources_query(struct mlxsw_pci *mlxsw_pci, char *mbox, return -EIO; } -static int mlxsw_pci_profile_get_kvd_sizes(const struct mlxsw_config_profile *profile, - struct mlxsw_resources *resources) +static int +mlxsw_pci_profile_get_kvd_sizes(const struct mlxsw_config_profile *profile, + struct mlxsw_res *res) { - u32 singles_size, doubles_size, linear_size; + u32 single_size, double_size, linear_size; - if (!resources->kvd_single_min_size_valid || - !resources->kvd_double_min_size_valid || + if (!MLXSW_RES_VALID(res, KVD_SINGLE_MIN_SIZE) || + !MLXSW_RES_VALID(res, KVD_DOUBLE_MIN_SIZE) || !profile->used_kvd_split_data) return -EIO; @@ -1276,31 +1213,31 @@ static int mlxsw_pci_profile_get_kvd_sizes(const struct mlxsw_config_profile *pr * Both sizes must be a multiplications of the * granularity from the profile. */ - doubles_size = (resources->kvd_size - linear_size); - doubles_size *= profile->kvd_hash_double_parts; - doubles_size /= (profile->kvd_hash_double_parts + - profile->kvd_hash_single_parts); - doubles_size /= profile->kvd_hash_granularity; - doubles_size *= profile->kvd_hash_granularity; - singles_size = resources->kvd_size - doubles_size - - linear_size; + double_size = MLXSW_RES_GET(res, KVD_SIZE) - linear_size; + double_size *= profile->kvd_hash_double_parts; + double_size /= profile->kvd_hash_double_parts + + profile->kvd_hash_single_parts; + double_size /= profile->kvd_hash_granularity; + double_size *= profile->kvd_hash_granularity; + single_size = MLXSW_RES_GET(res, KVD_SIZE) - double_size - + linear_size; /* Check results are legal. */ - if (singles_size < resources->kvd_single_min_size || - doubles_size < resources->kvd_double_min_size || - resources->kvd_size < linear_size) + if (single_size < MLXSW_RES_GET(res, KVD_SINGLE_MIN_SIZE) || + double_size < MLXSW_RES_GET(res, KVD_DOUBLE_MIN_SIZE) || + MLXSW_RES_GET(res, KVD_SIZE) < linear_size) return -EIO; - resources->kvd_single_size = singles_size; - resources->kvd_double_size = doubles_size; - resources->kvd_linear_size = linear_size; + MLXSW_RES_SET(res, KVD_SINGLE_SIZE, single_size); + MLXSW_RES_SET(res, KVD_DOUBLE_SIZE, double_size); + MLXSW_RES_SET(res, KVD_LINEAR_SIZE, linear_size); return 0; } static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox, const struct mlxsw_config_profile *profile, - struct mlxsw_resources *resources) + struct mlxsw_res *res) { int i; int err; @@ -1389,22 +1326,22 @@ static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox, mlxsw_cmd_mbox_config_profile_adaptive_routing_group_cap_set( mbox, profile->adaptive_routing_group_cap); } - if (resources->kvd_size_valid) { - err = mlxsw_pci_profile_get_kvd_sizes(profile, resources); + if (MLXSW_RES_VALID(res, KVD_SIZE)) { + err = mlxsw_pci_profile_get_kvd_sizes(profile, res); if (err) return err; mlxsw_cmd_mbox_config_profile_set_kvd_linear_size_set(mbox, 1); mlxsw_cmd_mbox_config_profile_kvd_linear_size_set(mbox, - resources->kvd_linear_size); + MLXSW_RES_GET(res, KVD_LINEAR_SIZE)); mlxsw_cmd_mbox_config_profile_set_kvd_hash_single_size_set(mbox, 1); mlxsw_cmd_mbox_config_profile_kvd_hash_single_size_set(mbox, - resources->kvd_single_size); + MLXSW_RES_GET(res, KVD_SINGLE_SIZE)); mlxsw_cmd_mbox_config_profile_set_kvd_hash_double_size_set( mbox, 1); mlxsw_cmd_mbox_config_profile_kvd_hash_double_size_set(mbox, - resources->kvd_double_size); + MLXSW_RES_GET(res, KVD_DOUBLE_SIZE)); } for (i = 0; i < MLXSW_CONFIG_PROFILE_SWID_COUNT; i++) @@ -1542,7 +1479,7 @@ static void mlxsw_pci_mbox_free(struct mlxsw_pci *mlxsw_pci, static int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core, const struct mlxsw_config_profile *profile, - struct mlxsw_resources *resources) + struct mlxsw_res *res) { struct mlxsw_pci *mlxsw_pci = bus_priv; struct pci_dev *pdev = mlxsw_pci->pdev; @@ -1601,12 +1538,12 @@ static int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core, if (err) goto err_boardinfo; - err = mlxsw_pci_resources_query(mlxsw_pci, mbox, resources, + err = mlxsw_pci_resources_query(mlxsw_pci, mbox, res, profile->resource_query_enable); if (err) goto err_query_resources; - err = mlxsw_pci_config_profile(mlxsw_pci, mbox, profile, resources); + err = mlxsw_pci_config_profile(mlxsw_pci, mbox, profile, res); if (err) goto err_config_profile; diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h new file mode 100644 index 0000000..a031e45 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -0,0 +1,121 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/resources.h + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Jiri Pirko + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MLXSW_RESOURCES_H +#define _MLXSW_RESOURCES_H + +#include +#include + +enum mlxsw_res_id { + MLXSW_RES_ID_KVD_SIZE, + MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE, + MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE, + MLXSW_RES_ID_MAX_SPAN, + MLXSW_RES_ID_MAX_SYSTEM_PORT, + MLXSW_RES_ID_MAX_LAG, + MLXSW_RES_ID_MAX_LAG_MEMBERS, + MLXSW_RES_ID_MAX_VRS, + MLXSW_RES_ID_MAX_RIFS, + + /* Internal resources. + * Determined by the SW, not queried from the HW. + */ + MLXSW_RES_ID_KVD_SINGLE_SIZE, + MLXSW_RES_ID_KVD_DOUBLE_SIZE, + MLXSW_RES_ID_KVD_LINEAR_SIZE, + + __MLXSW_RES_ID_MAX, +}; + +static u16 mlxsw_res_ids[] = { + [MLXSW_RES_ID_KVD_SIZE] = 0x1001, + [MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE] = 0x1002, + [MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE] = 0x1003, + [MLXSW_RES_ID_MAX_SPAN] = 0x2420, + [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, + [MLXSW_RES_ID_MAX_LAG] = 0x2520, + [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, + [MLXSW_RES_ID_MAX_VRS] = 0x2C01, + [MLXSW_RES_ID_MAX_RIFS] = 0x2C02, +}; + +struct mlxsw_res { + bool valid[__MLXSW_RES_ID_MAX]; + u64 values[__MLXSW_RES_ID_MAX]; +}; + +static inline bool mlxsw_res_valid(struct mlxsw_res *res, + enum mlxsw_res_id res_id) +{ + return res->valid[res_id]; +} + +#define MLXSW_RES_VALID(res, short_res_id) \ + mlxsw_res_valid(res, MLXSW_RES_ID_##short_res_id) + +static inline u64 mlxsw_res_get(struct mlxsw_res *res, + enum mlxsw_res_id res_id) +{ + if (WARN_ON(!res->valid[res_id])) + return 0; + return res->values[res_id]; +} + +#define MLXSW_RES_GET(res, short_res_id) \ + mlxsw_res_get(res, MLXSW_RES_ID_##short_res_id) + +static inline void mlxsw_res_set(struct mlxsw_res *res, + enum mlxsw_res_id res_id, u64 value) +{ + res->valid[res_id] = true; + res->values[res_id] = value; +} + +#define MLXSW_RES_SET(res, short_res_id, value) \ + mlxsw_res_set(res, MLXSW_RES_ID_##short_res_id, value) + +static inline void mlxsw_res_parse(struct mlxsw_res *res, u16 id, u64 value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mlxsw_res_ids); i++) { + if (mlxsw_res_ids[i] == id) { + mlxsw_res_set(res, i, value); + return; + } + } +} + +#endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 991a320..5d8f1d5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -168,14 +168,13 @@ static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp) static int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - if (!resources->max_span_valid) + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN)) return -EIO; - mlxsw_sp->span.entries_count = resources->max_span; + mlxsw_sp->span.entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, + MAX_SPAN); mlxsw_sp->span.entries = kcalloc(mlxsw_sp->span.entries_count, sizeof(struct mlxsw_sp_span_entry), GFP_KERNEL); @@ -2892,7 +2891,6 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp) static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; char slcr_pl[MLXSW_REG_SLCR_LEN]; int err; @@ -2909,11 +2907,11 @@ static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) if (err) return err; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - if (!(resources->max_lag_valid && resources->max_ports_in_lag_valid)) + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG) || + !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG_MEMBERS)) return -EIO; - mlxsw_sp->lags = kcalloc(resources->max_lag, + mlxsw_sp->lags = kcalloc(MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LAG), sizeof(struct mlxsw_sp_upper), GFP_KERNEL); if (!mlxsw_sp->lags) @@ -3183,11 +3181,9 @@ static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_rif; i++) + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) if (!mlxsw_sp->rifs[i]) return i; @@ -3710,14 +3706,15 @@ static bool mlxsw_sp_port_fdb_should_flush(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; u8 local_port = mlxsw_sp_port->local_port; u16 lag_id = mlxsw_sp_port->lag_id; - struct mlxsw_resources *resources; + u64 max_lag_members; int i, count = 0; if (!mlxsw_sp_port->lagged) return true; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_ports_in_lag; i++) { + max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core, + MAX_LAG_MEMBERS); + for (i = 0; i < max_lag_members; i++) { struct mlxsw_sp_port *lag_port; lag_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i); @@ -3923,13 +3920,13 @@ static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp, struct net_device *lag_dev, u16 *p_lag_id) { - struct mlxsw_resources *resources; struct mlxsw_sp_upper *lag; int free_lag_id = -1; + u64 max_lag; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_lag; i++) { + max_lag = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LAG); + for (i = 0; i < max_lag; i++) { lag = mlxsw_sp_lag_get(mlxsw_sp, i); if (lag->ref_count) { if (lag->dev == lag_dev) { @@ -3963,11 +3960,12 @@ mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 *p_port_index) { - struct mlxsw_resources *resources; + u64 max_lag_members; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_ports_in_lag; i++) { + max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core, + MAX_LAG_MEMBERS); + for (i = 0; i < max_lag_members; i++) { if (!mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i)) { *p_port_index = i; return 0; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 9b22863..cc54625 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -479,12 +479,9 @@ static inline struct mlxsw_sp_rif * mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, const struct net_device *dev) { - struct mlxsw_resources *resources; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - - for (i = 0; i < resources->max_rif; i++) + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) if (mlxsw_sp->rifs[i] && mlxsw_sp->rifs[i]->dev == dev) return mlxsw_sp->rifs[i]; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 78fc557d..113f667 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -379,12 +379,10 @@ static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; struct mlxsw_sp_vr *vr; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_virtual_routers; i++) { + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { vr = &mlxsw_sp->router.vrs[i]; if (!vr->used) return vr; @@ -426,14 +424,12 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp, u32 tb_id, enum mlxsw_sp_l3proto proto) { - struct mlxsw_resources *resources; struct mlxsw_sp_vr *vr; int i; tb_id = mlxsw_sp_fix_tb_id(tb_id); - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_virtual_routers; i++) { + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { vr = &mlxsw_sp->router.vrs[i]; if (vr->used && vr->proto == proto && vr->tb_id == tb_id) return vr; @@ -569,21 +565,20 @@ static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr) static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; struct mlxsw_sp_vr *vr; + u64 max_vrs; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - if (!resources->max_virtual_routers_valid) + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_VRS)) return -EIO; - mlxsw_sp->router.vrs = kcalloc(resources->max_virtual_routers, - sizeof(struct mlxsw_sp_vr), + max_vrs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); + mlxsw_sp->router.vrs = kcalloc(max_vrs, sizeof(struct mlxsw_sp_vr), GFP_KERNEL); if (!mlxsw_sp->router.vrs) return -ENOMEM; - for (i = 0; i < resources->max_virtual_routers; i++) { + for (i = 0; i < max_vrs; i++) { vr = &mlxsw_sp->router.vrs[i]; vr->id = i; } @@ -1875,15 +1870,13 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; struct mlxsw_sp_fib_entry *fib_entry; struct mlxsw_sp_fib_entry *tmp; struct mlxsw_sp_vr *vr; int i; int err; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_virtual_routers; i++) { + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { vr = &mlxsw_sp->router.vrs[i]; if (!vr->used) continue; @@ -1908,21 +1901,21 @@ static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; char rgcr_pl[MLXSW_REG_RGCR_LEN]; + u64 max_rifs; int err; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - if (!resources->max_rif_valid) + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS)) return -EIO; - mlxsw_sp->rifs = kcalloc(resources->max_rif, - sizeof(struct mlxsw_sp_rif *), GFP_KERNEL); + max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); + mlxsw_sp->rifs = kcalloc(max_rifs, sizeof(struct mlxsw_sp_rif *), + GFP_KERNEL); if (!mlxsw_sp->rifs) return -ENOMEM; mlxsw_reg_rgcr_pack(rgcr_pl, true); - mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, resources->max_rif); + mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); if (err) goto err_rgcr_fail; @@ -1936,15 +1929,13 @@ err_rgcr_fail: static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) { - struct mlxsw_resources *resources; char rgcr_pl[MLXSW_REG_RGCR_LEN]; int i; mlxsw_reg_rgcr_pack(rgcr_pl, false); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_rif; i++) + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) WARN_ON_ONCE(mlxsw_sp->rifs[i]); kfree(mlxsw_sp->rifs); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 5e00c79..b19552a7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1196,11 +1196,12 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp, u16 lag_id) { struct mlxsw_sp_port *mlxsw_sp_port; - struct mlxsw_resources *resources; + u64 max_lag_members; int i; - resources = mlxsw_core_resources_get(mlxsw_sp->core); - for (i = 0; i < resources->max_ports_in_lag; i++) { + max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core, + MAX_LAG_MEMBERS); + for (i = 0; i < max_lag_members; i++) { mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i); if (mlxsw_sp_port) return mlxsw_sp_port; -- cgit v1.1 From d65f2fa680d6f91438461df54c83a331b3a631c9 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Fri, 21 Oct 2016 00:18:08 +0300 Subject: net/sched: em_meta: Fix 'meta vlan' to correctly recognize zero VID frames META_COLLECTOR int_vlan_tag() assumes that if the accel tag (vlan_tci) is zero, then no vlan accel tag is present. This is incorrect for zero VID vlan accel packets, making the following match fail: tc filter add ... basic match 'meta(vlan mask 0xfff eq 0)' ... Apparently 'int_vlan_tag' was implemented prior VLAN_TAG_PRESENT was introduced in 05423b2 "vlan: allow null VLAN ID to be used" (and at time introduced, the 'vlan_tx_tag_get' call in em_meta was not adapted). Fix, testing skb_vlan_tag_present instead of testing skb_vlan_tag_get's value. Fixes: 05423b2413 ("vlan: allow null VLAN ID to be used") Fixes: 1a31f2042e ("netsched: Allow meta match on vlan tag on receive") Signed-off-by: Shmulik Ladkani Cc: Eric Dumazet Cc: Stephen Hemminger Signed-off-by: David S. Miller --- net/sched/em_meta.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index a309a07..41c80b6 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -176,11 +176,12 @@ META_COLLECTOR(int_vlan_tag) { unsigned short tag; - tag = skb_vlan_tag_get(skb); - if (!tag && __vlan_get_tag(skb, &tag)) - *err = -1; - else + if (skb_vlan_tag_present(skb)) + dst->value = skb_vlan_tag_get(skb); + else if (!__vlan_get_tag(skb, &tag)) dst->value = tag; + else + *err = -1; } -- cgit v1.1 From 7281a66590cbc284272e18cb4acbfb88ddaa4cd8 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Thu, 20 Oct 2016 19:45:43 -0700 Subject: net: allow to kill a task which waits net_mutex in copy_new_ns net_mutex can be locked for a long time. It may be because many namespaces are being destroyed or many processes decide to create a network namespace. Both these operations are heavy, so it is better to have an ability to kill a process which is waiting net_mutex. Cc: "David S. Miller" Cc: Eric W. Biederman Signed-off-by: Andrei Vagin Signed-off-by: David S. Miller --- net/core/net_namespace.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 989434f..b9243b1 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -379,7 +379,14 @@ struct net *copy_net_ns(unsigned long flags, get_user_ns(user_ns); - mutex_lock(&net_mutex); + rv = mutex_lock_killable(&net_mutex); + if (rv < 0) { + net_free(net); + dec_net_namespaces(ucounts); + put_user_ns(user_ns); + return ERR_PTR(rv); + } + net->ucounts = ucounts; rv = setup_net(net, user_ns); if (rv == 0) { -- cgit v1.1 From f76a9db351f8beb3259c4ba38058de0058ab8000 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 21 Oct 2016 16:10:22 +0200 Subject: lwt: Remove unused len field The field is initialized by ILA and MPLS but never used. Remove it. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/net/lwtunnel.h | 3 +-- net/ipv6/ila/ila_lwt.c | 4 +--- net/mpls/mpls_iptunnel.c | 5 +---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h index 67d235f..82e76fe 100644 --- a/include/net/lwtunnel.h +++ b/include/net/lwtunnel.h @@ -24,11 +24,10 @@ enum { struct lwtunnel_state { __u16 type; __u16 flags; + __u16 headroom; atomic_t refcnt; int (*orig_output)(struct net *net, struct sock *sk, struct sk_buff *skb); int (*orig_input)(struct sk_buff *); - int len; - __u16 headroom; struct rcu_head rcu; __u8 data[0]; }; diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index c7a39d0..a7bc54a 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -122,7 +122,6 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, struct ila_lwt *ilwt; struct ila_params *p; struct nlattr *tb[ILA_ATTR_MAX + 1]; - size_t encap_len = sizeof(*ilwt); struct lwtunnel_state *newts; const struct fib6_config *cfg6 = cfg; struct ila_addr *iaddr; @@ -155,7 +154,7 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, if (!tb[ILA_ATTR_LOCATOR]) return -EINVAL; - newts = lwtunnel_state_alloc(encap_len); + newts = lwtunnel_state_alloc(sizeof(*ilwt)); if (!newts) return -ENOMEM; @@ -166,7 +165,6 @@ static int ila_build_state(struct net_device *dev, struct nlattr *nla, return ret; } - newts->len = encap_len; p = ila_params_lwtunnel(newts); p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c index cf52cf3..2f7ccd9 100644 --- a/net/mpls/mpls_iptunnel.c +++ b/net/mpls/mpls_iptunnel.c @@ -133,7 +133,6 @@ static int mpls_build_state(struct net_device *dev, struct nlattr *nla, struct mpls_iptunnel_encap *tun_encap_info; struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1]; struct lwtunnel_state *newts; - int tun_encap_info_len; int ret; ret = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, @@ -144,13 +143,11 @@ static int mpls_build_state(struct net_device *dev, struct nlattr *nla, if (!tb[MPLS_IPTUNNEL_DST]) return -EINVAL; - tun_encap_info_len = sizeof(*tun_encap_info); - newts = lwtunnel_state_alloc(tun_encap_info_len); + newts = lwtunnel_state_alloc(sizeof(*tun_encap_info)); if (!newts) return -ENOMEM; - newts->len = tun_encap_info_len; tun_encap_info = mpls_lwtunnel_encap(newts); ret = nla_get_labels(tb[MPLS_IPTUNNEL_DST], MAX_NEW_LABELS, &tun_encap_info->labels, tun_encap_info->label); -- cgit v1.1 From 432490f9d455fb842d70219f22d9d2c812371676 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 21 Oct 2016 13:03:44 +0300 Subject: net: ip, diag -- Add diag interface for raw sockets In criu we are actively using diag interface to collect sockets present in the system when dumping applications. And while for unix, tcp, udp[lite], packet, netlink it works as expected, the raw sockets do not have. Thus add it. v2: - add missing sock_put calls in raw_diag_dump_one (by eric.dumazet@) - implement @destroy for diag requests (by dsa@) v3: - add export of raw_abort for IPv6 (by dsa@) - pass net-admin flag into inet_sk_diag_fill due to changes in net-next branch (by dsa@) v4: - use @pad in struct inet_diag_req_v2 for raw socket protocol specification: raw module carries sockets which may have custom protocol passed from socket() syscall and sole @sdiag_protocol is not enough to match underlied ones - start reporting protocol specifed in socket() call when sockets are raw ones for the same reason: user space tools like ss may parse this attribute and use it for socket matching v5 (by eric.dumazet@): - use sock_hold in raw_sock_get instead of atomic_inc, we're holding (raw_v4_hashinfo|raw_v6_hashinfo)->lock when looking up so counter won't be zero here. v6: - use sdiag_raw_protocol() helper which will access @pad structure used for raw sockets protocol specification: we can't simply rename this member without breaking uapi v7: - sine sdiag_raw_protocol() helper is not suitable for uapi lets rather make an alias structure with proper names. __check_inet_diag_req_raw helper will catch if any of structure unintentionally changed. CC: David S. Miller CC: Eric Dumazet CC: David Ahern CC: Alexey Kuznetsov CC: James Morris CC: Hideaki YOSHIFUJI CC: Patrick McHardy CC: Andrey Vagin CC: Stephen Hemminger Signed-off-by: Cyrill Gorcunov Signed-off-by: David S. Miller --- include/net/raw.h | 6 + include/net/rawv6.h | 7 ++ include/uapi/linux/inet_diag.h | 17 +++ net/ipv4/Kconfig | 8 ++ net/ipv4/Makefile | 1 + net/ipv4/inet_diag.c | 9 ++ net/ipv4/raw.c | 21 +++- net/ipv4/raw_diag.c | 261 +++++++++++++++++++++++++++++++++++++++++ net/ipv6/raw.c | 7 +- 9 files changed, 333 insertions(+), 4 deletions(-) create mode 100644 net/ipv4/raw_diag.c diff --git a/include/net/raw.h b/include/net/raw.h index 3e78900..57c33dd 100644 --- a/include/net/raw.h +++ b/include/net/raw.h @@ -23,6 +23,12 @@ extern struct proto raw_prot; +extern struct raw_hashinfo raw_v4_hashinfo; +struct sock *__raw_v4_lookup(struct net *net, struct sock *sk, + unsigned short num, __be32 raddr, + __be32 laddr, int dif); + +int raw_abort(struct sock *sk, int err); void raw_icmp_error(struct sk_buff *, int, u32); int raw_local_deliver(struct sk_buff *, int); diff --git a/include/net/rawv6.h b/include/net/rawv6.h index 87783de..cbe4e9d 100644 --- a/include/net/rawv6.h +++ b/include/net/rawv6.h @@ -3,6 +3,13 @@ #include +extern struct raw_hashinfo raw_v6_hashinfo; +struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, + unsigned short num, const struct in6_addr *loc_addr, + const struct in6_addr *rmt_addr, int dif); + +int raw_abort(struct sock *sk, int err); + void raw6_icmp_error(struct sk_buff *, int nexthdr, u8 type, u8 code, int inner_offset, __be32); bool raw6_local_deliver(struct sk_buff *, int); diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h index 509cd96..bbe2010 100644 --- a/include/uapi/linux/inet_diag.h +++ b/include/uapi/linux/inet_diag.h @@ -43,6 +43,23 @@ struct inet_diag_req_v2 { struct inet_diag_sockid id; }; +/* + * SOCK_RAW sockets require the underlied protocol to be + * additionally specified so we can use @pad member for + * this, but we can't rename it because userspace programs + * still may depend on this name. Instead lets use another + * structure definition as an alias for struct + * @inet_diag_req_v2. + */ +struct inet_diag_req_raw { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u8 idiag_ext; + __u8 sdiag_raw_protocol; + __u32 idiag_states; + struct inet_diag_sockid id; +}; + enum { INET_DIAG_REQ_NONE, INET_DIAG_REQ_BYTECODE, diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 300b068..28e051a 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -430,6 +430,14 @@ config INET_UDP_DIAG Support for UDP socket monitoring interface used by the ss tool. If unsure, say Y. +config INET_RAW_DIAG + tristate "RAW: socket monitoring interface" + depends on INET_DIAG && (IPV6 || IPV6=n) + default n + ---help--- + Support for RAW socket monitoring interface used by the ss tool. + If unsure, say Y. + config INET_DIAG_DESTROY bool "INET: allow privileged process to administratively close sockets" depends on INET_DIAG diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index bc6a6c8..48af58a 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_NETFILTER) += netfilter.o netfilter/ obj-$(CONFIG_INET_DIAG) += inet_diag.o obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o obj-$(CONFIG_INET_UDP_DIAG) += udp_diag.o +obj-$(CONFIG_INET_RAW_DIAG) += raw_diag.o obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o obj-$(CONFIG_TCP_CONG_BBR) += tcp_bbr.o obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 0a1d4a8..3b34024 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -200,6 +200,15 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO)) goto errout; + /* + * RAW sockets might have user-defined protocols assigned, + * so report the one supplied on socket creation. + */ + if (sk->sk_type == SOCK_RAW) { + if (nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol)) + goto errout; + } + if (!icsk) { handler->idiag_get_info(sk, r, NULL); goto out; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 90a85c9..03618ed 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -89,9 +89,10 @@ struct raw_frag_vec { int hlen; }; -static struct raw_hashinfo raw_v4_hashinfo = { +struct raw_hashinfo raw_v4_hashinfo = { .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), }; +EXPORT_SYMBOL_GPL(raw_v4_hashinfo); int raw_hash_sk(struct sock *sk) { @@ -120,7 +121,7 @@ void raw_unhash_sk(struct sock *sk) } EXPORT_SYMBOL_GPL(raw_unhash_sk); -static struct sock *__raw_v4_lookup(struct net *net, struct sock *sk, +struct sock *__raw_v4_lookup(struct net *net, struct sock *sk, unsigned short num, __be32 raddr, __be32 laddr, int dif) { sk_for_each_from(sk) { @@ -136,6 +137,7 @@ static struct sock *__raw_v4_lookup(struct net *net, struct sock *sk, found: return sk; } +EXPORT_SYMBOL_GPL(__raw_v4_lookup); /* * 0 - deliver @@ -912,6 +914,20 @@ static int compat_raw_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg } #endif +int raw_abort(struct sock *sk, int err) +{ + lock_sock(sk); + + sk->sk_err = err; + sk->sk_error_report(sk); + udp_disconnect(sk, 0); + + release_sock(sk); + + return 0; +} +EXPORT_SYMBOL_GPL(raw_abort); + struct proto raw_prot = { .name = "RAW", .owner = THIS_MODULE, @@ -937,6 +953,7 @@ struct proto raw_prot = { .compat_getsockopt = compat_raw_getsockopt, .compat_ioctl = compat_raw_ioctl, #endif + .diag_destroy = raw_abort, }; #ifdef CONFIG_PROC_FS diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c new file mode 100644 index 0000000..ef3bea0 --- /dev/null +++ b/net/ipv4/raw_diag.c @@ -0,0 +1,261 @@ +#include + +#include +#include + +#include +#include + +#ifdef pr_fmt +# undef pr_fmt +#endif + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +static struct raw_hashinfo * +raw_get_hashinfo(const struct inet_diag_req_v2 *r) +{ + if (r->sdiag_family == AF_INET) { + return &raw_v4_hashinfo; +#if IS_ENABLED(CONFIG_IPV6) + } else if (r->sdiag_family == AF_INET6) { + return &raw_v6_hashinfo; +#endif + } else { + pr_warn_once("Unexpected inet family %d\n", + r->sdiag_family); + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); + } +} + +/* + * Due to requirement of not breaking user API we can't simply + * rename @pad field in inet_diag_req_v2 structure, instead + * use helper to figure it out. + */ + +static struct sock *raw_lookup(struct net *net, struct sock *from, + const struct inet_diag_req_v2 *req) +{ + struct inet_diag_req_raw *r = (void *)req; + struct sock *sk = NULL; + + if (r->sdiag_family == AF_INET) + sk = __raw_v4_lookup(net, from, r->sdiag_raw_protocol, + r->id.idiag_dst[0], + r->id.idiag_src[0], + r->id.idiag_if); +#if IS_ENABLED(CONFIG_IPV6) + else + sk = __raw_v6_lookup(net, from, r->sdiag_raw_protocol, + (const struct in6_addr *)r->id.idiag_src, + (const struct in6_addr *)r->id.idiag_dst, + r->id.idiag_if); +#endif + return sk; +} + +static struct sock *raw_sock_get(struct net *net, const struct inet_diag_req_v2 *r) +{ + struct raw_hashinfo *hashinfo = raw_get_hashinfo(r); + struct sock *sk = NULL, *s; + int slot; + + if (IS_ERR(hashinfo)) + return ERR_CAST(hashinfo); + + read_lock(&hashinfo->lock); + for (slot = 0; slot < RAW_HTABLE_SIZE; slot++) { + sk_for_each(s, &hashinfo->ht[slot]) { + sk = raw_lookup(net, s, r); + if (sk) { + /* + * Grab it and keep until we fill + * diag meaage to be reported, so + * caller should call sock_put then. + * We can do that because we're keeping + * hashinfo->lock here. + */ + sock_hold(sk); + break; + } + } + } + read_unlock(&hashinfo->lock); + + return sk ? sk : ERR_PTR(-ENOENT); +} + +static int raw_diag_dump_one(struct sk_buff *in_skb, + const struct nlmsghdr *nlh, + const struct inet_diag_req_v2 *r) +{ + struct net *net = sock_net(in_skb->sk); + struct sk_buff *rep; + struct sock *sk; + int err; + + sk = raw_sock_get(net, r); + if (IS_ERR(sk)) + return PTR_ERR(sk); + + rep = nlmsg_new(sizeof(struct inet_diag_msg) + + sizeof(struct inet_diag_meminfo) + 64, + GFP_KERNEL); + if (!rep) { + sock_put(sk); + return -ENOMEM; + } + + err = inet_sk_diag_fill(sk, NULL, rep, r, + sk_user_ns(NETLINK_CB(in_skb).sk), + NETLINK_CB(in_skb).portid, + nlh->nlmsg_seq, 0, nlh, + netlink_net_capable(in_skb, CAP_NET_ADMIN)); + sock_put(sk); + + if (err < 0) { + kfree_skb(rep); + return err; + } + + err = netlink_unicast(net->diag_nlsk, rep, + NETLINK_CB(in_skb).portid, + MSG_DONTWAIT); + if (err > 0) + err = 0; + return err; +} + +static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, + struct netlink_callback *cb, + const struct inet_diag_req_v2 *r, + struct nlattr *bc, bool net_admin) +{ + if (!inet_diag_bc_sk(bc, sk)) + return 0; + + return inet_sk_diag_fill(sk, NULL, skb, r, + sk_user_ns(NETLINK_CB(cb->skb).sk), + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + cb->nlh, net_admin); +} + +static void raw_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, + const struct inet_diag_req_v2 *r, struct nlattr *bc) +{ + bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); + struct raw_hashinfo *hashinfo = raw_get_hashinfo(r); + struct net *net = sock_net(skb->sk); + int num, s_num, slot, s_slot; + struct sock *sk = NULL; + + if (IS_ERR(hashinfo)) + return; + + s_slot = cb->args[0]; + num = s_num = cb->args[1]; + + read_lock(&hashinfo->lock); + for (slot = s_slot; slot < RAW_HTABLE_SIZE; s_num = 0, slot++) { + num = 0; + + sk_for_each(sk, &hashinfo->ht[slot]) { + struct inet_sock *inet = inet_sk(sk); + + if (!net_eq(sock_net(sk), net)) + continue; + if (num < s_num) + goto next; + if (sk->sk_family != r->sdiag_family) + goto next; + if (r->id.idiag_sport != inet->inet_sport && + r->id.idiag_sport) + goto next; + if (r->id.idiag_dport != inet->inet_dport && + r->id.idiag_dport) + goto next; + if (sk_diag_dump(sk, skb, cb, r, bc, net_admin) < 0) + goto out_unlock; +next: + num++; + } + } + +out_unlock: + read_unlock(&hashinfo->lock); + + cb->args[0] = slot; + cb->args[1] = num; +} + +static void raw_diag_get_info(struct sock *sk, struct inet_diag_msg *r, + void *info) +{ + r->idiag_rqueue = sk_rmem_alloc_get(sk); + r->idiag_wqueue = sk_wmem_alloc_get(sk); +} + +#ifdef CONFIG_INET_DIAG_DESTROY +static int raw_diag_destroy(struct sk_buff *in_skb, + const struct inet_diag_req_v2 *r) +{ + struct net *net = sock_net(in_skb->sk); + struct sock *sk; + + sk = raw_sock_get(net, r); + if (IS_ERR(sk)) + return PTR_ERR(sk); + return sock_diag_destroy(sk, ECONNABORTED); +} +#endif + +static const struct inet_diag_handler raw_diag_handler = { + .dump = raw_diag_dump, + .dump_one = raw_diag_dump_one, + .idiag_get_info = raw_diag_get_info, + .idiag_type = IPPROTO_RAW, + .idiag_info_size = 0, +#ifdef CONFIG_INET_DIAG_DESTROY + .destroy = raw_diag_destroy, +#endif +}; + +static void __always_unused __check_inet_diag_req_raw(void) +{ + /* + * Make sure the two structures are identical, + * except the @pad field. + */ +#define __offset_mismatch(m1, m2) \ + (offsetof(struct inet_diag_req_v2, m1) != \ + offsetof(struct inet_diag_req_raw, m2)) + + BUILD_BUG_ON(sizeof(struct inet_diag_req_v2) != + sizeof(struct inet_diag_req_raw)); + BUILD_BUG_ON(__offset_mismatch(sdiag_family, sdiag_family)); + BUILD_BUG_ON(__offset_mismatch(sdiag_protocol, sdiag_protocol)); + BUILD_BUG_ON(__offset_mismatch(idiag_ext, idiag_ext)); + BUILD_BUG_ON(__offset_mismatch(pad, sdiag_raw_protocol)); + BUILD_BUG_ON(__offset_mismatch(idiag_states, idiag_states)); + BUILD_BUG_ON(__offset_mismatch(id, id)); +#undef __offset_mismatch +} + +static int __init raw_diag_init(void) +{ + return inet_diag_register(&raw_diag_handler); +} + +static void __exit raw_diag_exit(void) +{ + inet_diag_unregister(&raw_diag_handler); +} + +module_init(raw_diag_init); +module_exit(raw_diag_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-255 /* AF_INET - IPPROTO_RAW */); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10-255 /* AF_INET6 - IPPROTO_RAW */); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 54404f0..d7e8b95 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -65,11 +65,12 @@ #define ICMPV6_HDRLEN 4 /* ICMPv6 header, RFC 4443 Section 2.1 */ -static struct raw_hashinfo raw_v6_hashinfo = { +struct raw_hashinfo raw_v6_hashinfo = { .lock = __RW_LOCK_UNLOCKED(raw_v6_hashinfo.lock), }; +EXPORT_SYMBOL_GPL(raw_v6_hashinfo); -static struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, +struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, unsigned short num, const struct in6_addr *loc_addr, const struct in6_addr *rmt_addr, int dif) { @@ -102,6 +103,7 @@ static struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, found: return sk; } +EXPORT_SYMBOL_GPL(__raw_v6_lookup); /* * 0 - deliver @@ -1259,6 +1261,7 @@ struct proto rawv6_prot = { .compat_getsockopt = compat_rawv6_getsockopt, .compat_ioctl = compat_rawv6_ioctl, #endif + .diag_destroy = raw_abort, }; #ifdef CONFIG_PROC_FS -- cgit v1.1 From 7948b87308a489c2caa23574ea3c72298288c374 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Thu, 22 Sep 2016 14:41:28 +0300 Subject: iwlwifi: mvm: enable dynamic queue allocation mode New firmwares support dynamic queue allocation (DQA), which enables on-demand allocation of queues per RA/TID, instead of allocating them statically per vif. This allows an AP to send, for instance, BE traffic to STA2 even if it also needs to send traffic to a sleeping STA1, without being blocked by the sleeping station. The implementation in the driver is now ready, so we can enable this feature by default when running firmwares that support it. Signed-off-by: Liad Kaufman [reworded the commit message] Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 726ba48..cde8c6c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1111,9 +1111,8 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) { - /* Make sure DQA isn't allowed in driver until feature is complete */ - return false && fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DQA_SUPPORT); + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DQA_SUPPORT); } static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) -- cgit v1.1 From 7e62a699aafbd97928f19a8356d719b71b0e151c Mon Sep 17 00:00:00 2001 From: Aviya Erenfeld Date: Tue, 20 Sep 2016 18:07:44 +0300 Subject: iwlwifi: mvm: use dev_coredumpsg() iwlmvm currently uses dev_coredumpm() to collect multiple buffers, but this has the downside of pinning the module until the coredump expires, if the data isn't read by any userspace. Avoid this by using the new dev_coredumpsg() method. We still copy the data from the old way of generating it, but neither hold on to vmalloc'ed data for a long time, nor do we pin the module now. Signed-off-by: Aviya Erenfeld [rewrite commit message] Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 100 +++++++++++++----------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index d89d0a1..2e8e3e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -70,49 +70,6 @@ #include "iwl-prph.h" #include "iwl-csr.h" -static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, - void *data, size_t datalen) -{ - const struct iwl_mvm_dump_ptrs *dump_ptrs = data; - ssize_t bytes_read; - ssize_t bytes_read_trans; - - if (offset < dump_ptrs->op_mode_len) { - bytes_read = min_t(ssize_t, count, - dump_ptrs->op_mode_len - offset); - memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, - bytes_read); - offset += bytes_read; - count -= bytes_read; - - if (count == 0) - return bytes_read; - } else { - bytes_read = 0; - } - - if (!dump_ptrs->trans_ptr) - return bytes_read; - - offset -= dump_ptrs->op_mode_len; - bytes_read_trans = min_t(ssize_t, count, - dump_ptrs->trans_ptr->len - offset); - memcpy(buffer + bytes_read, - (u8 *)dump_ptrs->trans_ptr->data + offset, - bytes_read_trans); - - return bytes_read + bytes_read_trans; -} - -static void iwl_mvm_free_coredump(void *data) -{ - const struct iwl_mvm_dump_ptrs *fw_error_dump = data; - - vfree(fw_error_dump->op_mode_ptr); - vfree(fw_error_dump->trans_ptr); - kfree(fw_error_dump); -} - #define RADIO_REG_MAX_READ 0x2ad static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) @@ -491,6 +448,43 @@ static u32 iwl_dump_prph(struct iwl_trans *trans, return prph_len; } +/* + * alloc_sgtable - allocates scallerlist table in the given size, + * fills it with pages and returns it + * @size: the size (in bytes) of the table +*/ +static struct scatterlist *alloc_sgtable(int size) +{ + int alloc_size, nents, i; + struct page *new_page; + struct scatterlist *iter; + struct scatterlist *table; + + nents = DIV_ROUND_UP(size, PAGE_SIZE); + table = kcalloc(nents, sizeof(*table), GFP_KERNEL); + if (!table) + return NULL; + sg_init_table(table, nents); + iter = table; + for_each_sg(table, iter, sg_nents(table), i) { + new_page = alloc_page(GFP_KERNEL); + if (!new_page) { + /* release all previous allocated pages in the table */ + iter = table; + for_each_sg(table, iter, sg_nents(table), i) { + new_page = sg_page(iter); + if (new_page) + __free_page(new_page); + } + return NULL; + } + alloc_size = min_t(int, size, PAGE_SIZE); + size -= PAGE_SIZE; + sg_set_page(iter, new_page, alloc_size, 0); + } + return table; +} + void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) { struct iwl_fw_error_dump_file *dump_file; @@ -499,6 +493,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_mem *dump_mem; struct iwl_fw_error_dump_trigger_desc *dump_trig; struct iwl_mvm_dump_ptrs *fw_error_dump; + struct scatterlist *sg_dump_data; u32 sram_len, sram_ofs; struct iwl_fw_dbg_mem_seg_tlv * const *fw_dbg_mem = mvm->fw->dbg_mem_tlv; @@ -815,8 +810,23 @@ dump_trans_data: file_len += fw_error_dump->trans_ptr->len; dump_file->file_len = cpu_to_le32(file_len); - dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, - GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); + sg_dump_data = alloc_sgtable(file_len); + if (sg_dump_data) { + sg_pcopy_from_buffer(sg_dump_data, + sg_nents(sg_dump_data), + fw_error_dump->op_mode_ptr, + fw_error_dump->op_mode_len, 0); + sg_pcopy_from_buffer(sg_dump_data, + sg_nents(sg_dump_data), + fw_error_dump->trans_ptr->data, + fw_error_dump->trans_ptr->len, + fw_error_dump->op_mode_len); + dev_coredumpsg(mvm->trans->dev, sg_dump_data, file_len, + GFP_KERNEL); + } + vfree(fw_error_dump->op_mode_ptr); + vfree(fw_error_dump->trans_ptr); + kfree(fw_error_dump); out: iwl_mvm_free_fw_dump_desc(mvm); -- cgit v1.1 From dd2602d00f80dd8bf30f083fde20b4fbbcf82266 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Mon, 26 Sep 2016 21:23:37 +0800 Subject: netfilter: xt_multiport: Use switch case instead of multiple condition checks There are multiple equality condition checks in the original codes, so it is better to use switch case instead of them. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_multiport.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index ac1d3c3..ec06fb1 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -42,29 +42,31 @@ ports_match_v1(const struct xt_multiport_v1 *minfo, e = minfo->ports[++i]; pr_debug("src or dst matches with %d-%d?\n", s, e); - if (minfo->flags == XT_MULTIPORT_SOURCE - && src >= s && src <= e) - return true ^ minfo->invert; - if (minfo->flags == XT_MULTIPORT_DESTINATION - && dst >= s && dst <= e) - return true ^ minfo->invert; - if (minfo->flags == XT_MULTIPORT_EITHER - && ((dst >= s && dst <= e) - || (src >= s && src <= e))) - return true ^ minfo->invert; + switch (minfo->flags) { + case XT_MULTIPORT_SOURCE: + return (src >= s && src <= e) ^ minfo->invert; + case XT_MULTIPORT_DESTINATION: + return (dst >= s && dst <= e) ^ minfo->invert; + case XT_MULTIPORT_EITHER: + return ((dst >= s && dst <= e) || + (src >= s && src <= e)) ^ minfo->invert; + default: + break; + } } else { /* exact port matching */ pr_debug("src or dst matches with %d?\n", s); - if (minfo->flags == XT_MULTIPORT_SOURCE - && src == s) - return true ^ minfo->invert; - if (minfo->flags == XT_MULTIPORT_DESTINATION - && dst == s) - return true ^ minfo->invert; - if (minfo->flags == XT_MULTIPORT_EITHER - && (src == s || dst == s)) - return true ^ minfo->invert; + switch (minfo->flags) { + case XT_MULTIPORT_SOURCE: + return (src == s) ^ minfo->invert; + case XT_MULTIPORT_DESTINATION: + return (dst == s) ^ minfo->invert; + case XT_MULTIPORT_EITHER: + return (src == s || dst == s) ^ minfo->invert; + default: + break; + } } } -- cgit v1.1 From 0813fbc913582feef688265f2a5d7c077d6a3d1f Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Fri, 7 Oct 2016 14:02:16 -0700 Subject: netfilter: nfnetlink_log: Use GFP_NOWARN for skb allocation Since the code explicilty falls back to a smaller allocation when the large one fails, we shouldn't complain when that happens. Signed-off-by: Calvin Owens Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index eb086a1..7435505 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -330,7 +330,7 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size, * message. WARNING: has to be <= 128k due to slab restrictions */ n = max(inst_size, pkt_size); - skb = alloc_skb(n, GFP_ATOMIC); + skb = alloc_skb(n, GFP_ATOMIC | __GFP_NOWARN); if (!skb) { if (n > pkt_size) { /* try to allocate only as much as we need for current -- cgit v1.1 From 5efa0fc6d7f7930b18801f07cefae8eeacd6ac02 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 18 Oct 2016 17:35:18 +0200 Subject: netfilter: nf_tables: allow expressions to return STOLEN Currently not supported, we'd oops as skb was (or is) free'd elsewhere. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 0dd5c69..70de32a 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -178,6 +178,7 @@ next_rule: case NF_ACCEPT: case NF_DROP: case NF_QUEUE: + case NF_STOLEN: nft_trace_packet(&info, chain, rule, rulenum, NFT_TRACETYPE_RULE); return regs.verdict.code; -- cgit v1.1 From 0ecba4d9d1db51f670af7ba9049461116a3a2bea Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Wed, 19 Oct 2016 23:29:53 +0800 Subject: netfilter: nft_numgen: start round robin from zero Currently we start round robin from 1, but it's better to start round robin from 0. This is to keep consistent with xt_statistic in iptables. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_numgen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c index 55bc5ab..a66b360 100644 --- a/net/netfilter/nft_numgen.c +++ b/net/netfilter/nft_numgen.c @@ -65,7 +65,7 @@ static int nft_ng_inc_init(const struct nft_ctx *ctx, return -EOVERFLOW; priv->dreg = nft_parse_register(tb[NFTA_NG_DREG]); - atomic_set(&priv->counter, 0); + atomic_set(&priv->counter, priv->modulus - 1); return nft_validate_register_store(ctx, priv->dreg, NULL, NFT_DATA_VALUE, sizeof(u32)); -- cgit v1.1 From 96d9f2a72c719d985fdbae4e3d63ddc874947a7e Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Wed, 19 Oct 2016 23:31:29 +0800 Subject: netfilter: nft_meta: permit pkttype mangling in ip/ip6 prerouting After supporting this, we can combine it with hash expression to emulate the 'cluster match'. Suggested-by: Pablo Neira Ayuso Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 6c1e024..6499402 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -310,6 +310,11 @@ int nft_meta_set_validate(const struct nft_ctx *ctx, case NFPROTO_NETDEV: hooks = 1 << NF_NETDEV_INGRESS; break; + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + hooks = 1 << NF_INET_PRE_ROUTING; + break; default: return -EOPNOTSUPP; } -- cgit v1.1 From 254432613c588640f8b8b5c3641a3c27bbe14688 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 20 Oct 2016 18:07:14 +0200 Subject: netfilter: nft_ct: add notrack support This patch adds notrack support. I decided to add a new expression, given that this doesn't fit into the existing set operation. Notrack doesn't need a source register, and an hypothetical NFT_CT_NOTRACK key makes no sense since matching the untracked state is done through NFT_CT_STATE. I'm placing this new notrack expression into nft_ct.c, I think a single module is too much. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_ct.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index d7b0d171..6837348 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2008-2009 Patrick McHardy + * Copyright (c) 2016 Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -518,15 +519,61 @@ static struct nft_expr_type nft_ct_type __read_mostly = { .owner = THIS_MODULE, }; +static void nft_notrack_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct sk_buff *skb = pkt->skb; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(pkt->skb, &ctinfo); + /* Previously seen (loopback or untracked)? Ignore. */ + if (ct) + return; + + ct = nf_ct_untracked_get(); + atomic_inc(&ct->ct_general.use); + skb->nfct = &ct->ct_general; + skb->nfctinfo = IP_CT_NEW; +} + +static struct nft_expr_type nft_notrack_type; +static const struct nft_expr_ops nft_notrack_ops = { + .type = &nft_notrack_type, + .size = NFT_EXPR_SIZE(0), + .eval = nft_notrack_eval, +}; + +static struct nft_expr_type nft_notrack_type __read_mostly = { + .name = "notrack", + .ops = &nft_notrack_ops, + .owner = THIS_MODULE, +}; + static int __init nft_ct_module_init(void) { + int err; + BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > NFT_REG_SIZE); - return nft_register_expr(&nft_ct_type); + err = nft_register_expr(&nft_ct_type); + if (err < 0) + return err; + + err = nft_register_expr(&nft_notrack_type); + if (err < 0) + goto err1; + + return 0; +err1: + nft_unregister_expr(&nft_ct_type); + return err; } static void __exit nft_ct_module_exit(void) { + nft_unregister_expr(&nft_notrack_type); nft_unregister_expr(&nft_ct_type); } @@ -536,3 +583,4 @@ module_exit(nft_ct_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Patrick McHardy "); MODULE_ALIAS_NFT_EXPR("ct"); +MODULE_ALIAS_NFT_EXPR("notrack"); -- cgit v1.1 From 73e42ff72a86e930dd873f47ea3029dc53f82c25 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 21 Oct 2016 09:02:43 -0700 Subject: sch_htb: do not report fake rate estimators When I prepared commit d250a5f90e53 ("pkt_sched: gen_estimator: Dont report fake rate estimators"), htb still had an implicit rate estimator for all its classes. Then later, I made this rate estimator optional in commit 64153ce0a7b6 ("net_sched: htb: do not setup default rate estimators"), but I forgot to update htb use of gnet_stats_copy_rate_est() After this patch, "tc -s qdisc ..." no longer report fake rate estimators for HTB classes. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_htb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index c798d0d..9926fe4 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1145,7 +1145,7 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d) if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d, NULL, &cl->bstats) < 0 || - gnet_stats_copy_rate_est(d, NULL, &cl->rate_est) < 0 || + gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, NULL, &qs, qlen) < 0) return -1; -- cgit v1.1 From 3cf25904fe467aebeaa77d402b6cf3c6c5d6303b Mon Sep 17 00:00:00 2001 From: Xo Wang Date: Fri, 21 Oct 2016 10:20:12 -0700 Subject: net: phy: broadcom: Update Auxiliary Control Register macros Add the RXD-to-RXC skew (delay) time bit in the Miscellaneous Control shadow register and a mask for the shadow selector field. Remove a re-definition of MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL. Signed-off-by: Xo Wang Reviewed-by: Florian Fainelli Reviewed-by: Joel Stanley Signed-off-by: David S. Miller --- include/linux/brcmphy.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index e3354b7..22c4421 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -105,11 +105,12 @@ #define MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA 0x0800 #define MII_BCM54XX_AUXCTL_MISC_WREN 0x8000 +#define MII_BCM54XX_AUXCTL_MISC_RXD_RXC_SKEW 0x0100 #define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200 #define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000 #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007 -#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000 +#define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007 /* * Broadcom LED source encodings. These are used in BCM5461, BCM5481, -- cgit v1.1 From d92ead16be405b6d52ff7b366d1c9865ccc684bd Mon Sep 17 00:00:00 2001 From: Xo Wang Date: Fri, 21 Oct 2016 10:20:13 -0700 Subject: net: phy: broadcom: Add support for BCM54612E This PHY has internal delays enabled after reset. This clears the internal delay enables unless the interface specifically requests them. Signed-off-by: Xo Wang Reviewed-by: Florian Fainelli Reviewed-by: Joel Stanley Signed-off-by: David S. Miller --- drivers/net/phy/broadcom.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/brcmphy.h | 1 + 2 files changed, 49 insertions(+) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 870327e..583ef8a 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -337,6 +337,41 @@ static int bcm5481_config_aneg(struct phy_device *phydev) return ret; } +static int bcm54612e_config_aneg(struct phy_device *phydev) +{ + int ret; + + /* First, auto-negotiate. */ + ret = genphy_config_aneg(phydev); + + /* Clear TX internal delay unless requested. */ + if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) && + (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) { + /* Disable TXD to GTXCLK clock delay (default set) */ + /* Bit 9 is the only field in shadow register 00011 */ + bcm_phy_write_shadow(phydev, 0x03, 0); + } + + /* Clear RX internal delay unless requested. */ + if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) && + (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) { + u16 reg; + + /* Errata: reads require filling in the write selector field */ + bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, + MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC); + reg = phy_read(phydev, MII_BCM54XX_AUX_CTL); + /* Disable RXD to RXC delay (default set) */ + reg &= ~MII_BCM54XX_AUXCTL_MISC_RXD_RXC_SKEW; + /* Clear shadow selector field */ + reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK; + bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, + MII_BCM54XX_AUXCTL_MISC_WREN | reg); + } + + return ret; +} + static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set) { int val; @@ -485,6 +520,18 @@ static struct phy_driver broadcom_drivers[] = { .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, }, { + .phy_id = PHY_ID_BCM54612E, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM54612E", + .features = PHY_GBIT_FEATURES | + SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, + .config_init = bcm54xx_config_init, + .config_aneg = bcm54612e_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, +}, { .phy_id = PHY_ID_BCM54616S, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54616S", @@ -600,6 +647,7 @@ static struct mdio_device_id __maybe_unused broadcom_tbl[] = { { PHY_ID_BCM5411, 0xfffffff0 }, { PHY_ID_BCM5421, 0xfffffff0 }, { PHY_ID_BCM5461, 0xfffffff0 }, + { PHY_ID_BCM54612E, 0xfffffff0 }, { PHY_ID_BCM54616S, 0xfffffff0 }, { PHY_ID_BCM5464, 0xfffffff0 }, { PHY_ID_BCM5481, 0xfffffff0 }, diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 22c4421..60def78 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -18,6 +18,7 @@ #define PHY_ID_BCM5421 0x002060e0 #define PHY_ID_BCM5464 0x002060b0 #define PHY_ID_BCM5461 0x002060c0 +#define PHY_ID_BCM54612E 0x03625e60 #define PHY_ID_BCM54616S 0x03625d10 #define PHY_ID_BCM57780 0x03625d90 -- cgit v1.1 From 701470babace4c4bbcc8463a5609790f91f85bdb Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 22 Oct 2016 09:46:24 +0200 Subject: batman-adv: Revert "use core MTU range checking in misc drivers" The maximum MTU is defined via the slave devices of an batman-adv interface. Thus it is not possible to calculate the max_mtu during the creation of the batman-adv device when no slave devices are attached. Doing so would for example break non-fragmentation setups which then (incorrectly) allow an MTU of 1500 even when underlying device cannot transport 1500 bytes + batman-adv headers. Checking the dynamically calculated max_mtu via the minimum of the slave devices MTU during .ndo_change_mtu is also used by the bridge interface. Cc: Jarod Wilson Fixes: b3e3893e1253 ("net: use core MTU range checking in misc drivers") Signed-off-by: Sven Eckelmann Signed-off-by: David S. Miller --- net/batman-adv/soft-interface.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 112679d..49e16b6 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -158,6 +158,17 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) return 0; } +static int batadv_interface_change_mtu(struct net_device *dev, int new_mtu) +{ + /* check ranges */ + if ((new_mtu < 68) || (new_mtu > batadv_hardif_min_mtu(dev))) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} + /** * batadv_interface_set_rx_mode - set the rx mode of a device * @dev: registered network device to modify @@ -909,6 +920,7 @@ static const struct net_device_ops batadv_netdev_ops = { .ndo_vlan_rx_add_vid = batadv_interface_add_vid, .ndo_vlan_rx_kill_vid = batadv_interface_kill_vid, .ndo_set_mac_address = batadv_interface_set_mac_addr, + .ndo_change_mtu = batadv_interface_change_mtu, .ndo_set_rx_mode = batadv_interface_set_rx_mode, .ndo_start_xmit = batadv_interface_tx, .ndo_validate_addr = eth_validate_addr, @@ -975,7 +987,6 @@ struct net_device *batadv_softif_create(struct net *net, const char *name) dev_net_set(soft_iface, net); soft_iface->rtnl_link_ops = &batadv_link_ops; - soft_iface->max_mtu = batadv_hardif_min_mtu(soft_iface); ret = register_netdevice(soft_iface); if (ret < 0) { -- cgit v1.1 From 1aaa87aff688bbb47e9ab36c638e1bb876810a39 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 12:32:04 +0000 Subject: net: netcp: drop kfree for memory allocated with devm_kzalloc It's not necessary to free memory allocated with devm_kzalloc in the remove path and using kfree leads to a double free. Fixes: 84640e27f230 ("net: netcp: Add Keystone NetCP core ethernet driver") Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 11609d5..2fb5b6d 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -2058,7 +2058,6 @@ static void netcp_delete_interface(struct netcp_device *netcp_device, if (module->release) module->release(intf_modpriv->module_priv); list_del(&intf_modpriv->intf_list); - kfree(intf_modpriv); } WARN(!list_empty(&netcp->module_head), "%s interface module list is not empty!\n", ndev->name); @@ -2153,7 +2152,6 @@ static int netcp_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "Removing module \"%s\"\n", module->name); module->remove(netcp_device, inst_modpriv->module_priv); list_del(&inst_modpriv->inst_list); - kfree(inst_modpriv); } /* now that all modules are removed, clean up the interfaces */ -- cgit v1.1 From 68497a87c4f4f918cbbdbae88652e796c75e11bd Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 14:28:00 +0000 Subject: net: dsa: mv88e6xxx: use setup_timer to simplify the code Use setup_timer function instead of initializing timer with the function and data fields. Signed-off-by: Wei Yongjun Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 157360f..72b9dac2 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -639,9 +639,8 @@ static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip) { mutex_init(&chip->ppu_mutex); INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work); - init_timer(&chip->ppu_timer); - chip->ppu_timer.data = (unsigned long)chip; - chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; + setup_timer(&chip->ppu_timer, mv88e6xxx_ppu_reenable_timer, + (unsigned long)chip); } static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip) -- cgit v1.1 From a24a9d7aca2daef4ac87d1ef00ef21586ec6affe Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 14:28:38 +0000 Subject: net: eth: altera: Fix error return code in altera_tse_probe() Fix to return error code -EINVAL from the error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/altera/altera_tse_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index cc9787e..8e92084 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -1329,11 +1329,13 @@ static int altera_tse_probe(struct platform_device *pdev) if (upper_32_bits(priv->rxdescmem_busaddr)) { dev_dbg(priv->device, "SGDMA bus addresses greater than 32-bits\n"); + ret = -EINVAL; goto err_free_netdev; } if (upper_32_bits(priv->txdescmem_busaddr)) { dev_dbg(priv->device, "SGDMA bus addresses greater than 32-bits\n"); + ret = -EINVAL; goto err_free_netdev; } } else if (priv->dmaops && -- cgit v1.1 From 0942170f32e1e9076f9a5f350250b8ec59ac04fa Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 14:34:55 +0000 Subject: net: ns83820: use dev_kfree_skb_irq instead of kfree_skb It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled, spin_lock_irqsave() make sure always in irq disable context. So the kfree_skb() should be replaced with dev_kfree_skb_irq(). This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/natsemi/ns83820.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c index a34631e..93c4bdc 100644 --- a/drivers/net/ethernet/natsemi/ns83820.c +++ b/drivers/net/ethernet/natsemi/ns83820.c @@ -919,7 +919,7 @@ netdev_mangle_me_harder_failed: ndev->stats.rx_dropped++; } } else { - kfree_skb(skb); + dev_kfree_skb_irq(skb); } nr++; -- cgit v1.1 From 675a6ceefc31a952bd5a29b6433c2c2d8c595af3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 14:35:30 +0000 Subject: amd-xgbe: Fix error return code in xgbe_probe() Fix to return error code -ENODEV from the DMA is not supported error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Acked-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 667e120..6997f11 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -613,6 +613,7 @@ static int xgbe_probe(struct platform_device *pdev) attr = device_get_dma_attr(dev); if (attr == DEV_DMA_NOT_SUPPORTED) { dev_err(dev, "DMA is not supported"); + ret = -ENODEV; goto err_io; } pdata->coherent = (attr == DEV_DMA_COHERENT); -- cgit v1.1 From f850b4a77d71a9cac513dadff91891a78c12ba05 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 14:36:36 +0000 Subject: net: ena: use setup_timer() and mod_timer() Use setup_timer() instead of init_timer(), being the preferred/standard way to set a timer up. Also, quoting the mod_timer() function comment: -> mod_timer() is a more efficient way to update the expire field of an active timer (if the timer is inactive it will be activated). Use setup_timer and mod_timer to setup and arm a timer, to make the code cleaner and easier to read. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/amazon/ena/ena_netdev.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 2a55ab0..cc8b13e 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -3013,12 +3013,9 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->last_keep_alive_jiffies = jiffies; - init_timer(&adapter->timer_service); - adapter->timer_service.expires = round_jiffies(jiffies + HZ); - adapter->timer_service.function = ena_timer_service; - adapter->timer_service.data = (unsigned long)adapter; - - add_timer(&adapter->timer_service); + setup_timer(&adapter->timer_service, ena_timer_service, + (unsigned long)adapter); + mod_timer(&adapter->timer_service, round_jiffies(jiffies + HZ)); dev_info(&pdev->dev, "%s found at mem %lx, mac addr %pM Queues %d\n", DEVICE_NAME, (long)pci_resource_start(pdev, 0), -- cgit v1.1 From e2897b8238ab8fdee067cfe472dd52a3414d5716 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 14:40:17 +0000 Subject: net: netcp: add missing of_node_put() in netcp_probe() This node pointer is returned by of_get_child_by_name() with refcount incremented in this function. of_node_put() on it before exitting this function. This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 2fb5b6d..78b4c83 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -2120,6 +2120,8 @@ static int netcp_probe(struct platform_device *pdev) } } + of_node_put(interfaces); + /* Add the device instance to the list */ list_add_tail(&netcp_device->device_list, &netcp_devices); @@ -2132,6 +2134,8 @@ probe_quit_interface: netcp_delete_interface(netcp_device, netcp_intf->ndev); } + of_node_put(interfaces); + probe_quit: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.1 From 5d48f00d836a628c695c6e3f96881d73641f6a2f Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 23 Oct 2016 16:29:03 +0200 Subject: firewire: net: fix maximum possible MTU Commit b3e3893e1253 ("net: use core MTU range checking in misc drivers") mistakenly introduced an upper limit for firewire-net's MTU based on the local link layer controller's reception capability. Revert this. Neither RFC 2734 nor our implementation impose any particular upper limit. Actually, to be on the safe side and to make the code explicit, set ETH_MAX_MTU = 65535 as upper limit now. (I replaced sizeof(struct rfc2734_header) by the equivalent RFC2374_FRAG_HDR_SIZE in order to avoid distracting long/int conversions.) Fixes: b3e3893e1253('net: use core MTU range checking in misc drivers') CC: netdev@vger.kernel.org CC: linux1394-devel@lists.sourceforge.net CC: Jarod Wilson Signed-off-by: Stefan Richter Acked-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/firewire/net.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 8430222..9937954 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -1467,10 +1467,11 @@ static int fwnet_probe(struct fw_unit *unit, * Use the RFC 2734 default 1500 octets or the maximum payload * as initial MTU */ - net->max_mtu = (1 << (card->max_receive + 1)) - - sizeof(struct rfc2734_header) - IEEE1394_GASP_HDR_SIZE; - net->mtu = min(1500U, net->max_mtu); + net->mtu = min(1500U, + (1U << (card->max_receive + 1)) + - RFC2374_FRAG_HDR_SIZE - IEEE1394_GASP_HDR_SIZE); net->min_mtu = ETH_MIN_MTU; + net->max_mtu = ETH_MAX_MTU; /* Set our hardware address while we're at it */ ha = (union fwnet_hwaddr *)net->dev_addr; -- cgit v1.1 From 89ab88b01bacc5bfc338693da3925504757e7844 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 23 Oct 2016 16:30:56 +0200 Subject: firewire: net: set initial MTU = 1500 unconditionally, fix IPv6 on some CardBus cards firewire-net, like the older eth1394 driver, reduced the initial MTU to less than 1500 octets if the local link layer controller's asynchronous packet reception limit was lower. This is bogus, since this reception limit does not have anything to do with the transmission limit. Neither did this reduction affect the TX path positively, nor could it prevent link fragmentation at the RX path. Many FireWire CardBus cards have a max_rec of 9, causing an initial MTU of 1024 - 16 = 1008. RFC 2734 and RFC 3146 allow a minimum max_rec = 8, which would result in an initial MTU of 512 - 16 = 496. On such cards, IPv6 could only be employed if the MTU was manually increased to 1280 or more, i.e. IPv6 would not work without intervention from userland. We now always initialize the MTU to 1500, which is the default according to RFC 2734 and RFC 3146. On a VIA VT6316 based CardBus card which was affected by this, changing the MTU from 1008 to 1500 also increases TX bandwidth by 6 %. RX remains unaffected. CC: netdev@vger.kernel.org CC: linux1394-devel@lists.sourceforge.net CC: Jarod Wilson Signed-off-by: Stefan Richter Signed-off-by: David S. Miller --- drivers/firewire/net.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 9937954..03715e7 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -1463,13 +1463,7 @@ static int fwnet_probe(struct fw_unit *unit, goto out; dev->local_fifo = dev->handler.offset; - /* - * Use the RFC 2734 default 1500 octets or the maximum payload - * as initial MTU - */ - net->mtu = min(1500U, - (1U << (card->max_receive + 1)) - - RFC2374_FRAG_HDR_SIZE - IEEE1394_GASP_HDR_SIZE); + net->mtu = 1500U; net->min_mtu = ETH_MIN_MTU; net->max_mtu = ETH_MAX_MTU; -- cgit v1.1 From 6edf10173a1feb1078f2fc8c655baf9614e83493 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Sun, 23 Oct 2016 17:43:05 +0200 Subject: devlink: Prevent port_type_set() callback when it's not needed When a port_type_set() is been called and the new port type set is the same as the old one, just return success. Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/devlink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/devlink.c b/net/core/devlink.c index 1b50630..d2fd736 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -608,6 +608,8 @@ static int devlink_port_type_set(struct devlink *devlink, if (devlink->ops && devlink->ops->port_type_set) { if (port_type == DEVLINK_PORT_TYPE_NOTSET) return -EINVAL; + if (port_type == devlink_port->type) + return 0; err = devlink->ops->port_type_set(devlink_port, port_type); if (err) return err; -- cgit v1.1 From 8f20542386c57bc5f40a09e38d2772d84c0b2afa Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Oct 2016 10:28:57 +0200 Subject: wireless: deprecate WDS and disable by default The old WDS 4-addr frame support is very limited, e.g. * no encryption is possible on such links * it cannot support rate/HT/VHT negotiation * management APIs are very restricted These make the WDS legacy mode useless in practice. All of these are resolved by the 4-addr AP/client support, so there's also no reason to improve WDS in the future. Therefore, add a Kconfig option to disable legacy WDS. This gives people an "emergency valve" while they migrate to the better-supported 4-addr AP/client option; we plan to remove it (and the associated cfg80211/mac80211 code, which is the ultimate goal) in the future. Signed-off-by: Johannes Berg --- drivers/net/wireless/Kconfig | 13 +++++++++++++ drivers/net/wireless/ath/ath9k/init.c | 6 ++++++ drivers/net/wireless/broadcom/b43/main.c | 2 ++ drivers/net/wireless/broadcom/b43legacy/main.c | 2 ++ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 6 ++++-- net/wireless/core.c | 10 ++++++++++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 8c8edaf..8f5a3f4 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -17,6 +17,19 @@ menuconfig WLAN if WLAN +config WIRELESS_WDS + bool "mac80211-based legacy WDS support" if EXPERT + help + This option enables the deprecated WDS support, the newer + mac80211-based 4-addr AP/client support supersedes it with + a much better feature set (HT, VHT, ...) + + We plan to remove this option and code, so if you find + that you have to enable it, please let us know on the + linux-wireless@vger.kernel.org mailing list, so we can + help you migrate to 4-addr AP/client (or, if it's really + necessary, give up on our plan of removing it). + source "drivers/net/wireless/admtek/Kconfig" source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/atmel/Kconfig" diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index cfa3fe8..368d9b3 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -734,9 +734,11 @@ static const struct ieee80211_iface_limit if_limits[] = { BIT(NL80211_IFTYPE_P2P_GO) }, }; +#ifdef CONFIG_WIRELESS_WDS static const struct ieee80211_iface_limit wds_limits[] = { { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) }, }; +#endif #ifdef CONFIG_ATH9K_CHANNEL_CONTEXT @@ -774,6 +776,7 @@ static const struct ieee80211_iface_combination if_comb[] = { BIT(NL80211_CHAN_WIDTH_40), #endif }, +#ifdef CONFIG_WIRELESS_WDS { .limits = wds_limits, .n_limits = ARRAY_SIZE(wds_limits), @@ -781,6 +784,7 @@ static const struct ieee80211_iface_combination if_comb[] = { .num_different_channels = 1, .beacon_int_infra_match = true, }, +#endif }; #ifdef CONFIG_ATH9K_CHANNEL_CONTEXT @@ -851,7 +855,9 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT) | +#ifdef CONFIG_WIRELESS_WDS BIT(NL80211_IFTYPE_WDS) | +#endif BIT(NL80211_IFTYPE_OCB); if (ath9k_is_chanctx_enabled()) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 6e5d909..52f3541 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -5591,7 +5591,9 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_WIRELESS_WDS BIT(NL80211_IFTYPE_WDS) | +#endif BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index 83770d2..e97ab2b 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -3838,7 +3838,9 @@ static int b43legacy_wireless_init(struct ssb_device *dev) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_WIRELESS_WDS BIT(NL80211_IFTYPE_WDS) | +#endif BIT(NL80211_IFTYPE_ADHOC); hw->queues = 1; /* FIXME: hardware has more queues */ hw->max_rates = 2; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 4e0c565..236f790 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1362,11 +1362,13 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) if (rt2x00dev->bcn->limit > 0) rt2x00dev->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | #ifdef CONFIG_MAC80211_MESH BIT(NL80211_IFTYPE_MESH_POINT) | #endif - BIT(NL80211_IFTYPE_WDS); +#ifdef CONFIG_WIRELESS_WDS + BIT(NL80211_IFTYPE_WDS) | +#endif + BIT(NL80211_IFTYPE_AP); rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; diff --git a/net/wireless/core.c b/net/wireless/core.c index 8201e6d..f433f15 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -571,6 +571,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) return -EINVAL; } +#ifndef CONFIG_WIRELESS_WDS + if (WARN_ON(all_iftypes & BIT(NL80211_IFTYPE_WDS))) + return -EINVAL; +#endif + /* You can't even choose that many! */ if (WARN_ON(cnt < c->max_interfaces)) return -EINVAL; @@ -609,6 +614,11 @@ int wiphy_register(struct wiphy *wiphy) !rdev->ops->add_nan_func || !rdev->ops->del_nan_func))) return -EINVAL; +#ifndef CONFIG_WIRELESS_WDS + if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))) + return -EINVAL; +#endif + /* * if a wiphy has unsupported modes for regulatory channel enforcement, * opt-out of enforcement checking -- cgit v1.1 From 73c7da3dae1e7cd8febeab13767b2698b84dfa15 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 20 Oct 2016 20:08:22 +0100 Subject: cfg80211: add generic helper to check interface is running Add a helper using wdev to check if interface is running. This deals with both non-netdev and netdev interfaces. In struct wireless_dev replace 'p2p_started' and 'nan_started' by 'is_running' as those are mutually exclusive anyway, and unify all the code to use wdev_running(). Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 13 ++++++++++--- net/wireless/core.c | 8 ++++---- net/wireless/nl80211.c | 53 +++++++++++++++----------------------------------- 3 files changed, 30 insertions(+), 44 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2bbbcc3..ec39f89 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3781,8 +3781,8 @@ struct cfg80211_cached_keys; * @beacon_interval: beacon interval used on this device for transmitting * beacons, 0 when not valid * @address: The address for this device, valid only if @netdev is %NULL - * @p2p_started: true if this is a P2P Device that has been started - * @nan_started: true if this is a NAN interface that has been started + * @is_running: true if this is a non-netdev device that has been started, e.g. + * the P2P Device. * @cac_started: true if DFS channel availability check has been started * @cac_start_time: timestamp (jiffies) when the dfs state was entered. * @cac_time_ms: CAC time in ms @@ -3814,7 +3814,7 @@ struct wireless_dev { struct mutex mtx; - bool use_4addr, p2p_started, nan_started; + bool use_4addr, is_running; u8 address[ETH_ALEN] __aligned(sizeof(u16)); @@ -3871,6 +3871,13 @@ static inline u8 *wdev_address(struct wireless_dev *wdev) return wdev->address; } +static inline bool wdev_running(struct wireless_dev *wdev) +{ + if (wdev->netdev) + return netif_running(wdev->netdev); + return wdev->is_running; +} + /** * wdev_priv - return wiphy priv from wireless_dev * diff --git a/net/wireless/core.c b/net/wireless/core.c index f433f15..725adff 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -210,11 +210,11 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)) return; - if (!wdev->p2p_started) + if (!wdev_running(wdev)) return; rdev_stop_p2p_device(rdev, wdev); - wdev->p2p_started = false; + wdev->is_running = false; rdev->opencount--; @@ -233,11 +233,11 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN)) return; - if (!wdev->nan_started) + if (!wdev_running(wdev)) return; rdev_stop_nan(rdev, wdev); - wdev->nan_started = false; + wdev->is_running = false; rdev->opencount--; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fcf5b4f..46cd489 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10528,7 +10528,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) return -EOPNOTSUPP; - if (wdev->p2p_started) + if (wdev_running(wdev)) return 0; if (rfkill_blocked(rdev->rfkill)) @@ -10538,7 +10538,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) if (err) return err; - wdev->p2p_started = true; + wdev->is_running = true; rdev->opencount++; return 0; @@ -10570,7 +10570,7 @@ static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info) if (wdev->iftype != NL80211_IFTYPE_NAN) return -EOPNOTSUPP; - if (wdev->nan_started) + if (!wdev_running(wdev)) return -EEXIST; if (rfkill_blocked(rdev->rfkill)) @@ -10593,7 +10593,7 @@ static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info) if (err) return err; - wdev->nan_started = true; + wdev->is_running = true; rdev->opencount++; return 0; @@ -10678,7 +10678,7 @@ static int nl80211_nan_add_func(struct sk_buff *skb, if (wdev->iftype != NL80211_IFTYPE_NAN) return -EOPNOTSUPP; - if (!wdev->nan_started) + if (!wdev_running(wdev)) return -ENOTCONN; if (!info->attrs[NL80211_ATTR_NAN_FUNC]) @@ -10915,7 +10915,7 @@ static int nl80211_nan_del_func(struct sk_buff *skb, if (wdev->iftype != NL80211_IFTYPE_NAN) return -EOPNOTSUPP; - if (!wdev->nan_started) + if (!wdev_running(wdev)) return -ENOTCONN; if (!info->attrs[NL80211_ATTR_COOKIE]) @@ -10943,7 +10943,7 @@ static int nl80211_nan_change_config(struct sk_buff *skb, if (wdev->iftype != NL80211_IFTYPE_NAN) return -EOPNOTSUPP; - if (!wdev->nan_started) + if (!wdev_running(wdev)) return -ENOTCONN; if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) { @@ -11255,11 +11255,7 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { - if (wdev->netdev && - !netif_running(wdev->netdev)) - return -ENETDOWN; - if (!wdev->netdev && !wdev->p2p_started && - !wdev->nan_started) + if (!wdev_running(wdev)) return -ENETDOWN; } @@ -11422,10 +11418,7 @@ static int nl80211_vendor_cmd_dump(struct sk_buff *skb, return -EINVAL; if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { - if (wdev->netdev && - !netif_running(wdev->netdev)) - return -ENETDOWN; - if (!wdev->netdev && !wdev->p2p_started) + if (!wdev_running(wdev)) return -ENETDOWN; } } @@ -11796,29 +11789,15 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, info->user_ptr[1] = wdev; } - if (dev) { - if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && - !netif_running(dev)) { - if (rtnl) - rtnl_unlock(); - return -ENETDOWN; - } + if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && + !wdev_running(wdev)) { + if (rtnl) + rtnl_unlock(); + return -ENETDOWN; + } + if (dev) dev_hold(dev); - } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) { - if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE && - !wdev->p2p_started) { - if (rtnl) - rtnl_unlock(); - return -ENETDOWN; - } - if (wdev->iftype == NL80211_IFTYPE_NAN && - !wdev->nan_started) { - if (rtnl) - rtnl_unlock(); - return -ENETDOWN; - } - } info->user_ptr[0] = rdev; } -- cgit v1.1 From 0507a3ac6e98f50583912ec78d07c2e4daaf2b28 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Oct 2016 12:15:00 +0200 Subject: cfg80211: fix beacon interval in interface combination iteration We shouldn't abort the iteration with an error when one of the potential combinations can't accomodate the beacon interval request, we should just skip that particular combination. Fix the code to do so. Signed-off-by: Johannes Berg --- net/wireless/util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index ec17c28..78bf537 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1676,10 +1676,8 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if (params->beacon_int_gcd) { if (c->beacon_int_min_gcd && - params->beacon_int_gcd < c->beacon_int_min_gcd) { - kfree(limits); - return -EINVAL; - } + params->beacon_int_gcd < c->beacon_int_min_gcd) + goto cont; if (!c->beacon_int_min_gcd && params->beacon_int_different) goto cont; -- cgit v1.1 From 275fcf62c289d52f0fc14d774ab176c0d6196171 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Oct 2016 14:25:11 +0200 Subject: cfg80211: mesh: track (and thus validate) beacon interval This is needed for beacon interval validation; if we don't store it, then new interfaces added won't validate that the beacon interval is the same as existing ones. Fix this. Signed-off-by: Johannes Berg --- net/wireless/mesh.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index fa2066b..2d8518a 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -183,6 +183,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; wdev->chandef = setup->chandef; + wdev->beacon_interval = setup->beacon_interval; } return err; @@ -258,6 +259,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, err = rdev_leave_mesh(rdev, dev); if (!err) { wdev->mesh_id_len = 0; + wdev->beacon_interval = 0; memset(&wdev->chandef, 0, sizeof(wdev->chandef)); rdev_set_qos_map(rdev, dev, NULL); } -- cgit v1.1 From 56271da29c52adb67a9e59117cfdc5a2c4902a07 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Oct 2016 14:25:12 +0200 Subject: cfg80211: disallow beacon_int_min_gcd with IBSS This can't really be supported right now, because the IBSS interface may change its beacon interval at any time due to joining another network; thus, there's already "support" for different beacon intervals here, implicitly. Until we figure out how we should handle this case (continue to allow it to arbitrarily join? Join only if compatible?) disallow advertising that different beacon intervals are supported if IBSS is allowed. Signed-off-by: Johannes Berg --- net/wireless/core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/wireless/core.c b/net/wireless/core.c index 725adff..158c59e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -562,6 +562,21 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) c->limits[j].max > 1)) return -EINVAL; + /* + * This isn't well-defined right now. If you have an + * IBSS interface, then its beacon interval may change + * by joining other networks, and nothing prevents it + * from doing that. + * So technically we probably shouldn't even allow AP + * and IBSS in the same interface, but it seems that + * some drivers support that, possibly only with fixed + * beacon intervals for IBSS. + */ + if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) && + c->beacon_int_min_gcd)) { + return -EINVAL; + } + cnt += c->limits[j].max; /* * Don't advertise an unsupported type -- cgit v1.1 From 4c8dea638c16141adb046fd2e0cab51dfe43650c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Oct 2016 14:25:13 +0200 Subject: cfg80211: validate beacon int as part of iface combinations Remove the pointless checking against interface combinations in the initial basic beacon interval validation, that currently isn't taking into account radar detection or channels properly. Instead, just validate the basic range there, and then delay real checking to the interface combination validation that drivers must do. This means that drivers wanting to use the beacon_int_min_gcd will now have to pass the new_beacon_int when validating the AP/mesh start. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 +++----- net/wireless/util.c | 83 +++++++++++++++++++++++++++++++------------------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ec39f89..d1ffbc3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -784,19 +784,15 @@ struct cfg80211_csa_settings { * @iftype_num: array with the number of interfaces of each interface * type. The index is the interface type as specified in &enum * nl80211_iftype. - * @beacon_int_gcd: a value specifying GCD of all beaconing interfaces, - * the GCD of a single value is considered the value itself, so for - * a single interface this should be set to that interface's beacon - * interval - * @beacon_int_different: a flag indicating whether or not all beacon - * intervals (of beaconing interfaces) are different or not. + * @new_beacon_int: set this to the beacon interval of a new interface + * that's not operating yet, if such is to be checked as part of + * the verification */ struct iface_combination_params { int num_different_channels; u8 radar_detect; int iftype_num[NUM_NL80211_IFTYPES]; - u32 beacon_int_gcd; - bool beacon_int_different; + u32 new_beacon_int; }; /** diff --git a/net/wireless/util.c b/net/wireless/util.c index 78bf537..7681b65 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "core.h" #include "rdev-ops.h" @@ -1558,47 +1559,53 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, } EXPORT_SYMBOL(ieee80211_chandef_to_operating_class); -int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - enum nl80211_iftype iftype, u32 beacon_int) +static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int, + u32 *beacon_int_gcd, + bool *beacon_int_different) { struct wireless_dev *wdev; - struct iface_combination_params params = { - .beacon_int_gcd = beacon_int, /* GCD(n) = n */ - }; - if (beacon_int < 10 || beacon_int > 10000) - return -EINVAL; + *beacon_int_gcd = 0; + *beacon_int_different = false; - params.iftype_num[iftype] = 1; - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + list_for_each_entry(wdev, &wiphy->wdev_list, list) { if (!wdev->beacon_interval) continue; - params.iftype_num[wdev->iftype]++; - } - - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - u32 bi_prev = wdev->beacon_interval; - - if (!wdev->beacon_interval) + if (!*beacon_int_gcd) { + *beacon_int_gcd = wdev->beacon_interval; continue; + } - /* slight optimisation - skip identical BIs */ - if (wdev->beacon_interval == beacon_int) + if (wdev->beacon_interval == *beacon_int_gcd) continue; - params.beacon_int_different = true; - - /* Get the GCD */ - while (bi_prev != 0) { - u32 tmp_bi = bi_prev; + *beacon_int_different = true; + *beacon_int_gcd = gcd(*beacon_int_gcd, wdev->beacon_interval); + } - bi_prev = params.beacon_int_gcd % bi_prev; - params.beacon_int_gcd = tmp_bi; - } + if (new_beacon_int && *beacon_int_gcd != new_beacon_int) { + if (*beacon_int_gcd) + *beacon_int_different = true; + *beacon_int_gcd = gcd(*beacon_int_gcd, new_beacon_int); } +} - return cfg80211_check_combinations(&rdev->wiphy, ¶ms); +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype, u32 beacon_int) +{ + /* + * This is just a basic pre-condition check; if interface combinations + * are possible the driver must already be checking those with a call + * to cfg80211_check_combinations(), in which case we'll validate more + * through the cfg80211_calculate_bi_data() call and code in + * cfg80211_iter_combinations(). + */ + + if (beacon_int < 10 || beacon_int > 10000) + return -EINVAL; + + return 0; } int cfg80211_iter_combinations(struct wiphy *wiphy, @@ -1612,6 +1619,21 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, int i, j, iftype; int num_interfaces = 0; u32 used_iftypes = 0; + u32 beacon_int_gcd; + bool beacon_int_different; + + /* + * This is a bit strange, since the iteration used to rely only on + * the data given by the driver, but here it now relies on context, + * in form of the currently operating interfaces. + * This is OK for all current users, and saves us from having to + * push the GCD calculations into all the drivers. + * In the future, this should probably rely more on data that's in + * cfg80211 already - the only thing not would appear to be any new + * interfaces (while being brought up) and channel/radar data. + */ + cfg80211_calculate_bi_data(wiphy, params->new_beacon_int, + &beacon_int_gcd, &beacon_int_different); if (params->radar_detect) { rcu_read_lock(); @@ -1674,12 +1696,11 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if ((all_iftypes & used_iftypes) != used_iftypes) goto cont; - if (params->beacon_int_gcd) { + if (beacon_int_gcd) { if (c->beacon_int_min_gcd && - params->beacon_int_gcd < c->beacon_int_min_gcd) + beacon_int_gcd < c->beacon_int_min_gcd) goto cont; - if (!c->beacon_int_min_gcd && - params->beacon_int_different) + if (!c->beacon_int_min_gcd && beacon_int_different) goto cont; } -- cgit v1.1 From ac668afe414b1d41366f92a33b4d32428335db54 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Oct 2016 14:25:14 +0200 Subject: mac80211: validate new interface's beacon intervals As part of interface combination checking, verify any new interface's beacon intervals. In fact, just always add the beacon interval since that's harmless. With this patch, mac80211 is prepared for drivers that set the min_beacon_int_gcd parameter in interface combinations. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 ++- net/mac80211/util.c | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index eb68add..e91e503 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -864,6 +864,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, } sdata->needed_rx_chains = sdata->local->rx_chains; + sdata->vif.bss_conf.beacon_int = params->beacon_interval; + mutex_lock(&local->mtx); err = ieee80211_vif_use_channel(sdata, ¶ms->chandef, IEEE80211_CHANCTX_SHARED); @@ -894,7 +896,6 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, vlan->vif.type); } - sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->vif.bss_conf.dtim_period = params->dtim_period; sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.allow_p2p_go_ps = sdata->vif.p2p; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7f24bdc..ac59fbd 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3326,6 +3326,16 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) return -EINVAL; + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) { + /* + * always passing this is harmless, since it'll be the + * same value that cfg80211 finds if it finds the same + * interface ... and that's always allowed + */ + params.new_beacon_int = sdata->vif.bss_conf.beacon_int; + } + /* Always allow software iftypes */ if (local->hw.wiphy->software_iftypes & BIT(iftype)) { if (radar_detect) -- cgit v1.1 From 1794899e8b532553f5883187bdd45adeb268201a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Oct 2016 11:42:04 +0200 Subject: nl80211: move unsplit command advertising to a separate function When we split the wiphy dump because it got too large, I added a comment and asked that all new command advertising be done only for userspace clients capable of receiving split data, in order to not break older ones (which can't use the new commands anyway) This mostly worked, and we haven't added many new commands, but I occasionally get patches that modify the wrong place. Make this easier to detect and understand by splitting out the old commands to a separate function that makes it more clear it should never be modified again. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 167 +++++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 75 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 46cd489..1d07a51 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1328,6 +1328,95 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg, return 0; } +#define CMD(op, n) \ + do { \ + if (rdev->ops->op) { \ + i++; \ + if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \ + goto nla_put_failure; \ + } \ + } while (0) + +static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, + struct sk_buff *msg) +{ + int i = 0; + + /* + * do *NOT* add anything into this function, new things need to be + * advertised only to new versions of userspace that can deal with + * the split (and they can't possibly care about new features... + */ + CMD(add_virtual_intf, NEW_INTERFACE); + CMD(change_virtual_intf, SET_INTERFACE); + CMD(add_key, NEW_KEY); + CMD(start_ap, START_AP); + CMD(add_station, NEW_STATION); + CMD(add_mpath, NEW_MPATH); + CMD(update_mesh_config, SET_MESH_CONFIG); + CMD(change_bss, SET_BSS); + CMD(auth, AUTHENTICATE); + CMD(assoc, ASSOCIATE); + CMD(deauth, DEAUTHENTICATE); + CMD(disassoc, DISASSOCIATE); + CMD(join_ibss, JOIN_IBSS); + CMD(join_mesh, JOIN_MESH); + CMD(set_pmksa, SET_PMKSA); + CMD(del_pmksa, DEL_PMKSA); + CMD(flush_pmksa, FLUSH_PMKSA); + if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) + CMD(remain_on_channel, REMAIN_ON_CHANNEL); + CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); + CMD(mgmt_tx, FRAME); + CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); + if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) + goto nla_put_failure; + } + if (rdev->ops->set_monitor_channel || rdev->ops->start_ap || + rdev->ops->join_mesh) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) + goto nla_put_failure; + } + CMD(set_wds_peer, SET_WDS_PEER); + if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { + CMD(tdls_mgmt, TDLS_MGMT); + CMD(tdls_oper, TDLS_OPER); + } + if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + CMD(sched_scan_start, START_SCHED_SCAN); + CMD(probe_client, PROBE_CLIENT); + CMD(set_noack_map, SET_NOACK_MAP); + if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS)) + goto nla_put_failure; + } + CMD(start_p2p_device, START_P2P_DEVICE); + CMD(set_mcast_rate, SET_MCAST_RATE); +#ifdef CONFIG_NL80211_TESTMODE + CMD(testmode_cmd, TESTMODE); +#endif + + if (rdev->ops->connect || rdev->ops->auth) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_CONNECT)) + goto nla_put_failure; + } + + if (rdev->ops->disconnect || rdev->ops->deauth) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT)) + goto nla_put_failure; + } + + return i; + nla_put_failure: + return -ENOBUFS; +} + struct nl80211_dump_wiphy_state { s64 filter_wiphy; long start; @@ -1555,68 +1644,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (!nl_cmds) goto nla_put_failure; - i = 0; -#define CMD(op, n) \ - do { \ - if (rdev->ops->op) { \ - i++; \ - if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \ - goto nla_put_failure; \ - } \ - } while (0) - - CMD(add_virtual_intf, NEW_INTERFACE); - CMD(change_virtual_intf, SET_INTERFACE); - CMD(add_key, NEW_KEY); - CMD(start_ap, START_AP); - CMD(add_station, NEW_STATION); - CMD(add_mpath, NEW_MPATH); - CMD(update_mesh_config, SET_MESH_CONFIG); - CMD(change_bss, SET_BSS); - CMD(auth, AUTHENTICATE); - CMD(assoc, ASSOCIATE); - CMD(deauth, DEAUTHENTICATE); - CMD(disassoc, DISASSOCIATE); - CMD(join_ibss, JOIN_IBSS); - CMD(join_mesh, JOIN_MESH); - CMD(set_pmksa, SET_PMKSA); - CMD(del_pmksa, DEL_PMKSA); - CMD(flush_pmksa, FLUSH_PMKSA); - if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) - CMD(remain_on_channel, REMAIN_ON_CHANNEL); - CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); - CMD(mgmt_tx, FRAME); - CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); - if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) - goto nla_put_failure; - } - if (rdev->ops->set_monitor_channel || rdev->ops->start_ap || - rdev->ops->join_mesh) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) - goto nla_put_failure; - } - CMD(set_wds_peer, SET_WDS_PEER); - if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { - CMD(tdls_mgmt, TDLS_MGMT); - CMD(tdls_oper, TDLS_OPER); - } - if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) - CMD(sched_scan_start, START_SCHED_SCAN); - CMD(probe_client, PROBE_CLIENT); - CMD(set_noack_map, SET_NOACK_MAP); - if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS)) - goto nla_put_failure; - } - CMD(start_p2p_device, START_P2P_DEVICE); - CMD(set_mcast_rate, SET_MCAST_RATE); -#ifdef CONFIG_NL80211_TESTMODE - CMD(testmode_cmd, TESTMODE); -#endif + i = nl80211_add_commands_unsplit(rdev, msg); + if (i < 0) + goto nla_put_failure; if (state->split) { CMD(crit_proto_start, CRIT_PROTOCOL_START); CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); @@ -1627,21 +1657,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) CMD(add_tx_ts, ADD_TX_TS); } - /* add into the if now */ #undef CMD - if (rdev->ops->connect || rdev->ops->auth) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_CONNECT)) - goto nla_put_failure; - } - - if (rdev->ops->disconnect || rdev->ops->deauth) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT)) - goto nla_put_failure; - } - nla_nest_end(msg, nl_cmds); state->split_start++; if (state->split) -- cgit v1.1 From bfe2c7b1cce4a1e13669e74796a172d7dcbf9d27 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Oct 2016 14:42:21 +0200 Subject: nl80211: use nla_parse_nested() instead of nla_parse() It's just an inline doing the same thing, but the code is nicer with it. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 85 ++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1d07a51..7e746c9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2319,10 +2319,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_txq_params, info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], rem_txq_params) { - result = nla_parse(tb, NL80211_TXQ_ATTR_MAX, - nla_data(nl_txq_params), - nla_len(nl_txq_params), - txq_params_policy); + result = nla_parse_nested(tb, NL80211_TXQ_ATTR_MAX, + nl_txq_params, + txq_params_policy); if (result) return result; result = parse_txq_params(tb, &txq_params); @@ -3572,8 +3571,8 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, sband = rdev->wiphy.bands[band]; if (sband == NULL) return -EINVAL; - err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), - nla_len(tx_rates), nl80211_txattr_policy); + err = nla_parse_nested(tb, NL80211_TXRATE_MAX, tx_rates, + nl80211_txattr_policy); if (err) return err; if (tb[NL80211_TXRATE_LEGACY]) { @@ -6329,9 +6328,8 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], rem_reg_rules) { - r = nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, - nla_data(nl_reg_rule), nla_len(nl_reg_rule), - reg_rule_policy); + r = nla_parse_nested(tb, NL80211_REG_RULE_ATTR_MAX, + nl_reg_rule, reg_rule_policy); if (r) goto bad_reg; r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); @@ -6398,8 +6396,8 @@ static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy, if (!nla_ok(nest, nla_len(nest))) return -EINVAL; - err = nla_parse(attr, NL80211_BSS_SELECT_ATTR_MAX, nla_data(nest), - nla_len(nest), nl80211_bss_select_policy); + err = nla_parse_nested(attr, NL80211_BSS_SELECT_ATTR_MAX, nest, + nl80211_bss_select_policy); if (err) return err; @@ -6789,9 +6787,8 @@ nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans, if (WARN_ON(i >= n_plans)) return -EINVAL; - err = nla_parse(plan, NL80211_SCHED_SCAN_PLAN_MAX, - nla_data(attr), nla_len(attr), - nl80211_plan_policy); + err = nla_parse_nested(plan, NL80211_SCHED_SCAN_PLAN_MAX, + attr, nl80211_plan_policy); if (err) return err; @@ -6880,9 +6877,9 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, tmp) { struct nlattr *rssi; - err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, - nla_data(attr), nla_len(attr), - nl80211_match_policy); + err = nla_parse_nested(tb, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX, + attr, nl80211_match_policy); if (err) return ERR_PTR(err); /* add other standalone attributes here */ @@ -7053,9 +7050,9 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, tmp) { struct nlattr *ssid, *rssi; - err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, - nla_data(attr), nla_len(attr), - nl80211_match_policy); + err = nla_parse_nested(tb, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX, + attr, nl80211_match_policy); if (err) goto out_free; ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; @@ -9755,9 +9752,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, if (!rdev->wiphy.wowlan->tcp) return -EINVAL; - err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP, - nla_data(attr), nla_len(attr), - nl80211_wowlan_tcp_policy); + err = nla_parse_nested(tb, MAX_NL80211_WOWLAN_TCP, attr, + nl80211_wowlan_tcp_policy); if (err) return err; @@ -9902,9 +9898,7 @@ static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev, goto out; } - err = nla_parse(tb, NL80211_ATTR_MAX, - nla_data(attr), nla_len(attr), - nl80211_policy); + err = nla_parse_nested(tb, NL80211_ATTR_MAX, attr, nl80211_policy); if (err) goto out; @@ -9938,10 +9932,9 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) goto set_wakeup; } - err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, - nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - nl80211_wowlan_policy); + err = nla_parse_nested(tb, MAX_NL80211_WOWLAN_TRIG, + info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS], + nl80211_wowlan_policy); if (err) return err; @@ -10023,8 +10016,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) rem) { u8 *mask_pat; - nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), - nla_len(pat), NULL); + nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat, + NULL); err = -EINVAL; if (!pat_tb[NL80211_PKTPAT_MASK] || !pat_tb[NL80211_PKTPAT_PATTERN]) @@ -10234,8 +10227,8 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, int rem, pat_len, mask_len, pkt_offset, n_patterns = 0; struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; - err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule), - nla_len(rule), nl80211_coalesce_policy); + err = nla_parse_nested(tb, NL80211_ATTR_COALESCE_RULE_MAX, rule, + nl80211_coalesce_policy); if (err) return err; @@ -10273,8 +10266,7 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, rem) { u8 *mask_pat; - nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), - nla_len(pat), NULL); + nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat, NULL); if (!pat_tb[NL80211_PKTPAT_MASK] || !pat_tb[NL80211_PKTPAT_PATTERN]) return -EINVAL; @@ -10393,10 +10385,9 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_REKEY_DATA]) return -EINVAL; - err = nla_parse(tb, MAX_NL80211_REKEY_DATA, - nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]), - nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]), - nl80211_rekey_policy); + err = nla_parse_nested(tb, MAX_NL80211_REKEY_DATA, + info->attrs[NL80211_ATTR_REKEY_DATA], + nl80211_rekey_policy); if (err) return err; @@ -10705,10 +10696,9 @@ static int nl80211_nan_add_func(struct sk_buff *skb, wdev->owner_nlportid != info->snd_portid) return -ENOTCONN; - err = nla_parse(tb, NL80211_NAN_FUNC_ATTR_MAX, - nla_data(info->attrs[NL80211_ATTR_NAN_FUNC]), - nla_len(info->attrs[NL80211_ATTR_NAN_FUNC]), - nl80211_nan_func_policy); + err = nla_parse_nested(tb, NL80211_NAN_FUNC_ATTR_MAX, + info->attrs[NL80211_ATTR_NAN_FUNC], + nl80211_nan_func_policy); if (err) return err; @@ -10803,10 +10793,9 @@ static int nl80211_nan_add_func(struct sk_buff *skb, if (tb[NL80211_NAN_FUNC_SRF]) { struct nlattr *srf_tb[NUM_NL80211_NAN_SRF_ATTR]; - err = nla_parse(srf_tb, NL80211_NAN_SRF_ATTR_MAX, - nla_data(tb[NL80211_NAN_FUNC_SRF]), - nla_len(tb[NL80211_NAN_FUNC_SRF]), - nl80211_nan_srf_policy); + err = nla_parse_nested(srf_tb, NL80211_NAN_SRF_ATTR_MAX, + tb[NL80211_NAN_FUNC_SRF], + nl80211_nan_srf_policy); if (err) goto out; -- cgit v1.1 From 11b6b5a4ced2f2c76073b97ee08ca0eab8358fde Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:41:58 +0300 Subject: cfg80211: Rename SAE_DATA to more generic AUTH_DATA This adds defines and nl80211 extensions to allow FILS Authentication to be implemented similarly to SAE. FILS does not need the special rules for the Authentication transaction number and Status code fields, but it does need to add non-IE fields. The previously used NL80211_ATTR_SAE_DATA can be reused for this to avoid having to duplicate that implementation. Rename that attribute to more generic NL80211_ATTR_AUTH_DATA (with backwards compatibility define for NL80211_SAE_DATA). Also document the special rules related to the Authentication transaction number and Status code fiels. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 +++++++----- include/uapi/linux/nl80211.h | 15 ++++++++++++--- net/mac80211/mlme.c | 12 ++++++------ net/wireless/core.h | 2 +- net/wireless/mlme.c | 6 +++--- net/wireless/nl80211.c | 18 +++++++++--------- 6 files changed, 38 insertions(+), 27 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d1ffbc3..dffc265 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1785,9 +1785,11 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); * @key_len: length of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication * @key: WEP key for shared key authentication - * @sae_data: Non-IE data to use with SAE or %NULL. This starts with - * Authentication transaction sequence number field. - * @sae_data_len: Length of sae_data buffer in octets + * @auth_data: Fields and elements in Authentication frames. This contains + * the authentication frame body (non-IE and IE data), excluding the + * Authentication algorithm number, i.e., starting at the Authentication + * transaction sequence number field. + * @auth_data_len: Length of auth_data buffer in octets */ struct cfg80211_auth_request { struct cfg80211_bss *bss; @@ -1796,8 +1798,8 @@ struct cfg80211_auth_request { enum nl80211_auth_type auth_type; const u8 *key; u8 key_len, key_idx; - const u8 *sae_data; - size_t sae_data_len; + const u8 *auth_data; + size_t auth_data_len; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1362d24..18bcf44 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1638,8 +1638,16 @@ enum nl80211_commands { * the connection request from a station. nl80211_connect_failed_reason * enum has different reasons of connection failure. * - * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts - * with the Authentication transaction sequence number field. + * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames. + * This contains the authentication frame body (non-IE and IE data), + * excluding the Authentication algorithm number, i.e., starting at the + * Authentication transaction sequence number field. It is used with + * authentication algorithms that need special fields to be added into + * the frames (SAE and FILS). Currently, only the SAE cases use the + * initial two fields (Authentication transaction sequence number and + * Status code). However, those fields are included in the attribute data + * for all authentication algorithms to keep the attribute definition + * consistent. * * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) @@ -2195,7 +2203,7 @@ enum nl80211_attrs { NL80211_ATTR_CONN_FAILED_REASON, - NL80211_ATTR_SAE_DATA, + NL80211_ATTR_AUTH_DATA, NL80211_ATTR_VHT_CAPABILITY, @@ -2347,6 +2355,7 @@ enum nl80211_attrs { #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION #define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG #define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER +#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA /* * Allow user space programs to use #ifdef on new attributes by defining them diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c8d3a9b..32fd295 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4483,20 +4483,20 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, return -EOPNOTSUPP; } - auth_data = kzalloc(sizeof(*auth_data) + req->sae_data_len + + auth_data = kzalloc(sizeof(*auth_data) + req->auth_data_len + req->ie_len, GFP_KERNEL); if (!auth_data) return -ENOMEM; auth_data->bss = req->bss; - if (req->sae_data_len >= 4) { - __le16 *pos = (__le16 *) req->sae_data; + if (req->auth_data_len >= 4) { + __le16 *pos = (__le16 *) req->auth_data; auth_data->sae_trans = le16_to_cpu(pos[0]); auth_data->sae_status = le16_to_cpu(pos[1]); - memcpy(auth_data->data, req->sae_data + 4, - req->sae_data_len - 4); - auth_data->data_len += req->sae_data_len - 4; + memcpy(auth_data->data, req->auth_data + 4, + req->auth_data_len - 4); + auth_data->data_len += req->auth_data_len - 4; } if (req->ie && req->ie_len) { diff --git a/net/wireless/core.h b/net/wireless/core.h index 21e3188..fb2fcd5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -345,7 +345,7 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, const u8 *key, int key_len, int key_idx, - const u8 *sae_data, int sae_data_len); + const u8 *auth_data, int auth_data_len); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index cbb48e2..bd1f7a1 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -204,14 +204,14 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, const u8 *key, int key_len, int key_idx, - const u8 *sae_data, int sae_data_len) + const u8 *auth_data, int auth_data_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req = { .ie = ie, .ie_len = ie_len, - .sae_data = sae_data, - .sae_data_len = sae_data_len, + .auth_data = auth_data, + .auth_data_len = auth_data_len, .auth_type = auth_type, .key = key, .key_len = key_len, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7e746c9..7048511 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -357,7 +357,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, - [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, }, + [NL80211_ATTR_AUTH_DATA] = { .type = NLA_BINARY, }, [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN }, [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, @@ -7729,8 +7729,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct ieee80211_channel *chan; - const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL; - int err, ssid_len, ie_len = 0, sae_data_len = 0; + const u8 *bssid, *ssid, *ie = NULL, *auth_data = NULL; + int err, ssid_len, ie_len = 0, auth_data_len = 0; enum nl80211_auth_type auth_type; struct key_parse key; bool local_state_change; @@ -7811,16 +7811,16 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (auth_type == NL80211_AUTHTYPE_SAE && - !info->attrs[NL80211_ATTR_SAE_DATA]) + !info->attrs[NL80211_ATTR_AUTH_DATA]) return -EINVAL; - if (info->attrs[NL80211_ATTR_SAE_DATA]) { + if (info->attrs[NL80211_ATTR_AUTH_DATA]) { if (auth_type != NL80211_AUTHTYPE_SAE) return -EINVAL; - sae_data = nla_data(info->attrs[NL80211_ATTR_SAE_DATA]); - sae_data_len = nla_len(info->attrs[NL80211_ATTR_SAE_DATA]); + auth_data = nla_data(info->attrs[NL80211_ATTR_AUTH_DATA]); + auth_data_len = nla_len(info->attrs[NL80211_ATTR_AUTH_DATA]); /* need to include at least Auth Transaction and Status Code */ - if (sae_data_len < 4) + if (auth_data_len < 4) return -EINVAL; } @@ -7837,7 +7837,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, key.p.key, key.p.key_len, key.idx, - sae_data, sae_data_len); + auth_data, auth_data_len); wdev_unlock(dev->ieee80211_ptr); return err; } -- cgit v1.1 From 6ec63612c3c8da200d040dd4846d646a747722df Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:41:59 +0300 Subject: mac80211: Allow AUTH_DATA to be used for FILS The special SAE case should be limited only for SAE since the more generic AUTH_DATA can now be used with other authentication algorithms as well. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 32fd295..00db34b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4491,9 +4491,12 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, auth_data->bss = req->bss; if (req->auth_data_len >= 4) { - __le16 *pos = (__le16 *) req->auth_data; - auth_data->sae_trans = le16_to_cpu(pos[0]); - auth_data->sae_status = le16_to_cpu(pos[1]); + if (req->auth_type == NL80211_AUTHTYPE_SAE) { + __le16 *pos = (__le16 *) req->auth_data; + + auth_data->sae_trans = le16_to_cpu(pos[0]); + auth_data->sae_status = le16_to_cpu(pos[1]); + } memcpy(auth_data->data, req->auth_data + 4, req->auth_data_len - 4); auth_data->data_len += req->auth_data_len - 4; -- cgit v1.1 From 60b8084e844814631b57da3d35f272e0ff799ab2 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:42:00 +0300 Subject: cfg80211: Add feature flag for Fast Initial Link Setup (FILS) as STA This defines a feature flag that drivers can use to indicate that they support FILS authentication/association (IEEE 802.11ai) when using user space SME (NL80211_CMD_AUTHENTICATE) in station mode. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 18bcf44..7825fd4 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4647,6 +4647,8 @@ enum nl80211_feature_flags { * configuration (AP/mesh) with HT rates. * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate * configuration (AP/mesh) with VHT rates. + * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup + * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4661,6 +4663,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, NL80211_EXT_FEATURE_BEACON_RATE_HT, NL80211_EXT_FEATURE_BEACON_RATE_VHT, + NL80211_EXT_FEATURE_FILS_STA, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, -- cgit v1.1 From 3f817fe718c6cb3ddcc2ab04ba86faecc20ef8fe Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:42:01 +0300 Subject: cfg80211: Define IEEE P802.11ai (FILS) information elements Define the Element IDs and Element ID Extensions from IEEE P802.11ai/D11.0. In addition, add a new cfg80211_find_ext_ie() wrapper to make it easier to find information elements that used the Element ID Extension field. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 20 ++++++++++++++++++++ include/net/cfg80211.h | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a80516f..d428adf 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1960,6 +1960,26 @@ enum ieee80211_eid { WLAN_EID_VENDOR_SPECIFIC = 221, WLAN_EID_QOS_PARAMETER = 222, + WLAN_EID_CAG_NUMBER = 237, + WLAN_EID_AP_CSN = 239, + WLAN_EID_FILS_INDICATION = 240, + WLAN_EID_DILS = 241, + WLAN_EID_FRAGMENT = 242, + WLAN_EID_EXTENSION = 255 +}; + +/* Element ID Extensions for Element ID 255 */ +enum ieee80211_eid_ext { + WLAN_EID_EXT_ASSOC_DELAY_INFO = 1, + WLAN_EID_EXT_FILS_REQ_PARAMS = 2, + WLAN_EID_EXT_FILS_KEY_CONFIRM = 3, + WLAN_EID_EXT_FILS_SESSION = 4, + WLAN_EID_EXT_FILS_HLP_CONTAINER = 5, + WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN = 6, + WLAN_EID_EXT_KEY_DELIVERY = 7, + WLAN_EID_EXT_FILS_WRAPPED_DATA = 8, + WLAN_EID_EXT_FILS_PUBLIC_KEY = 12, + WLAN_EID_EXT_FILS_NONCE = 13, }; /* Action category code */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index dffc265..8ca2e9f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4181,6 +4181,27 @@ static inline const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) } /** + * cfg80211_find_ext_ie - find information element with EID Extension in data + * + * @ext_eid: element ID Extension + * @ies: data consisting of IEs + * @len: length of data + * + * Return: %NULL if the extended element ID could not be found or if + * the element is invalid (claims to be longer than the given + * data), or a pointer to the first byte of the requested + * element, that is the byte containing the element ID. + * + * Note: There are no checks on the element length other than + * having to fit into the given data. + */ +static inline const u8 *cfg80211_find_ext_ie(u8 ext_eid, const u8 *ies, int len) +{ + return cfg80211_find_ie_match(WLAN_EID_EXTENSION, ies, len, + &ext_eid, 1, 2); +} + +/** * cfg80211_find_vendor_ie - find vendor specific information element in data * * @oui: vendor OUI -- cgit v1.1 From 631810603a20874554b2f17adf42b72d0f15eda5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:42:02 +0300 Subject: cfg80211: Add Fast Initial Link Setup (FILS) auth algs This defines authentication algorithms for FILS (IEEE 802.11ai). Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 3 +++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/nl80211.c | 21 +++++++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d428adf..793a017 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1576,6 +1576,9 @@ struct ieee80211_vht_operation { #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 #define WLAN_AUTH_SAE 3 +#define WLAN_AUTH_FILS_SK 4 +#define WLAN_AUTH_FILS_SK_PFS 5 +#define WLAN_AUTH_FILS_PK 6 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 7825fd4..4dc2126 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3669,6 +3669,9 @@ enum nl80211_bss_status { * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals + * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key + * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS + * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -3681,6 +3684,9 @@ enum nl80211_auth_type { NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, NL80211_AUTHTYPE_SAE, + NL80211_AUTHTYPE_FILS_SK, + NL80211_AUTHTYPE_FILS_SK_PFS, + NL80211_AUTHTYPE_FILS_PK, /* keep last */ __NL80211_AUTHTYPE_NUM, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7048511..ff79862 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3778,12 +3778,23 @@ static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && auth_type == NL80211_AUTHTYPE_SAE) return false; + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_FILS_STA) && + (auth_type == NL80211_AUTHTYPE_FILS_SK || + auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || + auth_type == NL80211_AUTHTYPE_FILS_PK)) + return false; return true; case NL80211_CMD_CONNECT: case NL80211_CMD_START_AP: /* SAE not supported yet */ if (auth_type == NL80211_AUTHTYPE_SAE) return false; + /* FILS not supported yet */ + if (auth_type == NL80211_AUTHTYPE_FILS_SK || + auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || + auth_type == NL80211_AUTHTYPE_FILS_PK) + return false; return true; default: return false; @@ -7810,12 +7821,18 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE)) return -EINVAL; - if (auth_type == NL80211_AUTHTYPE_SAE && + if ((auth_type == NL80211_AUTHTYPE_SAE || + auth_type == NL80211_AUTHTYPE_FILS_SK || + auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || + auth_type == NL80211_AUTHTYPE_FILS_PK) && !info->attrs[NL80211_ATTR_AUTH_DATA]) return -EINVAL; if (info->attrs[NL80211_ATTR_AUTH_DATA]) { - if (auth_type != NL80211_AUTHTYPE_SAE) + if (auth_type != NL80211_AUTHTYPE_SAE && + auth_type != NL80211_AUTHTYPE_FILS_SK && + auth_type != NL80211_AUTHTYPE_FILS_SK_PFS && + auth_type != NL80211_AUTHTYPE_FILS_PK) return -EINVAL; auth_data = nla_data(info->attrs[NL80211_ATTR_AUTH_DATA]); auth_data_len = nla_len(info->attrs[NL80211_ATTR_AUTH_DATA]); -- cgit v1.1 From 348bd456699801920a309c66e382380809fbdf41 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:42:03 +0300 Subject: cfg80211: Add KEK/nonces for FILS association frames The new nl80211 attributes can be used to provide KEK and nonces to allow the driver to encrypt and decrypt FILS (Re)Association Request/Response frames in station mode. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 3 +++ include/net/cfg80211.h | 9 +++++++++ include/uapi/linux/nl80211.h | 8 ++++++++ net/wireless/nl80211.c | 12 ++++++++++++ 4 files changed, 32 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 793a017..fe84932 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2096,6 +2096,9 @@ enum ieee80211_key_len { #define IEEE80211_GCMP_MIC_LEN 16 #define IEEE80211_GCMP_PN_LEN 6 +#define FILS_NONCE_LEN 16 +#define FILS_MAX_KEK_LEN 64 + /* Public action codes */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8ca2e9f..738b4d8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1840,6 +1840,12 @@ enum cfg80211_assoc_req_flags { * @ht_capa_mask: The bits of ht_capa which are to be used. * @vht_capa: VHT capability override * @vht_capa_mask: VHT capability mask indicating which fields to use + * @fils_kek: FILS KEK for protecting (Re)Association Request/Response frame or + * %NULL if FILS is not used. + * @fils_kek_len: Length of fils_kek in octets + * @fils_nonces: FILS nonces (part of AAD) for protecting (Re)Association + * Request/Response frame or %NULL if FILS is not used. This field starts + * with 16 octets of STA Nonce followed by 16 octets of AP Nonce. */ struct cfg80211_assoc_request { struct cfg80211_bss *bss; @@ -1851,6 +1857,9 @@ struct cfg80211_assoc_request { struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; struct ieee80211_vht_cap vht_capa, vht_capa_mask; + const u8 *fils_kek; + size_t fils_kek_len; + const u8 *fils_nonces; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 4dc2126..a268a00 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1944,6 +1944,11 @@ enum nl80211_commands { * attribute. * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute. * See &enum nl80211_nan_match_attributes. + * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame + * protection. + * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association + * Request/Response frame protection. This attribute contains the 16 octet + * STA Nonce followed by 16 octets of AP Nonce. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -2344,6 +2349,9 @@ enum nl80211_attrs { NL80211_ATTR_NAN_FUNC, NL80211_ATTR_NAN_MATCH, + NL80211_ATTR_FILS_KEK, + NL80211_ATTR_FILS_NONCES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ff79862..667d5f7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -414,6 +414,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NAN_MASTER_PREF] = { .type = NLA_U8 }, [NL80211_ATTR_NAN_DUAL] = { .type = NLA_U8 }, [NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED }, + [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY, + .len = FILS_MAX_KEK_LEN }, + [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN }, }; /* policy for the key attributes */ @@ -8033,6 +8036,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) req.flags |= ASSOC_REQ_USE_RRM; } + if (info->attrs[NL80211_ATTR_FILS_KEK]) { + req.fils_kek = nla_data(info->attrs[NL80211_ATTR_FILS_KEK]); + req.fils_kek_len = nla_len(info->attrs[NL80211_ATTR_FILS_KEK]); + if (!info->attrs[NL80211_ATTR_FILS_NONCES]) + return -EINVAL; + req.fils_nonces = + nla_data(info->attrs[NL80211_ATTR_FILS_NONCES]); + } + err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); if (!err) { wdev_lock(dev->ieee80211_ptr); -- cgit v1.1 From dbc0c2cb2f0a02fcb034732cb3d55ea6359b9c8b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:42:04 +0300 Subject: mac80211: Add FILS auth alg mapping Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 00db34b..bf55379 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2618,6 +2618,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, case WLAN_AUTH_LEAP: case WLAN_AUTH_FT: case WLAN_AUTH_SAE: + case WLAN_AUTH_FILS_SK: + case WLAN_AUTH_FILS_SK_PFS: + case WLAN_AUTH_FILS_PK: break; case WLAN_AUTH_SHARED_KEY: if (ifmgd->auth_data->expected_transaction != 4) { @@ -4479,6 +4482,15 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, case NL80211_AUTHTYPE_SAE: auth_alg = WLAN_AUTH_SAE; break; + case NL80211_AUTHTYPE_FILS_SK: + auth_alg = WLAN_AUTH_FILS_SK; + break; + case NL80211_AUTHTYPE_FILS_SK_PFS: + auth_alg = WLAN_AUTH_FILS_SK_PFS; + break; + case NL80211_AUTHTYPE_FILS_PK: + auth_alg = WLAN_AUTH_FILS_PK; + break; default: return -EOPNOTSUPP; } -- cgit v1.1 From 39404feee691a2dc524a629ed6eb5cfe5d8898d1 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:42:05 +0300 Subject: mac80211: FILS AEAD protection for station mode association frames This adds support for encrypting (Re)Association Request frame and decryption (Re)Association Response frame when using FILS in station mode. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/Makefile | 1 + net/mac80211/aes_cmac.c | 8 +- net/mac80211/aes_cmac.h | 4 + net/mac80211/fils_aead.c | 344 +++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/fils_aead.h | 19 +++ net/mac80211/ieee80211_i.h | 4 + net/mac80211/mlme.c | 27 ++++ 7 files changed, 403 insertions(+), 4 deletions(-) create mode 100644 net/mac80211/fils_aead.c create mode 100644 net/mac80211/fils_aead.h diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index f9137a8..0b202b3 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -19,6 +19,7 @@ mac80211-y := \ aes_gcm.o \ aes_cmac.o \ aes_gmac.o \ + fils_aead.o \ cfg.o \ ethtool.o \ rx.o \ diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index bdf0790..d0bd5ff 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c @@ -23,7 +23,7 @@ #define AAD_LEN 20 -static void gf_mulx(u8 *pad) +void gf_mulx(u8 *pad) { int i, carry; @@ -35,9 +35,9 @@ static void gf_mulx(u8 *pad) pad[AES_BLOCK_SIZE - 1] ^= 0x87; } -static void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac, - size_t mac_len) +void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac, + size_t mac_len) { u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; const u8 *pos, *end; diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h index 3702041..c827e1d 100644 --- a/net/mac80211/aes_cmac.h +++ b/net/mac80211/aes_cmac.h @@ -11,6 +11,10 @@ #include +void gf_mulx(u8 *pad); +void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac, + size_t mac_len); struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[], size_t key_len); void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, diff --git a/net/mac80211/fils_aead.c b/net/mac80211/fils_aead.c new file mode 100644 index 0000000..b81b4f24 --- /dev/null +++ b/net/mac80211/fils_aead.c @@ -0,0 +1,344 @@ +/* + * FILS AEAD for (Re)Association Request/Response frames + * Copyright 2016, Qualcomm Atheros, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "ieee80211_i.h" +#include "aes_cmac.h" +#include "fils_aead.h" + +static int aes_s2v(struct crypto_cipher *tfm, + size_t num_elem, const u8 *addr[], size_t len[], u8 *v) +{ + u8 d[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + size_t i; + const u8 *data[2]; + size_t data_len[2], data_elems; + + /* D = AES-CMAC(K, ) */ + memset(tmp, 0, AES_BLOCK_SIZE); + data[0] = tmp; + data_len[0] = AES_BLOCK_SIZE; + aes_cmac_vector(tfm, 1, data, data_len, d, AES_BLOCK_SIZE); + + for (i = 0; i < num_elem - 1; i++) { + /* D = dbl(D) xor AES_CMAC(K, Si) */ + gf_mulx(d); /* dbl */ + aes_cmac_vector(tfm, 1, &addr[i], &len[i], tmp, + AES_BLOCK_SIZE); + crypto_xor(d, tmp, AES_BLOCK_SIZE); + } + + if (len[i] >= AES_BLOCK_SIZE) { + /* len(Sn) >= 128 */ + size_t j; + const u8 *pos; + + /* T = Sn xorend D */ + + /* Use a temporary buffer to perform xorend on Sn (addr[i]) to + * avoid modifying the const input argument. + */ + data[0] = addr[i]; + data_len[0] = len[i] - AES_BLOCK_SIZE; + pos = addr[i] + data_len[0]; + for (j = 0; j < AES_BLOCK_SIZE; j++) + tmp[j] = pos[j] ^ d[j]; + data[1] = tmp; + data_len[1] = AES_BLOCK_SIZE; + data_elems = 2; + } else { + /* len(Sn) < 128 */ + /* T = dbl(D) xor pad(Sn) */ + gf_mulx(d); /* dbl */ + memset(tmp, 0, AES_BLOCK_SIZE); + memcpy(tmp, addr[i], len[i]); + tmp[len[i]] = 0x80; + crypto_xor(d, tmp, AES_BLOCK_SIZE); + data[0] = d; + data_len[0] = sizeof(d); + data_elems = 1; + } + /* V = AES-CMAC(K, T) */ + aes_cmac_vector(tfm, data_elems, data, data_len, v, AES_BLOCK_SIZE); + + return 0; +} + +/* Note: addr[] and len[] needs to have one extra slot at the end. */ +static int aes_siv_encrypt(const u8 *key, size_t key_len, + const u8 *plain, size_t plain_len, + size_t num_elem, const u8 *addr[], + size_t len[], u8 *out) +{ + u8 v[AES_BLOCK_SIZE]; + struct crypto_cipher *tfm; + struct crypto_skcipher *tfm2; + struct skcipher_request *req; + int res; + struct scatterlist src[1], dst[1]; + u8 *tmp; + + key_len /= 2; /* S2V key || CTR key */ + + addr[num_elem] = plain; + len[num_elem] = plain_len; + num_elem++; + + /* S2V */ + + tfm = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + /* K1 for S2V */ + res = crypto_cipher_setkey(tfm, key, key_len); + if (!res) + res = aes_s2v(tfm, num_elem, addr, len, v); + crypto_free_cipher(tfm); + if (res) + return res; + + /* Use a temporary buffer of the plaintext to handle need for + * overwriting this during AES-CTR. + */ + tmp = kmemdup(plain, plain_len, GFP_KERNEL); + if (!tmp) { + res = -ENOMEM; + goto fail; + } + + /* IV for CTR before encrypted data */ + memcpy(out, v, AES_BLOCK_SIZE); + + /* Synthetic IV to be used as the initial counter in CTR: + * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) + */ + v[8] &= 0x7f; + v[12] &= 0x7f; + + /* CTR */ + + tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, 0); + if (IS_ERR(tfm2)) { + kfree(tmp); + return PTR_ERR(tfm2); + } + /* K2 for CTR */ + res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); + if (res) + goto fail; + + req = skcipher_request_alloc(tfm2, GFP_KERNEL); + if (!req) { + res = -ENOMEM; + goto fail; + } + + sg_init_one(src, tmp, plain_len); + sg_init_one(dst, out + AES_BLOCK_SIZE, plain_len); + skcipher_request_set_crypt(req, src, dst, plain_len, v); + res = crypto_skcipher_encrypt(req); + skcipher_request_free(req); +fail: + kfree(tmp); + crypto_free_skcipher(tfm2); + return res; +} + +/* Note: addr[] and len[] needs to have one extra slot at the end. */ +static int aes_siv_decrypt(const u8 *key, size_t key_len, + const u8 *iv_crypt, size_t iv_c_len, + size_t num_elem, const u8 *addr[], size_t len[], + u8 *out) +{ + struct crypto_cipher *tfm; + struct crypto_skcipher *tfm2; + struct skcipher_request *req; + struct scatterlist src[1], dst[1]; + size_t crypt_len; + int res; + u8 frame_iv[AES_BLOCK_SIZE], iv[AES_BLOCK_SIZE]; + u8 check[AES_BLOCK_SIZE]; + + crypt_len = iv_c_len - AES_BLOCK_SIZE; + key_len /= 2; /* S2V key || CTR key */ + addr[num_elem] = out; + len[num_elem] = crypt_len; + num_elem++; + + memcpy(iv, iv_crypt, AES_BLOCK_SIZE); + memcpy(frame_iv, iv_crypt, AES_BLOCK_SIZE); + + /* Synthetic IV to be used as the initial counter in CTR: + * Q = V bitand (1^64 || 0^1 || 1^31 || 0^1 || 1^31) + */ + iv[8] &= 0x7f; + iv[12] &= 0x7f; + + /* CTR */ + + tfm2 = crypto_alloc_skcipher("ctr(aes)", 0, 0); + if (IS_ERR(tfm2)) + return PTR_ERR(tfm2); + /* K2 for CTR */ + res = crypto_skcipher_setkey(tfm2, key + key_len, key_len); + if (res) { + crypto_free_skcipher(tfm2); + return res; + } + + req = skcipher_request_alloc(tfm2, GFP_KERNEL); + if (!req) { + crypto_free_skcipher(tfm2); + return -ENOMEM; + } + + sg_init_one(src, iv_crypt + AES_BLOCK_SIZE, crypt_len); + sg_init_one(dst, out, crypt_len); + skcipher_request_set_crypt(req, src, dst, crypt_len, iv); + res = crypto_skcipher_decrypt(req); + skcipher_request_free(req); + crypto_free_skcipher(tfm2); + if (res) + return res; + + /* S2V */ + + tfm = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + /* K1 for S2V */ + res = crypto_cipher_setkey(tfm, key, key_len); + if (!res) + res = aes_s2v(tfm, num_elem, addr, len, check); + crypto_free_cipher(tfm); + if (res) + return res; + if (memcmp(check, frame_iv, AES_BLOCK_SIZE) != 0) + return -EINVAL; + return 0; +} + +int fils_encrypt_assoc_req(struct sk_buff *skb, + struct ieee80211_mgd_assoc_data *assoc_data) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + u8 *capab, *ies, *encr; + const u8 *addr[5 + 1], *session; + size_t len[5 + 1]; + size_t crypt_len; + + if (ieee80211_is_reassoc_req(mgmt->frame_control)) { + capab = (u8 *)&mgmt->u.reassoc_req.capab_info; + ies = mgmt->u.reassoc_req.variable; + } else { + capab = (u8 *)&mgmt->u.assoc_req.capab_info; + ies = mgmt->u.assoc_req.variable; + } + + session = cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION, + ies, skb->data + skb->len - ies); + if (!session || session[1] != 1 + 8) + return -EINVAL; + /* encrypt after FILS Session element */ + encr = (u8 *)session + 2 + 1 + 8; + + /* AES-SIV AAD vectors */ + + /* The STA's MAC address */ + addr[0] = mgmt->sa; + len[0] = ETH_ALEN; + /* The AP's BSSID */ + addr[1] = mgmt->da; + len[1] = ETH_ALEN; + /* The STA's nonce */ + addr[2] = assoc_data->fils_nonces; + len[2] = FILS_NONCE_LEN; + /* The AP's nonce */ + addr[3] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; + len[3] = FILS_NONCE_LEN; + /* The (Re)Association Request frame from the Capability Information + * field to the FILS Session element (both inclusive). + */ + addr[4] = capab; + len[4] = encr - capab; + + crypt_len = skb->data + skb->len - encr; + skb_put(skb, AES_BLOCK_SIZE); + return aes_siv_encrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, + encr, crypt_len, 1, addr, len, encr); +} + +int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, + u8 *frame, size_t *frame_len, + struct ieee80211_mgd_assoc_data *assoc_data) +{ + struct ieee80211_mgmt *mgmt = (void *)frame; + u8 *capab, *ies, *encr; + const u8 *addr[5 + 1], *session; + size_t len[5 + 1]; + int res; + size_t crypt_len; + + if (*frame_len < 24 + 6) + return -EINVAL; + + capab = (u8 *)&mgmt->u.assoc_resp.capab_info; + ies = mgmt->u.assoc_resp.variable; + session = cfg80211_find_ext_ie(WLAN_EID_EXT_FILS_SESSION, + ies, frame + *frame_len - ies); + if (!session || session[1] != 1 + 8) { + mlme_dbg(sdata, + "No (valid) FILS Session element in (Re)Association Response frame from %pM", + mgmt->sa); + return -EINVAL; + } + /* decrypt after FILS Session element */ + encr = (u8 *)session + 2 + 1 + 8; + + /* AES-SIV AAD vectors */ + + /* The AP's BSSID */ + addr[0] = mgmt->sa; + len[0] = ETH_ALEN; + /* The STA's MAC address */ + addr[1] = mgmt->da; + len[1] = ETH_ALEN; + /* The AP's nonce */ + addr[2] = &assoc_data->fils_nonces[FILS_NONCE_LEN]; + len[2] = FILS_NONCE_LEN; + /* The STA's nonce */ + addr[3] = assoc_data->fils_nonces; + len[3] = FILS_NONCE_LEN; + /* The (Re)Association Response frame from the Capability Information + * field to the FILS Session element (both inclusive). + */ + addr[4] = capab; + len[4] = encr - capab; + + crypt_len = frame + *frame_len - encr; + if (crypt_len < AES_BLOCK_SIZE) { + mlme_dbg(sdata, + "Not enough room for AES-SIV data after FILS Session element in (Re)Association Response frame from %pM", + mgmt->sa); + return -EINVAL; + } + res = aes_siv_decrypt(assoc_data->fils_kek, assoc_data->fils_kek_len, + encr, crypt_len, 5, addr, len, encr); + if (res != 0) { + mlme_dbg(sdata, + "AES-SIV decryption of (Re)Association Response frame from %pM failed", + mgmt->sa); + return res; + } + *frame_len -= AES_BLOCK_SIZE; + return 0; +} diff --git a/net/mac80211/fils_aead.h b/net/mac80211/fils_aead.h new file mode 100644 index 0000000..fbc6523 --- /dev/null +++ b/net/mac80211/fils_aead.h @@ -0,0 +1,19 @@ +/* + * FILS AEAD for (Re)Association Request/Response frames + * Copyright 2016, Qualcomm Atheros, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FILS_AEAD_H +#define FILS_AEAD_H + +int fils_encrypt_assoc_req(struct sk_buff *skb, + struct ieee80211_mgd_assoc_data *assoc_data); +int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata, + u8 *frame, size_t *frame_len, + struct ieee80211_mgd_assoc_data *assoc_data); + +#endif /* FILS_AEAD_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b4e2b6c..d37a577 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -401,6 +401,10 @@ struct ieee80211_mgd_assoc_data { struct ieee80211_vht_cap ap_vht_cap; + u8 fils_nonces[2 * FILS_NONCE_LEN]; + u8 fils_kek[FILS_MAX_KEK_LEN]; + size_t fils_kek_len; + size_t ie_len; u8 ie[]; }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bf55379..d157b25 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -30,6 +30,7 @@ #include "driver-ops.h" #include "rate.h" #include "led.h" +#include "fils_aead.h" #define IEEE80211_AUTH_TIMEOUT (HZ / 5) #define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) @@ -652,6 +653,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ assoc_data->ie_len + /* extra IEs */ + (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) + 9, /* WMM */ GFP_KERNEL); if (!skb) @@ -875,6 +877,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) memcpy(pos, assoc_data->ie + offset, noffset - offset); } + if (assoc_data->fils_kek_len && + fils_encrypt_assoc_req(skb, assoc_data) < 0) { + dev_kfree_skb(skb); + return; + } + drv_mgd_prepare_tx(local, sdata); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; @@ -3146,6 +3154,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, reassoc ? "Rea" : "A", mgmt->sa, capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); + if (assoc_data->fils_kek_len && + fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0) + return; + pos = mgmt->u.assoc_resp.variable; ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); @@ -4707,6 +4719,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->ie_len = req->ie_len; } + if (req->fils_kek) { + /* should already be checked in cfg80211 - so warn */ + if (WARN_ON(req->fils_kek_len > FILS_MAX_KEK_LEN)) { + err = -EINVAL; + goto err_free; + } + memcpy(assoc_data->fils_kek, req->fils_kek, + req->fils_kek_len); + assoc_data->fils_kek_len = req->fils_kek_len; + } + + if (req->fils_nonces) + memcpy(assoc_data->fils_nonces, req->fils_nonces, + 2 * FILS_NONCE_LEN); + assoc_data->bss = req->bss; if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { -- cgit v1.1 From f3ca52aa52eebdcad0e6fd99586e0068e899caa7 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 27 Oct 2016 00:42:06 +0300 Subject: mac80211: Claim Fast Initial Link Setup (FILS) STA support With the previous commits, initial FILS authentication/association support is now functional in mac80211-based drivers for station role (and FILS AP case is covered by user space in hostapd withotu requiring mac80211 changes). Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0d9163c..1822c77 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -549,6 +549,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, NL80211_FEATURE_MAC_ON_CREATE | NL80211_FEATURE_USERSPACE_MPM | NL80211_FEATURE_FULL_AP_CLIENT_STATE; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_STA); if (!ops->hw_scan) wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | -- cgit v1.1 From ce0ce13a1c89ff8b94b7f8fb32eb4c43e111c82e Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Mon, 10 Oct 2016 19:12:22 +0200 Subject: cfg80211: configure multicast to unicast for AP interfaces Add the ability to configure if an AP (and associated VLANs) will do multicast-to-unicast conversion for ARP, IPv4 and IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent to each station separately, with the DA replaced by their own MAC address rather than the group address. Note that this may break certain expectations of the receiver, such as the ability to drop unicast IP packets received within multicast L2 frames, or the ability to not send ICMP destination unreachable messages for packets received in L2 multicast (which is required, but the receiver can't tell the difference if this new option is enabled.) This also doesn't implement the 802.11 DMS (directed multicast service). Signed-off-by: Michael Braun [fix disabling, add better documentation & commit message] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ++++++ include/uapi/linux/nl80211.h | 21 +++++++++++++++++++++ net/wireless/nl80211.c | 35 +++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 12 ++++++++++++ net/wireless/trace.h | 19 +++++++++++++++++++ 5 files changed, 93 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 738b4d8..41ae3f5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2742,6 +2742,8 @@ struct cfg80211_nan_func { * @nan_change_conf: changes NAN configuration. The changed parameters must * be specified in @changes (using &enum cfg80211_nan_conf_changes); * All other parameters must be ignored. + * + * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -3018,6 +3020,10 @@ struct cfg80211_ops { struct wireless_dev *wdev, struct cfg80211_nan_conf *conf, u32 changes); + + int (*set_multicast_to_unicast)(struct wiphy *wiphy, + struct net_device *dev, + const bool enabled); }; /* diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index a268a00..e21d23d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -600,6 +600,20 @@ * * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. * + * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform + * multicast to unicast conversion. When enabled, all multicast packets + * with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header) + * will be sent out to each station once with the destination (multicast) + * MAC address replaced by the station's MAC address. Note that this may + * break certain expectations of the receiver, e.g. the ability to drop + * unicast IP packets encapsulated in multicast L2 frames, or the ability + * to not send destination unreachable messages in such cases. + * This can only be toggled per BSS. Configure this on an interface of + * type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces + * (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode. + * If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this + * command, the feature is disabled. + * * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial * mesh config parameters may be given. * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the @@ -1069,6 +1083,8 @@ enum nl80211_commands { NL80211_CMD_CHANGE_NAN_CONFIG, NL80211_CMD_NAN_MATCH, + NL80211_CMD_SET_MULTICAST_TO_UNICAST, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1950,6 +1966,9 @@ enum nl80211_commands { * Request/Response frame protection. This attribute contains the 16 octet * STA Nonce followed by 16 octets of AP Nonce. * + * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast + * packets should be send out as unicast to all stations (flag attribute). + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2352,6 +2371,8 @@ enum nl80211_attrs { NL80211_ATTR_FILS_KEK, NL80211_ATTR_FILS_NONCES, + NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 667d5f7..0c9d00c9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -417,6 +417,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY, .len = FILS_MAX_KEK_LEN }, [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN }, + [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, }, }; /* policy for the key attributes */ @@ -1659,6 +1660,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) CMD(add_tx_ts, ADD_TX_TS); + CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST); } #undef CMD @@ -11766,6 +11768,31 @@ static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, return 0; } +static int nl80211_set_multicast_to_unicast(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + const struct nlattr *nla; + bool enabled; + + if (netif_running(dev)) + return -EBUSY; + + if (!rdev->ops->set_multicast_to_unicast) + return -EOPNOTSUPP; + + if (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + nla = info->attrs[NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED]; + enabled = nla_get_flag(nla); + + return rdev_set_multicast_to_unicast(rdev, dev, enabled); +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -12625,6 +12652,14 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST, + .doit = nl80211_set_multicast_to_unicast, + .policy = nl80211_policy, + .flags = GENL_UNS_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, }; /* notification functions */ diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 11cf83c..e9ecf23 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -562,6 +562,18 @@ static inline int rdev_set_wds_peer(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_set_multicast_to_unicast(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const bool enabled) +{ + int ret; + trace_rdev_set_multicast_to_unicast(&rdev->wiphy, dev, enabled); + ret = rdev->ops->set_multicast_to_unicast(&rdev->wiphy, dev, enabled); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) { trace_rdev_rfkill_poll(&rdev->wiphy); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index a3d0a91b..25f8318 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3030,6 +3030,25 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_abort_scan, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), TP_ARGS(wiphy, wdev) ); + +TRACE_EVENT(rdev_set_multicast_to_unicast, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const bool enabled), + TP_ARGS(wiphy, netdev, enabled), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(bool, enabled) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->enabled = enabled; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", unicast: %s", + WIPHY_PR_ARG, NETDEV_PR_ARG, + BOOL_TO_STR(__entry->enabled)) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.1 From 8ac63448651f579494894f9687a759b9112967df Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 4 Sep 2015 20:03:23 +0200 Subject: cfg80211: handle fragmented IEs in splitting The IEs "output" can sometimes combine IEs coming from userspace with IEs generated in the kernel - in particular mac80211 does this for association frames. Add support in this code for the 802.11 IE fragmentation. Signed-off-by: Johannes Berg --- net/wireless/util.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 7681b65..32060f8 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1382,6 +1382,25 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) return false; } +static size_t skip_ie(const u8 *ies, size_t ielen, size_t pos) +{ + /* we assume a validly formed IEs buffer */ + u8 len = ies[pos + 1]; + + pos += 2 + len; + + /* the IE itself must have 255 bytes for fragments to follow */ + if (len < 255) + return pos; + + while (pos < ielen && ies[pos] == WLAN_EID_FRAGMENT) { + len = ies[pos + 1]; + pos += 2 + len; + } + + return pos; +} + size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, const u8 *after_ric, int n_after_ric, @@ -1391,14 +1410,14 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { - pos += 2 + ies[pos + 1]; + pos = skip_ie(ies, ielen, pos); while (pos < ielen && !ieee80211_id_in_list(after_ric, n_after_ric, ies[pos])) - pos += 2 + ies[pos + 1]; + pos = skip_ie(ies, ielen, pos); } else { - pos += 2 + ies[pos + 1]; + pos = skip_ie(ies, ielen, pos); } } -- cgit v1.1 From 088e8df82f91a24728d49d9532cab7ebdee5117f Mon Sep 17 00:00:00 2001 From: vamsi krishna Date: Thu, 27 Oct 2016 16:51:11 +0300 Subject: cfg80211: Add support to update connection parameters Add functionality to update the connection parameters when in connected state, so that driver/firmware uses the updated parameters for subsequent roaming. This is for drivers that support internal BSS selection and roaming. The new command does not change the current association state, i.e., it can be used to update IE contents for future (re)associations without causing an immediate disassociation or reassociation with the current BSS. This commit implements the required functionality for updating IEs for (Re)Association Request frame only. Other parameters can be added in future when required. Signed-off-by: vamsi krishna Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 24 ++++++++++++++++++++++++ include/uapi/linux/nl80211.h | 8 ++++++++ net/wireless/nl80211.c | 40 ++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 12 ++++++++++++ net/wireless/trace.h | 18 ++++++++++++++++++ 5 files changed, 102 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 41ae3f5..c575583 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2051,6 +2051,18 @@ struct cfg80211_connect_params { }; /** + * enum cfg80211_connect_params_changed - Connection parameters being updated + * + * This enum provides information of all connect parameters that + * have to be updated as part of update_connect_params() call. + * + * @UPDATE_ASSOC_IES: Indicates whether association request IEs are updated + */ +enum cfg80211_connect_params_changed { + UPDATE_ASSOC_IES = BIT(0), +}; + +/** * enum wiphy_params_flags - set_wiphy_params bitfield values * @WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed * @WIPHY_PARAM_RETRY_LONG: wiphy->retry_long has changed @@ -2571,6 +2583,14 @@ struct cfg80211_nan_func { * cases, the result of roaming is indicated with a call to * cfg80211_roamed() or cfg80211_roamed_bss(). * (invoked with the wireless_dev mutex held) + * @update_connect_params: Update the connect parameters while connected to a + * BSS. The updated parameters can be used by driver/firmware for + * subsequent BSS selection (roaming) decisions and to form the + * Authentication/(Re)Association Request frames. This call does not + * request an immediate disassociation or reassociation with the current + * BSS, i.e., this impacts only subsequent (re)associations. The bits in + * changed are defined in &enum cfg80211_connect_params_changed. + * (invoked with the wireless_dev mutex held) * @disconnect: Disconnect from the BSS/ESS or stop connection attempts if * connection is in progress. Once done, call cfg80211_disconnected() in * case connection was already established (invoked with the @@ -2858,6 +2878,10 @@ struct cfg80211_ops { int (*connect)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); + int (*update_connect_params)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_connect_params *sme, + u32 changed); int (*disconnect)(struct wiphy *wiphy, struct net_device *dev, u16 reason_code); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e21d23d..259c9c7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -888,6 +888,12 @@ * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and * %NL80211_ATTR_COOKIE. * + * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters + * for subsequent roaming cases if the driver or firmware uses internal + * BSS selection. This command can be issued only while connected and it + * does not result in a change for the current association. Currently, + * only the %NL80211_ATTR_IE data is used and updated with this command. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1085,6 +1091,8 @@ enum nl80211_commands { NL80211_CMD_SET_MULTICAST_TO_UNICAST, + NL80211_CMD_UPDATE_CONNECT_PARAMS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0c9d00c9..bb30fa1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1661,6 +1661,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) CMD(add_tx_ts, ADD_TX_TS); CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST); + CMD(update_connect_params, UPDATE_CONNECT_PARAMS); } #undef CMD @@ -8779,6 +8780,37 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_update_connect_params(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_connect_params connect = {}; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + u32 changed = 0; + int ret; + + if (!rdev->ops->update_connect_params) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_IE]) { + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + changed |= UPDATE_ASSOC_IES; + } + + wdev_lock(dev->ieee80211_ptr); + if (!wdev->current_bss) + ret = -ENOLINK; + else + ret = rdev_update_connect_params(rdev, dev, &connect, changed); + wdev_unlock(dev->ieee80211_ptr); + + return ret; +} + static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -12232,6 +12264,14 @@ static const struct genl_ops nl80211_ops[] = { NL80211_FLAG_NEED_RTNL, }, { + .cmd = NL80211_CMD_UPDATE_CONNECT_PARAMS, + .doit = nl80211_update_connect_params, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { .cmd = NL80211_CMD_DISCONNECT, .doit = nl80211_disconnect, .policy = nl80211_policy, diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index e9ecf23..2f42507 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -490,6 +490,18 @@ static inline int rdev_connect(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_update_connect_params(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *sme, u32 changed) +{ + int ret; + trace_rdev_update_connect_params(&rdev->wiphy, dev, sme, changed); + ret = rdev->ops->update_connect_params(&rdev->wiphy, dev, sme, changed); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason_code) { diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 25f8318..ea1b47e 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1281,6 +1281,24 @@ TRACE_EVENT(rdev_connect, __entry->wpa_versions, __entry->flags, MAC_PR_ARG(prev_bssid)) ); +TRACE_EVENT(rdev_update_connect_params, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_connect_params *sme, u32 changed), + TP_ARGS(wiphy, netdev, sme, changed), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u32, changed) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->changed = changed; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", parameters changed: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->changed) +); + TRACE_EVENT(rdev_set_cqm_rssi_config, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, s32 rssi_thold, -- cgit v1.1 From 4fe77d82ef80c77031c9c6f8554cd0dee2aa423a Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 24 Oct 2016 20:32:57 +0800 Subject: skbedit: allow the user to specify bitmask for mark The user may want to use only some bits of the skb mark in his skbedit rules because the remaining part might be used by something else. Introduce the "mask" parameter to the skbedit actor in order to implement such functionality. When the mask is specified, only those bits selected by the latter are altered really changed by the actor, while the rest is left untouched. Signed-off-by: Antonio Quartulli Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/tc_act/tc_skbedit.h | 1 + include/uapi/linux/tc_act/tc_skbedit.h | 2 ++ net/sched/act_skbedit.c | 21 ++++++++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/net/tc_act/tc_skbedit.h b/include/net/tc_act/tc_skbedit.h index 5767e9d..19cd3d3 100644 --- a/include/net/tc_act/tc_skbedit.h +++ b/include/net/tc_act/tc_skbedit.h @@ -27,6 +27,7 @@ struct tcf_skbedit { u32 flags; u32 priority; u32 mark; + u32 mask; u16 queue_mapping; u16 ptype; }; diff --git a/include/uapi/linux/tc_act/tc_skbedit.h b/include/uapi/linux/tc_act/tc_skbedit.h index a4d00c6..2884425 100644 --- a/include/uapi/linux/tc_act/tc_skbedit.h +++ b/include/uapi/linux/tc_act/tc_skbedit.h @@ -28,6 +28,7 @@ #define SKBEDIT_F_QUEUE_MAPPING 0x2 #define SKBEDIT_F_MARK 0x4 #define SKBEDIT_F_PTYPE 0x8 +#define SKBEDIT_F_MASK 0x10 struct tc_skbedit { tc_gen; @@ -42,6 +43,7 @@ enum { TCA_SKBEDIT_MARK, TCA_SKBEDIT_PAD, TCA_SKBEDIT_PTYPE, + TCA_SKBEDIT_MASK, __TCA_SKBEDIT_MAX }; #define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1) diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index a133dcb..024f3a3 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -46,8 +46,10 @@ static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, if (d->flags & SKBEDIT_F_QUEUE_MAPPING && skb->dev->real_num_tx_queues > d->queue_mapping) skb_set_queue_mapping(skb, d->queue_mapping); - if (d->flags & SKBEDIT_F_MARK) - skb->mark = d->mark; + if (d->flags & SKBEDIT_F_MARK) { + skb->mark &= ~d->mask; + skb->mark |= d->mark & d->mask; + } if (d->flags & SKBEDIT_F_PTYPE) skb->pkt_type = d->ptype; @@ -61,6 +63,7 @@ static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, [TCA_SKBEDIT_PTYPE] = { .len = sizeof(u16) }, + [TCA_SKBEDIT_MASK] = { .len = sizeof(u32) }, }; static int tcf_skbedit_init(struct net *net, struct nlattr *nla, @@ -71,7 +74,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; struct tc_skbedit *parm; struct tcf_skbedit *d; - u32 flags = 0, *priority = NULL, *mark = NULL; + u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL; u16 *queue_mapping = NULL, *ptype = NULL; bool exists = false; int ret = 0, err; @@ -108,6 +111,11 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, mark = nla_data(tb[TCA_SKBEDIT_MARK]); } + if (tb[TCA_SKBEDIT_MASK] != NULL) { + flags |= SKBEDIT_F_MASK; + mask = nla_data(tb[TCA_SKBEDIT_MASK]); + } + parm = nla_data(tb[TCA_SKBEDIT_PARMS]); exists = tcf_hash_check(tn, parm->index, a, bind); @@ -145,6 +153,10 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, d->mark = *mark; if (flags & SKBEDIT_F_PTYPE) d->ptype = *ptype; + /* default behaviour is to use all the bits */ + d->mask = 0xffffffff; + if (flags & SKBEDIT_F_MASK) + d->mask = *mask; d->tcf_action = parm->action; @@ -182,6 +194,9 @@ static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, if ((d->flags & SKBEDIT_F_PTYPE) && nla_put_u16(skb, TCA_SKBEDIT_PTYPE, d->ptype)) goto nla_put_failure; + if ((d->flags & SKBEDIT_F_MASK) && + nla_put_u32(skb, TCA_SKBEDIT_MASK, d->mask)) + goto nla_put_failure; tcf_tm_dump(&t, &d->tcf_tm); if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD)) -- cgit v1.1 From c90c39dab3e02ce45427a214746711f33ad13be6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Oct 2016 14:40:01 +0200 Subject: genetlink: introduce and use genl_family_attrbuf() This helper function allows family implementations to access their family's attrbuf. This gets rid of the attrbuf usage in families, and also adds locking validation, since it's not valid to use the attrbuf with parallel_ops or outside of the dumpit callback. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- include/net/genetlink.h | 2 ++ net/ieee802154/nl802154.c | 7 ++++--- net/netlink/genetlink.c | 19 ++++++++++++++++++ net/nfc/netlink.c | 9 ++++----- net/tipc/netlink.c | 2 +- net/wireless/nl80211.c | 51 +++++++++++++++++++++++------------------------ 6 files changed, 55 insertions(+), 35 deletions(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 8d4608c..ef9defb 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -73,6 +73,8 @@ struct genl_family { struct module *module; }; +struct nlattr **genl_family_attrbuf(struct genl_family *family); + /** * struct genl_info - receiving information * @snd_seq: sending sequence number diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index d90a4ed..21aabad 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -263,13 +263,14 @@ nl802154_prepare_wpan_dev_dump(struct sk_buff *skb, if (!cb->args[0]) { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, - nl802154_fam.attrbuf, nl802154_fam.maxattr, + genl_family_attrbuf(&nl802154_fam), + nl802154_fam.maxattr, nl802154_policy); if (err) goto out_unlock; *wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk), - nl802154_fam.attrbuf); + genl_family_attrbuf(&nl802154_fam)); if (IS_ERR(*wpan_dev)) { err = PTR_ERR(*wpan_dev); goto out_unlock; @@ -575,7 +576,7 @@ static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb, struct netlink_callback *cb, struct nl802154_dump_wpan_phy_state *state) { - struct nlattr **tb = nl802154_fam.attrbuf; + struct nlattr **tb = genl_family_attrbuf(&nl802154_fam); int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, tb, nl802154_fam.maxattr, nl802154_policy); diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 23cc126..01291b7 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1096,6 +1096,25 @@ problem: subsys_initcall(genl_init); +/** + * genl_family_attrbuf - return family's attrbuf + * @family: the family + * + * Return the family's attrbuf, while validating that it's + * actually valid to access it. + * + * You cannot use this function with a family that has parallel_ops + * and you can only use it within (pre/post) doit/dumpit callbacks. + */ +struct nlattr **genl_family_attrbuf(struct genl_family *family) +{ + if (!WARN_ON(family->parallel_ops)) + lockdep_assert_held(&genl_mutex); + + return family->attrbuf; +} +EXPORT_SYMBOL(genl_family_attrbuf); + static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, gfp_t flags) { diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index ea023b3..79786bf 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -120,21 +120,20 @@ nla_put_failure: static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb) { + struct nlattr **attrbuf = genl_family_attrbuf(&nfc_genl_family); struct nfc_dev *dev; int rc; u32 idx; rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize, - nfc_genl_family.attrbuf, - nfc_genl_family.maxattr, - nfc_genl_policy); + attrbuf, nfc_genl_family.maxattr, nfc_genl_policy); if (rc < 0) return ERR_PTR(rc); - if (!nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]) + if (!attrbuf[NFC_ATTR_DEVICE_INDEX]) return ERR_PTR(-EINVAL); - idx = nla_get_u32(nfc_genl_family.attrbuf[NFC_ATTR_DEVICE_INDEX]); + idx = nla_get_u32(attrbuf[NFC_ATTR_DEVICE_INDEX]); dev = nfc_get_device(idx); if (!dev) diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 3200059..4b94f3c 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -262,7 +262,7 @@ int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr) { u32 maxattr = tipc_genl_family.maxattr; - *attr = tipc_genl_family.attrbuf; + *attr = genl_family_attrbuf(&tipc_genl_family); if (!*attr) return -EOPNOTSUPP; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c510810..7d8cb33 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -551,13 +551,14 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, if (!cb->args[0]) { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - nl80211_fam.attrbuf, nl80211_fam.maxattr, - nl80211_policy); + genl_family_attrbuf(&nl80211_fam), + nl80211_fam.maxattr, nl80211_policy); if (err) goto out_unlock; - *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), - nl80211_fam.attrbuf); + *wdev = __cfg80211_wdev_from_attrs( + sock_net(skb->sk), + genl_family_attrbuf(&nl80211_fam)); if (IS_ERR(*wdev)) { err = PTR_ERR(*wdev); goto out_unlock; @@ -1881,7 +1882,7 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb, struct netlink_callback *cb, struct nl80211_dump_wiphy_state *state) { - struct nlattr **tb = nl80211_fam.attrbuf; + struct nlattr **tb = genl_family_attrbuf(&nl80211_fam); int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, tb, nl80211_fam.maxattr, nl80211_policy); /* ignore parse errors for backward compatibility */ @@ -7643,6 +7644,7 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) { + struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam); struct survey_info survey; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; @@ -7655,7 +7657,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) return res; /* prepare_wdev_dump parsed the attributes */ - radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; + radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; if (!wdev->netdev) { res = -EINVAL; @@ -8478,14 +8480,14 @@ static int nl80211_testmode_dump(struct sk_buff *skb, */ phy_idx = cb->args[0] - 1; } else { + struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam); + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - nl80211_fam.attrbuf, nl80211_fam.maxattr, - nl80211_policy); + attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) goto out_err; - rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), - nl80211_fam.attrbuf); + rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf); if (IS_ERR(rdev)) { err = PTR_ERR(rdev); goto out_err; @@ -8493,9 +8495,8 @@ static int nl80211_testmode_dump(struct sk_buff *skb, phy_idx = rdev->wiphy_idx; rdev = NULL; - if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) - cb->args[1] = - (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; + if (attrbuf[NL80211_ATTR_TESTDATA]) + cb->args[1] = (long)attrbuf[NL80211_ATTR_TESTDATA]; } if (cb->args[1]) { @@ -11277,6 +11278,7 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, struct cfg80211_registered_device **rdev, struct wireless_dev **wdev) { + struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam); u32 vid, subcmd; unsigned int i; int vcmd_idx = -1; @@ -11312,31 +11314,28 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, } err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - nl80211_fam.attrbuf, nl80211_fam.maxattr, - nl80211_policy); + attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) goto out_unlock; - if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] || - !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { + if (!attrbuf[NL80211_ATTR_VENDOR_ID] || + !attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { err = -EINVAL; goto out_unlock; } - *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), - nl80211_fam.attrbuf); + *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), attrbuf); if (IS_ERR(*wdev)) *wdev = NULL; - *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), - nl80211_fam.attrbuf); + *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf); if (IS_ERR(*rdev)) { err = PTR_ERR(*rdev); goto out_unlock; } - vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]); - subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); + vid = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) { const struct wiphy_vendor_command *vcmd; @@ -11360,9 +11359,9 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, goto out_unlock; } - if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) { - data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]); - data_len = nla_len(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]); + if (attrbuf[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(attrbuf[NL80211_ATTR_VENDOR_DATA]); + data_len = nla_len(attrbuf[NL80211_ATTR_VENDOR_DATA]); } /* 0 is the first index - add 1 to parse only once */ -- cgit v1.1 From a07ea4d9941af5a0c6f0be2a71b51ac9c083c5e5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Oct 2016 14:40:02 +0200 Subject: genetlink: no longer support using static family IDs Static family IDs have never really been used, the only use case was the workaround I introduced for those users that assumed their family ID was also their multicast group ID. Additionally, because static family IDs would never be reserved by the generic netlink code, using a relatively low ID would only work for built-in families that can be registered immediately after generic netlink is started, which is basically only the control family (apart from the workaround code, which I also had to add code for so it would reserve those IDs) Thus, anything other than GENL_ID_GENERATE is flawed and luckily not used except in the cases I mentioned. Move those workarounds into a few lines of code, and then get rid of GENL_ID_GENERATE entirely, making it more robust. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/acpi/event.c | 1 - drivers/net/gtp.c | 1 - drivers/net/macsec.c | 1 - drivers/net/team/team.c | 1 - drivers/net/wireless/mac80211_hwsim.c | 1 - drivers/scsi/pmcraid.c | 6 ------ drivers/target/target_core_user.c | 1 - drivers/thermal/thermal_core.c | 1 - fs/dlm/netlink.c | 1 - fs/quota/netlink.c | 7 ------- include/linux/genl_magic_func.h | 1 - include/net/genetlink.h | 7 ++----- include/uapi/linux/genetlink.h | 1 - kernel/taskstats.c | 1 - net/batman-adv/netlink.c | 1 - net/core/devlink.c | 1 - net/core/drop_monitor.c | 1 - net/hsr/hsr_netlink.c | 1 - net/ieee802154/netlink.c | 1 - net/ieee802154/nl802154.c | 1 - net/ipv4/fou.c | 1 - net/ipv4/tcp_metrics.c | 1 - net/ipv6/ila/ila_xlat.c | 1 - net/irda/irnetlink.c | 1 - net/l2tp/l2tp_netlink.c | 1 - net/netfilter/ipvs/ip_vs_ctl.c | 1 - net/netlabel/netlabel_calipso.c | 1 - net/netlabel/netlabel_cipso_v4.c | 1 - net/netlabel/netlabel_mgmt.c | 1 - net/netlabel/netlabel_unlabeled.c | 1 - net/netlink/genetlink.c | 37 +++++++++++++++++++++-------------- net/nfc/netlink.c | 1 - net/openvswitch/datapath.c | 4 ---- net/tipc/netlink.c | 1 - net/tipc/netlink_compat.c | 1 - net/wimax/stack.c | 1 - net/wireless/nl80211.c | 1 - 37 files changed, 24 insertions(+), 69 deletions(-) diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index e24ea4e..8dfca3d 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -83,7 +83,6 @@ static const struct genl_multicast_group acpi_event_mcgrps[] = { }; static struct genl_family acpi_event_genl_family = { - .id = GENL_ID_GENERATE, .name = ACPI_GENL_FAMILY_NAME, .version = ACPI_GENL_VERSION, .maxattr = ACPI_GENL_ATTR_MAX, diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 97e0cbc..f66737b 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1095,7 +1095,6 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) } static struct genl_family gtp_genl_family = { - .id = GENL_ID_GENERATE, .name = "gtp", .version = 0, .hdrsize = 0, diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 1a134cb..a5309b8 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1422,7 +1422,6 @@ static void clear_tx_sa(struct macsec_tx_sa *tx_sa) } static struct genl_family macsec_fam = { - .id = GENL_ID_GENERATE, .name = MACSEC_GENL_NAME, .hdrsize = 0, .version = MACSEC_GENL_VERSION, diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index a380649..0b50205 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2151,7 +2151,6 @@ static struct rtnl_link_ops team_link_ops __read_mostly = { ***********************************/ static struct genl_family team_nl_family = { - .id = GENL_ID_GENERATE, .name = TEAM_GENL_NAME, .version = TEAM_GENL_VERSION, .maxattr = TEAM_ATTR_MAX, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index e95b79b..54b6cd6 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -589,7 +589,6 @@ struct hwsim_radiotap_ack_hdr { /* MAC80211_HWSIM netlinf family */ static struct genl_family hwsim_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = "MAC80211_HWSIM", .version = 1, diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 68a5c34..cc50eb8 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -1369,12 +1369,6 @@ static struct genl_multicast_group pmcraid_mcgrps[] = { }; static struct genl_family pmcraid_event_family = { - /* - * Due to prior multicast group abuse (the code having assumed that - * the family ID can be used as a multicast group ID) we need to - * statically allocate a family (and thus group) ID. - */ - .id = GENL_ID_PMCRAID, .name = "pmcraid", .version = 1, .maxattr = PMCRAID_AEN_ATTR_MAX, diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 62bf4fe..313a0ef 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -148,7 +148,6 @@ static const struct genl_multicast_group tcmu_mcgrps[] = { /* Our generic netlink family */ static struct genl_family tcmu_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = "TCM-USER", .version = 1, diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 226b0b4ac..68d7503 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -2164,7 +2164,6 @@ static const struct genl_multicast_group thermal_event_mcgrps[] = { }; static struct genl_family thermal_event_genl_family = { - .id = GENL_ID_GENERATE, .name = THERMAL_GENL_FAMILY_NAME, .version = THERMAL_GENL_VERSION, .maxattr = THERMAL_GENL_ATTR_MAX, diff --git a/fs/dlm/netlink.c b/fs/dlm/netlink.c index 1e6e227..00d2269 100644 --- a/fs/dlm/netlink.c +++ b/fs/dlm/netlink.c @@ -17,7 +17,6 @@ static uint32_t dlm_nl_seqnum; static uint32_t listener_nlportid; static struct genl_family family = { - .id = GENL_ID_GENERATE, .name = DLM_GENL_NAME, .version = DLM_GENL_VERSION, }; diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c index 8b25267..3965a5c 100644 --- a/fs/quota/netlink.c +++ b/fs/quota/netlink.c @@ -13,13 +13,6 @@ static const struct genl_multicast_group quota_mcgrps[] = { /* Netlink family structure for quota */ static struct genl_family quota_genl_family = { - /* - * Needed due to multicast group ID abuse - old code assumed - * the family ID was also a valid multicast group ID (which - * isn't true) and userspace might thus rely on it. Assign a - * static ID for this group to make dealing with that easier. - */ - .id = GENL_ID_VFS_DQUOT, .hdrsize = 0, .name = "VFS_DQUOT", .version = 1, diff --git a/include/linux/genl_magic_func.h b/include/linux/genl_magic_func.h index 667c311..7c070c1 100644 --- a/include/linux/genl_magic_func.h +++ b/include/linux/genl_magic_func.h @@ -260,7 +260,6 @@ static struct genl_ops ZZZ_genl_ops[] __read_mostly = { */ #define ZZZ_genl_family CONCAT_(GENL_MAGIC_FAMILY, _genl_family) static struct genl_family ZZZ_genl_family __read_mostly = { - .id = GENL_ID_GENERATE, .name = __stringify(GENL_MAGIC_FAMILY), .version = GENL_MAGIC_VERSION, #ifdef GENL_MAGIC_FAMILY_HDRSZ diff --git a/include/net/genetlink.h b/include/net/genetlink.h index ef9defb..43a5c39 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -20,7 +20,7 @@ struct genl_info; /** * struct genl_family - generic netlink family - * @id: protocol family idenfitier + * @id: protocol family identifier (private) * @hdrsize: length of user specific header in bytes * @name: name of family * @version: protocol version @@ -48,7 +48,7 @@ struct genl_info; * @n_ops: number of operations supported by this family (private) */ struct genl_family { - unsigned int id; + unsigned int id; /* private */ unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; @@ -149,9 +149,6 @@ static inline int genl_register_family(struct genl_family *family) * Registers the specified family and operations from the specified table. * Only one family may be registered with the same family name or identifier. * - * The family id may equal GENL_ID_GENERATE causing an unique id to - * be automatically generated and assigned. - * * Either a doit or dumpit callback must be specified for every registered * operation or the function will fail. Only one operation structure per * command identifier may be registered. diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h index 5512c90..d9b2db4 100644 --- a/include/uapi/linux/genetlink.h +++ b/include/uapi/linux/genetlink.h @@ -26,7 +26,6 @@ struct genlmsghdr { /* * List of reserved static generic netlink identifiers: */ -#define GENL_ID_GENERATE 0 #define GENL_ID_CTRL NLMSG_MIN_TYPE #define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1) #define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2) diff --git a/kernel/taskstats.c b/kernel/taskstats.c index b3f05ee..d7a1a94 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -42,7 +42,6 @@ static int family_registered; struct kmem_cache *taskstats_cache; static struct genl_family family = { - .id = GENL_ID_GENERATE, .name = TASKSTATS_GENL_NAME, .version = TASKSTATS_GENL_VERSION, .maxattr = TASKSTATS_CMD_ATTR_MAX, diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 64cb6ac..a03b0ed 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -49,7 +49,6 @@ #include "translation-table.h" struct genl_family batadv_netlink_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = BATADV_NL_NAME, .version = 1, diff --git a/net/core/devlink.c b/net/core/devlink.c index d2fd736..3008d9c 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -342,7 +342,6 @@ static void devlink_nl_post_doit(const struct genl_ops *ops, } static struct genl_family devlink_nl_family = { - .id = GENL_ID_GENERATE, .name = DEVLINK_GENL_NAME, .version = DEVLINK_GENL_VERSION, .maxattr = DEVLINK_ATTR_MAX, diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 72cfb0c..a5320df 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -60,7 +60,6 @@ struct dm_hw_stat_delta { }; static struct genl_family net_drop_monitor_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = "NET_DM", .version = 2, diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index d4d1617..2ad0394 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -132,7 +132,6 @@ static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = { }; static struct genl_family hsr_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = "HSR", .version = 1, diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index c8133c0..1914415 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -29,7 +29,6 @@ static unsigned int ieee802154_seq_num; static DEFINE_SPINLOCK(ieee802154_seq_lock); struct genl_family nl802154_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = IEEE802154_NL_NAME, .version = 1, diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 21aabad..1822998 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -34,7 +34,6 @@ static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, /* the netlink family */ static struct genl_family nl802154_fam = { - .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ .name = NL802154_GENL_NAME, /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index cf50f7e..e3fc527 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -623,7 +623,6 @@ static int fou_destroy(struct net *net, struct fou_cfg *cfg) } static struct genl_family fou_nl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = FOU_GENL_NAME, .version = FOU_GENL_VERSION, diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index bf1f3b2..3da3051 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -743,7 +743,6 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss, } static struct genl_family tcp_metrics_nl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = TCP_METRICS_GENL_NAME, .version = TCP_METRICS_GENL_VERSION, diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index e604013..0d57e27 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -119,7 +119,6 @@ static const struct rhashtable_params rht_params = { }; static struct genl_family ila_nl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = ILA_GENL_NAME, .version = ILA_GENL_VERSION, diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c index e15c40e..f23b81a 100644 --- a/net/irda/irnetlink.c +++ b/net/irda/irnetlink.c @@ -25,7 +25,6 @@ static struct genl_family irda_nl_family = { - .id = GENL_ID_GENERATE, .name = IRDA_NL_NAME, .hdrsize = 0, .version = IRDA_NL_VERSION, diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index bf31177..4fbf1f4 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -32,7 +32,6 @@ static struct genl_family l2tp_nl_family = { - .id = GENL_ID_GENERATE, .name = L2TP_GENL_NAME, .version = L2TP_GENL_VERSION, .hdrsize = 0, diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index c3c809b..ceed66c 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2841,7 +2841,6 @@ static struct nf_sockopt_ops ip_vs_sockopts = { /* IPVS genetlink family */ static struct genl_family ip_vs_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = IPVS_GENL_NAME, .version = IPVS_GENL_VERSION, diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 2ec93c5..152e503 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -61,7 +61,6 @@ struct netlbl_domhsh_walk_arg { /* NetLabel Generic NETLINK CALIPSO family */ static struct genl_family netlbl_calipso_gnl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = NETLBL_NLTYPE_CALIPSO_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index 7fd1104..755b284 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -60,7 +60,6 @@ struct netlbl_domhsh_walk_arg { /* NetLabel Generic NETLINK CIPSOv4 family */ static struct genl_family netlbl_cipsov4_gnl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = NETLBL_NLTYPE_CIPSOV4_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index f85d0e0..3b00f23 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -61,7 +61,6 @@ struct netlbl_domhsh_walk_arg { /* NetLabel Generic NETLINK CIPSOv4 family */ static struct genl_family netlbl_mgmt_gnl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = NETLBL_NLTYPE_MGMT_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 4528cff..c2ea8d1 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -124,7 +124,6 @@ static u8 netlabel_unlabel_acceptflg; /* NetLabel Generic NETLINK unlabeled family */ static struct genl_family netlbl_unlabel_gnl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = NETLBL_NLTYPE_UNLABELED_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 01291b7..f19ec96 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -349,8 +349,6 @@ static int genl_validate_ops(const struct genl_family *family) * * Registers the specified family after validating it first. Only one * family may be registered with the same family name or identifier. - * The family id may equal GENL_ID_GENERATE causing an unique id to - * be automatically generated and assigned. * * The family's ops array must already be assigned, you can use the * genl_register_family_with_ops() helper function. @@ -359,13 +357,7 @@ static int genl_validate_ops(const struct genl_family *family) */ int __genl_register_family(struct genl_family *family) { - int err = -EINVAL, i; - - if (family->id && family->id < GENL_MIN_ID) - goto errout; - - if (family->id > GENL_MAX_ID) - goto errout; + int err, i; err = genl_validate_ops(family); if (err) @@ -378,8 +370,27 @@ int __genl_register_family(struct genl_family *family) goto errout_locked; } - if (family->id == GENL_ID_GENERATE) { - u16 newid = genl_generate_id(); + if (family == &genl_ctrl) { + family->id = GENL_ID_CTRL; + } else { + u16 newid; + + /* this should be left zero in the struct */ + WARN_ON(family->id); + + /* + * Sadly, a few cases need to be special-cased + * due to them having previously abused the API + * and having used their family ID also as their + * multicast group ID, so we use reserved IDs + * for both to be sure we can do that mapping. + */ + if (strcmp(family->name, "pmcraid") == 0) + newid = GENL_ID_PMCRAID; + else if (strcmp(family->name, "VFS_DQUOT") == 0) + newid = GENL_ID_VFS_DQUOT; + else + newid = genl_generate_id(); if (!newid) { err = -ENOMEM; @@ -387,9 +398,6 @@ int __genl_register_family(struct genl_family *family) } family->id = newid; - } else if (genl_family_find_byid(family->id)) { - err = -EEXIST; - goto errout_locked; } if (family->maxattr && !family->parallel_ops) { @@ -419,7 +427,6 @@ int __genl_register_family(struct genl_family *family) errout_locked: genl_unlock_all(); -errout: return err; } EXPORT_SYMBOL(__genl_register_family); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 79786bf..c230403 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -39,7 +39,6 @@ static const struct genl_multicast_group nfc_genl_mcgrps[] = { }; static struct genl_family nfc_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = 0, .name = NFC_GENL_NAME, .version = NFC_GENL_VERSION, diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 194435a..f9fef7d 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -671,7 +671,6 @@ static const struct genl_ops dp_packet_genl_ops[] = { }; static struct genl_family dp_packet_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_PACKET_FAMILY, .version = OVS_PACKET_VERSION, @@ -1436,7 +1435,6 @@ static const struct genl_ops dp_flow_genl_ops[] = { }; static struct genl_family dp_flow_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_FLOW_FAMILY, .version = OVS_FLOW_VERSION, @@ -1822,7 +1820,6 @@ static const struct genl_ops dp_datapath_genl_ops[] = { }; static struct genl_family dp_datapath_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_DATAPATH_FAMILY, .version = OVS_DATAPATH_VERSION, @@ -2244,7 +2241,6 @@ static const struct genl_ops dp_vport_genl_ops[] = { }; struct genl_family dp_vport_genl_family = { - .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_VPORT_FAMILY, .version = OVS_VPORT_VERSION, diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 4b94f3c..383b8fe 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -136,7 +136,6 @@ const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = { * so we have a separate genl handling for the new API. */ struct genl_family tipc_genl_family = { - .id = GENL_ID_GENERATE, .name = TIPC_GENL_V2_NAME, .version = TIPC_GENL_V2_VERSION, .hdrsize = 0, diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 1fd4647..f04428e 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -1216,7 +1216,6 @@ send: } static struct genl_family tipc_genl_compat_family = { - .id = GENL_ID_GENERATE, .name = TIPC_GENL_NAME, .version = TIPC_GENL_VERSION, .hdrsize = TIPC_GENL_HDRLEN, diff --git a/net/wimax/stack.c b/net/wimax/stack.c index 3f816e2..8ac83a4 100644 --- a/net/wimax/stack.c +++ b/net/wimax/stack.c @@ -573,7 +573,6 @@ size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); struct genl_family wimax_gnl_family = { - .id = GENL_ID_GENERATE, .name = "WiMAX", .version = WIMAX_GNL_VERSION, .hdrsize = 0, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7d8cb33..714beaf 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -39,7 +39,6 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb, /* the netlink family */ static struct genl_family nl80211_fam = { - .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ .name = NL80211_GENL_NAME, /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ -- cgit v1.1 From 489111e5c25b93be80340c3113d71903d7c82136 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Oct 2016 14:40:03 +0200 Subject: genetlink: statically initialize families Instead of providing macros/inline functions to initialize the families, make all users initialize them statically and get rid of the macros. This reduces the kernel code size by about 1.6k on x86-64 (with allyesconfig). Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/acpi/event.c | 1 + drivers/net/gtp.c | 21 +++++++---- drivers/net/macsec.c | 21 +++++++---- drivers/net/team/team.c | 22 +++++++---- drivers/net/wireless/mac80211_hwsim.c | 26 +++++++------ drivers/scsi/pmcraid.c | 1 + drivers/target/target_core_user.c | 1 + drivers/thermal/thermal_core.c | 1 + fs/dlm/netlink.c | 15 +++++--- fs/quota/netlink.c | 1 + include/linux/drbd_genl.h | 2 +- include/linux/genl_magic_func.h | 28 ++++++++------ include/net/genetlink.h | 71 ++++++----------------------------- kernel/taskstats.c | 17 ++++++--- net/batman-adv/netlink.c | 25 +++++++----- net/core/devlink.c | 27 +++++++------ net/core/drop_monitor.c | 20 ++++++---- net/hsr/hsr_netlink.c | 22 +++++++---- net/ieee802154/netlink.c | 23 +++++++----- net/ieee802154/nl802154.c | 34 ++++++++--------- net/ipv4/fou.c | 22 ++++++----- net/ipv4/tcp_metrics.c | 22 ++++++----- net/ipv6/ila/ila_xlat.c | 24 +++++++----- net/irda/irnetlink.c | 19 ++++++---- net/l2tp/l2tp_netlink.c | 25 +++++++----- net/netfilter/ipvs/ip_vs_ctl.c | 22 ++++++----- net/netlabel/netlabel_calipso.c | 20 ++++++---- net/netlabel/netlabel_cipso_v4.c | 21 ++++++----- net/netlabel/netlabel_mgmt.c | 20 ++++++---- net/netlabel/netlabel_unlabeled.c | 20 ++++++---- net/netlink/genetlink.c | 35 +++++++++-------- net/nfc/netlink.c | 24 +++++++----- net/openvswitch/datapath.c | 4 ++ net/tipc/netlink.c | 22 ++++++----- net/tipc/netlink_compat.c | 20 +++++----- net/wimax/stack.c | 19 +++++----- net/wireless/nl80211.c | 33 ++++++++-------- 37 files changed, 414 insertions(+), 337 deletions(-) diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index 8dfca3d..1ab12ad 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -83,6 +83,7 @@ static const struct genl_multicast_group acpi_event_mcgrps[] = { }; static struct genl_family acpi_event_genl_family = { + .module = THIS_MODULE, .name = ACPI_GENL_FAMILY_NAME, .version = ACPI_GENL_VERSION, .maxattr = ACPI_GENL_ATTR_MAX, diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index f66737b..0604fd7 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1094,13 +1094,7 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) return 0; } -static struct genl_family gtp_genl_family = { - .name = "gtp", - .version = 0, - .hdrsize = 0, - .maxattr = GTPA_MAX, - .netnsok = true, -}; +static struct genl_family gtp_genl_family; static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, u32 type, struct pdp_ctx *pctx) @@ -1296,6 +1290,17 @@ static const struct genl_ops gtp_genl_ops[] = { }, }; +static struct genl_family gtp_genl_family = { + .name = "gtp", + .version = 0, + .hdrsize = 0, + .maxattr = GTPA_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = gtp_genl_ops, + .n_ops = ARRAY_SIZE(gtp_genl_ops), +}; + static int __net_init gtp_net_init(struct net *net) { struct gtp_net *gn = net_generic(net, gtp_net_id); @@ -1335,7 +1340,7 @@ static int __init gtp_init(void) if (err < 0) goto error_out; - err = genl_register_family_with_ops(>p_genl_family, gtp_genl_ops); + err = genl_register_family(>p_genl_family); if (err < 0) goto unreg_rtnl_link; diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index a5309b8..63ca7a3 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1421,13 +1421,7 @@ static void clear_tx_sa(struct macsec_tx_sa *tx_sa) macsec_txsa_put(tx_sa); } -static struct genl_family macsec_fam = { - .name = MACSEC_GENL_NAME, - .hdrsize = 0, - .version = MACSEC_GENL_VERSION, - .maxattr = MACSEC_ATTR_MAX, - .netnsok = true, -}; +static struct genl_family macsec_fam; static struct net_device *get_dev_from_nl(struct net *net, struct nlattr **attrs) @@ -2654,6 +2648,17 @@ static const struct genl_ops macsec_genl_ops[] = { }, }; +static struct genl_family macsec_fam = { + .name = MACSEC_GENL_NAME, + .hdrsize = 0, + .version = MACSEC_GENL_VERSION, + .maxattr = MACSEC_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = macsec_genl_ops, + .n_ops = ARRAY_SIZE(macsec_genl_ops), +}; + static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -3461,7 +3466,7 @@ static int __init macsec_init(void) if (err) goto notifier; - err = genl_register_family_with_ops(&macsec_fam, macsec_genl_ops); + err = genl_register_family(&macsec_fam); if (err) goto rtnl; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 0b50205..46bf7c1 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2150,12 +2150,7 @@ static struct rtnl_link_ops team_link_ops __read_mostly = { * Generic netlink custom interface ***********************************/ -static struct genl_family team_nl_family = { - .name = TEAM_GENL_NAME, - .version = TEAM_GENL_VERSION, - .maxattr = TEAM_ATTR_MAX, - .netnsok = true, -}; +static struct genl_family team_nl_family; static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = { [TEAM_ATTR_UNSPEC] = { .type = NLA_UNSPEC, }, @@ -2745,6 +2740,18 @@ static const struct genl_multicast_group team_nl_mcgrps[] = { { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, }, }; +static struct genl_family team_nl_family = { + .name = TEAM_GENL_NAME, + .version = TEAM_GENL_VERSION, + .maxattr = TEAM_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = team_nl_ops, + .n_ops = ARRAY_SIZE(team_nl_ops), + .mcgrps = team_nl_mcgrps, + .n_mcgrps = ARRAY_SIZE(team_nl_mcgrps), +}; + static int team_nl_send_multicast(struct sk_buff *skb, struct team *team, u32 portid) { @@ -2768,8 +2775,7 @@ static int team_nl_send_event_port_get(struct team *team, static int team_nl_init(void) { - return genl_register_family_with_ops_groups(&team_nl_family, team_nl_ops, - team_nl_mcgrps); + return genl_register_family(&team_nl_family); } static void team_nl_fini(void) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 54b6cd6..5d4637e 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -587,14 +587,8 @@ struct hwsim_radiotap_ack_hdr { __le16 rt_chbitmask; } __packed; -/* MAC80211_HWSIM netlinf family */ -static struct genl_family hwsim_genl_family = { - .hdrsize = 0, - .name = "MAC80211_HWSIM", - .version = 1, - .maxattr = HWSIM_ATTR_MAX, - .netnsok = true, -}; +/* MAC80211_HWSIM netlink family */ +static struct genl_family hwsim_genl_family; enum hwsim_multicast_groups { HWSIM_MCGRP_CONFIG, @@ -3234,6 +3228,18 @@ static const struct genl_ops hwsim_ops[] = { }, }; +static struct genl_family hwsim_genl_family = { + .name = "MAC80211_HWSIM", + .version = 1, + .maxattr = HWSIM_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = hwsim_ops, + .n_ops = ARRAY_SIZE(hwsim_ops), + .mcgrps = hwsim_mcgrps, + .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps), +}; + static void destroy_radio(struct work_struct *work) { struct mac80211_hwsim_data *data = @@ -3287,9 +3293,7 @@ static int hwsim_init_netlink(void) printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); - rc = genl_register_family_with_ops_groups(&hwsim_genl_family, - hwsim_ops, - hwsim_mcgrps); + rc = genl_register_family(&hwsim_genl_family); if (rc) goto failure; diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index cc50eb8..c0ab7bb 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -1369,6 +1369,7 @@ static struct genl_multicast_group pmcraid_mcgrps[] = { }; static struct genl_family pmcraid_event_family = { + .module = THIS_MODULE, .name = "pmcraid", .version = 1, .maxattr = PMCRAID_AEN_ATTR_MAX, diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 313a0ef..3483372 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -148,6 +148,7 @@ static const struct genl_multicast_group tcmu_mcgrps[] = { /* Our generic netlink family */ static struct genl_family tcmu_genl_family = { + .module = THIS_MODULE, .hdrsize = 0, .name = "TCM-USER", .version = 1, diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 68d7503..93b6caa 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -2164,6 +2164,7 @@ static const struct genl_multicast_group thermal_event_mcgrps[] = { }; static struct genl_family thermal_event_genl_family = { + .module = THIS_MODULE, .name = THERMAL_GENL_FAMILY_NAME, .version = THERMAL_GENL_VERSION, .maxattr = THERMAL_GENL_ATTR_MAX, diff --git a/fs/dlm/netlink.c b/fs/dlm/netlink.c index 00d2269..04042d6 100644 --- a/fs/dlm/netlink.c +++ b/fs/dlm/netlink.c @@ -16,10 +16,7 @@ static uint32_t dlm_nl_seqnum; static uint32_t listener_nlportid; -static struct genl_family family = { - .name = DLM_GENL_NAME, - .version = DLM_GENL_VERSION, -}; +static struct genl_family family; static int prepare_data(u8 cmd, struct sk_buff **skbp, size_t size) { @@ -75,9 +72,17 @@ static struct genl_ops dlm_nl_ops[] = { }, }; +static struct genl_family family = { + .name = DLM_GENL_NAME, + .version = DLM_GENL_VERSION, + .ops = dlm_nl_ops, + .n_ops = ARRAY_SIZE(dlm_nl_ops), + .module = THIS_MODULE, +}; + int __init dlm_netlink_init(void) { - return genl_register_family_with_ops(&family, dlm_nl_ops); + return genl_register_family(&family); } void dlm_netlink_exit(void) diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c index 3965a5c..9457c7b 100644 --- a/fs/quota/netlink.c +++ b/fs/quota/netlink.c @@ -13,6 +13,7 @@ static const struct genl_multicast_group quota_mcgrps[] = { /* Netlink family structure for quota */ static struct genl_family quota_genl_family = { + .module = THIS_MODULE, .hdrsize = 0, .name = "VFS_DQUOT", .version = 1, diff --git a/include/linux/drbd_genl.h b/include/linux/drbd_genl.h index c934d3a..2896f93 100644 --- a/include/linux/drbd_genl.h +++ b/include/linux/drbd_genl.h @@ -67,7 +67,7 @@ * genl_magic_func.h * generates an entry in the static genl_ops array, * and static register/unregister functions to - * genl_register_family_with_ops(). + * genl_register_family(). * * flags and handler: * GENL_op_init( .doit = x, .dumpit = y, .flags = something) diff --git a/include/linux/genl_magic_func.h b/include/linux/genl_magic_func.h index 7c070c1..40c2e39 100644 --- a/include/linux/genl_magic_func.h +++ b/include/linux/genl_magic_func.h @@ -259,15 +259,7 @@ static struct genl_ops ZZZ_genl_ops[] __read_mostly = { * {{{2 */ #define ZZZ_genl_family CONCAT_(GENL_MAGIC_FAMILY, _genl_family) -static struct genl_family ZZZ_genl_family __read_mostly = { - .name = __stringify(GENL_MAGIC_FAMILY), - .version = GENL_MAGIC_VERSION, -#ifdef GENL_MAGIC_FAMILY_HDRSZ - .hdrsize = NLA_ALIGN(GENL_MAGIC_FAMILY_HDRSZ), -#endif - .maxattr = ARRAY_SIZE(drbd_tla_nl_policy)-1, -}; - +static struct genl_family ZZZ_genl_family; /* * Magic: define multicast groups * Magic: define multicast group registration helper @@ -301,11 +293,23 @@ static int CONCAT_(GENL_MAGIC_FAMILY, _genl_multicast_ ## group)( \ #undef GENL_mc_group #define GENL_mc_group(group) +static struct genl_family ZZZ_genl_family __read_mostly = { + .name = __stringify(GENL_MAGIC_FAMILY), + .version = GENL_MAGIC_VERSION, +#ifdef GENL_MAGIC_FAMILY_HDRSZ + .hdrsize = NLA_ALIGN(GENL_MAGIC_FAMILY_HDRSZ), +#endif + .maxattr = ARRAY_SIZE(drbd_tla_nl_policy)-1, + .ops = ZZZ_genl_ops, + .n_ops = ARRAY_SIZE(ZZZ_genl_ops), + .mcgrps = ZZZ_genl_mcgrps, + .n_mcgrps = ARRAY_SIZE(ZZZ_genl_mcgrps), + .module = THIS_MODULE, +}; + int CONCAT_(GENL_MAGIC_FAMILY, _genl_register)(void) { - return genl_register_family_with_ops_groups(&ZZZ_genl_family, \ - ZZZ_genl_ops, \ - ZZZ_genl_mcgrps); + return genl_register_family(&ZZZ_genl_family); } void CONCAT_(GENL_MAGIC_FAMILY, _genl_unregister)(void) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 43a5c39..2298b50 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -39,13 +39,14 @@ struct genl_info; * Note that unbind() will not be called symmetrically if the * generic netlink family is removed while there are still open * sockets. - * @attrbuf: buffer to store parsed attributes - * @family_list: family list - * @mcgrps: multicast groups used by this family (private) - * @n_mcgrps: number of multicast groups (private) + * @attrbuf: buffer to store parsed attributes (private) + * @family_list: family list (private) + * @mcgrps: multicast groups used by this family + * @n_mcgrps: number of multicast groups * @mcgrp_offset: starting number of multicast group IDs in this family - * @ops: the operations supported by this family (private) - * @n_ops: number of operations supported by this family (private) + * (private) + * @ops: the operations supported by this family + * @n_ops: number of operations supported by this family */ struct genl_family { unsigned int id; /* private */ @@ -64,10 +65,10 @@ struct genl_family { int (*mcast_bind)(struct net *net, int group); void (*mcast_unbind)(struct net *net, int group); struct nlattr ** attrbuf; /* private */ - const struct genl_ops * ops; /* private */ - const struct genl_multicast_group *mcgrps; /* private */ - unsigned int n_ops; /* private */ - unsigned int n_mcgrps; /* private */ + const struct genl_ops * ops; + const struct genl_multicast_group *mcgrps; + unsigned int n_ops; + unsigned int n_mcgrps; unsigned int mcgrp_offset; /* private */ struct list_head family_list; /* private */ struct module *module; @@ -132,55 +133,7 @@ struct genl_ops { u8 flags; }; -int __genl_register_family(struct genl_family *family); - -static inline int genl_register_family(struct genl_family *family) -{ - family->module = THIS_MODULE; - return __genl_register_family(family); -} - -/** - * genl_register_family_with_ops - register a generic netlink family with ops - * @family: generic netlink family - * @ops: operations to be registered - * @n_ops: number of elements to register - * - * Registers the specified family and operations from the specified table. - * Only one family may be registered with the same family name or identifier. - * - * Either a doit or dumpit callback must be specified for every registered - * operation or the function will fail. Only one operation structure per - * command identifier may be registered. - * - * See include/net/genetlink.h for more documenation on the operations - * structure. - * - * Return 0 on success or a negative error code. - */ -static inline int -_genl_register_family_with_ops_grps(struct genl_family *family, - const struct genl_ops *ops, size_t n_ops, - const struct genl_multicast_group *mcgrps, - size_t n_mcgrps) -{ - family->module = THIS_MODULE; - family->ops = ops; - family->n_ops = n_ops; - family->mcgrps = mcgrps; - family->n_mcgrps = n_mcgrps; - return __genl_register_family(family); -} - -#define genl_register_family_with_ops(family, ops) \ - _genl_register_family_with_ops_grps((family), \ - (ops), ARRAY_SIZE(ops), \ - NULL, 0) -#define genl_register_family_with_ops_groups(family, ops, grps) \ - _genl_register_family_with_ops_grps((family), \ - (ops), ARRAY_SIZE(ops), \ - (grps), ARRAY_SIZE(grps)) - +int genl_register_family(struct genl_family *family); int genl_unregister_family(struct genl_family *family); void genl_notify(struct genl_family *family, struct sk_buff *skb, struct genl_info *info, u32 group, gfp_t flags); diff --git a/kernel/taskstats.c b/kernel/taskstats.c index d7a1a94..4075ece 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -41,11 +41,7 @@ static DEFINE_PER_CPU(__u32, taskstats_seqnum); static int family_registered; struct kmem_cache *taskstats_cache; -static struct genl_family family = { - .name = TASKSTATS_GENL_NAME, - .version = TASKSTATS_GENL_VERSION, - .maxattr = TASKSTATS_CMD_ATTR_MAX, -}; +static struct genl_family family; static const struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] = { [TASKSTATS_CMD_ATTR_PID] = { .type = NLA_U32 }, @@ -650,6 +646,15 @@ static const struct genl_ops taskstats_ops[] = { }, }; +static struct genl_family family = { + .name = TASKSTATS_GENL_NAME, + .version = TASKSTATS_GENL_VERSION, + .maxattr = TASKSTATS_CMD_ATTR_MAX, + .module = THIS_MODULE, + .ops = taskstats_ops, + .n_ops = ARRAY_SIZE(taskstats_ops), +}; + /* Needed early in initialization */ void __init taskstats_init_early(void) { @@ -666,7 +671,7 @@ static int __init taskstats_init(void) { int rc; - rc = genl_register_family_with_ops(&family, taskstats_ops); + rc = genl_register_family(&family); if (rc) return rc; diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index a03b0ed..e28cec3 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -48,13 +48,7 @@ #include "tp_meter.h" #include "translation-table.h" -struct genl_family batadv_netlink_family = { - .hdrsize = 0, - .name = BATADV_NL_NAME, - .version = 1, - .maxattr = BATADV_ATTR_MAX, - .netnsok = true, -}; +struct genl_family batadv_netlink_family; /* multicast groups */ enum batadv_netlink_multicast_groups { @@ -609,6 +603,19 @@ static struct genl_ops batadv_netlink_ops[] = { }; +struct genl_family batadv_netlink_family = { + .hdrsize = 0, + .name = BATADV_NL_NAME, + .version = 1, + .maxattr = BATADV_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = batadv_netlink_ops, + .n_ops = ARRAY_SIZE(batadv_netlink_ops), + .mcgrps = batadv_netlink_mcgrps, + .n_mcgrps = ARRAY_SIZE(batadv_netlink_mcgrps), +}; + /** * batadv_netlink_register - register batadv genl netlink family */ @@ -616,9 +623,7 @@ void __init batadv_netlink_register(void) { int ret; - ret = genl_register_family_with_ops_groups(&batadv_netlink_family, - batadv_netlink_ops, - batadv_netlink_mcgrps); + ret = genl_register_family(&batadv_netlink_family); if (ret) pr_warn("unable to register netlink family"); } diff --git a/net/core/devlink.c b/net/core/devlink.c index 3008d9c..063da80 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -341,14 +341,7 @@ static void devlink_nl_post_doit(const struct genl_ops *ops, mutex_unlock(&devlink_mutex); } -static struct genl_family devlink_nl_family = { - .name = DEVLINK_GENL_NAME, - .version = DEVLINK_GENL_VERSION, - .maxattr = DEVLINK_ATTR_MAX, - .netnsok = true, - .pre_doit = devlink_nl_pre_doit, - .post_doit = devlink_nl_post_doit, -}; +static struct genl_family devlink_nl_family; enum devlink_multicast_groups { DEVLINK_MCGRP_CONFIG, @@ -1619,6 +1612,20 @@ static const struct genl_ops devlink_nl_ops[] = { }, }; +static struct genl_family devlink_nl_family = { + .name = DEVLINK_GENL_NAME, + .version = DEVLINK_GENL_VERSION, + .maxattr = DEVLINK_ATTR_MAX, + .netnsok = true, + .pre_doit = devlink_nl_pre_doit, + .post_doit = devlink_nl_post_doit, + .module = THIS_MODULE, + .ops = devlink_nl_ops, + .n_ops = ARRAY_SIZE(devlink_nl_ops), + .mcgrps = devlink_nl_mcgrps, + .n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps), +}; + /** * devlink_alloc - Allocate new devlink instance resources * @@ -1841,9 +1848,7 @@ EXPORT_SYMBOL_GPL(devlink_sb_unregister); static int __init devlink_module_init(void) { - return genl_register_family_with_ops_groups(&devlink_nl_family, - devlink_nl_ops, - devlink_nl_mcgrps); + return genl_register_family(&devlink_nl_family); } static void __exit devlink_module_exit(void) diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index a5320df..80c0027 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -59,11 +59,7 @@ struct dm_hw_stat_delta { unsigned long last_drop_val; }; -static struct genl_family net_drop_monitor_family = { - .hdrsize = 0, - .name = "NET_DM", - .version = 2, -}; +static struct genl_family net_drop_monitor_family; static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data); @@ -350,6 +346,17 @@ static const struct genl_ops dropmon_ops[] = { }, }; +static struct genl_family net_drop_monitor_family = { + .hdrsize = 0, + .name = "NET_DM", + .version = 2, + .module = THIS_MODULE, + .ops = dropmon_ops, + .n_ops = ARRAY_SIZE(dropmon_ops), + .mcgrps = dropmon_mcgrps, + .n_mcgrps = ARRAY_SIZE(dropmon_mcgrps), +}; + static struct notifier_block dropmon_net_notifier = { .notifier_call = dropmon_net_event }; @@ -366,8 +373,7 @@ static int __init init_net_drop_monitor(void) return -ENOSPC; } - rc = genl_register_family_with_ops_groups(&net_drop_monitor_family, - dropmon_ops, dropmon_mcgrps); + rc = genl_register_family(&net_drop_monitor_family); if (rc) { pr_err("Could not create drop monitor netlink family\n"); return rc; diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index 2ad0394..aab34c7 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -131,12 +131,7 @@ static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = { [HSR_A_IF2_SEQ] = { .type = NLA_U16 }, }; -static struct genl_family hsr_genl_family = { - .hdrsize = 0, - .name = "HSR", - .version = 1, - .maxattr = HSR_A_MAX, -}; +static struct genl_family hsr_genl_family; static const struct genl_multicast_group hsr_mcgrps[] = { { .name = "hsr-network", }, @@ -466,6 +461,18 @@ static const struct genl_ops hsr_ops[] = { }, }; +static struct genl_family hsr_genl_family = { + .hdrsize = 0, + .name = "HSR", + .version = 1, + .maxattr = HSR_A_MAX, + .module = THIS_MODULE, + .ops = hsr_ops, + .n_ops = ARRAY_SIZE(hsr_ops), + .mcgrps = hsr_mcgrps, + .n_mcgrps = ARRAY_SIZE(hsr_mcgrps), +}; + int __init hsr_netlink_init(void) { int rc; @@ -474,8 +481,7 @@ int __init hsr_netlink_init(void) if (rc) goto fail_rtnl_link_register; - rc = genl_register_family_with_ops_groups(&hsr_genl_family, hsr_ops, - hsr_mcgrps); + rc = genl_register_family(&hsr_genl_family); if (rc) goto fail_genl_register_family; diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 1914415..08e6247 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -28,13 +28,6 @@ static unsigned int ieee802154_seq_num; static DEFINE_SPINLOCK(ieee802154_seq_lock); -struct genl_family nl802154_family = { - .hdrsize = 0, - .name = IEEE802154_NL_NAME, - .version = 1, - .maxattr = IEEE802154_ATTR_MAX, -}; - /* Requests to userspace */ struct sk_buff *ieee802154_nl_create(int flags, u8 req) { @@ -138,11 +131,21 @@ static const struct genl_multicast_group ieee802154_mcgrps[] = { [IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, }, }; +struct genl_family nl802154_family = { + .hdrsize = 0, + .name = IEEE802154_NL_NAME, + .version = 1, + .maxattr = IEEE802154_ATTR_MAX, + .module = THIS_MODULE, + .ops = ieee8021154_ops, + .n_ops = ARRAY_SIZE(ieee8021154_ops), + .mcgrps = ieee802154_mcgrps, + .n_mcgrps = ARRAY_SIZE(ieee802154_mcgrps), +}; + int __init ieee802154_nl_init(void) { - return genl_register_family_with_ops_groups(&nl802154_family, - ieee8021154_ops, - ieee802154_mcgrps); + return genl_register_family(&nl802154_family); } void ieee802154_nl_exit(void) diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 1822998..f7e7557 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -26,22 +26,8 @@ #include "rdev-ops.h" #include "core.h" -static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); - -static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); - /* the netlink family */ -static struct genl_family nl802154_fam = { - .name = NL802154_GENL_NAME, /* have users key off the name instead */ - .hdrsize = 0, /* no private header */ - .version = 1, /* no particular meaning now */ - .maxattr = NL802154_ATTR_MAX, - .netnsok = true, - .pre_doit = nl802154_pre_doit, - .post_doit = nl802154_post_doit, -}; +static struct genl_family nl802154_fam; /* multicast groups */ enum nl802154_multicast_groups { @@ -2476,11 +2462,25 @@ static const struct genl_ops nl802154_ops[] = { #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ }; +static struct genl_family nl802154_fam = { + .name = NL802154_GENL_NAME, /* have users key off the name instead */ + .hdrsize = 0, /* no private header */ + .version = 1, /* no particular meaning now */ + .maxattr = NL802154_ATTR_MAX, + .netnsok = true, + .pre_doit = nl802154_pre_doit, + .post_doit = nl802154_post_doit, + .module = THIS_MODULE, + .ops = nl802154_ops, + .n_ops = ARRAY_SIZE(nl802154_ops), + .mcgrps = nl802154_mcgrps, + .n_mcgrps = ARRAY_SIZE(nl802154_mcgrps), +}; + /* initialisation/exit functions */ int nl802154_init(void) { - return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops, - nl802154_mcgrps); + return genl_register_family(&nl802154_fam); } void nl802154_exit(void) diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index e3fc527..5b5226a 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -622,13 +622,7 @@ static int fou_destroy(struct net *net, struct fou_cfg *cfg) return err; } -static struct genl_family fou_nl_family = { - .hdrsize = 0, - .name = FOU_GENL_NAME, - .version = FOU_GENL_VERSION, - .maxattr = FOU_ATTR_MAX, - .netnsok = true, -}; +static struct genl_family fou_nl_family; static const struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = { [FOU_ATTR_PORT] = { .type = NLA_U16, }, @@ -830,6 +824,17 @@ static const struct genl_ops fou_nl_ops[] = { }, }; +static struct genl_family fou_nl_family = { + .hdrsize = 0, + .name = FOU_GENL_NAME, + .version = FOU_GENL_VERSION, + .maxattr = FOU_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = fou_nl_ops, + .n_ops = ARRAY_SIZE(fou_nl_ops), +}; + size_t fou_encap_hlen(struct ip_tunnel_encap *e) { return sizeof(struct udphdr); @@ -1085,8 +1090,7 @@ static int __init fou_init(void) if (ret) goto exit; - ret = genl_register_family_with_ops(&fou_nl_family, - fou_nl_ops); + ret = genl_register_family(&fou_nl_family); if (ret < 0) goto unregister; diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 3da3051..bba3c72 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -742,13 +742,7 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss, rcu_read_unlock(); } -static struct genl_family tcp_metrics_nl_family = { - .hdrsize = 0, - .name = TCP_METRICS_GENL_NAME, - .version = TCP_METRICS_GENL_VERSION, - .maxattr = TCP_METRICS_ATTR_MAX, - .netnsok = true, -}; +static struct genl_family tcp_metrics_nl_family; static const struct nla_policy tcp_metrics_nl_policy[TCP_METRICS_ATTR_MAX + 1] = { [TCP_METRICS_ATTR_ADDR_IPV4] = { .type = NLA_U32, }, @@ -1115,6 +1109,17 @@ static const struct genl_ops tcp_metrics_nl_ops[] = { }, }; +static struct genl_family tcp_metrics_nl_family = { + .hdrsize = 0, + .name = TCP_METRICS_GENL_NAME, + .version = TCP_METRICS_GENL_VERSION, + .maxattr = TCP_METRICS_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = tcp_metrics_nl_ops, + .n_ops = ARRAY_SIZE(tcp_metrics_nl_ops), +}; + static unsigned int tcpmhash_entries; static int __init set_tcpmhash_entries(char *str) { @@ -1178,8 +1183,7 @@ void __init tcp_metrics_init(void) if (ret < 0) panic("Could not allocate the tcp_metrics hash table\n"); - ret = genl_register_family_with_ops(&tcp_metrics_nl_family, - tcp_metrics_nl_ops); + ret = genl_register_family(&tcp_metrics_nl_family); if (ret < 0) panic("Could not register tcp_metrics generic netlink\n"); } diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 0d57e27..97f7b0c 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -118,14 +118,7 @@ static const struct rhashtable_params rht_params = { .obj_cmpfn = ila_cmpfn, }; -static struct genl_family ila_nl_family = { - .hdrsize = 0, - .name = ILA_GENL_NAME, - .version = ILA_GENL_VERSION, - .maxattr = ILA_ATTR_MAX, - .netnsok = true, - .parallel_ops = true, -}; +static struct genl_family ila_nl_family; static const struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, @@ -560,6 +553,18 @@ static const struct genl_ops ila_nl_ops[] = { }, }; +static struct genl_family ila_nl_family = { + .hdrsize = 0, + .name = ILA_GENL_NAME, + .version = ILA_GENL_VERSION, + .maxattr = ILA_ATTR_MAX, + .netnsok = true, + .parallel_ops = true, + .module = THIS_MODULE, + .ops = ila_nl_ops, + .n_ops = ARRAY_SIZE(ila_nl_ops), +}; + #define ILA_HASH_TABLE_SIZE 1024 static __net_init int ila_init_net(struct net *net) @@ -630,8 +635,7 @@ int ila_xlat_init(void) if (ret) goto exit; - ret = genl_register_family_with_ops(&ila_nl_family, - ila_nl_ops); + ret = genl_register_family(&ila_nl_family); if (ret < 0) goto unregister; diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c index f23b81a..0787734 100644 --- a/net/irda/irnetlink.c +++ b/net/irda/irnetlink.c @@ -24,12 +24,7 @@ -static struct genl_family irda_nl_family = { - .name = IRDA_NL_NAME, - .hdrsize = 0, - .version = IRDA_NL_VERSION, - .maxattr = IRDA_NL_CMD_MAX, -}; +static struct genl_family irda_nl_family; static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info) { @@ -146,9 +141,19 @@ static const struct genl_ops irda_nl_ops[] = { }; +static struct genl_family irda_nl_family = { + .name = IRDA_NL_NAME, + .hdrsize = 0, + .version = IRDA_NL_VERSION, + .maxattr = IRDA_NL_CMD_MAX, + .module = THIS_MODULE, + .ops = irda_nl_ops, + .n_ops = ARRAY_SIZE(irda_nl_ops), +}; + int irda_nl_register(void) { - return genl_register_family_with_ops(&irda_nl_family, irda_nl_ops); + return genl_register_family(&irda_nl_family); } void irda_nl_unregister(void) diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 4fbf1f4..e4e8c07 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -31,13 +31,7 @@ #include "l2tp_core.h" -static struct genl_family l2tp_nl_family = { - .name = L2TP_GENL_NAME, - .version = L2TP_GENL_VERSION, - .hdrsize = 0, - .maxattr = L2TP_ATTR_MAX, - .netnsok = true, -}; +static struct genl_family l2tp_nl_family; static const struct genl_multicast_group l2tp_multicast_group[] = { { @@ -976,6 +970,19 @@ static const struct genl_ops l2tp_nl_ops[] = { }, }; +static struct genl_family l2tp_nl_family = { + .name = L2TP_GENL_NAME, + .version = L2TP_GENL_VERSION, + .hdrsize = 0, + .maxattr = L2TP_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = l2tp_nl_ops, + .n_ops = ARRAY_SIZE(l2tp_nl_ops), + .mcgrps = l2tp_multicast_group, + .n_mcgrps = ARRAY_SIZE(l2tp_multicast_group), +}; + int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops) { int ret; @@ -1012,9 +1019,7 @@ EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); static int l2tp_nl_init(void) { pr_info("L2TP netlink interface\n"); - return genl_register_family_with_ops_groups(&l2tp_nl_family, - l2tp_nl_ops, - l2tp_multicast_group); + return genl_register_family(&l2tp_nl_family); } static void l2tp_nl_cleanup(void) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index ceed66c..ea3e8ae 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2840,13 +2840,7 @@ static struct nf_sockopt_ops ip_vs_sockopts = { */ /* IPVS genetlink family */ -static struct genl_family ip_vs_genl_family = { - .hdrsize = 0, - .name = IPVS_GENL_NAME, - .version = IPVS_GENL_VERSION, - .maxattr = IPVS_CMD_MAX, - .netnsok = true, /* Make ipvsadm to work on netns */ -}; +static struct genl_family ip_vs_genl_family; /* Policy used for first-level command attributes */ static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = { @@ -3871,10 +3865,20 @@ static const struct genl_ops ip_vs_genl_ops[] = { }, }; +static struct genl_family ip_vs_genl_family = { + .hdrsize = 0, + .name = IPVS_GENL_NAME, + .version = IPVS_GENL_VERSION, + .maxattr = IPVS_CMD_MAX, + .netnsok = true, /* Make ipvsadm to work on netns */ + .module = THIS_MODULE, + .ops = ip_vs_genl_ops, + .n_ops = ARRAY_SIZE(ip_vs_genl_ops), +}; + static int __init ip_vs_genl_register(void) { - return genl_register_family_with_ops(&ip_vs_genl_family, - ip_vs_genl_ops); + return genl_register_family(&ip_vs_genl_family); } static void ip_vs_genl_unregister(void) diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 152e503..ca7c9c4 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -60,12 +60,7 @@ struct netlbl_domhsh_walk_arg { }; /* NetLabel Generic NETLINK CALIPSO family */ -static struct genl_family netlbl_calipso_gnl_family = { - .hdrsize = 0, - .name = NETLBL_NLTYPE_CALIPSO_NAME, - .version = NETLBL_PROTO_VERSION, - .maxattr = NLBL_CALIPSO_A_MAX, -}; +static struct genl_family netlbl_calipso_gnl_family; /* NetLabel Netlink attribute policy */ static const struct nla_policy calipso_genl_policy[NLBL_CALIPSO_A_MAX + 1] = { @@ -354,6 +349,16 @@ static const struct genl_ops netlbl_calipso_ops[] = { }, }; +static struct genl_family netlbl_calipso_gnl_family = { + .hdrsize = 0, + .name = NETLBL_NLTYPE_CALIPSO_NAME, + .version = NETLBL_PROTO_VERSION, + .maxattr = NLBL_CALIPSO_A_MAX, + .module = THIS_MODULE, + .ops = netlbl_calipso_ops, + .n_ops = ARRAY_SIZE(netlbl_calipso_ops), +}; + /* NetLabel Generic NETLINK Protocol Functions */ @@ -367,8 +372,7 @@ static const struct genl_ops netlbl_calipso_ops[] = { */ int __init netlbl_calipso_genl_init(void) { - return genl_register_family_with_ops(&netlbl_calipso_gnl_family, - netlbl_calipso_ops); + return genl_register_family(&netlbl_calipso_gnl_family); } static const struct netlbl_calipso_ops *calipso_ops; diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index 755b284..a665eae 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -59,13 +59,7 @@ struct netlbl_domhsh_walk_arg { }; /* NetLabel Generic NETLINK CIPSOv4 family */ -static struct genl_family netlbl_cipsov4_gnl_family = { - .hdrsize = 0, - .name = NETLBL_NLTYPE_CIPSOV4_NAME, - .version = NETLBL_PROTO_VERSION, - .maxattr = NLBL_CIPSOV4_A_MAX, -}; - +static struct genl_family netlbl_cipsov4_gnl_family; /* NetLabel Netlink attribute policy */ static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = { [NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 }, @@ -766,6 +760,16 @@ static const struct genl_ops netlbl_cipsov4_ops[] = { }, }; +static struct genl_family netlbl_cipsov4_gnl_family = { + .hdrsize = 0, + .name = NETLBL_NLTYPE_CIPSOV4_NAME, + .version = NETLBL_PROTO_VERSION, + .maxattr = NLBL_CIPSOV4_A_MAX, + .module = THIS_MODULE, + .ops = netlbl_cipsov4_ops, + .n_ops = ARRAY_SIZE(netlbl_cipsov4_ops), +}; + /* * NetLabel Generic NETLINK Protocol Functions */ @@ -780,6 +784,5 @@ static const struct genl_ops netlbl_cipsov4_ops[] = { */ int __init netlbl_cipsov4_genl_init(void) { - return genl_register_family_with_ops(&netlbl_cipsov4_gnl_family, - netlbl_cipsov4_ops); + return genl_register_family(&netlbl_cipsov4_gnl_family); } diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index 3b00f23..ecfe8eb 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -60,12 +60,7 @@ struct netlbl_domhsh_walk_arg { }; /* NetLabel Generic NETLINK CIPSOv4 family */ -static struct genl_family netlbl_mgmt_gnl_family = { - .hdrsize = 0, - .name = NETLBL_NLTYPE_MGMT_NAME, - .version = NETLBL_PROTO_VERSION, - .maxattr = NLBL_MGMT_A_MAX, -}; +static struct genl_family netlbl_mgmt_gnl_family; /* NetLabel Netlink attribute policy */ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { @@ -833,6 +828,16 @@ static const struct genl_ops netlbl_mgmt_genl_ops[] = { }, }; +static struct genl_family netlbl_mgmt_gnl_family = { + .hdrsize = 0, + .name = NETLBL_NLTYPE_MGMT_NAME, + .version = NETLBL_PROTO_VERSION, + .maxattr = NLBL_MGMT_A_MAX, + .module = THIS_MODULE, + .ops = netlbl_mgmt_genl_ops, + .n_ops = ARRAY_SIZE(netlbl_mgmt_genl_ops), +}; + /* * NetLabel Generic NETLINK Protocol Functions */ @@ -847,6 +852,5 @@ static const struct genl_ops netlbl_mgmt_genl_ops[] = { */ int __init netlbl_mgmt_genl_init(void) { - return genl_register_family_with_ops(&netlbl_mgmt_gnl_family, - netlbl_mgmt_genl_ops); + return genl_register_family(&netlbl_mgmt_gnl_family); } diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index c2ea8d1..5dbbad4 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -123,12 +123,7 @@ static struct netlbl_unlhsh_iface __rcu *netlbl_unlhsh_def; static u8 netlabel_unlabel_acceptflg; /* NetLabel Generic NETLINK unlabeled family */ -static struct genl_family netlbl_unlabel_gnl_family = { - .hdrsize = 0, - .name = NETLBL_NLTYPE_UNLABELED_NAME, - .version = NETLBL_PROTO_VERSION, - .maxattr = NLBL_UNLABEL_A_MAX, -}; +static struct genl_family netlbl_unlabel_gnl_family; /* NetLabel Netlink attribute policy */ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1] = { @@ -1377,6 +1372,16 @@ static const struct genl_ops netlbl_unlabel_genl_ops[] = { }, }; +static struct genl_family netlbl_unlabel_gnl_family = { + .hdrsize = 0, + .name = NETLBL_NLTYPE_UNLABELED_NAME, + .version = NETLBL_PROTO_VERSION, + .maxattr = NLBL_UNLABEL_A_MAX, + .module = THIS_MODULE, + .ops = netlbl_unlabel_genl_ops, + .n_ops = ARRAY_SIZE(netlbl_unlabel_genl_ops), +}; + /* * NetLabel Generic NETLINK Protocol Functions */ @@ -1391,8 +1396,7 @@ static const struct genl_ops netlbl_unlabel_genl_ops[] = { */ int __init netlbl_unlabel_genl_init(void) { - return genl_register_family_with_ops(&netlbl_unlabel_gnl_family, - netlbl_unlabel_genl_ops); + return genl_register_family(&netlbl_unlabel_gnl_family); } /* diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index f19ec96..ca582ee 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -344,18 +344,18 @@ static int genl_validate_ops(const struct genl_family *family) } /** - * __genl_register_family - register a generic netlink family + * genl_register_family - register a generic netlink family * @family: generic netlink family * * Registers the specified family after validating it first. Only one * family may be registered with the same family name or identifier. * - * The family's ops array must already be assigned, you can use the - * genl_register_family_with_ops() helper function. + * The family's ops, multicast groups and module pointer must already + * be assigned. * * Return 0 on success or a negative error code. */ -int __genl_register_family(struct genl_family *family) +int genl_register_family(struct genl_family *family) { int err, i; @@ -429,7 +429,7 @@ errout_locked: genl_unlock_all(); return err; } -EXPORT_SYMBOL(__genl_register_family); +EXPORT_SYMBOL(genl_register_family); /** * genl_unregister_family - unregister generic netlink family @@ -452,7 +452,6 @@ int genl_unregister_family(struct genl_family *family) genl_unregister_mc_groups(family); list_del(&rc->family_list); - family->n_ops = 0; up_write(&cb_lock); wait_event(genl_sk_destructing_waitq, atomic_read(&genl_sk_destructing_cnt) == 0); @@ -681,13 +680,7 @@ static void genl_rcv(struct sk_buff *skb) * Controller **************************************************************************/ -static struct genl_family genl_ctrl = { - .id = GENL_ID_CTRL, - .name = "nlctrl", - .version = 0x2, - .maxattr = CTRL_ATTR_MAX, - .netnsok = true, -}; +static struct genl_family genl_ctrl; static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) @@ -997,6 +990,19 @@ static const struct genl_multicast_group genl_ctrl_groups[] = { { .name = "notify", }, }; +static struct genl_family genl_ctrl = { + .module = THIS_MODULE, + .ops = genl_ctrl_ops, + .n_ops = ARRAY_SIZE(genl_ctrl_ops), + .mcgrps = genl_ctrl_groups, + .n_mcgrps = ARRAY_SIZE(genl_ctrl_groups), + .id = GENL_ID_CTRL, + .name = "nlctrl", + .version = 0x2, + .maxattr = CTRL_ATTR_MAX, + .netnsok = true, +}; + static int genl_bind(struct net *net, int group) { int i, err = -ENOENT; @@ -1086,8 +1092,7 @@ static int __init genl_init(void) for (i = 0; i < GENL_FAM_TAB_SIZE; i++) INIT_LIST_HEAD(&family_ht[i]); - err = genl_register_family_with_ops_groups(&genl_ctrl, genl_ctrl_ops, - genl_ctrl_groups); + err = genl_register_family(&genl_ctrl); if (err < 0) goto problem; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index c230403..450b1e51 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -38,13 +38,7 @@ static const struct genl_multicast_group nfc_genl_mcgrps[] = { { .name = NFC_GENL_MCAST_EVENT_NAME, }, }; -static struct genl_family nfc_genl_family = { - .hdrsize = 0, - .name = NFC_GENL_NAME, - .version = NFC_GENL_VERSION, - .maxattr = NFC_ATTR_MAX, -}; - +static struct genl_family nfc_genl_family; static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { [NFC_ATTR_DEVICE_INDEX] = { .type = NLA_U32 }, [NFC_ATTR_DEVICE_NAME] = { .type = NLA_STRING, @@ -1752,6 +1746,18 @@ static const struct genl_ops nfc_genl_ops[] = { }, }; +static struct genl_family nfc_genl_family = { + .hdrsize = 0, + .name = NFC_GENL_NAME, + .version = NFC_GENL_VERSION, + .maxattr = NFC_ATTR_MAX, + .module = THIS_MODULE, + .ops = nfc_genl_ops, + .n_ops = ARRAY_SIZE(nfc_genl_ops), + .mcgrps = nfc_genl_mcgrps, + .n_mcgrps = ARRAY_SIZE(nfc_genl_mcgrps), +}; + struct urelease_work { struct work_struct w; @@ -1837,9 +1843,7 @@ int __init nfc_genl_init(void) { int rc; - rc = genl_register_family_with_ops_groups(&nfc_genl_family, - nfc_genl_ops, - nfc_genl_mcgrps); + rc = genl_register_family(&nfc_genl_family); if (rc) return rc; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index f9fef7d..ad6a111a 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -679,6 +679,7 @@ static struct genl_family dp_packet_genl_family = { .parallel_ops = true, .ops = dp_packet_genl_ops, .n_ops = ARRAY_SIZE(dp_packet_genl_ops), + .module = THIS_MODULE, }; static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats, @@ -1445,6 +1446,7 @@ static struct genl_family dp_flow_genl_family = { .n_ops = ARRAY_SIZE(dp_flow_genl_ops), .mcgrps = &ovs_dp_flow_multicast_group, .n_mcgrps = 1, + .module = THIS_MODULE, }; static size_t ovs_dp_cmd_msg_size(void) @@ -1830,6 +1832,7 @@ static struct genl_family dp_datapath_genl_family = { .n_ops = ARRAY_SIZE(dp_datapath_genl_ops), .mcgrps = &ovs_dp_datapath_multicast_group, .n_mcgrps = 1, + .module = THIS_MODULE, }; /* Called with ovs_mutex or RCU read lock. */ @@ -2251,6 +2254,7 @@ struct genl_family dp_vport_genl_family = { .n_ops = ARRAY_SIZE(dp_vport_genl_ops), .mcgrps = &ovs_dp_vport_multicast_group, .n_mcgrps = 1, + .module = THIS_MODULE, }; static struct genl_family * const dp_genl_families[] = { diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 383b8fe..74a405b 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -135,14 +135,6 @@ const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = { /* Users of the legacy API (tipc-config) can't handle that we add operations, * so we have a separate genl handling for the new API. */ -struct genl_family tipc_genl_family = { - .name = TIPC_GENL_V2_NAME, - .version = TIPC_GENL_V2_VERSION, - .hdrsize = 0, - .maxattr = TIPC_NLA_MAX, - .netnsok = true, -}; - static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_DISABLE, @@ -257,6 +249,17 @@ static const struct genl_ops tipc_genl_v2_ops[] = { #endif }; +struct genl_family tipc_genl_family = { + .name = TIPC_GENL_V2_NAME, + .version = TIPC_GENL_V2_VERSION, + .hdrsize = 0, + .maxattr = TIPC_NLA_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = tipc_genl_v2_ops, + .n_ops = ARRAY_SIZE(tipc_genl_v2_ops), +}; + int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr) { u32 maxattr = tipc_genl_family.maxattr; @@ -272,8 +275,7 @@ int tipc_netlink_start(void) { int res; - res = genl_register_family_with_ops(&tipc_genl_family, - tipc_genl_v2_ops); + res = genl_register_family(&tipc_genl_family); if (res) { pr_err("Failed to register netlink interface\n"); return res; diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index f04428e..07b1993 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -1215,27 +1215,29 @@ send: return err; } +static struct genl_ops tipc_genl_compat_ops[] = { + { + .cmd = TIPC_GENL_CMD, + .doit = tipc_nl_compat_recv, + }, +}; + static struct genl_family tipc_genl_compat_family = { .name = TIPC_GENL_NAME, .version = TIPC_GENL_VERSION, .hdrsize = TIPC_GENL_HDRLEN, .maxattr = 0, .netnsok = true, -}; - -static struct genl_ops tipc_genl_compat_ops[] = { - { - .cmd = TIPC_GENL_CMD, - .doit = tipc_nl_compat_recv, - }, + .module = THIS_MODULE, + .ops = tipc_genl_compat_ops, + .n_ops = ARRAY_SIZE(tipc_genl_compat_ops), }; int tipc_netlink_compat_start(void) { int res; - res = genl_register_family_with_ops(&tipc_genl_compat_family, - tipc_genl_compat_ops); + res = genl_register_family(&tipc_genl_compat_family); if (res) { pr_err("Failed to register legacy compat interface\n"); return res; diff --git a/net/wimax/stack.c b/net/wimax/stack.c index 8ac83a4..587e162 100644 --- a/net/wimax/stack.c +++ b/net/wimax/stack.c @@ -572,15 +572,20 @@ struct d_level D_LEVEL[] = { size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); +static const struct genl_multicast_group wimax_gnl_mcgrps[] = { + { .name = "msg", }, +}; + struct genl_family wimax_gnl_family = { .name = "WiMAX", .version = WIMAX_GNL_VERSION, .hdrsize = 0, .maxattr = WIMAX_GNL_ATTR_MAX, -}; - -static const struct genl_multicast_group wimax_gnl_mcgrps[] = { - { .name = "msg", }, + .module = THIS_MODULE, + .ops = wimax_gnl_ops, + .n_ops = ARRAY_SIZE(wimax_gnl_ops), + .mcgrps = wimax_gnl_mcgrps, + .n_mcgrps = ARRAY_SIZE(wimax_gnl_mcgrps), }; @@ -595,11 +600,7 @@ int __init wimax_subsys_init(void) d_parse_params(D_LEVEL, D_LEVEL_SIZE, wimax_debug_params, "wimax.debug"); - snprintf(wimax_gnl_family.name, sizeof(wimax_gnl_family.name), - "WiMAX"); - result = genl_register_family_with_ops_groups(&wimax_gnl_family, - wimax_gnl_ops, - wimax_gnl_mcgrps); + result = genl_register_family(&wimax_gnl_family); if (unlikely(result < 0)) { pr_err("cannot register generic netlink family: %d\n", result); goto error_register_family; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 714beaf..8e5ca3c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -32,21 +32,8 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, struct cfg80211_crypto_settings *settings, int cipher_limit); -static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); -static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); - /* the netlink family */ -static struct genl_family nl80211_fam = { - .name = NL80211_GENL_NAME, /* have users key off the name instead */ - .hdrsize = 0, /* no private header */ - .version = 1, /* no particular meaning now */ - .maxattr = NL80211_ATTR_MAX, - .netnsok = true, - .pre_doit = nl80211_pre_doit, - .post_doit = nl80211_post_doit, -}; +static struct genl_family nl80211_fam; /* multicast groups */ enum nl80211_multicast_groups { @@ -12599,6 +12586,21 @@ static const struct genl_ops nl80211_ops[] = { }, }; +static struct genl_family nl80211_fam = { + .name = NL80211_GENL_NAME, /* have users key off the name instead */ + .hdrsize = 0, /* no private header */ + .version = 1, /* no particular meaning now */ + .maxattr = NL80211_ATTR_MAX, + .netnsok = true, + .pre_doit = nl80211_pre_doit, + .post_doit = nl80211_post_doit, + .module = THIS_MODULE, + .ops = nl80211_ops, + .n_ops = ARRAY_SIZE(nl80211_ops), + .mcgrps = nl80211_mcgrps, + .n_mcgrps = ARRAY_SIZE(nl80211_mcgrps), +}; + /* notification functions */ void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, @@ -14565,8 +14567,7 @@ int nl80211_init(void) { int err; - err = genl_register_family_with_ops_groups(&nl80211_fam, nl80211_ops, - nl80211_mcgrps); + err = genl_register_family(&nl80211_fam); if (err) return err; -- cgit v1.1 From 2ae0f17df1cd52aafd1ab0415ea1f1dd56dc0e2a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Oct 2016 14:40:04 +0200 Subject: genetlink: use idr to track families Since generic netlink family IDs are small integers, allocated densely, IDR is an ideal match for lookups. Replace the existing hand-written hash-table with IDR for allocation and lookup. This lets the families only be written to once, during register, since the list_head can be removed and removal of a family won't cause any writes. It also slightly reduces the code size (by about 1.3k on x86-64). Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- include/net/genetlink.h | 31 +++-- include/uapi/linux/genetlink.h | 2 + net/netlink/genetlink.c | 271 ++++++++++++++++------------------------- 3 files changed, 123 insertions(+), 181 deletions(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 2298b50..3ec87ba 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -40,7 +40,6 @@ struct genl_info; * generic netlink family is removed while there are still open * sockets. * @attrbuf: buffer to store parsed attributes (private) - * @family_list: family list (private) * @mcgrps: multicast groups used by this family * @n_mcgrps: number of multicast groups * @mcgrp_offset: starting number of multicast group IDs in this family @@ -70,11 +69,10 @@ struct genl_family { unsigned int n_ops; unsigned int n_mcgrps; unsigned int mcgrp_offset; /* private */ - struct list_head family_list; /* private */ struct module *module; }; -struct nlattr **genl_family_attrbuf(struct genl_family *family); +struct nlattr **genl_family_attrbuf(const struct genl_family *family); /** * struct genl_info - receiving information @@ -134,12 +132,12 @@ struct genl_ops { }; int genl_register_family(struct genl_family *family); -int genl_unregister_family(struct genl_family *family); -void genl_notify(struct genl_family *family, struct sk_buff *skb, +int genl_unregister_family(const struct genl_family *family); +void genl_notify(const struct genl_family *family, struct sk_buff *skb, struct genl_info *info, u32 group, gfp_t flags); void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, - struct genl_family *family, int flags, u8 cmd); + const struct genl_family *family, int flags, u8 cmd); /** * genlmsg_nlhdr - Obtain netlink header from user specified header @@ -148,8 +146,8 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, * * Returns pointer to netlink header. */ -static inline struct nlmsghdr *genlmsg_nlhdr(void *user_hdr, - struct genl_family *family) +static inline struct nlmsghdr * +genlmsg_nlhdr(void *user_hdr, const struct genl_family *family) { return (struct nlmsghdr *)((char *)user_hdr - family->hdrsize - @@ -185,7 +183,7 @@ static inline int genlmsg_parse(const struct nlmsghdr *nlh, */ static inline void genl_dump_check_consistent(struct netlink_callback *cb, void *user_hdr, - struct genl_family *family) + const struct genl_family *family) { nl_dump_check_consistent(cb, genlmsg_nlhdr(user_hdr, family)); } @@ -202,7 +200,7 @@ static inline void genl_dump_check_consistent(struct netlink_callback *cb, */ static inline void *genlmsg_put_reply(struct sk_buff *skb, struct genl_info *info, - struct genl_family *family, + const struct genl_family *family, int flags, u8 cmd) { return genlmsg_put(skb, info->snd_portid, info->snd_seq, family, @@ -239,7 +237,7 @@ static inline void genlmsg_cancel(struct sk_buff *skb, void *hdr) * @group: offset of multicast group in groups array * @flags: allocation flags */ -static inline int genlmsg_multicast_netns(struct genl_family *family, +static inline int genlmsg_multicast_netns(const struct genl_family *family, struct net *net, struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags) { @@ -257,7 +255,7 @@ static inline int genlmsg_multicast_netns(struct genl_family *family, * @group: offset of multicast group in groups array * @flags: allocation flags */ -static inline int genlmsg_multicast(struct genl_family *family, +static inline int genlmsg_multicast(const struct genl_family *family, struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags) { @@ -275,7 +273,7 @@ static inline int genlmsg_multicast(struct genl_family *family, * * This function must hold the RTNL or rcu_read_lock(). */ -int genlmsg_multicast_allns(struct genl_family *family, +int genlmsg_multicast_allns(const struct genl_family *family, struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags); @@ -359,8 +357,9 @@ static inline struct sk_buff *genlmsg_new(size_t payload, gfp_t flags) * This function returns the number of broadcast listeners that have set the * NETLINK_RECV_NO_ENOBUFS socket option. */ -static inline int genl_set_err(struct genl_family *family, struct net *net, - u32 portid, u32 group, int code) +static inline int genl_set_err(const struct genl_family *family, + struct net *net, u32 portid, + u32 group, int code) { if (WARN_ON_ONCE(group >= family->n_mcgrps)) return -EINVAL; @@ -368,7 +367,7 @@ static inline int genl_set_err(struct genl_family *family, struct net *net, return netlink_set_err(net->genl_sock, portid, group, code); } -static inline int genl_has_listeners(struct genl_family *family, +static inline int genl_has_listeners(const struct genl_family *family, struct net *net, unsigned int group) { if (WARN_ON_ONCE(group >= family->n_mcgrps)) diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h index d9b2db4..adc8993 100644 --- a/include/uapi/linux/genetlink.h +++ b/include/uapi/linux/genetlink.h @@ -29,6 +29,8 @@ struct genlmsghdr { #define GENL_ID_CTRL NLMSG_MIN_TYPE #define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1) #define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2) +/* must be last reserved + 1 */ +#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3) /************************************************************************** * Controller diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index ca582ee..8565992 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -58,10 +59,8 @@ static void genl_unlock_all(void) up_write(&cb_lock); } -#define GENL_FAM_TAB_SIZE 16 -#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) +static DEFINE_IDR(genl_fam_idr); -static struct list_head family_ht[GENL_FAM_TAB_SIZE]; /* * Bitmap of multicast groups that are currently in use. * @@ -86,45 +85,29 @@ static unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) | static unsigned long *mc_groups = &mc_group_start; static unsigned long mc_groups_longs = 1; -static int genl_ctrl_event(int event, struct genl_family *family, +static int genl_ctrl_event(int event, const struct genl_family *family, const struct genl_multicast_group *grp, int grp_id); -static inline unsigned int genl_family_hash(unsigned int id) +static const struct genl_family *genl_family_find_byid(unsigned int id) { - return id & GENL_FAM_TAB_MASK; + return idr_find(&genl_fam_idr, id); } -static inline struct list_head *genl_family_chain(unsigned int id) +static const struct genl_family *genl_family_find_byname(char *name) { - return &family_ht[genl_family_hash(id)]; -} - -static struct genl_family *genl_family_find_byid(unsigned int id) -{ - struct genl_family *f; + const struct genl_family *family; + unsigned int id; - list_for_each_entry(f, genl_family_chain(id), family_list) - if (f->id == id) - return f; + idr_for_each_entry(&genl_fam_idr, family, id) + if (strcmp(family->name, name) == 0) + return family; return NULL; } -static struct genl_family *genl_family_find_byname(char *name) -{ - struct genl_family *f; - int i; - - for (i = 0; i < GENL_FAM_TAB_SIZE; i++) - list_for_each_entry(f, genl_family_chain(i), family_list) - if (strcmp(f->name, name) == 0) - return f; - - return NULL; -} - -static const struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) +static const struct genl_ops *genl_get_cmd(u8 cmd, + const struct genl_family *family) { int i; @@ -135,26 +118,6 @@ static const struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) return NULL; } -/* Of course we are going to have problems once we hit - * 2^16 alive types, but that can only happen by year 2K -*/ -static u16 genl_generate_id(void) -{ - static u16 id_gen_idx = GENL_MIN_ID; - int i; - - for (i = 0; i <= GENL_MAX_ID - GENL_MIN_ID; i++) { - if (id_gen_idx != GENL_ID_VFS_DQUOT && - id_gen_idx != GENL_ID_PMCRAID && - !genl_family_find_byid(id_gen_idx)) - return id_gen_idx; - if (++id_gen_idx > GENL_MAX_ID) - id_gen_idx = GENL_MIN_ID; - } - - return 0; -} - static int genl_allocate_reserve_groups(int n_groups, int *first_id) { unsigned long *new_groups; @@ -295,7 +258,7 @@ static int genl_validate_assign_mc_groups(struct genl_family *family) return err; } -static void genl_unregister_mc_groups(struct genl_family *family) +static void genl_unregister_mc_groups(const struct genl_family *family) { struct net *net; int i; @@ -358,6 +321,7 @@ static int genl_validate_ops(const struct genl_family *family) int genl_register_family(struct genl_family *family) { int err, i; + int start = GENL_START_ALLOC, end = GENL_MAX_ID; err = genl_validate_ops(family); if (err) @@ -370,34 +334,20 @@ int genl_register_family(struct genl_family *family) goto errout_locked; } + /* + * Sadly, a few cases need to be special-cased + * due to them having previously abused the API + * and having used their family ID also as their + * multicast group ID, so we use reserved IDs + * for both to be sure we can do that mapping. + */ if (family == &genl_ctrl) { - family->id = GENL_ID_CTRL; - } else { - u16 newid; - - /* this should be left zero in the struct */ - WARN_ON(family->id); - - /* - * Sadly, a few cases need to be special-cased - * due to them having previously abused the API - * and having used their family ID also as their - * multicast group ID, so we use reserved IDs - * for both to be sure we can do that mapping. - */ - if (strcmp(family->name, "pmcraid") == 0) - newid = GENL_ID_PMCRAID; - else if (strcmp(family->name, "VFS_DQUOT") == 0) - newid = GENL_ID_VFS_DQUOT; - else - newid = genl_generate_id(); - - if (!newid) { - err = -ENOMEM; - goto errout_locked; - } - - family->id = newid; + /* and this needs to be special for initial family lookups */ + start = end = GENL_ID_CTRL; + } else if (strcmp(family->name, "pmcraid") == 0) { + start = end = GENL_ID_PMCRAID; + } else if (strcmp(family->name, "VFS_DQUOT") == 0) { + start = end = GENL_ID_VFS_DQUOT; } if (family->maxattr && !family->parallel_ops) { @@ -410,11 +360,15 @@ int genl_register_family(struct genl_family *family) } else family->attrbuf = NULL; + family->id = idr_alloc(&genl_fam_idr, family, + start, end + 1, GFP_KERNEL); + if (!family->id) + goto errout_locked; + err = genl_validate_assign_mc_groups(family); if (err) - goto errout_locked; + goto errout_remove; - list_add_tail(&family->family_list, genl_family_chain(family->id)); genl_unlock_all(); /* send all events */ @@ -425,6 +379,8 @@ int genl_register_family(struct genl_family *family) return 0; +errout_remove: + idr_remove(&genl_fam_idr, family->id); errout_locked: genl_unlock_all(); return err; @@ -439,32 +395,29 @@ EXPORT_SYMBOL(genl_register_family); * * Returns 0 on success or a negative error code. */ -int genl_unregister_family(struct genl_family *family) +int genl_unregister_family(const struct genl_family *family) { - struct genl_family *rc; - genl_lock_all(); - list_for_each_entry(rc, genl_family_chain(family->id), family_list) { - if (family->id != rc->id || strcmp(rc->name, family->name)) - continue; + if (genl_family_find_byid(family->id)) { + genl_unlock_all(); + return -ENOENT; + } - genl_unregister_mc_groups(family); + genl_unregister_mc_groups(family); - list_del(&rc->family_list); - up_write(&cb_lock); - wait_event(genl_sk_destructing_waitq, - atomic_read(&genl_sk_destructing_cnt) == 0); - genl_unlock(); + idr_remove(&genl_fam_idr, family->id); - kfree(family->attrbuf); - genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); - return 0; - } + up_write(&cb_lock); + wait_event(genl_sk_destructing_waitq, + atomic_read(&genl_sk_destructing_cnt) == 0); + genl_unlock(); - genl_unlock_all(); + kfree(family->attrbuf); - return -ENOENT; + genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); + + return 0; } EXPORT_SYMBOL(genl_unregister_family); @@ -480,7 +433,7 @@ EXPORT_SYMBOL(genl_unregister_family); * Returns pointer to user specific header */ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, - struct genl_family *family, int flags, u8 cmd) + const struct genl_family *family, int flags, u8 cmd) { struct nlmsghdr *nlh; struct genlmsghdr *hdr; @@ -539,7 +492,7 @@ static int genl_lock_done(struct netlink_callback *cb) return rc; } -static int genl_family_rcv_msg(struct genl_family *family, +static int genl_family_rcv_msg(const struct genl_family *family, struct sk_buff *skb, struct nlmsghdr *nlh) { @@ -651,7 +604,7 @@ out: static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { - struct genl_family *family; + const struct genl_family *family; int err; family = genl_family_find_byid(nlh->nlmsg_type); @@ -682,7 +635,7 @@ static void genl_rcv(struct sk_buff *skb) static struct genl_family genl_ctrl; -static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq, +static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) { void *hdr; @@ -769,7 +722,7 @@ nla_put_failure: return -EMSGSIZE; } -static int ctrl_fill_mcgrp_info(struct genl_family *family, +static int ctrl_fill_mcgrp_info(const struct genl_family *family, const struct genl_multicast_group *grp, int grp_id, u32 portid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) @@ -812,37 +765,30 @@ nla_put_failure: static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) { - - int i, n = 0; + int n = 0; struct genl_family *rt; struct net *net = sock_net(skb->sk); - int chains_to_skip = cb->args[0]; - int fams_to_skip = cb->args[1]; - - for (i = chains_to_skip; i < GENL_FAM_TAB_SIZE; i++) { - n = 0; - list_for_each_entry(rt, genl_family_chain(i), family_list) { - if (!rt->netnsok && !net_eq(net, &init_net)) - continue; - if (++n < fams_to_skip) - continue; - if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - skb, CTRL_CMD_NEWFAMILY) < 0) - goto errout; - } + int fams_to_skip = cb->args[0]; + unsigned int id; - fams_to_skip = 0; - } + idr_for_each_entry(&genl_fam_idr, rt, id) { + if (!rt->netnsok && !net_eq(net, &init_net)) + continue; + + if (n++ < fams_to_skip) + continue; -errout: - cb->args[0] = i; - cb->args[1] = n; + if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + skb, CTRL_CMD_NEWFAMILY) < 0) + break; + } + cb->args[0] = n; return skb->len; } -static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, +static struct sk_buff *ctrl_build_family_msg(const struct genl_family *family, u32 portid, int seq, u8 cmd) { struct sk_buff *skb; @@ -862,7 +808,7 @@ static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, } static struct sk_buff * -ctrl_build_mcgrp_msg(struct genl_family *family, +ctrl_build_mcgrp_msg(const struct genl_family *family, const struct genl_multicast_group *grp, int grp_id, u32 portid, int seq, u8 cmd) { @@ -892,7 +838,7 @@ static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *msg; - struct genl_family *res = NULL; + const struct genl_family *res = NULL; int err = -EINVAL; if (info->attrs[CTRL_ATTR_FAMILY_ID]) { @@ -936,7 +882,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) return genlmsg_reply(msg, info); } -static int genl_ctrl_event(int event, struct genl_family *family, +static int genl_ctrl_event(int event, const struct genl_family *family, const struct genl_multicast_group *grp, int grp_id) { @@ -1005,25 +951,24 @@ static struct genl_family genl_ctrl = { static int genl_bind(struct net *net, int group) { - int i, err = -ENOENT; + struct genl_family *f; + int err = -ENOENT; + unsigned int id; down_read(&cb_lock); - for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { - struct genl_family *f; - - list_for_each_entry(f, genl_family_chain(i), family_list) { - if (group >= f->mcgrp_offset && - group < f->mcgrp_offset + f->n_mcgrps) { - int fam_grp = group - f->mcgrp_offset; - - if (!f->netnsok && net != &init_net) - err = -ENOENT; - else if (f->mcast_bind) - err = f->mcast_bind(net, fam_grp); - else - err = 0; - break; - } + + idr_for_each_entry(&genl_fam_idr, f, id) { + if (group >= f->mcgrp_offset && + group < f->mcgrp_offset + f->n_mcgrps) { + int fam_grp = group - f->mcgrp_offset; + + if (!f->netnsok && net != &init_net) + err = -ENOENT; + else if (f->mcast_bind) + err = f->mcast_bind(net, fam_grp); + else + err = 0; + break; } } up_read(&cb_lock); @@ -1033,21 +978,19 @@ static int genl_bind(struct net *net, int group) static void genl_unbind(struct net *net, int group) { - int i; + struct genl_family *f; + unsigned int id; down_read(&cb_lock); - for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { - struct genl_family *f; - list_for_each_entry(f, genl_family_chain(i), family_list) { - if (group >= f->mcgrp_offset && - group < f->mcgrp_offset + f->n_mcgrps) { - int fam_grp = group - f->mcgrp_offset; + idr_for_each_entry(&genl_fam_idr, f, id) { + if (group >= f->mcgrp_offset && + group < f->mcgrp_offset + f->n_mcgrps) { + int fam_grp = group - f->mcgrp_offset; - if (f->mcast_unbind) - f->mcast_unbind(net, fam_grp); - break; - } + if (f->mcast_unbind) + f->mcast_unbind(net, fam_grp); + break; } } up_read(&cb_lock); @@ -1087,10 +1030,7 @@ static struct pernet_operations genl_pernet_ops = { static int __init genl_init(void) { - int i, err; - - for (i = 0; i < GENL_FAM_TAB_SIZE; i++) - INIT_LIST_HEAD(&family_ht[i]); + int err; err = genl_register_family(&genl_ctrl); if (err < 0) @@ -1118,7 +1058,7 @@ subsys_initcall(genl_init); * You cannot use this function with a family that has parallel_ops * and you can only use it within (pre/post) doit/dumpit callbacks. */ -struct nlattr **genl_family_attrbuf(struct genl_family *family) +struct nlattr **genl_family_attrbuf(const struct genl_family *family) { if (!WARN_ON(family->parallel_ops)) lockdep_assert_held(&genl_mutex); @@ -1156,8 +1096,9 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, return err; } -int genlmsg_multicast_allns(struct genl_family *family, struct sk_buff *skb, - u32 portid, unsigned int group, gfp_t flags) +int genlmsg_multicast_allns(const struct genl_family *family, + struct sk_buff *skb, u32 portid, + unsigned int group, gfp_t flags) { if (WARN_ON_ONCE(group >= family->n_mcgrps)) return -EINVAL; @@ -1166,7 +1107,7 @@ int genlmsg_multicast_allns(struct genl_family *family, struct sk_buff *skb, } EXPORT_SYMBOL(genlmsg_multicast_allns); -void genl_notify(struct genl_family *family, struct sk_buff *skb, +void genl_notify(const struct genl_family *family, struct sk_buff *skb, struct genl_info *info, u32 group, gfp_t flags) { struct net *net = genl_info_net(info); -- cgit v1.1 From 56989f6d8568c21257dcec0f5e644d5570ba3281 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Oct 2016 14:40:05 +0200 Subject: genetlink: mark families as __ro_after_init Now genl_register_family() is the only thing (other than the users themselves, perhaps, but I didn't find any doing that) writing to the family struct. In all families that I found, genl_register_family() is only called from __init functions (some indirectly, in which case I've add __init annotations to clarifly things), so all can actually be marked __ro_after_init. This protects the data structure from accidental corruption. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/acpi/event.c | 4 ++-- drivers/net/gtp.c | 2 +- drivers/net/macsec.c | 2 +- drivers/net/team/team.c | 4 ++-- drivers/net/wireless/mac80211_hwsim.c | 4 ++-- drivers/scsi/pmcraid.c | 4 ++-- drivers/target/target_core_user.c | 2 +- drivers/thermal/thermal_core.c | 4 ++-- fs/dlm/netlink.c | 2 +- fs/quota/netlink.c | 2 +- include/linux/genl_magic_func.h | 2 +- kernel/taskstats.c | 2 +- net/batman-adv/netlink.c | 2 +- net/core/devlink.c | 2 +- net/core/drop_monitor.c | 2 +- net/hsr/hsr_netlink.c | 2 +- net/ieee802154/netlink.c | 2 +- net/ieee802154/nl802154.c | 4 ++-- net/ipv4/fou.c | 2 +- net/ipv4/tcp_metrics.c | 2 +- net/ipv6/ila/ila_xlat.c | 4 ++-- net/irda/irnetlink.c | 4 ++-- net/l2tp/l2tp_netlink.c | 4 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 2 +- net/netlabel/netlabel_calipso.c | 2 +- net/netlabel/netlabel_cipso_v4.c | 2 +- net/netlabel/netlabel_mgmt.c | 2 +- net/netlabel/netlabel_unlabeled.c | 2 +- net/netlink/genetlink.c | 2 +- net/nfc/netlink.c | 2 +- net/openvswitch/datapath.c | 10 +++++----- net/tipc/netlink.c | 4 ++-- net/tipc/netlink_compat.c | 4 ++-- net/wimax/stack.c | 2 +- net/wireless/nl80211.c | 4 ++-- 35 files changed, 51 insertions(+), 51 deletions(-) diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index 1ab12ad..7fceb3b 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -82,7 +82,7 @@ static const struct genl_multicast_group acpi_event_mcgrps[] = { { .name = ACPI_GENL_MCAST_GROUP_NAME, }, }; -static struct genl_family acpi_event_genl_family = { +static struct genl_family acpi_event_genl_family __ro_after_init = { .module = THIS_MODULE, .name = ACPI_GENL_FAMILY_NAME, .version = ACPI_GENL_VERSION, @@ -144,7 +144,7 @@ int acpi_bus_generate_netlink_event(const char *device_class, EXPORT_SYMBOL(acpi_bus_generate_netlink_event); -static int acpi_event_genetlink_init(void) +static int __init acpi_event_genetlink_init(void) { return genl_register_family(&acpi_event_genl_family); } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 0604fd7..719d19f 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1290,7 +1290,7 @@ static const struct genl_ops gtp_genl_ops[] = { }, }; -static struct genl_family gtp_genl_family = { +static struct genl_family gtp_genl_family __ro_after_init = { .name = "gtp", .version = 0, .hdrsize = 0, diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 63ca7a3..0a715ab 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -2648,7 +2648,7 @@ static const struct genl_ops macsec_genl_ops[] = { }, }; -static struct genl_family macsec_fam = { +static struct genl_family macsec_fam __ro_after_init = { .name = MACSEC_GENL_NAME, .hdrsize = 0, .version = MACSEC_GENL_VERSION, diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 46bf7c1..bdc5856 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2740,7 +2740,7 @@ static const struct genl_multicast_group team_nl_mcgrps[] = { { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, }, }; -static struct genl_family team_nl_family = { +static struct genl_family team_nl_family __ro_after_init = { .name = TEAM_GENL_NAME, .version = TEAM_GENL_VERSION, .maxattr = TEAM_ATTR_MAX, @@ -2773,7 +2773,7 @@ static int team_nl_send_event_port_get(struct team *team, port); } -static int team_nl_init(void) +static int __init team_nl_init(void) { return genl_register_family(&team_nl_family); } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 5d4637e..220e9dc 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -3228,7 +3228,7 @@ static const struct genl_ops hwsim_ops[] = { }, }; -static struct genl_family hwsim_genl_family = { +static struct genl_family hwsim_genl_family __ro_after_init = { .name = "MAC80211_HWSIM", .version = 1, .maxattr = HWSIM_ATTR_MAX, @@ -3287,7 +3287,7 @@ static struct notifier_block hwsim_netlink_notifier = { .notifier_call = mac80211_hwsim_netlink_notify, }; -static int hwsim_init_netlink(void) +static int __init hwsim_init_netlink(void) { int rc; diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index c0ab7bb..845affa 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -1368,7 +1368,7 @@ static struct genl_multicast_group pmcraid_mcgrps[] = { { .name = "events", /* not really used - see ID discussion below */ }, }; -static struct genl_family pmcraid_event_family = { +static struct genl_family pmcraid_event_family __ro_after_init = { .module = THIS_MODULE, .name = "pmcraid", .version = 1, @@ -1384,7 +1384,7 @@ static struct genl_family pmcraid_event_family = { * 0 if the pmcraid_event_family is successfully registered * with netlink generic, non-zero otherwise */ -static int pmcraid_netlink_init(void) +static int __init pmcraid_netlink_init(void) { int result; diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 3483372..0f173bf 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -147,7 +147,7 @@ static const struct genl_multicast_group tcmu_mcgrps[] = { }; /* Our generic netlink family */ -static struct genl_family tcmu_genl_family = { +static struct genl_family tcmu_genl_family __ro_after_init = { .module = THIS_MODULE, .hdrsize = 0, .name = "TCM-USER", diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 93b6caa..911fd96 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -2163,7 +2163,7 @@ static const struct genl_multicast_group thermal_event_mcgrps[] = { { .name = THERMAL_GENL_MCAST_GROUP_NAME, }, }; -static struct genl_family thermal_event_genl_family = { +static struct genl_family thermal_event_genl_family __ro_after_init = { .module = THIS_MODULE, .name = THERMAL_GENL_FAMILY_NAME, .version = THERMAL_GENL_VERSION, @@ -2235,7 +2235,7 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_generate_netlink_event); -static int genetlink_init(void) +static int __init genetlink_init(void) { return genl_register_family(&thermal_event_genl_family); } diff --git a/fs/dlm/netlink.c b/fs/dlm/netlink.c index 04042d6..0643ae4 100644 --- a/fs/dlm/netlink.c +++ b/fs/dlm/netlink.c @@ -72,7 +72,7 @@ static struct genl_ops dlm_nl_ops[] = { }, }; -static struct genl_family family = { +static struct genl_family family __ro_after_init = { .name = DLM_GENL_NAME, .version = DLM_GENL_VERSION, .ops = dlm_nl_ops, diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c index 9457c7b..e99b1a7 100644 --- a/fs/quota/netlink.c +++ b/fs/quota/netlink.c @@ -12,7 +12,7 @@ static const struct genl_multicast_group quota_mcgrps[] = { }; /* Netlink family structure for quota */ -static struct genl_family quota_genl_family = { +static struct genl_family quota_genl_family __ro_after_init = { .module = THIS_MODULE, .hdrsize = 0, .name = "VFS_DQUOT", diff --git a/include/linux/genl_magic_func.h b/include/linux/genl_magic_func.h index 40c2e39..377257d 100644 --- a/include/linux/genl_magic_func.h +++ b/include/linux/genl_magic_func.h @@ -293,7 +293,7 @@ static int CONCAT_(GENL_MAGIC_FAMILY, _genl_multicast_ ## group)( \ #undef GENL_mc_group #define GENL_mc_group(group) -static struct genl_family ZZZ_genl_family __read_mostly = { +static struct genl_family ZZZ_genl_family __ro_after_init = { .name = __stringify(GENL_MAGIC_FAMILY), .version = GENL_MAGIC_VERSION, #ifdef GENL_MAGIC_FAMILY_HDRSZ diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 4075ece..9b7f838 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -646,7 +646,7 @@ static const struct genl_ops taskstats_ops[] = { }, }; -static struct genl_family family = { +static struct genl_family family __ro_after_init = { .name = TASKSTATS_GENL_NAME, .version = TASKSTATS_GENL_VERSION, .maxattr = TASKSTATS_CMD_ATTR_MAX, diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index e28cec3..005012b 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -603,7 +603,7 @@ static struct genl_ops batadv_netlink_ops[] = { }; -struct genl_family batadv_netlink_family = { +struct genl_family batadv_netlink_family __ro_after_init = { .hdrsize = 0, .name = BATADV_NL_NAME, .version = 1, diff --git a/net/core/devlink.c b/net/core/devlink.c index 063da80..c14f8b6 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1612,7 +1612,7 @@ static const struct genl_ops devlink_nl_ops[] = { }, }; -static struct genl_family devlink_nl_family = { +static struct genl_family devlink_nl_family __ro_after_init = { .name = DEVLINK_GENL_NAME, .version = DEVLINK_GENL_VERSION, .maxattr = DEVLINK_ATTR_MAX, diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 80c0027..8e0c063 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -346,7 +346,7 @@ static const struct genl_ops dropmon_ops[] = { }, }; -static struct genl_family net_drop_monitor_family = { +static struct genl_family net_drop_monitor_family __ro_after_init = { .hdrsize = 0, .name = "NET_DM", .version = 2, diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index aab34c7..1ab30e7 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -461,7 +461,7 @@ static const struct genl_ops hsr_ops[] = { }, }; -static struct genl_family hsr_genl_family = { +static struct genl_family hsr_genl_family __ro_after_init = { .hdrsize = 0, .name = "HSR", .version = 1, diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 08e6247..6bde9e5 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -131,7 +131,7 @@ static const struct genl_multicast_group ieee802154_mcgrps[] = { [IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, }, }; -struct genl_family nl802154_family = { +struct genl_family nl802154_family __ro_after_init = { .hdrsize = 0, .name = IEEE802154_NL_NAME, .version = 1, diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index f7e7557..fc60cd0 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -2462,7 +2462,7 @@ static const struct genl_ops nl802154_ops[] = { #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ }; -static struct genl_family nl802154_fam = { +static struct genl_family nl802154_fam __ro_after_init = { .name = NL802154_GENL_NAME, /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ @@ -2478,7 +2478,7 @@ static struct genl_family nl802154_fam = { }; /* initialisation/exit functions */ -int nl802154_init(void) +int __init nl802154_init(void) { return genl_register_family(&nl802154_fam); } diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index 5b5226a..6cb57bb 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -824,7 +824,7 @@ static const struct genl_ops fou_nl_ops[] = { }, }; -static struct genl_family fou_nl_family = { +static struct genl_family fou_nl_family __ro_after_init = { .hdrsize = 0, .name = FOU_GENL_NAME, .version = FOU_GENL_VERSION, diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index bba3c72..d46f4d5 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -1109,7 +1109,7 @@ static const struct genl_ops tcp_metrics_nl_ops[] = { }, }; -static struct genl_family tcp_metrics_nl_family = { +static struct genl_family tcp_metrics_nl_family __ro_after_init = { .hdrsize = 0, .name = TCP_METRICS_GENL_NAME, .version = TCP_METRICS_GENL_VERSION, diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 97f7b0c..628ae6d 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -553,7 +553,7 @@ static const struct genl_ops ila_nl_ops[] = { }, }; -static struct genl_family ila_nl_family = { +static struct genl_family ila_nl_family __ro_after_init = { .hdrsize = 0, .name = ILA_GENL_NAME, .version = ILA_GENL_VERSION, @@ -627,7 +627,7 @@ static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral) return 0; } -int ila_xlat_init(void) +int __init ila_xlat_init(void) { int ret; diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c index 0787734..7fc340e 100644 --- a/net/irda/irnetlink.c +++ b/net/irda/irnetlink.c @@ -141,7 +141,7 @@ static const struct genl_ops irda_nl_ops[] = { }; -static struct genl_family irda_nl_family = { +static struct genl_family irda_nl_family __ro_after_init = { .name = IRDA_NL_NAME, .hdrsize = 0, .version = IRDA_NL_VERSION, @@ -151,7 +151,7 @@ static struct genl_family irda_nl_family = { .n_ops = ARRAY_SIZE(irda_nl_ops), }; -int irda_nl_register(void) +int __init irda_nl_register(void) { return genl_register_family(&irda_nl_family); } diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index e4e8c07..59aa2d2 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -970,7 +970,7 @@ static const struct genl_ops l2tp_nl_ops[] = { }, }; -static struct genl_family l2tp_nl_family = { +static struct genl_family l2tp_nl_family __ro_after_init = { .name = L2TP_GENL_NAME, .version = L2TP_GENL_VERSION, .hdrsize = 0, @@ -1016,7 +1016,7 @@ void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type) } EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); -static int l2tp_nl_init(void) +static int __init l2tp_nl_init(void) { pr_info("L2TP netlink interface\n"); return genl_register_family(&l2tp_nl_family); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index ea3e8ae..6b85ded 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3865,7 +3865,7 @@ static const struct genl_ops ip_vs_genl_ops[] = { }, }; -static struct genl_family ip_vs_genl_family = { +static struct genl_family ip_vs_genl_family __ro_after_init = { .hdrsize = 0, .name = IPVS_GENL_NAME, .version = IPVS_GENL_VERSION, diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index ca7c9c4..d177dd0 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -349,7 +349,7 @@ static const struct genl_ops netlbl_calipso_ops[] = { }, }; -static struct genl_family netlbl_calipso_gnl_family = { +static struct genl_family netlbl_calipso_gnl_family __ro_after_init = { .hdrsize = 0, .name = NETLBL_NLTYPE_CALIPSO_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index a665eae..4149d3e 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -760,7 +760,7 @@ static const struct genl_ops netlbl_cipsov4_ops[] = { }, }; -static struct genl_family netlbl_cipsov4_gnl_family = { +static struct genl_family netlbl_cipsov4_gnl_family __ro_after_init = { .hdrsize = 0, .name = NETLBL_NLTYPE_CIPSOV4_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index ecfe8eb..21e0095 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -828,7 +828,7 @@ static const struct genl_ops netlbl_mgmt_genl_ops[] = { }, }; -static struct genl_family netlbl_mgmt_gnl_family = { +static struct genl_family netlbl_mgmt_gnl_family __ro_after_init = { .hdrsize = 0, .name = NETLBL_NLTYPE_MGMT_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 5dbbad4..22dc1b9 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1372,7 +1372,7 @@ static const struct genl_ops netlbl_unlabel_genl_ops[] = { }, }; -static struct genl_family netlbl_unlabel_gnl_family = { +static struct genl_family netlbl_unlabel_gnl_family __ro_after_init = { .hdrsize = 0, .name = NETLBL_NLTYPE_UNLABELED_NAME, .version = NETLBL_PROTO_VERSION, diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 8565992..df0cbcd 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -936,7 +936,7 @@ static const struct genl_multicast_group genl_ctrl_groups[] = { { .name = "notify", }, }; -static struct genl_family genl_ctrl = { +static struct genl_family genl_ctrl __ro_after_init = { .module = THIS_MODULE, .ops = genl_ctrl_ops, .n_ops = ARRAY_SIZE(genl_ctrl_ops), diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 450b1e51..03f3d5c 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1746,7 +1746,7 @@ static const struct genl_ops nfc_genl_ops[] = { }, }; -static struct genl_family nfc_genl_family = { +static struct genl_family nfc_genl_family __ro_after_init = { .hdrsize = 0, .name = NFC_GENL_NAME, .version = NFC_GENL_VERSION, diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index ad6a111a..fa87601 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -670,7 +670,7 @@ static const struct genl_ops dp_packet_genl_ops[] = { } }; -static struct genl_family dp_packet_genl_family = { +static struct genl_family dp_packet_genl_family __ro_after_init = { .hdrsize = sizeof(struct ovs_header), .name = OVS_PACKET_FAMILY, .version = OVS_PACKET_VERSION, @@ -1435,7 +1435,7 @@ static const struct genl_ops dp_flow_genl_ops[] = { }, }; -static struct genl_family dp_flow_genl_family = { +static struct genl_family dp_flow_genl_family __ro_after_init = { .hdrsize = sizeof(struct ovs_header), .name = OVS_FLOW_FAMILY, .version = OVS_FLOW_VERSION, @@ -1821,7 +1821,7 @@ static const struct genl_ops dp_datapath_genl_ops[] = { }, }; -static struct genl_family dp_datapath_genl_family = { +static struct genl_family dp_datapath_genl_family __ro_after_init = { .hdrsize = sizeof(struct ovs_header), .name = OVS_DATAPATH_FAMILY, .version = OVS_DATAPATH_VERSION, @@ -2243,7 +2243,7 @@ static const struct genl_ops dp_vport_genl_ops[] = { }, }; -struct genl_family dp_vport_genl_family = { +struct genl_family dp_vport_genl_family __ro_after_init = { .hdrsize = sizeof(struct ovs_header), .name = OVS_VPORT_FAMILY, .version = OVS_VPORT_VERSION, @@ -2272,7 +2272,7 @@ static void dp_unregister_genl(int n_families) genl_unregister_family(dp_genl_families[i]); } -static int dp_register_genl(void) +static int __init dp_register_genl(void) { int err; int i; diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 74a405b..26ca8dd 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -249,7 +249,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { #endif }; -struct genl_family tipc_genl_family = { +struct genl_family tipc_genl_family __ro_after_init = { .name = TIPC_GENL_V2_NAME, .version = TIPC_GENL_V2_VERSION, .hdrsize = 0, @@ -271,7 +271,7 @@ int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr) return nlmsg_parse(nlh, GENL_HDRLEN, *attr, maxattr, tipc_nl_policy); } -int tipc_netlink_start(void) +int __init tipc_netlink_start(void) { int res; diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 07b1993..e1ae8a8 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -1222,7 +1222,7 @@ static struct genl_ops tipc_genl_compat_ops[] = { }, }; -static struct genl_family tipc_genl_compat_family = { +static struct genl_family tipc_genl_compat_family __ro_after_init = { .name = TIPC_GENL_NAME, .version = TIPC_GENL_VERSION, .hdrsize = TIPC_GENL_HDRLEN, @@ -1233,7 +1233,7 @@ static struct genl_family tipc_genl_compat_family = { .n_ops = ARRAY_SIZE(tipc_genl_compat_ops), }; -int tipc_netlink_compat_start(void) +int __init tipc_netlink_compat_start(void) { int res; diff --git a/net/wimax/stack.c b/net/wimax/stack.c index 587e162..5db7315 100644 --- a/net/wimax/stack.c +++ b/net/wimax/stack.c @@ -576,7 +576,7 @@ static const struct genl_multicast_group wimax_gnl_mcgrps[] = { { .name = "msg", }, }; -struct genl_family wimax_gnl_family = { +struct genl_family wimax_gnl_family __ro_after_init = { .name = "WiMAX", .version = WIMAX_GNL_VERSION, .hdrsize = 0, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8e5ca3c..271707d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -12586,7 +12586,7 @@ static const struct genl_ops nl80211_ops[] = { }, }; -static struct genl_family nl80211_fam = { +static struct genl_family nl80211_fam __ro_after_init = { .name = NL80211_GENL_NAME, /* have users key off the name instead */ .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ @@ -14563,7 +14563,7 @@ void nl80211_send_ap_stopped(struct wireless_dev *wdev) /* initialisation/exit functions */ -int nl80211_init(void) +int __init nl80211_init(void) { int err; -- cgit v1.1 From 110447f8269a608cd69203fa2ccd801ea92e0a1a Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 24 Oct 2016 14:42:26 +0200 Subject: ethernet: fix min/max MTU typos Fixes: d894be57ca92('ethernet: use net core MTU range checking in more drivers') CC: Jarod Wilson CC: Thomas Falcon Signed-off-by: Stefan Richter Acked-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/sb1250-mac.c | 2 +- drivers/net/ethernet/ibm/ibmveth.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index cb312e4..435a2e4 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2219,7 +2219,7 @@ static int sbmac_init(struct platform_device *pldev, long long base) dev->netdev_ops = &sbmac_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - dev->max_mtu = 0; + dev->min_mtu = 0; dev->max_mtu = ENET_PACKET_SIZE; netif_napi_add(dev, &sc->napi, sbmac_poll, 16); diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 29c05d0..4a81c89 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1549,7 +1549,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) } netdev->min_mtu = IBMVETH_MIN_MTU; - netdev->min_mtu = ETH_MAX_MTU; + netdev->max_mtu = ETH_MAX_MTU; memcpy(netdev->dev_addr, mac_addr_p, ETH_ALEN); -- cgit v1.1 From 002d8a1a6c11b9b2a8ac615095589111dd52749b Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 24 Oct 2016 19:09:53 -0700 Subject: net: skip genenerating uevents for network namespaces that are exiting No one can see these events, because a network namespace can not be destroyed, if it has sockets. Unlike other devices, uevent-s for network devices are generated only inside their network namespaces. They are filtered in kobj_bcast_filter() My experiments shows that net namespaces are destroyed more 30% faster with this optimization. Here is a perf output for destroying network namespaces without this patch. - 94.76% 0.02% kworker/u48:1 [kernel.kallsyms] [k] cleanup_net - 94.74% cleanup_net - 94.64% ops_exit_list.isra.4 - 41.61% default_device_exit_batch - 41.47% unregister_netdevice_many - rollback_registered_many - 40.36% netdev_unregister_kobject - 14.55% device_del + 13.71% kobject_uevent - 13.04% netdev_queue_update_kobjects + 12.96% kobject_put - 12.72% net_rx_queue_update_kobjects kobject_put - kobject_release + 12.69% kobject_uevent + 0.80% call_netdevice_notifiers_info + 19.57% nfsd_exit_net + 11.15% tcp_net_metrics_exit + 8.25% rpcsec_gss_exit_net It's very critical to optimize the exit path for network namespaces, because they are destroyed under net_mutex and many namespaces can be destroyed for one iteration. v2: use dev_set_uevent_suppress() Cc: Cong Wang Cc: "David S. Miller" Cc: Eric W. Biederman Signed-off-by: Andrei Vagin Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 6e4f347..d4fe286 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -950,10 +950,13 @@ net_rx_queue_update_kobjects(struct net_device *dev, int old_num, int new_num) } while (--i >= new_num) { + struct kobject *kobj = &dev->_rx[i].kobj; + + if (!list_empty(&dev_net(dev)->exit_list)) + kobj->uevent_suppress = 1; if (dev->sysfs_rx_queue_group) - sysfs_remove_group(&dev->_rx[i].kobj, - dev->sysfs_rx_queue_group); - kobject_put(&dev->_rx[i].kobj); + sysfs_remove_group(kobj, dev->sysfs_rx_queue_group); + kobject_put(kobj); } return error; @@ -1340,6 +1343,8 @@ netdev_queue_update_kobjects(struct net_device *dev, int old_num, int new_num) while (--i >= new_num) { struct netdev_queue *queue = dev->_tx + i; + if (!list_empty(&dev_net(dev)->exit_list)) + queue->kobj.uevent_suppress = 1; #ifdef CONFIG_BQL sysfs_remove_group(&queue->kobj, &dql_group); #endif @@ -1525,6 +1530,9 @@ void netdev_unregister_kobject(struct net_device *ndev) { struct device *dev = &(ndev->dev); + if (!list_empty(&dev_net(ndev)->exit_list)) + dev_set_uevent_suppress(dev, 1); + kobject_get(&dev->kobj); remove_queue_kobjects(ndev); -- cgit v1.1 From 514877182b537372352c14a0a50822572f66e831 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 28 Oct 2016 12:25:53 +0200 Subject: mac80211: fils_aead: fix encrypt error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc -Wmaybe-uninitialized reports a bug in aes_siv_encryp: net/mac80211/fils_aead.c: In function ‘aes_siv_encrypt.constprop’: net/mac80211/fils_aead.c:84:26: error: ‘tfm2’ may be used uninitialized in this function [-Werror=maybe-uninitialized] At the time that the memory allocation fails, 'tfm2' has not been allocated, so we should not attempt to free it later, and we can simply return an error. Fixes: 39404feee691 ("mac80211: FILS AEAD protection for station mode association frames") Signed-off-by: Arnd Bergmann Signed-off-by: Johannes Berg --- net/mac80211/fils_aead.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/mac80211/fils_aead.c b/net/mac80211/fils_aead.c index b81b4f24..ecfdd97 100644 --- a/net/mac80211/fils_aead.c +++ b/net/mac80211/fils_aead.c @@ -110,10 +110,8 @@ static int aes_siv_encrypt(const u8 *key, size_t key_len, * overwriting this during AES-CTR. */ tmp = kmemdup(plain, plain_len, GFP_KERNEL); - if (!tmp) { - res = -ENOMEM; - goto fail; - } + if (!tmp) + return -ENOMEM; /* IV for CTR before encrypted data */ memcpy(out, v, AES_BLOCK_SIZE); -- cgit v1.1 From 8c9583a81cd3fb51eff26a1459d00ffa5fdd4dbd Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 27 Oct 2016 15:12:57 +0200 Subject: mlxsw: spectrum: Remove extra whitespace Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 113f667..107934f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1980,7 +1980,7 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) if (err) goto err_vrs_init; - err = mlxsw_sp_neigh_init(mlxsw_sp); + err = mlxsw_sp_neigh_init(mlxsw_sp); if (err) goto err_neigh_init; -- cgit v1.1 From 62e86f9e828128ce6452a2d8ab4a26609885dda6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Oct 2016 15:12:58 +0200 Subject: mlxsw: pci: Rename header with HW definitions pci.h needs to be used for inner function declarations. So move the original one to more appropriate name, pci_hw.h. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/pci.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/pci.h | 231 --------------------------- drivers/net/ethernet/mellanox/mlxsw/pci_hw.h | 231 +++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 232 deletions(-) delete mode 100644 drivers/net/ethernet/mellanox/mlxsw/pci.h create mode 100644 drivers/net/ethernet/mellanox/mlxsw/pci_hw.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index c88f2ab..209630c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -48,7 +48,7 @@ #include #include -#include "pci.h" +#include "pci_hw.h" #include "core.h" #include "cmd.h" #include "port.h" diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h deleted file mode 100644 index d942a3e..0000000 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * drivers/net/ethernet/mellanox/mlxsw/pci.h - * Copyright (c) 2015 Mellanox Technologies. All rights reserved. - * Copyright (c) 2015 Jiri Pirko - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2 as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _MLXSW_PCI_H -#define _MLXSW_PCI_H - -#include - -#include "item.h" - -#define PCI_DEVICE_ID_MELLANOX_SWITCHX2 0xc738 -#define PCI_DEVICE_ID_MELLANOX_SPECTRUM 0xcb84 -#define MLXSW_PCI_BAR0_SIZE (1024 * 1024) /* 1MB */ -#define MLXSW_PCI_PAGE_SIZE 4096 - -#define MLXSW_PCI_CIR_BASE 0x71000 -#define MLXSW_PCI_CIR_IN_PARAM_HI MLXSW_PCI_CIR_BASE -#define MLXSW_PCI_CIR_IN_PARAM_LO (MLXSW_PCI_CIR_BASE + 0x04) -#define MLXSW_PCI_CIR_IN_MODIFIER (MLXSW_PCI_CIR_BASE + 0x08) -#define MLXSW_PCI_CIR_OUT_PARAM_HI (MLXSW_PCI_CIR_BASE + 0x0C) -#define MLXSW_PCI_CIR_OUT_PARAM_LO (MLXSW_PCI_CIR_BASE + 0x10) -#define MLXSW_PCI_CIR_TOKEN (MLXSW_PCI_CIR_BASE + 0x14) -#define MLXSW_PCI_CIR_CTRL (MLXSW_PCI_CIR_BASE + 0x18) -#define MLXSW_PCI_CIR_CTRL_GO_BIT BIT(23) -#define MLXSW_PCI_CIR_CTRL_EVREQ_BIT BIT(22) -#define MLXSW_PCI_CIR_CTRL_OPCODE_MOD_SHIFT 12 -#define MLXSW_PCI_CIR_CTRL_STATUS_SHIFT 24 -#define MLXSW_PCI_CIR_TIMEOUT_MSECS 1000 - -#define MLXSW_PCI_SW_RESET 0xF0010 -#define MLXSW_PCI_SW_RESET_RST_BIT BIT(0) -#define MLXSW_PCI_SW_RESET_TIMEOUT_MSECS 5000 -#define MLXSW_PCI_FW_READY 0xA1844 -#define MLXSW_PCI_FW_READY_MASK 0xFF -#define MLXSW_PCI_FW_READY_MAGIC 0x5E - -#define MLXSW_PCI_DOORBELL_SDQ_OFFSET 0x000 -#define MLXSW_PCI_DOORBELL_RDQ_OFFSET 0x200 -#define MLXSW_PCI_DOORBELL_CQ_OFFSET 0x400 -#define MLXSW_PCI_DOORBELL_EQ_OFFSET 0x600 -#define MLXSW_PCI_DOORBELL_ARM_CQ_OFFSET 0x800 -#define MLXSW_PCI_DOORBELL_ARM_EQ_OFFSET 0xA00 - -#define MLXSW_PCI_DOORBELL(offset, type_offset, num) \ - ((offset) + (type_offset) + (num) * 4) - -#define MLXSW_PCI_CQS_MAX 96 -#define MLXSW_PCI_EQS_COUNT 2 -#define MLXSW_PCI_EQ_ASYNC_NUM 0 -#define MLXSW_PCI_EQ_COMP_NUM 1 - -#define MLXSW_PCI_AQ_PAGES 8 -#define MLXSW_PCI_AQ_SIZE (MLXSW_PCI_PAGE_SIZE * MLXSW_PCI_AQ_PAGES) -#define MLXSW_PCI_WQE_SIZE 32 /* 32 bytes per element */ -#define MLXSW_PCI_CQE_SIZE 16 /* 16 bytes per element */ -#define MLXSW_PCI_EQE_SIZE 16 /* 16 bytes per element */ -#define MLXSW_PCI_WQE_COUNT (MLXSW_PCI_AQ_SIZE / MLXSW_PCI_WQE_SIZE) -#define MLXSW_PCI_CQE_COUNT (MLXSW_PCI_AQ_SIZE / MLXSW_PCI_CQE_SIZE) -#define MLXSW_PCI_EQE_COUNT (MLXSW_PCI_AQ_SIZE / MLXSW_PCI_EQE_SIZE) -#define MLXSW_PCI_EQE_UPDATE_COUNT 0x80 - -#define MLXSW_PCI_WQE_SG_ENTRIES 3 -#define MLXSW_PCI_WQE_TYPE_ETHERNET 0xA - -/* pci_wqe_c - * If set it indicates that a completion should be reported upon - * execution of this descriptor. - */ -MLXSW_ITEM32(pci, wqe, c, 0x00, 31, 1); - -/* pci_wqe_lp - * Local Processing, set if packet should be processed by the local - * switch hardware: - * For Ethernet EMAD (Direct Route and non Direct Route) - - * must be set if packet destination is local device - * For InfiniBand CTL - must be set if packet destination is local device - * Otherwise it must be clear - * Local Process packets must not exceed the size of 2K (including payload - * and headers). - */ -MLXSW_ITEM32(pci, wqe, lp, 0x00, 30, 1); - -/* pci_wqe_type - * Packet type. - */ -MLXSW_ITEM32(pci, wqe, type, 0x00, 23, 4); - -/* pci_wqe_byte_count - * Size of i-th scatter/gather entry, 0 if entry is unused. - */ -MLXSW_ITEM16_INDEXED(pci, wqe, byte_count, 0x02, 0, 14, 0x02, 0x00, false); - -/* pci_wqe_address - * Physical address of i-th scatter/gather entry. - * Gather Entries must be 2Byte aligned. - */ -MLXSW_ITEM64_INDEXED(pci, wqe, address, 0x08, 0, 64, 0x8, 0x0, false); - -/* pci_cqe_lag - * Packet arrives from a port which is a LAG - */ -MLXSW_ITEM32(pci, cqe, lag, 0x00, 23, 1); - -/* pci_cqe_system_port/lag_id - * When lag=0: System port on which the packet was received - * When lag=1: - * bits [15:4] LAG ID on which the packet was received - * bits [3:0] sub_port on which the packet was received - */ -MLXSW_ITEM32(pci, cqe, system_port, 0x00, 0, 16); -MLXSW_ITEM32(pci, cqe, lag_id, 0x00, 4, 12); -MLXSW_ITEM32(pci, cqe, lag_port_index, 0x00, 0, 4); - -/* pci_cqe_wqe_counter - * WQE count of the WQEs completed on the associated dqn - */ -MLXSW_ITEM32(pci, cqe, wqe_counter, 0x04, 16, 16); - -/* pci_cqe_byte_count - * Byte count of received packets including additional two - * Reserved Bytes that are append to the end of the frame. - * Reserved for Send CQE. - */ -MLXSW_ITEM32(pci, cqe, byte_count, 0x04, 0, 14); - -/* pci_cqe_trap_id - * Trap ID that captured the packet. - */ -MLXSW_ITEM32(pci, cqe, trap_id, 0x08, 0, 8); - -/* pci_cqe_crc - * Length include CRC. Indicates the length field includes - * the packet's CRC. - */ -MLXSW_ITEM32(pci, cqe, crc, 0x0C, 8, 1); - -/* pci_cqe_e - * CQE with Error. - */ -MLXSW_ITEM32(pci, cqe, e, 0x0C, 7, 1); - -/* pci_cqe_sr - * 1 - Send Queue - * 0 - Receive Queue - */ -MLXSW_ITEM32(pci, cqe, sr, 0x0C, 6, 1); - -/* pci_cqe_dqn - * Descriptor Queue (DQ) Number. - */ -MLXSW_ITEM32(pci, cqe, dqn, 0x0C, 1, 5); - -/* pci_cqe_owner - * Ownership bit. - */ -MLXSW_ITEM32(pci, cqe, owner, 0x0C, 0, 1); - -/* pci_eqe_event_type - * Event type. - */ -MLXSW_ITEM32(pci, eqe, event_type, 0x0C, 24, 8); -#define MLXSW_PCI_EQE_EVENT_TYPE_COMP 0x00 -#define MLXSW_PCI_EQE_EVENT_TYPE_CMD 0x0A - -/* pci_eqe_event_sub_type - * Event type. - */ -MLXSW_ITEM32(pci, eqe, event_sub_type, 0x0C, 16, 8); - -/* pci_eqe_cqn - * Completion Queue that triggeret this EQE. - */ -MLXSW_ITEM32(pci, eqe, cqn, 0x0C, 8, 7); - -/* pci_eqe_owner - * Ownership bit. - */ -MLXSW_ITEM32(pci, eqe, owner, 0x0C, 0, 1); - -/* pci_eqe_cmd_token - * Command completion event - token - */ -MLXSW_ITEM32(pci, eqe, cmd_token, 0x08, 16, 16); - -/* pci_eqe_cmd_status - * Command completion event - status - */ -MLXSW_ITEM32(pci, eqe, cmd_status, 0x08, 0, 8); - -/* pci_eqe_cmd_out_param_h - * Command completion event - output parameter - higher part - */ -MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x0C, 0, 32); - -/* pci_eqe_cmd_out_param_l - * Command completion event - output parameter - lower part - */ -MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x10, 0, 32); - -#endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h new file mode 100644 index 0000000..45bc1979 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h @@ -0,0 +1,231 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/pci_hw.h + * Copyright (c) 2015-2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015-2016 Jiri Pirko + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MLXSW_PCI_HW_H +#define _MLXSW_PCI_HW_H + +#include + +#include "item.h" + +#define PCI_DEVICE_ID_MELLANOX_SWITCHX2 0xc738 +#define PCI_DEVICE_ID_MELLANOX_SPECTRUM 0xcb84 +#define MLXSW_PCI_BAR0_SIZE (1024 * 1024) /* 1MB */ +#define MLXSW_PCI_PAGE_SIZE 4096 + +#define MLXSW_PCI_CIR_BASE 0x71000 +#define MLXSW_PCI_CIR_IN_PARAM_HI MLXSW_PCI_CIR_BASE +#define MLXSW_PCI_CIR_IN_PARAM_LO (MLXSW_PCI_CIR_BASE + 0x04) +#define MLXSW_PCI_CIR_IN_MODIFIER (MLXSW_PCI_CIR_BASE + 0x08) +#define MLXSW_PCI_CIR_OUT_PARAM_HI (MLXSW_PCI_CIR_BASE + 0x0C) +#define MLXSW_PCI_CIR_OUT_PARAM_LO (MLXSW_PCI_CIR_BASE + 0x10) +#define MLXSW_PCI_CIR_TOKEN (MLXSW_PCI_CIR_BASE + 0x14) +#define MLXSW_PCI_CIR_CTRL (MLXSW_PCI_CIR_BASE + 0x18) +#define MLXSW_PCI_CIR_CTRL_GO_BIT BIT(23) +#define MLXSW_PCI_CIR_CTRL_EVREQ_BIT BIT(22) +#define MLXSW_PCI_CIR_CTRL_OPCODE_MOD_SHIFT 12 +#define MLXSW_PCI_CIR_CTRL_STATUS_SHIFT 24 +#define MLXSW_PCI_CIR_TIMEOUT_MSECS 1000 + +#define MLXSW_PCI_SW_RESET 0xF0010 +#define MLXSW_PCI_SW_RESET_RST_BIT BIT(0) +#define MLXSW_PCI_SW_RESET_TIMEOUT_MSECS 5000 +#define MLXSW_PCI_FW_READY 0xA1844 +#define MLXSW_PCI_FW_READY_MASK 0xFF +#define MLXSW_PCI_FW_READY_MAGIC 0x5E + +#define MLXSW_PCI_DOORBELL_SDQ_OFFSET 0x000 +#define MLXSW_PCI_DOORBELL_RDQ_OFFSET 0x200 +#define MLXSW_PCI_DOORBELL_CQ_OFFSET 0x400 +#define MLXSW_PCI_DOORBELL_EQ_OFFSET 0x600 +#define MLXSW_PCI_DOORBELL_ARM_CQ_OFFSET 0x800 +#define MLXSW_PCI_DOORBELL_ARM_EQ_OFFSET 0xA00 + +#define MLXSW_PCI_DOORBELL(offset, type_offset, num) \ + ((offset) + (type_offset) + (num) * 4) + +#define MLXSW_PCI_CQS_MAX 96 +#define MLXSW_PCI_EQS_COUNT 2 +#define MLXSW_PCI_EQ_ASYNC_NUM 0 +#define MLXSW_PCI_EQ_COMP_NUM 1 + +#define MLXSW_PCI_AQ_PAGES 8 +#define MLXSW_PCI_AQ_SIZE (MLXSW_PCI_PAGE_SIZE * MLXSW_PCI_AQ_PAGES) +#define MLXSW_PCI_WQE_SIZE 32 /* 32 bytes per element */ +#define MLXSW_PCI_CQE_SIZE 16 /* 16 bytes per element */ +#define MLXSW_PCI_EQE_SIZE 16 /* 16 bytes per element */ +#define MLXSW_PCI_WQE_COUNT (MLXSW_PCI_AQ_SIZE / MLXSW_PCI_WQE_SIZE) +#define MLXSW_PCI_CQE_COUNT (MLXSW_PCI_AQ_SIZE / MLXSW_PCI_CQE_SIZE) +#define MLXSW_PCI_EQE_COUNT (MLXSW_PCI_AQ_SIZE / MLXSW_PCI_EQE_SIZE) +#define MLXSW_PCI_EQE_UPDATE_COUNT 0x80 + +#define MLXSW_PCI_WQE_SG_ENTRIES 3 +#define MLXSW_PCI_WQE_TYPE_ETHERNET 0xA + +/* pci_wqe_c + * If set it indicates that a completion should be reported upon + * execution of this descriptor. + */ +MLXSW_ITEM32(pci, wqe, c, 0x00, 31, 1); + +/* pci_wqe_lp + * Local Processing, set if packet should be processed by the local + * switch hardware: + * For Ethernet EMAD (Direct Route and non Direct Route) - + * must be set if packet destination is local device + * For InfiniBand CTL - must be set if packet destination is local device + * Otherwise it must be clear + * Local Process packets must not exceed the size of 2K (including payload + * and headers). + */ +MLXSW_ITEM32(pci, wqe, lp, 0x00, 30, 1); + +/* pci_wqe_type + * Packet type. + */ +MLXSW_ITEM32(pci, wqe, type, 0x00, 23, 4); + +/* pci_wqe_byte_count + * Size of i-th scatter/gather entry, 0 if entry is unused. + */ +MLXSW_ITEM16_INDEXED(pci, wqe, byte_count, 0x02, 0, 14, 0x02, 0x00, false); + +/* pci_wqe_address + * Physical address of i-th scatter/gather entry. + * Gather Entries must be 2Byte aligned. + */ +MLXSW_ITEM64_INDEXED(pci, wqe, address, 0x08, 0, 64, 0x8, 0x0, false); + +/* pci_cqe_lag + * Packet arrives from a port which is a LAG + */ +MLXSW_ITEM32(pci, cqe, lag, 0x00, 23, 1); + +/* pci_cqe_system_port/lag_id + * When lag=0: System port on which the packet was received + * When lag=1: + * bits [15:4] LAG ID on which the packet was received + * bits [3:0] sub_port on which the packet was received + */ +MLXSW_ITEM32(pci, cqe, system_port, 0x00, 0, 16); +MLXSW_ITEM32(pci, cqe, lag_id, 0x00, 4, 12); +MLXSW_ITEM32(pci, cqe, lag_port_index, 0x00, 0, 4); + +/* pci_cqe_wqe_counter + * WQE count of the WQEs completed on the associated dqn + */ +MLXSW_ITEM32(pci, cqe, wqe_counter, 0x04, 16, 16); + +/* pci_cqe_byte_count + * Byte count of received packets including additional two + * Reserved Bytes that are append to the end of the frame. + * Reserved for Send CQE. + */ +MLXSW_ITEM32(pci, cqe, byte_count, 0x04, 0, 14); + +/* pci_cqe_trap_id + * Trap ID that captured the packet. + */ +MLXSW_ITEM32(pci, cqe, trap_id, 0x08, 0, 8); + +/* pci_cqe_crc + * Length include CRC. Indicates the length field includes + * the packet's CRC. + */ +MLXSW_ITEM32(pci, cqe, crc, 0x0C, 8, 1); + +/* pci_cqe_e + * CQE with Error. + */ +MLXSW_ITEM32(pci, cqe, e, 0x0C, 7, 1); + +/* pci_cqe_sr + * 1 - Send Queue + * 0 - Receive Queue + */ +MLXSW_ITEM32(pci, cqe, sr, 0x0C, 6, 1); + +/* pci_cqe_dqn + * Descriptor Queue (DQ) Number. + */ +MLXSW_ITEM32(pci, cqe, dqn, 0x0C, 1, 5); + +/* pci_cqe_owner + * Ownership bit. + */ +MLXSW_ITEM32(pci, cqe, owner, 0x0C, 0, 1); + +/* pci_eqe_event_type + * Event type. + */ +MLXSW_ITEM32(pci, eqe, event_type, 0x0C, 24, 8); +#define MLXSW_PCI_EQE_EVENT_TYPE_COMP 0x00 +#define MLXSW_PCI_EQE_EVENT_TYPE_CMD 0x0A + +/* pci_eqe_event_sub_type + * Event type. + */ +MLXSW_ITEM32(pci, eqe, event_sub_type, 0x0C, 16, 8); + +/* pci_eqe_cqn + * Completion Queue that triggeret this EQE. + */ +MLXSW_ITEM32(pci, eqe, cqn, 0x0C, 8, 7); + +/* pci_eqe_owner + * Ownership bit. + */ +MLXSW_ITEM32(pci, eqe, owner, 0x0C, 0, 1); + +/* pci_eqe_cmd_token + * Command completion event - token + */ +MLXSW_ITEM32(pci, eqe, cmd_token, 0x08, 16, 16); + +/* pci_eqe_cmd_status + * Command completion event - status + */ +MLXSW_ITEM32(pci, eqe, cmd_status, 0x08, 0, 8); + +/* pci_eqe_cmd_out_param_h + * Command completion event - output parameter - higher part + */ +MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x0C, 0, 32); + +/* pci_eqe_cmd_out_param_l + * Command completion event - output parameter - lower part + */ +MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x10, 0, 32); + +#endif -- cgit v1.1 From 1d20d23c59c93a8b381063ef9785564018ad4c3a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Oct 2016 15:12:59 +0200 Subject: mlxsw: Move PCI id table definitions into driver modules So far, mlxsw_pci.ko is the module that registers PCI table for all drivers (spectrum and switchx2). That is problematic for example with dracut. Since mlxsw_spectrum.ko and mlxsw_switchx2.ko are loaded dynamically from within mlxsw_core.ko, dracut does not have track of them and avoids them from being included in initramfs. So make this in an ordinary way and define the PCI tables in individual driver modules, so it can be properly loaded and included in dracut initramfs image. As a side effect, this patch could remove no longer necessary driver "kind" strings which were used to link PCI ids with individual mlxsw drivers. Suggested-by: Ivan Vecera Tested-by: Ivan Vecera Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Acked-by: Ivan Vecera Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 4 +- drivers/net/ethernet/mellanox/mlxsw/core.c | 14 ------ drivers/net/ethernet/mellanox/mlxsw/core.h | 8 ---- drivers/net/ethernet/mellanox/mlxsw/pci.c | 56 ++++++++--------------- drivers/net/ethernet/mellanox/mlxsw/pci.h | 63 ++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/pci_hw.h | 2 - drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 25 ++++++++-- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 34 ++++++++++++-- 8 files changed, 135 insertions(+), 71 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/pci.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 5989f7c..d0bcf59 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -31,7 +31,7 @@ config MLXSW_PCI config MLXSW_SWITCHX2 tristate "Mellanox Technologies SwitchX-2 support" - depends on MLXSW_CORE && NET_SWITCHDEV + depends on MLXSW_CORE && MLXSW_PCI && NET_SWITCHDEV default m ---help--- This driver supports Mellanox Technologies SwitchX-2 Ethernet @@ -42,7 +42,7 @@ config MLXSW_SWITCHX2 config MLXSW_SPECTRUM tristate "Mellanox Technologies Spectrum support" - depends on MLXSW_CORE && NET_SWITCHDEV && VLAN_8021Q + depends on MLXSW_CORE && MLXSW_PCI && NET_SWITCHDEV && VLAN_8021Q default m ---help--- This driver supports Mellanox Technologies Spectrum Ethernet diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index a37d471..02183f6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -823,17 +823,6 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind) spin_lock(&mlxsw_core_driver_list_lock); mlxsw_driver = __driver_find(kind); - if (!mlxsw_driver) { - spin_unlock(&mlxsw_core_driver_list_lock); - request_module(MLXSW_MODULE_ALIAS_PREFIX "%s", kind); - spin_lock(&mlxsw_core_driver_list_lock); - mlxsw_driver = __driver_find(kind); - } - if (mlxsw_driver) { - if (!try_module_get(mlxsw_driver->owner)) - mlxsw_driver = NULL; - } - spin_unlock(&mlxsw_core_driver_list_lock); return mlxsw_driver; } @@ -845,9 +834,6 @@ static void mlxsw_core_driver_put(const char *kind) spin_lock(&mlxsw_core_driver_list_lock); mlxsw_driver = __driver_find(kind); spin_unlock(&mlxsw_core_driver_list_lock); - if (!mlxsw_driver) - return; - module_put(mlxsw_driver->owner); } static int mlxsw_core_debugfs_init(struct mlxsw_core *mlxsw_core) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 94a846d..0cf721c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -51,13 +51,6 @@ #include "cmd.h" #include "resources.h" -#define MLXSW_MODULE_ALIAS_PREFIX "mlxsw-driver-" -#define MODULE_MLXSW_DRIVER_ALIAS(kind) \ - MODULE_ALIAS(MLXSW_MODULE_ALIAS_PREFIX kind) - -#define MLXSW_DEVICE_KIND_SWITCHX2 "switchx2" -#define MLXSW_DEVICE_KIND_SPECTRUM "spectrum" - struct mlxsw_core; struct mlxsw_driver; struct mlxsw_bus; @@ -221,7 +214,6 @@ struct mlxsw_config_profile { struct mlxsw_driver { struct list_head list; const char *kind; - struct module *owner; size_t priv_size; int (*init)(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info); diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 209630c..63d89f7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -49,6 +49,7 @@ #include #include "pci_hw.h" +#include "pci.h" #include "core.h" #include "cmd.h" #include "port.h" @@ -56,26 +57,8 @@ static const char mlxsw_pci_driver_name[] = "mlxsw_pci"; -static const struct pci_device_id mlxsw_pci_id_table[] = { - {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHX2), 0}, - {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0}, - {0, } -}; - static struct dentry *mlxsw_pci_dbg_root; -static const char *mlxsw_pci_device_kind_get(const struct pci_device_id *id) -{ - switch (id->device) { - case PCI_DEVICE_ID_MELLANOX_SWITCHX2: - return MLXSW_DEVICE_KIND_SWITCHX2; - case PCI_DEVICE_ID_MELLANOX_SPECTRUM: - return MLXSW_DEVICE_KIND_SPECTRUM; - default: - BUG(); - } -} - #define mlxsw_pci_write32(mlxsw_pci, reg, val) \ iowrite32be(val, (mlxsw_pci)->hw_addr + (MLXSW_PCI_ ## reg)) #define mlxsw_pci_read32(mlxsw_pci, reg) \ @@ -1553,7 +1536,7 @@ static int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core, err = request_irq(mlxsw_pci->msix_entry.vector, mlxsw_pci_eq_irq_handler, 0, - mlxsw_pci_driver_name, mlxsw_pci); + mlxsw_pci->bus_info.device_kind, mlxsw_pci); if (err) { dev_err(&pdev->dev, "IRQ request failed\n"); goto err_request_eq_irq; @@ -1793,6 +1776,7 @@ static int mlxsw_pci_sw_reset(struct mlxsw_pci *mlxsw_pci) static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + const char *driver_name = pdev->driver->name; struct mlxsw_pci *mlxsw_pci; int err; @@ -1806,7 +1790,7 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_pci_enable_device; } - err = pci_request_regions(pdev, mlxsw_pci_driver_name); + err = pci_request_regions(pdev, driver_name); if (err) { dev_err(&pdev->dev, "pci_request_regions failed\n"); goto err_pci_request_regions; @@ -1857,7 +1841,7 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_msix_init; } - mlxsw_pci->bus_info.device_kind = mlxsw_pci_device_kind_get(id); + mlxsw_pci->bus_info.device_kind = driver_name; mlxsw_pci->bus_info.device_name = pci_name(mlxsw_pci->pdev); mlxsw_pci->bus_info.dev = &pdev->dev; @@ -1909,33 +1893,30 @@ static void mlxsw_pci_remove(struct pci_dev *pdev) kfree(mlxsw_pci); } -static struct pci_driver mlxsw_pci_driver = { - .name = mlxsw_pci_driver_name, - .id_table = mlxsw_pci_id_table, - .probe = mlxsw_pci_probe, - .remove = mlxsw_pci_remove, -}; +int mlxsw_pci_driver_register(struct pci_driver *pci_driver) +{ + pci_driver->probe = mlxsw_pci_probe; + pci_driver->remove = mlxsw_pci_remove; + return pci_register_driver(pci_driver); +} +EXPORT_SYMBOL(mlxsw_pci_driver_register); -static int __init mlxsw_pci_module_init(void) +void mlxsw_pci_driver_unregister(struct pci_driver *pci_driver) { - int err; + pci_unregister_driver(pci_driver); +} +EXPORT_SYMBOL(mlxsw_pci_driver_unregister); +static int __init mlxsw_pci_module_init(void) +{ mlxsw_pci_dbg_root = debugfs_create_dir(mlxsw_pci_driver_name, NULL); if (!mlxsw_pci_dbg_root) return -ENOMEM; - err = pci_register_driver(&mlxsw_pci_driver); - if (err) - goto err_register_driver; return 0; - -err_register_driver: - debugfs_remove_recursive(mlxsw_pci_dbg_root); - return err; } static void __exit mlxsw_pci_module_exit(void) { - pci_unregister_driver(&mlxsw_pci_driver); debugfs_remove_recursive(mlxsw_pci_dbg_root); } @@ -1945,4 +1926,3 @@ module_exit(mlxsw_pci_module_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Jiri Pirko "); MODULE_DESCRIPTION("Mellanox switch PCI interface driver"); -MODULE_DEVICE_TABLE(pci, mlxsw_pci_id_table); diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h new file mode 100644 index 0000000..35a0011 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -0,0 +1,63 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/pci.h + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Jiri Pirko + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MLXSW_PCI_H +#define _MLXSW_PCI_H + +#include + +#define PCI_DEVICE_ID_MELLANOX_SWITCHX2 0xc738 +#define PCI_DEVICE_ID_MELLANOX_SPECTRUM 0xcb84 + +#if IS_ENABLED(CONFIG_MLXSW_PCI) + +int mlxsw_pci_driver_register(struct pci_driver *pci_driver); +void mlxsw_pci_driver_unregister(struct pci_driver *pci_driver); + +#else + +static inline int +mlxsw_pci_driver_register(struct pci_driver *pci_driver) +{ + return 0; +} + +static inline void +mlxsw_pci_driver_unregister(struct pci_driver *pci_driver) +{ +} + +#endif + +#endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h index 45bc1979..708736f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h @@ -39,8 +39,6 @@ #include "item.h" -#define PCI_DEVICE_ID_MELLANOX_SWITCHX2 0xc738 -#define PCI_DEVICE_ID_MELLANOX_SPECTRUM 0xcb84 #define MLXSW_PCI_BAR0_SIZE (1024 * 1024) /* 1MB */ #define MLXSW_PCI_PAGE_SIZE 4096 diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 5d8f1d5..7805240 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ #include #include "spectrum.h" +#include "pci.h" #include "core.h" #include "reg.h" #include "port.h" @@ -3066,8 +3068,7 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = { }; static struct mlxsw_driver mlxsw_sp_driver = { - .kind = MLXSW_DEVICE_KIND_SPECTRUM, - .owner = THIS_MODULE, + .kind = mlxsw_sp_driver_name, .priv_size = sizeof(struct mlxsw_sp), .init = mlxsw_sp_init, .fini = mlxsw_sp_fini, @@ -4662,6 +4663,16 @@ static struct notifier_block mlxsw_sp_router_netevent_nb __read_mostly = { .notifier_call = mlxsw_sp_router_netevent_event, }; +static const struct pci_device_id mlxsw_sp_pci_id_table[] = { + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0}, + {0, }, +}; + +static struct pci_driver mlxsw_sp_pci_driver = { + .name = mlxsw_sp_driver_name, + .id_table = mlxsw_sp_pci_id_table, +}; + static int __init mlxsw_sp_module_init(void) { int err; @@ -4673,8 +4684,15 @@ static int __init mlxsw_sp_module_init(void) err = mlxsw_core_driver_register(&mlxsw_sp_driver); if (err) goto err_core_driver_register; + + err = mlxsw_pci_driver_register(&mlxsw_sp_pci_driver); + if (err) + goto err_pci_driver_register; + return 0; +err_pci_driver_register: + mlxsw_core_driver_unregister(&mlxsw_sp_driver); err_core_driver_register: unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb); unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); @@ -4684,6 +4702,7 @@ err_core_driver_register: static void __exit mlxsw_sp_module_exit(void) { + mlxsw_pci_driver_unregister(&mlxsw_sp_pci_driver); mlxsw_core_driver_unregister(&mlxsw_sp_driver); unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb); unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); @@ -4696,4 +4715,4 @@ module_exit(mlxsw_sp_module_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Jiri Pirko "); MODULE_DESCRIPTION("Mellanox Spectrum driver"); -MODULE_MLXSW_DRIVER_ALIAS(MLXSW_DEVICE_KIND_SPECTRUM); +MODULE_DEVICE_TABLE(pci, mlxsw_sp_pci_id_table); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 963618d..938e22d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include +#include "pci.h" #include "core.h" #include "reg.h" #include "port.h" @@ -1544,8 +1546,7 @@ static struct mlxsw_config_profile mlxsw_sx_config_profile = { }; static struct mlxsw_driver mlxsw_sx_driver = { - .kind = MLXSW_DEVICE_KIND_SWITCHX2, - .owner = THIS_MODULE, + .kind = mlxsw_sx_driver_name, .priv_size = sizeof(struct mlxsw_sx), .init = mlxsw_sx_init, .fini = mlxsw_sx_fini, @@ -1554,13 +1555,38 @@ static struct mlxsw_driver mlxsw_sx_driver = { .profile = &mlxsw_sx_config_profile, }; +static const struct pci_device_id mlxsw_sx_pci_id_table[] = { + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHX2), 0}, + {0, }, +}; + +static struct pci_driver mlxsw_sx_pci_driver = { + .name = mlxsw_sx_driver_name, + .id_table = mlxsw_sx_pci_id_table, +}; + static int __init mlxsw_sx_module_init(void) { - return mlxsw_core_driver_register(&mlxsw_sx_driver); + int err; + + err = mlxsw_core_driver_register(&mlxsw_sx_driver); + if (err) + return err; + + err = mlxsw_pci_driver_register(&mlxsw_sx_pci_driver); + if (err) + goto err_pci_driver_register; + + return 0; + +err_pci_driver_register: + mlxsw_core_driver_unregister(&mlxsw_sx_driver); + return err; } static void __exit mlxsw_sx_module_exit(void) { + mlxsw_pci_driver_unregister(&mlxsw_sx_pci_driver); mlxsw_core_driver_unregister(&mlxsw_sx_driver); } @@ -1570,4 +1596,4 @@ module_exit(mlxsw_sx_module_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Jiri Pirko "); MODULE_DESCRIPTION("Mellanox SwitchX-2 driver"); -MODULE_MLXSW_DRIVER_ALIAS(MLXSW_DEVICE_KIND_SWITCHX2); +MODULE_DEVICE_TABLE(pci, mlxsw_sx_pci_id_table); -- cgit v1.1 From f20a91f1cc70105852d029e642cef7c583f397d3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Oct 2016 15:13:00 +0200 Subject: mlxsw: spectrum: Set physical device for port netdevice Do this so the sysfs has "device" link correctly set. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 7805240..d652f7f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2222,6 +2222,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, dev = alloc_etherdev(sizeof(struct mlxsw_sp_port)); if (!dev) return -ENOMEM; + SET_NETDEV_DEV(dev, mlxsw_sp->bus_info->dev); mlxsw_sp_port = netdev_priv(dev); mlxsw_sp_port->dev = dev; mlxsw_sp_port->mlxsw_sp = mlxsw_sp; -- cgit v1.1 From 71fac305fb78967b2c856848dead355262ba9e03 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Oct 2016 15:13:01 +0200 Subject: mlxsw: switchx2: Set physical device for port netdevice Do this so the sysfs has "device" link correctly set. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 938e22d..dc3c5ed 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -968,6 +968,7 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port) dev = alloc_etherdev(sizeof(struct mlxsw_sx_port)); if (!dev) return -ENOMEM; + SET_NETDEV_DEV(dev, mlxsw_sx->bus_info->dev); mlxsw_sx_port = netdev_priv(dev); mlxsw_sx_port->dev = dev; mlxsw_sx_port->mlxsw_sx = mlxsw_sx; -- cgit v1.1 From b09edbd07f876c9f7046c4aae1831e58919cffea Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 25 Oct 2016 12:18:42 +0100 Subject: net caif: insert missing spaces in pr_* messages and unbreak multi-line strings Some of the pr_* messages are missing spaces, so insert these and also unbreak multi-line literal strings in pr_* messages Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- net/caif/cfcnfg.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index fa39fc2..273cb07 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c @@ -390,8 +390,7 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, rcu_read_lock(); if (adapt_layer == NULL) { - pr_debug("link setup response but no client exist," - "send linkdown back\n"); + pr_debug("link setup response but no client exist, send linkdown back\n"); cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); goto unlock; } @@ -401,8 +400,7 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); if (phyinfo == NULL) { - pr_err("ERROR: Link Layer Device disappeared" - "while connecting\n"); + pr_err("ERROR: Link Layer Device disappeared while connecting\n"); goto unlock; } @@ -436,8 +434,7 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); break; default: - pr_err("Protocol error. Link setup response " - "- unknown channel type\n"); + pr_err("Protocol error. Link setup response - unknown channel type\n"); goto unlock; } if (!servicel) -- cgit v1.1 From c73d2e8462d0bb1d47b0e8f6a33d97ab9a154824 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Wed, 14 Sep 2016 16:24:31 -0700 Subject: i40e: Fix client interaction This patch fixes a problem in the client interface that was causing random stack traces in RDMA driver load and unload tests. This patch fixes the problem by checking for an existing client before trying to open it. Without this patch, there is a timing related null pointer deref. Change-ID: Ib73d30671a27f6f9770dd53b3e5292b88d6b62da Signed-off-by: Carolyn Wyborny Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_client.c | 29 ++++++++++++--------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index 250db0b..6ffac03 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -565,7 +565,7 @@ void i40e_client_subtask(struct i40e_pf *pf) if (test_bit(__I40E_DOWN, &pf->vsi[pf->lan_vsi]->state)) continue; } else { - dev_warn(&pf->pdev->dev, "This client %s is being instanciated at probe\n", + dev_warn(&pf->pdev->dev, "This client %s is being instantiated at probe\n", client->name); } @@ -582,24 +582,21 @@ void i40e_client_subtask(struct i40e_pf *pf) dev_info(&pf->pdev->dev, "Added instance of Client %s to PF%d bus=0x%02x func=0x%02x\n", client->name, pf->hw.pf_id, pf->hw.bus.device, pf->hw.bus.func); - } - - mutex_lock(&i40e_client_instance_mutex); - /* Send an Open request to the client */ - atomic_inc(&cdev->ref_cnt); - if (client->ops && client->ops->open) - ret = client->ops->open(&cdev->lan_info, client); - atomic_dec(&cdev->ref_cnt); - if (!ret) { + mutex_lock(&i40e_client_instance_mutex); + atomic_inc(&cdev->ref_cnt); + if (client->ops && client->ops->open) + ret = client->ops->open(&cdev->lan_info, + client); + atomic_dec(&cdev->ref_cnt); + if (ret < 0) { + mutex_unlock(&i40e_client_instance_mutex); + i40e_client_del_instance(pf, client); + atomic_dec(&client->ref_cnt); + continue; + } set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); - } else { - /* remove client instance */ mutex_unlock(&i40e_client_instance_mutex); - i40e_client_del_instance(pf, client); - atomic_dec(&client->ref_cnt); - continue; } - mutex_unlock(&i40e_client_instance_mutex); } mutex_unlock(&i40e_client_mutex); } -- cgit v1.1 From ed245406da1409437f9d2c4e7c69112fb255964c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 14 Sep 2016 16:24:32 -0700 Subject: i40e: Rewrite Flow Director busy wait loop We can reorder the busy wait loop at the start of the Flow Director transmit function to reduce the overall code size while still retaining the same functionality. As such I am taking advantage of the opportunity to do so. Change-ID: I34c403ca001953c6ac9816e65d5305e73d869026 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 6287bf6..af36c44 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -122,7 +122,6 @@ static int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, struct device *dev; dma_addr_t dma; u32 td_cmd = 0; - u16 delay = 0; u16 i; /* find existing FDIR VSI */ @@ -137,15 +136,11 @@ static int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, dev = tx_ring->dev; /* we need two descriptors to add/del a filter and we can wait */ - do { - if (I40E_DESC_UNUSED(tx_ring) > 1) - break; + for (i = I40E_FD_CLEAN_DELAY; I40E_DESC_UNUSED(tx_ring) < 2; i--) { + if (!i) + return -EAGAIN; msleep_interruptible(1); - delay++; - } while (delay < I40E_FD_CLEAN_DELAY); - - if (!(I40E_DESC_UNUSED(tx_ring) > 1)) - return -EAGAIN; + } dma = dma_map_single(dev, raw_packet, I40E_FDIR_MAX_RAW_PACKET_SIZE, DMA_TO_DEVICE); -- cgit v1.1 From 7ed3573223dd6bf36b1bd35c8e5e3d0cd4f6f690 Mon Sep 17 00:00:00 2001 From: Filip Sadowski Date: Wed, 14 Sep 2016 16:24:33 -0700 Subject: i40e: Bit test mask correction Incorrect bit mask was used for testing "get link status" response. Instead of I40E_AQ_LSE_ENABLE (which is actually 0x03) it most probably should be I40E_AQ_LSE_IS_ENABLED (which is defined as 0x01). Change-ID: Ia199142906720507f847de3a33a25c61a9781b2f Signed-off-by: Filip Sadowski Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 2154a34..fe8100b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1849,7 +1849,7 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, else hw_link_info->crc_enable = false; - if (resp->command_flags & cpu_to_le16(I40E_AQ_LSE_ENABLE)) + if (resp->command_flags & cpu_to_le16(I40E_AQ_LSE_IS_ENABLED)) hw_link_info->lse_enable = true; else hw_link_info->lse_enable = false; -- cgit v1.1 From dc7621205a93a00a898c193cf985c4c1a65a47d6 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 14 Sep 2016 16:24:34 -0700 Subject: i40e: Remove unused function i40e_vsi_lookup The function is not used so there is no need to carry it forward. I have plans to add a slightly different function that can be inlined to handle the same kind of functionality. Change-ID: Ie2dfcb189dc75e5fbc156bac23003e3b4210ae0f Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 -- drivers/net/ethernet/intel/i40e/i40e_client.c | 31 --------------------------- 2 files changed, 33 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 2030d7c..34c7a5d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -728,8 +728,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); int i40e_vsi_release(struct i40e_vsi *vsi); -struct i40e_vsi *i40e_vsi_lookup(struct i40e_pf *pf, enum i40e_vsi_type type, - struct i40e_vsi *start_vsi); #ifdef I40E_FCOE void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt, diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index 6ffac03..417ac16 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -406,37 +406,6 @@ int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id, } /** - * i40e_vsi_lookup - finds a matching VSI from the PF list starting at start_vsi - * @pf: board private structure - * @type: vsi type - * @start_vsi: a VSI pointer from where to start the search - * - * Returns non NULL on success or NULL for failure - **/ -struct i40e_vsi *i40e_vsi_lookup(struct i40e_pf *pf, - enum i40e_vsi_type type, - struct i40e_vsi *start_vsi) -{ - struct i40e_vsi *vsi; - int i = 0; - - if (start_vsi) { - for (i = 0; i < pf->num_alloc_vsi; i++) { - vsi = pf->vsi[i]; - if (vsi == start_vsi) - break; - } - } - for (; i < pf->num_alloc_vsi; i++) { - vsi = pf->vsi[i]; - if (vsi && vsi->type == type) - return vsi; - } - - return NULL; -} - -/** * i40e_client_add_instance - add a client instance struct to the instance list * @pf: pointer to the board struct * @client: pointer to a client struct in the client list. -- cgit v1.1 From e1da71ca88170d1a6232951294b44dc0c824e464 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 14 Sep 2016 16:24:35 -0700 Subject: i40e: Drop code for unsupported flow types We cannot currently support SCTP in the hardware, and IPV4_FLOW is not used anywhere by the software so we can go through and drop the functionality related to these two flow types. In addition we cannot support masking based on the protocol value so if the user is expecting a value other than TCP or UDP we should simply return an error rather then trying to allocate a filter for a rule that will only partially match what the user requested. Change-ID: I10d52bb97d8104d76255fe244551814ff9531a63 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 31 +++++------------------------ 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index af36c44..7d160c9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -330,22 +330,6 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, return err ? -EOPNOTSUPP : 0; } -/** - * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for - * a specific flow spec - * @vsi: pointer to the targeted VSI - * @fd_data: the flow director data required for the FDir descriptor - * @add: true adds a filter, false removes it - * - * Returns 0 if the filters were successfully added or removed - **/ -static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi, - struct i40e_fdir_filter *fd_data, - bool add) -{ - return -EOPNOTSUPP; -} - #define I40E_IP_DUMMY_PACKET_LEN 34 /** * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for @@ -428,12 +412,6 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi, case UDP_V4_FLOW: ret = i40e_add_del_fdir_udpv4(vsi, input, add); break; - case SCTP_V4_FLOW: - ret = i40e_add_del_fdir_sctpv4(vsi, input, add); - break; - case IPV4_FLOW: - ret = i40e_add_del_fdir_ipv4(vsi, input, add); - break; case IP_USER_FLOW: switch (input->ip4_proto) { case IPPROTO_TCP: @@ -442,15 +420,16 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi, case IPPROTO_UDP: ret = i40e_add_del_fdir_udpv4(vsi, input, add); break; - case IPPROTO_SCTP: - ret = i40e_add_del_fdir_sctpv4(vsi, input, add); - break; - default: + case IPPROTO_IP: ret = i40e_add_del_fdir_ipv4(vsi, input, add); break; + default: + /* We cannot support masking based on protocol */ + goto unsupported_flow; } break; default: +unsupported_flow: dev_info(&pf->pdev->dev, "Could not specify spec type %d\n", input->flow_type); ret = -EINVAL; -- cgit v1.1 From 7be96322a57a3612ea7640e6ff56030a7350fd7f Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 14 Sep 2016 16:24:36 -0700 Subject: i40e: reopen client after reset Allow the client interface to reopen existing clients if they were closed. This allows clients to recover from reset, which is essential for supporting VF RDMA. In one instance, the driver was not clearing the open bit when the client was closed. Add the code to clear this bit so that the state is accurate and the driver will not attempt to reopen already-open clients. Remove the ref_cnt variable; it was just getting in the way and was not being used consistently. Change-ID: Ic71af4553b096963ac0c56a997f887c9a4ed162d Signed-off-by: Mitch Williams Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_client.c | 47 ++++++++++----------------- drivers/net/ethernet/intel/i40e/i40e_client.h | 2 -- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index 417ac16..7fe72ab 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -287,6 +287,7 @@ void i40e_notify_client_of_netdev_close(struct i40e_vsi *vsi, bool reset) } cdev->client->ops->close(&cdev->lan_info, cdev->client, reset); + clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); i40e_client_release_qvlist(&cdev->lan_info); } } @@ -544,28 +545,27 @@ void i40e_client_subtask(struct i40e_pf *pf) continue; if (!existing) { - /* Also up the ref_cnt for no. of instances of this - * client. - */ - atomic_inc(&client->ref_cnt); dev_info(&pf->pdev->dev, "Added instance of Client %s to PF%d bus=0x%02x func=0x%02x\n", client->name, pf->hw.pf_id, pf->hw.bus.device, pf->hw.bus.func); - mutex_lock(&i40e_client_instance_mutex); - atomic_inc(&cdev->ref_cnt); + } + + mutex_lock(&i40e_client_instance_mutex); + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, + &cdev->state)) { + /* Send an Open request to the client */ if (client->ops && client->ops->open) ret = client->ops->open(&cdev->lan_info, client); - atomic_dec(&cdev->ref_cnt); - if (ret < 0) { - mutex_unlock(&i40e_client_instance_mutex); + if (!ret) { + set_bit(__I40E_CLIENT_INSTANCE_OPENED, + &cdev->state); + } else { + /* remove client instance */ i40e_client_del_instance(pf, client); - atomic_dec(&client->ref_cnt); - continue; } - set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); - mutex_unlock(&i40e_client_instance_mutex); } + mutex_unlock(&i40e_client_instance_mutex); } mutex_unlock(&i40e_client_mutex); } @@ -660,10 +660,6 @@ static int i40e_client_release(struct i40e_client *client) continue; pf = (struct i40e_pf *)cdev->lan_info.pf; if (test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { - if (atomic_read(&cdev->ref_cnt) > 0) { - ret = I40E_ERR_NOT_READY; - goto out; - } if (client->ops && client->ops->close) client->ops->close(&cdev->lan_info, client, false); @@ -676,11 +672,9 @@ static int i40e_client_release(struct i40e_client *client) } /* delete the client instance from the list */ list_move(&cdev->list, &cdevs_tmp); - atomic_dec(&client->ref_cnt); dev_info(&pf->pdev->dev, "Deleted client instance of Client %s\n", client->name); } -out: mutex_unlock(&i40e_client_instance_mutex); /* free the client device and release its vsi */ @@ -1006,17 +1000,10 @@ int i40e_unregister_client(struct i40e_client *client) ret = -ENODEV; goto out; } - if (atomic_read(&client->ref_cnt) == 0) { - clear_bit(__I40E_CLIENT_REGISTERED, &client->state); - list_del(&client->list); - pr_info("i40e: Unregistered client %s with return code %d\n", - client->name, ret); - } else { - ret = I40E_ERR_NOT_READY; - pr_err("i40e: Client %s failed unregister - client has open instances\n", - client->name); - } - + clear_bit(__I40E_CLIENT_REGISTERED, &client->state); + list_del(&client->list); + pr_info("i40e: Unregistered client %s with return code %d\n", + client->name, ret); out: mutex_unlock(&i40e_client_mutex); return ret; diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.h b/drivers/net/ethernet/intel/i40e/i40e_client.h index 38a6c36..528bd79 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.h +++ b/drivers/net/ethernet/intel/i40e/i40e_client.h @@ -203,8 +203,6 @@ struct i40e_client_instance { struct i40e_info lan_info; struct i40e_client *client; unsigned long state; - /* A count of all the in-progress calls to the client */ - atomic_t ref_cnt; }; struct i40e_client { -- cgit v1.1 From 6d6fd1be2a1052cbe5b04c3678b30925a9344ac7 Mon Sep 17 00:00:00 2001 From: Preethi Banala Date: Wed, 14 Sep 2016 16:24:37 -0700 Subject: i40e: group base mode VF offload flags Group together the minimum set of offload capabilities that are always supported by VF in base mode. This define would be used by PF to make sure VF in base mode gets minimum of base capabilities . Change-ID: Id5e8f22ba169c8f0a38d22fc36b2cb531c02582c Signed-off-by: Preethi Banala Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl.h | 4 ++++ drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h index f861d31..974ba2b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h @@ -165,6 +165,10 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF 0X00080000 #define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00100000 +#define I40E_VF_BASE_MODE_OFFLOADS (I40E_VIRTCHNL_VF_OFFLOAD_L2 | \ + I40E_VIRTCHNL_VF_OFFLOAD_VLAN | \ + I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF) + struct i40e_virtchnl_vf_resource { u16 num_vsis; u16 num_queue_pairs; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index bd691ad..fc374f8 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -162,6 +162,10 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF 0X00080000 #define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00100000 +#define I40E_VF_BASE_MODE_OFFLOADS (I40E_VIRTCHNL_VF_OFFLOAD_L2 | \ + I40E_VIRTCHNL_VF_OFFLOAD_VLAN | \ + I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF) + struct i40e_virtchnl_vf_resource { u16 num_vsis; u16 num_queue_pairs; -- cgit v1.1 From 96db776a368263bcce9f7eb12e878b0aef1a1974 Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Wed, 14 Sep 2016 16:24:38 -0700 Subject: i40e/i40evf: fix interrupt affinity bug There exists a bug in which a 'perfect storm' can occur and cause interrupts to fail to be correctly affinitized. This causes unexpected behavior and has a substantial impact on performance when it happens. The bug occurs if there is heavy traffic, any number of CPUs that have an i40e interrupt are pegged at 100%, and the interrupt afffinity for those CPUs is changed. Instead of moving to the new CPU, the interrupt continues to be polled while there is heavy traffic. The bug is most readily realized as the driver is first brought up and all interrupts start on CPU0. If there is heavy traffic and the interrupt starts polling before the interrupt is affinitized, the interrupt will be stuck on CPU0 until traffic stops. The bug, however, can also be wrought out more simply by affinitizing all the interrupts to a single CPU and then attempting to move any of those interrupts off while there is heavy traffic. This patch fixes the bug by registering for update notifications from the kernel when the interrupt affinity changes. When that fires, we cache the intended affinity mask. Then, while polling, if the cpu is pegged at 100% and we failed to clean the rings, we check to make sure we have the correct affinity and stop polling if we're firing on the wrong CPU. When the kernel successfully moves the interrupt, it will start polling on the correct CPU. The performance impact is minimal since the only time this section gets executed is when performance is already compromised by the CPU. Change-ID: I4410a880159b9dba1f8297aa72bef36dca34e830 Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 + drivers/net/ethernet/intel/i40e/i40e_main.c | 64 +++++++++++++++++----- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 36 ++++++++++--- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 31 +++++++++-- drivers/net/ethernet/intel/i40evf/i40evf.h | 3 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 71 +++++++++++++++++-------- 6 files changed, 159 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 34c7a5d..01cce5b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -607,6 +607,8 @@ struct i40e_q_vector { unsigned long hung_detected; /* Set/Reset for hung_detection logic */ cpumask_t affinity_mask; + struct irq_affinity_notify affinity_notify; + struct rcu_head rcu; /* to avoid race with update stats on free */ char name[I40E_INT_NAME_STR_LEN]; bool arm_wb_state; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 83edbe8..9382ba80 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3317,6 +3317,33 @@ static irqreturn_t i40e_msix_clean_rings(int irq, void *data) } /** + * i40e_irq_affinity_notify - Callback for affinity changes + * @notify: context as to what irq was changed + * @mask: the new affinity mask + * + * This is a callback function used by the irq_set_affinity_notifier function + * so that we may register to receive changes to the irq affinity masks. + **/ +static void i40e_irq_affinity_notify(struct irq_affinity_notify *notify, + const cpumask_t *mask) +{ + struct i40e_q_vector *q_vector = + container_of(notify, struct i40e_q_vector, affinity_notify); + + q_vector->affinity_mask = *mask; +} + +/** + * i40e_irq_affinity_release - Callback for affinity notifier release + * @ref: internal core kernel usage + * + * This is a callback function used by the irq_set_affinity_notifier function + * to inform the current notification subscriber that they will no longer + * receive notifications. + **/ +static void i40e_irq_affinity_release(struct kref *ref) {} + +/** * i40e_vsi_request_irq_msix - Initialize MSI-X interrupts * @vsi: the VSI being configured * @basename: name for the vector @@ -3331,10 +3358,13 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) int rx_int_idx = 0; int tx_int_idx = 0; int vector, err; + int irq_num; for (vector = 0; vector < q_vectors; vector++) { struct i40e_q_vector *q_vector = vsi->q_vectors[vector]; + irq_num = pf->msix_entries[base + vector].vector; + if (q_vector->tx.ring && q_vector->rx.ring) { snprintf(q_vector->name, sizeof(q_vector->name) - 1, "%s-%s-%d", basename, "TxRx", rx_int_idx++); @@ -3349,7 +3379,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) /* skip this unused q_vector */ continue; } - err = request_irq(pf->msix_entries[base + vector].vector, + err = request_irq(irq_num, vsi->irq_handler, 0, q_vector->name, @@ -3359,9 +3389,13 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) "MSIX request_irq failed, error: %d\n", err); goto free_queue_irqs; } + + /* register for affinity change notifications */ + q_vector->affinity_notify.notify = i40e_irq_affinity_notify; + q_vector->affinity_notify.release = i40e_irq_affinity_release; + irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); /* assign the mask for this irq */ - irq_set_affinity_hint(pf->msix_entries[base + vector].vector, - &q_vector->affinity_mask); + irq_set_affinity_hint(irq_num, &q_vector->affinity_mask); } vsi->irqs_ready = true; @@ -3370,10 +3404,10 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) free_queue_irqs: while (vector) { vector--; - irq_set_affinity_hint(pf->msix_entries[base + vector].vector, - NULL); - free_irq(pf->msix_entries[base + vector].vector, - &(vsi->q_vectors[vector])); + irq_num = pf->msix_entries[base + vector].vector; + irq_set_affinity_notifier(irq_num, NULL); + irq_set_affinity_hint(irq_num, NULL); + free_irq(irq_num, &vsi->q_vectors[vector]); } return err; } @@ -4012,19 +4046,23 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi) vsi->irqs_ready = false; for (i = 0; i < vsi->num_q_vectors; i++) { - u16 vector = i + base; + int irq_num; + u16 vector; + + vector = i + base; + irq_num = pf->msix_entries[vector].vector; /* free only the irqs that were actually requested */ if (!vsi->q_vectors[i] || !vsi->q_vectors[i]->num_ringpairs) continue; + /* clear the affinity notifier in the IRQ descriptor */ + irq_set_affinity_notifier(irq_num, NULL); /* clear the affinity_mask in the IRQ descriptor */ - irq_set_affinity_hint(pf->msix_entries[vector].vector, - NULL); - synchronize_irq(pf->msix_entries[vector].vector); - free_irq(pf->msix_entries[vector].vector, - vsi->q_vectors[i]); + irq_set_affinity_hint(irq_num, NULL); + synchronize_irq(irq_num); + free_irq(irq_num, vsi->q_vectors[i]); /* Tear down the interrupt queue link list * diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 7d160c9..48e6533 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1999,12 +1999,25 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { + const cpumask_t *aff_mask = &q_vector->affinity_mask; + int cpu_id = smp_processor_id(); + + /* It is possible that the interrupt affinity has changed but, + * if the cpu is pegged at 100%, polling will never exit while + * traffic continues and the interrupt will be stuck on this + * cpu. We check to make sure affinity is correct before we + * continue to poll, otherwise we must stop polling so the + * interrupt can move to the correct cpu. + */ + if (likely(cpumask_test_cpu(cpu_id, aff_mask) || + !(vsi->back->flags & I40E_FLAG_MSIX_ENABLED))) { tx_only: - if (arm_wb) { - q_vector->tx.ring[0].tx_stats.tx_force_wb++; - i40e_enable_wb_on_itr(vsi, q_vector); + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; + i40e_enable_wb_on_itr(vsi, q_vector); + } + return budget; } - return budget; } if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR) @@ -2012,11 +2025,18 @@ tx_only: /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete_done(napi, work_done); - if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) { - i40e_update_enable_itr(vsi, q_vector); - } else { /* Legacy mode */ + + /* If we're prematurely stopping polling to fix the interrupt + * affinity we want to make sure polling starts back up so we + * issue a call to i40e_force_wb which triggers a SW interrupt. + */ + if (!clean_complete) + i40e_force_wb(vsi, q_vector); + else if (!(vsi->back->flags & I40E_FLAG_MSIX_ENABLED)) i40e_irq_dynamic_enable_icr0(vsi->back, false); - } + else + i40e_update_enable_itr(vsi, q_vector); + return 0; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 75f2a2c..dd8ad6b 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1461,12 +1461,24 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { + const cpumask_t *aff_mask = &q_vector->affinity_mask; + int cpu_id = smp_processor_id(); + + /* It is possible that the interrupt affinity has changed but, + * if the cpu is pegged at 100%, polling will never exit while + * traffic continues and the interrupt will be stuck on this + * cpu. We check to make sure affinity is correct before we + * continue to poll, otherwise we must stop polling so the + * interrupt can move to the correct cpu. + */ + if (likely(cpumask_test_cpu(cpu_id, aff_mask))) { tx_only: - if (arm_wb) { - q_vector->tx.ring[0].tx_stats.tx_force_wb++; - i40e_enable_wb_on_itr(vsi, q_vector); + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; + i40e_enable_wb_on_itr(vsi, q_vector); + } + return budget; } - return budget; } if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR) @@ -1474,7 +1486,16 @@ tx_only: /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete_done(napi, work_done); - i40e_update_enable_itr(vsi, q_vector); + + /* If we're prematurely stopping polling to fix the interrupt + * affinity we want to make sure polling starts back up so we + * issue a call to i40evf_force_wb which triggers a SW interrupt. + */ + if (!clean_complete) + i40evf_force_wb(vsi, q_vector); + else + i40e_update_enable_itr(vsi, q_vector); + return 0; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index c5fd724..fffe4cf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -107,7 +107,8 @@ struct i40e_q_vector { int v_idx; /* vector index in list */ char name[IFNAMSIZ + 9]; bool arm_wb_state; - cpumask_var_t affinity_mask; + cpumask_t affinity_mask; + struct irq_affinity_notify affinity_notify; }; /* Helper macros to switch between ints/sec and what the register uses. diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 777eb29..e95e873 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -496,6 +496,33 @@ static void i40evf_netpoll(struct net_device *netdev) #endif /** + * i40evf_irq_affinity_notify - Callback for affinity changes + * @notify: context as to what irq was changed + * @mask: the new affinity mask + * + * This is a callback function used by the irq_set_affinity_notifier function + * so that we may register to receive changes to the irq affinity masks. + **/ +static void i40evf_irq_affinity_notify(struct irq_affinity_notify *notify, + const cpumask_t *mask) +{ + struct i40e_q_vector *q_vector = + container_of(notify, struct i40e_q_vector, affinity_notify); + + q_vector->affinity_mask = *mask; +} + +/** + * i40evf_irq_affinity_release - Callback for affinity notifier release + * @ref: internal core kernel usage + * + * This is a callback function used by the irq_set_affinity_notifier function + * to inform the current notification subscriber that they will no longer + * receive notifications. + **/ +static void i40evf_irq_affinity_release(struct kref *ref) {} + +/** * i40evf_request_traffic_irqs - Initialize MSI-X interrupts * @adapter: board private structure * @@ -507,6 +534,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) { int vector, err, q_vectors; int rx_int_idx = 0, tx_int_idx = 0; + int irq_num; i40evf_irq_disable(adapter); /* Decrement for Other and TCP Timer vectors */ @@ -514,6 +542,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) for (vector = 0; vector < q_vectors; vector++) { struct i40e_q_vector *q_vector = &adapter->q_vectors[vector]; + irq_num = adapter->msix_entries[vector + NONQ_VECS].vector; if (q_vector->tx.ring && q_vector->rx.ring) { snprintf(q_vector->name, sizeof(q_vector->name) - 1, @@ -532,21 +561,23 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) /* skip this unused q_vector */ continue; } - err = request_irq( - adapter->msix_entries[vector + NONQ_VECS].vector, - i40evf_msix_clean_rings, - 0, - q_vector->name, - q_vector); + err = request_irq(irq_num, + i40evf_msix_clean_rings, + 0, + q_vector->name, + q_vector); if (err) { dev_info(&adapter->pdev->dev, "Request_irq failed, error: %d\n", err); goto free_queue_irqs; } + /* register for affinity change notifications */ + q_vector->affinity_notify.notify = i40evf_irq_affinity_notify; + q_vector->affinity_notify.release = + i40evf_irq_affinity_release; + irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); /* assign the mask for this irq */ - irq_set_affinity_hint( - adapter->msix_entries[vector + NONQ_VECS].vector, - q_vector->affinity_mask); + irq_set_affinity_hint(irq_num, &q_vector->affinity_mask); } return 0; @@ -554,11 +585,10 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) free_queue_irqs: while (vector) { vector--; - irq_set_affinity_hint( - adapter->msix_entries[vector + NONQ_VECS].vector, - NULL); - free_irq(adapter->msix_entries[vector + NONQ_VECS].vector, - &adapter->q_vectors[vector]); + irq_num = adapter->msix_entries[vector + NONQ_VECS].vector; + irq_set_affinity_notifier(irq_num, NULL); + irq_set_affinity_hint(irq_num, NULL); + free_irq(irq_num, &adapter->q_vectors[vector]); } return err; } @@ -599,16 +629,15 @@ static int i40evf_request_misc_irq(struct i40evf_adapter *adapter) **/ static void i40evf_free_traffic_irqs(struct i40evf_adapter *adapter) { - int i; - int q_vectors; + int vector, irq_num, q_vectors; q_vectors = adapter->num_msix_vectors - NONQ_VECS; - for (i = 0; i < q_vectors; i++) { - irq_set_affinity_hint(adapter->msix_entries[i+1].vector, - NULL); - free_irq(adapter->msix_entries[i+1].vector, - &adapter->q_vectors[i]); + for (vector = 0; vector < q_vectors; vector++) { + irq_num = adapter->msix_entries[vector + NONQ_VECS].vector; + irq_set_affinity_notifier(irq_num, NULL); + irq_set_affinity_hint(irq_num, NULL); + free_irq(irq_num, &adapter->q_vectors[vector]); } } -- cgit v1.1 From 4adbb642eaf53dfe2ea0a4170d8df95d115c12ac Mon Sep 17 00:00:00 2001 From: Bimmy Pujari Date: Wed, 14 Sep 2016 16:24:39 -0700 Subject: i40e/i40evf: Changed version from 1.6.16 to 1.6.19 Signed-off-by: Bimmy Pujari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9382ba80..c25247f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -41,7 +41,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 16 +#define DRV_VERSION_BUILD 19 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index e95e873..3e51ff2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -38,7 +38,7 @@ static const char i40evf_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 16 +#define DRV_VERSION_BUILD 19 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) \ -- cgit v1.1 From fe180a5e275adc7430ada9e4881dcca5ea1a8037 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 26 Sep 2016 20:17:01 -0700 Subject: i40e: Make struct i40e_stats const Move some data to text $ size drivers/net/ethernet/intel/i40e/i40e_ethtool.o* text data bss dec hex filename 25012 0 32 25044 61d4 drivers/net/ethernet/intel/i40e/i40e_ethtool.o.new 22868 2120 32 25020 61bc drivers/net/ethernet/intel/i40e/i40e_ethtool.o.old Signed-off-by: Joe Perches Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 92bc884..3a1f91e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -104,7 +104,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { * The PF_STATs are appended to the netdev stats only when ethtool -S * is queried on the base PF netdev, not on the VMDq or FCoE netdev. */ -static struct i40e_stats i40e_gstrings_stats[] = { +static const struct i40e_stats i40e_gstrings_stats[] = { I40E_PF_STAT("rx_bytes", stats.eth.rx_bytes), I40E_PF_STAT("tx_bytes", stats.eth.tx_bytes), I40E_PF_STAT("rx_unicast", stats.eth.rx_unicast), -- cgit v1.1 From 128150576f85b2745d849aba43980683a6267e50 Mon Sep 17 00:00:00 2001 From: Lihong Yang Date: Tue, 27 Sep 2016 11:28:48 -0700 Subject: i40e: fix confusing dmesg info for ethtool -L option Ethtool -L option with the combined parameter is for changing the number of multi-purpose channels of the specified network device. The pre-set maximum for the combined channels is cpu dependent. Currently, for an i40e device, when the user sets a value between 64 and the maximum that the cpu can support for the combined parameter, the i40e driver displays the confusing info in dmesg to only show 64 as the RSS count regardless of what the accepted user input is as long as it is larger than 64. This patch fixes the message in the i40e driver when the user uses ethtool -L to change the number of the combined channels to consistently display the user requested value if it is valid and accepted by ethtool. Change-ID: Ia80a68bc844b779a49e0f76e7d3dcc915032d9af Signed-off-by: Lihong Yang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c25247f..a4bae0a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8400,8 +8400,8 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) i40e_pf_config_rss(pf); } - dev_info(&pf->pdev->dev, "RSS count/HW max RSS count: %d/%d\n", - pf->alloc_rss_size, pf->rss_size_max); + dev_info(&pf->pdev->dev, "User requested queue count/HW max RSS count: %d/%d\n", + vsi->req_queue_pairs, pf->rss_size_max); return pf->alloc_rss_size; } -- cgit v1.1 From 99dad8b34c687cb8c4975fbabced76db35f88c14 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 27 Sep 2016 11:28:50 -0700 Subject: i40e: Drop redundant Rx descriptor processing code This patch cleans up several pieces of redundant code in the Rx clean-up paths. The first bit is that hdr_addr and the status_err_len portions of the Rx descriptor represent the same value. As such there is no point in setting them to 0 before setting them to 0. I'm dropping the second spot where we are updating the value to 0 so that we only have 1 write for this value instead of 2. The second piece is the checking for the DD bit in the packet. We only need to check for a non-zero value for the status_err_len because if the device is done with the descriptor it will have written something back and the DD is just one piece of it. In addition I have moved the reading of the Rx descriptor bits related to rx_ptype down so that they are actually below the dma_rmb() call so that we are guaranteed that we don't have any funky 64b on 32b calls causing any ordering issues. Change-ID: I256e44a025d3c64a7224aaaec37c852bfcb1871b Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 18 ++++++------------ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 18 ++++++------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 48e6533..daade4fe 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1220,7 +1220,6 @@ bool i40e_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) * because each write-back erases this info. */ rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset); - rx_desc->read.hdr_addr = 0; rx_desc++; bi++; @@ -1741,7 +1740,6 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) while (likely(total_rx_packets < budget)) { union i40e_rx_desc *rx_desc; struct sk_buff *skb; - u32 rx_status; u16 vlan_tag; u8 rx_ptype; u64 qword; @@ -1755,21 +1753,13 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_desc = I40E_RX_DESC(rx_ring, rx_ring->next_to_clean); - qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); - rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> - I40E_RXD_QW1_PTYPE_SHIFT; - rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> - I40E_RXD_QW1_STATUS_SHIFT; - - if (!(rx_status & BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) - break; - /* status_error_len will always be zero for unused descriptors * because it's cleared in cleanup, and overlaps with hdr_addr * which is always zero because packet split isn't used, if the * hardware wrote DD then it will be non-zero */ - if (!rx_desc->wb.qword1.status_error_len) + if (!i40e_test_staterr(rx_desc, + BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) break; /* This memory barrier is needed to keep us from reading @@ -1803,6 +1793,10 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> + I40E_RXD_QW1_PTYPE_SHIFT; + /* populate checksum, VLAN, and protocol */ i40e_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index dd8ad6b..e2d3622 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -705,7 +705,6 @@ bool i40evf_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) * because each write-back erases this info. */ rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset); - rx_desc->read.hdr_addr = 0; rx_desc++; bi++; @@ -1209,7 +1208,6 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) while (likely(total_rx_packets < budget)) { union i40e_rx_desc *rx_desc; struct sk_buff *skb; - u32 rx_status; u16 vlan_tag; u8 rx_ptype; u64 qword; @@ -1223,21 +1221,13 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_desc = I40E_RX_DESC(rx_ring, rx_ring->next_to_clean); - qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); - rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> - I40E_RXD_QW1_PTYPE_SHIFT; - rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> - I40E_RXD_QW1_STATUS_SHIFT; - - if (!(rx_status & BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) - break; - /* status_error_len will always be zero for unused descriptors * because it's cleared in cleanup, and overlaps with hdr_addr * which is always zero because packet split isn't used, if the * hardware wrote DD then it will be non-zero */ - if (!rx_desc->wb.qword1.status_error_len) + if (!i40e_test_staterr(rx_desc, + BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) break; /* This memory barrier is needed to keep us from reading @@ -1271,6 +1261,10 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> + I40E_RXD_QW1_PTYPE_SHIFT; + /* populate checksum, VLAN, and protocol */ i40evf_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype); -- cgit v1.1 From ab425cb7ffdb024a45b0b3bcbf58d52ead3c347c Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Tue, 27 Sep 2016 11:28:52 -0700 Subject: i40e: Fix for long link down notification time This patch fixes a problem where it could take a very long time (>100 msec) to print the link down notification. This problem is fixed by changing how often we update link info from fw, when link is down. Without this patch, it can take over 100msec to notify user link is down. Change-ID: Ib876eb30834c7080792becd13ee093b9cbb35d78 Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index fe8100b..a475946 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -2494,7 +2494,10 @@ i40e_status i40e_update_link_info(struct i40e_hw *hw) if (status) return status; - if (hw->phy.link_info.link_info & I40E_AQ_MEDIA_AVAILABLE) { + /* extra checking needed to ensure link info to user is timely */ + if ((hw->phy.link_info.link_info & I40E_AQ_MEDIA_AVAILABLE) && + ((hw->phy.link_info.link_info & I40E_AQ_LINK_UP) || + !(hw->phy.link_info_old.link_info & I40E_AQ_LINK_UP))) { status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, NULL); if (status) -- cgit v1.1 From 53cb6e9e8949b13f63db0bc96f8fcc249763acfb Mon Sep 17 00:00:00 2001 From: Filip Sadowski Date: Tue, 27 Sep 2016 11:28:53 -0700 Subject: i40e: Removal of workaround for simple MAC address filter deletion This is code refactoring. This patch removes the workaround which deleted a default MAC filter added by the firmware when the interface was brought up. This filter caused frames to pass disregarding the VLAN tagging. It used to be automatically applied after reset in pre-SRA FW versions. This workaround is not needed in production NICs and hence can be removed. Change-ID: I129fe1aae1f17b5a224c9b29a996d916aa1be1ec Signed-off-by: Filip Sadowski Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 41 ----------------------------- 1 file changed, 41 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index a4bae0a..2e787ff 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1287,39 +1287,6 @@ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, } /** - * i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM - * @vsi: the PF Main VSI - inappropriate for any other VSI - * @macaddr: the MAC address - * - * Remove whatever filter the firmware set up so the driver can manage - * its own filtering intelligently. - **/ -static void i40e_rm_default_mac_filter(struct i40e_vsi *vsi, u8 *macaddr) -{ - struct i40e_aqc_remove_macvlan_element_data element; - struct i40e_pf *pf = vsi->back; - - /* Only appropriate for the PF main VSI */ - if (vsi->type != I40E_VSI_MAIN) - return; - - memset(&element, 0, sizeof(element)); - ether_addr_copy(element.mac_addr, macaddr); - element.vlan_tag = 0; - /* Ignore error returns, some firmware does it this way... */ - element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; - i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL); - - memset(&element, 0, sizeof(element)); - ether_addr_copy(element.mac_addr, macaddr); - element.vlan_tag = 0; - /* ...and some firmware does it this way. */ - element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH | - I40E_AQC_MACVLAN_DEL_IGNORE_VLAN; - i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL); -} - -/** * i40e_add_filter - Add a mac/vlan filter to the VSI * @vsi: the VSI to be searched * @macaddr: the MAC address @@ -9218,12 +9185,6 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) if (vsi->type == I40E_VSI_MAIN) { SET_NETDEV_DEV(netdev, &pf->pdev->dev); ether_addr_copy(mac_addr, hw->mac.perm_addr); - /* The following steps are necessary to prevent reception - * of tagged packets - some older NVM configurations load a - * default a MAC-VLAN filter that accepts any tagged packet - * which must be replaced by a normal filter. - */ - i40e_rm_default_mac_filter(vsi, mac_addr); spin_lock_bh(&vsi->mac_filter_list_lock); i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, true); spin_unlock_bh(&vsi->mac_filter_list_lock); @@ -9741,8 +9702,6 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi) pf->vsi[pf->lan_vsi]->tc_config.enabled_tc = 0; pf->vsi[pf->lan_vsi]->seid = pf->main_vsi_seid; i40e_vsi_config_tc(pf->vsi[pf->lan_vsi], enabled_tc); - if (vsi->type == I40E_VSI_MAIN) - i40e_rm_default_mac_filter(vsi, pf->hw.mac.perm_addr); /* assign it some queues */ ret = i40e_alloc_rings(vsi); -- cgit v1.1 From cf465fe750412335b7a3c75263bd795aecef4fef Mon Sep 17 00:00:00 2001 From: Bimmy Pujari Date: Tue, 27 Sep 2016 11:28:54 -0700 Subject: i40e/i40evf: Changed version from 1.6.19 to 1.6.21 Signed-off-by: Bimmy Pujari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2e787ff..00c322d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -41,7 +41,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 19 +#define DRV_VERSION_BUILD 21 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 3e51ff2..bcb1caf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -38,7 +38,7 @@ static const char i40evf_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 19 +#define DRV_VERSION_BUILD 21 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) \ -- cgit v1.1 From c17ef430b9fd5f58074f5cdc0128d06a5ae92304 Mon Sep 17 00:00:00 2001 From: David Ertman Date: Fri, 30 Sep 2016 01:36:21 -0700 Subject: i40e: Fix bit logic error in failure case Patch a036244c0686 "i40e: Fix kernel panic on enable/disable LLDP" introduced an error in bit logic. Originally this bit manipulation was meant to clear two bits to indicate that DCB was not enabled or capable. An "&" was incorrectly used instead of an "|" bit operator to combine the two bitmasks into one. This also created a static checker error since the resultant code was a no-op. This patch fixes the error by using the correct bit-wise operator. Signed-off-by: Dave Ertman Reported-by: Dan Carpenter Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 00c322d..0c2328d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -10970,7 +10970,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = i40e_init_pf_dcb(pf); if (err) { dev_info(&pdev->dev, "DCB init failed %d, disabled\n", err); - pf->flags &= ~(I40E_FLAG_DCB_CAPABLE & I40E_FLAG_DCB_ENABLED); + pf->flags &= ~(I40E_FLAG_DCB_CAPABLE | I40E_FLAG_DCB_ENABLED); /* Continue without DCB enabled */ } #endif /* CONFIG_I40E_DCB */ -- cgit v1.1 From 5d4ca23e58f2db7c14b76070f31295244b6edb7b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 30 Sep 2016 08:21:46 -0400 Subject: i40e: Clean up handling of msglevel flags and debug parameter So the i40e driver had a really convoluted configuration for how to handle the debug flags contained in msg_level. Part of the issue is that the driver has its own 32 bit mask that it was using to track a separate set of debug features. From what I can tell it was trying to use the upper 4 bits to determine if the value was meant to represent a bit-mask or the numeric value provided by debug level. What this patch does is clean this up by compressing those 4 bits into bit 31, as a result we just have to perform a check against the value being negative to determine if we are looking at a debug level (positive), or a debug mask (negative). The debug level will populate the msg_level, and the debug mask will populate the debug_mask in the hardware struct. I added similar logic for ethtool. If the value being provided has bit 31 set we assume the value being provided is a debug mask, otherwise we assume it is a msg_enable mask. For displaying we only provide the msg_enable, and if debug_mask is in use we will print it to the dmesg log. Lastly I removed the debugfs interface. It is redundant with what we already have in ethtool and really doesn't belong anyway. Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 19 ------------------- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 7 ++++++- drivers/net/ethernet/intel/i40e/i40e_main.c | 23 ++++++++--------------- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 0c1875b..0354632 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -1210,24 +1210,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, "dump debug fwdata \n"); } - - } else if (strncmp(cmd_buf, "msg_enable", 10) == 0) { - u32 level; - cnt = sscanf(&cmd_buf[10], "%i", &level); - if (cnt) { - if (I40E_DEBUG_USER & level) { - pf->hw.debug_mask = level; - dev_info(&pf->pdev->dev, - "set hw.debug_mask = 0x%08x\n", - pf->hw.debug_mask); - } - pf->msg_enable = level; - dev_info(&pf->pdev->dev, "set msg_enable = 0x%08x\n", - pf->msg_enable); - } else { - dev_info(&pf->pdev->dev, "msg_enable = 0x%08x\n", - pf->msg_enable); - } } else if (strncmp(cmd_buf, "pfr", 3) == 0) { dev_info(&pf->pdev->dev, "debugfs: forcing PFR\n"); i40e_do_reset_safe(pf, BIT(__I40E_PF_RESET_REQUESTED)); @@ -1644,7 +1626,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " dump desc aq\n"); dev_info(&pf->pdev->dev, " dump reset stats\n"); dev_info(&pf->pdev->dev, " dump debug fwdata \n"); - dev_info(&pf->pdev->dev, " msg_enable [level]\n"); dev_info(&pf->pdev->dev, " read \n"); dev_info(&pf->pdev->dev, " write \n"); dev_info(&pf->pdev->dev, " clear_stats vsi [seid]\n"); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 3a1f91e..fb4fb52 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -978,6 +978,10 @@ static u32 i40e_get_msglevel(struct net_device *netdev) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; + u32 debug_mask = pf->hw.debug_mask; + + if (debug_mask) + netdev_info(netdev, "i40e debug_mask: 0x%08X\n", debug_mask); return pf->msg_enable; } @@ -989,7 +993,8 @@ static void i40e_set_msglevel(struct net_device *netdev, u32 data) if (I40E_DEBUG_USER & data) pf->hw.debug_mask = data; - pf->msg_enable = data; + else + pf->msg_enable = data; } static int i40e_get_regs_len(struct net_device *netdev) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0c2328d..7fa535f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -93,8 +93,8 @@ MODULE_DEVICE_TABLE(pci, i40e_pci_tbl); #define I40E_MAX_VF_COUNT 128 static int debug = -1; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); +module_param(debug, uint, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all), Debug mask (0x8XXXXXXX)"); MODULE_AUTHOR("Intel Corporation, "); MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); @@ -8511,15 +8511,6 @@ static int i40e_sw_init(struct i40e_pf *pf) int err = 0; int size; - pf->msg_enable = netif_msg_init(I40E_DEFAULT_MSG_ENABLE, - (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK)); - if (debug != -1 && debug != I40E_DEFAULT_MSG_ENABLE) { - if (I40E_DEBUG_USER & debug) - pf->hw.debug_mask = debug; - pf->msg_enable = netif_msg_init((debug & ~I40E_DEBUG_USER), - I40E_DEFAULT_MSG_ENABLE); - } - /* Set default capability flags */ pf->flags = I40E_FLAG_RX_CSUM_ENABLED | I40E_FLAG_MSI_ENABLED | @@ -10825,10 +10816,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mutex_init(&hw->aq.asq_mutex); mutex_init(&hw->aq.arq_mutex); - if (debug != -1) { - pf->msg_enable = pf->hw.debug_mask; - pf->msg_enable = debug; - } + pf->msg_enable = netif_msg_init(debug, + NETIF_MSG_DRV | + NETIF_MSG_PROBE | + NETIF_MSG_LINK); + if (debug < -1) + pf->hw.debug_mask = debug; /* do a special CORER for clearing PXE mode once at init */ if (hw->revision_id == 0 && -- cgit v1.1 From 93a205ee98a4881e8bf608e65562c19d45930a93 Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Tue, 25 Oct 2016 16:12:12 -0400 Subject: virtio-net: Update the mtu code to match virtio spec The virtio committee recently ratified a change, VIRTIO-152, which defines the mtu field to be 'max' MTU, not simply desired MTU. This commit brings the virtio-net device in compliance with VIRTIO-152. Additionally, drop the max_mtu branch - it cannot be taken since the u16 returned by virtio_cread16 will never exceed the initial value of max_mtu. Signed-off-by: Aaron Conole Acked-by: "Michael S. Tsirkin" Acked-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 720809f..2cafd12 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1870,10 +1870,12 @@ static int virtnet_probe(struct virtio_device *vdev) mtu = virtio_cread16(vdev, offsetof(struct virtio_net_config, mtu)); - if (mtu < dev->min_mtu || mtu > dev->max_mtu) + if (mtu < dev->min_mtu) { __virtio_clear_bit(vdev, VIRTIO_NET_F_MTU); - else + } else { dev->mtu = mtu; + dev->max_mtu = mtu; + } } if (vi->any_header_sg) -- cgit v1.1 From 0f16bc1355bab719958577b2e519a8cef1637b28 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Tue, 25 Oct 2016 22:26:09 +0800 Subject: driver: tun: Move tun check into the block of TUNSETIFF condition check When cmd is TUNSETIFF and tun is not null, the original codes go ahead, then reach the default case of switch(cmd) and set the ret is -EINVAL. It is not clear for readers. Now move the tun check into the block of TUNSETIFF condition check, and return -EEXIST instead of -EINVAL when the tfile already owns one tun. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/tun.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9328568..9142db8 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2005,7 +2005,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, rtnl_lock(); tun = __tun_get(tfile); - if (cmd == TUNSETIFF && !tun) { + if (cmd == TUNSETIFF) { + ret = -EEXIST; + if (tun) + goto unlock; + ifr.ifr_name[IFNAMSIZ-1] = '\0'; ret = tun_set_iff(sock_net(&tfile->sk), file, &ifr); -- cgit v1.1 From f8da977989c5d5d2f38e025d4ac1e18243723dbb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Oct 2016 17:53:22 +0200 Subject: net: ip, diag: include net/inet_sock.h The newly added raw_diag.c fails to build in some configurations unless we include this header: In file included from net/ipv4/raw_diag.c:6:0: include/net/raw.h:71:21: error: field 'inet' has incomplete type net/ipv4/raw_diag.c: In function 'raw_diag_dump': net/ipv4/raw_diag.c:166:29: error: implicit declaration of function 'inet_sk' Fixes: 432490f9d455 ("net: ip, diag -- Add diag interface for raw sockets") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- net/ipv4/raw_diag.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index ef3bea0..be93090 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -3,6 +3,7 @@ #include #include +#include #include #include -- cgit v1.1 From b15ca182ed136087f6a2cb9ffe880c923f36a56e Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 26 Oct 2016 10:53:16 +0200 Subject: netlink: Add nla_memdup() to wrap kmemdup() use on nlattr Wrap several common instances of: kmemdup(nla_data(attr), nla_len(attr), GFP_KERNEL); Signed-off-by: Thomas Graf Acked-by: Johannes Berg Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/netlink.h | 10 ++++++++++ net/sched/act_bpf.c | 4 +--- net/sched/cls_bpf.c | 4 +--- net/wireless/nl80211.c | 3 +-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/net/netlink.h b/include/net/netlink.h index 254a0fc..a34f53a 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -1191,6 +1191,16 @@ static inline struct in6_addr nla_get_in6_addr(const struct nlattr *nla) } /** + * nla_memdup - duplicate attribute memory (kmemdup) + * @src: netlink attribute to duplicate from + * @gfp: GFP mask + */ +static inline void *nla_memdup(const struct nlattr *src, gfp_t gfp) +{ + return kmemdup(nla_data(src), nla_len(src), gfp); +} + +/** * nla_nest_start - Start a new level of nested attributes * @skb: socket buffer to add attributes to * @attrtype: attribute type of container diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 1d39600..9ff06cf 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -226,9 +226,7 @@ static int tcf_bpf_init_from_efd(struct nlattr **tb, struct tcf_bpf_cfg *cfg) return PTR_ERR(fp); if (tb[TCA_ACT_BPF_NAME]) { - name = kmemdup(nla_data(tb[TCA_ACT_BPF_NAME]), - nla_len(tb[TCA_ACT_BPF_NAME]), - GFP_KERNEL); + name = nla_memdup(tb[TCA_ACT_BPF_NAME], GFP_KERNEL); if (!name) { bpf_prog_put(fp); return -ENOMEM; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index bb1d5a4..52dc85a 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -369,9 +369,7 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, return PTR_ERR(fp); if (tb[TCA_BPF_NAME]) { - name = kmemdup(nla_data(tb[TCA_BPF_NAME]), - nla_len(tb[TCA_BPF_NAME]), - GFP_KERNEL); + name = nla_memdup(tb[TCA_BPF_NAME], GFP_KERNEL); if (!name) { bpf_prog_put(fp); return -ENOMEM; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 271707d..0d3ab4b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10625,8 +10625,7 @@ static int handle_nan_filter(struct nlattr *attr_filter, i = 0; nla_for_each_nested(attr, attr_filter, rem) { - filter[i].filter = kmemdup(nla_data(attr), nla_len(attr), - GFP_KERNEL); + filter[i].filter = nla_memdup(attr, GFP_KERNEL); filter[i].len = nla_len(attr); i++; } -- cgit v1.1 From c778453b138889587eee23e246e231bb12d1e80d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 26 Oct 2016 12:03:03 +0300 Subject: switchdev: Remove redundant variable Instead of storing return value in 'err' and returning, just return directly. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- net/switchdev/switchdev.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 02beb35..6f145b5 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -624,13 +624,10 @@ EXPORT_SYMBOL_GPL(unregister_switchdev_notifier); int call_switchdev_notifiers(unsigned long val, struct net_device *dev, struct switchdev_notifier_info *info) { - int err; - ASSERT_RTNL(); info->dev = dev; - err = raw_notifier_call_chain(&switchdev_notif_chain, val, info); - return err; + return raw_notifier_call_chain(&switchdev_notif_chain, val, info); } EXPORT_SYMBOL_GPL(call_switchdev_notifiers); -- cgit v1.1 From 58effd7168e9b74252650a008b5229c7cf224c7b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 26 Oct 2016 14:03:54 -0200 Subject: net: phy: at803x: Add a definition for PHY ID mask Add a definition for PHY ID mask for improving code readability. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/phy/at803x.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index f279a89..dd47b69 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -58,6 +58,7 @@ #define ATH8030_PHY_ID 0x004dd076 #define ATH8031_PHY_ID 0x004dd074 #define ATH8035_PHY_ID 0x004dd072 +#define AT803X_PHY_ID_MASK 0xffffffef MODULE_DESCRIPTION("Atheros 803x PHY driver"); MODULE_AUTHOR("Matus Ujhelyi"); @@ -386,7 +387,7 @@ static struct phy_driver at803x_driver[] = { /* ATHEROS 8035 */ .phy_id = ATH8035_PHY_ID, .name = "Atheros 8035 ethernet", - .phy_id_mask = 0xffffffef, + .phy_id_mask = AT803X_PHY_ID_MASK, .probe = at803x_probe, .config_init = at803x_config_init, .set_wol = at803x_set_wol, @@ -403,7 +404,7 @@ static struct phy_driver at803x_driver[] = { /* ATHEROS 8030 */ .phy_id = ATH8030_PHY_ID, .name = "Atheros 8030 ethernet", - .phy_id_mask = 0xffffffef, + .phy_id_mask = AT803X_PHY_ID_MASK, .probe = at803x_probe, .config_init = at803x_config_init, .link_change_notify = at803x_link_change_notify, @@ -421,7 +422,7 @@ static struct phy_driver at803x_driver[] = { /* ATHEROS 8031 */ .phy_id = ATH8031_PHY_ID, .name = "Atheros 8031 ethernet", - .phy_id_mask = 0xffffffef, + .phy_id_mask = AT803X_PHY_ID_MASK, .probe = at803x_probe, .config_init = at803x_config_init, .set_wol = at803x_set_wol, @@ -439,9 +440,9 @@ static struct phy_driver at803x_driver[] = { module_phy_driver(at803x_driver); static struct mdio_device_id __maybe_unused atheros_tbl[] = { - { ATH8030_PHY_ID, 0xffffffef }, - { ATH8031_PHY_ID, 0xffffffef }, - { ATH8035_PHY_ID, 0xffffffef }, + { ATH8030_PHY_ID, AT803X_PHY_ID_MASK }, + { ATH8031_PHY_ID, AT803X_PHY_ID_MASK }, + { ATH8035_PHY_ID, AT803X_PHY_ID_MASK }, { } }; -- cgit v1.1 From 5ea8ea2cb7f1d0db15762c9b0bb9e7330425a071 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 26 Oct 2016 09:27:57 -0700 Subject: tcp/dccp: drop SYN packets if accept queue is full Per listen(fd, backlog) rules, there is really no point accepting a SYN, sending a SYNACK, and dropping the following ACK packet if accept queue is full, because application is not draining accept queue fast enough. This behavior is fooling TCP clients that believe they established a flow, while there is nothing at server side. They might then send about 10 MSS (if using IW10) that will be dropped anyway while server is under stress. Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- include/net/inet_connection_sock.h | 5 ----- net/dccp/ipv4.c | 8 +------- net/dccp/ipv6.c | 2 +- net/ipv4/tcp_input.c | 8 +------- 4 files changed, 3 insertions(+), 20 deletions(-) diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 197a30d..146054c 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -289,11 +289,6 @@ static inline int inet_csk_reqsk_queue_len(const struct sock *sk) return reqsk_queue_len(&inet_csk(sk)->icsk_accept_queue); } -static inline int inet_csk_reqsk_queue_young(const struct sock *sk) -{ - return reqsk_queue_len_young(&inet_csk(sk)->icsk_accept_queue); -} - static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk) { return inet_csk_reqsk_queue_len(sk) >= sk->sk_max_ack_backlog; diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 345a3ae..a957aca 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -588,13 +588,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (inet_csk_reqsk_queue_is_full(sk)) goto drop; - /* - * Accept backlog is full. If we have already queued enough - * of warm entries in syn queue, drop request. It is better than - * clogging syn queue with openreqs with exponentially increasing - * timeout. - */ - if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) + if (sk_acceptq_is_full(sk)) goto drop; req = inet_reqsk_alloc(&dccp_request_sock_ops, sk, true); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 3828f94..32f9f1a 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -325,7 +325,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (inet_csk_reqsk_queue_is_full(sk)) goto drop; - if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) + if (sk_acceptq_is_full(sk)) goto drop; req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk, true); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a27b9c0..f2c59c8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6298,13 +6298,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop; } - - /* Accept backlog is full. If we have already queued enough - * of warm entries in syn queue, drop request. It is better than - * clogging syn queue with openreqs with exponentially increasing - * timeout. - */ - if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) { + if (sk_acceptq_is_full(sk)) { NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); goto drop; } -- cgit v1.1 From b917783c7b350518f8c5d88bb5848aa8064408a6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 26 Oct 2016 18:49:46 +0200 Subject: flow_dissector: __skb_get_hash_symmetric arg can be const Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 +- net/core/flow_dissector.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 601258f..663fda2 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1086,7 +1086,7 @@ __skb_set_sw_hash(struct sk_buff *skb, __u32 hash, bool is_l4) } void __skb_get_hash(struct sk_buff *skb); -u32 __skb_get_hash_symmetric(struct sk_buff *skb); +u32 __skb_get_hash_symmetric(const struct sk_buff *skb); u32 skb_get_poff(const struct sk_buff *skb); u32 __skb_get_poff(const struct sk_buff *skb, void *data, const struct flow_keys *keys, int hlen); diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 1a7b80f..0cc607d 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -723,7 +723,7 @@ EXPORT_SYMBOL(make_flow_keys_digest); static struct flow_dissector flow_keys_dissector_symmetric __read_mostly; -u32 __skb_get_hash_symmetric(struct sk_buff *skb) +u32 __skb_get_hash_symmetric(const struct sk_buff *skb) { struct flow_keys keys; -- cgit v1.1 From 46b5ab1a7cfef72cc15e9de135650851619bc406 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 26 Oct 2016 13:21:33 -0700 Subject: net: dev: Fix non-RCU based lower dev walker netdev_walk_all_lower_dev is not properly walking the lower device list. Commit 1a3f060c1a47 made netdev_walk_all_lower_dev similar to netdev_walk_all_upper_dev_rcu and netdev_walk_all_lower_dev_rcu but failed to update its netdev_next_lower_dev iterator. This patch fixes that. Fixes: 1a3f060c1a47 ("net: Introduce new api for walking upper and lower devices") Reported-by: Ido Schimmel Signed-off-by: David Ahern Tested-by: Ido Schimmel Signed-off-by: David S. Miller --- net/core/dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index f55fb45..6aa43cd 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5400,12 +5400,12 @@ static struct net_device *netdev_next_lower_dev(struct net_device *dev, { struct netdev_adjacent *lower; - lower = list_entry(*iter, struct netdev_adjacent, list); + lower = list_entry((*iter)->next, struct netdev_adjacent, list); if (&lower->list == &dev->adj_list.lower) return NULL; - *iter = lower->list.next; + *iter = &lower->list; return lower->dev; } -- cgit v1.1 From ebb676daa1a340ccef25eb769aefc09b79c01f8a Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Thu, 27 Oct 2016 11:23:51 +0200 Subject: bpf: Print function name in addition to function id The verifier currently prints raw function ids when printing CALL instructions or when complaining: 5: (85) call 23 unknown func 23 print a meaningful function name instead: 5: (85) call bpf_redirect#23 unknown func bpf_redirect#23 Moves the function documentation to a single comment and renames all helpers names in the list to conform to the bpf_ prefix notation so they can be greped in the kernel source. Signed-off-by: Thomas Graf Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 574 ++++++++++++++++++++++++----------------------- kernel/bpf/verifier.c | 35 ++- 2 files changed, 316 insertions(+), 293 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 374ef58..e2f38e0 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -143,297 +143,301 @@ union bpf_attr { }; } __attribute__((aligned(8))); +/* BPF helper function descriptions: + * + * void *bpf_map_lookup_elem(&map, &key) + * Return: Map value or NULL + * + * int bpf_map_update_elem(&map, &key, &value, flags) + * Return: 0 on success or negative error + * + * int bpf_map_delete_elem(&map, &key) + * Return: 0 on success or negative error + * + * int bpf_probe_read(void *dst, int size, void *src) + * Return: 0 on success or negative error + * + * u64 bpf_ktime_get_ns(void) + * Return: current ktime + * + * int bpf_trace_printk(const char *fmt, int fmt_size, ...) + * Return: length of buffer written or negative error + * + * u32 bpf_prandom_u32(void) + * Return: random value + * + * u32 bpf_raw_smp_processor_id(void) + * Return: SMP processor ID + * + * int bpf_skb_store_bytes(skb, offset, from, len, flags) + * store bytes into packet + * @skb: pointer to skb + * @offset: offset within packet from skb->mac_header + * @from: pointer where to copy bytes from + * @len: number of bytes to store into packet + * @flags: bit 0 - if true, recompute skb->csum + * other bits - reserved + * Return: 0 on success or negative error + * + * int bpf_l3_csum_replace(skb, offset, from, to, flags) + * recompute IP checksum + * @skb: pointer to skb + * @offset: offset within packet where IP checksum is located + * @from: old value of header field + * @to: new value of header field + * @flags: bits 0-3 - size of header field + * other bits - reserved + * Return: 0 on success or negative error + * + * int bpf_l4_csum_replace(skb, offset, from, to, flags) + * recompute TCP/UDP checksum + * @skb: pointer to skb + * @offset: offset within packet where TCP/UDP checksum is located + * @from: old value of header field + * @to: new value of header field + * @flags: bits 0-3 - size of header field + * bit 4 - is pseudo header + * other bits - reserved + * Return: 0 on success or negative error + * + * int bpf_tail_call(ctx, prog_array_map, index) + * jump into another BPF program + * @ctx: context pointer passed to next program + * @prog_array_map: pointer to map which type is BPF_MAP_TYPE_PROG_ARRAY + * @index: index inside array that selects specific program to run + * Return: 0 on success or negative error + * + * int bpf_clone_redirect(skb, ifindex, flags) + * redirect to another netdev + * @skb: pointer to skb + * @ifindex: ifindex of the net device + * @flags: bit 0 - if set, redirect to ingress instead of egress + * other bits - reserved + * Return: 0 on success or negative error + * + * u64 bpf_get_current_pid_tgid(void) + * Return: current->tgid << 32 | current->pid + * + * u64 bpf_get_current_uid_gid(void) + * Return: current_gid << 32 | current_uid + * + * int bpf_get_current_comm(char *buf, int size_of_buf) + * stores current->comm into buf + * Return: 0 on success or negative error + * + * u32 bpf_get_cgroup_classid(skb) + * retrieve a proc's classid + * @skb: pointer to skb + * Return: classid if != 0 + * + * int bpf_skb_vlan_push(skb, vlan_proto, vlan_tci) + * Return: 0 on success or negative error + * + * int bpf_skb_vlan_pop(skb) + * Return: 0 on success or negative error + * + * int bpf_skb_get_tunnel_key(skb, key, size, flags) + * int bpf_skb_set_tunnel_key(skb, key, size, flags) + * retrieve or populate tunnel metadata + * @skb: pointer to skb + * @key: pointer to 'struct bpf_tunnel_key' + * @size: size of 'struct bpf_tunnel_key' + * @flags: room for future extensions + * Return: 0 on success or negative error + * + * u64 bpf_perf_event_read(&map, index) + * Return: Number events read or error code + * + * int bpf_redirect(ifindex, flags) + * redirect to another netdev + * @ifindex: ifindex of the net device + * @flags: bit 0 - if set, redirect to ingress instead of egress + * other bits - reserved + * Return: TC_ACT_REDIRECT + * + * u32 bpf_get_route_realm(skb) + * retrieve a dst's tclassid + * @skb: pointer to skb + * Return: realm if != 0 + * + * int bpf_perf_event_output(ctx, map, index, data, size) + * output perf raw sample + * @ctx: struct pt_regs* + * @map: pointer to perf_event_array map + * @index: index of event in the map + * @data: data on stack to be output as raw data + * @size: size of data + * Return: 0 on success or negative error + * + * int bpf_get_stackid(ctx, map, flags) + * walk user or kernel stack and return id + * @ctx: struct pt_regs* + * @map: pointer to stack_trace map + * @flags: bits 0-7 - numer of stack frames to skip + * bit 8 - collect user stack instead of kernel + * bit 9 - compare stacks by hash only + * bit 10 - if two different stacks hash into the same stackid + * discard old + * other bits - reserved + * Return: >= 0 stackid on success or negative error + * + * s64 bpf_csum_diff(from, from_size, to, to_size, seed) + * calculate csum diff + * @from: raw from buffer + * @from_size: length of from buffer + * @to: raw to buffer + * @to_size: length of to buffer + * @seed: optional seed + * Return: csum result or negative error code + * + * int bpf_skb_get_tunnel_opt(skb, opt, size) + * retrieve tunnel options metadata + * @skb: pointer to skb + * @opt: pointer to raw tunnel option data + * @size: size of @opt + * Return: option size + * + * int bpf_skb_set_tunnel_opt(skb, opt, size) + * populate tunnel options metadata + * @skb: pointer to skb + * @opt: pointer to raw tunnel option data + * @size: size of @opt + * Return: 0 on success or negative error + * + * int bpf_skb_change_proto(skb, proto, flags) + * Change protocol of the skb. Currently supported is v4 -> v6, + * v6 -> v4 transitions. The helper will also resize the skb. eBPF + * program is expected to fill the new headers via skb_store_bytes + * and lX_csum_replace. + * @skb: pointer to skb + * @proto: new skb->protocol type + * @flags: reserved + * Return: 0 on success or negative error + * + * int bpf_skb_change_type(skb, type) + * Change packet type of skb. + * @skb: pointer to skb + * @type: new skb->pkt_type type + * Return: 0 on success or negative error + * + * int bpf_skb_under_cgroup(skb, map, index) + * Check cgroup2 membership of skb + * @skb: pointer to skb + * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type + * @index: index of the cgroup in the bpf_map + * Return: + * == 0 skb failed the cgroup2 descendant test + * == 1 skb succeeded the cgroup2 descendant test + * < 0 error + * + * u32 bpf_get_hash_recalc(skb) + * Retrieve and possibly recalculate skb->hash. + * @skb: pointer to skb + * Return: hash + * + * u64 bpf_get_current_task(void) + * Returns current task_struct + * Return: current + * + * int bpf_probe_write_user(void *dst, void *src, int len) + * safely attempt to write to a location + * @dst: destination address in userspace + * @src: source address on stack + * @len: number of bytes to copy + * Return: 0 on success or negative error + * + * int bpf_current_task_under_cgroup(map, index) + * Check cgroup2 membership of current task + * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type + * @index: index of the cgroup in the bpf_map + * Return: + * == 0 current failed the cgroup2 descendant test + * == 1 current succeeded the cgroup2 descendant test + * < 0 error + * + * int bpf_skb_change_tail(skb, len, flags) + * The helper will resize the skb to the given new size, to be used f.e. + * with control messages. + * @skb: pointer to skb + * @len: new skb length + * @flags: reserved + * Return: 0 on success or negative error + * + * int bpf_skb_pull_data(skb, len) + * The helper will pull in non-linear data in case the skb is non-linear + * and not all of len are part of the linear section. Only needed for + * read/write with direct packet access. + * @skb: pointer to skb + * @len: len to make read/writeable + * Return: 0 on success or negative error + * + * s64 bpf_csum_update(skb, csum) + * Adds csum into skb->csum in case of CHECKSUM_COMPLETE. + * @skb: pointer to skb + * @csum: csum to add + * Return: csum on success or negative error + * + * void bpf_set_hash_invalid(skb) + * Invalidate current skb->hash. + * @skb: pointer to skb + * + * int bpf_get_numa_node_id() + * Return: Id of current NUMA node. + */ +#define __BPF_FUNC_MAPPER(FN) \ + FN(unspec), \ + FN(map_lookup_elem), \ + FN(map_update_elem), \ + FN(map_delete_elem), \ + FN(probe_read), \ + FN(ktime_get_ns), \ + FN(trace_printk), \ + FN(get_prandom_u32), \ + FN(get_smp_processor_id), \ + FN(skb_store_bytes), \ + FN(l3_csum_replace), \ + FN(l4_csum_replace), \ + FN(tail_call), \ + FN(clone_redirect), \ + FN(get_current_pid_tgid), \ + FN(get_current_uid_gid), \ + FN(get_current_comm), \ + FN(get_cgroup_classid), \ + FN(skb_vlan_push), \ + FN(skb_vlan_pop), \ + FN(skb_get_tunnel_key), \ + FN(skb_set_tunnel_key), \ + FN(perf_event_read), \ + FN(redirect), \ + FN(get_route_realm), \ + FN(perf_event_output), \ + FN(skb_load_bytes), \ + FN(get_stackid), \ + FN(csum_diff), \ + FN(skb_get_tunnel_opt), \ + FN(skb_set_tunnel_opt), \ + FN(skb_change_proto), \ + FN(skb_change_type), \ + FN(skb_under_cgroup), \ + FN(get_hash_recalc), \ + FN(get_current_task), \ + FN(probe_write_user), \ + FN(current_task_under_cgroup), \ + FN(skb_change_tail), \ + FN(skb_pull_data), \ + FN(csum_update), \ + FN(set_hash_invalid), \ + FN(get_numa_node_id), + /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call */ +#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x enum bpf_func_id { - BPF_FUNC_unspec, - BPF_FUNC_map_lookup_elem, /* void *map_lookup_elem(&map, &key) */ - BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */ - BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ - BPF_FUNC_probe_read, /* int bpf_probe_read(void *dst, int size, void *src) */ - BPF_FUNC_ktime_get_ns, /* u64 bpf_ktime_get_ns(void) */ - BPF_FUNC_trace_printk, /* int bpf_trace_printk(const char *fmt, int fmt_size, ...) */ - BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */ - BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */ - - /** - * skb_store_bytes(skb, offset, from, len, flags) - store bytes into packet - * @skb: pointer to skb - * @offset: offset within packet from skb->mac_header - * @from: pointer where to copy bytes from - * @len: number of bytes to store into packet - * @flags: bit 0 - if true, recompute skb->csum - * other bits - reserved - * Return: 0 on success - */ - BPF_FUNC_skb_store_bytes, - - /** - * l3_csum_replace(skb, offset, from, to, flags) - recompute IP checksum - * @skb: pointer to skb - * @offset: offset within packet where IP checksum is located - * @from: old value of header field - * @to: new value of header field - * @flags: bits 0-3 - size of header field - * other bits - reserved - * Return: 0 on success - */ - BPF_FUNC_l3_csum_replace, - - /** - * l4_csum_replace(skb, offset, from, to, flags) - recompute TCP/UDP checksum - * @skb: pointer to skb - * @offset: offset within packet where TCP/UDP checksum is located - * @from: old value of header field - * @to: new value of header field - * @flags: bits 0-3 - size of header field - * bit 4 - is pseudo header - * other bits - reserved - * Return: 0 on success - */ - BPF_FUNC_l4_csum_replace, - - /** - * bpf_tail_call(ctx, prog_array_map, index) - jump into another BPF program - * @ctx: context pointer passed to next program - * @prog_array_map: pointer to map which type is BPF_MAP_TYPE_PROG_ARRAY - * @index: index inside array that selects specific program to run - * Return: 0 on success - */ - BPF_FUNC_tail_call, - - /** - * bpf_clone_redirect(skb, ifindex, flags) - redirect to another netdev - * @skb: pointer to skb - * @ifindex: ifindex of the net device - * @flags: bit 0 - if set, redirect to ingress instead of egress - * other bits - reserved - * Return: 0 on success - */ - BPF_FUNC_clone_redirect, - - /** - * u64 bpf_get_current_pid_tgid(void) - * Return: current->tgid << 32 | current->pid - */ - BPF_FUNC_get_current_pid_tgid, - - /** - * u64 bpf_get_current_uid_gid(void) - * Return: current_gid << 32 | current_uid - */ - BPF_FUNC_get_current_uid_gid, - - /** - * bpf_get_current_comm(char *buf, int size_of_buf) - * stores current->comm into buf - * Return: 0 on success - */ - BPF_FUNC_get_current_comm, - - /** - * bpf_get_cgroup_classid(skb) - retrieve a proc's classid - * @skb: pointer to skb - * Return: classid if != 0 - */ - BPF_FUNC_get_cgroup_classid, - BPF_FUNC_skb_vlan_push, /* bpf_skb_vlan_push(skb, vlan_proto, vlan_tci) */ - BPF_FUNC_skb_vlan_pop, /* bpf_skb_vlan_pop(skb) */ - - /** - * bpf_skb_[gs]et_tunnel_key(skb, key, size, flags) - * retrieve or populate tunnel metadata - * @skb: pointer to skb - * @key: pointer to 'struct bpf_tunnel_key' - * @size: size of 'struct bpf_tunnel_key' - * @flags: room for future extensions - * Retrun: 0 on success - */ - BPF_FUNC_skb_get_tunnel_key, - BPF_FUNC_skb_set_tunnel_key, - BPF_FUNC_perf_event_read, /* u64 bpf_perf_event_read(&map, index) */ - /** - * bpf_redirect(ifindex, flags) - redirect to another netdev - * @ifindex: ifindex of the net device - * @flags: bit 0 - if set, redirect to ingress instead of egress - * other bits - reserved - * Return: TC_ACT_REDIRECT - */ - BPF_FUNC_redirect, - - /** - * bpf_get_route_realm(skb) - retrieve a dst's tclassid - * @skb: pointer to skb - * Return: realm if != 0 - */ - BPF_FUNC_get_route_realm, - - /** - * bpf_perf_event_output(ctx, map, index, data, size) - output perf raw sample - * @ctx: struct pt_regs* - * @map: pointer to perf_event_array map - * @index: index of event in the map - * @data: data on stack to be output as raw data - * @size: size of data - * Return: 0 on success - */ - BPF_FUNC_perf_event_output, - BPF_FUNC_skb_load_bytes, - - /** - * bpf_get_stackid(ctx, map, flags) - walk user or kernel stack and return id - * @ctx: struct pt_regs* - * @map: pointer to stack_trace map - * @flags: bits 0-7 - numer of stack frames to skip - * bit 8 - collect user stack instead of kernel - * bit 9 - compare stacks by hash only - * bit 10 - if two different stacks hash into the same stackid - * discard old - * other bits - reserved - * Return: >= 0 stackid on success or negative error - */ - BPF_FUNC_get_stackid, - - /** - * bpf_csum_diff(from, from_size, to, to_size, seed) - calculate csum diff - * @from: raw from buffer - * @from_size: length of from buffer - * @to: raw to buffer - * @to_size: length of to buffer - * @seed: optional seed - * Return: csum result - */ - BPF_FUNC_csum_diff, - - /** - * bpf_skb_[gs]et_tunnel_opt(skb, opt, size) - * retrieve or populate tunnel options metadata - * @skb: pointer to skb - * @opt: pointer to raw tunnel option data - * @size: size of @opt - * Return: 0 on success for set, option size for get - */ - BPF_FUNC_skb_get_tunnel_opt, - BPF_FUNC_skb_set_tunnel_opt, - - /** - * bpf_skb_change_proto(skb, proto, flags) - * Change protocol of the skb. Currently supported is - * v4 -> v6, v6 -> v4 transitions. The helper will also - * resize the skb. eBPF program is expected to fill the - * new headers via skb_store_bytes and lX_csum_replace. - * @skb: pointer to skb - * @proto: new skb->protocol type - * @flags: reserved - * Return: 0 on success or negative error - */ - BPF_FUNC_skb_change_proto, - - /** - * bpf_skb_change_type(skb, type) - * Change packet type of skb. - * @skb: pointer to skb - * @type: new skb->pkt_type type - * Return: 0 on success or negative error - */ - BPF_FUNC_skb_change_type, - - /** - * bpf_skb_under_cgroup(skb, map, index) - Check cgroup2 membership of skb - * @skb: pointer to skb - * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type - * @index: index of the cgroup in the bpf_map - * Return: - * == 0 skb failed the cgroup2 descendant test - * == 1 skb succeeded the cgroup2 descendant test - * < 0 error - */ - BPF_FUNC_skb_under_cgroup, - - /** - * bpf_get_hash_recalc(skb) - * Retrieve and possibly recalculate skb->hash. - * @skb: pointer to skb - * Return: hash - */ - BPF_FUNC_get_hash_recalc, - - /** - * u64 bpf_get_current_task(void) - * Returns current task_struct - * Return: current - */ - BPF_FUNC_get_current_task, - - /** - * bpf_probe_write_user(void *dst, void *src, int len) - * safely attempt to write to a location - * @dst: destination address in userspace - * @src: source address on stack - * @len: number of bytes to copy - * Return: 0 on success or negative error - */ - BPF_FUNC_probe_write_user, - - /** - * bpf_current_task_under_cgroup(map, index) - Check cgroup2 membership of current task - * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type - * @index: index of the cgroup in the bpf_map - * Return: - * == 0 current failed the cgroup2 descendant test - * == 1 current succeeded the cgroup2 descendant test - * < 0 error - */ - BPF_FUNC_current_task_under_cgroup, - - /** - * bpf_skb_change_tail(skb, len, flags) - * The helper will resize the skb to the given new size, - * to be used f.e. with control messages. - * @skb: pointer to skb - * @len: new skb length - * @flags: reserved - * Return: 0 on success or negative error - */ - BPF_FUNC_skb_change_tail, - - /** - * bpf_skb_pull_data(skb, len) - * The helper will pull in non-linear data in case the - * skb is non-linear and not all of len are part of the - * linear section. Only needed for read/write with direct - * packet access. - * @skb: pointer to skb - * @len: len to make read/writeable - * Return: 0 on success or negative error - */ - BPF_FUNC_skb_pull_data, - - /** - * bpf_csum_update(skb, csum) - * Adds csum into skb->csum in case of CHECKSUM_COMPLETE. - * @skb: pointer to skb - * @csum: csum to add - * Return: csum on success or negative error - */ - BPF_FUNC_csum_update, - - /** - * bpf_set_hash_invalid(skb) - * Invalidate current skb>hash. - * @skb: pointer to skb - */ - BPF_FUNC_set_hash_invalid, - - /** - * bpf_get_numa_node_id() - * Returns the id of the current NUMA node. - */ - BPF_FUNC_get_numa_node_id, - + __BPF_FUNC_MAPPER(__BPF_ENUM_FN) __BPF_FUNC_MAX_ID, }; +#undef __BPF_ENUM_FN /* All flags used by eBPF helper functions, placed here. */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 846d7ce..9002575 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19,6 +19,7 @@ #include #include #include +#include /* bpf_check() is a static code analyzer that walks eBPF program * instruction by instruction and updates register/stack state. @@ -190,6 +191,22 @@ static const char * const reg_type_str[] = { [PTR_TO_PACKET_END] = "pkt_end", }; +#define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x) +static const char * const func_id_str[] = { + __BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN) +}; +#undef __BPF_FUNC_STR_FN + +static const char *func_id_name(int id) +{ + BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID); + + if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id]) + return func_id_str[id]; + else + return "unknown"; +} + static void print_verifier_state(struct bpf_verifier_state *state) { struct bpf_reg_state *reg; @@ -354,7 +371,8 @@ static void print_bpf_insn(struct bpf_insn *insn) u8 opcode = BPF_OP(insn->code); if (opcode == BPF_CALL) { - verbose("(%02x) call %d\n", insn->code, insn->imm); + verbose("(%02x) call %s#%d\n", insn->code, + func_id_name(insn->imm), insn->imm); } else if (insn->code == (BPF_JMP | BPF_JA)) { verbose("(%02x) goto pc%+d\n", insn->code, insn->off); @@ -1114,8 +1132,8 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) return 0; error: - verbose("cannot pass map_type %d into func %d\n", - map->map_type, func_id); + verbose("cannot pass map_type %d into func %s#%d\n", + map->map_type, func_id_name(func_id), func_id); return -EINVAL; } @@ -1172,7 +1190,7 @@ static int check_call(struct bpf_verifier_env *env, int func_id) /* find function prototype */ if (func_id < 0 || func_id >= __BPF_FUNC_MAX_ID) { - verbose("invalid func %d\n", func_id); + verbose("invalid func %s#%d\n", func_id_name(func_id), func_id); return -EINVAL; } @@ -1180,7 +1198,7 @@ static int check_call(struct bpf_verifier_env *env, int func_id) fn = env->prog->aux->ops->get_func_proto(func_id); if (!fn) { - verbose("unknown func %d\n", func_id); + verbose("unknown func %s#%d\n", func_id_name(func_id), func_id); return -EINVAL; } @@ -1200,7 +1218,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id) */ err = check_raw_mode(fn); if (err) { - verbose("kernel subsystem misconfigured func %d\n", func_id); + verbose("kernel subsystem misconfigured func %s#%d\n", + func_id_name(func_id), func_id); return err; } @@ -1256,8 +1275,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id) regs[BPF_REG_0].map_ptr = meta.map_ptr; regs[BPF_REG_0].id = ++env->id_gen; } else { - verbose("unknown return type %d of func %d\n", - fn->ret_type, func_id); + verbose("unknown return type %d of func %s#%d\n", + fn->ret_type, func_id_name(func_id), func_id); return -EINVAL; } -- cgit v1.1 From 9b9375b5b7fdf75804de7df956f8e7ef0190c69b Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Thu, 27 Oct 2016 13:26:37 -0400 Subject: tcp_bbr: add a state transition diagram and accompanying comment Document the possible state transitions for a BBR flow, and also add a prose summary of the state machine, covering the life of a typical BBR flow. Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: Eric Dumazet Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- net/ipv4/tcp_bbr.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index 0ea66c2..b89bce4 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -14,6 +14,36 @@ * observed, or adjust the sending rate if it estimates there is a * traffic policer, in order to keep the drop rate reasonable. * + * Here is a state transition diagram for BBR: + * + * | + * V + * +---> STARTUP ----+ + * | | | + * | V | + * | DRAIN ----+ + * | | | + * | V | + * +---> PROBE_BW ----+ + * | ^ | | + * | | | | + * | +----+ | + * | | + * +---- PROBE_RTT <--+ + * + * A BBR flow starts in STARTUP, and ramps up its sending rate quickly. + * When it estimates the pipe is full, it enters DRAIN to drain the queue. + * In steady state a BBR flow only uses PROBE_BW and PROBE_RTT. + * A long-lived BBR flow spends the vast majority of its time remaining + * (repeatedly) in PROBE_BW, fully probing and utilizing the pipe's bandwidth + * in a fair manner, with a small, bounded queue. *If* a flow has been + * continuously sending for the entire min_rtt window, and hasn't seen an RTT + * sample that matches or decreases its min_rtt estimate for 10 seconds, then + * it briefly enters PROBE_RTT to cut inflight to a minimum value to re-probe + * the path's two-way propagation delay (min_rtt). When exiting PROBE_RTT, if + * we estimated that we reached the full bw of the pipe then we enter PROBE_BW; + * otherwise we enter STARTUP to try to fill the pipe. + * * BBR is described in detail in: * "BBR: Congestion-Based Congestion Control", * Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, @@ -51,7 +81,7 @@ enum bbr_mode { BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */ BBR_DRAIN, /* drain any queue created during startup */ BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */ - BBR_PROBE_RTT, /* cut cwnd to min to probe min_rtt */ + BBR_PROBE_RTT, /* cut inflight to min to probe min_rtt */ }; /* BBR congestion control block */ -- cgit v1.1 From 976990569360bcd7bb35f370c76904d109d20cc0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Oct 2016 22:32:22 +0200 Subject: rocker: set physical device for port netdevice Do this so the sysfs has "device" link correctly set. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 2e81b70..55b2ab9 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2534,7 +2534,7 @@ static void rocker_port_dev_addr_init(struct rocker_port *rocker_port) #define ROCKER_PORT_MAX_MTU 9000 static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) { - const struct pci_dev *pdev = rocker->pdev; + struct pci_dev *pdev = rocker->pdev; struct rocker_port *rocker_port; struct net_device *dev; int err; @@ -2542,6 +2542,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) dev = alloc_etherdev(sizeof(struct rocker_port)); if (!dev) return -ENOMEM; + SET_NETDEV_DEV(dev, &pdev->dev); rocker_port = netdev_priv(dev); rocker_port->dev = dev; rocker_port->rocker = rocker; -- cgit v1.1 From af1fee98219992ba2c12441a447719652ed7e983 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Fri, 28 Oct 2016 12:10:11 +0200 Subject: net: phy: Add support for Microsemi VSC 8530/40 Fast Ethernet PHY Signed-off-by: Raju Lakkaraju Signed-off-by: Allan W. Nielsen Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 2 +- drivers/net/phy/mscc.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 45f68ea..ff31c10 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -290,7 +290,7 @@ config MICROCHIP_PHY config MICROSEMI_PHY tristate "Microsemi PHYs" ---help--- - Currently supports the VSC8531 and VSC8541 PHYs + Currently supports VSC8530, VSC8531, VSC8540 and VSC8541 PHYs config NATIONAL_PHY tristate "National Semiconductor PHYs" diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index 113616b..d0026ab 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -65,7 +65,9 @@ enum rgmii_rx_clock_delay { #define SECURE_ON_PASSWD_LEN_4 0x4000 /* Microsemi PHY ID's */ +#define PHY_ID_VSC8530 0x00070560 #define PHY_ID_VSC8531 0x00070570 +#define PHY_ID_VSC8540 0x00070760 #define PHY_ID_VSC8541 0x00070770 #define MSCC_VDDMAC_1500 1500 @@ -399,6 +401,25 @@ static int vsc85xx_probe(struct phy_device *phydev) /* Microsemi VSC85xx PHYs */ static struct phy_driver vsc85xx_driver[] = { { + .phy_id = PHY_ID_VSC8530, + .name = "Microsemi FE VSC8530", + .phy_id_mask = 0xfffffff0, + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .soft_reset = &genphy_soft_reset, + .config_init = &vsc85xx_config_init, + .config_aneg = &genphy_config_aneg, + .aneg_done = &genphy_aneg_done, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc85xx_ack_interrupt, + .config_intr = &vsc85xx_config_intr, + .suspend = &genphy_suspend, + .resume = &genphy_resume, + .probe = &vsc85xx_probe, + .set_wol = &vsc85xx_wol_set, + .get_wol = &vsc85xx_wol_get, +}, +{ .phy_id = PHY_ID_VSC8531, .name = "Microsemi VSC8531", .phy_id_mask = 0xfffffff0, @@ -418,6 +439,25 @@ static struct phy_driver vsc85xx_driver[] = { .get_wol = &vsc85xx_wol_get, }, { + .phy_id = PHY_ID_VSC8540, + .name = "Microsemi FE VSC8540 SyncE", + .phy_id_mask = 0xfffffff0, + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .soft_reset = &genphy_soft_reset, + .config_init = &vsc85xx_config_init, + .config_aneg = &genphy_config_aneg, + .aneg_done = &genphy_aneg_done, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc85xx_ack_interrupt, + .config_intr = &vsc85xx_config_intr, + .suspend = &genphy_suspend, + .resume = &genphy_resume, + .probe = &vsc85xx_probe, + .set_wol = &vsc85xx_wol_set, + .get_wol = &vsc85xx_wol_get, +}, +{ .phy_id = PHY_ID_VSC8541, .name = "Microsemi VSC8541 SyncE", .phy_id_mask = 0xfffffff0, @@ -442,7 +482,9 @@ static struct phy_driver vsc85xx_driver[] = { module_phy_driver(vsc85xx_driver); static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = { + { PHY_ID_VSC8530, 0xfffffff0, }, { PHY_ID_VSC8531, 0xfffffff0, }, + { PHY_ID_VSC8540, 0xfffffff0, }, { PHY_ID_VSC8541, 0xfffffff0, }, { } }; -- cgit v1.1 From 0e82c7635997a11dbcfa98835c179084447ae2b0 Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Fri, 28 Oct 2016 16:01:41 -0700 Subject: genetlink: Fix generic netlink family unregister This patch fixes a typo in unregister operation. Following crash is fixed by this patch. It can be easily reproduced by repeating modprobe and rmmod module that uses genetlink. [ 261.446686] BUG: unable to handle kernel paging request at ffffffffa0264088 [ 261.448921] IP: [] strcmp+0xe/0x30 [ 261.450494] PGD 1c09067 [ 261.451266] PUD 1c0a063 [ 261.452091] PMD 8068d5067 [ 261.452525] PTE 0 [ 261.453164] [ 261.453618] Oops: 0000 [#1] SMP [ 261.454577] Modules linked in: openvswitch(+) ... [ 261.480753] RIP: 0010:[] [] strcmp+0xe/0x30 [ 261.483069] RSP: 0018:ffffc90003c0bc28 EFLAGS: 00010282 [ 261.510145] Call Trace: [ 261.510896] [] genl_family_find_byname+0x5a/0x70 [ 261.512819] [] genl_register_family+0xb9/0x630 [ 261.514805] [] dp_init+0xbc/0x120 [openvswitch] [ 261.518268] [] do_one_initcall+0x3d/0x160 [ 261.525041] [] do_init_module+0x60/0x1f1 [ 261.526754] [] load_module+0x22af/0x2860 [ 261.530144] [] SYSC_finit_module+0x96/0xd0 [ 261.531901] [] SyS_finit_module+0xe/0x10 [ 261.533605] [] do_syscall_64+0x6e/0x180 [ 261.535284] [] entry_SYSCALL64_slow_path+0x25/0x25 [ 261.546512] RIP [] strcmp+0xe/0x30 [ 261.550198] ---[ end trace 76505a814dd68770 ]--- Fixes: 2ae0f17df1c ("genetlink: use idr to track families"). Reported-by: Jarno Rajahalme CC: Johannes Berg Signed-off-by: Pravin B Shelar Reviewed-by: Johannes Berg Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index df0cbcd..caf04d7 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -399,7 +399,7 @@ int genl_unregister_family(const struct genl_family *family) { genl_lock_all(); - if (genl_family_find_byid(family->id)) { + if (!genl_family_find_byid(family->id)) { genl_unlock_all(); return -ENOENT; } -- cgit v1.1 From 357f4aae859b5d74554b0ccbb18556f1df4166c3 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 29 Oct 2016 22:16:58 +0200 Subject: firewire: net: really fix maximum possible MTU The maximum unicast datagram size /without/ link fragmentation is 4096 - 4 = 4092 (max IEEE 1394 async payload size at >= S800 bus speed, minus unfragmented encapssulation header). Max broadcast datagram size without fragmentation is 8 bytes less than that (due to GASP header). The maximum datagram size /with/ link fragmentation is 0xfff = 4095 for unicast and broadcast. This is because the RFC 2734 fragment encapsulation header field for datagram size is only 12 bits wide. Fixes: 5d48f00d836a('firewire: net: fix maximum possible MTU') Signed-off-by: Stefan Richter Signed-off-by: David S. Miller --- drivers/firewire/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 03715e7..363fc5e 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -1465,7 +1465,7 @@ static int fwnet_probe(struct fw_unit *unit, net->mtu = 1500U; net->min_mtu = ETH_MIN_MTU; - net->max_mtu = ETH_MAX_MTU; + net->max_mtu = 0xfff; /* Set our hardware address while we're at it */ ha = (union fwnet_hwaddr *)net->dev_addr; -- cgit v1.1 From 9bcb94c8617e1c3ccabcda5ebb4f18c64a74ab6d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 29 Oct 2016 10:13:47 +0200 Subject: batman-adv: Introduce missing headers for genetlink restructure Fixes: 56989f6d8568 ("genetlink: mark families as __ro_after_init") Fixes: 2ae0f17df1cd ("genetlink: use idr to track families") Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/netlink.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 005012b..2171281 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -20,11 +20,14 @@ #include #include +#include #include +#include #include #include #include #include +#include #include #include #include -- cgit v1.1 From ce1a21d1422647d7750b569d714efcbd8650b934 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 18 Oct 2016 15:57:36 +0200 Subject: batman-adv: Mark batadv_netlink_ops as const The genl_ops don't need to be written by anyone and thus can be moved in a ro memory range. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 2171281..0627381 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -530,7 +530,7 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb) return msg->len; } -static struct genl_ops batadv_netlink_ops[] = { +static const struct genl_ops batadv_netlink_ops[] = { { .cmd = BATADV_CMD_GET_MESH_INFO, .flags = GENL_ADMIN_PERM, -- cgit v1.1 From 75721643d5f2e6412e0a17b1bde25cfc23259b9d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 30 Sep 2016 15:21:01 +0200 Subject: batman-adv: Close two alignment holes in batadv_hard_iface Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 673a22e..c9db184 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -123,8 +123,8 @@ struct batadv_hard_iface_bat_v { * @list: list node for batadv_hardif_list * @if_num: identificator of the interface * @if_status: status of the interface for batman-adv - * @net_dev: pointer to the net_device * @num_bcasts: number of payload re-broadcasts on this interface (ARQ) + * @net_dev: pointer to the net_device * @hardif_obj: kobject of the per interface sysfs "mesh" directory * @refcount: number of contexts the object is used * @batman_adv_ptype: packet type describing packets that should be processed by @@ -141,8 +141,8 @@ struct batadv_hard_iface { struct list_head list; s16 if_num; char if_status; - struct net_device *net_dev; u8 num_bcasts; + struct net_device *net_dev; struct kobject *hardif_obj; struct kref refcount; struct packet_type batman_adv_ptype; -- cgit v1.1 From cbfc16de30d4c3a7bb6050d0c88fb6cb35f47d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 5 Jul 2016 20:01:36 +0200 Subject: batman-adv: Add wrapper for ARP reply creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing duplicate code. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/distributed-arp-table.c | 67 ++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index cbb4f32..49576c5 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -949,6 +949,41 @@ static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size) } /** + * batadv_dat_arp_create_reply - create an ARP Reply + * @bat_priv: the bat priv with all the soft interface information + * @ip_src: ARP sender IP + * @ip_dst: ARP target IP + * @hw_src: Ethernet source and ARP sender MAC + * @hw_dst: Ethernet destination and ARP target MAC + * @vid: VLAN identifier (optional, set to zero otherwise) + * + * Creates an ARP Reply from the given values, optionally encapsulated in a + * VLAN header. + * + * Return: An skb containing an ARP Reply. + */ +static struct sk_buff * +batadv_dat_arp_create_reply(struct batadv_priv *bat_priv, __be32 ip_src, + __be32 ip_dst, u8 *hw_src, u8 *hw_dst, + unsigned short vid) +{ + struct sk_buff *skb; + + skb = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_dst, bat_priv->soft_iface, + ip_src, hw_dst, hw_src, hw_dst); + if (!skb) + return NULL; + + skb_reset_mac_header(skb); + + if (vid & BATADV_VLAN_HAS_TAG) + skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), + vid & VLAN_VID_MASK); + + return skb; +} + +/** * batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to * answer using DAT * @bat_priv: the bat priv with all the soft interface information @@ -1005,20 +1040,12 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, goto out; } - skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, - bat_priv->soft_iface, ip_dst, hw_src, - dat_entry->mac_addr, hw_src); + skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src, + dat_entry->mac_addr, + hw_src, vid); if (!skb_new) goto out; - if (vid & BATADV_VLAN_HAS_TAG) { - skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), - vid & VLAN_VID_MASK); - if (!skb_new) - goto out; - } - - skb_reset_mac_header(skb_new); skb_new->protocol = eth_type_trans(skb_new, bat_priv->soft_iface); bat_priv->stats.rx_packets++; @@ -1081,25 +1108,11 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (!dat_entry) goto out; - skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, - bat_priv->soft_iface, ip_dst, hw_src, - dat_entry->mac_addr, hw_src); - + skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src, + dat_entry->mac_addr, hw_src, vid); if (!skb_new) goto out; - /* the rest of the TX path assumes that the mac_header offset pointing - * to the inner Ethernet header has been set, therefore reset it now. - */ - skb_reset_mac_header(skb_new); - - if (vid & BATADV_VLAN_HAS_TAG) { - skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), - vid & VLAN_VID_MASK); - if (!skb_new) - goto out; - } - /* To preserve backwards compatibility, the node has choose the outgoing * format based on the incoming request packet type. The assumption is * that a node not using the 4addr packet format doesn't support it. -- cgit v1.1 From b77697633d74927d05e7ccdfdbfa5eda20dec8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Sat, 6 Aug 2016 22:23:15 +0200 Subject: batman-adv: Remove unnecessary lockdep in batadv_mcast_mla_list_free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit batadv_mcast_mla_list_free() just frees some leftovers of a local feast in batadv_mcast_mla_update(). No lockdep needed as it has nothing to do with bat_priv->mcast.mla_list. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/multicast.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index 13661f4..45757fa 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -231,19 +231,15 @@ out: /** * batadv_mcast_mla_list_free - free a list of multicast addresses - * @bat_priv: the bat priv with all the soft interface information * @mcast_list: the list to free * * Removes and frees all items in the given mcast_list. */ -static void batadv_mcast_mla_list_free(struct batadv_priv *bat_priv, - struct hlist_head *mcast_list) +static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list) { struct batadv_hw_addr *mcast_entry; struct hlist_node *tmp; - lockdep_assert_held(&bat_priv->tt.commit_lock); - hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) { hlist_del(&mcast_entry->list); kfree(mcast_entry); @@ -560,7 +556,7 @@ update: batadv_mcast_mla_tt_add(bat_priv, &mcast_list); out: - batadv_mcast_mla_list_free(bat_priv, &mcast_list); + batadv_mcast_mla_list_free(&mcast_list); } /** -- cgit v1.1 From eb6915e2eb254fd83dc4472e22ebd6c54bf8ba47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Sun, 7 Aug 2016 12:34:18 +0200 Subject: batman-adv: Remove unused skb_reset_mac_header() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During broadcast queueing, the skb_reset_mac_header() sets the skb to a place invalid for a MAC header, pointing right into the batman-adv broadcast packet. Luckily, no one seems to actually use eth_hdr(skb) afterwards until batadv_send_skb_packet() resets the header to a valid position again. Therefore removing this unnecessary, weird skb_reset_mac_header() call. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/send.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index e1e9136..be3f6d7 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -586,8 +586,6 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, bcast_packet = (struct batadv_bcast_packet *)newskb->data; bcast_packet->ttl--; - skb_reset_mac_header(newskb); - forw_packet->skb = newskb; INIT_DELAYED_WORK(&forw_packet->delayed_work, -- cgit v1.1 From cbebd363b2e92841e2e5ffc52c2d618e7b1003f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Sat, 6 Aug 2016 22:23:16 +0200 Subject: batman-adv: Use own timer for multicast TT and TVLV updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of latching onto the OGM period, this patch introduces a worker dedicated to multicast TT and TVLV updates. The reasoning is, that upon roaming especially the translation table should be updated timely to minimize connectivity issues. With BATMAN V, the idea is to greatly increase the OGM interval to reduce overhead. Unfortunately, right now this could lead to a bad user experience if multicast traffic is involved. Therefore this patch introduces a fixed 500ms update interval for multicast TT entries and the multicast TVLV. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/main.h | 1 + net/batman-adv/multicast.c | 62 ++++++++++++++++++++++++++++++++++---- net/batman-adv/multicast.h | 6 ---- net/batman-adv/translation-table.c | 4 --- net/batman-adv/types.h | 4 ++- 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index daddca9..a6cc804 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -48,6 +48,7 @@ #define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */ #define BATADV_TT_WORK_PERIOD 5000 /* 5 seconds */ #define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */ +#define BATADV_MCAST_WORK_PERIOD 500 /* 0.5 seconds */ #define BATADV_DAT_ENTRY_TIMEOUT (5 * 60000) /* 5 mins in milliseconds */ /* sliding packet range of received originator messages in sequence numbers * (should be a multiple of our word size) diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index 45757fa..090a69f 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +62,18 @@ #include "translation-table.h" #include "tvlv.h" +static void batadv_mcast_mla_update(struct work_struct *work); + +/** + * batadv_mcast_start_timer - schedule the multicast periodic worker + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_mcast_start_timer(struct batadv_priv *bat_priv) +{ + queue_delayed_work(batadv_event_workqueue, &bat_priv->mcast.work, + msecs_to_jiffies(BATADV_MCAST_WORK_PERIOD)); +} + /** * batadv_mcast_get_bridge - get the bridge on top of the softif if it exists * @soft_iface: netdev struct of the mesh interface @@ -255,6 +269,8 @@ static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list) * translation table except the ones listed in the given mcast_list. * * If mcast_list is NULL then all are retracted. + * + * Do not call outside of the mcast worker! (or cancel mcast worker first) */ static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv, struct hlist_head *mcast_list) @@ -262,7 +278,7 @@ static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv, struct batadv_hw_addr *mcast_entry; struct hlist_node *tmp; - lockdep_assert_held(&bat_priv->tt.commit_lock); + WARN_ON(delayed_work_pending(&bat_priv->mcast.work)); hlist_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list, list) { @@ -287,6 +303,8 @@ static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv, * * Adds multicast listener announcements from the given mcast_list to the * translation table if they have not been added yet. + * + * Do not call outside of the mcast worker! (or cancel mcast worker first) */ static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv, struct hlist_head *mcast_list) @@ -294,7 +312,7 @@ static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv, struct batadv_hw_addr *mcast_entry; struct hlist_node *tmp; - lockdep_assert_held(&bat_priv->tt.commit_lock); + WARN_ON(delayed_work_pending(&bat_priv->mcast.work)); if (!mcast_list) return; @@ -528,13 +546,18 @@ update: } /** - * batadv_mcast_mla_update - update the own MLAs + * __batadv_mcast_mla_update - update the own MLAs * @bat_priv: the bat priv with all the soft interface information * * Updates the own multicast listener announcements in the translation * table as well as the own, announced multicast tvlv container. + * + * Note that non-conflicting reads and writes to bat_priv->mcast.mla_list + * in batadv_mcast_mla_tt_retract() and batadv_mcast_mla_tt_add() are + * ensured by the non-parallel execution of the worker this function + * belongs to. */ -void batadv_mcast_mla_update(struct batadv_priv *bat_priv) +static void __batadv_mcast_mla_update(struct batadv_priv *bat_priv) { struct net_device *soft_iface = bat_priv->soft_iface; struct hlist_head mcast_list = HLIST_HEAD_INIT; @@ -560,6 +583,29 @@ out: } /** + * batadv_mcast_mla_update - update the own MLAs + * @work: kernel work struct + * + * Updates the own multicast listener announcements in the translation + * table as well as the own, announced multicast tvlv container. + * + * In the end, reschedules the work timer. + */ +static void batadv_mcast_mla_update(struct work_struct *work) +{ + struct delayed_work *delayed_work; + struct batadv_priv_mcast *priv_mcast; + struct batadv_priv *bat_priv; + + delayed_work = to_delayed_work(work); + priv_mcast = container_of(delayed_work, struct batadv_priv_mcast, work); + bat_priv = container_of(priv_mcast, struct batadv_priv, mcast); + + __batadv_mcast_mla_update(bat_priv); + batadv_mcast_start_timer(bat_priv); +} + +/** * batadv_mcast_is_report_ipv4 - check for IGMP reports * @skb: the ethernet frame destined for the mesh * @@ -1128,6 +1174,9 @@ void batadv_mcast_init(struct batadv_priv *bat_priv) batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler, NULL, BATADV_TVLV_MCAST, 2, BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + + INIT_DELAYED_WORK(&bat_priv->mcast.work, batadv_mcast_mla_update); + batadv_mcast_start_timer(bat_priv); } #ifdef CONFIG_BATMAN_ADV_DEBUGFS @@ -1239,12 +1288,13 @@ int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset) */ void batadv_mcast_free(struct batadv_priv *bat_priv) { + cancel_delayed_work_sync(&bat_priv->mcast.work); + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2); batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2); - spin_lock_bh(&bat_priv->tt.commit_lock); + /* safely calling outside of worker, as worker was canceled above */ batadv_mcast_mla_tt_retract(bat_priv, NULL); - spin_unlock_bh(&bat_priv->tt.commit_lock); } /** diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h index 1fb00ba..2cddaf5 100644 --- a/net/batman-adv/multicast.h +++ b/net/batman-adv/multicast.h @@ -39,8 +39,6 @@ enum batadv_forw_mode { #ifdef CONFIG_BATMAN_ADV_MCAST -void batadv_mcast_mla_update(struct batadv_priv *bat_priv); - enum batadv_forw_mode batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, struct batadv_orig_node **mcast_single_orig); @@ -55,10 +53,6 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node); #else -static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv) -{ -} - static inline enum batadv_forw_mode batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, struct batadv_orig_node **mcast_single_orig) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index ad1e3bc..3cae8f4f 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -56,7 +56,6 @@ #include "hard-interface.h" #include "hash.h" #include "log.h" -#include "multicast.h" #include "netlink.h" #include "originator.h" #include "packet.h" @@ -3795,9 +3794,6 @@ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) { lockdep_assert_held(&bat_priv->tt.commit_lock); - /* Update multicast addresses in local translation table */ - batadv_mcast_mla_update(bat_priv); - if (atomic_read(&bat_priv->tt.local_changes) < 1) { if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt)) batadv_tt_tvlv_container_update(bat_priv); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index c9db184..0d46aea 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -785,9 +785,10 @@ struct batadv_mcast_querier_state { * @num_want_all_ipv6: counter for items in want_all_ipv6_list * @want_lists_lock: lock for protecting modifications to mcast want lists * (traversals are rcu-locked) + * @work: work queue callback item for multicast TT and TVLV updates */ struct batadv_priv_mcast { - struct hlist_head mla_list; + struct hlist_head mla_list; /* see __batadv_mcast_mla_update() */ struct hlist_head want_all_unsnoopables_list; struct hlist_head want_all_ipv4_list; struct hlist_head want_all_ipv6_list; @@ -802,6 +803,7 @@ struct batadv_priv_mcast { atomic_t num_want_all_ipv6; /* protects want_all_{unsnoopables,ipv4,ipv6}_list */ spinlock_t want_lists_lock; + struct delayed_work work; }; #endif -- cgit v1.1 From 3111beed0d595d26551afb607c9812fe49da2ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Sun, 7 Aug 2016 12:34:19 +0200 Subject: batman-adv: Simple (re)broadcast avoidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this patch, (re)broadcasting on a specific interfaces is avoided: * No neighbor: There is no need to broadcast on an interface if there is no node behind it. * Single neighbor is source: If there is just one neighbor on an interface and if this neighbor is the one we actually got this broadcast packet from, then we do not need to echo it back. * Single neighbor is originator: If there is just one neighbor on an interface and if this neighbor is the originator of this broadcast packet, then we do not need to echo it back. Goodies for BATMAN V: ("Upgrade your BATMAN IV network to V now to get these for free!") Thanks to the split of OGMv1 into two packet types, OGMv2 and ELP that is, we can now apply the same optimizations stated above to OGMv2 packets, too. Furthermore, with BATMAN V, rebroadcasts can be reduced in certain multi interface cases, too, where BATMAN IV cannot. This is thanks to the removal of the "secondary interface originator" concept in BATMAN V. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_v_ogm.c | 56 +++++++++++++++++++++++++++++++++++++++++ net/batman-adv/hard-interface.c | 52 ++++++++++++++++++++++++++++++++++++++ net/batman-adv/hard-interface.h | 16 ++++++++++++ net/batman-adv/originator.c | 13 +++++++--- net/batman-adv/routing.c | 2 +- net/batman-adv/send.c | 55 +++++++++++++++++++++++++++++++++++++++- net/batman-adv/send.h | 3 ++- net/batman-adv/soft-interface.c | 2 +- net/batman-adv/types.h | 2 ++ 9 files changed, 193 insertions(+), 8 deletions(-) diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 61ff5f8..9922ccd 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -140,6 +140,7 @@ static void batadv_v_ogm_send(struct work_struct *work) unsigned char *ogm_buff, *pkt_buff; int ogm_buff_len; u16 tvlv_len = 0; + int ret; bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work); bat_priv = container_of(bat_v, struct batadv_priv, bat_v); @@ -182,6 +183,31 @@ static void batadv_v_ogm_send(struct work_struct *work) if (!kref_get_unless_zero(&hard_iface->refcount)) continue; + ret = batadv_hardif_no_broadcast(hard_iface, NULL, NULL); + if (ret) { + char *type; + + switch (ret) { + case BATADV_HARDIF_BCAST_NORECIPIENT: + type = "no neighbor"; + break; + case BATADV_HARDIF_BCAST_DUPFWD: + type = "single neighbor is source"; + break; + case BATADV_HARDIF_BCAST_DUPORIG: + type = "single neighbor is originator"; + break; + default: + type = "unknown"; + } + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "OGM2 from ourselve on %s surpressed: %s\n", + hard_iface->net_dev->name, type); + + batadv_hardif_put(hard_iface); + continue; + } + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Sending own OGM2 packet (originator %pM, seqno %u, throughput %u, TTL %d) on interface %s [%pM]\n", ogm_packet->orig, ntohl(ogm_packet->seqno), @@ -651,6 +677,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, struct batadv_hard_iface *hard_iface; struct batadv_ogm2_packet *ogm_packet; u32 ogm_throughput, link_throughput, path_throughput; + int ret; ethhdr = eth_hdr(skb); ogm_packet = (struct batadv_ogm2_packet *)(skb->data + ogm_offset); @@ -716,6 +743,35 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, if (!kref_get_unless_zero(&hard_iface->refcount)) continue; + ret = batadv_hardif_no_broadcast(hard_iface, + ogm_packet->orig, + hardif_neigh->orig); + + if (ret) { + char *type; + + switch (ret) { + case BATADV_HARDIF_BCAST_NORECIPIENT: + type = "no neighbor"; + break; + case BATADV_HARDIF_BCAST_DUPFWD: + type = "single neighbor is source"; + break; + case BATADV_HARDIF_BCAST_DUPORIG: + type = "single neighbor is originator"; + break; + default: + type = "unknown"; + } + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "OGM2 packet from %pM on %s surpressed: %s\n", + ogm_packet->orig, hard_iface->net_dev->name, + type); + + batadv_hardif_put(hard_iface); + continue; + } + batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet, orig_node, neigh_node, if_incoming, hard_iface); diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 08ce361..a7a462e 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -228,6 +228,58 @@ bool batadv_is_wifi_netdev(struct net_device *net_device) return false; } +/** + * batadv_hardif_no_broadcast - check whether (re)broadcast is necessary + * @if_outgoing: the outgoing interface checked and considered for (re)broadcast + * @orig_addr: the originator of this packet + * @orig_neigh: originator address of the forwarder we just got the packet from + * (NULL if we originated) + * + * Checks whether a packet needs to be (re)broadcasted on the given interface. + * + * Return: + * BATADV_HARDIF_BCAST_NORECIPIENT: No neighbor on interface + * BATADV_HARDIF_BCAST_DUPFWD: Just one neighbor, but it is the forwarder + * BATADV_HARDIF_BCAST_DUPORIG: Just one neighbor, but it is the originator + * BATADV_HARDIF_BCAST_OK: Several neighbors, must broadcast + */ +int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing, + u8 *orig_addr, u8 *orig_neigh) +{ + struct batadv_hardif_neigh_node *hardif_neigh; + struct hlist_node *first; + int ret = BATADV_HARDIF_BCAST_OK; + + rcu_read_lock(); + + /* 0 neighbors -> no (re)broadcast */ + first = rcu_dereference(hlist_first_rcu(&if_outgoing->neigh_list)); + if (!first) { + ret = BATADV_HARDIF_BCAST_NORECIPIENT; + goto out; + } + + /* >1 neighbors -> (re)brodcast */ + if (rcu_dereference(hlist_next_rcu(first))) + goto out; + + hardif_neigh = hlist_entry(first, struct batadv_hardif_neigh_node, + list); + + /* 1 neighbor, is the originator -> no rebroadcast */ + if (orig_addr && batadv_compare_eth(hardif_neigh->orig, orig_addr)) { + ret = BATADV_HARDIF_BCAST_DUPORIG; + /* 1 neighbor, is the one we received from -> no rebroadcast */ + } else if (orig_neigh && + batadv_compare_eth(hardif_neigh->orig, orig_neigh)) { + ret = BATADV_HARDIF_BCAST_DUPFWD; + } + +out: + rcu_read_unlock(); + return ret; +} + static struct batadv_hard_iface * batadv_hardif_get_active(const struct net_device *soft_iface) { diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index a76724d..a043182 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -40,6 +40,20 @@ enum batadv_hard_if_state { }; /** + * enum batadv_hard_if_bcast - broadcast avoidance options + * @BATADV_HARDIF_BCAST_OK: Do broadcast on according hard interface + * @BATADV_HARDIF_BCAST_NORECIPIENT: Broadcast not needed, there is no recipient + * @BATADV_HARDIF_BCAST_DUPFWD: There is just the neighbor we got it from + * @BATADV_HARDIF_BCAST_DUPORIG: There is just the originator + */ +enum batadv_hard_if_bcast { + BATADV_HARDIF_BCAST_OK = 0, + BATADV_HARDIF_BCAST_NORECIPIENT, + BATADV_HARDIF_BCAST_DUPFWD, + BATADV_HARDIF_BCAST_DUPORIG, +}; + +/** * enum batadv_hard_if_cleanup - Cleanup modi for soft_iface after slave removal * @BATADV_IF_CLEANUP_KEEP: Don't automatically delete soft-interface * @BATADV_IF_CLEANUP_AUTO: Delete soft-interface after last slave was removed @@ -63,6 +77,8 @@ void batadv_hardif_remove_interfaces(void); int batadv_hardif_min_mtu(struct net_device *soft_iface); void batadv_update_min_mtu(struct net_device *soft_iface); void batadv_hardif_release(struct kref *ref); +int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing, + u8 *orig_addr, u8 *orig_neigh); /** * batadv_hardif_put - decrement the hard interface refcounter and possibly diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 518b1ed..ad26559 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -512,12 +512,14 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node, * batadv_hardif_neigh_create - create a hardif neighbour node * @hard_iface: the interface this neighbour is connected to * @neigh_addr: the interface address of the neighbour to retrieve + * @orig_node: originator object representing the neighbour * * Return: the hardif neighbour node if found or created or NULL otherwise. */ static struct batadv_hardif_neigh_node * batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, - const u8 *neigh_addr) + const u8 *neigh_addr, + struct batadv_orig_node *orig_node) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct batadv_hardif_neigh_node *hardif_neigh; @@ -536,6 +538,7 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, kref_get(&hard_iface->refcount); INIT_HLIST_NODE(&hardif_neigh->list); ether_addr_copy(hardif_neigh->addr, neigh_addr); + ether_addr_copy(hardif_neigh->orig, orig_node->orig); hardif_neigh->if_incoming = hard_iface; hardif_neigh->last_seen = jiffies; @@ -556,12 +559,14 @@ out: * node * @hard_iface: the interface this neighbour is connected to * @neigh_addr: the interface address of the neighbour to retrieve + * @orig_node: originator object representing the neighbour * * Return: the hardif neighbour node if found or created or NULL otherwise. */ static struct batadv_hardif_neigh_node * batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface, - const u8 *neigh_addr) + const u8 *neigh_addr, + struct batadv_orig_node *orig_node) { struct batadv_hardif_neigh_node *hardif_neigh; @@ -570,7 +575,7 @@ batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface, if (hardif_neigh) return hardif_neigh; - return batadv_hardif_neigh_create(hard_iface, neigh_addr); + return batadv_hardif_neigh_create(hard_iface, neigh_addr, orig_node); } /** @@ -630,7 +635,7 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node, goto out; hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface, - neigh_addr); + neigh_addr, orig_node); if (!hardif_neigh) goto out; diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 7e8dc64..a4cb157 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1170,7 +1170,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, batadv_skb_set_priority(skb, sizeof(struct batadv_bcast_packet)); /* rebroadcast packet */ - batadv_add_bcast_packet_to_list(bat_priv, skb, 1); + batadv_add_bcast_packet_to_list(bat_priv, skb, 1, false); /* don't hand the broadcast up if it is from an originator * from the same backbone. diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index be3f6d7..d9bbb00 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -549,6 +549,7 @@ _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, * @bat_priv: the bat priv with all the soft interface information * @skb: broadcast packet to add * @delay: number of jiffies to wait before sending + * @own_packet: true if it is a self-generated broadcast packet * * add a broadcast packet to the queue and setup timers. broadcast packets * are sent multiple times to increase probability for being received. @@ -560,7 +561,8 @@ _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, */ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, - unsigned long delay) + unsigned long delay, + bool own_packet) { struct batadv_hard_iface *primary_if; struct batadv_forw_packet *forw_packet; @@ -587,6 +589,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, bcast_packet->ttl--; forw_packet->skb = newskb; + forw_packet->own = own_packet; INIT_DELAYED_WORK(&forw_packet->delayed_work, batadv_send_outstanding_bcast_packet); @@ -603,11 +606,16 @@ err: static void batadv_send_outstanding_bcast_packet(struct work_struct *work) { struct batadv_hard_iface *hard_iface; + struct batadv_hardif_neigh_node *neigh_node; struct delayed_work *delayed_work; struct batadv_forw_packet *forw_packet; + struct batadv_bcast_packet *bcast_packet; struct sk_buff *skb1; struct net_device *soft_iface; struct batadv_priv *bat_priv; + u8 *neigh_addr; + u8 *orig_neigh; + int ret = 0; delayed_work = to_delayed_work(work); forw_packet = container_of(delayed_work, struct batadv_forw_packet, @@ -625,6 +633,8 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet)) goto out; + bcast_packet = (struct batadv_bcast_packet *)forw_packet->skb->data; + /* rebroadcast packet */ rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { @@ -634,6 +644,49 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) if (forw_packet->num_packets >= hard_iface->num_bcasts) continue; + if (forw_packet->own) { + neigh_node = NULL; + } else { + neigh_addr = eth_hdr(forw_packet->skb)->h_source; + neigh_node = batadv_hardif_neigh_get(hard_iface, + neigh_addr); + } + + orig_neigh = neigh_node ? neigh_node->orig : NULL; + + ret = batadv_hardif_no_broadcast(hard_iface, bcast_packet->orig, + orig_neigh); + + if (ret) { + char *type; + + switch (ret) { + case BATADV_HARDIF_BCAST_NORECIPIENT: + type = "no neighbor"; + break; + case BATADV_HARDIF_BCAST_DUPFWD: + type = "single neighbor is source"; + break; + case BATADV_HARDIF_BCAST_DUPORIG: + type = "single neighbor is originator"; + break; + default: + type = "unknown"; + } + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "BCAST packet from orig %pM on %s surpressed: %s\n", + bcast_packet->orig, + hard_iface->net_dev->name, type); + + if (neigh_node) + batadv_hardif_neigh_put(neigh_node); + + continue; + } + + if (neigh_node) + batadv_hardif_neigh_put(neigh_node); + if (!kref_get_unless_zero(&hard_iface->refcount)) continue; diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 999f786..86009b5 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -46,7 +46,8 @@ int batadv_send_unicast_skb(struct sk_buff *skb, struct batadv_neigh_node *neigh_node); int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, - unsigned long delay); + unsigned long delay, + bool own_packet); void batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, const struct batadv_hard_iface *hard_iface); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index f37c1c7..109f53b 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -357,7 +357,7 @@ send: seqno = atomic_inc_return(&bat_priv->bcast_seqno); bcast_packet->seqno = htonl(seqno); - batadv_add_bcast_packet_to_list(bat_priv, skb, brd_delay); + batadv_add_bcast_packet_to_list(bat_priv, skb, brd_delay, true); /* a copy is stored in the bcast list, therefore removing * the original skb. diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 0d46aea..98ebac0 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -408,6 +408,7 @@ struct batadv_hardif_neigh_node_bat_v { * struct batadv_hardif_neigh_node - unique neighbor per hard-interface * @list: list node for batadv_hard_iface::neigh_list * @addr: the MAC address of the neighboring interface + * @orig: the address of the originator this neighbor node belongs to * @if_incoming: pointer to incoming hard-interface * @last_seen: when last packet via this neighbor was received * @bat_v: B.A.T.M.A.N. V private data @@ -417,6 +418,7 @@ struct batadv_hardif_neigh_node_bat_v { struct batadv_hardif_neigh_node { struct hlist_node list; u8 addr[ETH_ALEN]; + u8 orig[ETH_ALEN]; struct batadv_hard_iface *if_incoming; unsigned long last_seen; #ifdef CONFIG_BATMAN_ADV_BATMAN_V -- cgit v1.1 From bd687fe41991611a6904b7cbc1d596f687584ebb Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 17 Jul 2016 21:04:00 +0200 Subject: batman-adv: use consume_skb for non-dropped packets kfree_skb assumes that an skb is dropped after an failure and notes that. consume_skb should be used in non-failure situations. Such information is important for dropmonitor netlink which tells how many packets were dropped and where this drop happened. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 13 ++++++++----- net/batman-adv/fragmentation.c | 20 ++++++++++++++------ net/batman-adv/network-coding.c | 24 +++++++++++++++--------- net/batman-adv/send.c | 27 +++++++++++++++++++-------- net/batman-adv/send.h | 3 ++- net/batman-adv/soft-interface.c | 2 +- 6 files changed, 59 insertions(+), 30 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 0b9be62..310f391 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -698,7 +698,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size); if (!forw_packet_aggr->skb) { - batadv_forw_packet_free(forw_packet_aggr); + batadv_forw_packet_free(forw_packet_aggr, true); return; } @@ -1611,7 +1611,7 @@ out: if (hardif_neigh) batadv_hardif_neigh_put(hardif_neigh); - kfree_skb(skb_priv); + consume_skb(skb_priv); } /** @@ -1783,6 +1783,7 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work) struct delayed_work *delayed_work; struct batadv_forw_packet *forw_packet; struct batadv_priv *bat_priv; + bool dropped = false; delayed_work = to_delayed_work(work); forw_packet = container_of(delayed_work, struct batadv_forw_packet, @@ -1792,8 +1793,10 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work) hlist_del(&forw_packet->list); spin_unlock_bh(&bat_priv->forw_bat_list_lock); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) + if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { + dropped = true; goto out; + } batadv_iv_ogm_emit(forw_packet); @@ -1810,7 +1813,7 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work) batadv_iv_ogm_schedule(forw_packet->if_incoming); out: - batadv_forw_packet_free(forw_packet); + batadv_forw_packet_free(forw_packet, dropped); } static int batadv_iv_ogm_receive(struct sk_buff *skb, @@ -1851,7 +1854,7 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, ogm_packet = (struct batadv_ogm_packet *)packet_pos; } - kfree_skb(skb); + consume_skb(skb); return NET_RX_SUCCESS; } diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 2b967a3..a2e28a1 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -42,17 +42,23 @@ /** * batadv_frag_clear_chain - delete entries in the fragment buffer chain * @head: head of chain with entries. + * @dropped: whether the chain is cleared because all fragments are dropped * * Free fragments in the passed hlist. Should be called with appropriate lock. */ -static void batadv_frag_clear_chain(struct hlist_head *head) +static void batadv_frag_clear_chain(struct hlist_head *head, bool dropped) { struct batadv_frag_list_entry *entry; struct hlist_node *node; hlist_for_each_entry_safe(entry, node, head, list) { hlist_del(&entry->list); - kfree_skb(entry->skb); + + if (dropped) + kfree_skb(entry->skb); + else + consume_skb(entry->skb); + kfree(entry); } } @@ -73,7 +79,7 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig_node, spin_lock_bh(&chain->lock); if (!check_cb || check_cb(chain)) { - batadv_frag_clear_chain(&chain->fragment_list); + batadv_frag_clear_chain(&chain->fragment_list, true); chain->size = 0; } @@ -118,7 +124,7 @@ static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain, return false; if (!hlist_empty(&chain->fragment_list)) - batadv_frag_clear_chain(&chain->fragment_list); + batadv_frag_clear_chain(&chain->fragment_list, true); chain->size = 0; chain->seqno = seqno; @@ -220,7 +226,7 @@ out: * exceeds the maximum size of one merged packet. Don't allow * packets to have different total_size. */ - batadv_frag_clear_chain(&chain->fragment_list); + batadv_frag_clear_chain(&chain->fragment_list, true); chain->size = 0; } else if (ntohs(frag_packet->total_size) == chain->size) { /* All fragments received. Hand over chain to caller. */ @@ -254,6 +260,7 @@ batadv_frag_merge_packets(struct hlist_head *chain) struct batadv_frag_list_entry *entry; struct sk_buff *skb_out; int size, hdr_size = sizeof(struct batadv_frag_packet); + bool dropped = false; /* Remove first entry, as this is the destination for the rest of the * fragments. @@ -270,6 +277,7 @@ batadv_frag_merge_packets(struct hlist_head *chain) if (pskb_expand_head(skb_out, 0, size - skb_out->len, GFP_ATOMIC) < 0) { kfree_skb(skb_out); skb_out = NULL; + dropped = true; goto free; } @@ -291,7 +299,7 @@ batadv_frag_merge_packets(struct hlist_head *chain) free: /* Locking is not needed, because 'chain' is not part of any orig. */ - batadv_frag_clear_chain(chain); + batadv_frag_clear_chain(chain, dropped); return skb_out; } diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index c213dde..3af66d3 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -260,10 +260,16 @@ static void batadv_nc_path_put(struct batadv_nc_path *nc_path) /** * batadv_nc_packet_free - frees nc packet * @nc_packet: the nc packet to free + * @dropped: whether the packet is freed because is is dropped */ -static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet) +static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet, + bool dropped) { - kfree_skb(nc_packet->skb); + if (dropped) + kfree_skb(nc_packet->skb); + else + consume_skb(nc_packet->skb); + batadv_nc_path_put(nc_packet->nc_path); kfree(nc_packet); } @@ -576,7 +582,7 @@ static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet) { batadv_send_unicast_skb(nc_packet->skb, nc_packet->neigh_node); nc_packet->skb = NULL; - batadv_nc_packet_free(nc_packet); + batadv_nc_packet_free(nc_packet, false); } /** @@ -610,7 +616,7 @@ static bool batadv_nc_sniffed_purge(struct batadv_priv *bat_priv, /* purge nc packet */ list_del(&nc_packet->list); - batadv_nc_packet_free(nc_packet); + batadv_nc_packet_free(nc_packet, true); res = true; @@ -1208,11 +1214,11 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, } /* skb_src is now coded into skb_dest, so free it */ - kfree_skb(skb_src); + consume_skb(skb_src); /* avoid duplicate free of skb from nc_packet */ nc_packet->skb = NULL; - batadv_nc_packet_free(nc_packet); + batadv_nc_packet_free(nc_packet, false); /* Send the coded packet and return true */ batadv_send_unicast_skb(skb_dest, first_dest); @@ -1399,7 +1405,7 @@ static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv, /* batadv_nc_skb_store_for_decoding() clones the skb, so we must free * our ref */ - kfree_skb(skb); + consume_skb(skb); } /** @@ -1723,7 +1729,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb, ether_addr_copy(unicast_packet->dest, orig_dest); unicast_packet->ttvn = ttvn; - batadv_nc_packet_free(nc_packet); + batadv_nc_packet_free(nc_packet, false); return unicast_packet; } @@ -1860,7 +1866,7 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb, return batadv_recv_unicast_packet(skb, recv_if); free_nc_packet: - batadv_nc_packet_free(nc_packet); + batadv_nc_packet_free(nc_packet, true); return NET_RX_DROP; } diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index d9bbb00..0f86293 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -451,13 +451,19 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, /** * batadv_forw_packet_free - free a forwarding packet * @forw_packet: The packet to free + * @dropped: whether the packet is freed because is is dropped * * This frees a forwarding packet and releases any resources it might * have claimed. */ -void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet) +void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet, + bool dropped) { - kfree_skb(forw_packet->skb); + if (dropped) + kfree_skb(forw_packet->skb); + else + consume_skb(forw_packet->skb); + if (forw_packet->if_incoming) batadv_hardif_put(forw_packet->if_incoming); if (forw_packet->if_outgoing) @@ -598,7 +604,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, return NETDEV_TX_OK; err_packet_free: - batadv_forw_packet_free(forw_packet); + batadv_forw_packet_free(forw_packet, true); err: return NETDEV_TX_BUSY; } @@ -613,6 +619,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) struct sk_buff *skb1; struct net_device *soft_iface; struct batadv_priv *bat_priv; + bool dropped = false; u8 *neigh_addr; u8 *orig_neigh; int ret = 0; @@ -627,11 +634,15 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) hlist_del(&forw_packet->list); spin_unlock_bh(&bat_priv->forw_bcast_list_lock); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) + if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { + dropped = true; goto out; + } - if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet)) + if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet)) { + dropped = true; goto out; + } bcast_packet = (struct batadv_bcast_packet *)forw_packet->skb->data; @@ -709,7 +720,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) } out: - batadv_forw_packet_free(forw_packet); + batadv_forw_packet_free(forw_packet, dropped); } void @@ -750,7 +761,7 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, if (pending) { hlist_del(&forw_packet->list); - batadv_forw_packet_free(forw_packet); + batadv_forw_packet_free(forw_packet, true); } } spin_unlock_bh(&bat_priv->forw_bcast_list_lock); @@ -777,7 +788,7 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, if (pending) { hlist_del(&forw_packet->list); - batadv_forw_packet_free(forw_packet); + batadv_forw_packet_free(forw_packet, true); } } spin_unlock_bh(&bat_priv->forw_bat_list_lock); diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 86009b5..c580194 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -27,7 +27,8 @@ struct sk_buff; -void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet); +void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet, + bool dropped); struct batadv_forw_packet * batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 109f53b..2f0304e 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -362,7 +362,7 @@ send: /* a copy is stored in the bcast list, therefore removing * the original skb. */ - kfree_skb(skb); + consume_skb(skb); /* unicast packet */ } else { -- cgit v1.1 From eaac2c876ecce420d24ff5d5b6d6a51390446798 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 17 Jul 2016 21:04:01 +0200 Subject: batman-adv: Count all non-success TX packets as dropped A failure during the submission also causes dropped packets. batadv_interface_tx should therefore also increase the DROPPED counter for these returns. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/soft-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 2f0304e..7b3494a 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -386,7 +386,7 @@ send: ret = batadv_send_skb_via_tt(bat_priv, skb, dst_hint, vid); } - if (ret == NET_XMIT_DROP) + if (ret != NET_XMIT_SUCCESS) goto dropped_freed; } -- cgit v1.1 From 8def0be82dd1507bcdc5fd35d12e35ace1e39dff Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 17 Jul 2016 21:04:02 +0200 Subject: batman-adv: Consume skb in batadv_frag_send_packet Sending functions in Linux consume the supplied skbuff. Doing the same in batadv_frag_send_packet avoids the hack of returning -1 (-EPERM) to signal the caller that he is responsible for cleaning up the skb. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/fragmentation.c | 50 ++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index a2e28a1..9c561e6 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -441,8 +442,7 @@ err: * @orig_node: final destination of the created fragments * @neigh_node: next-hop of the created fragments * - * Return: the netdev tx status or -1 in case of error. - * When -1 is returned the skb is not consumed. + * Return: the netdev tx status or a negative errno code on a failure */ int batadv_frag_send_packet(struct sk_buff *skb, struct batadv_orig_node *orig_node, @@ -455,7 +455,7 @@ int batadv_frag_send_packet(struct sk_buff *skb, unsigned int mtu = neigh_node->if_incoming->net_dev->mtu; unsigned int header_size = sizeof(frag_header); unsigned int max_fragment_size, max_packet_size; - int ret = -1; + int ret; /* To avoid merge and refragmentation at next-hops we never send * fragments larger than BATADV_FRAG_MAX_FRAG_SIZE @@ -465,13 +465,17 @@ int batadv_frag_send_packet(struct sk_buff *skb, max_packet_size = max_fragment_size * BATADV_FRAG_MAX_FRAGMENTS; /* Don't even try to fragment, if we need more than 16 fragments */ - if (skb->len > max_packet_size) - goto out; + if (skb->len > max_packet_size) { + ret = -EAGAIN; + goto free_skb; + } bat_priv = orig_node->bat_priv; primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto out; + if (!primary_if) { + ret = -EINVAL; + goto put_primary_if; + } /* Create one header to be copied to all fragments */ frag_header.packet_type = BATADV_UNICAST_FRAG; @@ -496,34 +500,35 @@ int batadv_frag_send_packet(struct sk_buff *skb, /* Eat and send fragments from the tail of skb */ while (skb->len > max_fragment_size) { skb_fragment = batadv_frag_create(skb, &frag_header, mtu); - if (!skb_fragment) - goto out; + if (!skb_fragment) { + ret = -ENOMEM; + goto free_skb; + } batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, skb_fragment->len + ETH_HLEN); ret = batadv_send_unicast_skb(skb_fragment, neigh_node); if (ret != NET_XMIT_SUCCESS) { - /* return -1 so that the caller can free the original - * skb - */ - ret = -1; - goto out; + ret = NET_XMIT_DROP; + goto free_skb; } frag_header.no++; /* The initial check in this function should cover this case */ if (frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1) { - ret = -1; - goto out; + ret = -EINVAL; + goto free_skb; } } /* Make room for the fragment header. */ if (batadv_skb_head_push(skb, header_size) < 0 || - pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0) - goto out; + pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0) { + ret = -ENOMEM; + goto free_skb; + } memcpy(skb->data, &frag_header, header_size); @@ -532,10 +537,13 @@ int batadv_frag_send_packet(struct sk_buff *skb, batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, skb->len + ETH_HLEN); ret = batadv_send_unicast_skb(skb, neigh_node); + /* skb was consumed */ + skb = NULL; -out: - if (primary_if) - batadv_hardif_put(primary_if); +put_primary_if: + batadv_hardif_put(primary_if); +free_skb: + kfree_skb(skb); return ret; } -- cgit v1.1 From 1ad5bcb2a032262170fdc508abfa0acb0565e2b5 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 17 Jul 2016 21:04:03 +0200 Subject: batman-adv: Consume skb in batadv_send_skb_to_orig Sending functions in Linux consume the supplied skbuff. Doing the same in batadv_send_skb_to_orig avoids the hack of returning -1 (-EPERM) to signal the caller that he is responsible for cleaning up the skb. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/routing.c | 11 ++--------- net/batman-adv/send.c | 39 ++++++++++++++++++++++----------------- net/batman-adv/tp_meter.c | 6 ------ net/batman-adv/tvlv.c | 5 +---- 4 files changed, 25 insertions(+), 36 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index a4cb157..4d2679a 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -262,9 +262,6 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, icmph->ttl = BATADV_TTL; res = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (res == -1) - goto out; - ret = NET_RX_SUCCESS; break; @@ -325,8 +322,7 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, icmp_packet->ttl = BATADV_TTL; res = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (res != -1) - ret = NET_RX_SUCCESS; + ret = NET_RX_SUCCESS; out: if (primary_if) @@ -413,8 +409,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, /* route it */ res = batadv_send_skb_to_orig(skb, orig_node, recv_if); - if (res != -1) - ret = NET_RX_SUCCESS; + ret = NET_RX_SUCCESS; out: if (orig_node) @@ -702,8 +697,6 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, len = skb->len; res = batadv_send_skb_to_orig(skb, orig_node, recv_if); - if (res == -1) - goto out; /* translate transmit result into receive result */ if (res == NET_XMIT_SUCCESS) { diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 0f86293..b00aac7 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -165,11 +165,9 @@ int batadv_send_unicast_skb(struct sk_buff *skb, * host, NULL can be passed as recv_if and no interface alternating is * attempted. * - * Return: -1 on failure (and the skb is not consumed), -EINPROGRESS if the - * skb is buffered for later transmit or the NET_XMIT status returned by the + * Return: negative errno code on a failure, -EINPROGRESS if the skb is + * buffered for later transmit or the NET_XMIT status returned by the * lower routine if the packet has been passed down. - * - * If the returning value is not -1 the skb has been consumed. */ int batadv_send_skb_to_orig(struct sk_buff *skb, struct batadv_orig_node *orig_node, @@ -177,12 +175,14 @@ int batadv_send_skb_to_orig(struct sk_buff *skb, { struct batadv_priv *bat_priv = orig_node->bat_priv; struct batadv_neigh_node *neigh_node; - int ret = -1; + int ret; /* batadv_find_router() increases neigh_nodes refcount if found. */ neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); - if (!neigh_node) - goto out; + if (!neigh_node) { + ret = -EINVAL; + goto free_skb; + } /* Check if the skb is too large to send in one piece and fragment * it if needed. @@ -191,8 +191,10 @@ int batadv_send_skb_to_orig(struct sk_buff *skb, skb->len > neigh_node->if_incoming->net_dev->mtu) { /* Fragment and send packet. */ ret = batadv_frag_send_packet(skb, orig_node, neigh_node); + /* skb was consumed */ + skb = NULL; - goto out; + goto put_neigh_node; } /* try to network code the packet, if it is received on an interface @@ -204,9 +206,13 @@ int batadv_send_skb_to_orig(struct sk_buff *skb, else ret = batadv_send_unicast_skb(skb, neigh_node); -out: - if (neigh_node) - batadv_neigh_node_put(neigh_node); + /* skb was consumed */ + skb = NULL; + +put_neigh_node: + batadv_neigh_node_put(neigh_node); +free_skb: + kfree_skb(skb); return ret; } @@ -327,7 +333,7 @@ int batadv_send_skb_unicast(struct batadv_priv *bat_priv, { struct batadv_unicast_packet *unicast_packet; struct ethhdr *ethhdr; - int res, ret = NET_XMIT_DROP; + int ret = NET_XMIT_DROP; if (!orig_node) goto out; @@ -364,13 +370,12 @@ int batadv_send_skb_unicast(struct batadv_priv *bat_priv, if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest, vid)) unicast_packet->ttvn = unicast_packet->ttvn - 1; - res = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (res != -1) - ret = NET_XMIT_SUCCESS; + ret = batadv_send_skb_to_orig(skb, orig_node, NULL); + /* skb was consumed */ + skb = NULL; out: - if (ret == NET_XMIT_DROP) - kfree_skb(skb); + kfree_skb(skb); return ret; } diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 2333777..f156452 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -615,9 +615,6 @@ static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src, batadv_tp_fill_prerandom(tp_vars, data, data_len); r = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (r == -1) - kfree_skb(skb); - if (r == NET_XMIT_SUCCESS) return 0; @@ -1206,9 +1203,6 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, /* send the ack */ r = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (r == -1) - kfree_skb(skb); - if (unlikely(r < 0) || (r == NET_XMIT_DROP)) { ret = BATADV_TP_REASON_DST_UNREACHABLE; goto out; diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 77654f0..a783420 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -600,7 +600,6 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src, unsigned char *tvlv_buff; unsigned int tvlv_len; ssize_t hdr_len = sizeof(*unicast_tvlv_packet); - int res; orig_node = batadv_orig_hash_find(bat_priv, dst); if (!orig_node) @@ -633,9 +632,7 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src, tvlv_buff += sizeof(*tvlv_hdr); memcpy(tvlv_buff, tvlv_value, tvlv_value_len); - res = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (res == -1) - kfree_skb(skb); + batadv_send_skb_to_orig(skb, orig_node, NULL); out: batadv_orig_node_put(orig_node); } -- cgit v1.1 From bdc379249c5b844c55213116e34e9b97c9029dbf Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 29 Sep 2016 19:35:38 +0300 Subject: IB/mlx5: Skip handling unknown events Do not dispatch unknown mlx5 core events on mlx5_ib_event. Signed-off-by: Saeed Mahameed Signed-off-by: Eugenia Emantayev Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 2217477..d02341e 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2358,6 +2358,8 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, ibev.event = IB_EVENT_CLIENT_REREGISTER; port = (u8)param; break; + default: + return; } ibev.device = &ibdev->ib_dev; -- cgit v1.1 From 5579e1519bad43b874922dbe87c74fdcbd97a7db Mon Sep 17 00:00:00 2001 From: Artemy Kovalyov Date: Wed, 31 Aug 2016 05:17:54 +0000 Subject: net/mlx5: Update struct mlx5_ifc_xrqc_bits Update struct mlx5_ifc_xrqc_bits according to last specification Signed-off-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky --- include/linux/mlx5/mlx5_ifc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 6045d4d..12f72e4 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -2844,7 +2844,7 @@ struct mlx5_ifc_xrqc_bits { struct mlx5_ifc_tag_matching_topology_context_bits tag_matching_topology_context; - u8 reserved_at_180[0x200]; + u8 reserved_at_180[0x880]; struct mlx5_ifc_wq_bits wq; }; -- cgit v1.1 From dd257efb1e0f8875ed7e42b88837a8dada0d0e41 Mon Sep 17 00:00:00 2001 From: Artemy Kovalyov Date: Wed, 31 Aug 2016 05:29:58 +0000 Subject: net/mlx5: Ensure SRQ physical address structure endianness SRQ physical address structure field should be in big-endian format. Signed-off-by: Artemy Kovalyov Signed-off-by: Leon Romanovsky Signed-off-by: Leon Romanovsky --- include/linux/mlx5/srq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mlx5/srq.h b/include/linux/mlx5/srq.h index 33c97dc..1cde0fd 100644 --- a/include/linux/mlx5/srq.h +++ b/include/linux/mlx5/srq.h @@ -55,7 +55,7 @@ struct mlx5_srq_attr { u32 lwm; u32 user_index; u64 db_record; - u64 *pas; + __be64 *pas; }; struct mlx5_core_dev; -- cgit v1.1 From 6887a825dc5cb41ef63fe166e28362590f9f6024 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Wed, 28 Sep 2016 17:29:41 +0300 Subject: net/mlx5: Fix length of async_event_mask According to PRM async_event_mask have to be 64 bits long. Signed-off-by: Eugenia Emantayev Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index aaca090..e74a73b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -469,7 +469,7 @@ void mlx5_eq_cleanup(struct mlx5_core_dev *dev) int mlx5_start_eqs(struct mlx5_core_dev *dev) { struct mlx5_eq_table *table = &dev->priv.eq_table; - u32 async_event_mask = MLX5_ASYNC_EVENT_MASK; + u64 async_event_mask = MLX5_ASYNC_EVENT_MASK; int err; if (MLX5_CAP_GEN(dev, pg)) -- cgit v1.1 From 86490d9a5969eaa8217a9329a6f0875cd0b041bc Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 18 Oct 2016 21:35:35 +0300 Subject: net/mlx5: Add ConnectX-5 PCIe 4.0 VF device ID For the mlx5 driver to support ConnectX-5 PCIe 4.0 VFs, we add the device ID "0x101a" to mlx5_core_pci_table. Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index d9c3c70..197e04c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1422,6 +1422,7 @@ static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1017) }, /* ConnectX-5, PCIe 3.0 */ { PCI_VDEVICE(MELLANOX, 0x1018), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 VF */ { PCI_VDEVICE(MELLANOX, 0x1019) }, /* ConnectX-5, PCIe 4.0 */ + { PCI_VDEVICE(MELLANOX, 0x101a), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5, PCIe 4.0 VF */ { 0, } }; -- cgit v1.1 From 813f854053c26204e2723c498def4c7870dcc7f4 Mon Sep 17 00:00:00 2001 From: Mohamad Haj Yahia Date: Thu, 11 Aug 2016 11:21:39 +0300 Subject: net/mlx5: Introduce TSAR manipulation firmware commands TSAR (stands for Transmit Scheduling ARbiter) is a hardware component that is responsible for selecting the next entity to serve on the transmit path. The arbitration defines the QoS policy between the agents connected to the TSAR. The TSAR is a consist two main features: 1) BW Allocation between agents: The TSAR implements a defecit weighted round robin between the agents. Each agent attached to the TSAR is assigned with a weight and it is awarded transmission tokens according to this weight. 2) Rate limer per agent: Each agent attached to the TSAR is (optionally) assigned with a rate limit. TSAR will not allow scheduling for an agent exceeding its defined rate limit. In this patch we implement the API of manipulating the TSAR. Signed-off-by: Mohamad Haj Yahia Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 13 +- .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 7 + drivers/net/ethernet/mellanox/mlx5/core/rl.c | 65 +++++++ include/linux/mlx5/mlx5_ifc.h | 199 ++++++++++++++++++++- 4 files changed, 279 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 1e639f8..8561102 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -318,6 +318,8 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY: case MLX5_CMD_OP_SET_FLOW_TABLE_ROOT: case MLX5_CMD_OP_DEALLOC_ENCAP_HEADER: + case MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_DESTROY_QOS_PARA_VPORT: return MLX5_CMD_STAT_OK; case MLX5_CMD_OP_QUERY_HCA_CAP: @@ -419,11 +421,14 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_QUERY_FLOW_TABLE: case MLX5_CMD_OP_CREATE_FLOW_GROUP: case MLX5_CMD_OP_QUERY_FLOW_GROUP: - case MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY: case MLX5_CMD_OP_ALLOC_FLOW_COUNTER: case MLX5_CMD_OP_QUERY_FLOW_COUNTER: case MLX5_CMD_OP_ALLOC_ENCAP_HEADER: + case MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_QUERY_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_MODIFY_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_CREATE_QOS_PARA_VPORT: *status = MLX5_DRIVER_STATUS_ABORTED; *synd = MLX5_DRIVER_SYND; return -EIO; @@ -580,6 +585,12 @@ const char *mlx5_command_str(int command) MLX5_COMMAND_STR_CASE(MODIFY_FLOW_TABLE); MLX5_COMMAND_STR_CASE(ALLOC_ENCAP_HEADER); MLX5_COMMAND_STR_CASE(DEALLOC_ENCAP_HEADER); + MLX5_COMMAND_STR_CASE(CREATE_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(DESTROY_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(QUERY_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(MODIFY_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(CREATE_QOS_PARA_VPORT); + MLX5_COMMAND_STR_CASE(DESTROY_QOS_PARA_VPORT); default: return "unknown command opcode"; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 3d0cfb9..bf43171 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -91,6 +91,13 @@ int mlx5_core_sriov_configure(struct pci_dev *dev, int num_vfs); bool mlx5_sriov_is_enabled(struct mlx5_core_dev *dev); int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id); int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id); +int mlx5_create_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, + void *context, u32 *element_id); +int mlx5_modify_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, + void *context, u32 element_id, + u32 modify_bitmask); +int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, + u32 element_id); int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev); cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev); u32 mlx5_get_msix_vec(struct mlx5_core_dev *dev, int vecidx); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/rl.c b/drivers/net/ethernet/mellanox/mlx5/core/rl.c index 104902a..e651e4c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/rl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/rl.c @@ -36,6 +36,71 @@ #include #include "mlx5_core.h" +/* Scheduling element fw management */ +int mlx5_create_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, + void *ctx, u32 *element_id) +{ + u32 in[MLX5_ST_SZ_DW(create_scheduling_element_in)] = {0}; + u32 out[MLX5_ST_SZ_DW(create_scheduling_element_in)] = {0}; + void *schedc; + int err; + + schedc = MLX5_ADDR_OF(create_scheduling_element_in, in, + scheduling_context); + MLX5_SET(create_scheduling_element_in, in, opcode, + MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT); + MLX5_SET(create_scheduling_element_in, in, scheduling_hierarchy, + hierarchy); + memcpy(schedc, ctx, MLX5_ST_SZ_BYTES(scheduling_context)); + + err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); + if (err) + return err; + + *element_id = MLX5_GET(create_scheduling_element_out, out, + scheduling_element_id); + return 0; +} + +int mlx5_modify_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, + void *ctx, u32 element_id, + u32 modify_bitmask) +{ + u32 in[MLX5_ST_SZ_DW(modify_scheduling_element_in)] = {0}; + u32 out[MLX5_ST_SZ_DW(modify_scheduling_element_in)] = {0}; + void *schedc; + + schedc = MLX5_ADDR_OF(modify_scheduling_element_in, in, + scheduling_context); + MLX5_SET(modify_scheduling_element_in, in, opcode, + MLX5_CMD_OP_MODIFY_SCHEDULING_ELEMENT); + MLX5_SET(modify_scheduling_element_in, in, scheduling_element_id, + element_id); + MLX5_SET(modify_scheduling_element_in, in, modify_bitmask, + modify_bitmask); + MLX5_SET(modify_scheduling_element_in, in, scheduling_hierarchy, + hierarchy); + memcpy(schedc, ctx, MLX5_ST_SZ_BYTES(scheduling_context)); + + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + +int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, + u32 element_id) +{ + u32 in[MLX5_ST_SZ_DW(destroy_scheduling_element_in)] = {0}; + u32 out[MLX5_ST_SZ_DW(destroy_scheduling_element_in)] = {0}; + + MLX5_SET(destroy_scheduling_element_in, in, opcode, + MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT); + MLX5_SET(destroy_scheduling_element_in, in, scheduling_element_id, + element_id); + MLX5_SET(destroy_scheduling_element_in, in, scheduling_hierarchy, + hierarchy); + + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + /* Finds an entry where we can register the given rate * If the rate already exists, return the entry where it is registered, * otherwise return the first available entry. diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 12f72e4..2632cb2 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -145,6 +145,12 @@ enum { MLX5_CMD_OP_QUERY_Q_COUNTER = 0x773, MLX5_CMD_OP_SET_RATE_LIMIT = 0x780, MLX5_CMD_OP_QUERY_RATE_LIMIT = 0x781, + MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT = 0x782, + MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT = 0x783, + MLX5_CMD_OP_QUERY_SCHEDULING_ELEMENT = 0x784, + MLX5_CMD_OP_MODIFY_SCHEDULING_ELEMENT = 0x785, + MLX5_CMD_OP_CREATE_QOS_PARA_VPORT = 0x786, + MLX5_CMD_OP_DESTROY_QOS_PARA_VPORT = 0x787, MLX5_CMD_OP_ALLOC_PD = 0x800, MLX5_CMD_OP_DEALLOC_PD = 0x801, MLX5_CMD_OP_ALLOC_UAR = 0x802, @@ -537,13 +543,27 @@ struct mlx5_ifc_e_switch_cap_bits { struct mlx5_ifc_qos_cap_bits { u8 packet_pacing[0x1]; - u8 reserved_0[0x1f]; - u8 reserved_1[0x20]; + u8 esw_scheduling[0x1]; + u8 reserved_at_2[0x1e]; + + u8 reserved_at_20[0x20]; + u8 packet_pacing_max_rate[0x20]; + u8 packet_pacing_min_rate[0x20]; - u8 reserved_2[0x10]; + + u8 reserved_at_80[0x10]; u8 packet_pacing_rate_table_size[0x10]; - u8 reserved_3[0x760]; + + u8 esw_element_type[0x10]; + u8 esw_tsar_type[0x10]; + + u8 reserved_at_c0[0x10]; + u8 max_qos_para_vport[0x10]; + + u8 max_tsar_bw_share[0x20]; + + u8 reserved_at_100[0x700]; }; struct mlx5_ifc_per_protocol_networking_offload_caps_bits { @@ -2333,6 +2353,30 @@ struct mlx5_ifc_sqc_bits { struct mlx5_ifc_wq_bits wq; }; +enum { + SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR = 0x0, + SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT = 0x1, + SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT_TC = 0x2, + SCHEDULING_CONTEXT_ELEMENT_TYPE_PARA_VPORT_TC = 0x3, +}; + +struct mlx5_ifc_scheduling_context_bits { + u8 element_type[0x8]; + u8 reserved_at_8[0x18]; + + u8 element_attributes[0x20]; + + u8 parent_element_id[0x20]; + + u8 reserved_at_60[0x40]; + + u8 bw_share[0x20]; + + u8 max_average_bw[0x20]; + + u8 reserved_at_e0[0x120]; +}; + struct mlx5_ifc_rqtc_bits { u8 reserved_at_0[0xa0]; @@ -2920,6 +2964,29 @@ struct mlx5_ifc_register_loopback_control_bits { u8 reserved_at_20[0x60]; }; +struct mlx5_ifc_vport_tc_element_bits { + u8 traffic_class[0x4]; + u8 reserved_at_4[0xc]; + u8 vport_number[0x10]; +}; + +struct mlx5_ifc_vport_element_bits { + u8 reserved_at_0[0x10]; + u8 vport_number[0x10]; +}; + +enum { + TSAR_ELEMENT_TSAR_TYPE_DWRR = 0x0, + TSAR_ELEMENT_TSAR_TYPE_ROUND_ROBIN = 0x1, + TSAR_ELEMENT_TSAR_TYPE_ETS = 0x2, +}; + +struct mlx5_ifc_tsar_element_bits { + u8 reserved_at_0[0x8]; + u8 tsar_type[0x8]; + u8 reserved_at_10[0x10]; +}; + struct mlx5_ifc_teardown_hca_out_bits { u8 status[0x8]; u8 reserved_at_8[0x18]; @@ -3540,6 +3607,39 @@ struct mlx5_ifc_query_special_contexts_in_bits { u8 reserved_at_40[0x40]; }; +struct mlx5_ifc_query_scheduling_element_out_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 reserved_at_40[0xc0]; + + struct mlx5_ifc_scheduling_context_bits scheduling_context; + + u8 reserved_at_300[0x100]; +}; + +enum { + SCHEDULING_HIERARCHY_E_SWITCH = 0x2, +}; + +struct mlx5_ifc_query_scheduling_element_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 scheduling_hierarchy[0x8]; + u8 reserved_at_48[0x18]; + + u8 scheduling_element_id[0x20]; + + u8 reserved_at_80[0x180]; +}; + struct mlx5_ifc_query_rqt_out_bits { u8 status[0x8]; u8 reserved_at_8[0x18]; @@ -4725,6 +4825,43 @@ struct mlx5_ifc_modify_sq_in_bits { struct mlx5_ifc_sqc_bits ctx; }; +struct mlx5_ifc_modify_scheduling_element_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x1c0]; +}; + +enum { + MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_BW_SHARE = 0x1, + MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW = 0x2, +}; + +struct mlx5_ifc_modify_scheduling_element_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 scheduling_hierarchy[0x8]; + u8 reserved_at_48[0x18]; + + u8 scheduling_element_id[0x20]; + + u8 reserved_at_80[0x20]; + + u8 modify_bitmask[0x20]; + + u8 reserved_at_c0[0x40]; + + struct mlx5_ifc_scheduling_context_bits scheduling_context; + + u8 reserved_at_300[0x100]; +}; + struct mlx5_ifc_modify_rqt_out_bits { u8 status[0x8]; u8 reserved_at_8[0x18]; @@ -5390,6 +5527,30 @@ struct mlx5_ifc_destroy_sq_in_bits { u8 reserved_at_60[0x20]; }; +struct mlx5_ifc_destroy_scheduling_element_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x1c0]; +}; + +struct mlx5_ifc_destroy_scheduling_element_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 scheduling_hierarchy[0x8]; + u8 reserved_at_48[0x18]; + + u8 scheduling_element_id[0x20]; + + u8 reserved_at_80[0x180]; +}; + struct mlx5_ifc_destroy_rqt_out_bits { u8 status[0x8]; u8 reserved_at_8[0x18]; @@ -6017,6 +6178,36 @@ struct mlx5_ifc_create_sq_in_bits { struct mlx5_ifc_sqc_bits ctx; }; +struct mlx5_ifc_create_scheduling_element_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x40]; + + u8 scheduling_element_id[0x20]; + + u8 reserved_at_a0[0x160]; +}; + +struct mlx5_ifc_create_scheduling_element_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 scheduling_hierarchy[0x8]; + u8 reserved_at_48[0x18]; + + u8 reserved_at_60[0xa0]; + + struct mlx5_ifc_scheduling_context_bits scheduling_context; + + u8 reserved_at_300[0x100]; +}; + struct mlx5_ifc_create_rqt_out_bits { u8 status[0x8]; u8 reserved_at_8[0x18]; -- cgit v1.1 From 1bd27b11c1df33a1dc3277e2a0abbf6bd33cc817 Mon Sep 17 00:00:00 2001 From: Mohamad Haj Yahia Date: Thu, 11 Aug 2016 11:26:36 +0300 Subject: net/mlx5: Introduce E-switch QoS management Add TSAR to the eswitch which will act as the vports rate limiter. Create/Destroy TSAR on Enable/Dsiable SRIOV. Attach/Detach vport to eswitch TSAR on Enable/Disable vport. Signed-off-by: Mohamad Haj Yahia Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 113 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 12 +++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index abbf2c3..2e11a94 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1351,6 +1351,106 @@ out: return err; } +/* Vport QoS management */ +static int esw_create_tsar(struct mlx5_eswitch *esw) +{ + u32 tsar_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0}; + struct mlx5_core_dev *dev = esw->dev; + int err; + + if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, esw_scheduling)) + return 0; + + if (esw->qos.enabled) + return -EEXIST; + + err = mlx5_create_scheduling_element_cmd(dev, + SCHEDULING_HIERARCHY_E_SWITCH, + &tsar_ctx, + &esw->qos.root_tsar_id); + if (err) { + esw_warn(esw->dev, "E-Switch create TSAR failed (%d)\n", err); + return err; + } + + esw->qos.enabled = true; + return 0; +} + +static void esw_destroy_tsar(struct mlx5_eswitch *esw) +{ + int err; + + if (!esw->qos.enabled) + return; + + err = mlx5_destroy_scheduling_element_cmd(esw->dev, + SCHEDULING_HIERARCHY_E_SWITCH, + esw->qos.root_tsar_id); + if (err) + esw_warn(esw->dev, "E-Switch destroy TSAR failed (%d)\n", err); + + esw->qos.enabled = false; +} + +static int esw_vport_enable_qos(struct mlx5_eswitch *esw, int vport_num, + u32 initial_max_rate) +{ + u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0}; + struct mlx5_vport *vport = &esw->vports[vport_num]; + struct mlx5_core_dev *dev = esw->dev; + void *vport_elem; + int err = 0; + + if (!esw->qos.enabled || !MLX5_CAP_GEN(dev, qos) || + !MLX5_CAP_QOS(dev, esw_scheduling)) + return 0; + + if (vport->qos.enabled) + return -EEXIST; + + MLX5_SET(scheduling_context, &sched_ctx, element_type, + SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT); + vport_elem = MLX5_ADDR_OF(scheduling_context, &sched_ctx, + element_attributes); + MLX5_SET(vport_element, vport_elem, vport_number, vport_num); + MLX5_SET(scheduling_context, &sched_ctx, parent_element_id, + esw->qos.root_tsar_id); + MLX5_SET(scheduling_context, &sched_ctx, max_average_bw, + initial_max_rate); + + err = mlx5_create_scheduling_element_cmd(dev, + SCHEDULING_HIERARCHY_E_SWITCH, + &sched_ctx, + &vport->qos.esw_tsar_ix); + if (err) { + esw_warn(esw->dev, "E-Switch create TSAR vport element failed (vport=%d,err=%d)\n", + vport_num, err); + return err; + } + + vport->qos.enabled = true; + return 0; +} + +static void esw_vport_disable_qos(struct mlx5_eswitch *esw, int vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + int err = 0; + + if (!vport->qos.enabled) + return; + + err = mlx5_destroy_scheduling_element_cmd(esw->dev, + SCHEDULING_HIERARCHY_E_SWITCH, + vport->qos.esw_tsar_ix); + if (err) + esw_warn(esw->dev, "E-Switch destroy TSAR vport element failed (vport=%d,err=%d)\n", + vport_num, err); + + vport->qos.enabled = false; +} + static void node_guid_gen_from_mac(u64 *node_guid, u8 mac[ETH_ALEN]) { ((u8 *)node_guid)[7] = mac[0]; @@ -1386,6 +1486,7 @@ static void esw_apply_vport_conf(struct mlx5_eswitch *esw, esw_vport_egress_config(esw, vport); } } + static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num, int enable_events) { @@ -1399,6 +1500,10 @@ static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num, /* Restore old vport configuration */ esw_apply_vport_conf(esw, vport); + /* Attach vport to the eswitch rate limiter */ + if (esw_vport_enable_qos(esw, vport_num, vport->info.max_rate)) + esw_warn(esw->dev, "Failed to attach vport %d to eswitch rate limiter", vport_num); + /* Sync with current vport context */ vport->enabled_events = enable_events; vport->enabled = true; @@ -1437,7 +1542,7 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) */ esw_vport_change_handle_locked(vport); vport->enabled_events = 0; - + esw_vport_disable_qos(esw, vport_num); if (vport_num && esw->mode == SRIOV_LEGACY) { mlx5_modify_vport_admin_state(esw->dev, MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, @@ -1483,6 +1588,10 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) if (err) goto abort; + err = esw_create_tsar(esw); + if (err) + esw_warn(esw->dev, "Failed to create eswitch TSAR"); + enabled_events = (mode == SRIOV_LEGACY) ? SRIOV_VPORT_EVENTS : UC_ADDR_CHANGE; for (i = 0; i <= nvfs; i++) esw_enable_vport(esw, i, enabled_events); @@ -1519,6 +1628,8 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) if (mc_promisc && mc_promisc->uplink_rule) mlx5_del_flow_rule(mc_promisc->uplink_rule); + esw_destroy_tsar(esw); + if (esw->mode == SRIOV_LEGACY) esw_destroy_legacy_fdb_table(esw); else if (esw->mode == SRIOV_OFFLOADS) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 2e2938e..fb8de34 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -115,6 +115,7 @@ struct mlx5_vport_info { u8 qos; u64 node_guid; int link_state; + u32 max_rate; bool spoofchk; bool trusted; }; @@ -133,6 +134,11 @@ struct mlx5_vport { struct mlx5_vport_info info; + struct { + bool enabled; + u32 esw_tsar_ix; + } qos; + bool enabled; u16 enabled_events; }; @@ -209,6 +215,12 @@ struct mlx5_eswitch { */ struct mutex state_lock; struct esw_mc_addr *mc_promisc; + + struct { + bool enabled; + u32 root_tsar_id; + } qos; + struct mlx5_esw_offload offloads; int mode; }; -- cgit v1.1 From bd77bf1cb595477528f06f5c52f913f70acd73bb Mon Sep 17 00:00:00 2001 From: Mohamad Haj Yahia Date: Thu, 11 Aug 2016 11:28:21 +0300 Subject: net/mlx5: Add SRIOV VF max rate configuration support Implement the vf set rate ndo by modifying the TSAR vport rate limit. Signed-off-by: Mohamad Haj Yahia Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 15 ++++++ drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 63 +++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 2 + 3 files changed, 80 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 7eaf380..7f763d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2945,6 +2945,20 @@ static int mlx5e_set_vf_trust(struct net_device *dev, int vf, bool setting) return mlx5_eswitch_set_vport_trust(mdev->priv.eswitch, vf + 1, setting); } + +static int mlx5e_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, + int max_tx_rate) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + + if (min_tx_rate) + return -EOPNOTSUPP; + + return mlx5_eswitch_set_vport_rate(mdev->priv.eswitch, vf + 1, + max_tx_rate); +} + static int mlx5_vport_link2ifla(u8 esw_link) { switch (esw_link) { @@ -3252,6 +3266,7 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { .ndo_set_vf_vlan = mlx5e_set_vf_vlan, .ndo_set_vf_spoofchk = mlx5e_set_vf_spoofchk, .ndo_set_vf_trust = mlx5e_set_vf_trust, + .ndo_set_vf_rate = mlx5e_set_vf_rate, .ndo_get_vf_config = mlx5e_get_vf_config, .ndo_set_vf_link_state = mlx5e_set_vf_link_state, .ndo_get_vf_stats = mlx5e_get_vf_stats, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 2e11a94..9ef01d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1451,6 +1451,47 @@ static void esw_vport_disable_qos(struct mlx5_eswitch *esw, int vport_num) vport->qos.enabled = false; } +static int esw_vport_qos_config(struct mlx5_eswitch *esw, int vport_num, + u32 max_rate) +{ + u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0}; + struct mlx5_vport *vport = &esw->vports[vport_num]; + struct mlx5_core_dev *dev = esw->dev; + void *vport_elem; + u32 bitmask = 0; + int err = 0; + + if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, esw_scheduling)) + return -EOPNOTSUPP; + + if (!vport->qos.enabled) + return -EIO; + + MLX5_SET(scheduling_context, &sched_ctx, element_type, + SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT); + vport_elem = MLX5_ADDR_OF(scheduling_context, &sched_ctx, + element_attributes); + MLX5_SET(vport_element, vport_elem, vport_number, vport_num); + MLX5_SET(scheduling_context, &sched_ctx, parent_element_id, + esw->qos.root_tsar_id); + MLX5_SET(scheduling_context, &sched_ctx, max_average_bw, + max_rate); + bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW; + + err = mlx5_modify_scheduling_element_cmd(dev, + SCHEDULING_HIERARCHY_E_SWITCH, + &sched_ctx, + vport->qos.esw_tsar_ix, + bitmask); + if (err) { + esw_warn(esw->dev, "E-Switch modify TSAR vport element failed (vport=%d,err=%d)\n", + vport_num, err); + return err; + } + + return 0; +} + static void node_guid_gen_from_mac(u64 *node_guid, u8 mac[ETH_ALEN]) { ((u8 *)node_guid)[7] = mac[0]; @@ -1888,6 +1929,7 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, ivi->qos = evport->info.qos; ivi->spoofchk = evport->info.spoofchk; ivi->trusted = evport->info.trusted; + ivi->max_tx_rate = evport->info.max_rate; mutex_unlock(&esw->state_lock); return 0; @@ -1981,6 +2023,27 @@ int mlx5_eswitch_set_vport_trust(struct mlx5_eswitch *esw, return 0; } +int mlx5_eswitch_set_vport_rate(struct mlx5_eswitch *esw, + int vport, u32 max_rate) +{ + struct mlx5_vport *evport; + int err = 0; + + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport)) + return -EINVAL; + + mutex_lock(&esw->state_lock); + evport = &esw->vports[vport]; + err = esw_vport_qos_config(esw, vport, max_rate); + if (!err) + evport->info.max_rate = max_rate; + + mutex_unlock(&esw->state_lock); + return err; +} + int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw, int vport, struct ifla_vf_stats *vf_stats) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index fb8de34..ddae90c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -246,6 +246,8 @@ int mlx5_eswitch_set_vport_spoofchk(struct mlx5_eswitch *esw, int vport, bool spoofchk); int mlx5_eswitch_set_vport_trust(struct mlx5_eswitch *esw, int vport_num, bool setting); +int mlx5_eswitch_set_vport_rate(struct mlx5_eswitch *esw, + int vport, u32 max_rate); int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, int vport, struct ifla_vf_info *ivi); int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw, -- cgit v1.1 From 0fd758d6112f867b2cc6df0f6a856048ff99b211 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Mon, 5 Sep 2016 10:58:04 +0000 Subject: net/mlx5: Don't unlock fte while still using it When adding a new rule to an fte, we need to hold the fte lock until we add that rule to the fte and increase the fte ref count. Fixes: 0c56b97503fd ("net/mlx5_core: Introduce flow steering API") Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 5da2cc8..a07ff30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1107,9 +1107,8 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, return rule; } rule = add_rule_fte(fte, fg, dest); - unlock_ref_node(&fte->node); if (IS_ERR(rule)) - goto unlock_fg; + goto unlock_fte; else goto add_rule; } @@ -1127,6 +1126,7 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, goto unlock_fg; } tree_init_node(&fte->node, 0, del_fte); + nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); rule = add_rule_fte(fte, fg, dest); if (IS_ERR(rule)) { kfree(fte); @@ -1139,6 +1139,8 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, list_add(&fte->node.list, prev); add_rule: tree_add_node(&rule->node, &fte->node); +unlock_fte: + unlock_ref_node(&fte->node); unlock_fg: unlock_ref_node(&fg->node); return rule; -- cgit v1.1 From 0501fc477c2bed1cce02acbc22d79418bfa2c8c3 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Sun, 11 Sep 2016 12:54:50 +0000 Subject: net/mlx5: Use fte status to decide on firmware command An fte status becomes FS_FTE_STATUS_EXISTING only after it was created in HW. We can use this in order to simplify the logic on what firmware command to use. If the status isn't FS_FTE_STATUS_EXISTING we need to create the fte, otherwise we need only to update it. Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index a07ff30..e2bab9d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -946,7 +946,7 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST); } - if (fte->dests_size == 1 || !dest) + if (!(fte->status & FS_FTE_STATUS_EXISTING)) err = mlx5_cmd_create_fte(get_dev(&ft->node), ft, fg->id, fte); else -- cgit v1.1 From 814fb8754155ef9535cad4ef1ce06429fe7988c6 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Fri, 16 Sep 2016 07:00:10 +0000 Subject: net/mlx5: Refactor find_flow_rule The way we compare between two dests will need to be used in other places in the future, so we factor out the comparison logic between two dests into a separate function. Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 29 ++++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index e2bab9d..fca6937 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -153,6 +153,8 @@ static void del_rule(struct fs_node *node); static void del_flow_table(struct fs_node *node); static void del_flow_group(struct fs_node *node); static void del_fte(struct fs_node *node); +static bool mlx5_flow_dests_cmp(struct mlx5_flow_destination *d1, + struct mlx5_flow_destination *d2); static void tree_init_node(struct fs_node *node, unsigned int refcount, @@ -1064,21 +1066,30 @@ out: return fg; } +static bool mlx5_flow_dests_cmp(struct mlx5_flow_destination *d1, + struct mlx5_flow_destination *d2) +{ + if (d1->type == d2->type) { + if ((d1->type == MLX5_FLOW_DESTINATION_TYPE_VPORT && + d1->vport_num == d2->vport_num) || + (d1->type == MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE && + d1->ft == d2->ft) || + (d1->type == MLX5_FLOW_DESTINATION_TYPE_TIR && + d1->tir_num == d2->tir_num)) + return true; + } + + return false; +} + static struct mlx5_flow_rule *find_flow_rule(struct fs_fte *fte, struct mlx5_flow_destination *dest) { struct mlx5_flow_rule *rule; list_for_each_entry(rule, &fte->node.children, node.list) { - if (rule->dest_attr.type == dest->type) { - if ((dest->type == MLX5_FLOW_DESTINATION_TYPE_VPORT && - dest->vport_num == rule->dest_attr.vport_num) || - (dest->type == MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE && - dest->ft == rule->dest_attr.ft) || - (dest->type == MLX5_FLOW_DESTINATION_TYPE_TIR && - dest->tir_num == rule->dest_attr.tir_num)) - return rule; - } + if (mlx5_flow_dests_cmp(&rule->dest_attr, dest)) + return rule; } return NULL; } -- cgit v1.1 From a62249857779733dc7cb5d43d6ecdc35fa81b88f Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Sun, 11 Sep 2016 13:03:06 +0000 Subject: net/mlx5: Group similer rules under the same fte When adding a new rule, if we can match it with compare_match_value and flow tag we might be able to insert the rule to the same fte. In order to do that, there must be an overlap between the actions of the fte and the new rule. When updating the action of an existing fte, we must tell the firmware we are doing so. Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index fca6937..43d7052 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -920,7 +920,8 @@ static struct mlx5_flow_rule *alloc_rule(struct mlx5_flow_destination *dest) /* fte should not be deleted while calling this function */ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, struct mlx5_flow_group *fg, - struct mlx5_flow_destination *dest) + struct mlx5_flow_destination *dest, + bool update_action) { struct mlx5_flow_table *ft; struct mlx5_flow_rule *rule; @@ -931,6 +932,9 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, if (!rule) return ERR_PTR(-ENOMEM); + if (update_action) + modify_mask |= BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION); + fs_get_obj(ft, fg->node.parent); /* Add dest to dests list- we need flow tables to be in the * end of the list for forward to next prio rules. @@ -1109,7 +1113,9 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, fs_for_each_fte(fte, fg) { nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); if (compare_match_value(&fg->mask, match_value, &fte->val) && - action == fte->action && flow_tag == fte->flow_tag) { + (action & fte->action) && flow_tag == fte->flow_tag) { + int old_action = fte->action; + rule = find_flow_rule(fte, dest); if (rule) { atomic_inc(&rule->node.refcount); @@ -1117,11 +1123,15 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, unlock_ref_node(&fg->node); return rule; } - rule = add_rule_fte(fte, fg, dest); - if (IS_ERR(rule)) + fte->action |= action; + rule = add_rule_fte(fte, fg, dest, + old_action != action); + if (IS_ERR(rule)) { + fte->action = old_action; goto unlock_fte; - else + } else { goto add_rule; + } } unlock_ref_node(&fte->node); } @@ -1138,7 +1148,7 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, } tree_init_node(&fte->node, 0, del_fte); nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); - rule = add_rule_fte(fte, fg, dest); + rule = add_rule_fte(fte, fg, dest, false); if (IS_ERR(rule)) { kfree(fte); goto unlock_fg; -- cgit v1.1 From 74491de937125d0c98c9b9c9208b4105717a3caa Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Wed, 31 Aug 2016 11:24:25 +0000 Subject: net/mlx5: Add multi dest support Currently when calling mlx5_add_flow_rule we accept only one flow destination, this commit allows to pass multiple destinations. This change forces us to change the return structure to a more flexible one. We introduce a flow handle (struct mlx5_flow_handle), it holds internally the number for rules created and holds an array where each cell points the to a flow rule. From the consumers (of mlx5_add_flow_rule) point of view this change is only cosmetic and requires only to change the type of the returned value they store. From the core point of view, we now need to use a loop when allocating and deleting rules (e.g given to us a flow handler). Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/main.c | 14 +- drivers/infiniband/hw/mlx5/mlx5_ib.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en.h | 14 +- drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 38 +-- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 49 ++-- .../ethernet/mellanox/mlx5/core/en_fs_ethtool.c | 19 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 6 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 32 +-- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 68 ++--- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 22 +- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 42 +-- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 289 ++++++++++++++------- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 5 + include/linux/mlx5/fs.h | 28 +- 14 files changed, 374 insertions(+), 254 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index d02341e..8e0dbd5 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1771,13 +1771,13 @@ static int mlx5_ib_destroy_flow(struct ib_flow *flow_id) mutex_lock(&dev->flow_db.lock); list_for_each_entry_safe(iter, tmp, &handler->list, list) { - mlx5_del_flow_rule(iter->rule); + mlx5_del_flow_rules(iter->rule); put_flow_table(dev, iter->prio, true); list_del(&iter->list); kfree(iter); } - mlx5_del_flow_rule(handler->rule); + mlx5_del_flow_rules(handler->rule); put_flow_table(dev, handler->prio, true); mutex_unlock(&dev->flow_db.lock); @@ -1907,10 +1907,10 @@ static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev, spec->match_criteria_enable = get_match_criteria_enable(spec->match_criteria); action = dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST : MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO; - handler->rule = mlx5_add_flow_rule(ft, spec, + handler->rule = mlx5_add_flow_rules(ft, spec, action, MLX5_FS_DEFAULT_FLOW_TAG, - dst); + dst, 1); if (IS_ERR(handler->rule)) { err = PTR_ERR(handler->rule); @@ -1941,7 +1941,7 @@ static struct mlx5_ib_flow_handler *create_dont_trap_rule(struct mlx5_ib_dev *de handler_dst = create_flow_rule(dev, ft_prio, flow_attr, dst); if (IS_ERR(handler_dst)) { - mlx5_del_flow_rule(handler->rule); + mlx5_del_flow_rules(handler->rule); ft_prio->refcount--; kfree(handler); handler = handler_dst; @@ -2004,7 +2004,7 @@ static struct mlx5_ib_flow_handler *create_leftovers_rule(struct mlx5_ib_dev *de &leftovers_specs[LEFTOVERS_UC].flow_attr, dst); if (IS_ERR(handler_ucast)) { - mlx5_del_flow_rule(handler->rule); + mlx5_del_flow_rules(handler->rule); ft_prio->refcount--; kfree(handler); handler = handler_ucast; @@ -2046,7 +2046,7 @@ static struct mlx5_ib_flow_handler *create_sniffer_rule(struct mlx5_ib_dev *dev, return handler_rx; err_tx: - mlx5_del_flow_rule(handler_rx->rule); + mlx5_del_flow_rules(handler_rx->rule); ft_rx->refcount--; kfree(handler_rx); err: diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index dcdcd19..d5d0077 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -153,7 +153,7 @@ struct mlx5_ib_flow_handler { struct list_head list; struct ib_flow ibflow; struct mlx5_ib_flow_prio *prio; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; }; struct mlx5_ib_flow_db { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 460363b..47ee8ff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -520,7 +520,7 @@ struct mlx5e_vxlan_db { struct mlx5e_l2_rule { u8 addr[ETH_ALEN + 2]; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; }; struct mlx5e_flow_table { @@ -541,10 +541,10 @@ struct mlx5e_tc_table { struct mlx5e_vlan_table { struct mlx5e_flow_table ft; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; - struct mlx5_flow_rule *active_vlans_rule[VLAN_N_VID]; - struct mlx5_flow_rule *untagged_rule; - struct mlx5_flow_rule *any_vlan_rule; - bool filter_disabled; + struct mlx5_flow_handle *active_vlans_rule[VLAN_N_VID]; + struct mlx5_flow_handle *untagged_rule; + struct mlx5_flow_handle *any_vlan_rule; + bool filter_disabled; }; struct mlx5e_l2_table { @@ -562,14 +562,14 @@ struct mlx5e_l2_table { /* L3/L4 traffic type classifier */ struct mlx5e_ttc_table { struct mlx5e_flow_table ft; - struct mlx5_flow_rule *rules[MLX5E_NUM_TT]; + struct mlx5_flow_handle *rules[MLX5E_NUM_TT]; }; #define ARFS_HASH_SHIFT BITS_PER_BYTE #define ARFS_HASH_SIZE BIT(BITS_PER_BYTE) struct arfs_table { struct mlx5e_flow_table ft; - struct mlx5_flow_rule *default_rule; + struct mlx5_flow_handle *default_rule; struct hlist_head rules_hash[ARFS_HASH_SIZE]; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index a8cb387..8ff22e8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -56,7 +56,7 @@ struct arfs_tuple { struct arfs_rule { struct mlx5e_priv *priv; struct work_struct arfs_work; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; struct hlist_node hlist; int rxq; /* Flow ID passed to ndo_rx_flow_steer */ @@ -104,7 +104,7 @@ static int arfs_disable(struct mlx5e_priv *priv) tt = arfs_get_tt(i); /* Modify ttc rules destination to bypass the aRFS tables*/ err = mlx5_modify_rule_destination(priv->fs.ttc.rules[tt], - &dest); + &dest, NULL); if (err) { netdev_err(priv->netdev, "%s: modify ttc destination failed\n", @@ -137,7 +137,7 @@ int mlx5e_arfs_enable(struct mlx5e_priv *priv) tt = arfs_get_tt(i); /* Modify ttc rules destination to point on the aRFS FTs */ err = mlx5_modify_rule_destination(priv->fs.ttc.rules[tt], - &dest); + &dest, NULL); if (err) { netdev_err(priv->netdev, "%s: modify ttc destination failed err=%d\n", @@ -151,7 +151,7 @@ int mlx5e_arfs_enable(struct mlx5e_priv *priv) static void arfs_destroy_table(struct arfs_table *arfs_t) { - mlx5_del_flow_rule(arfs_t->default_rule); + mlx5_del_flow_rules(arfs_t->default_rule); mlx5e_destroy_flow_table(&arfs_t->ft); } @@ -205,10 +205,10 @@ static int arfs_add_default_rule(struct mlx5e_priv *priv, goto out; } - arfs_t->default_rule = mlx5_add_flow_rule(arfs_t->ft.t, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - &dest); + arfs_t->default_rule = mlx5_add_flow_rules(arfs_t->ft.t, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, + &dest, 1); if (IS_ERR(arfs_t->default_rule)) { err = PTR_ERR(arfs_t->default_rule); arfs_t->default_rule = NULL; @@ -396,7 +396,7 @@ static void arfs_may_expire_flow(struct mlx5e_priv *priv) spin_unlock_bh(&priv->fs.arfs.arfs_lock); hlist_for_each_entry_safe(arfs_rule, htmp, &del_list, hlist) { if (arfs_rule->rule) - mlx5_del_flow_rule(arfs_rule->rule); + mlx5_del_flow_rules(arfs_rule->rule); hlist_del(&arfs_rule->hlist); kfree(arfs_rule); } @@ -420,7 +420,7 @@ static void arfs_del_rules(struct mlx5e_priv *priv) hlist_for_each_entry_safe(rule, htmp, &del_list, hlist) { cancel_work_sync(&rule->arfs_work); if (rule->rule) - mlx5_del_flow_rule(rule->rule); + mlx5_del_flow_rules(rule->rule); hlist_del(&rule->hlist); kfree(rule); } @@ -462,12 +462,12 @@ static struct arfs_table *arfs_get_table(struct mlx5e_arfs_tables *arfs, return NULL; } -static struct mlx5_flow_rule *arfs_add_rule(struct mlx5e_priv *priv, - struct arfs_rule *arfs_rule) +static struct mlx5_flow_handle *arfs_add_rule(struct mlx5e_priv *priv, + struct arfs_rule *arfs_rule) { struct mlx5e_arfs_tables *arfs = &priv->fs.arfs; struct arfs_tuple *tuple = &arfs_rule->tuple; - struct mlx5_flow_rule *rule = NULL; + struct mlx5_flow_handle *rule = NULL; struct mlx5_flow_destination dest; struct arfs_table *arfs_table; struct mlx5_flow_spec *spec; @@ -544,9 +544,9 @@ static struct mlx5_flow_rule *arfs_add_rule(struct mlx5e_priv *priv, } dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; dest.tir_num = priv->direct_tir[arfs_rule->rxq].tirn; - rule = mlx5_add_flow_rule(ft, spec, MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - &dest); + rule = mlx5_add_flow_rules(ft, spec, MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, + &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "%s: add rule(filter id=%d, rq idx=%d) failed, err=%d\n", @@ -559,14 +559,14 @@ out: } static void arfs_modify_rule_rq(struct mlx5e_priv *priv, - struct mlx5_flow_rule *rule, u16 rxq) + struct mlx5_flow_handle *rule, u16 rxq) { struct mlx5_flow_destination dst; int err = 0; dst.type = MLX5_FLOW_DESTINATION_TYPE_TIR; dst.tir_num = priv->direct_tir[rxq].tirn; - err = mlx5_modify_rule_destination(rule, &dst); + err = mlx5_modify_rule_destination(rule, &dst, NULL); if (err) netdev_warn(priv->netdev, "Failed to modfiy aRFS rule destination to rq=%d\n", rxq); @@ -578,7 +578,7 @@ static void arfs_handle_work(struct work_struct *work) struct arfs_rule, arfs_work); struct mlx5e_priv *priv = arfs_rule->priv; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; mutex_lock(&priv->state_lock); if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index 36fbc6b..bed544d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -160,7 +160,7 @@ static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, { struct mlx5_flow_table *ft = priv->fs.vlan.ft.t; struct mlx5_flow_destination dest; - struct mlx5_flow_rule **rule_p; + struct mlx5_flow_handle **rule_p; int err = 0; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; @@ -187,10 +187,10 @@ static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, break; } - *rule_p = mlx5_add_flow_rule(ft, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - &dest); + *rule_p = mlx5_add_flow_rules(ft, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, + &dest, 1); if (IS_ERR(*rule_p)) { err = PTR_ERR(*rule_p); @@ -229,20 +229,20 @@ static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv, switch (rule_type) { case MLX5E_VLAN_RULE_TYPE_UNTAGGED: if (priv->fs.vlan.untagged_rule) { - mlx5_del_flow_rule(priv->fs.vlan.untagged_rule); + mlx5_del_flow_rules(priv->fs.vlan.untagged_rule); priv->fs.vlan.untagged_rule = NULL; } break; case MLX5E_VLAN_RULE_TYPE_ANY_VID: if (priv->fs.vlan.any_vlan_rule) { - mlx5_del_flow_rule(priv->fs.vlan.any_vlan_rule); + mlx5_del_flow_rules(priv->fs.vlan.any_vlan_rule); priv->fs.vlan.any_vlan_rule = NULL; } break; case MLX5E_VLAN_RULE_TYPE_MATCH_VID: mlx5e_vport_context_update_vlans(priv); if (priv->fs.vlan.active_vlans_rule[vid]) { - mlx5_del_flow_rule(priv->fs.vlan.active_vlans_rule[vid]); + mlx5_del_flow_rules(priv->fs.vlan.active_vlans_rule[vid]); priv->fs.vlan.active_vlans_rule[vid] = NULL; } mlx5e_vport_context_update_vlans(priv); @@ -560,7 +560,7 @@ static void mlx5e_cleanup_ttc_rules(struct mlx5e_ttc_table *ttc) for (i = 0; i < MLX5E_NUM_TT; i++) { if (!IS_ERR_OR_NULL(ttc->rules[i])) { - mlx5_del_flow_rule(ttc->rules[i]); + mlx5_del_flow_rules(ttc->rules[i]); ttc->rules[i] = NULL; } } @@ -616,13 +616,14 @@ static struct { }, }; -static struct mlx5_flow_rule *mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, - struct mlx5_flow_table *ft, - struct mlx5_flow_destination *dest, - u16 etype, - u8 proto) +static struct mlx5_flow_handle * +mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, + struct mlx5_flow_table *ft, + struct mlx5_flow_destination *dest, + u16 etype, + u8 proto) { - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; int err = 0; @@ -643,10 +644,10 @@ static struct mlx5_flow_rule *mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, MLX5_SET(fte_match_param, spec->match_value, outer_headers.ethertype, etype); } - rule = mlx5_add_flow_rule(ft, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - dest); + rule = mlx5_add_flow_rules(ft, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, + dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "%s: add rule failed\n", __func__); @@ -660,7 +661,7 @@ static int mlx5e_generate_ttc_table_rules(struct mlx5e_priv *priv) { struct mlx5_flow_destination dest; struct mlx5e_ttc_table *ttc; - struct mlx5_flow_rule **rules; + struct mlx5_flow_handle **rules; struct mlx5_flow_table *ft; int tt; int err; @@ -801,7 +802,7 @@ static void mlx5e_del_l2_flow_rule(struct mlx5e_priv *priv, struct mlx5e_l2_rule *ai) { if (!IS_ERR_OR_NULL(ai->rule)) { - mlx5_del_flow_rule(ai->rule); + mlx5_del_flow_rules(ai->rule); ai->rule = NULL; } } @@ -847,9 +848,9 @@ static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, break; } - ai->rule = mlx5_add_flow_rule(ft, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); + ai->rule = mlx5_add_flow_rules(ft, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest, 1); if (IS_ERR(ai->rule)) { netdev_err(priv->netdev, "%s: add l2 rule(mac:%pM) failed\n", __func__, mv_dmac); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c index d17c242..cf52c06 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c @@ -36,7 +36,7 @@ struct mlx5e_ethtool_rule { struct list_head list; struct ethtool_rx_flow_spec flow_spec; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; struct mlx5e_ethtool_table *eth_ft; }; @@ -284,13 +284,14 @@ static bool outer_header_zero(u32 *match_criteria) size - 1); } -static struct mlx5_flow_rule *add_ethtool_flow_rule(struct mlx5e_priv *priv, - struct mlx5_flow_table *ft, - struct ethtool_rx_flow_spec *fs) +static struct mlx5_flow_handle * +add_ethtool_flow_rule(struct mlx5e_priv *priv, + struct mlx5_flow_table *ft, + struct ethtool_rx_flow_spec *fs) { struct mlx5_flow_destination *dst = NULL; struct mlx5_flow_spec *spec; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; int err = 0; u32 action; @@ -317,8 +318,8 @@ static struct mlx5_flow_rule *add_ethtool_flow_rule(struct mlx5e_priv *priv, } spec->match_criteria_enable = (!outer_header_zero(spec->match_criteria)); - rule = mlx5_add_flow_rule(ft, spec, action, - MLX5_FS_DEFAULT_FLOW_TAG, dst); + rule = mlx5_add_flow_rules(ft, spec, action, + MLX5_FS_DEFAULT_FLOW_TAG, dst, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "%s: failed to add ethtool steering rule: %d\n", @@ -335,7 +336,7 @@ static void del_ethtool_rule(struct mlx5e_priv *priv, struct mlx5e_ethtool_rule *eth_rule) { if (eth_rule->rule) - mlx5_del_flow_rule(eth_rule->rule); + mlx5_del_flow_rules(eth_rule->rule); list_del(ð_rule->list); priv->fs.ethtool.tot_num_rules--; put_flow_table(eth_rule->eth_ft); @@ -475,7 +476,7 @@ int mlx5e_ethtool_flow_replace(struct mlx5e_priv *priv, { struct mlx5e_ethtool_table *eth_ft; struct mlx5e_ethtool_rule *eth_rule; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; int num_tuples; int err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 3c97da1..88d3fd1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -328,7 +328,7 @@ static int mlx5e_init_rep_rx(struct mlx5e_priv *priv) struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_eswitch_rep *rep = priv->ppriv; struct mlx5_core_dev *mdev = priv->mdev; - struct mlx5_flow_rule *flow_rule; + struct mlx5_flow_handle *flow_rule; int err; int i; @@ -360,7 +360,7 @@ static int mlx5e_init_rep_rx(struct mlx5e_priv *priv) return 0; err_del_flow_rule: - mlx5_del_flow_rule(rep->vport_rx_rule); + mlx5_del_flow_rules(rep->vport_rx_rule); err_destroy_direct_tirs: mlx5e_destroy_direct_tirs(priv); err_destroy_direct_rqts: @@ -375,7 +375,7 @@ static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv) int i; mlx5e_tc_cleanup(priv); - mlx5_del_flow_rule(rep->vport_rx_rule); + mlx5_del_flow_rules(rep->vport_rx_rule); mlx5e_destroy_direct_tirs(priv); for (i = 0; i < priv->params.num_channels; i++) mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index ce8c54d..5d9ac0d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -47,21 +47,22 @@ struct mlx5e_tc_flow { struct rhash_head node; u64 cookie; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; struct mlx5_esw_flow_attr *attr; }; #define MLX5E_TC_TABLE_NUM_ENTRIES 1024 #define MLX5E_TC_TABLE_NUM_GROUPS 4 -static struct mlx5_flow_rule *mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, - struct mlx5_flow_spec *spec, - u32 action, u32 flow_tag) +static struct mlx5_flow_handle * +mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + u32 action, u32 flow_tag) { struct mlx5_core_dev *dev = priv->mdev; struct mlx5_flow_destination dest = { 0 }; struct mlx5_fc *counter = NULL; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; bool table_created = false; if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { @@ -94,9 +95,9 @@ static struct mlx5_flow_rule *mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, } spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - rule = mlx5_add_flow_rule(priv->fs.tc.t, spec, - action, flow_tag, - &dest); + rule = mlx5_add_flow_rules(priv->fs.tc.t, spec, + action, flow_tag, + &dest, 1); if (IS_ERR(rule)) goto err_add_rule; @@ -114,9 +115,10 @@ err_create_ft: return rule; } -static struct mlx5_flow_rule *mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, - struct mlx5_flow_spec *spec, - struct mlx5_esw_flow_attr *attr) +static struct mlx5_flow_handle * +mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + struct mlx5_esw_flow_attr *attr) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; int err; @@ -129,7 +131,7 @@ static struct mlx5_flow_rule *mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, } static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, - struct mlx5_flow_rule *rule, + struct mlx5_flow_handle *rule, struct mlx5_esw_flow_attr *attr) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; @@ -140,7 +142,7 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, if (esw && esw->mode == SRIOV_OFFLOADS) mlx5_eswitch_del_vlan_action(esw, attr); - mlx5_del_flow_rule(rule); + mlx5_del_flow_rules(rule); mlx5_fc_destroy(priv->mdev, counter); @@ -450,7 +452,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, u32 flow_tag, action; struct mlx5e_tc_flow *flow; struct mlx5_flow_spec *spec; - struct mlx5_flow_rule *old = NULL; + struct mlx5_flow_handle *old = NULL; struct mlx5_esw_flow_attr *old_attr = NULL; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; @@ -511,7 +513,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, goto out; err_del_rule: - mlx5_del_flow_rule(flow->rule); + mlx5_del_flow_rules(flow->rule); err_free: if (!old) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 9ef01d1..fcd8b15 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -56,7 +56,7 @@ struct esw_uc_addr { /* E-Switch MC FDB table hash node */ struct esw_mc_addr { /* SRIOV only */ struct l2addr_node node; - struct mlx5_flow_rule *uplink_rule; /* Forward to uplink rule */ + struct mlx5_flow_handle *uplink_rule; /* Forward to uplink rule */ u32 refcnt; }; @@ -65,7 +65,7 @@ struct vport_addr { struct l2addr_node node; u8 action; u32 vport; - struct mlx5_flow_rule *flow_rule; /* SRIOV only */ + struct mlx5_flow_handle *flow_rule; /* SRIOV only */ /* A flag indicating that mac was added due to mc promiscuous vport */ bool mc_promisc; }; @@ -237,13 +237,13 @@ static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index) } /* E-Switch FDB */ -static struct mlx5_flow_rule * +static struct mlx5_flow_handle * __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, u8 mac_c[ETH_ALEN], u8 mac_v[ETH_ALEN]) { int match_header = (is_zero_ether_addr(mac_c) ? 0 : MLX5_MATCH_OUTER_HEADERS); - struct mlx5_flow_rule *flow_rule = NULL; + struct mlx5_flow_handle *flow_rule = NULL; struct mlx5_flow_destination dest; struct mlx5_flow_spec *spec; void *mv_misc = NULL; @@ -286,9 +286,9 @@ __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, dmac_v, dmac_c, vport); spec->match_criteria_enable = match_header; flow_rule = - mlx5_add_flow_rule(esw->fdb_table.fdb, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest); + mlx5_add_flow_rules(esw->fdb_table.fdb, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + 0, &dest, 1); if (IS_ERR(flow_rule)) { esw_warn(esw->dev, "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n", @@ -300,7 +300,7 @@ __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, return flow_rule; } -static struct mlx5_flow_rule * +static struct mlx5_flow_handle * esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport) { u8 mac_c[ETH_ALEN]; @@ -309,7 +309,7 @@ esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport) return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac); } -static struct mlx5_flow_rule * +static struct mlx5_flow_handle * esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u32 vport) { u8 mac_c[ETH_ALEN]; @@ -322,7 +322,7 @@ esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u32 vport) return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac_v); } -static struct mlx5_flow_rule * +static struct mlx5_flow_handle * esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u32 vport) { u8 mac_c[ETH_ALEN]; @@ -515,7 +515,7 @@ static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) del_l2_table_entry(esw->dev, esw_uc->table_index); if (vaddr->flow_rule) - mlx5_del_flow_rule(vaddr->flow_rule); + mlx5_del_flow_rules(vaddr->flow_rule); vaddr->flow_rule = NULL; l2addr_hash_del(esw_uc); @@ -562,7 +562,7 @@ static void update_allmulti_vports(struct mlx5_eswitch *esw, case MLX5_ACTION_DEL: if (!iter_vaddr) continue; - mlx5_del_flow_rule(iter_vaddr->flow_rule); + mlx5_del_flow_rules(iter_vaddr->flow_rule); l2addr_hash_del(iter_vaddr); break; } @@ -632,7 +632,7 @@ static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) esw_mc->uplink_rule); if (vaddr->flow_rule) - mlx5_del_flow_rule(vaddr->flow_rule); + mlx5_del_flow_rules(vaddr->flow_rule); vaddr->flow_rule = NULL; /* If the multicast mac is added as a result of mc promiscuous vport, @@ -645,7 +645,7 @@ static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) update_allmulti_vports(esw, vaddr, esw_mc); if (esw_mc->uplink_rule) - mlx5_del_flow_rule(esw_mc->uplink_rule); + mlx5_del_flow_rules(esw_mc->uplink_rule); l2addr_hash_del(esw_mc); return 0; @@ -828,14 +828,14 @@ static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u32 vport_num, UPLINK_VPORT); allmulti_addr->refcnt++; } else if (vport->allmulti_rule) { - mlx5_del_flow_rule(vport->allmulti_rule); + mlx5_del_flow_rules(vport->allmulti_rule); vport->allmulti_rule = NULL; if (--allmulti_addr->refcnt > 0) goto promisc; if (allmulti_addr->uplink_rule) - mlx5_del_flow_rule(allmulti_addr->uplink_rule); + mlx5_del_flow_rules(allmulti_addr->uplink_rule); allmulti_addr->uplink_rule = NULL; } @@ -847,7 +847,7 @@ promisc: vport->promisc_rule = esw_fdb_set_vport_promisc_rule(esw, vport_num); } else if (vport->promisc_rule) { - mlx5_del_flow_rule(vport->promisc_rule); + mlx5_del_flow_rules(vport->promisc_rule); vport->promisc_rule = NULL; } } @@ -1015,10 +1015,10 @@ static void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan)) - mlx5_del_flow_rule(vport->egress.allowed_vlan); + mlx5_del_flow_rules(vport->egress.allowed_vlan); if (!IS_ERR_OR_NULL(vport->egress.drop_rule)) - mlx5_del_flow_rule(vport->egress.drop_rule); + mlx5_del_flow_rules(vport->egress.drop_rule); vport->egress.allowed_vlan = NULL; vport->egress.drop_rule = NULL; @@ -1173,10 +1173,10 @@ static void esw_vport_cleanup_ingress_rules(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { if (!IS_ERR_OR_NULL(vport->ingress.drop_rule)) - mlx5_del_flow_rule(vport->ingress.drop_rule); + mlx5_del_flow_rules(vport->ingress.drop_rule); if (!IS_ERR_OR_NULL(vport->ingress.allow_rule)) - mlx5_del_flow_rule(vport->ingress.allow_rule); + mlx5_del_flow_rules(vport->ingress.allow_rule); vport->ingress.drop_rule = NULL; vport->ingress.allow_rule = NULL; @@ -1253,9 +1253,9 @@ static int esw_vport_ingress_config(struct mlx5_eswitch *esw, spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; vport->ingress.allow_rule = - mlx5_add_flow_rule(vport->ingress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_ALLOW, - 0, NULL); + mlx5_add_flow_rules(vport->ingress.acl, spec, + MLX5_FLOW_CONTEXT_ACTION_ALLOW, + 0, NULL, 0); if (IS_ERR(vport->ingress.allow_rule)) { err = PTR_ERR(vport->ingress.allow_rule); esw_warn(esw->dev, @@ -1267,9 +1267,9 @@ static int esw_vport_ingress_config(struct mlx5_eswitch *esw, memset(spec, 0, sizeof(*spec)); vport->ingress.drop_rule = - mlx5_add_flow_rule(vport->ingress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_DROP, - 0, NULL); + mlx5_add_flow_rules(vport->ingress.acl, spec, + MLX5_FLOW_CONTEXT_ACTION_DROP, + 0, NULL, 0); if (IS_ERR(vport->ingress.drop_rule)) { err = PTR_ERR(vport->ingress.drop_rule); esw_warn(esw->dev, @@ -1321,9 +1321,9 @@ static int esw_vport_egress_config(struct mlx5_eswitch *esw, spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; vport->egress.allowed_vlan = - mlx5_add_flow_rule(vport->egress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_ALLOW, - 0, NULL); + mlx5_add_flow_rules(vport->egress.acl, spec, + MLX5_FLOW_CONTEXT_ACTION_ALLOW, + 0, NULL, 0); if (IS_ERR(vport->egress.allowed_vlan)) { err = PTR_ERR(vport->egress.allowed_vlan); esw_warn(esw->dev, @@ -1336,9 +1336,9 @@ static int esw_vport_egress_config(struct mlx5_eswitch *esw, /* Drop others rule (star rule) */ memset(spec, 0, sizeof(*spec)); vport->egress.drop_rule = - mlx5_add_flow_rule(vport->egress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_DROP, - 0, NULL); + mlx5_add_flow_rules(vport->egress.acl, spec, + MLX5_FLOW_CONTEXT_ACTION_DROP, + 0, NULL, 0); if (IS_ERR(vport->egress.drop_rule)) { err = PTR_ERR(vport->egress.drop_rule); esw_warn(esw->dev, @@ -1667,7 +1667,7 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) esw_disable_vport(esw, i); if (mc_promisc && mc_promisc->uplink_rule) - mlx5_del_flow_rule(mc_promisc->uplink_rule); + mlx5_del_flow_rules(mc_promisc->uplink_rule); esw_destroy_tsar(esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index ddae90c..6d414cb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -97,16 +97,16 @@ struct vport_ingress { struct mlx5_flow_group *allow_spoofchk_only_grp; struct mlx5_flow_group *allow_untagged_only_grp; struct mlx5_flow_group *drop_grp; - struct mlx5_flow_rule *allow_rule; - struct mlx5_flow_rule *drop_rule; + struct mlx5_flow_handle *allow_rule; + struct mlx5_flow_handle *drop_rule; }; struct vport_egress { struct mlx5_flow_table *acl; struct mlx5_flow_group *allowed_vlans_grp; struct mlx5_flow_group *drop_grp; - struct mlx5_flow_rule *allowed_vlan; - struct mlx5_flow_rule *drop_rule; + struct mlx5_flow_handle *allowed_vlan; + struct mlx5_flow_handle *drop_rule; }; struct mlx5_vport_info { @@ -125,8 +125,8 @@ struct mlx5_vport { int vport; struct hlist_head uc_list[MLX5_L2_ADDR_HASH_SIZE]; struct hlist_head mc_list[MLX5_L2_ADDR_HASH_SIZE]; - struct mlx5_flow_rule *promisc_rule; - struct mlx5_flow_rule *allmulti_rule; + struct mlx5_flow_handle *promisc_rule; + struct mlx5_flow_handle *allmulti_rule; struct work_struct vport_change_handler; struct vport_ingress ingress; @@ -162,7 +162,7 @@ struct mlx5_eswitch_fdb { struct mlx5_flow_table *fdb; struct mlx5_flow_group *send_to_vport_grp; struct mlx5_flow_group *miss_grp; - struct mlx5_flow_rule *miss_rule; + struct mlx5_flow_handle *miss_rule; int vlan_push_pop_refcount; } offloads; }; @@ -175,7 +175,7 @@ enum { }; struct mlx5_esw_sq { - struct mlx5_flow_rule *send_to_vport_rule; + struct mlx5_flow_handle *send_to_vport_rule; struct list_head list; }; @@ -188,7 +188,7 @@ struct mlx5_eswitch_rep { u8 hw_id[ETH_ALEN]; void *priv_data; - struct mlx5_flow_rule *vport_rx_rule; + struct mlx5_flow_handle *vport_rx_rule; struct list_head vport_sqs_list; u16 vlan; u32 vlan_refcount; @@ -257,11 +257,11 @@ int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw, struct mlx5_flow_spec; struct mlx5_esw_flow_attr; -struct mlx5_flow_rule * +struct mlx5_flow_handle * mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, struct mlx5_flow_spec *spec, struct mlx5_esw_flow_attr *attr); -struct mlx5_flow_rule * +struct mlx5_flow_handle * mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn); enum { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index c55ad8d..8b2a383 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -43,14 +43,14 @@ enum { FDB_SLOW_PATH }; -struct mlx5_flow_rule * +struct mlx5_flow_handle * mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, struct mlx5_flow_spec *spec, struct mlx5_esw_flow_attr *attr) { struct mlx5_flow_destination dest = { 0 }; struct mlx5_fc *counter = NULL; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; void *misc; int action; @@ -80,8 +80,8 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS; - rule = mlx5_add_flow_rule((struct mlx5_flow_table *)esw->fdb_table.fdb, - spec, action, 0, &dest); + rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, + spec, action, 0, &dest, 1); if (IS_ERR(rule)) mlx5_fc_destroy(esw->dev, counter); @@ -269,11 +269,11 @@ out: return err; } -static struct mlx5_flow_rule * +static struct mlx5_flow_handle * mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn) { struct mlx5_flow_destination dest; - struct mlx5_flow_rule *flow_rule; + struct mlx5_flow_handle *flow_rule; struct mlx5_flow_spec *spec; void *misc; @@ -296,9 +296,9 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; dest.vport_num = vport; - flow_rule = mlx5_add_flow_rule(esw->fdb_table.offloads.fdb, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest); + flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + 0, &dest, 1); if (IS_ERR(flow_rule)) esw_warn(esw->dev, "FDB: Failed to add send to vport rule err %ld\n", PTR_ERR(flow_rule)); out: @@ -315,7 +315,7 @@ void mlx5_eswitch_sqs2vport_stop(struct mlx5_eswitch *esw, return; list_for_each_entry_safe(esw_sq, tmp, &rep->vport_sqs_list, list) { - mlx5_del_flow_rule(esw_sq->send_to_vport_rule); + mlx5_del_flow_rules(esw_sq->send_to_vport_rule); list_del(&esw_sq->list); kfree(esw_sq); } @@ -325,7 +325,7 @@ int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep, u16 *sqns_array, int sqns_num) { - struct mlx5_flow_rule *flow_rule; + struct mlx5_flow_handle *flow_rule; struct mlx5_esw_sq *esw_sq; int err; int i; @@ -362,7 +362,7 @@ out_err: static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw) { struct mlx5_flow_destination dest; - struct mlx5_flow_rule *flow_rule = NULL; + struct mlx5_flow_handle *flow_rule = NULL; struct mlx5_flow_spec *spec; int err = 0; @@ -376,9 +376,9 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw) dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; dest.vport_num = 0; - flow_rule = mlx5_add_flow_rule(esw->fdb_table.offloads.fdb, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest); + flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + 0, &dest, 1); if (IS_ERR(flow_rule)) { err = PTR_ERR(flow_rule); esw_warn(esw->dev, "FDB: Failed to add miss flow rule err %d\n", err); @@ -501,7 +501,7 @@ static void esw_destroy_offloads_fdb_table(struct mlx5_eswitch *esw) return; esw_debug(esw->dev, "Destroy offloads FDB Table\n"); - mlx5_del_flow_rule(esw->fdb_table.offloads.miss_rule); + mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule); mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp); mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp); @@ -585,11 +585,11 @@ static void esw_destroy_vport_rx_group(struct mlx5_eswitch *esw) mlx5_destroy_flow_group(esw->offloads.vport_rx_group); } -struct mlx5_flow_rule * +struct mlx5_flow_handle * mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn) { struct mlx5_flow_destination dest; - struct mlx5_flow_rule *flow_rule; + struct mlx5_flow_handle *flow_rule; struct mlx5_flow_spec *spec; void *misc; @@ -610,9 +610,9 @@ mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn) dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; dest.tir_num = tirn; - flow_rule = mlx5_add_flow_rule(esw->offloads.ft_offloads, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest); + flow_rule = mlx5_add_flow_rules(esw->offloads.ft_offloads, spec, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + 0, &dest, 1); if (IS_ERR(flow_rule)) { esw_warn(esw->dev, "fs offloads: Failed to add vport rx rule err %ld\n", PTR_ERR(flow_rule)); goto out; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 43d7052..6732287 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -155,6 +155,9 @@ static void del_flow_group(struct fs_node *node); static void del_fte(struct fs_node *node); static bool mlx5_flow_dests_cmp(struct mlx5_flow_destination *d1, struct mlx5_flow_destination *d2); +static struct mlx5_flow_rule * +find_flow_rule(struct fs_fte *fte, + struct mlx5_flow_destination *dest); static void tree_init_node(struct fs_node *node, unsigned int refcount, @@ -640,8 +643,8 @@ static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio return err; } -int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, - struct mlx5_flow_destination *dest) +static int _mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, + struct mlx5_flow_destination *dest) { struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; @@ -666,6 +669,28 @@ int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, return err; } +int mlx5_modify_rule_destination(struct mlx5_flow_handle *handle, + struct mlx5_flow_destination *new_dest, + struct mlx5_flow_destination *old_dest) +{ + int i; + + if (!old_dest) { + if (handle->num_rules != 1) + return -EINVAL; + return _mlx5_modify_rule_destination(handle->rule[0], + new_dest); + } + + for (i = 0; i < handle->num_rules; i++) { + if (mlx5_flow_dests_cmp(new_dest, &handle->rule[i]->dest_attr)) + return _mlx5_modify_rule_destination(handle->rule[i], + new_dest); + } + + return -EINVAL; +} + /* Modify/set FWD rules that point on old_next_ft to point on new_next_ft */ static int connect_fwd_rules(struct mlx5_core_dev *dev, struct mlx5_flow_table *new_next_ft, @@ -688,7 +713,7 @@ static int connect_fwd_rules(struct mlx5_core_dev *dev, list_splice_init(&old_next_ft->fwd_rules, &new_next_ft->fwd_rules); mutex_unlock(&old_next_ft->lock); list_for_each_entry(iter, &new_next_ft->fwd_rules, next_ft) { - err = mlx5_modify_rule_destination(iter, &dest); + err = _mlx5_modify_rule_destination(iter, &dest); if (err) pr_err("mlx5_core: failed to modify rule to point on flow table %d\n", new_next_ft->id); @@ -917,41 +942,117 @@ static struct mlx5_flow_rule *alloc_rule(struct mlx5_flow_destination *dest) return rule; } -/* fte should not be deleted while calling this function */ -static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, - struct mlx5_flow_group *fg, - struct mlx5_flow_destination *dest, - bool update_action) +static struct mlx5_flow_handle *alloc_handle(int num_rules) { + struct mlx5_flow_handle *handle; + + handle = kzalloc(sizeof(*handle) + sizeof(handle->rule[0]) * + num_rules, GFP_KERNEL); + if (!handle) + return NULL; + + handle->num_rules = num_rules; + + return handle; +} + +static void destroy_flow_handle(struct fs_fte *fte, + struct mlx5_flow_handle *handle, + struct mlx5_flow_destination *dest, + int i) +{ + for (; --i >= 0;) { + if (atomic_dec_and_test(&handle->rule[i]->node.refcount)) { + fte->dests_size--; + list_del(&handle->rule[i]->node.list); + kfree(handle->rule[i]); + } + } + kfree(handle); +} + +static struct mlx5_flow_handle * +create_flow_handle(struct fs_fte *fte, + struct mlx5_flow_destination *dest, + int dest_num, + int *modify_mask, + bool *new_rule) +{ + struct mlx5_flow_handle *handle; + struct mlx5_flow_rule *rule = NULL; + static int count = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS); + static int dst = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST); + int type; + int i = 0; + + handle = alloc_handle((dest_num) ? dest_num : 1); + if (!handle) + return ERR_PTR(-ENOMEM); + + do { + if (dest) { + rule = find_flow_rule(fte, dest + i); + if (rule) { + atomic_inc(&rule->node.refcount); + goto rule_found; + } + } + + *new_rule = true; + rule = alloc_rule(dest + i); + if (!rule) + goto free_rules; + + /* Add dest to dests list- we need flow tables to be in the + * end of the list for forward to next prio rules. + */ + tree_init_node(&rule->node, 1, del_rule); + if (dest && + dest[i].type != MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE) + list_add(&rule->node.list, &fte->node.children); + else + list_add_tail(&rule->node.list, &fte->node.children); + if (dest) { + fte->dests_size++; + + type = dest[i].type == + MLX5_FLOW_DESTINATION_TYPE_COUNTER; + *modify_mask |= type ? count : dst; + } +rule_found: + handle->rule[i] = rule; + } while (++i < dest_num); + + return handle; + +free_rules: + destroy_flow_handle(fte, handle, dest, i); + return ERR_PTR(-ENOMEM); +} + +/* fte should not be deleted while calling this function */ +static struct mlx5_flow_handle * +add_rule_fte(struct fs_fte *fte, + struct mlx5_flow_group *fg, + struct mlx5_flow_destination *dest, + int dest_num, + bool update_action) +{ + struct mlx5_flow_handle *handle; struct mlx5_flow_table *ft; - struct mlx5_flow_rule *rule; int modify_mask = 0; int err; + bool new_rule = false; - rule = alloc_rule(dest); - if (!rule) - return ERR_PTR(-ENOMEM); + handle = create_flow_handle(fte, dest, dest_num, &modify_mask, + &new_rule); + if (IS_ERR(handle) || !new_rule) + goto out; if (update_action) modify_mask |= BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION); fs_get_obj(ft, fg->node.parent); - /* Add dest to dests list- we need flow tables to be in the - * end of the list for forward to next prio rules. - */ - tree_init_node(&rule->node, 1, del_rule); - if (dest && dest->type != MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE) - list_add(&rule->node.list, &fte->node.children); - else - list_add_tail(&rule->node.list, &fte->node.children); - if (dest) { - fte->dests_size++; - - modify_mask |= dest->type == MLX5_FLOW_DESTINATION_TYPE_COUNTER ? - BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS) : - BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST); - } - if (!(fte->status & FS_FTE_STATUS_EXISTING)) err = mlx5_cmd_create_fte(get_dev(&ft->node), ft, fg->id, fte); @@ -959,17 +1060,15 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, err = mlx5_cmd_update_fte(get_dev(&ft->node), ft, fg->id, modify_mask, fte); if (err) - goto free_rule; + goto free_handle; fte->status |= FS_FTE_STATUS_EXISTING; - return rule; +out: + return handle; -free_rule: - list_del(&rule->node.list); - kfree(rule); - if (dest) - fte->dests_size--; +free_handle: + destroy_flow_handle(fte, handle, dest, handle->num_rules); return ERR_PTR(err); } @@ -1098,16 +1197,18 @@ static struct mlx5_flow_rule *find_flow_rule(struct fs_fte *fte, return NULL; } -static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, - u32 *match_value, - u8 action, - u32 flow_tag, - struct mlx5_flow_destination *dest) +static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, + u32 *match_value, + u8 action, + u32 flow_tag, + struct mlx5_flow_destination *dest, + int dest_num) { - struct fs_fte *fte; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *handle; struct mlx5_flow_table *ft; struct list_head *prev; + struct fs_fte *fte; + int i; nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT); fs_for_each_fte(fte, fg) { @@ -1116,40 +1217,33 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, (action & fte->action) && flow_tag == fte->flow_tag) { int old_action = fte->action; - rule = find_flow_rule(fte, dest); - if (rule) { - atomic_inc(&rule->node.refcount); - unlock_ref_node(&fte->node); - unlock_ref_node(&fg->node); - return rule; - } fte->action |= action; - rule = add_rule_fte(fte, fg, dest, - old_action != action); - if (IS_ERR(rule)) { + handle = add_rule_fte(fte, fg, dest, dest_num, + old_action != action); + if (IS_ERR(handle)) { fte->action = old_action; goto unlock_fte; } else { - goto add_rule; + goto add_rules; } } unlock_ref_node(&fte->node); } fs_get_obj(ft, fg->node.parent); if (fg->num_ftes >= fg->max_ftes) { - rule = ERR_PTR(-ENOSPC); + handle = ERR_PTR(-ENOSPC); goto unlock_fg; } fte = create_fte(fg, match_value, action, flow_tag, &prev); if (IS_ERR(fte)) { - rule = (void *)fte; + handle = (void *)fte; goto unlock_fg; } tree_init_node(&fte->node, 0, del_fte); nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); - rule = add_rule_fte(fte, fg, dest, false); - if (IS_ERR(rule)) { + handle = add_rule_fte(fte, fg, dest, dest_num, false); + if (IS_ERR(handle)) { kfree(fte); goto unlock_fg; } @@ -1158,21 +1252,24 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, tree_add_node(&fte->node, &fg->node); list_add(&fte->node.list, prev); -add_rule: - tree_add_node(&rule->node, &fte->node); +add_rules: + for (i = 0; i < handle->num_rules; i++) { + if (atomic_read(&handle->rule[i]->node.refcount) == 1) + tree_add_node(&handle->rule[i]->node, &fte->node); + } unlock_fte: unlock_ref_node(&fte->node); unlock_fg: unlock_ref_node(&fg->node); - return rule; + return handle; } -struct mlx5_fc *mlx5_flow_rule_counter(struct mlx5_flow_rule *rule) +struct mlx5_fc *mlx5_flow_rule_counter(struct mlx5_flow_handle *handle) { struct mlx5_flow_rule *dst; struct fs_fte *fte; - fs_get_obj(fte, rule->node.parent); + fs_get_obj(fte, handle->rule[0]->node.parent); fs_for_each_dst(dst, fte) { if (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) @@ -1211,18 +1308,22 @@ static bool dest_is_valid(struct mlx5_flow_destination *dest, return true; } -static struct mlx5_flow_rule * -_mlx5_add_flow_rule(struct mlx5_flow_table *ft, - struct mlx5_flow_spec *spec, - u32 action, - u32 flow_tag, - struct mlx5_flow_destination *dest) +static struct mlx5_flow_handle * +_mlx5_add_flow_rules(struct mlx5_flow_table *ft, + struct mlx5_flow_spec *spec, + u32 action, + u32 flow_tag, + struct mlx5_flow_destination *dest, + int dest_num) { struct mlx5_flow_group *g; - struct mlx5_flow_rule *rule; + struct mlx5_flow_handle *rule; + int i; - if (!dest_is_valid(dest, action, ft)) - return ERR_PTR(-EINVAL); + for (i = 0; i < dest_num; i++) { + if (!dest_is_valid(&dest[i], action, ft)) + return ERR_PTR(-EINVAL); + } nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT); fs_for_each_fg(g, ft) @@ -1231,7 +1332,7 @@ _mlx5_add_flow_rule(struct mlx5_flow_table *ft, g->mask.match_criteria, spec->match_criteria)) { rule = add_rule_fg(g, spec->match_value, - action, flow_tag, dest); + action, flow_tag, dest, dest_num); if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) goto unlock; } @@ -1244,7 +1345,7 @@ _mlx5_add_flow_rule(struct mlx5_flow_table *ft, } rule = add_rule_fg(g, spec->match_value, - action, flow_tag, dest); + action, flow_tag, dest, dest_num); if (IS_ERR(rule)) { /* Remove assumes refcount > 0 and autogroup creates a group * with a refcount = 0. @@ -1265,17 +1366,18 @@ static bool fwd_next_prio_supported(struct mlx5_flow_table *ft) (MLX5_CAP_FLOWTABLE(get_dev(&ft->node), nic_rx_multi_path_tirs))); } -struct mlx5_flow_rule * -mlx5_add_flow_rule(struct mlx5_flow_table *ft, - struct mlx5_flow_spec *spec, - u32 action, - u32 flow_tag, - struct mlx5_flow_destination *dest) +struct mlx5_flow_handle * +mlx5_add_flow_rules(struct mlx5_flow_table *ft, + struct mlx5_flow_spec *spec, + u32 action, + u32 flow_tag, + struct mlx5_flow_destination *dest, + int dest_num) { struct mlx5_flow_root_namespace *root = find_root(&ft->node); struct mlx5_flow_destination gen_dest; struct mlx5_flow_table *next_ft = NULL; - struct mlx5_flow_rule *rule = NULL; + struct mlx5_flow_handle *handle = NULL; u32 sw_action = action; struct fs_prio *prio; @@ -1291,6 +1393,7 @@ mlx5_add_flow_rule(struct mlx5_flow_table *ft, gen_dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; gen_dest.ft = next_ft; dest = &gen_dest; + dest_num = 1; action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; } else { mutex_unlock(&root->chain_lock); @@ -1298,27 +1401,33 @@ mlx5_add_flow_rule(struct mlx5_flow_table *ft, } } - rule = _mlx5_add_flow_rule(ft, spec, action, flow_tag, dest); + handle = _mlx5_add_flow_rules(ft, spec, action, flow_tag, dest, + dest_num); if (sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { - if (!IS_ERR_OR_NULL(rule) && - (list_empty(&rule->next_ft))) { + if (!IS_ERR_OR_NULL(handle) && + (list_empty(&handle->rule[0]->next_ft))) { mutex_lock(&next_ft->lock); - list_add(&rule->next_ft, &next_ft->fwd_rules); + list_add(&handle->rule[0]->next_ft, + &next_ft->fwd_rules); mutex_unlock(&next_ft->lock); - rule->sw_action = MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO; + handle->rule[0]->sw_action = MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO; } mutex_unlock(&root->chain_lock); } - return rule; + return handle; } -EXPORT_SYMBOL(mlx5_add_flow_rule); +EXPORT_SYMBOL(mlx5_add_flow_rules); -void mlx5_del_flow_rule(struct mlx5_flow_rule *rule) +void mlx5_del_flow_rules(struct mlx5_flow_handle *handle) { - tree_remove_node(&rule->node); + int i; + + for (i = handle->num_rules - 1; i >= 0; i--) + tree_remove_node(&handle->rule[i]->node); + kfree(handle); } -EXPORT_SYMBOL(mlx5_del_flow_rule); +EXPORT_SYMBOL(mlx5_del_flow_rules); /* Assuming prio->node.children(flow tables) is sorted by level */ static struct mlx5_flow_table *find_next_ft(struct mlx5_flow_table *ft) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 71ff03b..d515088 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -94,6 +94,11 @@ struct mlx5_flow_rule { u32 sw_action; }; +struct mlx5_flow_handle { + int num_rules; + struct mlx5_flow_rule *rule[]; +}; + /* Type of children is mlx5_flow_group */ struct mlx5_flow_table { struct fs_node node; diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 93ebc5e..0dcd287 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -69,8 +69,8 @@ enum mlx5_flow_namespace_type { struct mlx5_flow_table; struct mlx5_flow_group; -struct mlx5_flow_rule; struct mlx5_flow_namespace; +struct mlx5_flow_handle; struct mlx5_flow_spec { u8 match_criteria_enable; @@ -127,18 +127,20 @@ void mlx5_destroy_flow_group(struct mlx5_flow_group *fg); /* Single destination per rule. * Group ID is implied by the match criteria. */ -struct mlx5_flow_rule * -mlx5_add_flow_rule(struct mlx5_flow_table *ft, - struct mlx5_flow_spec *spec, - u32 action, - u32 flow_tag, - struct mlx5_flow_destination *dest); -void mlx5_del_flow_rule(struct mlx5_flow_rule *fr); - -int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, - struct mlx5_flow_destination *dest); - -struct mlx5_fc *mlx5_flow_rule_counter(struct mlx5_flow_rule *rule); +struct mlx5_flow_handle * +mlx5_add_flow_rules(struct mlx5_flow_table *ft, + struct mlx5_flow_spec *spec, + u32 action, + u32 flow_tag, + struct mlx5_flow_destination *dest, + int dest_num); +void mlx5_del_flow_rules(struct mlx5_flow_handle *fr); + +int mlx5_modify_rule_destination(struct mlx5_flow_handle *handler, + struct mlx5_flow_destination *new_dest, + struct mlx5_flow_destination *old_dest); + +struct mlx5_fc *mlx5_flow_rule_counter(struct mlx5_flow_handle *handler); struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging); void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter); void mlx5_fc_query_cached(struct mlx5_fc *counter, -- cgit v1.1 From ae05831424ed99f1432492d6df7e8bfe139d2449 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Tue, 20 Sep 2016 07:59:05 +0000 Subject: net/mlx5: Add option to add fwd rule with counter Currently the code supports only drop rules to possess counters, add that ability also for fwd rules. Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 24 +++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 6732287..0dfd998 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -374,6 +374,7 @@ static void del_rule(struct fs_node *node) struct mlx5_core_dev *dev = get_dev(node); int match_len = MLX5_ST_SZ_BYTES(fte_match_param); int err; + bool update_fte = false; match_value = mlx5_vzalloc(match_len); if (!match_value) { @@ -392,13 +393,23 @@ static void del_rule(struct fs_node *node) list_del(&rule->next_ft); mutex_unlock(&rule->dest_attr.ft->lock); } + + if (rule->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER && + --fte->dests_size) { + modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION); + fte->action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT; + update_fte = true; + goto out; + } + if ((fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) && --fte->dests_size) { modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST), - err = mlx5_cmd_update_fte(dev, ft, - fg->id, - modify_mask, - fte); + update_fte = true; + } +out: + if (update_fte && fte->dests_size) { + err = mlx5_cmd_update_fte(dev, ft, fg->id, modify_mask, fte); if (err) mlx5_core_warn(dev, "%s can't del rule fg id=%d fte_index=%d\n", @@ -1287,8 +1298,9 @@ static bool counter_is_valid(struct mlx5_fc *counter, u32 action) if (!counter) return false; - /* Hardware support counter for a drop action only */ - return action == (MLX5_FLOW_CONTEXT_ACTION_DROP | MLX5_FLOW_CONTEXT_ACTION_COUNT); + return (action & (MLX5_FLOW_CONTEXT_ACTION_DROP | + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST)) && + (action & MLX5_FLOW_CONTEXT_ACTION_COUNT); } static bool dest_is_valid(struct mlx5_flow_destination *dest, -- cgit v1.1 From e37a79e5d4cac3831fac3d4afbf2461f56b4b7bd Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Tue, 20 Sep 2016 07:58:29 +0000 Subject: net/mlx5e: Add tc support for FWD rule with counter When creating a FWD rule using tc create also a HW counter for this rule. Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 3 ++- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 5d9ac0d..c2e4728 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -419,7 +419,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return -EINVAL; } - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_COUNT; out_priv = netdev_priv(out_dev); attr->out_rep = out_priv->ppriv; continue; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 8b2a383..53d9d6c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -48,11 +48,12 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, struct mlx5_flow_spec *spec, struct mlx5_esw_flow_attr *attr) { - struct mlx5_flow_destination dest = { 0 }; + struct mlx5_flow_destination dest[2] = {}; struct mlx5_fc *counter = NULL; struct mlx5_flow_handle *rule; void *misc; int action; + int i = 0; if (esw->mode != SRIOV_OFFLOADS) return ERR_PTR(-EOPNOTSUPP); @@ -60,15 +61,17 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, action = attr->action; if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { - dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; - dest.vport_num = attr->out_rep->vport; - action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; - } else if (action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { + dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT; + dest[i].vport_num = attr->out_rep->vport; + i++; + } + if (action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { counter = mlx5_fc_create(esw->dev, true); if (IS_ERR(counter)) return ERR_CAST(counter); - dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; - dest.counter = counter; + dest[i].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest[i].counter = counter; + i++; } misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters); @@ -81,8 +84,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, MLX5_MATCH_MISC_PARAMETERS; rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, - spec, action, 0, &dest, 1); - + spec, action, 0, dest, i); if (IS_ERR(rule)) mlx5_fc_destroy(esw->dev, counter); -- cgit v1.1 From 5b0907407e7f28249ddb44ca0e629dda3d300169 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:46 +0200 Subject: mlxsw: core: Zero payload buffers for couple of registers We recently discovered a bug in the firmware in which a field's length in one of the registers was incorrectly set. This caused the firmware to access garbage data that wasn't initialized by the driver and therefore emit error messages. While the bug is already fixed and the driver usually zeros the buffers passed to the firmware, there are a handful of cases where this isn't done. Zero the buffer in these cases and prevent similar bugs from recurring, as they tend to be hard to debug. Fixes: 52581961d83d ("mlxsw: core: Implement fan control using hwmon") Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 4 ++-- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index 1ac8bf1..ab710e3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -262,7 +262,7 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon, static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) { - char mtcap_pl[MLXSW_REG_MTCAP_LEN]; + char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0}; char mtmp_pl[MLXSW_REG_MTMP_LEN]; u8 sensor_count; int i; @@ -295,7 +295,7 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon) { - char mfcr_pl[MLXSW_REG_MFCR_LEN]; + char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0}; enum mlxsw_reg_mfcr_pwm_frequency freq; unsigned int type_index; unsigned int num; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index d652f7f..2573faa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -158,7 +158,7 @@ static void mlxsw_sp_txhdr_construct(struct sk_buff *skb, static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp) { - char spad_pl[MLXSW_REG_SPAD_LEN]; + char spad_pl[MLXSW_REG_SPAD_LEN] = {0}; int err; err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(spad), spad_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 8c8f5d8..8d11e5447 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -890,7 +890,7 @@ static const struct switchdev_ops mlxsw_sx_port_switchdev_ops = { static int mlxsw_sx_hw_id_get(struct mlxsw_sx *mlxsw_sx) { - char spad_pl[MLXSW_REG_SPAD_LEN]; + char spad_pl[MLXSW_REG_SPAD_LEN] = {0}; int err; err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(spad), spad_pl); -- cgit v1.1 From abc1de256c1987fe42eff80d95946ee9452bcac7 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 28 Oct 2016 21:35:47 +0200 Subject: mlxsw: switchx2: Check if port is usable before calling port create Do it in a same way we do it in spectrum. Check if port is usable first and only in that case create a port instance. Signed-off-by: Jiri Pirko Reviewed-by: Elad Raz Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 32 +++++++++----------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 8d11e5447..67f1b70 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -256,18 +256,17 @@ mlxsw_sx_port_system_port_mapping_set(struct mlxsw_sx_port *mlxsw_sx_port) return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sspr), sspr_pl); } -static int mlxsw_sx_port_module_check(struct mlxsw_sx_port *mlxsw_sx_port, - bool *p_usable) +static int mlxsw_sx_port_module_info_get(struct mlxsw_sx *mlxsw_sx, + u8 local_port, u8 *p_width) { - struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; char pmlp_pl[MLXSW_REG_PMLP_LEN]; int err; - mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sx_port->local_port); + mlxsw_reg_pmlp_pack(pmlp_pl, local_port); err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmlp), pmlp_pl); if (err) return err; - *p_usable = mlxsw_reg_pmlp_width_get(pmlp_pl) ? true : false; + *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl); return 0; } @@ -962,7 +961,6 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port) { struct mlxsw_sx_port *mlxsw_sx_port; struct net_device *dev; - bool usable; int err; dev = alloc_etherdev(sizeof(struct mlxsw_sx_port)); @@ -1005,19 +1003,6 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port) */ dev->needed_headroom = MLXSW_TXHDR_LEN; - err = mlxsw_sx_port_module_check(mlxsw_sx_port, &usable); - if (err) { - dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to check module\n", - mlxsw_sx_port->local_port); - goto err_port_module_check; - } - - if (!usable) { - dev_dbg(mlxsw_sx->bus_info->dev, "Port %d: Not usable, skipping initialization\n", - mlxsw_sx_port->local_port); - goto port_not_usable; - } - err = mlxsw_sx_port_system_port_mapping_set(mlxsw_sx_port); if (err) { dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set system port mapping\n", @@ -1097,8 +1082,6 @@ err_port_speed_set: mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT); err_port_swid_set: err_port_system_port_mapping_set: -port_not_usable: -err_port_module_check: err_dev_addr_get: free_percpu(mlxsw_sx_port->pcpu_stats); err_alloc_stats: @@ -1131,6 +1114,7 @@ static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) { size_t alloc_size; + u8 width; int i; int err; @@ -1140,6 +1124,11 @@ static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) return -ENOMEM; for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) { + err = mlxsw_sx_port_module_info_get(mlxsw_sx, i, &width); + if (err) + goto err_port_module_info_get; + if (!width) + continue; err = mlxsw_sx_port_create(mlxsw_sx, i); if (err) goto err_port_create; @@ -1147,6 +1136,7 @@ static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) return 0; err_port_create: +err_port_module_info_get: for (i--; i >= 1; i--) mlxsw_sx_port_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); -- cgit v1.1 From adc4e04a4bbd7696c9aa99eec951ddaf26ac27e1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 28 Oct 2016 21:35:48 +0200 Subject: mlxsw: switchx2: Move port used check outside port remove function Be symmentrical with create and do the check outside the remove function. Signed-off-by: Jiri Pirko Signed-off-by: Elad Raz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 67f1b70..6882e35 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1093,21 +1093,26 @@ static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) { struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port]; - if (!mlxsw_sx_port) - return; mlxsw_core_port_fini(&mlxsw_sx_port->core_port); unregister_netdev(mlxsw_sx_port->dev); /* This calls ndo_stop */ + mlxsw_sx->ports[local_port] = NULL; mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT); free_percpu(mlxsw_sx_port->pcpu_stats); free_netdev(mlxsw_sx_port->dev); } +static bool mlxsw_sx_port_created(struct mlxsw_sx *mlxsw_sx, u8 local_port) +{ + return mlxsw_sx->ports[local_port] != NULL; +} + static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) { int i; for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) - mlxsw_sx_port_remove(mlxsw_sx, i); + if (mlxsw_sx_port_created(mlxsw_sx, i)) + mlxsw_sx_port_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); } @@ -1138,7 +1143,8 @@ static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) err_port_create: err_port_module_info_get: for (i--; i >= 1; i--) - mlxsw_sx_port_remove(mlxsw_sx, i); + if (mlxsw_sx_port_created(mlxsw_sx, i)) + mlxsw_sx_port_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); return err; } -- cgit v1.1 From f83e21027b4d8871d896f6e180171c289b60718c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 28 Oct 2016 21:35:49 +0200 Subject: mlxsw: spectrum: Move port used check outside port remove function Be symmentrical with create and do the check outside the remove function. Signed-off-by: Jiri Pirko Signed-off-by: Elad Raz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 2573faa..d649064 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2403,8 +2403,6 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) { struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; - if (!mlxsw_sp_port) - return; cancel_delayed_work_sync(&mlxsw_sp_port->hw_stats.update_dw); mlxsw_core_port_fini(&mlxsw_sp_port->core_port); unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ @@ -2422,12 +2420,18 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) free_netdev(mlxsw_sp_port->dev); } +static bool mlxsw_sp_port_created(struct mlxsw_sp *mlxsw_sp, u8 local_port) +{ + return mlxsw_sp->ports[local_port] != NULL; +} + static void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp) { int i; for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) - mlxsw_sp_port_remove(mlxsw_sp, i); + if (mlxsw_sp_port_created(mlxsw_sp, i)) + mlxsw_sp_port_remove(mlxsw_sp, i); kfree(mlxsw_sp->ports); } @@ -2461,7 +2465,8 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) err_port_create: err_port_module_info_get: for (i--; i >= 1; i--) - mlxsw_sp_port_remove(mlxsw_sp, i); + if (mlxsw_sp_port_created(mlxsw_sp, i)) + mlxsw_sp_port_remove(mlxsw_sp, i); kfree(mlxsw_sp->ports); return err; } @@ -2503,7 +2508,8 @@ static int mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port, err_port_create: for (i--; i >= 0; i--) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i); + if (mlxsw_sp_port_created(mlxsw_sp, base_port + i)) + mlxsw_sp_port_remove(mlxsw_sp, base_port + i); i = count; err_port_swid_set: for (i--; i >= 0; i--) @@ -2593,7 +2599,8 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port, } for (i = 0; i < count; i++) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i); + if (mlxsw_sp_port_created(mlxsw_sp, base_port + i)) + mlxsw_sp_port_remove(mlxsw_sp, base_port + i); err = mlxsw_sp_port_split_create(mlxsw_sp, base_port, module, count); if (err) { @@ -2638,7 +2645,8 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port) base_port = base_port + 2; for (i = 0; i < count; i++) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i); + if (mlxsw_sp_port_created(mlxsw_sp, base_port + i)) + mlxsw_sp_port_remove(mlxsw_sp, base_port + i); mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count); -- cgit v1.1 From e50e789ce178b6ae7c18b3e90884123398bab5f3 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:50 +0200 Subject: mlxsw: switchx2: Add support for physical port names Export to userspace the front panel name of the port, so that udev can rename the ports accordingly. The convention suggested by switchdev documentation is used: pX Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 32 ++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 6882e35..941393b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -81,6 +81,9 @@ struct mlxsw_sx_port { struct mlxsw_sx_port_pcpu_stats __percpu *pcpu_stats; struct mlxsw_sx *mlxsw_sx; u8 local_port; + struct { + u8 module; + } mapping; }; /* tx_hdr_version @@ -257,7 +260,8 @@ mlxsw_sx_port_system_port_mapping_set(struct mlxsw_sx_port *mlxsw_sx_port) } static int mlxsw_sx_port_module_info_get(struct mlxsw_sx *mlxsw_sx, - u8 local_port, u8 *p_width) + u8 local_port, u8 *p_module, + u8 *p_width) { char pmlp_pl[MLXSW_REG_PMLP_LEN]; int err; @@ -266,6 +270,7 @@ static int mlxsw_sx_port_module_info_get(struct mlxsw_sx *mlxsw_sx, err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmlp), pmlp_pl); if (err) return err; + *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0); *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl); return 0; } @@ -383,12 +388,26 @@ mlxsw_sx_port_get_stats64(struct net_device *dev, return stats; } +static int mlxsw_sx_port_get_phys_port_name(struct net_device *dev, char *name, + size_t len) +{ + struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev); + int err; + + err = snprintf(name, len, "p%d", mlxsw_sx_port->mapping.module + 1); + if (err >= len) + return -EINVAL; + + return 0; +} + static const struct net_device_ops mlxsw_sx_port_netdev_ops = { .ndo_open = mlxsw_sx_port_open, .ndo_stop = mlxsw_sx_port_stop, .ndo_start_xmit = mlxsw_sx_port_xmit, .ndo_change_mtu = mlxsw_sx_port_change_mtu, .ndo_get_stats64 = mlxsw_sx_port_get_stats64, + .ndo_get_phys_port_name = mlxsw_sx_port_get_phys_port_name, }; static void mlxsw_sx_port_get_drvinfo(struct net_device *dev, @@ -957,7 +976,8 @@ mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port *mlxsw_sx_port, return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spmlr), spmlr_pl); } -static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port) +static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, + u8 module) { struct mlxsw_sx_port *mlxsw_sx_port; struct net_device *dev; @@ -971,6 +991,7 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port) mlxsw_sx_port->dev = dev; mlxsw_sx_port->mlxsw_sx = mlxsw_sx; mlxsw_sx_port->local_port = local_port; + mlxsw_sx_port->mapping.module = module; mlxsw_sx_port->pcpu_stats = netdev_alloc_pcpu_stats(struct mlxsw_sx_port_pcpu_stats); @@ -1119,7 +1140,7 @@ static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) { size_t alloc_size; - u8 width; + u8 module, width; int i; int err; @@ -1129,12 +1150,13 @@ static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) return -ENOMEM; for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) { - err = mlxsw_sx_port_module_info_get(mlxsw_sx, i, &width); + err = mlxsw_sx_port_module_info_get(mlxsw_sx, i, &module, + &width); if (err) goto err_port_module_info_get; if (!width) continue; - err = mlxsw_sx_port_create(mlxsw_sx, i); + err = mlxsw_sx_port_create(mlxsw_sx, i, module); if (err) goto err_port_create; } -- cgit v1.1 From 10dbf8f8ec587ca0d23464af30a524cef216b1fa Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:51 +0200 Subject: mlxsw: switchx2: Fix port speed configuration In SwitchX-2 we configure the port speed to negotiate with 40G link only. Add support for all other supported speeds. Fixes: 31557f0f9755 ("mlxsw: Introduce Mellanox SwitchX-2 ASIC support") Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 30 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 941393b..44dae8d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -662,6 +662,7 @@ static const struct mlxsw_sx_port_link_mode mlxsw_sx_port_link_mode[] = { }; #define MLXSW_SX_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sx_port_link_mode) +#define MLXSW_SX_PORT_BASE_SPEED 10000 /* Mb/s */ static u32 mlxsw_sx_from_ptys_supported_port(u32 ptys_eth_proto) { @@ -809,6 +810,18 @@ static u32 mlxsw_sx_to_ptys_speed(u32 speed) return ptys_proto; } +static u32 mlxsw_sx_to_ptys_upper_speed(u32 upper_speed) +{ + u32 ptys_proto = 0; + int i; + + for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) { + if (mlxsw_sx_port_link_mode[i].speed <= upper_speed) + ptys_proto |= mlxsw_sx_port_link_mode[i].mask; + } + return ptys_proto; +} + static int mlxsw_sx_port_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { @@ -955,13 +968,17 @@ static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port, return err; } -static int mlxsw_sx_port_speed_set(struct mlxsw_sx_port *mlxsw_sx_port, - u32 speed) +static int +mlxsw_sx_port_speed_by_width_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 width) { struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; + u32 upper_speed = MLXSW_SX_PORT_BASE_SPEED * width; char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 eth_proto_admin; - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, speed); + eth_proto_admin = mlxsw_sx_to_ptys_upper_speed(upper_speed); + mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, + eth_proto_admin); return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl); } @@ -977,7 +994,7 @@ mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port *mlxsw_sx_port, } static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, - u8 module) + u8 module, u8 width) { struct mlxsw_sx_port *mlxsw_sx_port; struct net_device *dev; @@ -1038,8 +1055,7 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, goto err_port_swid_set; } - err = mlxsw_sx_port_speed_set(mlxsw_sx_port, - MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4); + err = mlxsw_sx_port_speed_by_width_set(mlxsw_sx_port, width); if (err) { dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set speed\n", mlxsw_sx_port->local_port); @@ -1156,7 +1172,7 @@ static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) goto err_port_module_info_get; if (!width) continue; - err = mlxsw_sx_port_create(mlxsw_sx, i, module); + err = mlxsw_sx_port_create(mlxsw_sx, i, module, width); if (err) goto err_port_create; } -- cgit v1.1 From 401c8b4e3cc537b31afee3e130ae37a9c49b47c2 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:52 +0200 Subject: mlxsw: reg: Add eth prefix to PTYS pack and unpack We want to add Infiniband support to PTYS. In order to maintain proper conventions, we will change pack and unpack prefix to eth. Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 11 ++++++----- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 17 +++++++++-------- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index debcf26..3ecf348 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2131,8 +2131,8 @@ MLXSW_ITEM32(reg, ptys, eth_proto_oper, 0x24, 0, 32); */ MLXSW_ITEM32(reg, ptys, eth_proto_lp_advertise, 0x30, 0, 32); -static inline void mlxsw_reg_ptys_pack(char *payload, u8 local_port, - u32 proto_admin) +static inline void mlxsw_reg_ptys_eth_pack(char *payload, u8 local_port, + u32 proto_admin) { MLXSW_REG_ZERO(ptys, payload); mlxsw_reg_ptys_local_port_set(payload, local_port); @@ -2140,9 +2140,10 @@ static inline void mlxsw_reg_ptys_pack(char *payload, u8 local_port, mlxsw_reg_ptys_eth_proto_admin_set(payload, proto_admin); } -static inline void mlxsw_reg_ptys_unpack(char *payload, u32 *p_eth_proto_cap, - u32 *p_eth_proto_adm, - u32 *p_eth_proto_oper) +static inline void mlxsw_reg_ptys_eth_unpack(char *payload, + u32 *p_eth_proto_cap, + u32 *p_eth_proto_adm, + u32 *p_eth_proto_oper) { if (p_eth_proto_cap) *p_eth_proto_cap = mlxsw_reg_ptys_eth_proto_cap_get(payload); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index d649064..d1c2f9f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2003,12 +2003,12 @@ static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev, int err; autoneg = mlxsw_sp_port->link.autoneg; - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, 0); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); if (err) return err; - mlxsw_reg_ptys_unpack(ptys_pl, ð_proto_cap, ð_proto_admin, - ð_proto_oper); + mlxsw_reg_ptys_eth_unpack(ptys_pl, ð_proto_cap, ð_proto_admin, + ð_proto_oper); mlxsw_sp_port_get_link_supported(eth_proto_cap, cmd); @@ -2037,11 +2037,11 @@ mlxsw_sp_port_set_link_ksettings(struct net_device *dev, bool autoneg; int err; - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, 0); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); if (err) return err; - mlxsw_reg_ptys_unpack(ptys_pl, ð_proto_cap, NULL, NULL); + mlxsw_reg_ptys_eth_unpack(ptys_pl, ð_proto_cap, NULL, NULL); autoneg = cmd->base.autoneg == AUTONEG_ENABLE; eth_proto_new = autoneg ? @@ -2054,7 +2054,8 @@ mlxsw_sp_port_set_link_ksettings(struct net_device *dev, return -EINVAL; } - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, eth_proto_new); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, + eth_proto_new); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); if (err) return err; @@ -2092,8 +2093,8 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width) u32 eth_proto_admin; eth_proto_admin = mlxsw_sp_to_ptys_upper_speed(upper_speed); - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, - eth_proto_admin); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, + eth_proto_admin); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 44dae8d..7b74dcb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -762,14 +762,14 @@ static int mlxsw_sx_port_get_settings(struct net_device *dev, u32 eth_proto_oper; int err; - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, 0); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0); err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl); if (err) { netdev_err(dev, "Failed to get proto"); return err; } - mlxsw_reg_ptys_unpack(ptys_pl, ð_proto_cap, - ð_proto_admin, ð_proto_oper); + mlxsw_reg_ptys_eth_unpack(ptys_pl, ð_proto_cap, + ð_proto_admin, ð_proto_oper); cmd->supported = mlxsw_sx_from_ptys_supported_port(eth_proto_cap) | mlxsw_sx_from_ptys_supported_link(eth_proto_cap) | @@ -841,13 +841,14 @@ static int mlxsw_sx_port_set_settings(struct net_device *dev, mlxsw_sx_to_ptys_advert_link(cmd->advertising) : mlxsw_sx_to_ptys_speed(speed); - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, 0); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0); err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl); if (err) { netdev_err(dev, "Failed to get proto"); return err; } - mlxsw_reg_ptys_unpack(ptys_pl, ð_proto_cap, ð_proto_admin, NULL); + mlxsw_reg_ptys_eth_unpack(ptys_pl, ð_proto_cap, ð_proto_admin, + NULL); eth_proto_new = eth_proto_new & eth_proto_cap; if (!eth_proto_new) { @@ -857,7 +858,8 @@ static int mlxsw_sx_port_set_settings(struct net_device *dev, if (eth_proto_new == eth_proto_admin) return 0; - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, eth_proto_new); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, + eth_proto_new); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl); if (err) { netdev_err(dev, "Failed to set proto admin"); @@ -977,8 +979,8 @@ mlxsw_sx_port_speed_by_width_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 width) u32 eth_proto_admin; eth_proto_admin = mlxsw_sx_to_ptys_upper_speed(upper_speed); - mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, - eth_proto_admin); + mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, + eth_proto_admin); return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl); } -- cgit v1.1 From 794177027b200a2b9b29aa153fe2ae8bd5610c21 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:53 +0200 Subject: mlxsw: reg: Add Infiniband support to PTYS In order to support Infiniband fabric, we need to introduce IB speeds and capabilities to PTYS emads. Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 3ecf348..2f31af0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2054,6 +2054,7 @@ MLXSW_REG_DEFINE(ptys, MLXSW_REG_PTYS_ID, MLXSW_REG_PTYS_LEN); */ MLXSW_ITEM32(reg, ptys, local_port, 0x00, 16, 8); +#define MLXSW_REG_PTYS_PROTO_MASK_IB BIT(0) #define MLXSW_REG_PTYS_PROTO_MASK_ETH BIT(2) /* reg_ptys_proto_mask @@ -2112,18 +2113,61 @@ MLXSW_ITEM32(reg, ptys, an_status, 0x04, 28, 4); */ MLXSW_ITEM32(reg, ptys, eth_proto_cap, 0x0C, 0, 32); +/* reg_ptys_ib_link_width_cap + * IB port supported widths. + * Access: RO + */ +MLXSW_ITEM32(reg, ptys, ib_link_width_cap, 0x10, 16, 16); + +#define MLXSW_REG_PTYS_IB_SPEED_SDR BIT(0) +#define MLXSW_REG_PTYS_IB_SPEED_DDR BIT(1) +#define MLXSW_REG_PTYS_IB_SPEED_QDR BIT(2) +#define MLXSW_REG_PTYS_IB_SPEED_FDR10 BIT(3) +#define MLXSW_REG_PTYS_IB_SPEED_FDR BIT(4) +#define MLXSW_REG_PTYS_IB_SPEED_EDR BIT(5) + +/* reg_ptys_ib_proto_cap + * IB port supported speeds and protocols. + * Access: RO + */ +MLXSW_ITEM32(reg, ptys, ib_proto_cap, 0x10, 0, 16); + /* reg_ptys_eth_proto_admin * Speed and protocol to set port to. * Access: RW */ MLXSW_ITEM32(reg, ptys, eth_proto_admin, 0x18, 0, 32); +/* reg_ptys_ib_link_width_admin + * IB width to set port to. + * Access: RW + */ +MLXSW_ITEM32(reg, ptys, ib_link_width_admin, 0x1C, 16, 16); + +/* reg_ptys_ib_proto_admin + * IB speeds and protocols to set port to. + * Access: RW + */ +MLXSW_ITEM32(reg, ptys, ib_proto_admin, 0x1C, 0, 16); + /* reg_ptys_eth_proto_oper * The current speed and protocol configured for the port. * Access: RO */ MLXSW_ITEM32(reg, ptys, eth_proto_oper, 0x24, 0, 32); +/* reg_ptys_ib_link_width_oper + * The current IB width to set port to. + * Access: RO + */ +MLXSW_ITEM32(reg, ptys, ib_link_width_oper, 0x28, 16, 16); + +/* reg_ptys_ib_proto_oper + * The current IB speed and protocol. + * Access: RO + */ +MLXSW_ITEM32(reg, ptys, ib_proto_oper, 0x28, 0, 16); + /* reg_ptys_eth_proto_lp_advertise * The protocols that were advertised by the link partner during * autonegotiation. @@ -2153,6 +2197,33 @@ static inline void mlxsw_reg_ptys_eth_unpack(char *payload, *p_eth_proto_oper = mlxsw_reg_ptys_eth_proto_oper_get(payload); } +static inline void mlxsw_reg_ptys_ib_pack(char *payload, u8 local_port, + u16 proto_admin, u16 link_width) +{ + MLXSW_REG_ZERO(ptys, payload); + mlxsw_reg_ptys_local_port_set(payload, local_port); + mlxsw_reg_ptys_proto_mask_set(payload, MLXSW_REG_PTYS_PROTO_MASK_IB); + mlxsw_reg_ptys_ib_proto_admin_set(payload, proto_admin); + mlxsw_reg_ptys_ib_link_width_admin_set(payload, link_width); +} + +static inline void mlxsw_reg_ptys_ib_unpack(char *payload, u16 *p_ib_proto_cap, + u16 *p_ib_link_width_cap, + u16 *p_ib_proto_oper, + u16 *p_ib_link_width_oper) +{ + if (p_ib_proto_cap) + *p_ib_proto_cap = mlxsw_reg_ptys_ib_proto_cap_get(payload); + if (p_ib_link_width_cap) + *p_ib_link_width_cap = + mlxsw_reg_ptys_ib_link_width_cap_get(payload); + if (p_ib_proto_oper) + *p_ib_proto_oper = mlxsw_reg_ptys_ib_proto_oper_get(payload); + if (p_ib_link_width_oper) + *p_ib_link_width_oper = + mlxsw_reg_ptys_ib_link_width_oper_get(payload); +} + /* PPAD - Port Physical Address Register * ------------------------------------- * The PPAD register configures the per port physical MAC address. -- cgit v1.1 From 7136793e4a44651b273a457e31f00a51751dd123 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:54 +0200 Subject: mlxsw: reg: Add local-port to Infiniband port mapping In order to change a port type to Infiniband port we should change his mapping from local-port to Infiniband. Adding the PLIB (Port Local to InfiniBand) allows this mapping. Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 2f31af0..a61ce34 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2748,6 +2748,27 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port, mlxsw_reg_ppcnt_prio_tc_set(payload, prio_tc); } +/* PLIB - Port Local to InfiniBand Port + * ------------------------------------ + * The PLIB register performs mapping from Local Port into InfiniBand Port. + */ +#define MLXSW_REG_PLIB_ID 0x500A +#define MLXSW_REG_PLIB_LEN 0x10 + +MLXSW_REG_DEFINE(plib, MLXSW_REG_PLIB_ID, MLXSW_REG_PLIB_LEN); + +/* reg_plib_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, plib, local_port, 0x00, 16, 8); + +/* reg_plib_ib_port + * InfiniBand port remapping for local_port. + * Access: RW + */ +MLXSW_ITEM32(reg, plib, ib_port, 0x00, 0, 8); + /* PPTB - Port Prio To Buffer Register * ----------------------------------- * Configures the switch priority to buffer table. @@ -5188,6 +5209,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(paos), MLXSW_REG(pfcc), MLXSW_REG(ppcnt), + MLXSW_REG(plib), MLXSW_REG(pptb), MLXSW_REG(pbmc), MLXSW_REG(pspa), -- cgit v1.1 From 67963a33b4fde8f77b046f80daf83afc6e126991 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 28 Oct 2016 21:35:55 +0200 Subject: mlxsw: Make devlink port instances independent of spectrum/switchx2 port instances Currently, devlink register/unregister is done directly from spectrum/switchx2 port create/remove functions. With a need to introduce a port type change, the devlink port instances have to be persistent across type changes, therefore across port create/remove function calls. So do a bit of reshuffling to achieve that. Signed-off-by: Jiri Pirko Signed-off-by: Elad Raz Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 82 +++++++++++++++++++++----- drivers/net/ethernet/mellanox/mlxsw/core.h | 26 +++----- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 53 +++++++++++------ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 - drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 49 ++++++++++----- 5 files changed, 147 insertions(+), 64 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 02183f6..b50acb1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -90,6 +90,22 @@ struct mlxsw_core_pcpu_stats { u32 port_rx_invalid; }; +struct mlxsw_core_port { + struct devlink_port devlink_port; + void *port_driver_priv; +}; + +void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port) +{ + return mlxsw_core_port->port_driver_priv; +} +EXPORT_SYMBOL(mlxsw_core_port_driver_priv); + +static bool mlxsw_core_port_check(struct mlxsw_core_port *mlxsw_core_port) +{ + return mlxsw_core_port->port_driver_priv != NULL; +} + struct mlxsw_core { struct mlxsw_driver *driver; const struct mlxsw_bus *bus; @@ -114,6 +130,7 @@ struct mlxsw_core { } lag; struct mlxsw_res res; struct mlxsw_hwmon *hwmon; + struct mlxsw_core_port ports[MLXSW_PORT_MAX_PORTS]; unsigned long driver_priv[0]; /* driver_priv has to be always the last item */ }; @@ -928,7 +945,8 @@ static int mlxsw_devlink_sb_port_pool_get(struct devlink_port *devlink_port, struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - if (!mlxsw_driver->sb_port_pool_get) + if (!mlxsw_driver->sb_port_pool_get || + !mlxsw_core_port_check(mlxsw_core_port)) return -EOPNOTSUPP; return mlxsw_driver->sb_port_pool_get(mlxsw_core_port, sb_index, pool_index, p_threshold); @@ -942,7 +960,8 @@ static int mlxsw_devlink_sb_port_pool_set(struct devlink_port *devlink_port, struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - if (!mlxsw_driver->sb_port_pool_set) + if (!mlxsw_driver->sb_port_pool_set || + !mlxsw_core_port_check(mlxsw_core_port)) return -EOPNOTSUPP; return mlxsw_driver->sb_port_pool_set(mlxsw_core_port, sb_index, pool_index, threshold); @@ -958,7 +977,8 @@ mlxsw_devlink_sb_tc_pool_bind_get(struct devlink_port *devlink_port, struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - if (!mlxsw_driver->sb_tc_pool_bind_get) + if (!mlxsw_driver->sb_tc_pool_bind_get || + !mlxsw_core_port_check(mlxsw_core_port)) return -EOPNOTSUPP; return mlxsw_driver->sb_tc_pool_bind_get(mlxsw_core_port, sb_index, tc_index, pool_type, @@ -975,7 +995,8 @@ mlxsw_devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port, struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - if (!mlxsw_driver->sb_tc_pool_bind_set) + if (!mlxsw_driver->sb_tc_pool_bind_set || + !mlxsw_core_port_check(mlxsw_core_port)) return -EOPNOTSUPP; return mlxsw_driver->sb_tc_pool_bind_set(mlxsw_core_port, sb_index, tc_index, pool_type, @@ -1013,7 +1034,8 @@ mlxsw_devlink_sb_occ_port_pool_get(struct devlink_port *devlink_port, struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - if (!mlxsw_driver->sb_occ_port_pool_get) + if (!mlxsw_driver->sb_occ_port_pool_get || + !mlxsw_core_port_check(mlxsw_core_port)) return -EOPNOTSUPP; return mlxsw_driver->sb_occ_port_pool_get(mlxsw_core_port, sb_index, pool_index, p_cur, p_max); @@ -1029,7 +1051,8 @@ mlxsw_devlink_sb_occ_tc_port_bind_get(struct devlink_port *devlink_port, struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - if (!mlxsw_driver->sb_occ_tc_port_bind_get) + if (!mlxsw_driver->sb_occ_tc_port_bind_get || + !mlxsw_core_port_check(mlxsw_core_port)) return -EOPNOTSUPP; return mlxsw_driver->sb_occ_tc_port_bind_get(mlxsw_core_port, sb_index, tc_index, @@ -1656,28 +1679,59 @@ u64 mlxsw_core_res_get(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_res_get); -int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, - struct mlxsw_core_port *mlxsw_core_port, u8 local_port, - struct net_device *dev, bool split, u32 split_group) +int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port) { struct devlink *devlink = priv_to_devlink(mlxsw_core); + struct mlxsw_core_port *mlxsw_core_port = + &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + int err; - if (split) - devlink_port_split_set(devlink_port, split_group); - devlink_port_type_eth_set(devlink_port, dev); - return devlink_port_register(devlink, devlink_port, local_port); + err = devlink_port_register(devlink, devlink_port, local_port); + if (err) + memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port)); + return err; } EXPORT_SYMBOL(mlxsw_core_port_init); -void mlxsw_core_port_fini(struct mlxsw_core_port *mlxsw_core_port) +void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port) { + struct mlxsw_core_port *mlxsw_core_port = + &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; devlink_port_unregister(devlink_port); + memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port)); } EXPORT_SYMBOL(mlxsw_core_port_fini); +void mlxsw_core_port_set(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv, struct net_device *dev, + bool split, u32 split_group) +{ + struct mlxsw_core_port *mlxsw_core_port = + &mlxsw_core->ports[local_port]; + struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + + mlxsw_core_port->port_driver_priv = port_driver_priv; + if (split) + devlink_port_split_set(devlink_port, split_group); + devlink_port_type_eth_set(devlink_port, dev); +} +EXPORT_SYMBOL(mlxsw_core_port_set); + +void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv) +{ + struct mlxsw_core_port *mlxsw_core_port = + &mlxsw_core->ports[local_port]; + struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + + mlxsw_core_port->port_driver_priv = port_driver_priv; + devlink_port_type_clear(devlink_port); +} +EXPORT_SYMBOL(mlxsw_core_port_clear); + static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, const char *buf, size_t size) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 0cf721c..f4bbbd4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -52,6 +52,7 @@ #include "resources.h" struct mlxsw_core; +struct mlxsw_core_port; struct mlxsw_driver; struct mlxsw_bus; struct mlxsw_bus_info; @@ -141,23 +142,14 @@ u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core, void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core, u16 lag_id, u8 local_port); -struct mlxsw_core_port { - struct devlink_port devlink_port; -}; - -static inline void * -mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port) -{ - /* mlxsw_core_port is ensured to always be the first field in driver - * port structure. - */ - return mlxsw_core_port; -} - -int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, - struct mlxsw_core_port *mlxsw_core_port, u8 local_port, - struct net_device *dev, bool split, u32 split_group); -void mlxsw_core_port_fini(struct mlxsw_core_port *mlxsw_core_port); +void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port); +int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port); +void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port); +void mlxsw_core_port_set(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv, struct net_device *dev, + bool split, u32 split_group); +void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv); int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index d1c2f9f..ad4ff27 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2212,8 +2212,8 @@ static int mlxsw_sp_port_pvid_vport_destroy(struct mlxsw_sp_port *mlxsw_sp_port) return mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1); } -static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, - bool split, u8 module, u8 width, u8 lane) +static int __mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, + bool split, u8 module, u8 width, u8 lane) { struct mlxsw_sp_port *mlxsw_sp_port; struct net_device *dev; @@ -2358,20 +2358,11 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, goto err_register_netdev; } - err = mlxsw_core_port_init(mlxsw_sp->core, &mlxsw_sp_port->core_port, - mlxsw_sp_port->local_port, dev, - mlxsw_sp_port->split, module); - if (err) { - dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n", - mlxsw_sp_port->local_port); - goto err_core_port_init; - } - + mlxsw_core_port_set(mlxsw_sp->core, mlxsw_sp_port->local_port, + mlxsw_sp_port, dev, mlxsw_sp_port->split, module); mlxsw_core_schedule_dw(&mlxsw_sp_port->hw_stats.update_dw, 0); return 0; -err_core_port_init: - unregister_netdev(dev); err_register_netdev: mlxsw_sp->ports[local_port] = NULL; mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); @@ -2400,12 +2391,34 @@ err_port_active_vlans_alloc: return err; } -static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) +static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, + bool split, u8 module, u8 width, u8 lane) +{ + int err; + + err = mlxsw_core_port_init(mlxsw_sp->core, local_port); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n", + local_port); + return err; + } + err = __mlxsw_sp_port_create(mlxsw_sp, local_port, false, + module, width, lane); + if (err) + goto err_port_create; + return 0; + +err_port_create: + mlxsw_core_port_fini(mlxsw_sp->core, local_port); + return err; +} + +static void __mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) { struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; cancel_delayed_work_sync(&mlxsw_sp_port->hw_stats.update_dw); - mlxsw_core_port_fini(&mlxsw_sp_port->core_port); + mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp); unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ mlxsw_sp->ports[local_port] = NULL; mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); @@ -2421,6 +2434,12 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) free_netdev(mlxsw_sp_port->dev); } +static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) +{ + __mlxsw_sp_port_remove(mlxsw_sp, local_port); + mlxsw_core_port_fini(mlxsw_sp->core, local_port); +} + static bool mlxsw_sp_port_created(struct mlxsw_sp *mlxsw_sp, u8 local_port) { return mlxsw_sp->ports[local_port] != NULL; @@ -2456,8 +2475,8 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) if (!width) continue; mlxsw_sp->port_to_module[i] = module; - err = mlxsw_sp_port_create(mlxsw_sp, i, false, module, width, - lane); + err = mlxsw_sp_port_create(mlxsw_sp, i, false, + module, width, lane); if (err) goto err_port_create; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index cc54625..04a2bc7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -316,7 +316,6 @@ struct mlxsw_sp_port_pcpu_stats { }; struct mlxsw_sp_port { - struct mlxsw_core_port core_port; /* must be first */ struct net_device *dev; struct mlxsw_sp_port_pcpu_stats __percpu *pcpu_stats; struct mlxsw_sp *mlxsw_sp; diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 7b74dcb..8eac26f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -76,7 +76,6 @@ struct mlxsw_sx_port_pcpu_stats { }; struct mlxsw_sx_port { - struct mlxsw_core_port core_port; /* must be first */ struct net_device *dev; struct mlxsw_sx_port_pcpu_stats __percpu *pcpu_stats; struct mlxsw_sx *mlxsw_sx; @@ -995,8 +994,8 @@ mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port *mlxsw_sx_port, return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spmlr), spmlr_pl); } -static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, - u8 module, u8 width) +static int __mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, + u8 module, u8 width) { struct mlxsw_sx_port *mlxsw_sx_port; struct net_device *dev; @@ -1099,19 +1098,11 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, goto err_register_netdev; } - err = mlxsw_core_port_init(mlxsw_sx->core, &mlxsw_sx_port->core_port, - mlxsw_sx_port->local_port, dev, false, 0); - if (err) { - dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to init core port\n", - mlxsw_sx_port->local_port); - goto err_core_port_init; - } - + mlxsw_core_port_set(mlxsw_sx->core, mlxsw_sx_port->local_port, + mlxsw_sx_port, dev, false, 0); mlxsw_sx->ports[local_port] = mlxsw_sx_port; return 0; -err_core_port_init: - unregister_netdev(dev); err_register_netdev: err_port_mac_learning_mode_set: err_port_stp_state_set: @@ -1128,11 +1119,33 @@ err_alloc_stats: return err; } -static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, + u8 module, u8 width) +{ + int err; + + err = mlxsw_core_port_init(mlxsw_sx->core, local_port); + if (err) { + dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to init core port\n", + local_port); + return err; + } + err = __mlxsw_sx_port_create(mlxsw_sx, local_port, module, width); + if (err) + goto err_port_create; + + return 0; + +err_port_create: + mlxsw_core_port_fini(mlxsw_sx->core, local_port); + return err; +} + +static void __mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) { struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port]; - mlxsw_core_port_fini(&mlxsw_sx_port->core_port); + mlxsw_core_port_clear(mlxsw_sx->core, local_port, mlxsw_sx); unregister_netdev(mlxsw_sx_port->dev); /* This calls ndo_stop */ mlxsw_sx->ports[local_port] = NULL; mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT); @@ -1140,6 +1153,12 @@ static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) free_netdev(mlxsw_sx_port->dev); } +static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +{ + __mlxsw_sx_port_remove(mlxsw_sx, local_port); + mlxsw_core_port_fini(mlxsw_sx->core, local_port); +} + static bool mlxsw_sx_port_created(struct mlxsw_sx *mlxsw_sx, u8 local_port) { return mlxsw_sx->ports[local_port] != NULL; -- cgit v1.1 From c68b71cb2fa98e2fa1e177278b349cf2653d8d83 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:56 +0200 Subject: mlxsw: switchx2: Add Infiniband switch partition In order to put a port in Infiniband fabric it should be assigned to separate swid (Switch partition) that initialized as IB swid. Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/port.h | 1 + drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/port.h b/drivers/net/ethernet/mellanox/mlxsw/port.h index af371a8..7296071 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/port.h +++ b/drivers/net/ethernet/mellanox/mlxsw/port.h @@ -44,6 +44,7 @@ #define MLXSW_PORT_SWID_DISABLED_PORT 255 #define MLXSW_PORT_SWID_ALL_SWIDS 254 +#define MLXSW_PORT_SWID_TYPE_IB 1 #define MLXSW_PORT_SWID_TYPE_ETH 2 #define MLXSW_PORT_MID 0xd000 diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 8eac26f..a905589 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1590,13 +1590,17 @@ static struct mlxsw_config_profile mlxsw_sx_config_profile = { .used_flood_mode = 1, .flood_mode = 3, .used_max_ib_mc = 1, - .max_ib_mc = 0, + .max_ib_mc = 6, .used_max_pkey = 1, .max_pkey = 0, .swid_config = { { .used_type = 1, .type = MLXSW_PORT_SWID_TYPE_ETH, + }, + { + .used_type = 1, + .type = MLXSW_PORT_SWID_TYPE_IB, } }, .resource_query_enable = 0, -- cgit v1.1 From d808c7e490350c51a883dec65c4f47e9c5cad79f Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:57 +0200 Subject: mlxsw: core: Add "eth" prefix to mlxsw_core_port_set Since we are about to introduce IB port APIs, we will add prefixes to existing APIs. Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 8 ++++---- drivers/net/ethernet/mellanox/mlxsw/core.h | 6 +++--- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 5 +++-- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index b50acb1..dcd7202 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1705,9 +1705,9 @@ void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port) } EXPORT_SYMBOL(mlxsw_core_port_fini); -void mlxsw_core_port_set(struct mlxsw_core *mlxsw_core, u8 local_port, - void *port_driver_priv, struct net_device *dev, - bool split, u32 split_group) +void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv, struct net_device *dev, + bool split, u32 split_group) { struct mlxsw_core_port *mlxsw_core_port = &mlxsw_core->ports[local_port]; @@ -1718,7 +1718,7 @@ void mlxsw_core_port_set(struct mlxsw_core *mlxsw_core, u8 local_port, devlink_port_split_set(devlink_port, split_group); devlink_port_type_eth_set(devlink_port, dev); } -EXPORT_SYMBOL(mlxsw_core_port_set); +EXPORT_SYMBOL(mlxsw_core_port_eth_set); void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, void *port_driver_priv) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index f4bbbd4..82866d5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -145,9 +145,9 @@ void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core, void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port); int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port); void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port); -void mlxsw_core_port_set(struct mlxsw_core *mlxsw_core, u8 local_port, - void *port_driver_priv, struct net_device *dev, - bool split, u32 split_group); +void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv, struct net_device *dev, + bool split, u32 split_group); void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, void *port_driver_priv); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index ad4ff27..8bca020 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2358,8 +2358,9 @@ static int __mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, goto err_register_netdev; } - mlxsw_core_port_set(mlxsw_sp->core, mlxsw_sp_port->local_port, - mlxsw_sp_port, dev, mlxsw_sp_port->split, module); + mlxsw_core_port_eth_set(mlxsw_sp->core, mlxsw_sp_port->local_port, + mlxsw_sp_port, dev, mlxsw_sp_port->split, + module); mlxsw_core_schedule_dw(&mlxsw_sp_port->hw_stats.update_dw, 0); return 0; diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index a905589..d110e3f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1098,8 +1098,8 @@ static int __mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, goto err_register_netdev; } - mlxsw_core_port_set(mlxsw_sx->core, mlxsw_sx_port->local_port, - mlxsw_sx_port, dev, false, 0); + mlxsw_core_port_eth_set(mlxsw_sx->core, mlxsw_sx_port->local_port, + mlxsw_sx_port, dev, false, 0); mlxsw_sx->ports[local_port] = mlxsw_sx_port; return 0; -- cgit v1.1 From 0c81ea5db25986fb2a704105db454a790c59709c Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:58 +0200 Subject: mlxsw: core: Add port type (Eth/IB) set API Add "port_type_set" API to mlxsw core. The core layer send the change type callback to the port along with it's private information. Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 41 ++++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/core.h | 6 +++++ 2 files changed, 47 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index dcd7202..6004817 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -93,6 +93,7 @@ struct mlxsw_core_pcpu_stats { struct mlxsw_core_port { struct devlink_port devlink_port; void *port_driver_priv; + u8 local_port; }; void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port) @@ -937,6 +938,21 @@ static void *__dl_port(struct devlink_port *devlink_port) return container_of(devlink_port, struct mlxsw_core_port, devlink_port); } +static int mlxsw_devlink_port_type_set(struct devlink_port *devlink_port, + enum devlink_port_type port_type) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); + + if (!mlxsw_driver->port_type_set) + return -EOPNOTSUPP; + + return mlxsw_driver->port_type_set(mlxsw_core, + mlxsw_core_port->local_port, + port_type); +} + static int mlxsw_devlink_sb_port_pool_get(struct devlink_port *devlink_port, unsigned int sb_index, u16 pool_index, u32 *p_threshold) @@ -1060,6 +1076,7 @@ mlxsw_devlink_sb_occ_tc_port_bind_get(struct devlink_port *devlink_port, } static const struct devlink_ops mlxsw_devlink_ops = { + .port_type_set = mlxsw_devlink_port_type_set, .port_split = mlxsw_devlink_port_split, .port_unsplit = mlxsw_devlink_port_unsplit, .sb_pool_get = mlxsw_devlink_sb_pool_get, @@ -1687,6 +1704,7 @@ int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port) struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; int err; + mlxsw_core_port->local_port = local_port; err = devlink_port_register(devlink, devlink_port, local_port); if (err) memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port)); @@ -1720,6 +1738,18 @@ void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port, } EXPORT_SYMBOL(mlxsw_core_port_eth_set); +void mlxsw_core_port_ib_set(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv) +{ + struct mlxsw_core_port *mlxsw_core_port = + &mlxsw_core->ports[local_port]; + struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + + mlxsw_core_port->port_driver_priv = port_driver_priv; + devlink_port_type_ib_set(devlink_port, NULL); +} +EXPORT_SYMBOL(mlxsw_core_port_ib_set); + void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, void *port_driver_priv) { @@ -1732,6 +1762,17 @@ void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, } EXPORT_SYMBOL(mlxsw_core_port_clear); +enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core, + u8 local_port) +{ + struct mlxsw_core_port *mlxsw_core_port = + &mlxsw_core->ports[local_port]; + struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + + return devlink_port->type; +} +EXPORT_SYMBOL(mlxsw_core_port_type_get); + static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, const char *buf, size_t size) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 82866d5..c0acc1b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -148,8 +148,12 @@ void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port); void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port, void *port_driver_priv, struct net_device *dev, bool split, u32 split_group); +void mlxsw_core_port_ib_set(struct mlxsw_core *mlxsw_core, u8 local_port, + void *port_driver_priv); void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, void *port_driver_priv); +enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core, + u8 local_port); int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay); @@ -210,6 +214,8 @@ struct mlxsw_driver { int (*init)(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info); void (*fini)(struct mlxsw_core *mlxsw_core); + int (*port_type_set)(struct mlxsw_core *mlxsw_core, u8 local_port, + enum devlink_port_type new_type); int (*port_split)(struct mlxsw_core *mlxsw_core, u8 local_port, unsigned int count); int (*port_unsplit)(struct mlxsw_core *mlxsw_core, u8 local_port); -- cgit v1.1 From 03ddb787c729fec7b45d5e8640d05351fb0d1b6b Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:35:59 +0200 Subject: mlxsw: switchx2: Add eth prefix to port create and remove Since we are about to add Infiniband port remove and create we will add "eth" prefix to port create and remove APIs. Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index d110e3f..3aa0948 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -994,8 +994,8 @@ mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port *mlxsw_sx_port, return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spmlr), spmlr_pl); } -static int __mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, - u8 module, u8 width) +static int __mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, + u8 module, u8 width) { struct mlxsw_sx_port *mlxsw_sx_port; struct net_device *dev; @@ -1119,8 +1119,8 @@ err_alloc_stats: return err; } -static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, - u8 module, u8 width) +static int mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, + u8 module, u8 width) { int err; @@ -1130,7 +1130,7 @@ static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, local_port); return err; } - err = __mlxsw_sx_port_create(mlxsw_sx, local_port, module, width); + err = __mlxsw_sx_port_eth_create(mlxsw_sx, local_port, module, width); if (err) goto err_port_create; @@ -1141,7 +1141,7 @@ err_port_create: return err; } -static void __mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +static void __mlxsw_sx_port_eth_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) { struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port]; @@ -1153,9 +1153,9 @@ static void __mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) free_netdev(mlxsw_sx_port->dev); } -static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +static void mlxsw_sx_port_eth_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) { - __mlxsw_sx_port_remove(mlxsw_sx, local_port); + __mlxsw_sx_port_eth_remove(mlxsw_sx, local_port); mlxsw_core_port_fini(mlxsw_sx->core, local_port); } @@ -1170,7 +1170,7 @@ static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) if (mlxsw_sx_port_created(mlxsw_sx, i)) - mlxsw_sx_port_remove(mlxsw_sx, i); + mlxsw_sx_port_eth_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); } @@ -1193,7 +1193,7 @@ static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) goto err_port_module_info_get; if (!width) continue; - err = mlxsw_sx_port_create(mlxsw_sx, i, module, width); + err = mlxsw_sx_port_eth_create(mlxsw_sx, i, module, width); if (err) goto err_port_create; } @@ -1203,7 +1203,7 @@ err_port_create: err_port_module_info_get: for (i--; i >= 1; i--) if (mlxsw_sx_port_created(mlxsw_sx, i)) - mlxsw_sx_port_remove(mlxsw_sx, i); + mlxsw_sx_port_eth_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); return err; } -- cgit v1.1 From 64b92b0114122153035a7f95efa4af0a76e02bb5 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:36:00 +0200 Subject: mlxsw: switchx2: Add IB port support SwitchX-2 is IB capable device. This patch add a support to change the port type between Ethernet and Infiniband. When the port is set to IB, the FW implements the Subnet Management Agent (SMA) manage the port. All port attributes can be control remotely by the SM. Usage: $ devlink port show pci/0000:03:00.0/1: type eth netdev eth0 pci/0000:03:00.0/3: type eth netdev eth1 pci/0000:03:00.0/5: type eth netdev eth2 pci/0000:03:00.0/6: type eth netdev eth3 pci/0000:03:00.0/8: type eth netdev eth4 $ devlink port set pci/0000:03:00.0/1 type ib $ devlink port show pci/0000:03:00.0/1: type ib Signed-off-by: Elad Raz Reviewed-by: Jiri Pirko Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/ib.h | 39 +++++ drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 229 +++++++++++++++++++++++-- 2 files changed, 249 insertions(+), 19 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/ib.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/ib.h b/drivers/net/ethernet/mellanox/mlxsw/ib.h new file mode 100644 index 0000000..ce313aa --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/ib.h @@ -0,0 +1,39 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/ib.h + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Elad Raz + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _MLXSW_IB_H +#define _MLXSW_IB_H + +#define MLXSW_IB_DEFAULT_MTU 4096 + +#endif /* _MLXSW_IB_H */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 3aa0948..5208764 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -3,7 +3,7 @@ * Copyright (c) 2015 Mellanox Technologies. All rights reserved. * Copyright (c) 2015 Jiri Pirko * Copyright (c) 2015 Ido Schimmel - * Copyright (c) 2015 Elad Raz + * Copyright (c) 2015-2016 Elad Raz * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -53,6 +53,7 @@ #include "port.h" #include "trap.h" #include "txheader.h" +#include "ib.h" static const char mlxsw_sx_driver_name[] = "mlxsw_switchx2"; static const char mlxsw_sx_driver_version[] = "1.0"; @@ -218,14 +219,14 @@ static int mlxsw_sx_port_oper_status_get(struct mlxsw_sx_port *mlxsw_sx_port, return 0; } -static int mlxsw_sx_port_mtu_set(struct mlxsw_sx_port *mlxsw_sx_port, u16 mtu) +static int __mlxsw_sx_port_mtu_set(struct mlxsw_sx_port *mlxsw_sx_port, + u16 mtu) { struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; char pmtu_pl[MLXSW_REG_PMTU_LEN]; int max_mtu; int err; - mtu += MLXSW_TXHDR_LEN + ETH_HLEN; mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sx_port->local_port, 0); err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl); if (err) @@ -239,6 +240,32 @@ static int mlxsw_sx_port_mtu_set(struct mlxsw_sx_port *mlxsw_sx_port, u16 mtu) return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl); } +static int mlxsw_sx_port_mtu_eth_set(struct mlxsw_sx_port *mlxsw_sx_port, + u16 mtu) +{ + mtu += MLXSW_TXHDR_LEN + ETH_HLEN; + return __mlxsw_sx_port_mtu_set(mlxsw_sx_port, mtu); +} + +static int mlxsw_sx_port_mtu_ib_set(struct mlxsw_sx_port *mlxsw_sx_port, + u16 mtu) +{ + return __mlxsw_sx_port_mtu_set(mlxsw_sx_port, mtu); +} + +static int mlxsw_sx_port_ib_port_set(struct mlxsw_sx_port *mlxsw_sx_port, + u8 ib_port) +{ + struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; + char plib_pl[MLXSW_REG_PLIB_LEN] = {0}; + int err; + + mlxsw_reg_plib_local_port_set(plib_pl, mlxsw_sx_port->local_port); + mlxsw_reg_plib_ib_port_set(plib_pl, ib_port); + err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(plib), plib_pl); + return err; +} + static int mlxsw_sx_port_swid_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 swid) { struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; @@ -348,7 +375,7 @@ static int mlxsw_sx_port_change_mtu(struct net_device *dev, int mtu) struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev); int err; - err = mlxsw_sx_port_mtu_set(mlxsw_sx_port, mtu); + err = mlxsw_sx_port_mtu_eth_set(mlxsw_sx_port, mtu); if (err) return err; dev->mtu = mtu; @@ -969,6 +996,17 @@ static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port, return err; } +static int mlxsw_sx_port_ib_speed_set(struct mlxsw_sx_port *mlxsw_sx_port, + u16 speed, u16 width) +{ + struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; + char ptys_pl[MLXSW_REG_PTYS_LEN]; + + mlxsw_reg_ptys_ib_pack(ptys_pl, mlxsw_sx_port->local_port, speed, + width); + return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl); +} + static int mlxsw_sx_port_speed_by_width_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 width) { @@ -1063,7 +1101,7 @@ static int __mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, goto err_port_speed_set; } - err = mlxsw_sx_port_mtu_set(mlxsw_sx_port, ETH_DATA_LEN); + err = mlxsw_sx_port_mtu_eth_set(mlxsw_sx_port, ETH_DATA_LEN); if (err) { dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MTU\n", mlxsw_sx_port->local_port); @@ -1153,15 +1191,118 @@ static void __mlxsw_sx_port_eth_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) free_netdev(mlxsw_sx_port->dev); } -static void mlxsw_sx_port_eth_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +static bool mlxsw_sx_port_created(struct mlxsw_sx *mlxsw_sx, u8 local_port) { - __mlxsw_sx_port_eth_remove(mlxsw_sx, local_port); - mlxsw_core_port_fini(mlxsw_sx->core, local_port); + return mlxsw_sx->ports[local_port] != NULL; } -static bool mlxsw_sx_port_created(struct mlxsw_sx *mlxsw_sx, u8 local_port) +static int __mlxsw_sx_port_ib_create(struct mlxsw_sx *mlxsw_sx, u8 local_port, + u8 module, u8 width) { - return mlxsw_sx->ports[local_port] != NULL; + struct mlxsw_sx_port *mlxsw_sx_port; + int err; + + mlxsw_sx_port = kzalloc(sizeof(*mlxsw_sx_port), GFP_KERNEL); + if (!mlxsw_sx_port) + return -ENOMEM; + mlxsw_sx_port->mlxsw_sx = mlxsw_sx; + mlxsw_sx_port->local_port = local_port; + mlxsw_sx_port->mapping.module = module; + + err = mlxsw_sx_port_system_port_mapping_set(mlxsw_sx_port); + if (err) { + dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set system port mapping\n", + mlxsw_sx_port->local_port); + goto err_port_system_port_mapping_set; + } + + /* Adding port to Infiniband swid (1) */ + err = mlxsw_sx_port_swid_set(mlxsw_sx_port, 1); + if (err) { + dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set SWID\n", + mlxsw_sx_port->local_port); + goto err_port_swid_set; + } + + /* Expose the IB port number as it's front panel name */ + err = mlxsw_sx_port_ib_port_set(mlxsw_sx_port, module + 1); + if (err) { + dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set IB port\n", + mlxsw_sx_port->local_port); + goto err_port_ib_set; + } + + /* Supports all speeds from SDR to FDR (bitmask) and support bus width + * of 1x, 2x and 4x (3 bits bitmask) + */ + err = mlxsw_sx_port_ib_speed_set(mlxsw_sx_port, + MLXSW_REG_PTYS_IB_SPEED_EDR - 1, + BIT(3) - 1); + if (err) { + dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set speed\n", + mlxsw_sx_port->local_port); + goto err_port_speed_set; + } + + /* Change to the maximum MTU the device supports, the SMA will take + * care of the active MTU + */ + err = mlxsw_sx_port_mtu_ib_set(mlxsw_sx_port, MLXSW_IB_DEFAULT_MTU); + if (err) { + dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MTU\n", + mlxsw_sx_port->local_port); + goto err_port_mtu_set; + } + + err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true); + if (err) { + dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to change admin state to UP\n", + mlxsw_sx_port->local_port); + goto err_port_admin_set; + } + + mlxsw_core_port_ib_set(mlxsw_sx->core, mlxsw_sx_port->local_port, + mlxsw_sx_port); + mlxsw_sx->ports[local_port] = mlxsw_sx_port; + return 0; + +err_port_admin_set: +err_port_mtu_set: +err_port_speed_set: +err_port_ib_set: + mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT); +err_port_swid_set: +err_port_system_port_mapping_set: + kfree(mlxsw_sx_port); + return err; +} + +static void __mlxsw_sx_port_ib_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +{ + struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port]; + + mlxsw_core_port_clear(mlxsw_sx->core, local_port, mlxsw_sx); + mlxsw_sx->ports[local_port] = NULL; + mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false); + mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT); + kfree(mlxsw_sx_port); +} + +static void __mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +{ + enum devlink_port_type port_type = + mlxsw_core_port_type_get(mlxsw_sx->core, local_port); + + if (port_type == DEVLINK_PORT_TYPE_ETH) + __mlxsw_sx_port_eth_remove(mlxsw_sx, local_port); + else if (port_type == DEVLINK_PORT_TYPE_IB) + __mlxsw_sx_port_ib_remove(mlxsw_sx, local_port); +} + +static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port) +{ + __mlxsw_sx_port_remove(mlxsw_sx, local_port); + mlxsw_core_port_fini(mlxsw_sx->core, local_port); } static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) @@ -1170,7 +1311,7 @@ static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) if (mlxsw_sx_port_created(mlxsw_sx, i)) - mlxsw_sx_port_eth_remove(mlxsw_sx, i); + mlxsw_sx_port_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); } @@ -1203,17 +1344,41 @@ err_port_create: err_port_module_info_get: for (i--; i >= 1; i--) if (mlxsw_sx_port_created(mlxsw_sx, i)) - mlxsw_sx_port_eth_remove(mlxsw_sx, i); + mlxsw_sx_port_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); return err; } +static void mlxsw_sx_pude_eth_event_func(struct mlxsw_sx_port *mlxsw_sx_port, + enum mlxsw_reg_pude_oper_status status) +{ + if (status == MLXSW_PORT_OPER_STATUS_UP) { + netdev_info(mlxsw_sx_port->dev, "link up\n"); + netif_carrier_on(mlxsw_sx_port->dev); + } else { + netdev_info(mlxsw_sx_port->dev, "link down\n"); + netif_carrier_off(mlxsw_sx_port->dev); + } +} + +static void mlxsw_sx_pude_ib_event_func(struct mlxsw_sx_port *mlxsw_sx_port, + enum mlxsw_reg_pude_oper_status status) +{ + if (status == MLXSW_PORT_OPER_STATUS_UP) + pr_info("ib link for port %d - up\n", + mlxsw_sx_port->mapping.module + 1); + else + pr_info("ib link for port %d - down\n", + mlxsw_sx_port->mapping.module + 1); +} + static void mlxsw_sx_pude_event_func(const struct mlxsw_reg_info *reg, char *pude_pl, void *priv) { struct mlxsw_sx *mlxsw_sx = priv; struct mlxsw_sx_port *mlxsw_sx_port; enum mlxsw_reg_pude_oper_status status; + enum devlink_port_type port_type; u8 local_port; local_port = mlxsw_reg_pude_local_port_get(pude_pl); @@ -1225,13 +1390,11 @@ static void mlxsw_sx_pude_event_func(const struct mlxsw_reg_info *reg, } status = mlxsw_reg_pude_oper_status_get(pude_pl); - if (status == MLXSW_PORT_OPER_STATUS_UP) { - netdev_info(mlxsw_sx_port->dev, "link up\n"); - netif_carrier_on(mlxsw_sx_port->dev); - } else { - netdev_info(mlxsw_sx_port->dev, "link down\n"); - netif_carrier_off(mlxsw_sx_port->dev); - } + port_type = mlxsw_core_port_type_get(mlxsw_sx->core, local_port); + if (port_type == DEVLINK_PORT_TYPE_ETH) + mlxsw_sx_pude_eth_event_func(mlxsw_sx_port, status); + else if (port_type == DEVLINK_PORT_TYPE_IB) + mlxsw_sx_pude_ib_event_func(mlxsw_sx_port, status); } static struct mlxsw_event_listener mlxsw_sx_pude_event = { @@ -1305,6 +1468,33 @@ static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port, netif_receive_skb(skb); } +static int mlxsw_sx_port_type_set(struct mlxsw_core *mlxsw_core, u8 local_port, + enum devlink_port_type new_type) +{ + struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core); + u8 module, width; + int err; + + if (new_type == DEVLINK_PORT_TYPE_AUTO) + return -EOPNOTSUPP; + + __mlxsw_sx_port_remove(mlxsw_sx, local_port); + err = mlxsw_sx_port_module_info_get(mlxsw_sx, local_port, &module, + &width); + if (err) + goto err_port_module_info_get; + + if (new_type == DEVLINK_PORT_TYPE_ETH) + err = __mlxsw_sx_port_eth_create(mlxsw_sx, local_port, module, + width); + else if (new_type == DEVLINK_PORT_TYPE_IB) + err = __mlxsw_sx_port_ib_create(mlxsw_sx, local_port, module, + width); + +err_port_module_info_get: + return err; +} + static const struct mlxsw_rx_listener mlxsw_sx_rx_listener[] = { { .func = mlxsw_sx_rx_listener_func, @@ -1614,6 +1804,7 @@ static struct mlxsw_driver mlxsw_sx_driver = { .txhdr_construct = mlxsw_sx_txhdr_construct, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sx_config_profile, + .port_type_set = mlxsw_sx_port_type_set, }; static const struct pci_device_id mlxsw_sx_pci_id_table[] = { -- cgit v1.1 From d1ba52638456f20f81e9182b74d56ce036b39c6d Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Fri, 28 Oct 2016 21:36:01 +0200 Subject: mlxsw: switchib: Introduce SwitchIB and SwitchIB silicon driver SwitchIB and SwitchIB-2 are Infiniband switches with up to 36 ports. This driver initialize the hardware and Firmware which implements the IB management and connection with the SM. Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 11 + drivers/net/ethernet/mellanox/mlxsw/Makefile | 2 + drivers/net/ethernet/mellanox/mlxsw/pci.h | 2 + drivers/net/ethernet/mellanox/mlxsw/port.h | 3 + drivers/net/ethernet/mellanox/mlxsw/switchib.c | 598 +++++++++++++++++++++++++ 5 files changed, 616 insertions(+) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/switchib.c diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index d0bcf59..bac2e5e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -29,6 +29,17 @@ config MLXSW_PCI To compile this driver as a module, choose M here: the module will be called mlxsw_pci. +config MLXSW_SWITCHIB + tristate "Mellanox Technologies SwitchIB and SwitchIB-2 support" + depends on MLXSW_CORE && NET_SWITCHDEV + default m + ---help--- + This driver supports Mellanox Technologies SwitchIB and SwitchIB-2 + Infiniband Switch ASICs. + + To compile this driver as a module, choose M here: the + module will be called mlxsw_switchib. + config MLXSW_SWITCHX2 tristate "Mellanox Technologies SwitchX-2 support" depends on MLXSW_CORE && MLXSW_PCI && NET_SWITCHDEV diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index d20ae18..badef87 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -3,6 +3,8 @@ mlxsw_core-objs := core.o mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o mlxsw_pci-objs := pci.o +obj-$(CONFIG_MLXSW_SWITCHIB) += mlxsw_switchib.o +mlxsw_switchib-objs := switchib.o obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o mlxsw_switchx2-objs := switchx2.o obj-$(CONFIG_MLXSW_SPECTRUM) += mlxsw_spectrum.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index 35a0011..d655823 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -39,6 +39,8 @@ #define PCI_DEVICE_ID_MELLANOX_SWITCHX2 0xc738 #define PCI_DEVICE_ID_MELLANOX_SPECTRUM 0xcb84 +#define PCI_DEVICE_ID_MELLANOX_SWITCHIB 0xcb20 +#define PCI_DEVICE_ID_MELLANOX_SWITCHIB2 0xcf08 #if IS_ENABLED(CONFIG_MLXSW_PCI) diff --git a/drivers/net/ethernet/mellanox/mlxsw/port.h b/drivers/net/ethernet/mellanox/mlxsw/port.h index 7296071..3d42146 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/port.h +++ b/drivers/net/ethernet/mellanox/mlxsw/port.h @@ -52,6 +52,9 @@ #define MLXSW_PORT_MAX_PHY_PORTS 0x40 #define MLXSW_PORT_MAX_PORTS (MLXSW_PORT_MAX_PHY_PORTS + 1) +#define MLXSW_PORT_MAX_IB_PHY_PORTS 36 +#define MLXSW_PORT_MAX_IB_PORTS (MLXSW_PORT_MAX_IB_PHY_PORTS + 1) + #define MLXSW_PORT_DEVID_BITS_OFFSET 10 #define MLXSW_PORT_PHY_BITS_OFFSET 4 #define MLXSW_PORT_PHY_BITS_MASK (MLXSW_PORT_MAX_PHY_PORTS - 1) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c new file mode 100644 index 0000000..ec0b27e --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -0,0 +1,598 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/switchib.c + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Elad Raz + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" +#include "core.h" +#include "reg.h" +#include "port.h" +#include "trap.h" +#include "txheader.h" +#include "ib.h" + +static const char mlxsw_sib_driver_name[] = "mlxsw_switchib"; +static const char mlxsw_sib2_driver_name[] = "mlxsw_switchib2"; + +struct mlxsw_sib_port; + +struct mlxsw_sib { + struct mlxsw_sib_port **ports; + struct mlxsw_core *core; + const struct mlxsw_bus_info *bus_info; +}; + +struct mlxsw_sib_port { + struct mlxsw_sib *mlxsw_sib; + u8 local_port; + struct { + u8 module; + } mapping; +}; + +/* tx_v1_hdr_version + * Tx header version. + * Must be set to 1. + */ +MLXSW_ITEM32(tx_v1, hdr, version, 0x00, 28, 4); + +/* tx_v1_hdr_ctl + * Packet control type. + * 0 - Ethernet control (e.g. EMADs, LACP) + * 1 - Ethernet data + */ +MLXSW_ITEM32(tx_v1, hdr, ctl, 0x00, 26, 2); + +/* tx_v1_hdr_proto + * Packet protocol type. Must be set to 1 (Ethernet). + */ +MLXSW_ITEM32(tx_v1, hdr, proto, 0x00, 21, 3); + +/* tx_v1_hdr_swid + * Switch partition ID. Must be set to 0. + */ +MLXSW_ITEM32(tx_v1, hdr, swid, 0x00, 12, 3); + +/* tx_v1_hdr_control_tclass + * Indicates if the packet should use the control TClass and not one + * of the data TClasses. + */ +MLXSW_ITEM32(tx_v1, hdr, control_tclass, 0x00, 6, 1); + +/* tx_v1_hdr_port_mid + * Destination local port for unicast packets. + * Destination multicast ID for multicast packets. + * + * Control packets are directed to a specific egress port, while data + * packets are transmitted through the CPU port (0) into the switch partition, + * where forwarding rules are applied. + */ +MLXSW_ITEM32(tx_v1, hdr, port_mid, 0x04, 16, 16); + +/* tx_v1_hdr_type + * 0 - Data packets + * 6 - Control packets + */ +MLXSW_ITEM32(tx_v1, hdr, type, 0x0C, 0, 4); + +static void +mlxsw_sib_tx_v1_hdr_construct(struct sk_buff *skb, + const struct mlxsw_tx_info *tx_info) +{ + char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN); + + memset(txhdr, 0, MLXSW_TXHDR_LEN); + + mlxsw_tx_v1_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1); + mlxsw_tx_v1_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL); + mlxsw_tx_v1_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH); + mlxsw_tx_v1_hdr_swid_set(txhdr, 0); + mlxsw_tx_v1_hdr_control_tclass_set(txhdr, 1); + mlxsw_tx_v1_hdr_port_mid_set(txhdr, tx_info->local_port); + mlxsw_tx_v1_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL); +} + +static int +mlxsw_sib_port_admin_status_set(struct mlxsw_sib_port *mlxsw_sib_port, + bool is_up) +{ + struct mlxsw_sib *mlxsw_sib = mlxsw_sib_port->mlxsw_sib; + char paos_pl[MLXSW_REG_PAOS_LEN]; + + mlxsw_reg_paos_pack(paos_pl, mlxsw_sib_port->local_port, + is_up ? MLXSW_PORT_ADMIN_STATUS_UP : + MLXSW_PORT_ADMIN_STATUS_DOWN); + return mlxsw_reg_write(mlxsw_sib->core, MLXSW_REG(paos), paos_pl); +} + +static int mlxsw_sib_port_mtu_set(struct mlxsw_sib_port *mlxsw_sib_port, + u16 mtu) +{ + struct mlxsw_sib *mlxsw_sib = mlxsw_sib_port->mlxsw_sib; + char pmtu_pl[MLXSW_REG_PMTU_LEN]; + int max_mtu; + int err; + + mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sib_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sib->core, MLXSW_REG(pmtu), pmtu_pl); + if (err) + return err; + max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl); + + if (mtu > max_mtu) + return -EINVAL; + + mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sib_port->local_port, mtu); + return mlxsw_reg_write(mlxsw_sib->core, MLXSW_REG(pmtu), pmtu_pl); +} + +static int mlxsw_sib_port_set(struct mlxsw_sib_port *mlxsw_sib_port, u8 port) +{ + struct mlxsw_sib *mlxsw_sib = mlxsw_sib_port->mlxsw_sib; + char plib_pl[MLXSW_REG_PLIB_LEN] = {0}; + int err; + + mlxsw_reg_plib_local_port_set(plib_pl, mlxsw_sib_port->local_port); + mlxsw_reg_plib_ib_port_set(plib_pl, port); + err = mlxsw_reg_write(mlxsw_sib->core, MLXSW_REG(plib), plib_pl); + return err; +} + +static int mlxsw_sib_port_swid_set(struct mlxsw_sib_port *mlxsw_sib_port, + u8 swid) +{ + struct mlxsw_sib *mlxsw_sib = mlxsw_sib_port->mlxsw_sib; + char pspa_pl[MLXSW_REG_PSPA_LEN]; + + mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sib_port->local_port); + return mlxsw_reg_write(mlxsw_sib->core, MLXSW_REG(pspa), pspa_pl); +} + +static int mlxsw_sib_port_module_info_get(struct mlxsw_sib *mlxsw_sib, + u8 local_port, u8 *p_module, + u8 *p_width) +{ + char pmlp_pl[MLXSW_REG_PMLP_LEN]; + int err; + + mlxsw_reg_pmlp_pack(pmlp_pl, local_port); + err = mlxsw_reg_query(mlxsw_sib->core, MLXSW_REG(pmlp), pmlp_pl); + if (err) + return err; + *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0); + *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl); + return 0; +} + +static int mlxsw_sib_port_speed_set(struct mlxsw_sib_port *mlxsw_sib_port, + u16 speed, u16 width) +{ + struct mlxsw_sib *mlxsw_sib = mlxsw_sib_port->mlxsw_sib; + char ptys_pl[MLXSW_REG_PTYS_LEN]; + + mlxsw_reg_ptys_ib_pack(ptys_pl, mlxsw_sib_port->local_port, speed, + width); + return mlxsw_reg_write(mlxsw_sib->core, MLXSW_REG(ptys), ptys_pl); +} + +static bool mlxsw_sib_port_created(struct mlxsw_sib *mlxsw_sib, u8 local_port) +{ + return mlxsw_sib->ports[local_port] != NULL; +} + +static int __mlxsw_sib_port_create(struct mlxsw_sib *mlxsw_sib, u8 local_port, + u8 module, u8 width) +{ + struct mlxsw_sib_port *mlxsw_sib_port; + int err; + + mlxsw_sib_port = kzalloc(sizeof(*mlxsw_sib_port), GFP_KERNEL); + if (!mlxsw_sib_port) + return -ENOMEM; + mlxsw_sib_port->mlxsw_sib = mlxsw_sib; + mlxsw_sib_port->local_port = local_port; + mlxsw_sib_port->mapping.module = module; + + err = mlxsw_sib_port_swid_set(mlxsw_sib_port, 0); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to set SWID\n", + mlxsw_sib_port->local_port); + goto err_port_swid_set; + } + + /* Expose the IB port number as it's front panel name */ + err = mlxsw_sib_port_set(mlxsw_sib_port, module + 1); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to set IB port\n", + mlxsw_sib_port->local_port); + goto err_port_ib_set; + } + + /* Supports all speeds from SDR to FDR (bitmask) and support bus width + * of 1x, 2x and 4x (3 bits bitmask) + */ + err = mlxsw_sib_port_speed_set(mlxsw_sib_port, + MLXSW_REG_PTYS_IB_SPEED_EDR - 1, + BIT(3) - 1); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to set speed\n", + mlxsw_sib_port->local_port); + goto err_port_speed_set; + } + + /* Change to the maximum MTU the device supports, the SMA will take + * care of the active MTU + */ + err = mlxsw_sib_port_mtu_set(mlxsw_sib_port, MLXSW_IB_DEFAULT_MTU); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to set MTU\n", + mlxsw_sib_port->local_port); + goto err_port_mtu_set; + } + + err = mlxsw_sib_port_admin_status_set(mlxsw_sib_port, true); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to change admin state to UP\n", + mlxsw_sib_port->local_port); + goto err_port_admin_set; + } + + mlxsw_core_port_ib_set(mlxsw_sib->core, mlxsw_sib_port->local_port, + mlxsw_sib_port); + mlxsw_sib->ports[local_port] = mlxsw_sib_port; + return 0; + +err_port_admin_set: +err_port_mtu_set: +err_port_speed_set: +err_port_ib_set: + mlxsw_sib_port_swid_set(mlxsw_sib_port, MLXSW_PORT_SWID_DISABLED_PORT); +err_port_swid_set: + kfree(mlxsw_sib_port); + return err; +} + +static int mlxsw_sib_port_create(struct mlxsw_sib *mlxsw_sib, u8 local_port, + u8 module, u8 width) +{ + int err; + + err = mlxsw_core_port_init(mlxsw_sib->core, local_port); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to init core port\n", + local_port); + return err; + } + err = __mlxsw_sib_port_create(mlxsw_sib, local_port, module, width); + if (err) + goto err_port_create; + + return 0; + +err_port_create: + mlxsw_core_port_fini(mlxsw_sib->core, local_port); + return err; +} + +static void __mlxsw_sib_port_remove(struct mlxsw_sib *mlxsw_sib, u8 local_port) +{ + struct mlxsw_sib_port *mlxsw_sib_port = mlxsw_sib->ports[local_port]; + + mlxsw_core_port_clear(mlxsw_sib->core, local_port, mlxsw_sib); + mlxsw_sib->ports[local_port] = NULL; + mlxsw_sib_port_admin_status_set(mlxsw_sib_port, false); + mlxsw_sib_port_swid_set(mlxsw_sib_port, MLXSW_PORT_SWID_DISABLED_PORT); + kfree(mlxsw_sib_port); +} + +static void mlxsw_sib_port_remove(struct mlxsw_sib *mlxsw_sib, u8 local_port) +{ + __mlxsw_sib_port_remove(mlxsw_sib, local_port); + mlxsw_core_port_fini(mlxsw_sib->core, local_port); +} + +static void mlxsw_sib_ports_remove(struct mlxsw_sib *mlxsw_sib) +{ + int i; + + for (i = 1; i < MLXSW_PORT_MAX_IB_PORTS; i++) + if (mlxsw_sib_port_created(mlxsw_sib, i)) + mlxsw_sib_port_remove(mlxsw_sib, i); + kfree(mlxsw_sib->ports); +} + +static int mlxsw_sib_ports_create(struct mlxsw_sib *mlxsw_sib) +{ + size_t alloc_size; + u8 module, width; + int i; + int err; + + alloc_size = sizeof(struct mlxsw_sib_port *) * MLXSW_PORT_MAX_IB_PORTS; + mlxsw_sib->ports = kzalloc(alloc_size, GFP_KERNEL); + if (!mlxsw_sib->ports) + return -ENOMEM; + + for (i = 1; i < MLXSW_PORT_MAX_IB_PORTS; i++) { + err = mlxsw_sib_port_module_info_get(mlxsw_sib, i, &module, + &width); + if (err) + goto err_port_module_info_get; + if (!width) + continue; + err = mlxsw_sib_port_create(mlxsw_sib, i, module, width); + if (err) + goto err_port_create; + } + return 0; + +err_port_create: +err_port_module_info_get: + for (i--; i >= 1; i--) + if (mlxsw_sib_port_created(mlxsw_sib, i)) + mlxsw_sib_port_remove(mlxsw_sib, i); + kfree(mlxsw_sib->ports); + return err; +} + +static void +mlxsw_sib_pude_ib_event_func(struct mlxsw_sib_port *mlxsw_sib_port, + enum mlxsw_reg_pude_oper_status status) +{ + if (status == MLXSW_PORT_OPER_STATUS_UP) + pr_info("ib link for port %d - up\n", + mlxsw_sib_port->mapping.module + 1); + else + pr_info("ib link for port %d - down\n", + mlxsw_sib_port->mapping.module + 1); +} + +static void mlxsw_sib_pude_event_func(const struct mlxsw_reg_info *reg, + char *pude_pl, void *priv) +{ + struct mlxsw_sib *mlxsw_sib = priv; + struct mlxsw_sib_port *mlxsw_sib_port; + enum mlxsw_reg_pude_oper_status status; + u8 local_port; + + local_port = mlxsw_reg_pude_local_port_get(pude_pl); + mlxsw_sib_port = mlxsw_sib->ports[local_port]; + if (!mlxsw_sib_port) { + dev_warn(mlxsw_sib->bus_info->dev, "Port %d: Link event received for non-existent port\n", + local_port); + return; + } + + status = mlxsw_reg_pude_oper_status_get(pude_pl); + mlxsw_sib_pude_ib_event_func(mlxsw_sib_port, status); +} + +static struct mlxsw_event_listener mlxsw_sib_pude_event = { + .func = mlxsw_sib_pude_event_func, + .trap_id = MLXSW_TRAP_ID_PUDE, +}; + +static int mlxsw_sib_event_register(struct mlxsw_sib *mlxsw_sib, + enum mlxsw_event_trap_id trap_id) +{ + struct mlxsw_event_listener *el; + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + int err; + + switch (trap_id) { + case MLXSW_TRAP_ID_PUDE: + el = &mlxsw_sib_pude_event; + break; + } + err = mlxsw_core_event_listener_register(mlxsw_sib->core, el, + mlxsw_sib); + if (err) + return err; + + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id); + err = mlxsw_reg_write(mlxsw_sib->core, MLXSW_REG(hpkt), hpkt_pl); + if (err) + goto err_event_trap_set; + + return 0; + +err_event_trap_set: + mlxsw_core_event_listener_unregister(mlxsw_sib->core, el, mlxsw_sib); + return err; +} + +static void mlxsw_sib_event_unregister(struct mlxsw_sib *mlxsw_sib, + enum mlxsw_event_trap_id trap_id) +{ + struct mlxsw_event_listener *el; + + switch (trap_id) { + case MLXSW_TRAP_ID_PUDE: + el = &mlxsw_sib_pude_event; + break; + } + mlxsw_core_event_listener_unregister(mlxsw_sib->core, el, mlxsw_sib); +} + +static int mlxsw_sib_init(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info) +{ + struct mlxsw_sib *mlxsw_sib = mlxsw_core_driver_priv(mlxsw_core); + int err; + + mlxsw_sib->core = mlxsw_core; + mlxsw_sib->bus_info = mlxsw_bus_info; + + err = mlxsw_sib_ports_create(mlxsw_sib); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Failed to create ports\n"); + return err; + } + + err = mlxsw_sib_event_register(mlxsw_sib, MLXSW_TRAP_ID_PUDE); + if (err) { + dev_err(mlxsw_sib->bus_info->dev, "Failed to register for PUDE events\n"); + goto err_event_register; + } + + return 0; + +err_event_register: + mlxsw_sib_ports_remove(mlxsw_sib); + return err; +} + +static void mlxsw_sib_fini(struct mlxsw_core *mlxsw_core) +{ + struct mlxsw_sib *mlxsw_sib = mlxsw_core_driver_priv(mlxsw_core); + + mlxsw_sib_event_unregister(mlxsw_sib, MLXSW_TRAP_ID_PUDE); + mlxsw_sib_ports_remove(mlxsw_sib); +} + +static struct mlxsw_config_profile mlxsw_sib_config_profile = { + .used_max_system_port = 1, + .max_system_port = 48000, + .used_max_ib_mc = 1, + .max_ib_mc = 27, + .used_max_pkey = 1, + .max_pkey = 32, + .swid_config = { + { + .used_type = 1, + .type = MLXSW_PORT_SWID_TYPE_IB, + } + }, + .resource_query_enable = 0, +}; + +static struct mlxsw_driver mlxsw_sib_driver = { + .kind = mlxsw_sib_driver_name, + .priv_size = sizeof(struct mlxsw_sib), + .init = mlxsw_sib_init, + .fini = mlxsw_sib_fini, + .txhdr_construct = mlxsw_sib_tx_v1_hdr_construct, + .txhdr_len = MLXSW_TXHDR_LEN, + .profile = &mlxsw_sib_config_profile, +}; + +static struct mlxsw_driver mlxsw_sib2_driver = { + .kind = mlxsw_sib2_driver_name, + .priv_size = sizeof(struct mlxsw_sib), + .init = mlxsw_sib_init, + .fini = mlxsw_sib_fini, + .txhdr_construct = mlxsw_sib_tx_v1_hdr_construct, + .txhdr_len = MLXSW_TXHDR_LEN, + .profile = &mlxsw_sib_config_profile, +}; + +static const struct pci_device_id mlxsw_sib_pci_id_table[] = { + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHIB), 0}, + {0, }, +}; + +static struct pci_driver mlxsw_sib_pci_driver = { + .name = mlxsw_sib_driver_name, + .id_table = mlxsw_sib_pci_id_table, +}; + +static const struct pci_device_id mlxsw_sib2_pci_id_table[] = { + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHIB2), 0}, + {0, }, +}; + +static struct pci_driver mlxsw_sib2_pci_driver = { + .name = mlxsw_sib2_driver_name, + .id_table = mlxsw_sib2_pci_id_table, +}; + +static int __init mlxsw_sib_module_init(void) +{ + int err; + + err = mlxsw_core_driver_register(&mlxsw_sib_driver); + if (err) + return err; + + err = mlxsw_core_driver_register(&mlxsw_sib2_driver); + if (err) + goto err_sib2_driver_register; + + err = mlxsw_pci_driver_register(&mlxsw_sib_pci_driver); + if (err) + goto err_sib_pci_driver_register; + + err = mlxsw_pci_driver_register(&mlxsw_sib2_pci_driver); + if (err) + goto err_sib2_pci_driver_register; + + return 0; + +err_sib2_pci_driver_register: + mlxsw_pci_driver_unregister(&mlxsw_sib_pci_driver); +err_sib_pci_driver_register: + mlxsw_core_driver_unregister(&mlxsw_sib2_driver); +err_sib2_driver_register: + mlxsw_core_driver_unregister(&mlxsw_sib_driver); + return err; +} + +static void __exit mlxsw_sib_module_exit(void) +{ + mlxsw_pci_driver_unregister(&mlxsw_sib2_pci_driver); + mlxsw_pci_driver_unregister(&mlxsw_sib_pci_driver); + mlxsw_core_driver_unregister(&mlxsw_sib2_driver); + mlxsw_core_driver_unregister(&mlxsw_sib_driver); +} + +module_init(mlxsw_sib_module_init); +module_exit(mlxsw_sib_module_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Elad Raz "); +MODULE_DESCRIPTION("Mellanox SwitchIB and SwitchIB-2 driver"); +MODULE_ALIAS("mlxsw_switchib2"); +MODULE_DEVICE_TABLE(pci, mlxsw_sib_pci_id_table); +MODULE_DEVICE_TABLE(pci, mlxsw_sib2_pci_id_table); -- cgit v1.1 From d46b63494544356ed07ed4f7ec6f8d6edb6ddcf5 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 25 Oct 2016 18:41:31 +0200 Subject: net: bonding: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c9944d8..5708f17 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4080,16 +4080,16 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } -static int bond_ethtool_get_settings(struct net_device *bond_dev, - struct ethtool_cmd *ecmd) +static int bond_ethtool_get_link_ksettings(struct net_device *bond_dev, + struct ethtool_link_ksettings *cmd) { struct bonding *bond = netdev_priv(bond_dev); unsigned long speed = 0; struct list_head *iter; struct slave *slave; - ecmd->duplex = DUPLEX_UNKNOWN; - ecmd->port = PORT_OTHER; + cmd->base.duplex = DUPLEX_UNKNOWN; + cmd->base.port = PORT_OTHER; /* Since bond_slave_can_tx returns false for all inactive or down slaves, we * do not need to check mode. Though link speed might not represent @@ -4100,12 +4100,12 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev, if (bond_slave_can_tx(slave)) { if (slave->speed != SPEED_UNKNOWN) speed += slave->speed; - if (ecmd->duplex == DUPLEX_UNKNOWN && + if (cmd->base.duplex == DUPLEX_UNKNOWN && slave->duplex != DUPLEX_UNKNOWN) - ecmd->duplex = slave->duplex; + cmd->base.duplex = slave->duplex; } } - ethtool_cmd_speed_set(ecmd, speed ? : SPEED_UNKNOWN); + cmd->base.speed = speed ? : SPEED_UNKNOWN; return 0; } @@ -4121,8 +4121,8 @@ static void bond_ethtool_get_drvinfo(struct net_device *bond_dev, static const struct ethtool_ops bond_ethtool_ops = { .get_drvinfo = bond_ethtool_get_drvinfo, - .get_settings = bond_ethtool_get_settings, .get_link = ethtool_op_get_link, + .get_link_ksettings = bond_ethtool_get_link_ksettings, }; static const struct net_device_ops bond_netdev_ops = { -- cgit v1.1 From d8e4aa070751bfd646b8692cc12b0ee7e55d3266 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 31 Oct 2016 08:59:27 -0400 Subject: mv643xx_eth: Fix merge error. One merge conflict block wasn't resolved. Reported-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 8302c7e..69451b1 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -3220,15 +3220,12 @@ static int mv643xx_eth_probe(struct platform_device *pdev) dev->priv_flags |= IFF_UNICAST_FLT; dev->gso_max_segs = MV643XX_MAX_TSO_SEGS; -<<<<<<< HEAD /* MTU range: 64 - 9500 */ dev->min_mtu = 64; dev->max_mtu = 9500; SET_NETDEV_DEV(dev, &pdev->dev); -======= ->>>>>>> 2a26d99b251b8625d27aed14e97fc10707a3a81f if (mp->shared->win_protect) wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect); -- cgit v1.1 From 2a43ca0aa9084c41aaa1006fbf22155b2e94babf Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 31 Oct 2016 10:33:08 -0400 Subject: mv643xx_eth: Properly resolve merge conflict. The second SET_NETDEV_DEV() in the hunk should be removed. Reported-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 69451b1..6749b83 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -3224,8 +3224,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev) dev->min_mtu = 64; dev->max_mtu = 9500; - SET_NETDEV_DEV(dev, &pdev->dev); - if (mp->shared->win_protect) wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect); -- cgit v1.1 From c62cce2caee558e18aa05c01c2fd3b40f07174f2 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 24 Oct 2016 18:29:13 -0700 Subject: net: add an ioctl to get a socket network namespace Each socket operates in a network namespace where it has been created, so if we want to dump and restore a socket, we have to know its network namespace. We have a socket_diag to get information about sockets, it doesn't report sockets which are not bound or connected. This patch introduces a new socket ioctl, which is called SIOCGSKNS and used to get a file descriptor for a socket network namespace. A task must have CAP_NET_ADMIN in a target network namespace to use this ioctl. Cc: "David S. Miller" Cc: Eric W. Biederman Signed-off-by: Andrei Vagin Signed-off-by: David S. Miller --- fs/nsfs.c | 2 +- include/linux/proc_fs.h | 4 ++++ include/uapi/linux/sockios.h | 1 + net/socket.c | 13 +++++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/fs/nsfs.c b/fs/nsfs.c index 8718af8..8c9fb29 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -118,7 +118,7 @@ again: return ret; } -static int open_related_ns(struct ns_common *ns, +int open_related_ns(struct ns_common *ns, struct ns_common *(*get_ns)(struct ns_common *ns)) { struct path path = {}; diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index b97bf2e..368c7ad 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -82,4 +82,8 @@ static inline struct proc_dir_entry *proc_net_mkdir( return proc_mkdir_data(name, 0, parent, net); } +struct ns_common; +int open_related_ns(struct ns_common *ns, + struct ns_common *(*get_ns)(struct ns_common *ns)); + #endif /* _LINUX_PROC_FS_H */ diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h index 8e7890b..83cc54c 100644 --- a/include/uapi/linux/sockios.h +++ b/include/uapi/linux/sockios.h @@ -84,6 +84,7 @@ #define SIOCWANDEV 0x894A /* get/set netdev parameters */ #define SIOCOUTQNSD 0x894B /* output queue size (not sent only) */ +#define SIOCGSKNS 0x894C /* get socket network namespace */ /* ARP cache control calls. */ /* 0x8950 - 0x8952 * obsolete calls, don't re-use */ diff --git a/net/socket.c b/net/socket.c index 5a9bf5e..970a7ea 100644 --- a/net/socket.c +++ b/net/socket.c @@ -877,6 +877,11 @@ static long sock_do_ioctl(struct net *net, struct socket *sock, * what to do with it - that's up to the protocol still. */ +static struct ns_common *get_net_ns(struct ns_common *ns) +{ + return &get_net(container_of(ns, struct net, ns))->ns; +} + static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) { struct socket *sock; @@ -945,6 +950,13 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) err = dlci_ioctl_hook(cmd, argp); mutex_unlock(&dlci_ioctl_mutex); break; + case SIOCGSKNS: + err = -EPERM; + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + break; + + err = open_related_ns(&net->ns, get_net_ns); + break; default: err = sock_do_ioctl(net, sock, cmd, arg); break; @@ -3093,6 +3105,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, case SIOCSIFVLAN: case SIOCADDDLCI: case SIOCDELDLCI: + case SIOCGSKNS: return sock_ioctl(file, cmd, arg); case SIOCGIFFLAGS: -- cgit v1.1 From 20861f26e33d76a4f3587bcc866fa1dab3e01094 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Thu, 27 Oct 2016 09:05:22 +0800 Subject: driver: tun: Use new macro SOCK_IOC_TYPE instead of literal number 0x89 The current codes use _IOC_TYPE(cmd) == 0x89 to check if the cmd is one socket ioctl command like SIOCGIFHWADDR. But the literal number 0x89 may confuse readers. So create one macro SOCK_IOC_TYPE to enhance the readability. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/tun.c | 2 +- include/uapi/linux/sockios.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9142db8..1588469 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1985,7 +1985,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, int le; int ret; - if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) { + if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == SOCK_IOC_TYPE) { if (copy_from_user(&ifr, argp, ifreq_len)) return -EFAULT; } else { diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h index 83cc54c..79d029d 100644 --- a/include/uapi/linux/sockios.h +++ b/include/uapi/linux/sockios.h @@ -24,6 +24,8 @@ #define SIOCINQ FIONREAD #define SIOCOUTQ TIOCOUTQ /* output queue size (not sent + not acked) */ +#define SOCK_IOC_TYPE 0x89 + /* Routing table calls. */ #define SIOCADDRT 0x890B /* add routing table entry */ #define SIOCDELRT 0x890C /* delete routing table entry */ -- cgit v1.1 From 9cf1f6a8c4cbb7836b838b51b3b02ddf32c6c6a0 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 28 Oct 2016 11:43:20 -0400 Subject: net: Move functions for configuring traffic classes out of inline headers The functions for configuring the traffic class to queue mappings have other effects that need to be addressed. Instead of trying to export a bunch of new functions just relocate the functions so that we can instrument them directly with the functionality they will need. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/netdevice.h | 31 +++---------------------------- net/core/dev.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 20ce8df..e05ab3b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1920,34 +1920,9 @@ int netdev_set_prio_tc_map(struct net_device *dev, u8 prio, u8 tc) return 0; } -static inline -void netdev_reset_tc(struct net_device *dev) -{ - dev->num_tc = 0; - memset(dev->tc_to_txq, 0, sizeof(dev->tc_to_txq)); - memset(dev->prio_tc_map, 0, sizeof(dev->prio_tc_map)); -} - -static inline -int netdev_set_tc_queue(struct net_device *dev, u8 tc, u16 count, u16 offset) -{ - if (tc >= dev->num_tc) - return -EINVAL; - - dev->tc_to_txq[tc].count = count; - dev->tc_to_txq[tc].offset = offset; - return 0; -} - -static inline -int netdev_set_num_tc(struct net_device *dev, u8 num_tc) -{ - if (num_tc > TC_MAX_QUEUE) - return -EINVAL; - - dev->num_tc = num_tc; - return 0; -} +void netdev_reset_tc(struct net_device *dev); +int netdev_set_tc_queue(struct net_device *dev, u8 tc, u16 count, u16 offset); +int netdev_set_num_tc(struct net_device *dev, u8 num_tc); static inline int netdev_get_num_tc(struct net_device *dev) diff --git a/net/core/dev.c b/net/core/dev.c index 8341dad..2d54be9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2173,6 +2173,35 @@ error: EXPORT_SYMBOL(netif_set_xps_queue); #endif +void netdev_reset_tc(struct net_device *dev) +{ + dev->num_tc = 0; + memset(dev->tc_to_txq, 0, sizeof(dev->tc_to_txq)); + memset(dev->prio_tc_map, 0, sizeof(dev->prio_tc_map)); +} +EXPORT_SYMBOL(netdev_reset_tc); + +int netdev_set_tc_queue(struct net_device *dev, u8 tc, u16 count, u16 offset) +{ + if (tc >= dev->num_tc) + return -EINVAL; + + dev->tc_to_txq[tc].count = count; + dev->tc_to_txq[tc].offset = offset; + return 0; +} +EXPORT_SYMBOL(netdev_set_tc_queue); + +int netdev_set_num_tc(struct net_device *dev, u8 num_tc) +{ + if (num_tc > TC_MAX_QUEUE) + return -EINVAL; + + dev->num_tc = num_tc; + return 0; +} +EXPORT_SYMBOL(netdev_set_num_tc); + /* * Routine to help set real_num_tx_queues. To avoid skbs mapped to queues * greater then real_num_tx_queues stale skbs on the qdisc must be flushed. -- cgit v1.1 From 8d059b0f6f5b1d3acf829454e1087818ad660058 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 28 Oct 2016 11:43:49 -0400 Subject: net: Add sysfs value to determine queue traffic class Add a sysfs attribute for a Tx queue that allows us to determine the traffic class for a given queue. This will allow us to more easily determine this in the future. It is needed as XPS will take the traffic class for a group of queues into account in order to avoid pulling traffic from one traffic class into another. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 17 +++++++++++++++++ net/core/net-sysfs.c | 20 +++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e05ab3b..d91a418 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1920,6 +1920,7 @@ int netdev_set_prio_tc_map(struct net_device *dev, u8 prio, u8 tc) return 0; } +int netdev_txq_to_tc(struct net_device *dev, unsigned int txq); void netdev_reset_tc(struct net_device *dev); int netdev_set_tc_queue(struct net_device *dev, u8 tc, u16 count, u16 offset); int netdev_set_num_tc(struct net_device *dev, u8 num_tc); diff --git a/net/core/dev.c b/net/core/dev.c index 2d54be9..db0fdbb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1948,6 +1948,23 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq) } } +int netdev_txq_to_tc(struct net_device *dev, unsigned int txq) +{ + if (dev->num_tc) { + struct netdev_tc_txq *tc = &dev->tc_to_txq[0]; + int i; + + for (i = 0; i < TC_MAX_QUEUE; i++, tc++) { + if ((txq - tc->offset) < tc->count) + return i; + } + + return -1; + } + + return 0; +} + #ifdef CONFIG_XPS static DEFINE_MUTEX(xps_map_mutex); #define xmap_dereference(P) \ diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index d4fe286..38bd9b9 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1024,7 +1024,6 @@ static ssize_t show_trans_timeout(struct netdev_queue *queue, return sprintf(buf, "%lu", trans_timeout); } -#ifdef CONFIG_XPS static unsigned int get_netdev_queue_index(struct netdev_queue *queue) { struct net_device *dev = queue->dev; @@ -1036,6 +1035,21 @@ static unsigned int get_netdev_queue_index(struct netdev_queue *queue) return i; } +static ssize_t show_traffic_class(struct netdev_queue *queue, + struct netdev_queue_attribute *attribute, + char *buf) +{ + struct net_device *dev = queue->dev; + int index = get_netdev_queue_index(queue); + int tc = netdev_txq_to_tc(dev, index); + + if (tc < 0) + return -EINVAL; + + return sprintf(buf, "%u\n", tc); +} + +#ifdef CONFIG_XPS static ssize_t show_tx_maxrate(struct netdev_queue *queue, struct netdev_queue_attribute *attribute, char *buf) @@ -1078,6 +1092,9 @@ static struct netdev_queue_attribute queue_tx_maxrate = static struct netdev_queue_attribute queue_trans_timeout = __ATTR(tx_timeout, S_IRUGO, show_trans_timeout, NULL); +static struct netdev_queue_attribute queue_traffic_class = + __ATTR(traffic_class, S_IRUGO, show_traffic_class, NULL); + #ifdef CONFIG_BQL /* * Byte queue limits sysfs structures and functions. @@ -1263,6 +1280,7 @@ static struct netdev_queue_attribute xps_cpus_attribute = static struct attribute *netdev_queue_default_attrs[] = { &queue_trans_timeout.attr, + &queue_traffic_class.attr, #ifdef CONFIG_XPS &xps_cpus_attribute.attr, &queue_tx_maxrate.attr, -- cgit v1.1 From 6234f87407cb2c02a5828e161225e5a84163dc85 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 28 Oct 2016 11:46:49 -0400 Subject: net: Refactor removal of queues from XPS map and apply on num_tc changes This patch updates the code for removing queues from the XPS map and makes it so that we can apply the code any time we change either the number of traffic classes or the mapping of a given block of queues. This way we avoid having queues pulling traffic from a foreign traffic class. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- net/core/dev.c | 73 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index db0fdbb..108a6ad 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1970,32 +1970,50 @@ static DEFINE_MUTEX(xps_map_mutex); #define xmap_dereference(P) \ rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) -static struct xps_map *remove_xps_queue(struct xps_dev_maps *dev_maps, - int cpu, u16 index) +static bool remove_xps_queue(struct xps_dev_maps *dev_maps, + int tci, u16 index) { struct xps_map *map = NULL; int pos; if (dev_maps) - map = xmap_dereference(dev_maps->cpu_map[cpu]); + map = xmap_dereference(dev_maps->cpu_map[tci]); + if (!map) + return false; - for (pos = 0; map && pos < map->len; pos++) { - if (map->queues[pos] == index) { - if (map->len > 1) { - map->queues[pos] = map->queues[--map->len]; - } else { - RCU_INIT_POINTER(dev_maps->cpu_map[cpu], NULL); - kfree_rcu(map, rcu); - map = NULL; - } + for (pos = map->len; pos--;) { + if (map->queues[pos] != index) + continue; + + if (map->len > 1) { + map->queues[pos] = map->queues[--map->len]; break; } + + RCU_INIT_POINTER(dev_maps->cpu_map[tci], NULL); + kfree_rcu(map, rcu); + return false; } - return map; + return true; } -static void netif_reset_xps_queues_gt(struct net_device *dev, u16 index) +static bool remove_xps_queue_cpu(struct net_device *dev, + struct xps_dev_maps *dev_maps, + int cpu, u16 offset, u16 count) +{ + int i, j; + + for (i = count, j = offset; i--; j++) { + if (!remove_xps_queue(dev_maps, cpu, j)) + break; + } + + return i < 0; +} + +static void netif_reset_xps_queues(struct net_device *dev, u16 offset, + u16 count) { struct xps_dev_maps *dev_maps; int cpu, i; @@ -2007,21 +2025,16 @@ static void netif_reset_xps_queues_gt(struct net_device *dev, u16 index) if (!dev_maps) goto out_no_maps; - for_each_possible_cpu(cpu) { - for (i = index; i < dev->num_tx_queues; i++) { - if (!remove_xps_queue(dev_maps, cpu, i)) - break; - } - if (i == dev->num_tx_queues) - active = true; - } + for_each_possible_cpu(cpu) + active |= remove_xps_queue_cpu(dev, dev_maps, cpu, + offset, count); if (!active) { RCU_INIT_POINTER(dev->xps_maps, NULL); kfree_rcu(dev_maps, rcu); } - for (i = index; i < dev->num_tx_queues; i++) + for (i = offset + (count - 1); count--; i--) netdev_queue_numa_node_write(netdev_get_tx_queue(dev, i), NUMA_NO_NODE); @@ -2029,6 +2042,11 @@ out_no_maps: mutex_unlock(&xps_map_mutex); } +static void netif_reset_xps_queues_gt(struct net_device *dev, u16 index) +{ + netif_reset_xps_queues(dev, index, dev->num_tx_queues - index); +} + static struct xps_map *expand_xps_map(struct xps_map *map, int cpu, u16 index) { @@ -2192,6 +2210,9 @@ EXPORT_SYMBOL(netif_set_xps_queue); #endif void netdev_reset_tc(struct net_device *dev) { +#ifdef CONFIG_XPS + netif_reset_xps_queues_gt(dev, 0); +#endif dev->num_tc = 0; memset(dev->tc_to_txq, 0, sizeof(dev->tc_to_txq)); memset(dev->prio_tc_map, 0, sizeof(dev->prio_tc_map)); @@ -2203,6 +2224,9 @@ int netdev_set_tc_queue(struct net_device *dev, u8 tc, u16 count, u16 offset) if (tc >= dev->num_tc) return -EINVAL; +#ifdef CONFIG_XPS + netif_reset_xps_queues(dev, offset, count); +#endif dev->tc_to_txq[tc].count = count; dev->tc_to_txq[tc].offset = offset; return 0; @@ -2214,6 +2238,9 @@ int netdev_set_num_tc(struct net_device *dev, u8 num_tc) if (num_tc > TC_MAX_QUEUE) return -EINVAL; +#ifdef CONFIG_XPS + netif_reset_xps_queues_gt(dev, 0); +#endif dev->num_tc = num_tc; return 0; } -- cgit v1.1 From 184c449f91fef521042970cca46bd5cdfc0e3a37 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 28 Oct 2016 11:50:13 -0400 Subject: net: Add support for XPS with QoS via traffic classes This patch adds support for setting and using XPS when QoS via traffic classes is enabled. With this change we will factor in the priority and traffic class mapping of the packet and use that information to correctly select the queue. This allows us to define a set of queues for a given traffic class via mqprio and then configure the XPS mapping for those queues so that the traffic flows can avoid head-of-line blocking between the individual CPUs if so desired. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 +- net/core/dev.c | 117 ++++++++++++++++++++++++++++++++-------------- net/core/net-sysfs.c | 31 +++++++----- 3 files changed, 105 insertions(+), 47 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d91a418..66fd61c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -732,8 +732,8 @@ struct xps_dev_maps { struct rcu_head rcu; struct xps_map __rcu *cpu_map[0]; }; -#define XPS_DEV_MAPS_SIZE (sizeof(struct xps_dev_maps) + \ - (nr_cpu_ids * sizeof(struct xps_map *))) +#define XPS_DEV_MAPS_SIZE(_tcs) (sizeof(struct xps_dev_maps) + \ + (nr_cpu_ids * (_tcs) * sizeof(struct xps_map *))) #endif /* CONFIG_XPS */ #define TC_MAX_QUEUE 16 diff --git a/net/core/dev.c b/net/core/dev.c index 108a6ad..f23e286 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2002,14 +2002,22 @@ static bool remove_xps_queue_cpu(struct net_device *dev, struct xps_dev_maps *dev_maps, int cpu, u16 offset, u16 count) { - int i, j; + int num_tc = dev->num_tc ? : 1; + bool active = false; + int tci; - for (i = count, j = offset; i--; j++) { - if (!remove_xps_queue(dev_maps, cpu, j)) - break; + for (tci = cpu * num_tc; num_tc--; tci++) { + int i, j; + + for (i = count, j = offset; i--; j++) { + if (!remove_xps_queue(dev_maps, cpu, j)) + break; + } + + active |= i < 0; } - return i < 0; + return active; } static void netif_reset_xps_queues(struct net_device *dev, u16 offset, @@ -2086,20 +2094,28 @@ int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask, u16 index) { struct xps_dev_maps *dev_maps, *new_dev_maps = NULL; + int i, cpu, tci, numa_node_id = -2; + int maps_sz, num_tc = 1, tc = 0; struct xps_map *map, *new_map; - int maps_sz = max_t(unsigned int, XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES); - int cpu, numa_node_id = -2; bool active = false; + if (dev->num_tc) { + num_tc = dev->num_tc; + tc = netdev_txq_to_tc(dev, index); + if (tc < 0) + return -EINVAL; + } + + maps_sz = XPS_DEV_MAPS_SIZE(num_tc); + if (maps_sz < L1_CACHE_BYTES) + maps_sz = L1_CACHE_BYTES; + mutex_lock(&xps_map_mutex); dev_maps = xmap_dereference(dev->xps_maps); /* allocate memory for queue storage */ - for_each_online_cpu(cpu) { - if (!cpumask_test_cpu(cpu, mask)) - continue; - + for_each_cpu_and(cpu, cpu_online_mask, mask) { if (!new_dev_maps) new_dev_maps = kzalloc(maps_sz, GFP_KERNEL); if (!new_dev_maps) { @@ -2107,25 +2123,38 @@ int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask, return -ENOMEM; } - map = dev_maps ? xmap_dereference(dev_maps->cpu_map[cpu]) : + tci = cpu * num_tc + tc; + map = dev_maps ? xmap_dereference(dev_maps->cpu_map[tci]) : NULL; map = expand_xps_map(map, cpu, index); if (!map) goto error; - RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], map); + RCU_INIT_POINTER(new_dev_maps->cpu_map[tci], map); } if (!new_dev_maps) goto out_no_new_maps; for_each_possible_cpu(cpu) { + /* copy maps belonging to foreign traffic classes */ + for (i = tc, tci = cpu * num_tc; dev_maps && i--; tci++) { + /* fill in the new device map from the old device map */ + map = xmap_dereference(dev_maps->cpu_map[tci]); + RCU_INIT_POINTER(new_dev_maps->cpu_map[tci], map); + } + + /* We need to explicitly update tci as prevous loop + * could break out early if dev_maps is NULL. + */ + tci = cpu * num_tc + tc; + if (cpumask_test_cpu(cpu, mask) && cpu_online(cpu)) { /* add queue to CPU maps */ int pos = 0; - map = xmap_dereference(new_dev_maps->cpu_map[cpu]); + map = xmap_dereference(new_dev_maps->cpu_map[tci]); while ((pos < map->len) && (map->queues[pos] != index)) pos++; @@ -2139,26 +2168,36 @@ int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask, #endif } else if (dev_maps) { /* fill in the new device map from the old device map */ - map = xmap_dereference(dev_maps->cpu_map[cpu]); - RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], map); + map = xmap_dereference(dev_maps->cpu_map[tci]); + RCU_INIT_POINTER(new_dev_maps->cpu_map[tci], map); } + /* copy maps belonging to foreign traffic classes */ + for (i = num_tc - tc, tci++; dev_maps && --i; tci++) { + /* fill in the new device map from the old device map */ + map = xmap_dereference(dev_maps->cpu_map[tci]); + RCU_INIT_POINTER(new_dev_maps->cpu_map[tci], map); + } } rcu_assign_pointer(dev->xps_maps, new_dev_maps); /* Cleanup old maps */ - if (dev_maps) { - for_each_possible_cpu(cpu) { - new_map = xmap_dereference(new_dev_maps->cpu_map[cpu]); - map = xmap_dereference(dev_maps->cpu_map[cpu]); + if (!dev_maps) + goto out_no_old_maps; + + for_each_possible_cpu(cpu) { + for (i = num_tc, tci = cpu * num_tc; i--; tci++) { + new_map = xmap_dereference(new_dev_maps->cpu_map[tci]); + map = xmap_dereference(dev_maps->cpu_map[tci]); if (map && map != new_map) kfree_rcu(map, rcu); } - - kfree_rcu(dev_maps, rcu); } + kfree_rcu(dev_maps, rcu); + +out_no_old_maps: dev_maps = new_dev_maps; active = true; @@ -2173,11 +2212,12 @@ out_no_new_maps: /* removes queue from unused CPUs */ for_each_possible_cpu(cpu) { - if (cpumask_test_cpu(cpu, mask) && cpu_online(cpu)) - continue; - - if (remove_xps_queue(dev_maps, cpu, index)) - active = true; + for (i = tc, tci = cpu * num_tc; i--; tci++) + active |= remove_xps_queue(dev_maps, tci, index); + if (!cpumask_test_cpu(cpu, mask) || !cpu_online(cpu)) + active |= remove_xps_queue(dev_maps, tci, index); + for (i = num_tc - tc, tci++; --i; tci++) + active |= remove_xps_queue(dev_maps, tci, index); } /* free map if not active */ @@ -2193,11 +2233,14 @@ out_no_maps: error: /* remove any maps that we added */ for_each_possible_cpu(cpu) { - new_map = xmap_dereference(new_dev_maps->cpu_map[cpu]); - map = dev_maps ? xmap_dereference(dev_maps->cpu_map[cpu]) : - NULL; - if (new_map && new_map != map) - kfree(new_map); + for (i = num_tc, tci = cpu * num_tc; i--; tci++) { + new_map = xmap_dereference(new_dev_maps->cpu_map[tci]); + map = dev_maps ? + xmap_dereference(dev_maps->cpu_map[tci]) : + NULL; + if (new_map && new_map != map) + kfree(new_map); + } } mutex_unlock(&xps_map_mutex); @@ -3158,8 +3201,14 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb) rcu_read_lock(); dev_maps = rcu_dereference(dev->xps_maps); if (dev_maps) { - map = rcu_dereference( - dev_maps->cpu_map[skb->sender_cpu - 1]); + unsigned int tci = skb->sender_cpu - 1; + + if (dev->num_tc) { + tci *= dev->num_tc; + tci += netdev_get_prio_tc_map(dev, skb->priority); + } + + map = rcu_dereference(dev_maps->cpu_map[tci]); if (map) { if (map->len == 1) queue_index = map->queues[0]; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 38bd9b9..b0c04cf 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1210,29 +1210,38 @@ static ssize_t show_xps_map(struct netdev_queue *queue, struct netdev_queue_attribute *attribute, char *buf) { struct net_device *dev = queue->dev; + int cpu, len, num_tc = 1, tc = 0; struct xps_dev_maps *dev_maps; cpumask_var_t mask; unsigned long index; - int i, len; if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) return -ENOMEM; index = get_netdev_queue_index(queue); + if (dev->num_tc) { + num_tc = dev->num_tc; + tc = netdev_txq_to_tc(dev, index); + if (tc < 0) + return -EINVAL; + } + rcu_read_lock(); dev_maps = rcu_dereference(dev->xps_maps); if (dev_maps) { - for_each_possible_cpu(i) { - struct xps_map *map = - rcu_dereference(dev_maps->cpu_map[i]); - if (map) { - int j; - for (j = 0; j < map->len; j++) { - if (map->queues[j] == index) { - cpumask_set_cpu(i, mask); - break; - } + for_each_possible_cpu(cpu) { + int i, tci = cpu * num_tc + tc; + struct xps_map *map; + + map = rcu_dereference(dev_maps->cpu_map[tci]); + if (!map) + continue; + + for (i = map->len; i--;) { + if (map->queues[i] == index) { + cpumask_set_cpu(cpu, mask); + break; } } } -- cgit v1.1 From 8778b276645bb156b00d72275541a4e4b188d6dc Mon Sep 17 00:00:00 2001 From: Aaron Young Date: Fri, 28 Oct 2016 14:26:19 -0400 Subject: ldmvsw: tx queue stuck in stopped state after LDC reset The following patch fixes an issue with the ldmvsw driver where the network connection of a guest domain becomes non-functional after the guest domain has panic'd and rebooted. The root cause was determined to be from the following series of events: 1. Guest domain panics - resulting in the guest no longer processing network packets (from ldmvsw driver) 2. The ldmvsw driver (in the control domain) eventually exerts flow control due to no more available tx drings and stops the tx queue for the guest domain 3. The LDC of the network connection for the guest is reset when the guest domain reboots after the panic. 4. The LDC reset event is received by the ldmvsw driver and the ldmvsw responds by clearing the tx queue for the guest. 5. ldmvsw waits indefinitely for a DATA ACK from the guest - which is the normal method to re-enable the tx queue. But the ACK never comes because the tx queue was cleared due to the LDC reset. To fix this issue, in addition to clearing the tx queue, re-enable the tx queue on a LDC reset. This prevents the ldmvsw from getting caught in this deadlocked state of waiting for a DATA ACK which will never come. Signed-off-by: Aaron Young Acked-by: Sowmini Varadhan Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 58efe69..8878b75 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -704,9 +704,8 @@ static int handle_mcast(struct vnet_port *port, void *msgbuf) return 0; } -/* Got back a STOPPED LDC message on port. If the queue is stopped, - * wake it up so that we'll send out another START message at the - * next TX. +/* If the queue is stopped, wake it up so that we'll + * send out another START message at the next TX. */ static void maybe_tx_wakeup(struct vnet_port *port) { @@ -734,6 +733,7 @@ EXPORT_SYMBOL_GPL(sunvnet_port_is_up_common); static int vnet_event_napi(struct vnet_port *port, int budget) { + struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port); struct vio_driver_state *vio = &port->vio; int tx_wakeup, err; int npkts = 0; @@ -747,6 +747,16 @@ ldc_ctrl: if (event == LDC_EVENT_RESET) { vnet_port_reset(port); vio_port_up(vio); + + /* If the device is running but its tx queue was + * stopped (due to flow control), restart it. + * This is necessary since vnet_port_reset() + * clears the tx drings and thus we may never get + * back a VIO_TYPE_DATA ACK packet - which is + * the normal mechanism to restart the tx queue. + */ + if (netif_running(dev)) + maybe_tx_wakeup(port); } port->rx_event = 0; return 0; -- cgit v1.1 From 0f98621bef5d2b7ad41f6595899660af344f5016 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 29 Oct 2016 02:30:46 +0200 Subject: bpf, inode: add support for symlinks and fix mtime/ctime While commit bb35a6ef7da4 ("bpf, inode: allow for rename and link ops") added support for hard links that can be used for prog and map nodes, this work adds simple symlink support, which can be used f.e. for directories also when unpriviledged and works with cmdline tooling that understands S_IFLNK anyway. Since the switch in e27f4a942a0e ("bpf: Use mount_nodev not mount_ns to mount the bpf filesystem"), there can be various mount instances with mount_nodev() and thus hierarchy can be flattened to facilitate object sharing. Thus, we can keep bpf tooling also working by repointing paths. Most of the functionality can be used from vfs library operations. The symlink is stored in the inode itself, that is in i_link, which is sufficient in our case as opposed to storing it in the page cache. While at it, I noticed that bpf_mkdir() and bpf_mkobj() don't update the directories mtime and ctime, so add a common helper for it called bpf_dentry_finalize() that takes care of it for all cases now. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/inode.c | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 1ed8473..2565809 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -87,6 +87,7 @@ static struct inode *bpf_get_inode(struct super_block *sb, switch (mode & S_IFMT) { case S_IFDIR: case S_IFREG: + case S_IFLNK: break; default: return ERR_PTR(-EINVAL); @@ -119,6 +120,16 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type) return 0; } +static void bpf_dentry_finalize(struct dentry *dentry, struct inode *inode, + struct inode *dir) +{ + d_instantiate(dentry, inode); + dget(dentry); + + dir->i_mtime = current_time(dir); + dir->i_ctime = dir->i_mtime; +} + static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { struct inode *inode; @@ -133,9 +144,7 @@ static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inc_nlink(inode); inc_nlink(dir); - d_instantiate(dentry, inode); - dget(dentry); - + bpf_dentry_finalize(dentry, inode, dir); return 0; } @@ -151,9 +160,7 @@ static int bpf_mkobj_ops(struct inode *dir, struct dentry *dentry, inode->i_op = iops; inode->i_private = dentry->d_fsdata; - d_instantiate(dentry, inode); - dget(dentry); - + bpf_dentry_finalize(dentry, inode, dir); return 0; } @@ -181,13 +188,37 @@ bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags) { if (strchr(dentry->d_name.name, '.')) return ERR_PTR(-EPERM); + return simple_lookup(dir, dentry, flags); } +static int bpf_symlink(struct inode *dir, struct dentry *dentry, + const char *target) +{ + char *link = kstrdup(target, GFP_USER | __GFP_NOWARN); + struct inode *inode; + + if (!link) + return -ENOMEM; + + inode = bpf_get_inode(dir->i_sb, dir, S_IRWXUGO | S_IFLNK); + if (IS_ERR(inode)) { + kfree(link); + return PTR_ERR(inode); + } + + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = link; + + bpf_dentry_finalize(dentry, inode, dir); + return 0; +} + static const struct inode_operations bpf_dir_iops = { .lookup = bpf_lookup, .mknod = bpf_mkobj, .mkdir = bpf_mkdir, + .symlink = bpf_symlink, .rmdir = simple_rmdir, .rename = simple_rename, .link = simple_link, @@ -324,6 +355,8 @@ static void bpf_evict_inode(struct inode *inode) truncate_inode_pages_final(&inode->i_data); clear_inode(inode); + if (S_ISLNK(inode->i_mode)) + kfree(inode->i_link); if (!bpf_inode_type(inode, &type)) bpf_any_put(inode->i_private, type); } -- cgit v1.1 From 63215705a6cc4481dde21b5343bb706837f273d9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 29 Oct 2016 21:37:06 +0200 Subject: ptp: use permission-specific DEVICE_ATTR variants Use DEVICE_ATTR_RO for read only attributes. This simplifies the source code, improves readbility, and reduces the chance of inconsistencies. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @ro@ declarer name DEVICE_ATTR; identifier x,x_show; @@ DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL); @script:ocaml@ x << ro.x; x_show << ro.x_show; @@ if not (x^"_show" = x_show) then Coccilib.include_match false @@ declarer name DEVICE_ATTR_RO; identifier ro.x,ro.x_show; @@ - DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL); + DEVICE_ATTR_RO(x); // Signed-off-by: Julia Lawall Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/ptp_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c index 302e626..53d4395 100644 --- a/drivers/ptp/ptp_sysfs.c +++ b/drivers/ptp/ptp_sysfs.c @@ -28,7 +28,7 @@ static ssize_t clock_name_show(struct device *dev, struct ptp_clock *ptp = dev_get_drvdata(dev); return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name); } -static DEVICE_ATTR(clock_name, 0444, clock_name_show, NULL); +static DEVICE_ATTR_RO(clock_name); #define PTP_SHOW_INT(name, var) \ static ssize_t var##_show(struct device *dev, \ -- cgit v1.1 From 89d9123e8ee28cb380ce5532038e89fc19471a77 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 29 Oct 2016 21:37:09 +0200 Subject: solos-pci: use permission-specific DEVICE_ATTR variants Use DEVICE_ATTR_RW for read-write attributes. This simplifies the source code, improves readbility, and reduces the chance of inconsistencies. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @rw@ declarer name DEVICE_ATTR; identifier x,x_show,x_store; @@ DEVICE_ATTR(x, \(0644\|S_IRUGO|S_IWUSR\), x_show, x_store); @script:ocaml@ x << rw.x; x_show << rw.x_show; x_store << rw.x_store; @@ if not (x^"_show" = x_show && x^"_store" = x_store) then Coccilib.include_match false @@ declarer name DEVICE_ATTR_RW; identifier rw.x,rw.x_show,rw.x_store; @@ - DEVICE_ATTR(x, \(0644\|S_IRUGO|S_IWUSR\), x_show, x_store); + DEVICE_ATTR_RW(x); // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/atm/solos-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 6ac2b2b..5ad037c 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -584,7 +584,7 @@ static ssize_t hardware_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", data32); } -static DEVICE_ATTR(console, 0644, console_show, console_store); +static DEVICE_ATTR_RW(console); #define SOLOS_ATTR_RO(x) static DEVICE_ATTR(x, 0444, solos_param_show, NULL); -- cgit v1.1 From 0fefbfbaad298162737d5418eb85065879f99b3e Mon Sep 17 00:00:00 2001 From: Sudarsana Kalluru Date: Mon, 31 Oct 2016 07:14:21 +0200 Subject: qed*: Management firmware - notifications and defaults Management firmware is interested in various tidbits about the driver - including the driver state & several configuration related fields [MTU, primtary MAC, etc.]. This adds the necessray logic to update MFW with such configurations, some of which are passed directly via qed while for others APIs are provide so that qede would be able to later configure if needed. This also introduces a new default configuration for MTU which would replace the default inherited by being an ethernet device. Signed-off-by: Sudarsana Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 52 +++++++- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 59 ++++++++- drivers/net/ethernet/qlogic/qed/qed_main.c | 75 +++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.c | 163 ++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 102 +++++++++++++++ drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 2 + drivers/net/ethernet/qlogic/qede/qede_main.c | 8 ++ include/linux/qed/qed_if.h | 28 ++++ 9 files changed, 487 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 653bb57..f20243c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -226,6 +226,7 @@ struct qed_hw_info { u32 port_mode; u32 hw_mode; unsigned long device_capabilities; + u16 mtu; }; struct qed_hw_cid_data { diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index edae5fc..33fd69e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1057,8 +1057,10 @@ int qed_hw_init(struct qed_dev *cdev, bool allow_npar_tx_switch, const u8 *bin_fw_data) { - u32 load_code, param; - int rc, mfw_rc, i; + u32 load_code, param, drv_mb_param; + bool b_default_mtu = true; + struct qed_hwfn *p_hwfn; + int rc = 0, mfw_rc, i; if ((int_mode == QED_INT_MODE_MSI) && (cdev->num_hwfns > 1)) { DP_NOTICE(cdev, "MSI mode is not supported for CMT devices\n"); @@ -1074,6 +1076,12 @@ int qed_hw_init(struct qed_dev *cdev, for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + /* If management didn't provide a default, set one of our own */ + if (!p_hwfn->hw_info.mtu) { + p_hwfn->hw_info.mtu = 1500; + b_default_mtu = false; + } + if (IS_VF(cdev)) { p_hwfn->b_int_enabled = 1; continue; @@ -1157,6 +1165,38 @@ int qed_hw_init(struct qed_dev *cdev, p_hwfn->hw_init_done = true; } + if (IS_PF(cdev)) { + p_hwfn = QED_LEADING_HWFN(cdev); + drv_mb_param = (FW_MAJOR_VERSION << 24) | + (FW_MINOR_VERSION << 16) | + (FW_REVISION_VERSION << 8) | + (FW_ENGINEERING_VERSION); + rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt, + DRV_MSG_CODE_OV_UPDATE_STORM_FW_VER, + drv_mb_param, &load_code, ¶m); + if (rc) + DP_INFO(p_hwfn, "Failed to update firmware version\n"); + + if (!b_default_mtu) { + rc = qed_mcp_ov_update_mtu(p_hwfn, p_hwfn->p_main_ptt, + p_hwfn->hw_info.mtu); + if (rc) + DP_INFO(p_hwfn, + "Failed to update default mtu\n"); + } + + rc = qed_mcp_ov_update_driver_state(p_hwfn, + p_hwfn->p_main_ptt, + QED_OV_DRIVER_STATE_DISABLED); + if (rc) + DP_INFO(p_hwfn, "Failed to update driver state\n"); + + rc = qed_mcp_ov_update_eswitch(p_hwfn, p_hwfn->p_main_ptt, + QED_OV_ESWITCH_VEB); + if (rc) + DP_INFO(p_hwfn, "Failed to update eswitch mode\n"); + } + return 0; } @@ -1801,6 +1841,9 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, qed_get_num_funcs(p_hwfn, p_ptt); + if (qed_mcp_is_init(p_hwfn)) + p_hwfn->hw_info.mtu = p_hwfn->mcp_info->func_info.mtu; + return qed_hw_get_resc(p_hwfn); } @@ -1975,8 +2018,13 @@ int qed_hw_prepare(struct qed_dev *cdev, void qed_hw_remove(struct qed_dev *cdev) { + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); int i; + if (IS_PF(cdev)) + qed_mcp_ov_update_driver_state(p_hwfn, p_hwfn->p_main_ptt, + QED_OV_DRIVER_STATE_NOT_LOADED); + for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 72eee29..36de87a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -8564,6 +8564,15 @@ struct public_drv_mb { #define DRV_MSG_CODE_INIT_PHY 0x22000000 #define DRV_MSG_CODE_LINK_RESET 0x23000000 #define DRV_MSG_CODE_SET_DCBX 0x25000000 +#define DRV_MSG_CODE_OV_UPDATE_CURR_CFG 0x26000000 +#define DRV_MSG_CODE_OV_UPDATE_BUS_NUM 0x27000000 +#define DRV_MSG_CODE_OV_UPDATE_BOOT_PROGRESS 0x28000000 +#define DRV_MSG_CODE_OV_UPDATE_STORM_FW_VER 0x29000000 +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE 0x31000000 +#define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000 +#define DRV_MSG_CODE_OV_UPDATE_MTU 0x33000000 +#define DRV_MSG_CODE_OV_UPDATE_WOL 0x38000000 +#define DRV_MSG_CODE_OV_UPDATE_ESWITCH_MODE 0x39000000 #define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000 #define DRV_MSG_CODE_NIG_DRAIN 0x30000000 @@ -8574,6 +8583,13 @@ struct public_drv_mb { #define DRV_MSG_CODE_MCP_RESET 0x00090000 #define DRV_MSG_CODE_SET_VERSION 0x000f0000 #define DRV_MSG_CODE_MCP_HALT 0x00100000 +#define DRV_MSG_CODE_SET_VMAC 0x00110000 +#define DRV_MSG_CODE_GET_VMAC 0x00120000 +#define DRV_MSG_CODE_VMAC_TYPE_SHIFT 4 +#define DRV_MSG_CODE_VMAC_TYPE_MASK 0x30 +#define DRV_MSG_CODE_VMAC_TYPE_MAC 1 +#define DRV_MSG_CODE_VMAC_TYPE_WWNN 2 +#define DRV_MSG_CODE_VMAC_TYPE_WWPN 3 #define DRV_MSG_CODE_GET_STATS 0x00130000 #define DRV_MSG_CODE_STATS_TYPE_LAN 1 @@ -8589,7 +8605,10 @@ struct public_drv_mb { #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 drv_mb_param; -#define DRV_MB_PARAM_UNLOAD_WOL_MCP 0x00000001 +#define DRV_MB_PARAM_UNLOAD_WOL_UNKNOWN 0x00000000 +#define DRV_MB_PARAM_UNLOAD_WOL_MCP 0x00000001 +#define DRV_MB_PARAM_UNLOAD_WOL_DISABLED 0x00000002 +#define DRV_MB_PARAM_UNLOAD_WOL_ENABLED 0x00000003 #define DRV_MB_PARAM_DCBX_NOTIFY_MASK 0x000000FF #define DRV_MB_PARAM_DCBX_NOTIFY_SHIFT 3 @@ -8602,6 +8621,44 @@ struct public_drv_mb { #define DRV_MB_PARAM_LLDP_SEND_MASK 0x00000001 #define DRV_MB_PARAM_LLDP_SEND_SHIFT 0 +#define DRV_MB_PARAM_OV_CURR_CFG_SHIFT 0 +#define DRV_MB_PARAM_OV_CURR_CFG_MASK 0x0000000F +#define DRV_MB_PARAM_OV_CURR_CFG_NONE 0 +#define DRV_MB_PARAM_OV_CURR_CFG_OS 1 +#define DRV_MB_PARAM_OV_CURR_CFG_VENDOR_SPEC 2 +#define DRV_MB_PARAM_OV_CURR_CFG_OTHER 3 + +#define DRV_MB_PARAM_OV_STORM_FW_VER_SHIFT 0 +#define DRV_MB_PARAM_OV_STORM_FW_VER_MASK 0xFFFFFFFF +#define DRV_MB_PARAM_OV_STORM_FW_VER_MAJOR_MASK 0xFF000000 +#define DRV_MB_PARAM_OV_STORM_FW_VER_MINOR_MASK 0x00FF0000 +#define DRV_MB_PARAM_OV_STORM_FW_VER_BUILD_MASK 0x0000FF00 +#define DRV_MB_PARAM_OV_STORM_FW_VER_DROP_MASK 0x000000FF + +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_SHIFT 0 +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_MASK 0xF +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_UNKNOWN 0x1 +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_NOT_LOADED 0x2 +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_LOADING 0x3 +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_DISABLED 0x4 +#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_ACTIVE 0x5 + +#define DRV_MB_PARAM_OV_MTU_SIZE_SHIFT 0 +#define DRV_MB_PARAM_OV_MTU_SIZE_MASK 0xFFFFFFFF + +#define DRV_MB_PARAM_WOL_MASK (DRV_MB_PARAM_WOL_DEFAULT | \ + DRV_MB_PARAM_WOL_DISABLED | \ + DRV_MB_PARAM_WOL_ENABLED) +#define DRV_MB_PARAM_WOL_DEFAULT DRV_MB_PARAM_UNLOAD_WOL_MCP +#define DRV_MB_PARAM_WOL_DISABLED DRV_MB_PARAM_UNLOAD_WOL_DISABLED +#define DRV_MB_PARAM_WOL_ENABLED DRV_MB_PARAM_UNLOAD_WOL_ENABLED + +#define DRV_MB_PARAM_ESWITCH_MODE_MASK (DRV_MB_PARAM_ESWITCH_MODE_NONE | \ + DRV_MB_PARAM_ESWITCH_MODE_VEB | \ + DRV_MB_PARAM_ESWITCH_MODE_VEPA) +#define DRV_MB_PARAM_ESWITCH_MODE_NONE 0x0 +#define DRV_MB_PARAM_ESWITCH_MODE_VEB 0x1 +#define DRV_MB_PARAM_ESWITCH_MODE_VEPA 0x2 #define DRV_MB_PARAM_SET_LED_MODE_OPER 0x0 #define DRV_MB_PARAM_SET_LED_MODE_ON 0x1 diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index c418360..d9fa52a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -243,6 +243,8 @@ int qed_fill_dev_info(struct qed_dev *cdev, &dev_info->mfw_rev, NULL); } + dev_info->mtu = QED_LEADING_HWFN(cdev)->hw_info.mtu; + return 0; } @@ -1431,6 +1433,76 @@ static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode) return status; } +static int qed_update_drv_state(struct qed_dev *cdev, bool active) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *ptt; + int status = 0; + + if (IS_VF(cdev)) + return 0; + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) + return -EAGAIN; + + status = qed_mcp_ov_update_driver_state(hwfn, ptt, active ? + QED_OV_DRIVER_STATE_ACTIVE : + QED_OV_DRIVER_STATE_DISABLED); + + qed_ptt_release(hwfn, ptt); + + return status; +} + +static int qed_update_mac(struct qed_dev *cdev, u8 *mac) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *ptt; + int status = 0; + + if (IS_VF(cdev)) + return 0; + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) + return -EAGAIN; + + status = qed_mcp_ov_update_mac(hwfn, ptt, mac); + if (status) + goto out; + + status = qed_mcp_ov_update_current_config(hwfn, ptt, QED_OV_CLIENT_DRV); + +out: + qed_ptt_release(hwfn, ptt); + return status; +} + +static int qed_update_mtu(struct qed_dev *cdev, u16 mtu) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *ptt; + int status = 0; + + if (IS_VF(cdev)) + return 0; + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) + return -EAGAIN; + + status = qed_mcp_ov_update_mtu(hwfn, ptt, mtu); + if (status) + goto out; + + status = qed_mcp_ov_update_current_config(hwfn, ptt, QED_OV_CLIENT_DRV); + +out: + qed_ptt_release(hwfn, ptt); + return status; +} + static struct qed_selftest_ops qed_selftest_ops_pass = { .selftest_memory = &qed_selftest_memory, .selftest_interrupt = &qed_selftest_interrupt, @@ -1465,6 +1537,9 @@ const struct qed_common_ops qed_common_ops_pass = { .get_coalesce = &qed_get_coalesce, .set_coalesce = &qed_set_coalesce, .set_led = &qed_set_led, + .update_drv_state = &qed_update_drv_state, + .update_mac = &qed_update_mac, + .update_mtu = &qed_update_mtu, }; void qed_get_protocol_stats(struct qed_dev *cdev, diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index bdc9ba9..98dc913 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "qed.h" #include "qed_dcbx.h" #include "qed_hsi.h" @@ -1068,6 +1069,8 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, info->ovlan = (u16)(shmem_info.ovlan_stag & FUNC_MF_CFG_OV_STAG_MASK); + info->mtu = (u16)shmem_info.mtu_size; + DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP), "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x\n", info->pause_on_host, info->protocol, @@ -1223,6 +1226,166 @@ int qed_mcp_resume(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return (cpu_mode & MCP_REG_CPU_MODE_SOFT_HALT) ? -EAGAIN : 0; } +int qed_mcp_ov_update_current_config(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_client client) +{ + u32 resp = 0, param = 0; + u32 drv_mb_param; + int rc; + + switch (client) { + case QED_OV_CLIENT_DRV: + drv_mb_param = DRV_MB_PARAM_OV_CURR_CFG_OS; + break; + case QED_OV_CLIENT_USER: + drv_mb_param = DRV_MB_PARAM_OV_CURR_CFG_OTHER; + break; + case QED_OV_CLIENT_VENDOR_SPEC: + drv_mb_param = DRV_MB_PARAM_OV_CURR_CFG_VENDOR_SPEC; + break; + default: + DP_NOTICE(p_hwfn, "Invalid client type %d\n", client); + return -EINVAL; + } + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_CURR_CFG, + drv_mb_param, &resp, ¶m); + if (rc) + DP_ERR(p_hwfn, "MCP response failure, aborting\n"); + + return rc; +} + +int qed_mcp_ov_update_driver_state(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_driver_state drv_state) +{ + u32 resp = 0, param = 0; + u32 drv_mb_param; + int rc; + + switch (drv_state) { + case QED_OV_DRIVER_STATE_NOT_LOADED: + drv_mb_param = DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_NOT_LOADED; + break; + case QED_OV_DRIVER_STATE_DISABLED: + drv_mb_param = DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_DISABLED; + break; + case QED_OV_DRIVER_STATE_ACTIVE: + drv_mb_param = DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_ACTIVE; + break; + default: + DP_NOTICE(p_hwfn, "Invalid driver state %d\n", drv_state); + return -EINVAL; + } + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE, + drv_mb_param, &resp, ¶m); + if (rc) + DP_ERR(p_hwfn, "Failed to send driver state\n"); + + return rc; +} + +int qed_mcp_ov_update_mtu(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u16 mtu) +{ + u32 resp = 0, param = 0; + u32 drv_mb_param; + int rc; + + drv_mb_param = (u32)mtu << DRV_MB_PARAM_OV_MTU_SIZE_SHIFT; + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_MTU, + drv_mb_param, &resp, ¶m); + if (rc) + DP_ERR(p_hwfn, "Failed to send mtu value, rc = %d\n", rc); + + return rc; +} + +int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u8 *mac) +{ + struct qed_mcp_mb_params mb_params; + union drv_union_data union_data; + int rc; + + memset(&mb_params, 0, sizeof(mb_params)); + mb_params.cmd = DRV_MSG_CODE_SET_VMAC; + mb_params.param = DRV_MSG_CODE_VMAC_TYPE_MAC << + DRV_MSG_CODE_VMAC_TYPE_SHIFT; + mb_params.param |= MCP_PF_ID(p_hwfn); + ether_addr_copy(&union_data.raw_data[0], mac); + mb_params.p_data_src = &union_data; + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); + if (rc) + DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc); + + return rc; +} + +int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, enum qed_ov_wol wol) +{ + u32 resp = 0, param = 0; + u32 drv_mb_param; + int rc; + + switch (wol) { + case QED_OV_WOL_DEFAULT: + drv_mb_param = DRV_MB_PARAM_WOL_DEFAULT; + break; + case QED_OV_WOL_DISABLED: + drv_mb_param = DRV_MB_PARAM_WOL_DISABLED; + break; + case QED_OV_WOL_ENABLED: + drv_mb_param = DRV_MB_PARAM_WOL_ENABLED; + break; + default: + DP_ERR(p_hwfn, "Invalid wol state %d\n", wol); + return -EINVAL; + } + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_WOL, + drv_mb_param, &resp, ¶m); + if (rc) + DP_ERR(p_hwfn, "Failed to send wol mode, rc = %d\n", rc); + + return rc; +} + +int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_eswitch eswitch) +{ + u32 resp = 0, param = 0; + u32 drv_mb_param; + int rc; + + switch (eswitch) { + case QED_OV_ESWITCH_NONE: + drv_mb_param = DRV_MB_PARAM_ESWITCH_MODE_NONE; + break; + case QED_OV_ESWITCH_VEB: + drv_mb_param = DRV_MB_PARAM_ESWITCH_MODE_VEB; + break; + case QED_OV_ESWITCH_VEPA: + drv_mb_param = DRV_MB_PARAM_ESWITCH_MODE_VEPA; + break; + default: + DP_ERR(p_hwfn, "Invalid eswitch mode %d\n", eswitch); + return -EINVAL; + } + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_ESWITCH_MODE, + drv_mb_param, &resp, ¶m); + if (rc) + DP_ERR(p_hwfn, "Failed to send eswitch mode, rc = %d\n", rc); + + return rc; +} + int qed_mcp_set_led(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, enum qed_led_mode mode) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index dff520e..8950719 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -92,6 +92,8 @@ struct qed_mcp_function_info { #define QED_MCP_VLAN_UNSET (0xffff) u16 ovlan; + + u16 mtu; }; struct qed_mcp_nvm_common { @@ -147,6 +149,30 @@ union qed_mcp_protocol_stats { struct qed_mcp_rdma_stats rdma_stats; }; +enum qed_ov_eswitch { + QED_OV_ESWITCH_NONE, + QED_OV_ESWITCH_VEB, + QED_OV_ESWITCH_VEPA +}; + +enum qed_ov_client { + QED_OV_CLIENT_DRV, + QED_OV_CLIENT_USER, + QED_OV_CLIENT_VENDOR_SPEC +}; + +enum qed_ov_driver_state { + QED_OV_DRIVER_STATE_NOT_LOADED, + QED_OV_DRIVER_STATE_DISABLED, + QED_OV_DRIVER_STATE_ACTIVE +}; + +enum qed_ov_wol { + QED_OV_WOL_DEFAULT, + QED_OV_WOL_DISABLED, + QED_OV_WOL_ENABLED +}; + /** * @brief - returns the link params of the hw function * @@ -278,6 +304,69 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn, struct qed_mcp_drv_version *p_ver); /** + * @brief Notify MFW about the change in base device properties + * + * @param p_hwfn + * @param p_ptt + * @param client - qed client type + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_ov_update_current_config(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_client client); + +/** + * @brief Notify MFW about the driver state + * + * @param p_hwfn + * @param p_ptt + * @param drv_state - Driver state + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_ov_update_driver_state(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_driver_state drv_state); + +/** + * @brief Send MTU size to MFW + * + * @param p_hwfn + * @param p_ptt + * @param mtu - MTU size + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_ov_update_mtu(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u16 mtu); + +/** + * @brief Send MAC address to MFW + * + * @param p_hwfn + * @param p_ptt + * @param mac - MAC address + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u8 *mac); + +/** + * @brief Send WOL mode to MFW + * + * @param p_hwfn + * @param p_ptt + * @param wol - WOL mode + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_wol wol); + +/** * @brief Set LED status * * @param p_hwfn @@ -546,4 +635,17 @@ int __qed_configure_pf_min_bandwidth(struct qed_hwfn *p_hwfn, int qed_mcp_mask_parities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 mask_parities); +/** + * @brief Send eswitch mode to MFW + * + * @param p_hwfn + * @param p_ptt + * @param eswitch - eswitch mode + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_eswitch eswitch); + #endif diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 0100f5c..775fdaa 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -739,6 +739,8 @@ int qede_change_mtu(struct net_device *ndev, int new_mtu) qede_update_mtu(edev, &args); + edev->ops->common->update_mtu(edev->cdev, args.mtu); + return 0; } diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 8488ad3..df0bd0c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -2396,6 +2396,8 @@ static void qede_init_ndev(struct qede_dev *edev) /* Set network device HW mac */ ether_addr_copy(edev->ndev->dev_addr, edev->dev_info.common.hw_mac); + + ndev->mtu = edev->dev_info.common.mtu; } /* This function converts from 32b param to two params of level and module @@ -3751,6 +3753,8 @@ static int qede_open(struct net_device *ndev) udp_tunnel_get_rx_info(ndev); + edev->ops->common->update_drv_state(edev->cdev, true); + return 0; } @@ -3760,6 +3764,8 @@ static int qede_close(struct net_device *ndev) qede_unload(edev, QEDE_UNLOAD_NORMAL); + edev->ops->common->update_drv_state(edev->cdev, false); + return 0; } @@ -3820,6 +3826,8 @@ static int qede_set_mac_addr(struct net_device *ndev, void *p) if (rc) return rc; + edev->ops->common->update_mac(edev->cdev, addr->sa_data); + /* Add MAC filter according to the new unicast HW MAC address */ ether_addr_copy(edev->primary_mac, ndev->dev_addr); return qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_ADD, diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 8978a60..5c909cd 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -267,6 +267,7 @@ struct qed_dev_info { u8 mf_mode; bool tx_switching; bool rdma_supported; + u16 mtu; }; enum qed_sb_type { @@ -554,6 +555,33 @@ struct qed_common_ops { */ int (*set_led)(struct qed_dev *cdev, enum qed_led_mode mode); + +/** + * @brief update_drv_state - API to inform the change in the driver state. + * + * @param cdev + * @param active + * + */ + int (*update_drv_state)(struct qed_dev *cdev, bool active); + +/** + * @brief update_mac - API to inform the change in the mac address + * + * @param cdev + * @param mac + * + */ + int (*update_mac)(struct qed_dev *cdev, u8 *mac); + +/** + * @brief update_mtu - API to inform the change in the mtu + * + * @param cdev + * @param mtu + * + */ + int (*update_mtu)(struct qed_dev *cdev, u16 mtu); }; #define MASK_FIELD(_name, _value) \ -- cgit v1.1 From 7a4b21b7d1f0644456501e33d3917c9aaee76a75 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 31 Oct 2016 07:14:22 +0200 Subject: qed: Add nvram selftest Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 4 + drivers/net/ethernet/qlogic/qed/qed_main.c | 1 + drivers/net/ethernet/qlogic/qed/qed_mcp.c | 94 ++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 41 ++++++++++ drivers/net/ethernet/qlogic/qed/qed_selftest.c | 101 ++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_selftest.h | 10 +++ drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 7 ++ include/linux/qed/qed_if.h | 9 +++ 8 files changed, 267 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 36de87a..f7dfa2e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -8666,6 +8666,8 @@ struct public_drv_mb { #define DRV_MB_PARAM_BIST_REGISTER_TEST 1 #define DRV_MB_PARAM_BIST_CLOCK_TEST 2 +#define DRV_MB_PARAM_BIST_NVM_TEST_NUM_IMAGES 3 +#define DRV_MB_PARAM_BIST_NVM_TEST_IMAGE_BY_INDEX 4 #define DRV_MB_PARAM_BIST_RC_UNKNOWN 0 #define DRV_MB_PARAM_BIST_RC_PASSED 1 @@ -8674,6 +8676,8 @@ struct public_drv_mb { #define DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT 0 #define DRV_MB_PARAM_BIST_TEST_INDEX_MASK 0x000000FF +#define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_SHIFT 8 +#define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_MASK 0x0000FF00 u32 fw_mb_header; #define FW_MSG_CODE_MASK 0xffff0000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index d9fa52a..31f8e42 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1508,6 +1508,7 @@ static struct qed_selftest_ops qed_selftest_ops_pass = { .selftest_interrupt = &qed_selftest_interrupt, .selftest_register = &qed_selftest_register, .selftest_clock = &qed_selftest_clock, + .selftest_nvram = &qed_selftest_nvram, }; const struct qed_common_ops qed_common_ops_pass = { diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 98dc913..8be6157 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1434,6 +1434,52 @@ int qed_mcp_mask_parities(struct qed_hwfn *p_hwfn, return rc; } +int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len) +{ + u32 bytes_left = len, offset = 0, bytes_to_copy, read_len = 0; + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + u32 resp = 0, resp_param = 0; + struct qed_ptt *p_ptt; + int rc = 0; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EBUSY; + + while (bytes_left > 0) { + bytes_to_copy = min_t(u32, bytes_left, MCP_DRV_NVM_BUF_LEN); + + rc = qed_mcp_nvm_rd_cmd(p_hwfn, p_ptt, + DRV_MSG_CODE_NVM_READ_NVRAM, + addr + offset + + (bytes_to_copy << + DRV_MB_PARAM_NVM_LEN_SHIFT), + &resp, &resp_param, + &read_len, + (u32 *)(p_buf + offset)); + + if (rc || (resp != FW_MSG_CODE_NVM_OK)) { + DP_NOTICE(cdev, "MCP command rc = %d\n", rc); + break; + } + + /* This can be a lengthy process, and it's possible scheduler + * isn't preemptable. Sleep a bit to prevent CPU hogging. + */ + if (bytes_left % 0x1000 < + (bytes_left - read_len) % 0x1000) + usleep_range(1000, 2000); + + offset += read_len; + bytes_left -= read_len; + } + + cdev->mcp_nvm_resp = resp; + qed_ptt_release(p_hwfn, p_ptt); + + return rc; +} + int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { u32 drv_mb_param = 0, rsp, param; @@ -1475,3 +1521,51 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return rc; } + +int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 *num_images) +{ + u32 drv_mb_param = 0, rsp; + int rc = 0; + + drv_mb_param = (DRV_MB_PARAM_BIST_NVM_TEST_NUM_IMAGES << + DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT); + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_BIST_TEST, + drv_mb_param, &rsp, num_images); + if (rc) + return rc; + + if (((rsp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK)) + rc = -EINVAL; + + return rc; +} + +int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct bist_nvm_image_att *p_image_att, + u32 image_index) +{ + u32 buf_size = 0, param, resp = 0, resp_param = 0; + int rc; + + param = DRV_MB_PARAM_BIST_NVM_TEST_IMAGE_BY_INDEX << + DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT; + param |= image_index << DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_SHIFT; + + rc = qed_mcp_nvm_rd_cmd(p_hwfn, p_ptt, + DRV_MSG_CODE_BIST_TEST, param, + &resp, &resp_param, + &buf_size, + (u32 *)p_image_att); + if (rc) + return rc; + + if (((resp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK) || + (p_image_att->return_code != 1)) + rc = -EINVAL; + + return rc; +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 8950719..be8152d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -380,6 +380,18 @@ int qed_mcp_set_led(struct qed_hwfn *p_hwfn, enum qed_led_mode mode); /** + * @brief Read from nvm + * + * @param cdev + * @param addr - nvm offset + * @param p_buf - nvm read buffer + * @param len - buffer len + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len); + +/** * @brief Bist register test * * @param p_hwfn - hw function @@ -401,6 +413,35 @@ int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn, int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); +/** + * @brief Bist nvm test - get number of images + * + * @param p_hwfn - hw function + * @param p_ptt - PTT required for register access + * @param num_images - number of images if operation was + * successful. 0 if not. + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 *num_images); + +/** + * @brief Bist nvm test - get image attributes by index + * + * @param p_hwfn - hw function + * @param p_ptt - PTT required for register access + * @param p_image_att - Attributes of image + * @param image_index - Index of image to get information for + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct bist_nvm_image_att *p_image_att, + u32 image_index); + /* Using hwfn number (and not pf_num) is required since in CMT mode, * same pf_num may be used by two different hwfn * TODO - this shouldn't really be in .h file, but until all fields diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.c b/drivers/net/ethernet/qlogic/qed/qed_selftest.c index 9b7678f..48bfaec 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_selftest.c +++ b/drivers/net/ethernet/qlogic/qed/qed_selftest.c @@ -1,3 +1,4 @@ +#include #include "qed.h" #include "qed_dev_api.h" #include "qed_mcp.h" @@ -75,3 +76,103 @@ int qed_selftest_clock(struct qed_dev *cdev) return rc; } + +int qed_selftest_nvram(struct qed_dev *cdev) +{ + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn); + u32 num_images, i, j, nvm_crc, calc_crc; + struct bist_nvm_image_att image_att; + u8 *buf = NULL; + __be32 val; + int rc; + + if (!p_ptt) { + DP_ERR(p_hwfn, "failed to acquire ptt\n"); + return -EBUSY; + } + + /* Acquire from MFW the amount of available images */ + rc = qed_mcp_bist_nvm_test_get_num_images(p_hwfn, p_ptt, &num_images); + if (rc || !num_images) { + DP_ERR(p_hwfn, "Failed getting number of images\n"); + return -EINVAL; + } + + /* Iterate over images and validate CRC */ + for (i = 0; i < num_images; i++) { + /* This mailbox returns information about the image required for + * reading it. + */ + rc = qed_mcp_bist_nvm_test_get_image_att(p_hwfn, p_ptt, + &image_att, i); + if (rc) { + DP_ERR(p_hwfn, + "Failed getting image index %d attributes\n", + i); + goto err0; + } + + /* After MFW crash dump is collected - the image's CRC stops + * being valid. + */ + if (image_att.image_type == NVM_TYPE_MDUMP) + continue; + + DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n", + i, image_att.len); + + /* Allocate a buffer for holding the nvram image */ + buf = kzalloc(image_att.len, GFP_KERNEL); + if (!buf) { + rc = -ENOMEM; + goto err0; + } + + /* Read image into buffer */ + rc = qed_mcp_nvm_read(p_hwfn->cdev, image_att.nvm_start_addr, + buf, image_att.len); + if (rc) { + DP_ERR(p_hwfn, + "Failed reading image index %d from nvm.\n", i); + goto err1; + } + + /* Convert the buffer into big-endian format (excluding the + * closing 4 bytes of CRC). + */ + for (j = 0; j < image_att.len - 4; j += 4) { + val = cpu_to_be32(*(u32 *)&buf[j]); + *(u32 *)&buf[j] = (__force u32)val; + } + + /* Calc CRC for the "actual" image buffer, i.e. not including + * the last 4 CRC bytes. + */ + nvm_crc = *(u32 *)(buf + image_att.len - 4); + calc_crc = crc32(0xffffffff, buf, image_att.len - 4); + calc_crc = (__force u32)~cpu_to_be32(calc_crc); + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "nvm crc 0x%x, calc_crc 0x%x\n", nvm_crc, calc_crc); + + if (calc_crc != nvm_crc) { + rc = -EINVAL; + goto err1; + } + + /* Done with this image; Free to prevent double release + * on subsequent failure. + */ + kfree(buf); + buf = NULL; + } + + qed_ptt_release(p_hwfn, p_ptt); + return 0; + +err1: + kfree(buf); +err0: + qed_ptt_release(p_hwfn, p_ptt); + return rc; +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.h b/drivers/net/ethernet/qlogic/qed/qed_selftest.h index 50eb0b49..739ddb7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_selftest.h +++ b/drivers/net/ethernet/qlogic/qed/qed_selftest.h @@ -37,4 +37,14 @@ int qed_selftest_register(struct qed_dev *cdev); * @return int */ int qed_selftest_clock(struct qed_dev *cdev); + +/** + * @brief qed_selftest_nvram - Perform nvram test + * + * @param cdev + * + * @return int + */ +int qed_selftest_nvram(struct qed_dev *cdev); + #endif diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 775fdaa..a809408 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -157,6 +157,7 @@ enum qede_ethtool_tests { QEDE_ETHTOOL_MEMORY_TEST, QEDE_ETHTOOL_REGISTER_TEST, QEDE_ETHTOOL_CLOCK_TEST, + QEDE_ETHTOOL_NVRAM_TEST, QEDE_ETHTOOL_TEST_MAX }; @@ -166,6 +167,7 @@ static const char qede_tests_str_arr[QEDE_ETHTOOL_TEST_MAX][ETH_GSTRING_LEN] = { "Memory (online)\t\t", "Register (online)\t", "Clock (online)\t\t", + "Nvram (online)\t\t", }; static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) @@ -1392,6 +1394,11 @@ static void qede_self_test(struct net_device *dev, buf[QEDE_ETHTOOL_CLOCK_TEST] = 1; etest->flags |= ETH_TEST_FL_FAILED; } + + if (edev->ops->common->selftest->selftest_nvram(edev->cdev)) { + buf[QEDE_ETHTOOL_NVRAM_TEST] = 1; + etest->flags |= ETH_TEST_FL_FAILED; + } } static int qede_set_tunable(struct net_device *dev, diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 5c909cd..ffc2d2f 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -402,6 +402,15 @@ struct qed_selftest_ops { * @return 0 on success, error otherwise. */ int (*selftest_clock)(struct qed_dev *cdev); + +/** + * @brief selftest_nvram - Perform nvram test + * + * @param cdev + * + * @return 0 on success, error otherwise. + */ + int (*selftest_nvram) (struct qed_dev *cdev); }; struct qed_common_ops { -- cgit v1.1 From 14d39648cbfc6289e3f873d30f282b9517ebe860 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 31 Oct 2016 07:14:23 +0200 Subject: qed*: Add support for WoL Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 11 ++++- drivers/net/ethernet/qlogic/qed/qed_dev.c | 19 ++++++++- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 4 ++ drivers/net/ethernet/qlogic/qed/qed_main.c | 29 +++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.c | 56 ++++++++++++++++++++++++- drivers/net/ethernet/qlogic/qede/qede.h | 2 + drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 41 ++++++++++++++++++ drivers/net/ethernet/qlogic/qede/qede_main.c | 9 ++++ include/linux/qed/qed_if.h | 10 +++++ 9 files changed, 176 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index f20243c..8828ffa 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -195,6 +195,11 @@ enum qed_dev_cap { QED_DEV_CAP_ROCE, }; +enum qed_wol_support { + QED_WOL_SUPPORT_NONE, + QED_WOL_SUPPORT_PME, +}; + struct qed_hw_info { /* PCI personality */ enum qed_pci_personality personality; @@ -227,6 +232,8 @@ struct qed_hw_info { u32 hw_mode; unsigned long device_capabilities; u16 mtu; + + enum qed_wol_support b_wol_support; }; struct qed_hw_cid_data { @@ -539,7 +546,9 @@ struct qed_dev { u8 mcp_rev; u8 boot_mode; - u8 wol; + /* WoL related configurations */ + u8 wol_config; + u8 wol_mac[ETH_ALEN]; u32 int_mode; enum qed_coalescing_mode int_coalescing_mode; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 33fd69e..127ed5f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1364,8 +1364,24 @@ int qed_hw_reset(struct qed_dev *cdev) { int rc = 0; u32 unload_resp, unload_param; + u32 wol_param; int i; + switch (cdev->wol_config) { + case QED_OV_WOL_DISABLED: + wol_param = DRV_MB_PARAM_UNLOAD_WOL_DISABLED; + break; + case QED_OV_WOL_ENABLED: + wol_param = DRV_MB_PARAM_UNLOAD_WOL_ENABLED; + break; + default: + DP_NOTICE(cdev, + "Unknown WoL configuration %02x\n", cdev->wol_config); + /* Fallthrough */ + case QED_OV_WOL_DEFAULT: + wol_param = DRV_MB_PARAM_UNLOAD_WOL_MCP; + } + for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; @@ -1394,8 +1410,7 @@ int qed_hw_reset(struct qed_dev *cdev) /* Send unload command to MCP */ rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt, - DRV_MSG_CODE_UNLOAD_REQ, - DRV_MB_PARAM_UNLOAD_WOL_MCP, + DRV_MSG_CODE_UNLOAD_REQ, wol_param, &unload_resp, &unload_param); if (rc) { DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_REQ failed\n"); diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index f7dfa2e..fdb7a09 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -8601,6 +8601,7 @@ struct public_drv_mb { #define DRV_MSG_CODE_BIST_TEST 0x001e0000 #define DRV_MSG_CODE_SET_LED_MODE 0x00200000 +#define DRV_MSG_CODE_OS_WOL 0x002e0000 #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff @@ -8697,6 +8698,9 @@ struct public_drv_mb { #define FW_MSG_CODE_NVM_OK 0x00010000 #define FW_MSG_CODE_OK 0x00160000 +#define FW_MSG_CODE_OS_WOL_SUPPORTED 0x00800000 +#define FW_MSG_CODE_OS_WOL_NOT_SUPPORTED 0x00810000 + #define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 fw_mb_param; diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 31f8e42..b71d73a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -221,6 +221,10 @@ int qed_fill_dev_info(struct qed_dev *cdev, dev_info->fw_eng = FW_ENGINEERING_VERSION; dev_info->mf_mode = cdev->mf_mode; dev_info->tx_switching = true; + + if (QED_LEADING_HWFN(cdev)->hw_info.b_wol_support == + QED_WOL_SUPPORT_PME) + dev_info->wol_support = true; } else { qed_vf_get_fw_version(&cdev->hwfns[0], &dev_info->fw_major, &dev_info->fw_minor, &dev_info->fw_rev, @@ -1433,6 +1437,30 @@ static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode) return status; } +static int qed_update_wol(struct qed_dev *cdev, bool enabled) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *ptt; + int rc = 0; + + if (IS_VF(cdev)) + return 0; + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) + return -EAGAIN; + + rc = qed_mcp_ov_update_wol(hwfn, ptt, enabled ? QED_OV_WOL_ENABLED + : QED_OV_WOL_DISABLED); + if (rc) + goto out; + rc = qed_mcp_ov_update_current_config(hwfn, ptt, QED_OV_CLIENT_DRV); + +out: + qed_ptt_release(hwfn, ptt); + return rc; +} + static int qed_update_drv_state(struct qed_dev *cdev, bool active) { struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); @@ -1541,6 +1569,7 @@ const struct qed_common_ops qed_common_ops_pass = { .update_drv_state = &qed_update_drv_state, .update_mac = &qed_update_mac, .update_mtu = &qed_update_mtu, + .update_wol = &qed_update_wol, }; void qed_get_protocol_stats(struct qed_dev *cdev, diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 8be6157..768b35b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -330,6 +330,7 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, struct qed_mcp_mb_params *p_mb_params) { u32 union_data_addr; + int rc; /* MCP not initialized */ @@ -375,11 +376,32 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, u32 *o_mcp_param) { struct qed_mcp_mb_params mb_params; + union drv_union_data data_src; int rc; memset(&mb_params, 0, sizeof(mb_params)); + memset(&data_src, 0, sizeof(data_src)); mb_params.cmd = cmd; mb_params.param = param; + + /* In case of UNLOAD_DONE, set the primary MAC */ + if ((cmd == DRV_MSG_CODE_UNLOAD_DONE) && + (p_hwfn->cdev->wol_config == QED_OV_WOL_ENABLED)) { + u8 *p_mac = p_hwfn->cdev->wol_mac; + + data_src.wol_mac.mac_upper = p_mac[0] << 8 | p_mac[1]; + data_src.wol_mac.mac_lower = p_mac[2] << 24 | p_mac[3] << 16 | + p_mac[4] << 8 | p_mac[5]; + + DP_VERBOSE(p_hwfn, + (QED_MSG_SP | NETIF_MSG_IFDOWN), + "Setting WoL MAC: %pM --> [%08x,%08x]\n", + p_mac, data_src.wol_mac.mac_upper, + data_src.wol_mac.mac_lower); + + mb_params.p_data_src = &data_src; + } + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) return rc; @@ -1058,6 +1080,9 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, info->mac[3] = (u8)(shmem_info.mac_lower >> 16); info->mac[4] = (u8)(shmem_info.mac_lower >> 8); info->mac[5] = (u8)(shmem_info.mac_lower); + + /* Store primary MAC for later possible WoL */ + memcpy(&p_hwfn->cdev->wol_mac, info->mac, ETH_ALEN); } else { DP_NOTICE(p_hwfn, "MAC is 0 in shmem\n"); } @@ -1071,13 +1096,28 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, info->mtu = (u16)shmem_info.mtu_size; + p_hwfn->hw_info.b_wol_support = QED_WOL_SUPPORT_NONE; + p_hwfn->cdev->wol_config = (u8)QED_OV_WOL_DEFAULT; + if (qed_mcp_is_init(p_hwfn)) { + u32 resp = 0, param = 0; + int rc; + + rc = qed_mcp_cmd(p_hwfn, p_ptt, + DRV_MSG_CODE_OS_WOL, 0, &resp, ¶m); + if (rc) + return rc; + if (resp == FW_MSG_CODE_OS_WOL_SUPPORTED) + p_hwfn->hw_info.b_wol_support = QED_WOL_SUPPORT_PME; + } + DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP), - "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x\n", + "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x wol %02x\n", info->pause_on_host, info->protocol, info->bandwidth_min, info->bandwidth_max, info->mac[0], info->mac[1], info->mac[2], info->mac[3], info->mac[4], info->mac[5], - info->wwn_port, info->wwn_node, info->ovlan); + info->wwn_port, info->wwn_node, + info->ovlan, (u8)p_hwfn->hw_info.b_wol_support); return 0; } @@ -1322,6 +1362,9 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, if (rc) DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc); + /* Store primary MAC for later possible WoL */ + memcpy(p_hwfn->cdev->wol_mac, mac, ETH_ALEN); + return rc; } @@ -1332,6 +1375,12 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn, u32 drv_mb_param; int rc; + if (p_hwfn->hw_info.b_wol_support == QED_WOL_SUPPORT_NONE) { + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "Can't change WoL configuration when WoL isn't supported\n"); + return -EINVAL; + } + switch (wol) { case QED_OV_WOL_DEFAULT: drv_mb_param = DRV_MB_PARAM_WOL_DEFAULT; @@ -1352,6 +1401,9 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn, if (rc) DP_ERR(p_hwfn, "Failed to send wol mode, rc = %d\n", rc); + /* Store the WoL update for a future unload */ + p_hwfn->cdev->wol_config = (u8)wol; + return rc; } diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index cf8d354..0cba21b 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -193,6 +193,8 @@ struct qede_dev { u16 vxlan_dst_port; u16 geneve_dst_port; + bool wol_enabled; + struct qede_rdma_dev rdma_info; }; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index a809408..327c614e 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -483,6 +483,45 @@ static void qede_get_drvinfo(struct net_device *ndev, strlcpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info)); } +static void qede_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct qede_dev *edev = netdev_priv(ndev); + + if (edev->dev_info.common.wol_support) { + wol->supported = WAKE_MAGIC; + wol->wolopts = edev->wol_enabled ? WAKE_MAGIC : 0; + } +} + +static int qede_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct qede_dev *edev = netdev_priv(ndev); + bool wol_requested; + int rc; + + if (wol->wolopts & ~WAKE_MAGIC) { + DP_INFO(edev, + "Can't support WoL options other than magic-packet\n"); + return -EINVAL; + } + + wol_requested = !!(wol->wolopts & WAKE_MAGIC); + if (wol_requested == edev->wol_enabled) + return 0; + + /* Need to actually change configuration */ + if (!edev->dev_info.common.wol_support) { + DP_INFO(edev, "Device doesn't support WoL\n"); + return -EINVAL; + } + + rc = edev->ops->common->update_wol(edev->cdev, wol_requested); + if (!rc) + edev->wol_enabled = wol_requested; + + return rc; +} + static u32 qede_get_msglevel(struct net_device *ndev) { struct qede_dev *edev = netdev_priv(ndev); @@ -1449,6 +1488,8 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_drvinfo = qede_get_drvinfo, .get_regs_len = qede_get_regs_len, .get_regs = qede_get_regs, + .get_wol = qede_get_wol, + .set_wol = qede_set_wol, .get_msglevel = qede_get_msglevel, .set_msglevel = qede_set_msglevel, .nway_reset = qede_nway_reset, diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index df0bd0c..873f2eb 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -95,6 +95,7 @@ static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id); #define TX_TIMEOUT (5 * HZ) static void qede_remove(struct pci_dev *pdev); +static void qede_shutdown(struct pci_dev *pdev); static int qede_alloc_rx_buffer(struct qede_dev *edev, struct qede_rx_queue *rxq); static void qede_link_update(void *dev, struct qed_link_output *link); @@ -166,6 +167,7 @@ static struct pci_driver qede_pci_driver = { .id_table = qede_pci_tbl, .probe = qede_probe, .remove = qede_remove, + .shutdown = qede_shutdown, #ifdef CONFIG_QED_SRIOV .sriov_configure = qede_sriov_configure, #endif @@ -2705,6 +2707,8 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) /* Use global ops since we've freed edev */ qed_ops->common->slowpath_stop(cdev); + if (system_state == SYSTEM_POWER_OFF) + return; qed_ops->common->remove(cdev); dev_info(&pdev->dev, "Ending qede_remove successfully\n"); @@ -2715,6 +2719,11 @@ static void qede_remove(struct pci_dev *pdev) __qede_remove(pdev, QEDE_REMOVE_NORMAL); } +static void qede_shutdown(struct pci_dev *pdev) +{ + __qede_remove(pdev, QEDE_REMOVE_NORMAL); +} + /* ------------------------------------------------------------------------- * START OF LOAD / UNLOAD * ------------------------------------------------------------------------- diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index ffc2d2f..ea095b4 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -268,6 +268,8 @@ struct qed_dev_info { bool tx_switching; bool rdma_supported; u16 mtu; + + bool wol_support; }; enum qed_sb_type { @@ -591,6 +593,14 @@ struct qed_common_ops { * */ int (*update_mtu)(struct qed_dev *cdev, u16 mtu); + +/** + * @brief update_wol - update of changes in the WoL configuration + * + * @param cdev + * @param enabled - true iff WoL should be enabled. + */ + int (*update_wol) (struct qed_dev *cdev, bool enabled); }; #define MASK_FIELD(_name, _value) \ -- cgit v1.1 From d7455f6e445043aa2fe4bb719a7786e0c3dc1cf1 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 31 Oct 2016 07:14:24 +0200 Subject: qede: Decouple ethtool caps from qed While the qed_lm_maps is closely tied with the QED_LM_* defines, when iterating over the array use actual size instead of the qed define to prevent future possible issues. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 327c614e..fe7e7b8 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -320,7 +320,7 @@ static const struct qede_link_mode_mapping qed_lm_map[] = { { \ int i; \ \ - for (i = 0; i < QED_LM_COUNT; i++) { \ + for (i = 0; i < ARRAY_SIZE(qed_lm_map); i++) { \ if ((caps) & (qed_lm_map[i].qed_link_mode)) \ __set_bit(qed_lm_map[i].ethtool_link_mode,\ lk_ksettings->link_modes.name); \ @@ -331,7 +331,7 @@ static const struct qede_link_mode_mapping qed_lm_map[] = { { \ int i; \ \ - for (i = 0; i < QED_LM_COUNT; i++) { \ + for (i = 0; i < ARRAY_SIZE(qed_lm_map); i++) { \ if (test_bit(qed_lm_map[i].ethtool_link_mode, \ lk_ksettings->link_modes.name)) \ caps |= qed_lm_map[i].qed_link_mode; \ -- cgit v1.1 From 6927e82680699f7999d68c648d50574a5e4a8f37 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 31 Oct 2016 07:14:25 +0200 Subject: qed: Learn of RDMA capabilities per-device Today, RDMA capabilities are learned from management firmware which provides a per-device indication for all interfaces. Newer management firmware is capable of providing a per-device indication [would later be extended to either RoCE/iWARP]. Try using this newer learning mechanism, but fallback in case management firmware is too old to retain current functionality. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 7 +++ drivers/net/ethernet/qlogic/qed/qed_mcp.c | 78 +++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index fdb7a09..1d113ce 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -8601,6 +8601,7 @@ struct public_drv_mb { #define DRV_MSG_CODE_BIST_TEST 0x001e0000 #define DRV_MSG_CODE_SET_LED_MODE 0x00200000 +#define DRV_MSG_CODE_GET_PF_RDMA_PROTOCOL 0x002b0000 #define DRV_MSG_CODE_OS_WOL 0x002e0000 #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff @@ -8705,6 +8706,12 @@ struct public_drv_mb { u32 fw_mb_param; + /* get pf rdma protocol command responce */ +#define FW_MB_PARAM_GET_PF_RDMA_NONE 0x0 +#define FW_MB_PARAM_GET_PF_RDMA_ROCE 0x1 +#define FW_MB_PARAM_GET_PF_RDMA_IWARP 0x2 +#define FW_MB_PARAM_GET_PF_RDMA_BOTH 0x3 + u32 drv_pulse_mb; #define DRV_PULSE_SEQ_MASK 0x00007fff #define DRV_PULSE_SYSTEM_TIME_MASK 0xffff0000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 768b35b..0927488 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1024,28 +1024,89 @@ int qed_mcp_get_media_type(struct qed_dev *cdev, u32 *p_media_type) return 0; } +/* Old MFW has a global configuration for all PFs regarding RDMA support */ +static void +qed_mcp_get_shmem_proto_legacy(struct qed_hwfn *p_hwfn, + enum qed_pci_personality *p_proto) +{ + /* There wasn't ever a legacy MFW that published iwarp. + * So at this point, this is either plain l2 or RoCE. + */ + if (test_bit(QED_DEV_CAP_ROCE, &p_hwfn->hw_info.device_capabilities)) + *p_proto = QED_PCI_ETH_ROCE; + else + *p_proto = QED_PCI_ETH; + + DP_VERBOSE(p_hwfn, NETIF_MSG_IFUP, + "According to Legacy capabilities, L2 personality is %08x\n", + (u32) *p_proto); +} + +static int +qed_mcp_get_shmem_proto_mfw(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_pci_personality *p_proto) +{ + u32 resp = 0, param = 0; + int rc; + + rc = qed_mcp_cmd(p_hwfn, p_ptt, + DRV_MSG_CODE_GET_PF_RDMA_PROTOCOL, 0, &resp, ¶m); + if (rc) + return rc; + if (resp != FW_MSG_CODE_OK) { + DP_VERBOSE(p_hwfn, NETIF_MSG_IFUP, + "MFW lacks support for command; Returns %08x\n", + resp); + return -EINVAL; + } + + switch (param) { + case FW_MB_PARAM_GET_PF_RDMA_NONE: + *p_proto = QED_PCI_ETH; + break; + case FW_MB_PARAM_GET_PF_RDMA_ROCE: + *p_proto = QED_PCI_ETH_ROCE; + break; + case FW_MB_PARAM_GET_PF_RDMA_BOTH: + DP_NOTICE(p_hwfn, + "Current day drivers don't support RoCE & iWARP. Default to RoCE-only\n"); + *p_proto = QED_PCI_ETH_ROCE; + break; + case FW_MB_PARAM_GET_PF_RDMA_IWARP: + default: + DP_NOTICE(p_hwfn, + "MFW answers GET_PF_RDMA_PROTOCOL but param is %08x\n", + param); + return -EINVAL; + } + + DP_VERBOSE(p_hwfn, + NETIF_MSG_IFUP, + "According to capabilities, L2 personality is %08x [resp %08x param %08x]\n", + (u32) *p_proto, resp, param); + return 0; +} + static int qed_mcp_get_shmem_proto(struct qed_hwfn *p_hwfn, struct public_func *p_info, + struct qed_ptt *p_ptt, enum qed_pci_personality *p_proto) { int rc = 0; switch (p_info->config & FUNC_MF_CFG_PROTOCOL_MASK) { case FUNC_MF_CFG_PROTOCOL_ETHERNET: - if (test_bit(QED_DEV_CAP_ROCE, - &p_hwfn->hw_info.device_capabilities)) - *p_proto = QED_PCI_ETH_ROCE; - else - *p_proto = QED_PCI_ETH; + if (qed_mcp_get_shmem_proto_mfw(p_hwfn, p_ptt, p_proto)) + qed_mcp_get_shmem_proto_legacy(p_hwfn, p_proto); break; case FUNC_MF_CFG_PROTOCOL_ISCSI: *p_proto = QED_PCI_ISCSI; break; case FUNC_MF_CFG_PROTOCOL_ROCE: DP_NOTICE(p_hwfn, "RoCE personality is not a valid value!\n"); - rc = -EINVAL; - break; + /* Fallthrough */ default: rc = -EINVAL; } @@ -1065,7 +1126,8 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, info->pause_on_host = (shmem_info.config & FUNC_MF_CFG_PAUSE_ON_HOST_RING) ? 1 : 0; - if (qed_mcp_get_shmem_proto(p_hwfn, &shmem_info, &info->protocol)) { + if (qed_mcp_get_shmem_proto(p_hwfn, &shmem_info, p_ptt, + &info->protocol)) { DP_ERR(p_hwfn, "Unknown personality %08x\n", (u32)(shmem_info.config & FUNC_MF_CFG_PROTOCOL_MASK)); return -EINVAL; -- cgit v1.1 From 5a1f965aac7acf2bf968fbf6a80567dbd1e389f1 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 31 Oct 2016 07:14:26 +0200 Subject: qed: Use VF-queue feature Driver sets several restrictions about the number of supported VFs according to available HW/FW resources. This creates a problem as there are constellations which can't be supported [as limitation don't accurately describe the resources], as well as holes where enabling IOV would fail due to supposed lack of resources. This introduces a new interal feature - vf-queues, which would be used to lift some of the restriction and accurately enumerate the queues that can be used by a given PF's VFs. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 20 ++++++++++++++---- drivers/net/ethernet/qlogic/qed/qed_int.c | 32 ++++++++++++++++++++++++++++- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 17 ++++++--------- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 8828ffa..6d3013f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -174,6 +174,7 @@ enum QED_FEATURE { QED_PF_L2_QUE, QED_VF, QED_RDMA_CNQ, + QED_VF_L2_QUE, QED_MAX_FEATURES, }; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 127ed5f..d996afe 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1476,6 +1476,7 @@ static void get_function_id(struct qed_hwfn *p_hwfn) static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) { u32 *feat_num = p_hwfn->hw_info.feat_num; + struct qed_sb_cnt_info sb_cnt_info; int num_features = 1; if (IS_ENABLED(CONFIG_QED_RDMA) && @@ -1494,10 +1495,21 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) feat_num[QED_PF_L2_QUE] = min_t(u32, RESC_NUM(p_hwfn, QED_SB) / num_features, RESC_NUM(p_hwfn, QED_L2_QUEUE)); - DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, - "#PF_L2_QUEUES=%d #SBS=%d num_features=%d\n", - feat_num[QED_PF_L2_QUE], RESC_NUM(p_hwfn, QED_SB), - num_features); + + memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); + qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); + feat_num[QED_VF_L2_QUE] = + min_t(u32, + RESC_NUM(p_hwfn, QED_L2_QUEUE) - + FEAT_NUM(p_hwfn, QED_PF_L2_QUE), sb_cnt_info.sb_iov_cnt); + + DP_VERBOSE(p_hwfn, + NETIF_MSG_PROBE, + "#PF_L2_QUEUES=%d VF_L2_QUEUES=%d #ROCE_CNQ=%d #SBS=%d num_features=%d\n", + (int)FEAT_NUM(p_hwfn, QED_PF_L2_QUE), + (int)FEAT_NUM(p_hwfn, QED_VF_L2_QUE), + (int)FEAT_NUM(p_hwfn, QED_RDMA_CNQ), + RESC_NUM(p_hwfn, QED_SB), num_features); } static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c index 2adedc6..bb74e1c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.c +++ b/drivers/net/ethernet/qlogic/qed/qed_int.c @@ -3030,6 +3030,31 @@ int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) } } } + + /* There's a possibility the igu_sb_cnt_iov doesn't properly reflect + * the number of VF SBs [especially for first VF on engine, as we can't + * diffrentiate between empty entries and its entries]. + * Since we don't really support more SBs than VFs today, prevent any + * such configuration by sanitizing the number of SBs to equal the + * number of VFs. + */ + if (IS_PF_SRIOV(p_hwfn)) { + u16 total_vfs = p_hwfn->cdev->p_iov_info->total_vfs; + + if (total_vfs < p_igu_info->free_blks) { + DP_VERBOSE(p_hwfn, + (NETIF_MSG_INTR | QED_MSG_IOV), + "Limiting number of SBs for IOV - %04x --> %04x\n", + p_igu_info->free_blks, + p_hwfn->cdev->p_iov_info->total_vfs); + p_igu_info->free_blks = total_vfs; + } else if (total_vfs > p_igu_info->free_blks) { + DP_NOTICE(p_hwfn, + "IGU has only %04x SBs for VFs while the device has %04x VFs\n", + p_igu_info->free_blks, total_vfs); + return -EINVAL; + } + } p_igu_info->igu_sb_cnt_iov = p_igu_info->free_blks; DP_VERBOSE( @@ -3163,7 +3188,12 @@ u16 qed_int_queue_id_from_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id) return sb_id - p_info->igu_base_sb; } else if ((sb_id >= p_info->igu_base_sb_iov) && (sb_id < p_info->igu_base_sb_iov + p_info->igu_sb_cnt_iov)) { - return sb_id - p_info->igu_base_sb_iov + p_info->igu_sb_cnt; + /* We want the first VF queue to be adjacent to the + * last PF queue. Since L2 queues can be partial to + * SBs, we'll use the feature instead. + */ + return sb_id - p_info->igu_base_sb_iov + + FEAT_NUM(p_hwfn, QED_PF_L2_QUE); } else { DP_NOTICE(p_hwfn, "SB %d not in range for function\n", sb_id); return 0; diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 6f029f9..f3f742a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3470,7 +3470,6 @@ int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled) static int qed_sriov_enable(struct qed_dev *cdev, int num) { - struct qed_sb_cnt_info sb_cnt_info; int i, j, rc; if (num >= RESC_NUM(&cdev->hwfns[0], QED_VPORT)) { @@ -3483,7 +3482,11 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num) for_each_hwfn(cdev, j) { struct qed_hwfn *hwfn = &cdev->hwfns[j]; struct qed_ptt *ptt = qed_ptt_acquire(hwfn); - int num_sbs = 0, limit = 16; + int num_queues; + + /* Make sure not to use more than 16 queues per VF */ + num_queues = min_t(int, + FEAT_NUM(hwfn, QED_VF_L2_QUE) / num, 16); if (!ptt) { DP_ERR(hwfn, "Failed to acquire ptt\n"); @@ -3491,19 +3494,11 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num) goto err; } - if (IS_MF_DEFAULT(hwfn)) - limit = MAX_NUM_VFS_BB / hwfn->num_funcs_on_engine; - - memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); - qed_int_get_num_sbs(hwfn, &sb_cnt_info); - num_sbs = min_t(int, sb_cnt_info.sb_free_blk, limit); - for (i = 0; i < num; i++) { if (!qed_iov_is_valid_vfid(hwfn, i, false, true)) continue; - rc = qed_iov_init_hw_for_vf(hwfn, - ptt, i, num_sbs / num); + rc = qed_iov_init_hw_for_vf(hwfn, ptt, i, num_queues); if (rc) { DP_ERR(cdev, "Failed to enable VF[%d]\n", i); qed_ptt_release(hwfn, ptt); -- cgit v1.1 From 2edbff8dcb5da324fd4c4fe953629e4f6ca73c99 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Mon, 31 Oct 2016 07:14:27 +0200 Subject: qed: Learn resources from management firmware Currently, each interfaces assumes it receives an equal portion of HW/FW resources, but this is wasteful - different partitions [and specifically, parititions exposing different protocol support] might require different resources. Implement a new resource learning scheme where the information is received directly from the management firmware [which has knowledge of all of the functions and can serve as arbiter]. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 6 +- drivers/net/ethernet/qlogic/qed/qed_dev.c | 291 ++++++++++++++++++++++++------ drivers/net/ethernet/qlogic/qed/qed_hsi.h | 46 +++++ drivers/net/ethernet/qlogic/qed/qed_l2.c | 2 +- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 42 +++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 15 ++ include/linux/qed/qed_eth_if.h | 2 +- 7 files changed, 341 insertions(+), 63 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 6d3013f..50b8a01 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -154,7 +154,10 @@ struct qed_qm_iids { u32 tids; }; -enum QED_RESOURCES { +/* HW / FW resources, output of features supported below, most information + * is received from MFW. + */ +enum qed_resources { QED_SB, QED_L2_QUEUE, QED_VPORT, @@ -166,6 +169,7 @@ enum QED_RESOURCES { QED_RDMA_CNQ_RAM, QED_ILT, QED_LL2_QUEUE, + QED_CMDQS_CQS, QED_RDMA_STATS_QUEUE, QED_MAX_RESC, }; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index d996afe..5be7b8a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1512,47 +1512,240 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) RESC_NUM(p_hwfn, QED_SB), num_features); } -static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) +static enum resource_id_enum qed_hw_get_mfw_res_id(enum qed_resources res_id) +{ + enum resource_id_enum mfw_res_id = RESOURCE_NUM_INVALID; + + switch (res_id) { + case QED_SB: + mfw_res_id = RESOURCE_NUM_SB_E; + break; + case QED_L2_QUEUE: + mfw_res_id = RESOURCE_NUM_L2_QUEUE_E; + break; + case QED_VPORT: + mfw_res_id = RESOURCE_NUM_VPORT_E; + break; + case QED_RSS_ENG: + mfw_res_id = RESOURCE_NUM_RSS_ENGINES_E; + break; + case QED_PQ: + mfw_res_id = RESOURCE_NUM_PQ_E; + break; + case QED_RL: + mfw_res_id = RESOURCE_NUM_RL_E; + break; + case QED_MAC: + case QED_VLAN: + /* Each VFC resource can accommodate both a MAC and a VLAN */ + mfw_res_id = RESOURCE_VFC_FILTER_E; + break; + case QED_ILT: + mfw_res_id = RESOURCE_ILT_E; + break; + case QED_LL2_QUEUE: + mfw_res_id = RESOURCE_LL2_QUEUE_E; + break; + case QED_RDMA_CNQ_RAM: + case QED_CMDQS_CQS: + /* CNQ/CMDQS are the same resource */ + mfw_res_id = RESOURCE_CQS_E; + break; + case QED_RDMA_STATS_QUEUE: + mfw_res_id = RESOURCE_RDMA_STATS_QUEUE_E; + break; + default: + break; + } + + return mfw_res_id; +} + +static u32 qed_hw_get_dflt_resc_num(struct qed_hwfn *p_hwfn, + enum qed_resources res_id) { - u8 enabled_func_idx = p_hwfn->enabled_func_idx; - u32 *resc_start = p_hwfn->hw_info.resc_start; u8 num_funcs = p_hwfn->num_funcs_on_engine; - u32 *resc_num = p_hwfn->hw_info.resc_num; struct qed_sb_cnt_info sb_cnt_info; - int i, max_vf_vlan_filters; + u32 dflt_resc_num = 0; - memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); + switch (res_id) { + case QED_SB: + memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); + qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); + dflt_resc_num = sb_cnt_info.sb_cnt; + break; + case QED_L2_QUEUE: + dflt_resc_num = MAX_NUM_L2_QUEUES_BB / num_funcs; + break; + case QED_VPORT: + dflt_resc_num = MAX_NUM_VPORTS_BB / num_funcs; + break; + case QED_RSS_ENG: + dflt_resc_num = ETH_RSS_ENGINE_NUM_BB / num_funcs; + break; + case QED_PQ: + /* The granularity of the PQs is 8 */ + dflt_resc_num = MAX_QM_TX_QUEUES_BB / num_funcs; + dflt_resc_num &= ~0x7; + break; + case QED_RL: + dflt_resc_num = MAX_QM_GLOBAL_RLS / num_funcs; + break; + case QED_MAC: + case QED_VLAN: + /* Each VFC resource can accommodate both a MAC and a VLAN */ + dflt_resc_num = ETH_NUM_MAC_FILTERS / num_funcs; + break; + case QED_ILT: + dflt_resc_num = PXP_NUM_ILT_RECORDS_BB / num_funcs; + break; + case QED_LL2_QUEUE: + dflt_resc_num = MAX_NUM_LL2_RX_QUEUES / num_funcs; + break; + case QED_RDMA_CNQ_RAM: + case QED_CMDQS_CQS: + /* CNQ/CMDQS are the same resource */ + dflt_resc_num = NUM_OF_CMDQS_CQS / num_funcs; + break; + case QED_RDMA_STATS_QUEUE: + dflt_resc_num = RDMA_NUM_STATISTIC_COUNTERS_BB / num_funcs; + break; + default: + break; + } -#ifdef CONFIG_QED_SRIOV - max_vf_vlan_filters = QED_ETH_MAX_VF_NUM_VLAN_FILTERS; -#else - max_vf_vlan_filters = 0; -#endif + return dflt_resc_num; +} + +static const char *qed_hw_get_resc_name(enum qed_resources res_id) +{ + switch (res_id) { + case QED_SB: + return "SB"; + case QED_L2_QUEUE: + return "L2_QUEUE"; + case QED_VPORT: + return "VPORT"; + case QED_RSS_ENG: + return "RSS_ENG"; + case QED_PQ: + return "PQ"; + case QED_RL: + return "RL"; + case QED_MAC: + return "MAC"; + case QED_VLAN: + return "VLAN"; + case QED_RDMA_CNQ_RAM: + return "RDMA_CNQ_RAM"; + case QED_ILT: + return "ILT"; + case QED_LL2_QUEUE: + return "LL2_QUEUE"; + case QED_CMDQS_CQS: + return "CMDQS_CQS"; + case QED_RDMA_STATS_QUEUE: + return "RDMA_STATS_QUEUE"; + default: + return "UNKNOWN_RESOURCE"; + } +} - qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); +static int qed_hw_set_resc_info(struct qed_hwfn *p_hwfn, + enum qed_resources res_id) +{ + u32 dflt_resc_num = 0, dflt_resc_start = 0, mcp_resp, mcp_param; + u32 *p_resc_num, *p_resc_start; + struct resource_info resc_info; + int rc; + + p_resc_num = &RESC_NUM(p_hwfn, res_id); + p_resc_start = &RESC_START(p_hwfn, res_id); + + /* Default values assumes that each function received equal share */ + dflt_resc_num = qed_hw_get_dflt_resc_num(p_hwfn, res_id); + if (!dflt_resc_num) { + DP_ERR(p_hwfn, + "Failed to get default amount for resource %d [%s]\n", + res_id, qed_hw_get_resc_name(res_id)); + return -EINVAL; + } + dflt_resc_start = dflt_resc_num * p_hwfn->enabled_func_idx; + + memset(&resc_info, 0, sizeof(resc_info)); + resc_info.res_id = qed_hw_get_mfw_res_id(res_id); + if (resc_info.res_id == RESOURCE_NUM_INVALID) { + DP_ERR(p_hwfn, + "Failed to match resource %d [%s] with the MFW resources\n", + res_id, qed_hw_get_resc_name(res_id)); + return -EINVAL; + } + + rc = qed_mcp_get_resc_info(p_hwfn, p_hwfn->p_main_ptt, &resc_info, + &mcp_resp, &mcp_param); + if (rc) { + DP_NOTICE(p_hwfn, + "MFW response failure for an allocation request for resource %d [%s]\n", + res_id, qed_hw_get_resc_name(res_id)); + return rc; + } + + /* Default driver values are applied in the following cases: + * - The resource allocation MB command is not supported by the MFW + * - There is an internal error in the MFW while processing the request + * - The resource ID is unknown to the MFW + */ + if (mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_OK && + mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_DEPRECATED) { + DP_NOTICE(p_hwfn, + "Resource %d [%s]: No allocation info was received [mcp_resp 0x%x]. Applying default values [num %d, start %d].\n", + res_id, + qed_hw_get_resc_name(res_id), + mcp_resp, dflt_resc_num, dflt_resc_start); + *p_resc_num = dflt_resc_num; + *p_resc_start = dflt_resc_start; + goto out; + } + + /* Special handling for status blocks; Would be revised in future */ + if (res_id == QED_SB) { + resc_info.size -= 1; + resc_info.offset -= p_hwfn->enabled_func_idx; + } + + *p_resc_num = resc_info.size; + *p_resc_start = resc_info.offset; + +out: + /* PQs have to divide by 8 [that's the HW granularity]. + * Reduce number so it would fit. + */ + if ((res_id == QED_PQ) && ((*p_resc_num % 8) || (*p_resc_start % 8))) { + DP_INFO(p_hwfn, + "PQs need to align by 8; Number %08x --> %08x, Start %08x --> %08x\n", + *p_resc_num, + (*p_resc_num) & ~0x7, + *p_resc_start, (*p_resc_start) & ~0x7); + *p_resc_num &= ~0x7; + *p_resc_start &= ~0x7; + } - resc_num[QED_SB] = min_t(u32, - (MAX_SB_PER_PATH_BB / num_funcs), - sb_cnt_info.sb_cnt); - resc_num[QED_L2_QUEUE] = MAX_NUM_L2_QUEUES_BB / num_funcs; - resc_num[QED_VPORT] = MAX_NUM_VPORTS_BB / num_funcs; - resc_num[QED_RSS_ENG] = ETH_RSS_ENGINE_NUM_BB / num_funcs; - resc_num[QED_PQ] = MAX_QM_TX_QUEUES_BB / num_funcs; - resc_num[QED_RL] = min_t(u32, 64, resc_num[QED_VPORT]); - resc_num[QED_MAC] = ETH_NUM_MAC_FILTERS / num_funcs; - resc_num[QED_VLAN] = (ETH_NUM_VLAN_FILTERS - 1 /*For vlan0*/) / - num_funcs; - resc_num[QED_ILT] = PXP_NUM_ILT_RECORDS_BB / num_funcs; - resc_num[QED_LL2_QUEUE] = MAX_NUM_LL2_RX_QUEUES / num_funcs; - resc_num[QED_RDMA_CNQ_RAM] = NUM_OF_CMDQS_CQS / num_funcs; - resc_num[QED_RDMA_STATS_QUEUE] = RDMA_NUM_STATISTIC_COUNTERS_BB / - num_funcs; - - for (i = 0; i < QED_MAX_RESC; i++) - resc_start[i] = resc_num[i] * enabled_func_idx; + return 0; +} + +static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) +{ + u8 res_id; + int rc; + + for (res_id = 0; res_id < QED_MAX_RESC; res_id++) { + rc = qed_hw_set_resc_info(p_hwfn, res_id); + if (rc) + return rc; + } /* Sanity for ILT */ - if (RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_BB) { + if ((RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_BB)) { DP_NOTICE(p_hwfn, "Can't assign ILT pages [%08x,...,%08x]\n", RESC_START(p_hwfn, QED_ILT), RESC_END(p_hwfn, QED_ILT) - 1); @@ -1562,34 +1755,12 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) qed_hw_set_feat(p_hwfn); DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, - "The numbers for each resource are:\n" - "SB = %d start = %d\n" - "L2_QUEUE = %d start = %d\n" - "VPORT = %d start = %d\n" - "PQ = %d start = %d\n" - "RL = %d start = %d\n" - "MAC = %d start = %d\n" - "VLAN = %d start = %d\n" - "ILT = %d start = %d\n" - "LL2_QUEUE = %d start = %d\n", - p_hwfn->hw_info.resc_num[QED_SB], - p_hwfn->hw_info.resc_start[QED_SB], - p_hwfn->hw_info.resc_num[QED_L2_QUEUE], - p_hwfn->hw_info.resc_start[QED_L2_QUEUE], - p_hwfn->hw_info.resc_num[QED_VPORT], - p_hwfn->hw_info.resc_start[QED_VPORT], - p_hwfn->hw_info.resc_num[QED_PQ], - p_hwfn->hw_info.resc_start[QED_PQ], - p_hwfn->hw_info.resc_num[QED_RL], - p_hwfn->hw_info.resc_start[QED_RL], - p_hwfn->hw_info.resc_num[QED_MAC], - p_hwfn->hw_info.resc_start[QED_MAC], - p_hwfn->hw_info.resc_num[QED_VLAN], - p_hwfn->hw_info.resc_start[QED_VLAN], - p_hwfn->hw_info.resc_num[QED_ILT], - p_hwfn->hw_info.resc_start[QED_ILT], - RESC_NUM(p_hwfn, QED_LL2_QUEUE), - RESC_START(p_hwfn, QED_LL2_QUEUE)); + "The numbers for each resource are:\n"); + for (res_id = 0; res_id < QED_MAX_RESC; res_id++) + DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, "%s = %d start = %d\n", + qed_hw_get_resc_name(res_id), + RESC_NUM(p_hwfn, res_id), + RESC_START(p_hwfn, res_id)); return 0; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 1d113ce..048f9a3 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -8529,6 +8529,41 @@ struct mdump_config_stc { u32 valid_logs; }; +enum resource_id_enum { + RESOURCE_NUM_SB_E = 0, + RESOURCE_NUM_L2_QUEUE_E = 1, + RESOURCE_NUM_VPORT_E = 2, + RESOURCE_NUM_VMQ_E = 3, + RESOURCE_FACTOR_NUM_RSS_PF_E = 4, + RESOURCE_FACTOR_RSS_PER_VF_E = 5, + RESOURCE_NUM_RL_E = 6, + RESOURCE_NUM_PQ_E = 7, + RESOURCE_NUM_VF_E = 8, + RESOURCE_VFC_FILTER_E = 9, + RESOURCE_ILT_E = 10, + RESOURCE_CQS_E = 11, + RESOURCE_GFT_PROFILES_E = 12, + RESOURCE_NUM_TC_E = 13, + RESOURCE_NUM_RSS_ENGINES_E = 14, + RESOURCE_LL2_QUEUE_E = 15, + RESOURCE_RDMA_STATS_QUEUE_E = 16, + RESOURCE_MAX_NUM, + RESOURCE_NUM_INVALID = 0xFFFFFFFF +}; + +/* Resource ID is to be filled by the driver in the MB request + * Size, offset & flags to be filled by the MFW in the MB response + */ +struct resource_info { + enum resource_id_enum res_id; + u32 size; /* number of allocated resources */ + u32 offset; /* Offset of the 1st resource */ + u32 vf_size; + u32 vf_offset; + u32 flags; +#define RESOURCE_ELEMENT_STRICT (1 << 0) +}; + union drv_union_data { u32 ver_str[MCP_DRV_VER_STR_SIZE_DWORD]; struct mcp_mac wol_mac; @@ -8549,6 +8584,7 @@ union drv_union_data { u64 reserved_stats[11]; struct ocbb_data_stc ocbb_info; struct temperature_status_stc temp_info; + struct resource_info resource; struct bist_nvm_image_att nvm_image_att; struct mdump_config_stc mdump_config; }; @@ -8576,6 +8612,7 @@ struct public_drv_mb { #define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000 #define DRV_MSG_CODE_NIG_DRAIN 0x30000000 +#define DRV_MSG_GET_RESOURCE_ALLOC_MSG 0x34000000 #define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000 #define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000 #define DRV_MSG_CODE_NVM_GET_FILE_ATT 0x00030000 @@ -8666,6 +8703,12 @@ struct public_drv_mb { #define DRV_MB_PARAM_SET_LED_MODE_ON 0x1 #define DRV_MB_PARAM_SET_LED_MODE_OFF 0x2 + /* Resource Allocation params - Driver version support */ +#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_MASK 0xFFFF0000 +#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_SHIFT 16 +#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_MASK 0x0000FFFF +#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_SHIFT 0 + #define DRV_MB_PARAM_BIST_REGISTER_TEST 1 #define DRV_MB_PARAM_BIST_CLOCK_TEST 2 #define DRV_MB_PARAM_BIST_NVM_TEST_NUM_IMAGES 3 @@ -8694,6 +8737,9 @@ struct public_drv_mb { #define FW_MSG_CODE_DRV_UNLOAD_PORT 0x20120000 #define FW_MSG_CODE_DRV_UNLOAD_FUNCTION 0x20130000 #define FW_MSG_CODE_DRV_UNLOAD_DONE 0x21100000 +#define FW_MSG_CODE_RESOURCE_ALLOC_OK 0x34000000 +#define FW_MSG_CODE_RESOURCE_ALLOC_UNKNOWN 0x35000000 +#define FW_MSG_CODE_RESOURCE_ALLOC_DEPRECATED 0x36000000 #define FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE 0xb0010000 #define FW_MSG_CODE_NVM_OK 0x00010000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 6b0e22d..1e3a16e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1691,7 +1691,7 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev, } qed_vf_get_num_vlan_filters(&cdev->hwfns[0], - &info->num_vlan_filters); + (u8 *)&info->num_vlan_filters); qed_vf_get_port_mac(&cdev->hwfns[0], info->port_mac); info->is_legacy = !!cdev->hwfns[0].vf_iov_info->b_pre_fp_hsi; diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 0927488..d8e499e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1683,3 +1683,45 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, return rc; } + +#define QED_RESC_ALLOC_VERSION_MAJOR 1 +#define QED_RESC_ALLOC_VERSION_MINOR 0 +#define QED_RESC_ALLOC_VERSION \ + ((QED_RESC_ALLOC_VERSION_MAJOR << \ + DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_SHIFT) | \ + (QED_RESC_ALLOC_VERSION_MINOR << \ + DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_SHIFT)) +int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct resource_info *p_resc_info, + u32 *p_mcp_resp, u32 *p_mcp_param) +{ + struct qed_mcp_mb_params mb_params; + union drv_union_data *p_union_data; + int rc; + + memset(&mb_params, 0, sizeof(mb_params)); + mb_params.cmd = DRV_MSG_GET_RESOURCE_ALLOC_MSG; + mb_params.param = QED_RESC_ALLOC_VERSION; + p_union_data = (union drv_union_data *)p_resc_info; + mb_params.p_data_src = p_union_data; + mb_params.p_data_dst = p_union_data; + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); + if (rc) + return rc; + + *p_mcp_resp = mb_params.mcp_resp; + *p_mcp_param = mb_params.mcp_param; + + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "MFW resource_info: version 0x%x, res_id 0x%x, size 0x%x, offset 0x%x, vf_size 0x%x, vf_offset 0x%x, flags 0x%x\n", + *p_mcp_param, + p_resc_info->res_id, + p_resc_info->size, + p_resc_info->offset, + p_resc_info->vf_size, + p_resc_info->vf_offset, p_resc_info->flags); + + return 0; +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index be8152d..407a2c1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -689,4 +689,19 @@ int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, enum qed_ov_eswitch eswitch); +/** + * @brief - Gets the MFW allocation info for the given resource + * + * @param p_hwfn + * @param p_ptt + * @param p_resc_info - descriptor of requested resource + * @param p_mcp_resp + * @param p_mcp_param + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct resource_info *p_resc_info, + u32 *p_mcp_resp, u32 *p_mcp_param); #endif diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 1513080..9755a3f 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -22,7 +22,7 @@ struct qed_dev_eth_info { u8 num_tc; u8 port_mac[ETH_ALEN]; - u8 num_vlan_filters; + u16 num_vlan_filters; u16 num_mac_filters; /* Legacy VF - this affects the datapath, so qede has to know */ -- cgit v1.1 From 556d299fcb4af8f2e8eacf311c4eee352c746788 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 31 Oct 2016 13:21:02 +0100 Subject: net: pim: add common pimhdr struct and helpers Add the common pimhdr structure and helpers to access it, also cleanup the format of the header file. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/pim.h | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/include/linux/pim.h b/include/linux/pim.h index e1d756f..354235a 100644 --- a/include/linux/pim.h +++ b/include/linux/pim.h @@ -1,6 +1,7 @@ #ifndef __LINUX_PIM_H #define __LINUX_PIM_H +#include #include /* Message types - V1 */ @@ -13,20 +14,47 @@ #define PIM_NULL_REGISTER cpu_to_be32(0x40000000) -static inline bool ipmr_pimsm_enabled(void) -{ - return IS_BUILTIN(CONFIG_IP_PIMSM_V1) || IS_BUILTIN(CONFIG_IP_PIMSM_V2); -} +/* RFC7761, sec 4.9: + * The PIM header common to all PIM messages is: + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |PIM Ver| Type | Reserved | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct pimhdr { + __u8 type; + __u8 reserved; + __be16 csum; +}; /* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */ -struct pimreghdr -{ +struct pimreghdr { __u8 type; __u8 reserved; __be16 csum; __be32 flags; }; -struct sk_buff; -extern int pim_rcv_v1(struct sk_buff *); +int pim_rcv_v1(struct sk_buff *skb); + +static inline bool ipmr_pimsm_enabled(void) +{ + return IS_BUILTIN(CONFIG_IP_PIMSM_V1) || IS_BUILTIN(CONFIG_IP_PIMSM_V2); +} + +static inline struct pimhdr *pim_hdr(const struct sk_buff *skb) +{ + return (struct pimhdr *)skb_transport_header(skb); +} + +static inline u8 pim_hdr_version(const struct pimhdr *pimhdr) +{ + return pimhdr->type >> 4; +} + +static inline u8 pim_hdr_type(const struct pimhdr *pimhdr) +{ + return pimhdr->type & 0xf; +} #endif -- cgit v1.1 From 20bb6ce9879e19eee7539329eaa2408d12b00306 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 31 Oct 2016 13:21:03 +0100 Subject: net: pim: add a helper to check for IPv4 all pim routers address Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/pim.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/pim.h b/include/linux/pim.h index 354235a..1b6c0db 100644 --- a/include/linux/pim.h +++ b/include/linux/pim.h @@ -57,4 +57,10 @@ static inline u8 pim_hdr_type(const struct pimhdr *pimhdr) { return pimhdr->type & 0xf; } + +/* check if the address is 224.0.0.13, RFC7761 sec 4.3.1 */ +static inline bool pim_ipv4_all_pim_routers(__be32 addr) +{ + return addr == htonl(0xE000000D); +} #endif -- cgit v1.1 From 56245cae19f5ccb371fa63b09bb6b9ce7c0f1266 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 31 Oct 2016 13:21:04 +0100 Subject: net: pim: add all RFC7761 message types Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/pim.h | 31 ++++++++++++++++++++++++++++++- net/ipv4/ipmr.c | 2 +- net/ipv6/ip6mr.c | 2 +- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/include/linux/pim.h b/include/linux/pim.h index 1b6c0db..0e81b27 100644 --- a/include/linux/pim.h +++ b/include/linux/pim.h @@ -10,7 +10,36 @@ /* Message types - V2 */ #define PIM_VERSION 2 -#define PIM_REGISTER 1 + +/* RFC7761, sec 4.9: + * Type + * Types for specific PIM messages. PIM Types are: + * + * Message Type Destination + * --------------------------------------------------------------------- + * 0 = Hello Multicast to ALL-PIM-ROUTERS + * 1 = Register Unicast to RP + * 2 = Register-Stop Unicast to source of Register + * packet + * 3 = Join/Prune Multicast to ALL-PIM-ROUTERS + * 4 = Bootstrap Multicast to ALL-PIM-ROUTERS + * 5 = Assert Multicast to ALL-PIM-ROUTERS + * 6 = Graft (used in PIM-DM only) Unicast to RPF'(S) + * 7 = Graft-Ack (used in PIM-DM only) Unicast to source of Graft + * packet + * 8 = Candidate-RP-Advertisement Unicast to Domain's BSR + */ +enum { + PIM_TYPE_HELLO, + PIM_TYPE_REGISTER, + PIM_TYPE_REGISTER_STOP, + PIM_TYPE_JOIN_PRUNE, + PIM_TYPE_BOOTSTRAP, + PIM_TYPE_ASSERT, + PIM_TYPE_GRAFT, + PIM_TYPE_GRAFT_ACK, + PIM_TYPE_CANDIDATE_RP_ADV +}; #define PIM_NULL_REGISTER cpu_to_be32(0x40000000) diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 5f006e1..51d71a7 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2053,7 +2053,7 @@ static int pim_rcv(struct sk_buff *skb) goto drop; pim = (struct pimreghdr *)skb_transport_header(skb); - if (pim->type != ((PIM_VERSION << 4) | (PIM_REGISTER)) || + if (pim->type != ((PIM_VERSION << 4) | (PIM_TYPE_REGISTER)) || (pim->flags & PIM_NULL_REGISTER) || (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && csum_fold(skb_checksum(skb, 0, skb->len, 0)))) diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 7f4265b..52101b3 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -636,7 +636,7 @@ static int pim6_rcv(struct sk_buff *skb) goto drop; pim = (struct pimreghdr *)skb_transport_header(skb); - if (pim->type != ((PIM_VERSION << 4) | PIM_REGISTER) || + if (pim->type != ((PIM_VERSION << 4) | PIM_TYPE_REGISTER) || (pim->flags & PIM_NULL_REGISTER) || (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, sizeof(*pim), IPPROTO_PIM, -- cgit v1.1 From 91b02d3d133bbb101466325cdd9a6007cd9ec5a8 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 31 Oct 2016 13:21:05 +0100 Subject: bridge: mcast: add router port on PIM hello message When we receive a PIM Hello message on a port we can consider that it has a multicast router attached, thus it is correct to add it to the router list. The only catch is it shouldn't be considered for a querier. Using Daniel's description: leaf-11 leaf-12 leaf-13 \ | / bridge-1 / \ host-11 host-12 - all ports in bridge-1 are in a single vlan aware bridge - leaf-11 is the IGMP querier - leaf-13 is the PIM DR - host-11 TXes packets to 226.10.10.10 - bridge-1 only forwards the 226.10.10.10 traffic out the port to leaf-11, it should also forward this traffic out the port to leaf-13 Suggested-by: Daniel Walton Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- net/bridge/br_multicast.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 2136e45..073d54a 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_IPV6) #include @@ -1638,6 +1639,21 @@ static void br_multicast_err_count(const struct net_bridge *br, u64_stats_update_end(&pstats->syncp); } +static void br_multicast_pim(struct net_bridge *br, + struct net_bridge_port *port, + const struct sk_buff *skb) +{ + unsigned int offset = skb_transport_offset(skb); + struct pimhdr *pimhdr, _pimhdr; + + pimhdr = skb_header_pointer(skb, offset, sizeof(_pimhdr), &_pimhdr); + if (!pimhdr || pim_hdr_version(pimhdr) != PIM_VERSION || + pim_hdr_type(pimhdr) != PIM_TYPE_HELLO) + return; + + br_multicast_mark_router(br, port); +} + static int br_multicast_ipv4_rcv(struct net_bridge *br, struct net_bridge_port *port, struct sk_buff *skb, @@ -1650,8 +1666,12 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, err = ip_mc_check_igmp(skb, &skb_trimmed); if (err == -ENOMSG) { - if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) + if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) { BR_INPUT_SKB_CB(skb)->mrouters_only = 1; + } else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) { + if (ip_hdr(skb)->protocol == IPPROTO_PIM) + br_multicast_pim(br, port, skb); + } return 0; } else if (err < 0) { br_multicast_err_count(br, port, skb->protocol); -- cgit v1.1 From ff00f3a9670f97203b33ac00eeca81d75768919e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 4 Oct 2016 12:20:16 +0100 Subject: i40e: Add missing \n to end of dev_err message Trival fix, dev_err message is missing a \n, so add it. Signed-off-by: Colin Ian King Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 54b8ee2..64e54a5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2940,7 +2940,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, } if (max_tx_rate > speed) { - dev_err(&pf->pdev->dev, "Invalid max tx rate %d specified for VF %d.", + dev_err(&pf->pdev->dev, "Invalid max tx rate %d specified for VF %d.\n", max_tx_rate, vf->vf_id); ret = -EINVAL; goto error; -- cgit v1.1 From 1bc87e807a6510eb33402258ec1df9bf22b6ff1f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:31 -0700 Subject: i40e: drop is_vf and is_netdev fields in struct i40e_mac_filter Originally the is_vf and is_netdev fields were added in order to distinguish between VF and netdev filters in a single VSI. However, it can be noted that we use separate VSI for SRIOV VFs and for netdev VSI. Thus, since a single VSI should only ever have one type of filter, we can simply remove the checks and remove the typing. In a similar fashion, we can note that the only remaining way to get multiple filters of a single type is through a debug command that was added to debugfs. This command is useless in practice, and results in causing bugs if we keep counter tracking but lose the is_vf and is_netdev protections as desired above. Since the only time we'd actually have a counter value besides 0 and 1 is through use of this debugfs hook, we can remove this unnecessary command, and the entire counter logic it required. We vastly simplify mac filters by removing (a) the distinction between VF and netdev filters (b) counting logic (c) the ability to add and remove filters bypassing the stack via debugfs Change-ID: Idf916dd2a1159b1188ddbab5bef6b85ea6bf27d9 Signed-off-by: Jacob Keller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 19 +- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 88 +------- drivers/net/ethernet/intel/i40e/i40e_fcoe.c | 8 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 227 ++++++--------------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 19 +- 5 files changed, 89 insertions(+), 272 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 5a6f851..8445591 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -458,9 +458,6 @@ struct i40e_mac_filter { u8 macaddr[ETH_ALEN]; #define I40E_VLAN_ANY -1 s16 vlan; - u8 counter; /* number of instances of this filter */ - bool is_vf; /* filter belongs to a VF */ - bool is_netdev; /* filter belongs to a netdev */ enum i40e_filter_state state; }; @@ -723,10 +720,8 @@ u32 i40e_get_global_fd_count(struct i40e_pf *pf); bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features); void i40e_set_ethtool_ops(struct net_device *netdev); struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, - u8 *macaddr, s16 vlan, - bool is_vf, bool is_netdev); -void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan, - bool is_vf, bool is_netdev); + u8 *macaddr, s16 vlan); +void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan); int i40e_sync_vsi_filters(struct i40e_vsi *vsi); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); @@ -817,13 +812,11 @@ int i40e_vsi_open(struct i40e_vsi *vsi); void i40e_vlan_stripping_disable(struct i40e_vsi *vsi); int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid); int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid); -struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr, - bool is_vf, bool is_netdev); -int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, - bool is_vf, bool is_netdev); +struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, + u8 *macaddr); +int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr); bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi); -struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr, - bool is_vf, bool is_netdev); +struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr); #ifdef I40E_FCOE int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, struct tc_to_netdev *tc); diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 0354632..8f6ed8d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -168,9 +168,9 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) pf->hw.mac.port_addr); list_for_each_entry(f, &vsi->mac_filter_list, list) { dev_info(&pf->pdev->dev, - " mac_filter_list: %pM vid=%d, is_netdev=%d is_vf=%d counter=%d, state %s\n", - f->macaddr, f->vlan, f->is_netdev, f->is_vf, - f->counter, i40e_filter_state_string[f->state]); + " mac_filter_hash: %pM vid=%d, state %s\n", + f->macaddr, f->vlan, + i40e_filter_state_string[f->state]); } dev_info(&pf->pdev->dev, " active_filters %d, promisc_threshold %d, overflow promisc %s\n", vsi->active_filters, vsi->promisc_threshold, @@ -867,86 +867,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, "deleting relay %d\n", veb_seid); i40e_veb_release(pf->veb[i]); - - } else if (strncmp(cmd_buf, "add macaddr", 11) == 0) { - struct i40e_mac_filter *f; - int vlan = 0; - u8 ma[6]; - int ret; - - cnt = sscanf(&cmd_buf[11], - "%i %hhx:%hhx:%hhx:%hhx:%hhx:%hhx %i", - &vsi_seid, - &ma[0], &ma[1], &ma[2], &ma[3], &ma[4], &ma[5], - &vlan); - if (cnt == 7) { - vlan = 0; - } else if (cnt != 8) { - dev_info(&pf->pdev->dev, - "add macaddr: bad command string, cnt=%d\n", - cnt); - goto command_write_done; - } - - vsi = i40e_dbg_find_vsi(pf, vsi_seid); - if (!vsi) { - dev_info(&pf->pdev->dev, - "add macaddr: VSI %d not found\n", vsi_seid); - goto command_write_done; - } - - spin_lock_bh(&vsi->mac_filter_list_lock); - f = i40e_add_filter(vsi, ma, vlan, false, false); - spin_unlock_bh(&vsi->mac_filter_list_lock); - ret = i40e_sync_vsi_filters(vsi); - if (f && !ret) - dev_info(&pf->pdev->dev, - "add macaddr: %pM vlan=%d added to VSI %d\n", - ma, vlan, vsi_seid); - else - dev_info(&pf->pdev->dev, - "add macaddr: %pM vlan=%d to VSI %d failed, f=%p ret=%d\n", - ma, vlan, vsi_seid, f, ret); - - } else if (strncmp(cmd_buf, "del macaddr", 11) == 0) { - int vlan = 0; - u8 ma[6]; - int ret; - - cnt = sscanf(&cmd_buf[11], - "%i %hhx:%hhx:%hhx:%hhx:%hhx:%hhx %i", - &vsi_seid, - &ma[0], &ma[1], &ma[2], &ma[3], &ma[4], &ma[5], - &vlan); - if (cnt == 7) { - vlan = 0; - } else if (cnt != 8) { - dev_info(&pf->pdev->dev, - "del macaddr: bad command string, cnt=%d\n", - cnt); - goto command_write_done; - } - - vsi = i40e_dbg_find_vsi(pf, vsi_seid); - if (!vsi) { - dev_info(&pf->pdev->dev, - "del macaddr: VSI %d not found\n", vsi_seid); - goto command_write_done; - } - - spin_lock_bh(&vsi->mac_filter_list_lock); - i40e_del_filter(vsi, ma, vlan, false, false); - spin_unlock_bh(&vsi->mac_filter_list_lock); - ret = i40e_sync_vsi_filters(vsi); - if (!ret) - dev_info(&pf->pdev->dev, - "del macaddr: %pM vlan=%d removed from VSI %d\n", - ma, vlan, vsi_seid); - else - dev_info(&pf->pdev->dev, - "del macaddr: %pM vlan=%d from VSI %d failed, ret=%d\n", - ma, vlan, vsi_seid, ret); - } else if (strncmp(cmd_buf, "add pvid", 8) == 0) { i40e_status ret; u16 vid; @@ -1615,8 +1535,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " del vsi [vsi_seid]\n"); dev_info(&pf->pdev->dev, " add relay \n"); dev_info(&pf->pdev->dev, " del relay \n"); - dev_info(&pf->pdev->dev, " add macaddr [vlan]\n"); - dev_info(&pf->pdev->dev, " del macaddr [vlan]\n"); dev_info(&pf->pdev->dev, " add pvid \n"); dev_info(&pf->pdev->dev, " del pvid \n"); dev_info(&pf->pdev->dev, " dump switch\n"); diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index 58e6c15..0c2a00d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -1523,10 +1523,10 @@ void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi) */ netdev->dev_port = 1; spin_lock_bh(&vsi->mac_filter_list_lock); - i40e_add_filter(vsi, hw->mac.san_addr, 0, false, false); - i40e_add_filter(vsi, (u8[6]) FC_FCOE_FLOGI_MAC, 0, false, false); - i40e_add_filter(vsi, FIP_ALL_FCOE_MACS, 0, false, false); - i40e_add_filter(vsi, FIP_ALL_ENODE_MACS, 0, false, false); + i40e_add_filter(vsi, hw->mac.san_addr, 0); + i40e_add_filter(vsi, (u8[6]) FC_FCOE_FLOGI_MAC, 0); + i40e_add_filter(vsi, FIP_ALL_FCOE_MACS, 0); + i40e_add_filter(vsi, FIP_ALL_ENODE_MACS, 0); spin_unlock_bh(&vsi->mac_filter_list_lock); /* use san mac */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index d78a4dc..ed49113 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1145,14 +1145,11 @@ void i40e_update_stats(struct i40e_vsi *vsi) * @vsi: the VSI to be searched * @macaddr: the MAC address * @vlan: the vlan - * @is_vf: make sure its a VF filter, else doesn't matter - * @is_netdev: make sure its a netdev filter, else doesn't matter * * Returns ptr to the filter object or NULL **/ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, - u8 *macaddr, s16 vlan, - bool is_vf, bool is_netdev) + u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; @@ -1161,9 +1158,7 @@ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, list_for_each_entry(f, &vsi->mac_filter_list, list) { if ((ether_addr_equal(macaddr, f->macaddr)) && - (vlan == f->vlan) && - (!is_vf || f->is_vf) && - (!is_netdev || f->is_netdev)) + (vlan == f->vlan)) return f; } return NULL; @@ -1173,14 +1168,11 @@ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, * i40e_find_mac - Find a mac addr in the macvlan filters list * @vsi: the VSI to be searched * @macaddr: the MAC address we are searching for - * @is_vf: make sure its a VF filter, else doesn't matter - * @is_netdev: make sure its a netdev filter, else doesn't matter * * Returns the first filter with the provided MAC address or NULL if * MAC address was not found **/ -struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr, - bool is_vf, bool is_netdev) +struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr) { struct i40e_mac_filter *f; @@ -1188,9 +1180,7 @@ struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr, return NULL; list_for_each_entry(f, &vsi->mac_filter_list, list) { - if ((ether_addr_equal(macaddr, f->macaddr)) && - (!is_vf || f->is_vf) && - (!is_netdev || f->is_netdev)) + if ((ether_addr_equal(macaddr, f->macaddr))) return f; } return NULL; @@ -1221,26 +1211,21 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) * i40e_put_mac_in_vlan - Make macvlan filters from macaddrs and vlans * @vsi: the VSI to be searched * @macaddr: the mac address to be filtered - * @is_vf: true if it is a VF - * @is_netdev: true if it is a netdev * * Goes through all the macvlan filters and adds a * macvlan filter for each unique vlan that already exists * * Returns first filter found on success, else NULL **/ -struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr, - bool is_vf, bool is_netdev) +struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr) { struct i40e_mac_filter *f; list_for_each_entry(f, &vsi->mac_filter_list, list) { if (vsi->info.pvid) f->vlan = le16_to_cpu(vsi->info.pvid); - if (!i40e_find_filter(vsi, macaddr, f->vlan, - is_vf, is_netdev)) { - if (!i40e_add_filter(vsi, macaddr, f->vlan, - is_vf, is_netdev)) + if (!i40e_find_filter(vsi, macaddr, f->vlan)) { + if (!i40e_add_filter(vsi, macaddr, f->vlan)) return NULL; } } @@ -1253,15 +1238,12 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr, * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS * @vsi: the VSI to be searched * @macaddr: the mac address to be removed - * @is_vf: true if it is a VF - * @is_netdev: true if it is a netdev * * Removes a given MAC address from a VSI, regardless of VLAN * * Returns 0 for success, or error **/ -int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, - bool is_vf, bool is_netdev) +int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr) { struct i40e_mac_filter *f = NULL; int changed = 0; @@ -1269,13 +1251,8 @@ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, WARN(!spin_is_locked(&vsi->mac_filter_list_lock), "Missing mac_filter_list_lock\n"); list_for_each_entry(f, &vsi->mac_filter_list, list) { - if ((ether_addr_equal(macaddr, f->macaddr)) && - (is_vf == f->is_vf) && - (is_netdev == f->is_netdev)) { - f->counter--; - changed = 1; - if (f->counter == 0) - f->state = I40E_FILTER_REMOVE; + if ((ether_addr_equal(macaddr, f->macaddr))) { + f->state = I40E_FILTER_REMOVE; } } if (changed) { @@ -1291,8 +1268,6 @@ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, * @vsi: the VSI to be searched * @macaddr: the MAC address * @vlan: the vlan - * @is_vf: make sure its a VF filter, else doesn't matter - * @is_netdev: make sure its a netdev filter, else doesn't matter * * Returns ptr to the filter object or NULL when no memory available. * @@ -1300,11 +1275,9 @@ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, * being held. **/ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, - u8 *macaddr, s16 vlan, - bool is_vf, bool is_netdev) + u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; - int changed = false; if (!vsi || !macaddr) return NULL; @@ -1316,11 +1289,11 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, if (is_broadcast_ether_addr(macaddr)) return NULL; - f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev); + f = i40e_find_filter(vsi, macaddr, vlan); if (!f) { f = kzalloc(sizeof(*f), GFP_ATOMIC); if (!f) - goto add_filter_out; + return NULL; ether_addr_copy(f->macaddr, macaddr); f->vlan = vlan; @@ -1332,32 +1305,24 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, f->state = I40E_FILTER_FAILED; else f->state = I40E_FILTER_NEW; - changed = true; INIT_LIST_HEAD(&f->list); list_add_tail(&f->list, &vsi->mac_filter_list); - } - - /* increment counter and add a new flag if needed */ - if (is_vf) { - if (!f->is_vf) { - f->is_vf = true; - f->counter++; - } - } else if (is_netdev) { - if (!f->is_netdev) { - f->is_netdev = true; - f->counter++; - } - } else { - f->counter++; - } - if (changed) { vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; vsi->back->flags |= I40E_FLAG_FILTER_SYNC; } -add_filter_out: + /* If we're asked to add a filter that has been marked for removal, it + * is safe to simply restore it to active state. __i40e_del_filter + * will have simply deleted any filters which were previously marked + * NEW or FAILED, so if it is currently marked REMOVE it must have + * previously been ACTIVE. Since we haven't yet run the sync filters + * task, just restore this filter to the ACTIVE state so that the + * sync task leaves it in place + */ + if (f->state == I40E_FILTER_REMOVE) + f->state = I40E_FILTER_ACTIVE; + return f; } @@ -1366,8 +1331,6 @@ add_filter_out: * @vsi: the VSI to be searched * @macaddr: the MAC address * @vlan: the vlan - * @is_vf: make sure it's a VF filter, else doesn't matter - * @is_netdev: make sure it's a netdev filter, else doesn't matter * * NOTE: This function is expected to be called with mac_filter_list_lock * being held. @@ -1375,56 +1338,28 @@ add_filter_out: * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() * instead of list_for_each_entry(). **/ -void i40e_del_filter(struct i40e_vsi *vsi, - u8 *macaddr, s16 vlan, - bool is_vf, bool is_netdev) +void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; if (!vsi || !macaddr) return; - f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev); - if (!f || f->counter == 0) + f = i40e_find_filter(vsi, macaddr, vlan); + if (!f) return; - if (is_vf) { - if (f->is_vf) { - f->is_vf = false; - f->counter--; - } - } else if (is_netdev) { - if (f->is_netdev) { - f->is_netdev = false; - f->counter--; - } + if ((f->state == I40E_FILTER_FAILED) || + (f->state == I40E_FILTER_NEW)) { + /* this one never got added by the FW. Just remove it, + * no need to sync anything. + */ + list_del(&f->list); + kfree(f); } else { - /* make sure we don't remove a filter in use by VF or netdev */ - int min_f = 0; - - min_f += (f->is_vf ? 1 : 0); - min_f += (f->is_netdev ? 1 : 0); - - if (f->counter > min_f) - f->counter--; - } - - /* counter == 0 tells sync_filters_subtask to - * remove the filter from the firmware's list - */ - if (f->counter == 0) { - if ((f->state == I40E_FILTER_FAILED) || - (f->state == I40E_FILTER_NEW)) { - /* this one never got added by the FW. Just remove it, - * no need to sync anything. - */ - list_del(&f->list); - kfree(f); - } else { - f->state = I40E_FILTER_REMOVE; - vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - vsi->back->flags |= I40E_FLAG_FILTER_SYNC; - } + f->state = I40E_FILTER_REMOVE; + vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; + vsi->back->flags |= I40E_FLAG_FILTER_SYNC; } } @@ -1467,8 +1402,8 @@ static int i40e_set_mac(struct net_device *netdev, void *p) netdev_info(netdev, "set new mac address %pM\n", addr->sa_data); spin_lock_bh(&vsi->mac_filter_list_lock); - i40e_del_mac_all_vlan(vsi, netdev->dev_addr, false, true); - i40e_put_mac_in_vlan(vsi, addr->sa_data, false, true); + i40e_del_mac_all_vlan(vsi, netdev->dev_addr); + i40e_put_mac_in_vlan(vsi, addr->sa_data); spin_unlock_bh(&vsi->mac_filter_list_lock); ether_addr_copy(netdev->dev_addr, addr->sa_data); if (vsi->type == I40E_VSI_MAIN) { @@ -1653,33 +1588,26 @@ static void i40e_set_rx_mode(struct net_device *netdev) /* add addr if not already in the filter list */ netdev_for_each_uc_addr(uca, netdev) { - if (!i40e_find_mac(vsi, uca->addr, false, true)) { + if (!i40e_find_mac(vsi, uca->addr)) { if (i40e_is_vsi_in_vlan(vsi)) - i40e_put_mac_in_vlan(vsi, uca->addr, - false, true); + i40e_put_mac_in_vlan(vsi, uca->addr); else - i40e_add_filter(vsi, uca->addr, I40E_VLAN_ANY, - false, true); + i40e_add_filter(vsi, uca->addr, I40E_VLAN_ANY); } } netdev_for_each_mc_addr(mca, netdev) { - if (!i40e_find_mac(vsi, mca->addr, false, true)) { + if (!i40e_find_mac(vsi, mca->addr)) { if (i40e_is_vsi_in_vlan(vsi)) - i40e_put_mac_in_vlan(vsi, mca->addr, - false, true); + i40e_put_mac_in_vlan(vsi, mca->addr); else - i40e_add_filter(vsi, mca->addr, I40E_VLAN_ANY, - false, true); + i40e_add_filter(vsi, mca->addr, I40E_VLAN_ANY); } } /* remove filter if not in netdev list */ list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - if (!f->is_netdev) - continue; - netdev_for_each_mc_addr(mca, netdev) if (ether_addr_equal(mca->addr, f->macaddr)) goto bottom_of_search_loop; @@ -1693,7 +1621,7 @@ static void i40e_set_rx_mode(struct net_device *netdev) goto bottom_of_search_loop; /* f->macaddr wasn't found in uc, mc, or ha list so delete it */ - i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY, false, true); + i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY); bottom_of_search_loop: continue; @@ -1838,13 +1766,11 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* Create a list of filters to delete. */ list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { if (f->state == I40E_FILTER_REMOVE) { - WARN_ON(f->counter != 0); /* Move the element into temporary del_list */ list_move_tail(&f->list, &tmp_del_list); vsi->active_filters--; } if (f->state == I40E_FILTER_NEW) { - WARN_ON(f->counter == 0); /* Move the element into temporary add_list */ list_move_tail(&f->list, &tmp_add_list); } @@ -2323,17 +2249,12 @@ static void i40e_vlan_rx_register(struct net_device *netdev, u32 features) int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) { struct i40e_mac_filter *f, *ftmp, *add_f; - bool is_netdev, is_vf; - - is_vf = (vsi->type == I40E_VSI_SRIOV); - is_netdev = !!(vsi->netdev); /* Locked once because all functions invoked below iterates list*/ spin_lock_bh(&vsi->mac_filter_list_lock); - if (is_netdev) { - add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, vid, - is_vf, is_netdev); + if (vsi->netdev) { + add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, vid); if (!add_f) { dev_info(&vsi->back->pdev->dev, "Could not add vlan filter %d for %pM\n", @@ -2344,7 +2265,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) } list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - add_f = i40e_add_filter(vsi, f->macaddr, vid, is_vf, is_netdev); + add_f = i40e_add_filter(vsi, f->macaddr, vid); if (!add_f) { dev_info(&vsi->back->pdev->dev, "Could not add vlan filter %d for %pM\n", @@ -2360,13 +2281,11 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) * (and not all tags along with untagged) */ if (vid > 0) { - if (is_netdev && i40e_find_filter(vsi, vsi->netdev->dev_addr, - I40E_VLAN_ANY, - is_vf, is_netdev)) { + if (vsi->netdev && i40e_find_filter(vsi, vsi->netdev->dev_addr, + I40E_VLAN_ANY)) { i40e_del_filter(vsi, vsi->netdev->dev_addr, - I40E_VLAN_ANY, is_vf, is_netdev); - add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, 0, - is_vf, is_netdev); + I40E_VLAN_ANY); + add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, 0); if (!add_f) { dev_info(&vsi->back->pdev->dev, "Could not add filter 0 for %pM\n", @@ -2380,13 +2299,10 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) /* Do not assume that I40E_VLAN_ANY should be reset to VLAN 0 */ if (vid > 0 && !vsi->info.pvid) { list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - if (!i40e_find_filter(vsi, f->macaddr, I40E_VLAN_ANY, - is_vf, is_netdev)) + if (!i40e_find_filter(vsi, f->macaddr, I40E_VLAN_ANY)) continue; - i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY, - is_vf, is_netdev); - add_f = i40e_add_filter(vsi, f->macaddr, - 0, is_vf, is_netdev); + i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY); + add_f = i40e_add_filter(vsi, f->macaddr, 0); if (!add_f) { dev_info(&vsi->back->pdev->dev, "Could not add filter 0 for %pM\n", @@ -2417,20 +2333,16 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) { struct net_device *netdev = vsi->netdev; struct i40e_mac_filter *f, *ftmp, *add_f; - bool is_vf, is_netdev; int filter_count = 0; - is_vf = (vsi->type == I40E_VSI_SRIOV); - is_netdev = !!(netdev); - /* Locked once because all functions invoked below iterates list */ spin_lock_bh(&vsi->mac_filter_list_lock); - if (is_netdev) - i40e_del_filter(vsi, netdev->dev_addr, vid, is_vf, is_netdev); + if (vsi->netdev) + i40e_del_filter(vsi, netdev->dev_addr, vid); list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) - i40e_del_filter(vsi, f->macaddr, vid, is_vf, is_netdev); + i40e_del_filter(vsi, f->macaddr, vid); /* go through all the filters for this VSI and if there is only * vid == 0 it means there are no other filters, so vid 0 must @@ -2438,7 +2350,7 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) * on accept any traffic (with any tag present, or untagged) */ list_for_each_entry(f, &vsi->mac_filter_list, list) { - if (is_netdev) { + if (vsi->netdev) { if (f->vlan && ether_addr_equal(netdev->dev_addr, f->macaddr)) filter_count++; @@ -2448,10 +2360,9 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) filter_count++; } - if (!filter_count && is_netdev) { - i40e_del_filter(vsi, netdev->dev_addr, 0, is_vf, is_netdev); - f = i40e_add_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY, - is_vf, is_netdev); + if (!filter_count && vsi->netdev) { + i40e_del_filter(vsi, netdev->dev_addr, 0); + f = i40e_add_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY); if (!f) { dev_info(&vsi->back->pdev->dev, "Could not add filter %d for %pM\n", @@ -2463,9 +2374,8 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) if (!filter_count) { list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - i40e_del_filter(vsi, f->macaddr, 0, is_vf, is_netdev); - add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY, - is_vf, is_netdev); + i40e_del_filter(vsi, f->macaddr, 0); + add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY); if (!add_f) { dev_info(&vsi->back->pdev->dev, "Could not add filter %d for %pM\n", @@ -9155,7 +9065,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) SET_NETDEV_DEV(netdev, &pf->pdev->dev); ether_addr_copy(mac_addr, hw->mac.perm_addr); spin_lock_bh(&vsi->mac_filter_list_lock); - i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, true); + i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY); spin_unlock_bh(&vsi->mac_filter_list_lock); } else { /* relate the VSI_VMDQ name to the VSI_MAIN name */ @@ -9164,7 +9074,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) random_ether_addr(mac_addr); spin_lock_bh(&vsi->mac_filter_list_lock); - i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, false); + i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY); spin_unlock_bh(&vsi->mac_filter_list_lock); } @@ -9525,8 +9435,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) spin_lock_bh(&vsi->mac_filter_list_lock); list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) - i40e_del_filter(vsi, f->macaddr, f->vlan, - f->is_vf, f->is_netdev); + i40e_del_filter(vsi, f->macaddr, f->vlan); spin_unlock_bh(&vsi->mac_filter_list_lock); i40e_sync_vsi_filters(vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 64e54a5..37e6afc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -689,8 +689,8 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) spin_lock_bh(&vsi->mac_filter_list_lock); if (is_valid_ether_addr(vf->default_lan_addr.addr)) { f = i40e_add_filter(vsi, vf->default_lan_addr.addr, - vf->port_vlan_id ? vf->port_vlan_id : -1, - true, false); + vf->port_vlan_id ? + vf->port_vlan_id : -1); if (!f) dev_info(&pf->pdev->dev, "Could not add MAC filter %pM for VF %d\n", @@ -1933,14 +1933,12 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) for (i = 0; i < al->num_elements; i++) { struct i40e_mac_filter *f; - f = i40e_find_mac(vsi, al->list[i].addr, true, false); + f = i40e_find_mac(vsi, al->list[i].addr); if (!f) { if (i40e_is_vsi_in_vlan(vsi)) - f = i40e_put_mac_in_vlan(vsi, al->list[i].addr, - true, false); + f = i40e_put_mac_in_vlan(vsi, al->list[i].addr); else - f = i40e_add_filter(vsi, al->list[i].addr, -1, - true, false); + f = i40e_add_filter(vsi, al->list[i].addr, -1); } if (!f) { @@ -2006,7 +2004,7 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) spin_lock_bh(&vsi->mac_filter_list_lock); /* delete addresses from the list */ for (i = 0; i < al->num_elements; i++) - if (i40e_del_mac_all_vlan(vsi, al->list[i].addr, true, false)) { + if (i40e_del_mac_all_vlan(vsi, al->list[i].addr)) { ret = I40E_ERR_INVALID_MAC_ADDR; spin_unlock_bh(&vsi->mac_filter_list_lock); goto error_param; @@ -2722,14 +2720,13 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) /* delete the temporary mac address */ if (!is_zero_ether_addr(vf->default_lan_addr.addr)) i40e_del_filter(vsi, vf->default_lan_addr.addr, - vf->port_vlan_id ? vf->port_vlan_id : -1, - true, false); + vf->port_vlan_id ? vf->port_vlan_id : -1); /* Delete all the filters for this VSI - we're going to kill it * anyway. */ list_for_each_entry(f, &vsi->mac_filter_list, list) - i40e_del_filter(vsi, f->macaddr, f->vlan, true, false); + i40e_del_filter(vsi, f->macaddr, f->vlan); spin_unlock_bh(&vsi->mac_filter_list_lock); -- cgit v1.1 From 6622f5cdbaf3786314d76969d2aab132b36ba2e8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:32 -0700 Subject: i40e: make use of __dev_uc_sync and __dev_mc_sync The kernel provides __dev_uc_sync and __dev_mc_sync in order for drivers which need individual notification of add and delete for each filter. These functions allow us to vastly simplify our .set_rx_mode handler. We need to implement two functions for sync and unsync which add and remove filters respectively. This change avoids a very complex and inefficient algorithm which resulted in an abnormal latency for the .set_rx_mode NDO operation. The resulting code after this change is more readable, more efficient, and less code. Due to the callback signature used by these functions we also must update several other functions to take a const u8 * pointer. Change-Id: I2ca7fd4e10c0c07ed2291db1ea41bf5987fc6474 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 10 +-- drivers/net/ethernet/intel/i40e/i40e_main.c | 113 ++++++++++++++++------------ 2 files changed, 69 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 8445591..a9c0228 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -720,8 +720,8 @@ u32 i40e_get_global_fd_count(struct i40e_pf *pf); bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features); void i40e_set_ethtool_ops(struct net_device *netdev); struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, - u8 *macaddr, s16 vlan); -void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan); + const u8 *macaddr, s16 vlan); +void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan); int i40e_sync_vsi_filters(struct i40e_vsi *vsi); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); @@ -813,10 +813,10 @@ void i40e_vlan_stripping_disable(struct i40e_vsi *vsi); int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid); int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid); struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, - u8 *macaddr); -int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr); + const u8 *macaddr); +int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr); bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi); -struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr); +struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr); #ifdef I40E_FCOE int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, struct tc_to_netdev *tc); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index ed49113..c63dc01 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1149,7 +1149,7 @@ void i40e_update_stats(struct i40e_vsi *vsi) * Returns ptr to the filter object or NULL **/ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, - u8 *macaddr, s16 vlan) + const u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; @@ -1172,7 +1172,7 @@ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, * Returns the first filter with the provided MAC address or NULL if * MAC address was not found **/ -struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr) +struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr) { struct i40e_mac_filter *f; @@ -1217,7 +1217,8 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) * * Returns first filter found on success, else NULL **/ -struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr) +struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, + const u8 *macaddr) { struct i40e_mac_filter *f; @@ -1243,7 +1244,7 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr) * * Returns 0 for success, or error **/ -int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr) +int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr) { struct i40e_mac_filter *f = NULL; int changed = 0; @@ -1275,7 +1276,7 @@ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr) * being held. **/ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, - u8 *macaddr, s16 vlan) + const u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; @@ -1338,7 +1339,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() * instead of list_for_each_entry(). **/ -void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan) +void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; @@ -1568,6 +1569,52 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, } /** + * i40e_addr_sync - Callback for dev_(mc|uc)_sync to add address + * @netdev: the netdevice + * @addr: address to add + * + * Called by __dev_(mc|uc)_sync when an address needs to be added. We call + * __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock. + */ +static int i40e_addr_sync(struct net_device *netdev, const u8 *addr) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_mac_filter *f; + + if (i40e_is_vsi_in_vlan(vsi)) + f = i40e_put_mac_in_vlan(vsi, addr); + else + f = i40e_add_filter(vsi, addr, I40E_VLAN_ANY); + + if (f) + return 0; + else + return -ENOMEM; +} + +/** + * i40e_addr_unsync - Callback for dev_(mc|uc)_sync to remove address + * @netdev: the netdevice + * @addr: address to add + * + * Called by __dev_(mc|uc)_sync when an address needs to be removed. We call + * __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock. + */ +static int i40e_addr_unsync(struct net_device *netdev, const u8 *addr) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + + if (i40e_is_vsi_in_vlan(vsi)) + i40e_del_mac_all_vlan(vsi, addr); + else + i40e_del_filter(vsi, addr, I40E_VLAN_ANY); + + return 0; +} + +/** * i40e_set_rx_mode - NDO callback to set the netdev filters * @netdev: network interface device structure **/ @@ -1578,54 +1625,13 @@ static void i40e_set_rx_mode(struct net_device *netdev) #endif { struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_mac_filter *f, *ftmp; struct i40e_vsi *vsi = np->vsi; - struct netdev_hw_addr *uca; - struct netdev_hw_addr *mca; - struct netdev_hw_addr *ha; spin_lock_bh(&vsi->mac_filter_list_lock); - /* add addr if not already in the filter list */ - netdev_for_each_uc_addr(uca, netdev) { - if (!i40e_find_mac(vsi, uca->addr)) { - if (i40e_is_vsi_in_vlan(vsi)) - i40e_put_mac_in_vlan(vsi, uca->addr); - else - i40e_add_filter(vsi, uca->addr, I40E_VLAN_ANY); - } - } - - netdev_for_each_mc_addr(mca, netdev) { - if (!i40e_find_mac(vsi, mca->addr)) { - if (i40e_is_vsi_in_vlan(vsi)) - i40e_put_mac_in_vlan(vsi, mca->addr); - else - i40e_add_filter(vsi, mca->addr, I40E_VLAN_ANY); - } - } + __dev_uc_sync(netdev, i40e_addr_sync, i40e_addr_unsync); + __dev_mc_sync(netdev, i40e_addr_sync, i40e_addr_unsync); - /* remove filter if not in netdev list */ - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - - netdev_for_each_mc_addr(mca, netdev) - if (ether_addr_equal(mca->addr, f->macaddr)) - goto bottom_of_search_loop; - - netdev_for_each_uc_addr(uca, netdev) - if (ether_addr_equal(uca->addr, f->macaddr)) - goto bottom_of_search_loop; - - for_each_dev_addr(netdev, ha) - if (ether_addr_equal(ha->addr, f->macaddr)) - goto bottom_of_search_loop; - - /* f->macaddr wasn't found in uc, mc, or ha list so delete it */ - i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY); - -bottom_of_search_loop: - continue; - } spin_unlock_bh(&vsi->mac_filter_list_lock); /* check for other flag changes */ @@ -9434,8 +9440,17 @@ int i40e_vsi_release(struct i40e_vsi *vsi) } spin_lock_bh(&vsi->mac_filter_list_lock); + + /* clear the sync flag on all filters */ + if (vsi->netdev) { + __dev_uc_unsync(vsi->netdev, NULL); + __dev_mc_unsync(vsi->netdev, NULL); + } + + /* make sure any remaining filters are marked for deletion */ list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) i40e_del_filter(vsi, f->macaddr, f->vlan); + spin_unlock_bh(&vsi->mac_filter_list_lock); i40e_sync_vsi_filters(vsi); -- cgit v1.1 From 35ec2ff37c5a4897e75d408828adac737b17d2bf Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:33 -0700 Subject: i40e: move i40e_put_mac_in_vlan and i40e_del_mac_all_vlan A future patch will be modifying these functions and making a call to a static function which currently is defined after these functions. Move them in a separate patch to ease review and ensure the moved code is correct. Change-ID: I2ca7fd4e10c0c07ed2291db1ea41bf5987fc6474 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 113 ++++++++++++++-------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c63dc01..f7fe020 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1208,63 +1208,6 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) } /** - * i40e_put_mac_in_vlan - Make macvlan filters from macaddrs and vlans - * @vsi: the VSI to be searched - * @macaddr: the mac address to be filtered - * - * Goes through all the macvlan filters and adds a - * macvlan filter for each unique vlan that already exists - * - * Returns first filter found on success, else NULL - **/ -struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, - const u8 *macaddr) -{ - struct i40e_mac_filter *f; - - list_for_each_entry(f, &vsi->mac_filter_list, list) { - if (vsi->info.pvid) - f->vlan = le16_to_cpu(vsi->info.pvid); - if (!i40e_find_filter(vsi, macaddr, f->vlan)) { - if (!i40e_add_filter(vsi, macaddr, f->vlan)) - return NULL; - } - } - - return list_first_entry_or_null(&vsi->mac_filter_list, - struct i40e_mac_filter, list); -} - -/** - * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS - * @vsi: the VSI to be searched - * @macaddr: the mac address to be removed - * - * Removes a given MAC address from a VSI, regardless of VLAN - * - * Returns 0 for success, or error - **/ -int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr) -{ - struct i40e_mac_filter *f = NULL; - int changed = 0; - - WARN(!spin_is_locked(&vsi->mac_filter_list_lock), - "Missing mac_filter_list_lock\n"); - list_for_each_entry(f, &vsi->mac_filter_list, list) { - if ((ether_addr_equal(macaddr, f->macaddr))) { - f->state = I40E_FILTER_REMOVE; - } - } - if (changed) { - vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - vsi->back->flags |= I40E_FLAG_FILTER_SYNC; - return 0; - } - return -ENOENT; -} - -/** * i40e_add_filter - Add a mac/vlan filter to the VSI * @vsi: the VSI to be searched * @macaddr: the MAC address @@ -1365,6 +1308,62 @@ void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) } /** + * i40e_put_mac_in_vlan - Make macvlan filters from macaddrs and vlans + * @vsi: the VSI to be searched + * @macaddr: the mac address to be filtered + * + * Goes through all the macvlan filters and adds a + * macvlan filter for each unique vlan that already exists + * + * Returns first filter found on success, else NULL + **/ +struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, + const u8 *macaddr) +{ + struct i40e_mac_filter *f; + + list_for_each_entry(f, &vsi->mac_filter_list, list) { + if (vsi->info.pvid) + f->vlan = le16_to_cpu(vsi->info.pvid); + if (!i40e_find_filter(vsi, macaddr, f->vlan)) { + if (!i40e_add_filter(vsi, macaddr, f->vlan)) + return NULL; + } + } + + return list_first_entry_or_null(&vsi->mac_filter_list, + struct i40e_mac_filter, list); +} + +/** + * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS + * @vsi: the VSI to be searched + * @macaddr: the mac address to be removed + * + * Removes a given MAC address from a VSI, regardless of VLAN + * + * Returns 0 for success, or error + **/ +int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr) +{ + struct i40e_mac_filter *f = NULL; + int changed = 0; + + WARN(!spin_is_locked(&vsi->mac_filter_list_lock), + "Missing mac_filter_list_lock\n"); + list_for_each_entry(f, &vsi->mac_filter_list, list) { + if (ether_addr_equal(macaddr, f->macaddr)) + f->state = I40E_FILTER_REMOVE; + } + if (changed) { + vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; + vsi->back->flags |= I40E_FLAG_FILTER_SYNC; + return 0; + } + return -ENOENT; +} + +/** * i40e_set_mac - NDO callback to set mac address * @netdev: network interface device structure * @p: pointer to an address structure -- cgit v1.1 From 5feb3d7b0c0a66c8537ca8a79d8f80f54783e73f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:34 -0700 Subject: i40e: refactor i40e_put_mac_in_vlan to avoid changing f->vlan When a PVID has been assigned to a VSI, the function i40e_put_mac_in_vlan arbitrarily modifies all filters to have the same VLAN. This is obviously incorrect because it could be modifying active filters without putting them into the NEW state. The correct method is to remove then re-add filters which is already done in the code where we assign the PVID. Fix this issue and a few other minor nits at the same time. First, when we have a PVID don't even bother looping and simply add the filter with the PVID immediately. In the case of the loop, we now can remove several checks. We also don't need to use i40e_find_filter first before calling i40e_add_filter, since i40e_add_filter implicitly does a lookup already. Finally, update the return semantics of this function so that on failure to add a filter it returns NULL, but on success, it returns the last filter added. Otherwise, we're just returning the last filter in the list. An alternative fix might be to return 0 or an error code, but this is pretty invasive to every call site. Change-ID: I2325dfd843aec76d89fb0d7cb0e7c4f290a34840 Signed-off-by: Jacob Keller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index f7fe020..c096093 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1312,27 +1312,28 @@ void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) * @vsi: the VSI to be searched * @macaddr: the mac address to be filtered * - * Goes through all the macvlan filters and adds a - * macvlan filter for each unique vlan that already exists + * Goes through all the macvlan filters and adds a macvlan filter for each + * unique vlan that already exists. If a PVID has been assigned, instead only + * add the macaddr to that VLAN. * - * Returns first filter found on success, else NULL + * Returns last filter added on success, else NULL **/ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, const u8 *macaddr) { - struct i40e_mac_filter *f; + struct i40e_mac_filter *f, *add = NULL; + + if (vsi->info.pvid) + return i40e_add_filter(vsi, macaddr, + le16_to_cpu(vsi->info.pvid)); list_for_each_entry(f, &vsi->mac_filter_list, list) { - if (vsi->info.pvid) - f->vlan = le16_to_cpu(vsi->info.pvid); - if (!i40e_find_filter(vsi, macaddr, f->vlan)) { - if (!i40e_add_filter(vsi, macaddr, f->vlan)) - return NULL; - } + add = i40e_add_filter(vsi, macaddr, f->vlan); + if (!add) + return NULL; } - return list_first_entry_or_null(&vsi->mac_filter_list, - struct i40e_mac_filter, list); + return add; } /** -- cgit v1.1 From 57b341d66684e380cae332eadcc210e177a9f289 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:35 -0700 Subject: i40e: When searching all MAC/VLAN filters, ignore removed filters When adding new MAC address filters, the driver determines if it should behave in VLAN mode (where all MAC addresses get assigned to every existing VLAN) or in non-VLAN mode where MAC addresses get assigned the VLAN_ANY identifier. Under some circumstances it is possible that a VLAN has been marked for removal (such that all filters of that VLAN are set to I40E_FILTER_REMOVE), and a subsequent call to i40e_put_mac_in_vlan may occur prior to the driver subtask that syncs filters to the hardware. In this case, we may add filters to the new removed VLAN, even though it should have been removed. This is most obvious when first adding a new VLAN. We will delete all filters which are in I40E_VLAN_ANY (-1) and then re-add them as in VLAN 0 (untagged). Then before we sync filters, we will add new MAC address filter, which will be added to every VLAN that exists. Unfortunately, this will include I40E_VLAN_ANY, so we will end up incorrectly adding filters to the -1 VLAN. This can be fixed by simply skipping all filters which are marked for removal. A similar check is not necessary in i40e_del_mac_all_vlan, since we are deleting, and any filter which we find already marked for removal would simply be deleted again, which doesn't cause any issues. Change-Id: I7962154013ce02fe950584690aeeb3ed853d0086 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c096093..86ad953 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1328,6 +1328,8 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, le16_to_cpu(vsi->info.pvid)); list_for_each_entry(f, &vsi->mac_filter_list, list) { + if (f->state == I40E_FILTER_REMOVE) + continue; add = i40e_add_filter(vsi, macaddr, f->vlan); if (!add) return NULL; @@ -2271,6 +2273,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) } list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + if (f->state == I40E_FILTER_REMOVE) + continue; add_f = i40e_add_filter(vsi, f->macaddr, vid); if (!add_f) { dev_info(&vsi->back->pdev->dev, @@ -2305,6 +2309,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) /* Do not assume that I40E_VLAN_ANY should be reset to VLAN 0 */ if (vid > 0 && !vsi->info.pvid) { list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + if (f->state == I40E_FILTER_REMOVE) + continue; if (!i40e_find_filter(vsi, f->macaddr, I40E_VLAN_ANY)) continue; i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY); -- cgit v1.1 From 290d255719af42ae4baaf49165a69b9ad47f2049 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:36 -0700 Subject: i40e: implement __i40e_del_filter and use where applicable When inside a loop where we call i40e_del_filter we use an O(n^2) pattern where i40e_del_filter calls i40e_find_filter for us. We can avoid this O(n^2) logic by factoring a function, __i40e_del_filter() out from the i40e_del_filter code. This allows us to re-use the delete logic where appropriate without having to search for the filter twice. This new function benefits several functions including i40e_vsi_add_vlan, i40e_vsi_kill_vlan, i40e_del_mac_vlan_all, and i40e_vsi_release. Change-ID: I75fabe0f53bf73f56b80d342e5fdcfcc28f4d3eb Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 92 ++++++++++++++++++----------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 86ad953..25db8d8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1271,10 +1271,13 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, } /** - * i40e_del_filter - Remove a mac/vlan filter from the VSI - * @vsi: the VSI to be searched - * @macaddr: the MAC address - * @vlan: the vlan + * __i40e_del_filter - Remove a specific filter from the VSI + * @vsi: VSI to remove from + * @f: the filter to remove from the list + * + * This function should be called instead of i40e_del_filter only if you know + * the exact filter you will remove already, such as via i40e_find_filter or + * i40e_find_mac. * * NOTE: This function is expected to be called with mac_filter_list_lock * being held. @@ -1282,14 +1285,8 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() * instead of list_for_each_entry(). **/ -void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) +static void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f) { - struct i40e_mac_filter *f; - - if (!vsi || !macaddr) - return; - - f = i40e_find_filter(vsi, macaddr, vlan); if (!f) return; @@ -1308,6 +1305,29 @@ void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) } /** + * i40e_del_filter - Remove a MAC/VLAN filter from the VSI + * @vsi: the VSI to be searched + * @macaddr: the MAC address + * @vlan: the VLAN + * + * NOTE: This function is expected to be called with mac_filter_list_lock + * being held. + * ANOTHER NOTE: This function MUST be called from within the context of + * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() + * instead of list_for_each_entry(). + **/ +void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) +{ + struct i40e_mac_filter *f; + + if (!vsi || !macaddr) + return; + + f = i40e_find_filter(vsi, macaddr, vlan); + __i40e_del_filter(vsi, f); +} + +/** * i40e_put_mac_in_vlan - Make macvlan filters from macaddrs and vlans * @vsi: the VSI to be searched * @macaddr: the mac address to be filtered @@ -1349,21 +1369,22 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, **/ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr) { - struct i40e_mac_filter *f = NULL; - int changed = 0; + struct i40e_mac_filter *f, *ftmp; + bool found = false; WARN(!spin_is_locked(&vsi->mac_filter_list_lock), "Missing mac_filter_list_lock\n"); - list_for_each_entry(f, &vsi->mac_filter_list, list) { - if (ether_addr_equal(macaddr, f->macaddr)) - f->state = I40E_FILTER_REMOVE; + list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + if (ether_addr_equal(macaddr, f->macaddr)) { + __i40e_del_filter(vsi, f); + found = true; + } } - if (changed) { - vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - vsi->back->flags |= I40E_FLAG_FILTER_SYNC; + + if (found) return 0; - } - return -ENOENT; + else + return -ENOENT; } /** @@ -2256,7 +2277,7 @@ static void i40e_vlan_rx_register(struct net_device *netdev, u32 features) **/ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) { - struct i40e_mac_filter *f, *ftmp, *add_f; + struct i40e_mac_filter *f, *ftmp, *add_f, *del_f; /* Locked once because all functions invoked below iterates list*/ spin_lock_bh(&vsi->mac_filter_list_lock); @@ -2290,11 +2311,11 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) * with 0, so we now accept untagged and specified tagged traffic * (and not all tags along with untagged) */ - if (vid > 0) { - if (vsi->netdev && i40e_find_filter(vsi, vsi->netdev->dev_addr, - I40E_VLAN_ANY)) { - i40e_del_filter(vsi, vsi->netdev->dev_addr, - I40E_VLAN_ANY); + if (vid > 0 && vsi->netdev) { + del_f = i40e_find_filter(vsi, vsi->netdev->dev_addr, + I40E_VLAN_ANY); + if (del_f) { + __i40e_del_filter(vsi, del_f); add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, 0); if (!add_f) { dev_info(&vsi->back->pdev->dev, @@ -2311,9 +2332,11 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { if (f->state == I40E_FILTER_REMOVE) continue; - if (!i40e_find_filter(vsi, f->macaddr, I40E_VLAN_ANY)) + del_f = i40e_find_filter(vsi, f->macaddr, + I40E_VLAN_ANY); + if (!del_f) continue; - i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY); + __i40e_del_filter(vsi, del_f); add_f = i40e_add_filter(vsi, f->macaddr, 0); if (!add_f) { dev_info(&vsi->back->pdev->dev, @@ -2353,8 +2376,10 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) if (vsi->netdev) i40e_del_filter(vsi, netdev->dev_addr, vid); - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) - i40e_del_filter(vsi, f->macaddr, vid); + list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + if (f->vlan == vid) + __i40e_del_filter(vsi, f); + } /* go through all the filters for this VSI and if there is only * vid == 0 it means there are no other filters, so vid 0 must @@ -2386,7 +2411,8 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) if (!filter_count) { list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - i40e_del_filter(vsi, f->macaddr, 0); + if (!f->vlan) + __i40e_del_filter(vsi, f); add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY); if (!add_f) { dev_info(&vsi->back->pdev->dev, @@ -9455,7 +9481,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) /* make sure any remaining filters are marked for deletion */ list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) - i40e_del_filter(vsi, f->macaddr, f->vlan); + __i40e_del_filter(vsi, f); spin_unlock_bh(&vsi->mac_filter_list_lock); -- cgit v1.1 From 278e7d0b9d6864a9749b9473a273892aa1528621 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:37 -0700 Subject: i40e: store MAC/VLAN filters in a hash with the MAC Address as key Replace the mac_filter_list with a static size hash table of 8bits. The primary advantage of this is a decrease in latency of operations related to searching for specific MAC filters, including .set_rx_mode. Using a linked list resulted in several locations which were O(n^2). Using a hash table should give us latency growth closer to O(n*log(n)). Change-ID: I5330bd04053b880e670210933e35830b95948ebb Signed-off-by: Jacob Keller Tested-by: Andrew Bowers --- drivers/net/ethernet/intel/i40e/i40e.h | 24 ++- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 4 +- drivers/net/ethernet/intel/i40e/i40e_fcoe.c | 4 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 198 ++++++++++++--------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 38 ++-- 5 files changed, 161 insertions(+), 107 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index a9c0228..6545550 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -445,6 +446,20 @@ struct i40e_pf { u16 phy_led_val; }; +/** + * i40e_mac_to_hkey - Convert a 6-byte MAC Address to a u64 hash key + * @macaddr: the MAC Address as the base key + * + * Simply copies the address and returns it as a u64 for hashing + **/ +static inline u64 i40e_addr_to_hkey(const u8 *macaddr) +{ + u64 key = 0; + + ether_addr_copy((u8 *)&key, macaddr); + return key; +} + enum i40e_filter_state { I40E_FILTER_INVALID = 0, /* Invalid state */ I40E_FILTER_NEW, /* New, not sent to FW yet */ @@ -454,7 +469,7 @@ enum i40e_filter_state { /* There is no 'removed' state; the filter struct is freed */ }; struct i40e_mac_filter { - struct list_head list; + struct hlist_node hlist; u8 macaddr[ETH_ALEN]; #define I40E_VLAN_ANY -1 s16 vlan; @@ -498,9 +513,10 @@ struct i40e_vsi { #define I40E_VSI_FLAG_VEB_OWNER BIT(1) unsigned long flags; - /* Per VSI lock to protect elements/list (MAC filter) */ - spinlock_t mac_filter_list_lock; - struct list_head mac_filter_list; + /* Per VSI lock to protect elements/hash (MAC filter) */ + spinlock_t mac_filter_hash_lock; + /* Fixed size hash table with 2^8 buckets for MAC filters */ + DECLARE_HASHTABLE(mac_filter_hash, 8); /* VSI stats */ struct rtnl_link_stats64 net_stats; diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 8f6ed8d..b8a03a0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -134,7 +134,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) struct rtnl_link_stats64 *nstat; struct i40e_mac_filter *f; struct i40e_vsi *vsi; - int i; + int i, bkt; vsi = i40e_dbg_find_vsi(pf, seid); if (!vsi) { @@ -166,7 +166,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) pf->hw.mac.addr, pf->hw.mac.san_addr, pf->hw.mac.port_addr); - list_for_each_entry(f, &vsi->mac_filter_list, list) { + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { dev_info(&pf->pdev->dev, " mac_filter_hash: %pM vid=%d, state %s\n", f->macaddr, f->vlan, diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index 0c2a00d..b077ef8b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -1522,12 +1522,12 @@ void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi) * same PCI function. */ netdev->dev_port = 1; - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_add_filter(vsi, hw->mac.san_addr, 0); i40e_add_filter(vsi, (u8[6]) FC_FCOE_FLOGI_MAC, 0); i40e_add_filter(vsi, FIP_ALL_FCOE_MACS, 0); i40e_add_filter(vsi, FIP_ALL_ENODE_MACS, 0); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); /* use san mac */ ether_addr_copy(netdev->dev_addr, hw->mac.san_addr); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 25db8d8..ea4dc9f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1152,11 +1152,13 @@ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; + u64 key; if (!vsi || !macaddr) return NULL; - list_for_each_entry(f, &vsi->mac_filter_list, list) { + key = i40e_addr_to_hkey(macaddr); + hash_for_each_possible(vsi->mac_filter_hash, f, hlist, key) { if ((ether_addr_equal(macaddr, f->macaddr)) && (vlan == f->vlan)) return f; @@ -1175,11 +1177,13 @@ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr) { struct i40e_mac_filter *f; + u64 key; if (!vsi || !macaddr) return NULL; - list_for_each_entry(f, &vsi->mac_filter_list, list) { + key = i40e_addr_to_hkey(macaddr); + hash_for_each_possible(vsi->mac_filter_hash, f, hlist, key) { if ((ether_addr_equal(macaddr, f->macaddr))) return f; } @@ -1195,11 +1199,13 @@ struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr) bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) { struct i40e_mac_filter *f; + struct hlist_node *h; + int bkt; /* Only -1 for all the filters denotes not in vlan mode * so we have to go through all the list in order to make sure */ - list_for_each_entry(f, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->vlan >= 0 || vsi->info.pvid) return true; } @@ -1215,13 +1221,14 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) * * Returns ptr to the filter object or NULL when no memory available. * - * NOTE: This function is expected to be called with mac_filter_list_lock + * NOTE: This function is expected to be called with mac_filter_hash_lock * being held. **/ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; + u64 key; if (!vsi || !macaddr) return NULL; @@ -1249,8 +1256,10 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, f->state = I40E_FILTER_FAILED; else f->state = I40E_FILTER_NEW; - INIT_LIST_HEAD(&f->list); - list_add_tail(&f->list, &vsi->mac_filter_list); + INIT_HLIST_NODE(&f->hlist); + + key = i40e_addr_to_hkey(macaddr); + hash_add(vsi->mac_filter_hash, &f->hlist, key); vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; vsi->back->flags |= I40E_FLAG_FILTER_SYNC; @@ -1279,7 +1288,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, * the exact filter you will remove already, such as via i40e_find_filter or * i40e_find_mac. * - * NOTE: This function is expected to be called with mac_filter_list_lock + * NOTE: This function is expected to be called with mac_filter_hash_lock * being held. * ANOTHER NOTE: This function MUST be called from within the context of * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() @@ -1295,7 +1304,7 @@ static void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f) /* this one never got added by the FW. Just remove it, * no need to sync anything. */ - list_del(&f->list); + hash_del(&f->hlist); kfree(f); } else { f->state = I40E_FILTER_REMOVE; @@ -1310,7 +1319,7 @@ static void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f) * @macaddr: the MAC address * @vlan: the VLAN * - * NOTE: This function is expected to be called with mac_filter_list_lock + * NOTE: This function is expected to be called with mac_filter_hash_lock * being held. * ANOTHER NOTE: This function MUST be called from within the context of * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() @@ -1342,12 +1351,14 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, const u8 *macaddr) { struct i40e_mac_filter *f, *add = NULL; + struct hlist_node *h; + int bkt; if (vsi->info.pvid) return i40e_add_filter(vsi, macaddr, le16_to_cpu(vsi->info.pvid)); - list_for_each_entry(f, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->state == I40E_FILTER_REMOVE) continue; add = i40e_add_filter(vsi, macaddr, f->vlan); @@ -1369,12 +1380,14 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, **/ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr) { - struct i40e_mac_filter *f, *ftmp; + struct i40e_mac_filter *f; + struct hlist_node *h; bool found = false; + int bkt; - WARN(!spin_is_locked(&vsi->mac_filter_list_lock), - "Missing mac_filter_list_lock\n"); - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + WARN(!spin_is_locked(&vsi->mac_filter_hash_lock), + "Missing mac_filter_hash_lock\n"); + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (ether_addr_equal(macaddr, f->macaddr)) { __i40e_del_filter(vsi, f); found = true; @@ -1425,10 +1438,10 @@ static int i40e_set_mac(struct net_device *netdev, void *p) else netdev_info(netdev, "set new mac address %pM\n", addr->sa_data); - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_del_mac_all_vlan(vsi, netdev->dev_addr); i40e_put_mac_in_vlan(vsi, addr->sa_data); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); ether_addr_copy(netdev->dev_addr, addr->sa_data); if (vsi->type == I40E_VSI_MAIN) { i40e_status ret; @@ -1650,12 +1663,12 @@ static void i40e_set_rx_mode(struct net_device *netdev) struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); __dev_uc_sync(netdev, i40e_addr_sync, i40e_addr_unsync); __dev_mc_sync(netdev, i40e_addr_sync, i40e_addr_unsync); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); /* check for other flag changes */ if (vsi->current_netdev_flags != vsi->netdev->flags) { @@ -1678,13 +1691,17 @@ static void i40e_set_rx_mode(struct net_device *netdev) * MAC filter entries from list were slated to be removed from device. **/ static void i40e_undo_del_filter_entries(struct i40e_vsi *vsi, - struct list_head *from) + struct hlist_head *from) { - struct i40e_mac_filter *f, *ftmp; + struct i40e_mac_filter *f; + struct hlist_node *h; + + hlist_for_each_entry_safe(f, h, from, hlist) { + u64 key = i40e_addr_to_hkey(f->macaddr); - list_for_each_entry_safe(f, ftmp, from, list) { /* Move the element back into MAC filter list*/ - list_move_tail(&f->list, &vsi->mac_filter_list); + hlist_del(&f->hlist); + hash_add(vsi->mac_filter_hash, &f->hlist, key); } } @@ -1713,7 +1730,9 @@ i40e_update_filter_state(int count, /* Everything's good, mark all filters active. */ for (i = 0; i < count ; i++) { add_head->state = I40E_FILTER_ACTIVE; - add_head = list_next_entry(add_head, list); + add_head = hlist_entry(add_head->hlist.next, + typeof(struct i40e_mac_filter), + hlist); } } else if (aq_err == I40E_AQ_RC_ENOSPC) { /* Device ran out of filter space. Check the return value @@ -1727,14 +1746,18 @@ i40e_update_filter_state(int count, add_head->state = I40E_FILTER_ACTIVE; retval++; } - add_head = list_next_entry(add_head, list); + add_head = hlist_entry(add_head->hlist.next, + typeof(struct i40e_mac_filter), + hlist); } } else { /* Some other horrible thing happened, fail all filters */ retval = 0; for (i = 0; i < count ; i++) { add_head->state = I40E_FILTER_FAILED; - add_head = list_next_entry(add_head, list); + add_head = hlist_entry(add_head->hlist.next, + typeof(struct i40e_mac_filter), + hlist); } } return retval; @@ -1750,14 +1773,15 @@ i40e_update_filter_state(int count, **/ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) { - struct i40e_mac_filter *f, *ftmp, *add_head = NULL; - struct list_head tmp_add_list, tmp_del_list; + struct i40e_mac_filter *f, *add_head = NULL; + struct hlist_head tmp_add_list, tmp_del_list; struct i40e_hw *hw = &vsi->back->hw; bool promisc_changed = false; char vsi_name[16] = "PF"; int filter_list_len = 0; u32 changed_flags = 0; i40e_status aq_ret = 0; + struct hlist_node *h; int retval = 0; struct i40e_pf *pf; int num_add = 0; @@ -1766,6 +1790,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) u16 cmd_flags; int list_size; int fcnt; + int bkt; /* empty array typed pointers, kcalloc later */ struct i40e_aqc_add_macvlan_element_data *add_list; @@ -1780,8 +1805,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) vsi->current_netdev_flags = vsi->netdev->flags; } - INIT_LIST_HEAD(&tmp_add_list); - INIT_LIST_HEAD(&tmp_del_list); + INIT_HLIST_HEAD(&tmp_add_list); + INIT_HLIST_HEAD(&tmp_del_list); if (vsi->type == I40E_VSI_SRIOV) snprintf(vsi_name, sizeof(vsi_name) - 1, "VF %d", vsi->vf_id); @@ -1791,24 +1816,25 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) if (vsi->flags & I40E_VSI_FLAG_FILTER_CHANGED) { vsi->flags &= ~I40E_VSI_FLAG_FILTER_CHANGED; - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); /* Create a list of filters to delete. */ - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->state == I40E_FILTER_REMOVE) { /* Move the element into temporary del_list */ - list_move_tail(&f->list, &tmp_del_list); + hash_del(&f->hlist); + hlist_add_head(&f->hlist, &tmp_del_list); vsi->active_filters--; } if (f->state == I40E_FILTER_NEW) { - /* Move the element into temporary add_list */ - list_move_tail(&f->list, &tmp_add_list); + hash_del(&f->hlist); + hlist_add_head(&f->hlist, &tmp_add_list); } } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); } /* Now process 'del_list' outside the lock */ - if (!list_empty(&tmp_del_list)) { + if (!hlist_empty(&tmp_del_list)) { filter_list_len = hw->aq.asq_buf_size / sizeof(struct i40e_aqc_remove_macvlan_element_data); list_size = filter_list_len * @@ -1816,14 +1842,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) del_list = kzalloc(list_size, GFP_ATOMIC); if (!del_list) { /* Undo VSI's MAC filter entry element updates */ - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_undo_del_filter_entries(vsi, &tmp_del_list); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); retval = -ENOMEM; goto out; } - list_for_each_entry_safe(f, ftmp, &tmp_del_list, list) { + hlist_for_each_entry_safe(f, h, &tmp_del_list, hlist) { cmd_flags = 0; /* add to delete list */ @@ -1864,7 +1890,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* Release memory for MAC filter entries which were * synced up with HW. */ - list_del(&f->list); + hlist_del(&f->hlist); kfree(f); } @@ -1891,7 +1917,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) del_list = NULL; } - if (!list_empty(&tmp_add_list)) { + if (!hlist_empty(&tmp_add_list)) { /* Do all the adds now. */ filter_list_len = hw->aq.asq_buf_size / sizeof(struct i40e_aqc_add_macvlan_element_data); @@ -1903,7 +1929,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) goto out; } num_add = 0; - list_for_each_entry(f, &tmp_add_list, list) { + hlist_for_each_entry(f, &tmp_add_list, hlist) { if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state)) { f->state = I40E_FILTER_FAILED; @@ -1974,11 +2000,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* Now move all of the filters from the temp add list back to * the VSI's list. */ - spin_lock_bh(&vsi->mac_filter_list_lock); - list_for_each_entry_safe(f, ftmp, &tmp_add_list, list) { - list_move_tail(&f->list, &vsi->mac_filter_list); + spin_lock_bh(&vsi->mac_filter_hash_lock); + hlist_for_each_entry_safe(f, h, &tmp_add_list, hlist) { + u64 key = i40e_addr_to_hkey(f->macaddr); + + hlist_del(&f->hlist); + hash_add(vsi->mac_filter_hash, &f->hlist, key); } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); kfree(add_list); add_list = NULL; } @@ -1990,12 +2019,12 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* See if we have any failed filters. We can't drop out of * promiscuous until these have all been deleted. */ - spin_lock_bh(&vsi->mac_filter_list_lock); - list_for_each_entry(f, &vsi->mac_filter_list, list) { + spin_lock_bh(&vsi->mac_filter_hash_lock); + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { if (f->state == I40E_FILTER_FAILED) failed_count++; } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); if (!failed_count) { dev_info(&pf->pdev->dev, "filter logjam cleared on %s, leaving overflow promiscuous mode\n", @@ -2277,10 +2306,12 @@ static void i40e_vlan_rx_register(struct net_device *netdev, u32 features) **/ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) { - struct i40e_mac_filter *f, *ftmp, *add_f, *del_f; + struct i40e_mac_filter *f, *add_f, *del_f; + struct hlist_node *h; + int bkt; /* Locked once because all functions invoked below iterates list*/ - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); if (vsi->netdev) { add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, vid); @@ -2288,12 +2319,12 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) dev_info(&vsi->back->pdev->dev, "Could not add vlan filter %d for %pM\n", vid, vsi->netdev->dev_addr); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } } - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->state == I40E_FILTER_REMOVE) continue; add_f = i40e_add_filter(vsi, f->macaddr, vid); @@ -2301,7 +2332,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) dev_info(&vsi->back->pdev->dev, "Could not add vlan filter %d for %pM\n", vid, f->macaddr); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } } @@ -2321,7 +2352,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) dev_info(&vsi->back->pdev->dev, "Could not add filter 0 for %pM\n", vsi->netdev->dev_addr); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } } @@ -2329,7 +2360,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) /* Do not assume that I40E_VLAN_ANY should be reset to VLAN 0 */ if (vid > 0 && !vsi->info.pvid) { - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->state == I40E_FILTER_REMOVE) continue; del_f = i40e_find_filter(vsi, f->macaddr, @@ -2342,13 +2373,13 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) dev_info(&vsi->back->pdev->dev, "Could not add filter 0 for %pM\n", f->macaddr); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } } } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); /* schedule our worker thread which will take care of * applying the new filter changes @@ -2367,16 +2398,18 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) { struct net_device *netdev = vsi->netdev; - struct i40e_mac_filter *f, *ftmp, *add_f; + struct i40e_mac_filter *f, *add_f; + struct hlist_node *h; int filter_count = 0; + int bkt; /* Locked once because all functions invoked below iterates list */ - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); if (vsi->netdev) i40e_del_filter(vsi, netdev->dev_addr, vid); - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->vlan == vid) __i40e_del_filter(vsi, f); } @@ -2386,7 +2419,7 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) * be replaced with -1. This signifies that we should from now * on accept any traffic (with any tag present, or untagged) */ - list_for_each_entry(f, &vsi->mac_filter_list, list) { + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { if (vsi->netdev) { if (f->vlan && ether_addr_equal(netdev->dev_addr, f->macaddr)) @@ -2404,13 +2437,13 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) dev_info(&vsi->back->pdev->dev, "Could not add filter %d for %pM\n", I40E_VLAN_ANY, netdev->dev_addr); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } } if (!filter_count) { - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (!f->vlan) __i40e_del_filter(vsi, f); add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY); @@ -2418,13 +2451,13 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) dev_info(&vsi->back->pdev->dev, "Could not add filter %d for %pM\n", I40E_VLAN_ANY, f->macaddr); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } } } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); /* schedule our worker thread which will take care of * applying the new filter changes @@ -7302,7 +7335,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) pf->rss_table_size : 64; vsi->netdev_registered = false; vsi->work_limit = I40E_DEFAULT_IRQ_WORK; - INIT_LIST_HEAD(&vsi->mac_filter_list); + hash_init(vsi->mac_filter_hash); vsi->irqs_ready = false; ret = i40e_set_num_rings_in_vsi(vsi); @@ -7317,7 +7350,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) i40e_vsi_setup_irqhandler(vsi, i40e_msix_clean_rings); /* Initialize VSI lock */ - spin_lock_init(&vsi->mac_filter_list_lock); + spin_lock_init(&vsi->mac_filter_hash_lock); pf->vsi[vsi_idx] = vsi; ret = vsi_idx; goto unlock_pf; @@ -9102,18 +9135,18 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) if (vsi->type == I40E_VSI_MAIN) { SET_NETDEV_DEV(netdev, &pf->pdev->dev); ether_addr_copy(mac_addr, hw->mac.perm_addr); - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); } else { /* relate the VSI_VMDQ name to the VSI_MAIN name */ snprintf(netdev->name, IFNAMSIZ, "%sv%%d", pf->vsi[pf->lan_vsi]->netdev->name); random_ether_addr(mac_addr); - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); } ether_addr_copy(netdev->dev_addr, mac_addr); @@ -9202,7 +9235,9 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; struct i40e_vsi_context ctxt; - struct i40e_mac_filter *f, *ftmp; + struct i40e_mac_filter *f; + struct hlist_node *h; + int bkt; u8 enabled_tc = 0x1; /* TC0 enabled */ int f_count = 0; @@ -9401,13 +9436,13 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) vsi->active_filters = 0; clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); /* If macvlan filters already exist, force them to get loaded */ - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { f->state = I40E_FILTER_NEW; f_count++; } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); if (f_count) { vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; @@ -9437,11 +9472,12 @@ err: **/ int i40e_vsi_release(struct i40e_vsi *vsi) { - struct i40e_mac_filter *f, *ftmp; + struct i40e_mac_filter *f; + struct hlist_node *h; struct i40e_veb *veb = NULL; struct i40e_pf *pf; u16 uplink_seid; - int i, n; + int i, n, bkt; pf = vsi->back; @@ -9471,7 +9507,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) i40e_vsi_disable_irq(vsi); } - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); /* clear the sync flag on all filters */ if (vsi->netdev) { @@ -9480,10 +9516,10 @@ int i40e_vsi_release(struct i40e_vsi *vsi) } /* make sure any remaining filters are marked for deletion */ - list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) __i40e_del_filter(vsi, f); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); i40e_sync_vsi_filters(vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 37e6afc..2f1f5e3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -686,7 +686,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) if (vf->port_vlan_id) i40e_vsi_add_pvid(vsi, vf->port_vlan_id); - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); if (is_valid_ether_addr(vf->default_lan_addr.addr)) { f = i40e_add_filter(vsi, vf->default_lan_addr.addr, vf->port_vlan_id ? @@ -696,7 +696,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) "Could not add MAC filter %pM for VF %d\n", vf->default_lan_addr.addr, vf->vf_id); } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); i40e_write_rx_ctl(&pf->hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)hena); i40e_write_rx_ctl(&pf->hw, I40E_VFQF_HENA1(1, vf->vf_id), @@ -1449,9 +1449,9 @@ static void i40e_vc_reset_vf_msg(struct i40e_vf *vf) static inline int i40e_getnum_vf_vsi_vlan_filters(struct i40e_vsi *vsi) { struct i40e_mac_filter *f; - int num_vlans = 0; + int num_vlans = 0, bkt; - list_for_each_entry(f, &vsi->mac_filter_list, list) { + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) num_vlans++; } @@ -1481,6 +1481,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, struct i40e_vsi *vsi; bool alluni = false; int aq_err = 0; + int bkt; vsi = i40e_find_vsi_from_id(pf, info->vsi_id); if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || @@ -1507,7 +1508,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, vf->port_vlan_id, NULL); } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { - list_for_each_entry(f, &vsi->mac_filter_list, list) { + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { if (f->vlan < 0 || f->vlan > I40E_MAX_VLANID) continue; aq_ret = i40e_aq_set_vsi_mc_promisc_on_vlan(hw, @@ -1557,7 +1558,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, vf->port_vlan_id, NULL); } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { - list_for_each_entry(f, &vsi->mac_filter_list, list) { + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { aq_ret = 0; if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) { aq_ret = @@ -1927,7 +1928,7 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) /* Lock once, because all function inside for loop accesses VSI's * MAC filter list which needs to be protected using same lock. */ - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); /* add new addresses to the list */ for (i = 0; i < al->num_elements; i++) { @@ -1946,13 +1947,13 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) "Unable to add MAC filter %pM for VF %d\n", al->list[i].addr, vf->vf_id); ret = I40E_ERR_PARAM; - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); goto error_param; } else { vf->num_mac++; } } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); /* program the updated filter list */ ret = i40e_sync_vsi_filters(vsi); @@ -2001,18 +2002,18 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) } vsi = pf->vsi[vf->lan_vsi_idx]; - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); /* delete addresses from the list */ for (i = 0; i < al->num_elements; i++) if (i40e_del_mac_all_vlan(vsi, al->list[i].addr)) { ret = I40E_ERR_INVALID_MAC_ADDR; - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); goto error_param; } else { vf->num_mac--; } - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); /* program the updated filter list */ ret = i40e_sync_vsi_filters(vsi); @@ -2687,6 +2688,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) struct i40e_mac_filter *f; struct i40e_vf *vf; int ret = 0; + int bkt; /* validate the request */ if (vf_id >= pf->num_alloc_vfs) { @@ -2713,9 +2715,9 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) } /* Lock once because below invoked function add/del_filter requires - * mac_filter_list_lock to be held + * mac_filter_hash_lock to be held */ - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); /* delete the temporary mac address */ if (!is_zero_ether_addr(vf->default_lan_addr.addr)) @@ -2725,10 +2727,10 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) /* Delete all the filters for this VSI - we're going to kill it * anyway. */ - list_for_each_entry(f, &vsi->mac_filter_list, list) + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) i40e_del_filter(vsi, f->macaddr, f->vlan); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id); /* program mac filter */ @@ -2800,9 +2802,9 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, /* duplicate request, so just return success */ goto error_pvid; - spin_lock_bh(&vsi->mac_filter_list_lock); + spin_lock_bh(&vsi->mac_filter_hash_lock); is_vsi_in_vlan = i40e_is_vsi_in_vlan(vsi); - spin_unlock_bh(&vsi->mac_filter_list_lock); + spin_unlock_bh(&vsi->mac_filter_hash_lock); if (le16_to_cpu(vsi->info.pvid) == 0 && is_vsi_in_vlan) { dev_err(&pf->pdev->dev, -- cgit v1.1 From 4a2ce27bb5f291c38d8e49ef16899828289e4d3d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:38 -0700 Subject: i40e: properly cleanup on allocation failure in i40e_sync_vsi_filters Currently, we fail to correctly restore filters on the temporary add list when we fail to allocate memory either for deletion or addition. Replace calls to "goto out;" with calls to a new location that correctly handles memory allocation failures. Note that it is safe for us to call i40e_undo_filter_entries on the tmp_del_list even after we've deleted filters because at this point it will be empty, so we don't need to separate the logic for add and delete failure. Change-Id: Iee107fd219c6e03e2fd9645c2debf8e8384a8521 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 39 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index ea4dc9f..35f06ba 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1683,15 +1683,16 @@ static void i40e_set_rx_mode(struct net_device *netdev) } /** - * i40e_undo_del_filter_entries - Undo the changes made to MAC filter entries - * @vsi: pointer to vsi struct + * i40e_undo_filter_entries - Undo the changes made to MAC filter entries + * @vsi: Pointer to VSI struct * @from: Pointer to list which contains MAC filter entries - changes to * those entries needs to be undone. * - * MAC filter entries from list were slated to be removed from device. + * MAC filter entries from list were slated to be sent to firmware, either for + * addition or deletion. **/ -static void i40e_undo_del_filter_entries(struct i40e_vsi *vsi, - struct hlist_head *from) +static void i40e_undo_filter_entries(struct i40e_vsi *vsi, + struct hlist_head *from) { struct i40e_mac_filter *f; struct hlist_node *h; @@ -1840,14 +1841,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) list_size = filter_list_len * sizeof(struct i40e_aqc_remove_macvlan_element_data); del_list = kzalloc(list_size, GFP_ATOMIC); - if (!del_list) { - /* Undo VSI's MAC filter entry element updates */ - spin_lock_bh(&vsi->mac_filter_hash_lock); - i40e_undo_del_filter_entries(vsi, &tmp_del_list); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - retval = -ENOMEM; - goto out; - } + if (!del_list) + goto err_no_memory; hlist_for_each_entry_safe(f, h, &tmp_del_list, hlist) { cmd_flags = 0; @@ -1924,10 +1919,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) list_size = filter_list_len * sizeof(struct i40e_aqc_add_macvlan_element_data); add_list = kzalloc(list_size, GFP_ATOMIC); - if (!add_list) { - retval = -ENOMEM; - goto out; - } + if (!add_list) + goto err_no_memory; + num_add = 0; hlist_for_each_entry(f, &tmp_add_list, hlist) { if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, @@ -2152,6 +2146,17 @@ out: clear_bit(__I40E_CONFIG_BUSY, &vsi->state); return retval; + +err_no_memory: + /* Restore elements on the temporary add and delete lists */ + spin_lock_bh(&vsi->mac_filter_hash_lock); + i40e_undo_filter_entries(vsi, &tmp_del_list); + i40e_undo_filter_entries(vsi, &tmp_add_list); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; + clear_bit(__I40E_CONFIG_BUSY, &vsi->state); + return -ENOMEM; } /** -- cgit v1.1 From 84f5ca6cf4ea1d91fa9ee858ff95211127b0166b Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Wed, 5 Oct 2016 09:30:39 -0700 Subject: i40e: fix MAC filters when removing VLANs Currently there exists a bug where adding at least one VLAN and then removing all VLANs leaves the mac filters for the VSI with an incorrect value for 'vid' which indicates the mac filter's VLAN status. The current implementation for handling the removal of VLANs is wrong for a couple reasons. The first is that when i40e_vsi_kill_vlan iterates through the MAC filters, it fails to account for the MAC filter status; i.e. it's not accommodating for filters that are about to be deleted. The second problem is that MAC filters can be deleted in other places (specifically i40e_set_rx_mode). Thus if it occurs that all the VLAN MAC filters get deleted we need to switch out of VLAN mode, but the code path through i40e_vsi_kill_vlan has already been executed and we're now stuck in VLAN mode. This patch fixes the issue by removing the check from i40e_vsi_kill_vlan and puts the check instead in i40e_sync_vsi_filters where we're guaranteed to see all filter deletions and can properly detect when we need to switch out of VLAN mode. Change-ID: Ib38fe6034b356eee9a0e20b8a9eeed5ff2debcd9 Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 120 +++++++++++++++++----------- 1 file changed, 72 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 35f06ba..6cf9089 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1774,19 +1774,22 @@ i40e_update_filter_state(int count, **/ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) { - struct i40e_mac_filter *f, *add_head = NULL; struct hlist_head tmp_add_list, tmp_del_list; + struct i40e_mac_filter *f, *add_head = NULL; struct i40e_hw *hw = &vsi->back->hw; + unsigned int vlan_any_filters = 0; + unsigned int non_vlan_filters = 0; + unsigned int vlan_filters = 0; bool promisc_changed = false; char vsi_name[16] = "PF"; int filter_list_len = 0; - u32 changed_flags = 0; i40e_status aq_ret = 0; + u32 changed_flags = 0; struct hlist_node *h; - int retval = 0; struct i40e_pf *pf; int num_add = 0; int num_del = 0; + int retval = 0; int aq_err = 0; u16 cmd_flags; int list_size; @@ -1825,11 +1828,75 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) hash_del(&f->hlist); hlist_add_head(&f->hlist, &tmp_del_list); vsi->active_filters--; + + /* Avoid counting removed filters */ + continue; } if (f->state == I40E_FILTER_NEW) { hash_del(&f->hlist); hlist_add_head(&f->hlist, &tmp_add_list); } + + /* Count the number of each type of filter we have + * remaining, ignoring any filters we're about to + * delete. + */ + if (f->vlan > 0) + vlan_filters++; + else if (!f->vlan) + non_vlan_filters++; + else + vlan_any_filters++; + } + + /* We should never have VLAN=-1 filters at the same time as we + * have either VLAN=0 or VLAN>0 filters, so warn about this + * case here to help catch any issues. + */ + WARN_ON(vlan_any_filters && (vlan_filters + non_vlan_filters)); + + /* If we only have VLAN=0 filters remaining, and don't have + * any other VLAN filters, we need to convert these VLAN=0 + * filters into VLAN=-1 (I40E_VLAN_ANY) so that we operate + * correctly in non-VLAN mode and receive all traffic tagged + * or untagged. + */ + if (non_vlan_filters && !vlan_filters) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, + hlist) { + /* Only replace VLAN=0 filters */ + if (f->vlan) + continue; + + /* Allocate a replacement element */ + add_head = kzalloc(sizeof(*add_head), + GFP_KERNEL); + if (!add_head) + goto err_no_memory_locked; + + /* Copy the filter, with new state and VLAN */ + *add_head = *f; + add_head->state = I40E_FILTER_NEW; + add_head->vlan = I40E_VLAN_ANY; + + /* Move the replacement to the add list */ + INIT_HLIST_NODE(&add_head->hlist); + hlist_add_head(&add_head->hlist, + &tmp_add_list); + + /* Move the original to the delete list */ + f->state = I40E_FILTER_REMOVE; + hash_del(&f->hlist); + hlist_add_head(&f->hlist, &tmp_del_list); + vsi->active_filters--; + } + + /* Also update any filters on the tmp_add list */ + hlist_for_each_entry(f, &tmp_add_list, hlist) { + if (!f->vlan) + f->vlan = I40E_VLAN_ANY; + } + add_head = NULL; } spin_unlock_bh(&vsi->mac_filter_hash_lock); } @@ -2150,6 +2217,7 @@ out: err_no_memory: /* Restore elements on the temporary add and delete lists */ spin_lock_bh(&vsi->mac_filter_hash_lock); +err_no_memory_locked: i40e_undo_filter_entries(vsi, &tmp_del_list); i40e_undo_filter_entries(vsi, &tmp_add_list); spin_unlock_bh(&vsi->mac_filter_hash_lock); @@ -2403,9 +2471,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) { struct net_device *netdev = vsi->netdev; - struct i40e_mac_filter *f, *add_f; + struct i40e_mac_filter *f; struct hlist_node *h; - int filter_count = 0; int bkt; /* Locked once because all functions invoked below iterates list */ @@ -2419,49 +2486,6 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) __i40e_del_filter(vsi, f); } - /* go through all the filters for this VSI and if there is only - * vid == 0 it means there are no other filters, so vid 0 must - * be replaced with -1. This signifies that we should from now - * on accept any traffic (with any tag present, or untagged) - */ - hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - if (vsi->netdev) { - if (f->vlan && - ether_addr_equal(netdev->dev_addr, f->macaddr)) - filter_count++; - } - - if (f->vlan) - filter_count++; - } - - if (!filter_count && vsi->netdev) { - i40e_del_filter(vsi, netdev->dev_addr, 0); - f = i40e_add_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY); - if (!f) { - dev_info(&vsi->back->pdev->dev, - "Could not add filter %d for %pM\n", - I40E_VLAN_ANY, netdev->dev_addr); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - return -ENOMEM; - } - } - - if (!filter_count) { - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { - if (!f->vlan) - __i40e_del_filter(vsi, f); - add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY); - if (!add_f) { - dev_info(&vsi->back->pdev->dev, - "Could not add filter %d for %pM\n", - I40E_VLAN_ANY, f->macaddr); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - return -ENOMEM; - } - } - } - spin_unlock_bh(&vsi->mac_filter_hash_lock); /* schedule our worker thread which will take care of -- cgit v1.1 From cbebb85f306f6023ebaa26ca66380571b4b680bf Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:40 -0700 Subject: i40e: avoid looping to check whether we're in VLAN mode We determine that a VSI is in vlan_mode whenever it has any filters with a VLAN other than -1 (I40E_VLAN_ALL). The previous method of doing so was to perform a loop whenever we needed the check. However, we can notice that only place where filters are added (i40e_add_filter) can change the condition from false to true, and the only place we can return to false is in i40e_vsi_sync_filters_subtask. Thus, we can remove the loop and use a boolean directly. Doing this avoids looping over filters repeatedly especially while we're already inside a loop over all the filters. This should reduce the latency of filter operations throughout the driver. Change-ID: Iafde08df588da2a2ea666997d05e11fad8edc338 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_main.c | 48 ++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 6545550..60dbb5b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -517,6 +517,7 @@ struct i40e_vsi { spinlock_t mac_filter_hash_lock; /* Fixed size hash table with 2^8 buckets for MAC filters */ DECLARE_HASHTABLE(mac_filter_hash, 8); + bool has_vlan_filter; /* VSI stats */ struct rtnl_link_stats64 net_stats; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 6cf9089..b4529b5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1198,19 +1198,31 @@ struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr) **/ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) { - struct i40e_mac_filter *f; - struct hlist_node *h; - int bkt; + /* If we have a PVID, always operate in VLAN mode */ + if (vsi->info.pvid) + return true; - /* Only -1 for all the filters denotes not in vlan mode - * so we have to go through all the list in order to make sure + /* We need to operate in VLAN mode whenever we have any filters with + * a VLAN other than I40E_VLAN_ALL. We could check the table each + * time, incurring search cost repeatedly. However, we can notice two + * things: + * + * 1) the only place where we can gain a VLAN filter is in + * i40e_add_filter. + * + * 2) the only place where filters are actually removed is in + * i40e_vsi_sync_filters_subtask. + * + * Thus, we can simply use a boolean value, has_vlan_filters which we + * will set to true when we add a VLAN filter in i40e_add_filter. Then + * we have to perform the full search after deleting filters in + * i40e_vsi_sync_filters_subtask, but we already have to search + * filters here and can perform the check at the same time. This + * results in avoiding embedding a loop for VLAN mode inside another + * loop over all the filters, and should maintain correctness as noted + * above. */ - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { - if (f->vlan >= 0 || vsi->info.pvid) - return true; - } - - return false; + return vsi->has_vlan_filter; } /** @@ -1246,6 +1258,12 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, if (!f) return NULL; + /* Update the boolean indicating if we need to function in + * VLAN mode. + */ + if (vlan >= 0) + vsi->has_vlan_filter = true; + ether_addr_copy(f->macaddr, macaddr); f->vlan = vlan; /* If we're in overflow promisc mode, set the state directly @@ -1979,6 +1997,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) del_list = NULL; } + /* After finishing notifying firmware of the deleted filters, update + * the cached value of vsi->has_vlan_filter. Note that we are safe to + * use just !!vlan_filters here because if we only have VLAN=0 (that + * is, non_vlan_filters) these will all be converted to VLAN=-1 in the + * logic above already so this value would still be correct. + */ + vsi->has_vlan_filter = !!vlan_filters; + if (!hlist_empty(&tmp_add_list)) { /* Do all the adds now. */ filter_list_len = hw->aq.asq_buf_size / -- cgit v1.1 From 0093631966aad046ddf21e157a43765c295e16a3 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:41 -0700 Subject: i40e: remove duplicate add/delete adminq command code for filters We duplicate some code around adding and deleting filters using the adminq interface. This is prone to errors in case there are bugs. Use functions which extract the logic to their own portion so that we don't duplicate it twice in code. Change-ID: I60d68aeb887976787dec00b23ab386a106e61465 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 156 +++++++++++++++------------- 1 file changed, 84 insertions(+), 72 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b4529b5..5fe54e5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1783,6 +1783,80 @@ i40e_update_filter_state(int count, } /** + * i40e_aqc_del_filters - Request firmware to delete a set of filters + * @vsi: ptr to the VSI + * @vsi_name: name to display in messages + * @list: the list of filters to send to firmware + * @num_del: the number of filters to delete + * @retval: Set to -EIO on failure to delete + * + * Send a request to firmware via AdminQ to delete a set of filters. Uses + * *retval instead of a return value so that success does not force ret_val to + * be set to 0. This ensures that a sequence of calls to this function + * preserve the previous value of *retval on successful delete. + */ +static +void i40e_aqc_del_filters(struct i40e_vsi *vsi, const char *vsi_name, + struct i40e_aqc_remove_macvlan_element_data *list, + int num_del, int *retval) +{ + struct i40e_hw *hw = &vsi->back->hw; + i40e_status aq_ret; + int aq_err; + + aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid, list, num_del, NULL); + aq_err = hw->aq.asq_last_status; + + /* Explicitly ignore and do not report when firmware returns ENOENT */ + if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) { + *retval = -EIO; + dev_info(&vsi->back->pdev->dev, + "ignoring delete macvlan error on %s, err %s, aq_err %s\n", + vsi_name, i40e_stat_str(hw, aq_ret), + i40e_aq_str(hw, aq_err)); + } +} + +/** + * i40e_aqc_add_filters - Request firmware to add a set of filters + * @vsi: ptr to the VSI + * @vsi_name: name to display in messages + * @list: the list of filters to send to firmware + * @add_head: Position in the add hlist + * @num_add: the number of filters to add + * @promisc_change: set to true on exit if promiscuous mode was forced on + * + * Send a request to firmware via AdminQ to add a chunk of filters. Will set + * promisc_changed to true if the firmware has run out of space for more + * filters. + */ +static +void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, + struct i40e_aqc_add_macvlan_element_data *list, + struct i40e_mac_filter *add_head, + int num_add, bool *promisc_changed) +{ + struct i40e_hw *hw = &vsi->back->hw; + i40e_status aq_ret; + int aq_err, fcnt; + + aq_ret = i40e_aq_add_macvlan(hw, vsi->seid, list, num_add, NULL); + aq_err = hw->aq.asq_last_status; + fcnt = i40e_update_filter_state(num_add, list, add_head, aq_ret); + vsi->active_filters += fcnt; + + if (fcnt != num_add) { + *promisc_changed = true; + set_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); + vsi->promisc_threshold = (vsi->active_filters * 3) / 4; + dev_warn(&vsi->back->pdev->dev, + "Error %s adding RX filters on %s, promiscuous mode forced on\n", + i40e_aq_str(hw, aq_err), + vsi_name); + } +} + +/** * i40e_sync_vsi_filters - Update the VSI filter list to the HW * @vsi: ptr to the VSI * @@ -1808,10 +1882,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) int num_add = 0; int num_del = 0; int retval = 0; - int aq_err = 0; u16 cmd_flags; int list_size; - int fcnt; int bkt; /* empty array typed pointers, kcalloc later */ @@ -1948,24 +2020,10 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* flush a full buffer */ if (num_del == filter_list_len) { - aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid, - del_list, - num_del, NULL); - aq_err = hw->aq.asq_last_status; - num_del = 0; + i40e_aqc_del_filters(vsi, vsi_name, del_list, + num_del, &retval); memset(del_list, 0, list_size); - - /* Explicitly ignore and do not report when - * firmware returns ENOENT. - */ - if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) { - retval = -EIO; - dev_info(&pf->pdev->dev, - "ignoring delete macvlan error on %s, err %s, aq_err %s\n", - vsi_name, - i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, aq_err)); - } + num_del = 0; } /* Release memory for MAC filter entries which were * synced up with HW. @@ -1975,22 +2033,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if (num_del) { - aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid, del_list, - num_del, NULL); - aq_err = hw->aq.asq_last_status; - num_del = 0; - - /* Explicitly ignore and do not report when firmware - * returns ENOENT. - */ - if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) { - retval = -EIO; - dev_info(&pf->pdev->dev, - "ignoring delete macvlan error on %s, err %s aq_err %s\n", - vsi_name, - i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, aq_err)); - } + i40e_aqc_del_filters(vsi, vsi_name, del_list, + num_del, &retval); } kfree(del_list); @@ -2041,48 +2085,16 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* flush a full buffer */ if (num_add == filter_list_len) { - aq_ret = i40e_aq_add_macvlan(hw, vsi->seid, - add_list, num_add, - NULL); - aq_err = hw->aq.asq_last_status; - fcnt = i40e_update_filter_state(num_add, - add_list, - add_head, - aq_ret); - vsi->active_filters += fcnt; - - if (fcnt != num_add) { - promisc_changed = true; - set_bit(__I40E_FILTER_OVERFLOW_PROMISC, - &vsi->state); - vsi->promisc_threshold = - (vsi->active_filters * 3) / 4; - dev_warn(&pf->pdev->dev, - "Error %s adding RX filters on %s, promiscuous mode forced on\n", - i40e_aq_str(hw, aq_err), - vsi_name); - } + i40e_aqc_add_filters(vsi, vsi_name, add_list, + add_head, num_add, + &promisc_changed); memset(add_list, 0, list_size); num_add = 0; } } if (num_add) { - aq_ret = i40e_aq_add_macvlan(hw, vsi->seid, - add_list, num_add, NULL); - aq_err = hw->aq.asq_last_status; - fcnt = i40e_update_filter_state(num_add, add_list, - add_head, aq_ret); - vsi->active_filters += fcnt; - if (fcnt != num_add) { - promisc_changed = true; - set_bit(__I40E_FILTER_OVERFLOW_PROMISC, - &vsi->state); - vsi->promisc_threshold = - (vsi->active_filters * 3) / 4; - dev_warn(&pf->pdev->dev, - "Error %s adding RX filters on %s, promiscuous mode forced on\n", - i40e_aq_str(hw, aq_err), vsi_name); - } + i40e_aqc_add_filters(vsi, vsi_name, add_list, add_head, + num_add, &promisc_changed); } /* Now move all of the filters from the temp add list back to * the VSI's list. -- cgit v1.1 From 144ed1763003c6a88a4b788cc5da1d8a1ddf061f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:42 -0700 Subject: i40e: correct check for reading TSYNINDX from the receive descriptor When hardware has taken a timestamp for a received packet, it indicates which RXTIME register the timestamp was placed in by some bits in the receive descriptor. It uses 3 bits, one to indicate if the descriptor index is valid (ie: there was a timestamp) and 2 bits to indicate which of the 4 registers to read. However, the driver currently does not check the TSYNVALID bit and only checks the index. It assumes a zero index means no timestamp, and a non zero index means a timestamp occurred. While this appears to be true, it prevents ever reading a timestamp in RXTIME[0], and causes the first timestamp the device captures to be ignored. Fix this by using the TSYNVALID bit correctly as the true indicator of whether the packet has an associated timestamp. Also rename the variable rsyn to tsyn as this is more descriptive and matches the register names. Change-ID: I4437e8f3a3df2c2ddb458b0fb61420f3dafc4c12 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index daade4fe..c9eb6b8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1410,11 +1410,12 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring, u64 qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); u32 rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> I40E_RXD_QW1_STATUS_SHIFT; - u32 rsyn = (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> + u32 tsynvalid = rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK; + u32 tsyn = (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT; - if (unlikely(rsyn)) { - i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, rsyn); + if (unlikely(tsynvalid)) { + i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, tsyn); rx_ring->last_rx_timestamp = jiffies; } -- cgit v1.1 From 195512629c956286265dcfeeea2028c42ee0c483 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:43 -0700 Subject: i40e: use a mutex instead of spinlock in PTP user entry points We need a locking mechanism to protect the hardware SYSTIME register which is split over 2 values, and has internal hardware latching. We can't allow multiple accesses at the same time. However.... The spinlock_t is overkill here, especially use of spin_lock_irqsave, since every PTP access will halt hardirqs. Notice that the only places which need the SYSTIME value are user context and are capable of sleeping. Thus, it is safe to use a mutex here instead of the spinlock. Change-ID: I971761a89b58c6aad953590162e85a327fbba232 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 20 +++++++------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 60dbb5b..21f2bb3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -430,7 +430,7 @@ struct i40e_pf { struct sk_buff *ptp_tx_skb; struct hwtstamp_config tstamp_config; unsigned long last_rx_ptp_check; - spinlock_t tmreg_lock; /* Used to protect the device time registers. */ + struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */ u64 ptp_base_adj; u32 tx_hwtstamp_timeouts; u32 rx_hwtstamp_cleared; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index f1fecea..177b7fb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -159,16 +159,15 @@ static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); struct timespec64 now, then; - unsigned long flags; then = ns_to_timespec64(delta); - spin_lock_irqsave(&pf->tmreg_lock, flags); + mutex_lock(&pf->tmreg_lock); i40e_ptp_read(pf, &now); now = timespec64_add(now, then); i40e_ptp_write(pf, (const struct timespec64 *)&now); - spin_unlock_irqrestore(&pf->tmreg_lock, flags); + mutex_unlock(&pf->tmreg_lock); return 0; } @@ -184,11 +183,10 @@ static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) static int i40e_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) { struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); - unsigned long flags; - spin_lock_irqsave(&pf->tmreg_lock, flags); + mutex_lock(&pf->tmreg_lock); i40e_ptp_read(pf, ts); - spin_unlock_irqrestore(&pf->tmreg_lock, flags); + mutex_unlock(&pf->tmreg_lock); return 0; } @@ -205,11 +203,10 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) { struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); - unsigned long flags; - spin_lock_irqsave(&pf->tmreg_lock, flags); + mutex_lock(&pf->tmreg_lock); i40e_ptp_write(pf, ts); - spin_unlock_irqrestore(&pf->tmreg_lock, flags); + mutex_unlock(&pf->tmreg_lock); return 0; } @@ -658,10 +655,7 @@ void i40e_ptp_init(struct i40e_pf *pf) return; } - /* we have to initialize the lock first, since we can't control - * when the user will enter the PHC device entry points - */ - spin_lock_init(&pf->tmreg_lock); + mutex_init(&pf->tmreg_lock); /* ensure we have a clock device */ err = i40e_ptp_create_clock(pf); -- cgit v1.1 From 124905012db8bdeebf5a7d1ddc841eaadda84a75 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:44 -0700 Subject: i40e: replace PTP Rx timestamp hang logic The current Rx timestamp hang logic is not very robust because it does not notice a register is hung until all four timestamps have been latched and we wait a full 5 seconds. Replace this logic with a newer Rx hang detection based on storing the jiffies when we first notice a receive timestamp event. We store each register's time separately, along with a flag indicating if it is currently latched. Upon first transitioning to latch, we will update the latch_events[i] jiffies value. This indicates the time we first noticed this event. The watchdog routine will simply check that the either the flag has been cleared, or we have passed at least one second. In this case, it is able to clear the Rx timestamp register under the assumption that it was for a dropped frame. The benefit if this strategy is that we should be able to detect and clear out stalled RXTIME_H registers before we exhaust the supply of 4, and avoid complete stall of Rx timestamp events. Change-ID: Id55458c0cd7a5dd0c951ff2b8ac0b2509364131f Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 4 +- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 117 +++++++++++++++++++--------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 4 +- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 2 - 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 21f2bb3..9121e46 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -429,11 +429,13 @@ struct i40e_pf { struct ptp_clock_info ptp_caps; struct sk_buff *ptp_tx_skb; struct hwtstamp_config tstamp_config; - unsigned long last_rx_ptp_check; struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */ u64 ptp_base_adj; u32 tx_hwtstamp_timeouts; u32 rx_hwtstamp_cleared; + u32 latch_event_flags; + spinlock_t ptp_rx_lock; /* Used to protect Rx timestamp registers. */ + unsigned long latch_events[4]; bool ptp_tx; bool ptp_rx; u16 rss_table_size; /* HW RSS table size */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 177b7fb..5e2272c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -227,6 +227,47 @@ static int i40e_ptp_feature_enable(struct ptp_clock_info *ptp, } /** + * i40e_ptp_update_latch_events - Read I40E_PRTTSYN_STAT_1 and latch events + * @pf: the PF data structure + * + * This function reads I40E_PRTTSYN_STAT_1 and updates the corresponding timers + * for noticed latch events. This allows the driver to keep track of the first + * time a latch event was noticed which will be used to help clear out Rx + * timestamps for packets that got dropped or lost. + * + * This function will return the current value of I40E_PRTTSYN_STAT_1 and is + * expected to be called only while under the ptp_rx_lock. + **/ +static u32 i40e_ptp_get_rx_events(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + u32 prttsyn_stat, new_latch_events; + int i; + + prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_1); + new_latch_events = prttsyn_stat & ~pf->latch_event_flags; + + /* Update the jiffies time for any newly latched timestamp. This + * ensures that we store the time that we first discovered a timestamp + * was latched by the hardware. The service task will later determine + * if we should free the latch and drop that timestamp should too much + * time pass. This flow ensures that we only update jiffies for new + * events latched since the last time we checked, and not all events + * currently latched, so that the service task accounting remains + * accurate. + */ + for (i = 0; i < 4; i++) { + if (new_latch_events & BIT(i)) + pf->latch_events[i] = jiffies; + } + + /* Finally, we store the current status of the Rx timestamp latches */ + pf->latch_event_flags = prttsyn_stat; + + return prttsyn_stat; +} + +/** * i40e_ptp_rx_hang - Detect error case when Rx timestamp registers are hung * @vsi: The VSI with the rings relevant to 1588 * @@ -239,10 +280,7 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; - struct i40e_ring *rx_ring; - unsigned long rx_event; - u32 prttsyn_stat; - int n; + int i; /* Since we cannot turn off the Rx timestamp logic if the device is * configured for Tx timestamping, we check if Rx timestamping is @@ -252,42 +290,30 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi) if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_rx) return; - prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_1); + spin_lock_bh(&pf->ptp_rx_lock); - /* Unless all four receive timestamp registers are latched, we are not - * concerned about a possible PTP Rx hang, so just update the timeout - * counter and exit. - */ - if (!(prttsyn_stat & ((I40E_PRTTSYN_STAT_1_RXT0_MASK << - I40E_PRTTSYN_STAT_1_RXT0_SHIFT) | - (I40E_PRTTSYN_STAT_1_RXT1_MASK << - I40E_PRTTSYN_STAT_1_RXT1_SHIFT) | - (I40E_PRTTSYN_STAT_1_RXT2_MASK << - I40E_PRTTSYN_STAT_1_RXT2_SHIFT) | - (I40E_PRTTSYN_STAT_1_RXT3_MASK << - I40E_PRTTSYN_STAT_1_RXT3_SHIFT)))) { - pf->last_rx_ptp_check = jiffies; - return; - } + /* Update current latch times for Rx events */ + i40e_ptp_get_rx_events(pf); - /* Determine the most recent watchdog or rx_timestamp event. */ - rx_event = pf->last_rx_ptp_check; - for (n = 0; n < vsi->num_queue_pairs; n++) { - rx_ring = vsi->rx_rings[n]; - if (time_after(rx_ring->last_rx_timestamp, rx_event)) - rx_event = rx_ring->last_rx_timestamp; + /* Check all the currently latched Rx events and see whether they have + * been latched for over a second. It is assumed that any timestamp + * should have been cleared within this time, or else it was captured + * for a dropped frame that the driver never received. Thus, we will + * clear any timestamp that has been latched for over 1 second. + */ + for (i = 0; i < 4; i++) { + if ((pf->latch_event_flags & BIT(i)) && + time_is_before_jiffies(pf->latch_events[i] + HZ)) { + rd32(hw, I40E_PRTTSYN_RXTIME_H(i)); + pf->latch_event_flags &= ~BIT(i); + pf->rx_hwtstamp_cleared++; + dev_warn(&pf->pdev->dev, + "Clearing a missed Rx timestamp event for RXTIME[%d]\n", + i); + } } - /* Only need to read the high RXSTMP register to clear the lock */ - if (time_is_before_jiffies(rx_event + 5 * HZ)) { - rd32(hw, I40E_PRTTSYN_RXTIME_H(0)); - rd32(hw, I40E_PRTTSYN_RXTIME_H(1)); - rd32(hw, I40E_PRTTSYN_RXTIME_H(2)); - rd32(hw, I40E_PRTTSYN_RXTIME_H(3)); - pf->last_rx_ptp_check = jiffies; - pf->rx_hwtstamp_cleared++; - WARN_ONCE(1, "Detected Rx timestamp register hang\n"); - } + spin_unlock_bh(&pf->ptp_rx_lock); } /** @@ -350,14 +376,25 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct sk_buff *skb, u8 index) hw = &pf->hw; - prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_1); + spin_lock_bh(&pf->ptp_rx_lock); + + /* Get current Rx events and update latch times */ + prttsyn_stat = i40e_ptp_get_rx_events(pf); - if (!(prttsyn_stat & BIT(index))) + /* TODO: Should we warn about missing Rx timestamp event? */ + if (!(prttsyn_stat & BIT(index))) { + spin_unlock_bh(&pf->ptp_rx_lock); return; + } + + /* Clear the latched event since we're about to read its register */ + pf->latch_event_flags &= ~BIT(index); lo = rd32(hw, I40E_PRTTSYN_RXTIME_L(index)); hi = rd32(hw, I40E_PRTTSYN_RXTIME_H(index)); + spin_unlock_bh(&pf->ptp_rx_lock); + ns = (((u64)hi) << 32) | lo; i40e_ptp_convert_to_hwtstamp(skb_hwtstamps(skb), ns); @@ -511,12 +548,15 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, } /* Clear out all 1588-related registers to clear and unlatch them. */ + spin_lock_bh(&pf->ptp_rx_lock); rd32(hw, I40E_PRTTSYN_STAT_0); rd32(hw, I40E_PRTTSYN_TXTIME_H); rd32(hw, I40E_PRTTSYN_RXTIME_H(0)); rd32(hw, I40E_PRTTSYN_RXTIME_H(1)); rd32(hw, I40E_PRTTSYN_RXTIME_H(2)); rd32(hw, I40E_PRTTSYN_RXTIME_H(3)); + pf->latch_event_flags = 0; + spin_unlock_bh(&pf->ptp_rx_lock); /* Enable/disable the Tx timestamp interrupt based on user input. */ regval = rd32(hw, I40E_PRTTSYN_CTL0); @@ -656,6 +696,7 @@ void i40e_ptp_init(struct i40e_pf *pf) } mutex_init(&pf->tmreg_lock); + spin_lock_init(&pf->ptp_rx_lock); /* ensure we have a clock device */ err = i40e_ptp_create_clock(pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index c9eb6b8..783ac4e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1414,10 +1414,8 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring, u32 tsyn = (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT; - if (unlikely(tsynvalid)) { + if (unlikely(tsynvalid)) i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, tsyn); - rx_ring->last_rx_timestamp = jiffies; - } i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 5088405..42f04d6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -307,8 +307,6 @@ struct i40e_ring { u8 atr_sample_rate; u8 atr_count; - unsigned long last_rx_timestamp; - bool ring_active; /* is ring online or not */ bool arm_wb; /* do something to arm write back */ u8 packet_stride; -- cgit v1.1 From 7d3f04af69bed2db87d6a72d100e182f773e54b7 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Oct 2016 09:30:45 -0700 Subject: i40evf: avoid an extra msleep while Remove the second call to msleep outside the loop, and move the msleep within the loop as the first step. This guarantees that a single loop will wait the minimum time first, and then after the reset finishes we no longer need an extra msleep. Change-ID: Ib2086f0a142402b614f67846bc091754203a0b9a Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index bcb1caf..db36744 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1746,15 +1746,17 @@ static void i40evf_reset_task(struct work_struct *work) /* wait until the reset is complete and the PF is responding to us */ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { + /* sleep first to make sure a minimum wait time is met */ + msleep(I40EVF_RESET_WAIT_MS); + reg_val = rd32(hw, I40E_VFGEN_RSTAT) & I40E_VFGEN_RSTAT_VFR_STATE_MASK; if (reg_val == I40E_VFR_VFACTIVE) break; - msleep(I40EVF_RESET_WAIT_MS); } + pci_set_master(adapter->pdev); - /* extra wait to make sure minimum wait is met */ - msleep(I40EVF_RESET_WAIT_MS); + if (i == I40EVF_RESET_WAIT_COUNT) { struct i40evf_mac_filter *ftmp; struct i40evf_vlan_filter *fv, *fvtmp; -- cgit v1.1 From 4b8164467b854fbeb7ecbb14cf53b6be9113ef03 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 11 Oct 2016 15:26:53 -0700 Subject: i40e: Add common function for finding VSI by type This patch adds a common method for finding a VSI by type. The main motivation for doing this is that the Flow Director path actually had two ways of handling this, one stopped on first match and one did not. This patch makes it so that all callers of this function will get the same approach for finding a VSI. Change-ID: Ibf25de8acd8466582520694424aa87da66965fbd Signed-off-by: Alexander Duyck Signed-off-by: Bimmy Pujari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 19 +++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 12 +----------- drivers/net/ethernet/intel/i40e/i40e_main.c | 21 ++++++--------------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 5 +---- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 9121e46..bb02280 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -723,6 +723,25 @@ int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size); void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, u16 rss_table_size, u16 rss_size); struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id); +/** + * i40e_find_vsi_by_type - Find and return Flow Director VSI + * @pf: PF to search for VSI + * @type: Value indicating type of VSI we are looking for + **/ +static inline struct i40e_vsi * +i40e_find_vsi_by_type(struct i40e_pf *pf, u16 type) +{ + int i; + + for (i = 0; i < pf->num_alloc_vsi; i++) { + struct i40e_vsi *vsi = pf->vsi[i]; + + if (vsi && vsi->type == type) + return vsi; + } + + return NULL; +} void i40e_update_stats(struct i40e_vsi *vsi); void i40e_update_eth_stats(struct i40e_vsi *vsi); struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index fb4fb52..962a9bb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1768,17 +1768,7 @@ static inline bool i40e_active_vfs(struct i40e_pf *pf) static inline bool i40e_active_vmdqs(struct i40e_pf *pf) { - struct i40e_vsi **vsi = pf->vsi; - int i; - - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (!vsi[i]) - continue; - if (vsi[i]->type == I40E_VSI_VMDQ2) - return true; - } - - return false; + return !!i40e_find_vsi_by_type(pf, I40E_VSI_VMDQ2); } static void i40e_diag_test(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5fe54e5..9ec977a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6718,7 +6718,6 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi); static void i40e_fdir_sb_setup(struct i40e_pf *pf) { struct i40e_vsi *vsi; - int i; /* quick workaround for an NVM issue that leaves a critical register * uninitialized @@ -6729,6 +6728,7 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf) 0xeacb7d61, 0xaa4f05b6, 0x9c5c89ed, 0xfc425ddb, 0xa4654832, 0xfc7461d4, 0x8f827619, 0xf5c63c21, 0x95b3a76d}; + int i; for (i = 0; i <= I40E_GLQF_HKEY_MAX_INDEX; i++) wr32(&pf->hw, I40E_GLQF_HKEY(i), hkey[i]); @@ -6738,13 +6738,7 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf) return; /* find existing VSI and see if it needs configuring */ - vsi = NULL; - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) { - vsi = pf->vsi[i]; - break; - } - } + vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); /* create a new VSI if none exists */ if (!vsi) { @@ -6766,15 +6760,12 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf) **/ static void i40e_fdir_teardown(struct i40e_pf *pf) { - int i; + struct i40e_vsi *vsi; i40e_fdir_filter_exit(pf); - for (i = 0; i < pf->num_alloc_vsi; i++) { - if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) { - i40e_vsi_release(pf->vsi[i]); - break; - } - } + vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); + if (vsi) + i40e_vsi_release(vsi); } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 783ac4e..75b8f5b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -125,10 +125,7 @@ static int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u16 i; /* find existing FDIR VSI */ - vsi = NULL; - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) - vsi = pf->vsi[i]; + vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); if (!vsi) return -ENOENT; -- cgit v1.1 From 1dc8b538795fcf92161cc056ceebb852cf375b17 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 11 Oct 2016 15:26:54 -0700 Subject: i40e: Reorder logic for coalescing RS bits This patch reorders the logic at the end of i40e_tx_map to address the fact that the logic was rather convoluted and much larger than it needed to be. In order to try and coalesce the code paths I have updated some of the comments and repurposed some of the variables in order to reduce unnecessary overhead. This patch does the following: 1. Quit tracking skb->xmit_more with a flag, just max out packet_stride 2. Drop tail_bump and do_rs and instead just use desc_count and td_cmd 3. Pull comments from ixgbe that make need for wmb() more explicit. Change-ID: Ic7da85ec75043c634e87fef958109789bcc6317c Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 105 +++++++++++++------------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 - drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 105 +++++++++++++------------- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 1 - 4 files changed, 108 insertions(+), 104 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 75b8f5b..5544b50 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -616,7 +616,7 @@ u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw) return 0; } -#define WB_STRIDE 0x3 +#define WB_STRIDE 4 /** * i40e_clean_tx_irq - Reclaim resources after transmit completes @@ -732,7 +732,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, unsigned int j = i40e_get_tx_pending(tx_ring, false); if (budget && - ((j / (WB_STRIDE + 1)) == 0) && (j != 0) && + ((j / WB_STRIDE) == 0) && (j > 0) && !test_bit(__I40E_DOWN, &vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; @@ -2700,9 +2700,7 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, u32 td_tag = 0; dma_addr_t dma; u16 gso_segs; - u16 desc_count = 0; - bool tail_bump = true; - bool do_rs = false; + u16 desc_count = 1; if (tx_flags & I40E_TX_FLAGS_HW_VLAN) { td_cmd |= I40E_TX_DESC_CMD_IL2TAG1; @@ -2785,8 +2783,7 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_bi = &tx_ring->tx_bi[i]; } - /* set next_to_watch value indicating a packet is present */ - first->next_to_watch = tx_desc; + netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount); i++; if (i == tx_ring->count) @@ -2794,66 +2791,72 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_ring->next_to_use = i; - netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount); i40e_maybe_stop_tx(tx_ring, DESC_NEEDED); + /* write last descriptor with EOP bit */ + td_cmd |= I40E_TX_DESC_CMD_EOP; + + /* We can OR these values together as they both are checked against + * 4 below and at this point desc_count will be used as a boolean value + * after this if/else block. + */ + desc_count |= ++tx_ring->packet_stride; + /* Algorithm to optimize tail and RS bit setting: - * if xmit_more is supported - * if xmit_more is true - * do not update tail and do not mark RS bit. - * if xmit_more is false and last xmit_more was false - * if every packet spanned less than 4 desc - * then set RS bit on 4th packet and update tail - * on every packet - * else - * update tail and set RS bit on every packet. - * if xmit_more is false and last_xmit_more was true - * update tail and set RS bit. + * if queue is stopped + * mark RS bit + * reset packet counter + * else if xmit_more is supported and is true + * advance packet counter to 4 + * reset desc_count to 0 * - * Optimization: wmb to be issued only in case of tail update. - * Also optimize the Descriptor WB path for RS bit with the same - * algorithm. + * if desc_count >= 4 + * mark RS bit + * reset packet counter + * if desc_count > 0 + * update tail * - * Note: If there are less than 4 packets + * Note: If there are less than 4 descriptors * pending and interrupts were disabled the service task will * trigger a force WB. */ - if (skb->xmit_more && - !netif_xmit_stopped(txring_txq(tx_ring))) { - tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; - tail_bump = false; - } else if (!skb->xmit_more && - !netif_xmit_stopped(txring_txq(tx_ring)) && - (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) && - (tx_ring->packet_stride < WB_STRIDE) && - (desc_count < WB_STRIDE)) { - tx_ring->packet_stride++; - } else { + if (netif_xmit_stopped(txring_txq(tx_ring))) { + goto do_rs; + } else if (skb->xmit_more) { + /* set stride to arm on next packet and reset desc_count */ + tx_ring->packet_stride = WB_STRIDE; + desc_count = 0; + } else if (desc_count >= WB_STRIDE) { +do_rs: + /* write last descriptor with RS bit set */ + td_cmd |= I40E_TX_DESC_CMD_RS; tx_ring->packet_stride = 0; - tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; - do_rs = true; } - if (do_rs) - tx_ring->packet_stride = 0; tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD : - I40E_TX_DESC_CMD_EOP) << - I40E_TXD_QW1_CMD_SHIFT); + build_ctob(td_cmd, td_offset, size, td_tag); + + /* Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. + * + * We also use this memory barrier to make certain all of the + * status bits have been updated before next_to_watch is written. + */ + wmb(); + + /* set next_to_watch value indicating a packet is present */ + first->next_to_watch = tx_desc; /* notify HW of packet */ - if (!tail_bump) { - prefetchw(tx_desc + 1); - } else { - /* Force memory writes to complete before letting h/w - * know there are new descriptors to fetch. (Only - * applicable for weak-ordered memory model archs, - * such as IA-64). - */ - wmb(); + if (desc_count) { writel(i, tx_ring->tail); + + /* we need this if more than one processor can write to our tail + * at a time, it synchronizes IO on IA64/Altix systems + */ + mmiowb(); } + return; dma_error: diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 42f04d6..de8550f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -313,7 +313,6 @@ struct i40e_ring { u16 flags; #define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) -#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2) /* stats structs */ struct i40e_queue_stats stats; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index e2d3622..c4b174af 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -150,7 +150,7 @@ u32 i40evf_get_tx_pending(struct i40e_ring *ring, bool in_sw) return 0; } -#define WB_STRIDE 0x3 +#define WB_STRIDE 4 /** * i40e_clean_tx_irq - Reclaim resources after transmit completes @@ -266,7 +266,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, unsigned int j = i40evf_get_tx_pending(tx_ring, false); if (budget && - ((j / (WB_STRIDE + 1)) == 0) && (j > 0) && + ((j / WB_STRIDE) == 0) && (j > 0) && !test_bit(__I40E_DOWN, &vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; @@ -1950,9 +1950,7 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, u32 td_tag = 0; dma_addr_t dma; u16 gso_segs; - u16 desc_count = 0; - bool tail_bump = true; - bool do_rs = false; + u16 desc_count = 1; if (tx_flags & I40E_TX_FLAGS_HW_VLAN) { td_cmd |= I40E_TX_DESC_CMD_IL2TAG1; @@ -2035,8 +2033,7 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_bi = &tx_ring->tx_bi[i]; } - /* set next_to_watch value indicating a packet is present */ - first->next_to_watch = tx_desc; + netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount); i++; if (i == tx_ring->count) @@ -2044,66 +2041,72 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_ring->next_to_use = i; - netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount); i40e_maybe_stop_tx(tx_ring, DESC_NEEDED); + /* write last descriptor with EOP bit */ + td_cmd |= I40E_TX_DESC_CMD_EOP; + + /* We can OR these values together as they both are checked against + * 4 below and at this point desc_count will be used as a boolean value + * after this if/else block. + */ + desc_count |= ++tx_ring->packet_stride; + /* Algorithm to optimize tail and RS bit setting: - * if xmit_more is supported - * if xmit_more is true - * do not update tail and do not mark RS bit. - * if xmit_more is false and last xmit_more was false - * if every packet spanned less than 4 desc - * then set RS bit on 4th packet and update tail - * on every packet - * else - * update tail and set RS bit on every packet. - * if xmit_more is false and last_xmit_more was true - * update tail and set RS bit. + * if queue is stopped + * mark RS bit + * reset packet counter + * else if xmit_more is supported and is true + * advance packet counter to 4 + * reset desc_count to 0 * - * Optimization: wmb to be issued only in case of tail update. - * Also optimize the Descriptor WB path for RS bit with the same - * algorithm. + * if desc_count >= 4 + * mark RS bit + * reset packet counter + * if desc_count > 0 + * update tail * - * Note: If there are less than 4 packets + * Note: If there are less than 4 descriptors * pending and interrupts were disabled the service task will * trigger a force WB. */ - if (skb->xmit_more && - !netif_xmit_stopped(txring_txq(tx_ring))) { - tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; - tail_bump = false; - } else if (!skb->xmit_more && - !netif_xmit_stopped(txring_txq(tx_ring)) && - (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) && - (tx_ring->packet_stride < WB_STRIDE) && - (desc_count < WB_STRIDE)) { - tx_ring->packet_stride++; - } else { + if (netif_xmit_stopped(txring_txq(tx_ring))) { + goto do_rs; + } else if (skb->xmit_more) { + /* set stride to arm on next packet and reset desc_count */ + tx_ring->packet_stride = WB_STRIDE; + desc_count = 0; + } else if (desc_count >= WB_STRIDE) { +do_rs: + /* write last descriptor with RS bit set */ + td_cmd |= I40E_TX_DESC_CMD_RS; tx_ring->packet_stride = 0; - tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; - do_rs = true; } - if (do_rs) - tx_ring->packet_stride = 0; tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD : - I40E_TX_DESC_CMD_EOP) << - I40E_TXD_QW1_CMD_SHIFT); + build_ctob(td_cmd, td_offset, size, td_tag); + + /* Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. + * + * We also use this memory barrier to make certain all of the + * status bits have been updated before next_to_watch is written. + */ + wmb(); + + /* set next_to_watch value indicating a packet is present */ + first->next_to_watch = tx_desc; /* notify HW of packet */ - if (!tail_bump) { - prefetchw(tx_desc + 1); - } else { - /* Force memory writes to complete before letting h/w - * know there are new descriptors to fetch. (Only - * applicable for weak-ordered memory model archs, - * such as IA-64). - */ - wmb(); + if (desc_count) { writel(i, tx_ring->tail); + + /* we need this if more than one processor can write to our tail + * at a time, it synchronizes IO on IA64/Altix systems + */ + mmiowb(); } + return; dma_error: diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index abcdeca..a586e19 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -309,7 +309,6 @@ struct i40e_ring { bool ring_active; /* is ring online or not */ bool arm_wb; /* do something to arm write back */ u8 packet_stride; -#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2) u16 flags; #define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) -- cgit v1.1 From 13fd3f9cc3def8b276c7913ae4acbfa2653cb198 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 11 Oct 2016 15:26:55 -0700 Subject: i40e: clear mac filter count on reset When a VF is reset, it gets a new VSI, so all of its MAC filters go away. Correctly set the number of filters to 0 when freeing VF resources. This corrects a problem with failure to add filters when the VF driver is reloaded. Change-ID: I2acbecf734287b67473bb225293e14b5096acbef Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 2f1f5e3..69e184b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -811,6 +811,7 @@ static void i40e_free_vf_res(struct i40e_vf *vf) i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]); vf->lan_vsi_idx = 0; vf->lan_vsi_id = 0; + vf->num_mac = 0; } msix_vf = pf->hw.func_caps.num_msix_vectors_vf; -- cgit v1.1 From 999b315d39906341fadcb65ee6aba7f5cf38b830 Mon Sep 17 00:00:00 2001 From: Michal Kosiarz Date: Tue, 11 Oct 2016 15:26:56 -0700 Subject: i40e: Fix for division by zero For some cases when reading from device are incorrect or image is incorrect, this part of code causes crash due to division by zero. Change-ID: I8961029a7a87b0a479995823ef8fcbf6471405e1 Signed-off-by: Michal Kosiarz Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index a475946..98791ba 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -3313,8 +3313,10 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, /* partition id is 1-based, and functions are evenly spread * across the ports as partitions */ - hw->partition_id = (hw->pf_id / hw->num_ports) + 1; - hw->num_partitions = num_functions / hw->num_ports; + if (hw->num_ports != 0) { + hw->partition_id = (hw->pf_id / hw->num_ports) + 1; + hw->num_partitions = num_functions / hw->num_ports; + } /* additional HW specific goodies that might * someday be HW version specific -- cgit v1.1 From 81fa7c97bebd6e1a52c4e059eeffe18df5b3f11f Mon Sep 17 00:00:00 2001 From: Maciej Sosin Date: Tue, 11 Oct 2016 15:26:57 -0700 Subject: i40e: Implementation of ERROR state for NVM update state machine This patch adds I40E_NVMUPD_STATE_ERROR state for NVM update. Without this patch driver has no possibility to return NVM image write failure.This state is being set when ARQ rises error. arq_last_status is also updated every time when ARQ event comes, not only on error cases. Change-ID: I67ce43ba22a240773c2821b436e96054db0b7c81 Signed-off-by: Maciej Sosin Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq.c | 4 ++-- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 16 ++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_type.h | 1 + drivers/net/ethernet/intel/i40evf/i40e_adminq.c | 4 ++-- drivers/net/ethernet/intel/i40evf/i40e_type.h | 1 + 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 738b42a..56fb272 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -964,11 +964,11 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, desc = I40E_ADMINQ_DESC(hw->aq.arq, ntc); desc_idx = ntc; + hw->aq.arq_last_status = + (enum i40e_admin_queue_err)le16_to_cpu(desc->retval); flags = le16_to_cpu(desc->flags); if (flags & I40E_AQ_FLAG_ERR) { ret_code = I40E_ERR_ADMIN_QUEUE_ERROR; - hw->aq.arq_last_status = - (enum i40e_admin_queue_err)le16_to_cpu(desc->retval); i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQRX: Event received with error 0x%X.\n", diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 954efe3..38ee18f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -722,9 +722,20 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, *((u16 *)&bytes[2]) = hw->nvm_wait_opcode; } + /* Clear error status on read */ + if (hw->nvmupd_state == I40E_NVMUPD_STATE_ERROR) + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + return 0; } + /* Clear status even it is not read and log */ + if (hw->nvmupd_state == I40E_NVMUPD_STATE_ERROR) { + i40e_debug(hw, I40E_DEBUG_NVM, + "Clearing I40E_NVMUPD_STATE_ERROR state without reading\n"); + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } + switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT: status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); @@ -1074,6 +1085,11 @@ void i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode) } hw->nvm_wait_opcode = 0; + if (hw->aq.arq_last_status) { + hw->nvmupd_state = I40E_NVMUPD_STATE_ERROR; + return; + } + switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT_WAIT: hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index bd5f13b..d9a2660 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -366,6 +366,7 @@ enum i40e_nvmupd_state { I40E_NVMUPD_STATE_WRITING, I40E_NVMUPD_STATE_INIT_WAIT, I40E_NVMUPD_STATE_WRITE_WAIT, + I40E_NVMUPD_STATE_ERROR }; /* nvm_access definition and its masks/shifts need to be accessible to diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index 44f7ed7..9638515 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -912,11 +912,11 @@ i40e_status i40evf_clean_arq_element(struct i40e_hw *hw, desc = I40E_ADMINQ_DESC(hw->aq.arq, ntc); desc_idx = ntc; + hw->aq.arq_last_status = + (enum i40e_admin_queue_err)le16_to_cpu(desc->retval); flags = le16_to_cpu(desc->flags); if (flags & I40E_AQ_FLAG_ERR) { ret_code = I40E_ERR_ADMIN_QUEUE_ERROR; - hw->aq.arq_last_status = - (enum i40e_admin_queue_err)le16_to_cpu(desc->retval); i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQRX: Event received with error 0x%X.\n", diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 97f96e0..ca7afe5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -348,6 +348,7 @@ enum i40e_nvmupd_state { I40E_NVMUPD_STATE_WRITING, I40E_NVMUPD_STATE_INIT_WAIT, I40E_NVMUPD_STATE_WRITE_WAIT, + I40E_NVMUPD_STATE_ERROR }; /* nvm_access definition and its masks/shifts need to be accessible to -- cgit v1.1 From 3aa7b74dbeedfb32406fec70cfd76d797209e8c9 Mon Sep 17 00:00:00 2001 From: Filip Sadowski Date: Tue, 11 Oct 2016 15:26:58 -0700 Subject: i40e: removed unreachable code Removed some of unnecessary if statements and unreachable code found by static code analysis tool. The return value of i40e_vsi_control_rings(..., false) is always 0. So, test for non-zero will never be true. The function has been split into "int i40e_vsi_start_rings()" and "void i40e_vsi_stop_rings()" for better understanding. Similarly, the function i40e_vsi_kill_vlan() never fails. So, checking for return value is also unnecessary. Function definition changed to void. The i40e_loopback_test() function is not implemented. The function and all references to loopback testing were removed. Change-ID: Id45cf66f6689ce2bc4e887de13f073e30e8431bd Signed-off-by: Filip Sadowski Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 5 +-- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 18 ---------- drivers/net/ethernet/intel/i40e/i40e_main.c | 41 ++++++++++++---------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 29 +++++---------- 4 files changed, 33 insertions(+), 60 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index bb02280..29c2318 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -773,7 +773,8 @@ void i40e_service_event_schedule(struct i40e_pf *pf); void i40e_notify_client_of_vf_msg(struct i40e_vsi *vsi, u32 vf_id, u8 *msg, u16 len); -int i40e_vsi_control_rings(struct i40e_vsi *vsi, bool enable); +int i40e_vsi_start_rings(struct i40e_vsi *vsi); +void i40e_vsi_stop_rings(struct i40e_vsi *vsi); int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count); struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, u16 uplink_seid, u16 downlink_seid, u8 enabled_tc); @@ -849,7 +850,7 @@ int i40e_close(struct net_device *netdev); int i40e_vsi_open(struct i40e_vsi *vsi); void i40e_vlan_stripping_disable(struct i40e_vsi *vsi); int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid); -int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid); +void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid); struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, const u8 *macaddr); int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 962a9bb..b9e1162 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -216,7 +216,6 @@ enum i40e_ethtool_test_id { I40E_ETH_TEST_REG = 0, I40E_ETH_TEST_EEPROM, I40E_ETH_TEST_INTR, - I40E_ETH_TEST_LOOPBACK, I40E_ETH_TEST_LINK, }; @@ -224,7 +223,6 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { "Register test (offline)", "Eeprom test (offline)", "Interrupt test (offline)", - "Loopback test (offline)", "Link test (on/offline)" }; @@ -1744,17 +1742,6 @@ static int i40e_intr_test(struct net_device *netdev, u64 *data) return *data; } -static int i40e_loopback_test(struct net_device *netdev, u64 *data) -{ - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_pf *pf = np->vsi->back; - - netif_info(pf, hw, netdev, "loopback test not implemented\n"); - *data = 0; - - return *data; -} - static inline bool i40e_active_vfs(struct i40e_pf *pf) { struct i40e_vf *vfs = pf->vf; @@ -1790,7 +1777,6 @@ static void i40e_diag_test(struct net_device *netdev, data[I40E_ETH_TEST_REG] = 1; data[I40E_ETH_TEST_EEPROM] = 1; data[I40E_ETH_TEST_INTR] = 1; - data[I40E_ETH_TEST_LOOPBACK] = 1; data[I40E_ETH_TEST_LINK] = 1; eth_test->flags |= ETH_TEST_FL_FAILED; clear_bit(__I40E_TESTING, &pf->state); @@ -1818,9 +1804,6 @@ static void i40e_diag_test(struct net_device *netdev, if (i40e_intr_test(netdev, &data[I40E_ETH_TEST_INTR])) eth_test->flags |= ETH_TEST_FL_FAILED; - if (i40e_loopback_test(netdev, &data[I40E_ETH_TEST_LOOPBACK])) - eth_test->flags |= ETH_TEST_FL_FAILED; - /* run reg test last, a reset is required after it */ if (i40e_reg_test(netdev, &data[I40E_ETH_TEST_REG])) eth_test->flags |= ETH_TEST_FL_FAILED; @@ -1841,7 +1824,6 @@ static void i40e_diag_test(struct net_device *netdev, data[I40E_ETH_TEST_REG] = 0; data[I40E_ETH_TEST_EEPROM] = 0; data[I40E_ETH_TEST_INTR] = 0; - data[I40E_ETH_TEST_LOOPBACK] = 0; } skip_ol_tests: diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9ec977a..5c6a5ce 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2503,10 +2503,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) * i40e_vsi_kill_vlan - Remove vsi membership for given vlan * @vsi: the vsi being configured * @vid: vlan id to be removed (0 = untagged only , -1 = any) - * - * Return: 0 on success or negative otherwise **/ -int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) +void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) { struct net_device *netdev = vsi->netdev; struct i40e_mac_filter *f; @@ -2530,7 +2528,6 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) * applying the new filter changes */ i40e_service_event_schedule(vsi->back); - return 0; } /** @@ -4017,30 +4014,36 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) } /** - * i40e_vsi_control_rings - Start or stop a VSI's rings + * i40e_vsi_start_rings - Start a VSI's rings * @vsi: the VSI being configured - * @enable: start or stop the rings **/ -int i40e_vsi_control_rings(struct i40e_vsi *vsi, bool request) +int i40e_vsi_start_rings(struct i40e_vsi *vsi) { int ret = 0; /* do rx first for enable and last for disable */ - if (request) { - ret = i40e_vsi_control_rx(vsi, request); - if (ret) - return ret; - ret = i40e_vsi_control_tx(vsi, request); - } else { - /* Ignore return value, we need to shutdown whatever we can */ - i40e_vsi_control_tx(vsi, request); - i40e_vsi_control_rx(vsi, request); - } + ret = i40e_vsi_control_rx(vsi, true); + if (ret) + return ret; + ret = i40e_vsi_control_tx(vsi, true); return ret; } /** + * i40e_vsi_stop_rings - Stop a VSI's rings + * @vsi: the VSI being configured + **/ +void i40e_vsi_stop_rings(struct i40e_vsi *vsi) +{ + /* do rx first for enable and last for disable + * Ignore return value, we need to shutdown whatever we can + */ + i40e_vsi_control_tx(vsi, false); + i40e_vsi_control_rx(vsi, false); +} + +/** * i40e_vsi_free_irq - Free the irq association with the OS * @vsi: the VSI being configured **/ @@ -5238,7 +5241,7 @@ static int i40e_up_complete(struct i40e_vsi *vsi) i40e_configure_msi_and_legacy(vsi); /* start rings */ - err = i40e_vsi_control_rings(vsi, true); + err = i40e_vsi_start_rings(vsi); if (err) return err; @@ -5335,7 +5338,7 @@ void i40e_down(struct i40e_vsi *vsi) netif_tx_disable(vsi->netdev); } i40e_vsi_disable_irq(vsi); - i40e_vsi_control_rings(vsi, false); + i40e_vsi_stop_rings(vsi); i40e_napi_disable_all(vsi); for (i = 0; i < vsi->num_queue_pairs; i++) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 69e184b..53b46553 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -991,7 +991,7 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) if (vf->lan_vsi_idx == 0) goto complete_reset; - i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], false); + i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); complete_reset: /* reallocate VF resources to reset the VSI state */ i40e_free_vf_res(vf); @@ -1032,8 +1032,7 @@ void i40e_free_vfs(struct i40e_pf *pf) i40e_notify_client_of_vf_enable(pf, 0); for (i = 0; i < pf->num_alloc_vfs; i++) if (test_bit(I40E_VF_STAT_INIT, &pf->vf[i].vf_states)) - i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx], - false); + i40e_vsi_stop_rings(pf->vsi[pf->vf[i].lan_vsi_idx]); /* Disable IOV before freeing resources. This lets any VF drivers * running in the host get themselves cleaned up before we yank @@ -1759,7 +1758,7 @@ static int i40e_vc_enable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) goto error_param; } - if (i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], true)) + if (i40e_vsi_start_rings(pf->vsi[vf->lan_vsi_idx])) aq_ret = I40E_ERR_TIMEOUT; error_param: /* send the response to the VF */ @@ -1798,8 +1797,7 @@ static int i40e_vc_disable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) goto error_param; } - if (i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], false)) - aq_ret = I40E_ERR_TIMEOUT; + i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); error_param: /* send the response to the VF */ @@ -2139,9 +2137,8 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) } for (i = 0; i < vfl->num_elements; i++) { - int ret = i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]); - if (!ret) - vf->num_vlan--; + i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]); + vf->num_vlan--; if (test_bit(I40E_VF_STAT_UC_PROMISC, &vf->vf_states)) i40e_aq_set_vsi_uc_promisc_on_vlan(&pf->hw, vsi->seid, @@ -2153,11 +2150,6 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) false, vfl->vlan_id[i], NULL); - - if (ret) - dev_err(&pf->pdev->dev, - "Unable to delete VLAN filter %d for VF %d, error %d\n", - vfl->vlan_id[i], vf->vf_id, ret); } error_param: @@ -2835,13 +2827,8 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, if (vsi->info.pvid) { /* kill old VLAN */ - ret = i40e_vsi_kill_vlan(vsi, (le16_to_cpu(vsi->info.pvid) & - VLAN_VID_MASK)); - if (ret) { - dev_info(&vsi->back->pdev->dev, - "remove VLAN failed, ret=%d, aq_err=%d\n", - ret, pf->hw.aq.asq_last_status); - } + i40e_vsi_kill_vlan(vsi, (le16_to_cpu(vsi->info.pvid) & + VLAN_VID_MASK)); } if (vlan_id || qos) ret = i40e_vsi_add_pvid(vsi, vlanprio); -- cgit v1.1 From a13925a46ebc176c4703c25b5f2edebba5bd2506 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 31 Oct 2016 15:32:16 -0400 Subject: net: cris: make eth_v10.c explicitly non-modular The Makefile/Kconfig currently controlling compilation of this code is: drivers/net/cris/Makefile:obj-$(CONFIG_ETRAX_ARCH_V10) += eth_v10.o arch/cris/Kconfig:config ETRAX_ARCH_V10 arch/cris/Kconfig: bool ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. There was a one line wrapper for the int init function, which made no sense; hence we just put the device_initcall on the true init function itself and delete the pointless wrapper. In doing that we get rid of the following compile warning: WARNING: drivers/net/built-in.o(.text+0x1e28): Section mismatch in reference from the function etrax_init_module() to the function .init.text:etrax_ethernet_init() We don't replace module.h with init.h since the file already has that. Cc: "David S. Miller" Cc: Mikael Starvik Cc: Jesper Nilsson Cc: netdev@vger.kernel.org Cc: linux-cris-kernel@axis.com Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- drivers/net/cris/eth_v10.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index e128826..91c876a 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -7,9 +7,6 @@ * */ - -#include - #include #include #include @@ -411,6 +408,7 @@ etrax_ethernet_init(void) led_next_time = jiffies; return 0; } +device_initcall(etrax_ethernet_init) /* set MAC address of the interface. called from the core after a * SIOCSIFADDR ioctl, and from the bootup above. @@ -1714,11 +1712,6 @@ e100_netpoll(struct net_device* netdev) } #endif -static int -etrax_init_module(void) -{ - return etrax_ethernet_init(); -} static int __init e100_boot_setup(char* str) @@ -1741,5 +1734,3 @@ e100_boot_setup(char* str) } __setup("etrax100_eth=", e100_boot_setup); - -module_init(etrax_init_module); -- cgit v1.1 From bd68a2a854ad5a85f0c8d0a9c8048ca3f6391efb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 31 Oct 2016 13:32:55 -0700 Subject: net: set SK_MEM_QUANTUM to 4096 Systems with large pages (64KB pages for example) do not always have huge quantity of memory. A big SK_MEM_QUANTUM value leads to fewer interactions with the global counters (like tcp_memory_allocated) but might trigger memory pressure much faster, giving suboptimal TCP performance since windows are lowered to ridiculous values. Note that sysctl_mem units being in pages and in ABI, we also need to change sk_prot_mem_limits() accordingly. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index f13ac87..93331a1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1162,11 +1162,6 @@ static inline void sk_enter_memory_pressure(struct sock *sk) sk->sk_prot->enter_memory_pressure(sk); } -static inline long sk_prot_mem_limits(const struct sock *sk, int index) -{ - return sk->sk_prot->sysctl_mem[index]; -} - static inline long sk_memory_allocated(const struct sock *sk) { @@ -1281,11 +1276,27 @@ int __sk_mem_schedule(struct sock *sk, int size, int kind); void __sk_mem_reduce_allocated(struct sock *sk, int amount); void __sk_mem_reclaim(struct sock *sk, int amount); -#define SK_MEM_QUANTUM ((int)PAGE_SIZE) +/* We used to have PAGE_SIZE here, but systems with 64KB pages + * do not necessarily have 16x time more memory than 4KB ones. + */ +#define SK_MEM_QUANTUM 4096 #define SK_MEM_QUANTUM_SHIFT ilog2(SK_MEM_QUANTUM) #define SK_MEM_SEND 0 #define SK_MEM_RECV 1 +/* sysctl_mem values are in pages, we convert them in SK_MEM_QUANTUM units */ +static inline long sk_prot_mem_limits(const struct sock *sk, int index) +{ + long val = sk->sk_prot->sysctl_mem[index]; + +#if PAGE_SIZE > SK_MEM_QUANTUM + val <<= PAGE_SHIFT - SK_MEM_QUANTUM_SHIFT; +#elif PAGE_SIZE < SK_MEM_QUANTUM + val >>= SK_MEM_QUANTUM_SHIFT - PAGE_SHIFT; +#endif + return val; +} + static inline int sk_mem_pages(int amt) { return (amt + SK_MEM_QUANTUM - 1) >> SK_MEM_QUANTUM_SHIFT; -- cgit v1.1 From 0183eb1c7504b290221f79bc3c345736f8ad657b Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 31 Oct 2016 22:26:53 +0200 Subject: qede: Don't override priv_flags Driver is now setting the ndev's priv_flags instead of adding to it, causing pktgen failure to utilize various features due to the loss of the IFF_TX_SKB_SHARING indication. Fixes: 7b7e70f979e3 ("qed*: Allow unicast filtering") Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 873f2eb..df02c45 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -2366,7 +2366,7 @@ static void qede_init_ndev(struct qede_dev *edev) qede_set_ethtool_ops(ndev); - ndev->priv_flags = IFF_UNICAST_FLT; + ndev->priv_flags |= IFF_UNICAST_FLT; /* user-changeble features */ hw_features = NETIF_F_GRO | NETIF_F_SG | -- cgit v1.1 From b0fca3122f6220e9a5bd975cdc5c4a87f575b740 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 31 Oct 2016 22:26:54 +0200 Subject: qed: Correct VF mac number When relaxing the limitation on the number of unicast MAC filters an interface can configure, qed started passing the MAC quota to qede. However, the value is initialized only for PFs, causing VFs to always try and configure themselves as promiscuous [as they believe they lack the resources to configure the rx-mode]. Fixes: 7b7e70f979e3 ("qed*: Allow unicast filtering") Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_l2.c | 2 ++ drivers/net/ethernet/qlogic/qed/qed_vf.c | 7 +++++++ drivers/net/ethernet/qlogic/qed/qed_vf.h | 13 +++++++++++++ 3 files changed, 22 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 1e3a16e..900b253 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1692,6 +1692,8 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev, qed_vf_get_num_vlan_filters(&cdev->hwfns[0], (u8 *)&info->num_vlan_filters); + qed_vf_get_num_mac_filters(&cdev->hwfns[0], + (u8 *)&info->num_mac_filters); qed_vf_get_port_mac(&cdev->hwfns[0], info->port_mac); info->is_legacy = !!cdev->hwfns[0].vf_iov_info->b_pre_fp_hsi; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index f580bf4..3c06336 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -1171,6 +1171,13 @@ void qed_vf_get_num_vlan_filters(struct qed_hwfn *p_hwfn, u8 *num_vlan_filters) *num_vlan_filters = p_vf->acquire_resp.resc.num_vlan_filters; } +void qed_vf_get_num_mac_filters(struct qed_hwfn *p_hwfn, u8 *num_mac_filters) +{ + struct qed_vf_iov *p_vf = p_hwfn->vf_iov_info; + + *num_mac_filters = p_vf->acquire_resp.resc.num_mac_filters; +} + bool qed_vf_check_mac(struct qed_hwfn *p_hwfn, u8 *mac) { struct qed_bulletin_content *bulletin; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 944745b..325c250 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -623,6 +623,14 @@ void qed_vf_get_num_vlan_filters(struct qed_hwfn *p_hwfn, u8 *num_vlan_filters); /** + * @brief Get number of MAC filters allocated for VF by qed + * + * @param p_hwfn + * @param num_rxqs - allocated MAC filters + */ +void qed_vf_get_num_mac_filters(struct qed_hwfn *p_hwfn, u8 *num_mac_filters); + +/** * @brief Check if VF can set a MAC address * * @param p_hwfn @@ -872,6 +880,11 @@ static inline void qed_vf_get_num_vlan_filters(struct qed_hwfn *p_hwfn, { } +static inline void qed_vf_get_num_mac_filters(struct qed_hwfn *p_hwfn, + u8 *num_mac_filters) +{ +} + static inline bool qed_vf_check_mac(struct qed_hwfn *p_hwfn, u8 *mac) { return false; -- cgit v1.1 From 7ff5c83a1deb09419eaf33608dec120a00fcf923 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:09 +0000 Subject: nfp: simplify nfp_net_poll() There are few variables in nfp_net_poll() which are used only once or unused but set. Remove them. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index d365760..f6a42c1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1525,21 +1525,15 @@ static int nfp_net_poll(struct napi_struct *napi, int budget) { struct nfp_net_r_vector *r_vec = container_of(napi, struct nfp_net_r_vector, napi); - struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring; - struct nfp_net_tx_ring *tx_ring = r_vec->tx_ring; - struct nfp_net *nn = r_vec->nfp_net; - struct netdev_queue *txq; unsigned int pkts_polled; - tx_ring = &nn->tx_rings[rx_ring->idx]; - txq = netdev_get_tx_queue(nn->netdev, tx_ring->idx); - nfp_net_tx_complete(tx_ring); + nfp_net_tx_complete(r_vec->tx_ring); - pkts_polled = nfp_net_rx(rx_ring, budget); + pkts_polled = nfp_net_rx(r_vec->rx_ring, budget); if (pkts_polled < budget) { napi_complete_done(napi, pkts_polled); - nfp_net_irq_unmask(nn, r_vec->irq_idx); + nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_idx); } return pkts_polled; -- cgit v1.1 From 416db5c1e448805866a6f23ebf7c78443f3d3bd5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:10 +0000 Subject: nfp: remove support for nfp3200 Drop all code related to nfp3200. It was never widely deployed as a NIC. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 7 +- .../net/ethernet/netronome/nfp/nfp_net_common.c | 95 ++-------------------- drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h | 13 +-- .../net/ethernet/netronome/nfp/nfp_netvf_main.c | 45 ++-------- 4 files changed, 22 insertions(+), 138 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index ed824e1..808c38a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -421,7 +421,6 @@ struct nfp_stat_pair { * @netdev: Backpointer to net_device structure * @nfp_fallback: Is the driver used in fallback mode? * @is_vf: Is the driver attached to a VF? - * @is_nfp3200: Is the driver for a NFP-3200 card? * @fw_loaded: Is the firmware loaded? * @bpf_offload_skip_sw: Offloaded BPF program will not be rerun by cls_bpf * @ctrl: Local copy of the control register/word. @@ -487,7 +486,6 @@ struct nfp_net { unsigned nfp_fallback:1; unsigned is_vf:1; - unsigned is_nfp3200:1; unsigned fw_loaded:1; unsigned bpf_offload_skip_sw:1; @@ -593,16 +591,13 @@ static inline void nn_writeb(struct nfp_net *nn, int off, u8 val) writeb(val, nn->ctrl_bar + off); } -/* NFP-3200 can't handle 16-bit accesses too well */ static inline u16 nn_readw(struct nfp_net *nn, int off) { - WARN_ON_ONCE(nn->is_nfp3200); return readw(nn->ctrl_bar + off); } static inline void nn_writew(struct nfp_net *nn, int off, u16 val) { - WARN_ON_ONCE(nn->is_nfp3200); writew(val, nn->ctrl_bar + off); } @@ -650,7 +645,7 @@ static inline void nn_pci_flush(struct nfp_net *nn) #define NFP_QCP_QUEUE_STS_HI 0x000c #define NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask 0x3ffff -/* The offset of a QCP queues in the PCIe Target (same on NFP3200 and NFP6000 */ +/* The offset of a QCP queues in the PCIe Target */ #define NFP_PCIE_QUEUE(_q) (0x80000 + (NFP_QCP_QUEUE_ADDR_SZ * ((_q) & 0xff))) /* nfp_qcp_ptr - Read or Write Pointer of a queue */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index f6a42c1..4e4b6e3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -249,43 +249,14 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update) */ /** - * nfp_net_irq_unmask_msix() - Unmask MSI-X after automasking - * @nn: NFP Network structure - * @entry_nr: MSI-X table entry - * - * Clear the MSI-X table mask bit for the given entry bypassing Linux irq - * handling subsystem. Use *only* to reenable automasked vectors. - */ -static void nfp_net_irq_unmask_msix(struct nfp_net *nn, unsigned int entry_nr) -{ - struct list_head *msi_head = &nn->pdev->dev.msi_list; - struct msi_desc *entry; - u32 off; - - /* All MSI-Xs have the same mask_base */ - entry = list_first_entry(msi_head, struct msi_desc, list); - - off = (PCI_MSIX_ENTRY_SIZE * entry_nr) + - PCI_MSIX_ENTRY_VECTOR_CTRL; - writel(0, entry->mask_base + off); - readl(entry->mask_base); -} - -/** * nfp_net_irq_unmask() - Unmask automasked interrupt * @nn: NFP Network structure * @entry_nr: MSI-X table entry * - * If MSI-X auto-masking is enabled clear the mask bit, otherwise - * clear the ICR for the entry. + * Clear the ICR for the IRQ entry. */ static void nfp_net_irq_unmask(struct nfp_net *nn, unsigned int entry_nr) { - if (nn->ctrl & NFP_NET_CFG_CTRL_MSIXAUTO) { - nfp_net_irq_unmask_msix(nn, entry_nr); - return; - } - nn_writeb(nn, NFP_NET_CFG_ICR(entry_nr), NFP_NET_CFG_ICR_UNMASKED); nn_pci_flush(nn); } @@ -1368,20 +1339,6 @@ nfp_net_parse_meta(struct net_device *netdev, struct sk_buff *skb, * more cleanly separate packet receive code from other bookkeeping * functions performed in the napi poll function. * - * There are differences between the NFP-3200 firmware and the - * NFP-6000 firmware. The NFP-3200 firmware uses a dedicated RX queue - * to indicate that new packets have arrived. The NFP-6000 does not - * have this queue and uses the DD bit in the RX descriptor. This - * method cannot be used on the NFP-3200 as it causes a race - * condition: The RX ring write pointer on the NFP-3200 is updated - * after packets (and descriptors) have been DMAed. If the DD bit is - * used and subsequently the read pointer is updated this may lead to - * the RX queue to underflow (if the firmware has not yet update the - * write pointer). Therefore we use slightly ugly conditional code - * below to handle the differences. We may, in the future update the - * NFP-3200 firmware to behave the same as the firmware on the - * NFP-6000. - * * Return: Number of packets received. */ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) @@ -1389,41 +1346,19 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; unsigned int data_len, meta_len; - int avail = 0, pkts_polled = 0; struct sk_buff *skb, *new_skb; struct nfp_net_rx_desc *rxd; dma_addr_t new_dma_addr; - u32 qcp_wr_p; + int pkts_polled = 0; int idx; - if (nn->is_nfp3200) { - /* Work out how many packets arrived */ - qcp_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx); - idx = rx_ring->rd_p % rx_ring->cnt; - - if (qcp_wr_p == idx) - /* No new packets */ - return 0; - - if (qcp_wr_p > idx) - avail = qcp_wr_p - idx; - else - avail = qcp_wr_p + rx_ring->cnt - idx; - } else { - avail = budget + 1; - } - - while (avail > 0 && pkts_polled < budget) { + while (pkts_polled < budget) { idx = rx_ring->rd_p % rx_ring->cnt; rxd = &rx_ring->rxds[idx]; - if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) { - if (nn->is_nfp3200) - nn_dbg(nn, "RX descriptor not valid (DD)%d:%u rxd[0]=%#x rxd[1]=%#x\n", - rx_ring->idx, idx, - rxd->vals[0], rxd->vals[1]); + if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) break; - } + /* Memory barrier to ensure that we won't do other reads * before the DD bit. */ @@ -1431,7 +1366,6 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) rx_ring->rd_p++; pkts_polled++; - avail--; skb = rx_ring->rxbufs[idx].skb; @@ -1508,9 +1442,6 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) napi_gro_receive(&rx_ring->r_vec->napi, skb); } - if (nn->is_nfp3200) - nfp_qcp_rd_ptr_add(rx_ring->qcp_rx, pkts_polled); - return pkts_polled; } @@ -1895,9 +1826,8 @@ static void nfp_net_write_mac_addr(struct nfp_net *nn) { nn_writel(nn, NFP_NET_CFG_MACADDR + 0, get_unaligned_be32(nn->netdev->dev_addr)); - /* We can't do writew for NFP-3200 compatibility */ - nn_writel(nn, NFP_NET_CFG_MACADDR + 4, - get_unaligned_be16(nn->netdev->dev_addr + 4) << 16); + nn_writew(nn, NFP_NET_CFG_MACADDR + 6, + get_unaligned_be16(nn->netdev->dev_addr + 4)); } static void nfp_net_vec_clear_ring_data(struct nfp_net *nn, unsigned int idx) @@ -2675,8 +2605,7 @@ static const struct net_device_ops nfp_net_netdev_ops = { */ void nfp_net_info(struct nfp_net *nn) { - nn_info(nn, "Netronome %s %sNetdev: TxQs=%d/%d RxQs=%d/%d\n", - nn->is_nfp3200 ? "NFP-32xx" : "NFP-6xxx", + nn_info(nn, "Netronome NFP-6xxx %sNetdev: TxQs=%d/%d RxQs=%d/%d\n", nn->is_vf ? "VF " : "", nn->num_tx_rings, nn->max_tx_rings, nn->num_rx_rings, nn->max_rx_rings); @@ -2891,13 +2820,7 @@ int nfp_net_netdev_init(struct net_device *netdev) nn->ctrl |= NFP_NET_CFG_CTRL_IRQMOD; } - /* On NFP-3200 enable MSI-X auto-masking, if supported and the - * interrupts are not shared. - */ - if (nn->is_nfp3200 && nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO) - nn->ctrl |= NFP_NET_CFG_CTRL_MSIXAUTO; - - /* On NFP4000/NFP6000, determine RX packet/metadata boundary offset */ + /* Determine RX packet/metadata boundary offset */ if (nn->fw_ver.major >= 2) nn->rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); else diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 93b10b4..385ba35 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -50,7 +50,7 @@ /** * Configuration BAR size. * - * The configuration BAR is 8K in size, but on the NFP6000, due to + * The configuration BAR is 8K in size, but due to * THB-350, 32k needs to be reserved. */ #define NFP_NET_CFG_BAR_SZ (32 * 1024) @@ -186,18 +186,13 @@ #define NFP_NET_CFG_START_RXQ 0x004c /** - * NFP-3200 workaround (0x0050 - 0x0058) - * @NFP_NET_CFG_SPARE_ADDR: DMA address for ME code to use (e.g. YDS-155 fix) - */ -#define NFP_NET_CFG_SPARE_ADDR 0x0050 -/** - * NFP6000/NFP4000 - Prepend configuration + * Prepend configuration */ #define NFP_NET_CFG_RX_OFFSET 0x0050 #define NFP_NET_CFG_RX_OFFSET_DYNAMIC 0 /* Prepend mode */ /** - * NFP6000/NFP4000 - VXLAN/UDP encap configuration + * VXLAN/UDP encap configuration * @NFP_NET_CFG_VXLAN_PORT: Base address of table of tunnels' UDP dst ports * @NFP_NET_CFG_VXLAN_SZ: Size of the UDP port table in bytes */ @@ -205,7 +200,7 @@ #define NFP_NET_CFG_VXLAN_SZ 0x0008 /** - * NFP6000 - BPF section + * BPF section * @NFP_NET_CFG_BPF_ABI: BPF ABI version * @NFP_NET_CFG_BPF_CAP: BPF capabilities * @NFP_NET_CFG_BPF_MAX_LEN: Maximum size of JITed BPF code in bytes diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c index 2800bbf..d065235 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c @@ -63,9 +63,7 @@ static void nfp_netvf_get_mac_addr(struct nfp_net *nn) u8 mac_addr[ETH_ALEN]; put_unaligned_be32(nn_readl(nn, NFP_NET_CFG_MACADDR + 0), &mac_addr[0]); - /* We can't do readw for NFP-3200 compatibility */ - put_unaligned_be16(nn_readl(nn, NFP_NET_CFG_MACADDR + 4) >> 16, - &mac_addr[4]); + put_unaligned_be16(nn_readw(nn, NFP_NET_CFG_MACADDR + 6), &mac_addr[4]); if (!is_valid_ether_addr(mac_addr)) { eth_hw_addr_random(nn->netdev); @@ -86,7 +84,6 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, int tx_bar_no, rx_bar_no; u8 __iomem *ctrl_bar; struct nfp_net *nn; - int is_nfp3200; u32 startq; int stride; int err; @@ -101,15 +98,6 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, goto err_pci_disable; } - switch (pdev->device) { - case PCI_DEVICE_NFP6000VF: - is_nfp3200 = 0; - break; - default: - err = -ENODEV; - goto err_pci_regions; - } - pci_set_master(pdev); err = dma_set_mask_and_coherent(&pdev->dev, @@ -149,15 +137,9 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, } else { switch (fw_ver.major) { case 1 ... 4: - if (is_nfp3200) { - stride = 2; - tx_bar_no = NFP_NET_Q0_BAR; - rx_bar_no = NFP_NET_Q1_BAR; - } else { - stride = 4; - tx_bar_no = NFP_NET_Q0_BAR; - rx_bar_no = tx_bar_no; - } + stride = 4; + tx_bar_no = NFP_NET_Q0_BAR; + rx_bar_no = tx_bar_no; break; default: dev_err(&pdev->dev, "Unsupported Firmware ABI %d.%d.%d.%d\n", @@ -189,20 +171,10 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, max_rx_rings = (rx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2; } - /* XXX Implement a workaround for THB-350 here. Ideally, we - * have a different PCI ID for A rev VFs. - */ - switch (pdev->device) { - case PCI_DEVICE_NFP6000VF: - startq = readl(ctrl_bar + NFP_NET_CFG_START_TXQ); - tx_bar_off = NFP_PCIE_QUEUE(startq); - startq = readl(ctrl_bar + NFP_NET_CFG_START_RXQ); - rx_bar_off = NFP_PCIE_QUEUE(startq); - break; - default: - err = -ENODEV; - goto err_ctrl_unmap; - } + startq = readl(ctrl_bar + NFP_NET_CFG_START_TXQ); + tx_bar_off = NFP_PCIE_QUEUE(startq); + startq = readl(ctrl_bar + NFP_NET_CFG_START_RXQ); + rx_bar_off = NFP_PCIE_QUEUE(startq); /* Allocate and initialise the netdev */ nn = nfp_net_netdev_alloc(pdev, max_tx_rings, max_rx_rings); @@ -214,7 +186,6 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, nn->fw_ver = fw_ver; nn->ctrl_bar = ctrl_bar; nn->is_vf = 1; - nn->is_nfp3200 = is_nfp3200; nn->stride_tx = stride; nn->stride_rx = stride; -- cgit v1.1 From 8c8b0e99eb5c008158e095ecc3a42755ba9a7f8d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:11 +0000 Subject: nfp: remove unnecessary call to ether_setup() ether_setup() will be invoked by alloc_etherdev_mqs(), no need to call it again. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 4e4b6e3..bd28e42 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2839,7 +2839,6 @@ int nfp_net_netdev_init(struct net_device *netdev) return err; /* Finalise the netdev setup */ - ether_setup(netdev); netdev->netdev_ops = &nfp_net_netdev_ops; netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000); -- cgit v1.1 From fa95f1d2bf1816eee736192fa1f75ed96c0ab9cf Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:12 +0000 Subject: nfp: remove inline attributes and dead code Don't declare functions as static inline in .c files and remove dead code it was hiding. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index bd28e42..025483f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -576,7 +576,7 @@ static void nfp_net_aux_irq_free(struct nfp_net *nn, u32 ctrl_offset, * * Return: True if the ring is full. */ -static inline int nfp_net_tx_full(struct nfp_net_tx_ring *tx_ring, int dcnt) +static int nfp_net_tx_full(struct nfp_net_tx_ring *tx_ring, int dcnt) { return (tx_ring->wr_p - tx_ring->rd_p) >= (tx_ring->cnt - dcnt); } @@ -1032,19 +1032,6 @@ static void nfp_net_tx_timeout(struct net_device *netdev) */ /** - * nfp_net_rx_space() - return the number of free slots on the RX ring - * @rx_ring: RX ring structure - * - * Make sure we leave at least one slot free. - * - * Return: True if there is space on the RX ring - */ -static inline int nfp_net_rx_space(struct nfp_net_rx_ring *rx_ring) -{ - return (rx_ring->cnt - 1) - (rx_ring->wr_p - rx_ring->rd_p); -} - -/** * nfp_net_rx_alloc_one() - Allocate and map skb for RX * @rx_ring: RX ring structure of the skb * @dma_addr: Pointer to storage for DMA address (output param) -- cgit v1.1 From bf187ea01b07739167420088adb385f9385a7c7f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:13 +0000 Subject: nfp: centralize the buffer size calculation Add a helper function to calculate the buffer size at run time. Buffer lengths will now depend on the FW prepend configuration instead of assuming the most space consuming configuration and defaulting to 2k buffers at initialization time. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 1 - .../net/ethernet/netronome/nfp/nfp_net_common.c | 29 ++++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 808c38a..9b90fab 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -75,7 +75,6 @@ /* Default size for MTU and freelist buffer sizes */ #define NFP_NET_DEFAULT_MTU 1500 -#define NFP_NET_DEFAULT_RX_BUFSZ 2048 /* Maximum number of bytes prepended to a packet */ #define NFP_NET_MAX_PREPEND 64 diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 025483f..77c645a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1030,6 +1030,19 @@ static void nfp_net_tx_timeout(struct net_device *netdev) /* Receive processing */ +static unsigned int +nfp_net_calc_fl_bufsz(struct nfp_net *nn, unsigned int mtu) +{ + unsigned int fl_bufsz; + + if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) + fl_bufsz = NFP_NET_MAX_PREPEND; + else + fl_bufsz = nn->rx_offset; + fl_bufsz += ETH_HLEN + VLAN_HLEN * 2 + mtu; + + return fl_bufsz; +} /** * nfp_net_rx_alloc_one() - Allocate and map skb for RX @@ -2191,7 +2204,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) old_mtu = netdev->mtu; old_fl_bufsz = nn->fl_bufsz; - new_fl_bufsz = NFP_NET_MAX_PREPEND + ETH_HLEN + VLAN_HLEN * 2 + new_mtu; + new_fl_bufsz = nfp_net_calc_fl_bufsz(nn, new_mtu); if (!netif_running(netdev)) { netdev->mtu = new_mtu; @@ -2731,12 +2744,18 @@ int nfp_net_netdev_init(struct net_device *netdev) nfp_net_write_mac_addr(nn); + /* Determine RX packet/metadata boundary offset */ + if (nn->fw_ver.major >= 2) + nn->rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); + else + nn->rx_offset = NFP_NET_RX_OFFSET; + /* Set default MTU and Freelist buffer size */ if (nn->max_mtu < NFP_NET_DEFAULT_MTU) netdev->mtu = nn->max_mtu; else netdev->mtu = NFP_NET_DEFAULT_MTU; - nn->fl_bufsz = NFP_NET_DEFAULT_RX_BUFSZ; + nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, netdev->mtu); /* Advertise/enable offloads based on capabilities * @@ -2807,12 +2826,6 @@ int nfp_net_netdev_init(struct net_device *netdev) nn->ctrl |= NFP_NET_CFG_CTRL_IRQMOD; } - /* Determine RX packet/metadata boundary offset */ - if (nn->fw_ver.major >= 2) - nn->rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); - else - nn->rx_offset = NFP_NET_RX_OFFSET; - /* Stash the re-configuration queue away. First odd queue in TX Bar */ nn->qcp_cfg = nn->tx_bar + NFP_QCP_QUEUE_ADDR_SZ; -- cgit v1.1 From e9949aebe04a1d917a15f4dc71e77deaefe9de94 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:14 +0000 Subject: nfp: add buffer drop/recycle helper for RX nfp_net_rx() is quite long already and about to get longer. Move buffer drop/recycle to a helper. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 27 ++++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 77c645a..abd4f17 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1330,6 +1330,20 @@ nfp_net_parse_meta(struct net_device *netdev, struct sk_buff *skb, return data; } +static void +nfp_net_rx_drop(struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, + struct nfp_net_rx_buf *rxbuf, struct sk_buff *skb) +{ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_drops++; + u64_stats_update_end(&r_vec->rx_sync); + + if (rxbuf) + nfp_net_rx_give_one(rx_ring, rxbuf->skb, rxbuf->dma_addr); + if (skb) + dev_kfree_skb_any(skb); +} + /** * nfp_net_rx() - receive up to @budget packets on @rx_ring * @rx_ring: RX ring to receive from @@ -1372,11 +1386,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) new_skb = nfp_net_rx_alloc_one(rx_ring, &new_dma_addr, nn->fl_bufsz); if (!new_skb) { - nfp_net_rx_give_one(rx_ring, rx_ring->rxbufs[idx].skb, - rx_ring->rxbufs[idx].dma_addr); - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->rx_drops++; - u64_stats_update_end(&r_vec->rx_sync); + nfp_net_rx_drop(r_vec, rx_ring, &rx_ring->rxbufs[idx], + NULL); continue; } @@ -1420,12 +1431,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) end = nfp_net_parse_meta(nn->netdev, skb, meta_len); if (unlikely(end != skb->data)) { - u64_stats_update_begin(&r_vec->rx_sync); - r_vec->rx_drops++; - u64_stats_update_end(&r_vec->rx_sync); - - dev_kfree_skb_any(skb); nn_warn_ratelimit(nn, "invalid RX packet metadata\n"); + nfp_net_rx_drop(r_vec, rx_ring, NULL, skb); continue; } } -- cgit v1.1 From c0f031bc88660e8b96060b76aa72fbe8859bdcc8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:15 +0000 Subject: nfp: use alloc_frag() and build_skb() Speed up RX processing by moving to the alloc_frag()/build_skb() paradigm. Since we're no longer mapping the entire buffer for DMA add helpers which take care of calculating offsets and lengths. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 8 +- .../net/ethernet/netronome/nfp/nfp_net_common.c | 115 +++++++++++++-------- .../net/ethernet/netronome/nfp/nfp_net_debugfs.c | 9 +- 3 files changed, 82 insertions(+), 50 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 9b90fab..9ca066b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -101,6 +101,10 @@ /* Offload definitions */ #define NFP_NET_N_VXLAN_PORTS (NFP_NET_CFG_VXLAN_SZ / sizeof(__be16)) +#define NFP_NET_RX_BUF_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN) +#define NFP_NET_RX_BUF_NON_DATA (NFP_NET_RX_BUF_HEADROOM + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) + /* Forward declarations */ struct nfp_net; struct nfp_net_r_vector; @@ -277,11 +281,11 @@ struct nfp_net_rx_hash { /** * struct nfp_net_rx_buf - software RX buffer descriptor - * @skb: sk_buff associated with this buffer + * @frag: page fragment buffer * @dma_addr: DMA mapping address of the buffer */ struct nfp_net_rx_buf { - struct sk_buff *skb; + void *frag; dma_addr_t dma_addr; }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index abd4f17..449ec79 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,22 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, put_unaligned_le32(reg, fw_ver); } +static dma_addr_t +nfp_net_dma_map_rx(struct nfp_net *nn, void *frag, unsigned int bufsz, + int direction) +{ + return dma_map_single(&nn->pdev->dev, frag + NFP_NET_RX_BUF_HEADROOM, + bufsz - NFP_NET_RX_BUF_NON_DATA, direction); +} + +static void +nfp_net_dma_unmap_rx(struct nfp_net *nn, dma_addr_t dma_addr, + unsigned int bufsz, int direction) +{ + dma_unmap_single(&nn->pdev->dev, dma_addr, + bufsz - NFP_NET_RX_BUF_NON_DATA, direction); +} + /* Firmware reconfig * * Firmware reconfig may take a while so we have two versions of it - @@ -1035,64 +1052,67 @@ nfp_net_calc_fl_bufsz(struct nfp_net *nn, unsigned int mtu) { unsigned int fl_bufsz; + fl_bufsz = NFP_NET_RX_BUF_HEADROOM; if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) - fl_bufsz = NFP_NET_MAX_PREPEND; + fl_bufsz += NFP_NET_MAX_PREPEND; else - fl_bufsz = nn->rx_offset; + fl_bufsz += nn->rx_offset; fl_bufsz += ETH_HLEN + VLAN_HLEN * 2 + mtu; + fl_bufsz = SKB_DATA_ALIGN(fl_bufsz); + fl_bufsz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + return fl_bufsz; } /** - * nfp_net_rx_alloc_one() - Allocate and map skb for RX + * nfp_net_rx_alloc_one() - Allocate and map page frag for RX * @rx_ring: RX ring structure of the skb * @dma_addr: Pointer to storage for DMA address (output param) * @fl_bufsz: size of freelist buffers * - * This function will allcate a new skb, map it for DMA. + * This function will allcate a new page frag, map it for DMA. * - * Return: allocated skb or NULL on failure. + * Return: allocated page frag or NULL on failure. */ -static struct sk_buff * +static void * nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, unsigned int fl_bufsz) { struct nfp_net *nn = rx_ring->r_vec->nfp_net; - struct sk_buff *skb; + void *frag; - skb = netdev_alloc_skb(nn->netdev, fl_bufsz); - if (!skb) { - nn_warn_ratelimit(nn, "Failed to alloc receive SKB\n"); + frag = netdev_alloc_frag(fl_bufsz); + if (!frag) { + nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n"); return NULL; } - *dma_addr = dma_map_single(&nn->pdev->dev, skb->data, - fl_bufsz, DMA_FROM_DEVICE); + *dma_addr = nfp_net_dma_map_rx(nn, frag, fl_bufsz, DMA_FROM_DEVICE); if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) { - dev_kfree_skb_any(skb); + skb_free_frag(frag); nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); return NULL; } - return skb; + return frag; } /** * nfp_net_rx_give_one() - Put mapped skb on the software and hardware rings * @rx_ring: RX ring structure - * @skb: Skb to put on rings + * @frag: page fragment buffer * @dma_addr: DMA address of skb mapping */ static void nfp_net_rx_give_one(struct nfp_net_rx_ring *rx_ring, - struct sk_buff *skb, dma_addr_t dma_addr) + void *frag, dma_addr_t dma_addr) { unsigned int wr_idx; wr_idx = rx_ring->wr_p % rx_ring->cnt; /* Stash SKB and DMA address away */ - rx_ring->rxbufs[wr_idx].skb = skb; + rx_ring->rxbufs[wr_idx].frag = frag; rx_ring->rxbufs[wr_idx].dma_addr = dma_addr; /* Fill freelist descriptor */ @@ -1127,9 +1147,9 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) wr_idx = rx_ring->wr_p % rx_ring->cnt; last_idx = rx_ring->cnt - 1; rx_ring->rxbufs[wr_idx].dma_addr = rx_ring->rxbufs[last_idx].dma_addr; - rx_ring->rxbufs[wr_idx].skb = rx_ring->rxbufs[last_idx].skb; + rx_ring->rxbufs[wr_idx].frag = rx_ring->rxbufs[last_idx].frag; rx_ring->rxbufs[last_idx].dma_addr = 0; - rx_ring->rxbufs[last_idx].skb = NULL; + rx_ring->rxbufs[last_idx].frag = NULL; memset(rx_ring->rxds, 0, sizeof(*rx_ring->rxds) * rx_ring->cnt); rx_ring->wr_p = 0; @@ -1149,7 +1169,6 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) static void nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) { - struct pci_dev *pdev = nn->pdev; unsigned int i; for (i = 0; i < rx_ring->cnt - 1; i++) { @@ -1157,14 +1176,14 @@ nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) * fails to allocate enough buffers and calls here to free * already allocated ones. */ - if (!rx_ring->rxbufs[i].skb) + if (!rx_ring->rxbufs[i].frag) continue; - dma_unmap_single(&pdev->dev, rx_ring->rxbufs[i].dma_addr, - rx_ring->bufsz, DMA_FROM_DEVICE); - dev_kfree_skb_any(rx_ring->rxbufs[i].skb); + nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[i].dma_addr, + rx_ring->bufsz, DMA_FROM_DEVICE); + skb_free_frag(rx_ring->rxbufs[i].frag); rx_ring->rxbufs[i].dma_addr = 0; - rx_ring->rxbufs[i].skb = NULL; + rx_ring->rxbufs[i].frag = NULL; } } @@ -1182,10 +1201,10 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) rxbufs = rx_ring->rxbufs; for (i = 0; i < rx_ring->cnt - 1; i++) { - rxbufs[i].skb = + rxbufs[i].frag = nfp_net_rx_alloc_one(rx_ring, &rxbufs[i].dma_addr, rx_ring->bufsz); - if (!rxbufs[i].skb) { + if (!rxbufs[i].frag) { nfp_net_rx_ring_bufs_free(nn, rx_ring); return -ENOMEM; } @@ -1203,7 +1222,7 @@ static void nfp_net_rx_ring_fill_freelist(struct nfp_net_rx_ring *rx_ring) unsigned int i; for (i = 0; i < rx_ring->cnt - 1; i++) - nfp_net_rx_give_one(rx_ring, rx_ring->rxbufs[i].skb, + nfp_net_rx_give_one(rx_ring, rx_ring->rxbufs[i].frag, rx_ring->rxbufs[i].dma_addr); } @@ -1338,8 +1357,13 @@ nfp_net_rx_drop(struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, r_vec->rx_drops++; u64_stats_update_end(&r_vec->rx_sync); + /* skb is build based on the frag, free_skb() would free the frag + * so to be able to reuse it we need an extra ref. + */ + if (skb && rxbuf && skb->head == rxbuf->frag) + page_ref_inc(virt_to_head_page(rxbuf->frag)); if (rxbuf) - nfp_net_rx_give_one(rx_ring, rxbuf->skb, rxbuf->dma_addr); + nfp_net_rx_give_one(rx_ring, rxbuf->frag, rxbuf->dma_addr); if (skb) dev_kfree_skb_any(skb); } @@ -1360,10 +1384,12 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; unsigned int data_len, meta_len; - struct sk_buff *skb, *new_skb; + struct nfp_net_rx_buf *rxbuf; struct nfp_net_rx_desc *rxd; dma_addr_t new_dma_addr; + struct sk_buff *skb; int pkts_polled = 0; + void *new_frag; int idx; while (pkts_polled < budget) { @@ -1381,21 +1407,23 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) rx_ring->rd_p++; pkts_polled++; - skb = rx_ring->rxbufs[idx].skb; - - new_skb = nfp_net_rx_alloc_one(rx_ring, &new_dma_addr, - nn->fl_bufsz); - if (!new_skb) { - nfp_net_rx_drop(r_vec, rx_ring, &rx_ring->rxbufs[idx], - NULL); + rxbuf = &rx_ring->rxbufs[idx]; + skb = build_skb(rxbuf->frag, nn->fl_bufsz); + if (unlikely(!skb)) { + nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); + continue; + } + new_frag = nfp_net_rx_alloc_one(rx_ring, &new_dma_addr, + nn->fl_bufsz); + if (unlikely(!new_frag)) { + nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); continue; } - dma_unmap_single(&nn->pdev->dev, - rx_ring->rxbufs[idx].dma_addr, - nn->fl_bufsz, DMA_FROM_DEVICE); + nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[idx].dma_addr, + nn->fl_bufsz, DMA_FROM_DEVICE); - nfp_net_rx_give_one(rx_ring, new_skb, new_dma_addr); + nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); /* < meta_len > * <-- [rx_offset] --> @@ -1413,9 +1441,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) data_len = le16_to_cpu(rxd->rxd.data_len); if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) - skb_reserve(skb, meta_len); + skb_reserve(skb, NFP_NET_RX_BUF_HEADROOM + meta_len); else - skb_reserve(skb, nn->rx_offset); + skb_reserve(skb, + NFP_NET_RX_BUF_HEADROOM + nn->rx_offset); skb_put(skb, data_len - meta_len); /* Stats update */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index f7c9a5b..180cf70 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -44,8 +44,8 @@ static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) struct nfp_net_r_vector *r_vec = file->private; struct nfp_net_rx_ring *rx_ring; struct nfp_net_rx_desc *rxd; - struct sk_buff *skb; struct nfp_net *nn; + void *frag; int i; rtnl_lock(); @@ -73,10 +73,9 @@ static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) seq_printf(file, "%04d: 0x%08x 0x%08x", i, rxd->vals[0], rxd->vals[1]); - skb = READ_ONCE(rx_ring->rxbufs[i].skb); - if (skb) - seq_printf(file, " skb->head=%p skb->data=%p", - skb->head, skb->data); + frag = READ_ONCE(rx_ring->rxbufs[i].frag); + if (frag) + seq_printf(file, " frag=%p", frag); if (rx_ring->rxbufs[i].dma_addr) seq_printf(file, " dma_addr=%pad", -- cgit v1.1 From b64b7bb6d492ef1bbe9aa9ed7eeb18f7148ece3d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:16 +0000 Subject: nfp: add separate buffer allocation function for napi Introduce a separate buffer allocation function to be called from NAPI. We can make assumptions about the context and buffer size. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 449ec79..b43d2db 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1098,6 +1098,26 @@ nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, return frag; } +static void *nfp_net_napi_alloc_one(struct nfp_net *nn, dma_addr_t *dma_addr) +{ + void *frag; + + frag = napi_alloc_frag(nn->fl_bufsz); + if (!frag) { + nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n"); + return NULL; + } + + *dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, DMA_FROM_DEVICE); + if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) { + skb_free_frag(frag); + nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); + return NULL; + } + + return frag; +} + /** * nfp_net_rx_give_one() - Put mapped skb on the software and hardware rings * @rx_ring: RX ring structure @@ -1413,8 +1433,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); continue; } - new_frag = nfp_net_rx_alloc_one(rx_ring, &new_dma_addr, - nn->fl_bufsz); + new_frag = nfp_net_napi_alloc_one(nn, &new_dma_addr); if (unlikely(!new_frag)) { nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); continue; -- cgit v1.1 From 1a1d74d378b13ad3f93e8975a0ade0980a49d28b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:17 +0000 Subject: nfp: use AND instead of modulo to get ring indexes We already force ring sizes to be power of 2 so replace modulo operations with AND (size - 1) in index calculations. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index b43d2db..7225ab6 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -778,7 +778,7 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) if (dma_mapping_error(&nn->pdev->dev, dma_addr)) goto err_free; - wr_idx = tx_ring->wr_p % tx_ring->cnt; + wr_idx = tx_ring->wr_p & (tx_ring->cnt - 1); /* Stash the soft descriptor of the head then initialize it */ txbuf = &tx_ring->txbufs[wr_idx]; @@ -822,7 +822,7 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) if (dma_mapping_error(&nn->pdev->dev, dma_addr)) goto err_unmap; - wr_idx = (wr_idx + 1) % tx_ring->cnt; + wr_idx = (wr_idx + 1) & (tx_ring->cnt - 1); tx_ring->txbufs[wr_idx].skb = skb; tx_ring->txbufs[wr_idx].dma_addr = dma_addr; tx_ring->txbufs[wr_idx].fidx = f; @@ -917,7 +917,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) todo = qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p; while (todo--) { - idx = tx_ring->rd_p % tx_ring->cnt; + idx = tx_ring->rd_p & (tx_ring->cnt - 1); tx_ring->rd_p++; skb = tx_ring->txbufs[idx].skb; @@ -992,7 +992,7 @@ nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) int nr_frags, fidx, idx; struct sk_buff *skb; - idx = tx_ring->rd_p % tx_ring->cnt; + idx = tx_ring->rd_p & (tx_ring->cnt - 1); skb = tx_ring->txbufs[idx].skb; nr_frags = skb_shinfo(skb)->nr_frags; fidx = tx_ring->txbufs[idx].fidx; @@ -1129,7 +1129,7 @@ static void nfp_net_rx_give_one(struct nfp_net_rx_ring *rx_ring, { unsigned int wr_idx; - wr_idx = rx_ring->wr_p % rx_ring->cnt; + wr_idx = rx_ring->wr_p & (rx_ring->cnt - 1); /* Stash SKB and DMA address away */ rx_ring->rxbufs[wr_idx].frag = frag; @@ -1164,7 +1164,7 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) unsigned int wr_idx, last_idx; /* Move the empty entry to the end of the list */ - wr_idx = rx_ring->wr_p % rx_ring->cnt; + wr_idx = rx_ring->wr_p & (rx_ring->cnt - 1); last_idx = rx_ring->cnt - 1; rx_ring->rxbufs[wr_idx].dma_addr = rx_ring->rxbufs[last_idx].dma_addr; rx_ring->rxbufs[wr_idx].frag = rx_ring->rxbufs[last_idx].frag; @@ -1413,7 +1413,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) int idx; while (pkts_polled < budget) { - idx = rx_ring->rd_p % rx_ring->cnt; + idx = rx_ring->rd_p & (rx_ring->cnt - 1); rxd = &rx_ring->rxds[idx]; if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) -- cgit v1.1 From 23a4e389bdc71fda949b5a19a28fa99c4d746d43 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:18 +0000 Subject: nfp: create separate define for max number of vectors We are currently using define for max TX rings to allocate IRQ vectors. It's OK since the max number of rings for TX and RX are currently the same, but lets make the code nicer by taking max of the two. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 9ca066b..55a497c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -87,6 +87,9 @@ /* Queue/Ring definitions */ #define NFP_NET_MAX_TX_RINGS 64 /* Max. # of Tx rings per device */ #define NFP_NET_MAX_RX_RINGS 64 /* Max. # of Rx rings per device */ +#define NFP_NET_MAX_R_VECS (NFP_NET_MAX_TX_RINGS > NFP_NET_MAX_RX_RINGS ? \ + NFP_NET_MAX_TX_RINGS : NFP_NET_MAX_RX_RINGS) +#define NFP_NET_MAX_IRQS (NFP_NET_NON_Q_VECTORS + NFP_NET_MAX_R_VECS) #define NFP_NET_MIN_TX_DESCS 256 /* Min. # of Tx descs per ring */ #define NFP_NET_MIN_RX_DESCS 256 /* Min. # of Rx descs per ring */ @@ -539,9 +542,8 @@ struct nfp_net { u8 num_irqs; u8 num_r_vecs; - struct nfp_net_r_vector r_vecs[NFP_NET_MAX_TX_RINGS]; - struct msix_entry irq_entries[NFP_NET_NON_Q_VECTORS + - NFP_NET_MAX_TX_RINGS]; + struct nfp_net_r_vector r_vecs[NFP_NET_MAX_R_VECS]; + struct msix_entry irq_entries[NFP_NET_MAX_IRQS]; irq_handler_t lsc_handler; char lsc_name[IFNAMSIZ + 8]; -- cgit v1.1 From a4b562bb8ebd418dcfeba2840a7ece25a74d8385 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:19 +0000 Subject: nfp: use unsigned int for vector/ring counts Use unsigned int consistently for vector/ring counts. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 17 +++++++++-------- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 5 +++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 55a497c..0ea6ebd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -528,11 +528,11 @@ struct nfp_net { struct timer_list rx_filter_stats_timer; spinlock_t rx_filter_lock; - int max_tx_rings; - int max_rx_rings; + unsigned int max_tx_rings; + unsigned int max_rx_rings; - int num_tx_rings; - int num_rx_rings; + unsigned int num_tx_rings; + unsigned int num_rx_rings; int stride_tx; int stride_rx; @@ -540,8 +540,8 @@ struct nfp_net { int txd_cnt; int rxd_cnt; - u8 num_irqs; - u8 num_r_vecs; + unsigned int num_irqs; + unsigned int num_r_vecs; struct nfp_net_r_vector r_vecs[NFP_NET_MAX_R_VECS]; struct msix_entry irq_entries[NFP_NET_MAX_IRQS]; @@ -757,8 +757,9 @@ extern const char nfp_net_driver_version[]; void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, void __iomem *ctrl_bar); -struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, - int max_tx_rings, int max_rx_rings); +struct nfp_net * +nfp_net_netdev_alloc(struct pci_dev *pdev, + unsigned int max_tx_rings, unsigned int max_rx_rings); void nfp_net_netdev_free(struct nfp_net *nn); int nfp_net_netdev_init(struct net_device *netdev); void nfp_net_netdev_clean(struct net_device *netdev); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 7225ab6..49a1f54 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -318,8 +318,8 @@ static int nfp_net_msix_alloc(struct nfp_net *nn, int nr_vecs) */ static int nfp_net_irqs_wanted(struct nfp_net *nn) { + unsigned int vecs; int ncpus; - int vecs; ncpus = num_online_cpus(); @@ -2701,7 +2701,8 @@ void nfp_net_info(struct nfp_net *nn) * Return: NFP Net device structure, or ERR_PTR on error. */ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, - int max_tx_rings, int max_rx_rings) + unsigned int max_tx_rings, + unsigned int max_rx_rings) { struct net_device *netdev; struct nfp_net *nn; -- cgit v1.1 From 4b27a1eb7b83da956c3939869ed22f5ca0e1f1e1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:20 +0000 Subject: nfp: remove nfp_net_irqs_wanted() nfp_net_irqs_wanted() doesn't really encapsulate much logic, remove it and inline the calculations. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 27 ++++------------------ 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 49a1f54..f26c392 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -308,28 +308,6 @@ static int nfp_net_msix_alloc(struct nfp_net *nn, int nr_vecs) } /** - * nfp_net_irqs_wanted() - Work out how many interrupt vectors we want - * @nn: NFP Network structure - * - * We want a vector per CPU (or ring), whatever is smaller plus - * NFP_NET_NON_Q_VECTORS for LSC etc. - * - * Return: Number of interrupts wanted - */ -static int nfp_net_irqs_wanted(struct nfp_net *nn) -{ - unsigned int vecs; - int ncpus; - - ncpus = num_online_cpus(); - - vecs = max_t(int, nn->num_tx_rings, nn->num_rx_rings); - vecs = min_t(int, vecs, ncpus); - - return vecs + NFP_NET_NON_Q_VECTORS; -} - -/** * nfp_net_irqs_alloc() - allocates MSI-X irqs * @nn: NFP Network structure * @@ -339,7 +317,7 @@ int nfp_net_irqs_alloc(struct nfp_net *nn) { int wanted_irqs; - wanted_irqs = nfp_net_irqs_wanted(nn); + wanted_irqs = nn->num_r_vecs + NFP_NET_NON_Q_VECTORS; nn->num_irqs = nfp_net_msix_alloc(nn, wanted_irqs); if (nn->num_irqs == 0) { @@ -2726,6 +2704,9 @@ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, nn->num_tx_rings = min_t(int, nqs, max_tx_rings); nn->num_rx_rings = min_t(int, nqs, max_rx_rings); + nn->num_r_vecs = max(nn->num_tx_rings, nn->num_rx_rings); + nn->num_r_vecs = min_t(unsigned int, nn->num_r_vecs, num_online_cpus()); + nn->txd_cnt = NFP_NET_TX_DESCS_DEFAULT; nn->rxd_cnt = NFP_NET_RX_DESCS_DEFAULT; -- cgit v1.1 From b33ae997a362d64a1178be17804c2c3899770a4b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:21 +0000 Subject: nfp: replace num_irqs with max_r_vecs num_irqs is not used anywhere, replace it with max_r_vecs which holds number of allocated RX/TX vectors and is going to be useful soon. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 4 ++-- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 0ea6ebd..e871325 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -456,7 +456,7 @@ struct nfp_stat_pair { * @rxd_cnt: Size of the RX ring in number of descriptors * @tx_rings: Array of pre-allocated TX ring structures * @rx_rings: Array of pre-allocated RX ring structures - * @num_irqs: Number of allocated interrupt vectors + * @max_r_vecs: Number of allocated interrupt vectors for RX/TX * @num_r_vecs: Number of used ring vectors * @r_vecs: Pre-allocated array of ring vectors * @irq_entries: Pre-allocated array of MSI-X entries @@ -540,7 +540,7 @@ struct nfp_net { int txd_cnt; int rxd_cnt; - unsigned int num_irqs; + unsigned int max_r_vecs; unsigned int num_r_vecs; struct nfp_net_r_vector r_vecs[NFP_NET_MAX_R_VECS]; struct msix_entry irq_entries[NFP_NET_MAX_IRQS]; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index f26c392..4b866fc 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -316,22 +316,24 @@ static int nfp_net_msix_alloc(struct nfp_net *nn, int nr_vecs) int nfp_net_irqs_alloc(struct nfp_net *nn) { int wanted_irqs; + unsigned int n; wanted_irqs = nn->num_r_vecs + NFP_NET_NON_Q_VECTORS; - nn->num_irqs = nfp_net_msix_alloc(nn, wanted_irqs); - if (nn->num_irqs == 0) { + n = nfp_net_msix_alloc(nn, wanted_irqs); + if (n == 0) { nn_err(nn, "Failed to allocate MSI-X IRQs\n"); return 0; } - nn->num_r_vecs = nn->num_irqs - NFP_NET_NON_Q_VECTORS; + nn->max_r_vecs = n - NFP_NET_NON_Q_VECTORS; + nn->num_r_vecs = nn->max_r_vecs; - if (nn->num_irqs < wanted_irqs) + if (n < wanted_irqs) nn_warn(nn, "Unable to allocate %d vectors. Got %d instead\n", - wanted_irqs, nn->num_irqs); + wanted_irqs, n); - return nn->num_irqs; + return n; } /** -- cgit v1.1 From cbeaf7aa733a134721cdcda44688e53a6380cd31 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 31 Oct 2016 20:43:22 +0000 Subject: nfp: bring back support for different ring counts We used to always allocate the same number of TX and RX rings so the support for having r_vectors without one of the rings was dropped. That makes us, however, unnecessarily limited to 8 TX rings (8 is the Linux RSS default) most of the time. Also we are about to add channel count configuration via ethtool, so bring that support back. TX rings can now default to num_online_cpus() and RX rings to netif_get_num_default_rss_queues(). Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 118 +++++++++++++-------- 1 file changed, 71 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 4b866fc..97e0bbe 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -483,13 +483,13 @@ static void nfp_net_irqs_assign(struct net_device *netdev) struct nfp_net_r_vector *r_vec; int r; - /* Assumes nn->num_tx_rings == nn->num_rx_rings */ - if (nn->num_tx_rings > nn->num_r_vecs) { - nn_warn(nn, "More rings (%d) than vectors (%d).\n", - nn->num_tx_rings, nn->num_r_vecs); - nn->num_tx_rings = nn->num_r_vecs; - nn->num_rx_rings = nn->num_r_vecs; - } + if (nn->num_rx_rings > nn->num_r_vecs || + nn->num_tx_rings > nn->num_r_vecs) + nn_warn(nn, "More rings (%d,%d) than vectors (%d).\n", + nn->num_rx_rings, nn->num_tx_rings, nn->num_r_vecs); + + nn->num_rx_rings = min(nn->num_r_vecs, nn->num_rx_rings); + nn->num_tx_rings = min(nn->num_r_vecs, nn->num_tx_rings); nn->lsc_handler = nfp_net_irq_lsc; nn->exn_handler = nfp_net_irq_exn; @@ -1491,11 +1491,12 @@ static int nfp_net_poll(struct napi_struct *napi, int budget) { struct nfp_net_r_vector *r_vec = container_of(napi, struct nfp_net_r_vector, napi); - unsigned int pkts_polled; - - nfp_net_tx_complete(r_vec->tx_ring); + unsigned int pkts_polled = 0; - pkts_polled = nfp_net_rx(r_vec->rx_ring, budget); + if (r_vec->tx_ring) + nfp_net_tx_complete(r_vec->tx_ring); + if (r_vec->rx_ring) + pkts_polled = nfp_net_rx(r_vec->rx_ring, budget); if (pkts_polled < budget) { napi_complete_done(napi, pkts_polled); @@ -1743,7 +1744,7 @@ nfp_net_shadow_rx_rings_free(struct nfp_net *nn, struct nfp_net_rx_ring *rings) if (!rings) return; - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->num_rx_rings; r++) { nfp_net_rx_ring_bufs_free(nn, &rings[r]); nfp_net_rx_ring_free(&rings[r]); } @@ -1758,11 +1759,19 @@ nfp_net_prepare_vector(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, struct msix_entry *entry = &nn->irq_entries[r_vec->irq_idx]; int err; - r_vec->tx_ring = &nn->tx_rings[idx]; - nfp_net_tx_ring_init(r_vec->tx_ring, r_vec, idx); + if (idx < nn->num_tx_rings) { + r_vec->tx_ring = &nn->tx_rings[idx]; + nfp_net_tx_ring_init(r_vec->tx_ring, r_vec, idx); + } else { + r_vec->tx_ring = NULL; + } - r_vec->rx_ring = &nn->rx_rings[idx]; - nfp_net_rx_ring_init(r_vec->rx_ring, r_vec, idx); + if (idx < nn->num_rx_rings) { + r_vec->rx_ring = &nn->rx_rings[idx]; + nfp_net_rx_ring_init(r_vec->rx_ring, r_vec, idx); + } else { + r_vec->rx_ring = NULL; + } snprintf(r_vec->name, sizeof(r_vec->name), "%s-rxtx-%d", nn->netdev->name, idx); @@ -1839,13 +1848,13 @@ void nfp_net_coalesce_write_cfg(struct nfp_net *nn) /* copy RX interrupt coalesce parameters */ value = (nn->rx_coalesce_max_frames << 16) | (factor * nn->rx_coalesce_usecs); - for (i = 0; i < nn->num_r_vecs; i++) + for (i = 0; i < nn->num_rx_rings; i++) nn_writel(nn, NFP_NET_CFG_RXR_IRQ_MOD(i), value); /* copy TX interrupt coalesce parameters */ value = (nn->tx_coalesce_max_frames << 16) | (factor * nn->tx_coalesce_usecs); - for (i = 0; i < nn->num_r_vecs; i++) + for (i = 0; i < nn->num_tx_rings; i++) nn_writel(nn, NFP_NET_CFG_TXR_IRQ_MOD(i), value); } @@ -1903,27 +1912,33 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn) if (err) nn_err(nn, "Could not disable device: %d\n", err); - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->num_rx_rings; r++) nfp_net_rx_ring_reset(nn->r_vecs[r].rx_ring); + for (r = 0; r < nn->num_tx_rings; r++) nfp_net_tx_ring_reset(nn, nn->r_vecs[r].tx_ring); + for (r = 0; r < nn->num_r_vecs; r++) nfp_net_vec_clear_ring_data(nn, r); - } nn->ctrl = new_ctrl; } static void -nfp_net_vec_write_ring_data(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, - unsigned int idx) +nfp_net_rx_ring_hw_cfg_write(struct nfp_net *nn, + struct nfp_net_rx_ring *rx_ring, unsigned int idx) { /* Write the DMA address, size and MSI-X info to the device */ - nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), r_vec->rx_ring->dma); - nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), ilog2(r_vec->rx_ring->cnt)); - nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), r_vec->irq_idx); + nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), rx_ring->dma); + nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), ilog2(rx_ring->cnt)); + nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), rx_ring->r_vec->irq_idx); +} - nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), r_vec->tx_ring->dma); - nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), ilog2(r_vec->tx_ring->cnt)); - nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), r_vec->irq_idx); +static void +nfp_net_tx_ring_hw_cfg_write(struct nfp_net *nn, + struct nfp_net_tx_ring *tx_ring, unsigned int idx) +{ + nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), tx_ring->dma); + nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), ilog2(tx_ring->cnt)); + nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), tx_ring->r_vec->irq_idx); } static int __nfp_net_set_config_and_enable(struct nfp_net *nn) @@ -1948,8 +1963,10 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) update |= NFP_NET_CFG_UPDATE_IRQMOD; } - for (r = 0; r < nn->num_r_vecs; r++) - nfp_net_vec_write_ring_data(nn, &nn->r_vecs[r], r); + for (r = 0; r < nn->num_tx_rings; r++) + nfp_net_tx_ring_hw_cfg_write(nn, &nn->tx_rings[r], r); + for (r = 0; r < nn->num_rx_rings; r++) + nfp_net_rx_ring_hw_cfg_write(nn, &nn->rx_rings[r], r); nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, nn->num_tx_rings == 64 ? 0xffffffffffffffffULL : ((u64)1 << nn->num_tx_rings) - 1); @@ -1975,7 +1992,7 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) nn->ctrl = new_ctrl; - for (r = 0; r < nn->num_r_vecs; r++) + for (r = 0; r < nn->num_rx_rings; r++) nfp_net_rx_ring_fill_freelist(nn->r_vecs[r].rx_ring); /* Since reconfiguration requests while NFP is down are ignored we @@ -2067,20 +2084,22 @@ static int nfp_net_netdev_open(struct net_device *netdev) for (r = 0; r < nn->num_r_vecs; r++) { err = nfp_net_prepare_vector(nn, &nn->r_vecs[r], r); if (err) - goto err_free_prev_vecs; - + goto err_cleanup_vec_p; + } + for (r = 0; r < nn->num_tx_rings; r++) { err = nfp_net_tx_ring_alloc(nn->r_vecs[r].tx_ring, nn->txd_cnt); if (err) - goto err_cleanup_vec_p; - + goto err_free_tx_ring_p; + } + for (r = 0; r < nn->num_rx_rings; r++) { err = nfp_net_rx_ring_alloc(nn->r_vecs[r].rx_ring, nn->fl_bufsz, nn->rxd_cnt); if (err) - goto err_free_tx_ring_p; + goto err_flush_free_rx_ring_p; err = nfp_net_rx_ring_bufs_alloc(nn, nn->r_vecs[r].rx_ring); if (err) - goto err_flush_rx_ring_p; + goto err_free_rx_ring_p; } err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings); @@ -2113,17 +2132,21 @@ static int nfp_net_netdev_open(struct net_device *netdev) return 0; err_free_rings: - r = nn->num_r_vecs; -err_free_prev_vecs: + r = nn->num_rx_rings; +err_flush_free_rx_ring_p: while (r--) { nfp_net_rx_ring_bufs_free(nn, nn->r_vecs[r].rx_ring); -err_flush_rx_ring_p: +err_free_rx_ring_p: nfp_net_rx_ring_free(nn->r_vecs[r].rx_ring); + } + r = nn->num_tx_rings; err_free_tx_ring_p: + while (r--) nfp_net_tx_ring_free(nn->r_vecs[r].tx_ring); + r = nn->num_r_vecs; err_cleanup_vec_p: + while (r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); - } kfree(nn->tx_rings); err_free_rx_rings: kfree(nn->rx_rings); @@ -2162,12 +2185,14 @@ static void nfp_net_close_free_all(struct nfp_net *nn) { unsigned int r; - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->num_rx_rings; r++) { nfp_net_rx_ring_bufs_free(nn, nn->r_vecs[r].rx_ring); nfp_net_rx_ring_free(nn->r_vecs[r].rx_ring); + } + for (r = 0; r < nn->num_tx_rings; r++) nfp_net_tx_ring_free(nn->r_vecs[r].tx_ring); + for (r = 0; r < nn->num_r_vecs; r++) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); - } kfree(nn->rx_rings); kfree(nn->tx_rings); @@ -2686,7 +2711,6 @@ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, { struct net_device *netdev; struct nfp_net *nn; - int nqs; netdev = alloc_etherdev_mqs(sizeof(struct nfp_net), max_tx_rings, max_rx_rings); @@ -2702,9 +2726,9 @@ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, nn->max_tx_rings = max_tx_rings; nn->max_rx_rings = max_rx_rings; - nqs = netif_get_num_default_rss_queues(); - nn->num_tx_rings = min_t(int, nqs, max_tx_rings); - nn->num_rx_rings = min_t(int, nqs, max_rx_rings); + nn->num_tx_rings = min_t(unsigned int, max_tx_rings, num_online_cpus()); + nn->num_rx_rings = min_t(unsigned int, max_rx_rings, + netif_get_num_default_rss_queues()); nn->num_r_vecs = max(nn->num_tx_rings, nn->num_rx_rings); nn->num_r_vecs = min_t(unsigned int, nn->num_r_vecs, num_online_cpus()); -- cgit v1.1 From 7cf87fa27873a954f2d665dfc61fc22227d96ad8 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:34 +0100 Subject: tipc: return early for non-blocking sockets at link congestion Until now, in stream/mcast send() we pass the message to the link layer even when the link is congested and add the socket to the link's wakeup queue. This is unnecessary for non-blocking sockets. If a socket is set to non-blocking and sends multicast with zero back off time while receiving EAGAIN, we exhaust the memory. In this commit, we return immediately at stream/mcast send() for non-blocking sockets. Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index f9f5f3c..adf3e6e 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -697,6 +697,9 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, uint mtu; int rc; + if (!timeo && tsk->link_cong) + return -ELINKCONG; + msg_set_type(mhdr, TIPC_MCAST_MSG); msg_set_lookup_scope(mhdr, TIPC_CLUSTER_SCOPE); msg_set_destport(mhdr, 0); @@ -1072,6 +1075,9 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz) } timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); + if (!timeo && tsk->link_cong) + return -ELINKCONG; + dnode = tsk_peer_node(tsk); skb_queue_head_init(&pktchain); -- cgit v1.1 From 4891d8fe16343660b4c1ddbcca4792300b5fd4f4 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:35 +0100 Subject: tipc: wakeup sleeping users at disconnect Until now, in filter_connect() when we terminate a connection due to an error message from peer, we set the socket state to DISCONNECTING. The socket is notified about this broken connection using EPIPE when a user tries to send a message. However if a socket was waiting on a poll() while the connection is being terminated, we fail to wakeup that socket. In this commit, we wakeup sleeping sockets at connection termination. Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index adf3e6e..cd01deb 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1599,6 +1599,7 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) /* Let timer expire on it's own */ tipc_node_remove_conn(net, tsk_peer_node(tsk), tsk->portid); + sk->sk_state_change(sk); } return true; -- cgit v1.1 From cb5da847af4418f91d0e83fc91c2fcb1def5537d Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:36 +0100 Subject: tipc: set kern=0 in sk_alloc() during tipc_accept() Until now, tipc_accept() calls sk_alloc() with kern=1. This is incorrect as the data socket's owner is the user application. Thus for these accepted data sockets the network namespace refcount is skipped. In this commit, we fix this by setting kern=0. Acked-by: Ying Xue Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index cd01deb..82aec2e 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2093,7 +2093,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) buf = skb_peek(&sk->sk_receive_queue); - res = tipc_sk_create(sock_net(sock->sk), new_sock, 0, 1); + res = tipc_sk_create(sock_net(sock->sk), new_sock, 0, 0); if (res) goto exit; security_sk_clone(sock->sk, new_sock->sk); -- cgit v1.1 From ba8aebe9431a79c627214ddc782c3be9f2617e8d Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:37 +0100 Subject: tipc: rename struct tipc_skb_cb member handle to bytes_read In this commit, we rename handle to bytes_read indicating the purpose of the member. Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/msg.h | 2 +- net/tipc/socket.c | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 50a7398..8d40861 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -95,7 +95,7 @@ struct plist; #define TIPC_MEDIA_INFO_OFFSET 5 struct tipc_skb_cb { - void *handle; + u32 bytes_read; struct sk_buff *tail; bool validated; bool wakeup_pending; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 82aec2e..c543ae6 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -465,7 +465,7 @@ static int tipc_release(struct socket *sock) skb = __skb_dequeue(&sk->sk_receive_queue); if (skb == NULL) break; - if (TIPC_SKB_CB(skb)->handle != NULL) + if (TIPC_SKB_CB(skb)->bytes_read) kfree_skb(skb); else { if ((sock->state == SS_CONNECTING) || @@ -1435,7 +1435,7 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m, struct tipc_msg *msg; long timeo; unsigned int sz; - int sz_to_copy, target, needed; + int target; int sz_copied = 0; u32 err; int res = 0, hlen; @@ -1483,11 +1483,13 @@ restart: /* Capture message data (if valid) & compute return value (always) */ if (!err) { - u32 offset = (u32)(unsigned long)(TIPC_SKB_CB(buf)->handle); + u32 offset = TIPC_SKB_CB(buf)->bytes_read; + u32 needed; + int sz_to_copy; sz -= offset; needed = (buf_len - sz_copied); - sz_to_copy = (sz <= needed) ? sz : needed; + sz_to_copy = min(sz, needed); res = skb_copy_datagram_msg(buf, hlen + offset, m, sz_to_copy); if (res) @@ -1497,8 +1499,8 @@ restart: if (sz_to_copy < sz) { if (!(flags & MSG_PEEK)) - TIPC_SKB_CB(buf)->handle = - (void *)(unsigned long)(offset + sz_to_copy); + TIPC_SKB_CB(buf)->bytes_read = + offset + sz_to_copy; goto exit; } } else { @@ -1742,7 +1744,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb, } /* Enqueue message */ - TIPC_SKB_CB(skb)->handle = NULL; + TIPC_SKB_CB(skb)->bytes_read = 0; __skb_queue_tail(&sk->sk_receive_queue, skb); skb_set_owner_r(skb, sk); @@ -2177,7 +2179,7 @@ restart: /* Disconnect and send a 'FIN+' or 'FIN-' message to peer */ skb = __skb_dequeue(&sk->sk_receive_queue); if (skb) { - if (TIPC_SKB_CB(skb)->handle != NULL) { + if (TIPC_SKB_CB(skb)->bytes_read) { kfree_skb(skb); goto restart; } -- cgit v1.1 From aeda16b6aec88b8b0135d678281e2f20fc9258e6 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:38 +0100 Subject: tipc: rename tsk->remote to tsk->peer for consistent naming Until now, the peer information for connect is stored in tsk->remote but the rest of code uses the name peer for peer/remote. In this commit, we rename tsk->remote to tsk->peer to align with naming convention followed in the rest of the code. There is no functional change in this commit. Acked-by: Ying Xue Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index c543ae6..0546556 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -65,7 +65,6 @@ * @max_pkt: maximum packet size "hint" used when building messages sent by port * @portid: unique port identity in TIPC socket hash table * @phdr: preformatted message header used when sending messages - * @port_list: adjacent ports in TIPC's global list of ports * @publications: list of publications for port * @pub_count: total # of publications port has made during its lifetime * @probing_state: @@ -75,7 +74,7 @@ * @link_cong: non-zero if owner must sleep because of link congestion * @sent_unacked: # messages sent by socket, and not yet acked by peer * @rcv_unacked: # messages read by user, but not yet acked back to peer - * @remote: 'connected' peer for dgram/rdm + * @peer: 'connected' peer for dgram/rdm * @node: hash table node * @rcu: rcu struct for tipc_sock */ @@ -101,7 +100,7 @@ struct tipc_sock { u16 peer_caps; u16 rcv_unacked; u16 rcv_win; - struct sockaddr_tipc remote; + struct sockaddr_tipc peer; struct rhash_head node; struct rcu_head rcu; }; @@ -904,7 +903,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) return -EMSGSIZE; if (unlikely(!dest)) { if (tsk->connected && sock->state == SS_READY) - dest = &tsk->remote; + dest = &tsk->peer; else return -EDESTADDRREQ; } else if (unlikely(m->msg_namelen < sizeof(*dest)) || @@ -1939,12 +1938,12 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, /* DGRAM/RDM connect(), just save the destaddr */ if (sock->state == SS_READY) { if (dst->family == AF_UNSPEC) { - memset(&tsk->remote, 0, sizeof(struct sockaddr_tipc)); + memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc)); tsk->connected = 0; } else if (destlen != sizeof(struct sockaddr_tipc)) { res = -EINVAL; } else { - memcpy(&tsk->remote, dest, destlen); + memcpy(&tsk->peer, dest, destlen); tsk->connected = 1; } goto exit; -- cgit v1.1 From 87227fe7e42060af9bc8977fc17427e7c9cadb5d Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:39 +0100 Subject: tipc: remove tsk->connected for connectionless sockets Until now, for connectionless sockets the peer information during connect is stored in tsk->peer and a connection state is set in tsk->connected. This is redundant. In this commit, for connectionless sockets we update: - __tipc_sendmsg(), when the destination is NULL the peer existence is determined by tsk->peer.family, instead of tsk->connected. - tipc_connect(), remove set/unset of tsk->connected. Hence tsk->connected is no longer used for connectionless sockets. There is no functional change in this commit. Acked-by: Ying Xue Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 0546556..524abe47 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -902,7 +902,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) if (dsz > TIPC_MAX_USER_MSG_SIZE) return -EMSGSIZE; if (unlikely(!dest)) { - if (tsk->connected && sock->state == SS_READY) + if (sock->state == SS_READY && tsk->peer.family == AF_TIPC) dest = &tsk->peer; else return -EDESTADDRREQ; @@ -1939,12 +1939,10 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, if (sock->state == SS_READY) { if (dst->family == AF_UNSPEC) { memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc)); - tsk->connected = 0; } else if (destlen != sizeof(struct sockaddr_tipc)) { res = -EINVAL; } else { memcpy(&tsk->peer, dest, destlen); - tsk->connected = 1; } goto exit; } -- cgit v1.1 From d6fb7e9c9946578667283805b9a73f318e3a0554 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:40 +0100 Subject: tipc: remove tsk->connected from tipc_sock Until now, we determine if a socket is connected or not based on tsk->connected, which is set once when the probing state is set to TIPC_CONN_OK. It is unset when the sock->state is updated from SS_CONNECTED to any other state. In this commit, we remove connected variable from tipc_sock and derive socket connection status from the following condition: sock->state == SS_CONNECTED => tsk->connected There is no functional change in this commit. Acked-by: Ying Xue Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 524abe47..7b6a184 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -58,7 +58,6 @@ /** * struct tipc_sock - TIPC socket structure * @sk: socket - interacts with 'port' and with user via the socket API - * @connected: non-zero if port is currently connected to a peer port * @conn_type: TIPC type used when connection was established * @conn_instance: TIPC instance used when connection was established * @published: non-zero if port has one or more associated names @@ -80,7 +79,6 @@ */ struct tipc_sock { struct sock sk; - int connected; u32 conn_type; u32 conn_instance; int published; @@ -293,6 +291,11 @@ static void tsk_rej_rx_queue(struct sock *sk) tipc_sk_respond(sk, skb, TIPC_ERR_NO_PORT); } +static bool tipc_sk_connected(struct sock *sk) +{ + return sk->sk_socket->state == SS_CONNECTED; +} + /* tsk_peer_msg - verify if message was sent by connected port's peer * * Handles cases where the node's network address has changed from @@ -300,12 +303,13 @@ static void tsk_rej_rx_queue(struct sock *sk) */ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) { - struct tipc_net *tn = net_generic(sock_net(&tsk->sk), tipc_net_id); + struct sock *sk = &tsk->sk; + struct tipc_net *tn = net_generic(sock_net(sk), tipc_net_id); u32 peer_port = tsk_peer_port(tsk); u32 orig_node; u32 peer_node; - if (unlikely(!tsk->connected)) + if (unlikely(!tipc_sk_connected(sk))) return false; if (unlikely(msg_origport(msg) != peer_port)) @@ -470,7 +474,6 @@ static int tipc_release(struct socket *sock) if ((sock->state == SS_CONNECTING) || (sock->state == SS_CONNECTED)) { sock->state = SS_DISCONNECTING; - tsk->connected = 0; tipc_node_remove_conn(net, dnode, tsk->portid); } tipc_sk_respond(sk, skb, TIPC_ERR_NO_PORT); @@ -480,7 +483,7 @@ static int tipc_release(struct socket *sock) tipc_sk_withdraw(tsk, 0, NULL); sk_stop_timer(sk, &sk->sk_timer); tipc_sk_remove(tsk); - if (tsk->connected) { + if (tipc_sk_connected(sk)) { skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, tsk_own_node(tsk), tsk_peer_port(tsk), @@ -1010,7 +1013,7 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) done = sk_wait_event(sk, timeo_p, (!tsk->link_cong && !tsk_conn_cong(tsk)) || - !tsk->connected); + !tipc_sk_connected(sk)); finish_wait(sk_sleep(sk), &wait); } while (!done); return 0; @@ -1152,7 +1155,6 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, tsk->probing_intv = CONN_PROBING_INTERVAL; tsk->probing_state = TIPC_CONN_OK; - tsk->connected = 1; sk_reset_timer(sk, &sk->sk_timer, jiffies + tsk->probing_intv); tipc_node_add_conn(net, peer_node, tsk->portid, peer_port); tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid); @@ -1261,13 +1263,14 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg, static void tipc_sk_send_ack(struct tipc_sock *tsk) { - struct net *net = sock_net(&tsk->sk); + struct sock *sk = &tsk->sk; + struct net *net = sock_net(sk); struct sk_buff *skb = NULL; struct tipc_msg *msg; u32 peer_port = tsk_peer_port(tsk); u32 dnode = tsk_peer_node(tsk); - if (!tsk->connected) + if (!tipc_sk_connected(sk)) return; skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode, tsk_own_node(tsk), peer_port, @@ -1596,7 +1599,6 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) if (unlikely(msg_errcode(hdr))) { sock->state = SS_DISCONNECTING; - tsk->connected = 0; /* Let timer expire on it's own */ tipc_node_remove_conn(net, tsk_peer_node(tsk), tsk->portid); @@ -2189,7 +2191,6 @@ restart: if (skb) tipc_node_xmit_skb(net, skb, dnode, tsk->portid); } - tsk->connected = 0; sock->state = SS_DISCONNECTING; tipc_node_remove_conn(net, dnode, tsk->portid); /* fall through */ @@ -2221,7 +2222,7 @@ static void tipc_sk_timeout(unsigned long data) u32 own_node = tsk_own_node(tsk); bh_lock_sock(sk); - if (!tsk->connected) { + if (!tipc_sk_connected(sk)) { bh_unlock_sock(sk); goto exit; } @@ -2231,7 +2232,6 @@ static void tipc_sk_timeout(unsigned long data) if (tsk->probing_state == TIPC_CONN_PROBING) { if (!sock_owned_by_user(sk)) { sk->sk_socket->state = SS_DISCONNECTING; - tsk->connected = 0; tipc_node_remove_conn(sock_net(sk), tsk_peer_node(tsk), tsk_peer_port(tsk)); sk->sk_state_change(sk); @@ -2257,11 +2257,12 @@ exit: static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq) { - struct net *net = sock_net(&tsk->sk); + struct sock *sk = &tsk->sk; + struct net *net = sock_net(sk); struct publication *publ; u32 key; - if (tsk->connected) + if (tipc_sk_connected(sk)) return -EINVAL; key = tsk->portid + tsk->pub_count + 1; if (key == tsk->portid) @@ -2719,6 +2720,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, struct nlattr *attrs; struct net *net = sock_net(skb->sk); struct tipc_net *tn = net_generic(net, tipc_net_id); + struct sock *sk = &tsk->sk; hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &tipc_genl_family, NLM_F_MULTI, TIPC_NL_SOCK_GET); @@ -2733,7 +2735,7 @@ static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tn->own_addr)) goto attr_msg_cancel; - if (tsk->connected) { + if (tipc_sk_connected(sk)) { err = __tipc_nl_add_sk_con(skb, tsk); if (err) goto attr_msg_cancel; -- cgit v1.1 From 360aab6b49b93937bafd45034d33e7d44148fe82 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:41 +0100 Subject: tipc: remove probing_intv from tipc_sock Until now, probing_intv is a variable in struct tipc_sock but is always set to a constant CONN_PROBING_INTERVAL. The socket connection is probed based on this value. In this commit, we remove this variable and setup the socket timer based on the constant CONN_PROBING_INTERVAL. There is no functional change in this commit. Acked-by: Ying Xue Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 7b6a184..1b1aa941 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -67,7 +67,6 @@ * @publications: list of publications for port * @pub_count: total # of publications port has made during its lifetime * @probing_state: - * @probing_intv: * @conn_timeout: the time we can wait for an unresponded setup request * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue * @link_cong: non-zero if owner must sleep because of link congestion @@ -89,7 +88,6 @@ struct tipc_sock { struct list_head publications; u32 pub_count; u32 probing_state; - unsigned long probing_intv; uint conn_timeout; atomic_t dupl_rcvcnt; bool link_cong; @@ -1153,9 +1151,8 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, msg_set_lookup_scope(msg, 0); msg_set_hdr_sz(msg, SHORT_H_SIZE); - tsk->probing_intv = CONN_PROBING_INTERVAL; tsk->probing_state = TIPC_CONN_OK; - sk_reset_timer(sk, &sk->sk_timer, jiffies + tsk->probing_intv); + sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTERVAL); tipc_node_add_conn(net, peer_node, tsk->portid, peer_port); tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid); tsk->peer_caps = tipc_node_get_capabilities(net, peer_node); @@ -2240,13 +2237,15 @@ static void tipc_sk_timeout(unsigned long data) sk_reset_timer(sk, &sk->sk_timer, (HZ / 20)); } - } else { - skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, - INT_H_SIZE, 0, peer_node, own_node, - peer_port, tsk->portid, TIPC_OK); - tsk->probing_state = TIPC_CONN_PROBING; - sk_reset_timer(sk, &sk->sk_timer, jiffies + tsk->probing_intv); + bh_unlock_sock(sk); + goto exit; } + + skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, + INT_H_SIZE, 0, peer_node, own_node, + peer_port, tsk->portid, TIPC_OK); + tsk->probing_state = TIPC_CONN_PROBING; + sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTERVAL); bh_unlock_sock(sk); if (skb) tipc_node_xmit_skb(sock_net(sk), skb, peer_node, tsk->portid); -- cgit v1.1 From c752023aab5856559a295d045530af89cc51df06 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:42 +0100 Subject: tipc: remove socket state SS_READY Until now, tipc socket state SS_READY declares that the socket is a connectionless socket. In this commit, we remove the state SS_READY and replace it with a condition which returns true for datagram / connectionless sockets. Acked-by: Ying Xue Acked-by: Jon Maloy Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 1b1aa941..a8c1076 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -45,7 +45,6 @@ #include "netlink.h" #define SS_LISTENING -1 /* socket is listening */ -#define SS_READY -2 /* socket is connectionless */ #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ #define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ @@ -294,6 +293,16 @@ static bool tipc_sk_connected(struct sock *sk) return sk->sk_socket->state == SS_CONNECTED; } +/* tipc_sk_type_connectionless - check if the socket is datagram socket + * @sk: socket + * + * Returns true if connection less, false otherwise + */ +static bool tipc_sk_type_connectionless(struct sock *sk) +{ + return sk->sk_type == SOCK_RDM || sk->sk_type == SOCK_DGRAM; +} + /* tsk_peer_msg - verify if message was sent by connected port's peer * * Handles cases where the node's network address has changed from @@ -345,7 +354,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock, { struct tipc_net *tn; const struct proto_ops *ops; - socket_state state; struct sock *sk; struct tipc_sock *tsk; struct tipc_msg *msg; @@ -357,16 +365,13 @@ static int tipc_sk_create(struct net *net, struct socket *sock, switch (sock->type) { case SOCK_STREAM: ops = &stream_ops; - state = SS_UNCONNECTED; break; case SOCK_SEQPACKET: ops = &packet_ops; - state = SS_UNCONNECTED; break; case SOCK_DGRAM: case SOCK_RDM: ops = &msg_ops; - state = SS_READY; break; default: return -EPROTOTYPE; @@ -387,7 +392,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, /* Finish initializing socket data structures */ sock->ops = ops; - sock->state = state; + sock->state = SS_UNCONNECTED; sock_init_data(sock, sk); if (tipc_sk_insert(tsk)) { pr_warn("Socket create failed; port number exhausted\n"); @@ -407,7 +412,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, tsk->snd_win = tsk_adv_blocks(RCVBUF_MIN); tsk->rcv_win = tsk->snd_win; - if (sock->state == SS_READY) { + if (tipc_sk_type_connectionless(sk)) { tsk_set_unreturnable(tsk, true); if (sock->type == SOCK_DGRAM) tsk_set_unreliable(tsk, true); @@ -651,12 +656,19 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, sock_poll_wait(file, sk_sleep(sk), wait); + if (tipc_sk_type_connectionless(sk)) { + if (!tsk->link_cong) + mask |= POLLOUT; + if (!skb_queue_empty(&sk->sk_receive_queue)) + mask |= (POLLIN | POLLRDNORM); + return mask; + } + switch ((int)sock->state) { case SS_UNCONNECTED: if (!tsk->link_cong) mask |= POLLOUT; break; - case SS_READY: case SS_CONNECTED: if (!tsk->link_cong && !tsk_conn_cong(tsk)) mask |= POLLOUT; @@ -893,6 +905,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) struct tipc_msg *mhdr = &tsk->phdr; u32 dnode, dport; struct sk_buff_head pktchain; + bool is_connectionless = tipc_sk_type_connectionless(sk); struct sk_buff *skb; struct tipc_name_seq *seq; struct iov_iter save; @@ -903,7 +916,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) if (dsz > TIPC_MAX_USER_MSG_SIZE) return -EMSGSIZE; if (unlikely(!dest)) { - if (sock->state == SS_READY && tsk->peer.family == AF_TIPC) + if (is_connectionless && tsk->peer.family == AF_TIPC) dest = &tsk->peer; else return -EDESTADDRREQ; @@ -911,7 +924,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) dest->family != AF_TIPC) { return -EINVAL; } - if (unlikely(sock->state != SS_READY)) { + if (!is_connectionless) { if (sock->state == SS_LISTENING) return -EPIPE; if (sock->state != SS_UNCONNECTED) @@ -966,7 +979,7 @@ new_mtu: TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong; rc = tipc_node_xmit(net, &pktchain, dnode, tsk->portid); if (likely(!rc)) { - if (sock->state != SS_READY) + if (!is_connectionless) sock->state = SS_CONNECTING; return dsz; } @@ -1337,6 +1350,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, struct tipc_sock *tsk = tipc_sk(sk); struct sk_buff *buf; struct tipc_msg *msg; + bool is_connectionless = tipc_sk_type_connectionless(sk); long timeo; unsigned int sz; u32 err; @@ -1348,7 +1362,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, lock_sock(sk); - if (unlikely(sock->state == SS_UNCONNECTED)) { + if (!is_connectionless && unlikely(sock->state == SS_UNCONNECTED)) { res = -ENOTCONN; goto exit; } @@ -1393,8 +1407,8 @@ restart: goto exit; res = sz; } else { - if ((sock->state == SS_READY) || - ((err == TIPC_CONN_SHUTDOWN) || m->msg_control)) + if (is_connectionless || err == TIPC_CONN_SHUTDOWN || + m->msg_control) res = 0; else res = -ECONNRESET; @@ -1403,7 +1417,7 @@ restart: if (unlikely(flags & MSG_PEEK)) goto exit; - if (likely(sock->state != SS_READY)) { + if (likely(!is_connectionless)) { tsk->rcv_unacked += tsk_inc(tsk, hlen + sz); if (unlikely(tsk->rcv_unacked >= (tsk->rcv_win / 4))) tipc_sk_send_ack(tsk); @@ -1699,7 +1713,6 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *skb) static bool filter_rcv(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *xmitq) { - struct socket *sock = sk->sk_socket; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_msg *hdr = buf_msg(skb); unsigned int limit = rcvbuf_limit(sk, skb); @@ -1725,7 +1738,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb, } /* Reject if wrong message type for current socket state */ - if (unlikely(sock->state == SS_READY)) { + if (tipc_sk_type_connectionless(sk)) { if (msg_connected(hdr)) { err = TIPC_ERR_NO_PORT; goto reject; @@ -1935,7 +1948,7 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, lock_sock(sk); /* DGRAM/RDM connect(), just save the destaddr */ - if (sock->state == SS_READY) { + if (tipc_sk_type_connectionless(sk)) { if (dst->family == AF_UNSPEC) { memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc)); } else if (destlen != sizeof(struct sockaddr_tipc)) { -- cgit v1.1 From 0c288c86928e50d6d8d2efa4ca23dca58d28543e Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:43 +0100 Subject: tipc: create TIPC_LISTEN as a new sk_state Until now, tipc maintains the socket state in sock->state variable. This is used to maintain generic socket states, but in tipc we overload it and save tipc socket states like TIPC_LISTEN. Other protocols like TCP, UDP store protocol specific states in sk->sk_state instead. In this commit, we : - declare a new tipc state TIPC_LISTEN, that replaces SS_LISTEN - Create a new function tipc_set_state(), to update sk->sk_state. - TIPC_LISTEN state is maintained in sk->sk_state. - replace references to SS_LISTEN with TIPC_LISTEN. There is no functional change in this commit. Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 62 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index a8c1076..ce7d9be 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -44,8 +44,6 @@ #include "bcast.h" #include "netlink.h" -#define SS_LISTENING -1 /* socket is listening */ - #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ #define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ #define TIPC_FWD_MSG 1 @@ -54,6 +52,10 @@ #define TIPC_MAX_PORT 0xffffffff #define TIPC_MIN_PORT 1 +enum { + TIPC_LISTEN = TCP_LISTEN, +}; + /** * struct tipc_sock - TIPC socket structure * @sk: socket - interacts with 'port' and with user via the socket API @@ -337,6 +339,31 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) return false; } +/* tipc_set_sk_state - set the sk_state of the socket + * @sk: socket + * + * Caller must hold socket lock + * + * Returns 0 on success, errno otherwise + */ +static int tipc_set_sk_state(struct sock *sk, int state) +{ + int oldstate = sk->sk_socket->state; + int res = -EINVAL; + + switch (state) { + case TIPC_LISTEN: + if (oldstate == SS_UNCONNECTED) + res = 0; + break; + } + + if (!res) + sk->sk_state = state; + + return res; +} + /** * tipc_sk_create - create a TIPC socket * @net: network namespace (must be default network) @@ -666,15 +693,22 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, switch ((int)sock->state) { case SS_UNCONNECTED: - if (!tsk->link_cong) - mask |= POLLOUT; + switch (sk->sk_state) { + case TIPC_LISTEN: + if (!skb_queue_empty(&sk->sk_receive_queue)) + mask |= (POLLIN | POLLRDNORM); + break; + default: + if (!tsk->link_cong) + mask |= POLLOUT; + break; + } break; case SS_CONNECTED: if (!tsk->link_cong && !tsk_conn_cong(tsk)) mask |= POLLOUT; /* fall thru' */ case SS_CONNECTING: - case SS_LISTENING: if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= (POLLIN | POLLRDNORM); break; @@ -925,7 +959,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) return -EINVAL; } if (!is_connectionless) { - if (sock->state == SS_LISTENING) + if (sk->sk_state == TIPC_LISTEN) return -EPIPE; if (sock->state != SS_UNCONNECTED) return -EISCONN; @@ -1651,7 +1685,6 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) msg_set_dest_droppable(hdr, 1); return false; - case SS_LISTENING: case SS_UNCONNECTED: /* Accept only SYN message */ @@ -2026,15 +2059,9 @@ static int tipc_listen(struct socket *sock, int len) int res; lock_sock(sk); - - if (sock->state != SS_UNCONNECTED) - res = -EINVAL; - else { - sock->state = SS_LISTENING; - res = 0; - } - + res = tipc_set_sk_state(sk, TIPC_LISTEN); release_sock(sk); + return res; } @@ -2060,9 +2087,6 @@ static int tipc_wait_for_accept(struct socket *sock, long timeo) err = 0; if (!skb_queue_empty(&sk->sk_receive_queue)) break; - err = -EINVAL; - if (sock->state != SS_LISTENING) - break; err = -EAGAIN; if (!timeo) break; @@ -2093,7 +2117,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) lock_sock(sk); - if (sock->state != SS_LISTENING) { + if (sk->sk_state != TIPC_LISTEN) { res = -EINVAL; goto exit; } -- cgit v1.1 From 8ea642ee9a0dbcb25aace4cfd0ed41aeee893a7f Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:44 +0100 Subject: tipc: create TIPC_ESTABLISHED as a new sk_state Until now, tipc maintains probing state for connected sockets in tsk->probing_state variable. In this commit, we express this information as socket states and this remove the variable. We set probe_unacked flag when a probe is sent out and reset it if we receive a reply. Instead of the probing state TIPC_CONN_OK, we create a new state TIPC_ESTABLISHED. There is no functional change in this commit. Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index ce7d9be..9215e21 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -47,13 +47,12 @@ #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ #define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ #define TIPC_FWD_MSG 1 -#define TIPC_CONN_OK 0 -#define TIPC_CONN_PROBING 1 #define TIPC_MAX_PORT 0xffffffff #define TIPC_MIN_PORT 1 enum { TIPC_LISTEN = TCP_LISTEN, + TIPC_ESTABLISHED = TCP_ESTABLISHED, }; /** @@ -88,9 +87,9 @@ struct tipc_sock { struct list_head sock_list; struct list_head publications; u32 pub_count; - u32 probing_state; uint conn_timeout; atomic_t dupl_rcvcnt; + bool probe_unacked; bool link_cong; u16 snt_unacked; u16 snd_win; @@ -356,6 +355,11 @@ static int tipc_set_sk_state(struct sock *sk, int state) if (oldstate == SS_UNCONNECTED) res = 0; break; + case TIPC_ESTABLISHED: + if (oldstate == SS_CONNECTING || + oldstate == SS_UNCONNECTED) + res = 0; + break; } if (!res) @@ -858,7 +862,7 @@ static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb, if (!tsk_peer_msg(tsk, hdr)) goto exit; - tsk->probing_state = TIPC_CONN_OK; + tsk->probe_unacked = false; if (mtyp == CONN_PROBE) { msg_set_type(hdr, CONN_PROBE_REPLY); @@ -1198,8 +1202,8 @@ static void tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port, msg_set_lookup_scope(msg, 0); msg_set_hdr_sz(msg, SHORT_H_SIZE); - tsk->probing_state = TIPC_CONN_OK; sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTERVAL); + tipc_set_sk_state(sk, TIPC_ESTABLISHED); tipc_node_add_conn(net, peer_node, tsk->portid, peer_port); tsk->max_pkt = tipc_node_get_mtu(net, peer_node, tsk->portid); tsk->peer_caps = tipc_node_get_capabilities(net, peer_node); @@ -2263,7 +2267,7 @@ static void tipc_sk_timeout(unsigned long data) peer_port = tsk_peer_port(tsk); peer_node = tsk_peer_node(tsk); - if (tsk->probing_state == TIPC_CONN_PROBING) { + if (tsk->probe_unacked) { if (!sock_owned_by_user(sk)) { sk->sk_socket->state = SS_DISCONNECTING; tipc_node_remove_conn(sock_net(sk), tsk_peer_node(tsk), @@ -2281,7 +2285,7 @@ static void tipc_sk_timeout(unsigned long data) skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, 0, peer_node, own_node, peer_port, tsk->portid, TIPC_OK); - tsk->probing_state = TIPC_CONN_PROBING; + tsk->probe_unacked = true; sk_reset_timer(sk, &sk->sk_timer, jiffies + CONN_PROBING_INTERVAL); bh_unlock_sock(sk); if (skb) -- cgit v1.1 From 438adcaf0df181fb6bf2afa917c741cc8c9241c3 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:45 +0100 Subject: tipc: create TIPC_OPEN as a new sk_state In this commit, we create a new tipc socket state TIPC_OPEN in sk_state. We primarily replace the SS_UNCONNECTED sock->state with TIPC_OPEN. Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 97 ++++++++++++++++++++++++------------------------------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 9215e21..b14dd25 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -53,6 +53,7 @@ enum { TIPC_LISTEN = TCP_LISTEN, TIPC_ESTABLISHED = TCP_ESTABLISHED, + TIPC_OPEN = TCP_CLOSE, }; /** @@ -348,16 +349,21 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) static int tipc_set_sk_state(struct sock *sk, int state) { int oldstate = sk->sk_socket->state; + int oldsk_state = sk->sk_state; int res = -EINVAL; switch (state) { + case TIPC_OPEN: + res = 0; + break; case TIPC_LISTEN: - if (oldstate == SS_UNCONNECTED) + if (oldsk_state == TIPC_OPEN) res = 0; break; case TIPC_ESTABLISHED: if (oldstate == SS_CONNECTING || - oldstate == SS_UNCONNECTED) + oldstate == SS_UNCONNECTED || + oldsk_state == TIPC_OPEN) res = 0; break; } @@ -423,8 +429,8 @@ static int tipc_sk_create(struct net *net, struct socket *sock, /* Finish initializing socket data structures */ sock->ops = ops; - sock->state = SS_UNCONNECTED; sock_init_data(sock, sk); + tipc_set_sk_state(sk, TIPC_OPEN); if (tipc_sk_insert(tsk)) { pr_warn("Socket create failed; port number exhausted\n"); return -EINVAL; @@ -448,6 +454,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, if (sock->type == SOCK_DGRAM) tsk_set_unreliable(tsk, true); } + return 0; } @@ -652,28 +659,6 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, * exits. TCP and other protocols seem to rely on higher level poll routines * to handle any preventable race conditions, so TIPC will do the same ... * - * TIPC sets the returned events as follows: - * - * socket state flags set - * ------------ --------- - * unconnected no read flags - * POLLOUT if port is not congested - * - * connecting POLLIN/POLLRDNORM if ACK/NACK in rx queue - * no write flags - * - * connected POLLIN/POLLRDNORM if data in rx queue - * POLLOUT if port is not congested - * - * disconnecting POLLIN/POLLRDNORM/POLLHUP - * no write flags - * - * listening POLLIN if SYN in rx queue - * no write flags - * - * ready POLLIN/POLLRDNORM if data in rx queue - * [connectionless] POLLOUT (since port cannot be congested) - * * IMPORTANT: The fact that a read or write operation is indicated does NOT * imply that the operation will succeed, merely that it should be performed * and will not block. @@ -687,27 +672,7 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, sock_poll_wait(file, sk_sleep(sk), wait); - if (tipc_sk_type_connectionless(sk)) { - if (!tsk->link_cong) - mask |= POLLOUT; - if (!skb_queue_empty(&sk->sk_receive_queue)) - mask |= (POLLIN | POLLRDNORM); - return mask; - } - switch ((int)sock->state) { - case SS_UNCONNECTED: - switch (sk->sk_state) { - case TIPC_LISTEN: - if (!skb_queue_empty(&sk->sk_receive_queue)) - mask |= (POLLIN | POLLRDNORM); - break; - default: - if (!tsk->link_cong) - mask |= POLLOUT; - break; - } - break; case SS_CONNECTED: if (!tsk->link_cong && !tsk_conn_cong(tsk)) mask |= POLLOUT; @@ -719,6 +684,20 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, case SS_DISCONNECTING: mask = (POLLIN | POLLRDNORM | POLLHUP); break; + default: + switch (sk->sk_state) { + case TIPC_OPEN: + if (!tsk->link_cong) + mask |= POLLOUT; + if (tipc_sk_type_connectionless(sk) && + (!skb_queue_empty(&sk->sk_receive_queue))) + mask |= (POLLIN | POLLRDNORM); + break; + case TIPC_LISTEN: + if (!skb_queue_empty(&sk->sk_receive_queue)) + mask |= (POLLIN | POLLRDNORM); + break; + } } return mask; @@ -965,7 +944,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) if (!is_connectionless) { if (sk->sk_state == TIPC_LISTEN) return -EPIPE; - if (sock->state != SS_UNCONNECTED) + if (sk->sk_state != TIPC_OPEN) return -EISCONN; if (tsk->published) return -EOPNOTSUPP; @@ -1400,7 +1379,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, lock_sock(sk); - if (!is_connectionless && unlikely(sock->state == SS_UNCONNECTED)) { + if (!is_connectionless && unlikely(sk->sk_state == TIPC_OPEN)) { res = -ENOTCONN; goto exit; } @@ -1497,7 +1476,7 @@ static int tipc_recv_stream(struct socket *sock, struct msghdr *m, lock_sock(sk); - if (unlikely(sock->state == SS_UNCONNECTED)) { + if (unlikely(sk->sk_state == TIPC_OPEN)) { res = -ENOTCONN; goto exit; } @@ -1689,17 +1668,22 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) msg_set_dest_droppable(hdr, 1); return false; - case SS_UNCONNECTED: + case SS_DISCONNECTING: + break; + } + switch (sk->sk_state) { + case TIPC_OPEN: + break; + case TIPC_LISTEN: /* Accept only SYN message */ if (!msg_connected(hdr) && !(msg_errcode(hdr))) return true; break; - case SS_DISCONNECTING: - break; default: - pr_err("Unknown socket state %u\n", sock->state); + pr_err("Unknown sk_state %u\n", sk->sk_state); } + return false; } @@ -2008,8 +1992,9 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, } previous = sock->state; - switch (sock->state) { - case SS_UNCONNECTED: + + switch (sk->sk_state) { + case TIPC_OPEN: /* Send a 'SYN-' to destination */ m.msg_name = dest; m.msg_namelen = destlen; @@ -2029,6 +2014,10 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, * case is EINPROGRESS, rather than EALREADY. */ res = -EINPROGRESS; + break; + } + + switch (sock->state) { case SS_CONNECTING: if (previous == SS_CONNECTING) res = -EALREADY; -- cgit v1.1 From 9fd4b070f6311faa7ba1ae746e955fedfbe3e045 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:46 +0100 Subject: tipc: create TIPC_DISCONNECTING as a new sk_state In this commit, we create a new tipc socket state TIPC_DISCONNECTING in sk_state. TIPC_DISCONNECTING is replacing the socket connection status update using SS_DISCONNECTING. TIPC_DISCONNECTING is set for connection oriented sockets at: - tipc_shutdown() - connection probe timeout - when we receive an error message on the connection. There is no functional change in this commit. Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index b14dd25..a48c0c0 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -54,6 +54,7 @@ enum { TIPC_LISTEN = TCP_LISTEN, TIPC_ESTABLISHED = TCP_ESTABLISHED, TIPC_OPEN = TCP_CLOSE, + TIPC_DISCONNECTING = TCP_CLOSE_WAIT, }; /** @@ -362,10 +363,14 @@ static int tipc_set_sk_state(struct sock *sk, int state) break; case TIPC_ESTABLISHED: if (oldstate == SS_CONNECTING || - oldstate == SS_UNCONNECTED || oldsk_state == TIPC_OPEN) res = 0; break; + case TIPC_DISCONNECTING: + if (oldstate == SS_CONNECTING || + oldsk_state == TIPC_ESTABLISHED) + res = 0; + break; } if (!res) @@ -621,13 +626,14 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; - struct tipc_sock *tsk = tipc_sk(sock->sk); + struct sock *sk = sock->sk; + struct tipc_sock *tsk = tipc_sk(sk); struct tipc_net *tn = net_generic(sock_net(sock->sk), tipc_net_id); memset(addr, 0, sizeof(*addr)); if (peer) { if ((sock->state != SS_CONNECTED) && - ((peer != 2) || (sock->state != SS_DISCONNECTING))) + ((peer != 2) || (sk->sk_state != TIPC_DISCONNECTING))) return -ENOTCONN; addr->addr.id.ref = tsk_peer_port(tsk); addr->addr.id.node = tsk_peer_node(tsk); @@ -693,6 +699,9 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, (!skb_queue_empty(&sk->sk_receive_queue))) mask |= (POLLIN | POLLRDNORM); break; + case TIPC_DISCONNECTING: + mask = (POLLIN | POLLRDNORM | POLLHUP); + break; case TIPC_LISTEN: if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= (POLLIN | POLLRDNORM); @@ -1028,7 +1037,7 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) int err = sock_error(sk); if (err) return err; - if (sock->state == SS_DISCONNECTING) + if (sk->sk_state == TIPC_DISCONNECTING) return -EPIPE; else if (sock->state != SS_CONNECTED) return -ENOTCONN; @@ -1098,7 +1107,7 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz) return -EMSGSIZE; if (unlikely(sock->state != SS_CONNECTED)) { - if (sock->state == SS_DISCONNECTING) + if (sk->sk_state == TIPC_DISCONNECTING) return -EPIPE; else return -ENOTCONN; @@ -1626,7 +1635,7 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) return false; if (unlikely(msg_errcode(hdr))) { - sock->state = SS_DISCONNECTING; + tipc_set_sk_state(sk, TIPC_DISCONNECTING); /* Let timer expire on it's own */ tipc_node_remove_conn(net, tsk_peer_node(tsk), tsk->portid); @@ -1641,13 +1650,13 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) return false; if (unlikely(msg_errcode(hdr))) { - sock->state = SS_DISCONNECTING; + tipc_set_sk_state(sk, TIPC_DISCONNECTING); sk->sk_err = ECONNREFUSED; return true; } if (unlikely(!msg_isdata(hdr))) { - sock->state = SS_DISCONNECTING; + tipc_set_sk_state(sk, TIPC_DISCONNECTING); sk->sk_err = EINVAL; return true; } @@ -1674,6 +1683,7 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) switch (sk->sk_state) { case TIPC_OPEN: + case TIPC_DISCONNECTING: break; case TIPC_LISTEN: /* Accept only SYN message */ @@ -2195,9 +2205,7 @@ static int tipc_shutdown(struct socket *sock, int how) lock_sock(sk); - switch (sock->state) { - case SS_CONNECTING: - case SS_CONNECTED: + if (sock->state == SS_CONNECTING || sock->state == SS_CONNECTED) { restart: dnode = tsk_peer_node(tsk); @@ -2218,11 +2226,12 @@ restart: if (skb) tipc_node_xmit_skb(net, skb, dnode, tsk->portid); } - sock->state = SS_DISCONNECTING; + tipc_set_sk_state(sk, TIPC_DISCONNECTING); tipc_node_remove_conn(net, dnode, tsk->portid); - /* fall through */ + } - case SS_DISCONNECTING: + switch (sk->sk_state) { + case TIPC_DISCONNECTING: /* Discard any unreceived messages */ __skb_queue_purge(&sk->sk_receive_queue); @@ -2258,7 +2267,7 @@ static void tipc_sk_timeout(unsigned long data) if (tsk->probe_unacked) { if (!sock_owned_by_user(sk)) { - sk->sk_socket->state = SS_DISCONNECTING; + tipc_set_sk_state(sk, TIPC_DISCONNECTING); tipc_node_remove_conn(sock_net(sk), tsk_peer_node(tsk), tsk_peer_port(tsk)); sk->sk_state_change(sk); -- cgit v1.1 From 6f00089c7372ba9732c046fe242301dfb0a13233 Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:47 +0100 Subject: tipc: remove SS_DISCONNECTING state In this commit, we replace the references to SS_DISCONNECTING with the combination of sk_state TIPC_DISCONNECTING and flags set in sk_shutdown. We introduce a new function _tipc_shutdown(), which provides the common code required by tipc_release() and tipc_shutdown(). Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 132 +++++++++++++++++++++--------------------------------- 1 file changed, 52 insertions(+), 80 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index a48c0c0..e732b1f 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -442,6 +442,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, } msg_set_origport(msg, tsk->portid); setup_timer(&sk->sk_timer, tipc_sk_timeout, (unsigned long)tsk); + sk->sk_shutdown = 0; sk->sk_backlog_rcv = tipc_backlog_rcv; sk->sk_rcvbuf = sysctl_tipc_rmem[1]; sk->sk_data_ready = tipc_data_ready; @@ -470,6 +471,44 @@ static void tipc_sk_callback(struct rcu_head *head) sock_put(&tsk->sk); } +/* Caller should hold socket lock for the socket. */ +static void __tipc_shutdown(struct socket *sock, int error) +{ + struct sock *sk = sock->sk; + struct tipc_sock *tsk = tipc_sk(sk); + struct net *net = sock_net(sk); + u32 dnode = tsk_peer_node(tsk); + struct sk_buff *skb; + + /* Reject all unreceived messages, except on an active connection + * (which disconnects locally & sends a 'FIN+' to peer). + */ + while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { + if (TIPC_SKB_CB(skb)->bytes_read) { + kfree_skb(skb); + } else { + if (!tipc_sk_type_connectionless(sk) && + sk->sk_state != TIPC_DISCONNECTING) { + tipc_set_sk_state(sk, TIPC_DISCONNECTING); + tipc_node_remove_conn(net, dnode, tsk->portid); + } + tipc_sk_respond(sk, skb, error); + } + } + if (sk->sk_state != TIPC_DISCONNECTING) { + skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, + TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, + tsk_own_node(tsk), tsk_peer_port(tsk), + tsk->portid, error); + if (skb) + tipc_node_xmit_skb(net, skb, dnode, tsk->portid); + if (!tipc_sk_type_connectionless(sk)) { + tipc_node_remove_conn(net, dnode, tsk->portid); + tipc_set_sk_state(sk, TIPC_DISCONNECTING); + } + } +} + /** * tipc_release - destroy a TIPC socket * @sock: socket to destroy @@ -489,10 +528,7 @@ static void tipc_sk_callback(struct rcu_head *head) static int tipc_release(struct socket *sock) { struct sock *sk = sock->sk; - struct net *net; struct tipc_sock *tsk; - struct sk_buff *skb; - u32 dnode; /* * Exit if socket isn't fully initialized (occurs when a failed accept() @@ -501,46 +537,16 @@ static int tipc_release(struct socket *sock) if (sk == NULL) return 0; - net = sock_net(sk); tsk = tipc_sk(sk); lock_sock(sk); - /* - * Reject all unreceived messages, except on an active connection - * (which disconnects locally & sends a 'FIN+' to peer) - */ - dnode = tsk_peer_node(tsk); - while (sock->state != SS_DISCONNECTING) { - skb = __skb_dequeue(&sk->sk_receive_queue); - if (skb == NULL) - break; - if (TIPC_SKB_CB(skb)->bytes_read) - kfree_skb(skb); - else { - if ((sock->state == SS_CONNECTING) || - (sock->state == SS_CONNECTED)) { - sock->state = SS_DISCONNECTING; - tipc_node_remove_conn(net, dnode, tsk->portid); - } - tipc_sk_respond(sk, skb, TIPC_ERR_NO_PORT); - } - } - + __tipc_shutdown(sock, TIPC_ERR_NO_PORT); + sk->sk_shutdown = SHUTDOWN_MASK; tipc_sk_withdraw(tsk, 0, NULL); sk_stop_timer(sk, &sk->sk_timer); tipc_sk_remove(tsk); - if (tipc_sk_connected(sk)) { - skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, - TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, - tsk_own_node(tsk), tsk_peer_port(tsk), - tsk->portid, TIPC_ERR_NO_PORT); - if (skb) - tipc_node_xmit_skb(net, skb, dnode, tsk->portid); - tipc_node_remove_conn(net, dnode, tsk->portid); - } /* Reject any messages that accumulated in backlog queue */ - sock->state = SS_DISCONNECTING; release_sock(sk); call_rcu(&tsk->rcu, tipc_sk_callback); @@ -678,6 +684,11 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, sock_poll_wait(file, sk_sleep(sk), wait); + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP | POLLIN | POLLRDNORM; + if (sk->sk_shutdown == SHUTDOWN_MASK) + mask |= POLLHUP; + switch ((int)sock->state) { case SS_CONNECTED: if (!tsk->link_cong && !tsk_conn_cong(tsk)) @@ -687,9 +698,6 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= (POLLIN | POLLRDNORM); break; - case SS_DISCONNECTING: - mask = (POLLIN | POLLRDNORM | POLLHUP); - break; default: switch (sk->sk_state) { case TIPC_OPEN: @@ -882,7 +890,7 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) int err = sock_error(sk); if (err) return err; - if (sock->state == SS_DISCONNECTING) + if (sk->sk_shutdown & SEND_SHUTDOWN) return -EPIPE; if (!*timeo_p) return -EAGAIN; @@ -1335,7 +1343,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) for (;;) { prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); if (timeo && skb_queue_empty(&sk->sk_receive_queue)) { - if (sock->state == SS_DISCONNECTING) { + if (sk->sk_shutdown & RCV_SHUTDOWN) { err = -ENOTCONN; break; } @@ -1676,9 +1684,6 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) /* 'ACK-' message is neither accepted nor rejected: */ msg_set_dest_droppable(hdr, 1); return false; - - case SS_DISCONNECTING: - break; } switch (sk->sk_state) { @@ -2191,13 +2196,6 @@ exit: static int tipc_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; - struct net *net = sock_net(sk); - struct tipc_sock *tsk = tipc_sk(sk); - struct sk_buff *skb; - u32 dnode = tsk_peer_node(tsk); - u32 dport = tsk_peer_port(tsk); - u32 onode = tipc_own_addr(net); - u32 oport = tsk->portid; int res; if (how != SHUT_RDWR) @@ -2205,43 +2203,17 @@ static int tipc_shutdown(struct socket *sock, int how) lock_sock(sk); - if (sock->state == SS_CONNECTING || sock->state == SS_CONNECTED) { - -restart: - dnode = tsk_peer_node(tsk); - - /* Disconnect and send a 'FIN+' or 'FIN-' message to peer */ - skb = __skb_dequeue(&sk->sk_receive_queue); - if (skb) { - if (TIPC_SKB_CB(skb)->bytes_read) { - kfree_skb(skb); - goto restart; - } - tipc_sk_respond(sk, skb, TIPC_CONN_SHUTDOWN); - } else { - skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, - TIPC_CONN_MSG, SHORT_H_SIZE, - 0, dnode, onode, dport, oport, - TIPC_CONN_SHUTDOWN); - if (skb) - tipc_node_xmit_skb(net, skb, dnode, tsk->portid); - } - tipc_set_sk_state(sk, TIPC_DISCONNECTING); - tipc_node_remove_conn(net, dnode, tsk->portid); - } - - switch (sk->sk_state) { - case TIPC_DISCONNECTING: + __tipc_shutdown(sock, TIPC_CONN_SHUTDOWN); + sk->sk_shutdown = SEND_SHUTDOWN; + if (sk->sk_state == TIPC_DISCONNECTING) { /* Discard any unreceived messages */ __skb_queue_purge(&sk->sk_receive_queue); /* Wake up anyone sleeping in poll */ sk->sk_state_change(sk); res = 0; - break; - - default: + } else { res = -ENOTCONN; } -- cgit v1.1 From 99a20889816a653e192db23701b0fda00399b91f Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:48 +0100 Subject: tipc: create TIPC_CONNECTING as a new sk_state In this commit, we create a new tipc socket state TIPC_CONNECTING by primarily replacing the SS_CONNECTING with TIPC_CONNECTING. There is no functional change in this commit. Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 60 ++++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index e732b1f..074f4d5 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -55,6 +55,7 @@ enum { TIPC_ESTABLISHED = TCP_ESTABLISHED, TIPC_OPEN = TCP_CLOSE, TIPC_DISCONNECTING = TCP_CLOSE_WAIT, + TIPC_CONNECTING = TCP_SYN_SENT, }; /** @@ -349,7 +350,6 @@ static bool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) */ static int tipc_set_sk_state(struct sock *sk, int state) { - int oldstate = sk->sk_socket->state; int oldsk_state = sk->sk_state; int res = -EINVAL; @@ -358,16 +358,17 @@ static int tipc_set_sk_state(struct sock *sk, int state) res = 0; break; case TIPC_LISTEN: + case TIPC_CONNECTING: if (oldsk_state == TIPC_OPEN) res = 0; break; case TIPC_ESTABLISHED: - if (oldstate == SS_CONNECTING || + if (oldsk_state == TIPC_CONNECTING || oldsk_state == TIPC_OPEN) res = 0; break; case TIPC_DISCONNECTING: - if (oldstate == SS_CONNECTING || + if (oldsk_state == TIPC_CONNECTING || oldsk_state == TIPC_ESTABLISHED) res = 0; break; @@ -689,16 +690,12 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; - switch ((int)sock->state) { - case SS_CONNECTED: + if ((int)sock->state == SS_CONNECTED) { if (!tsk->link_cong && !tsk_conn_cong(tsk)) mask |= POLLOUT; - /* fall thru' */ - case SS_CONNECTING: if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= (POLLIN | POLLRDNORM); - break; - default: + } else { switch (sk->sk_state) { case TIPC_OPEN: if (!tsk->link_cong) @@ -711,6 +708,7 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, mask = (POLLIN | POLLRDNORM | POLLHUP); break; case TIPC_LISTEN: + case TIPC_CONNECTING: if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= (POLLIN | POLLRDNORM); break; @@ -1014,7 +1012,7 @@ new_mtu: rc = tipc_node_xmit(net, &pktchain, dnode, tsk->portid); if (likely(!rc)) { if (!is_connectionless) - sock->state = SS_CONNECTING; + tipc_set_sk_state(sk, TIPC_CONNECTING); return dsz; } if (rc == -ELINKCONG) { @@ -1650,9 +1648,10 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) sk->sk_state_change(sk); } return true; + } - case SS_CONNECTING: - + switch (sk->sk_state) { + case TIPC_CONNECTING: /* Accept only ACK or NACK message */ if (unlikely(!msg_connected(hdr))) return false; @@ -1684,9 +1683,7 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) /* 'ACK-' message is neither accepted nor rejected: */ msg_set_dest_droppable(hdr, 1); return false; - } - switch (sk->sk_state) { case TIPC_OPEN: case TIPC_DISCONNECTING: break; @@ -1955,7 +1952,8 @@ static int tipc_wait_for_connect(struct socket *sock, long *timeo_p) return sock_intr_errno(*timeo_p); prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - done = sk_wait_event(sk, timeo_p, sock->state != SS_CONNECTING); + done = sk_wait_event(sk, timeo_p, + sk->sk_state != TIPC_CONNECTING); finish_wait(sk_sleep(sk), &wait); } while (!done); return 0; @@ -1978,7 +1976,7 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; struct msghdr m = {NULL,}; long timeout = (flags & O_NONBLOCK) ? 0 : tsk->conn_timeout; - socket_state previous; + int previous; int res = 0; lock_sock(sk); @@ -2006,7 +2004,7 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, goto exit; } - previous = sock->state; + previous = sk->sk_state; switch (sk->sk_state) { case TIPC_OPEN: @@ -2024,31 +2022,29 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, if ((res < 0) && (res != -EWOULDBLOCK)) goto exit; - /* Just entered SS_CONNECTING state; the only + /* Just entered TIPC_CONNECTING state; the only * difference is that return value in non-blocking * case is EINPROGRESS, rather than EALREADY. */ res = -EINPROGRESS; - break; - } - - switch (sock->state) { - case SS_CONNECTING: - if (previous == SS_CONNECTING) - res = -EALREADY; - if (!timeout) + /* fall thru' */ + case TIPC_CONNECTING: + if (!timeout) { + if (previous == TIPC_CONNECTING) + res = -EALREADY; goto exit; + } timeout = msecs_to_jiffies(timeout); /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ res = tipc_wait_for_connect(sock, &timeout); - break; - case SS_CONNECTED: + goto exit; + } + + if (sock->state == SS_CONNECTED) res = -EISCONN; - break; - default: + else res = -EINVAL; - break; - } + exit: release_sock(sk); return res; -- cgit v1.1 From f40acbaf425e88a992ff5d41f6237a8e465de8af Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 1 Nov 2016 14:02:49 +0100 Subject: tipc: remove SS_CONNECTED sock state In this commit, we replace references to sock->state SS_CONNECTE with sk_state TIPC_ESTABLISHED. Finally, the sock->state is no longer explicitly used by tipc. The FSM below is for various types of connection oriented sockets. Stream Server Listening Socket: +-----------+ +-------------+ | TIPC_OPEN |------>| TIPC_LISTEN | +-----------+ +-------------+ Stream Server Data Socket: +-----------+ +------------------+ | TIPC_OPEN |------>| TIPC_ESTABLISHED | +-----------+ +------------------+ ^ | | | | v +--------------------+ | TIPC_DISCONNECTING | +--------------------+ Stream Socket Client: +-----------+ +-----------------+ | TIPC_OPEN |------>| TIPC_CONNECTING |------+ +-----------+ +-----------------+ | | | | | v | +------------------+ | | TIPC_ESTABLISHED | | +------------------+ | ^ | | | | | | v | +--------------------+ | | TIPC_DISCONNECTING |<--+ +--------------------+ Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 86 ++++++++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 074f4d5..1493963 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -294,7 +294,7 @@ static void tsk_rej_rx_queue(struct sock *sk) static bool tipc_sk_connected(struct sock *sk) { - return sk->sk_socket->state == SS_CONNECTED; + return sk->sk_state == TIPC_ESTABLISHED; } /* tipc_sk_type_connectionless - check if the socket is datagram socket @@ -639,7 +639,7 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, memset(addr, 0, sizeof(*addr)); if (peer) { - if ((sock->state != SS_CONNECTED) && + if ((!tipc_sk_connected(sk)) && ((peer != 2) || (sk->sk_state != TIPC_DISCONNECTING))) return -ENOTCONN; addr->addr.id.ref = tsk_peer_port(tsk); @@ -690,29 +690,26 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; - if ((int)sock->state == SS_CONNECTED) { + switch (sk->sk_state) { + case TIPC_ESTABLISHED: if (!tsk->link_cong && !tsk_conn_cong(tsk)) mask |= POLLOUT; + /* fall thru' */ + case TIPC_LISTEN: + case TIPC_CONNECTING: if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= (POLLIN | POLLRDNORM); - } else { - switch (sk->sk_state) { - case TIPC_OPEN: - if (!tsk->link_cong) - mask |= POLLOUT; - if (tipc_sk_type_connectionless(sk) && - (!skb_queue_empty(&sk->sk_receive_queue))) - mask |= (POLLIN | POLLRDNORM); - break; - case TIPC_DISCONNECTING: - mask = (POLLIN | POLLRDNORM | POLLHUP); - break; - case TIPC_LISTEN: - case TIPC_CONNECTING: - if (!skb_queue_empty(&sk->sk_receive_queue)) - mask |= (POLLIN | POLLRDNORM); - break; - } + break; + case TIPC_OPEN: + if (!tsk->link_cong) + mask |= POLLOUT; + if (tipc_sk_type_connectionless(sk) && + (!skb_queue_empty(&sk->sk_receive_queue))) + mask |= (POLLIN | POLLRDNORM); + break; + case TIPC_DISCONNECTING: + mask = (POLLIN | POLLRDNORM | POLLHUP); + break; } return mask; @@ -1045,7 +1042,7 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) return err; if (sk->sk_state == TIPC_DISCONNECTING) return -EPIPE; - else if (sock->state != SS_CONNECTED) + else if (!tipc_sk_connected(sk)) return -ENOTCONN; if (!*timeo_p) return -EAGAIN; @@ -1112,7 +1109,7 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz) if (dsz > (uint)INT_MAX) return -EMSGSIZE; - if (unlikely(sock->state != SS_CONNECTED)) { + if (unlikely(!tipc_sk_connected(sk))) { if (sk->sk_state == TIPC_DISCONNECTING) return -EPIPE; else @@ -1627,29 +1624,11 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) { struct sock *sk = &tsk->sk; struct net *net = sock_net(sk); - struct socket *sock = sk->sk_socket; struct tipc_msg *hdr = buf_msg(skb); if (unlikely(msg_mcast(hdr))) return false; - switch ((int)sock->state) { - case SS_CONNECTED: - - /* Accept only connection-based messages sent by peer */ - if (unlikely(!tsk_peer_msg(tsk, hdr))) - return false; - - if (unlikely(msg_errcode(hdr))) { - tipc_set_sk_state(sk, TIPC_DISCONNECTING); - /* Let timer expire on it's own */ - tipc_node_remove_conn(net, tsk_peer_node(tsk), - tsk->portid); - sk->sk_state_change(sk); - } - return true; - } - switch (sk->sk_state) { case TIPC_CONNECTING: /* Accept only ACK or NACK message */ @@ -1670,7 +1649,6 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) tipc_sk_finish_conn(tsk, msg_origport(hdr), msg_orignode(hdr)); msg_set_importance(&tsk->phdr, msg_importance(hdr)); - sock->state = SS_CONNECTED; /* If 'ACK+' message, add to socket receive queue */ if (msg_data_sz(hdr)) @@ -1692,6 +1670,19 @@ static bool filter_connect(struct tipc_sock *tsk, struct sk_buff *skb) if (!msg_connected(hdr) && !(msg_errcode(hdr))) return true; break; + case TIPC_ESTABLISHED: + /* Accept only connection-based messages sent by peer */ + if (unlikely(!tsk_peer_msg(tsk, hdr))) + return false; + + if (unlikely(msg_errcode(hdr))) { + tipc_set_sk_state(sk, TIPC_DISCONNECTING); + /* Let timer expire on it's own */ + tipc_node_remove_conn(net, tsk_peer_node(tsk), + tsk->portid); + sk->sk_state_change(sk); + } + return true; default: pr_err("Unknown sk_state %u\n", sk->sk_state); } @@ -2037,13 +2028,13 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, timeout = msecs_to_jiffies(timeout); /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ res = tipc_wait_for_connect(sock, &timeout); - goto exit; - } - - if (sock->state == SS_CONNECTED) + break; + case TIPC_ESTABLISHED: res = -EISCONN; - else + break; + default: res = -EINVAL; + } exit: release_sock(sk); @@ -2152,7 +2143,6 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) /* Connect new socket to it's peer */ tipc_sk_finish_conn(new_tsock, msg_origport(msg), msg_orignode(msg)); - new_sock->state = SS_CONNECTED; tsk_set_importance(new_tsock, msg_importance(msg)); if (msg_named(msg)) { -- cgit v1.1 From e58e415968110648231ed6783d38e78032661cee Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 31 Oct 2016 15:54:00 -0700 Subject: net: Enable support for VRF with ipv4 multicast Enable support for IPv4 multicast: - similar to unicast the flow struct is updated to L3 master device if relevant prior to calling fib_rules_lookup. The table id is saved to the lookup arg so the rule action for ipmr can return the table associated with the device. - ip_mr_forward needs to check for master device mismatch as well since the skb->dev is set to it - allow multicast address on VRF device for Rx by checking for the daddr in the VRF device as well as the original ingress device - on Tx need to drop to __mkroute_output when FIB lookup fails for multicast destination address. - if CONFIG_IP_MROUTE_MULTIPLE_TABLES is enabled VRF driver creates IPMR FIB rules on first device create similar to FIB rules. In addition the VRF driver does not divert IPv4 multicast packets: it breaks on Tx since the fib lookup fails on the mcast address. With this patch, ipmr forwarding and local rx/tx work. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/vrf.c | 23 ++++++++++++++++++----- net/ipv4/ipmr.c | 13 ++++++++++++- net/ipv4/route.c | 41 ++++++++++++++++++++++++++--------------- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 820de6a..3bca246 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -272,11 +272,6 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, if (IS_ERR(rt)) goto err; - if (rt->rt_type != RTN_UNICAST && rt->rt_type != RTN_LOCAL) { - ip_rt_put(rt); - goto err; - } - skb_dst_drop(skb); /* if dst.dev is loopback or the VRF device again this is locally @@ -611,6 +606,10 @@ static struct sk_buff *vrf_ip_out(struct net_device *vrf_dev, struct dst_entry *dst = NULL; struct rtable *rth; + /* don't divert multicast */ + if (ipv4_is_multicast(ip_hdr(skb)->daddr)) + return skb; + rcu_read_lock(); rth = rcu_dereference(vrf->rth); @@ -999,6 +998,9 @@ static struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev, skb->skb_iif = vrf_dev->ifindex; IPCB(skb)->flags |= IPSKB_L3SLAVE; + if (ipv4_is_multicast(ip_hdr(skb)->daddr)) + goto out; + /* loopback traffic; do not push through packet taps again. * Reset pkt_type for upper layers to process skb */ @@ -1162,8 +1164,19 @@ static int vrf_add_fib_rules(const struct net_device *dev) if (err < 0) goto ipv6_err; +#if IS_ENABLED(CONFIG_IP_MROUTE_MULTIPLE_TABLES) + err = vrf_fib_rule(dev, RTNL_FAMILY_IPMR, true); + if (err < 0) + goto ipmr_err; +#endif + return 0; +#if IS_ENABLED(CONFIG_IP_MROUTE_MULTIPLE_TABLES) +ipmr_err: + vrf_fib_rule(dev, AF_INET6, false); +#endif + ipv6_err: vrf_fib_rule(dev, AF_INET, false); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 51d71a7..f2fd13b 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -137,6 +137,9 @@ static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, .flags = FIB_LOOKUP_NOREF, }; + /* update flow if oif or iif point to device enslaved to l3mdev */ + l3mdev_update_flow(net, flowi4_to_flowi(flp4)); + err = fib_rules_lookup(net->ipv4.mr_rules_ops, flowi4_to_flowi(flp4), 0, &arg); if (err < 0) @@ -163,7 +166,9 @@ static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, return -EINVAL; } - mrt = ipmr_get_table(rule->fr_net, rule->table); + arg->table = fib_rule_get_table(rule, arg); + + mrt = ipmr_get_table(rule->fr_net, arg->table); if (!mrt) return -EAGAIN; res->mrt = mrt; @@ -1809,6 +1814,12 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt, /* Wrong interface: drop packet and (maybe) send PIM assert. */ if (mrt->vif_table[vif].dev != skb->dev) { + struct net_device *mdev; + + mdev = l3mdev_master_dev_rcu(mrt->vif_table[vif].dev); + if (mdev == skb->dev) + goto forward; + if (rt_is_output_route(skb_rtable(skb))) { /* It is our own packet, looped back. * Very complicated situation... diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 62d4d90..4392db8 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1980,25 +1980,35 @@ int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr, */ if (ipv4_is_multicast(daddr)) { struct in_device *in_dev = __in_dev_get_rcu(dev); + int our = 0; - if (in_dev) { - int our = ip_check_mc_rcu(in_dev, daddr, saddr, - ip_hdr(skb)->protocol); - if (our + if (in_dev) + our = ip_check_mc_rcu(in_dev, daddr, saddr, + ip_hdr(skb)->protocol); + + /* check l3 master if no match yet */ + if ((!in_dev || !our) && netif_is_l3_slave(dev)) { + struct in_device *l3_in_dev; + + l3_in_dev = __in_dev_get_rcu(skb->dev); + if (l3_in_dev) + our = ip_check_mc_rcu(l3_in_dev, daddr, saddr, + ip_hdr(skb)->protocol); + } + + res = -EINVAL; + if (our #ifdef CONFIG_IP_MROUTE - || - (!ipv4_is_local_multicast(daddr) && - IN_DEV_MFORWARD(in_dev)) + || + (!ipv4_is_local_multicast(daddr) && + IN_DEV_MFORWARD(in_dev)) #endif - ) { - int res = ip_route_input_mc(skb, daddr, saddr, - tos, dev, our); - rcu_read_unlock(); - return res; - } + ) { + res = ip_route_input_mc(skb, daddr, saddr, + tos, dev, our); } rcu_read_unlock(); - return -EINVAL; + return res; } res = ip_route_input_slow(skb, daddr, saddr, tos, dev); rcu_read_unlock(); @@ -2266,7 +2276,8 @@ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, res.fi = NULL; res.table = NULL; if (fl4->flowi4_oif && - !netif_index_is_l3_master(net, fl4->flowi4_oif)) { + (ipv4_is_multicast(fl4->daddr) || + !netif_index_is_l3_master(net, fl4->flowi4_oif))) { /* Apparently, routing tables are wrong. Assume, that the destination is on link. -- cgit v1.1 From 22ca904ad70afc831d8503e80be1b6558a978759 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 1 Nov 2016 14:45:52 +0000 Subject: genetlink: fix error return code in genl_register_family() Fix to return a negative error code from the idr_alloc() error handling case instead of 0, as done elsewhere in this function. Also fix the return value check of idr_alloc() since idr_alloc return negative errors on failure, not zero. Fixes: 2ae0f17df1cd ("genetlink: use idr to track families") Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index caf04d7..bbd3bff 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -362,8 +362,10 @@ int genl_register_family(struct genl_family *family) family->id = idr_alloc(&genl_fam_idr, family, start, end + 1, GFP_KERNEL); - if (!family->id) + if (family->id < 0) { + err = family->id; goto errout_locked; + } err = genl_validate_assign_mc_groups(family); if (err) -- cgit v1.1 From f6d0cbcf09c506b9b022df8f9d7693a7cec3c732 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 24 Oct 2016 16:56:40 +0200 Subject: netfilter: nf_tables: add fib expression Add FIB expression, supported for ipv4, ipv6 and inet family (the latter just dispatches to ipv4 or ipv6 one based on nfproto). Currently supports fetching output interface index/name and the rtm_type associated with an address. This can be used for adding path filtering. rtm_type is useful to e.g. enforce a strong-end host model where packets are only accepted if daddr is configured on the interface the packet arrived on. The fib expression is a native nftables alternative to the xtables addrtype and rp_filter matches. FIB result order for oif/oifname retrieval is as follows: - if packet is local (skb has rtable, RTF_LOCAL set, this will also catch looped-back multicast packets), set oif to the loopback interface. - if fib lookup returns an error, or result points to local, store zero result. This means '--local' option of -m rpfilter is not supported. It is possible to use 'fib type local' or add explicit saddr/daddr matching rules to create exceptions if this is really needed. - store result in the destination register. In case of multiple routes, search set for desired oif in case strict matching is requested. ipv4 and ipv6 behave fib expressions are supposed to behave the same. [ I have collapsed Arnd Bergmann's ("netfilter: nf_tables: fib warnings") http://patchwork.ozlabs.org/patch/688615/ to address fallout from this patch after rebasing nf-next, that was posted to address compilation warnings. --pablo ] Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nft_fib.h | 31 ++++ include/uapi/linux/netfilter/nf_tables.h | 36 ++++ net/ipv4/netfilter/Kconfig | 8 + net/ipv4/netfilter/Makefile | 1 + net/ipv4/netfilter/nft_fib_ipv4.c | 238 ++++++++++++++++++++++++++ net/ipv6/netfilter/Kconfig | 8 + net/ipv6/netfilter/Makefile | 1 + net/ipv6/netfilter/nft_fib_ipv6.c | 275 +++++++++++++++++++++++++++++++ net/netfilter/Kconfig | 13 ++ net/netfilter/Makefile | 2 + net/netfilter/nft_fib.c | 159 ++++++++++++++++++ net/netfilter/nft_fib_inet.c | 82 +++++++++ 12 files changed, 854 insertions(+) create mode 100644 include/net/netfilter/nft_fib.h create mode 100644 net/ipv4/netfilter/nft_fib_ipv4.c create mode 100644 net/ipv6/netfilter/nft_fib_ipv6.c create mode 100644 net/netfilter/nft_fib.c create mode 100644 net/netfilter/nft_fib_inet.c diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h new file mode 100644 index 0000000..cbedda0 --- /dev/null +++ b/include/net/netfilter/nft_fib.h @@ -0,0 +1,31 @@ +#ifndef _NFT_FIB_H_ +#define _NFT_FIB_H_ + +struct nft_fib { + enum nft_registers dreg:8; + u8 result; + u32 flags; +}; + +extern const struct nla_policy nft_fib_policy[]; + +int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr); +int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nlattr * const tb[]); +int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nft_data **data); + + +void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt); +void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt); + +void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt); +void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt); + +void nft_fib_store_result(void *reg, enum nft_fib_result r, + const struct nft_pktinfo *pkt, int index); +#endif diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index c6c4477..a054ad2 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1109,6 +1109,42 @@ enum nft_gen_attributes { }; #define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1) +/* + * enum nft_fib_attributes - nf_tables fib expression netlink attributes + * + * @NFTA_FIB_DREG: destination register (NLA_U32) + * @NFTA_FIB_RESULT: desired result (NLA_U32) + * @NFTA_FIB_FLAGS: flowi fields to initialize when querying the FIB (NLA_U32) + * + * The FIB expression performs a route lookup according + * to the packet data. + */ +enum nft_fib_attributes { + NFTA_FIB_UNSPEC, + NFTA_FIB_DREG, + NFTA_FIB_RESULT, + NFTA_FIB_FLAGS, + __NFTA_FIB_MAX +}; +#define NFTA_FIB_MAX (__NFTA_FIB_MAX - 1) + +enum nft_fib_result { + NFT_FIB_RESULT_UNSPEC, + NFT_FIB_RESULT_OIF, + NFT_FIB_RESULT_OIFNAME, + NFT_FIB_RESULT_ADDRTYPE, + __NFT_FIB_RESULT_MAX +}; +#define NFT_FIB_RESULT_MAX (__NFT_FIB_RESULT_MAX - 1) + +enum nft_fib_flags { + NFTA_FIB_F_SADDR = 1 << 0, /* look up src */ + NFTA_FIB_F_DADDR = 1 << 1, /* look up dst */ + NFTA_FIB_F_MARK = 1 << 2, /* use skb->mark */ + NFTA_FIB_F_IIF = 1 << 3, /* restrict to iif */ + NFTA_FIB_F_OIF = 1 << 4, /* restrict to oif */ +}; + /** * enum nft_trace_attributes - nf_tables trace netlink attributes * diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index d613309..f80bb8f 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -54,6 +54,14 @@ config NFT_DUP_IPV4 help This module enables IPv4 packet duplication support for nf_tables. +config NFT_FIB_IPV4 + select NFT_FIB + tristate "nf_tables fib / ip route lookup support" + help + This module enables IPv4 FIB lookups, e.g. for reverse path filtering. + It also allows query of the FIB for the route type, e.g. local, unicast, + multicast or blackhole. + endif # NF_TABLES_IPV4 config NF_TABLES_ARP diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 853328f..59d7786 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o +obj-$(CONFIG_NFT_FIB_IPV4) += nft_fib_ipv4.o obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o obj-$(CONFIG_NFT_REDIR_IPV4) += nft_redir_ipv4.o obj-$(CONFIG_NFT_DUP_IPV4) += nft_dup_ipv4.o diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c new file mode 100644 index 0000000..db91fd4 --- /dev/null +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -0,0 +1,238 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* don't try to find route from mcast/bcast/zeronet */ +static __be32 get_saddr(__be32 addr) +{ + if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) || + ipv4_is_zeronet(addr)) + return 0; + return addr; +} + +static bool fib4_is_local(const struct sk_buff *skb) +{ + const struct rtable *rt = skb_rtable(skb); + + return rt && (rt->rt_flags & RTCF_LOCAL); +} + +#define DSCP_BITS 0xfc + +void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + u32 *dst = ®s->data[priv->dreg]; + const struct net_device *dev = NULL; + const struct iphdr *iph; + __be32 addr; + + if (priv->flags & NFTA_FIB_F_IIF) + dev = pkt->in; + else if (priv->flags & NFTA_FIB_F_OIF) + dev = pkt->out; + + iph = ip_hdr(pkt->skb); + if (priv->flags & NFTA_FIB_F_DADDR) + addr = iph->daddr; + else + addr = iph->saddr; + + *dst = inet_dev_addr_type(pkt->net, dev, addr); +} +EXPORT_SYMBOL_GPL(nft_fib4_eval_type); + +static int get_ifindex(const struct net_device *dev) +{ + return dev ? dev->ifindex : 0; +} + +void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + u32 *dest = ®s->data[priv->dreg]; + const struct iphdr *iph; + struct fib_result res; + struct flowi4 fl4 = { + .flowi4_scope = RT_SCOPE_UNIVERSE, + .flowi4_iif = LOOPBACK_IFINDEX, + }; + const struct net_device *oif; + struct net_device *found; +#ifdef CONFIG_IP_ROUTE_MULTIPATH + int i; +#endif + + /* + * Do not set flowi4_oif, it restricts results (for example, asking + * for oif 3 will get RTN_UNICAST result even if the daddr exits + * on another interface. + * + * Search results for the desired outinterface instead. + */ + if (priv->flags & NFTA_FIB_F_OIF) + oif = pkt->out; + else if (priv->flags & NFTA_FIB_F_IIF) + oif = pkt->in; + else + oif = NULL; + + if (pkt->hook == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) { + nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX); + return; + } + + iph = ip_hdr(pkt->skb); + if (ipv4_is_multicast(iph->daddr) && + ipv4_is_zeronet(iph->saddr) && + ipv4_is_local_multicast(iph->daddr)) { + nft_fib_store_result(dest, priv->result, pkt, + get_ifindex(pkt->skb->dev)); + return; + } + + if (priv->flags & NFTA_FIB_F_MARK) + fl4.flowi4_mark = pkt->skb->mark; + + fl4.flowi4_tos = iph->tos & DSCP_BITS; + + if (priv->flags & NFTA_FIB_F_DADDR) { + fl4.daddr = iph->daddr; + fl4.saddr = get_saddr(iph->saddr); + } else { + fl4.daddr = iph->saddr; + fl4.saddr = get_saddr(iph->daddr); + } + + if (fib_lookup(pkt->net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) + return; + + switch (res.type) { + case RTN_UNICAST: + break; + case RTN_LOCAL: /* should not appear here, see fib4_is_local() above */ + return; + default: + break; + } + + if (!oif) { + found = FIB_RES_DEV(res); + goto ok; + } + +#ifdef CONFIG_IP_ROUTE_MULTIPATH + for (i = 0; i < res.fi->fib_nhs; i++) { + struct fib_nh *nh = &res.fi->fib_nh[i]; + + if (nh->nh_dev == oif) { + found = nh->nh_dev; + goto ok; + } + } + return; +#else + found = FIB_RES_DEV(res); + if (found != oif) + return; +#endif +ok: + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + *dest = found->ifindex; + break; + case NFT_FIB_RESULT_OIFNAME: + strncpy((char *)dest, found->name, IFNAMSIZ); + break; + default: + WARN_ON_ONCE(1); + break; + } +} +EXPORT_SYMBOL_GPL(nft_fib4_eval); + +static struct nft_expr_type nft_fib4_type; + +static const struct nft_expr_ops nft_fib4_type_ops = { + .type = &nft_fib4_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), + .eval = nft_fib4_eval_type, + .init = nft_fib_init, + .dump = nft_fib_dump, + .validate = nft_fib_validate, +}; + +static const struct nft_expr_ops nft_fib4_ops = { + .type = &nft_fib4_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), + .eval = nft_fib4_eval, + .init = nft_fib_init, + .dump = nft_fib_dump, + .validate = nft_fib_validate, +}; + +static const struct nft_expr_ops * +nft_fib4_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + enum nft_fib_result result; + + if (!tb[NFTA_FIB_RESULT]) + return ERR_PTR(-EINVAL); + + result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT])); + + switch (result) { + case NFT_FIB_RESULT_OIF: + return &nft_fib4_ops; + case NFT_FIB_RESULT_OIFNAME: + return &nft_fib4_ops; + case NFT_FIB_RESULT_ADDRTYPE: + return &nft_fib4_type_ops; + default: + return ERR_PTR(-EOPNOTSUPP); + } +} + +static struct nft_expr_type nft_fib4_type __read_mostly = { + .name = "fib", + .select_ops = &nft_fib4_select_ops, + .policy = nft_fib_policy, + .maxattr = NFTA_FIB_MAX, + .family = NFPROTO_IPV4, + .owner = THIS_MODULE, +}; + +static int __init nft_fib4_module_init(void) +{ + return nft_register_expr(&nft_fib4_type); +} + +static void __exit nft_fib4_module_exit(void) +{ + nft_unregister_expr(&nft_fib4_type); +} + +module_init(nft_fib4_module_init); +module_exit(nft_fib4_module_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Westphal "); +MODULE_ALIAS_NFT_AF_EXPR(2, "fib"); diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index e10a04c..144f1de 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -54,6 +54,14 @@ config NFT_DUP_IPV6 help This module enables IPv6 packet duplication support for nf_tables. +config NFT_FIB_IPV6 + tristate "nf_tables fib / ipv6 route lookup support" + select NFT_FIB + help + This module enables IPv6 FIB lookups, e.g. for reverse path filtering. + It also allows query of the FIB for the route type, e.g. local, unicast, + multicast or blackhole. + endif # NF_TABLES_IPV6 endif # NF_TABLES diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index b4f7d0b..7984f30 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o obj-$(CONFIG_NFT_REDIR_IPV6) += nft_redir_ipv6.o obj-$(CONFIG_NFT_DUP_IPV6) += nft_dup_ipv6.o +obj-$(CONFIG_NFT_FIB_IPV6) += nft_fib_ipv6.o # matches obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c new file mode 100644 index 0000000..ff1f1b6 --- /dev/null +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -0,0 +1,275 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static bool fib6_is_local(const struct sk_buff *skb) +{ + const struct rt6_info *rt = (const void *)skb_dst(skb); + + return rt && (rt->rt6i_flags & RTF_LOCAL); +} + +static int get_ifindex(const struct net_device *dev) +{ + return dev ? dev->ifindex : 0; +} + +static int nft_fib6_flowi_init(struct flowi6 *fl6, const struct nft_fib *priv, + const struct nft_pktinfo *pkt, + const struct net_device *dev) +{ + const struct ipv6hdr *iph = ipv6_hdr(pkt->skb); + int lookup_flags = 0; + + if (priv->flags & NFTA_FIB_F_DADDR) { + fl6->daddr = iph->daddr; + fl6->saddr = iph->saddr; + } else { + fl6->daddr = iph->saddr; + fl6->saddr = iph->daddr; + } + + if (ipv6_addr_type(&fl6->daddr) & IPV6_ADDR_LINKLOCAL) { + lookup_flags |= RT6_LOOKUP_F_IFACE; + fl6->flowi6_oif = get_ifindex(dev ? dev : pkt->skb->dev); + } + + if (ipv6_addr_type(&fl6->saddr) & IPV6_ADDR_UNICAST) + lookup_flags |= RT6_LOOKUP_F_HAS_SADDR; + + if (priv->flags & NFTA_FIB_F_MARK) + fl6->flowi6_mark = pkt->skb->mark; + + fl6->flowlabel = (*(__be32 *)iph) & IPV6_FLOWINFO_MASK; + + return lookup_flags; +} + +static u32 __nft_fib6_eval_type(const struct nft_fib *priv, + const struct nft_pktinfo *pkt) +{ + const struct net_device *dev = NULL; + const struct nf_ipv6_ops *v6ops; + const struct nf_afinfo *afinfo; + int route_err, addrtype; + struct rt6_info *rt; + struct flowi6 fl6 = { + .flowi6_iif = LOOPBACK_IFINDEX, + .flowi6_proto = pkt->tprot, + }; + u32 ret = 0; + + afinfo = nf_get_afinfo(NFPROTO_IPV6); + if (!afinfo) + return RTN_UNREACHABLE; + + if (priv->flags & NFTA_FIB_F_IIF) + dev = pkt->in; + else if (priv->flags & NFTA_FIB_F_OIF) + dev = pkt->out; + + nft_fib6_flowi_init(&fl6, priv, pkt, dev); + + v6ops = nf_get_ipv6_ops(); + if (dev && v6ops && v6ops->chk_addr(pkt->net, &fl6.daddr, dev, true)) + ret = RTN_LOCAL; + + route_err = afinfo->route(pkt->net, (struct dst_entry **)&rt, + flowi6_to_flowi(&fl6), false); + if (route_err) + goto err; + + if (rt->rt6i_flags & RTF_REJECT) { + route_err = rt->dst.error; + dst_release(&rt->dst); + goto err; + } + + if (ipv6_anycast_destination((struct dst_entry *)rt, &fl6.daddr)) + ret = RTN_ANYCAST; + else if (!dev && rt->rt6i_flags & RTF_LOCAL) + ret = RTN_LOCAL; + + dst_release(&rt->dst); + + if (ret) + return ret; + + addrtype = ipv6_addr_type(&fl6.daddr); + + if (addrtype & IPV6_ADDR_MULTICAST) + return RTN_MULTICAST; + if (addrtype & IPV6_ADDR_UNICAST) + return RTN_UNICAST; + + return RTN_UNSPEC; + err: + switch (route_err) { + case -EINVAL: + return RTN_BLACKHOLE; + case -EACCES: + return RTN_PROHIBIT; + case -EAGAIN: + return RTN_THROW; + default: + break; + } + + return RTN_UNREACHABLE; +} + +void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + u32 *dest = ®s->data[priv->dreg]; + + *dest = __nft_fib6_eval_type(priv, pkt); +} +EXPORT_SYMBOL_GPL(nft_fib6_eval_type); + +void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + const struct net_device *oif = NULL; + u32 *dest = ®s->data[priv->dreg]; + struct flowi6 fl6 = { + .flowi6_iif = LOOPBACK_IFINDEX, + .flowi6_proto = pkt->tprot, + }; + struct rt6_info *rt; + int lookup_flags; + + if (priv->flags & NFTA_FIB_F_IIF) + oif = pkt->in; + else if (priv->flags & NFTA_FIB_F_OIF) + oif = pkt->out; + + lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif); + + if (pkt->hook == NF_INET_PRE_ROUTING && fib6_is_local(pkt->skb)) { + nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX); + return; + } + + *dest = 0; + again: + rt = (void *)ip6_route_lookup(pkt->net, &fl6, lookup_flags); + if (rt->dst.error) + goto put_rt_err; + + /* Should not see RTF_LOCAL here */ + if (rt->rt6i_flags & (RTF_REJECT | RTF_ANYCAST | RTF_LOCAL)) + goto put_rt_err; + + if (oif && oif != rt->rt6i_idev->dev) { + /* multipath route? Try again with F_IFACE */ + if ((lookup_flags & RT6_LOOKUP_F_IFACE) == 0) { + lookup_flags |= RT6_LOOKUP_F_IFACE; + fl6.flowi6_oif = oif->ifindex; + ip6_rt_put(rt); + goto again; + } + } + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + *dest = rt->rt6i_idev->dev->ifindex; + break; + case NFT_FIB_RESULT_OIFNAME: + strncpy((char *)dest, rt->rt6i_idev->dev->name, IFNAMSIZ); + break; + default: + WARN_ON_ONCE(1); + break; + } + + put_rt_err: + ip6_rt_put(rt); +} +EXPORT_SYMBOL_GPL(nft_fib6_eval); + +static struct nft_expr_type nft_fib6_type; + +static const struct nft_expr_ops nft_fib6_type_ops = { + .type = &nft_fib6_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), + .eval = nft_fib6_eval_type, + .init = nft_fib_init, + .dump = nft_fib_dump, + .validate = nft_fib_validate, +}; + +static const struct nft_expr_ops nft_fib6_ops = { + .type = &nft_fib6_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), + .eval = nft_fib6_eval, + .init = nft_fib_init, + .dump = nft_fib_dump, + .validate = nft_fib_validate, +}; + +static const struct nft_expr_ops * +nft_fib6_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + enum nft_fib_result result; + + if (!tb[NFTA_FIB_RESULT]) + return ERR_PTR(-EINVAL); + + result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT])); + + switch (result) { + case NFT_FIB_RESULT_OIF: + return &nft_fib6_ops; + case NFT_FIB_RESULT_OIFNAME: + return &nft_fib6_ops; + case NFT_FIB_RESULT_ADDRTYPE: + return &nft_fib6_type_ops; + default: + return ERR_PTR(-EOPNOTSUPP); + } +} + +static struct nft_expr_type nft_fib6_type __read_mostly = { + .name = "fib", + .select_ops = &nft_fib6_select_ops, + .policy = nft_fib_policy, + .maxattr = NFTA_FIB_MAX, + .family = NFPROTO_IPV6, + .owner = THIS_MODULE, +}; + +static int __init nft_fib6_module_init(void) +{ + return nft_register_expr(&nft_fib6_type); +} + +static void __exit nft_fib6_module_exit(void) +{ + nft_unregister_expr(&nft_fib6_type); +} +module_init(nft_fib6_module_init); +module_exit(nft_fib6_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Westphal "); +MODULE_ALIAS_NFT_AF_EXPR(10, "fib"); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index e8d56d9..9bcf899 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -581,6 +581,19 @@ config NFT_HASH This option adds the "hash" expression that you can use to perform a hash operation on registers. +config NFT_FIB + tristate + +config NFT_FIB_INET + depends on NF_TABLES_INET + depends on NFT_FIB_IPV4 + depends on NFT_FIB_IPV6 + tristate "Netfilter nf_tables fib inet support" + help + This option allows using the FIB expression from the inet table. + The lookup will be delegated to the IPv4 or IPv6 FIB depending + on the protocol of the packet. + if NF_TABLES_NETDEV config NF_DUP_NETDEV diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index c23c3c8..8faa36c 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -96,6 +96,8 @@ obj-$(CONFIG_NFT_LOG) += nft_log.o obj-$(CONFIG_NFT_MASQ) += nft_masq.o obj-$(CONFIG_NFT_REDIR) += nft_redir.o obj-$(CONFIG_NFT_HASH) += nft_hash.o +obj-$(CONFIG_NFT_FIB) += nft_fib.o +obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o # nf_tables netdev obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c new file mode 100644 index 0000000..4944a8b --- /dev/null +++ b/net/netfilter/nft_fib.c @@ -0,0 +1,159 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Generic part shared by ipv4 and ipv6 backends. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = { + [NFTA_FIB_DREG] = { .type = NLA_U32 }, + [NFTA_FIB_RESULT] = { .type = NLA_U32 }, + [NFTA_FIB_FLAGS] = { .type = NLA_U32 }, +}; +EXPORT_SYMBOL(nft_fib_policy); + +#define NFTA_FIB_F_ALL (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR | \ + NFTA_FIB_F_MARK | NFTA_FIB_F_IIF | NFTA_FIB_F_OIF) + +int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nft_data **data) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: /* fallthrough */ + case NFT_FIB_RESULT_OIFNAME: + hooks = (1 << NF_INET_PRE_ROUTING); + break; + case NFT_FIB_RESULT_ADDRTYPE: + if (priv->flags & NFTA_FIB_F_IIF) + hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_FORWARD); + else if (priv->flags & NFTA_FIB_F_OIF) + hooks = (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_FORWARD); + else + hooks = (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_FORWARD) | + (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING); + + break; + default: + return -EINVAL; + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} +EXPORT_SYMBOL_GPL(nft_fib_validate); + +int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_fib *priv = nft_expr_priv(expr); + unsigned int len; + int err; + + if (!tb[NFTA_FIB_DREG] || !tb[NFTA_FIB_RESULT] || !tb[NFTA_FIB_FLAGS]) + return -EINVAL; + + priv->flags = ntohl(nla_get_be32(tb[NFTA_FIB_FLAGS])); + + if (priv->flags == 0 || (priv->flags & ~NFTA_FIB_F_ALL)) + return -EINVAL; + + if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == + (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) + return -EINVAL; + if ((priv->flags & (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) == + (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) + return -EINVAL; + if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 0) + return -EINVAL; + + priv->result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT])); + priv->dreg = nft_parse_register(tb[NFTA_FIB_DREG]); + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + if (priv->flags & NFTA_FIB_F_OIF) + return -EINVAL; + len = sizeof(int); + break; + case NFT_FIB_RESULT_OIFNAME: + if (priv->flags & NFTA_FIB_F_OIF) + return -EINVAL; + len = IFNAMSIZ; + break; + case NFT_FIB_RESULT_ADDRTYPE: + len = sizeof(u32); + break; + default: + return -EINVAL; + } + + err = nft_validate_register_store(ctx, priv->dreg, NULL, + NFT_DATA_VALUE, len); + if (err < 0) + return err; + + return nft_fib_validate(ctx, expr, NULL); +} +EXPORT_SYMBOL_GPL(nft_fib_init); + +int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + + if (nft_dump_register(skb, NFTA_FIB_DREG, priv->dreg)) + return -1; + + if (nla_put_be32(skb, NFTA_FIB_RESULT, htonl(priv->result))) + return -1; + + if (nla_put_be32(skb, NFTA_FIB_FLAGS, htonl(priv->flags))) + return -1; + + return 0; +} +EXPORT_SYMBOL_GPL(nft_fib_dump); + +void nft_fib_store_result(void *reg, enum nft_fib_result r, + const struct nft_pktinfo *pkt, int index) +{ + struct net_device *dev; + u32 *dreg = reg; + + switch (r) { + case NFT_FIB_RESULT_OIF: + *dreg = index; + break; + case NFT_FIB_RESULT_OIFNAME: + dev = dev_get_by_index_rcu(pkt->net, index); + strncpy(reg, dev ? dev->name : "", IFNAMSIZ); + break; + default: + WARN_ON_ONCE(1); + *dreg = 0; + break; + } +} +EXPORT_SYMBOL_GPL(nft_fib_store_result); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Westphal "); diff --git a/net/netfilter/nft_fib_inet.c b/net/netfilter/nft_fib_inet.c new file mode 100644 index 0000000..fe8943b --- /dev/null +++ b/net/netfilter/nft_fib_inet.c @@ -0,0 +1,82 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void nft_fib_inet_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + + switch (pkt->pf) { + case NFPROTO_IPV4: + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + return nft_fib4_eval(expr, regs, pkt); + case NFT_FIB_RESULT_ADDRTYPE: + return nft_fib4_eval_type(expr, regs, pkt); + } + break; + case NFPROTO_IPV6: + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + return nft_fib6_eval(expr, regs, pkt); + case NFT_FIB_RESULT_ADDRTYPE: + return nft_fib6_eval_type(expr, regs, pkt); + } + break; + } + + regs->verdict.code = NF_DROP; +} + +static struct nft_expr_type nft_fib_inet_type; +static const struct nft_expr_ops nft_fib_inet_ops = { + .type = &nft_fib_inet_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), + .eval = nft_fib_inet_eval, + .init = nft_fib_init, + .dump = nft_fib_dump, + .validate = nft_fib_validate, +}; + +static struct nft_expr_type nft_fib_inet_type __read_mostly = { + .family = NFPROTO_INET, + .name = "fib", + .ops = &nft_fib_inet_ops, + .policy = nft_fib_policy, + .maxattr = NFTA_FIB_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_fib_inet_module_init(void) +{ + return nft_register_expr(&nft_fib_inet_type); +} + +static void __exit nft_fib_inet_module_exit(void) +{ + nft_unregister_expr(&nft_fib_inet_type); +} + +module_init(nft_fib_inet_module_init); +module_exit(nft_fib_inet_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Westphal "); +MODULE_ALIAS_NFT_AF_EXPR(1, "fib"); -- cgit v1.1 From 1fddf4bad0ac9f4d32c74af286fc1eec2a03c82c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 27 Oct 2016 19:49:42 +0100 Subject: netfilter: nf_log: add packet logging for netdev family Move layer 2 packet logging into nf_log_l2packet() that resides in nf_log_common.c, so this can be shared by both bridge and netdev families. This patch adds the boiler plate code to register the netdev logging family. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_log.h | 5 +++ net/bridge/netfilter/Kconfig | 1 + net/bridge/netfilter/nf_log_bridge.c | 16 +------- net/netfilter/Kconfig | 4 ++ net/netfilter/Makefile | 3 ++ net/netfilter/nf_log_common.c | 27 ++++++++++++ net/netfilter/nf_log_netdev.c | 80 ++++++++++++++++++++++++++++++++++++ 7 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 net/netfilter/nf_log_netdev.c diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index 309cd26..a559aa4 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -109,5 +109,10 @@ void nf_log_dump_packet_common(struct nf_log_buf *m, u_int8_t pf, const struct net_device *out, const struct nf_loginfo *loginfo, const char *prefix); +void nf_log_l2packet(struct net *net, u_int8_t pf, unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, const char *prefix); #endif /* _NF_LOG_H */ diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 9cebf47..e7ef1a1 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -22,6 +22,7 @@ config NFT_BRIDGE_REJECT config NF_LOG_BRIDGE tristate "Bridge packet logging" + select NF_LOG_COMMON endif # NF_TABLES_BRIDGE diff --git a/net/bridge/netfilter/nf_log_bridge.c b/net/bridge/netfilter/nf_log_bridge.c index 1663df5..c197b1f 100644 --- a/net/bridge/netfilter/nf_log_bridge.c +++ b/net/bridge/netfilter/nf_log_bridge.c @@ -24,21 +24,7 @@ static void nf_log_bridge_packet(struct net *net, u_int8_t pf, const struct nf_loginfo *loginfo, const char *prefix) { - switch (eth_hdr(skb)->h_proto) { - case htons(ETH_P_IP): - nf_log_packet(net, NFPROTO_IPV4, hooknum, skb, in, out, - loginfo, "%s", prefix); - break; - case htons(ETH_P_IPV6): - nf_log_packet(net, NFPROTO_IPV6, hooknum, skb, in, out, - loginfo, "%s", prefix); - break; - case htons(ETH_P_ARP): - case htons(ETH_P_RARP): - nf_log_packet(net, NFPROTO_ARP, hooknum, skb, in, out, - loginfo, "%s", prefix); - break; - } + nf_log_l2packet(net, pf, hooknum, skb, in, out, loginfo, prefix); } static struct nf_logger nf_bridge_logger __read_mostly = { diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 9bcf899..854dadb 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -57,6 +57,10 @@ config NF_CONNTRACK config NF_LOG_COMMON tristate +config NF_LOG_NETDEV + tristate "Netdev packet logging" + select NF_LOG_COMMON + if NF_CONNTRACK config NF_CONNTRACK_MARK diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 8faa36c..8edd791 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -48,6 +48,9 @@ nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ # generic transport layer logging obj-$(CONFIG_NF_LOG_COMMON) += nf_log_common.o +# packet logging for netdev family +obj-$(CONFIG_NF_LOG_NETDEV) += nf_log_netdev.o + obj-$(CONFIG_NF_NAT) += nf_nat.o obj-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o diff --git a/net/netfilter/nf_log_common.c b/net/netfilter/nf_log_common.c index 119fe1c..ed9b808 100644 --- a/net/netfilter/nf_log_common.c +++ b/net/netfilter/nf_log_common.c @@ -175,6 +175,33 @@ nf_log_dump_packet_common(struct nf_log_buf *m, u_int8_t pf, } EXPORT_SYMBOL_GPL(nf_log_dump_packet_common); +/* bridge and netdev logging families share this code. */ +void nf_log_l2packet(struct net *net, u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + switch (eth_hdr(skb)->h_proto) { + case htons(ETH_P_IP): + nf_log_packet(net, NFPROTO_IPV4, hooknum, skb, in, out, + loginfo, "%s", prefix); + break; + case htons(ETH_P_IPV6): + nf_log_packet(net, NFPROTO_IPV6, hooknum, skb, in, out, + loginfo, "%s", prefix); + break; + case htons(ETH_P_ARP): + case htons(ETH_P_RARP): + nf_log_packet(net, NFPROTO_ARP, hooknum, skb, in, out, + loginfo, "%s", prefix); + break; + } +} +EXPORT_SYMBOL_GPL(nf_log_l2packet); + static int __init nf_log_common_init(void) { return 0; diff --git a/net/netfilter/nf_log_netdev.c b/net/netfilter/nf_log_netdev.c new file mode 100644 index 0000000..1f64594 --- /dev/null +++ b/net/netfilter/nf_log_netdev.c @@ -0,0 +1,80 @@ +/* + * (C) 2016 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include + +static void nf_log_netdev_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + nf_log_l2packet(net, pf, hooknum, skb, in, out, loginfo, prefix); +} + +static struct nf_logger nf_netdev_logger __read_mostly = { + .name = "nf_log_netdev", + .type = NF_LOG_TYPE_LOG, + .logfn = nf_log_netdev_packet, + .me = THIS_MODULE, +}; + +static int __net_init nf_log_netdev_net_init(struct net *net) +{ + return nf_log_set(net, NFPROTO_NETDEV, &nf_netdev_logger); +} + +static void __net_exit nf_log_netdev_net_exit(struct net *net) +{ + nf_log_unset(net, &nf_netdev_logger); +} + +static struct pernet_operations nf_log_netdev_net_ops = { + .init = nf_log_netdev_net_init, + .exit = nf_log_netdev_net_exit, +}; + +static int __init nf_log_netdev_init(void) +{ + int ret; + + /* Request to load the real packet loggers. */ + nf_logger_request_module(NFPROTO_IPV4, NF_LOG_TYPE_LOG); + nf_logger_request_module(NFPROTO_IPV6, NF_LOG_TYPE_LOG); + nf_logger_request_module(NFPROTO_ARP, NF_LOG_TYPE_LOG); + + ret = register_pernet_subsys(&nf_log_netdev_net_ops); + if (ret < 0) + return ret; + + nf_log_register(NFPROTO_NETDEV, &nf_netdev_logger); + return 0; +} + +static void __exit nf_log_netdev_exit(void) +{ + unregister_pernet_subsys(&nf_log_netdev_net_ops); + nf_log_unregister(&nf_netdev_logger); +} + +module_init(nf_log_netdev_init); +module_exit(nf_log_netdev_exit); + +MODULE_AUTHOR("Pablo Neira Ayuso "); +MODULE_DESCRIPTION("Netfilter netdev packet logging"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NF_LOGGER(5, 0); /* NFPROTO_NETDEV */ -- cgit v1.1 From 8db4c5be88f62ffd7a552f70687a10c614dc697b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 27 Oct 2016 19:49:48 +0100 Subject: netfilter: move socket lookup infrastructure to nf_socket_ipv{4,6}.c We need this split to reuse existing codebase for the upcoming nf_tables socket expression. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_socket.h | 27 ++++ net/ipv4/netfilter/Kconfig | 6 + net/ipv4/netfilter/Makefile | 2 + net/ipv4/netfilter/nf_socket_ipv4.c | 163 +++++++++++++++++++ net/ipv6/netfilter/Kconfig | 6 + net/ipv6/netfilter/Makefile | 2 + net/ipv6/netfilter/nf_socket_ipv6.c | 151 ++++++++++++++++++ net/netfilter/Kconfig | 3 +- net/netfilter/xt_socket.c | 305 ++---------------------------------- 9 files changed, 369 insertions(+), 296 deletions(-) create mode 100644 include/net/netfilter/nf_socket.h create mode 100644 net/ipv4/netfilter/nf_socket_ipv4.c create mode 100644 net/ipv6/netfilter/nf_socket_ipv6.c diff --git a/include/net/netfilter/nf_socket.h b/include/net/netfilter/nf_socket.h new file mode 100644 index 0000000..f2fc39c --- /dev/null +++ b/include/net/netfilter/nf_socket.h @@ -0,0 +1,27 @@ +#ifndef _NF_SOCK_H_ +#define _NF_SOCK_H_ + +struct net_device; +struct sk_buff; +struct sock; +struct net; + +static inline bool nf_sk_is_transparent(struct sock *sk) +{ + switch (sk->sk_state) { + case TCP_TIME_WAIT: + return inet_twsk(sk)->tw_transparent; + case TCP_NEW_SYN_RECV: + return inet_rsk(inet_reqsk(sk))->no_srccheck; + default: + return inet_sk(sk)->transparent; + } +} + +struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb, + const struct net_device *indev); + +struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb, + const struct net_device *indev); + +#endif diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index f80bb8f..c11eb17 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -25,6 +25,12 @@ config NF_CONNTRACK_IPV4 To compile it as a module, choose M here. If unsure, say N. +config NF_SOCKET_IPV4 + tristate "IPv4 socket lookup support" + help + This option enables the IPv4 socket lookup infrastructure. This is + is required by the iptables socket match. + if NF_TABLES config NF_TABLES_IPV4 diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 59d7786..f462fee 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_NF_NAT_IPV4) += nf_nat_ipv4.o # defrag obj-$(CONFIG_NF_DEFRAG_IPV4) += nf_defrag_ipv4.o +obj-$(CONFIG_NF_SOCKET_IPV4) += nf_socket_ipv4.o + # logging obj-$(CONFIG_NF_LOG_ARP) += nf_log_arp.o obj-$(CONFIG_NF_LOG_IPV4) += nf_log_ipv4.o diff --git a/net/ipv4/netfilter/nf_socket_ipv4.c b/net/ipv4/netfilter/nf_socket_ipv4.c new file mode 100644 index 0000000..a83d558 --- /dev/null +++ b/net/ipv4/netfilter/nf_socket_ipv4.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2007-2008 BalaBit IT Ltd. + * Author: Krisztian Kovacs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#if IS_ENABLED(CONFIG_NF_CONNTRACK) +#include +#endif + +static int +extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, + __be32 *raddr, __be32 *laddr, + __be16 *rport, __be16 *lport) +{ + unsigned int outside_hdrlen = ip_hdrlen(skb); + struct iphdr *inside_iph, _inside_iph; + struct icmphdr *icmph, _icmph; + __be16 *ports, _ports[2]; + + icmph = skb_header_pointer(skb, outside_hdrlen, + sizeof(_icmph), &_icmph); + if (icmph == NULL) + return 1; + + switch (icmph->type) { + case ICMP_DEST_UNREACH: + case ICMP_SOURCE_QUENCH: + case ICMP_REDIRECT: + case ICMP_TIME_EXCEEDED: + case ICMP_PARAMETERPROB: + break; + default: + return 1; + } + + inside_iph = skb_header_pointer(skb, outside_hdrlen + + sizeof(struct icmphdr), + sizeof(_inside_iph), &_inside_iph); + if (inside_iph == NULL) + return 1; + + if (inside_iph->protocol != IPPROTO_TCP && + inside_iph->protocol != IPPROTO_UDP) + return 1; + + ports = skb_header_pointer(skb, outside_hdrlen + + sizeof(struct icmphdr) + + (inside_iph->ihl << 2), + sizeof(_ports), &_ports); + if (ports == NULL) + return 1; + + /* the inside IP packet is the one quoted from our side, thus + * its saddr is the local address */ + *protocol = inside_iph->protocol; + *laddr = inside_iph->saddr; + *lport = ports[0]; + *raddr = inside_iph->daddr; + *rport = ports[1]; + + return 0; +} + +static struct sock * +nf_socket_get_sock_v4(struct net *net, struct sk_buff *skb, const int doff, + const u8 protocol, + const __be32 saddr, const __be32 daddr, + const __be16 sport, const __be16 dport, + const struct net_device *in) +{ + switch (protocol) { + case IPPROTO_TCP: + return inet_lookup(net, &tcp_hashinfo, skb, doff, + saddr, sport, daddr, dport, + in->ifindex); + case IPPROTO_UDP: + return udp4_lib_lookup(net, saddr, sport, daddr, dport, + in->ifindex); + } + return NULL; +} + +struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb, + const struct net_device *indev) +{ + __be32 uninitialized_var(daddr), uninitialized_var(saddr); + __be16 uninitialized_var(dport), uninitialized_var(sport); + const struct iphdr *iph = ip_hdr(skb); + struct sk_buff *data_skb = NULL; + u8 uninitialized_var(protocol); +#if IS_ENABLED(CONFIG_NF_CONNTRACK) + enum ip_conntrack_info ctinfo; + struct nf_conn const *ct; +#endif + int doff = 0; + + if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { + struct udphdr _hdr, *hp; + + hp = skb_header_pointer(skb, ip_hdrlen(skb), + sizeof(_hdr), &_hdr); + if (hp == NULL) + return NULL; + + protocol = iph->protocol; + saddr = iph->saddr; + sport = hp->source; + daddr = iph->daddr; + dport = hp->dest; + data_skb = (struct sk_buff *)skb; + doff = iph->protocol == IPPROTO_TCP ? + ip_hdrlen(skb) + __tcp_hdrlen((struct tcphdr *)hp) : + ip_hdrlen(skb) + sizeof(*hp); + + } else if (iph->protocol == IPPROTO_ICMP) { + if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, + &sport, &dport)) + return NULL; + } else { + return NULL; + } + +#if IS_ENABLED(CONFIG_NF_CONNTRACK) + /* Do the lookup with the original socket address in + * case this is a reply packet of an established + * SNAT-ted connection. + */ + ct = nf_ct_get(skb, &ctinfo); + if (ct && !nf_ct_is_untracked(ct) && + ((iph->protocol != IPPROTO_ICMP && + ctinfo == IP_CT_ESTABLISHED_REPLY) || + (iph->protocol == IPPROTO_ICMP && + ctinfo == IP_CT_RELATED_REPLY)) && + (ct->status & IPS_SRC_NAT_DONE)) { + + daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; + dport = (iph->protocol == IPPROTO_TCP) ? + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port : + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; + } +#endif + + return nf_socket_get_sock_v4(net, data_skb, doff, protocol, saddr, + daddr, sport, dport, indev); +} +EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v4); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); +MODULE_DESCRIPTION("Netfilter IPv4 socket lookup infrastructure"); diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 144f1de..6acb2ee 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -25,6 +25,12 @@ config NF_CONNTRACK_IPV6 To compile it as a module, choose M here. If unsure, say N. +config NF_SOCKET_IPV6 + tristate "IPv6 socket lookup support" + help + This option enables the IPv6 socket lookup infrastructure. This + is used by the ip6tables socket match. + if NF_TABLES config NF_TABLES_IPV6 diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 7984f30..fe180c9 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -24,6 +24,8 @@ obj-$(CONFIG_NF_NAT_MASQUERADE_IPV6) += nf_nat_masquerade_ipv6.o nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o +obj-$(CONFIG_NF_SOCKET_IPV6) += nf_socket_ipv6.o + # logging obj-$(CONFIG_NF_LOG_IPV6) += nf_log_ipv6.o diff --git a/net/ipv6/netfilter/nf_socket_ipv6.c b/net/ipv6/netfilter/nf_socket_ipv6.c new file mode 100644 index 0000000..ebb2bf8 --- /dev/null +++ b/net/ipv6/netfilter/nf_socket_ipv6.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2007-2008 BalaBit IT Ltd. + * Author: Krisztian Kovacs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if IS_ENABLED(CONFIG_NF_CONNTRACK) +#include +#endif + +static int +extract_icmp6_fields(const struct sk_buff *skb, + unsigned int outside_hdrlen, + int *protocol, + const struct in6_addr **raddr, + const struct in6_addr **laddr, + __be16 *rport, + __be16 *lport, + struct ipv6hdr *ipv6_var) +{ + const struct ipv6hdr *inside_iph; + struct icmp6hdr *icmph, _icmph; + __be16 *ports, _ports[2]; + u8 inside_nexthdr; + __be16 inside_fragoff; + int inside_hdrlen; + + icmph = skb_header_pointer(skb, outside_hdrlen, + sizeof(_icmph), &_icmph); + if (icmph == NULL) + return 1; + + if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK) + return 1; + + inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), + sizeof(*ipv6_var), ipv6_var); + if (inside_iph == NULL) + return 1; + inside_nexthdr = inside_iph->nexthdr; + + inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + + sizeof(*ipv6_var), + &inside_nexthdr, &inside_fragoff); + if (inside_hdrlen < 0) + return 1; /* hjm: Packet has no/incomplete transport layer headers. */ + + if (inside_nexthdr != IPPROTO_TCP && + inside_nexthdr != IPPROTO_UDP) + return 1; + + ports = skb_header_pointer(skb, inside_hdrlen, + sizeof(_ports), &_ports); + if (ports == NULL) + return 1; + + /* the inside IP packet is the one quoted from our side, thus + * its saddr is the local address */ + *protocol = inside_nexthdr; + *laddr = &inside_iph->saddr; + *lport = ports[0]; + *raddr = &inside_iph->daddr; + *rport = ports[1]; + + return 0; +} + +static struct sock * +nf_socket_get_sock_v6(struct net *net, struct sk_buff *skb, int doff, + const u8 protocol, + const struct in6_addr *saddr, const struct in6_addr *daddr, + const __be16 sport, const __be16 dport, + const struct net_device *in) +{ + switch (protocol) { + case IPPROTO_TCP: + return inet6_lookup(net, &tcp_hashinfo, skb, doff, + saddr, sport, daddr, dport, + in->ifindex); + case IPPROTO_UDP: + return udp6_lib_lookup(net, saddr, sport, daddr, dport, + in->ifindex); + } + + return NULL; +} + +struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb, + const struct net_device *indev) +{ + __be16 uninitialized_var(dport), uninitialized_var(sport); + const struct in6_addr *daddr = NULL, *saddr = NULL; + struct ipv6hdr *iph = ipv6_hdr(skb); + struct sk_buff *data_skb = NULL; + int doff = 0; + int thoff = 0, tproto; + + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) { + pr_debug("unable to find transport header in IPv6 packet, dropping\n"); + return NULL; + } + + if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) { + struct udphdr _hdr, *hp; + + hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); + if (hp == NULL) + return NULL; + + saddr = &iph->saddr; + sport = hp->source; + daddr = &iph->daddr; + dport = hp->dest; + data_skb = (struct sk_buff *)skb; + doff = tproto == IPPROTO_TCP ? + thoff + __tcp_hdrlen((struct tcphdr *)hp) : + thoff + sizeof(*hp); + + } else if (tproto == IPPROTO_ICMPV6) { + struct ipv6hdr ipv6_var; + + if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, + &sport, &dport, &ipv6_var)) + return NULL; + } else { + return NULL; + } + + return nf_socket_get_sock_v6(net, data_skb, doff, tproto, saddr, daddr, + sport, dport, indev); +} +EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v6); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); +MODULE_DESCRIPTION("Netfilter IPv6 socket lookup infrastructure"); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 854dadb..ed00ec1 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1426,9 +1426,10 @@ config NETFILTER_XT_MATCH_SOCKET tristate '"socket" match support' depends on NETFILTER_XTABLES depends on NETFILTER_ADVANCED - depends on !NF_CONNTRACK || NF_CONNTRACK depends on IPV6 || IPV6=n depends on IP6_NF_IPTABLES || IP6_NF_IPTABLES=n + depends on NF_SOCKET_IPV4 + depends on NF_SOCKET_IPV6 select NF_DEFRAG_IPV4 select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES != n help diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index b10ade2..018c369 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -22,76 +22,14 @@ #include #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) -#define XT_SOCKET_HAVE_IPV6 1 #include #include #include #endif +#include #include -#if IS_ENABLED(CONFIG_NF_CONNTRACK) -#define XT_SOCKET_HAVE_CONNTRACK 1 -#include -#endif - -static int -extract_icmp4_fields(const struct sk_buff *skb, - u8 *protocol, - __be32 *raddr, - __be32 *laddr, - __be16 *rport, - __be16 *lport) -{ - unsigned int outside_hdrlen = ip_hdrlen(skb); - struct iphdr *inside_iph, _inside_iph; - struct icmphdr *icmph, _icmph; - __be16 *ports, _ports[2]; - - icmph = skb_header_pointer(skb, outside_hdrlen, - sizeof(_icmph), &_icmph); - if (icmph == NULL) - return 1; - - switch (icmph->type) { - case ICMP_DEST_UNREACH: - case ICMP_SOURCE_QUENCH: - case ICMP_REDIRECT: - case ICMP_TIME_EXCEEDED: - case ICMP_PARAMETERPROB: - break; - default: - return 1; - } - - inside_iph = skb_header_pointer(skb, outside_hdrlen + - sizeof(struct icmphdr), - sizeof(_inside_iph), &_inside_iph); - if (inside_iph == NULL) - return 1; - - if (inside_iph->protocol != IPPROTO_TCP && - inside_iph->protocol != IPPROTO_UDP) - return 1; - - ports = skb_header_pointer(skb, outside_hdrlen + - sizeof(struct icmphdr) + - (inside_iph->ihl << 2), - sizeof(_ports), &_ports); - if (ports == NULL) - return 1; - - /* the inside IP packet is the one quoted from our side, thus - * its saddr is the local address */ - *protocol = inside_iph->protocol; - *laddr = inside_iph->saddr; - *lport = ports[0]; - *raddr = inside_iph->daddr; - *rport = ports[1]; - - return 0; -} - /* "socket" match based redirection (no specific rule) * =================================================== * @@ -111,104 +49,6 @@ extract_icmp4_fields(const struct sk_buff *skb, * then local services could intercept traffic going through the * box. */ -static struct sock * -xt_socket_get_sock_v4(struct net *net, struct sk_buff *skb, const int doff, - const u8 protocol, - const __be32 saddr, const __be32 daddr, - const __be16 sport, const __be16 dport, - const struct net_device *in) -{ - switch (protocol) { - case IPPROTO_TCP: - return inet_lookup(net, &tcp_hashinfo, skb, doff, - saddr, sport, daddr, dport, - in->ifindex); - case IPPROTO_UDP: - return udp4_lib_lookup(net, saddr, sport, daddr, dport, - in->ifindex); - } - return NULL; -} - -static bool xt_socket_sk_is_transparent(struct sock *sk) -{ - switch (sk->sk_state) { - case TCP_TIME_WAIT: - return inet_twsk(sk)->tw_transparent; - - case TCP_NEW_SYN_RECV: - return inet_rsk(inet_reqsk(sk))->no_srccheck; - - default: - return inet_sk(sk)->transparent; - } -} - -static struct sock *xt_socket_lookup_slow_v4(struct net *net, - const struct sk_buff *skb, - const struct net_device *indev) -{ - const struct iphdr *iph = ip_hdr(skb); - struct sk_buff *data_skb = NULL; - int doff = 0; - __be32 uninitialized_var(daddr), uninitialized_var(saddr); - __be16 uninitialized_var(dport), uninitialized_var(sport); - u8 uninitialized_var(protocol); -#ifdef XT_SOCKET_HAVE_CONNTRACK - struct nf_conn const *ct; - enum ip_conntrack_info ctinfo; -#endif - - if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { - struct udphdr _hdr, *hp; - - hp = skb_header_pointer(skb, ip_hdrlen(skb), - sizeof(_hdr), &_hdr); - if (hp == NULL) - return NULL; - - protocol = iph->protocol; - saddr = iph->saddr; - sport = hp->source; - daddr = iph->daddr; - dport = hp->dest; - data_skb = (struct sk_buff *)skb; - doff = iph->protocol == IPPROTO_TCP ? - ip_hdrlen(skb) + __tcp_hdrlen((struct tcphdr *)hp) : - ip_hdrlen(skb) + sizeof(*hp); - - } else if (iph->protocol == IPPROTO_ICMP) { - if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, - &sport, &dport)) - return NULL; - } else { - return NULL; - } - -#ifdef XT_SOCKET_HAVE_CONNTRACK - /* Do the lookup with the original socket address in - * case this is a reply packet of an established - * SNAT-ted connection. - */ - ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct) && - ((iph->protocol != IPPROTO_ICMP && - ctinfo == IP_CT_ESTABLISHED_REPLY) || - (iph->protocol == IPPROTO_ICMP && - ctinfo == IP_CT_RELATED_REPLY)) && - (ct->status & IPS_SRC_NAT_DONE)) { - - daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; - dport = (iph->protocol == IPPROTO_TCP) ? - ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port : - ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; - } -#endif - - return xt_socket_get_sock_v4(net, data_skb, doff, protocol, saddr, - daddr, sport, dport, indev); -} - static bool socket_match(const struct sk_buff *skb, struct xt_action_param *par, const struct xt_socket_mtinfo1 *info) @@ -217,7 +57,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, struct sock *sk = skb->sk; if (!sk) - sk = xt_socket_lookup_slow_v4(par->net, skb, par->in); + sk = nf_sk_lookup_slow_v4(par->net, skb, par->in); if (sk) { bool wildcard; bool transparent = true; @@ -233,7 +73,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, * if XT_SOCKET_TRANSPARENT is used */ if (info->flags & XT_SOCKET_TRANSPARENT) - transparent = xt_socket_sk_is_transparent(sk); + transparent = nf_sk_is_transparent(sk); if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && transparent) @@ -265,132 +105,7 @@ socket_mt4_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) return socket_match(skb, par, par->matchinfo); } -#ifdef XT_SOCKET_HAVE_IPV6 - -static int -extract_icmp6_fields(const struct sk_buff *skb, - unsigned int outside_hdrlen, - int *protocol, - const struct in6_addr **raddr, - const struct in6_addr **laddr, - __be16 *rport, - __be16 *lport, - struct ipv6hdr *ipv6_var) -{ - const struct ipv6hdr *inside_iph; - struct icmp6hdr *icmph, _icmph; - __be16 *ports, _ports[2]; - u8 inside_nexthdr; - __be16 inside_fragoff; - int inside_hdrlen; - - icmph = skb_header_pointer(skb, outside_hdrlen, - sizeof(_icmph), &_icmph); - if (icmph == NULL) - return 1; - - if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK) - return 1; - - inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), - sizeof(*ipv6_var), ipv6_var); - if (inside_iph == NULL) - return 1; - inside_nexthdr = inside_iph->nexthdr; - - inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + - sizeof(*ipv6_var), - &inside_nexthdr, &inside_fragoff); - if (inside_hdrlen < 0) - return 1; /* hjm: Packet has no/incomplete transport layer headers. */ - - if (inside_nexthdr != IPPROTO_TCP && - inside_nexthdr != IPPROTO_UDP) - return 1; - - ports = skb_header_pointer(skb, inside_hdrlen, - sizeof(_ports), &_ports); - if (ports == NULL) - return 1; - - /* the inside IP packet is the one quoted from our side, thus - * its saddr is the local address */ - *protocol = inside_nexthdr; - *laddr = &inside_iph->saddr; - *lport = ports[0]; - *raddr = &inside_iph->daddr; - *rport = ports[1]; - - return 0; -} - -static struct sock * -xt_socket_get_sock_v6(struct net *net, struct sk_buff *skb, int doff, - const u8 protocol, - const struct in6_addr *saddr, const struct in6_addr *daddr, - const __be16 sport, const __be16 dport, - const struct net_device *in) -{ - switch (protocol) { - case IPPROTO_TCP: - return inet6_lookup(net, &tcp_hashinfo, skb, doff, - saddr, sport, daddr, dport, - in->ifindex); - case IPPROTO_UDP: - return udp6_lib_lookup(net, saddr, sport, daddr, dport, - in->ifindex); - } - - return NULL; -} - -static struct sock *xt_socket_lookup_slow_v6(struct net *net, - const struct sk_buff *skb, - const struct net_device *indev) -{ - __be16 uninitialized_var(dport), uninitialized_var(sport); - const struct in6_addr *daddr = NULL, *saddr = NULL; - struct ipv6hdr *iph = ipv6_hdr(skb); - struct sk_buff *data_skb = NULL; - int doff = 0; - int thoff = 0, tproto; - - tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); - if (tproto < 0) { - pr_debug("unable to find transport header in IPv6 packet, dropping\n"); - return NULL; - } - - if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) { - struct udphdr _hdr, *hp; - - hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); - if (hp == NULL) - return NULL; - - saddr = &iph->saddr; - sport = hp->source; - daddr = &iph->daddr; - dport = hp->dest; - data_skb = (struct sk_buff *)skb; - doff = tproto == IPPROTO_TCP ? - thoff + __tcp_hdrlen((struct tcphdr *)hp) : - thoff + sizeof(*hp); - - } else if (tproto == IPPROTO_ICMPV6) { - struct ipv6hdr ipv6_var; - - if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, - &sport, &dport, &ipv6_var)) - return NULL; - } else { - return NULL; - } - - return xt_socket_get_sock_v6(net, data_skb, doff, tproto, saddr, daddr, - sport, dport, indev); -} - +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) static bool socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) { @@ -399,7 +114,7 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk = skb->sk; if (!sk) - sk = xt_socket_lookup_slow_v6(par->net, skb, par->in); + sk = nf_sk_lookup_slow_v6(par->net, skb, par->in); if (sk) { bool wildcard; bool transparent = true; @@ -415,7 +130,7 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) * if XT_SOCKET_TRANSPARENT is used */ if (info->flags & XT_SOCKET_TRANSPARENT) - transparent = xt_socket_sk_is_transparent(sk); + transparent = nf_sk_is_transparent(sk); if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && transparent) @@ -488,7 +203,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = { (1 << NF_INET_LOCAL_IN), .me = THIS_MODULE, }, -#ifdef XT_SOCKET_HAVE_IPV6 +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) { .name = "socket", .revision = 1, @@ -512,7 +227,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = { (1 << NF_INET_LOCAL_IN), .me = THIS_MODULE, }, -#ifdef XT_SOCKET_HAVE_IPV6 +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) { .name = "socket", .revision = 2, @@ -536,7 +251,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = { (1 << NF_INET_LOCAL_IN), .me = THIS_MODULE, }, -#ifdef XT_SOCKET_HAVE_IPV6 +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) { .name = "socket", .revision = 3, @@ -554,7 +269,7 @@ static struct xt_match socket_mt_reg[] __read_mostly = { static int __init socket_mt_init(void) { nf_defrag_ipv4_enable(); -#ifdef XT_SOCKET_HAVE_IPV6 +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) nf_defrag_ipv6_enable(); #endif -- cgit v1.1 From 2fa841938c648fe4359691f41e8e1f37ff1a3aa2 Mon Sep 17 00:00:00 2001 From: "Anders K. Pedersen" Date: Fri, 28 Oct 2016 05:54:15 +0000 Subject: netfilter: nf_tables: introduce routing expression Introduces an nftables rt expression for routing related data with support for nexthop (i.e. the directly connected IP address that an outgoing packet is sent to), which can be used either for matching or accounting, eg. # nft add rule filter postrouting \ ip daddr 192.168.1.0/24 rt nexthop != 192.168.0.1 drop This will drop any traffic to 192.168.1.0/24 that is not routed via 192.168.0.1. # nft add rule filter postrouting \ flow table acct { rt nexthop timeout 600s counter } # nft add rule ip6 filter postrouting \ flow table acct { rt nexthop timeout 600s counter } These rules count outgoing traffic per nexthop. Note that the timeout releases an entry if no traffic is seen for this nexthop within 10 minutes. # nft add rule inet filter postrouting \ ether type ip \ flow table acct { rt nexthop timeout 600s counter } # nft add rule inet filter postrouting \ ether type ip6 \ flow table acct { rt nexthop timeout 600s counter } Same as above, but via the inet family, where the ether type must be specified explicitly. "rt classid" is also implemented identical to "meta rtclassid", since it is more logical to have this match in the routing expression going forward. Signed-off-by: Anders K. Pedersen Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 27 ++++++ net/netfilter/Kconfig | 6 ++ net/netfilter/Makefile | 1 + net/netfilter/nft_rt.c | 153 +++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 net/netfilter/nft_rt.c diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index a054ad2..14e5f61 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -759,6 +759,19 @@ enum nft_meta_keys { }; /** + * enum nft_rt_keys - nf_tables routing expression keys + * + * @NFT_RT_CLASSID: realm value of packet's route (skb->dst->tclassid) + * @NFT_RT_NEXTHOP4: routing nexthop for IPv4 + * @NFT_RT_NEXTHOP6: routing nexthop for IPv6 + */ +enum nft_rt_keys { + NFT_RT_CLASSID, + NFT_RT_NEXTHOP4, + NFT_RT_NEXTHOP6, +}; + +/** * enum nft_hash_attributes - nf_tables hash expression netlink attributes * * @NFTA_HASH_SREG: source register (NLA_U32) @@ -797,6 +810,20 @@ enum nft_meta_attributes { #define NFTA_META_MAX (__NFTA_META_MAX - 1) /** + * enum nft_rt_attributes - nf_tables routing expression netlink attributes + * + * @NFTA_RT_DREG: destination register (NLA_U32) + * @NFTA_RT_KEY: routing data item to load (NLA_U32: nft_rt_keys) + */ +enum nft_rt_attributes { + NFTA_RT_UNSPEC, + NFTA_RT_DREG, + NFTA_RT_KEY, + __NFTA_RT_MAX +}; +#define NFTA_RT_MAX (__NFTA_RT_MAX - 1) + +/** * enum nft_ct_keys - nf_tables ct expression keys * * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index ed00ec1..44410d3 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -478,6 +478,12 @@ config NFT_META This option adds the "meta" expression that you can use to match and to set packet metainformation such as the packet mark. +config NFT_RT + tristate "Netfilter nf_tables routing module" + help + This option adds the "rt" expression that you can use to match + packet routing information such as the packet nexthop. + config NFT_NUMGEN tristate "Netfilter nf_tables number generator module" help diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 8edd791..5bbf767 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_NF_TABLES_NETDEV) += nf_tables_netdev.o obj-$(CONFIG_NFT_COMPAT) += nft_compat.o obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o obj-$(CONFIG_NFT_META) += nft_meta.o +obj-$(CONFIG_NFT_RT) += nft_rt.o obj-$(CONFIG_NFT_NUMGEN) += nft_numgen.o obj-$(CONFIG_NFT_CT) += nft_ct.o obj-$(CONFIG_NFT_LIMIT) += nft_limit.o diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c new file mode 100644 index 0000000..9e5ec1f --- /dev/null +++ b/net/netfilter/nft_rt.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016 Anders K. Pedersen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_rt { + enum nft_rt_keys key:8; + enum nft_registers dreg:8; +}; + +void nft_rt_get_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_rt *priv = nft_expr_priv(expr); + const struct sk_buff *skb = pkt->skb; + u32 *dest = ®s->data[priv->dreg]; + const struct dst_entry *dst; + + dst = skb_dst(skb); + if (!dst) + goto err; + + switch (priv->key) { +#ifdef CONFIG_IP_ROUTE_CLASSID + case NFT_RT_CLASSID: + *dest = dst->tclassid; + break; +#endif + case NFT_RT_NEXTHOP4: + if (pkt->pf != NFPROTO_IPV4) + goto err; + + *dest = rt_nexthop((const struct rtable *)dst, + ip_hdr(skb)->daddr); + break; + case NFT_RT_NEXTHOP6: + if (pkt->pf != NFPROTO_IPV6) + goto err; + + memcpy(dest, rt6_nexthop((struct rt6_info *)dst, + &ipv6_hdr(skb)->daddr), + sizeof(struct in6_addr)); + break; + default: + WARN_ON(1); + goto err; + } + return; + +err: + regs->verdict.code = NFT_BREAK; +} + +const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = { + [NFTA_RT_DREG] = { .type = NLA_U32 }, + [NFTA_RT_KEY] = { .type = NLA_U32 }, +}; + +int nft_rt_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_rt *priv = nft_expr_priv(expr); + unsigned int len; + + if (tb[NFTA_RT_KEY] == NULL || + tb[NFTA_RT_DREG] == NULL) + return -EINVAL; + + priv->key = ntohl(nla_get_be32(tb[NFTA_RT_KEY])); + switch (priv->key) { +#ifdef CONFIG_IP_ROUTE_CLASSID + case NFT_RT_CLASSID: +#endif + case NFT_RT_NEXTHOP4: + len = sizeof(u32); + break; + case NFT_RT_NEXTHOP6: + len = sizeof(struct in6_addr); + break; + default: + return -EOPNOTSUPP; + } + + priv->dreg = nft_parse_register(tb[NFTA_RT_DREG]); + return nft_validate_register_store(ctx, priv->dreg, NULL, + NFT_DATA_VALUE, len); +} + +int nft_rt_get_dump(struct sk_buff *skb, + const struct nft_expr *expr) +{ + const struct nft_rt *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_RT_KEY, htonl(priv->key))) + goto nla_put_failure; + if (nft_dump_register(skb, NFTA_RT_DREG, priv->dreg)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_rt_type; +static const struct nft_expr_ops nft_rt_get_ops = { + .type = &nft_rt_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_rt)), + .eval = nft_rt_get_eval, + .init = nft_rt_get_init, + .dump = nft_rt_get_dump, +}; + +static struct nft_expr_type nft_rt_type __read_mostly = { + .name = "rt", + .ops = &nft_rt_get_ops, + .policy = nft_rt_policy, + .maxattr = NFTA_RT_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_rt_module_init(void) +{ + return nft_register_expr(&nft_rt_type); +} + +static void __exit nft_rt_module_exit(void) +{ + nft_unregister_expr(&nft_rt_type); +} + +module_init(nft_rt_module_init); +module_exit(nft_rt_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anders K. Pedersen "); +MODULE_ALIAS_NFT_EXPR("rt"); -- cgit v1.1 From e41e9d623cd77dbe970b15a4c06a0765c3657bdd Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sat, 29 Oct 2016 21:56:27 +0800 Subject: netfilter: nf_tables: remove useless U8_MAX validation After call nft_data_init, size is already validated and desc.len will not exceed the sizeof(struct nft_data), i.e. 16 bytes. So it will never exceed U8_MAX. Furthermore, in nft_immediate_init, we forget to call nft_data_uninit when desc.len exceeds U8_MAX, although this will not happen, but it's a logical mistake. Now remove these redundant validation introduced by commit 36b701fae12a ("netfilter: nf_tables: validate maximum value of u32 netlink attributes") Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_cmp.c | 3 --- net/netfilter/nft_immediate.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 2e53739..e25b35d 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -84,9 +84,6 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, if (err < 0) return err; - if (desc.len > U8_MAX) - return -ERANGE; - priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); priv->len = desc.len; return 0; diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index d17018f..4528ade 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -54,9 +54,6 @@ static int nft_immediate_init(const struct nft_ctx *ctx, if (err < 0) return err; - if (desc.len > U8_MAX) - return -ERANGE; - priv->dlen = desc.len; priv->dreg = nft_parse_register(tb[NFTA_IMMEDIATE_DREG]); -- cgit v1.1 From 886bc50348fd69972baa4616ce95b7dc9ff8afe7 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 31 Oct 2016 00:35:07 +0100 Subject: netfilter: nf_queue: place volatile data in own cacheline As the comment indicates, the data at the end of nfqnl_instance struct is written on every queue/dequeue, so it should reside in its own cacheline. Before this change, 'lock' was in first cacheline so we dirtied both. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index af832c5..5379f78 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -69,7 +69,7 @@ struct nfqnl_instance { * Following fields are dirtied for each queued packet, * keep them in same cache line if possible. */ - spinlock_t lock; + spinlock_t lock ____cacheline_aligned_in_smp; unsigned int queue_total; unsigned int id_sequence; /* 'sequence' of pkt ids */ struct list_head queue_list; /* packets in queue */ -- cgit v1.1 From a1a8f7fe92a23af30d5e766316452b5312061fa2 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Tue, 1 Nov 2016 16:08:28 +0200 Subject: net/sched: cls_flower: add missing unbind call when destroying flows tcf_unbind was called in fl_delete but was missing in fl_destroy when force deleting flows. Fixes: 77b9900ef53a ('tc: introduce Flower classifier') Signed-off-by: Roi Dayan Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index f6f40fb..a5f6370 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -280,6 +280,7 @@ static bool fl_destroy(struct tcf_proto *tp, bool force) list_for_each_entry_safe(f, next, &head->filters, list) { fl_hw_destroy_filter(tp, (unsigned long)f); list_del_rcu(&f->list); + tcf_unbind_filter(tp, &f->res); call_rcu(&f->rcu, fl_destroy_filter); } RCU_INIT_POINTER(tp->root, NULL); -- cgit v1.1 From 13fa876ebd03e90cd528c387410a9eb0b9c8de3f Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Tue, 1 Nov 2016 16:08:29 +0200 Subject: net/sched: cls_flower: merge filter delete/destroy common code Move common code from fl_delete and fl_detroy to __fl_delete. Signed-off-by: Roi Dayan Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index a5f6370..a8fb1ca 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -269,6 +269,14 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, &tc); } +static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) +{ + list_del_rcu(&f->list); + fl_hw_destroy_filter(tp, (unsigned long)f); + tcf_unbind_filter(tp, &f->res); + call_rcu(&f->rcu, fl_destroy_filter); +} + static bool fl_destroy(struct tcf_proto *tp, bool force) { struct cls_fl_head *head = rtnl_dereference(tp->root); @@ -277,12 +285,8 @@ static bool fl_destroy(struct tcf_proto *tp, bool force) if (!force && !list_empty(&head->filters)) return false; - list_for_each_entry_safe(f, next, &head->filters, list) { - fl_hw_destroy_filter(tp, (unsigned long)f); - list_del_rcu(&f->list); - tcf_unbind_filter(tp, &f->res); - call_rcu(&f->rcu, fl_destroy_filter); - } + list_for_each_entry_safe(f, next, &head->filters, list) + __fl_delete(tp, f); RCU_INIT_POINTER(tp->root, NULL); if (head->mask_assigned) rhashtable_destroy(&head->ht); @@ -742,10 +746,7 @@ static int fl_delete(struct tcf_proto *tp, unsigned long arg) rhashtable_remove_fast(&head->ht, &f->ht_node, head->ht_params); - list_del_rcu(&f->list); - fl_hw_destroy_filter(tp, (unsigned long)f); - tcf_unbind_filter(tp, &f->res); - call_rcu(&f->rcu, fl_destroy_filter); + __fl_delete(tp, f); return 0; } -- cgit v1.1 From e4ff952a7e11e51789949ed738c3c9455e5fd6c0 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 1 Nov 2016 00:49:41 +0800 Subject: sctp: clean up sctp_packet_transmit After adding sctp gso, sctp_packet_transmit is a quite big function now. This patch is to extract the codes for packing packet to sctp_packet_pack from sctp_packet_transmit, and add some comments, simplify the err path by freeing auth chunk when freeing packet chunk_list in out path and freeing head skb early if it fails to pack packet. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Acked-by: Neil Horman Signed-off-by: David S. Miller --- net/sctp/output.c | 435 ++++++++++++++++++++---------------------------------- 1 file changed, 158 insertions(+), 277 deletions(-) diff --git a/net/sctp/output.c b/net/sctp/output.c index 7b50e43..f5320a8 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -399,187 +399,72 @@ static void sctp_packet_set_owner_w(struct sk_buff *skb, struct sock *sk) atomic_inc(&sk->sk_wmem_alloc); } -/* All packets are sent to the network through this function from - * sctp_outq_tail(). - * - * The return value is a normal kernel error return value. - */ -int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) +static int sctp_packet_pack(struct sctp_packet *packet, + struct sk_buff *head, int gso, gfp_t gfp) { struct sctp_transport *tp = packet->transport; - struct sctp_association *asoc = tp->asoc; - struct sctphdr *sh; - struct sk_buff *nskb = NULL, *head = NULL; + struct sctp_auth_chunk *auth = NULL; struct sctp_chunk *chunk, *tmp; - struct sock *sk; - int err = 0; - int padding; /* How much padding do we need? */ - int pkt_size; - __u8 has_data = 0; - int gso = 0; - int pktcount = 0; + int pkt_count = 0, pkt_size; + struct sock *sk = head->sk; + struct sk_buff *nskb; int auth_len = 0; - struct dst_entry *dst; - unsigned char *auth = NULL; /* pointer to auth in skb data */ - - pr_debug("%s: packet:%p\n", __func__, packet); - /* Do NOT generate a chunkless packet. */ - if (list_empty(&packet->chunk_list)) - return err; - - /* Set up convenience variables... */ - chunk = list_entry(packet->chunk_list.next, struct sctp_chunk, list); - sk = chunk->skb->sk; - - /* Allocate the head skb, or main one if not in GSO */ - if (packet->size > tp->pathmtu && !packet->ipfragok) { - if (sk_can_gso(sk)) { - gso = 1; - pkt_size = packet->overhead; - } else { - /* If this happens, we trash this packet and try - * to build a new one, hopefully correct this - * time. Application may notice this error. - */ - pr_err_once("Trying to GSO but underlying device doesn't support it."); - goto err; - } - } else { - pkt_size = packet->size; - } - head = alloc_skb(pkt_size + MAX_HEADER, gfp); - if (!head) - goto err; if (gso) { - NAPI_GRO_CB(head)->last = head; skb_shinfo(head)->gso_type = sk->sk_gso_type; + NAPI_GRO_CB(head)->last = head; + } else { + nskb = head; + pkt_size = packet->size; + goto merge; } - /* Make sure the outbound skb has enough header room reserved. */ - skb_reserve(head, packet->overhead + MAX_HEADER); - - /* Set the owning socket so that we know where to get the - * destination IP address. - */ - sctp_packet_set_owner_w(head, sk); - - if (!sctp_transport_dst_check(tp)) { - sctp_transport_route(tp, NULL, sctp_sk(sk)); - if (asoc && (asoc->param_flags & SPP_PMTUD_ENABLE)) { - sctp_assoc_sync_pmtu(sk, asoc); - } - } - dst = dst_clone(tp->dst); - if (!dst) { - if (asoc) - IP_INC_STATS(sock_net(asoc->base.sk), - IPSTATS_MIB_OUTNOROUTES); - goto nodst; - } - skb_dst_set(head, dst); - - /* Build the SCTP header. */ - sh = (struct sctphdr *)skb_push(head, sizeof(struct sctphdr)); - skb_reset_transport_header(head); - sh->source = htons(packet->source_port); - sh->dest = htons(packet->destination_port); - - /* From 6.8 Adler-32 Checksum Calculation: - * After the packet is constructed (containing the SCTP common - * header and one or more control or DATA chunks), the - * transmitter shall: - * - * 1) Fill in the proper Verification Tag in the SCTP common - * header and initialize the checksum field to 0's. - */ - sh->vtag = htonl(packet->vtag); - sh->checksum = 0; - - pr_debug("***sctp_transmit_packet***\n"); - do { - /* Set up convenience variables... */ - chunk = list_entry(packet->chunk_list.next, struct sctp_chunk, list); - pktcount++; - - /* Calculate packet size, so it fits in PMTU. Leave - * other chunks for the next packets. - */ - if (gso) { - pkt_size = packet->overhead; - list_for_each_entry(chunk, &packet->chunk_list, list) { - int padded = SCTP_PAD4(chunk->skb->len); - - if (chunk == packet->auth) - auth_len = padded; - else if (auth_len + padded + packet->overhead > - tp->pathmtu) - goto nomem; - else if (pkt_size + padded > tp->pathmtu) - break; - pkt_size += padded; - } - - /* Allocate a new skb. */ - nskb = alloc_skb(pkt_size + MAX_HEADER, gfp); - if (!nskb) - goto nomem; + /* calculate the pkt_size and alloc nskb */ + pkt_size = packet->overhead; + list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, + list) { + int padded = SCTP_PAD4(chunk->skb->len); - /* Make sure the outbound skb has enough header - * room reserved. - */ - skb_reserve(nskb, packet->overhead + MAX_HEADER); - } else { - nskb = head; + if (chunk == packet->auth) + auth_len = padded; + else if (auth_len + padded + packet->overhead > + tp->pathmtu) + return 0; + else if (pkt_size + padded > tp->pathmtu) + break; + pkt_size += padded; } + nskb = alloc_skb(pkt_size + MAX_HEADER, gfp); + if (!nskb) + return 0; + skb_reserve(nskb, packet->overhead + MAX_HEADER); - /** - * 3.2 Chunk Field Descriptions - * - * The total length of a chunk (including Type, Length and - * Value fields) MUST be a multiple of 4 bytes. If the length - * of the chunk is not a multiple of 4 bytes, the sender MUST - * pad the chunk with all zero bytes and this padding is not - * included in the chunk length field. The sender should - * never pad with more than 3 bytes. - * - * [This whole comment explains SCTP_PAD4() below.] - */ - +merge: + /* merge chunks into nskb and append nskb into head list */ pkt_size -= packet->overhead; list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) { + int padding; + list_del_init(&chunk->list); if (sctp_chunk_is_data(chunk)) { - /* 6.3.1 C4) When data is in flight and when allowed - * by rule C5, a new RTT measurement MUST be made each - * round trip. Furthermore, new RTT measurements - * SHOULD be made no more than once per round-trip - * for a given destination transport address. - */ - if (!sctp_chunk_retransmitted(chunk) && !tp->rto_pending) { chunk->rtt_in_progress = 1; tp->rto_pending = 1; } - - has_data = 1; } padding = SCTP_PAD4(chunk->skb->len) - chunk->skb->len; if (padding) memset(skb_put(chunk->skb, padding), 0, padding); - /* if this is the auth chunk that we are adding, - * store pointer where it will be added and put - * the auth into the packet. - */ if (chunk == packet->auth) - auth = skb_tail_pointer(nskb); + auth = (struct sctp_auth_chunk *) + skb_tail_pointer(nskb); - memcpy(skb_put(nskb, chunk->skb->len), - chunk->skb->data, chunk->skb->len); + memcpy(skb_put(nskb, chunk->skb->len), chunk->skb->data, + chunk->skb->len); pr_debug("*** Chunk:%p[%s] %s 0x%x, length:%d, chunk->skb->len:%d, rtt_in_progress:%d\n", chunk, @@ -589,11 +474,6 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) ntohs(chunk->chunk_hdr->length), chunk->skb->len, chunk->rtt_in_progress); - /* If this is a control chunk, this is our last - * reference. Free data chunks after they've been - * acknowledged or have failed. - * Re-queue auth chunks if needed. - */ pkt_size -= SCTP_PAD4(chunk->skb->len); if (!sctp_chunk_is_data(chunk) && chunk != packet->auth) @@ -603,160 +483,161 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) break; } - /* SCTP-AUTH, Section 6.2 - * The sender MUST calculate the MAC as described in RFC2104 [2] - * using the hash function H as described by the MAC Identifier and - * the shared association key K based on the endpoint pair shared key - * described by the shared key identifier. The 'data' used for the - * computation of the AUTH-chunk is given by the AUTH chunk with its - * HMAC field set to zero (as shown in Figure 6) followed by all - * chunks that are placed after the AUTH chunk in the SCTP packet. - */ - if (auth) - sctp_auth_calculate_hmac(asoc, nskb, - (struct sctp_auth_chunk *)auth, - gfp); - - if (packet->auth) { - if (!list_empty(&packet->chunk_list)) { - /* We will generate more packets, so re-queue - * auth chunk. - */ + if (auth) { + sctp_auth_calculate_hmac(tp->asoc, nskb, auth, gfp); + /* free auth if no more chunks, or add it back */ + if (list_empty(&packet->chunk_list)) + sctp_chunk_free(packet->auth); + else list_add(&packet->auth->list, &packet->chunk_list); - } else { - sctp_chunk_free(packet->auth); - packet->auth = NULL; - } } - if (!gso) - break; - - if (skb_gro_receive(&head, nskb)) { - kfree_skb(nskb); - goto nomem; + if (gso) { + if (skb_gro_receive(&head, nskb)) { + kfree_skb(nskb); + return 0; + } + if (WARN_ON_ONCE(skb_shinfo(head)->gso_segs >= + sk->sk_gso_max_segs)) + return 0; } - nskb = NULL; - if (WARN_ON_ONCE(skb_shinfo(head)->gso_segs >= - sk->sk_gso_max_segs)) - goto nomem; + + pkt_count++; } while (!list_empty(&packet->chunk_list)); - /* 2) Calculate the Adler-32 checksum of the whole packet, - * including the SCTP common header and all the - * chunks. - * - * Note: Adler-32 is no longer applicable, as has been replaced - * by CRC32-C as described in . - * - * If it's a GSO packet, it's postponed to sctp_skb_segment. - */ - if (!sctp_checksum_disable || gso) { - if (!gso && (!(dst->dev->features & NETIF_F_SCTP_CRC) || - dst_xfrm(dst) || packet->ipfragok)) { - sh->checksum = sctp_compute_cksum(head, 0); - } else { - /* no need to seed pseudo checksum for SCTP */ - head->ip_summed = CHECKSUM_PARTIAL; - head->csum_start = skb_transport_header(head) - head->head; - head->csum_offset = offsetof(struct sctphdr, checksum); + if (gso) { + memset(head->cb, 0, max(sizeof(struct inet_skb_parm), + sizeof(struct inet6_skb_parm))); + skb_shinfo(head)->gso_segs = pkt_count; + skb_shinfo(head)->gso_size = GSO_BY_FRAGS; + rcu_read_lock(); + if (skb_dst(head) != tp->dst) { + dst_hold(tp->dst); + sk_setup_caps(sk, tp->dst); } + rcu_read_unlock(); + goto chksum; } - /* IP layer ECN support - * From RFC 2481 - * "The ECN-Capable Transport (ECT) bit would be set by the - * data sender to indicate that the end-points of the - * transport protocol are ECN-capable." - * - * Now setting the ECT bit all the time, as it should not cause - * any problems protocol-wise even if our peer ignores it. - * - * Note: The works for IPv6 layer checks this bit too later - * in transmission. See IP6_ECN_flow_xmit(). - */ - tp->af_specific->ecn_capable(sk); + if (sctp_checksum_disable) + return 1; - /* Set up the IP options. */ - /* BUG: not implemented - * For v4 this all lives somewhere in sk->sk_opt... - */ + if (!(skb_dst(head)->dev->features & NETIF_F_SCTP_CRC) || + dst_xfrm(skb_dst(head)) || packet->ipfragok) { + struct sctphdr *sh = + (struct sctphdr *)skb_transport_header(head); - /* Dump that on IP! */ - if (asoc) { - asoc->stats.opackets += pktcount; - if (asoc->peer.last_sent_to != tp) - /* Considering the multiple CPU scenario, this is a - * "correcter" place for last_sent_to. --xguo - */ - asoc->peer.last_sent_to = tp; + sh->checksum = sctp_compute_cksum(head, 0); + } else { +chksum: + head->ip_summed = CHECKSUM_PARTIAL; + head->csum_start = skb_transport_header(head) - head->head; + head->csum_offset = offsetof(struct sctphdr, checksum); } - if (has_data) { - struct timer_list *timer; - unsigned long timeout; + return pkt_count; +} + +/* All packets are sent to the network through this function from + * sctp_outq_tail(). + * + * The return value is always 0 for now. + */ +int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) +{ + struct sctp_transport *tp = packet->transport; + struct sctp_association *asoc = tp->asoc; + struct sctp_chunk *chunk, *tmp; + int pkt_count, gso = 0; + struct dst_entry *dst; + struct sk_buff *head; + struct sctphdr *sh; + struct sock *sk; - /* Restart the AUTOCLOSE timer when sending data. */ - if (sctp_state(asoc, ESTABLISHED) && - asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE]) { - timer = &asoc->timers[SCTP_EVENT_TIMEOUT_AUTOCLOSE]; - timeout = asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE]; + pr_debug("%s: packet:%p\n", __func__, packet); + if (list_empty(&packet->chunk_list)) + return 0; + chunk = list_entry(packet->chunk_list.next, struct sctp_chunk, list); + sk = chunk->skb->sk; - if (!mod_timer(timer, jiffies + timeout)) - sctp_association_hold(asoc); + /* check gso */ + if (packet->size > tp->pathmtu && !packet->ipfragok) { + if (!sk_can_gso(sk)) { + pr_err_once("Trying to GSO but underlying device doesn't support it."); + goto out; } + gso = 1; + } + + /* alloc head skb */ + head = alloc_skb((gso ? packet->overhead : packet->size) + + MAX_HEADER, gfp); + if (!head) + goto out; + skb_reserve(head, packet->overhead + MAX_HEADER); + sctp_packet_set_owner_w(head, sk); + + /* set sctp header */ + sh = (struct sctphdr *)skb_push(head, sizeof(struct sctphdr)); + skb_reset_transport_header(head); + sh->source = htons(packet->source_port); + sh->dest = htons(packet->destination_port); + sh->vtag = htonl(packet->vtag); + sh->checksum = 0; + + /* update dst if in need */ + if (!sctp_transport_dst_check(tp)) { + sctp_transport_route(tp, NULL, sctp_sk(sk)); + if (asoc && asoc->param_flags & SPP_PMTUD_ENABLE) + sctp_assoc_sync_pmtu(sk, asoc); } + dst = dst_clone(tp->dst); + if (!dst) { + IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); + kfree_skb(head); + goto out; + } + skb_dst_set(head, dst); + /* pack up chunks */ + pkt_count = sctp_packet_pack(packet, head, gso, gfp); + if (!pkt_count) { + kfree_skb(head); + goto out; + } pr_debug("***sctp_transmit_packet*** skb->len:%d\n", head->len); - if (gso) { - /* Cleanup our debris for IP stacks */ - memset(head->cb, 0, max(sizeof(struct inet_skb_parm), - sizeof(struct inet6_skb_parm))); + /* start autoclose timer */ + if (packet->has_data && sctp_state(asoc, ESTABLISHED) && + asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE]) { + struct timer_list *timer = + &asoc->timers[SCTP_EVENT_TIMEOUT_AUTOCLOSE]; + unsigned long timeout = + asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE]; - skb_shinfo(head)->gso_segs = pktcount; - skb_shinfo(head)->gso_size = GSO_BY_FRAGS; + if (!mod_timer(timer, jiffies + timeout)) + sctp_association_hold(asoc); + } - /* We have to refresh this in case we are xmiting to - * more than one transport at a time - */ - rcu_read_lock(); - if (__sk_dst_get(sk) != tp->dst) { - dst_hold(tp->dst); - sk_setup_caps(sk, tp->dst); - } - rcu_read_unlock(); + /* sctp xmit */ + tp->af_specific->ecn_capable(sk); + if (asoc) { + asoc->stats.opackets += pkt_count; + if (asoc->peer.last_sent_to != tp) + asoc->peer.last_sent_to = tp; } head->ignore_df = packet->ipfragok; tp->af_specific->sctp_xmit(head, tp); - goto out; - -nomem: - if (packet->auth && list_empty(&packet->auth->list)) - sctp_chunk_free(packet->auth); - -nodst: - /* FIXME: Returning the 'err' will effect all the associations - * associated with a socket, although only one of the paths of the - * association is unreachable. - * The real failure of a transport or association can be passed on - * to the user via notifications. So setting this error may not be - * required. - */ - /* err = -EHOSTUNREACH; */ - kfree_skb(head); -err: +out: list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) { list_del_init(&chunk->list); if (!sctp_chunk_is_data(chunk)) sctp_chunk_free(chunk); } - -out: sctp_packet_reset(packet); - return err; + return 0; } /******************************************************************** -- cgit v1.1 From ccc109b8ed24c6a84cc46a78ec3df9a57a6c674a Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 2 Nov 2016 17:12:23 +0200 Subject: net/mlx4_en: Add TX_XDP for CQ types Support XDP CQ type, and refactor the CQ type enum. Rename the is_tx field to match the change. Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_cq.c | 18 +++++++++--------- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 7 ++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index e3be7e4..1427311 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -65,7 +65,7 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv, cq->buf_size = cq->size * mdev->dev->caps.cqe_size; cq->ring = ring; - cq->is_tx = mode; + cq->type = mode; cq->vector = mdev->dev->caps.num_comp_vectors; /* Allocate HW buffers on provided NUMA node. @@ -104,7 +104,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, *cq->mcq.arm_db = 0; memset(cq->buf, 0, cq->buf_size); - if (cq->is_tx == RX) { + if (cq->type == RX) { if (!mlx4_is_eq_vector_valid(mdev->dev, priv->port, cq->vector)) { cq->vector = cpumask_first(priv->rx_ring[cq->ring]->affinity_mask); @@ -141,11 +141,11 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, cq->vector = rx_cq->vector; } - if (!cq->is_tx) + if (cq->type == RX) cq->size = priv->rx_ring[cq->ring]->actual_size; - if ((cq->is_tx && priv->hwtstamp_config.tx_type) || - (!cq->is_tx && priv->hwtstamp_config.rx_filter)) + if ((cq->type != RX && priv->hwtstamp_config.tx_type) || + (cq->type == RX && priv->hwtstamp_config.rx_filter)) timestamp_en = 1; err = mlx4_cq_alloc(mdev->dev, cq->size, &cq->wqres.mtt, @@ -154,10 +154,10 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, if (err) goto free_eq; - cq->mcq.comp = cq->is_tx ? mlx4_en_tx_irq : mlx4_en_rx_irq; + cq->mcq.comp = cq->type != RX ? mlx4_en_tx_irq : mlx4_en_rx_irq; cq->mcq.event = mlx4_en_cq_event; - if (cq->is_tx) + if (cq->type != RX) netif_tx_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq, NAPI_POLL_WEIGHT); else @@ -181,7 +181,7 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq) mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size); if (mlx4_is_eq_vector_valid(mdev->dev, priv->port, cq->vector) && - cq->is_tx == RX) + cq->type == RX) mlx4_release_eq(priv->mdev->dev, cq->vector); cq->vector = 0; cq->buf_size = 0; @@ -193,7 +193,7 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq) void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) { napi_disable(&cq->napi); - if (!cq->is_tx) { + if (cq->type == RX) { napi_hash_del(&cq->napi); synchronize_rcu(); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index a3528dd..83c914a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -207,8 +207,9 @@ enum { */ enum cq_type { - RX = 0, - TX = 1, + TX, + TX_XDP, + RX, }; @@ -361,7 +362,7 @@ struct mlx4_en_cq { int size; int buf_size; int vector; - enum cq_type is_tx; + enum cq_type type; u16 moder_time; u16 moder_cnt; struct mlx4_cqe *buf; -- cgit v1.1 From 67f8b1dcb9ee7f1e165da4eb2ec53483a6b141ea Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 2 Nov 2016 17:12:24 +0200 Subject: net/mlx4_en: Refactor the XDP forwarding rings scheme Separately manage the two types of TX rings: regular ones, and XDP. Upon an XDP set, do not borrow regular TX rings and convert them into XDP ones, but allocate new ones, unless we hit the max number of rings. Which means that in systems with smaller #cores we will not consume the current TX rings for XDP, while we are still in the num TX limit. XDP TX rings counters are not shown in ethtool statistics. Instead, XDP counters will be added to the respective RX rings in a downstream patch. This has no performance implications. Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_cq.c | 10 +- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 76 +++-- drivers/net/ethernet/mellanox/mlx4/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 378 ++++++++++++++---------- drivers/net/ethernet/mellanox/mlx4/en_port.c | 4 +- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 8 +- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 9 +- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 11 +- 8 files changed, 293 insertions(+), 205 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 1427311..03f05c4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -127,15 +127,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, /* For TX we use the same irq per ring we assigned for the RX */ struct mlx4_en_cq *rx_cq; - int xdp_index; - - /* The xdp tx irq must align with the rx ring that forwards to - * it, so reindex these from 0. This should only happen when - * tx_ring_num is not a multiple of rx_ring_num. - */ - xdp_index = (priv->xdp_ring_num - priv->tx_ring_num) + cq_idx; - if (xdp_index >= 0) - cq_idx = xdp_index; + cq_idx = cq_idx % priv->rx_ring_num; rx_cq = priv->rx_cq[cq_idx]; cq->vector = rx_cq->vector; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index bdda17d..e8ccb95 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -49,16 +49,19 @@ static int mlx4_en_moderation_update(struct mlx4_en_priv *priv) { - int i; + int i, t; int err = 0; - for (i = 0; i < priv->tx_ring_num; i++) { - priv->tx_cq[i]->moder_cnt = priv->tx_frames; - priv->tx_cq[i]->moder_time = priv->tx_usecs; - if (priv->port_up) { - err = mlx4_en_set_cq_moder(priv, priv->tx_cq[i]); - if (err) - return err; + for (t = 0 ; t < MLX4_EN_NUM_TX_TYPES; t++) { + for (i = 0; i < priv->tx_ring_num[t]; i++) { + priv->tx_cq[t][i]->moder_cnt = priv->tx_frames; + priv->tx_cq[t][i]->moder_time = priv->tx_usecs; + if (priv->port_up) { + err = mlx4_en_set_cq_moder(priv, + priv->tx_cq[t][i]); + if (err) + return err; + } } } @@ -336,7 +339,7 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset) switch (sset) { case ETH_SS_STATS: return bitmap_iterator_count(&it) + - (priv->tx_ring_num * 2) + + (priv->tx_ring_num[TX] * 2) + (priv->rx_ring_num * 3); case ETH_SS_TEST: return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags @@ -397,9 +400,9 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, if (bitmap_iterator_test(&it)) data[index++] = ((unsigned long *)&priv->pkstats)[i]; - for (i = 0; i < priv->tx_ring_num; i++) { - data[index++] = priv->tx_ring[i]->packets; - data[index++] = priv->tx_ring[i]->bytes; + for (i = 0; i < priv->tx_ring_num[TX]; i++) { + data[index++] = priv->tx_ring[TX][i]->packets; + data[index++] = priv->tx_ring[TX][i]->bytes; } for (i = 0; i < priv->rx_ring_num; i++) { data[index++] = priv->rx_ring[i]->packets; @@ -467,7 +470,7 @@ static void mlx4_en_get_strings(struct net_device *dev, strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[strings]); - for (i = 0; i < priv->tx_ring_num; i++) { + for (i = 0; i < priv->tx_ring_num[TX]; i++) { sprintf(data + (index++) * ETH_GSTRING_LEN, "tx%d_packets", i); sprintf(data + (index++) * ETH_GSTRING_LEN, @@ -1060,7 +1063,7 @@ static int mlx4_en_set_ringparam(struct net_device *dev, if (rx_size == (priv->port_up ? priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size) && - tx_size == priv->tx_ring[0]->size) + tx_size == priv->tx_ring[TX][0]->size) return 0; tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); @@ -1105,7 +1108,7 @@ static void mlx4_en_get_ringparam(struct net_device *dev, param->tx_max_pending = MLX4_EN_MAX_TX_SIZE; param->rx_pending = priv->port_up ? priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size; - param->tx_pending = priv->tx_ring[0]->size; + param->tx_pending = priv->tx_ring[TX][0]->size; } static u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev) @@ -1710,7 +1713,7 @@ static void mlx4_en_get_channels(struct net_device *dev, channel->max_tx = MLX4_EN_MAX_TX_RING_P_UP; channel->rx_count = priv->rx_ring_num; - channel->tx_count = priv->tx_ring_num / MLX4_EN_NUM_UP; + channel->tx_count = priv->tx_ring_num[TX] / MLX4_EN_NUM_UP; } static int mlx4_en_set_channels(struct net_device *dev, @@ -1721,6 +1724,7 @@ static int mlx4_en_set_channels(struct net_device *dev, struct mlx4_en_port_profile new_prof; struct mlx4_en_priv *tmp; int port_up = 0; + int xdp_count; int err = 0; if (channel->other_count || channel->combined_count || @@ -1729,20 +1733,25 @@ static int mlx4_en_set_channels(struct net_device *dev, !channel->tx_count || !channel->rx_count) return -EINVAL; - if (channel->tx_count * MLX4_EN_NUM_UP <= priv->xdp_ring_num) { - en_err(priv, "Minimum %d tx channels required with XDP on\n", - priv->xdp_ring_num / MLX4_EN_NUM_UP + 1); - return -EINVAL; - } - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) return -ENOMEM; mutex_lock(&mdev->state_lock); + xdp_count = priv->tx_ring_num[TX_XDP] ? channel->rx_count : 0; + if (channel->tx_count * MLX4_EN_NUM_UP + xdp_count > MAX_TX_RINGS) { + err = -EINVAL; + en_err(priv, + "Total number of TX and XDP rings (%d) exceeds the maximum supported (%d)\n", + channel->tx_count * MLX4_EN_NUM_UP + xdp_count, + MAX_TX_RINGS); + goto out; + } + memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile)); new_prof.num_tx_rings_p_up = channel->tx_count; - new_prof.tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP; + new_prof.tx_ring_num[TX] = channel->tx_count * MLX4_EN_NUM_UP; + new_prof.tx_ring_num[TX_XDP] = xdp_count; new_prof.rx_ring_num = channel->rx_count; err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof); @@ -1756,14 +1765,13 @@ static int mlx4_en_set_channels(struct net_device *dev, mlx4_en_safe_replace_resources(priv, tmp); - netif_set_real_num_tx_queues(dev, priv->tx_ring_num - - priv->xdp_ring_num); + netif_set_real_num_tx_queues(dev, priv->tx_ring_num[TX]); netif_set_real_num_rx_queues(dev, priv->rx_ring_num); if (dev->num_tc) mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP); - en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num); + en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num[TX]); en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num); if (port_up) { @@ -1774,8 +1782,8 @@ static int mlx4_en_set_channels(struct net_device *dev, err = mlx4_en_moderation_update(priv); out: - kfree(tmp); mutex_unlock(&mdev->state_lock); + kfree(tmp); return err; } @@ -1823,11 +1831,15 @@ static int mlx4_en_set_priv_flags(struct net_device *dev, u32 flags) int ret = 0; if (bf_enabled_new != bf_enabled_old) { + int t; + if (bf_enabled_new) { bool bf_supported = true; - for (i = 0; i < priv->tx_ring_num; i++) - bf_supported &= priv->tx_ring[i]->bf_alloced; + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) + for (i = 0; i < priv->tx_ring_num[t]; i++) + bf_supported &= + priv->tx_ring[t][i]->bf_alloced; if (!bf_supported) { en_err(priv, "BlueFlame is not supported\n"); @@ -1839,8 +1851,10 @@ static int mlx4_en_set_priv_flags(struct net_device *dev, u32 flags) priv->pflags &= ~MLX4_EN_PRIV_FLAGS_BLUEFLAME; } - for (i = 0; i < priv->tx_ring_num; i++) - priv->tx_ring[i]->bf_enabled = bf_enabled_new; + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) + for (i = 0; i < priv->tx_ring_num[t]; i++) + priv->tx_ring[t][i]->bf_enabled = + bf_enabled_new; en_info(priv, "BlueFlame %s\n", bf_enabled_new ? "Enabled" : "Disabled"); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index bf7628d..36a7a54 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -169,7 +169,7 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) params->prof[i].tx_ppp = pfctx; params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE; params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE; - params->prof[i].tx_ring_num = params->num_tx_rings_p_up * + params->prof[i].tx_ring_num[TX] = params->num_tx_rings_p_up * MLX4_EN_NUM_UP; params->prof[i].rss_rings = 0; params->prof[i].inline_thold = inline_thold; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 58b749d..edf0a99 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1214,8 +1214,8 @@ static void mlx4_en_netpoll(struct net_device *dev) struct mlx4_en_cq *cq; int i; - for (i = 0; i < priv->tx_ring_num; i++) { - cq = priv->tx_cq[i]; + for (i = 0; i < priv->tx_ring_num[TX]; i++) { + cq = priv->tx_cq[TX][i]; napi_schedule(&cq->napi); } } @@ -1299,12 +1299,14 @@ static void mlx4_en_tx_timeout(struct net_device *dev) if (netif_msg_timer(priv)) en_warn(priv, "Tx timeout called on port:%d\n", priv->port); - for (i = 0; i < priv->tx_ring_num; i++) { + for (i = 0; i < priv->tx_ring_num[TX]; i++) { + struct mlx4_en_tx_ring *tx_ring = priv->tx_ring[TX][i]; + if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i))) continue; en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n", - i, priv->tx_ring[i]->qpn, priv->tx_ring[i]->cqn, - priv->tx_ring[i]->cons, priv->tx_ring[i]->prod); + i, tx_ring->qpn, tx_ring->cqn, + tx_ring->cons, tx_ring->prod); } priv->port_stats.tx_timeout++; @@ -1328,7 +1330,7 @@ mlx4_en_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv) { struct mlx4_en_cq *cq; - int i; + int i, t; /* If we haven't received a specific coalescing setting * (module param), we set the moderation parameters as follows: @@ -1353,10 +1355,12 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv) priv->last_moder_bytes[i] = 0; } - for (i = 0; i < priv->tx_ring_num; i++) { - cq = priv->tx_cq[i]; - cq->moder_cnt = priv->tx_frames; - cq->moder_time = priv->tx_usecs; + for (t = 0 ; t < MLX4_EN_NUM_TX_TYPES; t++) { + for (i = 0; i < priv->tx_ring_num[t]; i++) { + cq = priv->tx_cq[t][i]; + cq->moder_cnt = priv->tx_frames; + cq->moder_time = priv->tx_usecs; + } } /* Reset auto-moderation params */ @@ -1526,19 +1530,13 @@ static void mlx4_en_free_affinity_hint(struct mlx4_en_priv *priv, int ring_idx) static void mlx4_en_init_recycle_ring(struct mlx4_en_priv *priv, int tx_ring_idx) { - struct mlx4_en_tx_ring *tx_ring = priv->tx_ring[tx_ring_idx]; - int rr_index; + struct mlx4_en_tx_ring *tx_ring = priv->tx_ring[TX_XDP][tx_ring_idx]; + int rr_index = tx_ring_idx; - rr_index = (priv->xdp_ring_num - priv->tx_ring_num) + tx_ring_idx; - if (rr_index >= 0) { - tx_ring->free_tx_desc = mlx4_en_recycle_tx_desc; - tx_ring->recycle_ring = priv->rx_ring[rr_index]; - en_dbg(DRV, priv, - "Set tx_ring[%d]->recycle_ring = rx_ring[%d]\n", - tx_ring_idx, rr_index); - } else { - tx_ring->recycle_ring = NULL; - } + tx_ring->free_tx_desc = mlx4_en_recycle_tx_desc; + tx_ring->recycle_ring = priv->rx_ring[rr_index]; + en_dbg(DRV, priv, "Set tx_ring[%d][%d]->recycle_ring = rx_ring[%d]\n", + TX_XDP, tx_ring_idx, rr_index); } int mlx4_en_start_port(struct net_device *dev) @@ -1548,9 +1546,8 @@ int mlx4_en_start_port(struct net_device *dev) struct mlx4_en_cq *cq; struct mlx4_en_tx_ring *tx_ring; int rx_index = 0; - int tx_index = 0; int err = 0; - int i; + int i, t; int j; u8 mc_list[16] = {0}; @@ -1635,43 +1632,51 @@ int mlx4_en_start_port(struct net_device *dev) goto rss_err; /* Configure tx cq's and rings */ - for (i = 0; i < priv->tx_ring_num; i++) { - /* Configure cq */ - cq = priv->tx_cq[i]; - err = mlx4_en_activate_cq(priv, cq, i); - if (err) { - en_err(priv, "Failed allocating Tx CQ\n"); - goto tx_err; - } - err = mlx4_en_set_cq_moder(priv, cq); - if (err) { - en_err(priv, "Failed setting cq moderation parameters\n"); - mlx4_en_deactivate_cq(priv, cq); - goto tx_err; - } - en_dbg(DRV, priv, "Resetting index of collapsed CQ:%d to -1\n", i); - cq->buf->wqe_index = cpu_to_be16(0xffff); + for (t = 0 ; t < MLX4_EN_NUM_TX_TYPES; t++) { + u8 num_tx_rings_p_up = t == TX ? priv->num_tx_rings_p_up : 1; - /* Configure ring */ - tx_ring = priv->tx_ring[i]; - err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn, - i / priv->num_tx_rings_p_up); - if (err) { - en_err(priv, "Failed allocating Tx ring\n"); - mlx4_en_deactivate_cq(priv, cq); - goto tx_err; - } - tx_ring->tx_queue = netdev_get_tx_queue(dev, i); - - mlx4_en_init_recycle_ring(priv, i); + for (i = 0; i < priv->tx_ring_num[t]; i++) { + /* Configure cq */ + cq = priv->tx_cq[t][i]; + err = mlx4_en_activate_cq(priv, cq, i); + if (err) { + en_err(priv, "Failed allocating Tx CQ\n"); + goto tx_err; + } + err = mlx4_en_set_cq_moder(priv, cq); + if (err) { + en_err(priv, "Failed setting cq moderation parameters\n"); + mlx4_en_deactivate_cq(priv, cq); + goto tx_err; + } + en_dbg(DRV, priv, + "Resetting index of collapsed CQ:%d to -1\n", i); + cq->buf->wqe_index = cpu_to_be16(0xffff); + + /* Configure ring */ + tx_ring = priv->tx_ring[t][i]; + err = mlx4_en_activate_tx_ring(priv, tx_ring, + cq->mcq.cqn, + i / num_tx_rings_p_up); + if (err) { + en_err(priv, "Failed allocating Tx ring\n"); + mlx4_en_deactivate_cq(priv, cq); + goto tx_err; + } + if (t != TX_XDP) { + tx_ring->tx_queue = netdev_get_tx_queue(dev, i); + tx_ring->recycle_ring = NULL; + } else { + mlx4_en_init_recycle_ring(priv, i); + } - /* Arm CQ for TX completions */ - mlx4_en_arm_cq(priv, cq); + /* Arm CQ for TX completions */ + mlx4_en_arm_cq(priv, cq); - /* Set initial ownership of all Tx TXBBs to SW (1) */ - for (j = 0; j < tx_ring->buf_size; j += STAMP_STRIDE) - *((u32 *) (tx_ring->buf + j)) = 0xffffffff; - ++tx_index; + /* Set initial ownership of all Tx TXBBs to SW (1) */ + for (j = 0; j < tx_ring->buf_size; j += STAMP_STRIDE) + *((u32 *)(tx_ring->buf + j)) = 0xffffffff; + } } /* Configure port */ @@ -1746,9 +1751,18 @@ int mlx4_en_start_port(struct net_device *dev) return 0; tx_err: - while (tx_index--) { - mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[tx_index]); - mlx4_en_deactivate_cq(priv, priv->tx_cq[tx_index]); + if (t == MLX4_EN_NUM_TX_TYPES) { + t--; + i = priv->tx_ring_num[t]; + } + while (t >= 0) { + while (i--) { + mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[t][i]); + mlx4_en_deactivate_cq(priv, priv->tx_cq[t][i]); + } + if (!t--) + break; + i = priv->tx_ring_num[t]; } mlx4_en_destroy_drop_qp(priv); rss_err: @@ -1773,7 +1787,7 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_en_mc_list *mclist, *tmp; struct ethtool_flow_id *flow, *tmp_flow; - int i; + int i, t; u8 mc_list[16] = {0}; if (!priv->port_up) { @@ -1859,14 +1873,17 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) mlx4_en_destroy_drop_qp(priv); /* Free TX Rings */ - for (i = 0; i < priv->tx_ring_num; i++) { - mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[i]); - mlx4_en_deactivate_cq(priv, priv->tx_cq[i]); + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + for (i = 0; i < priv->tx_ring_num[t]; i++) { + mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[t][i]); + mlx4_en_deactivate_cq(priv, priv->tx_cq[t][i]); + } } msleep(10); - for (i = 0; i < priv->tx_ring_num; i++) - mlx4_en_free_tx_buf(dev, priv->tx_ring[i]); + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) + for (i = 0; i < priv->tx_ring_num[t]; i++) + mlx4_en_free_tx_buf(dev, priv->tx_ring[t][i]); if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0) mlx4_en_delete_rss_steer_rules(priv); @@ -1915,6 +1932,7 @@ static void mlx4_en_clear_stats(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_tx_ring **tx_ring; int i; if (!mlx4_is_slave(mdev->dev)) @@ -1932,15 +1950,16 @@ static void mlx4_en_clear_stats(struct net_device *dev) sizeof(priv->tx_priority_flowstats)); memset(&priv->pf_stats, 0, sizeof(priv->pf_stats)); - for (i = 0; i < priv->tx_ring_num; i++) { - priv->tx_ring[i]->bytes = 0; - priv->tx_ring[i]->packets = 0; - priv->tx_ring[i]->tx_csum = 0; - priv->tx_ring[i]->tx_dropped = 0; - priv->tx_ring[i]->queue_stopped = 0; - priv->tx_ring[i]->wake_queue = 0; - priv->tx_ring[i]->tso_packets = 0; - priv->tx_ring[i]->xmit_more = 0; + tx_ring = priv->tx_ring[TX]; + for (i = 0; i < priv->tx_ring_num[TX]; i++) { + tx_ring[i]->bytes = 0; + tx_ring[i]->packets = 0; + tx_ring[i]->tx_csum = 0; + tx_ring[i]->tx_dropped = 0; + tx_ring[i]->queue_stopped = 0; + tx_ring[i]->wake_queue = 0; + tx_ring[i]->tso_packets = 0; + tx_ring[i]->xmit_more = 0; } for (i = 0; i < priv->rx_ring_num; i++) { priv->rx_ring[i]->bytes = 0; @@ -1996,17 +2015,20 @@ static int mlx4_en_close(struct net_device *dev) static void mlx4_en_free_resources(struct mlx4_en_priv *priv) { - int i; + int i, t; #ifdef CONFIG_RFS_ACCEL priv->dev->rx_cpu_rmap = NULL; #endif - for (i = 0; i < priv->tx_ring_num; i++) { - if (priv->tx_ring && priv->tx_ring[i]) - mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]); - if (priv->tx_cq && priv->tx_cq[i]) - mlx4_en_destroy_cq(priv, &priv->tx_cq[i]); + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + for (i = 0; i < priv->tx_ring_num[t]; i++) { + if (priv->tx_ring[t] && priv->tx_ring[t][i]) + mlx4_en_destroy_tx_ring(priv, + &priv->tx_ring[t][i]); + if (priv->tx_cq[t] && priv->tx_cq[t][i]) + mlx4_en_destroy_cq(priv, &priv->tx_cq[t][i]); + } } for (i = 0; i < priv->rx_ring_num; i++) { @@ -2022,20 +2044,22 @@ static void mlx4_en_free_resources(struct mlx4_en_priv *priv) static int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) { struct mlx4_en_port_profile *prof = priv->prof; - int i; + int i, t; int node; /* Create tx Rings */ - for (i = 0; i < priv->tx_ring_num; i++) { - node = cpu_to_node(i % num_online_cpus()); - if (mlx4_en_create_cq(priv, &priv->tx_cq[i], - prof->tx_ring_size, i, TX, node)) - goto err; - - if (mlx4_en_create_tx_ring(priv, &priv->tx_ring[i], - prof->tx_ring_size, TXBB_SIZE, - node, i)) - goto err; + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + for (i = 0; i < priv->tx_ring_num[t]; i++) { + node = cpu_to_node(i % num_online_cpus()); + if (mlx4_en_create_cq(priv, &priv->tx_cq[t][i], + prof->tx_ring_size, i, t, node)) + goto err; + + if (mlx4_en_create_tx_ring(priv, &priv->tx_ring[t][i], + prof->tx_ring_size, + TXBB_SIZE, node, i)) + goto err; + } } /* Create rx Rings */ @@ -2067,11 +2091,14 @@ err: if (priv->rx_cq[i]) mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); } - for (i = 0; i < priv->tx_ring_num; i++) { - if (priv->tx_ring[i]) - mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]); - if (priv->tx_cq[i]) - mlx4_en_destroy_cq(priv, &priv->tx_cq[i]); + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + for (i = 0; i < priv->tx_ring_num[t]; i++) { + if (priv->tx_ring[t][i]) + mlx4_en_destroy_tx_ring(priv, + &priv->tx_ring[t][i]); + if (priv->tx_cq[t][i]) + mlx4_en_destroy_cq(priv, &priv->tx_cq[t][i]); + } } return -ENOMEM; } @@ -2088,10 +2115,11 @@ static int mlx4_en_copy_priv(struct mlx4_en_priv *dst, struct mlx4_en_priv *src, struct mlx4_en_port_profile *prof) { + int t; + memcpy(&dst->hwtstamp_config, &prof->hwtstamp_config, sizeof(dst->hwtstamp_config)); dst->num_tx_rings_p_up = src->mdev->profile.num_tx_rings_p_up; - dst->tx_ring_num = prof->tx_ring_num; dst->rx_ring_num = prof->rx_ring_num; dst->flags = prof->flags; dst->mdev = src->mdev; @@ -2101,33 +2129,50 @@ static int mlx4_en_copy_priv(struct mlx4_en_priv *dst, dst->stride = roundup_pow_of_two(sizeof(struct mlx4_en_rx_desc) + DS_SIZE * MLX4_EN_MAX_RX_FRAGS); - dst->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS, - GFP_KERNEL); - if (!dst->tx_ring) - return -ENOMEM; + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + dst->tx_ring_num[t] = prof->tx_ring_num[t]; + if (!dst->tx_ring_num[t]) + continue; - dst->tx_cq = kzalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS, - GFP_KERNEL); - if (!dst->tx_cq) { - kfree(dst->tx_ring); - return -ENOMEM; + dst->tx_ring[t] = kzalloc(sizeof(struct mlx4_en_tx_ring *) * + MAX_TX_RINGS, GFP_KERNEL); + if (!dst->tx_ring[t]) + goto err_free_tx; + + dst->tx_cq[t] = kzalloc(sizeof(struct mlx4_en_cq *) * + MAX_TX_RINGS, GFP_KERNEL); + if (!dst->tx_cq[t]) { + kfree(dst->tx_ring[t]); + goto err_free_tx; + } } + return 0; + +err_free_tx: + while (t--) { + kfree(dst->tx_ring[t]); + kfree(dst->tx_cq[t]); + } + return -ENOMEM; } static void mlx4_en_update_priv(struct mlx4_en_priv *dst, struct mlx4_en_priv *src) { + int t; memcpy(dst->rx_ring, src->rx_ring, sizeof(struct mlx4_en_rx_ring *) * src->rx_ring_num); memcpy(dst->rx_cq, src->rx_cq, sizeof(struct mlx4_en_cq *) * src->rx_ring_num); memcpy(&dst->hwtstamp_config, &src->hwtstamp_config, sizeof(dst->hwtstamp_config)); - dst->tx_ring_num = src->tx_ring_num; + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + dst->tx_ring_num[t] = src->tx_ring_num[t]; + dst->tx_ring[t] = src->tx_ring[t]; + dst->tx_cq[t] = src->tx_cq[t]; + } dst->rx_ring_num = src->rx_ring_num; - dst->tx_ring = src->tx_ring; - dst->tx_cq = src->tx_cq; memcpy(dst->prof, src->prof, sizeof(struct mlx4_en_port_profile)); } @@ -2135,14 +2180,18 @@ int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv, struct mlx4_en_priv *tmp, struct mlx4_en_port_profile *prof) { + int t; + mlx4_en_copy_priv(tmp, priv, prof); if (mlx4_en_alloc_resources(tmp)) { en_warn(priv, "%s: Resource allocation failed, using previous configuration\n", __func__); - kfree(tmp->tx_ring); - kfree(tmp->tx_cq); + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + kfree(tmp->tx_ring[t]); + kfree(tmp->tx_cq[t]); + } return -ENOMEM; } return 0; @@ -2161,6 +2210,7 @@ void mlx4_en_destroy_netdev(struct net_device *dev) struct mlx4_en_dev *mdev = priv->mdev; bool shutdown = mdev->dev->persist->interface_state & MLX4_INTERFACE_STATE_SHUTDOWN; + int t; en_dbg(DRV, priv, "Destroying netdev on port:%d\n", priv->port); @@ -2197,8 +2247,10 @@ void mlx4_en_destroy_netdev(struct net_device *dev) mlx4_en_free_resources(priv); - kfree(priv->tx_ring); - kfree(priv->tx_cq); + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + kfree(priv->tx_ring[t]); + kfree(priv->tx_cq[t]); + } if (!shutdown) free_netdev(dev); @@ -2214,7 +2266,7 @@ static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu) en_dbg(DRV, priv, "Change MTU called - current:%d new:%d\n", dev->mtu, new_mtu); - if (priv->xdp_ring_num && MLX4_EN_EFF_MTU(new_mtu) > FRAG_SZ0) { + if (priv->tx_ring_num[TX_XDP] && MLX4_EN_EFF_MTU(new_mtu) > FRAG_SZ0) { en_err(priv, "MTU size:%d requires frags but XDP running\n", new_mtu); return -EOPNOTSUPP; @@ -2605,7 +2657,7 @@ static netdev_features_t mlx4_en_features_check(struct sk_buff *skb, static int mlx4_en_set_tx_maxrate(struct net_device *dev, int queue_index, u32 maxrate) { struct mlx4_en_priv *priv = netdev_priv(dev); - struct mlx4_en_tx_ring *tx_ring = priv->tx_ring[queue_index]; + struct mlx4_en_tx_ring *tx_ring = priv->tx_ring[TX][queue_index]; struct mlx4_update_qp_params params; int err; @@ -2633,18 +2685,21 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog) { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_port_profile new_prof; struct bpf_prog *old_prog; + struct mlx4_en_priv *tmp; + int tx_changed = 0; int xdp_ring_num; int port_up = 0; int err; int i; - xdp_ring_num = prog ? ALIGN(priv->rx_ring_num, MLX4_EN_NUM_UP) : 0; + xdp_ring_num = prog ? priv->rx_ring_num : 0; /* No need to reconfigure buffers when simply swapping the * program for a new one. */ - if (priv->xdp_ring_num == xdp_ring_num) { + if (priv->tx_ring_num[TX_XDP] == xdp_ring_num) { if (prog) { prog = bpf_prog_add(prog, priv->rx_ring_num - 1); if (IS_ERR(prog)) @@ -2668,28 +2723,41 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog) return -EOPNOTSUPP; } - if (priv->tx_ring_num < xdp_ring_num + MLX4_EN_NUM_UP) { - en_err(priv, - "Minimum %d tx channels required to run XDP\n", - (xdp_ring_num + MLX4_EN_NUM_UP) / MLX4_EN_NUM_UP); - return -EINVAL; - } + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; if (prog) { prog = bpf_prog_add(prog, priv->rx_ring_num - 1); - if (IS_ERR(prog)) - return PTR_ERR(prog); + if (IS_ERR(prog)) { + err = PTR_ERR(prog); + goto out; + } } mutex_lock(&mdev->state_lock); + memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile)); + new_prof.tx_ring_num[TX_XDP] = xdp_ring_num; + + if (priv->tx_ring_num[TX] + xdp_ring_num > MAX_TX_RINGS) { + tx_changed = 1; + new_prof.tx_ring_num[TX] = + MAX_TX_RINGS - ALIGN(xdp_ring_num, MLX4_EN_NUM_UP); + en_warn(priv, "Reducing the number of TX rings, to not exceed the max total rings number.\n"); + } + + err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof); + if (err) + goto unlock_out; + if (priv->port_up) { port_up = 1; mlx4_en_stop_port(dev, 1); } - priv->xdp_ring_num = xdp_ring_num; - netif_set_real_num_tx_queues(dev, priv->tx_ring_num - - priv->xdp_ring_num); + mlx4_en_safe_replace_resources(priv, tmp); + if (tx_changed) + netif_set_real_num_tx_queues(dev, priv->tx_ring_num[TX]); for (i = 0; i < priv->rx_ring_num; i++) { old_prog = rcu_dereference_protected( @@ -2709,15 +2777,18 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog) } } +unlock_out: mutex_unlock(&mdev->state_lock); - return 0; +out: + kfree(tmp); + return err; } static bool mlx4_xdp_attached(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); - return !!priv->xdp_ring_num; + return !!priv->tx_ring_num[TX_XDP]; } static int mlx4_xdp(struct net_device *dev, struct netdev_xdp *xdp) @@ -3061,7 +3132,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, { struct net_device *dev; struct mlx4_en_priv *priv; - int i; + int i, t; int err; dev = alloc_etherdev_mqs(sizeof(struct mlx4_en_priv), @@ -3069,7 +3140,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, if (dev == NULL) return -ENOMEM; - netif_set_real_num_tx_queues(dev, prof->tx_ring_num); + netif_set_real_num_tx_queues(dev, prof->tx_ring_num[TX]); netif_set_real_num_rx_queues(dev, prof->rx_ring_num); SET_NETDEV_DEV(dev, &mdev->dev->persist->pdev->dev); @@ -3106,21 +3177,27 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, priv->ctrl_flags = cpu_to_be32(MLX4_WQE_CTRL_CQ_UPDATE | MLX4_WQE_CTRL_SOLICITED); priv->num_tx_rings_p_up = mdev->profile.num_tx_rings_p_up; - priv->tx_ring_num = prof->tx_ring_num; priv->tx_work_limit = MLX4_EN_DEFAULT_TX_WORK; netdev_rss_key_fill(priv->rss_key, sizeof(priv->rss_key)); - priv->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS, - GFP_KERNEL); - if (!priv->tx_ring) { - err = -ENOMEM; - goto out; - } - priv->tx_cq = kzalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS, - GFP_KERNEL); - if (!priv->tx_cq) { - err = -ENOMEM; - goto out; + for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) { + priv->tx_ring_num[t] = prof->tx_ring_num[t]; + if (!priv->tx_ring_num[t]) + continue; + + priv->tx_ring[t] = kzalloc(sizeof(struct mlx4_en_tx_ring *) * + MAX_TX_RINGS, GFP_KERNEL); + if (!priv->tx_ring[t]) { + err = -ENOMEM; + goto err_free_tx; + } + priv->tx_cq[t] = kzalloc(sizeof(struct mlx4_en_cq *) * + MAX_TX_RINGS, GFP_KERNEL); + if (!priv->tx_cq[t]) { + kfree(priv->tx_ring[t]); + err = -ENOMEM; + goto out; + } } priv->rx_ring_num = prof->rx_ring_num; priv->cqe_factor = (mdev->dev->caps.cqe_size == 64) ? 1 : 0; @@ -3203,7 +3280,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, else dev->netdev_ops = &mlx4_netdev_ops; dev->watchdog_timeo = MLX4_EN_WATCHDOG_TIMEOUT; - netif_set_real_num_tx_queues(dev, priv->tx_ring_num); + netif_set_real_num_tx_queues(dev, priv->tx_ring_num[TX]); netif_set_real_num_rx_queues(dev, priv->rx_ring_num); dev->ethtool_ops = &mlx4_en_ethtool_ops; @@ -3303,7 +3380,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, netif_carrier_off(dev); mlx4_en_set_default_moderation(priv); - en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num); + en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num[TX]); en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num); mlx4_en_update_loopback_state(priv->dev, priv->dev->features); @@ -3363,6 +3440,11 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, return 0; +err_free_tx: + while (t--) { + kfree(priv->tx_ring[t]); + kfree(priv->tx_cq[t]); + } out: mlx4_en_destroy_netdev(dev); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c index 59473a0..2e1ab46 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c @@ -196,8 +196,8 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->port_stats.tso_packets = 0; priv->port_stats.xmit_more = 0; - for (i = 0; i < priv->tx_ring_num; i++) { - const struct mlx4_en_tx_ring *ring = priv->tx_ring[i]; + for (i = 0; i < priv->tx_ring_num[TX]; i++) { + const struct mlx4_en_tx_ring *ring = priv->tx_ring[TX][i]; stats->tx_packets += ring->packets; stats->tx_bytes += ring->bytes; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index f2e8bed..71196f6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -788,7 +788,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud struct bpf_prog *xdp_prog; int doorbell_pending; struct sk_buff *skb; - int tx_index; int index; int nr; unsigned int length; @@ -808,7 +807,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud rcu_read_lock(); xdp_prog = rcu_dereference(ring->xdp_prog); doorbell_pending = 0; - tx_index = (priv->tx_ring_num - priv->xdp_ring_num) + cq->ring; /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx * descriptor offset can be deduced from the CQE index instead of @@ -905,7 +903,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud break; case XDP_TX: if (likely(!mlx4_en_xmit_frame(frags, dev, - length, tx_index, + length, cq->ring, &doorbell_pending))) goto consumed; goto xdp_drop; /* Drop on xmit failure */ @@ -1082,7 +1080,7 @@ consumed: out: rcu_read_unlock(); if (doorbell_pending) - mlx4_en_xmit_doorbell(priv->tx_ring[tx_index]); + mlx4_en_xmit_doorbell(priv->tx_ring[TX_XDP][cq->ring]); AVG_PERF_COUNTER(priv->pstats.rx_coal_avg, polled); mlx4_cq_set_ci(&cq->mcq); @@ -1162,7 +1160,7 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) /* bpf requires buffers to be set up as 1 packet per page. * This only works when num_frags == 1. */ - if (priv->xdp_ring_num) { + if (priv->tx_ring_num[TX_XDP]) { dma_dir = PCI_DMA_BIDIRECTIONAL; /* This will gain efficient xdp frame recycling at the expense * of more costly truesize accounting diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index e2509bb..95dc864 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -392,7 +392,8 @@ int mlx4_en_free_tx_buf(struct net_device *dev, struct mlx4_en_tx_ring *ring) cnt++; } - netdev_tx_reset_queue(ring->tx_queue); + if (ring->tx_queue) + netdev_tx_reset_queue(ring->tx_queue); if (cnt) en_dbg(DRV, priv, "Freed %d uncompleted tx descriptors\n", cnt); @@ -405,7 +406,7 @@ static bool mlx4_en_process_tx_cq(struct net_device *dev, { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_cq *mcq = &cq->mcq; - struct mlx4_en_tx_ring *ring = priv->tx_ring[cq->ring]; + struct mlx4_en_tx_ring *ring = priv->tx_ring[cq->type][cq->ring]; struct mlx4_cqe *cqe; u16 index; u16 new_index, ring_index, stamp_index; @@ -807,7 +808,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) bool bf_ok; tx_ind = skb_get_queue_mapping(skb); - ring = priv->tx_ring[tx_ind]; + ring = priv->tx_ring[TX][tx_ind]; if (!priv->port_up) goto tx_drop; @@ -1101,7 +1102,7 @@ netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_alloc *frame, BUILD_BUG_ON_MSG(ALIGN(CTRL_SIZE + DS_SIZE, TXBB_SIZE) != TXBB_SIZE, "mlx4_en_xmit_frame requires minimum size tx desc"); - ring = priv->tx_ring[tx_ind]; + ring = priv->tx_ring[TX_XDP][tx_ind]; if (!priv->port_up) goto tx_drop; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 83c914a..6e06936 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -207,8 +207,10 @@ enum { */ enum cq_type { + /* keep tx types first */ TX, TX_XDP, +#define MLX4_EN_NUM_TX_TYPES (TX_XDP + 1) RX, }; @@ -373,7 +375,7 @@ struct mlx4_en_cq { struct mlx4_en_port_profile { u32 flags; - u32 tx_ring_num; + u32 tx_ring_num[MLX4_EN_NUM_TX_TYPES]; u32 rx_ring_num; u32 tx_ring_size; u32 rx_ring_size; @@ -570,17 +572,16 @@ struct mlx4_en_priv { u32 flags; u8 num_tx_rings_p_up; u32 tx_work_limit; - u32 tx_ring_num; + u32 tx_ring_num[MLX4_EN_NUM_TX_TYPES]; u32 rx_ring_num; u32 rx_skb_size; struct mlx4_en_frag_info frag_info[MLX4_EN_MAX_RX_FRAGS]; u16 num_frags; u16 log_rx_info; - int xdp_ring_num; - struct mlx4_en_tx_ring **tx_ring; + struct mlx4_en_tx_ring **tx_ring[MLX4_EN_NUM_TX_TYPES]; struct mlx4_en_rx_ring *rx_ring[MAX_RX_RINGS]; - struct mlx4_en_cq **tx_cq; + struct mlx4_en_cq **tx_cq[MLX4_EN_NUM_TX_TYPES]; struct mlx4_en_cq *rx_cq[MAX_RX_RINGS]; struct mlx4_qp drop_qp; struct work_struct rx_mode_task; -- cgit v1.1 From 15fca2c8eb4166123a3edbd151765b00af190b75 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 2 Nov 2016 17:12:25 +0200 Subject: net/mlx4_en: Add ethtool statistics for XDP cases XDP statistics are reported in ethtool, in total and per ring, as follows: - xdp_drop: the number of packets dropped by xdp. - xdp_tx: the number of packets forwarded by xdp. - xdp_tx_full: the number of times an xdp forward failed due to a full tx xdp ring. In addition, all packets that are dropped/forwarded by XDP are no longer accounted in rx_packets/rx_bytes of the ring, so that they count traffic that is passed to the stack. Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 25 ++++++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 4 ++++ drivers/net/ethernet/mellanox/mlx4/en_port.c | 6 ++++++ drivers/net/ethernet/mellanox/mlx4/en_rx.c | 12 +++++++----- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 8 ++++---- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 7 ++++++- drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h | 10 +++++++++- 7 files changed, 60 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index e8ccb95..487a58f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -195,6 +195,10 @@ static const char main_strings[][ETH_GSTRING_LEN] = { "tx_prio_7_packets", "tx_prio_7_bytes", "tx_novlan_packets", "tx_novlan_bytes", + /* xdp statistics */ + "rx_xdp_drop", + "rx_xdp_tx", + "rx_xdp_tx_full", }; static const char mlx4_en_test_names[][ETH_GSTRING_LEN]= { @@ -340,7 +344,7 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset) case ETH_SS_STATS: return bitmap_iterator_count(&it) + (priv->tx_ring_num[TX] * 2) + - (priv->rx_ring_num * 3); + (priv->rx_ring_num * (3 + NUM_XDP_STATS)); case ETH_SS_TEST: return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2; @@ -400,6 +404,10 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, if (bitmap_iterator_test(&it)) data[index++] = ((unsigned long *)&priv->pkstats)[i]; + for (i = 0; i < NUM_XDP_STATS; i++, bitmap_iterator_inc(&it)) + if (bitmap_iterator_test(&it)) + data[index++] = ((unsigned long *)&priv->xdp_stats)[i]; + for (i = 0; i < priv->tx_ring_num[TX]; i++) { data[index++] = priv->tx_ring[TX][i]->packets; data[index++] = priv->tx_ring[TX][i]->bytes; @@ -408,6 +416,9 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, data[index++] = priv->rx_ring[i]->packets; data[index++] = priv->rx_ring[i]->bytes; data[index++] = priv->rx_ring[i]->dropped; + data[index++] = priv->rx_ring[i]->xdp_drop; + data[index++] = priv->rx_ring[i]->xdp_tx; + data[index++] = priv->rx_ring[i]->xdp_tx_full; } spin_unlock_bh(&priv->stats_lock); @@ -470,6 +481,12 @@ static void mlx4_en_get_strings(struct net_device *dev, strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[strings]); + for (i = 0; i < NUM_XDP_STATS; i++, strings++, + bitmap_iterator_inc(&it)) + if (bitmap_iterator_test(&it)) + strcpy(data + (index++) * ETH_GSTRING_LEN, + main_strings[strings]); + for (i = 0; i < priv->tx_ring_num[TX]; i++) { sprintf(data + (index++) * ETH_GSTRING_LEN, "tx%d_packets", i); @@ -483,6 +500,12 @@ static void mlx4_en_get_strings(struct net_device *dev, "rx%d_bytes", i); sprintf(data + (index++) * ETH_GSTRING_LEN, "rx%d_dropped", i); + sprintf(data + (index++) * ETH_GSTRING_LEN, + "rx%d_xdp_drop", i); + sprintf(data + (index++) * ETH_GSTRING_LEN, + "rx%d_xdp_tx", i); + sprintf(data + (index++) * ETH_GSTRING_LEN, + "rx%d_xdp_tx_full", i); } break; case ETH_SS_PRIV_FLAGS: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index edf0a99..0f6225c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -3125,6 +3125,10 @@ void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev, if (!mlx4_is_slave(dev)) bitmap_set(stats_bitmap->bitmap, last_i, NUM_PKT_STATS); + last_i += NUM_PKT_STATS; + + bitmap_set(stats_bitmap->bitmap, last_i, NUM_XDP_STATS); + last_i += NUM_XDP_STATS; } int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c index 2e1ab46..1eb4c1e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c @@ -179,6 +179,9 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->port_stats.rx_chksum_good = 0; priv->port_stats.rx_chksum_none = 0; priv->port_stats.rx_chksum_complete = 0; + priv->xdp_stats.rx_xdp_drop = 0; + priv->xdp_stats.rx_xdp_tx = 0; + priv->xdp_stats.rx_xdp_tx_full = 0; for (i = 0; i < priv->rx_ring_num; i++) { stats->rx_packets += priv->rx_ring[i]->packets; stats->rx_bytes += priv->rx_ring[i]->bytes; @@ -186,6 +189,9 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->port_stats.rx_chksum_good += priv->rx_ring[i]->csum_ok; priv->port_stats.rx_chksum_none += priv->rx_ring[i]->csum_none; priv->port_stats.rx_chksum_complete += priv->rx_ring[i]->csum_complete; + priv->xdp_stats.rx_xdp_drop += priv->rx_ring[i]->xdp_drop; + priv->xdp_stats.rx_xdp_tx += priv->rx_ring[i]->xdp_tx; + priv->xdp_stats.rx_xdp_tx_full += priv->rx_ring[i]->xdp_tx_full; } stats->tx_packets = 0; stats->tx_bytes = 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 71196f6..2cc9100 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -875,8 +875,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud */ length = be32_to_cpu(cqe->byte_cnt); length -= ring->fcs_del; - ring->bytes += length; - ring->packets++; l2_tunnel = (dev->hw_enc_features & NETIF_F_RXCSUM) && (cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_L2_TUNNEL)); @@ -902,22 +900,26 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud case XDP_PASS: break; case XDP_TX: - if (likely(!mlx4_en_xmit_frame(frags, dev, + if (likely(!mlx4_en_xmit_frame(ring, frags, dev, length, cq->ring, &doorbell_pending))) goto consumed; - goto xdp_drop; /* Drop on xmit failure */ + goto xdp_drop_no_cnt; /* Drop on xmit failure */ default: bpf_warn_invalid_xdp_action(act); case XDP_ABORTED: case XDP_DROP: -xdp_drop: + ring->xdp_drop++; +xdp_drop_no_cnt: if (likely(mlx4_en_rx_recycle(ring, frags))) goto consumed; goto next; } } + ring->bytes += length; + ring->packets++; + if (likely(dev->features & NETIF_F_RXCSUM)) { if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_TCP | MLX4_CQE_STATUS_UDP)) { diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 95dc864..5de3cbe 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -1079,7 +1079,8 @@ tx_drop: return NETDEV_TX_OK; } -netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_alloc *frame, +netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_ring *rx_ring, + struct mlx4_en_rx_alloc *frame, struct net_device *dev, unsigned int length, int tx_ind, int *doorbell_pending) { @@ -1154,8 +1155,7 @@ netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_alloc *frame, ((ring->prod & ring->size) ? cpu_to_be32(MLX4_EN_BIT_DESC_OWN) : 0); - ring->packets++; - ring->bytes += tx_info->nr_bytes; + rx_ring->xdp_tx++; AVG_PERF_COUNTER(priv->pstats.tx_pktsz_avg, length); ring->prod += nr_txbb; @@ -1179,7 +1179,7 @@ netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_alloc *frame, return NETDEV_TX_OK; tx_drop_count: - ring->tx_dropped++; + rx_ring->xdp_tx_full++; tx_drop: return NETDEV_TX_BUSY; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 6e06936..eff2165 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -350,6 +350,9 @@ struct mlx4_en_rx_ring { unsigned long csum_ok; unsigned long csum_none; unsigned long csum_complete; + unsigned long xdp_drop; + unsigned long xdp_tx; + unsigned long xdp_tx_full; unsigned long dropped; int hwtstamp_rx_filter; cpumask_var_t affinity_mask; @@ -599,6 +602,7 @@ struct mlx4_en_priv { struct mlx4_en_flow_stats_rx rx_flowstats; struct mlx4_en_flow_stats_tx tx_flowstats; struct mlx4_en_port_stats port_stats; + struct mlx4_en_xdp_stats xdp_stats; struct mlx4_en_stats_bitmap stats_bitmap; struct list_head mc_list; struct list_head curr_list; @@ -687,7 +691,8 @@ void mlx4_en_tx_irq(struct mlx4_cq *mcq); u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback); netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev); -netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_alloc *frame, +netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_ring *rx_ring, + struct mlx4_en_rx_alloc *frame, struct net_device *dev, unsigned int length, int tx_ind, int *doorbell_pending); void mlx4_en_xmit_doorbell(struct mlx4_en_tx_ring *ring); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h index 7fd466c..48641cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h @@ -55,6 +55,13 @@ struct mlx4_en_perf_stats { #define NUM_PERF_COUNTERS 6 }; +struct mlx4_en_xdp_stats { + unsigned long rx_xdp_drop; + unsigned long rx_xdp_tx; + unsigned long rx_xdp_tx_full; +#define NUM_XDP_STATS 3 +}; + #define NUM_MAIN_STATS 21 #define MLX4_NUM_PRIORITIES 8 @@ -107,7 +114,8 @@ enum { }; #define NUM_ALL_STATS (NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + \ - NUM_FLOW_STATS + NUM_PERF_STATS + NUM_PF_STATS) + NUM_FLOW_STATS + NUM_PERF_STATS + NUM_PF_STATS + \ + NUM_XDP_STATS) #define MLX4_FIND_NETDEV_STAT(n) (offsetof(struct net_device_stats, n) / \ sizeof(((struct net_device_stats *)0)->n)) -- cgit v1.1 From bc8ee596afe8f35b379f87575c46d800dd8e7e68 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 1 Nov 2016 16:32:25 +0100 Subject: net: mii: add generic function to support ksetting support The old ethtool api (get_setting and set_setting) has generic mii functions mii_ethtool_sset and mii_ethtool_gset. To support the new ethtool api ({get|set}_link_ksettings), we add two generics mii function mii_ethtool_{get|set}_link_ksettings_get. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/mii.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mii.h | 4 ++ 2 files changed, 199 insertions(+) diff --git a/drivers/net/mii.c b/drivers/net/mii.c index 993570b..0443546 100644 --- a/drivers/net/mii.c +++ b/drivers/net/mii.c @@ -135,6 +135,101 @@ int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) } /** + * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd + * @mii: MII interface + * @cmd: requested ethtool_link_ksettings + * + * The @cmd parameter is expected to have been cleared before calling + * mii_ethtool_get_link_ksettings(). + * + * Returns 0 for success, negative on error. + */ +int mii_ethtool_get_link_ksettings(struct mii_if_info *mii, + struct ethtool_link_ksettings *cmd) +{ + struct net_device *dev = mii->dev; + u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0; + u32 nego, supported, advertising, lp_advertising; + + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); + if (mii->supports_gmii) + supported |= SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full; + + /* only supports twisted-pair */ + cmd->base.port = PORT_MII; + + /* this isn't fully supported at higher layers */ + cmd->base.phy_address = mii->phy_id; + cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22; + + advertising = ADVERTISED_TP | ADVERTISED_MII; + + bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); + bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR); + if (mii->supports_gmii) { + ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000); + stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000); + } + if (bmcr & BMCR_ANENABLE) { + advertising |= ADVERTISED_Autoneg; + cmd->base.autoneg = AUTONEG_ENABLE; + + advertising |= mii_get_an(mii, MII_ADVERTISE); + if (mii->supports_gmii) + advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000); + + if (bmsr & BMSR_ANEGCOMPLETE) { + lp_advertising = mii_get_an(mii, MII_LPA); + lp_advertising |= + mii_stat1000_to_ethtool_lpa_t(stat1000); + } else { + lp_advertising = 0; + } + + nego = advertising & lp_advertising; + + if (nego & (ADVERTISED_1000baseT_Full | + ADVERTISED_1000baseT_Half)) { + cmd->base.speed = SPEED_1000; + cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full); + } else if (nego & (ADVERTISED_100baseT_Full | + ADVERTISED_100baseT_Half)) { + cmd->base.speed = SPEED_100; + cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full); + } else { + cmd->base.speed = SPEED_10; + cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full); + } + } else { + cmd->base.autoneg = AUTONEG_DISABLE; + + cmd->base.speed = ((bmcr & BMCR_SPEED1000 && + (bmcr & BMCR_SPEED100) == 0) ? + SPEED_1000 : + ((bmcr & BMCR_SPEED100) ? + SPEED_100 : SPEED_10)); + cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ? + DUPLEX_FULL : DUPLEX_HALF; + } + + mii->full_duplex = cmd->base.duplex; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising, + lp_advertising); + + /* ignore maxtxpkt, maxrxpkt for now */ + + return 0; +} + +/** * mii_ethtool_sset - set settings that are specified in @ecmd * @mii: MII interface * @ecmd: requested ethtool_cmd @@ -227,6 +322,104 @@ int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) } /** + * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd + * @mii: MII interfaces + * @cmd: requested ethtool_link_ksettings + * + * Returns 0 for success, negative on error. + */ +int mii_ethtool_set_link_ksettings(struct mii_if_info *mii, + const struct ethtool_link_ksettings *cmd) +{ + struct net_device *dev = mii->dev; + u32 speed = cmd->base.speed; + + if (speed != SPEED_10 && + speed != SPEED_100 && + speed != SPEED_1000) + return -EINVAL; + if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL) + return -EINVAL; + if (cmd->base.port != PORT_MII) + return -EINVAL; + if (cmd->base.phy_address != mii->phy_id) + return -EINVAL; + if (cmd->base.autoneg != AUTONEG_DISABLE && + cmd->base.autoneg != AUTONEG_ENABLE) + return -EINVAL; + if ((speed == SPEED_1000) && (!mii->supports_gmii)) + return -EINVAL; + + /* ignore supported, maxtxpkt, maxrxpkt */ + + if (cmd->base.autoneg == AUTONEG_ENABLE) { + u32 bmcr, advert, tmp; + u32 advert2 = 0, tmp2 = 0; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32( + &advertising, cmd->link_modes.advertising); + + if ((advertising & (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full)) == 0) + return -EINVAL; + + /* advertise only what has been requested */ + advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE); + tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4); + if (mii->supports_gmii) { + advert2 = mii->mdio_read(dev, mii->phy_id, + MII_CTRL1000); + tmp2 = advert2 & + ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + } + tmp |= ethtool_adv_to_mii_adv_t(advertising); + + if (mii->supports_gmii) + tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising); + if (advert != tmp) { + mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp); + mii->advertising = tmp; + } + if ((mii->supports_gmii) && (advert2 != tmp2)) + mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2); + + /* turn on autonegotiation, and force a renegotiate */ + bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); + bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); + mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr); + + mii->force_media = 0; + } else { + u32 bmcr, tmp; + + /* turn off auto negotiation, set speed and duplexity */ + bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); + tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 | + BMCR_SPEED1000 | BMCR_FULLDPLX); + if (speed == SPEED_1000) + tmp |= BMCR_SPEED1000; + else if (speed == SPEED_100) + tmp |= BMCR_SPEED100; + if (cmd->base.duplex == DUPLEX_FULL) { + tmp |= BMCR_FULLDPLX; + mii->full_duplex = 1; + } else { + mii->full_duplex = 0; + } + if (bmcr != tmp) + mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp); + + mii->force_media = 1; + } + return 0; +} + +/** * mii_check_gmii_support - check if the MII supports Gb interfaces * @mii: the MII interface */ @@ -466,7 +659,9 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(mii_link_ok); EXPORT_SYMBOL(mii_nway_restart); EXPORT_SYMBOL(mii_ethtool_gset); +EXPORT_SYMBOL(mii_ethtool_get_link_ksettings); EXPORT_SYMBOL(mii_ethtool_sset); +EXPORT_SYMBOL(mii_ethtool_set_link_ksettings); EXPORT_SYMBOL(mii_check_link); EXPORT_SYMBOL(mii_check_media); EXPORT_SYMBOL(mii_check_gmii_support); diff --git a/include/linux/mii.h b/include/linux/mii.h index 47492c96..1629a0c 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -31,7 +31,11 @@ struct mii_if_info { extern int mii_link_ok (struct mii_if_info *mii); extern int mii_nway_restart (struct mii_if_info *mii); extern int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd); +extern int mii_ethtool_get_link_ksettings( + struct mii_if_info *mii, struct ethtool_link_ksettings *cmd); extern int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd); +extern int mii_ethtool_set_link_ksettings( + struct mii_if_info *mii, const struct ethtool_link_ksettings *cmd); extern int mii_check_gmii_support(struct mii_if_info *mii); extern void mii_check_link (struct mii_if_info *mii); extern unsigned int mii_check_media (struct mii_if_info *mii, -- cgit v1.1 From e19b7883ef8b447a732a603e0bbb6232941cf08a Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 1 Nov 2016 16:32:26 +0100 Subject: net: 3c59x: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c59x.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 3ecf613..b3560a3 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -2907,18 +2907,20 @@ static int vortex_nway_reset(struct net_device *dev) return mii_nway_restart(&vp->mii); } -static int vortex_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int vortex_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct vortex_private *vp = netdev_priv(dev); - return mii_ethtool_gset(&vp->mii, cmd); + return mii_ethtool_get_link_ksettings(&vp->mii, cmd); } -static int vortex_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int vortex_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct vortex_private *vp = netdev_priv(dev); - return mii_ethtool_sset(&vp->mii, cmd); + return mii_ethtool_set_link_ksettings(&vp->mii, cmd); } static u32 vortex_get_msglevel(struct net_device *dev) @@ -3031,13 +3033,13 @@ static const struct ethtool_ops vortex_ethtool_ops = { .set_msglevel = vortex_set_msglevel, .get_ethtool_stats = vortex_get_ethtool_stats, .get_sset_count = vortex_get_sset_count, - .get_settings = vortex_get_settings, - .set_settings = vortex_set_settings, .get_link = ethtool_op_get_link, .nway_reset = vortex_nway_reset, .get_wol = vortex_get_wol, .set_wol = vortex_set_wol, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = vortex_get_link_ksettings, + .set_link_ksettings = vortex_set_link_ksettings, }; #ifdef CONFIG_PCI -- cgit v1.1 From b646cf299e14338c7348a68acc006d1673f1ba0a Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 1 Nov 2016 16:32:27 +0100 Subject: net: 3c509: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c509.c | 55 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 9f9a5f4..a753378 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -1040,67 +1040,68 @@ el3_link_ok(struct net_device *dev) } static int -el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) +el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd) { u16 tmp; int ioaddr = dev->base_addr; + u32 supported; EL3WINDOW(0); /* obtain current transceiver via WN4_MEDIA? */ tmp = inw(ioaddr + WN0_ADDR_CONF); - ecmd->transceiver = XCVR_INTERNAL; switch (tmp >> 14) { case 0: - ecmd->port = PORT_TP; + cmd->base.port = PORT_TP; break; case 1: - ecmd->port = PORT_AUI; - ecmd->transceiver = XCVR_EXTERNAL; + cmd->base.port = PORT_AUI; break; case 3: - ecmd->port = PORT_BNC; + cmd->base.port = PORT_BNC; default: break; } - ecmd->duplex = DUPLEX_HALF; - ecmd->supported = 0; + cmd->base.duplex = DUPLEX_HALF; + supported = 0; tmp = inw(ioaddr + WN0_CONF_CTRL); if (tmp & (1<<13)) - ecmd->supported |= SUPPORTED_AUI; + supported |= SUPPORTED_AUI; if (tmp & (1<<12)) - ecmd->supported |= SUPPORTED_BNC; + supported |= SUPPORTED_BNC; if (tmp & (1<<9)) { - ecmd->supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half | + supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; /* hmm... */ EL3WINDOW(4); tmp = inw(ioaddr + WN4_NETDIAG); if (tmp & FD_ENABLE) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; } - ethtool_cmd_speed_set(ecmd, SPEED_10); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + cmd->base.speed = SPEED_10; EL3WINDOW(1); return 0; } static int -el3_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) +el3_netdev_set_ecmd(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { u16 tmp; int ioaddr = dev->base_addr; - if (ecmd->speed != SPEED_10) + if (cmd->base.speed != SPEED_10) return -EINVAL; - if ((ecmd->duplex != DUPLEX_HALF) && (ecmd->duplex != DUPLEX_FULL)) - return -EINVAL; - if ((ecmd->transceiver != XCVR_INTERNAL) && (ecmd->transceiver != XCVR_EXTERNAL)) + if ((cmd->base.duplex != DUPLEX_HALF) && + (cmd->base.duplex != DUPLEX_FULL)) return -EINVAL; /* change XCVR type */ EL3WINDOW(0); tmp = inw(ioaddr + WN0_ADDR_CONF); - switch (ecmd->port) { + switch (cmd->base.port) { case PORT_TP: tmp &= ~(3<<14); dev->if_port = 0; @@ -1130,7 +1131,7 @@ el3_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) EL3WINDOW(4); tmp = inw(ioaddr + WN4_NETDIAG); - if (ecmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) tmp |= FD_ENABLE; else tmp &= ~FD_ENABLE; @@ -1146,24 +1147,26 @@ static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } -static int el3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int el3_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct el3_private *lp = netdev_priv(dev); int ret; spin_lock_irq(&lp->lock); - ret = el3_netdev_get_ecmd(dev, ecmd); + ret = el3_netdev_get_ecmd(dev, cmd); spin_unlock_irq(&lp->lock); return ret; } -static int el3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int el3_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct el3_private *lp = netdev_priv(dev); int ret; spin_lock_irq(&lp->lock); - ret = el3_netdev_set_ecmd(dev, ecmd); + ret = el3_netdev_set_ecmd(dev, cmd); spin_unlock_irq(&lp->lock); return ret; } @@ -1191,11 +1194,11 @@ static void el3_set_msglevel(struct net_device *dev, u32 v) static const struct ethtool_ops ethtool_ops = { .get_drvinfo = el3_get_drvinfo, - .get_settings = el3_get_settings, - .set_settings = el3_set_settings, .get_link = el3_get_link, .get_msglevel = el3_get_msglevel, .set_msglevel = el3_set_msglevel, + .get_link_ksettings = el3_get_link_ksettings, + .set_link_ksettings = el3_set_link_ksettings, }; static void -- cgit v1.1 From 2331ccc5b3231c35b758fe274f45fbed38467f94 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 1 Nov 2016 10:53:42 -0700 Subject: tcp: enhance tcp collapsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As Ilya Lesokhin suggested, we can collapse two skbs at retransmit time even if the skb at the right has fragments. We simply have to use more generic skb_copy_bits() instead of skb_copy_from_linear_data() in tcp_collapse_retrans() Also need to guard this skb_copy_bits() in case there is nothing to copy, otherwise skb_put() could panic if left skb has frags. Tested: Used following packetdrill test // Establish a connection. 0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +0 < S 0:0(0) win 32792 +0 > S. 0:0(0) ack 1 +.100 < . 1:1(0) ack 1 win 257 +0 accept(3, ..., ...) = 4 +0 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 +0 write(4, ..., 200) = 200 +0 > P. 1:201(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 201:401(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 401:601(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 601:801(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 801:1001(200) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1001:1101(100) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1101:1201(100) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1201:1301(100) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1301:1401(100) ack 1 +.100 < . 1:1(0) ack 1 win 257 // Check that TCP collapse works : +0 > P. 1:1001(1000) ack 1 Reported-by: Ilya Lesokhin Signed-off-by: Eric Dumazet Cc: Neal Cardwell Cc: Yuchung Cheng Cc: Ilpo Järvinen Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 896e9df..f57b5aa 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2529,8 +2529,9 @@ static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) tcp_unlink_write_queue(next_skb, sk); - skb_copy_from_linear_data(next_skb, skb_put(skb, next_skb_size), - next_skb_size); + if (next_skb_size) + skb_copy_bits(next_skb, 0, skb_put(skb, next_skb_size), + next_skb_size); if (next_skb->ip_summed == CHECKSUM_PARTIAL) skb->ip_summed = CHECKSUM_PARTIAL; @@ -2567,14 +2568,11 @@ static bool tcp_can_collapse(const struct sock *sk, const struct sk_buff *skb) { if (tcp_skb_pcount(skb) > 1) return false; - /* TODO: SACK collapsing could be used to remove this condition */ - if (skb_shinfo(skb)->nr_frags != 0) - return false; if (skb_cloned(skb)) return false; if (skb == tcp_send_head(sk)) return false; - /* Some heurestics for collapsing over SACK'd could be invented */ + /* Some heuristics for collapsing over SACK'd could be invented */ if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) return false; -- cgit v1.1 From cc89c323a30e73114a21fc125d2f841658a09987 Mon Sep 17 00:00:00 2001 From: Woojung Huh Date: Tue, 1 Nov 2016 20:02:00 +0000 Subject: lan78xx: Use irq_domain for phy interrupt from USB Int. EP To utilize phylib with interrupt fully than handling some of phy stuff in the MAC driver, create irq_domain for USB interrupt EP of phy interrupt and pass the irq number to phy_connect_direct() instead of PHY_IGNORE_INTERRUPT. Idea comes from drivers/gpio/gpio-dl2.c Signed-off-by: Woojung Huh Signed-off-by: David S. Miller --- drivers/net/usb/lan78xx.c | 215 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 189 insertions(+), 26 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index c4e748e..bcd9010 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -30,13 +30,17 @@ #include #include #include +#include +#include +#include +#include #include #include "lan78xx.h" #define DRIVER_AUTHOR "WOOJUNG HUH " #define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices" #define DRIVER_NAME "lan78xx" -#define DRIVER_VERSION "1.0.4" +#define DRIVER_VERSION "1.0.5" #define TX_TIMEOUT_JIFFIES (5 * HZ) #define THROTTLE_JIFFIES (HZ / 8) @@ -89,6 +93,38 @@ /* statistic update interval (mSec) */ #define STAT_UPDATE_TIMER (1 * 1000) +/* defines interrupts from interrupt EP */ +#define MAX_INT_EP (32) +#define INT_EP_INTEP (31) +#define INT_EP_OTP_WR_DONE (28) +#define INT_EP_EEE_TX_LPI_START (26) +#define INT_EP_EEE_TX_LPI_STOP (25) +#define INT_EP_EEE_RX_LPI (24) +#define INT_EP_MAC_RESET_TIMEOUT (23) +#define INT_EP_RDFO (22) +#define INT_EP_TXE (21) +#define INT_EP_USB_STATUS (20) +#define INT_EP_TX_DIS (19) +#define INT_EP_RX_DIS (18) +#define INT_EP_PHY (17) +#define INT_EP_DP (16) +#define INT_EP_MAC_ERR (15) +#define INT_EP_TDFU (14) +#define INT_EP_TDFO (13) +#define INT_EP_UTX (12) +#define INT_EP_GPIO_11 (11) +#define INT_EP_GPIO_10 (10) +#define INT_EP_GPIO_9 (9) +#define INT_EP_GPIO_8 (8) +#define INT_EP_GPIO_7 (7) +#define INT_EP_GPIO_6 (6) +#define INT_EP_GPIO_5 (5) +#define INT_EP_GPIO_4 (4) +#define INT_EP_GPIO_3 (3) +#define INT_EP_GPIO_2 (2) +#define INT_EP_GPIO_1 (1) +#define INT_EP_GPIO_0 (0) + static const char lan78xx_gstrings[][ETH_GSTRING_LEN] = { "RX FCS Errors", "RX Alignment Errors", @@ -296,6 +332,15 @@ struct statstage { struct lan78xx_statstage64 curr_stat; }; +struct irq_domain_data { + struct irq_domain *irqdomain; + unsigned int phyirq; + struct irq_chip *irqchip; + irq_flow_handler_t irq_handler; + u32 irqenable; + struct mutex irq_lock; /* for irq bus access */ +}; + struct lan78xx_net { struct net_device *net; struct usb_device *udev; @@ -351,6 +396,8 @@ struct lan78xx_net { int delta; struct statstage stats; + + struct irq_domain_data domain_data; }; /* use ethtool to change the level for any given device */ @@ -1096,11 +1143,6 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) int ladv, radv, ret; u32 buf; - /* clear PHY interrupt status */ - ret = phy_read(phydev, LAN88XX_INT_STS); - if (unlikely(ret < 0)) - return -EIO; - /* clear LAN78xx interrupt status */ ret = lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_); if (unlikely(ret < 0)) @@ -1120,16 +1162,12 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) if (unlikely(ret < 0)) return -EIO; - phy_mac_interrupt(phydev, 0); - del_timer(&dev->stat_monitor); } else if (phydev->link && !dev->link_on) { dev->link_on = true; phy_ethtool_ksettings_get(phydev, &ecmd); - ret = phy_read(phydev, LAN88XX_INT_STS); - if (dev->udev->speed == USB_SPEED_SUPER) { if (ecmd.base.speed == 1000) { /* disable U2 */ @@ -1163,7 +1201,6 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv, radv); - phy_mac_interrupt(phydev, 1); if (!timer_pending(&dev->stat_monitor)) { dev->delta = 1; @@ -1202,7 +1239,10 @@ static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb) if (intdata & INT_ENP_PHY_INT) { netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata); - lan78xx_defer_kevent(dev, EVENT_LINK_RESET); + lan78xx_defer_kevent(dev, EVENT_LINK_RESET); + + if (dev->domain_data.phyirq > 0) + generic_handle_irq(dev->domain_data.phyirq); } else netdev_warn(dev->net, "unexpected interrupt: 0x%08x\n", intdata); @@ -1844,6 +1884,127 @@ static void lan78xx_link_status_change(struct net_device *net) } } +static int irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct irq_domain_data *data = d->host_data; + + irq_set_chip_data(irq, data); + irq_set_chip_and_handler(irq, data->irqchip, data->irq_handler); + irq_set_noprobe(irq); + + return 0; +} + +static void irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops chip_domain_ops = { + .map = irq_map, + .unmap = irq_unmap, +}; + +static void lan78xx_irq_mask(struct irq_data *irqd) +{ + struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); + + data->irqenable &= ~BIT(irqd_to_hwirq(irqd)); +} + +static void lan78xx_irq_unmask(struct irq_data *irqd) +{ + struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); + + data->irqenable |= BIT(irqd_to_hwirq(irqd)); +} + +static void lan78xx_irq_bus_lock(struct irq_data *irqd) +{ + struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); + + mutex_lock(&data->irq_lock); +} + +static void lan78xx_irq_bus_sync_unlock(struct irq_data *irqd) +{ + struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); + struct lan78xx_net *dev = + container_of(data, struct lan78xx_net, domain_data); + u32 buf; + int ret; + + /* call register access here because irq_bus_lock & irq_bus_sync_unlock + * are only two callbacks executed in non-atomic contex. + */ + ret = lan78xx_read_reg(dev, INT_EP_CTL, &buf); + if (buf != data->irqenable) + ret = lan78xx_write_reg(dev, INT_EP_CTL, data->irqenable); + + mutex_unlock(&data->irq_lock); +} + +static struct irq_chip lan78xx_irqchip = { + .name = "lan78xx-irqs", + .irq_mask = lan78xx_irq_mask, + .irq_unmask = lan78xx_irq_unmask, + .irq_bus_lock = lan78xx_irq_bus_lock, + .irq_bus_sync_unlock = lan78xx_irq_bus_sync_unlock, +}; + +static int lan78xx_setup_irq_domain(struct lan78xx_net *dev) +{ + struct device_node *of_node; + struct irq_domain *irqdomain; + unsigned int irqmap = 0; + u32 buf; + int ret = 0; + + of_node = dev->udev->dev.parent->of_node; + + mutex_init(&dev->domain_data.irq_lock); + + lan78xx_read_reg(dev, INT_EP_CTL, &buf); + dev->domain_data.irqenable = buf; + + dev->domain_data.irqchip = &lan78xx_irqchip; + dev->domain_data.irq_handler = handle_simple_irq; + + irqdomain = irq_domain_add_simple(of_node, MAX_INT_EP, 0, + &chip_domain_ops, &dev->domain_data); + if (irqdomain) { + /* create mapping for PHY interrupt */ + irqmap = irq_create_mapping(irqdomain, INT_EP_PHY); + if (!irqmap) { + irq_domain_remove(irqdomain); + + irqdomain = NULL; + ret = -EINVAL; + } + } else { + ret = -EINVAL; + } + + dev->domain_data.irqdomain = irqdomain; + dev->domain_data.phyirq = irqmap; + + return ret; +} + +static void lan78xx_remove_irq_domain(struct lan78xx_net *dev) +{ + if (dev->domain_data.phyirq > 0) { + irq_dispose_mapping(dev->domain_data.phyirq); + + if (dev->domain_data.irqdomain) + irq_domain_remove(dev->domain_data.irqdomain); + } + dev->domain_data.phyirq = 0; + dev->domain_data.irqdomain = NULL; +} + static int lan78xx_phy_init(struct lan78xx_net *dev) { int ret; @@ -1856,15 +2017,12 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) return -EIO; } - /* Enable PHY interrupts. - * We handle our own interrupt - */ - ret = phy_read(phydev, LAN88XX_INT_STS); - ret = phy_write(phydev, LAN88XX_INT_MASK, - LAN88XX_INT_MASK_MDINTPIN_EN_ | - LAN88XX_INT_MASK_LINK_CHANGE_); - - phydev->irq = PHY_IGNORE_INTERRUPT; + /* if phyirq is not set, use polling mode in phylib */ + if (dev->domain_data.phyirq > 0) + phydev->irq = dev->domain_data.phyirq; + else + phydev->irq = 0; + netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq); ret = phy_connect_direct(dev->net, phydev, lan78xx_link_status_change, @@ -2255,11 +2413,6 @@ static int lan78xx_reset(struct lan78xx_net *dev) buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; ret = lan78xx_write_reg(dev, MAC_CR, buf); - /* enable PHY interrupts */ - ret = lan78xx_read_reg(dev, INT_EP_CTL, &buf); - buf |= INT_ENP_PHY_INT; - ret = lan78xx_write_reg(dev, INT_EP_CTL, buf); - ret = lan78xx_read_reg(dev, MAC_TX, &buf); buf |= MAC_TX_TXEN_; ret = lan78xx_write_reg(dev, MAC_TX, buf); @@ -2668,6 +2821,14 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) dev->net->hw_features = dev->net->features; + ret = lan78xx_setup_irq_domain(dev); + if (ret < 0) { + netdev_warn(dev->net, + "lan78xx_setup_irq_domain() failed : %d", ret); + kfree(pdata); + return ret; + } + /* Init all registers */ ret = lan78xx_reset(dev); @@ -2684,6 +2845,8 @@ static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf) { struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); + lan78xx_remove_irq_domain(dev); + lan78xx_remove_mdio(dev); if (pdata) { -- cgit v1.1 From 3de864f8c9e488fa57a28f5deb45bb808810ee92 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Tue, 1 Nov 2016 23:05:00 +0300 Subject: net: ip, diag -- Adjust raw_abort to use unlocked __udp_disconnect While being preparing patches for killing raw sockets via diag netlink interface I noticed that my runs are stuck: | [root@pcs7 ~]# cat /proc/`pidof ss`/stack | [] __lock_sock+0x80/0xc4 | [] lock_sock_nested+0x47/0x95 | [] udp_disconnect+0x19/0x33 | [] raw_abort+0x33/0x42 | [] sock_diag_destroy+0x4d/0x52 which has not been the case before. I narrowed it down to the commit | commit 286c72deabaa240b7eebbd99496ed3324d69f3c0 | Author: Eric Dumazet | Date: Thu Oct 20 09:39:40 2016 -0700 | | udp: must lock the socket in udp_disconnect() where we start locking the socket for different reason. So the raw_abort escaped the renaming and we have to fix this typo using __udp_disconnect instead. Fixes: 286c72deabaa ("udp: must lock the socket in udp_disconnect()") CC: David S. Miller CC: Eric Dumazet CC: David Ahern CC: Alexey Kuznetsov CC: James Morris CC: Hideaki YOSHIFUJI CC: Patrick McHardy CC: Andrey Vagin CC: Stephen Hemminger Signed-off-by: Cyrill Gorcunov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index d78d738..6a0bd68 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -920,7 +920,7 @@ int raw_abort(struct sock *sk, int err) sk->sk_err = err; sk->sk_error_report(sk); - udp_disconnect(sk, 0); + __udp_disconnect(sk, 0); release_sock(sk); -- cgit v1.1 From 1913540a1328d70edf8597f6c2248575d9d2ad62 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Nov 2016 14:55:25 -0700 Subject: ila: Fix crash caused by rhashtable changes commit ca26893f05e86 ("rhashtable: Add rhlist interface") added a field to rhashtable_iter so that length became 56 bytes and would exceed the size of args in netlink_callback (which is 48 bytes). The netlink diag dump function already has been allocating a iter structure and storing the pointed to that in the args of netlink_callback. ila_xlat also uses rhahstable_iter but is still putting that directly in the arg block. Now since rhashtable_iter size is increased we are overwriting beyond the structure. The next field happens to be cb_mutex pointer in netlink_sock and hence the crash. Fix is to alloc the rhashtable_iter and save it as pointer in arg. Tested: modprobe ila ./ip ila add loc 3333:0:0:0 loc_match 2222:0:0:1, ./ip ila list # NO crash now Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- net/ipv6/ila/ila_xlat.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 628ae6d..af8f52e 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -474,7 +474,15 @@ static int ila_nl_dump_start(struct netlink_callback *cb) { struct net *net = sock_net(cb->skb->sk); struct ila_net *ilan = net_generic(net, ila_net_id); - struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args; + struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args[0]; + + if (!iter) { + iter = kmalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + cb->args[0] = (long)iter; + } return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter, GFP_KERNEL); @@ -482,16 +490,18 @@ static int ila_nl_dump_start(struct netlink_callback *cb) static int ila_nl_dump_done(struct netlink_callback *cb) { - struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args; + struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args[0]; rhashtable_walk_exit(&iter->rhiter); + kfree(iter); + return 0; } static int ila_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) { - struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args; + struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args[0]; struct rhashtable_iter *rhiter = &iter->rhiter; struct ila_map *ila; int ret; -- cgit v1.1 From f7a5537cd2a56b5fdd043f89d37d500b6c698422 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 2 Nov 2016 00:11:51 +0100 Subject: net: 3com: typhoon: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Reviewed-by: David Dillow Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/typhoon.c | 60 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 506b507..dbdf06f 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -996,28 +996,30 @@ typhoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) } static int -typhoon_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +typhoon_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct typhoon *tp = netdev_priv(dev); + u32 supported, advertising; - cmd->supported = SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + supported = SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg; switch (tp->xcvr_select) { case TYPHOON_XCVR_10HALF: - cmd->advertising = ADVERTISED_10baseT_Half; + advertising = ADVERTISED_10baseT_Half; break; case TYPHOON_XCVR_10FULL: - cmd->advertising = ADVERTISED_10baseT_Full; + advertising = ADVERTISED_10baseT_Full; break; case TYPHOON_XCVR_100HALF: - cmd->advertising = ADVERTISED_100baseT_Half; + advertising = ADVERTISED_100baseT_Half; break; case TYPHOON_XCVR_100FULL: - cmd->advertising = ADVERTISED_100baseT_Full; + advertising = ADVERTISED_100baseT_Full; break; case TYPHOON_XCVR_AUTONEG: - cmd->advertising = ADVERTISED_10baseT_Half | + advertising = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | @@ -1026,54 +1028,52 @@ typhoon_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) } if(tp->capabilities & TYPHOON_FIBER) { - cmd->supported |= SUPPORTED_FIBRE; - cmd->advertising |= ADVERTISED_FIBRE; - cmd->port = PORT_FIBRE; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_FIBRE; } else { - cmd->supported |= SUPPORTED_10baseT_Half | + supported |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_TP; - cmd->advertising |= ADVERTISED_TP; - cmd->port = PORT_TP; + advertising |= ADVERTISED_TP; + cmd->base.port = PORT_TP; } /* need to get stats to make these link speed/duplex valid */ typhoon_do_get_stats(tp); - ethtool_cmd_speed_set(cmd, tp->speed); - cmd->duplex = tp->duplex; - cmd->phy_address = 0; - cmd->transceiver = XCVR_INTERNAL; + cmd->base.speed = tp->speed; + cmd->base.duplex = tp->duplex; + cmd->base.phy_address = 0; if(tp->xcvr_select == TYPHOON_XCVR_AUTONEG) - cmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; else - cmd->autoneg = AUTONEG_DISABLE; - cmd->maxtxpkt = 1; - cmd->maxrxpkt = 1; + cmd->base.autoneg = AUTONEG_DISABLE; return 0; } static int -typhoon_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +typhoon_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct typhoon *tp = netdev_priv(dev); - u32 speed = ethtool_cmd_speed(cmd); + u32 speed = cmd->base.speed; struct cmd_desc xp_cmd; __le16 xcvr; int err; err = -EINVAL; - if (cmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { xcvr = TYPHOON_XCVR_AUTONEG; } else { - if (cmd->duplex == DUPLEX_HALF) { + if (cmd->base.duplex == DUPLEX_HALF) { if (speed == SPEED_10) xcvr = TYPHOON_XCVR_10HALF; else if (speed == SPEED_100) xcvr = TYPHOON_XCVR_100HALF; else goto out; - } else if (cmd->duplex == DUPLEX_FULL) { + } else if (cmd->base.duplex == DUPLEX_FULL) { if (speed == SPEED_10) xcvr = TYPHOON_XCVR_10FULL; else if (speed == SPEED_100) @@ -1091,12 +1091,12 @@ typhoon_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) goto out; tp->xcvr_select = xcvr; - if(cmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { tp->speed = 0xff; /* invalid */ tp->duplex = 0xff; /* invalid */ } else { tp->speed = speed; - tp->duplex = cmd->duplex; + tp->duplex = cmd->base.duplex; } out: @@ -1145,13 +1145,13 @@ typhoon_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) } static const struct ethtool_ops typhoon_ethtool_ops = { - .get_settings = typhoon_get_settings, - .set_settings = typhoon_set_settings, .get_drvinfo = typhoon_get_drvinfo, .get_wol = typhoon_get_wol, .set_wol = typhoon_set_wol, .get_link = ethtool_op_get_link, .get_ringparam = typhoon_get_ringparam, + .get_link_ksettings = typhoon_get_link_ksettings, + .set_link_ksettings = typhoon_set_link_ksettings, }; static int -- cgit v1.1 From 17197236d62c44da127be461c63ac5cc2cce1e53 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Tue, 1 Nov 2016 17:58:50 -0700 Subject: enic: set skb->hash type properly Driver sets the skb l4/l3 hash based on NIC_CFG_RSS_HASH_TYPE_*, which is bit mask. This is wrong. Hw actually provides us enum. Use CQ_ENET_RQ_DESC_RSS_TYPE_* to set l3 and l4 hash type. Fixes: bf751ba802fe ("driver/net: enic: record q_number and rss_hash for skb") Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller --- drivers/net/ethernet/cisco/enic/enic.h | 2 +- drivers/net/ethernet/cisco/enic/enic_main.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 130f910..9023c85 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -33,7 +33,7 @@ #define DRV_NAME "enic" #define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver" -#define DRV_VERSION "2.3.0.20" +#define DRV_VERSION "2.3.0.31" #define DRV_COPYRIGHT "Copyright 2008-2013 Cisco Systems, Inc" #define ENIC_BARS_MAX 6 diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index f514faf..cdd7a1a 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1166,12 +1166,18 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, skb->protocol = eth_type_trans(skb, netdev); skb_record_rx_queue(skb, q_number); if (netdev->features & NETIF_F_RXHASH) { - skb_set_hash(skb, rss_hash, - (rss_type & - (NIC_CFG_RSS_HASH_TYPE_TCP_IPV6_EX | - NIC_CFG_RSS_HASH_TYPE_TCP_IPV6 | - NIC_CFG_RSS_HASH_TYPE_TCP_IPV4)) ? - PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); + switch (rss_type) { + case CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv4: + case CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6: + case CQ_ENET_RQ_DESC_RSS_TYPE_TCP_IPv6_EX: + skb_set_hash(skb, rss_hash, PKT_HASH_TYPE_L4); + break; + case CQ_ENET_RQ_DESC_RSS_TYPE_IPv4: + case CQ_ENET_RQ_DESC_RSS_TYPE_IPv6: + case CQ_ENET_RQ_DESC_RSS_TYPE_IPv6_EX: + skb_set_hash(skb, rss_hash, PKT_HASH_TYPE_L3); + break; + } } /* Hardware does not provide whole packet checksum. It only -- cgit v1.1 From b250a7fc3bbec80a3c4717a262863d93f4146b88 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:55:54 +0100 Subject: netfilter: get rid of useless debugging from core This patch remove compile time code to catch inconventional verdicts. We have better ways to handle this case these days, eg. pr_debug() but even though I don't think this is useful at all, so let's remove this. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 004af03..3d4aa96 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -323,15 +323,6 @@ unsigned int nf_iterate(struct sk_buff *skb, repeat: verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state); if (verdict != NF_ACCEPT) { -#ifdef CONFIG_NETFILTER_DEBUG - if (unlikely((verdict & NF_VERDICT_MASK) - > NF_MAX_VERDICT)) { - NFDEBUG("Evil return from %p(%u).\n", - (*entryp)->ops.hook, state->hook); - *entryp = rcu_dereference((*entryp)->next); - continue; - } -#endif if (verdict != NF_REPEAT) return verdict; goto repeat; -- cgit v1.1 From d2be66f6858d6243395763cb643de55bad96d33d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:05 +0100 Subject: netfilter: remove comments that predate rcu days We cannot block/sleep on nf_iterate because netfilter runs under rcu read lock these days, where blocking is well-known to be illegal. So let's remove these old comments. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 3d4aa96..76014ad 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -308,18 +308,11 @@ unsigned int nf_iterate(struct sk_buff *skb, { unsigned int verdict; - /* - * The caller must not block between calls to this - * function because of risk of continuing from deleted element. - */ while (*entryp) { if (state->thresh > (*entryp)->ops.priority) { *entryp = rcu_dereference((*entryp)->next); continue; } - - /* Optimization: we don't need to hold module - reference here, since function can't sleep. --RR */ repeat: verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state); if (verdict != NF_ACCEPT) { -- cgit v1.1 From 1610a73c4175e7d63985316b52ac932b65a4dc90 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:12 +0100 Subject: netfilter: kill NF_HOOK_THRESH() and state->tresh Patch c5136b15ea36 ("netfilter: bridge: add and use br_nf_hook_thresh") introduced br_nf_hook_thresh(). Replace NF_HOOK_THRESH() by br_nf_hook_thresh from br_nf_forward_finish(), so we have no more callers for this macro. As a result, state->thresh and explicit thresh parameter in the hook state structure is not required anymore. And we can get rid of skip-hook-under-thresh loop in nf_iterate() in the core path that is only used by br_netfilter to search for the filter hook. Suggested-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 50 +++++++++-------------------------- include/linux/netfilter_ingress.h | 2 +- net/bridge/br_netfilter_hooks.c | 8 +++--- net/bridge/netfilter/ebtable_broute.c | 2 +- net/netfilter/core.c | 4 --- net/netfilter/nf_queue.c | 2 -- 6 files changed, 19 insertions(+), 49 deletions(-) diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index abc7fdc..e0d000f 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -49,7 +49,6 @@ struct sock; struct nf_hook_state { unsigned int hook; - int thresh; u_int8_t pf; struct net_device *in; struct net_device *out; @@ -84,7 +83,7 @@ struct nf_hook_entry { static inline void nf_hook_state_init(struct nf_hook_state *p, struct nf_hook_entry *hook_entry, unsigned int hook, - int thresh, u_int8_t pf, + u_int8_t pf, struct net_device *indev, struct net_device *outdev, struct sock *sk, @@ -92,7 +91,6 @@ static inline void nf_hook_state_init(struct nf_hook_state *p, int (*okfn)(struct net *, struct sock *, struct sk_buff *)) { p->hook = hook; - p->thresh = thresh; p->pf = pf; p->in = indev; p->out = outdev; @@ -155,20 +153,16 @@ extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state); /** - * nf_hook_thresh - call a netfilter hook + * nf_hook - call a netfilter hook * * Returns 1 if the hook has allowed the packet to pass. The function * okfn must be invoked by the caller in this case. Any other return * value indicates the packet has been consumed by the hook. */ -static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, - struct net *net, - struct sock *sk, - struct sk_buff *skb, - struct net_device *indev, - struct net_device *outdev, - int (*okfn)(struct net *, struct sock *, struct sk_buff *), - int thresh) +static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, + struct sock *sk, struct sk_buff *skb, + struct net_device *indev, struct net_device *outdev, + int (*okfn)(struct net *, struct sock *, struct sk_buff *)) { struct nf_hook_entry *hook_head; int ret = 1; @@ -185,8 +179,8 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, if (hook_head) { struct nf_hook_state state; - nf_hook_state_init(&state, hook_head, hook, thresh, - pf, indev, outdev, sk, net, okfn); + nf_hook_state_init(&state, hook_head, hook, pf, indev, outdev, + sk, net, okfn); ret = nf_hook_slow(skb, &state); } @@ -195,14 +189,6 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, return ret; } -static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, - struct sock *sk, struct sk_buff *skb, - struct net_device *indev, struct net_device *outdev, - int (*okfn)(struct net *, struct sock *, struct sk_buff *)) -{ - return nf_hook_thresh(pf, hook, net, sk, skb, indev, outdev, okfn, INT_MIN); -} - /* Activate hook; either okfn or kfree_skb called, unless a hook returns NF_STOLEN (in which case, it's up to the hook to deal with the consequences). @@ -221,19 +207,6 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, */ static inline int -NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, - struct sk_buff *skb, struct net_device *in, - struct net_device *out, - int (*okfn)(struct net *, struct sock *, struct sk_buff *), - int thresh) -{ - int ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, thresh); - if (ret == 1) - ret = okfn(net, sk, skb); - return ret; -} - -static inline int NF_HOOK_COND(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb, struct net_device *in, struct net_device *out, int (*okfn)(struct net *, struct sock *, struct sk_buff *), @@ -242,7 +215,7 @@ NF_HOOK_COND(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, int ret; if (!cond || - ((ret = nf_hook_thresh(pf, hook, net, sk, skb, in, out, okfn, INT_MIN)) == 1)) + ((ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn)) == 1)) ret = okfn(net, sk, skb); return ret; } @@ -252,7 +225,10 @@ NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct struct net_device *in, struct net_device *out, int (*okfn)(struct net *, struct sock *, struct sk_buff *)) { - return NF_HOOK_THRESH(pf, hook, net, sk, skb, in, out, okfn, INT_MIN); + int ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn); + if (ret == 1) + ret = okfn(net, sk, skb); + return ret; } /* Call setsockopt() */ diff --git a/include/linux/netfilter_ingress.h b/include/linux/netfilter_ingress.h index 33e37fb..fd44e41 100644 --- a/include/linux/netfilter_ingress.h +++ b/include/linux/netfilter_ingress.h @@ -26,7 +26,7 @@ static inline int nf_hook_ingress(struct sk_buff *skb) if (unlikely(!e)) return 0; - nf_hook_state_init(&state, e, NF_NETDEV_INGRESS, INT_MIN, + nf_hook_state_init(&state, e, NF_NETDEV_INGRESS, NFPROTO_NETDEV, skb->dev, NULL, NULL, dev_net(skb->dev), NULL); return nf_hook_slow(skb, &state); diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 2fe9345..d0d66fa 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -561,8 +561,8 @@ static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff } nf_bridge_push_encap_header(skb); - NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, net, sk, skb, - in, skb->dev, br_forward_finish, 1); + br_nf_hook_thresh(NF_BR_FORWARD, net, sk, skb, in, skb->dev, + br_forward_finish); return 0; } @@ -1016,8 +1016,8 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, /* We may already have this, but read-locks nest anyway */ rcu_read_lock(); - nf_hook_state_init(&state, elem, hook, NF_BR_PRI_BRNF + 1, - NFPROTO_BRIDGE, indev, outdev, sk, net, okfn); + nf_hook_state_init(&state, elem, hook, NFPROTO_BRIDGE, indev, outdev, + sk, net, okfn); ret = nf_hook_slow(skb, &state); rcu_read_unlock(); diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index ec94c6f..599679e 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -53,7 +53,7 @@ static int ebt_broute(struct sk_buff *skb) struct nf_hook_state state; int ret; - nf_hook_state_init(&state, NULL, NF_BR_BROUTING, INT_MIN, + nf_hook_state_init(&state, NULL, NF_BR_BROUTING, NFPROTO_BRIDGE, skb->dev, NULL, NULL, dev_net(skb->dev), NULL); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 76014ad..cb0232c 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -309,10 +309,6 @@ unsigned int nf_iterate(struct sk_buff *skb, unsigned int verdict; while (*entryp) { - if (state->thresh > (*entryp)->ops.priority) { - *entryp = rcu_dereference((*entryp)->next); - continue; - } repeat: verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state); if (verdict != NF_ACCEPT) { diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 8f08d75..0fb3896 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -200,8 +200,6 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) verdict = NF_DROP; } - entry->state.thresh = INT_MIN; - if (verdict == NF_ACCEPT) { hook_entry = rcu_dereference(hook_entry->next); if (hook_entry) -- cgit v1.1 From 06fd3a392bb36ff162d10cb7d5794185b94edb2f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:17 +0100 Subject: netfilter: deprecate NF_STOP NF_STOP is only used by br_netfilter these days, and it can be emulated with a combination of NF_STOLEN plus explicit call to the ->okfn() function as Florian suggests. To retain binary compatibility with userspace nf_queue application, we have to keep NF_STOP around, so libnetfilter_queue userspace userspace applications still work if they use NF_STOP for some exotic reason. Out of tree modules using NF_STOP would break, but we don't care about those. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter.h | 2 +- net/bridge/br_netfilter_hooks.c | 6 ++++-- net/netfilter/core.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/netfilter.h b/include/uapi/linux/netfilter.h index d93f949..7550e91 100644 --- a/include/uapi/linux/netfilter.h +++ b/include/uapi/linux/netfilter.h @@ -13,7 +13,7 @@ #define NF_STOLEN 2 #define NF_QUEUE 3 #define NF_REPEAT 4 -#define NF_STOP 5 +#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */ #define NF_MAX_VERDICT NF_STOP /* we overload the higher bits for encoding auxiliary data such as the queue diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index d0d66fa..7e3645f 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -845,8 +845,10 @@ static unsigned int ip_sabotage_in(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { - if (skb->nf_bridge && !skb->nf_bridge->in_prerouting) - return NF_STOP; + if (skb->nf_bridge && !skb->nf_bridge->in_prerouting) { + state->okfn(state->net, state->sk, skb); + return NF_STOLEN; + } return NF_ACCEPT; } diff --git a/net/netfilter/core.c b/net/netfilter/core.c index cb0232c..14f97b6 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -333,7 +333,7 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state) entry = rcu_dereference(state->hook_entries); next_hook: verdict = nf_iterate(skb, state, &entry); - if (verdict == NF_ACCEPT || verdict == NF_STOP) { + if (verdict == NF_ACCEPT) { ret = 1; } else if ((verdict & NF_VERDICT_MASK) == NF_DROP) { kfree_skb(skb); -- cgit v1.1 From 613dbd95723aee7abd16860745691b6c7bda20dc Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:21 +0100 Subject: netfilter: x_tables: move hook state into xt_action_param structure Place pointer to hook state in xt_action_param structure instead of copying the fields that we need. After this change xt_action_param fits into one cacheline. This patch also adds a set of new wrapper functions to fetch relevant hook state structure fields. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/x_tables.h | 48 +++++++++++++++++++++++------- include/net/netfilter/nf_tables.h | 11 +++---- net/bridge/netfilter/ebt_arpreply.c | 3 +- net/bridge/netfilter/ebt_log.c | 11 +++---- net/bridge/netfilter/ebt_nflog.c | 6 ++-- net/bridge/netfilter/ebt_redirect.c | 6 ++-- net/bridge/netfilter/ebtables.c | 6 +--- net/ipv4/netfilter/arp_tables.c | 6 +--- net/ipv4/netfilter/ip_tables.c | 6 +--- net/ipv4/netfilter/ipt_MASQUERADE.c | 3 +- net/ipv4/netfilter/ipt_REJECT.c | 4 +-- net/ipv4/netfilter/ipt_SYNPROXY.c | 4 +-- net/ipv4/netfilter/ipt_rpfilter.c | 2 +- net/ipv6/netfilter/ip6_tables.c | 6 +--- net/ipv6/netfilter/ip6t_MASQUERADE.c | 2 +- net/ipv6/netfilter/ip6t_REJECT.c | 23 ++++++++------ net/ipv6/netfilter/ip6t_SYNPROXY.c | 4 +-- net/ipv6/netfilter/ip6t_rpfilter.c | 3 +- net/netfilter/ipset/ip_set_core.c | 6 ++-- net/netfilter/ipset/ip_set_hash_netiface.c | 2 +- net/netfilter/xt_AUDIT.c | 10 +++---- net/netfilter/xt_LOG.c | 6 ++-- net/netfilter/xt_NETMAP.c | 20 ++++++------- net/netfilter/xt_NFLOG.c | 6 ++-- net/netfilter/xt_NFQUEUE.c | 4 +-- net/netfilter/xt_REDIRECT.c | 4 +-- net/netfilter/xt_TCPMSS.c | 4 +-- net/netfilter/xt_TEE.c | 4 +-- net/netfilter/xt_TPROXY.c | 16 +++++----- net/netfilter/xt_addrtype.c | 10 +++---- net/netfilter/xt_cluster.c | 2 +- net/netfilter/xt_connlimit.c | 8 ++--- net/netfilter/xt_conntrack.c | 8 ++--- net/netfilter/xt_devgroup.c | 4 +-- net/netfilter/xt_dscp.c | 2 +- net/netfilter/xt_ipvs.c | 4 +-- net/netfilter/xt_nfacct.c | 2 +- net/netfilter/xt_osf.c | 10 +++---- net/netfilter/xt_owner.c | 2 +- net/netfilter/xt_pkttype.c | 4 +-- net/netfilter/xt_policy.c | 4 +-- net/netfilter/xt_recent.c | 10 +++---- net/netfilter/xt_set.c | 26 ++++++++-------- net/netfilter/xt_socket.c | 4 +-- net/sched/act_ipt.c | 12 ++++---- net/sched/em_ipset.c | 17 ++++++----- 46 files changed, 196 insertions(+), 169 deletions(-) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 2ad1a2b..cd4eaf8 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -4,6 +4,7 @@ #include #include +#include #include /* Test a struct->invflags and a boolean for inequality */ @@ -17,14 +18,9 @@ * @target: the target extension * @matchinfo: per-match data * @targetinfo: per-target data - * @net network namespace through which the action was invoked - * @in: input netdevice - * @out: output netdevice + * @state: pointer to hook state this packet came from * @fragoff: packet is a fragment, this is the data offset * @thoff: position of transport header relative to skb->data - * @hook: hook number given packet came from - * @family: Actual NFPROTO_* through which the function is invoked - * (helpful when match->family == NFPROTO_UNSPEC) * * Fields written to by extensions: * @@ -38,15 +34,47 @@ struct xt_action_param { union { const void *matchinfo, *targinfo; }; - struct net *net; - const struct net_device *in, *out; + const struct nf_hook_state *state; int fragoff; unsigned int thoff; - unsigned int hooknum; - u_int8_t family; bool hotdrop; }; +static inline struct net *xt_net(const struct xt_action_param *par) +{ + return par->state->net; +} + +static inline struct net_device *xt_in(const struct xt_action_param *par) +{ + return par->state->in; +} + +static inline const char *xt_inname(const struct xt_action_param *par) +{ + return par->state->in->name; +} + +static inline struct net_device *xt_out(const struct xt_action_param *par) +{ + return par->state->out; +} + +static inline const char *xt_outname(const struct xt_action_param *par) +{ + return par->state->out->name; +} + +static inline unsigned int xt_hooknum(const struct xt_action_param *par) +{ + return par->state->hook; +} + +static inline u_int8_t xt_family(const struct xt_action_param *par) +{ + return par->state->pf; +} + /** * struct xt_mtchk_param - parameters for match extensions' * checkentry functions diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 5031e07..4406034 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -30,11 +30,12 @@ static inline void nft_set_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_state *state) { pkt->skb = skb; - pkt->net = pkt->xt.net = state->net; - pkt->in = pkt->xt.in = state->in; - pkt->out = pkt->xt.out = state->out; - pkt->hook = pkt->xt.hooknum = state->hook; - pkt->pf = pkt->xt.family = state->pf; + pkt->net = state->net; + pkt->in = state->in; + pkt->out = state->out; + pkt->hook = state->hook; + pkt->pf = state->pf; + pkt->xt.state = state; } static inline void nft_set_pktinfo_proto_unspec(struct nft_pktinfo *pkt, diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c index 070cf13..5929309 100644 --- a/net/bridge/netfilter/ebt_arpreply.c +++ b/net/bridge/netfilter/ebt_arpreply.c @@ -51,7 +51,8 @@ ebt_arpreply_tg(struct sk_buff *skb, const struct xt_action_param *par) if (diptr == NULL) return EBT_DROP; - arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, (struct net_device *)par->in, + arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, + (struct net_device *)xt_in(par), *diptr, shp, info->mac, shp); return info->target; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 9a11086..e88bd48 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -179,7 +179,7 @@ ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_log_info *info = par->targinfo; struct nf_loginfo li; - struct net *net = par->net; + struct net *net = xt_net(par); li.type = NF_LOG_TYPE_LOG; li.u.log.level = info->loglevel; @@ -190,11 +190,12 @@ ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par) * nf_log_packet() with NFT_LOG_TYPE_LOG here. --Pablo */ if (info->bitmask & EBT_LOG_NFLOG) - nf_log_packet(net, NFPROTO_BRIDGE, par->hooknum, skb, - par->in, par->out, &li, "%s", info->prefix); + nf_log_packet(net, NFPROTO_BRIDGE, xt_hooknum(par), skb, + xt_in(par), xt_out(par), &li, "%s", + info->prefix); else - ebt_log_packet(net, NFPROTO_BRIDGE, par->hooknum, skb, par->in, - par->out, &li, info->prefix); + ebt_log_packet(net, NFPROTO_BRIDGE, xt_hooknum(par), skb, + xt_in(par), xt_out(par), &li, info->prefix); return EBT_CONTINUE; } diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 5481615..c1dc486 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -23,16 +23,16 @@ static unsigned int ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ebt_nflog_info *info = par->targinfo; + struct net *net = xt_net(par); struct nf_loginfo li; - struct net *net = par->net; li.type = NF_LOG_TYPE_ULOG; li.u.ulog.copy_len = info->len; li.u.ulog.group = info->group; li.u.ulog.qthreshold = info->threshold; - nf_log_packet(net, PF_BRIDGE, par->hooknum, skb, par->in, - par->out, &li, "%s", info->prefix); + nf_log_packet(net, PF_BRIDGE, xt_hooknum(par), skb, xt_in(par), + xt_out(par), &li, "%s", info->prefix); return EBT_CONTINUE; } diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 2e7c4f9..8d2a85e 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -23,12 +23,12 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) if (!skb_make_writable(skb, 0)) return EBT_DROP; - if (par->hooknum != NF_BR_BROUTING) + if (xt_hooknum(par) != NF_BR_BROUTING) /* rcu_read_lock()ed by nf_hook_thresh */ ether_addr_copy(eth_hdr(skb)->h_dest, - br_port_get_rcu(par->in)->br->dev->dev_addr); + br_port_get_rcu(xt_in(par))->br->dev->dev_addr); else - ether_addr_copy(eth_hdr(skb)->h_dest, par->in->dev_addr); + ether_addr_copy(eth_hdr(skb)->h_dest, xt_in(par)->dev_addr); skb->pkt_type = PACKET_HOST; return info->target; } diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f5c11bb..1ab6014 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -194,12 +194,8 @@ unsigned int ebt_do_table(struct sk_buff *skb, const struct ebt_table_info *private; struct xt_action_param acpar; - acpar.family = NFPROTO_BRIDGE; - acpar.net = state->net; - acpar.in = state->in; - acpar.out = state->out; + acpar.state = state; acpar.hotdrop = false; - acpar.hooknum = hook; read_lock_bh(&table->lock); private = table->private; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index b31df59..e76ab23 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -217,11 +217,7 @@ unsigned int arpt_do_table(struct sk_buff *skb, */ e = get_entry(table_base, private->hook_entry[hook]); - acpar.net = state->net; - acpar.in = state->in; - acpar.out = state->out; - acpar.hooknum = hook; - acpar.family = NFPROTO_ARP; + acpar.state = state; acpar.hotdrop = false; arp = arp_hdr(skb); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 7c00ce9..de4fa03 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -261,11 +261,7 @@ ipt_do_table(struct sk_buff *skb, acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; acpar.thoff = ip_hdrlen(skb); acpar.hotdrop = false; - acpar.net = state->net; - acpar.in = state->in; - acpar.out = state->out; - acpar.family = NFPROTO_IPV4; - acpar.hooknum = hook; + acpar.state = state; IP_NF_ASSERT(table->valid_hooks & (1 << hook)); local_bh_disable(); diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index da7f02a..34cfb9b 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -55,7 +55,8 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) range.min_proto = mr->range[0].min; range.max_proto = mr->range[0].max; - return nf_nat_masquerade_ipv4(skb, par->hooknum, &range, par->out); + return nf_nat_masquerade_ipv4(skb, xt_hooknum(par), &range, + xt_out(par)); } static struct xt_target masquerade_tg_reg __read_mostly = { diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 1d16c0f..8bd0d7b 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -34,7 +34,7 @@ static unsigned int reject_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct ipt_reject_info *reject = par->targinfo; - int hook = par->hooknum; + int hook = xt_hooknum(par); switch (reject->with) { case IPT_ICMP_NET_UNREACHABLE: @@ -59,7 +59,7 @@ reject_tg(struct sk_buff *skb, const struct xt_action_param *par) nf_send_unreach(skb, ICMP_PKT_FILTERED, hook); break; case IPT_TCP_RESET: - nf_send_reset(par->net, skb, hook); + nf_send_reset(xt_net(par), skb, hook); case IPT_ICMP_ECHOREPLY: /* Doesn't happen. */ break; diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index db5b875..3614116 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -263,12 +263,12 @@ static unsigned int synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_synproxy_info *info = par->targinfo; - struct net *net = par->net; + struct net *net = xt_net(par); struct synproxy_net *snet = synproxy_pernet(net); struct synproxy_options opts = {}; struct tcphdr *th, _th; - if (nf_ip_checksum(skb, par->hooknum, par->thoff, IPPROTO_TCP)) + if (nf_ip_checksum(skb, xt_hooknum(par), par->thoff, IPPROTO_TCP)) return NF_DROP; th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th); diff --git a/net/ipv4/netfilter/ipt_rpfilter.c b/net/ipv4/netfilter/ipt_rpfilter.c index 78cc64e..59b4994 100644 --- a/net/ipv4/netfilter/ipt_rpfilter.c +++ b/net/ipv4/netfilter/ipt_rpfilter.c @@ -95,7 +95,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) flow.flowi4_tos = RT_TOS(iph->tos); flow.flowi4_scope = RT_SCOPE_UNIVERSE; - return rpfilter_lookup_reverse(par->net, &flow, par->in, info->flags) ^ invert; + return rpfilter_lookup_reverse(xt_net(par), &flow, xt_in(par), info->flags) ^ invert; } static int rpfilter_check(const struct xt_mtchk_param *par) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 55aacea..7eac01d 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -291,11 +291,7 @@ ip6t_do_table(struct sk_buff *skb, * rule is also a fragment-specific rule, non-fragments won't * match it. */ acpar.hotdrop = false; - acpar.net = state->net; - acpar.in = state->in; - acpar.out = state->out; - acpar.family = NFPROTO_IPV6; - acpar.hooknum = hook; + acpar.state = state; IP_NF_ASSERT(table->valid_hooks & (1 << hook)); diff --git a/net/ipv6/netfilter/ip6t_MASQUERADE.c b/net/ipv6/netfilter/ip6t_MASQUERADE.c index 7f9f45d..2b1a158 100644 --- a/net/ipv6/netfilter/ip6t_MASQUERADE.c +++ b/net/ipv6/netfilter/ip6t_MASQUERADE.c @@ -24,7 +24,7 @@ static unsigned int masquerade_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - return nf_nat_masquerade_ipv6(skb, par->targinfo, par->out); + return nf_nat_masquerade_ipv6(skb, par->targinfo, xt_out(par)); } static int masquerade_tg6_checkentry(const struct xt_tgchk_param *par) diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index db29bbf..fa51a20 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -39,35 +39,40 @@ static unsigned int reject_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct ip6t_reject_info *reject = par->targinfo; - struct net *net = par->net; + struct net *net = xt_net(par); switch (reject->with) { case IP6T_ICMP6_NO_ROUTE: - nf_send_unreach6(net, skb, ICMPV6_NOROUTE, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_NOROUTE, xt_hooknum(par)); break; case IP6T_ICMP6_ADM_PROHIBITED: - nf_send_unreach6(net, skb, ICMPV6_ADM_PROHIBITED, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_ADM_PROHIBITED, + xt_hooknum(par)); break; case IP6T_ICMP6_NOT_NEIGHBOUR: - nf_send_unreach6(net, skb, ICMPV6_NOT_NEIGHBOUR, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_NOT_NEIGHBOUR, + xt_hooknum(par)); break; case IP6T_ICMP6_ADDR_UNREACH: - nf_send_unreach6(net, skb, ICMPV6_ADDR_UNREACH, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_ADDR_UNREACH, + xt_hooknum(par)); break; case IP6T_ICMP6_PORT_UNREACH: - nf_send_unreach6(net, skb, ICMPV6_PORT_UNREACH, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_PORT_UNREACH, + xt_hooknum(par)); break; case IP6T_ICMP6_ECHOREPLY: /* Do nothing */ break; case IP6T_TCP_RESET: - nf_send_reset6(net, skb, par->hooknum); + nf_send_reset6(net, skb, xt_hooknum(par)); break; case IP6T_ICMP6_POLICY_FAIL: - nf_send_unreach6(net, skb, ICMPV6_POLICY_FAIL, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_POLICY_FAIL, xt_hooknum(par)); break; case IP6T_ICMP6_REJECT_ROUTE: - nf_send_unreach6(net, skb, ICMPV6_REJECT_ROUTE, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_REJECT_ROUTE, + xt_hooknum(par)); break; } diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 06bed74..99a1216 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -277,12 +277,12 @@ static unsigned int synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_synproxy_info *info = par->targinfo; - struct net *net = par->net; + struct net *net = xt_net(par); struct synproxy_net *snet = synproxy_pernet(net); struct synproxy_options opts = {}; struct tcphdr *th, _th; - if (nf_ip6_checksum(skb, par->hooknum, par->thoff, IPPROTO_TCP)) + if (nf_ip6_checksum(skb, xt_hooknum(par), par->thoff, IPPROTO_TCP)) return NF_DROP; th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th); diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c index 1ee1b25..d5263dc 100644 --- a/net/ipv6/netfilter/ip6t_rpfilter.c +++ b/net/ipv6/netfilter/ip6t_rpfilter.c @@ -93,7 +93,8 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) if (unlikely(saddrtype == IPV6_ADDR_ANY)) return true ^ invert; /* not routable: forward path will drop it */ - return rpfilter_lookup_reverse6(par->net, skb, par->in, info->flags) ^ invert; + return rpfilter_lookup_reverse6(xt_net(par), skb, xt_in(par), + info->flags) ^ invert; } static int rpfilter_check(const struct xt_mtchk_param *par) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index a748b0c..3f1b945 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -541,7 +541,7 @@ int ip_set_test(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get(par->net, index); + struct ip_set *set = ip_set_rcu_get(xt_net(par), index); int ret = 0; BUG_ON(!set); @@ -579,7 +579,7 @@ int ip_set_add(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get(par->net, index); + struct ip_set *set = ip_set_rcu_get(xt_net(par), index); int ret; BUG_ON(!set); @@ -601,7 +601,7 @@ int ip_set_del(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get(par->net, index); + struct ip_set *set = ip_set_rcu_get(xt_net(par), index); int ret = 0; BUG_ON(!set); diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index f0f688d..aa1a776 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -170,7 +170,7 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); e.ip &= ip_set_netmask(e.cidr); -#define IFACE(dir) (par->dir ? par->dir->name : "") +#define IFACE(dir) (par->state->dir ? par->state->dir->name : "") #define SRCDIR (opt->flags & IPSET_DIM_TWO_SRC) if (opt->cmdflags & IPSET_FLAG_PHYSDEV) { diff --git a/net/netfilter/xt_AUDIT.c b/net/netfilter/xt_AUDIT.c index 4973cbd..19247a17 100644 --- a/net/netfilter/xt_AUDIT.c +++ b/net/netfilter/xt_AUDIT.c @@ -132,9 +132,9 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par) goto errout; audit_log_format(ab, "action=%hhu hook=%u len=%u inif=%s outif=%s", - info->type, par->hooknum, skb->len, - par->in ? par->in->name : "?", - par->out ? par->out->name : "?"); + info->type, xt_hooknum(par), skb->len, + xt_in(par) ? xt_inname(par) : "?", + xt_out(par) ? xt_outname(par) : "?"); if (skb->mark) audit_log_format(ab, " mark=%#x", skb->mark); @@ -144,7 +144,7 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par) eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, ntohs(eth_hdr(skb)->h_proto)); - if (par->family == NFPROTO_BRIDGE) { + if (xt_family(par) == NFPROTO_BRIDGE) { switch (eth_hdr(skb)->h_proto) { case htons(ETH_P_IP): audit_ip4(ab, skb); @@ -157,7 +157,7 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par) } } - switch (par->family) { + switch (xt_family(par)) { case NFPROTO_IPV4: audit_ip4(ab, skb); break; diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c index 1763ab8..c3b2017e 100644 --- a/net/netfilter/xt_LOG.c +++ b/net/netfilter/xt_LOG.c @@ -32,15 +32,15 @@ static unsigned int log_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_log_info *loginfo = par->targinfo; + struct net *net = xt_net(par); struct nf_loginfo li; - struct net *net = par->net; li.type = NF_LOG_TYPE_LOG; li.u.log.level = loginfo->level; li.u.log.logflags = loginfo->logflags; - nf_log_packet(net, par->family, par->hooknum, skb, par->in, par->out, - &li, "%s", loginfo->prefix); + nf_log_packet(net, xt_family(par), xt_hooknum(par), skb, xt_in(par), + xt_out(par), &li, "%s", loginfo->prefix); return XT_CONTINUE; } diff --git a/net/netfilter/xt_NETMAP.c b/net/netfilter/xt_NETMAP.c index b253e07..94d0b54 100644 --- a/net/netfilter/xt_NETMAP.c +++ b/net/netfilter/xt_NETMAP.c @@ -33,8 +33,8 @@ netmap_tg6(struct sk_buff *skb, const struct xt_action_param *par) netmask.ip6[i] = ~(range->min_addr.ip6[i] ^ range->max_addr.ip6[i]); - if (par->hooknum == NF_INET_PRE_ROUTING || - par->hooknum == NF_INET_LOCAL_OUT) + if (xt_hooknum(par) == NF_INET_PRE_ROUTING || + xt_hooknum(par) == NF_INET_LOCAL_OUT) new_addr.in6 = ipv6_hdr(skb)->daddr; else new_addr.in6 = ipv6_hdr(skb)->saddr; @@ -51,7 +51,7 @@ netmap_tg6(struct sk_buff *skb, const struct xt_action_param *par) newrange.min_proto = range->min_proto; newrange.max_proto = range->max_proto; - return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); + return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); } static int netmap_tg6_checkentry(const struct xt_tgchk_param *par) @@ -72,16 +72,16 @@ netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par) const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; struct nf_nat_range newrange; - NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || - par->hooknum == NF_INET_POST_ROUTING || - par->hooknum == NF_INET_LOCAL_OUT || - par->hooknum == NF_INET_LOCAL_IN); + NF_CT_ASSERT(xt_hooknum(par) == NF_INET_PRE_ROUTING || + xt_hooknum(par) == NF_INET_POST_ROUTING || + xt_hooknum(par) == NF_INET_LOCAL_OUT || + xt_hooknum(par) == NF_INET_LOCAL_IN); ct = nf_ct_get(skb, &ctinfo); netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); - if (par->hooknum == NF_INET_PRE_ROUTING || - par->hooknum == NF_INET_LOCAL_OUT) + if (xt_hooknum(par) == NF_INET_PRE_ROUTING || + xt_hooknum(par) == NF_INET_LOCAL_OUT) new_ip = ip_hdr(skb)->daddr & ~netmask; else new_ip = ip_hdr(skb)->saddr & ~netmask; @@ -96,7 +96,7 @@ netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par) newrange.max_proto = mr->range[0].max; /* Hand modified range to generic setup. */ - return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); + return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); } static int netmap_tg4_check(const struct xt_tgchk_param *par) diff --git a/net/netfilter/xt_NFLOG.c b/net/netfilter/xt_NFLOG.c index 8668a5c..c7f8958 100644 --- a/net/netfilter/xt_NFLOG.c +++ b/net/netfilter/xt_NFLOG.c @@ -25,8 +25,8 @@ static unsigned int nflog_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_nflog_info *info = par->targinfo; + struct net *net = xt_net(par); struct nf_loginfo li; - struct net *net = par->net; li.type = NF_LOG_TYPE_ULOG; li.u.ulog.copy_len = info->len; @@ -37,8 +37,8 @@ nflog_tg(struct sk_buff *skb, const struct xt_action_param *par) if (info->flags & XT_NFLOG_F_COPY_LEN) li.u.ulog.flags |= NF_LOG_F_COPY_LEN; - nfulnl_log_packet(net, par->family, par->hooknum, skb, par->in, - par->out, &li, info->prefix); + nfulnl_log_packet(net, xt_family(par), xt_hooknum(par), skb, + xt_in(par), xt_out(par), &li, info->prefix); return XT_CONTINUE; } diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index 8f1779f..a360b99 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -43,7 +43,7 @@ nfqueue_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) if (info->queues_total > 1) { queue = nfqueue_hash(skb, queue, info->queues_total, - par->family, jhash_initval); + xt_family(par), jhash_initval); } return NF_QUEUE_NR(queue); } @@ -98,7 +98,7 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) queue = info->queuenum + cpu % info->queues_total; } else { queue = nfqueue_hash(skb, queue, info->queues_total, - par->family, jhash_initval); + xt_family(par), jhash_initval); } } diff --git a/net/netfilter/xt_REDIRECT.c b/net/netfilter/xt_REDIRECT.c index 03f0b37..651dce6 100644 --- a/net/netfilter/xt_REDIRECT.c +++ b/net/netfilter/xt_REDIRECT.c @@ -31,7 +31,7 @@ static unsigned int redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - return nf_nat_redirect_ipv6(skb, par->targinfo, par->hooknum); + return nf_nat_redirect_ipv6(skb, par->targinfo, xt_hooknum(par)); } static int redirect_tg6_checkentry(const struct xt_tgchk_param *par) @@ -62,7 +62,7 @@ static int redirect_tg4_check(const struct xt_tgchk_param *par) static unsigned int redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par) { - return nf_nat_redirect_ipv4(skb, par->targinfo, par->hooknum); + return nf_nat_redirect_ipv4(skb, par->targinfo, xt_hooknum(par)); } static struct xt_target redirect_tg_reg[] __read_mostly = { diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 872db2d..27241a7 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -108,7 +108,7 @@ tcpmss_mangle_packet(struct sk_buff *skb, return -1; if (info->mss == XT_TCPMSS_CLAMP_PMTU) { - struct net *net = par->net; + struct net *net = xt_net(par); unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family); unsigned int min_mtu = min(dst_mtu(skb_dst(skb)), in_mtu); @@ -172,7 +172,7 @@ tcpmss_mangle_packet(struct sk_buff *skb, * length IPv6 header of 60, ergo the default MSS value is 1220 * Since no MSS was provided, we must use the default values */ - if (par->family == NFPROTO_IPV4) + if (xt_family(par) == NFPROTO_IPV4) newmss = min(newmss, (u16)536); else newmss = min(newmss, (u16)1220); diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c index 0471db4..1c57ace 100644 --- a/net/netfilter/xt_TEE.c +++ b/net/netfilter/xt_TEE.c @@ -33,7 +33,7 @@ tee_tg4(struct sk_buff *skb, const struct xt_action_param *par) const struct xt_tee_tginfo *info = par->targinfo; int oif = info->priv ? info->priv->oif : 0; - nf_dup_ipv4(par->net, skb, par->hooknum, &info->gw.in, oif); + nf_dup_ipv4(xt_net(par), skb, xt_hooknum(par), &info->gw.in, oif); return XT_CONTINUE; } @@ -45,7 +45,7 @@ tee_tg6(struct sk_buff *skb, const struct xt_action_param *par) const struct xt_tee_tginfo *info = par->targinfo; int oif = info->priv ? info->priv->oif : 0; - nf_dup_ipv6(par->net, skb, par->hooknum, &info->gw.in6, oif); + nf_dup_ipv6(xt_net(par), skb, xt_hooknum(par), &info->gw.in6, oif); return XT_CONTINUE; } diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 663c4c3..dbd72cc 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -364,7 +364,8 @@ tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tproxy_target_info *tgi = par->targinfo; - return tproxy_tg4(par->net, skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value); + return tproxy_tg4(xt_net(par), skb, tgi->laddr, tgi->lport, + tgi->mark_mask, tgi->mark_value); } static unsigned int @@ -372,7 +373,8 @@ tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; - return tproxy_tg4(par->net, skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value); + return tproxy_tg4(xt_net(par), skb, tgi->laddr.ip, tgi->lport, + tgi->mark_mask, tgi->mark_value); } #ifdef XT_TPROXY_HAVE_IPV6 @@ -442,7 +444,7 @@ tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff, * to a listener socket if there's one */ struct sock *sk2; - sk2 = nf_tproxy_get_sock_v6(par->net, skb, thoff, hp, tproto, + sk2 = nf_tproxy_get_sock_v6(xt_net(par), skb, thoff, hp, tproto, &iph->saddr, tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr), hp->source, @@ -485,10 +487,10 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) * addresses, this happens if the redirect already happened * and the current packet belongs to an already established * connection */ - sk = nf_tproxy_get_sock_v6(par->net, skb, thoff, hp, tproto, + sk = nf_tproxy_get_sock_v6(xt_net(par), skb, thoff, hp, tproto, &iph->saddr, &iph->daddr, hp->source, hp->dest, - par->in, NFT_LOOKUP_ESTABLISHED); + xt_in(par), NFT_LOOKUP_ESTABLISHED); laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr); lport = tgi->lport ? tgi->lport : hp->dest; @@ -500,10 +502,10 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) else if (!sk) /* no there's no established connection, check if * there's a listener on the redirected addr/port */ - sk = nf_tproxy_get_sock_v6(par->net, skb, thoff, hp, + sk = nf_tproxy_get_sock_v6(xt_net(par), skb, thoff, hp, tproto, &iph->saddr, laddr, hp->source, lport, - par->in, NFT_LOOKUP_LISTENER); + xt_in(par), NFT_LOOKUP_LISTENER); /* NOTE: assign_sock consumes our sk reference */ if (sk && tproxy_sk_is_transparent(sk)) { diff --git a/net/netfilter/xt_addrtype.c b/net/netfilter/xt_addrtype.c index 11d6091..e329dab 100644 --- a/net/netfilter/xt_addrtype.c +++ b/net/netfilter/xt_addrtype.c @@ -125,7 +125,7 @@ static inline bool match_type(struct net *net, const struct net_device *dev, static bool addrtype_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) { - struct net *net = par->net; + struct net *net = xt_net(par); const struct xt_addrtype_info *info = par->matchinfo; const struct iphdr *iph = ip_hdr(skb); bool ret = true; @@ -143,19 +143,19 @@ addrtype_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) static bool addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) { - struct net *net = par->net; + struct net *net = xt_net(par); const struct xt_addrtype_info_v1 *info = par->matchinfo; const struct iphdr *iph; const struct net_device *dev = NULL; bool ret = true; if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) - dev = par->in; + dev = xt_in(par); else if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) - dev = par->out; + dev = xt_out(par); #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - if (par->family == NFPROTO_IPV6) + if (xt_family(par) == NFPROTO_IPV6) return addrtype_mt6(net, dev, skb, info); #endif iph = ip_hdr(skb); diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 96fa26b..9a9884a 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -112,7 +112,7 @@ xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par) * know, matches should not alter packets, but we are doing this here * because we would need to add a PKTTYPE target for this sole purpose. */ - if (!xt_cluster_is_multicast_addr(skb, par->family) && + if (!xt_cluster_is_multicast_addr(skb, xt_family(par)) && skb->pkt_type == PACKET_MULTICAST) { pskb->pkt_type = PACKET_HOST; } diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index b6dc322..bb38453 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -317,7 +317,7 @@ static int count_them(struct net *net, static bool connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) { - struct net *net = par->net; + struct net *net = xt_net(par); const struct xt_connlimit_info *info = par->matchinfo; union nf_inet_addr addr; struct nf_conntrack_tuple tuple; @@ -332,11 +332,11 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; zone = nf_ct_zone(ct); } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), - par->family, net, &tuple)) { + xt_family(par), net, &tuple)) { goto hotdrop; } - if (par->family == NFPROTO_IPV6) { + if (xt_family(par) == NFPROTO_IPV6) { const struct ipv6hdr *iph = ipv6_hdr(skb); memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ? &iph->daddr : &iph->saddr, sizeof(addr.ip6)); @@ -347,7 +347,7 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) } connections = count_them(net, info->data, tuple_ptr, &addr, - &info->mask, par->family, zone); + &info->mask, xt_family(par), zone); if (connections == 0) /* kmalloc failed, drop it entirely */ goto hotdrop; diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index a3b8f69..2dea15e 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -200,22 +200,22 @@ conntrack_mt(const struct sk_buff *skb, struct xt_action_param *par, return false; if (info->match_flags & XT_CONNTRACK_ORIGSRC) - if (conntrack_mt_origsrc(ct, info, par->family) ^ + if (conntrack_mt_origsrc(ct, info, xt_family(par)) ^ !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) return false; if (info->match_flags & XT_CONNTRACK_ORIGDST) - if (conntrack_mt_origdst(ct, info, par->family) ^ + if (conntrack_mt_origdst(ct, info, xt_family(par)) ^ !(info->invert_flags & XT_CONNTRACK_ORIGDST)) return false; if (info->match_flags & XT_CONNTRACK_REPLSRC) - if (conntrack_mt_replsrc(ct, info, par->family) ^ + if (conntrack_mt_replsrc(ct, info, xt_family(par)) ^ !(info->invert_flags & XT_CONNTRACK_REPLSRC)) return false; if (info->match_flags & XT_CONNTRACK_REPLDST) - if (conntrack_mt_repldst(ct, info, par->family) ^ + if (conntrack_mt_repldst(ct, info, xt_family(par)) ^ !(info->invert_flags & XT_CONNTRACK_REPLDST)) return false; diff --git a/net/netfilter/xt_devgroup.c b/net/netfilter/xt_devgroup.c index d9202cd..96ebe1c 100644 --- a/net/netfilter/xt_devgroup.c +++ b/net/netfilter/xt_devgroup.c @@ -24,12 +24,12 @@ static bool devgroup_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct xt_devgroup_info *info = par->matchinfo; if (info->flags & XT_DEVGROUP_MATCH_SRC && - (((info->src_group ^ par->in->group) & info->src_mask ? 1 : 0) ^ + (((info->src_group ^ xt_in(par)->group) & info->src_mask ? 1 : 0) ^ ((info->flags & XT_DEVGROUP_INVERT_SRC) ? 1 : 0))) return false; if (info->flags & XT_DEVGROUP_MATCH_DST && - (((info->dst_group ^ par->out->group) & info->dst_mask ? 1 : 0) ^ + (((info->dst_group ^ xt_out(par)->group) & info->dst_mask ? 1 : 0) ^ ((info->flags & XT_DEVGROUP_INVERT_DST) ? 1 : 0))) return false; diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index 64670fc..236ac80 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -58,7 +58,7 @@ static bool tos_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_tos_match_info *info = par->matchinfo; - if (par->family == NFPROTO_IPV4) + if (xt_family(par) == NFPROTO_IPV4) return ((ip_hdr(skb)->tos & info->tos_mask) == info->tos_value) ^ !!info->invert; else diff --git a/net/netfilter/xt_ipvs.c b/net/netfilter/xt_ipvs.c index 71a9d95..0fdc890 100644 --- a/net/netfilter/xt_ipvs.c +++ b/net/netfilter/xt_ipvs.c @@ -48,9 +48,9 @@ static bool ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_ipvs_mtinfo *data = par->matchinfo; - struct netns_ipvs *ipvs = net_ipvs(par->net); + struct netns_ipvs *ipvs = net_ipvs(xt_net(par)); /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */ - const u_int8_t family = par->family; + const u_int8_t family = xt_family(par); struct ip_vs_iphdr iph; struct ip_vs_protocol *pp; struct ip_vs_conn *cp; diff --git a/net/netfilter/xt_nfacct.c b/net/netfilter/xt_nfacct.c index cf32759..cc0518f 100644 --- a/net/netfilter/xt_nfacct.c +++ b/net/netfilter/xt_nfacct.c @@ -26,7 +26,7 @@ static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par) nfnl_acct_update(skb, info->nfacct); - overquota = nfnl_acct_overquota(par->net, skb, info->nfacct); + overquota = nfnl_acct_overquota(xt_net(par), skb, info->nfacct); return overquota == NFACCT_UNDERQUOTA ? false : true; } diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index 2455b69..c05fefc 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -201,7 +201,7 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) unsigned char opts[MAX_IPOPTLEN]; const struct xt_osf_finger *kf; const struct xt_osf_user_finger *f; - struct net *net = p->net; + struct net *net = xt_net(p); if (!info) return false; @@ -326,8 +326,8 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) fcount++; if (info->flags & XT_OSF_LOG) - nf_log_packet(net, p->family, p->hooknum, skb, - p->in, p->out, NULL, + nf_log_packet(net, xt_family(p), xt_hooknum(p), skb, + xt_in(p), xt_out(p), NULL, "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n", f->genre, f->version, f->subtype, &ip->saddr, ntohs(tcp->source), @@ -341,8 +341,8 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) rcu_read_unlock(); if (!fcount && (info->flags & XT_OSF_LOG)) - nf_log_packet(net, p->family, p->hooknum, skb, p->in, - p->out, NULL, + nf_log_packet(net, xt_family(p), xt_hooknum(p), skb, xt_in(p), + xt_out(p), NULL, "Remote OS is not known: %pI4:%u -> %pI4:%u\n", &ip->saddr, ntohs(tcp->source), &ip->daddr, ntohs(tcp->dest)); diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c index a20e731..16477df 100644 --- a/net/netfilter/xt_owner.c +++ b/net/netfilter/xt_owner.c @@ -63,7 +63,7 @@ owner_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct xt_owner_match_info *info = par->matchinfo; const struct file *filp; struct sock *sk = skb_to_full_sk(skb); - struct net *net = par->net; + struct net *net = xt_net(par); if (sk == NULL || sk->sk_socket == NULL) return (info->match ^ info->invert) == 0; diff --git a/net/netfilter/xt_pkttype.c b/net/netfilter/xt_pkttype.c index 5b645cb..57efb70 100644 --- a/net/netfilter/xt_pkttype.c +++ b/net/netfilter/xt_pkttype.c @@ -30,10 +30,10 @@ pkttype_mt(const struct sk_buff *skb, struct xt_action_param *par) if (skb->pkt_type != PACKET_LOOPBACK) type = skb->pkt_type; - else if (par->family == NFPROTO_IPV4 && + else if (xt_family(par) == NFPROTO_IPV4 && ipv4_is_multicast(ip_hdr(skb)->daddr)) type = PACKET_MULTICAST; - else if (par->family == NFPROTO_IPV6 && + else if (xt_family(par) == NFPROTO_IPV6 && ipv6_hdr(skb)->daddr.s6_addr[0] == 0xFF) type = PACKET_MULTICAST; else diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index f23e97b..2b4ab18 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -116,9 +116,9 @@ policy_mt(const struct sk_buff *skb, struct xt_action_param *par) int ret; if (info->flags & XT_POLICY_MATCH_IN) - ret = match_policy_in(skb, info, par->family); + ret = match_policy_in(skb, info, xt_family(par)); else - ret = match_policy_out(skb, info, par->family); + ret = match_policy_out(skb, info, xt_family(par)); if (ret < 0) ret = info->flags & XT_POLICY_MATCH_NONE ? true : false; diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index e3b7a09..bf25000 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -236,7 +236,7 @@ static void recent_table_flush(struct recent_table *t) static bool recent_mt(const struct sk_buff *skb, struct xt_action_param *par) { - struct net *net = par->net; + struct net *net = xt_net(par); struct recent_net *recent_net = recent_pernet(net); const struct xt_recent_mtinfo_v1 *info = par->matchinfo; struct recent_table *t; @@ -245,7 +245,7 @@ recent_mt(const struct sk_buff *skb, struct xt_action_param *par) u_int8_t ttl; bool ret = info->invert; - if (par->family == NFPROTO_IPV4) { + if (xt_family(par) == NFPROTO_IPV4) { const struct iphdr *iph = ip_hdr(skb); if (info->side == XT_RECENT_DEST) @@ -266,7 +266,7 @@ recent_mt(const struct sk_buff *skb, struct xt_action_param *par) } /* use TTL as seen before forwarding */ - if (par->out != NULL && skb->sk == NULL) + if (xt_out(par) != NULL && skb->sk == NULL) ttl++; spin_lock_bh(&recent_lock); @@ -274,12 +274,12 @@ recent_mt(const struct sk_buff *skb, struct xt_action_param *par) nf_inet_addr_mask(&addr, &addr_mask, &t->mask); - e = recent_entry_lookup(t, &addr_mask, par->family, + e = recent_entry_lookup(t, &addr_mask, xt_family(par), (info->check_set & XT_RECENT_TTL) ? ttl : 0); if (e == NULL) { if (!(info->check_set & XT_RECENT_SET)) goto out; - e = recent_entry_init(t, &addr_mask, par->family, ttl); + e = recent_entry_init(t, &addr_mask, xt_family(par), ttl); if (e == NULL) par->hotdrop = true; ret = !ret; diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 5669e5b..1bfede7 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -55,7 +55,7 @@ set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_set_info_match_v0 *info = par->matchinfo; - ADT_OPT(opt, par->family, info->match_set.u.compat.dim, + ADT_OPT(opt, xt_family(par), info->match_set.u.compat.dim, info->match_set.u.compat.flags, 0, UINT_MAX); return match_set(info->match_set.index, skb, par, &opt, @@ -118,7 +118,7 @@ set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_set_info_match_v1 *info = par->matchinfo; - ADT_OPT(opt, par->family, info->match_set.dim, + ADT_OPT(opt, xt_family(par), info->match_set.dim, info->match_set.flags, 0, UINT_MAX); if (opt.flags & IPSET_RETURN_NOMATCH) @@ -184,7 +184,7 @@ set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) const struct xt_set_info_match_v3 *info = par->matchinfo; int ret; - ADT_OPT(opt, par->family, info->match_set.dim, + ADT_OPT(opt, xt_family(par), info->match_set.dim, info->match_set.flags, info->flags, UINT_MAX); if (info->packets.op != IPSET_COUNTER_NONE || @@ -231,7 +231,7 @@ set_match_v4(const struct sk_buff *skb, struct xt_action_param *par) const struct xt_set_info_match_v4 *info = par->matchinfo; int ret; - ADT_OPT(opt, par->family, info->match_set.dim, + ADT_OPT(opt, xt_family(par), info->match_set.dim, info->match_set.flags, info->flags, UINT_MAX); if (info->packets.op != IPSET_COUNTER_NONE || @@ -259,9 +259,9 @@ set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_set_info_target_v0 *info = par->targinfo; - ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim, + ADT_OPT(add_opt, xt_family(par), info->add_set.u.compat.dim, info->add_set.u.compat.flags, 0, UINT_MAX); - ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim, + ADT_OPT(del_opt, xt_family(par), info->del_set.u.compat.dim, info->del_set.u.compat.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) @@ -332,9 +332,9 @@ set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_set_info_target_v1 *info = par->targinfo; - ADT_OPT(add_opt, par->family, info->add_set.dim, + ADT_OPT(add_opt, xt_family(par), info->add_set.dim, info->add_set.flags, 0, UINT_MAX); - ADT_OPT(del_opt, par->family, info->del_set.dim, + ADT_OPT(del_opt, xt_family(par), info->del_set.dim, info->del_set.flags, 0, UINT_MAX); if (info->add_set.index != IPSET_INVALID_ID) @@ -401,9 +401,9 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_set_info_target_v2 *info = par->targinfo; - ADT_OPT(add_opt, par->family, info->add_set.dim, + ADT_OPT(add_opt, xt_family(par), info->add_set.dim, info->add_set.flags, info->flags, info->timeout); - ADT_OPT(del_opt, par->family, info->del_set.dim, + ADT_OPT(del_opt, xt_family(par), info->del_set.dim, info->del_set.flags, 0, UINT_MAX); /* Normalize to fit into jiffies */ @@ -429,11 +429,11 @@ set_target_v3(struct sk_buff *skb, const struct xt_action_param *par) const struct xt_set_info_target_v3 *info = par->targinfo; int ret; - ADT_OPT(add_opt, par->family, info->add_set.dim, + ADT_OPT(add_opt, xt_family(par), info->add_set.dim, info->add_set.flags, info->flags, info->timeout); - ADT_OPT(del_opt, par->family, info->del_set.dim, + ADT_OPT(del_opt, xt_family(par), info->del_set.dim, info->del_set.flags, 0, UINT_MAX); - ADT_OPT(map_opt, par->family, info->map_set.dim, + ADT_OPT(map_opt, xt_family(par), info->map_set.dim, info->map_set.flags, 0, UINT_MAX); /* Normalize to fit into jiffies */ diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 018c369..2198914 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -57,7 +57,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, struct sock *sk = skb->sk; if (!sk) - sk = nf_sk_lookup_slow_v4(par->net, skb, par->in); + sk = nf_sk_lookup_slow_v4(xt_net(par), skb, xt_in(par)); if (sk) { bool wildcard; bool transparent = true; @@ -114,7 +114,7 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk = skb->sk; if (!sk) - sk = nf_sk_lookup_slow_v6(par->net, skb, par->in); + sk = nf_sk_lookup_slow_v6(xt_net(par), skb, xt_in(par)); if (sk) { bool wildcard; bool transparent = true; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 378c1c9..ce7ea6c 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -213,6 +213,12 @@ static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a, int ret = 0, result = 0; struct tcf_ipt *ipt = to_ipt(a); struct xt_action_param par; + struct nf_hook_state state = { + .net = dev_net(skb->dev), + .in = skb->dev, + .hook = ipt->tcfi_hook, + .pf = NFPROTO_IPV4, + }; if (skb_unclone(skb, GFP_ATOMIC)) return TC_ACT_UNSPEC; @@ -226,13 +232,9 @@ static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a, * worry later - danger - this API seems to have changed * from earlier kernels */ - par.net = dev_net(skb->dev); - par.in = skb->dev; - par.out = NULL; - par.hooknum = ipt->tcfi_hook; + par.state = &state; par.target = ipt->tcfi_t->u.kernel.target; par.targinfo = ipt->tcfi_t->data; - par.family = NFPROTO_IPV4; ret = par.target->target(skb, &par); switch (ret) { diff --git a/net/sched/em_ipset.c b/net/sched/em_ipset.c index c66ca94..c1b23e3 100644 --- a/net/sched/em_ipset.c +++ b/net/sched/em_ipset.c @@ -57,17 +57,20 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em, struct xt_action_param acpar; const struct xt_set_info *set = (const void *) em->data; struct net_device *dev, *indev = NULL; + struct nf_hook_state state = { + .net = em->net, + }; int ret, network_offset; switch (tc_skb_protocol(skb)) { case htons(ETH_P_IP): - acpar.family = NFPROTO_IPV4; + state.pf = NFPROTO_IPV4; if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) return 0; acpar.thoff = ip_hdrlen(skb); break; case htons(ETH_P_IPV6): - acpar.family = NFPROTO_IPV6; + state.pf = NFPROTO_IPV6; if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) return 0; /* doesn't call ipv6_find_hdr() because ipset doesn't use thoff, yet */ @@ -77,9 +80,7 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em, return 0; } - acpar.hooknum = 0; - - opt.family = acpar.family; + opt.family = state.pf; opt.dim = set->dim; opt.flags = set->flags; opt.cmdflags = 0; @@ -95,9 +96,9 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em, if (skb->skb_iif) indev = dev_get_by_index_rcu(em->net, skb->skb_iif); - acpar.net = em->net; - acpar.in = indev ? indev : dev; - acpar.out = dev; + state.in = indev ? indev : dev; + state.out = dev; + acpar.state = &state; ret = ip_set_test(set->index, skb, &acpar, &opt); -- cgit v1.1 From 0e5a1c7eb3fc705c4cc6c1e058e81d1f2e721c72 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:26 +0100 Subject: netfilter: nf_tables: use hook state from xt_action_param structure Don't copy relevant fields from hook state structure, instead use the one that is already available in struct xt_action_param. This patch also adds a set of new wrapper functions to fetch relevant hook state structure fields. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 35 +++++++++++++++++++++++--------- net/bridge/netfilter/nft_meta_bridge.c | 2 +- net/bridge/netfilter/nft_reject_bridge.c | 30 ++++++++++++++++----------- net/ipv4/netfilter/nft_dup_ipv4.c | 2 +- net/ipv4/netfilter/nft_fib_ipv4.c | 14 ++++++------- net/ipv4/netfilter/nft_masq_ipv4.c | 4 ++-- net/ipv4/netfilter/nft_redir_ipv4.c | 3 +-- net/ipv4/netfilter/nft_reject_ipv4.c | 4 ++-- net/ipv6/netfilter/nft_dup_ipv6.c | 2 +- net/ipv6/netfilter/nft_fib_ipv6.c | 16 +++++++-------- net/ipv6/netfilter/nft_masq_ipv6.c | 3 ++- net/ipv6/netfilter/nft_redir_ipv6.c | 3 ++- net/ipv6/netfilter/nft_reject_ipv6.c | 6 +++--- net/netfilter/nf_dup_netdev.c | 2 +- net/netfilter/nf_tables_core.c | 10 ++++----- net/netfilter/nf_tables_trace.c | 8 ++++---- net/netfilter/nft_fib.c | 2 +- net/netfilter/nft_fib_inet.c | 2 +- net/netfilter/nft_log.c | 5 +++-- net/netfilter/nft_lookup.c | 5 ++--- net/netfilter/nft_meta.c | 6 +++--- net/netfilter/nft_queue.c | 2 +- net/netfilter/nft_reject_inet.c | 18 ++++++++-------- net/netfilter/nft_rt.c | 4 ++-- 24 files changed, 105 insertions(+), 83 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 4406034..3295fb8 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -14,27 +14,42 @@ struct nft_pktinfo { struct sk_buff *skb; - struct net *net; - const struct net_device *in; - const struct net_device *out; - u8 pf; - u8 hook; bool tprot_set; u8 tprot; /* for x_tables compatibility */ struct xt_action_param xt; }; +static inline struct net *nft_net(const struct nft_pktinfo *pkt) +{ + return pkt->xt.state->net; +} + +static inline unsigned int nft_hook(const struct nft_pktinfo *pkt) +{ + return pkt->xt.state->hook; +} + +static inline u8 nft_pf(const struct nft_pktinfo *pkt) +{ + return pkt->xt.state->pf; +} + +static inline const struct net_device *nft_in(const struct nft_pktinfo *pkt) +{ + return pkt->xt.state->in; +} + +static inline const struct net_device *nft_out(const struct nft_pktinfo *pkt) +{ + return pkt->xt.state->out; +} + static inline void nft_set_pktinfo(struct nft_pktinfo *pkt, struct sk_buff *skb, const struct nf_hook_state *state) { pkt->skb = skb; - pkt->net = state->net; - pkt->in = state->in; - pkt->out = state->out; - pkt->hook = state->hook; - pkt->pf = state->pf; pkt->xt.state = state; } diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index ad47a92..5974dbc 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -23,7 +23,7 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { const struct nft_meta *priv = nft_expr_priv(expr); - const struct net_device *in = pkt->in, *out = pkt->out; + const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); u32 *dest = ®s->data[priv->dreg]; const struct net_bridge_port *p; diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index 4b3df6b..206dc26 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -315,17 +315,20 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr, case htons(ETH_P_IP): switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nft_reject_br_send_v4_unreach(pkt->net, pkt->skb, - pkt->in, pkt->hook, + nft_reject_br_send_v4_unreach(nft_net(pkt), pkt->skb, + nft_in(pkt), + nft_hook(pkt), priv->icmp_code); break; case NFT_REJECT_TCP_RST: - nft_reject_br_send_v4_tcp_reset(pkt->net, pkt->skb, - pkt->in, pkt->hook); + nft_reject_br_send_v4_tcp_reset(nft_net(pkt), pkt->skb, + nft_in(pkt), + nft_hook(pkt)); break; case NFT_REJECT_ICMPX_UNREACH: - nft_reject_br_send_v4_unreach(pkt->net, pkt->skb, - pkt->in, pkt->hook, + nft_reject_br_send_v4_unreach(nft_net(pkt), pkt->skb, + nft_in(pkt), + nft_hook(pkt), nft_reject_icmp_code(priv->icmp_code)); break; } @@ -333,17 +336,20 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr, case htons(ETH_P_IPV6): switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nft_reject_br_send_v6_unreach(pkt->net, pkt->skb, - pkt->in, pkt->hook, + nft_reject_br_send_v6_unreach(nft_net(pkt), pkt->skb, + nft_in(pkt), + nft_hook(pkt), priv->icmp_code); break; case NFT_REJECT_TCP_RST: - nft_reject_br_send_v6_tcp_reset(pkt->net, pkt->skb, - pkt->in, pkt->hook); + nft_reject_br_send_v6_tcp_reset(nft_net(pkt), pkt->skb, + nft_in(pkt), + nft_hook(pkt)); break; case NFT_REJECT_ICMPX_UNREACH: - nft_reject_br_send_v6_unreach(pkt->net, pkt->skb, - pkt->in, pkt->hook, + nft_reject_br_send_v6_unreach(nft_net(pkt), pkt->skb, + nft_in(pkt), + nft_hook(pkt), nft_reject_icmpv6_code(priv->icmp_code)); break; } diff --git a/net/ipv4/netfilter/nft_dup_ipv4.c b/net/ipv4/netfilter/nft_dup_ipv4.c index bf855e6..7ab544f 100644 --- a/net/ipv4/netfilter/nft_dup_ipv4.c +++ b/net/ipv4/netfilter/nft_dup_ipv4.c @@ -30,7 +30,7 @@ static void nft_dup_ipv4_eval(const struct nft_expr *expr, }; int oif = regs->data[priv->sreg_dev]; - nf_dup_ipv4(pkt->net, pkt->skb, pkt->hook, &gw, oif); + nf_dup_ipv4(nft_net(pkt), pkt->skb, nft_hook(pkt), &gw, oif); } static int nft_dup_ipv4_init(const struct nft_ctx *ctx, diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index db91fd4..1b49966 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -45,9 +45,9 @@ void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, __be32 addr; if (priv->flags & NFTA_FIB_F_IIF) - dev = pkt->in; + dev = nft_in(pkt); else if (priv->flags & NFTA_FIB_F_OIF) - dev = pkt->out; + dev = nft_out(pkt); iph = ip_hdr(pkt->skb); if (priv->flags & NFTA_FIB_F_DADDR) @@ -55,7 +55,7 @@ void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, else addr = iph->saddr; - *dst = inet_dev_addr_type(pkt->net, dev, addr); + *dst = inet_dev_addr_type(nft_net(pkt), dev, addr); } EXPORT_SYMBOL_GPL(nft_fib4_eval_type); @@ -89,13 +89,13 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, * Search results for the desired outinterface instead. */ if (priv->flags & NFTA_FIB_F_OIF) - oif = pkt->out; + oif = nft_out(pkt); else if (priv->flags & NFTA_FIB_F_IIF) - oif = pkt->in; + oif = nft_in(pkt); else oif = NULL; - if (pkt->hook == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) { + if (nft_hook(pkt) == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) { nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX); return; } @@ -122,7 +122,7 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, fl4.saddr = get_saddr(iph->daddr); } - if (fib_lookup(pkt->net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) + if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) return; switch (res.type) { diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c index 51ced81..4f697e4 100644 --- a/net/ipv4/netfilter/nft_masq_ipv4.c +++ b/net/ipv4/netfilter/nft_masq_ipv4.c @@ -31,8 +31,8 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr, range.max_proto.all = *(__be16 *)®s->data[priv->sreg_proto_max]; } - regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, pkt->hook, - &range, pkt->out); + regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, nft_hook(pkt), + &range, nft_out(pkt)); } static struct nft_expr_type nft_masq_ipv4_type; diff --git a/net/ipv4/netfilter/nft_redir_ipv4.c b/net/ipv4/netfilter/nft_redir_ipv4.c index c09d438..16df049 100644 --- a/net/ipv4/netfilter/nft_redir_ipv4.c +++ b/net/ipv4/netfilter/nft_redir_ipv4.c @@ -35,8 +35,7 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr, mr.range[0].flags |= priv->flags; - regs->verdict.code = nf_nat_redirect_ipv4(pkt->skb, &mr, - pkt->hook); + regs->verdict.code = nf_nat_redirect_ipv4(pkt->skb, &mr, nft_hook(pkt)); } static struct nft_expr_type nft_redir_ipv4_type; diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c index 2c2553b..517ce93 100644 --- a/net/ipv4/netfilter/nft_reject_ipv4.c +++ b/net/ipv4/netfilter/nft_reject_ipv4.c @@ -27,10 +27,10 @@ static void nft_reject_ipv4_eval(const struct nft_expr *expr, switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nf_send_unreach(pkt->skb, priv->icmp_code, pkt->hook); + nf_send_unreach(pkt->skb, priv->icmp_code, nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset(pkt->net, pkt->skb, pkt->hook); + nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt)); break; default: break; diff --git a/net/ipv6/netfilter/nft_dup_ipv6.c b/net/ipv6/netfilter/nft_dup_ipv6.c index 8bfd470..26074a8 100644 --- a/net/ipv6/netfilter/nft_dup_ipv6.c +++ b/net/ipv6/netfilter/nft_dup_ipv6.c @@ -28,7 +28,7 @@ static void nft_dup_ipv6_eval(const struct nft_expr *expr, struct in6_addr *gw = (struct in6_addr *)®s->data[priv->sreg_addr]; int oif = regs->data[priv->sreg_dev]; - nf_dup_ipv6(pkt->net, pkt->skb, pkt->hook, gw, oif); + nf_dup_ipv6(nft_net(pkt), pkt->skb, nft_hook(pkt), gw, oif); } static int nft_dup_ipv6_init(const struct nft_ctx *ctx, diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index ff1f1b6..d526bb5 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -80,17 +80,17 @@ static u32 __nft_fib6_eval_type(const struct nft_fib *priv, return RTN_UNREACHABLE; if (priv->flags & NFTA_FIB_F_IIF) - dev = pkt->in; + dev = nft_in(pkt); else if (priv->flags & NFTA_FIB_F_OIF) - dev = pkt->out; + dev = nft_out(pkt); nft_fib6_flowi_init(&fl6, priv, pkt, dev); v6ops = nf_get_ipv6_ops(); - if (dev && v6ops && v6ops->chk_addr(pkt->net, &fl6.daddr, dev, true)) + if (dev && v6ops && v6ops->chk_addr(nft_net(pkt), &fl6.daddr, dev, true)) ret = RTN_LOCAL; - route_err = afinfo->route(pkt->net, (struct dst_entry **)&rt, + route_err = afinfo->route(nft_net(pkt), (struct dst_entry **)&rt, flowi6_to_flowi(&fl6), false); if (route_err) goto err; @@ -158,20 +158,20 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, int lookup_flags; if (priv->flags & NFTA_FIB_F_IIF) - oif = pkt->in; + oif = nft_in(pkt); else if (priv->flags & NFTA_FIB_F_OIF) - oif = pkt->out; + oif = nft_out(pkt); lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif); - if (pkt->hook == NF_INET_PRE_ROUTING && fib6_is_local(pkt->skb)) { + if (nft_hook(pkt) == NF_INET_PRE_ROUTING && fib6_is_local(pkt->skb)) { nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX); return; } *dest = 0; again: - rt = (void *)ip6_route_lookup(pkt->net, &fl6, lookup_flags); + rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, lookup_flags); if (rt->dst.error) goto put_rt_err; diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c index 9597ffb..a2aff12 100644 --- a/net/ipv6/netfilter/nft_masq_ipv6.c +++ b/net/ipv6/netfilter/nft_masq_ipv6.c @@ -32,7 +32,8 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr, range.max_proto.all = *(__be16 *)®s->data[priv->sreg_proto_max]; } - regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out); + regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, + nft_out(pkt)); } static struct nft_expr_type nft_masq_ipv6_type; diff --git a/net/ipv6/netfilter/nft_redir_ipv6.c b/net/ipv6/netfilter/nft_redir_ipv6.c index aca44e8..bfcd5af 100644 --- a/net/ipv6/netfilter/nft_redir_ipv6.c +++ b/net/ipv6/netfilter/nft_redir_ipv6.c @@ -35,7 +35,8 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr, range.flags |= priv->flags; - regs->verdict.code = nf_nat_redirect_ipv6(pkt->skb, &range, pkt->hook); + regs->verdict.code = + nf_nat_redirect_ipv6(pkt->skb, &range, nft_hook(pkt)); } static struct nft_expr_type nft_redir_ipv6_type; diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c index 92bda99..057deea 100644 --- a/net/ipv6/netfilter/nft_reject_ipv6.c +++ b/net/ipv6/netfilter/nft_reject_ipv6.c @@ -27,11 +27,11 @@ static void nft_reject_ipv6_eval(const struct nft_expr *expr, switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nf_send_unreach6(pkt->net, pkt->skb, priv->icmp_code, - pkt->hook); + nf_send_unreach6(nft_net(pkt), pkt->skb, priv->icmp_code, + nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset6(pkt->net, pkt->skb, pkt->hook); + nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt)); break; default: break; diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index 7ec6972..44ae986 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -19,7 +19,7 @@ void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif) struct net_device *dev; struct sk_buff *skb; - dev = dev_get_by_index_rcu(pkt->net, oif); + dev = dev_get_by_index_rcu(nft_net(pkt), oif); if (dev == NULL) return; diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 70de32a..b63b1ed 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -53,10 +53,10 @@ static noinline void __nft_trace_packet(struct nft_traceinfo *info, nft_trace_notify(info); - nf_log_trace(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in, - pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", - chain->table->name, chain->name, comments[type], - rulenum); + nf_log_trace(nft_net(pkt), nft_pf(pkt), nft_hook(pkt), pkt->skb, + nft_in(pkt), nft_out(pkt), &trace_loginfo, + "TRACE: %s:%s:%s:%u ", + chain->table->name, chain->name, comments[type], rulenum); } static inline void nft_trace_packet(struct nft_traceinfo *info, @@ -124,7 +124,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv) { const struct nft_chain *chain = priv, *basechain = chain; - const struct net *net = pkt->net; + const struct net *net = nft_net(pkt); const struct nft_rule *rule; const struct nft_expr *expr, *last; struct nft_regs regs; diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c index ab695f8..12eb904 100644 --- a/net/netfilter/nf_tables_trace.c +++ b/net/netfilter/nf_tables_trace.c @@ -171,7 +171,7 @@ void nft_trace_notify(struct nft_traceinfo *info) unsigned int size; int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_TRACE; - if (!nfnetlink_has_listeners(pkt->net, NFNLGRP_NFTRACE)) + if (!nfnetlink_has_listeners(nft_net(pkt), NFNLGRP_NFTRACE)) return; size = nlmsg_total_size(sizeof(struct nfgenmsg)) + @@ -207,7 +207,7 @@ void nft_trace_notify(struct nft_traceinfo *info) nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; - if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(pkt->pf))) + if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(nft_pf(pkt)))) goto nla_put_failure; if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type))) @@ -249,7 +249,7 @@ void nft_trace_notify(struct nft_traceinfo *info) goto nla_put_failure; if (!info->packet_dumped) { - if (nf_trace_fill_dev_info(skb, pkt->in, pkt->out)) + if (nf_trace_fill_dev_info(skb, nft_in(pkt), nft_out(pkt))) goto nla_put_failure; if (nf_trace_fill_pkt_info(skb, pkt)) @@ -258,7 +258,7 @@ void nft_trace_notify(struct nft_traceinfo *info) } nlmsg_end(skb, nlh); - nfnetlink_send(skb, pkt->net, 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC); + nfnetlink_send(skb, nft_net(pkt), 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC); return; nla_put_failure: diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index 4944a8b..249c9b8 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -144,7 +144,7 @@ void nft_fib_store_result(void *reg, enum nft_fib_result r, *dreg = index; break; case NFT_FIB_RESULT_OIFNAME: - dev = dev_get_by_index_rcu(pkt->net, index); + dev = dev_get_by_index_rcu(nft_net(pkt), index); strncpy(reg, dev ? dev->name : "", IFNAMSIZ); break; default: diff --git a/net/netfilter/nft_fib_inet.c b/net/netfilter/nft_fib_inet.c index fe8943b..9120fc7 100644 --- a/net/netfilter/nft_fib_inet.c +++ b/net/netfilter/nft_fib_inet.c @@ -21,7 +21,7 @@ static void nft_fib_inet_eval(const struct nft_expr *expr, { const struct nft_fib *priv = nft_expr_priv(expr); - switch (pkt->pf) { + switch (nft_pf(pkt)) { case NFPROTO_IPV4: switch (priv->result) { case NFT_FIB_RESULT_OIF: diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index 1b01404..6271e40 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -32,8 +32,9 @@ static void nft_log_eval(const struct nft_expr *expr, { const struct nft_log *priv = nft_expr_priv(expr); - nf_log_packet(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in, - pkt->out, &priv->loginfo, "%s", priv->prefix); + nf_log_packet(nft_net(pkt), nft_pf(pkt), nft_hook(pkt), pkt->skb, + nft_in(pkt), nft_out(pkt), &priv->loginfo, "%s", + priv->prefix); } static const struct nla_policy nft_log_policy[NFTA_LOG_MAX + 1] = { diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 8166b69..a8ce49b 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -35,9 +35,8 @@ static void nft_lookup_eval(const struct nft_expr *expr, const struct nft_set_ext *ext; bool found; - found = set->ops->lookup(pkt->net, set, ®s->data[priv->sreg], &ext) ^ - priv->invert; - + found = set->ops->lookup(nft_net(pkt), set, ®s->data[priv->sreg], + &ext) ^ priv->invert; if (!found) { regs->verdict.code = NFT_BREAK; return; diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 6499402..66c7f4b 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -36,7 +36,7 @@ void nft_meta_get_eval(const struct nft_expr *expr, { const struct nft_meta *priv = nft_expr_priv(expr); const struct sk_buff *skb = pkt->skb; - const struct net_device *in = pkt->in, *out = pkt->out; + const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); struct sock *sk; u32 *dest = ®s->data[priv->dreg]; @@ -49,7 +49,7 @@ void nft_meta_get_eval(const struct nft_expr *expr, *(__be16 *)dest = skb->protocol; break; case NFT_META_NFPROTO: - *dest = pkt->pf; + *dest = nft_pf(pkt); break; case NFT_META_L4PROTO: if (!pkt->tprot_set) @@ -146,7 +146,7 @@ void nft_meta_get_eval(const struct nft_expr *expr, break; } - switch (pkt->pf) { + switch (nft_pf(pkt)) { case NFPROTO_IPV4: if (ipv4_is_multicast(ip_hdr(skb)->daddr)) *dest = PACKET_MULTICAST; diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index 393d359..3e19fa1 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -43,7 +43,7 @@ static void nft_queue_eval(const struct nft_expr *expr, queue = priv->queuenum + cpu % priv->queues_total; } else { queue = nfqueue_hash(pkt->skb, queue, - priv->queues_total, pkt->pf, + priv->queues_total, nft_pf(pkt), jhash_initval); } } diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c index e79d9ca..9e90a02 100644 --- a/net/netfilter/nft_reject_inet.c +++ b/net/netfilter/nft_reject_inet.c @@ -23,36 +23,36 @@ static void nft_reject_inet_eval(const struct nft_expr *expr, { struct nft_reject *priv = nft_expr_priv(expr); - switch (pkt->pf) { + switch (nft_pf(pkt)) { case NFPROTO_IPV4: switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: nf_send_unreach(pkt->skb, priv->icmp_code, - pkt->hook); + nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset(pkt->net, pkt->skb, pkt->hook); + nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt)); break; case NFT_REJECT_ICMPX_UNREACH: nf_send_unreach(pkt->skb, nft_reject_icmp_code(priv->icmp_code), - pkt->hook); + nft_hook(pkt)); break; } break; case NFPROTO_IPV6: switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nf_send_unreach6(pkt->net, pkt->skb, priv->icmp_code, - pkt->hook); + nf_send_unreach6(nft_net(pkt), pkt->skb, + priv->icmp_code, nft_hook(pkt)); break; case NFT_REJECT_TCP_RST: - nf_send_reset6(pkt->net, pkt->skb, pkt->hook); + nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt)); break; case NFT_REJECT_ICMPX_UNREACH: - nf_send_unreach6(pkt->net, pkt->skb, + nf_send_unreach6(nft_net(pkt), pkt->skb, nft_reject_icmpv6_code(priv->icmp_code), - pkt->hook); + nft_hook(pkt)); break; } break; diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index 9e5ec1f..d3eb640 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -43,14 +43,14 @@ void nft_rt_get_eval(const struct nft_expr *expr, break; #endif case NFT_RT_NEXTHOP4: - if (pkt->pf != NFPROTO_IPV4) + if (nft_pf(pkt) != NFPROTO_IPV4) goto err; *dest = rt_nexthop((const struct rtable *)dst, ip_hdr(skb)->daddr); break; case NFT_RT_NEXTHOP6: - if (pkt->pf != NFPROTO_IPV6) + if (nft_pf(pkt) != NFPROTO_IPV6) goto err; memcpy(dest, rt6_nexthop((struct rt6_info *)dst, -- cgit v1.1 From c63cbc4604199af6627633f2bb3847a8951f48e5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:30 +0100 Subject: netfilter: use switch() to handle verdict cases from nf_hook_slow() Use switch() for verdict handling and add explicit handling for NF_STOLEN and other non-conventional verdicts. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 14f97b6..6462337 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -328,22 +328,32 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state) { struct nf_hook_entry *entry; unsigned int verdict; - int ret = 0; + int ret; entry = rcu_dereference(state->hook_entries); next_hook: verdict = nf_iterate(skb, state, &entry); - if (verdict == NF_ACCEPT) { + switch (verdict & NF_VERDICT_MASK) { + case NF_ACCEPT: ret = 1; - } else if ((verdict & NF_VERDICT_MASK) == NF_DROP) { + break; + case NF_DROP: kfree_skb(skb); ret = NF_DROP_GETERR(verdict); if (ret == 0) ret = -EPERM; - } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { + break; + case NF_QUEUE: ret = nf_queue(skb, state, &entry, verdict); if (ret == 1 && entry) goto next_hook; + /* Fall through. */ + default: + /* Implicit handling for NF_STOLEN, as well as any other non + * conventional verdicts. + */ + ret = 0; + break; } return ret; } -- cgit v1.1 From 01886bd91f1ba418ce669dfe97a06ca9504e482a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:35 +0100 Subject: netfilter: remove hook_entries field from nf_hook_state This field is only useful for nf_queue, so store it in the nf_queue_entry structure instead, away from the core path. Pass hook_head to nf_hook_slow(). Since we always have a valid entry on the first iteration in nf_iterate(), we can use 'do { ... } while (entry)' loop instead. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 10 ++++------ include/linux/netfilter_ingress.h | 4 ++-- include/net/netfilter/nf_queue.h | 1 + net/bridge/br_netfilter_hooks.c | 4 ++-- net/bridge/netfilter/ebtable_broute.c | 2 +- net/netfilter/core.c | 9 ++++----- net/netfilter/nf_queue.c | 13 +++++-------- net/netfilter/nfnetlink_queue.c | 2 +- 8 files changed, 20 insertions(+), 25 deletions(-) diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index e0d000f..6923014 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -54,7 +54,6 @@ struct nf_hook_state { struct net_device *out; struct sock *sk; struct net *net; - struct nf_hook_entry __rcu *hook_entries; int (*okfn)(struct net *, struct sock *, struct sk_buff *); }; @@ -81,7 +80,6 @@ struct nf_hook_entry { }; static inline void nf_hook_state_init(struct nf_hook_state *p, - struct nf_hook_entry *hook_entry, unsigned int hook, u_int8_t pf, struct net_device *indev, @@ -96,7 +94,6 @@ static inline void nf_hook_state_init(struct nf_hook_state *p, p->out = outdev; p->sk = sk; p->net = net; - RCU_INIT_POINTER(p->hook_entries, hook_entry); p->okfn = okfn; } @@ -150,7 +147,8 @@ void nf_unregister_sockopt(struct nf_sockopt_ops *reg); extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; #endif -int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state); +int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, + struct nf_hook_entry *entry); /** * nf_hook - call a netfilter hook @@ -179,10 +177,10 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, if (hook_head) { struct nf_hook_state state; - nf_hook_state_init(&state, hook_head, hook, pf, indev, outdev, + nf_hook_state_init(&state, hook, pf, indev, outdev, sk, net, okfn); - ret = nf_hook_slow(skb, &state); + ret = nf_hook_slow(skb, &state, hook_head); } rcu_read_unlock(); diff --git a/include/linux/netfilter_ingress.h b/include/linux/netfilter_ingress.h index fd44e41..2dc3b49 100644 --- a/include/linux/netfilter_ingress.h +++ b/include/linux/netfilter_ingress.h @@ -26,10 +26,10 @@ static inline int nf_hook_ingress(struct sk_buff *skb) if (unlikely(!e)) return 0; - nf_hook_state_init(&state, e, NF_NETDEV_INGRESS, + nf_hook_state_init(&state, NF_NETDEV_INGRESS, NFPROTO_NETDEV, skb->dev, NULL, NULL, dev_net(skb->dev), NULL); - return nf_hook_slow(skb, &state); + return nf_hook_slow(skb, &state, e); } static inline void nf_hook_ingress_init(struct net_device *dev) diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 2280cfe..09948d1 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -12,6 +12,7 @@ struct nf_queue_entry { unsigned int id; struct nf_hook_state state; + struct nf_hook_entry *hook; u16 size; /* sizeof(entry) + saved route keys */ /* extra space to store route keys */ diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 7e3645f..8155bd2 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -1018,10 +1018,10 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, /* We may already have this, but read-locks nest anyway */ rcu_read_lock(); - nf_hook_state_init(&state, elem, hook, NFPROTO_BRIDGE, indev, outdev, + nf_hook_state_init(&state, hook, NFPROTO_BRIDGE, indev, outdev, sk, net, okfn); - ret = nf_hook_slow(skb, &state); + ret = nf_hook_slow(skb, &state, elem); rcu_read_unlock(); if (ret == 1) ret = okfn(net, sk, skb); diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index 599679e..8fe36dc 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -53,7 +53,7 @@ static int ebt_broute(struct sk_buff *skb) struct nf_hook_state state; int ret; - nf_hook_state_init(&state, NULL, NF_BR_BROUTING, + nf_hook_state_init(&state, NF_BR_BROUTING, NFPROTO_BRIDGE, skb->dev, NULL, NULL, dev_net(skb->dev), NULL); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 6462337..ebece48 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -308,7 +308,7 @@ unsigned int nf_iterate(struct sk_buff *skb, { unsigned int verdict; - while (*entryp) { + do { repeat: verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state); if (verdict != NF_ACCEPT) { @@ -317,20 +317,19 @@ repeat: goto repeat; } *entryp = rcu_dereference((*entryp)->next); - } + } while (*entryp); return NF_ACCEPT; } /* Returns 1 if okfn() needs to be executed by the caller, * -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock. */ -int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state) +int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, + struct nf_hook_entry *entry) { - struct nf_hook_entry *entry; unsigned int verdict; int ret; - entry = rcu_dereference(state->hook_entries); next_hook: verdict = nf_iterate(skb, state, &entry); switch (verdict & NF_VERDICT_MASK) { diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 0fb3896..2e39e38 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -108,7 +108,7 @@ void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry) } static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, - unsigned int queuenum) + struct nf_hook_entry *hook_entry, unsigned int queuenum) { int status = -ENOENT; struct nf_queue_entry *entry = NULL; @@ -136,6 +136,7 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, *entry = (struct nf_queue_entry) { .skb = skb, .state = *state, + .hook = hook_entry, .size = sizeof(*entry) + afinfo->route_key_size, }; @@ -163,8 +164,7 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, struct nf_hook_entry *entry = *entryp; int ret; - RCU_INIT_POINTER(state->hook_entries, entry); - ret = __nf_queue(skb, state, verdict >> NF_VERDICT_QBITS); + ret = __nf_queue(skb, state, entry, verdict >> NF_VERDICT_QBITS); if (ret < 0) { if (ret == -ESRCH && (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) { @@ -179,15 +179,12 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) { - struct nf_hook_entry *hook_entry; + struct nf_hook_entry *hook_entry = entry->hook; + struct nf_hook_ops *elem = &hook_entry->ops; struct sk_buff *skb = entry->skb; const struct nf_afinfo *afinfo; - struct nf_hook_ops *elem; int err; - hook_entry = rcu_dereference(entry->state.hook_entries); - elem = &hook_entry->ops; - nf_queue_entry_release_refs(entry); /* Continue traversal iff userspace said ok... */ diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 5379f78..1e33115b 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -919,7 +919,7 @@ static struct notifier_block nfqnl_dev_notifier = { static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long entry_ptr) { - return rcu_access_pointer(entry->state.hook_entries) == + return rcu_access_pointer(entry->hook) == (struct nf_hook_entry *)entry_ptr; } -- cgit v1.1 From 26dfab7216291cee94d6012d06c255fcc15cd72a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:39 +0100 Subject: netfilter: merge nf_iterate() into nf_hook_slow() nf_iterate() has become rather simple, we can integrate this code into nf_hook_slow() to reduce the amount of LOC in the core path. However, we still need nf_iterate() around for nf_queue packet handling, so move this function there where we only need it. I think it should be possible to refactor nf_queue code to get rid of it definitely, but given this is slow path anyway, let's have a look this later. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 73 +++++++++++++++++--------------------------- net/netfilter/nf_internals.h | 5 --- net/netfilter/nf_queue.c | 20 ++++++++++++ 3 files changed, 48 insertions(+), 50 deletions(-) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index ebece48..bd9272e 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -302,26 +302,6 @@ void _nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n) } EXPORT_SYMBOL(_nf_unregister_hooks); -unsigned int nf_iterate(struct sk_buff *skb, - struct nf_hook_state *state, - struct nf_hook_entry **entryp) -{ - unsigned int verdict; - - do { -repeat: - verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state); - if (verdict != NF_ACCEPT) { - if (verdict != NF_REPEAT) - return verdict; - goto repeat; - } - *entryp = rcu_dereference((*entryp)->next); - } while (*entryp); - return NF_ACCEPT; -} - - /* Returns 1 if okfn() needs to be executed by the caller, * -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock. */ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, @@ -330,31 +310,34 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, unsigned int verdict; int ret; -next_hook: - verdict = nf_iterate(skb, state, &entry); - switch (verdict & NF_VERDICT_MASK) { - case NF_ACCEPT: - ret = 1; - break; - case NF_DROP: - kfree_skb(skb); - ret = NF_DROP_GETERR(verdict); - if (ret == 0) - ret = -EPERM; - break; - case NF_QUEUE: - ret = nf_queue(skb, state, &entry, verdict); - if (ret == 1 && entry) - goto next_hook; - /* Fall through. */ - default: - /* Implicit handling for NF_STOLEN, as well as any other non - * conventional verdicts. - */ - ret = 0; - break; - } - return ret; + do { + verdict = entry->ops.hook(entry->ops.priv, skb, state); + switch (verdict & NF_VERDICT_MASK) { + case NF_ACCEPT: + entry = rcu_dereference(entry->next); + break; + case NF_DROP: + kfree_skb(skb); + ret = NF_DROP_GETERR(verdict); + if (ret == 0) + ret = -EPERM; + return ret; + case NF_REPEAT: + continue; + case NF_QUEUE: + ret = nf_queue(skb, state, &entry, verdict); + if (ret == 1 && entry) + continue; + return ret; + default: + /* Implicit handling for NF_STOLEN, as well as any other + * non conventional verdicts. + */ + return 0; + } + } while (entry); + + return 1; } EXPORT_SYMBOL(nf_hook_slow); diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 9fdb655..c46d214 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -11,11 +11,6 @@ #define NFDEBUG(format, args...) #endif - -/* core.c */ -unsigned int nf_iterate(struct sk_buff *skb, struct nf_hook_state *state, - struct nf_hook_entry **entryp); - /* nf_queue.c */ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, struct nf_hook_entry **entryp, unsigned int verdict); diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 2e39e38..77cba9f 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -177,6 +177,26 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, return 0; } +static unsigned int nf_iterate(struct sk_buff *skb, + struct nf_hook_state *state, + struct nf_hook_entry **entryp) +{ + unsigned int verdict; + + do { +repeat: + verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state); + if (verdict != NF_ACCEPT) { + if (verdict != NF_REPEAT) + return verdict; + goto repeat; + } + *entryp = rcu_dereference((*entryp)->next); + } while (*entryp); + + return NF_ACCEPT; +} + void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) { struct nf_hook_entry *hook_entry = entry->hook; -- cgit v1.1 From 08733a0cb7decce40bbbd0331a0449465f13c444 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 10:56:43 +0100 Subject: netfilter: handle NF_REPEAT from nf_conntrack_in() NF_REPEAT is only needed from nf_conntrack_in() under a very specific case required by the TCP protocol tracker, we can handle this case without returning to the core hook path. Handling of NF_REPEAT from the nf_reinject() is left untouched. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 2 -- net/netfilter/nf_conntrack_core.c | 11 ++++++----- net/openvswitch/conntrack.c | 8 ++------ 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index bd9272e..de30e08 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -322,8 +322,6 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, if (ret == 0) ret = -EPERM; return ret; - case NF_REPEAT: - continue; case NF_QUEUE: ret = nf_queue(skb, state, &entry, verdict); if (ret == 1 && entry) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index df2f5a3..de4b8a7 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1305,7 +1305,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, if (skb->nfct) goto out; } - +repeat: ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, l3proto, l4proto, &set_reply, &ctinfo); if (!ct) { @@ -1345,11 +1345,12 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, nf_conntrack_event_cache(IPCT_REPLY, ct); out: if (tmpl) { - /* Special case: we have to repeat this hook, assign the - * template again to this packet. We assume that this packet - * has no conntrack assigned. This is used by nf_ct_tcp. */ + /* Special case: TCP tracker reports an attempt to reopen a + * closed/aborted connection. We have to go back and create a + * fresh conntrack. + */ if (ret == NF_REPEAT) - skb->nfct = (struct nf_conntrack *)tmpl; + goto repeat; else nf_ct_put(tmpl); } diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 31045ef..9b8a028 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -725,12 +725,8 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key, skb->nfctinfo = IP_CT_NEW; } - /* Repeat if requested, see nf_iterate(). */ - do { - err = nf_conntrack_in(net, info->family, - NF_INET_PRE_ROUTING, skb); - } while (err == NF_REPEAT); - + err = nf_conntrack_in(net, info->family, + NF_INET_PRE_ROUTING, skb); if (err != NF_ACCEPT) return -ENOENT; -- cgit v1.1 From cd05a0eca8752c63bf79c10ae1a877d36cca995a Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 2 Nov 2016 15:36:31 +0300 Subject: net: ip, raw_diag -- Fix socket leaking for destroy request In raw_diag_destroy the helper raw_sock_get returns with sock_hold call, so we have to put it then. CC: David S. Miller CC: Eric Dumazet CC: David Ahern CC: Andrey Vagin CC: Stephen Hemminger Signed-off-by: Cyrill Gorcunov Acked-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/raw_diag.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index be93090..bf0b884 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -205,11 +205,14 @@ static int raw_diag_destroy(struct sk_buff *in_skb, { struct net *net = sock_net(in_skb->sk); struct sock *sk; + int err; sk = raw_sock_get(net, r); if (IS_ERR(sk)) return PTR_ERR(sk); - return sock_diag_destroy(sk, ECONNABORTED); + err = sock_diag_destroy(sk, ECONNABORTED); + sock_put(sk); + return err; } #endif -- cgit v1.1 From 9999370faed945be2913cd34aa24e84317a0ceda Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 2 Nov 2016 15:36:32 +0300 Subject: net: ip, raw_diag -- Use jump for exiting from nested loop I managed to miss that sk_for_each is called under "for" cycle so need to use goto here to return matching socket. CC: David S. Miller CC: Eric Dumazet CC: David Ahern CC: Andrey Vagin CC: Stephen Hemminger Signed-off-by: Cyrill Gorcunov Acked-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/raw_diag.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index bf0b884..e1a51ca 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -79,10 +79,11 @@ static struct sock *raw_sock_get(struct net *net, const struct inet_diag_req_v2 * hashinfo->lock here. */ sock_hold(sk); - break; + goto out_unlock; } } } +out_unlock: read_unlock(&hashinfo->lock); return sk ? sk : ERR_PTR(-ENOENT); -- cgit v1.1 From 5ed7414062e7cc63759529b9d82a1e73e86fe6c0 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 2 Nov 2016 15:02:36 +0100 Subject: net: stmmac: Add OXNAS Glue Driver Add Synopsys Designware MAC Glue layer for the Oxford Semiconductor OX820. Acked-by: Joachim Eastwood Signed-off-by: Neil Armstrong Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 ++ drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c | 217 ++++++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 3818c5e..6e9fcc3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -69,6 +69,17 @@ config DWMAC_MESON the stmmac device driver. This driver is used for Meson6, Meson8, Meson8b and GXBB SoCs. +config DWMAC_OXNAS + tristate "Oxford Semiconductor OXNAS dwmac support" + default ARCH_OXNAS + depends on OF && COMMON_CLK && (ARCH_OXNAS || COMPILE_TEST) + select MFD_SYSCON + help + Support for Ethernet controller on Oxford Semiconductor OXNAS SoCs. + + This selects the Oxford Semiconductor OXNASSoC glue layer support for + the stmmac device driver. This driver is used for OX820. + config DWMAC_ROCKCHIP tristate "Rockchip dwmac support" default ARCH_ROCKCHIP diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 5d6ece5..8f83a86 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o +obj-$(CONFIG_DWMAC_OXNAS) += dwmac-oxnas.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c new file mode 100644 index 0000000..c355975 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c @@ -0,0 +1,217 @@ +/* + * Oxford Semiconductor OXNAS DWMAC glue layer + * + * Copyright (C) 2016 Neil Armstrong + * Copyright (C) 2014 Daniel Golle + * Copyright (C) 2013 Ma Haijun + * Copyright (C) 2012 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stmmac_platform.h" + +/* System Control regmap offsets */ +#define OXNAS_DWMAC_CTRL_REGOFFSET 0x78 +#define OXNAS_DWMAC_DELAY_REGOFFSET 0x100 + +/* Control Register */ +#define DWMAC_CKEN_RX_IN 14 +#define DWMAC_CKEN_RXN_OUT 13 +#define DWMAC_CKEN_RX_OUT 12 +#define DWMAC_CKEN_TX_IN 10 +#define DWMAC_CKEN_TXN_OUT 9 +#define DWMAC_CKEN_TX_OUT 8 +#define DWMAC_RX_SOURCE 7 +#define DWMAC_TX_SOURCE 6 +#define DWMAC_LOW_TX_SOURCE 4 +#define DWMAC_AUTO_TX_SOURCE 3 +#define DWMAC_RGMII 2 +#define DWMAC_SIMPLE_MUX 1 +#define DWMAC_CKEN_GTX 0 + +/* Delay register */ +#define DWMAC_TX_VARDELAY_SHIFT 0 +#define DWMAC_TXN_VARDELAY_SHIFT 8 +#define DWMAC_RX_VARDELAY_SHIFT 16 +#define DWMAC_RXN_VARDELAY_SHIFT 24 +#define DWMAC_TX_VARDELAY(d) ((d) << DWMAC_TX_VARDELAY_SHIFT) +#define DWMAC_TXN_VARDELAY(d) ((d) << DWMAC_TXN_VARDELAY_SHIFT) +#define DWMAC_RX_VARDELAY(d) ((d) << DWMAC_RX_VARDELAY_SHIFT) +#define DWMAC_RXN_VARDELAY(d) ((d) << DWMAC_RXN_VARDELAY_SHIFT) + +struct oxnas_dwmac { + struct device *dev; + struct clk *clk; + struct regmap *regmap; +}; + +static int oxnas_dwmac_init(struct oxnas_dwmac *dwmac) +{ + unsigned int value; + int ret; + + /* Reset HW here before changing the glue configuration */ + ret = device_reset(dwmac->dev); + if (ret) + return ret; + + ret = clk_prepare_enable(dwmac->clk); + if (ret) + return ret; + + ret = regmap_read(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, &value); + if (ret < 0) { + clk_disable_unprepare(dwmac->clk); + return ret; + } + + /* Enable GMII_GTXCLK to follow GMII_REFCLK, required for gigabit PHY */ + value |= BIT(DWMAC_CKEN_GTX) | + /* Use simple mux for 25/125 Mhz clock switching */ + BIT(DWMAC_SIMPLE_MUX) | + /* set auto switch tx clock source */ + BIT(DWMAC_AUTO_TX_SOURCE) | + /* enable tx & rx vardelay */ + BIT(DWMAC_CKEN_TX_OUT) | + BIT(DWMAC_CKEN_TXN_OUT) | + BIT(DWMAC_CKEN_TX_IN) | + BIT(DWMAC_CKEN_RX_OUT) | + BIT(DWMAC_CKEN_RXN_OUT) | + BIT(DWMAC_CKEN_RX_IN); + regmap_write(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, value); + + /* set tx & rx vardelay */ + value = DWMAC_TX_VARDELAY(4) | + DWMAC_TXN_VARDELAY(2) | + DWMAC_RX_VARDELAY(10) | + DWMAC_RXN_VARDELAY(8); + regmap_write(dwmac->regmap, OXNAS_DWMAC_DELAY_REGOFFSET, value); + + return 0; +} + +static int oxnas_dwmac_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct device_node *sysctrl; + struct oxnas_dwmac *dwmac; + int ret; + + sysctrl = of_parse_phandle(pdev->dev.of_node, "oxsemi,sys-ctrl", 0); + if (!sysctrl) { + dev_err(&pdev->dev, "failed to get sys-ctrl node\n"); + return -EINVAL; + } + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); + if (IS_ERR(plat_dat)) + return PTR_ERR(plat_dat); + + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); + if (!dwmac) + return -ENOMEM; + + dwmac->dev = &pdev->dev; + plat_dat->bsp_priv = dwmac; + + dwmac->regmap = syscon_node_to_regmap(sysctrl); + if (IS_ERR(dwmac->regmap)) { + dev_err(&pdev->dev, "failed to have sysctrl regmap\n"); + return PTR_ERR(dwmac->regmap); + } + + dwmac->clk = devm_clk_get(&pdev->dev, "gmac"); + if (IS_ERR(dwmac->clk)) + return PTR_ERR(dwmac->clk); + + ret = oxnas_dwmac_init(dwmac); + if (ret) + return ret; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + clk_disable_unprepare(dwmac->clk); + + return ret; +} + +static int oxnas_dwmac_remove(struct platform_device *pdev) +{ + struct oxnas_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev); + int ret = stmmac_dvr_remove(&pdev->dev); + + clk_disable_unprepare(dwmac->clk); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int oxnas_dwmac_suspend(struct device *dev) +{ + struct oxnas_dwmac *dwmac = get_stmmac_bsp_priv(dev); + int ret; + + ret = stmmac_suspend(dev); + clk_disable_unprepare(dwmac->clk); + + return ret; +} + +static int oxnas_dwmac_resume(struct device *dev) +{ + struct oxnas_dwmac *dwmac = get_stmmac_bsp_priv(dev); + int ret; + + ret = oxnas_dwmac_init(dwmac); + if (ret) + return ret; + + ret = stmmac_resume(dev); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(oxnas_dwmac_pm_ops, + oxnas_dwmac_suspend, oxnas_dwmac_resume); + +static const struct of_device_id oxnas_dwmac_match[] = { + { .compatible = "oxsemi,ox820-dwmac" }, + { } +}; +MODULE_DEVICE_TABLE(of, oxnas_dwmac_match); + +static struct platform_driver oxnas_dwmac_driver = { + .probe = oxnas_dwmac_probe, + .remove = oxnas_dwmac_remove, + .driver = { + .name = "oxnas-dwmac", + .pm = &oxnas_dwmac_pm_ops, + .of_match_table = oxnas_dwmac_match, + }, +}; +module_platform_driver(oxnas_dwmac_driver); + +MODULE_AUTHOR("Neil Armstrong "); +MODULE_DESCRIPTION("Oxford Semiconductor OXNAS DWMAC glue layer"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 52b6c5c2180011ec6d3ecf31e3ab05df7796400c Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 2 Nov 2016 15:02:37 +0100 Subject: dt-bindings: net: Add OXNAS DWMAC Bindings Signed-off-by: Neil Armstrong Signed-off-by: David S. Miller --- .../devicetree/bindings/net/oxnas-dwmac.txt | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/oxnas-dwmac.txt diff --git a/Documentation/devicetree/bindings/net/oxnas-dwmac.txt b/Documentation/devicetree/bindings/net/oxnas-dwmac.txt new file mode 100644 index 0000000..df0534e --- /dev/null +++ b/Documentation/devicetree/bindings/net/oxnas-dwmac.txt @@ -0,0 +1,39 @@ +* Oxford Semiconductor OXNAS DWMAC Ethernet controller + +The device inherits all the properties of the dwmac/stmmac devices +described in the file stmmac.txt in the current directory with the +following changes. + +Required properties on all platforms: + +- compatible: For the OX820 SoC, it should be : + - "oxsemi,ox820-dwmac" to select glue + - "snps,dwmac-3.512" to select IP version. + +- clocks: Should contain phandles to the following clocks +- clock-names: Should contain the following: + - "stmmaceth" for the host clock - see stmmac.txt + - "gmac" for the peripheral gate clock + +- oxsemi,sys-ctrl: a phandle to the system controller syscon node + +Example : + +etha: ethernet@40400000 { + compatible = "oxsemi,ox820-dwmac", "snps,dwmac-3.512"; + reg = <0x40400000 0x2000>; + interrupts = , + ; + interrupt-names = "macirq", "eth_wake_irq"; + mac-address = [000000000000]; /* Filled in by U-Boot */ + phy-mode = "rgmii"; + + clocks = <&stdclk CLK_820_ETHA>, <&gmacclk>; + clock-names = "gmac", "stmmaceth"; + resets = <&reset RESET_MAC>; + + /* Regmap for sys registers */ + oxsemi,sys-ctrl = <&sys>; + + status = "disabled"; +}; -- cgit v1.1 From 70ecc24841326396a827deb55c3fefac582a729d Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 2 Nov 2016 11:02:16 -0400 Subject: ipv4: add IP_RECVFRAGSIZE cmsg The IP stack records the largest fragment of a reassembled packet in IPCB(skb)->frag_max_size. When reading a datagram or raw packet that arrived fragmented, expose the value to allow applications to estimate receive path MTU. Tested: Sent data over a veth pair of which the source has a small mtu. Sent data using netcat, received using a dedicated process. Verified that the cmsg IP_RECVFRAGSIZE is returned only when data arrives fragmented, and in that cases matches the veth mtu. ip link add veth0 type veth peer name veth1 ip netns add from ip netns add to ip link set dev veth1 netns to ip netns exec to ip addr add dev veth1 192.168.10.1/24 ip netns exec to ip link set dev veth1 up ip link set dev veth0 netns from ip netns exec from ip addr add dev veth0 192.168.10.2/24 ip netns exec from ip link set dev veth0 up ip netns exec from ip link set dev veth0 mtu 1300 ip netns exec from ethtool -K veth0 ufo off dd if=/dev/zero bs=1 count=1400 2>/dev/null > payload ip netns exec to ./recv_cmsg_recvfragsize -4 -u -p 6000 & ip netns exec from nc -q 1 -u 192.168.10.1 6000 < payload using github.com/wdebruij/kerneltools/blob/master/tests/recvfragsize.c Signed-off-by: Willem de Bruijn Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 1 + include/uapi/linux/in.h | 1 + net/ipv4/ip_sockglue.c | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 236a810..c9cff97 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -228,6 +228,7 @@ struct inet_sock { #define IP_CMSG_PASSSEC BIT(5) #define IP_CMSG_ORIGDSTADDR BIT(6) #define IP_CMSG_CHECKSUM BIT(7) +#define IP_CMSG_RECVFRAGSIZE BIT(8) /** * sk_to_full_sk - Access to a full socket diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index eaf9491..4e557f4 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -117,6 +117,7 @@ struct in_addr { #define IP_NODEFRAG 22 #define IP_CHECKSUM 23 #define IP_BIND_ADDRESS_NO_PORT 24 +#define IP_RECVFRAGSIZE 25 /* IP_MTU_DISCOVER values */ #define IP_PMTUDISC_DONT 0 /* Never send DF frames */ diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index b8a2d63..ecbaae2 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -97,6 +97,17 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); } +static void ip_cmsg_recv_fragsize(struct msghdr *msg, struct sk_buff *skb) +{ + int val; + + if (IPCB(skb)->frag_max_size == 0) + return; + + val = IPCB(skb)->frag_max_size; + put_cmsg(msg, SOL_IP, IP_RECVFRAGSIZE, sizeof(val), &val); +} + static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb, int tlen, int offset) { @@ -218,6 +229,9 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, if (flags & IP_CMSG_CHECKSUM) ip_cmsg_recv_checksum(msg, skb, tlen, offset); + + if (flags & IP_CMSG_RECVFRAGSIZE) + ip_cmsg_recv_fragsize(msg, skb); } EXPORT_SYMBOL(ip_cmsg_recv_offset); @@ -614,6 +628,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, case IP_MULTICAST_LOOP: case IP_RECVORIGDSTADDR: case IP_CHECKSUM: + case IP_RECVFRAGSIZE: if (optlen >= sizeof(int)) { if (get_user(val, (int __user *) optval)) return -EFAULT; @@ -726,6 +741,14 @@ static int do_ip_setsockopt(struct sock *sk, int level, } } break; + case IP_RECVFRAGSIZE: + if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM) + goto e_inval; + if (val) + inet->cmsg_flags |= IP_CMSG_RECVFRAGSIZE; + else + inet->cmsg_flags &= ~IP_CMSG_RECVFRAGSIZE; + break; case IP_TOS: /* This sets both TOS and Precedence */ if (sk->sk_type == SOCK_STREAM) { val &= ~INET_ECN_MASK; @@ -1357,6 +1380,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_CHECKSUM: val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0; break; + case IP_RECVFRAGSIZE: + val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0; + break; case IP_TOS: val = inet->tos; break; -- cgit v1.1 From 0cc0aa614b4c24b21b2492c0a1753035ee8c6edb Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 2 Nov 2016 11:02:17 -0400 Subject: ipv6: add IPV6_RECVFRAGSIZE cmsg When reading a datagram or raw packet that arrived fragmented, expose the maximum fragment size if recorded to allow applications to estimate receive path MTU. At this point, the field is only recorded when ipv6 connection tracking is enabled. A follow-up patch will record this field also in the ipv6 input path. Tested using the test for IP_RECVFRAGSIZE plus ip netns exec to ip addr add dev veth1 fc07::1/64 ip netns exec from ip addr add dev veth0 fc07::2/64 ip netns exec to ./recv_cmsg_recvfragsize -6 -u -p 6000 & ip netns exec from nc -q 1 -u fc07::1 6000 < payload Both with and without enabling connection tracking ip6tables -A INPUT -m state --state NEW -p udp -j LOG Signed-off-by: Willem de Bruijn Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/ipv6.h | 5 +++-- include/uapi/linux/in6.h | 1 + net/ipv6/datagram.c | 5 +++++ net/ipv6/ipv6_sockglue.c | 8 ++++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index ca1ad9e..1afb6e8 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -229,8 +229,9 @@ struct ipv6_pinfo { rxflow:1, rxtclass:1, rxpmtu:1, - rxorigdstaddr:1; - /* 2 bits hole */ + rxorigdstaddr:1, + recvfragsize:1; + /* 1 bits hole */ } bits; __u16 all; } rxopt; diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index b39ea4f..46444f8 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -283,6 +283,7 @@ struct in6_flowlabel_req { #define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR #define IPV6_TRANSPARENT 75 #define IPV6_UNICAST_IF 76 +#define IPV6_RECVFRAGSIZE 77 /* * Multicast Routing: diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 37874e2..620c79a 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -715,6 +715,11 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, put_cmsg(msg, SOL_IPV6, IPV6_ORIGDSTADDR, sizeof(sin6), &sin6); } } + if (np->rxopt.bits.recvfragsize && opt->frag_max_size) { + int val = opt->frag_max_size; + + put_cmsg(msg, SOL_IPV6, IPV6_RECVFRAGSIZE, sizeof(val), &val); + } } void ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 636ec56..6c12678 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -868,6 +868,10 @@ pref_skip_coa: np->autoflowlabel = valbool; retv = 0; break; + case IPV6_RECVFRAGSIZE: + np->rxopt.bits.recvfragsize = valbool; + retv = 0; + break; } release_sock(sk); @@ -1310,6 +1314,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val = np->autoflowlabel; break; + case IPV6_RECVFRAGSIZE: + val = np->rxopt.bits.recvfragsize; + break; + default: return -ENOPROTOOPT; } -- cgit v1.1 From dbd1759e6a9c4596d8609897c414da603da80914 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 2 Nov 2016 11:02:18 -0400 Subject: ipv6: on reassembly, record frag_max_size IP6CB and IPCB have a frag_max_size field. In IPv6 this field is filled in when packets are reassembled by the connection tracking code. Also fill in when reassembling in the input path, to expose it through cmsg IPV6_RECVFRAGSIZE in all cases. Signed-off-by: Willem de Bruijn Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv6/reassembly.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 3815e85..e1da5b8 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -211,7 +211,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, { struct sk_buff *prev, *next; struct net_device *dev; - int offset, end; + int offset, end, fragsize; struct net *net = dev_net(skb_dst(skb)->dev); u8 ecn; @@ -336,6 +336,10 @@ found: fq->ecn |= ecn; add_frag_mem_limit(fq->q.net, skb->truesize); + fragsize = -skb_network_offset(skb) + skb->len; + if (fragsize > fq->q.max_size) + fq->q.max_size = fragsize; + /* The first fragment. * nhoffset is obtained from the first fragment, of course. */ @@ -495,6 +499,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn); IP6CB(head)->nhoff = nhoff; IP6CB(head)->flags |= IP6SKB_FRAGMENTED; + IP6CB(head)->frag_max_size = fq->q.max_size; /* Yes, and fold redundant checksum back. 8) */ skb_postpush_rcsum(head, skb_network_header(head), -- cgit v1.1 From 5e5f89e70b29153cb28011f0cc47984bfcc3bef4 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Thu, 3 Nov 2016 09:41:55 +0100 Subject: mlxsw: pci: Fix the FW ready mask length The system-status register is actually 16-bit wide and not 8 bit-wide. Fixes: 233fa44bd67ae ("mlxsw: pci: Implement reset done check") Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/pci_hw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h index 708736f..d147ddd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h @@ -60,7 +60,7 @@ #define MLXSW_PCI_SW_RESET_RST_BIT BIT(0) #define MLXSW_PCI_SW_RESET_TIMEOUT_MSECS 5000 #define MLXSW_PCI_FW_READY 0xA1844 -#define MLXSW_PCI_FW_READY_MASK 0xFF +#define MLXSW_PCI_FW_READY_MASK 0xFFFF #define MLXSW_PCI_FW_READY_MAGIC 0x5E #define MLXSW_PCI_DOORBELL_SDQ_OFFSET 0x000 -- cgit v1.1 From 5976c5f45c40588b90dda173ded9010917f8f45e Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Thu, 3 Nov 2016 13:24:21 +0100 Subject: net/sched: cls_flower: Support matching on SCTP ports Support matching on SCTP ports in the same way that matching on TCP and UDP ports is already supported. Example usage: tc qdisc add dev eth0 ingress tc filter add dev eth0 protocol ip parent ffff: \ flower indev eth0 ip_proto sctp dst_port 80 \ action drop Signed-off-by: Simon Horman Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 5 +++++ net/sched/cls_flower.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 8fd715f..eb94781 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -447,6 +447,11 @@ enum { TCA_FLOWER_KEY_TCP_DST_MASK, /* be16 */ TCA_FLOWER_KEY_UDP_SRC_MASK, /* be16 */ TCA_FLOWER_KEY_UDP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST_MASK, /* be16 */ + + TCA_FLOWER_KEY_SCTP_SRC, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST, /* be16 */ __TCA_FLOWER_MAX, }; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index a8fb1ca..db4cd88 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -344,6 +344,10 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, }; static void fl_set_key_val(struct nlattr **tb, @@ -453,6 +457,13 @@ static int fl_set_key(struct net *net, struct nlattr **tb, fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, sizeof(key->tp.dst)); + } else if (key->basic.ip_proto == IPPROTO_SCTP) { + fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, + &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, + sizeof(key->tp.src)); + fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, + &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, + sizeof(key->tp.dst)); } if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || @@ -897,6 +908,14 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, sizeof(key->tp.dst)))) goto nla_put_failure; + else if (key->basic.ip_proto == IPPROTO_SCTP && + (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, + &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, + sizeof(key->tp.src)) || + fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, + &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, + sizeof(key->tp.dst)))) + goto nla_put_failure; if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && (fl_dump_key_val(skb, &key->enc_ipv4.src, -- cgit v1.1 From 18abed211c0503167a566d3ff330dc7ca44e5fc6 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:26 +0100 Subject: net: dsa: mv88e6xxx: add port files The Marvell switches contains one internal SMI device per port, called "Port Registers". Depending on the model, the addresses of these devices start from 0x0, 0x8 or 0x10. Start moving Port Registers specific code to their own files. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/Makefile | 1 + drivers/net/dsa/mv88e6xxx/chip.c | 17 +---------------- drivers/net/dsa/mv88e6xxx/port.c | 31 +++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 24 ++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 drivers/net/dsa/mv88e6xxx/port.c create mode 100644 drivers/net/dsa/mv88e6xxx/port.h diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index 10ce820..c36be31 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o mv88e6xxx-objs := chip.o mv88e6xxx-objs += global1.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o +mv88e6xxx-objs += port.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 72b9dac2..b32b242 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -37,6 +37,7 @@ #include "mv88e6xxx.h" #include "global1.h" #include "global2.h" +#include "port.h" static void assert_reg_lock(struct mv88e6xxx_chip *chip) { @@ -221,22 +222,6 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) return 0; } -static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, - u16 *val) -{ - int addr = chip->info->port_base_addr + port; - - return mv88e6xxx_read(chip, addr, reg, val); -} - -static int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, - u16 val) -{ - int addr = chip->info->port_base_addr + port; - - return mv88e6xxx_write(chip, addr, reg, val); -} - static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, int reg, u16 *val) { diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c new file mode 100644 index 0000000..3b36c92 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -0,0 +1,31 @@ +/* + * Marvell 88E6xxx Switch Port Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016 Vivien Didelot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "mv88e6xxx.h" +#include "port.h" + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, + u16 *val) +{ + int addr = chip->info->port_base_addr + port; + + return mv88e6xxx_read(chip, addr, reg, val); +} + +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, + u16 val) +{ + int addr = chip->info->port_base_addr + port; + + return mv88e6xxx_write(chip, addr, reg, val); +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h new file mode 100644 index 0000000..ae1ae2b --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -0,0 +1,24 @@ +/* + * Marvell 88E6xxx Switch Port Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016 Vivien Didelot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _MV88E6XXX_PORT_H +#define _MV88E6XXX_PORT_H + +#include "mv88e6xxx.h" + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, + u16 *val); +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, + u16 val); + +#endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From e28def33291c2b1e3a53da2dddd46a04f9f985ab Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:27 +0100 Subject: net: dsa: mv88e6xxx: add port state setter Add the port STP state setter to the port files. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 49 ++++------------------------------------ drivers/net/dsa/mv88e6xxx/port.c | 31 +++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 3 files changed, 37 insertions(+), 45 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b32b242..12c1175 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1215,41 +1215,6 @@ static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too); } -static const char * const mv88e6xxx_port_state_names[] = { - [PORT_CONTROL_STATE_DISABLED] = "Disabled", - [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening", - [PORT_CONTROL_STATE_LEARNING] = "Learning", - [PORT_CONTROL_STATE_FORWARDING] = "Forwarding", -}; - -static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port, - u8 state) -{ - struct dsa_switch *ds = chip->ds; - u16 reg; - int err; - u8 oldstate; - - err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); - if (err) - return err; - - oldstate = reg & PORT_CONTROL_STATE_MASK; - - reg &= ~PORT_CONTROL_STATE_MASK; - reg |= state; - - err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); - if (err) - return err; - - netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n", - mv88e6xxx_port_state_names[state], - mv88e6xxx_port_state_names[oldstate]); - - return 0; -} - static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) { struct net_device *bridge = chip->ports[port].bridge_dev; @@ -1313,13 +1278,11 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, } mutex_lock(&chip->reg_lock); - err = _mv88e6xxx_port_state(chip, port, stp_state); + err = mv88e6xxx_port_set_state(chip, port, stp_state); mutex_unlock(&chip->reg_lock); if (err) - netdev_err(ds->ports[port].netdev, - "failed to update state to %s\n", - mv88e6xxx_port_state_names[stp_state]); + netdev_err(ds->ports[port].netdev, "failed to update state\n"); } static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) @@ -2526,12 +2489,8 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) /* Set all ports to the disabled state. */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { - err = mv88e6xxx_port_read(chip, i, PORT_CONTROL, ®); - if (err) - return err; - - err = mv88e6xxx_port_write(chip, i, PORT_CONTROL, - reg & 0xfffc); + err = mv88e6xxx_port_set_state(chip, i, + PORT_CONTROL_STATE_DISABLED); if (err) return err; } diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 3b36c92..8d59fe7 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -29,3 +29,34 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, return mv88e6xxx_write(chip, addr, reg, val); } + +/* Offset 0x04: Port Control Register */ + +static const char * const mv88e6xxx_port_state_names[] = { + [PORT_CONTROL_STATE_DISABLED] = "Disabled", + [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening", + [PORT_CONTROL_STATE_LEARNING] = "Learning", + [PORT_CONTROL_STATE_FORWARDING] = "Forwarding", +}; + +int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); + if (err) + return err; + + reg &= ~PORT_CONTROL_STATE_MASK; + reg |= state; + + err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); + if (err) + return err; + + netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n", + mv88e6xxx_port_state_names[state]); + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index ae1ae2b..ac13988 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -21,4 +21,6 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, u16 val); +int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); + #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From 5a7921f46df2aded2c3e49b3e2104a0d1041dea4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:28 +0100 Subject: net: dsa: mv88e6xxx: add port vlan map setter Add a port function to access the Port Based VLAN Map register. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 14 ++------------ drivers/net/dsa/mv88e6xxx/port.c | 25 +++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 12c1175..0877777 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1218,16 +1218,13 @@ static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) { struct net_device *bridge = chip->ports[port].bridge_dev; - const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1; struct dsa_switch *ds = chip->ds; u16 output_ports = 0; - u16 reg; - int err; int i; /* allow CPU port or DSA link(s) to send frames to every port */ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { - output_ports = mask; + output_ports = ~0; } else { for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { /* allow sending frames to every group member */ @@ -1243,14 +1240,7 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) /* prevent frames from going back out of the port they came in on */ output_ports &= ~BIT(port); - err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); - if (err) - return err; - - reg &= ~mask; - reg |= output_ports & mask; - - return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); + return mv88e6xxx_port_set_vlan_map(chip, port, output_ports); } static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 8d59fe7..c6a22ae 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -60,3 +60,28 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state) return 0; } + +/* Offset 0x06: Port Based VLAN Map */ + +int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) +{ + const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0); + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); + if (err) + return err; + + reg &= ~mask; + reg |= map & mask; + + err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); + if (err) + return err; + + netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n", + map); + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index ac13988..037d638 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -23,4 +23,6 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); +int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); + #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From b4e48c500eb80120df1a4259a04b131e71c5ca62 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:29 +0100 Subject: net: dsa: mv88e6xxx: add port FID accessors Add functions to port files to access the ports default FID. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 77 +++------------------------------------- drivers/net/dsa/mv88e6xxx/port.c | 67 ++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 3 ++ 3 files changed, 74 insertions(+), 73 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 0877777..20f59f1 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1674,75 +1674,6 @@ loadpurge: return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); } -static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port, - u16 *new, u16 *old) -{ - struct dsa_switch *ds = chip->ds; - u16 upper_mask; - u16 fid; - u16 reg; - int err; - - if (mv88e6xxx_num_databases(chip) == 4096) - upper_mask = 0xff; - else if (mv88e6xxx_num_databases(chip) == 256) - upper_mask = 0xf; - else - return -EOPNOTSUPP; - - /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */ - err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); - if (err) - return err; - - fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12; - - if (new) { - reg &= ~PORT_BASE_VLAN_FID_3_0_MASK; - reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK; - - err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); - if (err) - return err; - } - - /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */ - err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); - if (err) - return err; - - fid |= (reg & upper_mask) << 4; - - if (new) { - reg &= ~upper_mask; - reg |= (*new >> 4) & upper_mask; - - err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg); - if (err) - return err; - - netdev_dbg(ds->ports[port].netdev, - "FID %d (was %d)\n", *new, fid); - } - - if (old) - *old = fid; - - return 0; -} - -static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip, - int port, u16 *fid) -{ - return _mv88e6xxx_port_fid(chip, port, NULL, fid); -} - -static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip, - int port, u16 fid) -{ - return _mv88e6xxx_port_fid(chip, port, &fid, NULL); -} - static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) { DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); @@ -1753,7 +1684,7 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) /* Set every FID bit used by the (un)bridged ports */ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - err = _mv88e6xxx_port_fid_get(chip, i, fid); + err = mv88e6xxx_port_get_fid(chip, i, fid); if (err) return err; @@ -2203,7 +2134,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, /* Null VLAN ID corresponds to the port private database */ if (vid == 0) - err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid); + err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid); else err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); if (err) @@ -2379,7 +2310,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, int err; /* Dump port's default Filtering Information Database (VLAN ID 0) */ - err = _mv88e6xxx_port_fid_get(chip, port, &fid); + err = mv88e6xxx_port_get_fid(chip, port, &fid); if (err) return err; @@ -2782,7 +2713,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) * database, and allow bidirectional communication between the * CPU and DSA port(s), and the other ports. */ - err = _mv88e6xxx_port_fid_set(chip, port, 0); + err = mv88e6xxx_port_set_fid(chip, port, 0); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index c6a22ae..a7da812 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -61,6 +61,8 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state) return 0; } +/* Offset 0x05: Port Control 1 */ + /* Offset 0x06: Port Based VLAN Map */ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) @@ -85,3 +87,68 @@ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) return 0; } + +int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid) +{ + const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4; + u16 reg; + int err; + + /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */ + err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); + if (err) + return err; + + *fid = (reg & 0xf000) >> 12; + + /* Port's default FID upper bits are located in reg 0x05, offset 0 */ + if (upper_mask) { + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); + if (err) + return err; + + *fid |= (reg & upper_mask) << 4; + } + + return 0; +} + +int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid) +{ + const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4; + u16 reg; + int err; + + if (fid >= mv88e6xxx_num_databases(chip)) + return -EINVAL; + + /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */ + err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); + if (err) + return err; + + reg &= 0x0fff; + reg |= (fid & 0x000f) << 12; + + err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); + if (err) + return err; + + /* Port's default FID upper bits are located in reg 0x05, offset 0 */ + if (upper_mask) { + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); + if (err) + return err; + + reg &= ~upper_mask; + reg |= (fid >> 4) & upper_mask; + + err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg); + if (err) + return err; + } + + netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid); + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 037d638..0df29b9 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -25,4 +25,7 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); +int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid); +int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid); + #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From 77064f37b9c26e13290c06f90951d8f0482837dc Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:30 +0100 Subject: net: dsa: mv88e6xxx: add port PVID accessors Add port functions to access the ports default VID. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 51 ++++------------------------------------ drivers/net/dsa/mv88e6xxx/port.c | 38 ++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 3 +++ 3 files changed, 45 insertions(+), 47 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 20f59f1..9c0a028 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1288,49 +1288,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); } -static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port, - u16 *new, u16 *old) -{ - struct dsa_switch *ds = chip->ds; - u16 pvid, reg; - int err; - - err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); - if (err) - return err; - - pvid = reg & PORT_DEFAULT_VLAN_MASK; - - if (new) { - reg &= ~PORT_DEFAULT_VLAN_MASK; - reg |= *new & PORT_DEFAULT_VLAN_MASK; - - err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg); - if (err) - return err; - - netdev_dbg(ds->ports[port].netdev, - "DefaultVID %d (was %d)\n", *new, pvid); - } - - if (old) - *old = pvid; - - return 0; -} - -static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip, - int port, u16 *pvid) -{ - return _mv88e6xxx_port_pvid(chip, port, NULL, pvid); -} - -static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip, - int port, u16 pvid) -{ - return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL); -} - static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip) { return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); @@ -1510,7 +1467,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); - err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); + err = mv88e6xxx_port_get_pvid(chip, port, &pvid); if (err) goto unlock; @@ -1958,7 +1915,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, "failed to add VLAN %d%c\n", vid, untagged ? 'u' : 't'); - if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end)) + if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end)) netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n", vlan->vid_end); @@ -2013,7 +1970,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); - err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); + err = mv88e6xxx_port_get_pvid(chip, port, &pvid); if (err) goto unlock; @@ -2023,7 +1980,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, goto unlock; if (vid == pvid) { - err = _mv88e6xxx_port_pvid_set(chip, port, 0); + err = mv88e6xxx_port_set_pvid(chip, port, 0); if (err) goto unlock; } diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index a7da812..104fe2d 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -152,3 +152,41 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid) return 0; } + +/* Offset 0x07: Default Port VLAN ID & Priority */ + +int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); + if (err) + return err; + + *pvid = reg & PORT_DEFAULT_VLAN_MASK; + + return 0; +} + +int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); + if (err) + return err; + + reg &= ~PORT_DEFAULT_VLAN_MASK; + reg |= pvid & PORT_DEFAULT_VLAN_MASK; + + err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg); + if (err) + return err; + + netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n", + pvid); + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 0df29b9..4489d9e 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -28,4 +28,7 @@ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid); int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid); +int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid); +int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid); + #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From 385a0995cc646d5df0ec06e375db47225776ab31 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:31 +0100 Subject: net: dsa: mv88e6xxx: add port 802.1Q mode setter Add port functions to set the port 802.1Q mode. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 33 ++------------------------------- drivers/net/dsa/mv88e6xxx/port.c | 32 ++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 3 +++ 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 9c0a028..181d3b9 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1806,48 +1806,19 @@ unlock: return err; } -static const char * const mv88e6xxx_port_8021q_mode_names[] = { - [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled", - [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback", - [PORT_CONTROL_2_8021Q_CHECK] = "Check", - [PORT_CONTROL_2_8021Q_SECURE] = "Secure", -}; - static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) { struct mv88e6xxx_chip *chip = ds->priv; - u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : + u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : PORT_CONTROL_2_8021Q_DISABLED; - u16 reg; int err; if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) return -EOPNOTSUPP; mutex_lock(&chip->reg_lock); - - err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); - if (err) - goto unlock; - - old = reg & PORT_CONTROL_2_8021Q_MASK; - - if (new != old) { - reg &= ~PORT_CONTROL_2_8021Q_MASK; - reg |= new & PORT_CONTROL_2_8021Q_MASK; - - err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); - if (err) - goto unlock; - - netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n", - mv88e6xxx_port_8021q_mode_names[new], - mv88e6xxx_port_8021q_mode_names[old]); - } - - err = 0; -unlock: + err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); mutex_unlock(&chip->reg_lock); return err; diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 104fe2d..53d17e6 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -190,3 +190,35 @@ int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid) return 0; } + +/* Offset 0x08: Port Control 2 Register */ + +static const char * const mv88e6xxx_port_8021q_mode_names[] = { + [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled", + [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback", + [PORT_CONTROL_2_8021Q_CHECK] = "Check", + [PORT_CONTROL_2_8021Q_SECURE] = "Secure", +}; + +int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, + u16 mode) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); + if (err) + return err; + + reg &= ~PORT_CONTROL_2_8021Q_MASK; + reg |= mode & PORT_CONTROL_2_8021Q_MASK; + + err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); + if (err) + return err; + + netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n", + mv88e6xxx_port_8021q_mode_names[mode]); + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 4489d9e..921eecf 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -31,4 +31,7 @@ int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid); int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid); int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid); +int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, + u16 mode); + #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From 08ef7f1022e85539d5e3ce925e1fab60b205ae39 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:32 +0100 Subject: net: dsa: mv88e6xxx: add port link setter Most of the chips will have a port register control bits to force the port's link up, down, or let normal link detection occurs. Implement such operation to use it later when setting duplex, etc. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 17 +++++++++++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 10 +++++++++ drivers/net/dsa/mv88e6xxx/port.c | 41 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 4 files changed, 70 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 181d3b9..cc43e6f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3160,42 +3160,49 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6095_ops = { .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6123_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6131_ops = { .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6161_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6165_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6171_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3204,12 +3211,14 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6175_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3218,12 +3227,14 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6185_ops = { .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3232,6 +3243,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3240,6 +3252,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3248,18 +3261,21 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6350_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6351_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3268,6 +3284,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 6f48e58..474d16d 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -727,6 +727,16 @@ struct mv88e6xxx_ops { u16 *val); int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); + +#define LINK_FORCED_DOWN 0 +#define LINK_FORCED_UP 1 +#define LINK_UNFORCED -2 + + /* Port's MAC link state + * Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down, + * or LINK_UNFORCED for normal link detection. + */ + int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link); }; enum stat_type { diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 53d17e6..09e25f9 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -30,6 +30,47 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, return mv88e6xxx_write(chip, addr, reg, val); } +/* Offset 0x01: MAC (or PCS or Physical) Control Register + * + * Link, Duplex and Flow Control have one force bit, one value bit. + */ + +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); + if (err) + return err; + + reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP); + + switch (link) { + case LINK_FORCED_DOWN: + reg |= PORT_PCS_CTRL_FORCE_LINK; + break; + case LINK_FORCED_UP: + reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP; + break; + case LINK_UNFORCED: + /* normal link detection */ + break; + default: + return -EINVAL; + } + + err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); + if (err) + return err; + + netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n", + reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce", + reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down"); + + return 0; +} + /* Offset 0x04: Port Control Register */ static const char * const mv88e6xxx_port_state_names[] = { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 921eecf..87e7249 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -21,6 +21,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, u16 val); +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); + int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); -- cgit v1.1 From 7f1ae07b51e8b617922b2b73850361eafa17a684 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:33 +0100 Subject: net: dsa: mv88e6xxx: add port duplex setter Similarly to port's link, add setter to force port's half duplex, full duplex or let normal duplex detection occurs. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 17 +++++++++++++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 9 +++++++++ drivers/net/dsa/mv88e6xxx/port.c | 36 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 4 files changed, 64 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index cc43e6f..49a6935 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3161,6 +3161,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3168,6 +3169,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3175,6 +3177,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3182,6 +3185,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3189,6 +3193,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3196,6 +3201,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3203,6 +3209,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3212,6 +3219,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3219,6 +3227,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3228,6 +3237,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3235,6 +3245,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3244,6 +3255,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3253,6 +3265,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3262,6 +3275,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3269,6 +3283,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3276,6 +3291,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3285,6 +3301,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 474d16d..ab48eb9 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -737,6 +737,15 @@ struct mv88e6xxx_ops { * or LINK_UNFORCED for normal link detection. */ int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link); + +#define DUPLEX_UNFORCED -2 + + /* Port's MAC duplex mode + * + * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, + * or DUPLEX_UNFORCED for normal duplex detection. + */ + int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); }; enum stat_type { diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 09e25f9..17b5444 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -71,6 +71,42 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) return 0; } +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); + if (err) + return err; + + reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL); + + switch (dup) { + case DUPLEX_HALF: + reg |= PORT_PCS_CTRL_FORCE_DUPLEX; + break; + case DUPLEX_FULL: + reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL; + break; + case DUPLEX_UNFORCED: + /* normal duplex detection */ + break; + default: + return -EINVAL; + } + + err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); + if (err) + return err; + + netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n", + reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce", + reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half"); + + return 0; +} + /* Offset 0x04: Port Control Register */ static const char * const mv88e6xxx_port_state_names[] = { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 87e7249..f73e90e 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -23,6 +23,8 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); + int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); -- cgit v1.1 From a0a0f6229b81c6398b6614767a2e227c9224a38b Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:34 +0100 Subject: net: dsa: mv88e6xxx: add port's RGMII delay setter Some chips such as 88E6352 and 88E6390 can be programmed to add delays to RXCLK for IND inputs or to GTXCLK for OUTD outputs when port is in RGMII mode. Add a port function to program such delays according to the provided PHY interface mode. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 4 +++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 6 ++++ drivers/net/dsa/mv88e6xxx/port.c | 58 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 5 +++ 4 files changed, 73 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 49a6935..bb93d0a 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3220,6 +3220,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3238,6 +3239,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3256,6 +3258,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3302,6 +3305,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index ab48eb9..c7527c0 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -728,6 +728,12 @@ struct mv88e6xxx_ops { int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); + /* RGMII Receive/Transmit Timing Control + * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise. + */ + int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); + #define LINK_FORCED_DOWN 0 #define LINK_FORCED_UP 1 #define LINK_UNFORCED -2 diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 17b5444..838068d 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -35,6 +35,64 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, * Link, Duplex and Flow Control have one force bit, one value bit. */ +static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); + if (err) + return err; + + reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK | + PORT_PCS_CTRL_RGMII_DELAY_TXCLK); + + switch (mode) { + case PHY_INTERFACE_MODE_RGMII_RXID: + reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK | + PORT_PCS_CTRL_RGMII_DELAY_TXCLK; + break; + default: + /* no delay */ + break; + } + + err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); + if (err) + return err; + + netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n", + reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no", + reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no"); + + return 0; +} + +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port < 5) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port != 0) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) { u16 reg; diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index f73e90e..3472b79 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -21,6 +21,11 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, u16 val); +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); + int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); -- cgit v1.1 From 96a2b40c7bd32a955a47a4d521afa4940886f4e5 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:35 +0100 Subject: net: dsa: mv88e6xxx: add port's MAC speed setter While the two bits for link, duplex or RGMII delays are used the same way on chips supporting the said feature, the two bits for speed have different meaning for most of the chips out there. Speed value is stored in bits 1:0, 0x3 means unforce (normal detection). Some chips reuse values for alternative speeds when bit 12 is set. Newer chips with speed > 1Gbps reuse value 0x3 thus need a new bit 13. Here are the values to write in register 0x1 to (un)force speed: | Speed | 88E6065 | 88E6185 | 88E6352 | 88E6390 | 88E6390X | | ------- | ------- | ------- | ------- | ------- | -------- | | 10 | 0x0000 | 0x0000 | 0x0000 | 0x2000 | 0x2000 | | 100 | 0x0001 | 0x0001 | 0x0001 | 0x2001 | 0x2001 | | 200 | 0x0002 | NA | 0x1001 | 0x3001 | 0x3001 | | 1000 | NA | 0x0002 | 0x0002 | 0x2002 | 0x2002 | | 2500 | NA | NA | NA | 0x3003 | 0x3003 | | 10000 | NA | NA | NA | NA | 0x2003 | | unforce | 0x0003 | 0x0003 | 0x0003 | 0x0000 | 0x0000 | This patch implements a generic mv88e6xxx_port_set_speed() function used by chip-specific wrappers to filter supported ports and speeds. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 33 ++++++-- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 24 +++++- drivers/net/dsa/mv88e6xxx/port.c | 138 ++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 6 ++ 4 files changed, 189 insertions(+), 12 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index bb93d0a..23c05e3 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -725,7 +725,7 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_DUPLEX_FULL | PORT_PCS_CTRL_FORCE_DUPLEX | - PORT_PCS_CTRL_UNFORCED); + PORT_PCS_CTRL_SPEED_UNFORCED); reg |= PORT_PCS_CTRL_FORCE_LINK; if (phydev->link) @@ -736,13 +736,13 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, switch (phydev->speed) { case SPEED_1000: - reg |= PORT_PCS_CTRL_1000; + reg |= PORT_PCS_CTRL_SPEED_1000; break; case SPEED_100: - reg |= PORT_PCS_CTRL_100; + reg |= PORT_PCS_CTRL_SPEED_100; break; case SPEED_10: - reg |= PORT_PCS_CTRL_10; + reg |= PORT_PCS_CTRL_SPEED_10; break; default: pr_info("Unknown speed"); @@ -2421,17 +2421,17 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) */ err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { - reg &= ~PORT_PCS_CTRL_UNFORCED; + reg &= ~PORT_PCS_CTRL_SPEED_UNFORCED; reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP | PORT_PCS_CTRL_DUPLEX_FULL | PORT_PCS_CTRL_FORCE_DUPLEX; if (mv88e6xxx_6065_family(chip)) - reg |= PORT_PCS_CTRL_100; + reg |= PORT_PCS_CTRL_SPEED_100; else - reg |= PORT_PCS_CTRL_1000; + reg |= PORT_PCS_CTRL_SPEED_1000; } else { - reg |= PORT_PCS_CTRL_UNFORCED; + reg |= PORT_PCS_CTRL_SPEED_UNFORCED; } err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); @@ -3162,6 +3162,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3170,6 +3171,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3178,6 +3180,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .phy_write = mv88e6xxx_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3186,6 +3189,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3194,6 +3198,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .phy_write = mv88e6xxx_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3202,6 +3207,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .phy_write = mv88e6xxx_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3210,6 +3216,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3221,6 +3228,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3229,6 +3237,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3240,6 +3249,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3248,6 +3258,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .phy_write = mv88e6xxx_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3259,6 +3270,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3269,6 +3281,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3279,6 +3292,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3287,6 +3301,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3295,6 +3310,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3306,6 +3322,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index c7527c0..9296130 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -61,16 +61,22 @@ #define PORT_PCS_CTRL 0x01 #define PORT_PCS_CTRL_RGMII_DELAY_RXCLK BIT(15) #define PORT_PCS_CTRL_RGMII_DELAY_TXCLK BIT(14) +#define PORT_PCS_CTRL_FORCE_SPEED BIT(13) /* 6390 */ +#define PORT_PCS_CTRL_ALTSPEED BIT(12) /* 6390 */ +#define PORT_PCS_CTRL_200BASE BIT(12) /* 6352 */ #define PORT_PCS_CTRL_FC BIT(7) #define PORT_PCS_CTRL_FORCE_FC BIT(6) #define PORT_PCS_CTRL_LINK_UP BIT(5) #define PORT_PCS_CTRL_FORCE_LINK BIT(4) #define PORT_PCS_CTRL_DUPLEX_FULL BIT(3) #define PORT_PCS_CTRL_FORCE_DUPLEX BIT(2) -#define PORT_PCS_CTRL_10 0x00 -#define PORT_PCS_CTRL_100 0x01 -#define PORT_PCS_CTRL_1000 0x02 -#define PORT_PCS_CTRL_UNFORCED 0x03 +#define PORT_PCS_CTRL_SPEED_MASK (0x03) +#define PORT_PCS_CTRL_SPEED_10 (0x00) +#define PORT_PCS_CTRL_SPEED_100 (0x01) +#define PORT_PCS_CTRL_SPEED_200 (0x02) /* 6065 and non Gb chips */ +#define PORT_PCS_CTRL_SPEED_1000 (0x02) +#define PORT_PCS_CTRL_SPEED_10000 (0x03) /* 6390X */ +#define PORT_PCS_CTRL_SPEED_UNFORCED (0x03) #define PORT_PAUSE_CTRL 0x02 #define PORT_SWITCH_ID 0x03 #define PORT_SWITCH_ID_PROD_NUM_6085 0x04a @@ -752,6 +758,16 @@ struct mv88e6xxx_ops { * or DUPLEX_UNFORCED for normal duplex detection. */ int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); + +#define SPEED_MAX INT_MAX +#define SPEED_UNFORCED -2 + + /* Port's MAC speed (in Mbps) + * + * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid. + * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value. + */ + int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); }; enum stat_type { diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 838068d..18eeed0 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -33,6 +33,10 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, /* Offset 0x01: MAC (or PCS or Physical) Control Register * * Link, Duplex and Flow Control have one force bit, one value bit. + * + * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value. + * Alternative values require the 200BASE (or AltSpeed) bit 12 set. + * Newer chips need a ForcedSpd bit 13 set to consider the value. */ static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, @@ -165,6 +169,140 @@ int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) return 0; } +static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, + int speed, bool alt_bit, bool force_bit) +{ + u16 reg, ctrl; + int err; + + switch (speed) { + case 10: + ctrl = PORT_PCS_CTRL_SPEED_10; + break; + case 100: + ctrl = PORT_PCS_CTRL_SPEED_100; + break; + case 200: + if (alt_bit) + ctrl = PORT_PCS_CTRL_SPEED_100 | PORT_PCS_CTRL_ALTSPEED; + else + ctrl = PORT_PCS_CTRL_SPEED_200; + break; + case 1000: + ctrl = PORT_PCS_CTRL_SPEED_1000; + break; + case 2500: + ctrl = PORT_PCS_CTRL_SPEED_1000 | PORT_PCS_CTRL_ALTSPEED; + break; + case 10000: + /* all bits set, fall through... */ + case SPEED_UNFORCED: + ctrl = PORT_PCS_CTRL_SPEED_UNFORCED; + break; + default: + return -EOPNOTSUPP; + } + + err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); + if (err) + return err; + + reg &= ~PORT_PCS_CTRL_SPEED_MASK; + if (alt_bit) + reg &= ~PORT_PCS_CTRL_ALTSPEED; + if (force_bit) { + reg &= ~PORT_PCS_CTRL_FORCE_SPEED; + if (speed) + ctrl |= PORT_PCS_CTRL_FORCE_SPEED; + } + reg |= ctrl; + + err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); + if (err) + return err; + + if (speed) + netdev_dbg(chip->ds->ports[port].netdev, + "Speed set to %d Mbps\n", speed); + else + netdev_dbg(chip->ds->ports[port].netdev, "Speed unforced\n"); + + return 0; +} + +/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 200; + + if (speed > 200) + return -EOPNOTSUPP; + + /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 1000; + + if (speed == 200 || speed > 1000) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 1000; + + if (speed > 1000) + return -EOPNOTSUPP; + + if (speed == 200 && port < 5) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, false); +} + +/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = port < 9 ? 1000 : 2500; + + if (speed > 2500) + return -EOPNOTSUPP; + + if (speed == 200 && port != 0) + return -EOPNOTSUPP; + + if (speed == 2500 && port < 9) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + +/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = port < 9 ? 1000 : 10000; + + if (speed == 200 && port != 0) + return -EOPNOTSUPP; + + if (speed >= 2500 && port < 9) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + /* Offset 0x04: Port Control Register */ static const char * const mv88e6xxx_port_state_names[] = { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 3472b79..499129c 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -30,6 +30,12 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); + int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); -- cgit v1.1 From d78343d2d796dbfe61450490b885782c72c820b5 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Nov 2016 03:23:36 +0100 Subject: net: dsa: mv88e6xxx: setup port's MAC Now that we have setters to configure the port's MAC, use them to refactor the port setup and adjust_link code. Note that port's MAC speed, duplex or RGMII delay must not be changed unless the port's link is forced down. So wrap all that in a mv88e6xxx_port_setup_mac function. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 140 +++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 80 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 23c05e3..c8f824d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -701,6 +701,47 @@ static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip) return chip->info->family == MV88E6XXX_FAMILY_6352; } +static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, + int link, int speed, int duplex, + phy_interface_t mode) +{ + int err; + + if (!chip->info->ops->port_set_link) + return 0; + + /* Port's MAC control must not be changed unless the link is down */ + err = chip->info->ops->port_set_link(chip, port, 0); + if (err) + return err; + + if (chip->info->ops->port_set_speed) { + err = chip->info->ops->port_set_speed(chip, port, speed); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_duplex) { + err = chip->info->ops->port_set_duplex(chip, port, duplex); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_rgmii_delay) { + err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + err = 0; +restore_link: + if (chip->info->ops->port_set_link(chip, port, link)) + netdev_err(chip->ds->ports[port].netdev, + "failed to restore MAC's link\n"); + + return err; +} + /* We expect the switch to perform auto negotiation if there is a real * phy. However, in the case of a fixed link phy, we force the port * settings from the fixed link settings. @@ -709,64 +750,18 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct mv88e6xxx_chip *chip = ds->priv; - u16 reg; int err; if (!phy_is_pseudo_fixed_link(phydev)) return; mutex_lock(&chip->reg_lock); - - err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); - if (err) - goto out; - - reg &= ~(PORT_PCS_CTRL_LINK_UP | - PORT_PCS_CTRL_FORCE_LINK | - PORT_PCS_CTRL_DUPLEX_FULL | - PORT_PCS_CTRL_FORCE_DUPLEX | - PORT_PCS_CTRL_SPEED_UNFORCED); - - reg |= PORT_PCS_CTRL_FORCE_LINK; - if (phydev->link) - reg |= PORT_PCS_CTRL_LINK_UP; - - if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100) - goto out; - - switch (phydev->speed) { - case SPEED_1000: - reg |= PORT_PCS_CTRL_SPEED_1000; - break; - case SPEED_100: - reg |= PORT_PCS_CTRL_SPEED_100; - break; - case SPEED_10: - reg |= PORT_PCS_CTRL_SPEED_10; - break; - default: - pr_info("Unknown speed"); - goto out; - } - - reg |= PORT_PCS_CTRL_FORCE_DUPLEX; - if (phydev->duplex == DUPLEX_FULL) - reg |= PORT_PCS_CTRL_DUPLEX_FULL; - - if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) && - (port >= mv88e6xxx_num_ports(chip) - 2)) { - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) - reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) - reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) - reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK | - PORT_PCS_CTRL_RGMII_DELAY_TXCLK); - } - mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); - -out: + err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, + phydev->duplex, phydev->interface); mutex_unlock(&chip->reg_lock); + + if (err && err != -EOPNOTSUPP) + netdev_err(ds->ports[port].netdev, "failed to configure MAC\n"); } static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) @@ -2409,35 +2404,20 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) int err; u16 reg; - if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || - mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || - mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) || - mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) { - /* MAC Forcing register: don't force link, speed, - * duplex or flow control state to any particular - * values on physical ports, but force the CPU port - * and all DSA ports to their maximum bandwidth and - * full duplex. - */ - err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); - if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { - reg &= ~PORT_PCS_CTRL_SPEED_UNFORCED; - reg |= PORT_PCS_CTRL_FORCE_LINK | - PORT_PCS_CTRL_LINK_UP | - PORT_PCS_CTRL_DUPLEX_FULL | - PORT_PCS_CTRL_FORCE_DUPLEX; - if (mv88e6xxx_6065_family(chip)) - reg |= PORT_PCS_CTRL_SPEED_100; - else - reg |= PORT_PCS_CTRL_SPEED_1000; - } else { - reg |= PORT_PCS_CTRL_SPEED_UNFORCED; - } - - err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); - if (err) - return err; - } + /* MAC Forcing register: don't force link, speed, duplex or flow control + * state to any particular values on physical ports, but force the CPU + * port and all DSA ports to their maximum bandwidth and full duplex. + */ + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP, + SPEED_MAX, DUPLEX_FULL, + PHY_INTERFACE_MODE_NA); + else + err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, + SPEED_UNFORCED, DUPLEX_UNFORCED, + PHY_INTERFACE_MODE_NA); + if (err) + return err; /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, * disable Header mode, enable IGMP/MLD snooping, disable VLAN -- cgit v1.1 From 86741ec25462e4c8cdce6df2f41ead05568c7d5e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 4 Nov 2016 02:23:41 +0900 Subject: net: core: Add a UID field to struct sock. Protocol sockets (struct sock) don't have UIDs, but most of the time, they map 1:1 to userspace sockets (struct socket) which do. Various operations such as the iptables xt_owner match need access to the "UID of a socket", and do so by following the backpointer to the struct socket. This involves taking sk_callback_lock and doesn't work when there is no socket because userspace has already called close(). Simplify this by adding a sk_uid field to struct sock whose value matches the UID of the corresponding struct socket. The semantics are as follows: 1. Whenever sk_socket is non-null: sk_uid is the same as the UID in sk_socket, i.e., matches the return value of sock_i_uid. Specifically, the UID is set when userspace calls socket(), fchown(), or accept(). 2. When sk_socket is NULL, sk_uid is defined as follows: - For a socket that no longer has a sk_socket because userspace has called close(): the previous UID. - For a cloned socket (e.g., an incoming connection that is established but on which userspace has not yet called accept): the UID of the socket it was cloned from. - For a socket that has never had an sk_socket: UID 0 inside the user namespace corresponding to the network namespace the socket belongs to. Kernel sockets created by sock_create_kern are a special case of #1 and sk_uid is the user that created them. For kernel sockets created at network namespace creation time, such as the per-processor ICMP and TCP sockets, this is the user that created the network namespace. Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- include/net/sock.h | 7 +++++++ net/core/sock.c | 5 ++++- net/socket.c | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/net/sock.h b/include/net/sock.h index 93331a1..cf617ee 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -419,6 +419,7 @@ struct sock { u32 sk_max_ack_backlog; __u32 sk_priority; __u32 sk_mark; + kuid_t sk_uid; struct pid *sk_peer_pid; const struct cred *sk_peer_cred; long sk_rcvtimeo; @@ -1664,6 +1665,7 @@ static inline void sock_graft(struct sock *sk, struct socket *parent) sk->sk_wq = parent->wq; parent->sk = sk; sk_set_socket(sk, parent); + sk->sk_uid = SOCK_INODE(parent)->i_uid; security_sock_graft(sk, parent); write_unlock_bh(&sk->sk_callback_lock); } @@ -1671,6 +1673,11 @@ static inline void sock_graft(struct sock *sk, struct socket *parent) kuid_t sock_i_uid(struct sock *sk); unsigned long sock_i_ino(struct sock *sk); +static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk) +{ + return sk ? sk->sk_uid : make_kuid(net->user_ns, 0); +} + static inline u32 net_tx_rndhash(void) { u32 v = prandom_u32(); diff --git a/net/core/sock.c b/net/core/sock.c index d8e4532e..40dbc13 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2460,8 +2460,11 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_type = sock->type; sk->sk_wq = sock->wq; sock->sk = sk; - } else + sk->sk_uid = SOCK_INODE(sock)->i_uid; + } else { sk->sk_wq = NULL; + sk->sk_uid = make_kuid(sock_net(sk)->user_ns, 0); + } rwlock_init(&sk->sk_callback_lock); lockdep_set_class_and_name(&sk->sk_callback_lock, diff --git a/net/socket.c b/net/socket.c index 970a7ea..4ce33c3 100644 --- a/net/socket.c +++ b/net/socket.c @@ -518,8 +518,22 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer, return used; } +int sockfs_setattr(struct dentry *dentry, struct iattr *iattr) +{ + int err = simple_setattr(dentry, iattr); + + if (!err) { + struct socket *sock = SOCKET_I(d_inode(dentry)); + + sock->sk->sk_uid = iattr->ia_uid; + } + + return err; +} + static const struct inode_operations sockfs_inode_ops = { .listxattr = sockfs_listxattr, + .setattr = sockfs_setattr, }; /** -- cgit v1.1 From 622ec2c9d52405973c9f1ca5116eb1c393adfc7d Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 4 Nov 2016 02:23:42 +0900 Subject: net: core: add UID to flows, rules, and routes - Define a new FIB rule attributes, FRA_UID_RANGE, to describe a range of UIDs. - Define a RTA_UID attribute for per-UID route lookups and dumps. - Support passing these attributes to and from userspace via rtnetlink. The value INVALID_UID indicates no UID was specified. - Add a UID field to the flow structures. Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- include/net/fib_rules.h | 9 ++++- include/net/flow.h | 5 +++ include/uapi/linux/fib_rules.h | 6 ++++ include/uapi/linux/rtnetlink.h | 1 + net/core/fib_rules.c | 74 ++++++++++++++++++++++++++++++++++++++++-- net/ipv4/fib_frontend.c | 1 + net/ipv4/route.c | 11 +++++++ net/ipv6/route.c | 7 ++++ 8 files changed, 111 insertions(+), 3 deletions(-) diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 456e4a6..8dbfdf7 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -8,6 +8,11 @@ #include #include +struct fib_kuid_range { + kuid_t start; + kuid_t end; +}; + struct fib_rule { struct list_head list; int iifindex; @@ -30,6 +35,7 @@ struct fib_rule { int suppress_prefixlen; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; + struct fib_kuid_range uid_range; struct rcu_head rcu; }; @@ -92,7 +98,8 @@ struct fib_rules_ops { [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \ [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \ [FRA_GOTO] = { .type = NLA_U32 }, \ - [FRA_L3MDEV] = { .type = NLA_U8 } + [FRA_L3MDEV] = { .type = NLA_U8 }, \ + [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/include/net/flow.h b/include/net/flow.h index 035aa77..51373f3 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -11,6 +11,7 @@ #include #include #include +#include /* * ifindex generation is per-net namespace, and loopback is @@ -37,6 +38,7 @@ struct flowi_common { #define FLOWI_FLAG_SKIP_NH_OIF 0x04 __u32 flowic_secid; struct flowi_tunnel flowic_tun_key; + kuid_t flowic_uid; }; union flowi_uli { @@ -74,6 +76,7 @@ struct flowi4 { #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid #define flowi4_tun_key __fl_common.flowic_tun_key +#define flowi4_uid __fl_common.flowic_uid /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; @@ -131,6 +134,7 @@ struct flowi6 { #define flowi6_flags __fl_common.flowic_flags #define flowi6_secid __fl_common.flowic_secid #define flowi6_tun_key __fl_common.flowic_tun_key +#define flowi6_uid __fl_common.flowic_uid struct in6_addr daddr; struct in6_addr saddr; /* Note: flowi6_tos is encoded in flowlabel, too. */ @@ -176,6 +180,7 @@ struct flowi { #define flowi_flags u.__fl_common.flowic_flags #define flowi_secid u.__fl_common.flowic_secid #define flowi_tun_key u.__fl_common.flowic_tun_key +#define flowi_uid u.__fl_common.flowic_uid } __attribute__((__aligned__(BITS_PER_LONG/8))); static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4) diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 14404b3..bbf02a6 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -29,6 +29,11 @@ struct fib_rule_hdr { __u32 flags; }; +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + enum { FRA_UNSPEC, FRA_DST, /* destination address */ @@ -51,6 +56,7 @@ enum { FRA_OIFNAME, FRA_PAD, FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + FRA_UID_RANGE, /* UID range */ __FRA_MAX }; diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 5a78be5..e14377f 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -318,6 +318,7 @@ enum rtattr_type_t { RTA_ENCAP, RTA_EXPIRES, RTA_PAD, + RTA_UID, __RTA_MAX }; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index be4629c..5de436a 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -18,6 +18,11 @@ #include #include +static const struct fib_kuid_range fib_kuid_range_unset = { + KUIDT_INIT(0), + KUIDT_INIT(~0), +}; + int fib_default_rule_add(struct fib_rules_ops *ops, u32 pref, u32 table, u32 flags) { @@ -33,6 +38,7 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->table = table; r->flags = flags; r->fr_net = ops->fro_net; + r->uid_range = fib_kuid_range_unset; r->suppress_prefixlen = -1; r->suppress_ifgroup = -1; @@ -172,6 +178,34 @@ void fib_rules_unregister(struct fib_rules_ops *ops) } EXPORT_SYMBOL_GPL(fib_rules_unregister); +static int uid_range_set(struct fib_kuid_range *range) +{ + return uid_valid(range->start) && uid_valid(range->end); +} + +static struct fib_kuid_range nla_get_kuid_range(struct nlattr **tb) +{ + struct fib_rule_uid_range *in; + struct fib_kuid_range out; + + in = (struct fib_rule_uid_range *)nla_data(tb[FRA_UID_RANGE]); + + out.start = make_kuid(current_user_ns(), in->start); + out.end = make_kuid(current_user_ns(), in->end); + + return out; +} + +static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range) +{ + struct fib_rule_uid_range out = { + from_kuid_munged(current_user_ns(), range->start), + from_kuid_munged(current_user_ns(), range->end) + }; + + return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out); +} + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, struct flowi *fl, int flags, struct fib_lookup_arg *arg) @@ -193,6 +227,10 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, if (rule->l3mdev && !l3mdev_fib_rule_match(rule->fr_net, fl, arg)) goto out; + if (uid_lt(fl->flowi_uid, rule->uid_range.start) || + uid_gt(fl->flowi_uid, rule->uid_range.end)) + goto out; + ret = ops->match(rule, fl, flags); out: return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; @@ -429,6 +467,21 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh) if (rule->l3mdev && rule->table) goto errout_free; + if (tb[FRA_UID_RANGE]) { + if (current_user_ns() != net->user_ns) { + err = -EPERM; + goto errout_free; + } + + rule->uid_range = nla_get_kuid_range(tb); + + if (!uid_range_set(&rule->uid_range) || + !uid_lte(rule->uid_range.start, rule->uid_range.end)) + goto errout_free; + } else { + rule->uid_range = fib_kuid_range_unset; + } + if ((nlh->nlmsg_flags & NLM_F_EXCL) && rule_exists(ops, frh, tb, rule)) { err = -EEXIST; @@ -497,6 +550,7 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh) struct fib_rules_ops *ops = NULL; struct fib_rule *rule, *tmp; struct nlattr *tb[FRA_MAX+1]; + struct fib_kuid_range range; int err = -EINVAL; if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) @@ -516,6 +570,14 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh) if (err < 0) goto errout; + if (tb[FRA_UID_RANGE]) { + range = nla_get_kuid_range(tb); + if (!uid_range_set(&range)) + goto errout; + } else { + range = fib_kuid_range_unset; + } + list_for_each_entry(rule, &ops->rules_list, list) { if (frh->action && (frh->action != rule->action)) continue; @@ -552,6 +614,11 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh) (rule->l3mdev != nla_get_u8(tb[FRA_L3MDEV]))) continue; + if (uid_range_set(&range) && + (!uid_eq(rule->uid_range.start, range.start) || + !uid_eq(rule->uid_range.end, range.end))) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -619,7 +686,8 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */ + nla_total_size(4) /* FRA_FWMARK */ + nla_total_size(4) /* FRA_FWMASK */ - + nla_total_size_64bit(8); /* FRA_TUN_ID */ + + nla_total_size_64bit(8) /* FRA_TUN_ID */ + + nla_total_size(sizeof(struct fib_kuid_range)); if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -679,7 +747,9 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, (rule->tun_id && nla_put_be64(skb, FRA_TUN_ID, rule->tun_id, FRA_PAD)) || (rule->l3mdev && - nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev))) + nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) || + (uid_range_set(&rule->uid_range) && + nla_put_uid_range(skb, &rule->uid_range))) goto nla_put_failure; if (rule->suppress_ifgroup != -1) { diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c3b8047..d93eea8 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -610,6 +610,7 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { [RTA_FLOW] = { .type = NLA_U32 }, [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, [RTA_ENCAP] = { .type = NLA_NESTED }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4392db8..92e59a6 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2504,6 +2504,11 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id, nla_put_u32(skb, RTA_MARK, fl4->flowi4_mark)) goto nla_put_failure; + if (!uid_eq(fl4->flowi4_uid, INVALID_UID) && + nla_put_u32(skb, RTA_UID, + from_kuid_munged(current_user_ns(), fl4->flowi4_uid))) + goto nla_put_failure; + error = rt->dst.error; if (rt_is_input_route(rt)) { @@ -2556,6 +2561,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) int mark; struct sk_buff *skb; u32 table_id = RT_TABLE_MAIN; + kuid_t uid; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy); if (err < 0) @@ -2583,6 +2589,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; mark = tb[RTA_MARK] ? nla_get_u32(tb[RTA_MARK]) : 0; + if (tb[RTA_UID]) + uid = make_kuid(current_user_ns(), nla_get_u32(tb[RTA_UID])); + else + uid = (iif ? INVALID_UID : current_uid()); memset(&fl4, 0, sizeof(fl4)); fl4.daddr = dst; @@ -2590,6 +2600,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) fl4.flowi4_tos = rtm->rtm_tos; fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0; fl4.flowi4_mark = mark; + fl4.flowi4_uid = uid; if (iif) { struct net_device *dev; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 947ed1d..fdb9c87 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2797,6 +2797,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, [RTA_ENCAP] = { .type = NLA_NESTED }, [RTA_EXPIRES] = { .type = NLA_U32 }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -3371,6 +3372,12 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) if (tb[RTA_MARK]) fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); + if (tb[RTA_UID]) + fl6.flowi6_uid = make_kuid(current_user_ns(), + nla_get_u32(tb[RTA_UID])); + else + fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); + if (iif) { struct net_device *dev; int flags = 0; -- cgit v1.1 From e2d118a1cb5e60d077131a09db1d81b90a5295fe Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 4 Nov 2016 02:23:43 +0900 Subject: net: inet: Support UID-based routing in IP protocols. - Use the UID in routing lookups made by protocol connect() and sendmsg() functions. - Make sure that routing lookups triggered by incoming packets (e.g., Path MTU discovery) take the UID of the socket into account. - For packets not associated with a userspace socket, (e.g., ping replies) use UID 0 inside the user namespace corresponding to the network namespace the socket belongs to. This allows all namespaces to apply routing and iptables rules to kernel-originated traffic in that namespaces by matching UID 0. This is better than using the UID of the kernel socket that is sending the traffic, because the UID of kernel sockets created at namespace creation time (e.g., the per-processor ICMP and TCP sockets) is the UID of the user that created the socket, which might not be mapped in the namespace. Tested: compiles allnoconfig, allyesconfig, allmodconfig Tested: https://android-review.googlesource.com/253302 Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- include/net/flow.h | 4 +++- include/net/ip.h | 1 + include/net/ip6_route.h | 5 +++-- include/net/route.h | 5 +++-- net/ipv4/icmp.c | 2 ++ net/ipv4/inet_connection_sock.c | 4 ++-- net/ipv4/ip_output.c | 3 ++- net/ipv4/ping.c | 3 ++- net/ipv4/raw.c | 2 +- net/ipv4/route.c | 26 +++++++++++++++----------- net/ipv4/syncookies.c | 2 +- net/ipv4/tcp_ipv4.c | 9 ++++++--- net/ipv4/udp.c | 3 ++- net/ipv6/af_inet6.c | 1 + net/ipv6/ah6.c | 5 +++-- net/ipv6/datagram.c | 1 + net/ipv6/esp6.c | 5 +++-- net/ipv6/icmp.c | 7 +++++-- net/ipv6/inet6_connection_sock.c | 2 ++ net/ipv6/ip6_gre.c | 4 ++++ net/ipv6/ip6_tunnel.c | 4 ++++ net/ipv6/ip6_vti.c | 5 +++-- net/ipv6/ipcomp6.c | 5 +++-- net/ipv6/netfilter.c | 1 + net/ipv6/ping.c | 1 + net/ipv6/raw.c | 1 + net/ipv6/route.c | 13 +++++++++---- net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 2 ++ net/ipv6/udp.c | 1 + net/l2tp/l2tp_ip6.c | 1 + 31 files changed, 89 insertions(+), 40 deletions(-) diff --git a/include/net/flow.h b/include/net/flow.h index 51373f3..6bbbca8 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -96,7 +96,8 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + kuid_t uid) { fl4->flowi4_oif = oif; fl4->flowi4_iif = LOOPBACK_IFINDEX; @@ -107,6 +108,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->flowi4_flags = flags; fl4->flowi4_secid = 0; fl4->flowi4_tun_key.tun_id = 0; + fl4->flowi4_uid = uid; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; diff --git a/include/net/ip.h b/include/net/ip.h index 5413883..55cdaac 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -179,6 +179,7 @@ struct ip_reply_arg { /* -1 if not needed */ int bound_dev_if; u8 tos; + kuid_t uid; }; #define IP_REPLY_ARG_NOSRCCHECK 1 diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index f83e78d..9dc2c18 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -140,9 +140,10 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, const struct in6_addr *gwaddr); void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif, - u32 mark); + u32 mark, kuid_t uid); void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); -void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); +void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, + kuid_t uid); void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, u32 mark); void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk); diff --git a/include/net/route.h b/include/net/route.h index 0429d47..c0874c8 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -153,7 +153,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport); + daddr, saddr, dport, sport, sock_net_uid(net, sk)); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -269,7 +269,8 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flow_flags |= FLOWI_FLAG_ANYSRC; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, - protocol, flow_flags, dst, src, dport, sport); + protocol, flow_flags, dst, src, dport, sport, + sk->sk_uid); } static inline struct rtable *ip_route_connect(struct flowi4 *fl4, diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 38abe70..53a890b 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -425,6 +425,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) fl4.daddr = daddr; fl4.saddr = saddr; fl4.flowi4_mark = mark; + fl4.flowi4_uid = sock_net_uid(net, NULL); fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); fl4.flowi4_proto = IPPROTO_ICMP; fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev); @@ -473,6 +474,7 @@ static struct rtable *icmp_route_lookup(struct net *net, param->replyopts.opt.opt.faddr : iph->saddr); fl4->saddr = saddr; fl4->flowi4_mark = mark; + fl4->flowi4_uid = sock_net_uid(net, NULL); fl4->flowi4_tos = RT_TOS(tos); fl4->flowi4_proto = IPPROTO_ICMP; fl4->fl4_icmp_type = type; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 61a9dee..d5d3ead 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -415,7 +415,7 @@ struct dst_entry *inet_csk_route_req(const struct sock *sk, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, ireq->ir_loc_addr, ireq->ir_rmt_port, - htons(ireq->ir_num)); + htons(ireq->ir_num), sk->sk_uid); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -452,7 +452,7 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, ireq->ir_loc_addr, ireq->ir_rmt_port, - htons(ireq->ir_num)); + htons(ireq->ir_num), sk->sk_uid); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 03e7f73..37dfacd 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1587,7 +1587,8 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol, ip_reply_arg_flowi_flags(arg), daddr, saddr, - tcp_hdr(skb)->source, tcp_hdr(skb)->dest); + tcp_hdr(skb)->source, tcp_hdr(skb)->dest, + arg->uid); security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(net, &fl4); if (IS_ERR(rt)) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 205e200..d11129f 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -789,7 +789,8 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); + inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, + sk->sk_uid); security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); rt = ip_route_output_flow(net, &fl4, sk); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 6a0bd68..838ea5e 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -606,7 +606,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk) | (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), - daddr, saddr, 0, 0); + daddr, saddr, 0, 0, sk->sk_uid); if (!inet->hdrincl) { rfv.msg = msg; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 92e59a6..2355883 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -507,7 +507,8 @@ void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) } EXPORT_SYMBOL(__ip_select_ident); -static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, +static void __build_flow_key(const struct net *net, struct flowi4 *fl4, + const struct sock *sk, const struct iphdr *iph, int oif, u8 tos, u8 prot, u32 mark, int flow_flags) @@ -523,7 +524,8 @@ static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, flowi4_init_output(fl4, oif, mark, tos, RT_SCOPE_UNIVERSE, prot, flow_flags, - iph->daddr, iph->saddr, 0, 0); + iph->daddr, iph->saddr, 0, 0, + sock_net_uid(net, sk)); } static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, @@ -535,7 +537,7 @@ static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, u8 prot = iph->protocol; u32 mark = skb->mark; - __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0); + __build_flow_key(sock_net(sk), fl4, sk, iph, oif, tos, prot, mark, 0); } static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) @@ -552,7 +554,7 @@ static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk), - daddr, inet->inet_saddr, 0, 0); + daddr, inet->inet_saddr, 0, 0, sk->sk_uid); rcu_read_unlock(); } @@ -800,7 +802,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf rt = (struct rtable *) dst; - __build_flow_key(&fl4, sk, iph, oif, tos, prot, mark, 0); + __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0); __ip_do_redirect(rt, skb, &fl4, true); } @@ -1018,7 +1020,7 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, if (!mark) mark = IP4_REPLY_MARK(net, skb->mark); - __build_flow_key(&fl4, NULL, iph, oif, + __build_flow_key(net, &fl4, NULL, iph, oif, RT_TOS(iph->tos), protocol, mark, flow_flags); rt = __ip_route_output_key(net, &fl4); if (!IS_ERR(rt)) { @@ -1034,7 +1036,7 @@ static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) struct flowi4 fl4; struct rtable *rt; - __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); + __build_flow_key(sock_net(sk), &fl4, sk, iph, 0, 0, 0, 0, 0); if (!fl4.flowi4_mark) fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark); @@ -1053,6 +1055,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) struct rtable *rt; struct dst_entry *odst = NULL; bool new = false; + struct net *net = sock_net(sk); bh_lock_sock(sk); @@ -1066,7 +1069,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) goto out; } - __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); + __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0); rt = (struct rtable *)odst; if (odst->obsolete && !odst->ops->check(odst, 0)) { @@ -1106,7 +1109,7 @@ void ipv4_redirect(struct sk_buff *skb, struct net *net, struct flowi4 fl4; struct rtable *rt; - __build_flow_key(&fl4, NULL, iph, oif, + __build_flow_key(net, &fl4, NULL, iph, oif, RT_TOS(iph->tos), protocol, mark, flow_flags); rt = __ip_route_output_key(net, &fl4); if (!IS_ERR(rt)) { @@ -1121,9 +1124,10 @@ void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk) const struct iphdr *iph = (const struct iphdr *) skb->data; struct flowi4 fl4; struct rtable *rt; + struct net *net = sock_net(sk); - __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); - rt = __ip_route_output_key(sock_net(sk), &fl4); + __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0); + rt = __ip_route_output_key(net, &fl4); if (!IS_ERR(rt)) { __ip_do_redirect(rt, skb, &fl4, false); ip_rt_put(rt); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index e3c4043..0dc6286 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -372,7 +372,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), opt->srr ? opt->faddr : ireq->ir_rmt_addr, - ireq->ir_loc_addr, th->source, th->dest); + ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid); security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b9b82826..6491b7c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -691,6 +691,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) offsetof(struct inet_timewait_sock, tw_bound_dev_if)); arg.tos = ip_hdr(skb)->tos; + arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL); local_bh_disable(); ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk), skb, &TCP_SKB_CB(skb)->header.h4.opt, @@ -711,7 +712,7 @@ out: outside socket context is ugly, certainly. What can I do? */ -static void tcp_v4_send_ack(struct net *net, +static void tcp_v4_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, @@ -726,6 +727,7 @@ static void tcp_v4_send_ack(struct net *net, #endif ]; } rep; + struct net *net = sock_net(sk); struct ip_reply_arg arg; memset(&rep.th, 0, sizeof(struct tcphdr)); @@ -775,6 +777,7 @@ static void tcp_v4_send_ack(struct net *net, if (oif) arg.bound_dev_if = oif; arg.tos = tos; + arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL); local_bh_disable(); ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk), skb, &TCP_SKB_CB(skb)->header.h4.opt, @@ -790,7 +793,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); - tcp_v4_send_ack(sock_net(sk), skb, + tcp_v4_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp + tcptw->tw_ts_offset, @@ -818,7 +821,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, * exception of segments, MUST be right-shifted by * Rcv.Wind.Shift bits: */ - tcp_v4_send_ack(sock_net(sk), skb, seq, + tcp_v4_send_ack(sk, skb, seq, tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, tcp_time_stamp, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 195992e..a32a890 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1019,7 +1019,8 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, flow_flags, - faddr, saddr, dport, inet->inet_sport); + faddr, saddr, dport, inet->inet_sport, + sk->sk_uid); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 46ad699..c86911b 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -678,6 +678,7 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sk->sk_uid; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); rcu_read_lock(); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 0630a4d5..189eb10 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -662,9 +662,10 @@ static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 620c79a..c5d76d2 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -54,6 +54,7 @@ static void ip6_datagram_flow_key_init(struct flowi6 *fl6, struct sock *sk) fl6->fl6_dport = inet->inet_dport; fl6->fl6_sport = inet->inet_sport; fl6->flowlabel = np->flow_label; + fl6->flowi6_uid = sk->sk_uid; if (!fl6->flowi6_oif) fl6->flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 060a60b..218f0cb 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -474,9 +474,10 @@ static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index bd59c34..ab249fe 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -92,9 +92,10 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct net *net = dev_net(skb->dev); if (type == ICMPV6_PKT_TOOBIG) - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); else if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); if (!(type & ICMPV6_INFOMSG_MASK)) if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) @@ -484,6 +485,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, fl6.flowi6_oif = iif; fl6.fl6_icmp_type = type; fl6.fl6_icmp_code = code; + fl6.flowi6_uid = sock_net_uid(net, NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); @@ -658,6 +660,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl6.flowi6_oif = skb->dev->ifindex; fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; fl6.flowi6_mark = mark; + fl6.flowi6_uid = sock_net_uid(net, NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 532c3ef..1c86c47 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -88,6 +88,7 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk, fl6->flowi6_mark = ireq->ir_mark; fl6->fl6_dport = ireq->ir_rmt_port; fl6->fl6_sport = htons(ireq->ir_num); + fl6->flowi6_uid = sk->sk_uid; security_req_classify_flow(req, flowi6_to_flowi(fl6)); dst = ip6_dst_lookup_flow(sk, fl6, final_p); @@ -136,6 +137,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, fl6->flowi6_mark = sk->sk_mark; fl6->fl6_sport = inet->inet_sport; fl6->fl6_dport = inet->inet_dport; + fl6->flowi6_uid = sk->sk_uid; security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); rcu_read_lock(); diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index d7d6d3a..710bc79 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -548,6 +548,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev) if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM)); if (err) return -1; @@ -602,6 +604,8 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev) if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + if (gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM))) return -1; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 03e050d..bfa889c 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1240,6 +1240,8 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) fl6.flowi6_mark = skb->mark; } + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) return -1; @@ -1318,6 +1320,8 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) fl6.flowi6_mark = skb->mark; } + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) return -1; diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 35c5b2d..af3f0e0 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -608,9 +608,10 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 1b9316e..54d165b 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -74,9 +74,10 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0); + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); xfrm_state_put(x); return 0; diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index d11c468..39970e2 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -26,6 +26,7 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb) struct flowi6 fl6 = { .flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, .flowi6_mark = skb->mark, + .flowi6_uid = sock_net_uid(net, skb->sk), .daddr = iph->daddr, .saddr = iph->saddr, }; diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 66e2d9d..e1f8b34 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -113,6 +113,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.daddr = *daddr; fl6.flowi6_oif = oif; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sk->sk_uid; fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 610e093..291ebc2 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -776,6 +776,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sk->sk_uid; ipc6.hlimit = -1; ipc6.tclass = -1; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fdb9c87..6aa014e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1405,7 +1405,7 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, } void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark) + int oif, u32 mark, kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1417,6 +1417,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) @@ -1430,7 +1431,7 @@ void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) struct dst_entry *dst; ip6_update_pmtu(skb, sock_net(sk), mtu, - sk->sk_bound_dev_if, sk->sk_mark); + sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid); dst = __sk_dst_get(sk); if (!dst || !dst->obsolete || @@ -1522,7 +1523,8 @@ static struct dst_entry *ip6_route_redirect(struct net *net, flags, __ip6_route_redirect); } -void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) +void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, + kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1535,6 +1537,7 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); rt6_do_redirect(dst, NULL, skb); @@ -1556,6 +1559,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, fl6.flowi6_mark = mark; fl6.daddr = msg->dest; fl6.saddr = iph->daddr; + fl6.flowi6_uid = sock_net_uid(net, NULL); dst = ip6_route_redirect(net, &fl6, &iph->saddr); rt6_do_redirect(dst, NULL, skb); @@ -1564,7 +1568,8 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) { - ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); + ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, + sk->sk_uid); } EXPORT_SYMBOL_GPL(ip6_sk_redirect); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 59c4839..97830a6 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -227,6 +227,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.flowi6_mark = ireq->ir_mark; fl6.fl6_dport = ireq->ir_rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; + fl6.flowi6_uid = sk->sk_uid; security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5a27ab4..aece1b1 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -233,6 +233,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = usin->sin6_port; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sk->sk_uid; opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk)); final_p = fl6_update_dst(&fl6, opt, &final); @@ -824,6 +825,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark); fl6.fl6_dport = t1->dest; fl6.fl6_sport = t1->source; + fl6.flowi6_uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); /* Pass a socket to ip6_dst_lookup either it is for RST diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a7700bbf..9103c5c 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1138,6 +1138,7 @@ do_udp_sendmsg: fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sk->sk_uid; sockc.tsflags = sk->sk_tsflags; if (msg->msg_controllen) { diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index ad3468c..1cea54f 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -519,6 +519,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sk->sk_uid; ipc6.hlimit = -1; ipc6.tclass = -1; -- cgit v1.1 From d9682c90cf5468bd7277d85ae7d80911f25c5edf Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:17:38 -0500 Subject: amd-xgbe: Fix formatting of PCS register dump Fix the length value used for the PCS register dump so that the full value can be displayed. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 84c5d29..e9b01fc 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1257,33 +1257,33 @@ static void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) dev_dbg(dev, "\n************* PHY Reg dump **********************\n"); - dev_dbg(dev, "PCS Control Reg (%#04x) = %#04x\n", MDIO_CTRL1, + dev_dbg(dev, "PCS Control Reg (%#06x) = %#06x\n", MDIO_CTRL1, XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1)); - dev_dbg(dev, "PCS Status Reg (%#04x) = %#04x\n", MDIO_STAT1, + dev_dbg(dev, "PCS Status Reg (%#06x) = %#06x\n", MDIO_STAT1, XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1)); - dev_dbg(dev, "Phy Id (PHYS ID 1 %#04x)= %#04x\n", MDIO_DEVID1, + dev_dbg(dev, "Phy Id (PHYS ID 1 %#06x)= %#06x\n", MDIO_DEVID1, XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID1)); - dev_dbg(dev, "Phy Id (PHYS ID 2 %#04x)= %#04x\n", MDIO_DEVID2, + dev_dbg(dev, "Phy Id (PHYS ID 2 %#06x)= %#06x\n", MDIO_DEVID2, XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID2)); - dev_dbg(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS1, + dev_dbg(dev, "Devices in Package (%#06x)= %#06x\n", MDIO_DEVS1, XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS1)); - dev_dbg(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS2, + dev_dbg(dev, "Devices in Package (%#06x)= %#06x\n", MDIO_DEVS2, XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS2)); - dev_dbg(dev, "Auto-Neg Control Reg (%#04x) = %#04x\n", MDIO_CTRL1, + dev_dbg(dev, "Auto-Neg Control Reg (%#06x) = %#06x\n", MDIO_CTRL1, XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1)); - dev_dbg(dev, "Auto-Neg Status Reg (%#04x) = %#04x\n", MDIO_STAT1, + dev_dbg(dev, "Auto-Neg Status Reg (%#06x) = %#06x\n", MDIO_STAT1, XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_STAT1)); - dev_dbg(dev, "Auto-Neg Ad Reg 1 (%#04x) = %#04x\n", + dev_dbg(dev, "Auto-Neg Ad Reg 1 (%#06x) = %#06x\n", MDIO_AN_ADVERTISE, XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE)); - dev_dbg(dev, "Auto-Neg Ad Reg 2 (%#04x) = %#04x\n", + dev_dbg(dev, "Auto-Neg Ad Reg 2 (%#06x) = %#06x\n", MDIO_AN_ADVERTISE + 1, XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1)); - dev_dbg(dev, "Auto-Neg Ad Reg 3 (%#04x) = %#04x\n", + dev_dbg(dev, "Auto-Neg Ad Reg 3 (%#06x) = %#06x\n", MDIO_AN_ADVERTISE + 2, XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2)); - dev_dbg(dev, "Auto-Neg Completion Reg (%#04x) = %#04x\n", + dev_dbg(dev, "Auto-Neg Completion Reg (%#06x) = %#06x\n", MDIO_AN_COMP_STAT, XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_COMP_STAT)); -- cgit v1.1 From 586e3cfb260a0c6026321417bb5af331cc717f40 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:17:48 -0500 Subject: amd-xgbe: Prepare for priority-based FIFO allocation Currently, the Rx and Tx fifos are evenly allocated between the hardware queues of the device. As more queues are instantiated, the fifo memory needs to be able to be allocated based on queue priority. This allows for higher priority queues to have more fifo memory than lower priority queues. Prepare for this by modifying the current fifo calculation to assign the fifo queue allocation in an array that is then used to program the hardware. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 55 ++++++++++++++++++++++---------- drivers/net/ethernet/amd/xgbe/xgbe.h | 3 +- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 1babcc1..18f8001 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -2000,19 +2000,37 @@ static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata) XGMAC_IOWRITE_BITS(pdata, MTL_OMR, RAA, MTL_RAA_SP); } -static unsigned int xgbe_calculate_per_queue_fifo(unsigned int fifo_size, - unsigned int queue_count) +static unsigned int xgbe_get_tx_fifo_size(struct xgbe_prv_data *pdata) { - unsigned int q_fifo_size; - unsigned int p_fifo; + unsigned int fifo_size; /* Calculate the configured fifo size */ - q_fifo_size = 1 << (fifo_size + 7); + fifo_size = 1 << (pdata->hw_feat.tx_fifo_size + 7); /* The configured value may not be the actual amount of fifo RAM */ - q_fifo_size = min_t(unsigned int, XGBE_FIFO_MAX, q_fifo_size); + return min_t(unsigned int, XGMAC_FIFO_TX_MAX, fifo_size); +} + +static unsigned int xgbe_get_rx_fifo_size(struct xgbe_prv_data *pdata) +{ + unsigned int fifo_size; - q_fifo_size = q_fifo_size / queue_count; + /* Calculate the configured fifo size */ + fifo_size = 1 << (pdata->hw_feat.rx_fifo_size + 7); + + /* The configured value may not be the actual amount of fifo RAM */ + return min_t(unsigned int, XGMAC_FIFO_RX_MAX, fifo_size); +} + +static void xgbe_calculate_equal_fifo(unsigned int fifo_size, + unsigned int queue_count, + unsigned int *fifo) +{ + unsigned int q_fifo_size; + unsigned int p_fifo; + unsigned int i; + + q_fifo_size = fifo_size / queue_count; /* Each increment in the queue fifo size represents 256 bytes of * fifo, with 0 representing 256 bytes. Distribute the fifo equally @@ -2022,39 +2040,44 @@ static unsigned int xgbe_calculate_per_queue_fifo(unsigned int fifo_size, if (p_fifo) p_fifo--; - return p_fifo; + for (i = 0; i < queue_count; i++) + fifo[i] = p_fifo; } static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) { unsigned int fifo_size; + unsigned int fifo[XGBE_MAX_QUEUES]; unsigned int i; - fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size, - pdata->tx_q_count); + fifo_size = xgbe_get_tx_fifo_size(pdata); + + xgbe_calculate_equal_fifo(fifo_size, pdata->tx_q_count, fifo); for (i = 0; i < pdata->tx_q_count; i++) - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TQS, fifo_size); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TQS, fifo[i]); netif_info(pdata, drv, pdata->netdev, "%d Tx hardware queues, %d byte fifo per queue\n", - pdata->tx_q_count, ((fifo_size + 1) * 256)); + pdata->tx_q_count, ((fifo[0] + 1) * 256)); } static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata) { unsigned int fifo_size; + unsigned int fifo[XGBE_MAX_QUEUES]; unsigned int i; - fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size, - pdata->rx_q_count); + fifo_size = xgbe_get_rx_fifo_size(pdata); + + xgbe_calculate_equal_fifo(fifo_size, pdata->rx_q_count, fifo); for (i = 0; i < pdata->rx_q_count; i++) - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RQS, fifo_size); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RQS, fifo[i]); netif_info(pdata, drv, pdata->netdev, "%d Rx hardware queues, %d byte fifo per queue\n", - pdata->rx_q_count, ((fifo_size + 1) * 256)); + pdata->rx_q_count, ((fifo[0] + 1) * 256)); } static void xgbe_config_queue_mapping(struct xgbe_prv_data *pdata) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 5dd17dc..d838b44 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -208,7 +208,8 @@ #define XGMAC_DRIVER_CONTEXT 1 #define XGMAC_IOCTL_CONTEXT 2 -#define XGBE_FIFO_MAX 81920 +#define XGMAC_FIFO_RX_MAX 81920 +#define XGMAC_FIFO_TX_MAX 81920 #define XGBE_TC_MIN_QUANTUM 10 -- cgit v1.1 From 43e0dcf7084014258ed555690fd6653f689ec368 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:18:16 -0500 Subject: amd-xgbe: Perform priority-based hardware FIFO allocation Allocate the FIFO across the hardware Rx queues based on the priority of the queues. Giving more FIFO resources to queues with a higher priority. If PFC is active but not enabled for a queue, then less resources can allocated to the queue. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 547 +++++++++++++++++++++++-------- drivers/net/ethernet/amd/xgbe/xgbe.h | 26 ++ 2 files changed, 434 insertions(+), 139 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 18f8001..f8fffea 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -123,6 +123,11 @@ #include "xgbe.h" #include "xgbe-common.h" +static inline unsigned int xgbe_get_max_frame(struct xgbe_prv_data *pdata) +{ + return pdata->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; +} + static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata, unsigned int usec) { @@ -491,6 +496,27 @@ static void xgbe_config_rss(struct xgbe_prv_data *pdata) "error configuring RSS, RSS disabled\n"); } +static bool xgbe_is_pfc_queue(struct xgbe_prv_data *pdata, + unsigned int queue) +{ + unsigned int prio, tc; + + for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) { + /* Does this queue handle the priority? */ + if (pdata->prio2q_map[prio] != queue) + continue; + + /* Get the Traffic Class for this priority */ + tc = pdata->ets->prio_tc[prio]; + + /* Check if PFC is enabled for this traffic class */ + if (pdata->pfc->pfc_en & (1 << tc)) + return true; + } + + return false; +} + static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata) { unsigned int max_q_count, q_count; @@ -528,27 +554,14 @@ static int xgbe_enable_tx_flow_control(struct xgbe_prv_data *pdata) for (i = 0; i < pdata->rx_q_count; i++) { unsigned int ehfc = 0; - if (pfc && ets) { - unsigned int prio; - - for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) { - unsigned int tc; - - /* Does this queue handle the priority? */ - if (pdata->prio2q_map[prio] != i) - continue; - - /* Get the Traffic Class for this priority */ - tc = ets->prio_tc[prio]; - - /* Check if flow control should be enabled */ - if (pfc->pfc_en & (1 << tc)) { + if (pdata->rx_rfd[i]) { + /* Flow control thresholds are established */ + if (pfc && ets) { + if (xgbe_is_pfc_queue(pdata, i)) ehfc = 1; - break; - } + } else { + ehfc = 1; } - } else { - ehfc = 1; } XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, ehfc); @@ -1327,106 +1340,6 @@ static int xgbe_config_tstamp(struct xgbe_prv_data *pdata, return 0; } -static void xgbe_config_tc(struct xgbe_prv_data *pdata) -{ - unsigned int offset, queue, prio; - u8 i; - - netdev_reset_tc(pdata->netdev); - if (!pdata->num_tcs) - return; - - netdev_set_num_tc(pdata->netdev, pdata->num_tcs); - - for (i = 0, queue = 0, offset = 0; i < pdata->num_tcs; i++) { - while ((queue < pdata->tx_q_count) && - (pdata->q2tc_map[queue] == i)) - queue++; - - netif_dbg(pdata, drv, pdata->netdev, "TC%u using TXq%u-%u\n", - i, offset, queue - 1); - netdev_set_tc_queue(pdata->netdev, i, queue - offset, offset); - offset = queue; - } - - if (!pdata->ets) - return; - - for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) - netdev_set_prio_tc_map(pdata->netdev, prio, - pdata->ets->prio_tc[prio]); -} - -static void xgbe_config_dcb_tc(struct xgbe_prv_data *pdata) -{ - struct ieee_ets *ets = pdata->ets; - unsigned int total_weight, min_weight, weight; - unsigned int mask, reg, reg_val; - unsigned int i, prio; - - if (!ets) - return; - - /* Set Tx to deficit weighted round robin scheduling algorithm (when - * traffic class is using ETS algorithm) - */ - XGMAC_IOWRITE_BITS(pdata, MTL_OMR, ETSALG, MTL_ETSALG_DWRR); - - /* Set Traffic Class algorithms */ - total_weight = pdata->netdev->mtu * pdata->hw_feat.tc_cnt; - min_weight = total_weight / 100; - if (!min_weight) - min_weight = 1; - - for (i = 0; i < pdata->hw_feat.tc_cnt; i++) { - /* Map the priorities to the traffic class */ - mask = 0; - for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) { - if (ets->prio_tc[prio] == i) - mask |= (1 << prio); - } - mask &= 0xff; - - netif_dbg(pdata, drv, pdata->netdev, "TC%u PRIO mask=%#x\n", - i, mask); - reg = MTL_TCPM0R + (MTL_TCPM_INC * (i / MTL_TCPM_TC_PER_REG)); - reg_val = XGMAC_IOREAD(pdata, reg); - - reg_val &= ~(0xff << ((i % MTL_TCPM_TC_PER_REG) << 3)); - reg_val |= (mask << ((i % MTL_TCPM_TC_PER_REG) << 3)); - - XGMAC_IOWRITE(pdata, reg, reg_val); - - /* Set the traffic class algorithm */ - switch (ets->tc_tsa[i]) { - case IEEE_8021QAZ_TSA_STRICT: - netif_dbg(pdata, drv, pdata->netdev, - "TC%u using SP\n", i); - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, - MTL_TSA_SP); - break; - case IEEE_8021QAZ_TSA_ETS: - weight = total_weight * ets->tc_tx_bw[i] / 100; - weight = clamp(weight, min_weight, total_weight); - - netif_dbg(pdata, drv, pdata->netdev, - "TC%u using DWRR (weight %u)\n", i, weight); - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, - MTL_TSA_ETS); - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_QWR, QW, - weight); - break; - } - } - - xgbe_config_tc(pdata); -} - -static void xgbe_config_dcb_pfc(struct xgbe_prv_data *pdata) -{ - xgbe_config_flow_control(pdata); -} - static void xgbe_tx_start_xmit(struct xgbe_channel *channel, struct xgbe_ring *ring) { @@ -2000,6 +1913,96 @@ static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata) XGMAC_IOWRITE_BITS(pdata, MTL_OMR, RAA, MTL_RAA_SP); } +static void xgbe_queue_flow_control_threshold(struct xgbe_prv_data *pdata, + unsigned int queue, + unsigned int q_fifo_size) +{ + unsigned int frame_fifo_size; + unsigned int rfa, rfd; + + frame_fifo_size = XGMAC_FLOW_CONTROL_ALIGN(xgbe_get_max_frame(pdata)); + + if (pdata->pfcq[queue] && (q_fifo_size > pdata->pfc_rfa)) { + /* PFC is active for this queue */ + rfa = pdata->pfc_rfa; + rfd = rfa + frame_fifo_size; + if (rfd > XGMAC_FLOW_CONTROL_MAX) + rfd = XGMAC_FLOW_CONTROL_MAX; + if (rfa >= XGMAC_FLOW_CONTROL_MAX) + rfa = XGMAC_FLOW_CONTROL_MAX - XGMAC_FLOW_CONTROL_UNIT; + } else { + /* This path deals with just maximum frame sizes which are + * limited to a jumbo frame of 9,000 (plus headers, etc.) + * so we can never exceed the maximum allowable RFA/RFD + * values. + */ + if (q_fifo_size <= 2048) { + /* rx_rfd to zero to signal no flow control */ + pdata->rx_rfa[queue] = 0; + pdata->rx_rfd[queue] = 0; + return; + } + + if (q_fifo_size <= 4096) { + /* Between 2048 and 4096 */ + pdata->rx_rfa[queue] = 0; /* Full - 1024 bytes */ + pdata->rx_rfd[queue] = 1; /* Full - 1536 bytes */ + return; + } + + if (q_fifo_size <= frame_fifo_size) { + /* Between 4096 and max-frame */ + pdata->rx_rfa[queue] = 2; /* Full - 2048 bytes */ + pdata->rx_rfd[queue] = 5; /* Full - 3584 bytes */ + return; + } + + if (q_fifo_size <= (frame_fifo_size * 3)) { + /* Between max-frame and 3 max-frames, + * trigger if we get just over a frame of data and + * resume when we have just under half a frame left. + */ + rfa = q_fifo_size - frame_fifo_size; + rfd = rfa + (frame_fifo_size / 2); + } else { + /* Above 3 max-frames - trigger when just over + * 2 frames of space available + */ + rfa = frame_fifo_size * 2; + rfa += XGMAC_FLOW_CONTROL_UNIT; + rfd = rfa + frame_fifo_size; + } + } + + pdata->rx_rfa[queue] = XGMAC_FLOW_CONTROL_VALUE(rfa); + pdata->rx_rfd[queue] = XGMAC_FLOW_CONTROL_VALUE(rfd); +} + +static void xgbe_calculate_flow_control_threshold(struct xgbe_prv_data *pdata, + unsigned int *fifo) +{ + unsigned int q_fifo_size; + unsigned int i; + + for (i = 0; i < pdata->rx_q_count; i++) { + q_fifo_size = (fifo[i] + 1) * XGMAC_FIFO_UNIT; + + xgbe_queue_flow_control_threshold(pdata, i, q_fifo_size); + } +} + +static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->rx_q_count; i++) { + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFA, + pdata->rx_rfa[i]); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFD, + pdata->rx_rfd[i]); + } +} + static unsigned int xgbe_get_tx_fifo_size(struct xgbe_prv_data *pdata) { unsigned int fifo_size; @@ -2032,18 +2035,158 @@ static void xgbe_calculate_equal_fifo(unsigned int fifo_size, q_fifo_size = fifo_size / queue_count; - /* Each increment in the queue fifo size represents 256 bytes of - * fifo, with 0 representing 256 bytes. Distribute the fifo equally - * between the queues. + /* Calculate the fifo setting by dividing the queue's fifo size + * by the fifo allocation increment (with 0 representing the + * base allocation increment so decrement the result by 1). */ - p_fifo = q_fifo_size / 256; + p_fifo = q_fifo_size / XGMAC_FIFO_UNIT; if (p_fifo) p_fifo--; + /* Distribute the fifo equally amongst the queues */ for (i = 0; i < queue_count; i++) fifo[i] = p_fifo; } +static unsigned int xgbe_set_nonprio_fifos(unsigned int fifo_size, + unsigned int queue_count, + unsigned int *fifo) +{ + unsigned int i; + + BUILD_BUG_ON_NOT_POWER_OF_2(XGMAC_FIFO_MIN_ALLOC); + + if (queue_count <= IEEE_8021QAZ_MAX_TCS) + return fifo_size; + + /* Rx queues 9 and up are for specialized packets, + * such as PTP or DCB control packets, etc. and + * don't require a large fifo + */ + for (i = IEEE_8021QAZ_MAX_TCS; i < queue_count; i++) { + fifo[i] = (XGMAC_FIFO_MIN_ALLOC / XGMAC_FIFO_UNIT) - 1; + fifo_size -= XGMAC_FIFO_MIN_ALLOC; + } + + return fifo_size; +} + +static unsigned int xgbe_get_pfc_delay(struct xgbe_prv_data *pdata) +{ + unsigned int delay; + + /* If a delay has been provided, use that */ + if (pdata->pfc->delay) + return pdata->pfc->delay / 8; + + /* Allow for two maximum size frames */ + delay = xgbe_get_max_frame(pdata); + delay += XGMAC_ETH_PREAMBLE; + delay *= 2; + + /* Allow for PFC frame */ + delay += XGMAC_PFC_DATA_LEN; + delay += ETH_HLEN + ETH_FCS_LEN; + delay += XGMAC_ETH_PREAMBLE; + + /* Allow for miscellaneous delays (LPI exit, cable, etc.) */ + delay += XGMAC_PFC_DELAYS; + + return delay; +} + +static unsigned int xgbe_get_pfc_queues(struct xgbe_prv_data *pdata) +{ + unsigned int count, prio_queues; + unsigned int i; + + if (!pdata->pfc->pfc_en) + return 0; + + count = 0; + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); + for (i = 0; i < prio_queues; i++) { + if (!xgbe_is_pfc_queue(pdata, i)) + continue; + + pdata->pfcq[i] = 1; + count++; + } + + return count; +} + +static void xgbe_calculate_dcb_fifo(struct xgbe_prv_data *pdata, + unsigned int fifo_size, + unsigned int *fifo) +{ + unsigned int q_fifo_size, rem_fifo, addn_fifo; + unsigned int prio_queues; + unsigned int pfc_count; + unsigned int i; + + q_fifo_size = XGMAC_FIFO_ALIGN(xgbe_get_max_frame(pdata)); + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); + pfc_count = xgbe_get_pfc_queues(pdata); + + if (!pfc_count || ((q_fifo_size * prio_queues) > fifo_size)) { + /* No traffic classes with PFC enabled or can't do lossless */ + xgbe_calculate_equal_fifo(fifo_size, prio_queues, fifo); + return; + } + + /* Calculate how much fifo we have to play with */ + rem_fifo = fifo_size - (q_fifo_size * prio_queues); + + /* Calculate how much more than base fifo PFC needs, which also + * becomes the threshold activation point (RFA) + */ + pdata->pfc_rfa = xgbe_get_pfc_delay(pdata); + pdata->pfc_rfa = XGMAC_FLOW_CONTROL_ALIGN(pdata->pfc_rfa); + + if (pdata->pfc_rfa > q_fifo_size) { + addn_fifo = pdata->pfc_rfa - q_fifo_size; + addn_fifo = XGMAC_FIFO_ALIGN(addn_fifo); + } else { + addn_fifo = 0; + } + + /* Calculate DCB fifo settings: + * - distribute remaining fifo between the VLAN priority + * queues based on traffic class PFC enablement and overall + * priority (0 is lowest priority, so start at highest) + */ + i = prio_queues; + while (i > 0) { + i--; + + fifo[i] = (q_fifo_size / XGMAC_FIFO_UNIT) - 1; + + if (!pdata->pfcq[i] || !addn_fifo) + continue; + + if (addn_fifo > rem_fifo) { + netdev_warn(pdata->netdev, + "RXq%u cannot set needed fifo size\n", i); + if (!rem_fifo) + continue; + + addn_fifo = rem_fifo; + } + + fifo[i] += (addn_fifo / XGMAC_FIFO_UNIT); + rem_fifo -= addn_fifo; + } + + if (rem_fifo) { + unsigned int inc_fifo = rem_fifo / prio_queues; + + /* Distribute remaining fifo across queues */ + for (i = 0; i < prio_queues; i++) + fifo[i] += (inc_fifo / XGMAC_FIFO_UNIT); + } +} + static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) { unsigned int fifo_size; @@ -2059,25 +2202,50 @@ static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) netif_info(pdata, drv, pdata->netdev, "%d Tx hardware queues, %d byte fifo per queue\n", - pdata->tx_q_count, ((fifo[0] + 1) * 256)); + pdata->tx_q_count, ((fifo[0] + 1) * XGMAC_FIFO_UNIT)); } static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata) { unsigned int fifo_size; unsigned int fifo[XGBE_MAX_QUEUES]; + unsigned int prio_queues; unsigned int i; + /* Clear any DCB related fifo/queue information */ + memset(pdata->pfcq, 0, sizeof(pdata->pfcq)); + pdata->pfc_rfa = 0; + fifo_size = xgbe_get_rx_fifo_size(pdata); + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); + + /* Assign a minimum fifo to the non-VLAN priority queues */ + fifo_size = xgbe_set_nonprio_fifos(fifo_size, pdata->rx_q_count, fifo); - xgbe_calculate_equal_fifo(fifo_size, pdata->rx_q_count, fifo); + if (pdata->pfc && pdata->ets) + xgbe_calculate_dcb_fifo(pdata, fifo_size, fifo); + else + xgbe_calculate_equal_fifo(fifo_size, prio_queues, fifo); for (i = 0; i < pdata->rx_q_count; i++) XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RQS, fifo[i]); - netif_info(pdata, drv, pdata->netdev, - "%d Rx hardware queues, %d byte fifo per queue\n", - pdata->rx_q_count, ((fifo[0] + 1) * 256)); + xgbe_calculate_flow_control_threshold(pdata, fifo); + xgbe_config_flow_control_threshold(pdata); + + if (pdata->pfc && pdata->ets && pdata->pfc->pfc_en) { + netif_info(pdata, drv, pdata->netdev, + "%u Rx hardware queues\n", pdata->rx_q_count); + for (i = 0; i < pdata->rx_q_count; i++) + netif_info(pdata, drv, pdata->netdev, + "RxQ%u, %u byte fifo queue\n", i, + ((fifo[i] + 1) * XGMAC_FIFO_UNIT)); + } else { + netif_info(pdata, drv, pdata->netdev, + "%u Rx hardware queues, %u byte fifo per queue\n", + pdata->rx_q_count, + ((fifo[0] + 1) * XGMAC_FIFO_UNIT)); + } } static void xgbe_config_queue_mapping(struct xgbe_prv_data *pdata) @@ -2113,8 +2281,7 @@ static void xgbe_config_queue_mapping(struct xgbe_prv_data *pdata) } /* Map the 8 VLAN priority values to available MTL Rx queues */ - prio_queues = min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, - pdata->rx_q_count); + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); ppq = IEEE_8021QAZ_MAX_TCS / prio_queues; ppq_extra = IEEE_8021QAZ_MAX_TCS % prio_queues; @@ -2162,16 +2329,120 @@ static void xgbe_config_queue_mapping(struct xgbe_prv_data *pdata) } } -static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata) +static void xgbe_config_tc(struct xgbe_prv_data *pdata) { - unsigned int i; + unsigned int offset, queue, prio; + u8 i; - for (i = 0; i < pdata->rx_q_count; i++) { - /* Activate flow control when less than 4k left in fifo */ - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFA, 2); + netdev_reset_tc(pdata->netdev); + if (!pdata->num_tcs) + return; + + netdev_set_num_tc(pdata->netdev, pdata->num_tcs); + + for (i = 0, queue = 0, offset = 0; i < pdata->num_tcs; i++) { + while ((queue < pdata->tx_q_count) && + (pdata->q2tc_map[queue] == i)) + queue++; + + netif_dbg(pdata, drv, pdata->netdev, "TC%u using TXq%u-%u\n", + i, offset, queue - 1); + netdev_set_tc_queue(pdata->netdev, i, queue - offset, offset); + offset = queue; + } + + if (!pdata->ets) + return; + + for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) + netdev_set_prio_tc_map(pdata->netdev, prio, + pdata->ets->prio_tc[prio]); +} + +static void xgbe_config_dcb_tc(struct xgbe_prv_data *pdata) +{ + struct ieee_ets *ets = pdata->ets; + unsigned int total_weight, min_weight, weight; + unsigned int mask, reg, reg_val; + unsigned int i, prio; + + if (!ets) + return; + + /* Set Tx to deficit weighted round robin scheduling algorithm (when + * traffic class is using ETS algorithm) + */ + XGMAC_IOWRITE_BITS(pdata, MTL_OMR, ETSALG, MTL_ETSALG_DWRR); + + /* Set Traffic Class algorithms */ + total_weight = pdata->netdev->mtu * pdata->hw_feat.tc_cnt; + min_weight = total_weight / 100; + if (!min_weight) + min_weight = 1; + + for (i = 0; i < pdata->hw_feat.tc_cnt; i++) { + /* Map the priorities to the traffic class */ + mask = 0; + for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) { + if (ets->prio_tc[prio] == i) + mask |= (1 << prio); + } + mask &= 0xff; + + netif_dbg(pdata, drv, pdata->netdev, "TC%u PRIO mask=%#x\n", + i, mask); + reg = MTL_TCPM0R + (MTL_TCPM_INC * (i / MTL_TCPM_TC_PER_REG)); + reg_val = XGMAC_IOREAD(pdata, reg); - /* De-activate flow control when more than 6k left in fifo */ - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFD, 4); + reg_val &= ~(0xff << ((i % MTL_TCPM_TC_PER_REG) << 3)); + reg_val |= (mask << ((i % MTL_TCPM_TC_PER_REG) << 3)); + + XGMAC_IOWRITE(pdata, reg, reg_val); + + /* Set the traffic class algorithm */ + switch (ets->tc_tsa[i]) { + case IEEE_8021QAZ_TSA_STRICT: + netif_dbg(pdata, drv, pdata->netdev, + "TC%u using SP\n", i); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, + MTL_TSA_SP); + break; + case IEEE_8021QAZ_TSA_ETS: + weight = total_weight * ets->tc_tx_bw[i] / 100; + weight = clamp(weight, min_weight, total_weight); + + netif_dbg(pdata, drv, pdata->netdev, + "TC%u using DWRR (weight %u)\n", i, weight); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, + MTL_TSA_ETS); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_QWR, QW, + weight); + break; + } + } + + xgbe_config_tc(pdata); +} + +static void xgbe_config_dcb_pfc(struct xgbe_prv_data *pdata) +{ + if (!test_bit(XGBE_DOWN, &pdata->dev_state)) { + /* Just stop the Tx queues while Rx fifo is changed */ + netif_tx_stop_all_queues(pdata->netdev); + + /* Suspend Rx so that fifo's can be adjusted */ + pdata->hw_if.disable_rx(pdata); + } + + xgbe_config_rx_fifo_size(pdata); + xgbe_config_flow_control(pdata); + + if (!test_bit(XGBE_DOWN, &pdata->dev_state)) { + /* Resume Rx */ + pdata->hw_if.enable_rx(pdata); + + /* Resume Tx queues */ + netif_tx_start_all_queues(pdata->netdev); } } @@ -2879,12 +3150,10 @@ static int xgbe_init(struct xgbe_prv_data *pdata) xgbe_config_rx_threshold(pdata, pdata->rx_threshold); xgbe_config_tx_fifo_size(pdata); xgbe_config_rx_fifo_size(pdata); - xgbe_config_flow_control_threshold(pdata); /*TODO: Error Packet and undersized good Packet forwarding enable (FEP and FUP) */ xgbe_config_dcb_tc(pdata); - xgbe_config_dcb_pfc(pdata); xgbe_enable_mtl_interrupts(pdata); /* diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index d838b44..a4e1b8d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -158,6 +158,7 @@ #define XGBE_MAX_DMA_CHANNELS 16 #define XGBE_MAX_QUEUES 16 +#define XGBE_PRIORITY_QUEUES 8 #define XGBE_DMA_STOP_TIMEOUT 5 /* DMA cache settings - Outer sharable, write-back, write-allocate */ @@ -177,6 +178,13 @@ #define XGMAC_MAX_STD_PACKET 1518 #define XGMAC_JUMBO_PACKET_MTU 9000 #define XGMAC_MAX_JUMBO_PACKET 9018 +#define XGMAC_ETH_PREAMBLE (12 + 8) /* Inter-frame gap + preamble */ + +#define XGMAC_PFC_DATA_LEN 46 +#define XGMAC_PFC_DELAYS 14000 + +#define XGMAC_PRIO_QUEUES(_cnt) \ + min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, (_cnt)) /* Common property names */ #define XGBE_MAC_ADDR_PROPERTY "mac-address" @@ -210,6 +218,12 @@ #define XGMAC_FIFO_RX_MAX 81920 #define XGMAC_FIFO_TX_MAX 81920 +#define XGMAC_FIFO_MIN_ALLOC 2048 +#define XGMAC_FIFO_UNIT 256 +#define XGMAC_FIFO_ALIGN(_x) \ + (((_x) + XGMAC_FIFO_UNIT - 1) & ~(XGMAC_FIFO_UNIT - 1)) +#define XGMAC_FIFO_FC_OFF 2048 +#define XGMAC_FIFO_FC_MIN 4096 #define XGBE_TC_MIN_QUANTUM 10 @@ -234,6 +248,14 @@ /* Flow control queue count */ #define XGMAC_MAX_FLOW_CONTROL_QUEUES 8 +/* Flow control threshold units */ +#define XGMAC_FLOW_CONTROL_UNIT 512 +#define XGMAC_FLOW_CONTROL_ALIGN(_x) \ + (((_x) + XGMAC_FLOW_CONTROL_UNIT - 1) & ~(XGMAC_FLOW_CONTROL_UNIT - 1)) +#define XGMAC_FLOW_CONTROL_VALUE(_x) \ + (((_x) < 1024) ? 0 : ((_x) / XGMAC_FLOW_CONTROL_UNIT) - 2) +#define XGMAC_FLOW_CONTROL_MAX 33280 + /* Maximum MAC address hash table size (256 bits = 8 bytes) */ #define XGBE_MAC_HASH_TABLE_SIZE 8 @@ -843,6 +865,8 @@ struct xgbe_prv_data { unsigned int pause_autoneg; unsigned int tx_pause; unsigned int rx_pause; + unsigned int rx_rfa[XGBE_MAX_QUEUES]; + unsigned int rx_rfd[XGBE_MAX_QUEUES]; /* Receive Side Scaling settings */ u8 rss_key[XGBE_RSS_HASH_KEY_SIZE]; @@ -882,6 +906,8 @@ struct xgbe_prv_data { struct ieee_pfc *pfc; unsigned int q2tc_map[XGBE_MAX_QUEUES]; unsigned int prio2q_map[IEEE_8021QAZ_MAX_TCS]; + unsigned int pfcq[XGBE_MAX_QUEUES]; + unsigned int pfc_rfa; u8 num_tcs; /* Hardware features of the device */ -- cgit v1.1 From e57f7a3feaef0e9e196801f1625bca1337405421 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:18:27 -0500 Subject: amd-xgbe: Prepare for working with more than one type of phy Prepare the code to be able to work with more than one type of phy by adding additional callable functions into the phy interface and removing phy specific settings/functions from non-phy related files. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/Makefile | 3 +- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 58 +- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 6 +- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 19 +- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 214 ++----- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 549 ++++++------------ drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c | 821 +++++++++++++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe.h | 130 +++-- 8 files changed, 1140 insertions(+), 660 deletions(-) create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile index 171a7e6..60b4ae2 100644 --- a/drivers/net/ethernet/amd/xgbe/Makefile +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -2,7 +2,8 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ - xgbe-ptp.o + xgbe-ptp.o \ + xgbe-phy-v1.o amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index f8fffea..75c3df1 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -717,32 +717,26 @@ static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata) XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xffffffff); } -static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata) +static int xgbe_set_speed(struct xgbe_prv_data *pdata, int speed) { - if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x3) - return 0; - - XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3); - - return 0; -} - -static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata) -{ - if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x2) - return 0; + unsigned int ss; - XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2); - - return 0; -} - -static int xgbe_set_xgmii_speed(struct xgbe_prv_data *pdata) -{ - if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0) - return 0; + switch (speed) { + case SPEED_1000: + ss = 0x03; + break; + case SPEED_2500: + ss = 0x02; + break; + case SPEED_10000: + ss = 0x00; + break; + default: + return -EINVAL; + } - XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0); + if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) != ss) + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, ss); return 0; } @@ -2469,19 +2463,7 @@ static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata) static void xgbe_config_mac_speed(struct xgbe_prv_data *pdata) { - switch (pdata->phy_speed) { - case SPEED_10000: - xgbe_set_xgmii_speed(pdata); - break; - - case SPEED_2500: - xgbe_set_gmii_2500_speed(pdata); - break; - - case SPEED_1000: - xgbe_set_gmii_speed(pdata); - break; - } + xgbe_set_speed(pdata, pdata->phy_speed); } static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata) @@ -3195,9 +3177,7 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if) hw_if->read_mmd_regs = xgbe_read_mmd_regs; hw_if->write_mmd_regs = xgbe_write_mmd_regs; - hw_if->set_gmii_speed = xgbe_set_gmii_speed; - hw_if->set_gmii_2500_speed = xgbe_set_gmii_2500_speed; - hw_if->set_xgmii_speed = xgbe_set_xgmii_speed; + hw_if->set_speed = xgbe_set_speed; hw_if->enable_tx = xgbe_enable_tx; hw_if->disable_tx = xgbe_disable_tx; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index c4e6682..dd166a0 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -778,7 +778,7 @@ static void xgbe_free_rx_data(struct xgbe_prv_data *pdata) DBGPR("<--xgbe_free_rx_data\n"); } -static int xgbe_phy_init(struct xgbe_prv_data *pdata) +static int xgbe_phy_reset(struct xgbe_prv_data *pdata) { pdata->phy_link = -1; pdata->phy_speed = SPEED_UNKNOWN; @@ -1292,8 +1292,8 @@ static int xgbe_open(struct net_device *netdev) DBGPR("-->xgbe_open\n"); - /* Initialize the phy */ - ret = xgbe_phy_init(pdata); + /* Reset the phy settings */ + ret = xgbe_phy_reset(pdata); if (ret) return ret; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 4007b42..46c959b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -316,24 +316,7 @@ static int xgbe_set_settings(struct net_device *netdev, } if (cmd->autoneg == AUTONEG_DISABLE) { - switch (speed) { - case SPEED_10000: - break; - case SPEED_2500: - if (pdata->speed_set != XGBE_SPEEDSET_2500_10000) { - netdev_err(netdev, "unsupported speed %u\n", - speed); - return -EINVAL; - } - break; - case SPEED_1000: - if (pdata->speed_set != XGBE_SPEEDSET_1000_10000) { - netdev_err(netdev, "unsupported speed %u\n", - speed); - return -EINVAL; - } - break; - default: + if (!pdata->phy_if.phy_valid_speed(pdata, speed)) { netdev_err(netdev, "unsupported speed %u\n", speed); return -EINVAL; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 6997f11..38c5985 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -125,6 +125,7 @@ #include #include #include +#include #include #include #include @@ -145,42 +146,6 @@ MODULE_PARM_DESC(debug, " Network interface message level setting"); static const u32 default_msg_level = (NETIF_MSG_LINK | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP); -static const u32 xgbe_serdes_blwc[] = { - XGBE_SPEED_1000_BLWC, - XGBE_SPEED_2500_BLWC, - XGBE_SPEED_10000_BLWC, -}; - -static const u32 xgbe_serdes_cdr_rate[] = { - XGBE_SPEED_1000_CDR, - XGBE_SPEED_2500_CDR, - XGBE_SPEED_10000_CDR, -}; - -static const u32 xgbe_serdes_pq_skew[] = { - XGBE_SPEED_1000_PQ, - XGBE_SPEED_2500_PQ, - XGBE_SPEED_10000_PQ, -}; - -static const u32 xgbe_serdes_tx_amp[] = { - XGBE_SPEED_1000_TXAMP, - XGBE_SPEED_2500_TXAMP, - XGBE_SPEED_10000_TXAMP, -}; - -static const u32 xgbe_serdes_dfe_tap_cfg[] = { - XGBE_SPEED_1000_DFE_TAP_CONFIG, - XGBE_SPEED_2500_DFE_TAP_CONFIG, - XGBE_SPEED_10000_DFE_TAP_CONFIG, -}; - -static const u32 xgbe_serdes_dfe_tap_ena[] = { - XGBE_SPEED_1000_DFE_TAP_ENABLE, - XGBE_SPEED_2500_DFE_TAP_ENABLE, - XGBE_SPEED_10000_DFE_TAP_ENABLE, -}; - static void xgbe_default_config(struct xgbe_prv_data *pdata) { DBGPR("-->xgbe_default_config\n"); @@ -207,9 +172,22 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata) xgbe_init_function_ptrs_dev(&pdata->hw_if); xgbe_init_function_ptrs_phy(&pdata->phy_if); xgbe_init_function_ptrs_desc(&pdata->desc_if); + + pdata->vdata->init_function_ptrs_phy_impl(&pdata->phy_if); } #ifdef CONFIG_ACPI +static const struct acpi_device_id xgbe_acpi_match[]; + +static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(xgbe_acpi_match, pdata->dev); + + return id ? (struct xgbe_version_data *)id->driver_data : NULL; +} + static int xgbe_acpi_support(struct xgbe_prv_data *pdata) { struct device *dev = pdata->dev; @@ -237,6 +215,11 @@ static int xgbe_acpi_support(struct xgbe_prv_data *pdata) return 0; } #else /* CONFIG_ACPI */ +static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) +{ + return NULL; +} + static int xgbe_acpi_support(struct xgbe_prv_data *pdata) { return -EINVAL; @@ -244,6 +227,17 @@ static int xgbe_acpi_support(struct xgbe_prv_data *pdata) #endif /* CONFIG_ACPI */ #ifdef CONFIG_OF +static const struct of_device_id xgbe_of_match[]; + +static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) +{ + const struct of_device_id *id; + + id = of_match_device(xgbe_of_match, pdata->dev); + + return id ? (struct xgbe_version_data *)id->data : NULL; +} + static int xgbe_of_support(struct xgbe_prv_data *pdata) { struct device *dev = pdata->dev; @@ -292,6 +286,11 @@ static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata) return phy_pdev; } #else /* CONFIG_OF */ +static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) +{ + return NULL; +} + static int xgbe_of_support(struct xgbe_prv_data *pdata) { return -EINVAL; @@ -333,11 +332,17 @@ static struct platform_device *xgbe_get_phy_pdev(struct xgbe_prv_data *pdata) return phy_pdev; } +static struct xgbe_version_data *xgbe_get_vdata(struct xgbe_prv_data *pdata) +{ + return pdata->use_acpi ? xgbe_acpi_vdata(pdata) + : xgbe_of_vdata(pdata); +} + static int xgbe_probe(struct platform_device *pdev) { struct xgbe_prv_data *pdata; struct net_device *netdev; - struct device *dev = &pdev->dev, *phy_dev; + struct device *dev = &pdev->dev; struct platform_device *phy_pdev; struct resource *res; const char *phy_mode; @@ -374,13 +379,17 @@ static int xgbe_probe(struct platform_device *pdev) /* Check if we should use ACPI or DT */ pdata->use_acpi = dev->of_node ? 0 : 1; + /* Get the version data */ + pdata->vdata = xgbe_get_vdata(pdata); + phy_pdev = xgbe_get_phy_pdev(pdata); if (!phy_pdev) { dev_err(dev, "unable to obtain phy device\n"); ret = -EINVAL; goto err_phydev; } - phy_dev = &phy_pdev->dev; + pdata->phy_pdev = phy_pdev; + pdata->phy_dev = &phy_pdev->dev; if (pdev == phy_pdev) { /* New style device tree or ACPI: @@ -492,115 +501,6 @@ static int xgbe_probe(struct platform_device *pdev) if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) pdata->per_channel_irq = 1; - /* Retrieve the PHY speedset */ - ret = device_property_read_u32(phy_dev, XGBE_SPEEDSET_PROPERTY, - &pdata->speed_set); - if (ret) { - dev_err(dev, "invalid %s property\n", XGBE_SPEEDSET_PROPERTY); - goto err_io; - } - - switch (pdata->speed_set) { - case XGBE_SPEEDSET_1000_10000: - case XGBE_SPEEDSET_2500_10000: - break; - default: - dev_err(dev, "invalid %s property\n", XGBE_SPEEDSET_PROPERTY); - ret = -EINVAL; - goto err_io; - } - - /* Retrieve the PHY configuration properties */ - if (device_property_present(phy_dev, XGBE_BLWC_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_BLWC_PROPERTY, - pdata->serdes_blwc, - XGBE_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_BLWC_PROPERTY); - goto err_io; - } - } else { - memcpy(pdata->serdes_blwc, xgbe_serdes_blwc, - sizeof(pdata->serdes_blwc)); - } - - if (device_property_present(phy_dev, XGBE_CDR_RATE_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_CDR_RATE_PROPERTY, - pdata->serdes_cdr_rate, - XGBE_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_CDR_RATE_PROPERTY); - goto err_io; - } - } else { - memcpy(pdata->serdes_cdr_rate, xgbe_serdes_cdr_rate, - sizeof(pdata->serdes_cdr_rate)); - } - - if (device_property_present(phy_dev, XGBE_PQ_SKEW_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_PQ_SKEW_PROPERTY, - pdata->serdes_pq_skew, - XGBE_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PQ_SKEW_PROPERTY); - goto err_io; - } - } else { - memcpy(pdata->serdes_pq_skew, xgbe_serdes_pq_skew, - sizeof(pdata->serdes_pq_skew)); - } - - if (device_property_present(phy_dev, XGBE_TX_AMP_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_TX_AMP_PROPERTY, - pdata->serdes_tx_amp, - XGBE_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_TX_AMP_PROPERTY); - goto err_io; - } - } else { - memcpy(pdata->serdes_tx_amp, xgbe_serdes_tx_amp, - sizeof(pdata->serdes_tx_amp)); - } - - if (device_property_present(phy_dev, XGBE_DFE_CFG_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_DFE_CFG_PROPERTY, - pdata->serdes_dfe_tap_cfg, - XGBE_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_DFE_CFG_PROPERTY); - goto err_io; - } - } else { - memcpy(pdata->serdes_dfe_tap_cfg, xgbe_serdes_dfe_tap_cfg, - sizeof(pdata->serdes_dfe_tap_cfg)); - } - - if (device_property_present(phy_dev, XGBE_DFE_ENA_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_DFE_ENA_PROPERTY, - pdata->serdes_dfe_tap_ena, - XGBE_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_DFE_ENA_PROPERTY); - goto err_io; - } - } else { - memcpy(pdata->serdes_dfe_tap_ena, xgbe_serdes_dfe_tap_ena, - sizeof(pdata->serdes_dfe_tap_ena)); - } - /* Obtain device settings unique to ACPI/OF */ if (pdata->use_acpi) ret = xgbe_acpi_support(pdata); @@ -705,7 +605,9 @@ static int xgbe_probe(struct platform_device *pdev) XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1); /* Call MDIO/PHY initialization routine */ - pdata->phy_if.phy_init(pdata); + ret = pdata->phy_if.phy_init(pdata); + if (ret) + goto err_io; /* Set device operations */ netdev->netdev_ops = xgbe_get_netdev_ops(); @@ -780,8 +682,6 @@ static int xgbe_probe(struct platform_device *pdev) xgbe_debugfs_init(pdata); - platform_device_put(phy_pdev); - netdev_notice(netdev, "net device enabled\n"); DBGPR("<-- xgbe_probe\n"); @@ -817,6 +717,8 @@ static int xgbe_remove(struct platform_device *pdev) xgbe_ptp_unregister(pdata); + pdata->phy_if.phy_exit(pdata); + flush_workqueue(pdata->an_workqueue); destroy_workqueue(pdata->an_workqueue); @@ -825,6 +727,8 @@ static int xgbe_remove(struct platform_device *pdev) unregister_netdev(netdev); + platform_device_put(pdata->phy_pdev); + free_netdev(netdev); DBGPR("<--xgbe_remove\n"); @@ -879,9 +783,14 @@ static int xgbe_resume(struct device *dev) } #endif /* CONFIG_PM */ +static const struct xgbe_version_data xgbe_v1 = { + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v1, +}; + #ifdef CONFIG_ACPI static const struct acpi_device_id xgbe_acpi_match[] = { - { "AMDI8001", 0 }, + { .id = "AMDI8001", + .driver_data = (kernel_ulong_t)&xgbe_v1 }, {}, }; @@ -890,7 +799,8 @@ MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match); #ifdef CONFIG_OF static const struct of_device_id xgbe_of_match[] = { - { .compatible = "amd,xgbe-seattle-v1a", }, + { .compatible = "amd,xgbe-seattle-v1a", + .data = &xgbe_v1 }, {}, }; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index e9b01fc..35c302f 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -145,280 +145,91 @@ static void xgbe_an_disable_kr_training(struct xgbe_prv_data *pdata) XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); } -static void xgbe_pcs_power_cycle(struct xgbe_prv_data *pdata) +static void xgbe_kr_mode(struct xgbe_prv_data *pdata) { - unsigned int reg; - - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); - - reg |= MDIO_CTRL1_LPOWER; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); - - usleep_range(75, 100); - - reg &= ~MDIO_CTRL1_LPOWER; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); -} - -static void xgbe_serdes_start_ratechange(struct xgbe_prv_data *pdata) -{ - /* Assert Rx and Tx ratechange */ - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1); -} - -static void xgbe_serdes_complete_ratechange(struct xgbe_prv_data *pdata) -{ - unsigned int wait; - u16 status; - - /* Release Rx and Tx ratechange */ - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0); - - /* Wait for Rx and Tx ready */ - wait = XGBE_RATECHANGE_COUNT; - while (wait--) { - usleep_range(50, 75); - - status = XSIR0_IOREAD(pdata, SIR0_STATUS); - if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) && - XSIR_GET_BITS(status, SIR0_STATUS, TX_READY)) - goto rx_reset; - } - - netif_dbg(pdata, link, pdata->netdev, "SerDes rx/tx not ready (%#hx)\n", - status); - -rx_reset: - /* Perform Rx reset for the DFE changes */ - XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 0); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 1); -} - -static void xgbe_xgmii_mode(struct xgbe_prv_data *pdata) -{ - unsigned int reg; - /* Enable KR training */ xgbe_an_enable_kr_training(pdata); /* Set MAC to 10G speed */ - pdata->hw_if.set_xgmii_speed(pdata); - - /* Set PCS to KR/10G speed */ - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); - reg &= ~MDIO_PCS_CTRL2_TYPE; - reg |= MDIO_PCS_CTRL2_10GBR; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); - - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); - reg &= ~MDIO_CTRL1_SPEEDSEL; - reg |= MDIO_CTRL1_SPEED10G; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); - - xgbe_pcs_power_cycle(pdata); - - /* Set SerDes to 10G speed */ - xgbe_serdes_start_ratechange(pdata); - - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_10000_RATE); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_10000_WORD); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_10000_PLL); - - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, - pdata->serdes_cdr_rate[XGBE_SPEED_10000]); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, - pdata->serdes_tx_amp[XGBE_SPEED_10000]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, - pdata->serdes_blwc[XGBE_SPEED_10000]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, - pdata->serdes_pq_skew[XGBE_SPEED_10000]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, - pdata->serdes_dfe_tap_cfg[XGBE_SPEED_10000]); - XRXTX_IOWRITE(pdata, RXTX_REG22, - pdata->serdes_dfe_tap_ena[XGBE_SPEED_10000]); - - xgbe_serdes_complete_ratechange(pdata); - - netif_dbg(pdata, link, pdata->netdev, "10GbE KR mode set\n"); + pdata->hw_if.set_speed(pdata, SPEED_10000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KR); } -static void xgbe_gmii_2500_mode(struct xgbe_prv_data *pdata) +static void xgbe_kx_2500_mode(struct xgbe_prv_data *pdata) { - unsigned int reg; - /* Disable KR training */ xgbe_an_disable_kr_training(pdata); /* Set MAC to 2.5G speed */ - pdata->hw_if.set_gmii_2500_speed(pdata); - - /* Set PCS to KX/1G speed */ - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); - reg &= ~MDIO_PCS_CTRL2_TYPE; - reg |= MDIO_PCS_CTRL2_10GBX; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); - - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); - reg &= ~MDIO_CTRL1_SPEEDSEL; - reg |= MDIO_CTRL1_SPEED1G; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); - - xgbe_pcs_power_cycle(pdata); - - /* Set SerDes to 2.5G speed */ - xgbe_serdes_start_ratechange(pdata); - - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_2500_RATE); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_2500_WORD); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_2500_PLL); - - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, - pdata->serdes_cdr_rate[XGBE_SPEED_2500]); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, - pdata->serdes_tx_amp[XGBE_SPEED_2500]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, - pdata->serdes_blwc[XGBE_SPEED_2500]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, - pdata->serdes_pq_skew[XGBE_SPEED_2500]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, - pdata->serdes_dfe_tap_cfg[XGBE_SPEED_2500]); - XRXTX_IOWRITE(pdata, RXTX_REG22, - pdata->serdes_dfe_tap_ena[XGBE_SPEED_2500]); - - xgbe_serdes_complete_ratechange(pdata); - - netif_dbg(pdata, link, pdata->netdev, "2.5GbE KX mode set\n"); + pdata->hw_if.set_speed(pdata, SPEED_2500); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_2500); } -static void xgbe_gmii_mode(struct xgbe_prv_data *pdata) +static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata) { - unsigned int reg; - /* Disable KR training */ xgbe_an_disable_kr_training(pdata); /* Set MAC to 1G speed */ - pdata->hw_if.set_gmii_speed(pdata); - - /* Set PCS to KX/1G speed */ - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); - reg &= ~MDIO_PCS_CTRL2_TYPE; - reg |= MDIO_PCS_CTRL2_10GBX; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); - - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); - reg &= ~MDIO_CTRL1_SPEEDSEL; - reg |= MDIO_CTRL1_SPEED1G; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); - - xgbe_pcs_power_cycle(pdata); - - /* Set SerDes to 1G speed */ - xgbe_serdes_start_ratechange(pdata); - - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_1000_RATE); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_1000_WORD); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_1000_PLL); - - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, - pdata->serdes_cdr_rate[XGBE_SPEED_1000]); - XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, - pdata->serdes_tx_amp[XGBE_SPEED_1000]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, - pdata->serdes_blwc[XGBE_SPEED_1000]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, - pdata->serdes_pq_skew[XGBE_SPEED_1000]); - XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, - pdata->serdes_dfe_tap_cfg[XGBE_SPEED_1000]); - XRXTX_IOWRITE(pdata, RXTX_REG22, - pdata->serdes_dfe_tap_ena[XGBE_SPEED_1000]); - - xgbe_serdes_complete_ratechange(pdata); - - netif_dbg(pdata, link, pdata->netdev, "1GbE KX mode set\n"); + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_1000); } -static void xgbe_cur_mode(struct xgbe_prv_data *pdata, - enum xgbe_mode *mode) +static enum xgbe_mode xgbe_cur_mode(struct xgbe_prv_data *pdata) { - unsigned int reg; - - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); - if ((reg & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR) - *mode = XGBE_MODE_KR; - else - *mode = XGBE_MODE_KX; + return pdata->phy_if.phy_impl.cur_mode(pdata); } static bool xgbe_in_kr_mode(struct xgbe_prv_data *pdata) { - enum xgbe_mode mode; - - xgbe_cur_mode(pdata, &mode); - - return (mode == XGBE_MODE_KR); + return (xgbe_cur_mode(pdata) == XGBE_MODE_KR); } -static void xgbe_switch_mode(struct xgbe_prv_data *pdata) +static void xgbe_change_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) { - /* If we are in KR switch to KX, and vice-versa */ - if (xgbe_in_kr_mode(pdata)) { - if (pdata->speed_set == XGBE_SPEEDSET_1000_10000) - xgbe_gmii_mode(pdata); - else - xgbe_gmii_2500_mode(pdata); - } else { - xgbe_xgmii_mode(pdata); + switch (mode) { + case XGBE_MODE_KX_1000: + xgbe_kx_1000_mode(pdata); + break; + case XGBE_MODE_KX_2500: + xgbe_kx_2500_mode(pdata); + break; + case XGBE_MODE_KR: + xgbe_kr_mode(pdata); + break; + case XGBE_MODE_UNKNOWN: + break; + default: + netif_dbg(pdata, link, pdata->netdev, + "invalid operation mode requested (%u)\n", mode); } } -static void xgbe_set_mode(struct xgbe_prv_data *pdata, - enum xgbe_mode mode) -{ - enum xgbe_mode cur_mode; - - xgbe_cur_mode(pdata, &cur_mode); - if (mode != cur_mode) - xgbe_switch_mode(pdata); -} - -static bool xgbe_use_xgmii_mode(struct xgbe_prv_data *pdata) +static void xgbe_switch_mode(struct xgbe_prv_data *pdata) { - if (pdata->phy.autoneg == AUTONEG_ENABLE) { - if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) - return true; - } else { - if (pdata->phy.speed == SPEED_10000) - return true; - } - - return false; + xgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata)); } -static bool xgbe_use_gmii_2500_mode(struct xgbe_prv_data *pdata) +static void xgbe_set_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) { - if (pdata->phy.autoneg == AUTONEG_ENABLE) { - if (pdata->phy.advertising & ADVERTISED_2500baseX_Full) - return true; - } else { - if (pdata->phy.speed == SPEED_2500) - return true; - } + if (mode == xgbe_cur_mode(pdata)) + return; - return false; + xgbe_change_mode(pdata, mode); } -static bool xgbe_use_gmii_mode(struct xgbe_prv_data *pdata) +static bool xgbe_use_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) { - if (pdata->phy.autoneg == AUTONEG_ENABLE) { - if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full) - return true; - } else { - if (pdata->phy.speed == SPEED_1000) - return true; - } - - return false; + return pdata->phy_if.phy_impl.use_mode(pdata, mode); } static void xgbe_set_an(struct xgbe_prv_data *pdata, bool enable, bool restart) @@ -476,13 +287,15 @@ static enum xgbe_an xgbe_an_tx_training(struct xgbe_prv_data *pdata, /* Start KR training */ reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); if (reg & XGBE_KR_TRAINING_ENABLE) { - XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1); + if (pdata->phy_if.phy_impl.kr_training_pre) + pdata->phy_if.phy_impl.kr_training_pre(pdata); reg |= XGBE_KR_TRAINING_START; XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); - XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0); + if (pdata->phy_if.phy_impl.kr_training_post) + pdata->phy_if.phy_impl.kr_training_post(pdata); netif_dbg(pdata, link, pdata->netdev, "KR training initiated\n"); @@ -571,7 +384,7 @@ static enum xgbe_an xgbe_an_page_received(struct xgbe_prv_data *pdata) } state = xgbe_in_kr_mode(pdata) ? &pdata->kr_state - : &pdata->kx_state; + : &pdata->kx_state; switch (*state) { case XGBE_RX_BPA: @@ -907,24 +720,28 @@ static void xgbe_phy_adjust_link(struct xgbe_prv_data *pdata) xgbe_phy_print_status(pdata); } +static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) +{ + return pdata->phy_if.phy_impl.valid_speed(pdata, speed); +} + static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) { + enum xgbe_mode mode; + netif_dbg(pdata, link, pdata->netdev, "fixed PHY configuration\n"); /* Disable auto-negotiation */ xgbe_disable_an(pdata); - /* Validate/Set specified speed */ - switch (pdata->phy.speed) { - case SPEED_10000: - xgbe_set_mode(pdata, XGBE_MODE_KR); - break; - - case SPEED_2500: - case SPEED_1000: - xgbe_set_mode(pdata, XGBE_MODE_KX); + /* Set specified mode for specified speed */ + mode = pdata->phy_if.phy_impl.get_mode(pdata, pdata->phy.speed); + switch (mode) { + case XGBE_MODE_KX_1000: + case XGBE_MODE_KX_2500: + case XGBE_MODE_KR: break; - + case XGBE_MODE_UNKNOWN: default: return -EINVAL; } @@ -933,6 +750,8 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) if (pdata->phy.duplex != DUPLEX_FULL) return -EINVAL; + xgbe_set_mode(pdata, mode); + return 0; } @@ -950,11 +769,12 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) disable_irq(pdata->an_irq); /* Start auto-negotiation in a supported mode */ - if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) { + if (xgbe_use_mode(pdata, XGBE_MODE_KR)) { xgbe_set_mode(pdata, XGBE_MODE_KR); - } else if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) || - (pdata->phy.advertising & ADVERTISED_2500baseX_Full)) { - xgbe_set_mode(pdata, XGBE_MODE_KX); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) { + xgbe_set_mode(pdata, XGBE_MODE_KX_2500); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { + xgbe_set_mode(pdata, XGBE_MODE_KX_1000); } else { enable_irq(pdata->an_irq); return -EINVAL; @@ -1016,108 +836,45 @@ static void xgbe_check_link_timeout(struct xgbe_prv_data *pdata) } } -static void xgbe_phy_status_force(struct xgbe_prv_data *pdata) +static enum xgbe_mode xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) { - if (xgbe_in_kr_mode(pdata)) { - pdata->phy.speed = SPEED_10000; - } else { - switch (pdata->speed_set) { - case XGBE_SPEEDSET_1000_10000: - pdata->phy.speed = SPEED_1000; - break; - - case XGBE_SPEEDSET_2500_10000: - pdata->phy.speed = SPEED_2500; - break; - } - } - pdata->phy.duplex = DUPLEX_FULL; + return pdata->phy_if.phy_impl.an_outcome(pdata); } -static void xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) +static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) { - unsigned int ad_reg, lp_reg; + enum xgbe_mode mode; pdata->phy.lp_advertising = 0; if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect) - return xgbe_phy_status_force(pdata); - - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_Backplane; - - /* Compare Advertisement and Link Partner register 1 */ - ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); - if (lp_reg & 0x400) - pdata->phy.lp_advertising |= ADVERTISED_Pause; - if (lp_reg & 0x800) - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; - - if (pdata->phy.pause_autoneg) { - /* Set flow control based on auto-negotiation result */ - pdata->phy.tx_pause = 0; - pdata->phy.rx_pause = 0; - - if (ad_reg & lp_reg & 0x400) { - pdata->phy.tx_pause = 1; - pdata->phy.rx_pause = 1; - } else if (ad_reg & lp_reg & 0x800) { - if (ad_reg & 0x400) - pdata->phy.rx_pause = 1; - else if (lp_reg & 0x400) - pdata->phy.tx_pause = 1; - } - } - - /* Compare Advertisement and Link Partner register 2 */ - ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); - lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); - if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; - if (lp_reg & 0x20) { - switch (pdata->speed_set) { - case XGBE_SPEEDSET_1000_10000: - pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; - break; - case XGBE_SPEEDSET_2500_10000: - pdata->phy.lp_advertising |= ADVERTISED_2500baseX_Full; - break; - } - } + mode = xgbe_cur_mode(pdata); + else + mode = xgbe_phy_status_aneg(pdata); - ad_reg &= lp_reg; - if (ad_reg & 0x80) { + switch (mode) { + case XGBE_MODE_KX_1000: + pdata->phy.speed = SPEED_1000; + break; + case XGBE_MODE_KX_2500: + pdata->phy.speed = SPEED_2500; + break; + case XGBE_MODE_KR: pdata->phy.speed = SPEED_10000; - xgbe_set_mode(pdata, XGBE_MODE_KR); - } else if (ad_reg & 0x20) { - switch (pdata->speed_set) { - case XGBE_SPEEDSET_1000_10000: - pdata->phy.speed = SPEED_1000; - break; - - case XGBE_SPEEDSET_2500_10000: - pdata->phy.speed = SPEED_2500; - break; - } - - xgbe_set_mode(pdata, XGBE_MODE_KX); - } else { + break; + case XGBE_MODE_UNKNOWN: + default: pdata->phy.speed = SPEED_UNKNOWN; } - /* Compare Advertisement and Link Partner register 3 */ - ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); - if (lp_reg & 0xc000) - pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; - pdata->phy.duplex = DUPLEX_FULL; + + xgbe_set_mode(pdata, mode); } static void xgbe_phy_status(struct xgbe_prv_data *pdata) { - unsigned int reg, link_aneg; + unsigned int link_aneg; if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { netif_carrier_off(pdata->netdev); @@ -1128,20 +885,14 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE); - /* Get the link status. Link status is latched low, so read - * once to clear and then read again to get current state - */ - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); - pdata->phy.link = (reg & MDIO_STAT1_LSTATUS) ? 1 : 0; - + pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata); if (pdata->phy.link) { if (link_aneg && !xgbe_phy_aneg_done(pdata)) { xgbe_check_link_timeout(pdata); return; } - xgbe_phy_status_aneg(pdata); + xgbe_phy_status_result(pdata); if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) clear_bit(XGBE_LINK_INIT, &pdata->dev_state); @@ -1155,7 +906,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) return; } - xgbe_phy_status_aneg(pdata); + xgbe_phy_status_result(pdata); netif_carrier_off(pdata->netdev); } @@ -1168,6 +919,12 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) { netif_dbg(pdata, link, pdata->netdev, "stopping PHY\n"); + if (!pdata->phy_started) + return; + + /* Indicate the PHY is down */ + pdata->phy_started = 0; + /* Disable auto-negotiation */ xgbe_disable_an(pdata); @@ -1176,6 +933,8 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) devm_free_irq(pdata->dev, pdata->an_irq, pdata); + pdata->phy_if.phy_impl.stop(pdata); + pdata->phy.link = 0; netif_carrier_off(pdata->netdev); @@ -1189,28 +948,35 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) netif_dbg(pdata, link, pdata->netdev, "starting PHY\n"); + ret = pdata->phy_if.phy_impl.start(pdata); + if (ret) + return ret; + ret = devm_request_irq(pdata->dev, pdata->an_irq, xgbe_an_isr, 0, pdata->an_name, pdata); if (ret) { netdev_err(netdev, "phy irq request failed\n"); - return ret; + goto err_stop; } /* Set initial mode - call the mode setting routines * directly to insure we are properly configured */ - if (xgbe_use_xgmii_mode(pdata)) { - xgbe_xgmii_mode(pdata); - } else if (xgbe_use_gmii_mode(pdata)) { - xgbe_gmii_mode(pdata); - } else if (xgbe_use_gmii_2500_mode(pdata)) { - xgbe_gmii_2500_mode(pdata); + if (xgbe_use_mode(pdata, XGBE_MODE_KR)) { + xgbe_kr_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) { + xgbe_kx_2500_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { + xgbe_kx_1000_mode(pdata); } else { ret = -EINVAL; goto err_irq; } + /* Indicate the PHY is up and running */ + pdata->phy_started = 1; + /* Set up advertisement registers based on current settings */ xgbe_an_init(pdata); @@ -1222,25 +988,19 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) err_irq: devm_free_irq(pdata->dev, pdata->an_irq, pdata); +err_stop: + pdata->phy_if.phy_impl.stop(pdata); + return ret; } static int xgbe_phy_reset(struct xgbe_prv_data *pdata) { - unsigned int count, reg; - - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); - reg |= MDIO_CTRL1_RESET; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); - - count = 50; - do { - msleep(20); - reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); - } while ((reg & MDIO_CTRL1_RESET) && --count); + int ret; - if (reg & MDIO_CTRL1_RESET) - return -ETIMEDOUT; + ret = pdata->phy_if.phy_impl.reset(pdata); + if (ret) + return ret; /* Disable auto-negotiation for now */ xgbe_disable_an(pdata); @@ -1290,41 +1050,57 @@ static void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) dev_dbg(dev, "\n*************************************************\n"); } -static void xgbe_phy_init(struct xgbe_prv_data *pdata) +static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) +{ + if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) + return SPEED_10000; + else if (pdata->phy.advertising & ADVERTISED_2500baseX_Full) + return SPEED_2500; + else if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full) + return SPEED_1000; + + return SPEED_UNKNOWN; +} + +static void xgbe_phy_exit(struct xgbe_prv_data *pdata) { + xgbe_phy_stop(pdata); + + pdata->phy_if.phy_impl.exit(pdata); +} + +static int xgbe_phy_init(struct xgbe_prv_data *pdata) +{ + int ret; + mutex_init(&pdata->an_mutex); INIT_WORK(&pdata->an_irq_work, xgbe_an_irq_work); INIT_WORK(&pdata->an_work, xgbe_an_state_machine); pdata->mdio_mmd = MDIO_MMD_PCS; - /* Initialize supported features */ - pdata->phy.supported = SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_Backplane; - pdata->phy.supported |= SUPPORTED_10000baseKR_Full; - switch (pdata->speed_set) { - case XGBE_SPEEDSET_1000_10000: - pdata->phy.supported |= SUPPORTED_1000baseKX_Full; - break; - case XGBE_SPEEDSET_2500_10000: - pdata->phy.supported |= SUPPORTED_2500baseX_Full; - break; - } - + /* Check for FEC support */ pdata->fec_ability = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECABLE); pdata->fec_ability &= (MDIO_PMA_10GBR_FECABLE_ABLE | MDIO_PMA_10GBR_FECABLE_ERRABLE); - if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) - pdata->phy.supported |= SUPPORTED_10000baseR_FEC; + /* Setup the phy (including supported features) */ + ret = pdata->phy_if.phy_impl.init(pdata); + if (ret) + return ret; pdata->phy.advertising = pdata->phy.supported; pdata->phy.address = 0; - pdata->phy.autoneg = AUTONEG_ENABLE; - pdata->phy.speed = SPEED_UNKNOWN; - pdata->phy.duplex = DUPLEX_UNKNOWN; + if (pdata->phy.advertising & ADVERTISED_Autoneg) { + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + } else { + pdata->phy.autoneg = AUTONEG_DISABLE; + pdata->phy.speed = xgbe_phy_best_advertised_speed(pdata); + pdata->phy.duplex = DUPLEX_FULL; + } pdata->phy.link = 0; @@ -1346,11 +1122,14 @@ static void xgbe_phy_init(struct xgbe_prv_data *pdata) if (netif_msg_drv(pdata)) xgbe_dump_phy_registers(pdata); + + return 0; } void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if) { phy_if->phy_init = xgbe_phy_init; + phy_if->phy_exit = xgbe_phy_exit; phy_if->phy_reset = xgbe_phy_reset; phy_if->phy_start = xgbe_phy_start; @@ -1358,4 +1137,6 @@ void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if) phy_if->phy_status = xgbe_phy_status; phy_if->phy_config_aneg = xgbe_phy_config_aneg; + + phy_if->phy_valid_speed = xgbe_phy_valid_speed; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c new file mode 100644 index 0000000..20ccb99b --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -0,0 +1,821 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_BLWC_PROPERTY "amd,serdes-blwc" +#define XGBE_CDR_RATE_PROPERTY "amd,serdes-cdr-rate" +#define XGBE_PQ_SKEW_PROPERTY "amd,serdes-pq-skew" +#define XGBE_TX_AMP_PROPERTY "amd,serdes-tx-amp" +#define XGBE_DFE_CFG_PROPERTY "amd,serdes-dfe-tap-config" +#define XGBE_DFE_ENA_PROPERTY "amd,serdes-dfe-tap-enable" + +/* Default SerDes settings */ +#define XGBE_SPEED_1000_BLWC 1 +#define XGBE_SPEED_1000_CDR 0x2 +#define XGBE_SPEED_1000_PLL 0x0 +#define XGBE_SPEED_1000_PQ 0xa +#define XGBE_SPEED_1000_RATE 0x3 +#define XGBE_SPEED_1000_TXAMP 0xf +#define XGBE_SPEED_1000_WORD 0x1 +#define XGBE_SPEED_1000_DFE_TAP_CONFIG 0x3 +#define XGBE_SPEED_1000_DFE_TAP_ENABLE 0x0 + +#define XGBE_SPEED_2500_BLWC 1 +#define XGBE_SPEED_2500_CDR 0x2 +#define XGBE_SPEED_2500_PLL 0x0 +#define XGBE_SPEED_2500_PQ 0xa +#define XGBE_SPEED_2500_RATE 0x1 +#define XGBE_SPEED_2500_TXAMP 0xf +#define XGBE_SPEED_2500_WORD 0x1 +#define XGBE_SPEED_2500_DFE_TAP_CONFIG 0x3 +#define XGBE_SPEED_2500_DFE_TAP_ENABLE 0x0 + +#define XGBE_SPEED_10000_BLWC 0 +#define XGBE_SPEED_10000_CDR 0x7 +#define XGBE_SPEED_10000_PLL 0x1 +#define XGBE_SPEED_10000_PQ 0x12 +#define XGBE_SPEED_10000_RATE 0x0 +#define XGBE_SPEED_10000_TXAMP 0xa +#define XGBE_SPEED_10000_WORD 0x7 +#define XGBE_SPEED_10000_DFE_TAP_CONFIG 0x1 +#define XGBE_SPEED_10000_DFE_TAP_ENABLE 0x7f + +/* Rate-change complete wait/retry count */ +#define XGBE_RATECHANGE_COUNT 500 + +static const u32 xgbe_phy_blwc[] = { + XGBE_SPEED_1000_BLWC, + XGBE_SPEED_2500_BLWC, + XGBE_SPEED_10000_BLWC, +}; + +static const u32 xgbe_phy_cdr_rate[] = { + XGBE_SPEED_1000_CDR, + XGBE_SPEED_2500_CDR, + XGBE_SPEED_10000_CDR, +}; + +static const u32 xgbe_phy_pq_skew[] = { + XGBE_SPEED_1000_PQ, + XGBE_SPEED_2500_PQ, + XGBE_SPEED_10000_PQ, +}; + +static const u32 xgbe_phy_tx_amp[] = { + XGBE_SPEED_1000_TXAMP, + XGBE_SPEED_2500_TXAMP, + XGBE_SPEED_10000_TXAMP, +}; + +static const u32 xgbe_phy_dfe_tap_cfg[] = { + XGBE_SPEED_1000_DFE_TAP_CONFIG, + XGBE_SPEED_2500_DFE_TAP_CONFIG, + XGBE_SPEED_10000_DFE_TAP_CONFIG, +}; + +static const u32 xgbe_phy_dfe_tap_ena[] = { + XGBE_SPEED_1000_DFE_TAP_ENABLE, + XGBE_SPEED_2500_DFE_TAP_ENABLE, + XGBE_SPEED_10000_DFE_TAP_ENABLE, +}; + +struct xgbe_phy_data { + /* 1000/10000 vs 2500/10000 indicator */ + unsigned int speed_set; + + /* SerDes UEFI configurable settings. + * Switching between modes/speeds requires new values for some + * SerDes settings. The values can be supplied as device + * properties in array format. The first array entry is for + * 1GbE, second for 2.5GbE and third for 10GbE + */ + u32 blwc[XGBE_SPEEDS]; + u32 cdr_rate[XGBE_SPEEDS]; + u32 pq_skew[XGBE_SPEEDS]; + u32 tx_amp[XGBE_SPEEDS]; + u32 dfe_tap_cfg[XGBE_SPEEDS]; + u32 dfe_tap_ena[XGBE_SPEEDS]; +}; + +static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata) +{ + XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1); +} + +static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata) +{ + XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0); +} + +static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_Backplane; + + /* Compare Advertisement and Link Partner register 1 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + if (lp_reg & 0x400) + pdata->phy.lp_advertising |= ADVERTISED_Pause; + if (lp_reg & 0x800) + pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + + if (pdata->phy.pause_autoneg) { + /* Set flow control based on auto-negotiation result */ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (ad_reg & lp_reg & 0x400) { + pdata->phy.tx_pause = 1; + pdata->phy.rx_pause = 1; + } else if (ad_reg & lp_reg & 0x800) { + if (ad_reg & 0x400) + pdata->phy.rx_pause = 1; + else if (lp_reg & 0x400) + pdata->phy.tx_pause = 1; + } + } + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + if (lp_reg & 0x20) { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + pdata->phy.lp_advertising |= ADVERTISED_2500baseX_Full; + else + pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + } + + ad_reg &= lp_reg; + if (ad_reg & 0x80) { + mode = XGBE_MODE_KR; + } else if (ad_reg & 0x20) { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + mode = XGBE_MODE_KX_2500; + else + mode = XGBE_MODE_KX_1000; + } else { + mode = XGBE_MODE_UNKNOWN; + } + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + + return mode; +} + +static void xgbe_phy_pcs_power_cycle(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + + reg |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + usleep_range(75, 100); + + reg &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); +} + +static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata) +{ + /* Assert Rx and Tx ratechange */ + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1); +} + +static void xgbe_phy_complete_ratechange(struct xgbe_prv_data *pdata) +{ + unsigned int wait; + u16 status; + + /* Release Rx and Tx ratechange */ + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0); + + /* Wait for Rx and Tx ready */ + wait = XGBE_RATECHANGE_COUNT; + while (wait--) { + usleep_range(50, 75); + + status = XSIR0_IOREAD(pdata, SIR0_STATUS); + if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) && + XSIR_GET_BITS(status, SIR0_STATUS, TX_READY)) + goto rx_reset; + } + + netif_dbg(pdata, link, pdata->netdev, "SerDes rx/tx not ready (%#hx)\n", + status); + +rx_reset: + /* Perform Rx reset for the DFE changes */ + XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 0); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 1); +} + +static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + /* Set PCS to KR/10G speed */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= ~MDIO_PCS_CTRL2_TYPE; + reg |= MDIO_PCS_CTRL2_10GBR; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_CTRL1_SPEEDSEL; + reg |= MDIO_CTRL1_SPEED10G; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + xgbe_phy_pcs_power_cycle(pdata); + + /* Set SerDes to 10G speed */ + xgbe_phy_start_ratechange(pdata); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_10000_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_10000_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_10000_PLL); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, + phy_data->cdr_rate[XGBE_SPEED_10000]); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, + phy_data->tx_amp[XGBE_SPEED_10000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, + phy_data->blwc[XGBE_SPEED_10000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, + phy_data->pq_skew[XGBE_SPEED_10000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, + phy_data->dfe_tap_cfg[XGBE_SPEED_10000]); + XRXTX_IOWRITE(pdata, RXTX_REG22, + phy_data->dfe_tap_ena[XGBE_SPEED_10000]); + + xgbe_phy_complete_ratechange(pdata); + + netif_dbg(pdata, link, pdata->netdev, "10GbE KR mode set\n"); +} + +static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + /* Set PCS to KX/1G speed */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= ~MDIO_PCS_CTRL2_TYPE; + reg |= MDIO_PCS_CTRL2_10GBX; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_CTRL1_SPEEDSEL; + reg |= MDIO_CTRL1_SPEED1G; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + xgbe_phy_pcs_power_cycle(pdata); + + /* Set SerDes to 2.5G speed */ + xgbe_phy_start_ratechange(pdata); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_2500_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_2500_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_2500_PLL); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, + phy_data->cdr_rate[XGBE_SPEED_2500]); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, + phy_data->tx_amp[XGBE_SPEED_2500]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, + phy_data->blwc[XGBE_SPEED_2500]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, + phy_data->pq_skew[XGBE_SPEED_2500]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, + phy_data->dfe_tap_cfg[XGBE_SPEED_2500]); + XRXTX_IOWRITE(pdata, RXTX_REG22, + phy_data->dfe_tap_ena[XGBE_SPEED_2500]); + + xgbe_phy_complete_ratechange(pdata); + + netif_dbg(pdata, link, pdata->netdev, "2.5GbE KX mode set\n"); +} + +static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + /* Set PCS to KX/1G speed */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= ~MDIO_PCS_CTRL2_TYPE; + reg |= MDIO_PCS_CTRL2_10GBX; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_CTRL1_SPEEDSEL; + reg |= MDIO_CTRL1_SPEED1G; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + xgbe_phy_pcs_power_cycle(pdata); + + /* Set SerDes to 1G speed */ + xgbe_phy_start_ratechange(pdata); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_1000_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_1000_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_1000_PLL); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, + phy_data->cdr_rate[XGBE_SPEED_1000]); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, + phy_data->tx_amp[XGBE_SPEED_1000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, + phy_data->blwc[XGBE_SPEED_1000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, + phy_data->pq_skew[XGBE_SPEED_1000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, + phy_data->dfe_tap_cfg[XGBE_SPEED_1000]); + XRXTX_IOWRITE(pdata, RXTX_REG22, + phy_data->dfe_tap_ena[XGBE_SPEED_1000]); + + xgbe_phy_complete_ratechange(pdata); + + netif_dbg(pdata, link, pdata->netdev, "1GbE KX mode set\n"); +} + +static enum xgbe_mode xgbe_phy_cur_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= MDIO_PCS_CTRL2_TYPE; + + if (reg == MDIO_PCS_CTRL2_10GBR) { + mode = XGBE_MODE_KR; + } else { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + mode = XGBE_MODE_KX_2500; + else + mode = XGBE_MODE_KX_1000; + } + + return mode; +} + +static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + + /* If we are in KR switch to KX, and vice-versa */ + if (xgbe_phy_cur_mode(pdata) == XGBE_MODE_KR) { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + mode = XGBE_MODE_KX_2500; + else + mode = XGBE_MODE_KX_1000; + } else { + mode = XGBE_MODE_KR; + } + + return mode; +} + +static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata, + int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (speed) { + case SPEED_1000: + return (phy_data->speed_set == XGBE_SPEEDSET_1000_10000) + ? XGBE_MODE_KX_1000 : XGBE_MODE_UNKNOWN; + case SPEED_2500: + return (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + ? XGBE_MODE_KX_2500 : XGBE_MODE_UNKNOWN; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_1000: + xgbe_phy_kx_1000_mode(pdata); + break; + case XGBE_MODE_KX_2500: + xgbe_phy_kx_2500_mode(pdata); + break; + case XGBE_MODE_KR: + xgbe_phy_kr_mode(pdata); + break; + default: + break; + } +} + +static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode, u32 advert) +{ + if (pdata->phy.autoneg == AUTONEG_ENABLE) { + if (pdata->phy.advertising & advert) + return true; + } else { + enum xgbe_mode cur_mode; + + cur_mode = xgbe_phy_get_mode(pdata, pdata->phy.speed); + if (cur_mode == mode) + return true; + } + + return false; +} + +static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_1000: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseKX_Full); + case XGBE_MODE_KX_2500: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_2500baseX_Full); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseKR_Full); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (speed) { + case SPEED_1000: + if (phy_data->speed_set != XGBE_SPEEDSET_1000_10000) + return false; + return true; + case SPEED_2500: + if (phy_data->speed_set != XGBE_SPEEDSET_2500_10000) + return false; + return true; + case SPEED_10000: + return true; + default: + return false; + } +} + +static int xgbe_phy_link_status(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + /* Link status is latched low, so read once to clear + * and then read again to get current state + */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + + return (reg & MDIO_STAT1_LSTATUS) ? 1 : 0; +} + +static void xgbe_phy_stop(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for stop */ +} + +static int xgbe_phy_start(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for start */ + return 0; +} + +static int xgbe_phy_reset(struct xgbe_prv_data *pdata) +{ + unsigned int reg, count; + + /* Perform a software reset of the PCS */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg |= MDIO_CTRL1_RESET; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + count = 50; + do { + msleep(20); + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + } while ((reg & MDIO_CTRL1_RESET) && --count); + + if (reg & MDIO_CTRL1_RESET) + return -ETIMEDOUT; + + return 0; +} + +static void xgbe_phy_exit(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for exit */ +} + +static int xgbe_phy_init(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data; + int ret; + + phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + + /* Retrieve the PHY speedset */ + ret = device_property_read_u32(pdata->phy_dev, XGBE_SPEEDSET_PROPERTY, + &phy_data->speed_set); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_SPEEDSET_PROPERTY); + return ret; + } + + switch (phy_data->speed_set) { + case XGBE_SPEEDSET_1000_10000: + case XGBE_SPEEDSET_2500_10000: + break; + default: + dev_err(pdata->dev, "invalid %s property\n", + XGBE_SPEEDSET_PROPERTY); + return -EINVAL; + } + + /* Retrieve the PHY configuration properties */ + if (device_property_present(pdata->phy_dev, XGBE_BLWC_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_BLWC_PROPERTY, + phy_data->blwc, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_BLWC_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->blwc, xgbe_phy_blwc, + sizeof(phy_data->blwc)); + } + + if (device_property_present(pdata->phy_dev, XGBE_CDR_RATE_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_CDR_RATE_PROPERTY, + phy_data->cdr_rate, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_CDR_RATE_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->cdr_rate, xgbe_phy_cdr_rate, + sizeof(phy_data->cdr_rate)); + } + + if (device_property_present(pdata->phy_dev, XGBE_PQ_SKEW_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_PQ_SKEW_PROPERTY, + phy_data->pq_skew, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_PQ_SKEW_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->pq_skew, xgbe_phy_pq_skew, + sizeof(phy_data->pq_skew)); + } + + if (device_property_present(pdata->phy_dev, XGBE_TX_AMP_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_TX_AMP_PROPERTY, + phy_data->tx_amp, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_TX_AMP_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->tx_amp, xgbe_phy_tx_amp, + sizeof(phy_data->tx_amp)); + } + + if (device_property_present(pdata->phy_dev, XGBE_DFE_CFG_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_DFE_CFG_PROPERTY, + phy_data->dfe_tap_cfg, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_DFE_CFG_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->dfe_tap_cfg, xgbe_phy_dfe_tap_cfg, + sizeof(phy_data->dfe_tap_cfg)); + } + + if (device_property_present(pdata->phy_dev, XGBE_DFE_ENA_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_DFE_ENA_PROPERTY, + phy_data->dfe_tap_ena, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_DFE_ENA_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->dfe_tap_ena, xgbe_phy_dfe_tap_ena, + sizeof(phy_data->dfe_tap_ena)); + } + + /* Initialize supported features */ + pdata->phy.supported = SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_Backplane; + pdata->phy.supported |= SUPPORTED_10000baseKR_Full; + switch (phy_data->speed_set) { + case XGBE_SPEEDSET_1000_10000: + pdata->phy.supported |= SUPPORTED_1000baseKX_Full; + break; + case XGBE_SPEEDSET_2500_10000: + pdata->phy.supported |= SUPPORTED_2500baseX_Full; + break; + } + + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + pdata->phy.supported |= SUPPORTED_10000baseR_FEC; + + pdata->phy_data = phy_data; + + return 0; +} + +void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if) +{ + struct xgbe_phy_impl_if *phy_impl = &phy_if->phy_impl; + + phy_impl->init = xgbe_phy_init; + phy_impl->exit = xgbe_phy_exit; + + phy_impl->reset = xgbe_phy_reset; + phy_impl->start = xgbe_phy_start; + phy_impl->stop = xgbe_phy_stop; + + phy_impl->link_status = xgbe_phy_link_status; + + phy_impl->valid_speed = xgbe_phy_valid_speed; + + phy_impl->use_mode = xgbe_phy_use_mode; + phy_impl->set_mode = xgbe_phy_set_mode; + phy_impl->get_mode = xgbe_phy_get_mode; + phy_impl->switch_mode = xgbe_phy_switch_mode; + phy_impl->cur_mode = xgbe_phy_cur_mode; + + phy_impl->an_outcome = xgbe_phy_an_outcome; + + phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; + phy_impl->kr_training_post = xgbe_phy_kr_training_post; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index a4e1b8d..0d33aef 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -129,7 +129,7 @@ #include #define XGBE_DRV_NAME "amd-xgbe" -#define XGBE_DRV_VERSION "1.0.2" +#define XGBE_DRV_VERSION "1.0.3" #define XGBE_DRV_DESC "AMD 10 Gigabit Ethernet Driver" /* Descriptor related defines */ @@ -191,12 +191,6 @@ #define XGBE_PHY_MODE_PROPERTY "phy-mode" #define XGBE_DMA_IRQS_PROPERTY "amd,per-channel-interrupt" #define XGBE_SPEEDSET_PROPERTY "amd,speed-set" -#define XGBE_BLWC_PROPERTY "amd,serdes-blwc" -#define XGBE_CDR_RATE_PROPERTY "amd,serdes-cdr-rate" -#define XGBE_PQ_SKEW_PROPERTY "amd,serdes-pq-skew" -#define XGBE_TX_AMP_PROPERTY "amd,serdes-tx-amp" -#define XGBE_DFE_CFG_PROPERTY "amd,serdes-dfe-tap-config" -#define XGBE_DFE_ENA_PROPERTY "amd,serdes-dfe-tap-enable" /* Device-tree clock names */ #define XGBE_DMA_CLOCK "dma_clk" @@ -274,40 +268,6 @@ #define XGBE_AN_PG_RCV 0x04 #define XGBE_AN_INT_MASK 0x07 -/* Rate-change complete wait/retry count */ -#define XGBE_RATECHANGE_COUNT 500 - -/* Default SerDes settings */ -#define XGBE_SPEED_10000_BLWC 0 -#define XGBE_SPEED_10000_CDR 0x7 -#define XGBE_SPEED_10000_PLL 0x1 -#define XGBE_SPEED_10000_PQ 0x12 -#define XGBE_SPEED_10000_RATE 0x0 -#define XGBE_SPEED_10000_TXAMP 0xa -#define XGBE_SPEED_10000_WORD 0x7 -#define XGBE_SPEED_10000_DFE_TAP_CONFIG 0x1 -#define XGBE_SPEED_10000_DFE_TAP_ENABLE 0x7f - -#define XGBE_SPEED_2500_BLWC 1 -#define XGBE_SPEED_2500_CDR 0x2 -#define XGBE_SPEED_2500_PLL 0x0 -#define XGBE_SPEED_2500_PQ 0xa -#define XGBE_SPEED_2500_RATE 0x1 -#define XGBE_SPEED_2500_TXAMP 0xf -#define XGBE_SPEED_2500_WORD 0x1 -#define XGBE_SPEED_2500_DFE_TAP_CONFIG 0x3 -#define XGBE_SPEED_2500_DFE_TAP_ENABLE 0x0 - -#define XGBE_SPEED_1000_BLWC 1 -#define XGBE_SPEED_1000_CDR 0x2 -#define XGBE_SPEED_1000_PLL 0x0 -#define XGBE_SPEED_1000_PQ 0xa -#define XGBE_SPEED_1000_RATE 0x3 -#define XGBE_SPEED_1000_TXAMP 0xf -#define XGBE_SPEED_1000_WORD 0x1 -#define XGBE_SPEED_1000_DFE_TAP_CONFIG 0x3 -#define XGBE_SPEED_1000_DFE_TAP_ENABLE 0x0 - struct xgbe_prv_data; struct xgbe_packet_data { @@ -527,8 +487,10 @@ enum xgbe_rx { }; enum xgbe_mode { - XGBE_MODE_KR = 0, - XGBE_MODE_KX, + XGBE_MODE_KX_1000 = 0, + XGBE_MODE_KX_2500, + XGBE_MODE_KR, + XGBE_MODE_UNKNOWN, }; enum xgbe_speedset { @@ -624,9 +586,7 @@ struct xgbe_hw_if { int (*read_mmd_regs)(struct xgbe_prv_data *, int, int); void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int); - int (*set_gmii_speed)(struct xgbe_prv_data *); - int (*set_gmii_2500_speed)(struct xgbe_prv_data *); - int (*set_xgmii_speed)(struct xgbe_prv_data *); + int (*set_speed)(struct xgbe_prv_data *, int); void (*enable_tx)(struct xgbe_prv_data *); void (*disable_tx)(struct xgbe_prv_data *); @@ -707,9 +667,50 @@ struct xgbe_hw_if { int (*set_rss_lookup_table)(struct xgbe_prv_data *, const u32 *); }; +/* This structure represents implementation specific routines for an + * implementation of a PHY. All routines are required unless noted below. + * Optional routines: + * kr_training_pre, kr_training_post + */ +struct xgbe_phy_impl_if { + /* Perform Setup/teardown actions */ + int (*init)(struct xgbe_prv_data *); + void (*exit)(struct xgbe_prv_data *); + + /* Perform start/stop specific actions */ + int (*reset)(struct xgbe_prv_data *); + int (*start)(struct xgbe_prv_data *); + void (*stop)(struct xgbe_prv_data *); + + /* Return the link status */ + int (*link_status)(struct xgbe_prv_data *); + + /* Indicate if a particular speed is valid */ + bool (*valid_speed)(struct xgbe_prv_data *, int); + + /* Check if the specified mode can/should be used */ + bool (*use_mode)(struct xgbe_prv_data *, enum xgbe_mode); + /* Switch the PHY into various modes */ + void (*set_mode)(struct xgbe_prv_data *, enum xgbe_mode); + /* Retrieve mode needed for a specific speed */ + enum xgbe_mode (*get_mode)(struct xgbe_prv_data *, int); + /* Retrieve new/next mode when trying to auto-negotiate */ + enum xgbe_mode (*switch_mode)(struct xgbe_prv_data *); + /* Retrieve current mode */ + enum xgbe_mode (*cur_mode)(struct xgbe_prv_data *); + + /* Process results of auto-negotiation */ + enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); + + /* Pre/Post KR training enablement support */ + void (*kr_training_pre)(struct xgbe_prv_data *); + void (*kr_training_post)(struct xgbe_prv_data *); +}; + struct xgbe_phy_if { - /* For initial PHY setup */ - void (*phy_init)(struct xgbe_prv_data *); + /* For PHY setup/teardown */ + int (*phy_init)(struct xgbe_prv_data *); + void (*phy_exit)(struct xgbe_prv_data *); /* For PHY support when setting device up/down */ int (*phy_reset)(struct xgbe_prv_data *); @@ -719,6 +720,12 @@ struct xgbe_phy_if { /* For PHY support while device is up */ void (*phy_status)(struct xgbe_prv_data *); int (*phy_config_aneg)(struct xgbe_prv_data *); + + /* For PHY settings validation */ + bool (*phy_valid_speed)(struct xgbe_prv_data *, int); + + /* PHY implementation specific services */ + struct xgbe_phy_impl_if phy_impl; }; struct xgbe_desc_if { @@ -778,11 +785,20 @@ struct xgbe_hw_features { unsigned int aux_snap_num; /* Number of Aux snapshot inputs */ }; +struct xgbe_version_data { + void (*init_function_ptrs_phy_impl)(struct xgbe_phy_if *); +}; + struct xgbe_prv_data { struct net_device *netdev; struct platform_device *pdev; struct acpi_device *adev; struct device *dev; + struct platform_device *phy_pdev; + struct device *phy_dev; + + /* Version related data */ + struct xgbe_version_data *vdata; /* ACPI or DT flag */ unsigned int use_acpi; @@ -928,6 +944,8 @@ struct xgbe_prv_data { int phy_speed; /* MDIO/PHY related settings */ + unsigned int phy_started; + void *phy_data; struct xgbe_phy phy; int mdio_mmd; unsigned long link_check; @@ -938,21 +956,6 @@ struct xgbe_prv_data { int an_irq; struct work_struct an_irq_work; - unsigned int speed_set; - - /* SerDes UEFI configurable settings. - * Switching between modes/speeds requires new values for some - * SerDes settings. The values can be supplied as device - * properties in array format. The first array entry is for - * 1GbE, second for 2.5GbE and third for 10GbE - */ - u32 serdes_blwc[XGBE_SPEEDS]; - u32 serdes_cdr_rate[XGBE_SPEEDS]; - u32 serdes_pq_skew[XGBE_SPEEDS]; - u32 serdes_tx_amp[XGBE_SPEEDS]; - u32 serdes_dfe_tap_cfg[XGBE_SPEEDS]; - u32 serdes_dfe_tap_ena[XGBE_SPEEDS]; - /* Auto-negotiation state machine support */ unsigned int an_int; struct mutex an_mutex; @@ -982,6 +985,7 @@ struct xgbe_prv_data { void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *); void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *); +void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *); void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *); const struct net_device_ops *xgbe_get_netdev_ops(void); const struct ethtool_ops *xgbe_get_ethtool_ops(void); -- cgit v1.1 From a64def4161d4b8fffea43807f582ffc3e4e5d128 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:18:38 -0500 Subject: amd-xgbe: Prepare for introduction of clause 37 autoneg Prepare for the future introduction of clause 37 auto-negotiation by updating the current auto-negotiation related functions to identify them as clause 73 functions. Move interrupt enablement to the enable/disable auto-negotiation functions. Update what will be common routines to check for the current type of AN and process accordingly. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 5 + drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 235 ++++++++++++++++++---------- drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c | 7 + drivers/net/ethernet/amd/xgbe/xgbe.h | 14 +- 4 files changed, 176 insertions(+), 85 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index bbef959..695e982 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -1052,6 +1052,11 @@ #endif /* MDIO mask values */ +#define XGBE_AN_CL73_INT_CMPLT BIT(0) +#define XGBE_AN_CL73_INC_LINK BIT(1) +#define XGBE_AN_CL73_PG_RCV BIT(2) +#define XGBE_AN_CL73_INT_MASK 0x07 + #define XGBE_XNP_MCF_NULL_MESSAGE 0x001 #define XGBE_XNP_ACK_PROCESSED BIT(12) #define XGBE_XNP_MP_FORMATTED BIT(13) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 35c302f..d5bfbe4 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -125,7 +125,33 @@ #include "xgbe.h" #include "xgbe-common.h" -static void xgbe_an_enable_kr_training(struct xgbe_prv_data *pdata) +static void xgbe_an73_clear_interrupts(struct xgbe_prv_data *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); +} + +static void xgbe_an73_disable_interrupts(struct xgbe_prv_data *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); +} + +static void xgbe_an73_enable_interrupts(struct xgbe_prv_data *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, XGBE_AN_CL73_INT_MASK); +} + +static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata) +{ + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + xgbe_an73_enable_interrupts(pdata); + break; + default: + break; + } +} + +static void xgbe_an73_enable_kr_training(struct xgbe_prv_data *pdata) { unsigned int reg; @@ -135,7 +161,7 @@ static void xgbe_an_enable_kr_training(struct xgbe_prv_data *pdata) XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); } -static void xgbe_an_disable_kr_training(struct xgbe_prv_data *pdata) +static void xgbe_an73_disable_kr_training(struct xgbe_prv_data *pdata) { unsigned int reg; @@ -148,7 +174,7 @@ static void xgbe_an_disable_kr_training(struct xgbe_prv_data *pdata) static void xgbe_kr_mode(struct xgbe_prv_data *pdata) { /* Enable KR training */ - xgbe_an_enable_kr_training(pdata); + xgbe_an73_enable_kr_training(pdata); /* Set MAC to 10G speed */ pdata->hw_if.set_speed(pdata, SPEED_10000); @@ -160,7 +186,7 @@ static void xgbe_kr_mode(struct xgbe_prv_data *pdata) static void xgbe_kx_2500_mode(struct xgbe_prv_data *pdata) { /* Disable KR training */ - xgbe_an_disable_kr_training(pdata); + xgbe_an73_disable_kr_training(pdata); /* Set MAC to 2.5G speed */ pdata->hw_if.set_speed(pdata, SPEED_2500); @@ -172,7 +198,7 @@ static void xgbe_kx_2500_mode(struct xgbe_prv_data *pdata) static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata) { /* Disable KR training */ - xgbe_an_disable_kr_training(pdata); + xgbe_an73_disable_kr_training(pdata); /* Set MAC to 1G speed */ pdata->hw_if.set_speed(pdata, SPEED_1000); @@ -232,7 +258,8 @@ static bool xgbe_use_mode(struct xgbe_prv_data *pdata, return pdata->phy_if.phy_impl.use_mode(pdata, mode); } -static void xgbe_set_an(struct xgbe_prv_data *pdata, bool enable, bool restart) +static void xgbe_an73_set(struct xgbe_prv_data *pdata, bool enable, + bool restart) { unsigned int reg; @@ -248,22 +275,46 @@ static void xgbe_set_an(struct xgbe_prv_data *pdata, bool enable, bool restart) XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, reg); } -static void xgbe_restart_an(struct xgbe_prv_data *pdata) +static void xgbe_an73_restart(struct xgbe_prv_data *pdata) { - xgbe_set_an(pdata, true, true); + xgbe_an73_enable_interrupts(pdata); + xgbe_an73_set(pdata, true, true); - netif_dbg(pdata, link, pdata->netdev, "AN enabled/restarted\n"); + netif_dbg(pdata, link, pdata->netdev, "CL73 AN enabled/restarted\n"); } -static void xgbe_disable_an(struct xgbe_prv_data *pdata) +static void xgbe_an73_disable(struct xgbe_prv_data *pdata) { - xgbe_set_an(pdata, false, false); + xgbe_an73_set(pdata, false, false); + xgbe_an73_disable_interrupts(pdata); - netif_dbg(pdata, link, pdata->netdev, "AN disabled\n"); + netif_dbg(pdata, link, pdata->netdev, "CL73 AN disabled\n"); } -static enum xgbe_an xgbe_an_tx_training(struct xgbe_prv_data *pdata, - enum xgbe_rx *state) +static void xgbe_an_restart(struct xgbe_prv_data *pdata) +{ + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + xgbe_an73_restart(pdata); + break; + default: + break; + } +} + +static void xgbe_an_disable(struct xgbe_prv_data *pdata) +{ + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + xgbe_an73_disable(pdata); + break; + default: + break; + } +} + +static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) { unsigned int ad_reg, lp_reg, reg; @@ -304,8 +355,8 @@ static enum xgbe_an xgbe_an_tx_training(struct xgbe_prv_data *pdata, return XGBE_AN_PAGE_RECEIVED; } -static enum xgbe_an xgbe_an_tx_xnp(struct xgbe_prv_data *pdata, - enum xgbe_rx *state) +static enum xgbe_an xgbe_an73_tx_xnp(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) { u16 msg; @@ -321,8 +372,8 @@ static enum xgbe_an xgbe_an_tx_xnp(struct xgbe_prv_data *pdata, return XGBE_AN_PAGE_RECEIVED; } -static enum xgbe_an xgbe_an_rx_bpa(struct xgbe_prv_data *pdata, - enum xgbe_rx *state) +static enum xgbe_an xgbe_an73_rx_bpa(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) { unsigned int link_support; unsigned int reg, ad_reg, lp_reg; @@ -341,12 +392,12 @@ static enum xgbe_an xgbe_an_rx_bpa(struct xgbe_prv_data *pdata, return ((ad_reg & XGBE_XNP_NP_EXCHANGE) || (lp_reg & XGBE_XNP_NP_EXCHANGE)) - ? xgbe_an_tx_xnp(pdata, state) - : xgbe_an_tx_training(pdata, state); + ? xgbe_an73_tx_xnp(pdata, state) + : xgbe_an73_tx_training(pdata, state); } -static enum xgbe_an xgbe_an_rx_xnp(struct xgbe_prv_data *pdata, - enum xgbe_rx *state) +static enum xgbe_an xgbe_an73_rx_xnp(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) { unsigned int ad_reg, lp_reg; @@ -356,11 +407,11 @@ static enum xgbe_an xgbe_an_rx_xnp(struct xgbe_prv_data *pdata, return ((ad_reg & XGBE_XNP_NP_EXCHANGE) || (lp_reg & XGBE_XNP_NP_EXCHANGE)) - ? xgbe_an_tx_xnp(pdata, state) - : xgbe_an_tx_training(pdata, state); + ? xgbe_an73_tx_xnp(pdata, state) + : xgbe_an73_tx_training(pdata, state); } -static enum xgbe_an xgbe_an_page_received(struct xgbe_prv_data *pdata) +static enum xgbe_an xgbe_an73_page_received(struct xgbe_prv_data *pdata) { enum xgbe_rx *state; unsigned long an_timeout; @@ -379,7 +430,7 @@ static enum xgbe_an xgbe_an_page_received(struct xgbe_prv_data *pdata) pdata->an_start = jiffies; netif_dbg(pdata, link, pdata->netdev, - "AN timed out, resetting state\n"); + "CL73 AN timed out, resetting state\n"); } } @@ -388,11 +439,11 @@ static enum xgbe_an xgbe_an_page_received(struct xgbe_prv_data *pdata) switch (*state) { case XGBE_RX_BPA: - ret = xgbe_an_rx_bpa(pdata, state); + ret = xgbe_an73_rx_bpa(pdata, state); break; case XGBE_RX_XNP: - ret = xgbe_an_rx_xnp(pdata, state); + ret = xgbe_an73_rx_xnp(pdata, state); break; default: @@ -402,7 +453,7 @@ static enum xgbe_an xgbe_an_page_received(struct xgbe_prv_data *pdata) return ret; } -static enum xgbe_an xgbe_an_incompat_link(struct xgbe_prv_data *pdata) +static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) { /* Be sure we aren't looping trying to negotiate */ if (xgbe_in_kr_mode(pdata)) { @@ -424,23 +475,19 @@ static enum xgbe_an xgbe_an_incompat_link(struct xgbe_prv_data *pdata) return XGBE_AN_NO_LINK; } - xgbe_disable_an(pdata); + xgbe_an73_disable(pdata); xgbe_switch_mode(pdata); - xgbe_restart_an(pdata); + xgbe_an73_restart(pdata); return XGBE_AN_INCOMPAT_LINK; } -static irqreturn_t xgbe_an_isr(int irq, void *data) +static void xgbe_an73_isr(struct xgbe_prv_data *pdata) { - struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; - - netif_dbg(pdata, intr, pdata->netdev, "AN interrupt received\n"); - /* Disable AN interrupts */ - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); + xgbe_an73_disable_interrupts(pdata); /* Save the interrupt(s) that fired */ pdata->an_int = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT); @@ -452,8 +499,22 @@ static irqreturn_t xgbe_an_isr(int irq, void *data) queue_work(pdata->an_workqueue, &pdata->an_irq_work); } else { /* Enable AN interrupts */ - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, - XGBE_AN_INT_MASK); + xgbe_an73_enable_interrupts(pdata); + } +} + +static irqreturn_t xgbe_an_isr(int irq, void *data) +{ + struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; + + netif_dbg(pdata, intr, pdata->netdev, "AN interrupt received\n"); + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + xgbe_an73_isr(pdata); + break; + default: + break; } return IRQ_HANDLED; @@ -492,28 +553,23 @@ static const char *xgbe_state_as_string(enum xgbe_an state) } } -static void xgbe_an_state_machine(struct work_struct *work) +static void xgbe_an73_state_machine(struct xgbe_prv_data *pdata) { - struct xgbe_prv_data *pdata = container_of(work, - struct xgbe_prv_data, - an_work); enum xgbe_an cur_state = pdata->an_state; - mutex_lock(&pdata->an_mutex); - if (!pdata->an_int) - goto out; + return; next_int: - if (pdata->an_int & XGBE_AN_PG_RCV) { + if (pdata->an_int & XGBE_AN_CL73_PG_RCV) { pdata->an_state = XGBE_AN_PAGE_RECEIVED; - pdata->an_int &= ~XGBE_AN_PG_RCV; - } else if (pdata->an_int & XGBE_AN_INC_LINK) { + pdata->an_int &= ~XGBE_AN_CL73_PG_RCV; + } else if (pdata->an_int & XGBE_AN_CL73_INC_LINK) { pdata->an_state = XGBE_AN_INCOMPAT_LINK; - pdata->an_int &= ~XGBE_AN_INC_LINK; - } else if (pdata->an_int & XGBE_AN_INT_CMPLT) { + pdata->an_int &= ~XGBE_AN_CL73_INC_LINK; + } else if (pdata->an_int & XGBE_AN_CL73_INT_CMPLT) { pdata->an_state = XGBE_AN_COMPLETE; - pdata->an_int &= ~XGBE_AN_INT_CMPLT; + pdata->an_int &= ~XGBE_AN_CL73_INT_CMPLT; } else { pdata->an_state = XGBE_AN_ERROR; } @@ -521,7 +577,7 @@ next_int: pdata->an_result = pdata->an_state; again: - netif_dbg(pdata, link, pdata->netdev, "AN %s\n", + netif_dbg(pdata, link, pdata->netdev, "CL73 AN %s\n", xgbe_state_as_string(pdata->an_state)); cur_state = pdata->an_state; @@ -532,14 +588,14 @@ again: break; case XGBE_AN_PAGE_RECEIVED: - pdata->an_state = xgbe_an_page_received(pdata); + pdata->an_state = xgbe_an73_page_received(pdata); pdata->an_supported++; break; case XGBE_AN_INCOMPAT_LINK: pdata->an_supported = 0; pdata->parallel_detect = 0; - pdata->an_state = xgbe_an_incompat_link(pdata); + pdata->an_state = xgbe_an73_incompat_link(pdata); break; case XGBE_AN_COMPLETE: @@ -558,14 +614,14 @@ again: if (pdata->an_state == XGBE_AN_NO_LINK) { pdata->an_int = 0; - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + xgbe_an73_clear_interrupts(pdata); } else if (pdata->an_state == XGBE_AN_ERROR) { netdev_err(pdata->netdev, "error during auto-negotiation, state=%u\n", cur_state); pdata->an_int = 0; - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + xgbe_an73_clear_interrupts(pdata); } if (pdata->an_state >= XGBE_AN_COMPLETE) { @@ -575,7 +631,7 @@ again: pdata->kx_state = XGBE_RX_BPA; pdata->an_start = 0; - netif_dbg(pdata, link, pdata->netdev, "AN result: %s\n", + netif_dbg(pdata, link, pdata->netdev, "CL73 AN result: %s\n", xgbe_state_as_string(pdata->an_result)); } @@ -585,14 +641,29 @@ again: if (pdata->an_int) goto next_int; -out: - /* Enable AN interrupts on the way out */ - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, XGBE_AN_INT_MASK); + xgbe_an73_enable_interrupts(pdata); +} + +static void xgbe_an_state_machine(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + an_work); + + mutex_lock(&pdata->an_mutex); + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + xgbe_an73_state_machine(pdata); + break; + default: + break; + } mutex_unlock(&pdata->an_mutex); } -static void xgbe_an_init(struct xgbe_prv_data *pdata) +static void xgbe_an73_init(struct xgbe_prv_data *pdata) { unsigned int reg; @@ -637,7 +708,20 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata) XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); - netif_dbg(pdata, link, pdata->netdev, "AN initialized\n"); + netif_dbg(pdata, link, pdata->netdev, "CL73 AN initialized\n"); +} + +static void xgbe_an_init(struct xgbe_prv_data *pdata) +{ + /* Set up advertisement registers based on current settings */ + pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata); + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + xgbe_an73_init(pdata); + break; + default: + break; + } } static const char *xgbe_phy_fc_string(struct xgbe_prv_data *pdata) @@ -732,7 +816,7 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) netif_dbg(pdata, link, pdata->netdev, "fixed PHY configuration\n"); /* Disable auto-negotiation */ - xgbe_disable_an(pdata); + xgbe_an_disable(pdata); /* Set specified mode for specified speed */ mode = pdata->phy_if.phy_impl.get_mode(pdata, pdata->phy.speed); @@ -781,10 +865,10 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) } /* Disable and stop any in progress auto-negotiation */ - xgbe_disable_an(pdata); + xgbe_an_disable(pdata); /* Clear any auto-negotitation interrupts */ - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + xgbe_an73_clear_interrupts(pdata); pdata->an_result = XGBE_AN_READY; pdata->an_state = XGBE_AN_READY; @@ -794,11 +878,8 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) /* Re-enable auto-negotiation interrupt */ enable_irq(pdata->an_irq); - /* Set up advertisement registers based on current settings */ xgbe_an_init(pdata); - - /* Enable and start auto-negotiation */ - xgbe_restart_an(pdata); + xgbe_an_restart(pdata); return 0; } @@ -926,10 +1007,7 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) pdata->phy_started = 0; /* Disable auto-negotiation */ - xgbe_disable_an(pdata); - - /* Disable auto-negotiation interrupts */ - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); + xgbe_an_disable(pdata); devm_free_irq(pdata->dev, pdata->an_irq, pdata); @@ -977,11 +1055,8 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) /* Indicate the PHY is up and running */ pdata->phy_started = 1; - /* Set up advertisement registers based on current settings */ xgbe_an_init(pdata); - - /* Enable auto-negotiation interrupts */ - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); + xgbe_an_enable_interrupts(pdata); return xgbe_phy_config_aneg(pdata); @@ -1003,10 +1078,10 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata) return ret; /* Disable auto-negotiation for now */ - xgbe_disable_an(pdata); + xgbe_an_disable(pdata); /* Clear auto-negotiation interrupts */ - XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + xgbe_an73_clear_interrupts(pdata); return 0; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c index 20ccb99b..9c913a0 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -295,6 +295,11 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) return mode; } +static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) +{ + return XGBE_AN_MODE_CL73; +} + static void xgbe_phy_pcs_power_cycle(struct xgbe_prv_data *pdata) { unsigned int reg; @@ -814,6 +819,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if) phy_impl->switch_mode = xgbe_phy_switch_mode; phy_impl->cur_mode = xgbe_phy_cur_mode; + phy_impl->an_mode = xgbe_phy_an_mode; + phy_impl->an_outcome = xgbe_phy_an_outcome; phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 0d33aef..d90ecbc 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -263,11 +263,6 @@ #define XGBE_AN_MS_TIMEOUT 500 #define XGBE_LINK_TIMEOUT 10 -#define XGBE_AN_INT_CMPLT 0x01 -#define XGBE_AN_INC_LINK 0x02 -#define XGBE_AN_PG_RCV 0x04 -#define XGBE_AN_INT_MASK 0x07 - struct xgbe_prv_data; struct xgbe_packet_data { @@ -470,6 +465,11 @@ enum xgbe_speed { XGBE_SPEEDS, }; +enum xgbe_an_mode { + XGBE_AN_MODE_CL73 = 0, + XGBE_AN_MODE_NONE, +}; + enum xgbe_an { XGBE_AN_READY = 0, XGBE_AN_PAGE_RECEIVED, @@ -699,6 +699,9 @@ struct xgbe_phy_impl_if { /* Retrieve current mode */ enum xgbe_mode (*cur_mode)(struct xgbe_prv_data *); + /* Retrieve current auto-negotiation mode */ + enum xgbe_an_mode (*an_mode)(struct xgbe_prv_data *); + /* Process results of auto-negotiation */ enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); @@ -968,6 +971,7 @@ struct xgbe_prv_data { unsigned int parallel_detect; unsigned int fec_ability; unsigned long an_start; + enum xgbe_an_mode an_mode; unsigned int lpm_ctrl; /* CTRL1 for resume */ -- cgit v1.1 From 1bf40ada62901e14395902d0934a4e51e04afe46 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:18:47 -0500 Subject: amd-xgbe: Add support for clause 37 auto-negotiation Add support to be able to use clause 37 auto-negotiation. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 41 +++++ drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 242 +++++++++++++++++++++++++++- drivers/net/ethernet/amd/xgbe/xgbe.h | 11 +- 3 files changed, 286 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 695e982..8bcf4ef 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -1027,6 +1027,10 @@ #define MDIO_PMA_10GBR_FECCTRL 0x00ab #endif +#ifndef MDIO_PCS_DIG_CTRL +#define MDIO_PCS_DIG_CTRL 0x8000 +#endif + #ifndef MDIO_AN_XNP #define MDIO_AN_XNP 0x0016 #endif @@ -1047,10 +1051,34 @@ #define MDIO_AN_INT 0x8002 #endif +#ifndef MDIO_VEND2_AN_ADVERTISE +#define MDIO_VEND2_AN_ADVERTISE 0x0004 +#endif + +#ifndef MDIO_VEND2_AN_LP_ABILITY +#define MDIO_VEND2_AN_LP_ABILITY 0x0005 +#endif + +#ifndef MDIO_VEND2_AN_CTRL +#define MDIO_VEND2_AN_CTRL 0x8001 +#endif + +#ifndef MDIO_VEND2_AN_STAT +#define MDIO_VEND2_AN_STAT 0x8002 +#endif + #ifndef MDIO_CTRL1_SPEED1G #define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100) #endif +#ifndef MDIO_VEND2_CTRL1_AN_ENABLE +#define MDIO_VEND2_CTRL1_AN_ENABLE BIT(12) +#endif + +#ifndef MDIO_VEND2_CTRL1_AN_RESTART +#define MDIO_VEND2_CTRL1_AN_RESTART BIT(9) +#endif + /* MDIO mask values */ #define XGBE_AN_CL73_INT_CMPLT BIT(0) #define XGBE_AN_CL73_INC_LINK BIT(1) @@ -1065,6 +1093,19 @@ #define XGBE_KR_TRAINING_START BIT(0) #define XGBE_KR_TRAINING_ENABLE BIT(1) +#define XGBE_PCS_CL37_BP BIT(12) + +#define XGBE_AN_CL37_INT_CMPLT BIT(0) +#define XGBE_AN_CL37_INT_MASK 0x01 + +#define XGBE_AN_CL37_HD_MASK 0x40 +#define XGBE_AN_CL37_FD_MASK 0x20 + +#define XGBE_AN_CL37_PCS_MODE_MASK 0x06 +#define XGBE_AN_CL37_PCS_MODE_BASEX 0x00 +#define XGBE_AN_CL37_PCS_MODE_SGMII 0x04 +#define XGBE_AN_CL37_TX_CONFIG_MASK 0x08 + /* Bit setting and getting macros * The get macro will extract the current bit field value from within * the variable diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index d5bfbe4..723eb90 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -125,6 +125,41 @@ #include "xgbe.h" #include "xgbe-common.h" +static void xgbe_an37_clear_interrupts(struct xgbe_prv_data *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT); + reg &= ~XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg); +} + +static void xgbe_an37_disable_interrupts(struct xgbe_prv_data *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); + reg &= ~XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL); + reg &= ~XGBE_PCS_CL37_BP; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg); +} + +static void xgbe_an37_enable_interrupts(struct xgbe_prv_data *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL); + reg |= XGBE_PCS_CL37_BP; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); + reg |= XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); +} + static void xgbe_an73_clear_interrupts(struct xgbe_prv_data *pdata) { XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); @@ -146,11 +181,21 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata) case XGBE_AN_MODE_CL73: xgbe_an73_enable_interrupts(pdata); break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_enable_interrupts(pdata); + break; default: break; } } +static void xgbe_an_clear_interrupts_all(struct xgbe_prv_data *pdata) +{ + xgbe_an73_clear_interrupts(pdata); + xgbe_an37_clear_interrupts(pdata); +} + static void xgbe_an73_enable_kr_training(struct xgbe_prv_data *pdata) { unsigned int reg; @@ -258,6 +303,39 @@ static bool xgbe_use_mode(struct xgbe_prv_data *pdata, return pdata->phy_if.phy_impl.use_mode(pdata, mode); } +static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable, + bool restart) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_CTRL1); + reg &= ~MDIO_VEND2_CTRL1_AN_ENABLE; + + if (enable) + reg |= MDIO_VEND2_CTRL1_AN_ENABLE; + + if (restart) + reg |= MDIO_VEND2_CTRL1_AN_RESTART; + + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg); +} + +static void xgbe_an37_restart(struct xgbe_prv_data *pdata) +{ + xgbe_an37_enable_interrupts(pdata); + xgbe_an37_set(pdata, true, true); + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN enabled/restarted\n"); +} + +static void xgbe_an37_disable(struct xgbe_prv_data *pdata) +{ + xgbe_an37_set(pdata, false, false); + xgbe_an37_disable_interrupts(pdata); + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN disabled\n"); +} + static void xgbe_an73_set(struct xgbe_prv_data *pdata, bool enable, bool restart) { @@ -297,6 +375,10 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata) case XGBE_AN_MODE_CL73: xgbe_an73_restart(pdata); break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_restart(pdata); + break; default: break; } @@ -308,11 +390,21 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata) case XGBE_AN_MODE_CL73: xgbe_an73_disable(pdata); break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_disable(pdata); + break; default: break; } } +static void xgbe_an_disable_all(struct xgbe_prv_data *pdata) +{ + xgbe_an73_disable(pdata); + xgbe_an37_disable(pdata); +} + static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata, enum xgbe_rx *state) { @@ -484,6 +576,30 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) return XGBE_AN_INCOMPAT_LINK; } +static void xgbe_an37_isr(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + /* Disable AN interrupts */ + xgbe_an37_disable_interrupts(pdata); + + /* Save the interrupt(s) that fired */ + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT); + pdata->an_int = reg & XGBE_AN_CL37_INT_MASK; + pdata->an_status = reg & ~XGBE_AN_CL37_INT_MASK; + + if (pdata->an_int) { + /* Clear the interrupt(s) that fired and process them */ + reg &= ~XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg); + + queue_work(pdata->an_workqueue, &pdata->an_irq_work); + } else { + /* Enable AN interrupts */ + xgbe_an37_enable_interrupts(pdata); + } +} + static void xgbe_an73_isr(struct xgbe_prv_data *pdata) { /* Disable AN interrupts */ @@ -513,6 +629,10 @@ static irqreturn_t xgbe_an_isr(int irq, void *data) case XGBE_AN_MODE_CL73: xgbe_an73_isr(pdata); break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_isr(pdata); + break; default: break; } @@ -553,6 +673,64 @@ static const char *xgbe_state_as_string(enum xgbe_an state) } } +static void xgbe_an37_state_machine(struct xgbe_prv_data *pdata) +{ + enum xgbe_an cur_state = pdata->an_state; + + if (!pdata->an_int) + return; + + if (pdata->an_int & XGBE_AN_CL37_INT_CMPLT) { + pdata->an_state = XGBE_AN_COMPLETE; + pdata->an_int &= ~XGBE_AN_CL37_INT_CMPLT; + + /* If SGMII is enabled, check the link status */ + if ((pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) && + !(pdata->an_status & XGBE_SGMII_AN_LINK_STATUS)) + pdata->an_state = XGBE_AN_NO_LINK; + } + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN %s\n", + xgbe_state_as_string(pdata->an_state)); + + cur_state = pdata->an_state; + + switch (pdata->an_state) { + case XGBE_AN_READY: + break; + + case XGBE_AN_COMPLETE: + netif_dbg(pdata, link, pdata->netdev, + "Auto negotiation successful\n"); + break; + + case XGBE_AN_NO_LINK: + break; + + default: + pdata->an_state = XGBE_AN_ERROR; + } + + if (pdata->an_state == XGBE_AN_ERROR) { + netdev_err(pdata->netdev, + "error during auto-negotiation, state=%u\n", + cur_state); + + pdata->an_int = 0; + xgbe_an37_clear_interrupts(pdata); + } + + if (pdata->an_state >= XGBE_AN_COMPLETE) { + pdata->an_result = pdata->an_state; + pdata->an_state = XGBE_AN_READY; + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN result: %s\n", + xgbe_state_as_string(pdata->an_result)); + } + + xgbe_an37_enable_interrupts(pdata); +} + static void xgbe_an73_state_machine(struct xgbe_prv_data *pdata) { enum xgbe_an cur_state = pdata->an_state; @@ -574,8 +752,6 @@ next_int: pdata->an_state = XGBE_AN_ERROR; } - pdata->an_result = pdata->an_state; - again: netif_dbg(pdata, link, pdata->netdev, "CL73 AN %s\n", xgbe_state_as_string(pdata->an_state)); @@ -656,6 +832,10 @@ static void xgbe_an_state_machine(struct work_struct *work) case XGBE_AN_MODE_CL73: xgbe_an73_state_machine(pdata); break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_state_machine(pdata); + break; default: break; } @@ -663,6 +843,50 @@ static void xgbe_an_state_machine(struct work_struct *work) mutex_unlock(&pdata->an_mutex); } +static void xgbe_an37_init(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + /* Set up Advertisement register */ + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); + if (pdata->phy.advertising & ADVERTISED_Pause) + reg |= 0x100; + else + reg &= ~0x100; + + if (pdata->phy.advertising & ADVERTISED_Asym_Pause) + reg |= 0x80; + else + reg &= ~0x80; + + /* Full duplex, but not half */ + reg |= XGBE_AN_CL37_FD_MASK; + reg &= ~XGBE_AN_CL37_HD_MASK; + + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE, reg); + + /* Set up the Control register */ + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); + reg &= XGBE_AN_CL37_TX_CONFIG_MASK; + reg &= XGBE_AN_CL37_PCS_MODE_MASK; + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL37: + reg |= XGBE_AN_CL37_PCS_MODE_BASEX; + break; + case XGBE_AN_MODE_CL37_SGMII: + reg |= XGBE_AN_CL37_PCS_MODE_SGMII; + break; + default: + break; + } + + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN (%s) initialized\n", + (pdata->an_mode == XGBE_AN_MODE_CL37) ? "BaseX" : "SGMII"); +} + static void xgbe_an73_init(struct xgbe_prv_data *pdata) { unsigned int reg; @@ -719,6 +943,10 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata) case XGBE_AN_MODE_CL73: xgbe_an73_init(pdata); break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_init(pdata); + break; default: break; } @@ -865,10 +1093,10 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) } /* Disable and stop any in progress auto-negotiation */ - xgbe_an_disable(pdata); + xgbe_an_disable_all(pdata); /* Clear any auto-negotitation interrupts */ - xgbe_an73_clear_interrupts(pdata); + xgbe_an_clear_interrupts_all(pdata); pdata->an_result = XGBE_AN_READY; pdata->an_state = XGBE_AN_READY; @@ -1007,7 +1235,7 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) pdata->phy_started = 0; /* Disable auto-negotiation */ - xgbe_an_disable(pdata); + xgbe_an_disable_all(pdata); devm_free_irq(pdata->dev, pdata->an_irq, pdata); @@ -1078,10 +1306,10 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata) return ret; /* Disable auto-negotiation for now */ - xgbe_an_disable(pdata); + xgbe_an_disable_all(pdata); /* Clear auto-negotiation interrupts */ - xgbe_an73_clear_interrupts(pdata); + xgbe_an_clear_interrupts_all(pdata); return 0; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index d90ecbc..8cb4f20 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -261,7 +261,13 @@ /* Auto-negotiation */ #define XGBE_AN_MS_TIMEOUT 500 -#define XGBE_LINK_TIMEOUT 10 +#define XGBE_LINK_TIMEOUT 5 + +#define XGBE_SGMII_AN_LINK_STATUS BIT(1) +#define XGBE_SGMII_AN_LINK_SPEED (BIT(2) | BIT(3)) +#define XGBE_SGMII_AN_LINK_SPEED_100 0x04 +#define XGBE_SGMII_AN_LINK_SPEED_1000 0x08 +#define XGBE_SGMII_AN_LINK_DUPLEX BIT(4) struct xgbe_prv_data; @@ -467,6 +473,8 @@ enum xgbe_speed { enum xgbe_an_mode { XGBE_AN_MODE_CL73 = 0, + XGBE_AN_MODE_CL37, + XGBE_AN_MODE_CL37_SGMII, XGBE_AN_MODE_NONE, }; @@ -961,6 +969,7 @@ struct xgbe_prv_data { /* Auto-negotiation state machine support */ unsigned int an_int; + unsigned int an_status; struct mutex an_mutex; enum xgbe_an an_result; enum xgbe_an an_state; -- cgit v1.1 From b03a4a6fb309a000a0fba5f2af06ffc5767b0e45 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:18:56 -0500 Subject: amd-xgbe: Prepare for a new PCS register access method Prepare the code to be able to support accessing of the PCS registers in a new way, while maintaining the current access method. Provide a version specific field that indicates the method to use. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 21 +++--- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 107 +++++++++++++++++++++++++--- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 1 + drivers/net/ethernet/amd/xgbe/xgbe.h | 9 +++ 4 files changed, 119 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 8bcf4ef..6c40915 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -852,14 +852,9 @@ #define MTL_TSA_SP 0x00 #define MTL_TSA_ETS 0x02 -/* PCS MMD select register offset - * The MMD select register is used for accessing PCS registers - * when the underlying APB3 interface is using indirect addressing. - * Indirect addressing requires accessing registers in two phases, - * an address phase and a data phase. The address phases requires - * writing an address selection value to the MMD select regiesters. - */ -#define PCS_MMD_SELECT 0xff +/* PCS register offsets */ +#define PCS_V1_WINDOW_SELECT 0x03fc +#define PCS_V2_WINDOW_SELECT 0x9064 /* SerDes integration register offsets */ #define SIR0_KR_RT_1 0x002c @@ -1241,12 +1236,18 @@ do { \ /* Macros for building, reading or writing register values or bits * within the register values of XPCS registers. */ -#define XPCS_IOWRITE(_pdata, _off, _val) \ +#define XPCS32_IOWRITE(_pdata, _off, _val) \ iowrite32(_val, (_pdata)->xpcs_regs + (_off)) -#define XPCS_IOREAD(_pdata, _off) \ +#define XPCS32_IOREAD(_pdata, _off) \ ioread32((_pdata)->xpcs_regs + (_off)) +#define XPCS16_IOWRITE(_pdata, _off, _val) \ + iowrite16(_val, (_pdata)->xpcs_regs + (_off)) + +#define XPCS16_IOREAD(_pdata, _off) \ + ioread16((_pdata)->xpcs_regs + (_off)) + /* Macros for building, reading or writing register values or bits * within the register values of SerDes integration registers. */ diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 75c3df1..b8a04e7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1026,8 +1026,71 @@ static int xgbe_config_rx_mode(struct xgbe_prv_data *pdata) return 0; } -static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad, - int mmd_reg) +static int xgbe_read_mmd_regs_v2(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg) +{ + unsigned long flags; + unsigned int mmd_address, index, offset; + int mmd_data; + + if (mmd_reg & MII_ADDR_C45) + mmd_address = mmd_reg & ~MII_ADDR_C45; + else + mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + + /* The PCS registers are accessed using mmio. The underlying + * management interface uses indirect addressing to access the MMD + * register sets. This requires accessing of the PCS register in two + * phases, an address phase and a data phase. + * + * The mmio interface is based on 16-bit offsets and values. All + * register offsets must therefore be adjusted by left shifting the + * offset 1 bit and reading 16 bits of data. + */ + mmd_address <<= 1; + index = mmd_address & ~pdata->xpcs_window_mask; + offset = pdata->xpcs_window + (mmd_address & pdata->xpcs_window_mask); + + spin_lock_irqsave(&pdata->xpcs_lock, flags); + XPCS32_IOWRITE(pdata, PCS_V2_WINDOW_SELECT, index); + mmd_data = XPCS16_IOREAD(pdata, offset); + spin_unlock_irqrestore(&pdata->xpcs_lock, flags); + + return mmd_data; +} + +static void xgbe_write_mmd_regs_v2(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg, int mmd_data) +{ + unsigned long flags; + unsigned int mmd_address, index, offset; + + if (mmd_reg & MII_ADDR_C45) + mmd_address = mmd_reg & ~MII_ADDR_C45; + else + mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + + /* The PCS registers are accessed using mmio. The underlying + * management interface uses indirect addressing to access the MMD + * register sets. This requires accessing of the PCS register in two + * phases, an address phase and a data phase. + * + * The mmio interface is based on 16-bit offsets and values. All + * register offsets must therefore be adjusted by left shifting the + * offset 1 bit and writing 16 bits of data. + */ + mmd_address <<= 1; + index = mmd_address & ~pdata->xpcs_window_mask; + offset = pdata->xpcs_window + (mmd_address & pdata->xpcs_window_mask); + + spin_lock_irqsave(&pdata->xpcs_lock, flags); + XPCS32_IOWRITE(pdata, PCS_V2_WINDOW_SELECT, index); + XPCS16_IOWRITE(pdata, offset, mmd_data); + spin_unlock_irqrestore(&pdata->xpcs_lock, flags); +} + +static int xgbe_read_mmd_regs_v1(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg) { unsigned long flags; unsigned int mmd_address; @@ -1048,15 +1111,15 @@ static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad, * offset 2 bits and reading 32 bits of data. */ spin_lock_irqsave(&pdata->xpcs_lock, flags); - XPCS_IOWRITE(pdata, PCS_MMD_SELECT << 2, mmd_address >> 8); - mmd_data = XPCS_IOREAD(pdata, (mmd_address & 0xff) << 2); + XPCS32_IOWRITE(pdata, PCS_V1_WINDOW_SELECT, mmd_address >> 8); + mmd_data = XPCS32_IOREAD(pdata, (mmd_address & 0xff) << 2); spin_unlock_irqrestore(&pdata->xpcs_lock, flags); return mmd_data; } -static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad, - int mmd_reg, int mmd_data) +static void xgbe_write_mmd_regs_v1(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg, int mmd_data) { unsigned int mmd_address; unsigned long flags; @@ -1073,14 +1136,40 @@ static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad, * * The mmio interface is based on 32-bit offsets and values. All * register offsets must therefore be adjusted by left shifting the - * offset 2 bits and reading 32 bits of data. + * offset 2 bits and writing 32 bits of data. */ spin_lock_irqsave(&pdata->xpcs_lock, flags); - XPCS_IOWRITE(pdata, PCS_MMD_SELECT << 2, mmd_address >> 8); - XPCS_IOWRITE(pdata, (mmd_address & 0xff) << 2, mmd_data); + XPCS32_IOWRITE(pdata, PCS_V1_WINDOW_SELECT, mmd_address >> 8); + XPCS32_IOWRITE(pdata, (mmd_address & 0xff) << 2, mmd_data); spin_unlock_irqrestore(&pdata->xpcs_lock, flags); } +static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg) +{ + switch (pdata->vdata->xpcs_access) { + case XGBE_XPCS_ACCESS_V1: + return xgbe_read_mmd_regs_v1(pdata, prtad, mmd_reg); + + case XGBE_XPCS_ACCESS_V2: + default: + return xgbe_read_mmd_regs_v2(pdata, prtad, mmd_reg); + } +} + +static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg, int mmd_data) +{ + switch (pdata->vdata->xpcs_access) { + case XGBE_XPCS_ACCESS_V1: + return xgbe_write_mmd_regs_v1(pdata, prtad, mmd_reg, mmd_data); + + case XGBE_XPCS_ACCESS_V2: + default: + return xgbe_write_mmd_regs_v2(pdata, prtad, mmd_reg, mmd_data); + } +} + static int xgbe_tx_complete(struct xgbe_ring_desc *rdesc) { return !XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 38c5985..d9864f0 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -785,6 +785,7 @@ static int xgbe_resume(struct device *dev) static const struct xgbe_version_data xgbe_v1 = { .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v1, + .xpcs_access = XGBE_XPCS_ACCESS_V1, }; #ifdef CONFIG_ACPI diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 8cb4f20..160b498 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -471,6 +471,11 @@ enum xgbe_speed { XGBE_SPEEDS, }; +enum xgbe_xpcs_access { + XGBE_XPCS_ACCESS_V1 = 0, + XGBE_XPCS_ACCESS_V2, +}; + enum xgbe_an_mode { XGBE_AN_MODE_CL73 = 0, XGBE_AN_MODE_CL37, @@ -798,6 +803,7 @@ struct xgbe_hw_features { struct xgbe_version_data { void (*init_function_ptrs_phy_impl)(struct xgbe_phy_if *); + enum xgbe_xpcs_access xpcs_access; }; struct xgbe_prv_data { @@ -826,6 +832,9 @@ struct xgbe_prv_data { /* XPCS indirect addressing lock */ spinlock_t xpcs_lock; + unsigned int xpcs_window; + unsigned int xpcs_window_size; + unsigned int xpcs_window_mask; /* RSS addressing mutex */ struct mutex rss_mutex; -- cgit v1.1 From e5a20b9072eb38fad9b8cff4e6824c6cdbee5a00 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:19:07 -0500 Subject: amd-xgbe: Support for 64-bit management counter registers Add support for reading all management counter registers as 64-bit values. The indication of whether to read the high 32-bits to form a 64-bit value is indicated in the version data. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 36 +++++++++++++++++++++++--------- drivers/net/ethernet/amd/xgbe/xgbe.h | 1 + 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index b8a04e7..fbd60ee 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -2588,17 +2588,33 @@ static u64 xgbe_mmc_read(struct xgbe_prv_data *pdata, unsigned int reg_lo) bool read_hi; u64 val; - switch (reg_lo) { - /* These registers are always 64 bit */ - case MMC_TXOCTETCOUNT_GB_LO: - case MMC_TXOCTETCOUNT_G_LO: - case MMC_RXOCTETCOUNT_GB_LO: - case MMC_RXOCTETCOUNT_G_LO: - read_hi = true; - break; + if (pdata->vdata->mmc_64bit) { + switch (reg_lo) { + /* These registers are always 32 bit */ + case MMC_RXRUNTERROR: + case MMC_RXJABBERERROR: + case MMC_RXUNDERSIZE_G: + case MMC_RXOVERSIZE_G: + case MMC_RXWATCHDOGERROR: + read_hi = false; + break; - default: - read_hi = false; + default: + read_hi = true; + } + } else { + switch (reg_lo) { + /* These registers are always 64 bit */ + case MMC_TXOCTETCOUNT_GB_LO: + case MMC_TXOCTETCOUNT_G_LO: + case MMC_RXOCTETCOUNT_GB_LO: + case MMC_RXOCTETCOUNT_G_LO: + read_hi = true; + break; + + default: + read_hi = false; + } } val = XGMAC_IOREAD(pdata, reg_lo); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 160b498..7cbf91b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -804,6 +804,7 @@ struct xgbe_hw_features { struct xgbe_version_data { void (*init_function_ptrs_phy_impl)(struct xgbe_phy_if *); enum xgbe_xpcs_access xpcs_access; + unsigned int mmc_64bit; }; struct xgbe_prv_data { -- cgit v1.1 From 4b8acdf5fe97d01962f3a62eaff3f010f8de5869 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:19:17 -0500 Subject: amd-xgbe: Update how to determine DMA channel status Tx and Rx DMA channel status determiniation is different depending on the version of the hardware. Update the channel status processing code to account for the change. Also, reduce the timeout value used when stopping the channels. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 4 ++ drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 58 +++++++++++++++++++---------- drivers/net/ethernet/amd/xgbe/xgbe.h | 2 +- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 6c40915..8036ee5 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -790,6 +790,10 @@ #define MTL_Q_RQOMR_RSF_WIDTH 1 #define MTL_Q_RQOMR_RTC_INDEX 0 #define MTL_Q_RQOMR_RTC_WIDTH 2 +#define MTL_Q_TQDR_TRCSTS_INDEX 1 +#define MTL_Q_TQDR_TRCSTS_WIDTH 2 +#define MTL_Q_TQDR_TXQSTS_INDEX 4 +#define MTL_Q_TQDR_TXQSTS_WIDTH 1 #define MTL_Q_TQOMR_FTQ_INDEX 0 #define MTL_Q_TQOMR_FTQ_WIDTH 1 #define MTL_Q_TQOMR_Q2TCMAP_INDEX 8 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index fbd60ee..0a7ab63 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -2944,20 +2944,48 @@ static void xgbe_config_mmc(struct xgbe_prv_data *pdata) XGMAC_IOWRITE_BITS(pdata, MMC_CR, CR, 1); } +static void xgbe_txq_prepare_tx_stop(struct xgbe_prv_data *pdata, + unsigned int queue) +{ + unsigned int tx_status; + unsigned long tx_timeout; + + /* The Tx engine cannot be stopped if it is actively processing + * packets. Wait for the Tx queue to empty the Tx fifo. Don't + * wait forever though... + */ + tx_timeout = jiffies + (XGBE_DMA_STOP_TIMEOUT * HZ); + while (time_before(jiffies, tx_timeout)) { + tx_status = XGMAC_MTL_IOREAD(pdata, queue, MTL_Q_TQDR); + if ((XGMAC_GET_BITS(tx_status, MTL_Q_TQDR, TRCSTS) != 1) && + (XGMAC_GET_BITS(tx_status, MTL_Q_TQDR, TXQSTS) == 0)) + break; + + usleep_range(500, 1000); + } + + if (!time_before(jiffies, tx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Tx queue %u to empty\n", + queue); +} + static void xgbe_prepare_tx_stop(struct xgbe_prv_data *pdata, - struct xgbe_channel *channel) + unsigned int queue) { unsigned int tx_dsr, tx_pos, tx_qidx; unsigned int tx_status; unsigned long tx_timeout; + if (XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER) > 0x20) + return xgbe_txq_prepare_tx_stop(pdata, queue); + /* Calculate the status register to read and the position within */ - if (channel->queue_index < DMA_DSRX_FIRST_QUEUE) { + if (queue < DMA_DSRX_FIRST_QUEUE) { tx_dsr = DMA_DSR0; - tx_pos = (channel->queue_index * DMA_DSR_Q_WIDTH) + - DMA_DSR0_TPS_START; + tx_pos = (queue * DMA_DSR_Q_WIDTH) + DMA_DSR0_TPS_START; } else { - tx_qidx = channel->queue_index - DMA_DSRX_FIRST_QUEUE; + tx_qidx = queue - DMA_DSRX_FIRST_QUEUE; tx_dsr = DMA_DSR1 + ((tx_qidx / DMA_DSRX_QPR) * DMA_DSRX_INC); tx_pos = ((tx_qidx % DMA_DSRX_QPR) * DMA_DSR_Q_WIDTH) + @@ -2982,7 +3010,7 @@ static void xgbe_prepare_tx_stop(struct xgbe_prv_data *pdata, if (!time_before(jiffies, tx_timeout)) netdev_info(pdata->netdev, "timed out waiting for Tx DMA channel %u to stop\n", - channel->queue_index); + queue); } static void xgbe_enable_tx(struct xgbe_prv_data *pdata) @@ -3014,13 +3042,8 @@ static void xgbe_disable_tx(struct xgbe_prv_data *pdata) unsigned int i; /* Prepare for Tx DMA channel stop */ - channel = pdata->channel; - for (i = 0; i < pdata->channel_count; i++, channel++) { - if (!channel->tx_ring) - break; - - xgbe_prepare_tx_stop(pdata, channel); - } + for (i = 0; i < pdata->tx_q_count; i++) + xgbe_prepare_tx_stop(pdata, i); /* Disable MAC Tx */ XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0); @@ -3144,13 +3167,8 @@ static void xgbe_powerdown_tx(struct xgbe_prv_data *pdata) unsigned int i; /* Prepare for Tx DMA channel stop */ - channel = pdata->channel; - for (i = 0; i < pdata->channel_count; i++, channel++) { - if (!channel->tx_ring) - break; - - xgbe_prepare_tx_stop(pdata, channel); - } + for (i = 0; i < pdata->tx_q_count; i++) + xgbe_prepare_tx_stop(pdata, i); /* Disable MAC Tx */ XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 7cbf91b..d5cae15 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -159,7 +159,7 @@ #define XGBE_MAX_DMA_CHANNELS 16 #define XGBE_MAX_QUEUES 16 #define XGBE_PRIORITY_QUEUES 8 -#define XGBE_DMA_STOP_TIMEOUT 5 +#define XGBE_DMA_STOP_TIMEOUT 1 /* DMA cache settings - Outer sharable, write-back, write-allocate */ #define XGBE_DMA_OS_AXDOMAIN 0x2 -- cgit v1.1 From bd8255d8ba35ae03f0a6d4d7d55b46660f2fc198 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 3 Nov 2016 13:19:27 -0500 Subject: amd-xgbe: Prepare for supporting PCI devices Update the driver framework to separate out platform/ACPI specific code from general code during device initialization. This will allow for the introduction of PCI device support. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/Makefile | 3 +- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 16 +- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 22 +- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 569 ++++------------------- drivers/net/ethernet/amd/xgbe/xgbe-platform.c | 632 ++++++++++++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe.h | 23 +- 6 files changed, 755 insertions(+), 510 deletions(-) create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-platform.c diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile index 60b4ae2..217d59e 100644 --- a/drivers/net/ethernet/amd/xgbe/Makefile +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -3,7 +3,8 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ xgbe-ptp.o \ - xgbe-phy-v1.o + xgbe-phy-v1.o \ + xgbe-platform.o amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 0a7ab63..9037319 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -2088,24 +2088,16 @@ static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata) static unsigned int xgbe_get_tx_fifo_size(struct xgbe_prv_data *pdata) { - unsigned int fifo_size; - - /* Calculate the configured fifo size */ - fifo_size = 1 << (pdata->hw_feat.tx_fifo_size + 7); - /* The configured value may not be the actual amount of fifo RAM */ - return min_t(unsigned int, XGMAC_FIFO_TX_MAX, fifo_size); + return min_t(unsigned int, pdata->tx_max_fifo_size, + pdata->hw_feat.tx_fifo_size); } static unsigned int xgbe_get_rx_fifo_size(struct xgbe_prv_data *pdata) { - unsigned int fifo_size; - - /* Calculate the configured fifo size */ - fifo_size = 1 << (pdata->hw_feat.rx_fifo_size + 7); - /* The configured value may not be the actual amount of fifo RAM */ - return min_t(unsigned int, XGMAC_FIFO_RX_MAX, fifo_size); + return min_t(unsigned int, pdata->rx_max_fifo_size, + pdata->hw_feat.rx_fifo_size); } static void xgbe_calculate_equal_fifo(unsigned int fifo_size, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index dd166a0..a43e9303 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -114,7 +114,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include @@ -160,18 +159,8 @@ static int xgbe_alloc_channels(struct xgbe_prv_data *pdata) channel->dma_regs = pdata->xgmac_regs + DMA_CH_BASE + (DMA_CH_INC * i); - if (pdata->per_channel_irq) { - /* Get the DMA interrupt (offset 1) */ - ret = platform_get_irq(pdata->pdev, i + 1); - if (ret < 0) { - netdev_err(pdata->netdev, - "platform_get_irq %u failed\n", - i + 1); - goto err_irq; - } - - channel->dma_irq = ret; - } + if (pdata->per_channel_irq) + channel->dma_irq = pdata->channel_irq[i]; if (i < pdata->tx_ring_count) { spin_lock_init(&tx_ring->lock); @@ -194,9 +183,6 @@ static int xgbe_alloc_channels(struct xgbe_prv_data *pdata) return 0; -err_irq: - kfree(rx_ring); - err_rx_ring: kfree(tx_ring); @@ -590,6 +576,10 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) hw_feat->tx_ch_cnt++; hw_feat->tc_cnt++; + /* Translate the fifo sizes into actual numbers */ + hw_feat->rx_fifo_size = 1 << (hw_feat->rx_fifo_size + 7); + hw_feat->tx_fifo_size = 1 << (hw_feat->tx_fifo_size + 7); + DBGPR("<--xgbe_get_all_hw_features\n"); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index d9864f0..c7187fc 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -116,20 +116,10 @@ #include #include -#include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "xgbe.h" #include "xgbe-common.h" @@ -176,196 +166,21 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata) pdata->vdata->init_function_ptrs_phy_impl(&pdata->phy_if); } -#ifdef CONFIG_ACPI -static const struct acpi_device_id xgbe_acpi_match[]; - -static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(xgbe_acpi_match, pdata->dev); - - return id ? (struct xgbe_version_data *)id->driver_data : NULL; -} - -static int xgbe_acpi_support(struct xgbe_prv_data *pdata) -{ - struct device *dev = pdata->dev; - u32 property; - int ret; - - /* Obtain the system clock setting */ - ret = device_property_read_u32(dev, XGBE_ACPI_DMA_FREQ, &property); - if (ret) { - dev_err(dev, "unable to obtain %s property\n", - XGBE_ACPI_DMA_FREQ); - return ret; - } - pdata->sysclk_rate = property; - - /* Obtain the PTP clock setting */ - ret = device_property_read_u32(dev, XGBE_ACPI_PTP_FREQ, &property); - if (ret) { - dev_err(dev, "unable to obtain %s property\n", - XGBE_ACPI_PTP_FREQ); - return ret; - } - pdata->ptpclk_rate = property; - - return 0; -} -#else /* CONFIG_ACPI */ -static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) -{ - return NULL; -} - -static int xgbe_acpi_support(struct xgbe_prv_data *pdata) -{ - return -EINVAL; -} -#endif /* CONFIG_ACPI */ - -#ifdef CONFIG_OF -static const struct of_device_id xgbe_of_match[]; - -static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) -{ - const struct of_device_id *id; - - id = of_match_device(xgbe_of_match, pdata->dev); - - return id ? (struct xgbe_version_data *)id->data : NULL; -} - -static int xgbe_of_support(struct xgbe_prv_data *pdata) -{ - struct device *dev = pdata->dev; - - /* Obtain the system clock setting */ - pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK); - if (IS_ERR(pdata->sysclk)) { - dev_err(dev, "dma devm_clk_get failed\n"); - return PTR_ERR(pdata->sysclk); - } - pdata->sysclk_rate = clk_get_rate(pdata->sysclk); - - /* Obtain the PTP clock setting */ - pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK); - if (IS_ERR(pdata->ptpclk)) { - dev_err(dev, "ptp devm_clk_get failed\n"); - return PTR_ERR(pdata->ptpclk); - } - pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk); - - return 0; -} - -static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata) -{ - struct device *dev = pdata->dev; - struct device_node *phy_node; - struct platform_device *phy_pdev; - - phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0); - if (phy_node) { - /* Old style device tree: - * The XGBE and PHY resources are separate - */ - phy_pdev = of_find_device_by_node(phy_node); - of_node_put(phy_node); - } else { - /* New style device tree: - * The XGBE and PHY resources are grouped together with - * the PHY resources listed last - */ - get_device(dev); - phy_pdev = pdata->pdev; - } - - return phy_pdev; -} -#else /* CONFIG_OF */ -static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) -{ - return NULL; -} - -static int xgbe_of_support(struct xgbe_prv_data *pdata) -{ - return -EINVAL; -} - -static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata) -{ - return NULL; -} -#endif /* CONFIG_OF */ - -static unsigned int xgbe_resource_count(struct platform_device *pdev, - unsigned int type) -{ - unsigned int count; - int i; - - for (i = 0, count = 0; i < pdev->num_resources; i++) { - struct resource *res = &pdev->resource[i]; - - if (type == resource_type(res)) - count++; - } - - return count; -} - -static struct platform_device *xgbe_get_phy_pdev(struct xgbe_prv_data *pdata) -{ - struct platform_device *phy_pdev; - - if (pdata->use_acpi) { - get_device(pdata->dev); - phy_pdev = pdata->pdev; - } else { - phy_pdev = xgbe_of_get_phy_pdev(pdata); - } - - return phy_pdev; -} - -static struct xgbe_version_data *xgbe_get_vdata(struct xgbe_prv_data *pdata) -{ - return pdata->use_acpi ? xgbe_acpi_vdata(pdata) - : xgbe_of_vdata(pdata); -} - -static int xgbe_probe(struct platform_device *pdev) +struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev) { struct xgbe_prv_data *pdata; struct net_device *netdev; - struct device *dev = &pdev->dev; - struct platform_device *phy_pdev; - struct resource *res; - const char *phy_mode; - unsigned int i, phy_memnum, phy_irqnum; - enum dev_dma_attr attr; - int ret; - - DBGPR("--> xgbe_probe\n"); netdev = alloc_etherdev_mq(sizeof(struct xgbe_prv_data), XGBE_MAX_DMA_CHANNELS); if (!netdev) { - dev_err(dev, "alloc_etherdev failed\n"); - ret = -ENOMEM; - goto err_alloc; + dev_err(dev, "alloc_etherdev_mq failed\n"); + return ERR_PTR(-ENOMEM); } SET_NETDEV_DEV(netdev, dev); pdata = netdev_priv(netdev); pdata->netdev = netdev; - pdata->pdev = pdev; - pdata->adev = ACPI_COMPANION(dev); pdata->dev = dev; - platform_set_drvdata(pdev, netdev); spin_lock_init(&pdata->lock); spin_lock_init(&pdata->xpcs_lock); @@ -376,186 +191,82 @@ static int xgbe_probe(struct platform_device *pdev) set_bit(XGBE_DOWN, &pdata->dev_state); - /* Check if we should use ACPI or DT */ - pdata->use_acpi = dev->of_node ? 0 : 1; + return pdata; +} - /* Get the version data */ - pdata->vdata = xgbe_get_vdata(pdata); +void xgbe_free_pdata(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; - phy_pdev = xgbe_get_phy_pdev(pdata); - if (!phy_pdev) { - dev_err(dev, "unable to obtain phy device\n"); - ret = -EINVAL; - goto err_phydev; - } - pdata->phy_pdev = phy_pdev; - pdata->phy_dev = &phy_pdev->dev; - - if (pdev == phy_pdev) { - /* New style device tree or ACPI: - * The XGBE and PHY resources are grouped together with - * the PHY resources listed last - */ - phy_memnum = xgbe_resource_count(pdev, IORESOURCE_MEM) - 3; - phy_irqnum = xgbe_resource_count(pdev, IORESOURCE_IRQ) - 1; - } else { - /* Old style device tree: - * The XGBE and PHY resources are separate - */ - phy_memnum = 0; - phy_irqnum = 0; - } + free_netdev(netdev); +} - /* Set and validate the number of descriptors for a ring */ - BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT); - pdata->tx_desc_count = XGBE_TX_DESC_CNT; - if (pdata->tx_desc_count & (pdata->tx_desc_count - 1)) { - dev_err(dev, "tx descriptor count (%d) is not valid\n", - pdata->tx_desc_count); - ret = -EINVAL; - goto err_io; - } - BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_RX_DESC_CNT); - pdata->rx_desc_count = XGBE_RX_DESC_CNT; - if (pdata->rx_desc_count & (pdata->rx_desc_count - 1)) { - dev_err(dev, "rx descriptor count (%d) is not valid\n", - pdata->rx_desc_count); - ret = -EINVAL; - goto err_io; - } +void xgbe_set_counts(struct xgbe_prv_data *pdata) +{ + /* Set all the function pointers */ + xgbe_init_all_fptrs(pdata); - /* Obtain the mmio areas for the device */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pdata->xgmac_regs = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->xgmac_regs)) { - dev_err(dev, "xgmac ioremap failed\n"); - ret = PTR_ERR(pdata->xgmac_regs); - goto err_io; - } - if (netif_msg_probe(pdata)) - dev_dbg(dev, "xgmac_regs = %p\n", pdata->xgmac_regs); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - pdata->xpcs_regs = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->xpcs_regs)) { - dev_err(dev, "xpcs ioremap failed\n"); - ret = PTR_ERR(pdata->xpcs_regs); - goto err_io; - } - if (netif_msg_probe(pdata)) - dev_dbg(dev, "xpcs_regs = %p\n", pdata->xpcs_regs); - - res = platform_get_resource(phy_pdev, IORESOURCE_MEM, phy_memnum++); - pdata->rxtx_regs = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->rxtx_regs)) { - dev_err(dev, "rxtx ioremap failed\n"); - ret = PTR_ERR(pdata->rxtx_regs); - goto err_io; - } - if (netif_msg_probe(pdata)) - dev_dbg(dev, "rxtx_regs = %p\n", pdata->rxtx_regs); - - res = platform_get_resource(phy_pdev, IORESOURCE_MEM, phy_memnum++); - pdata->sir0_regs = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->sir0_regs)) { - dev_err(dev, "sir0 ioremap failed\n"); - ret = PTR_ERR(pdata->sir0_regs); - goto err_io; - } - if (netif_msg_probe(pdata)) - dev_dbg(dev, "sir0_regs = %p\n", pdata->sir0_regs); - - res = platform_get_resource(phy_pdev, IORESOURCE_MEM, phy_memnum++); - pdata->sir1_regs = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->sir1_regs)) { - dev_err(dev, "sir1 ioremap failed\n"); - ret = PTR_ERR(pdata->sir1_regs); - goto err_io; - } - if (netif_msg_probe(pdata)) - dev_dbg(dev, "sir1_regs = %p\n", pdata->sir1_regs); - - /* Retrieve the MAC address */ - ret = device_property_read_u8_array(dev, XGBE_MAC_ADDR_PROPERTY, - pdata->mac_addr, - sizeof(pdata->mac_addr)); - if (ret || !is_valid_ether_addr(pdata->mac_addr)) { - dev_err(dev, "invalid %s property\n", XGBE_MAC_ADDR_PROPERTY); - if (!ret) - ret = -EINVAL; - goto err_io; - } + /* Populate the hardware features */ + xgbe_get_all_hw_features(pdata); - /* Retrieve the PHY mode - it must be "xgmii" */ - ret = device_property_read_string(dev, XGBE_PHY_MODE_PROPERTY, - &phy_mode); - if (ret || strcmp(phy_mode, phy_modes(PHY_INTERFACE_MODE_XGMII))) { - dev_err(dev, "invalid %s property\n", XGBE_PHY_MODE_PROPERTY); - if (!ret) - ret = -EINVAL; - goto err_io; - } - pdata->phy_mode = PHY_INTERFACE_MODE_XGMII; + /* Set default max values if not provided */ + if (!pdata->tx_max_channel_count) + pdata->tx_max_channel_count = pdata->hw_feat.tx_ch_cnt; + if (!pdata->rx_max_channel_count) + pdata->rx_max_channel_count = pdata->hw_feat.rx_ch_cnt; - /* Check for per channel interrupt support */ - if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) - pdata->per_channel_irq = 1; + if (!pdata->tx_max_q_count) + pdata->tx_max_q_count = pdata->hw_feat.tx_q_cnt; + if (!pdata->rx_max_q_count) + pdata->rx_max_q_count = pdata->hw_feat.rx_q_cnt; - /* Obtain device settings unique to ACPI/OF */ - if (pdata->use_acpi) - ret = xgbe_acpi_support(pdata); - else - ret = xgbe_of_support(pdata); - if (ret) - goto err_io; - - /* Set the DMA coherency values */ - attr = device_get_dma_attr(dev); - if (attr == DEV_DMA_NOT_SUPPORTED) { - dev_err(dev, "DMA is not supported"); - ret = -ENODEV; - goto err_io; - } - pdata->coherent = (attr == DEV_DMA_COHERENT); - if (pdata->coherent) { - pdata->axdomain = XGBE_DMA_OS_AXDOMAIN; - pdata->arcache = XGBE_DMA_OS_ARCACHE; - pdata->awcache = XGBE_DMA_OS_AWCACHE; - } else { - pdata->axdomain = XGBE_DMA_SYS_AXDOMAIN; - pdata->arcache = XGBE_DMA_SYS_ARCACHE; - pdata->awcache = XGBE_DMA_SYS_AWCACHE; - } + /* Calculate the number of Tx and Rx rings to be created + * -Tx (DMA) Channels map 1-to-1 to Tx Queues so set + * the number of Tx queues to the number of Tx channels + * enabled + * -Rx (DMA) Channels do not map 1-to-1 so use the actual + * number of Rx queues or maximum allowed + */ + pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(), + pdata->hw_feat.tx_ch_cnt); + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->tx_max_channel_count); + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->tx_max_q_count); - /* Get the device interrupt */ - ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "platform_get_irq 0 failed\n"); - goto err_io; - } - pdata->dev_irq = ret; + pdata->tx_q_count = pdata->tx_ring_count; + + pdata->rx_ring_count = min_t(unsigned int, + netif_get_num_default_rss_queues(), + pdata->hw_feat.rx_ch_cnt); + pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count, + pdata->rx_max_channel_count); + + pdata->rx_q_count = min_t(unsigned int, pdata->hw_feat.rx_q_cnt, + pdata->rx_max_q_count); - /* Get the auto-negotiation interrupt */ - ret = platform_get_irq(phy_pdev, phy_irqnum++); - if (ret < 0) { - dev_err(dev, "platform_get_irq phy 0 failed\n"); - goto err_io; + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "TX/RX DMA channel count = %u/%u\n", + pdata->tx_ring_count, pdata->rx_ring_count); + dev_dbg(pdata->dev, "TX/RX hardware queue count = %u/%u\n", + pdata->tx_q_count, pdata->rx_q_count); } - pdata->an_irq = ret; +} + +int xgbe_config_netdev(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct device *dev = pdata->dev; + unsigned int i; + int ret; netdev->irq = pdata->dev_irq; netdev->base_addr = (unsigned long)pdata->xgmac_regs; memcpy(netdev->dev_addr, pdata->mac_addr, netdev->addr_len); - /* Set all the function pointers */ - xgbe_init_all_fptrs(pdata); - /* Issue software reset to device */ pdata->hw_if.exit(pdata); - /* Populate the hardware features */ - xgbe_get_all_hw_features(pdata); - /* Set default configuration data */ xgbe_default_config(pdata); @@ -564,33 +275,33 @@ static int xgbe_probe(struct platform_device *pdev) DMA_BIT_MASK(pdata->hw_feat.dma_width)); if (ret) { dev_err(dev, "dma_set_mask_and_coherent failed\n"); - goto err_io; + return ret; } - /* Calculate the number of Tx and Rx rings to be created - * -Tx (DMA) Channels map 1-to-1 to Tx Queues so set - * the number of Tx queues to the number of Tx channels - * enabled - * -Rx (DMA) Channels do not map 1-to-1 so use the actual - * number of Rx queues - */ - pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(), - pdata->hw_feat.tx_ch_cnt); - pdata->tx_q_count = pdata->tx_ring_count; + /* Set default max values if not provided */ + if (!pdata->tx_max_fifo_size) + pdata->tx_max_fifo_size = pdata->hw_feat.tx_fifo_size; + if (!pdata->rx_max_fifo_size) + pdata->rx_max_fifo_size = pdata->hw_feat.rx_fifo_size; + + /* Set and validate the number of descriptors for a ring */ + BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT); + pdata->tx_desc_count = XGBE_TX_DESC_CNT; + + BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_RX_DESC_CNT); + pdata->rx_desc_count = XGBE_RX_DESC_CNT; + + /* Set the number of queues */ ret = netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count); if (ret) { dev_err(dev, "error setting real tx queue count\n"); - goto err_io; + return ret; } - pdata->rx_ring_count = min_t(unsigned int, - netif_get_num_default_rss_queues(), - pdata->hw_feat.rx_ch_cnt); - pdata->rx_q_count = pdata->hw_feat.rx_q_cnt; ret = netif_set_real_num_rx_queues(netdev, pdata->rx_ring_count); if (ret) { dev_err(dev, "error setting real rx queue count\n"); - goto err_io; + return ret; } /* Initialize RSS hash key and lookup table */ @@ -607,7 +318,7 @@ static int xgbe_probe(struct platform_device *pdev) /* Call MDIO/PHY initialization routine */ ret = pdata->phy_if.phy_init(pdata); if (ret) - goto err_io; + return ret; /* Set device operations */ netdev->netdev_ops = xgbe_get_netdev_ops(); @@ -654,7 +365,7 @@ static int xgbe_probe(struct platform_device *pdev) ret = register_netdev(netdev); if (ret) { dev_err(dev, "net device registration failed\n"); - goto err_io; + return ret; } /* Create the PHY/ANEG name based on netdev name */ @@ -682,10 +393,6 @@ static int xgbe_probe(struct platform_device *pdev) xgbe_debugfs_init(pdata); - netdev_notice(netdev, "net device enabled\n"); - - DBGPR("<-- xgbe_probe\n"); - return 0; err_wq: @@ -694,24 +401,12 @@ err_wq: err_netdev: unregister_netdev(netdev); -err_io: - platform_device_put(phy_pdev); - -err_phydev: - free_netdev(netdev); - -err_alloc: - dev_notice(dev, "net device not enabled\n"); - return ret; } -static int xgbe_remove(struct platform_device *pdev) +void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) { - struct net_device *netdev = platform_get_drvdata(pdev); - struct xgbe_prv_data *pdata = netdev_priv(netdev); - - DBGPR("-->xgbe_remove\n"); + struct net_device *netdev = pdata->netdev; xgbe_debugfs_exit(pdata); @@ -726,103 +421,23 @@ static int xgbe_remove(struct platform_device *pdev) destroy_workqueue(pdata->dev_workqueue); unregister_netdev(netdev); - - platform_device_put(pdata->phy_pdev); - - free_netdev(netdev); - - DBGPR("<--xgbe_remove\n"); - - return 0; } -#ifdef CONFIG_PM -static int xgbe_suspend(struct device *dev) +static int __init xgbe_mod_init(void) { - struct net_device *netdev = dev_get_drvdata(dev); - struct xgbe_prv_data *pdata = netdev_priv(netdev); - int ret = 0; - - DBGPR("-->xgbe_suspend\n"); - - if (netif_running(netdev)) - ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT); - - pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); - pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + int ret; - DBGPR("<--xgbe_suspend\n"); + ret = xgbe_platform_init(); + if (ret) + return ret; - return ret; + return 0; } -static int xgbe_resume(struct device *dev) +static void __exit xgbe_mod_exit(void) { - struct net_device *netdev = dev_get_drvdata(dev); - struct xgbe_prv_data *pdata = netdev_priv(netdev); - int ret = 0; - - DBGPR("-->xgbe_resume\n"); - - pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; - XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); - - if (netif_running(netdev)) { - ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT); - - /* Schedule a restart in case the link or phy state changed - * while we were powered down. - */ - schedule_work(&pdata->restart_work); - } - - DBGPR("<--xgbe_resume\n"); - - return ret; + xgbe_platform_exit(); } -#endif /* CONFIG_PM */ - -static const struct xgbe_version_data xgbe_v1 = { - .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v1, - .xpcs_access = XGBE_XPCS_ACCESS_V1, -}; - -#ifdef CONFIG_ACPI -static const struct acpi_device_id xgbe_acpi_match[] = { - { .id = "AMDI8001", - .driver_data = (kernel_ulong_t)&xgbe_v1 }, - {}, -}; - -MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match); -#endif - -#ifdef CONFIG_OF -static const struct of_device_id xgbe_of_match[] = { - { .compatible = "amd,xgbe-seattle-v1a", - .data = &xgbe_v1 }, - {}, -}; - -MODULE_DEVICE_TABLE(of, xgbe_of_match); -#endif - -static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume); - -static struct platform_driver xgbe_driver = { - .driver = { - .name = "amd-xgbe", -#ifdef CONFIG_ACPI - .acpi_match_table = xgbe_acpi_match, -#endif -#ifdef CONFIG_OF - .of_match_table = xgbe_of_match, -#endif - .pm = &xgbe_pm_ops, - }, - .probe = xgbe_probe, - .remove = xgbe_remove, -}; -module_platform_driver(xgbe_driver); +module_init(xgbe_mod_init); +module_exit(xgbe_mod_exit); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c new file mode 100644 index 0000000..0edbcd5 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c @@ -0,0 +1,632 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xgbe.h" +#include "xgbe-common.h" + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgbe_acpi_match[]; + +static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(xgbe_acpi_match, pdata->dev); + + return id ? (struct xgbe_version_data *)id->driver_data : NULL; +} + +static int xgbe_acpi_support(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + u32 property; + int ret; + + /* Obtain the system clock setting */ + ret = device_property_read_u32(dev, XGBE_ACPI_DMA_FREQ, &property); + if (ret) { + dev_err(dev, "unable to obtain %s property\n", + XGBE_ACPI_DMA_FREQ); + return ret; + } + pdata->sysclk_rate = property; + + /* Obtain the PTP clock setting */ + ret = device_property_read_u32(dev, XGBE_ACPI_PTP_FREQ, &property); + if (ret) { + dev_err(dev, "unable to obtain %s property\n", + XGBE_ACPI_PTP_FREQ); + return ret; + } + pdata->ptpclk_rate = property; + + return 0; +} +#else /* CONFIG_ACPI */ +static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) +{ + return NULL; +} + +static int xgbe_acpi_support(struct xgbe_prv_data *pdata) +{ + return -EINVAL; +} +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_OF +static const struct of_device_id xgbe_of_match[]; + +static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) +{ + const struct of_device_id *id; + + id = of_match_device(xgbe_of_match, pdata->dev); + + return id ? (struct xgbe_version_data *)id->data : NULL; +} + +static int xgbe_of_support(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + + /* Obtain the system clock setting */ + pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK); + if (IS_ERR(pdata->sysclk)) { + dev_err(dev, "dma devm_clk_get failed\n"); + return PTR_ERR(pdata->sysclk); + } + pdata->sysclk_rate = clk_get_rate(pdata->sysclk); + + /* Obtain the PTP clock setting */ + pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK); + if (IS_ERR(pdata->ptpclk)) { + dev_err(dev, "ptp devm_clk_get failed\n"); + return PTR_ERR(pdata->ptpclk); + } + pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk); + + return 0; +} + +static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + struct device_node *phy_node; + struct platform_device *phy_pdev; + + phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0); + if (phy_node) { + /* Old style device tree: + * The XGBE and PHY resources are separate + */ + phy_pdev = of_find_device_by_node(phy_node); + of_node_put(phy_node); + } else { + /* New style device tree: + * The XGBE and PHY resources are grouped together with + * the PHY resources listed last + */ + get_device(dev); + phy_pdev = pdata->platdev; + } + + return phy_pdev; +} +#else /* CONFIG_OF */ +static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) +{ + return NULL; +} + +static int xgbe_of_support(struct xgbe_prv_data *pdata) +{ + return -EINVAL; +} + +static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata) +{ + return NULL; +} +#endif /* CONFIG_OF */ + +static unsigned int xgbe_resource_count(struct platform_device *pdev, + unsigned int type) +{ + unsigned int count; + int i; + + for (i = 0, count = 0; i < pdev->num_resources; i++) { + struct resource *res = &pdev->resource[i]; + + if (type == resource_type(res)) + count++; + } + + return count; +} + +static struct platform_device *xgbe_get_phy_pdev(struct xgbe_prv_data *pdata) +{ + struct platform_device *phy_pdev; + + if (pdata->use_acpi) { + get_device(pdata->dev); + phy_pdev = pdata->platdev; + } else { + phy_pdev = xgbe_of_get_phy_pdev(pdata); + } + + return phy_pdev; +} + +static struct xgbe_version_data *xgbe_get_vdata(struct xgbe_prv_data *pdata) +{ + return pdata->use_acpi ? xgbe_acpi_vdata(pdata) + : xgbe_of_vdata(pdata); +} + +static int xgbe_platform_probe(struct platform_device *pdev) +{ + struct xgbe_prv_data *pdata; + struct device *dev = &pdev->dev; + struct platform_device *phy_pdev; + struct resource *res; + const char *phy_mode; + unsigned int phy_memnum, phy_irqnum; + unsigned int dma_irqnum, dma_irqend; + enum dev_dma_attr attr; + int ret; + + pdata = xgbe_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->platdev = pdev; + pdata->adev = ACPI_COMPANION(dev); + platform_set_drvdata(pdev, pdata); + + /* Check if we should use ACPI or DT */ + pdata->use_acpi = dev->of_node ? 0 : 1; + + /* Get the version data */ + pdata->vdata = xgbe_get_vdata(pdata); + + phy_pdev = xgbe_get_phy_pdev(pdata); + if (!phy_pdev) { + dev_err(dev, "unable to obtain phy device\n"); + ret = -EINVAL; + goto err_phydev; + } + pdata->phy_platdev = phy_pdev; + pdata->phy_dev = &phy_pdev->dev; + + if (pdev == phy_pdev) { + /* New style device tree or ACPI: + * The XGBE and PHY resources are grouped together with + * the PHY resources listed last + */ + phy_memnum = xgbe_resource_count(pdev, IORESOURCE_MEM) - 3; + phy_irqnum = xgbe_resource_count(pdev, IORESOURCE_IRQ) - 1; + dma_irqnum = 1; + dma_irqend = phy_irqnum; + } else { + /* Old style device tree: + * The XGBE and PHY resources are separate + */ + phy_memnum = 0; + phy_irqnum = 0; + dma_irqnum = 1; + dma_irqend = xgbe_resource_count(pdev, IORESOURCE_IRQ); + } + + /* Obtain the mmio areas for the device */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pdata->xgmac_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pdata->xgmac_regs)) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = PTR_ERR(pdata->xgmac_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xgmac_regs = %p\n", pdata->xgmac_regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pdata->xpcs_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pdata->xpcs_regs)) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = PTR_ERR(pdata->xpcs_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xpcs_regs = %p\n", pdata->xpcs_regs); + + res = platform_get_resource(phy_pdev, IORESOURCE_MEM, phy_memnum++); + pdata->rxtx_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pdata->rxtx_regs)) { + dev_err(dev, "rxtx ioremap failed\n"); + ret = PTR_ERR(pdata->rxtx_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "rxtx_regs = %p\n", pdata->rxtx_regs); + + res = platform_get_resource(phy_pdev, IORESOURCE_MEM, phy_memnum++); + pdata->sir0_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pdata->sir0_regs)) { + dev_err(dev, "sir0 ioremap failed\n"); + ret = PTR_ERR(pdata->sir0_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "sir0_regs = %p\n", pdata->sir0_regs); + + res = platform_get_resource(phy_pdev, IORESOURCE_MEM, phy_memnum++); + pdata->sir1_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pdata->sir1_regs)) { + dev_err(dev, "sir1 ioremap failed\n"); + ret = PTR_ERR(pdata->sir1_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "sir1_regs = %p\n", pdata->sir1_regs); + + /* Retrieve the MAC address */ + ret = device_property_read_u8_array(dev, XGBE_MAC_ADDR_PROPERTY, + pdata->mac_addr, + sizeof(pdata->mac_addr)); + if (ret || !is_valid_ether_addr(pdata->mac_addr)) { + dev_err(dev, "invalid %s property\n", XGBE_MAC_ADDR_PROPERTY); + if (!ret) + ret = -EINVAL; + goto err_io; + } + + /* Retrieve the PHY mode - it must be "xgmii" */ + ret = device_property_read_string(dev, XGBE_PHY_MODE_PROPERTY, + &phy_mode); + if (ret || strcmp(phy_mode, phy_modes(PHY_INTERFACE_MODE_XGMII))) { + dev_err(dev, "invalid %s property\n", XGBE_PHY_MODE_PROPERTY); + if (!ret) + ret = -EINVAL; + goto err_io; + } + pdata->phy_mode = PHY_INTERFACE_MODE_XGMII; + + /* Check for per channel interrupt support */ + if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) + pdata->per_channel_irq = 1; + + /* Obtain device settings unique to ACPI/OF */ + if (pdata->use_acpi) + ret = xgbe_acpi_support(pdata); + else + ret = xgbe_of_support(pdata); + if (ret) + goto err_io; + + /* Set the DMA coherency values */ + attr = device_get_dma_attr(dev); + if (attr == DEV_DMA_NOT_SUPPORTED) { + dev_err(dev, "DMA is not supported"); + ret = -ENODEV; + goto err_io; + } + pdata->coherent = (attr == DEV_DMA_COHERENT); + if (pdata->coherent) { + pdata->axdomain = XGBE_DMA_OS_AXDOMAIN; + pdata->arcache = XGBE_DMA_OS_ARCACHE; + pdata->awcache = XGBE_DMA_OS_AWCACHE; + } else { + pdata->axdomain = XGBE_DMA_SYS_AXDOMAIN; + pdata->arcache = XGBE_DMA_SYS_ARCACHE; + pdata->awcache = XGBE_DMA_SYS_AWCACHE; + } + + /* Set the maximum fifo amounts */ + pdata->tx_max_fifo_size = pdata->vdata->tx_max_fifo_size; + pdata->rx_max_fifo_size = pdata->vdata->rx_max_fifo_size; + + /* Set the hardware channel and queue counts */ + xgbe_set_counts(pdata); + + /* Get the device interrupt */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "platform_get_irq 0 failed\n"); + goto err_io; + } + pdata->dev_irq = ret; + + /* Get the per channel DMA interrupts */ + if (pdata->per_channel_irq) { + unsigned int i, max = ARRAY_SIZE(pdata->channel_irq); + + for (i = 0; (i < max) && (dma_irqnum < dma_irqend); i++) { + ret = platform_get_irq(pdata->platdev, dma_irqnum++); + if (ret < 0) { + netdev_err(pdata->netdev, + "platform_get_irq %u failed\n", + dma_irqnum - 1); + goto err_io; + } + + pdata->channel_irq[i] = ret; + } + } + + /* Get the auto-negotiation interrupt */ + ret = platform_get_irq(phy_pdev, phy_irqnum++); + if (ret < 0) { + dev_err(dev, "platform_get_irq phy 0 failed\n"); + goto err_io; + } + pdata->an_irq = ret; + + /* Configure the netdev resource */ + ret = xgbe_config_netdev(pdata); + if (ret) + goto err_io; + + netdev_notice(pdata->netdev, "net device enabled\n"); + + return 0; + +err_io: + platform_device_put(phy_pdev); + +err_phydev: + xgbe_free_pdata(pdata); + +err_alloc: + dev_notice(dev, "net device not enabled\n"); + + return ret; +} + +static int xgbe_platform_remove(struct platform_device *pdev) +{ + struct xgbe_prv_data *pdata = platform_get_drvdata(pdev); + + xgbe_deconfig_netdev(pdata); + + platform_device_put(pdata->phy_platdev); + + xgbe_free_pdata(pdata); + + return 0; +} + +#ifdef CONFIG_PM +static int xgbe_platform_suspend(struct device *dev) +{ + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + DBGPR("-->xgbe_suspend\n"); + + if (netif_running(netdev)) + ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT); + + pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + DBGPR("<--xgbe_suspend\n"); + + return ret; +} + +static int xgbe_platform_resume(struct device *dev) +{ + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + DBGPR("-->xgbe_resume\n"); + + pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + if (netif_running(netdev)) { + ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT); + + /* Schedule a restart in case the link or phy state changed + * while we were powered down. + */ + schedule_work(&pdata->restart_work); + } + + DBGPR("<--xgbe_resume\n"); + + return ret; +} +#endif /* CONFIG_PM */ + +static const struct xgbe_version_data xgbe_v1 = { + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v1, + .xpcs_access = XGBE_XPCS_ACCESS_V1, + .tx_max_fifo_size = 81920, + .rx_max_fifo_size = 81920, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgbe_acpi_match[] = { + { .id = "AMDI8001", + .driver_data = (kernel_ulong_t)&xgbe_v1 }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id xgbe_of_match[] = { + { .compatible = "amd,xgbe-seattle-v1a", + .data = &xgbe_v1 }, + {}, +}; + +MODULE_DEVICE_TABLE(of, xgbe_of_match); +#endif + +static SIMPLE_DEV_PM_OPS(xgbe_platform_pm_ops, + xgbe_platform_suspend, xgbe_platform_resume); + +static struct platform_driver xgbe_driver = { + .driver = { + .name = "amd-xgbe", +#ifdef CONFIG_ACPI + .acpi_match_table = xgbe_acpi_match, +#endif +#ifdef CONFIG_OF + .of_match_table = xgbe_of_match, +#endif + .pm = &xgbe_platform_pm_ops, + }, + .probe = xgbe_platform_probe, + .remove = xgbe_platform_remove, +}; + +int xgbe_platform_init(void) +{ + return platform_driver_register(&xgbe_driver); +} + +void xgbe_platform_exit(void) +{ + platform_driver_unregister(&xgbe_driver); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index d5cae15..0779247 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -210,8 +210,6 @@ #define XGMAC_DRIVER_CONTEXT 1 #define XGMAC_IOCTL_CONTEXT 2 -#define XGMAC_FIFO_RX_MAX 81920 -#define XGMAC_FIFO_TX_MAX 81920 #define XGMAC_FIFO_MIN_ALLOC 2048 #define XGMAC_FIFO_UNIT 256 #define XGMAC_FIFO_ALIGN(_x) \ @@ -805,14 +803,16 @@ struct xgbe_version_data { void (*init_function_ptrs_phy_impl)(struct xgbe_phy_if *); enum xgbe_xpcs_access xpcs_access; unsigned int mmc_64bit; + unsigned int tx_max_fifo_size; + unsigned int rx_max_fifo_size; }; struct xgbe_prv_data { struct net_device *netdev; - struct platform_device *pdev; + struct platform_device *platdev; struct acpi_device *adev; struct device *dev; - struct platform_device *phy_pdev; + struct platform_device *phy_platdev; struct device *phy_dev; /* Version related data */ @@ -845,6 +845,7 @@ struct xgbe_prv_data { int dev_irq; unsigned int per_channel_irq; + int channel_irq[XGBE_MAX_DMA_CHANNELS]; struct xgbe_hw_if hw_if; struct xgbe_phy_if phy_if; @@ -863,12 +864,16 @@ struct xgbe_prv_data { /* Rings for Tx/Rx on a DMA channel */ struct xgbe_channel *channel; + unsigned int tx_max_channel_count; + unsigned int rx_max_channel_count; unsigned int channel_count; unsigned int tx_ring_count; unsigned int tx_desc_count; unsigned int rx_ring_count; unsigned int rx_desc_count; + unsigned int tx_max_q_count; + unsigned int rx_max_q_count; unsigned int tx_q_count; unsigned int rx_q_count; @@ -880,11 +885,13 @@ struct xgbe_prv_data { unsigned int tx_threshold; unsigned int tx_pbl; unsigned int tx_osp_mode; + unsigned int tx_max_fifo_size; /* Rx settings */ unsigned int rx_sf_mode; unsigned int rx_threshold; unsigned int rx_pbl; + unsigned int rx_max_fifo_size; /* Tx coalescing settings */ unsigned int tx_usecs; @@ -1005,6 +1012,14 @@ struct xgbe_prv_data { }; /* Function prototypes*/ +struct xgbe_prv_data *xgbe_alloc_pdata(struct device *); +void xgbe_free_pdata(struct xgbe_prv_data *); +void xgbe_set_counts(struct xgbe_prv_data *); +int xgbe_config_netdev(struct xgbe_prv_data *); +void xgbe_deconfig_netdev(struct xgbe_prv_data *); + +int xgbe_platform_init(void); +void xgbe_platform_exit(void); void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *); void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *); -- cgit v1.1 From 81cc2e434a9e8514bfad030683f7464c26306e35 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:11:57 +0000 Subject: nfp: add support for ethtool .get_channels Report number of rings via ethtool .get_channels API. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 3418f22..a7386d1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -614,6 +614,21 @@ static int nfp_net_set_coalesce(struct net_device *netdev, return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_IRQMOD); } +static void nfp_net_get_channels(struct net_device *netdev, + struct ethtool_channels *channel) +{ + struct nfp_net *nn = netdev_priv(netdev); + + channel->max_rx = min(nn->max_rx_rings, nn->max_r_vecs); + channel->max_tx = min(nn->max_tx_rings, nn->max_r_vecs); + channel->max_combined = min(channel->max_rx, channel->max_tx); + channel->max_other = NFP_NET_NON_Q_VECTORS; + channel->combined_count = min(nn->num_rx_rings, nn->num_tx_rings); + channel->rx_count = nn->num_rx_rings - channel->combined_count; + channel->tx_count = nn->num_tx_rings - channel->combined_count; + channel->other_count = NFP_NET_NON_Q_VECTORS; +} + static const struct ethtool_ops nfp_net_ethtool_ops = { .get_drvinfo = nfp_net_get_drvinfo, .get_link = ethtool_op_get_link, @@ -632,6 +647,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .get_regs = nfp_net_get_regs, .get_coalesce = nfp_net_get_coalesce, .set_coalesce = nfp_net_set_coalesce, + .get_channels = nfp_net_get_channels, }; void nfp_net_set_ethtool_ops(struct net_device *netdev) -- cgit v1.1 From 68453c7a8973399441080bcb20512d38033e64f7 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:11:58 +0000 Subject: nfp: centralize runtime reconfiguration logic All functions which need to reallocate ring resources at runtime look very similar. Centralize that logic into a separate function. Encapsulate configuration parameters in a structure. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 10 +- .../net/ethernet/netronome/nfp/nfp_net_common.c | 208 +++++++++------------ .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 19 ++ 3 files changed, 118 insertions(+), 119 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index e871325..14b5e21 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -583,6 +583,12 @@ struct nfp_net { struct dentry *debugfs_dir; }; +struct nfp_net_ring_set { + unsigned int mtu; + unsigned int dcnt; + void *rings; +}; + /* Functions to read/write from/to a BAR * Performs any endian conversion necessary. */ @@ -771,7 +777,9 @@ void nfp_net_rss_write_key(struct nfp_net *nn); void nfp_net_coalesce_write_cfg(struct nfp_net *nn); int nfp_net_irqs_alloc(struct nfp_net *nn); void nfp_net_irqs_disable(struct nfp_net *nn); -int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt); +int +nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, + struct nfp_net_ring_set *tx); #ifdef CONFIG_NFP_NET_DEBUG void nfp_net_debugfs_create(void); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 97e0bbe..e58532d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1573,7 +1573,7 @@ err_alloc: } static struct nfp_net_tx_ring * -nfp_net_shadow_tx_rings_prepare(struct nfp_net *nn, u32 buf_cnt) +nfp_net_shadow_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_tx_ring *rings; unsigned int r; @@ -1585,11 +1585,11 @@ nfp_net_shadow_tx_rings_prepare(struct nfp_net *nn, u32 buf_cnt) for (r = 0; r < nn->num_tx_rings; r++) { nfp_net_tx_ring_init(&rings[r], nn->tx_rings[r].r_vec, r); - if (nfp_net_tx_ring_alloc(&rings[r], buf_cnt)) + if (nfp_net_tx_ring_alloc(&rings[r], s->dcnt)) goto err_free_prev; } - return rings; + return s->rings = rings; err_free_prev: while (r--) @@ -1598,27 +1598,29 @@ err_free_prev: return NULL; } -static struct nfp_net_tx_ring * -nfp_net_shadow_tx_rings_swap(struct nfp_net *nn, struct nfp_net_tx_ring *rings) +static void +nfp_net_shadow_tx_rings_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { - struct nfp_net_tx_ring *old = nn->tx_rings; + struct nfp_net_tx_ring *rings = s->rings; + struct nfp_net_ring_set new = *s; unsigned int r; + s->dcnt = nn->txd_cnt; + s->rings = nn->tx_rings; + for (r = 0; r < nn->num_tx_rings; r++) - old[r].r_vec->tx_ring = &rings[r]; + nn->tx_rings[r].r_vec->tx_ring = &rings[r]; - nn->tx_rings = rings; - return old; + nn->txd_cnt = new.dcnt; + nn->tx_rings = new.rings; } static void -nfp_net_shadow_tx_rings_free(struct nfp_net *nn, struct nfp_net_tx_ring *rings) +nfp_net_shadow_tx_rings_free(struct nfp_net *nn, struct nfp_net_ring_set *s) { + struct nfp_net_tx_ring *rings = s->rings; unsigned int r; - if (!rings) - return; - for (r = 0; r < nn->num_tx_rings; r++) nfp_net_tx_ring_free(&rings[r]); @@ -1691,9 +1693,9 @@ err_alloc: } static struct nfp_net_rx_ring * -nfp_net_shadow_rx_rings_prepare(struct nfp_net *nn, unsigned int fl_bufsz, - u32 buf_cnt) +nfp_net_shadow_rx_rings_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) { + unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(nn, s->mtu); struct nfp_net_rx_ring *rings; unsigned int r; @@ -1704,14 +1706,14 @@ nfp_net_shadow_rx_rings_prepare(struct nfp_net *nn, unsigned int fl_bufsz, for (r = 0; r < nn->num_rx_rings; r++) { nfp_net_rx_ring_init(&rings[r], nn->rx_rings[r].r_vec, r); - if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, buf_cnt)) + if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt)) goto err_free_prev; if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r])) goto err_free_ring; } - return rings; + return s->rings = rings; err_free_prev: while (r--) { @@ -1723,27 +1725,32 @@ err_free_ring: return NULL; } -static struct nfp_net_rx_ring * -nfp_net_shadow_rx_rings_swap(struct nfp_net *nn, struct nfp_net_rx_ring *rings) +static void +nfp_net_shadow_rx_rings_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { - struct nfp_net_rx_ring *old = nn->rx_rings; + struct nfp_net_rx_ring *rings = s->rings; + struct nfp_net_ring_set new = *s; unsigned int r; + s->mtu = nn->netdev->mtu; + s->dcnt = nn->rxd_cnt; + s->rings = nn->rx_rings; + for (r = 0; r < nn->num_rx_rings; r++) - old[r].r_vec->rx_ring = &rings[r]; + nn->rx_rings[r].r_vec->rx_ring = &rings[r]; - nn->rx_rings = rings; - return old; + nn->netdev->mtu = new.mtu; + nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, new.mtu); + nn->rxd_cnt = new.dcnt; + nn->rx_rings = new.rings; } static void -nfp_net_shadow_rx_rings_free(struct nfp_net *nn, struct nfp_net_rx_ring *rings) +nfp_net_shadow_rx_rings_free(struct nfp_net *nn, struct nfp_net_ring_set *s) { + struct nfp_net_rx_ring *rings = s->rings; unsigned int r; - if (!rings) - return; - for (r = 0; r < nn->num_rx_rings; r++) { nfp_net_rx_ring_bufs_free(nn, &rings[r]); nfp_net_rx_ring_free(&rings[r]); @@ -2255,89 +2262,50 @@ static void nfp_net_set_rx_mode(struct net_device *netdev) nn->ctrl = new_ctrl; } -static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) +static int +nfp_net_ring_swap_enable(struct nfp_net *nn, + struct nfp_net_ring_set *rx, + struct nfp_net_ring_set *tx) { - unsigned int old_mtu, old_fl_bufsz, new_fl_bufsz; - struct nfp_net *nn = netdev_priv(netdev); - struct nfp_net_rx_ring *tmp_rings; - int err; - - old_mtu = netdev->mtu; - old_fl_bufsz = nn->fl_bufsz; - new_fl_bufsz = nfp_net_calc_fl_bufsz(nn, new_mtu); - - if (!netif_running(netdev)) { - netdev->mtu = new_mtu; - nn->fl_bufsz = new_fl_bufsz; - return 0; - } - - /* Prepare new rings */ - tmp_rings = nfp_net_shadow_rx_rings_prepare(nn, new_fl_bufsz, - nn->rxd_cnt); - if (!tmp_rings) - return -ENOMEM; - - /* Stop device, swap in new rings, try to start the firmware */ - nfp_net_close_stack(nn); - nfp_net_clear_config_and_disable(nn); - - tmp_rings = nfp_net_shadow_rx_rings_swap(nn, tmp_rings); - - netdev->mtu = new_mtu; - nn->fl_bufsz = new_fl_bufsz; - - err = nfp_net_set_config_and_enable(nn); - if (err) { - const int err_new = err; + if (rx) + nfp_net_shadow_rx_rings_swap(nn, rx); + if (tx) + nfp_net_shadow_tx_rings_swap(nn, tx); - /* Try with old configuration and old rings */ - tmp_rings = nfp_net_shadow_rx_rings_swap(nn, tmp_rings); - - netdev->mtu = old_mtu; - nn->fl_bufsz = old_fl_bufsz; - - err = __nfp_net_set_config_and_enable(nn); - if (err) - nn_err(nn, "Can't restore MTU - FW communication failed (%d,%d)\n", - err_new, err); - } - - nfp_net_shadow_rx_rings_free(nn, tmp_rings); - - nfp_net_open_stack(nn); + return __nfp_net_set_config_and_enable(nn); +} - return err; +static void +nfp_net_ring_reconfig_down(struct nfp_net *nn, + struct nfp_net_ring_set *rx, + struct nfp_net_ring_set *tx) +{ + nn->netdev->mtu = rx ? rx->mtu : nn->netdev->mtu; + nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, nn->netdev->mtu); + nn->rxd_cnt = rx ? rx->dcnt : nn->rxd_cnt; + nn->txd_cnt = tx ? tx->dcnt : nn->txd_cnt; } -int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) +int +nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, + struct nfp_net_ring_set *tx) { - struct nfp_net_tx_ring *tx_rings = NULL; - struct nfp_net_rx_ring *rx_rings = NULL; - u32 old_rxd_cnt, old_txd_cnt; int err; if (!netif_running(nn->netdev)) { - nn->rxd_cnt = rxd_cnt; - nn->txd_cnt = txd_cnt; + nfp_net_ring_reconfig_down(nn, rx, tx); return 0; } - old_rxd_cnt = nn->rxd_cnt; - old_txd_cnt = nn->txd_cnt; - /* Prepare new rings */ - if (nn->rxd_cnt != rxd_cnt) { - rx_rings = nfp_net_shadow_rx_rings_prepare(nn, nn->fl_bufsz, - rxd_cnt); - if (!rx_rings) + if (rx) { + if (!nfp_net_shadow_rx_rings_prepare(nn, rx)) return -ENOMEM; } - if (nn->txd_cnt != txd_cnt) { - tx_rings = nfp_net_shadow_tx_rings_prepare(nn, txd_cnt); - if (!tx_rings) { - nfp_net_shadow_rx_rings_free(nn, rx_rings); - return -ENOMEM; + if (tx) { + if (!nfp_net_shadow_tx_rings_prepare(nn, tx)) { + err = -ENOMEM; + goto err_free_rx; } } @@ -2345,39 +2313,43 @@ int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) nfp_net_close_stack(nn); nfp_net_clear_config_and_disable(nn); - if (rx_rings) - rx_rings = nfp_net_shadow_rx_rings_swap(nn, rx_rings); - if (tx_rings) - tx_rings = nfp_net_shadow_tx_rings_swap(nn, tx_rings); - - nn->rxd_cnt = rxd_cnt; - nn->txd_cnt = txd_cnt; - - err = nfp_net_set_config_and_enable(nn); + err = nfp_net_ring_swap_enable(nn, rx, tx); if (err) { - const int err_new = err; - - /* Try with old configuration and old rings */ - if (rx_rings) - rx_rings = nfp_net_shadow_rx_rings_swap(nn, rx_rings); - if (tx_rings) - tx_rings = nfp_net_shadow_tx_rings_swap(nn, tx_rings); + int err2; - nn->rxd_cnt = old_rxd_cnt; - nn->txd_cnt = old_txd_cnt; + nfp_net_clear_config_and_disable(nn); - err = __nfp_net_set_config_and_enable(nn); - if (err) + /* Try with old configuration and old rings */ + err2 = nfp_net_ring_swap_enable(nn, rx, tx); + if (err2) nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", - err_new, err); + err, err2); } - nfp_net_shadow_rx_rings_free(nn, rx_rings); - nfp_net_shadow_tx_rings_free(nn, tx_rings); + if (rx) + nfp_net_shadow_rx_rings_free(nn, rx); + if (tx) + nfp_net_shadow_tx_rings_free(nn, tx); nfp_net_open_stack(nn); return err; + +err_free_rx: + if (rx) + nfp_net_shadow_rx_rings_free(nn, rx); + return err; +} + +static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct nfp_net *nn = netdev_priv(netdev); + struct nfp_net_ring_set rx = { + .mtu = new_mtu, + .dcnt = nn->rxd_cnt, + }; + + return nfp_net_ring_reconfig(nn, &rx, NULL); } static struct rtnl_link_stats64 *nfp_net_stat64(struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index a7386d1..3f48256 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -158,6 +158,25 @@ static void nfp_net_get_ringparam(struct net_device *netdev, ring->tx_pending = nn->txd_cnt; } +static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) +{ + struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; + struct nfp_net_ring_set rx = { + .mtu = nn->netdev->mtu, + .dcnt = rxd_cnt, + }; + struct nfp_net_ring_set tx = { + .dcnt = txd_cnt, + }; + + if (nn->rxd_cnt != rxd_cnt) + reconfig_rx = ℞ + if (nn->txd_cnt != txd_cnt) + reconfig_tx = &tx; + + return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx); +} + static int nfp_net_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { -- cgit v1.1 From 0ae42dfcf249a6b6535d37e119e06efb31eef979 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:11:59 +0000 Subject: nfp: rename ring allocation helpers "Shadow" in ring helpers used to mean that the helper will allocate rings without touching existing configuration, this was used for reconfiguration while the device was running. We will soon use the same helpers for .ndo_open() path, so replace "shadow" with "ring_set". No functional changes. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index e58532d..b7b2851 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1573,7 +1573,7 @@ err_alloc: } static struct nfp_net_tx_ring * -nfp_net_shadow_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_tx_ring *rings; unsigned int r; @@ -1599,7 +1599,7 @@ err_free_prev: } static void -nfp_net_shadow_tx_rings_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_tx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_tx_ring *rings = s->rings; struct nfp_net_ring_set new = *s; @@ -1616,7 +1616,7 @@ nfp_net_shadow_tx_rings_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) } static void -nfp_net_shadow_tx_rings_free(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_tx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_tx_ring *rings = s->rings; unsigned int r; @@ -1693,7 +1693,7 @@ err_alloc: } static struct nfp_net_rx_ring * -nfp_net_shadow_rx_rings_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) { unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(nn, s->mtu); struct nfp_net_rx_ring *rings; @@ -1726,7 +1726,7 @@ err_free_ring: } static void -nfp_net_shadow_rx_rings_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_rx_ring *rings = s->rings; struct nfp_net_ring_set new = *s; @@ -1746,7 +1746,7 @@ nfp_net_shadow_rx_rings_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) } static void -nfp_net_shadow_rx_rings_free(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_rx_ring *rings = s->rings; unsigned int r; @@ -2268,9 +2268,9 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_ring_set *tx) { if (rx) - nfp_net_shadow_rx_rings_swap(nn, rx); + nfp_net_rx_ring_set_swap(nn, rx); if (tx) - nfp_net_shadow_tx_rings_swap(nn, tx); + nfp_net_tx_ring_set_swap(nn, tx); return __nfp_net_set_config_and_enable(nn); } @@ -2299,11 +2299,11 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, /* Prepare new rings */ if (rx) { - if (!nfp_net_shadow_rx_rings_prepare(nn, rx)) + if (!nfp_net_rx_ring_set_prepare(nn, rx)) return -ENOMEM; } if (tx) { - if (!nfp_net_shadow_tx_rings_prepare(nn, tx)) { + if (!nfp_net_tx_ring_set_prepare(nn, tx)) { err = -ENOMEM; goto err_free_rx; } @@ -2327,9 +2327,9 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, } if (rx) - nfp_net_shadow_rx_rings_free(nn, rx); + nfp_net_rx_ring_set_free(nn, rx); if (tx) - nfp_net_shadow_tx_rings_free(nn, tx); + nfp_net_tx_ring_set_free(nn, tx); nfp_net_open_stack(nn); @@ -2337,7 +2337,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, err_free_rx: if (rx) - nfp_net_shadow_rx_rings_free(nn, rx); + nfp_net_rx_ring_set_free(nn, rx); return err; } -- cgit v1.1 From a10b563d8846c8171bacdcad0aa5078b6d06da33 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:00 +0000 Subject: nfp: reuse ring helpers on .ndo_open() path Ring allocation helpers encapsulate all ring allocation and initialization steps nicely. Reuse them on .ndo_open() path. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 60 ++++++++-------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index b7b2851..50aeaea 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2051,6 +2051,13 @@ static void nfp_net_open_stack(struct nfp_net *nn) static int nfp_net_netdev_open(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); + struct nfp_net_ring_set rx = { + .mtu = nn->netdev->mtu, + .dcnt = nn->rxd_cnt, + }; + struct nfp_net_ring_set tx = { + .dcnt = nn->txd_cnt, + }; int err, r; if (nn->ctrl & NFP_NET_CFG_CTRL_ENABLE) { @@ -2075,38 +2082,22 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_free_exn; disable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector); - nn->rx_rings = kcalloc(nn->num_rx_rings, sizeof(*nn->rx_rings), - GFP_KERNEL); - if (!nn->rx_rings) { - err = -ENOMEM; - goto err_free_lsc; - } - nn->tx_rings = kcalloc(nn->num_tx_rings, sizeof(*nn->tx_rings), - GFP_KERNEL); - if (!nn->tx_rings) { - err = -ENOMEM; - goto err_free_rx_rings; - } - for (r = 0; r < nn->num_r_vecs; r++) { err = nfp_net_prepare_vector(nn, &nn->r_vecs[r], r); if (err) goto err_cleanup_vec_p; } - for (r = 0; r < nn->num_tx_rings; r++) { - err = nfp_net_tx_ring_alloc(nn->r_vecs[r].tx_ring, nn->txd_cnt); - if (err) - goto err_free_tx_ring_p; + + nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx); + if (!nn->rx_rings) { + err = -ENOMEM; + goto err_cleanup_vec; } - for (r = 0; r < nn->num_rx_rings; r++) { - err = nfp_net_rx_ring_alloc(nn->r_vecs[r].rx_ring, - nn->fl_bufsz, nn->rxd_cnt); - if (err) - goto err_flush_free_rx_ring_p; - err = nfp_net_rx_ring_bufs_alloc(nn, nn->r_vecs[r].rx_ring); - if (err) - goto err_free_rx_ring_p; + nn->tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx); + if (!nn->tx_rings) { + err = -ENOMEM; + goto err_free_rx_rings; } err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings); @@ -2139,25 +2130,14 @@ static int nfp_net_netdev_open(struct net_device *netdev) return 0; err_free_rings: - r = nn->num_rx_rings; -err_flush_free_rx_ring_p: - while (r--) { - nfp_net_rx_ring_bufs_free(nn, nn->r_vecs[r].rx_ring); -err_free_rx_ring_p: - nfp_net_rx_ring_free(nn->r_vecs[r].rx_ring); - } - r = nn->num_tx_rings; -err_free_tx_ring_p: - while (r--) - nfp_net_tx_ring_free(nn->r_vecs[r].tx_ring); + nfp_net_tx_ring_set_free(nn, &tx); +err_free_rx_rings: + nfp_net_rx_ring_set_free(nn, &rx); +err_cleanup_vec: r = nn->num_r_vecs; err_cleanup_vec_p: while (r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); - kfree(nn->tx_rings); -err_free_rx_rings: - kfree(nn->rx_rings); -err_free_lsc: nfp_net_aux_irq_free(nn, NFP_NET_CFG_LSC, NFP_NET_IRQ_LSC_IDX); err_free_exn: nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX); -- cgit v1.1 From 0668b60ba60228cb0fa21bc69baa6269edf7b6d3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:01 +0000 Subject: nfp: loosen relation between rings and IRQs vectors Upcoming XDP support will break the assumption that one can iterate over IRQ vectors to get to all the rings easily. Use nn->.x_ring arrays directly. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 50aeaea..97cc21e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1920,9 +1920,9 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn) nn_err(nn, "Could not disable device: %d\n", err); for (r = 0; r < nn->num_rx_rings; r++) - nfp_net_rx_ring_reset(nn->r_vecs[r].rx_ring); + nfp_net_rx_ring_reset(&nn->rx_rings[r]); for (r = 0; r < nn->num_tx_rings; r++) - nfp_net_tx_ring_reset(nn, nn->r_vecs[r].tx_ring); + nfp_net_tx_ring_reset(nn, &nn->tx_rings[r]); for (r = 0; r < nn->num_r_vecs; r++) nfp_net_vec_clear_ring_data(nn, r); @@ -2000,7 +2000,7 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) nn->ctrl = new_ctrl; for (r = 0; r < nn->num_rx_rings; r++) - nfp_net_rx_ring_fill_freelist(nn->r_vecs[r].rx_ring); + nfp_net_rx_ring_fill_freelist(&nn->rx_rings[r]); /* Since reconfiguration requests while NFP is down are ignored we * have to wipe the entire VXLAN configuration and reinitialize it. @@ -2173,11 +2173,11 @@ static void nfp_net_close_free_all(struct nfp_net *nn) unsigned int r; for (r = 0; r < nn->num_rx_rings; r++) { - nfp_net_rx_ring_bufs_free(nn, nn->r_vecs[r].rx_ring); - nfp_net_rx_ring_free(nn->r_vecs[r].rx_ring); + nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r]); + nfp_net_rx_ring_free(&nn->rx_rings[r]); } for (r = 0; r < nn->num_tx_rings; r++) - nfp_net_tx_ring_free(nn->r_vecs[r].tx_ring); + nfp_net_tx_ring_free(&nn->tx_rings[r]); for (r = 0; r < nn->num_r_vecs; r++) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); -- cgit v1.1 From e31230f9260f7bcf069e3962111f7e4656a1bfd4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:02 +0000 Subject: nfp: add helper to reassign rings to IRQ vectors Instead of fixing ring -> vector relations up in ring swap functions put the reassignment into a helper function which will reinit all links. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 40 +++++++++------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 97cc21e..2a4e1f1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1601,16 +1601,11 @@ err_free_prev: static void nfp_net_tx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { - struct nfp_net_tx_ring *rings = s->rings; struct nfp_net_ring_set new = *s; - unsigned int r; s->dcnt = nn->txd_cnt; s->rings = nn->tx_rings; - for (r = 0; r < nn->num_tx_rings; r++) - nn->tx_rings[r].r_vec->tx_ring = &rings[r]; - nn->txd_cnt = new.dcnt; nn->tx_rings = new.rings; } @@ -1728,17 +1723,12 @@ err_free_ring: static void nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { - struct nfp_net_rx_ring *rings = s->rings; struct nfp_net_ring_set new = *s; - unsigned int r; s->mtu = nn->netdev->mtu; s->dcnt = nn->rxd_cnt; s->rings = nn->rx_rings; - for (r = 0; r < nn->num_rx_rings; r++) - nn->rx_rings[r].r_vec->rx_ring = &rings[r]; - nn->netdev->mtu = new.mtu; nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, new.mtu); nn->rxd_cnt = new.dcnt; @@ -1759,6 +1749,14 @@ nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) kfree(rings); } +static void +nfp_net_vector_assign_rings(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + int idx) +{ + r_vec->rx_ring = idx < nn->num_rx_rings ? &nn->rx_rings[idx] : NULL; + r_vec->tx_ring = idx < nn->num_tx_rings ? &nn->tx_rings[idx] : NULL; +} + static int nfp_net_prepare_vector(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, int idx) @@ -1766,20 +1764,6 @@ nfp_net_prepare_vector(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, struct msix_entry *entry = &nn->irq_entries[r_vec->irq_idx]; int err; - if (idx < nn->num_tx_rings) { - r_vec->tx_ring = &nn->tx_rings[idx]; - nfp_net_tx_ring_init(r_vec->tx_ring, r_vec, idx); - } else { - r_vec->tx_ring = NULL; - } - - if (idx < nn->num_rx_rings) { - r_vec->rx_ring = &nn->rx_rings[idx]; - nfp_net_rx_ring_init(r_vec->rx_ring, r_vec, idx); - } else { - r_vec->rx_ring = NULL; - } - snprintf(r_vec->name, sizeof(r_vec->name), "%s-rxtx-%d", nn->netdev->name, idx); err = request_irq(entry->vector, r_vec->handler, 0, r_vec->name, r_vec); @@ -2100,6 +2084,9 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_free_rx_rings; } + for (r = 0; r < nn->max_r_vecs; r++) + nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); + err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings); if (err) goto err_free_rings; @@ -2247,11 +2234,16 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { + unsigned int r; + if (rx) nfp_net_rx_ring_set_swap(nn, rx); if (tx) nfp_net_tx_ring_set_swap(nn, tx); + for (r = 0; r < nn->max_r_vecs; r++) + nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); + return __nfp_net_set_config_and_enable(nn); } -- cgit v1.1 From 1e9e10d0c1365092972552d8c8f9d97517bcf581 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:03 +0000 Subject: nfp: move RSS indirection table init into a separate function We will need to rerun the initialization of the RSS indirection table after the number of rings is changed. Move the code to a separate function. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 2a4e1f1..09cec6e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2229,6 +2229,15 @@ static void nfp_net_set_rx_mode(struct net_device *netdev) nn->ctrl = new_ctrl; } +static void nfp_net_rss_init_itbl(struct nfp_net *nn) +{ + int i; + + for (i = 0; i < sizeof(nn->rss_itbl); i++) + nn->rss_itbl[i] = + ethtool_rxfh_indir_default(i, nn->num_rx_rings); +} + static int nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_ring_set *rx, @@ -2707,13 +2716,9 @@ void nfp_net_netdev_free(struct nfp_net *nn) */ static void nfp_net_rss_init(struct nfp_net *nn) { - int i; - netdev_rss_key_fill(nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ); - for (i = 0; i < sizeof(nn->rss_itbl); i++) - nn->rss_itbl[i] = - ethtool_rxfh_indir_default(i, nn->num_rx_rings); + nfp_net_rss_init_itbl(nn); /* Enable IPv4/IPv6 TCP by default */ nn->rss_cfg = NFP_NET_CFG_RSS_IPV4_TCP | -- cgit v1.1 From 164d1e9e5d5235c44851e606d01dd699d8bb15d3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:04 +0000 Subject: nfp: add support for ethtool .set_channels Allow changing the number of rings via ethtool .set_channels API. Runtime reconfig needs to be extended to handle number of rings. We need to be able to activate interrupt vectors before rings are assigned to them. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 1 + .../net/ethernet/netronome/nfp/nfp_net_common.c | 93 +++++++++++++++++----- .../net/ethernet/netronome/nfp/nfp_net_debugfs.c | 4 +- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 47 +++++++++++ 4 files changed, 123 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 14b5e21..486e7c6 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -584,6 +584,7 @@ struct nfp_net { }; struct nfp_net_ring_set { + unsigned int n_rings; unsigned int mtu; unsigned int dcnt; void *rings; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 09cec6e..5063627 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -494,7 +494,7 @@ static void nfp_net_irqs_assign(struct net_device *netdev) nn->lsc_handler = nfp_net_irq_lsc; nn->exn_handler = nfp_net_irq_exn; - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->max_r_vecs; r++) { r_vec = &nn->r_vecs[r]; r_vec->nfp_net = nn; r_vec->handler = nfp_net_irq_rxtx; @@ -1578,12 +1578,12 @@ nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) struct nfp_net_tx_ring *rings; unsigned int r; - rings = kcalloc(nn->num_tx_rings, sizeof(*rings), GFP_KERNEL); + rings = kcalloc(s->n_rings, sizeof(*rings), GFP_KERNEL); if (!rings) return NULL; - for (r = 0; r < nn->num_tx_rings; r++) { - nfp_net_tx_ring_init(&rings[r], nn->tx_rings[r].r_vec, r); + for (r = 0; r < s->n_rings; r++) { + nfp_net_tx_ring_init(&rings[r], &nn->r_vecs[r], r); if (nfp_net_tx_ring_alloc(&rings[r], s->dcnt)) goto err_free_prev; @@ -1605,9 +1605,11 @@ nfp_net_tx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) s->dcnt = nn->txd_cnt; s->rings = nn->tx_rings; + s->n_rings = nn->num_tx_rings; nn->txd_cnt = new.dcnt; nn->tx_rings = new.rings; + nn->num_tx_rings = new.n_rings; } static void @@ -1616,7 +1618,7 @@ nfp_net_tx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) struct nfp_net_tx_ring *rings = s->rings; unsigned int r; - for (r = 0; r < nn->num_tx_rings; r++) + for (r = 0; r < s->n_rings; r++) nfp_net_tx_ring_free(&rings[r]); kfree(rings); @@ -1694,12 +1696,12 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) struct nfp_net_rx_ring *rings; unsigned int r; - rings = kcalloc(nn->num_rx_rings, sizeof(*rings), GFP_KERNEL); + rings = kcalloc(s->n_rings, sizeof(*rings), GFP_KERNEL); if (!rings) return NULL; - for (r = 0; r < nn->num_rx_rings; r++) { - nfp_net_rx_ring_init(&rings[r], nn->rx_rings[r].r_vec, r); + for (r = 0; r < s->n_rings; r++) { + nfp_net_rx_ring_init(&rings[r], &nn->r_vecs[r], r); if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt)) goto err_free_prev; @@ -1728,11 +1730,13 @@ nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) s->mtu = nn->netdev->mtu; s->dcnt = nn->rxd_cnt; s->rings = nn->rx_rings; + s->n_rings = nn->num_rx_rings; nn->netdev->mtu = new.mtu; nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, new.mtu); nn->rxd_cnt = new.dcnt; nn->rx_rings = new.rings; + nn->num_rx_rings = new.n_rings; } static void @@ -1741,7 +1745,7 @@ nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) struct nfp_net_rx_ring *rings = s->rings; unsigned int r; - for (r = 0; r < nn->num_rx_rings; r++) { + for (r = 0; r < s->n_rings; r++) { nfp_net_rx_ring_bufs_free(nn, &rings[r]); nfp_net_rx_ring_free(&rings[r]); } @@ -1764,19 +1768,20 @@ nfp_net_prepare_vector(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, struct msix_entry *entry = &nn->irq_entries[r_vec->irq_idx]; int err; + /* Setup NAPI */ + netif_napi_add(nn->netdev, &r_vec->napi, + nfp_net_poll, NAPI_POLL_WEIGHT); + snprintf(r_vec->name, sizeof(r_vec->name), "%s-rxtx-%d", nn->netdev->name, idx); err = request_irq(entry->vector, r_vec->handler, 0, r_vec->name, r_vec); if (err) { + netif_napi_del(&r_vec->napi); nn_err(nn, "Error requesting IRQ %d\n", entry->vector); return err; } disable_irq(entry->vector); - /* Setup NAPI */ - netif_napi_add(nn->netdev, &r_vec->napi, - nfp_net_poll, NAPI_POLL_WEIGHT); - irq_set_affinity_hint(entry->vector, &r_vec->affinity_mask); nn_dbg(nn, "RV%02d: irq=%03d/%03d\n", idx, entry->vector, entry->entry); @@ -2036,10 +2041,12 @@ static int nfp_net_netdev_open(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_ring_set rx = { + .n_rings = nn->num_rx_rings, .mtu = nn->netdev->mtu, .dcnt = nn->rxd_cnt, }; struct nfp_net_ring_set tx = { + .n_rings = nn->num_tx_rings, .dcnt = nn->txd_cnt, }; int err, r; @@ -2239,49 +2246,89 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn) } static int -nfp_net_ring_swap_enable(struct nfp_net *nn, +nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { unsigned int r; + int err; if (rx) nfp_net_rx_ring_set_swap(nn, rx); if (tx) nfp_net_tx_ring_set_swap(nn, tx); + swap(*num_vecs, nn->num_r_vecs); + for (r = 0; r < nn->max_r_vecs; r++) nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); + if (nn->netdev->real_num_rx_queues != nn->num_rx_rings) { + if (!netif_is_rxfh_configured(nn->netdev)) + nfp_net_rss_init_itbl(nn); + + err = netif_set_real_num_rx_queues(nn->netdev, + nn->num_rx_rings); + if (err) + return err; + } + + if (nn->netdev->real_num_tx_queues != nn->num_tx_rings) { + err = netif_set_real_num_tx_queues(nn->netdev, + nn->num_tx_rings); + if (err) + return err; + } + return __nfp_net_set_config_and_enable(nn); } static void nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_ring_set *rx, - struct nfp_net_ring_set *tx) + struct nfp_net_ring_set *tx, + unsigned int num_vecs) { nn->netdev->mtu = rx ? rx->mtu : nn->netdev->mtu; nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, nn->netdev->mtu); nn->rxd_cnt = rx ? rx->dcnt : nn->rxd_cnt; nn->txd_cnt = tx ? tx->dcnt : nn->txd_cnt; + nn->num_rx_rings = rx ? rx->n_rings : nn->num_rx_rings; + nn->num_tx_rings = tx ? tx->n_rings : nn->num_tx_rings; + nn->num_r_vecs = num_vecs; + + if (!netif_is_rxfh_configured(nn->netdev)) + nfp_net_rss_init_itbl(nn); } int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { + unsigned int num_vecs, r; int err; + num_vecs = max(rx ? rx->n_rings : nn->num_rx_rings, + tx ? tx->n_rings : nn->num_tx_rings); + if (!netif_running(nn->netdev)) { - nfp_net_ring_reconfig_down(nn, rx, tx); + nfp_net_ring_reconfig_down(nn, rx, tx, num_vecs); return 0; } /* Prepare new rings */ + for (r = nn->num_r_vecs; r < num_vecs; r++) { + err = nfp_net_prepare_vector(nn, &nn->r_vecs[r], r); + if (err) { + num_vecs = r; + goto err_cleanup_vecs; + } + } if (rx) { - if (!nfp_net_rx_ring_set_prepare(nn, rx)) - return -ENOMEM; + if (!nfp_net_rx_ring_set_prepare(nn, rx)) { + err = -ENOMEM; + goto err_cleanup_vecs; + } } if (tx) { if (!nfp_net_tx_ring_set_prepare(nn, tx)) { @@ -2294,18 +2341,20 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, nfp_net_close_stack(nn); nfp_net_clear_config_and_disable(nn); - err = nfp_net_ring_swap_enable(nn, rx, tx); + err = nfp_net_ring_swap_enable(nn, &num_vecs, rx, tx); if (err) { int err2; nfp_net_clear_config_and_disable(nn); /* Try with old configuration and old rings */ - err2 = nfp_net_ring_swap_enable(nn, rx, tx); + err2 = nfp_net_ring_swap_enable(nn, &num_vecs, rx, tx); if (err2) nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", err, err2); } + for (r = num_vecs - 1; r >= nn->num_r_vecs; r--) + nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); if (rx) nfp_net_rx_ring_set_free(nn, rx); @@ -2319,6 +2368,9 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, err_free_rx: if (rx) nfp_net_rx_ring_set_free(nn, rx); +err_cleanup_vecs: + for (r = num_vecs - 1; r >= nn->num_r_vecs; r--) + nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); return err; } @@ -2326,6 +2378,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) { struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_ring_set rx = { + .n_rings = nn->num_rx_rings, .mtu = new_mtu, .dcnt = nn->rxd_cnt, }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index 180cf70..e90df81 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -207,13 +207,13 @@ void nfp_net_debugfs_adapter_add(struct nfp_net *nn) if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx)) return; - for (i = 0; i < nn->num_rx_rings; i++) { + for (i = 0; i < min(nn->max_rx_rings, nn->max_r_vecs); i++) { sprintf(int_name, "%d", i); debugfs_create_file(int_name, S_IRUSR, rx, &nn->r_vecs[i], &nfp_rx_q_fops); } - for (i = 0; i < nn->num_tx_rings; i++) { + for (i = 0; i < min(nn->max_tx_rings, nn->max_r_vecs); i++) { sprintf(int_name, "%d", i); debugfs_create_file(int_name, S_IRUSR, tx, &nn->r_vecs[i], &nfp_tx_q_fops); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 3f48256..b87f1b7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -162,10 +162,12 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) { struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; struct nfp_net_ring_set rx = { + .n_rings = nn->num_rx_rings, .mtu = nn->netdev->mtu, .dcnt = rxd_cnt, }; struct nfp_net_ring_set tx = { + .n_rings = nn->num_tx_rings, .dcnt = txd_cnt, }; @@ -648,6 +650,50 @@ static void nfp_net_get_channels(struct net_device *netdev, channel->other_count = NFP_NET_NON_Q_VECTORS; } +static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, + unsigned int total_tx) +{ + struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; + struct nfp_net_ring_set rx = { + .n_rings = total_rx, + .mtu = nn->netdev->mtu, + .dcnt = nn->rxd_cnt, + }; + struct nfp_net_ring_set tx = { + .n_rings = total_tx, + .dcnt = nn->txd_cnt, + }; + + if (nn->num_rx_rings != total_rx) + reconfig_rx = ℞ + if (nn->num_tx_rings != total_tx) + reconfig_tx = &tx; + + return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx); +} + +static int nfp_net_set_channels(struct net_device *netdev, + struct ethtool_channels *channel) +{ + struct nfp_net *nn = netdev_priv(netdev); + unsigned int total_rx, total_tx; + + /* Reject unsupported */ + if (!channel->combined_count || + channel->other_count != NFP_NET_NON_Q_VECTORS || + (channel->rx_count && channel->tx_count)) + return -EINVAL; + + total_rx = channel->combined_count + channel->rx_count; + total_tx = channel->combined_count + channel->tx_count; + + if (total_rx > min(nn->max_rx_rings, nn->max_r_vecs) || + total_tx > min(nn->max_tx_rings, nn->max_r_vecs)) + return -EINVAL; + + return nfp_net_set_num_rings(nn, total_rx, total_tx); +} + static const struct ethtool_ops nfp_net_ethtool_ops = { .get_drvinfo = nfp_net_get_drvinfo, .get_link = ethtool_op_get_link, @@ -667,6 +713,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .get_coalesce = nfp_net_get_coalesce, .set_coalesce = nfp_net_set_coalesce, .get_channels = nfp_net_get_channels, + .set_channels = nfp_net_set_channels, }; void nfp_net_set_ethtool_ops(struct net_device *netdev) -- cgit v1.1 From 13df4c666c551bba97618d7e3908c2ecd03ceefb Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:05 +0000 Subject: nfp: reorganize nfp_net_rx() to get packet offsets early Calculate packet offsets early in nfp_net_rx() so that we will be able to use them in upcoming XDP handler. While at it move relevant variables into the loop scope. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 56 ++++++++++++---------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 5063627..2ab6366 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1383,16 +1383,17 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; - unsigned int data_len, meta_len; - struct nfp_net_rx_buf *rxbuf; - struct nfp_net_rx_desc *rxd; - dma_addr_t new_dma_addr; struct sk_buff *skb; int pkts_polled = 0; - void *new_frag; int idx; while (pkts_polled < budget) { + unsigned int meta_len, data_len, data_off, pkt_len, pkt_off; + struct nfp_net_rx_buf *rxbuf; + struct nfp_net_rx_desc *rxd; + dma_addr_t new_dma_addr; + void *new_frag; + idx = rx_ring->rd_p & (rx_ring->cnt - 1); rxd = &rx_ring->rxds[idx]; @@ -1408,22 +1409,6 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) pkts_polled++; rxbuf = &rx_ring->rxbufs[idx]; - skb = build_skb(rxbuf->frag, nn->fl_bufsz); - if (unlikely(!skb)) { - nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); - continue; - } - new_frag = nfp_net_napi_alloc_one(nn, &new_dma_addr); - if (unlikely(!new_frag)) { - nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); - continue; - } - - nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[idx].dma_addr, - nn->fl_bufsz, DMA_FROM_DEVICE); - - nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); - /* < meta_len > * <-- [rx_offset] --> * --------------------------------------------------------- @@ -1438,20 +1423,39 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) */ meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; data_len = le16_to_cpu(rxd->rxd.data_len); + pkt_len = data_len - meta_len; if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) - skb_reserve(skb, NFP_NET_RX_BUF_HEADROOM + meta_len); + pkt_off = meta_len; else - skb_reserve(skb, - NFP_NET_RX_BUF_HEADROOM + nn->rx_offset); - skb_put(skb, data_len - meta_len); + pkt_off = nn->rx_offset; + data_off = NFP_NET_RX_BUF_HEADROOM + pkt_off; /* Stats update */ u64_stats_update_begin(&r_vec->rx_sync); r_vec->rx_pkts++; - r_vec->rx_bytes += skb->len; + r_vec->rx_bytes += pkt_len; u64_stats_update_end(&r_vec->rx_sync); + skb = build_skb(rxbuf->frag, nn->fl_bufsz); + if (unlikely(!skb)) { + nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); + continue; + } + new_frag = nfp_net_napi_alloc_one(nn, &new_dma_addr); + if (unlikely(!new_frag)) { + nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); + continue; + } + + nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[idx].dma_addr, + nn->fl_bufsz, DMA_FROM_DEVICE); + + nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); + + skb_reserve(skb, data_off); + skb_put(skb, pkt_len); + if (nn->fw_ver.major <= 3) { nfp_net_set_hash_desc(nn->netdev, skb, rxd); } else if (meta_len) { -- cgit v1.1 From 68f929ff2654bced015ccb9b5555667f46f88dfa Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:06 +0000 Subject: debugfs: constify argument to debugfs_real_fops() seq_file users can only access const version of file pointer, because the ->file member of struct seq_operations is marked as such. Make parameter to debugfs_real_fops() const. CC: Greg Kroah-Hartman CC: Nicolai Stange CC: Christian Lamparter CC: LKML Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/linux/debugfs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 4d3f0d1..bf1907d 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -52,7 +52,8 @@ extern struct srcu_struct debugfs_srcu; * Must only be called under the protection established by * debugfs_use_file_start(). */ -static inline const struct file_operations *debugfs_real_fops(struct file *filp) +static inline const struct file_operations * +debugfs_real_fops(const struct file *filp) __must_hold(&debugfs_srcu) { /* -- cgit v1.1 From ecd63a0217d5f1e8a92f7516f5586d1177b95de2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:07 +0000 Subject: nfp: add XDP support in the driver Add XDP support. Separate stack's and XDP's TX rings logically. Add functions for handling XDP_TX and cleanup of XDP's TX rings. For XDP allocate all RX buffers as separate pages and map them with DMA_BIDIRECTIONAL. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 17 +- .../net/ethernet/netronome/nfp/nfp_net_common.c | 474 +++++++++++++++++---- .../net/ethernet/netronome/nfp/nfp_net_debugfs.c | 37 +- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 22 +- 4 files changed, 449 insertions(+), 101 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 486e7c6..abc9e56 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -171,7 +171,10 @@ struct nfp_net_tx_desc { * on the head's buffer). Equal to skb->len for non-TSO packets. */ struct nfp_net_tx_buf { - struct sk_buff *skb; + union { + struct sk_buff *skb; + void *frag; + }; dma_addr_t dma_addr; short int fidx; u16 pkt_cnt; @@ -341,6 +344,7 @@ struct nfp_net_rx_ring { * @napi: NAPI structure for this ring vec * @tx_ring: Pointer to TX ring * @rx_ring: Pointer to RX ring + * @xdp_ring: Pointer to an extra TX ring for XDP * @irq_idx: Index into MSI-X table * @rx_sync: Seqlock for atomic updates of RX stats * @rx_pkts: Number of received packets @@ -384,6 +388,8 @@ struct nfp_net_r_vector { u64 hw_csum_rx_inner_ok; u64 hw_csum_rx_error; + struct nfp_net_tx_ring *xdp_ring; + struct u64_stats_sync tx_sync; u64 tx_pkts; u64 tx_bytes; @@ -432,6 +438,7 @@ struct nfp_stat_pair { * @ctrl: Local copy of the control register/word. * @fl_bufsz: Currently configured size of the freelist buffers * @rx_offset: Offset in the RX buffers where packet data starts + * @xdp_prog: Installed XDP program * @cpp: Pointer to the CPP handle * @nfp_dev_cpp: Pointer to the NFP Device handle * @ctrl_area: Pointer to the CPP area for the control BAR @@ -451,6 +458,7 @@ struct nfp_stat_pair { * @max_tx_rings: Maximum number of TX rings supported by the Firmware * @max_rx_rings: Maximum number of RX rings supported by the Firmware * @num_tx_rings: Currently configured number of TX rings + * @num_stack_tx_rings: Number of TX rings used by the stack (not XDP) * @num_rx_rings: Currently configured number of RX rings * @txd_cnt: Size of the TX ring in number of descriptors * @rxd_cnt: Size of the RX ring in number of descriptors @@ -500,6 +508,8 @@ struct nfp_net { u32 rx_offset; + struct bpf_prog *xdp_prog; + struct nfp_net_tx_ring *tx_rings; struct nfp_net_rx_ring *rx_rings; @@ -532,6 +542,7 @@ struct nfp_net { unsigned int max_rx_rings; unsigned int num_tx_rings; + unsigned int num_stack_tx_rings; unsigned int num_rx_rings; int stride_tx; @@ -779,8 +790,8 @@ void nfp_net_coalesce_write_cfg(struct nfp_net *nn); int nfp_net_irqs_alloc(struct nfp_net *nn); void nfp_net_irqs_disable(struct nfp_net *nn); int -nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, - struct nfp_net_ring_set *tx); +nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, + struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx); #ifdef CONFIG_NFP_NET_DEBUG void nfp_net_debugfs_create(void); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 2ab6366..fa43dbc 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -41,6 +41,7 @@ * Chris Telfer */ +#include #include #include #include @@ -490,6 +491,7 @@ static void nfp_net_irqs_assign(struct net_device *netdev) nn->num_rx_rings = min(nn->num_r_vecs, nn->num_rx_rings); nn->num_tx_rings = min(nn->num_r_vecs, nn->num_tx_rings); + nn->num_stack_tx_rings = nn->num_tx_rings; nn->lsc_handler = nfp_net_irq_lsc; nn->exn_handler = nfp_net_irq_exn; @@ -713,6 +715,13 @@ static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, u64_stats_update_end(&r_vec->tx_sync); } +static void nfp_net_tx_xmit_more_flush(struct nfp_net_tx_ring *tx_ring) +{ + wmb(); + nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add); + tx_ring->wr_ptr_add = 0; +} + /** * nfp_net_tx() - Main transmit entry point * @skb: SKB to transmit @@ -827,12 +836,8 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) nfp_net_tx_ring_stop(nd_q, tx_ring); tx_ring->wr_ptr_add += nr_frags + 1; - if (!skb->xmit_more || netif_xmit_stopped(nd_q)) { - /* force memory write before we let HW know */ - wmb(); - nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add); - tx_ring->wr_ptr_add = 0; - } + if (!skb->xmit_more || netif_xmit_stopped(nd_q)) + nfp_net_tx_xmit_more_flush(tx_ring); skb_tx_timestamp(skb); @@ -954,6 +959,56 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); } +static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + u32 done_pkts = 0, done_bytes = 0; + int idx, todo; + u32 qcp_rd_p; + + /* Work out how many descriptors have been transmitted */ + qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); + + if (qcp_rd_p == tx_ring->qcp_rd_p) + return; + + if (qcp_rd_p > tx_ring->qcp_rd_p) + todo = qcp_rd_p - tx_ring->qcp_rd_p; + else + todo = qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p; + + while (todo--) { + idx = tx_ring->rd_p & (tx_ring->cnt - 1); + tx_ring->rd_p++; + + if (!tx_ring->txbufs[idx].frag) + continue; + + nfp_net_dma_unmap_rx(nn, tx_ring->txbufs[idx].dma_addr, + nn->fl_bufsz, DMA_BIDIRECTIONAL); + __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); + + done_pkts++; + done_bytes += tx_ring->txbufs[idx].real_len; + + tx_ring->txbufs[idx].dma_addr = 0; + tx_ring->txbufs[idx].frag = NULL; + tx_ring->txbufs[idx].fidx = -2; + } + + tx_ring->qcp_rd_p = qcp_rd_p; + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_bytes += done_bytes; + r_vec->tx_pkts += done_pkts; + u64_stats_update_end(&r_vec->tx_sync); + + WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, + "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", + tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); +} + /** * nfp_net_tx_ring_reset() - Free any untransmitted buffers and reset pointers * @nn: NFP Net device @@ -964,39 +1019,47 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) static void nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) { + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; const struct skb_frag_struct *frag; - struct netdev_queue *nd_q; struct pci_dev *pdev = nn->pdev; + struct netdev_queue *nd_q; while (tx_ring->rd_p != tx_ring->wr_p) { - int nr_frags, fidx, idx; - struct sk_buff *skb; + struct nfp_net_tx_buf *tx_buf; + int idx; idx = tx_ring->rd_p & (tx_ring->cnt - 1); - skb = tx_ring->txbufs[idx].skb; - nr_frags = skb_shinfo(skb)->nr_frags; - fidx = tx_ring->txbufs[idx].fidx; + tx_buf = &tx_ring->txbufs[idx]; - if (fidx == -1) { - /* unmap head */ - dma_unmap_single(&pdev->dev, - tx_ring->txbufs[idx].dma_addr, - skb_headlen(skb), DMA_TO_DEVICE); + if (tx_ring == r_vec->xdp_ring) { + nfp_net_dma_unmap_rx(nn, tx_buf->dma_addr, + nn->fl_bufsz, DMA_BIDIRECTIONAL); + __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); } else { - /* unmap fragment */ - frag = &skb_shinfo(skb)->frags[fidx]; - dma_unmap_page(&pdev->dev, - tx_ring->txbufs[idx].dma_addr, - skb_frag_size(frag), DMA_TO_DEVICE); - } + struct sk_buff *skb = tx_ring->txbufs[idx].skb; + int nr_frags = skb_shinfo(skb)->nr_frags; + + if (tx_buf->fidx == -1) { + /* unmap head */ + dma_unmap_single(&pdev->dev, tx_buf->dma_addr, + skb_headlen(skb), + DMA_TO_DEVICE); + } else { + /* unmap fragment */ + frag = &skb_shinfo(skb)->frags[tx_buf->fidx]; + dma_unmap_page(&pdev->dev, tx_buf->dma_addr, + skb_frag_size(frag), + DMA_TO_DEVICE); + } - /* check for last gather fragment */ - if (fidx == nr_frags - 1) - dev_kfree_skb_any(skb); + /* check for last gather fragment */ + if (tx_buf->fidx == nr_frags - 1) + dev_kfree_skb_any(skb); + } - tx_ring->txbufs[idx].dma_addr = 0; - tx_ring->txbufs[idx].skb = NULL; - tx_ring->txbufs[idx].fidx = -2; + tx_buf->dma_addr = 0; + tx_buf->skb = NULL; + tx_buf->fidx = -2; tx_ring->qcp_rd_p++; tx_ring->rd_p++; @@ -1008,6 +1071,9 @@ nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) tx_ring->qcp_rd_p = 0; tx_ring->wr_ptr_add = 0; + if (tx_ring == r_vec->xdp_ring) + return; + nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx); netdev_tx_reset_queue(nd_q); } @@ -1017,7 +1083,7 @@ static void nfp_net_tx_timeout(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); int i; - for (i = 0; i < nn->num_tx_rings; i++) { + for (i = 0; i < nn->netdev->real_num_tx_queues; i++) { if (!netif_tx_queue_stopped(netdev_get_tx_queue(netdev, i))) continue; nn_warn(nn, "TX timeout on ring: %d\n", i); @@ -1045,11 +1111,21 @@ nfp_net_calc_fl_bufsz(struct nfp_net *nn, unsigned int mtu) return fl_bufsz; } +static void +nfp_net_free_frag(void *frag, bool xdp) +{ + if (!xdp) + skb_free_frag(frag); + else + __free_page(virt_to_page(frag)); +} + /** * nfp_net_rx_alloc_one() - Allocate and map page frag for RX * @rx_ring: RX ring structure of the skb * @dma_addr: Pointer to storage for DMA address (output param) * @fl_bufsz: size of freelist buffers + * @xdp: Whether XDP is enabled * * This function will allcate a new page frag, map it for DMA. * @@ -1057,20 +1133,26 @@ nfp_net_calc_fl_bufsz(struct nfp_net *nn, unsigned int mtu) */ static void * nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, - unsigned int fl_bufsz) + unsigned int fl_bufsz, bool xdp) { struct nfp_net *nn = rx_ring->r_vec->nfp_net; + int direction; void *frag; - frag = netdev_alloc_frag(fl_bufsz); + if (!xdp) + frag = netdev_alloc_frag(fl_bufsz); + else + frag = page_address(alloc_page(GFP_KERNEL | __GFP_COLD)); if (!frag) { nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n"); return NULL; } - *dma_addr = nfp_net_dma_map_rx(nn, frag, fl_bufsz, DMA_FROM_DEVICE); + direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; + + *dma_addr = nfp_net_dma_map_rx(nn, frag, fl_bufsz, direction); if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) { - skb_free_frag(frag); + nfp_net_free_frag(frag, xdp); nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); return NULL; } @@ -1078,19 +1160,23 @@ nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, return frag; } -static void *nfp_net_napi_alloc_one(struct nfp_net *nn, dma_addr_t *dma_addr) +static void * +nfp_net_napi_alloc_one(struct nfp_net *nn, int direction, dma_addr_t *dma_addr) { void *frag; - frag = napi_alloc_frag(nn->fl_bufsz); + if (!nn->xdp_prog) + frag = napi_alloc_frag(nn->fl_bufsz); + else + frag = page_address(alloc_page(GFP_ATOMIC | __GFP_COLD)); if (!frag) { nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n"); return NULL; } - *dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, DMA_FROM_DEVICE); + *dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, direction); if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) { - skb_free_frag(frag); + nfp_net_free_frag(frag, nn->xdp_prog); nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); return NULL; } @@ -1161,14 +1247,17 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring * @nn: NFP Net device * @rx_ring: RX ring to remove buffers from + * @xdp: Whether XDP is enabled * * Assumes that the device is stopped and buffers are in [0, ring->cnt - 1) * entries. After device is disabled nfp_net_rx_ring_reset() must be called * to restore required ring geometry. */ static void -nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) +nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, + bool xdp) { + int direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; unsigned int i; for (i = 0; i < rx_ring->cnt - 1; i++) { @@ -1180,8 +1269,8 @@ nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) continue; nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[i].dma_addr, - rx_ring->bufsz, DMA_FROM_DEVICE); - skb_free_frag(rx_ring->rxbufs[i].frag); + rx_ring->bufsz, direction); + nfp_net_free_frag(rx_ring->rxbufs[i].frag, xdp); rx_ring->rxbufs[i].dma_addr = 0; rx_ring->rxbufs[i].frag = NULL; } @@ -1191,9 +1280,11 @@ nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) * nfp_net_rx_ring_bufs_alloc() - Fill RX ring with buffers (don't give to FW) * @nn: NFP Net device * @rx_ring: RX ring to remove buffers from + * @xdp: Whether XDP is enabled */ static int -nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) +nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, + bool xdp) { struct nfp_net_rx_buf *rxbufs; unsigned int i; @@ -1203,9 +1294,9 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring) for (i = 0; i < rx_ring->cnt - 1; i++) { rxbufs[i].frag = nfp_net_rx_alloc_one(rx_ring, &rxbufs[i].dma_addr, - rx_ring->bufsz); + rx_ring->bufsz, xdp); if (!rxbufs[i].frag) { - nfp_net_rx_ring_bufs_free(nn, rx_ring); + nfp_net_rx_ring_bufs_free(nn, rx_ring, xdp); return -ENOMEM; } } @@ -1368,6 +1459,68 @@ nfp_net_rx_drop(struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, dev_kfree_skb_any(skb); } +static void +nfp_net_tx_xdp_buf(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, + struct nfp_net_tx_ring *tx_ring, + struct nfp_net_rx_buf *rxbuf, unsigned int pkt_off, + unsigned int pkt_len) +{ + struct nfp_net_tx_buf *txbuf; + struct nfp_net_tx_desc *txd; + dma_addr_t new_dma_addr; + void *new_frag; + int wr_idx; + + if (unlikely(nfp_net_tx_full(tx_ring, 1))) { + nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL); + return; + } + + new_frag = nfp_net_napi_alloc_one(nn, DMA_BIDIRECTIONAL, &new_dma_addr); + if (unlikely(!new_frag)) { + nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL); + return; + } + nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); + + wr_idx = tx_ring->wr_p & (tx_ring->cnt - 1); + + /* Stash the soft descriptor of the head then initialize it */ + txbuf = &tx_ring->txbufs[wr_idx]; + txbuf->frag = rxbuf->frag; + txbuf->dma_addr = rxbuf->dma_addr; + txbuf->fidx = -1; + txbuf->pkt_cnt = 1; + txbuf->real_len = pkt_len; + + dma_sync_single_for_device(&nn->pdev->dev, rxbuf->dma_addr + pkt_off, + pkt_len, DMA_TO_DEVICE); + + /* Build TX descriptor */ + txd = &tx_ring->txds[wr_idx]; + txd->offset_eop = PCIE_DESC_TX_EOP; + txd->dma_len = cpu_to_le16(pkt_len); + nfp_desc_set_dma_addr(txd, rxbuf->dma_addr + pkt_off); + txd->data_len = cpu_to_le16(pkt_len); + + txd->flags = 0; + txd->mss = 0; + txd->l4_offset = 0; + + tx_ring->wr_p++; + tx_ring->wr_ptr_add++; +} + +static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, unsigned int len) +{ + struct xdp_buff xdp; + + xdp.data = data; + xdp.data_end = data + len; + + return BPF_PROG_RUN(prog, (void *)&xdp); +} + /** * nfp_net_rx() - receive up to @budget packets on @rx_ring * @rx_ring: RX ring to receive from @@ -1383,10 +1536,20 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_tx_ring *tx_ring; + struct bpf_prog *xdp_prog; + unsigned int true_bufsz; struct sk_buff *skb; int pkts_polled = 0; + int rx_dma_map_dir; int idx; + rcu_read_lock(); + xdp_prog = READ_ONCE(nn->xdp_prog); + rx_dma_map_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; + true_bufsz = xdp_prog ? PAGE_SIZE : nn->fl_bufsz; + tx_ring = r_vec->xdp_ring; + while (pkts_polled < budget) { unsigned int meta_len, data_len, data_off, pkt_len, pkt_off; struct nfp_net_rx_buf *rxbuf; @@ -1437,19 +1600,45 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) r_vec->rx_bytes += pkt_len; u64_stats_update_end(&r_vec->rx_sync); - skb = build_skb(rxbuf->frag, nn->fl_bufsz); + if (xdp_prog) { + int act; + + dma_sync_single_for_cpu(&nn->pdev->dev, + rxbuf->dma_addr + pkt_off, + pkt_len, DMA_FROM_DEVICE); + act = nfp_net_run_xdp(xdp_prog, rxbuf->frag + data_off, + pkt_len); + switch (act) { + case XDP_PASS: + break; + case XDP_TX: + nfp_net_tx_xdp_buf(nn, rx_ring, tx_ring, rxbuf, + pkt_off, pkt_len); + continue; + default: + bpf_warn_invalid_xdp_action(act); + case XDP_ABORTED: + case XDP_DROP: + nfp_net_rx_give_one(rx_ring, rxbuf->frag, + rxbuf->dma_addr); + continue; + } + } + + skb = build_skb(rxbuf->frag, true_bufsz); if (unlikely(!skb)) { nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); continue; } - new_frag = nfp_net_napi_alloc_one(nn, &new_dma_addr); + new_frag = nfp_net_napi_alloc_one(nn, rx_dma_map_dir, + &new_dma_addr); if (unlikely(!new_frag)) { nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); continue; } - nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[idx].dma_addr, - nn->fl_bufsz, DMA_FROM_DEVICE); + nfp_net_dma_unmap_rx(nn, rxbuf->dma_addr, nn->fl_bufsz, + rx_dma_map_dir); nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); @@ -1481,6 +1670,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) napi_gro_receive(&rx_ring->r_vec->napi, skb); } + if (xdp_prog && tx_ring->wr_ptr_add) + nfp_net_tx_xmit_more_flush(tx_ring); + rcu_read_unlock(); + return pkts_polled; } @@ -1499,8 +1692,11 @@ static int nfp_net_poll(struct napi_struct *napi, int budget) if (r_vec->tx_ring) nfp_net_tx_complete(r_vec->tx_ring); - if (r_vec->rx_ring) + if (r_vec->rx_ring) { pkts_polled = nfp_net_rx(r_vec->rx_ring, budget); + if (r_vec->xdp_ring) + nfp_net_xdp_complete(r_vec->xdp_ring); + } if (pkts_polled < budget) { napi_complete_done(napi, pkts_polled); @@ -1540,10 +1736,12 @@ static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring * @tx_ring: TX Ring structure to allocate * @cnt: Ring buffer count + * @is_xdp: True if ring will be used for XDP * * Return: 0 on success, negative errno otherwise. */ -static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt) +static int +nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt, bool is_xdp) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; @@ -1563,11 +1761,14 @@ static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt) if (!tx_ring->txbufs) goto err_alloc; - netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, tx_ring->idx); + if (!is_xdp) + netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, + tx_ring->idx); - nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p\n", + nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p %s\n", tx_ring->idx, tx_ring->qcidx, - tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds); + tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds, + is_xdp ? "XDP" : ""); return 0; @@ -1577,7 +1778,8 @@ err_alloc: } static struct nfp_net_tx_ring * -nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, + unsigned int num_stack_tx_rings) { struct nfp_net_tx_ring *rings; unsigned int r; @@ -1587,9 +1789,14 @@ nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) return NULL; for (r = 0; r < s->n_rings; r++) { - nfp_net_tx_ring_init(&rings[r], &nn->r_vecs[r], r); + int bias = 0; + + if (r >= num_stack_tx_rings) + bias = num_stack_tx_rings; - if (nfp_net_tx_ring_alloc(&rings[r], s->dcnt)) + nfp_net_tx_ring_init(&rings[r], &nn->r_vecs[r - bias], r); + + if (nfp_net_tx_ring_alloc(&rings[r], s->dcnt, bias)) goto err_free_prev; } @@ -1694,7 +1901,8 @@ err_alloc: } static struct nfp_net_rx_ring * -nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, + bool xdp) { unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(nn, s->mtu); struct nfp_net_rx_ring *rings; @@ -1710,7 +1918,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt)) goto err_free_prev; - if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r])) + if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r], xdp)) goto err_free_ring; } @@ -1718,7 +1926,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s) err_free_prev: while (r--) { - nfp_net_rx_ring_bufs_free(nn, &rings[r]); + nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp); err_free_ring: nfp_net_rx_ring_free(&rings[r]); } @@ -1744,13 +1952,14 @@ nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) } static void -nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s, + bool xdp) { struct nfp_net_rx_ring *rings = s->rings; unsigned int r; for (r = 0; r < s->n_rings; r++) { - nfp_net_rx_ring_bufs_free(nn, &rings[r]); + nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp); nfp_net_rx_ring_free(&rings[r]); } @@ -1762,7 +1971,11 @@ nfp_net_vector_assign_rings(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, int idx) { r_vec->rx_ring = idx < nn->num_rx_rings ? &nn->rx_rings[idx] : NULL; - r_vec->tx_ring = idx < nn->num_tx_rings ? &nn->tx_rings[idx] : NULL; + r_vec->tx_ring = + idx < nn->num_stack_tx_rings ? &nn->tx_rings[idx] : NULL; + + r_vec->xdp_ring = idx < nn->num_tx_rings - nn->num_stack_tx_rings ? + &nn->tx_rings[nn->num_stack_tx_rings + idx] : NULL; } static int @@ -2083,13 +2296,14 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_cleanup_vec_p; } - nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx); + nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx, nn->xdp_prog); if (!nn->rx_rings) { err = -ENOMEM; goto err_cleanup_vec; } - nn->tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx); + nn->tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx, + nn->num_stack_tx_rings); if (!nn->tx_rings) { err = -ENOMEM; goto err_free_rx_rings; @@ -2098,7 +2312,7 @@ static int nfp_net_netdev_open(struct net_device *netdev) for (r = 0; r < nn->max_r_vecs; r++) nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); - err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings); + err = netif_set_real_num_tx_queues(netdev, nn->num_stack_tx_rings); if (err) goto err_free_rings; @@ -2130,7 +2344,7 @@ static int nfp_net_netdev_open(struct net_device *netdev) err_free_rings: nfp_net_tx_ring_set_free(nn, &tx); err_free_rx_rings: - nfp_net_rx_ring_set_free(nn, &rx); + nfp_net_rx_ring_set_free(nn, &rx, nn->xdp_prog); err_cleanup_vec: r = nn->num_r_vecs; err_cleanup_vec_p: @@ -2171,7 +2385,7 @@ static void nfp_net_close_free_all(struct nfp_net *nn) unsigned int r; for (r = 0; r < nn->num_rx_rings; r++) { - nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r]); + nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r], nn->xdp_prog); nfp_net_rx_ring_free(&nn->rx_rings[r]); } for (r = 0; r < nn->num_tx_rings; r++) @@ -2251,6 +2465,8 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn) static int nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, + unsigned int *stack_tx_rings, + struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { @@ -2263,6 +2479,8 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, nfp_net_tx_ring_set_swap(nn, tx); swap(*num_vecs, nn->num_r_vecs); + swap(*stack_tx_rings, nn->num_stack_tx_rings); + *xdp_prog = xchg(&nn->xdp_prog, *xdp_prog); for (r = 0; r < nn->max_r_vecs; r++) nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); @@ -2277,9 +2495,9 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, return err; } - if (nn->netdev->real_num_tx_queues != nn->num_tx_rings) { + if (nn->netdev->real_num_tx_queues != nn->num_stack_tx_rings) { err = netif_set_real_num_tx_queues(nn->netdev, - nn->num_tx_rings); + nn->num_stack_tx_rings); if (err) return err; } @@ -2287,11 +2505,30 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, return __nfp_net_set_config_and_enable(nn); } +static int +nfp_net_check_config(struct nfp_net *nn, struct bpf_prog *xdp_prog, + struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) +{ + /* XDP-enabled tests */ + if (!xdp_prog) + return 0; + if (rx && nfp_net_calc_fl_bufsz(nn, rx->mtu) > PAGE_SIZE) { + nn_warn(nn, "MTU too large w/ XDP enabled\n"); + return -EINVAL; + } + if (tx && tx->n_rings > nn->max_tx_rings) { + nn_warn(nn, "Insufficient number of TX rings w/ XDP enabled\n"); + return -EINVAL; + } + + return 0; +} + static void -nfp_net_ring_reconfig_down(struct nfp_net *nn, +nfp_net_ring_reconfig_down(struct nfp_net *nn, struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx, - unsigned int num_vecs) + unsigned int stack_tx_rings, unsigned int num_vecs) { nn->netdev->mtu = rx ? rx->mtu : nn->netdev->mtu; nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, nn->netdev->mtu); @@ -2299,24 +2536,34 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, nn->txd_cnt = tx ? tx->dcnt : nn->txd_cnt; nn->num_rx_rings = rx ? rx->n_rings : nn->num_rx_rings; nn->num_tx_rings = tx ? tx->n_rings : nn->num_tx_rings; + nn->num_stack_tx_rings = stack_tx_rings; nn->num_r_vecs = num_vecs; + *xdp_prog = xchg(&nn->xdp_prog, *xdp_prog); if (!netif_is_rxfh_configured(nn->netdev)) nfp_net_rss_init_itbl(nn); } int -nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, - struct nfp_net_ring_set *tx) +nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, + struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { - unsigned int num_vecs, r; + unsigned int stack_tx_rings, num_vecs, r; int err; - num_vecs = max(rx ? rx->n_rings : nn->num_rx_rings, - tx ? tx->n_rings : nn->num_tx_rings); + stack_tx_rings = tx ? tx->n_rings : nn->num_tx_rings; + if (*xdp_prog) + stack_tx_rings -= rx ? rx->n_rings : nn->num_rx_rings; + + num_vecs = max(rx ? rx->n_rings : nn->num_rx_rings, stack_tx_rings); + + err = nfp_net_check_config(nn, *xdp_prog, rx, tx); + if (err) + return err; if (!netif_running(nn->netdev)) { - nfp_net_ring_reconfig_down(nn, rx, tx, num_vecs); + nfp_net_ring_reconfig_down(nn, xdp_prog, rx, tx, + stack_tx_rings, num_vecs); return 0; } @@ -2329,13 +2576,13 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, } } if (rx) { - if (!nfp_net_rx_ring_set_prepare(nn, rx)) { + if (!nfp_net_rx_ring_set_prepare(nn, rx, *xdp_prog)) { err = -ENOMEM; goto err_cleanup_vecs; } } if (tx) { - if (!nfp_net_tx_ring_set_prepare(nn, tx)) { + if (!nfp_net_tx_ring_set_prepare(nn, tx, stack_tx_rings)) { err = -ENOMEM; goto err_free_rx; } @@ -2345,14 +2592,16 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, nfp_net_close_stack(nn); nfp_net_clear_config_and_disable(nn); - err = nfp_net_ring_swap_enable(nn, &num_vecs, rx, tx); + err = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings, + xdp_prog, rx, tx); if (err) { int err2; nfp_net_clear_config_and_disable(nn); /* Try with old configuration and old rings */ - err2 = nfp_net_ring_swap_enable(nn, &num_vecs, rx, tx); + err2 = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings, + xdp_prog, rx, tx); if (err2) nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", err, err2); @@ -2361,7 +2610,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); if (rx) - nfp_net_rx_ring_set_free(nn, rx); + nfp_net_rx_ring_set_free(nn, rx, *xdp_prog); if (tx) nfp_net_tx_ring_set_free(nn, tx); @@ -2371,7 +2620,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx, err_free_rx: if (rx) - nfp_net_rx_ring_set_free(nn, rx); + nfp_net_rx_ring_set_free(nn, rx, *xdp_prog); err_cleanup_vecs: for (r = num_vecs - 1; r >= nn->num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); @@ -2387,7 +2636,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) .dcnt = nn->rxd_cnt, }; - return nfp_net_ring_reconfig(nn, &rx, NULL); + return nfp_net_ring_reconfig(nn, &nn->xdp_prog, &rx, NULL); } static struct rtnl_link_stats64 *nfp_net_stat64(struct net_device *netdev, @@ -2653,6 +2902,56 @@ static void nfp_net_del_vxlan_port(struct net_device *netdev, nfp_net_set_vxlan_port(nn, idx, 0); } +static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) +{ + struct nfp_net_ring_set rx = { + .n_rings = nn->num_rx_rings, + .mtu = nn->netdev->mtu, + .dcnt = nn->rxd_cnt, + }; + struct nfp_net_ring_set tx = { + .n_rings = nn->num_tx_rings, + .dcnt = nn->txd_cnt, + }; + int err; + + if (!prog && !nn->xdp_prog) + return 0; + if (prog && nn->xdp_prog) { + prog = xchg(&nn->xdp_prog, prog); + bpf_prog_put(prog); + return 0; + } + + tx.n_rings += prog ? nn->num_rx_rings : -nn->num_rx_rings; + + /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ + err = nfp_net_ring_reconfig(nn, &prog, &rx, &tx); + if (err) + return err; + + /* @prog got swapped and is now the old one */ + if (prog) + bpf_prog_put(prog); + + return 0; +} + +static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp) +{ + struct nfp_net *nn = netdev_priv(netdev); + + switch (xdp->command) { + case XDP_SETUP_PROG: + return nfp_net_xdp_setup(nn, xdp->prog); + case XDP_QUERY_PROG: + xdp->prog_attached = !!nn->xdp_prog; + return 0; + default: + return -EINVAL; + } +} + static const struct net_device_ops nfp_net_netdev_ops = { .ndo_open = nfp_net_netdev_open, .ndo_stop = nfp_net_netdev_close, @@ -2667,6 +2966,7 @@ static const struct net_device_ops nfp_net_netdev_ops = { .ndo_features_check = nfp_net_features_check, .ndo_udp_tunnel_add = nfp_net_add_vxlan_port, .ndo_udp_tunnel_del = nfp_net_del_vxlan_port, + .ndo_xdp = nfp_net_xdp, }; /** @@ -2929,5 +3229,9 @@ int nfp_net_netdev_init(struct net_device *netdev) */ void nfp_net_netdev_clean(struct net_device *netdev) { - unregister_netdev(netdev); + struct nfp_net *nn = netdev_priv(netdev); + + if (nn->xdp_prog) + bpf_prog_put(nn->xdp_prog); + unregister_netdev(nn->netdev); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index e90df81..c66f3f9 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -114,6 +114,16 @@ static const struct file_operations nfp_rx_q_fops = { .llseek = seq_lseek }; +static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f); + +static const struct file_operations nfp_tx_q_fops = { + .owner = THIS_MODULE, + .open = nfp_net_debugfs_tx_q_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek +}; + static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) { struct nfp_net_r_vector *r_vec = file->private; @@ -126,10 +136,13 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) rtnl_lock(); - if (!r_vec->nfp_net || !r_vec->tx_ring) + if (debugfs_real_fops(file->file) == &nfp_tx_q_fops) + tx_ring = r_vec->tx_ring; + else + tx_ring = r_vec->xdp_ring; + if (!r_vec->nfp_net || !tx_ring) goto out; nn = r_vec->nfp_net; - tx_ring = r_vec->tx_ring; if (!netif_running(nn->netdev)) goto out; @@ -148,9 +161,14 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) txd->vals[2], txd->vals[3]); skb = READ_ONCE(tx_ring->txbufs[i].skb); - if (skb) - seq_printf(file, " skb->head=%p skb->data=%p", - skb->head, skb->data); + if (skb) { + if (tx_ring == r_vec->tx_ring) + seq_printf(file, " skb->head=%p skb->data=%p", + skb->head, skb->data); + else + seq_printf(file, " frag=%p", skb); + } + if (tx_ring->txbufs[i].dma_addr) seq_printf(file, " dma_addr=%pad", &tx_ring->txbufs[i].dma_addr); @@ -176,7 +194,7 @@ static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f) return single_open(f, nfp_net_debugfs_tx_q_read, inode->i_private); } -static const struct file_operations nfp_tx_q_fops = { +static const struct file_operations nfp_xdp_q_fops = { .owner = THIS_MODULE, .open = nfp_net_debugfs_tx_q_open, .release = single_release, @@ -186,7 +204,7 @@ static const struct file_operations nfp_tx_q_fops = { void nfp_net_debugfs_adapter_add(struct nfp_net *nn) { - struct dentry *queues, *tx, *rx; + struct dentry *queues, *tx, *rx, *xdp; char int_name[16]; int i; @@ -204,13 +222,16 @@ void nfp_net_debugfs_adapter_add(struct nfp_net *nn) rx = debugfs_create_dir("rx", queues); tx = debugfs_create_dir("tx", queues); - if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx)) + xdp = debugfs_create_dir("xdp", queues); + if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx) || IS_ERR_OR_NULL(xdp)) return; for (i = 0; i < min(nn->max_rx_rings, nn->max_r_vecs); i++) { sprintf(int_name, "%d", i); debugfs_create_file(int_name, S_IRUSR, rx, &nn->r_vecs[i], &nfp_rx_q_fops); + debugfs_create_file(int_name, S_IRUSR, xdp, + &nn->r_vecs[i], &nfp_xdp_q_fops); } for (i = 0; i < min(nn->max_tx_rings, nn->max_r_vecs); i++) { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index b87f1b7..1b26e96 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -176,7 +176,8 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) if (nn->txd_cnt != txd_cnt) reconfig_tx = &tx; - return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx); + return nfp_net_ring_reconfig(nn, &nn->xdp_prog, + reconfig_rx, reconfig_tx); } static int nfp_net_set_ringparam(struct net_device *netdev, @@ -639,14 +640,19 @@ static void nfp_net_get_channels(struct net_device *netdev, struct ethtool_channels *channel) { struct nfp_net *nn = netdev_priv(netdev); + unsigned int num_tx_rings; + + num_tx_rings = nn->num_tx_rings; + if (nn->xdp_prog) + num_tx_rings -= nn->num_rx_rings; channel->max_rx = min(nn->max_rx_rings, nn->max_r_vecs); channel->max_tx = min(nn->max_tx_rings, nn->max_r_vecs); channel->max_combined = min(channel->max_rx, channel->max_tx); channel->max_other = NFP_NET_NON_Q_VECTORS; - channel->combined_count = min(nn->num_rx_rings, nn->num_tx_rings); + channel->combined_count = min(nn->num_rx_rings, num_tx_rings); channel->rx_count = nn->num_rx_rings - channel->combined_count; - channel->tx_count = nn->num_tx_rings - channel->combined_count; + channel->tx_count = num_tx_rings - channel->combined_count; channel->other_count = NFP_NET_NON_Q_VECTORS; } @@ -666,10 +672,16 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, if (nn->num_rx_rings != total_rx) reconfig_rx = ℞ - if (nn->num_tx_rings != total_tx) + if (nn->num_stack_tx_rings != total_tx || + (nn->xdp_prog && reconfig_rx)) reconfig_tx = &tx; - return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx); + /* nfp_net_check_config() will catch tx.n_rings > nn->max_tx_rings */ + if (nn->xdp_prog) + tx.n_rings += total_rx; + + return nfp_net_ring_reconfig(nn, &nn->xdp_prog, + reconfig_rx, reconfig_tx); } static int nfp_net_set_channels(struct net_device *netdev, -- cgit v1.1 From 2e9d594d50652395f35744ea98624430c57aae2b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:08 +0000 Subject: nfp: remove unnecessary parameters from nfp_net_bpf_offload() nfp_net_bpf_offload() takes all .setup_tc() parameters but it doesn't use them at the moment. Remove unnecessary ones to make it possible for XDP to reuse this function. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 4 +--- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 2 +- drivers/net/ethernet/netronome/nfp/nfp_net_offload.c | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index abc9e56..fd29a63 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -817,8 +817,6 @@ static inline void nfp_net_debugfs_adapter_del(struct nfp_net *nn) #endif /* CONFIG_NFP_NET_DEBUG */ void nfp_net_filter_stats_timer(unsigned long data); -int -nfp_net_bpf_offload(struct nfp_net *nn, u32 handle, __be16 proto, - struct tc_cls_bpf_offload *cls_bpf); +int nfp_net_bpf_offload(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf); #endif /* _NFP_NET_H_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index fa43dbc..1e8e00d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2694,7 +2694,7 @@ nfp_net_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, return -ENOTSUPP; if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) - return nfp_net_bpf_offload(nn, handle, proto, tc->cls_bpf); + return nfp_net_bpf_offload(nn, tc->cls_bpf); return -EINVAL; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c index cfed40c..4bb6f16 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c @@ -233,9 +233,7 @@ static int nfp_net_bpf_stop(struct nfp_net *nn) return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN); } -int -nfp_net_bpf_offload(struct nfp_net *nn, u32 handle, __be16 proto, - struct tc_cls_bpf_offload *cls_bpf) +int nfp_net_bpf_offload(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) { struct nfp_bpf_result res; dma_addr_t dma_addr; -- cgit v1.1 From 6d6770755f053e0ab2f7c3ffcfeaaf8dbbe89092 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 3 Nov 2016 17:12:09 +0000 Subject: nfp: add support for offload of XDP programs Most infrastructure can be reused, provide separate handling of context offsets and exit codes. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_bpf.h | 1 + drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c | 92 +++++++++++++++++++++- .../net/ethernet/netronome/nfp/nfp_bpf_verifier.c | 3 + drivers/net/ethernet/netronome/nfp/nfp_net.h | 2 + .../net/ethernet/netronome/nfp/nfp_net_common.c | 44 ++++++++++- .../net/ethernet/netronome/nfp/nfp_net_offload.c | 3 + 6 files changed, 139 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf.h b/drivers/net/ethernet/netronome/nfp/nfp_bpf.h index 87aa8a3..76a19f1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_bpf.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf.h @@ -62,6 +62,7 @@ enum nfp_bpf_action_type { NN_ACT_TC_DROP, NN_ACT_TC_REDIR, NN_ACT_DIRECT, + NN_ACT_XDP, }; /* Software register representation, hardware encoding in asm.h */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c index f8df530..335beb8 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c @@ -1126,7 +1126,7 @@ static int data_ind_ld4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) meta->insn.src_reg * 2, true, 4); } -static int mem_ldx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +static int mem_ldx4_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { if (meta->insn.off == offsetof(struct sk_buff, len)) emit_alu(nfp_prog, reg_both(meta->insn.dst_reg * 2), @@ -1134,12 +1134,42 @@ static int mem_ldx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) else return -ENOTSUPP; - wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0); + return 0; +} + +static int mem_ldx4_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + u32 dst = reg_both(meta->insn.dst_reg * 2); + + if (meta->insn.off != offsetof(struct xdp_md, data) && + meta->insn.off != offsetof(struct xdp_md, data_end)) + return -ENOTSUPP; + + emit_alu(nfp_prog, dst, reg_none(), ALU_OP_NONE, NFP_BPF_ABI_PKT); + + if (meta->insn.off == offsetof(struct xdp_md, data)) + return 0; + + emit_alu(nfp_prog, dst, dst, ALU_OP_ADD, NFP_BPF_ABI_LEN); return 0; } -static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +static int mem_ldx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + int ret; + + if (nfp_prog->act == NN_ACT_XDP) + ret = mem_ldx4_xdp(nfp_prog, meta); + else + ret = mem_ldx4_skb(nfp_prog, meta); + + wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0); + + return ret; +} + +static int mem_stx4_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { if (meta->insn.off == offsetof(struct sk_buff, mark)) return wrp_set_mark(nfp_prog, meta->insn.src_reg * 2); @@ -1147,6 +1177,18 @@ static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) return -ENOTSUPP; } +static int mem_stx4_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return -ENOTSUPP; +} + +static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + if (nfp_prog->act == NN_ACT_XDP) + return mem_stx4_xdp(nfp_prog, meta); + return mem_stx4_skb(nfp_prog, meta); +} + static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { if (meta->insn.off < 0) /* TODO */ @@ -1530,6 +1572,47 @@ static void nfp_outro_tc_da(struct nfp_prog *nfp_prog) emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_b(2), SHF_SC_L_SHF, 16); } +static void nfp_outro_xdp(struct nfp_prog *nfp_prog) +{ + /* XDP return codes: + * 0 aborted 0x82 -> drop, count as stat3 + * 1 drop 0x22 -> drop, count as stat1 + * 2 pass 0x11 -> pass, count as stat0 + * 3 tx 0x44 -> redir, count as stat2 + * * unknown 0x82 -> drop, count as stat3 + */ + /* Target for aborts */ + nfp_prog->tgt_abort = nfp_prog_current_offset(nfp_prog); + + emit_br_def(nfp_prog, nfp_prog->tgt_done, 2); + + emit_alu(nfp_prog, reg_a(0), + reg_none(), ALU_OP_NONE, NFP_BPF_ABI_FLAGS); + emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_imm(0x82), SHF_SC_L_SHF, 16); + + /* Target for normal exits */ + nfp_prog->tgt_out = nfp_prog_current_offset(nfp_prog); + + /* if R0 > 3 jump to abort */ + emit_alu(nfp_prog, reg_none(), reg_imm(3), ALU_OP_SUB, reg_b(0)); + emit_br(nfp_prog, BR_BLO, nfp_prog->tgt_abort, 0); + + wrp_immed(nfp_prog, reg_b(2), 0x44112282); + + emit_shf(nfp_prog, reg_a(1), + reg_none(), SHF_OP_NONE, reg_b(0), SHF_SC_L_SHF, 3); + + emit_alu(nfp_prog, reg_none(), reg_a(1), ALU_OP_OR, reg_imm(0)); + emit_shf(nfp_prog, reg_b(2), + reg_imm(0xff), SHF_OP_AND, reg_b(2), SHF_SC_R_SHF, 0); + + emit_br_def(nfp_prog, nfp_prog->tgt_done, 2); + + emit_alu(nfp_prog, reg_a(0), + reg_none(), ALU_OP_NONE, NFP_BPF_ABI_FLAGS); + emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_b(2), SHF_SC_L_SHF, 16); +} + static void nfp_outro(struct nfp_prog *nfp_prog) { switch (nfp_prog->act) { @@ -1540,6 +1623,9 @@ static void nfp_outro(struct nfp_prog *nfp_prog) case NN_ACT_TC_REDIR: nfp_outro_tc_legacy(nfp_prog); break; + case NN_ACT_XDP: + nfp_outro_xdp(nfp_prog); + break; } } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c b/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c index 144cae8..b3361f9 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c @@ -80,6 +80,9 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog, { const struct bpf_reg_state *reg0 = &env->cur_state.regs[0]; + if (nfp_prog->act == NN_ACT_XDP) + return 0; + if (reg0->type != CONST_IMM) { pr_info("unsupported exit state: %d, imm: %llx\n", reg0->type, reg0->imm); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index fd29a63..2115f44 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -435,6 +435,7 @@ struct nfp_stat_pair { * @is_vf: Is the driver attached to a VF? * @fw_loaded: Is the firmware loaded? * @bpf_offload_skip_sw: Offloaded BPF program will not be rerun by cls_bpf + * @bpf_offload_xdp: Offloaded BPF program is XDP * @ctrl: Local copy of the control register/word. * @fl_bufsz: Currently configured size of the freelist buffers * @rx_offset: Offset in the RX buffers where packet data starts @@ -502,6 +503,7 @@ struct nfp_net { unsigned is_vf:1; unsigned fw_loaded:1; unsigned bpf_offload_skip_sw:1; + unsigned bpf_offload_xdp:1; u32 ctrl; u32 fl_bufsz; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 1e8e00d..99edb9f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1600,7 +1600,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) r_vec->rx_bytes += pkt_len; u64_stats_update_end(&r_vec->rx_sync); - if (xdp_prog) { + if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF && + nn->bpf_offload_xdp)) { int act; dma_sync_single_for_cpu(&nn->pdev->dev, @@ -2693,8 +2694,12 @@ nfp_net_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, if (proto != htons(ETH_P_ALL)) return -ENOTSUPP; - if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) - return nfp_net_bpf_offload(nn, tc->cls_bpf); + if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { + if (!nn->bpf_offload_xdp) + return nfp_net_bpf_offload(nn, tc->cls_bpf); + else + return -EBUSY; + } return -EINVAL; } @@ -2902,6 +2907,34 @@ static void nfp_net_del_vxlan_port(struct net_device *netdev, nfp_net_set_vxlan_port(nn, idx, 0); } +static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog) +{ + struct tc_cls_bpf_offload cmd = { + .prog = prog, + }; + int ret; + + if (!nfp_net_ebpf_capable(nn)) + return -EINVAL; + + if (nn->ctrl & NFP_NET_CFG_CTRL_BPF) { + if (!nn->bpf_offload_xdp) + return prog ? -EBUSY : 0; + cmd.command = prog ? TC_CLSBPF_REPLACE : TC_CLSBPF_DESTROY; + } else { + if (!prog) + return 0; + cmd.command = TC_CLSBPF_ADD; + } + + ret = nfp_net_bpf_offload(nn, &cmd); + /* Stop offload if replace not possible */ + if (ret && cmd.command == TC_CLSBPF_REPLACE) + nfp_net_xdp_offload(nn, NULL); + nn->bpf_offload_xdp = prog && !ret; + return ret; +} + static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) { struct nfp_net_ring_set rx = { @@ -2920,6 +2953,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) if (prog && nn->xdp_prog) { prog = xchg(&nn->xdp_prog, prog); bpf_prog_put(prog); + nfp_net_xdp_offload(nn, nn->xdp_prog); return 0; } @@ -2934,6 +2968,8 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) if (prog) bpf_prog_put(prog); + nfp_net_xdp_offload(nn, nn->xdp_prog); + return 0; } @@ -3233,5 +3269,7 @@ void nfp_net_netdev_clean(struct net_device *netdev) if (nn->xdp_prog) bpf_prog_put(nn->xdp_prog); + if (nn->bpf_offload_xdp) + nfp_net_xdp_offload(nn, NULL); unregister_netdev(nn->netdev); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c index 4bb6f16..18a851e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c @@ -111,6 +111,9 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) const struct tc_action *a; LIST_HEAD(actions); + if (!cls_bpf->exts) + return NN_ACT_XDP; + /* TC direct action */ if (cls_bpf->exts_integrated) { if (tc_no_actions(cls_bpf->exts)) -- cgit v1.1 From 0c1b7de4f33f64665c45a15980a02582740034ff Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Wed, 21 Sep 2016 20:21:52 -0400 Subject: ixgbe: Add X552 iXFI configuration helper function X553 doesn't need all the initialization that X552 did for iXFI. This patch will allow native SPI SFP+ to work with X553 devices. Future patches will add additional configuration as needed. Signed-off-by: Don Skidmore Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 86 ++++++++++++++++----------- 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 7e6b926..3e3458e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1128,47 +1128,17 @@ out: return ret; } -/** ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI mode. +/** + * ixgbe_setup_ixfi_x550em_x - MAC specific iXFI configuration * @hw: pointer to hardware structure - * @speed: the link speed to force * - * Configures the integrated KR PHY to use iXFI mode. Used to connect an - * internal and external PHY at a specific speed, without autonegotiation. + * iXfI configuration needed for ixgbe_mac_X550EM_x devices. **/ -static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) +static s32 ixgbe_setup_ixfi_x550em_x(struct ixgbe_hw *hw) { s32 status; u32 reg_val; - /* Disable AN and force speed to 10G Serial. */ - status = ixgbe_read_iosf_sb_reg_x550(hw, - IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); - if (status) - return status; - - reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE; - reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_MASK; - - /* Select forced link speed for internal PHY. */ - switch (*speed) { - case IXGBE_LINK_SPEED_10GB_FULL: - reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_10G; - break; - case IXGBE_LINK_SPEED_1GB_FULL: - reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_1G; - break; - default: - /* Other link speeds are not supported by internal KR PHY. */ - return IXGBE_ERR_LINK_SETUP; - } - - status = ixgbe_write_iosf_sb_reg_x550(hw, - IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); - if (status) - return status; - /* Disable training protocol FSM. */ status = ixgbe_read_iosf_sb_reg_x550(hw, IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id), @@ -1228,9 +1198,57 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) status = ixgbe_write_iosf_sb_reg_x550(hw, IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id), IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + return status; +} + +/** ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI mode. + * @hw: pointer to hardware structure + * @speed: the link speed to force + * + * Configures the integrated KR PHY to use iXFI mode. Used to connect an + * internal and external PHY at a specific speed, without autonegotiation. + **/ +static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) +{ + s32 status; + u32 reg_val; + + /* Disable AN and force speed to 10G Serial. */ + status = ixgbe_read_iosf_sb_reg_x550(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + if (status) + return status; + + reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE; + reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_MASK; + + /* Select forced link speed for internal PHY. */ + switch (*speed) { + case IXGBE_LINK_SPEED_10GB_FULL: + reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_10G; + break; + case IXGBE_LINK_SPEED_1GB_FULL: + reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_1G; + break; + default: + /* Other link speeds are not supported by internal KR PHY. */ + return IXGBE_ERR_LINK_SETUP; + } + + status = ixgbe_write_iosf_sb_reg_x550(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); if (status) return status; + /* Additional configuration needed for x550em_x */ + if (hw->mac.type == ixgbe_mac_X550EM_x) { + status = ixgbe_setup_ixfi_x550em_x(hw); + if (status) + return status; + } + /* Toggle port SW reset by AN reset. */ status = ixgbe_read_iosf_sb_reg_x550(hw, IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), -- cgit v1.1 From 805cedd663549bfc74dbbc5dca73810df0589396 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Thu, 20 Oct 2016 21:42:00 -0400 Subject: ixgbe: Add support to retrieve and store LED link active This patch adds support to get the LED link active via the LEDCTL register. If the LEDCTL register does not have LED link active (LED mode field = 0x0100) set then default LED link active returned. Signed-off-by: Don Skidmore Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 46 ++++++++++++++++++++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_common.h | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 4 +-- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 2 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 3 ++ 8 files changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index fb51be7..506e346 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -1179,6 +1179,7 @@ static const struct ixgbe_mac_operations mac_ops_82598 = { .get_link_capabilities = &ixgbe_get_link_capabilities_82598, .led_on = &ixgbe_led_on_generic, .led_off = &ixgbe_led_off_generic, + .init_led_link_act = ixgbe_init_led_link_act_generic, .blink_led_start = &ixgbe_blink_led_start_generic, .blink_led_stop = &ixgbe_blink_led_stop_generic, .set_rar = &ixgbe_set_rar_generic, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index 63b2500..1c1076b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -2204,6 +2204,7 @@ static const struct ixgbe_mac_operations mac_ops_82599 = { .get_link_capabilities = &ixgbe_get_link_capabilities_82599, .led_on = &ixgbe_led_on_generic, .led_off = &ixgbe_led_off_generic, + .init_led_link_act = ixgbe_init_led_link_act_generic, .blink_led_start = &ixgbe_blink_led_start_generic, .blink_led_stop = &ixgbe_blink_led_stop_generic, .set_rar = &ixgbe_set_rar_generic, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 77d3039..f7600dd 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -390,6 +390,9 @@ s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw) status = hw->mac.ops.start_hw(hw); } + /* Initialize the LED link active for LED blink support */ + hw->mac.ops.init_led_link_act(hw); + return status; } @@ -773,6 +776,49 @@ s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw) } /** + * ixgbe_init_led_link_act_generic - Store the LED index link/activity. + * @hw: pointer to hardware structure + * + * Store the index for the link active LED. This will be used to support + * blinking the LED. + **/ +s32 ixgbe_init_led_link_act_generic(struct ixgbe_hw *hw) +{ + struct ixgbe_mac_info *mac = &hw->mac; + u32 led_reg, led_mode; + u16 i; + + led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL); + + /* Get LED link active from the LEDCTL register */ + for (i = 0; i < 4; i++) { + led_mode = led_reg >> IXGBE_LED_MODE_SHIFT(i); + + if ((led_mode & IXGBE_LED_MODE_MASK_BASE) == + IXGBE_LED_LINK_ACTIVE) { + mac->led_link_act = i; + return 0; + } + } + + /* If LEDCTL register does not have the LED link active set, then use + * known MAC defaults. + */ + switch (hw->mac.type) { + case ixgbe_mac_x550em_a: + mac->led_link_act = 0; + break; + case ixgbe_mac_X550EM_x: + mac->led_link_act = 1; + break; + default: + mac->led_link_act = 2; + } + + return 0; +} + +/** * ixgbe_led_on_generic - Turns on the software controllable LEDs. * @hw: pointer to hardware structure * @index: led number to turn on diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index 6d4c260..5b3e3c6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -49,6 +49,7 @@ s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw); s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index); s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index); +s32 ixgbe_init_led_link_act_generic(struct ixgbe_hw *hw); s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw); s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset, u16 data); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index f49f803..fd192bf 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -2225,11 +2225,11 @@ static int ixgbe_set_phys_id(struct net_device *netdev, return 2; case ETHTOOL_ID_ON: - hw->mac.ops.led_on(hw, hw->bus.func); + hw->mac.ops.led_on(hw, hw->mac.led_link_act); break; case ETHTOOL_ID_OFF: - hw->mac.ops.led_off(hw, hw->bus.func); + hw->mac.ops.led_off(hw, hw->mac.led_link_act); break; case ETHTOOL_ID_INACTIVE: diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 31d82e3..82813f9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3352,6 +3352,7 @@ struct ixgbe_mac_operations { s32 (*led_off)(struct ixgbe_hw *, u32); s32 (*blink_led_start)(struct ixgbe_hw *, u32); s32 (*blink_led_stop)(struct ixgbe_hw *, u32); + s32 (*init_led_link_act)(struct ixgbe_hw *); /* RAR, Multicast, VLAN */ s32 (*set_rar)(struct ixgbe_hw *, u32, u8 *, u32, u32); @@ -3462,6 +3463,7 @@ struct ixgbe_mac_info { u8 san_mac_rar_index; struct ixgbe_thermal_sensor_data thermal_sensor_data; bool set_lben; + u8 led_link_act; }; struct ixgbe_phy_info { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index f2b1d48..5bf1d49 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -851,6 +851,7 @@ static const struct ixgbe_mac_operations mac_ops_X540 = { .get_link_capabilities = &ixgbe_get_copper_link_capabilities_generic, .led_on = &ixgbe_led_on_generic, .led_off = &ixgbe_led_off_generic, + .init_led_link_act = ixgbe_init_led_link_act_generic, .blink_led_start = &ixgbe_blink_led_start_X540, .blink_led_stop = &ixgbe_blink_led_stop_X540, .set_rar = &ixgbe_set_rar_generic, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 3e3458e..f4385fc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -2952,6 +2952,7 @@ static const struct ixgbe_mac_operations mac_ops_X550 = { X550_COMMON_MAC .led_on = ixgbe_led_on_generic, .led_off = ixgbe_led_off_generic, + .init_led_link_act = ixgbe_init_led_link_act_generic, .reset_hw = &ixgbe_reset_hw_X540, .get_media_type = &ixgbe_get_media_type_X540, .get_san_mac_addr = &ixgbe_get_san_mac_addr_generic, @@ -2972,6 +2973,7 @@ static const struct ixgbe_mac_operations mac_ops_X550EM_x = { X550_COMMON_MAC .led_on = ixgbe_led_on_t_x550em, .led_off = ixgbe_led_off_t_x550em, + .init_led_link_act = ixgbe_init_led_link_act_generic, .reset_hw = &ixgbe_reset_hw_X550em, .get_media_type = &ixgbe_get_media_type_X550em, .get_san_mac_addr = NULL, @@ -2992,6 +2994,7 @@ static struct ixgbe_mac_operations mac_ops_x550em_a = { X550_COMMON_MAC .led_on = ixgbe_led_on_t_x550em, .led_off = ixgbe_led_off_t_x550em, + .init_led_link_act = ixgbe_init_led_link_act_generic, .reset_hw = ixgbe_reset_hw_X550em, .get_media_type = ixgbe_get_media_type_X550em, .get_san_mac_addr = NULL, -- cgit v1.1 From d2e455a8884dcf5549ef67fcc2714ac0a64b9dc8 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Fri, 21 Oct 2016 21:10:54 -0400 Subject: ixgbe: Update setup PHY link to unset all speeds This patch updates ixgbe_setup_phy_link_generic to set/unset auto-negotiation for all speeds. This ensures that unsupported speeds are unset. This is necessary since the PHY NVM may advertise unsupported speeds. Signed-off-by: Don Skidmore Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 77 ++++++++++++++-------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 021ab9b..648739a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -705,53 +705,52 @@ s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw) ixgbe_get_copper_link_capabilities_generic(hw, &speed, &autoneg); - if (speed & IXGBE_LINK_SPEED_10GB_FULL) { - /* Set or unset auto-negotiation 10G advertisement */ - hw->phy.ops.read_reg(hw, MDIO_AN_10GBT_CTRL, - MDIO_MMD_AN, - &autoneg_reg); + /* Set or unset auto-negotiation 10G advertisement */ + hw->phy.ops.read_reg(hw, MDIO_AN_10GBT_CTRL, MDIO_MMD_AN, &autoneg_reg); - autoneg_reg &= ~MDIO_AN_10GBT_CTRL_ADV10G; - if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) - autoneg_reg |= MDIO_AN_10GBT_CTRL_ADV10G; + autoneg_reg &= ~MDIO_AN_10GBT_CTRL_ADV10G; + if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) && + (speed & IXGBE_LINK_SPEED_10GB_FULL)) + autoneg_reg |= MDIO_AN_10GBT_CTRL_ADV10G; - hw->phy.ops.write_reg(hw, MDIO_AN_10GBT_CTRL, - MDIO_MMD_AN, - autoneg_reg); - } + hw->phy.ops.write_reg(hw, MDIO_AN_10GBT_CTRL, MDIO_MMD_AN, autoneg_reg); - if (speed & IXGBE_LINK_SPEED_1GB_FULL) { - /* Set or unset auto-negotiation 1G advertisement */ - hw->phy.ops.read_reg(hw, - IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, - MDIO_MMD_AN, - &autoneg_reg); - - autoneg_reg &= ~IXGBE_MII_1GBASE_T_ADVERTISE; - if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) - autoneg_reg |= IXGBE_MII_1GBASE_T_ADVERTISE; + hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, + MDIO_MMD_AN, &autoneg_reg); - hw->phy.ops.write_reg(hw, - IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, - MDIO_MMD_AN, - autoneg_reg); + if (hw->mac.type == ixgbe_mac_X550) { + /* Set or unset auto-negotiation 5G advertisement */ + autoneg_reg &= ~IXGBE_MII_5GBASE_T_ADVERTISE; + if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_5GB_FULL) && + (speed & IXGBE_LINK_SPEED_5GB_FULL)) + autoneg_reg |= IXGBE_MII_5GBASE_T_ADVERTISE; + + /* Set or unset auto-negotiation 2.5G advertisement */ + autoneg_reg &= ~IXGBE_MII_2_5GBASE_T_ADVERTISE; + if ((hw->phy.autoneg_advertised & + IXGBE_LINK_SPEED_2_5GB_FULL) && + (speed & IXGBE_LINK_SPEED_2_5GB_FULL)) + autoneg_reg |= IXGBE_MII_2_5GBASE_T_ADVERTISE; } - if (speed & IXGBE_LINK_SPEED_100_FULL) { - /* Set or unset auto-negotiation 100M advertisement */ - hw->phy.ops.read_reg(hw, MDIO_AN_ADVERTISE, - MDIO_MMD_AN, - &autoneg_reg); + /* Set or unset auto-negotiation 1G advertisement */ + autoneg_reg &= ~IXGBE_MII_1GBASE_T_ADVERTISE; + if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) && + (speed & IXGBE_LINK_SPEED_1GB_FULL)) + autoneg_reg |= IXGBE_MII_1GBASE_T_ADVERTISE; - autoneg_reg &= ~(ADVERTISE_100FULL | - ADVERTISE_100HALF); - if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) - autoneg_reg |= ADVERTISE_100FULL; + hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, + MDIO_MMD_AN, autoneg_reg); - hw->phy.ops.write_reg(hw, MDIO_AN_ADVERTISE, - MDIO_MMD_AN, - autoneg_reg); - } + /* Set or unset auto-negotiation 100M advertisement */ + hw->phy.ops.read_reg(hw, MDIO_AN_ADVERTISE, MDIO_MMD_AN, &autoneg_reg); + + autoneg_reg &= ~(ADVERTISE_100FULL | ADVERTISE_100HALF); + if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) && + (speed & IXGBE_LINK_SPEED_100_FULL)) + autoneg_reg |= ADVERTISE_100FULL; + + hw->phy.ops.write_reg(hw, MDIO_AN_ADVERTISE, MDIO_MMD_AN, autoneg_reg); /* Blocked by MNG FW so don't reset PHY */ if (ixgbe_check_reset_blocked(hw)) -- cgit v1.1 From 4dc4000b35119fb83266f978475f5881e263358e Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 26 Sep 2016 14:08:13 -0700 Subject: ixgbe: do not use ixgbe specific mdio defines Replace some ixgbe specific MDIO defines with their equivalent from the kernel. Signed-off-by: Emil Tantilov Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 8 +--- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 16 ------- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 65 +++++++++++++-------------- 3 files changed, 33 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 648739a..ebf46af 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -2395,9 +2395,7 @@ s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on) if (!on && ixgbe_mng_present(hw)) return 0; - status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, - ®); + status = hw->phy.ops.read_reg(hw, MDIO_CTRL1, MDIO_MMD_VEND1, ®); if (status) return status; @@ -2409,8 +2407,6 @@ s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on) reg |= IXGBE_MDIO_PHY_SET_LOW_POWER_MODE; } - status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, - reg); + status = hw->phy.ops.write_reg(hw, MDIO_CTRL1, MDIO_MMD_VEND1, reg); return status; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 82813f9..6886e57 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -874,19 +874,13 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_1GB 0x4 /* 1Gb/s */ #define IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_10GB 0x6 /* 10Gb/s */ -#define IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG 0x20 /* 10G Control Reg */ #define IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG 0xC400 /* 1G Provisioning 1 */ #define IXGBE_MII_AUTONEG_XNP_TX_REG 0x17 /* 1G XNP Transmit */ -#define IXGBE_MII_AUTONEG_ADVERTISE_REG 0x10 /* 100M Advertisement */ -#define IXGBE_MII_10GBASE_T_ADVERTISE 0x1000 /* full duplex, bit:12*/ #define IXGBE_MII_1GBASE_T_ADVERTISE_XNP_TX 0x4000 /* full duplex, bit:14*/ #define IXGBE_MII_1GBASE_T_ADVERTISE 0x8000 /* full duplex, bit:15*/ #define IXGBE_MII_2_5GBASE_T_ADVERTISE 0x0400 #define IXGBE_MII_5GBASE_T_ADVERTISE 0x0800 -#define IXGBE_MII_100BASE_T_ADVERTISE 0x0100 /* full duplex, bit:8 */ -#define IXGBE_MII_100BASE_T_ADVERTISE_HALF 0x0080 /* half duplex, bit:7 */ #define IXGBE_MII_RESTART 0x200 -#define IXGBE_MII_AUTONEG_COMPLETE 0x20 #define IXGBE_MII_AUTONEG_LINK_UP 0x04 #define IXGBE_MII_AUTONEG_REG 0x0 @@ -1320,30 +1314,20 @@ struct ixgbe_thermal_sensor_data { /* MDIO definitions */ #define IXGBE_MDIO_ZERO_DEV_TYPE 0x0 -#define IXGBE_MDIO_PMA_PMD_DEV_TYPE 0x1 #define IXGBE_MDIO_PCS_DEV_TYPE 0x3 -#define IXGBE_MDIO_PHY_XS_DEV_TYPE 0x4 -#define IXGBE_MDIO_AUTO_NEG_DEV_TYPE 0x7 -#define IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE 0x1E /* Device 30 */ #define IXGBE_TWINAX_DEV 1 #define IXGBE_MDIO_COMMAND_TIMEOUT 100 /* PHY Timeout for 1 GB mode */ -#define IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL 0x0 /* VS1 Control Reg */ -#define IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS 0x1 /* VS1 Status Reg */ #define IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS 0x0008 /* 1 = Link Up */ #define IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS 0x0010 /* 0 - 10G, 1 - 1G */ #define IXGBE_MDIO_VENDOR_SPECIFIC_1_10G_SPEED 0x0018 #define IXGBE_MDIO_VENDOR_SPECIFIC_1_1G_SPEED 0x0010 -#define IXGBE_MDIO_AUTO_NEG_CONTROL 0x0 /* AUTO_NEG Control Reg */ -#define IXGBE_MDIO_AUTO_NEG_STATUS 0x1 /* AUTO_NEG Status Reg */ #define IXGBE_MDIO_AUTO_NEG_VENDOR_STAT 0xC800 /* AUTO_NEG Vendor Status Reg */ #define IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM 0xCC00 /* AUTO_NEG Vendor TX Reg */ #define IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM2 0xCC01 /* AUTO_NEG Vendor Tx Reg */ #define IXGBE_MDIO_AUTO_NEG_VEN_LSC 0x1 /* AUTO_NEG Vendor Tx LSC */ -#define IXGBE_MDIO_AUTO_NEG_ADVT 0x10 /* AUTO_NEG Advt Reg */ -#define IXGBE_MDIO_AUTO_NEG_LP 0x13 /* AUTO_NEG LP Status Reg */ #define IXGBE_MDIO_AUTO_NEG_EEE_ADVT 0x3C /* AUTO_NEG EEE Advt Reg */ #define IXGBE_MDIO_PHY_SET_LOW_POWER_MODE 0x0800 /* Set low power mode */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index f4385fc..e9fa83b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1571,8 +1571,7 @@ static s32 ixgbe_check_link_t_X550em(struct ixgbe_hw *hw, /* MAC link is up, so check external PHY link. * Read this twice back to back to indicate current status. */ - status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + status = hw->phy.ops.read_reg(hw, MDIO_STAT1, MDIO_MMD_AN, &autoneg_status); if (status) return status; @@ -1758,7 +1757,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc) /* Vendor alarm triggered */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status || !(reg & IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN)) @@ -1766,7 +1765,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc) /* Vendor Auto-Neg alarm triggered or Global alarm 1 triggered */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_FLAG, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status || !(reg & (IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN | @@ -1775,7 +1774,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc) /* Global alarm triggered */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_ALARM_1, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status) @@ -1790,7 +1789,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc) if (reg & IXGBE_MDIO_GLOBAL_ALM_1_DEV_FAULT) { /* device fault alarm triggered */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_FAULT_MSG, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status) return status; @@ -1805,14 +1804,14 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc) /* Vendor alarm 2 triggered */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, ®); + MDIO_MMD_AN, ®); if (status || !(reg & IXGBE_MDIO_GLOBAL_STD_ALM2_INT)) return status; /* link connect/disconnect event occurred */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM2, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, ®); + MDIO_MMD_AN, ®); if (status) return status; @@ -1844,20 +1843,20 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) /* Enable link status change alarm */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, ®); + MDIO_MMD_AN, ®); if (status) return status; reg |= IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN; status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, reg); + MDIO_MMD_AN, reg); if (status) return status; /* Enable high temperature failure and global fault alarms */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status) return status; @@ -1866,14 +1865,14 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) IXGBE_MDIO_GLOBAL_INT_DEV_FAULT_EN); status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, reg); if (status) return status; /* Enable vendor Auto-Neg alarm and Global Interrupt Mask 1 alarm */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status) return status; @@ -1882,14 +1881,14 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) IXGBE_MDIO_GLOBAL_ALARM_1_INT); status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, reg); if (status) return status; /* Enable chip-wide vendor alarm */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_STD_MASK, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status) return status; @@ -1897,7 +1896,7 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) reg |= IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN; status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_STD_MASK, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, reg); return status; @@ -2038,14 +2037,12 @@ static s32 ixgbe_ext_phy_t_x550em_get_link(struct ixgbe_hw *hw, bool *link_up) *link_up = false; /* read this twice back to back to indicate current status */ - ret = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + ret = hw->phy.ops.read_reg(hw, MDIO_STAT1, MDIO_MMD_AN, &autoneg_status); if (ret) return ret; - ret = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + ret = hw->phy.ops.read_reg(hw, MDIO_STAT1, MDIO_MMD_AN, &autoneg_status); if (ret) return ret; @@ -2091,7 +2088,7 @@ static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw) return 0; status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + MDIO_MMD_AN, &speed); if (status) return status; @@ -2152,10 +2149,10 @@ static s32 ixgbe_led_on_t_x550em(struct ixgbe_hw *hw, u32 led_idx) /* To turn on the LED, set mode to ON. */ hw->phy.ops.read_reg(hw, IXGBE_X557_LED_PROVISIONING + led_idx, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, &phy_data); + MDIO_MMD_VEND1, &phy_data); phy_data |= IXGBE_X557_LED_MANUAL_SET_MASK; hw->phy.ops.write_reg(hw, IXGBE_X557_LED_PROVISIONING + led_idx, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, phy_data); + MDIO_MMD_VEND1, phy_data); return 0; } @@ -2174,10 +2171,10 @@ static s32 ixgbe_led_off_t_x550em(struct ixgbe_hw *hw, u32 led_idx) /* To turn on the LED, set mode to ON. */ hw->phy.ops.read_reg(hw, IXGBE_X557_LED_PROVISIONING + led_idx, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, &phy_data); + MDIO_MMD_VEND1, &phy_data); phy_data &= ~IXGBE_X557_LED_MANUAL_SET_MASK; hw->phy.ops.write_reg(hw, IXGBE_X557_LED_PROVISIONING + led_idx, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, phy_data); + MDIO_MMD_VEND1, phy_data); return 0; } @@ -2198,7 +2195,7 @@ static s32 ixgbe_get_lcd_t_x550em(struct ixgbe_hw *hw, *lcd_speed = IXGBE_LINK_SPEED_UNKNOWN; status = hw->phy.ops.read_reg(hw, IXGBE_AUTO_NEG_LP_STATUS, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + MDIO_MMD_AN, &an_lp_status); if (status) return status; @@ -2345,7 +2342,7 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) return ixgbe_set_copper_phy_power(hw, false); status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + MDIO_MMD_AN, &speed); if (status) return status; @@ -2367,20 +2364,20 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) /* Clear AN completed indication */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + MDIO_MMD_AN, &autoneg_reg); if (status) return status; - status = hw->phy.ops.read_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + status = hw->phy.ops.read_reg(hw, MDIO_AN_10GBT_CTRL, + MDIO_MMD_AN, &an_10g_cntl_reg); if (status) return status; status = hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, - IXGBE_MDIO_AUTO_NEG_DEV_TYPE, + MDIO_MMD_AN, &autoneg_reg); if (status) return status; @@ -2538,7 +2535,7 @@ static s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_TX_VENDOR_ALARMS_3, - IXGBE_MDIO_PMA_PMD_DEV_TYPE, + MDIO_MMD_PMAPMD, ®); if (status) return status; @@ -2549,7 +2546,7 @@ static s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) if (reg & IXGBE_MDIO_TX_VENDOR_ALARMS_3_RST_MASK) { status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_RES_PR_10, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, ®); if (status) return status; @@ -2558,7 +2555,7 @@ static s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_RES_PR_10, - IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + MDIO_MMD_VEND1, reg); if (status) return status; -- cgit v1.1 From 21d882eb3a7e8d706247f32c0352390791751e8f Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 26 Sep 2016 14:08:18 -0700 Subject: ixgbe: fix link status check for copper X550em Read the PHY register twice in order to get the correct value for autoneg_status. Signed-off-by: Emil Tantilov Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index e9fa83b..51b6ade 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1556,7 +1556,7 @@ static s32 ixgbe_check_link_t_X550em(struct ixgbe_hw *hw, bool link_up_wait_to_complete) { u32 status; - u16 autoneg_status; + u16 i, autoneg_status; if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper) return IXGBE_ERR_CONFIG; @@ -1568,13 +1568,18 @@ static s32 ixgbe_check_link_t_X550em(struct ixgbe_hw *hw, if (status || !(*link_up)) return status; - /* MAC link is up, so check external PHY link. - * Read this twice back to back to indicate current status. - */ - status = hw->phy.ops.read_reg(hw, MDIO_STAT1, MDIO_MMD_AN, - &autoneg_status); - if (status) - return status; + /* MAC link is up, so check external PHY link. + * Link status is latching low, and can only be used to detect link + * drop, and not the current status of the link without performing + * back-to-back reads. + */ + for (i = 0; i < 2; i++) { + status = hw->phy.ops.read_reg(hw, MDIO_STAT1, MDIO_MMD_AN, + &autoneg_status); + + if (status) + return status; + } /* If external PHY link is not up, then indicate link not up */ if (!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS)) -- cgit v1.1 From 2916500db3075cf2dd7968fe0f140ef4df7c6d31 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Tue, 27 Sep 2016 14:31:12 -0400 Subject: ixgbe: Add X553 PHY FC autoneg support This patch adds X553 flow control auto negotiation for fiber and backplain. To enable this new function pointers were added as well as creating a function to dynamically set function pointer we can't define only on MAC type. Signed-off-by: Don Skidmore Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 + drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c | 3 +- drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 16 +- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 6 + drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 211 ++++++++++++++++++++++++ 7 files changed, 232 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index b06e32d..ef81c3d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -1027,4 +1027,6 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, struct ixgbe_ring *tx_ring); u32 ixgbe_rss_indir_tbl_entries(struct ixgbe_adapter *adapter); void ixgbe_store_reta(struct ixgbe_adapter *adapter); +s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, + u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm); #endif /* _IXGBE_H_ */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index 506e346..805ab31 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -367,7 +367,7 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw) } /* Negotiate the fc mode to use */ - ixgbe_fc_autoneg(hw); + hw->mac.ops.fc_autoneg(hw); /* Disable any previous flow control settings */ fctrl_reg = IXGBE_READ_REG(hw, IXGBE_FCTRL); @@ -1194,6 +1194,7 @@ static const struct ixgbe_mac_operations mac_ops_82598 = { .set_vfta = &ixgbe_set_vfta_82598, .fc_enable = &ixgbe_fc_enable_82598, .setup_fc = ixgbe_setup_fc_generic, + .fc_autoneg = ixgbe_fc_autoneg, .set_fw_drv_ver = NULL, .acquire_swfw_sync = &ixgbe_acquire_swfw_sync, .release_swfw_sync = &ixgbe_release_swfw_sync, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index 1c1076b..e00aaeb 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -2220,6 +2220,7 @@ static const struct ixgbe_mac_operations mac_ops_82599 = { .set_vfta = &ixgbe_set_vfta_generic, .fc_enable = &ixgbe_fc_enable_generic, .setup_fc = ixgbe_setup_fc_generic, + .fc_autoneg = ixgbe_fc_autoneg, .set_fw_drv_ver = &ixgbe_set_fw_drv_ver_generic, .init_uta_tables = &ixgbe_init_uta_tables_generic, .setup_sfp = &ixgbe_setup_sfp_modules_82599, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index f7600dd..8832df3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -298,10 +298,12 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw) IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext); IXGBE_WRITE_FLUSH(hw); - /* Setup flow control */ - ret_val = hw->mac.ops.setup_fc(hw); - if (ret_val) - return ret_val; + /* Setup flow control if method for doing so */ + if (hw->mac.ops.setup_fc) { + ret_val = hw->mac.ops.setup_fc(hw); + if (ret_val) + return ret_val; + } /* Cashe bit indicating need for crosstalk fix */ switch (hw->mac.type) { @@ -2173,7 +2175,7 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw) } /* Negotiate the fc mode to use */ - ixgbe_fc_autoneg(hw); + hw->mac.ops.fc_autoneg(hw); /* Disable any previous flow control settings */ mflcn_reg = IXGBE_READ_REG(hw, IXGBE_MFLCN); @@ -2277,8 +2279,8 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw) * Find the intersection between advertised settings and link partner's * advertised settings **/ -static s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, - u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm) +s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg, + u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm) { if ((!(adv_reg)) || (!(lp_reg))) return IXGBE_ERR_FC_NOT_NEGOTIATED; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 6886e57..f2f60cc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3357,6 +3357,7 @@ struct ixgbe_mac_operations { /* Flow Control */ s32 (*fc_enable)(struct ixgbe_hw *); s32 (*setup_fc)(struct ixgbe_hw *); + void (*fc_autoneg)(struct ixgbe_hw *); /* Manageability interface */ s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8); @@ -3579,10 +3580,12 @@ struct ixgbe_info { #define IXGBE_FUSES0_REV_MASK (3u << 6) #define IXGBE_KRM_PORT_CAR_GEN_CTRL(P) ((P) ? 0x8010 : 0x4010) +#define IXGBE_KRM_LINK_S1(P) ((P) ? 0x8200 : 0x4200) #define IXGBE_KRM_LINK_CTRL_1(P) ((P) ? 0x820C : 0x420C) #define IXGBE_KRM_AN_CNTL_1(P) ((P) ? 0x822C : 0x422C) #define IXGBE_KRM_AN_CNTL_8(P) ((P) ? 0x8248 : 0x4248) #define IXGBE_KRM_SGMII_CTRL(P) ((P) ? 0x82A0 : 0x42A0) +#define IXGBE_KRM_LP_BASE_PAGE_HIGH(P) ((P) ? 0x836C : 0x436C) #define IXGBE_KRM_DSP_TXFFE_STATE_4(P) ((P) ? 0x8634 : 0x4634) #define IXGBE_KRM_DSP_TXFFE_STATE_5(P) ((P) ? 0x8638 : 0x4638) #define IXGBE_KRM_RX_TRN_LINKUP_CTRL(P) ((P) ? 0x8B00 : 0x4B00) @@ -3604,6 +3607,7 @@ struct ixgbe_info { #define IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR BIT(18) #define IXGBE_KRM_LINK_CTRL_1_TETH_EEE_CAP_KX BIT(24) #define IXGBE_KRM_LINK_CTRL_1_TETH_EEE_CAP_KR BIT(26) +#define IXGBE_KRM_LINK_S1_MAC_AN_COMPLETE BIT(28) #define IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE BIT(29) #define IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART BIT(31) @@ -3613,6 +3617,8 @@ struct ixgbe_info { #define IXGBE_KRM_AN_CNTL_8_LINEAR BIT(0) #define IXGBE_KRM_AN_CNTL_8_LIMITING BIT(1) +#define IXGBE_KRM_LP_BASE_PAGE_HIGH_SYM_PAUSE BIT(10) +#define IXGBE_KRM_LP_BASE_PAGE_HIGH_ASM_PAUSE BIT(11) #define IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_100_D BIT(12) #define IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_10_D BIT(19) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 5bf1d49..e2ff823 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -867,6 +867,7 @@ static const struct ixgbe_mac_operations mac_ops_X540 = { .set_vfta = &ixgbe_set_vfta_generic, .fc_enable = &ixgbe_fc_enable_generic, .setup_fc = ixgbe_setup_fc_generic, + .fc_autoneg = ixgbe_fc_autoneg, .set_fw_drv_ver = &ixgbe_set_fw_drv_ver_generic, .init_uta_tables = &ixgbe_init_uta_tables_generic, .setup_sfp = NULL, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 51b6ade..961ce3a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -28,6 +28,9 @@ static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *, ixgbe_link_speed); static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *); +static void ixgbe_fc_autoneg_fiber_x550em_a(struct ixgbe_hw *); +static void ixgbe_fc_autoneg_backplane_x550em_a(struct ixgbe_hw *); +static s32 ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *); static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw) { @@ -1639,6 +1642,27 @@ ixgbe_setup_sgmii(struct ixgbe_hw *hw, __always_unused ixgbe_link_speed speed, return rc; } +/** ixgbe_init_mac_link_ops_X550em_a - Init mac link function pointers + * @hw: pointer to hardware structure + **/ +static void ixgbe_init_mac_link_ops_X550em_a(struct ixgbe_hw *hw) +{ + struct ixgbe_mac_info *mac = &hw->mac; + + switch (mac->ops.get_media_type(hw)) { + case ixgbe_media_type_fiber: + mac->ops.setup_fc = NULL; + mac->ops.fc_autoneg = ixgbe_fc_autoneg_fiber_x550em_a; + break; + case ixgbe_media_type_backplane: + mac->ops.fc_autoneg = ixgbe_fc_autoneg_backplane_x550em_a; + mac->ops.setup_fc = ixgbe_setup_fc_backplane_x550em_a; + break; + default: + break; + } +} + /** ixgbe_init_mac_link_ops_X550em - init mac link function pointers * @hw: pointer to hardware structure **/ @@ -1686,6 +1710,10 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) default: break; } + + /* Additional modification for X550em_a devices */ + if (hw->mac.type == ixgbe_mac_x550em_a) + ixgbe_init_mac_link_ops_X550em_a(hw); } /** ixgbe_setup_sfp_modules_X550em - Setup SFP module @@ -2301,6 +2329,90 @@ static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *hw) return rc; } +/** + * ixgbe_fc_autoneg_backplane_x550em_a - Enable flow control IEEE clause 37 + * @hw: pointer to hardware structure + **/ +static void ixgbe_fc_autoneg_backplane_x550em_a(struct ixgbe_hw *hw) +{ + u32 link_s1, lp_an_page_low, an_cntl_1; + s32 status = IXGBE_ERR_FC_NOT_NEGOTIATED; + ixgbe_link_speed speed; + bool link_up; + + /* AN should have completed when the cable was plugged in. + * Look for reasons to bail out. Bail out if: + * - FC autoneg is disabled, or if + * - link is not up. + */ + if (hw->fc.disable_fc_autoneg) { + hw_err(hw, "Flow control autoneg is disabled"); + goto out; + } + + hw->mac.ops.check_link(hw, &speed, &link_up, false); + if (!link_up) { + hw_err(hw, "The link is down"); + goto out; + } + + /* Check at auto-negotiation has completed */ + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_LINK_S1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &link_s1); + + if (status || (link_s1 & IXGBE_KRM_LINK_S1_MAC_AN_COMPLETE) == 0) { + hw_dbg(hw, "Auto-Negotiation did not complete\n"); + status = IXGBE_ERR_FC_NOT_NEGOTIATED; + goto out; + } + + /* Read the 10g AN autoc and LP ability registers and resolve + * local flow control settings accordingly + */ + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &an_cntl_1); + + if (status) { + hw_dbg(hw, "Auto-Negotiation did not complete\n"); + goto out; + } + + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_LP_BASE_PAGE_HIGH(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &lp_an_page_low); + + if (status) { + hw_dbg(hw, "Auto-Negotiation did not complete\n"); + goto out; + } + + status = ixgbe_negotiate_fc(hw, an_cntl_1, lp_an_page_low, + IXGBE_KRM_AN_CNTL_1_SYM_PAUSE, + IXGBE_KRM_AN_CNTL_1_ASM_PAUSE, + IXGBE_KRM_LP_BASE_PAGE_HIGH_SYM_PAUSE, + IXGBE_KRM_LP_BASE_PAGE_HIGH_ASM_PAUSE); + +out: + if (!status) { + hw->fc.fc_was_autonegged = true; + } else { + hw->fc.fc_was_autonegged = false; + hw->fc.current_mode = hw->fc.requested_mode; + } +} + +/** + * ixgbe_fc_autoneg_fiber_x550em_a - passthrough FC settings + * @hw: pointer to hardware structure + **/ +static void ixgbe_fc_autoneg_fiber_x550em_a(struct ixgbe_hw *hw) +{ + hw->fc.fc_was_autonegged = false; + hw->fc.current_mode = hw->fc.requested_mode; +} + /** ixgbe_enter_lplu_x550em - Transition to low power states * @hw: pointer to hardware structure * @@ -2749,6 +2861,102 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw, } /** + * ixgbe_setup_fc_backplane_x550em_a - Set up flow control + * @hw: pointer to hardware structure + * + * Called at init time to set up flow control. + **/ +static s32 ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *hw) +{ + s32 status = 0; + u32 link_ctrl = 0; + u32 an_cntl = 0; + + /* Validate the requested mode */ + if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) { + hw_err(hw, "ixgbe_fc_rx_pause not valid in strict IEEE mode\n"); + return IXGBE_ERR_INVALID_LINK_SETTINGS; + } + + if (hw->fc.requested_mode == ixgbe_fc_default) + hw->fc.requested_mode = ixgbe_fc_full; + + /* Set up the 1G and 10G flow control advertisement registers so the + * HW will be able to do FC autoneg once the cable is plugged in. If + * we link at 10G, the 1G advertisement is harmless and vice versa. + */ + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &an_cntl); + + if (status) { + hw_dbg(hw, "Auto-Negotiation did not complete\n"); + return status; + } + + /* The possible values of fc.requested_mode are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but + * we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: Invalid. + */ + switch (hw->fc.requested_mode) { + case ixgbe_fc_none: + /* Flow control completely disabled by software override. */ + an_cntl &= ~(IXGBE_KRM_AN_CNTL_1_SYM_PAUSE | + IXGBE_KRM_AN_CNTL_1_ASM_PAUSE); + break; + case ixgbe_fc_tx_pause: + /* Tx Flow control is enabled, and Rx Flow control is + * disabled by software override. + */ + an_cntl |= IXGBE_KRM_AN_CNTL_1_ASM_PAUSE; + an_cntl &= ~IXGBE_KRM_AN_CNTL_1_SYM_PAUSE; + break; + case ixgbe_fc_rx_pause: + /* Rx Flow control is enabled and Tx Flow control is + * disabled by software override. Since there really + * isn't a way to advertise that we are capable of RX + * Pause ONLY, we will advertise that we support both + * symmetric and asymmetric Rx PAUSE, as such we fall + * through to the fc_full statement. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + case ixgbe_fc_full: + /* Flow control (both Rx and Tx) is enabled by SW override. */ + an_cntl |= IXGBE_KRM_AN_CNTL_1_SYM_PAUSE | + IXGBE_KRM_AN_CNTL_1_ASM_PAUSE; + break; + default: + hw_err(hw, "Flow control param set incorrectly\n"); + return IXGBE_ERR_CONFIG; + } + + status = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, an_cntl); + + /* Restart auto-negotiation. */ + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &link_ctrl); + if (status) { + hw_dbg(hw, "Auto-Negotiation did not complete\n"); + return status; + } + + link_ctrl |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; + status = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, link_ctrl); + + return status; +} + +/** * ixgbe_set_mux - Set mux for port 1 access with CS4227 * @hw: pointer to hardware structure * @state: set mux if 1, clear if 0 @@ -2969,6 +3177,7 @@ static const struct ixgbe_mac_operations mac_ops_X550 = { .prot_autoc_read = prot_autoc_read_generic, .prot_autoc_write = prot_autoc_write_generic, .setup_fc = ixgbe_setup_fc_generic, + .fc_autoneg = ixgbe_fc_autoneg, }; static const struct ixgbe_mac_operations mac_ops_X550EM_x = { @@ -2988,6 +3197,7 @@ static const struct ixgbe_mac_operations mac_ops_X550EM_x = { .release_swfw_sync = &ixgbe_release_swfw_sync_X550em, .init_swfw_sync = &ixgbe_init_swfw_sync_X540, .setup_fc = NULL, /* defined later */ + .fc_autoneg = ixgbe_fc_autoneg, .read_iosf_sb_reg = ixgbe_read_iosf_sb_reg_x550, .write_iosf_sb_reg = ixgbe_write_iosf_sb_reg_x550, }; @@ -3008,6 +3218,7 @@ static struct ixgbe_mac_operations mac_ops_x550em_a = { .acquire_swfw_sync = ixgbe_acquire_swfw_sync_x550em_a, .release_swfw_sync = ixgbe_release_swfw_sync_x550em_a, .setup_fc = ixgbe_setup_fc_x550em, + .fc_autoneg = NULL, /* defined later */ .read_iosf_sb_reg = ixgbe_read_iosf_sb_reg_x550a, .write_iosf_sb_reg = ixgbe_write_iosf_sb_reg_x550a, }; -- cgit v1.1 From 1fa71252778d6fe151438b009416e4ce7bc1c802 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 28 Sep 2016 15:36:38 -0700 Subject: ixgbe: Handle previously-freed msix_entries The msix_entries memory can be freed by a previous suspend or remove, so don't crash on close when it isn't there. Signed-off-by: Mark Rustad Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 5e1f57c..de634f0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3070,6 +3070,9 @@ static void ixgbe_free_irq(struct ixgbe_adapter *adapter) return; } + if (!adapter->msix_entries) + return; + for (vector = 0; vector < adapter->num_q_vectors; vector++) { struct ixgbe_q_vector *q_vector = adapter->q_vector[vector]; struct msix_entry *entry = &adapter->msix_entries[vector]; -- cgit v1.1 From 812d7dff107fa272caf9aa4f8e2420d1d1eaa429 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 3 Oct 2016 17:09:58 -0700 Subject: ixgbe: remove SFP ixfi support Remove SFP ixfi code since there is no HW that currently supports it. Signed-off-by: Emil Tantilov Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 76 ++++----------------------- 1 file changed, 11 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 961ce3a..c6669c27 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1313,7 +1313,7 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, __always_unused bool autoneg_wait_to_complete) { s32 status; - u16 slice, value; + u16 reg_slice, reg_val; bool setup_linear = false; /* Check if SFP module is supported and linear */ @@ -1329,71 +1329,17 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, if (status) return status; - if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) { - /* Configure CS4227 LINE side to 10G SR. */ - slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12); - value = IXGBE_CS4227_SPEED_10G; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, - slice, value); - if (status) - goto i2c_err; - - slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12); - value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, - slice, value); - if (status) - goto i2c_err; - - /* Configure CS4227 for HOST connection rate then type. */ - slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12); - value = speed & IXGBE_LINK_SPEED_10GB_FULL ? - IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, - slice, value); - if (status) - goto i2c_err; - - slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12); - if (setup_linear) - value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1; - else - value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, - slice, value); - if (status) - goto i2c_err; - - /* Setup XFI internal link. */ - status = ixgbe_setup_ixfi_x550em(hw, &speed); - if (status) { - hw_dbg(hw, "setup_ixfi failed with %d\n", status); - return status; - } - } else { - /* Configure internal PHY for KR/KX. */ - status = ixgbe_setup_kr_speed_x550em(hw, speed); - if (status) { - hw_dbg(hw, "setup_kr_speed failed with %d\n", status); - return status; - } - - /* Configure CS4227 LINE side to proper mode. */ - slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12); - if (setup_linear) - value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1; - else - value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, - slice, value); - if (status) - goto i2c_err; - } - - return 0; + /* Configure internal PHY for KR/KX. */ + ixgbe_setup_kr_speed_x550em(hw, speed); -i2c_err: - hw_dbg(hw, "combined i2c access failed with %d\n", status); + /* Configure CS4227 LINE side to proper mode. */ + reg_slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12); + if (setup_linear) + reg_val = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 0x1; + else + reg_val = (IXGBE_CS4227_EDC_MODE_SR << 1) | 0x1; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, + reg_slice, reg_val); return status; } -- cgit v1.1 From b71f6c40bb36f9691420f66283f3c3acca11016b Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 10 Oct 2016 14:54:03 -0700 Subject: ixgbe: use link instead of I2C combined abstraction Introduce ixgbe_link_operations struct with the following changes: read_i2c_combined => read_link read_i2c_combined_unlocked => read_link_unlocked write_i2c_combined => write_link write_i2c_combined_unlocked => write_link_unlocked This will allow X550EM_a to override these methods for MDIO access while X550EM_x provides methods to use I2C combined access. This also adds a new structure, ixgbe_link_info, to hold information about the link. Initially this is just method pointers and a bus address. The functions involved in combined I2C accesses were moved from ixgbe_phy.c to ixgbe_x550.c. The underlying functions that carry out the combined I2C accesses were left in ixgbe_phy.c because they share some functions with other I2C methods. v2 - set hw->link.ops in probe. v3 - check ii->link_ops before setting it since we don't have it for all devices. Signed-off-by: Emil Tantilov Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 + drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 68 +---------------- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h | 12 +-- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 26 +++++-- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 106 +++++++++++++++++++++++--- 5 files changed, 124 insertions(+), 90 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index de634f0..9a62b55 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9507,6 +9507,8 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->mac.ops = *ii->mac_ops; hw->mac.type = ii->mac; hw->mvals = ii->mvals; + if (ii->link_ops) + hw->link.ops = *ii->link_ops; /* EEPROM */ hw->eeprom.ops = *ii->eeprom_ops; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index ebf46af..0c65814 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -109,8 +109,8 @@ static u8 ixgbe_ones_comp_byte_add(u8 add1, u8 add2) * * Returns an error code on error. */ -static s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 *val, bool lock) +s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val, bool lock) { u32 swfw_mask = hw->phy.phy_semaphore_mask; int max_retry = 10; @@ -178,36 +178,6 @@ fail: } /** - * ixgbe_read_i2c_combined_generic - Perform I2C read combined operation - * @hw: pointer to the hardware structure - * @addr: I2C bus address to read from - * @reg: I2C device register to read from - * @val: pointer to location to receive read value - * - * Returns an error code on error. - */ -s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 *val) -{ - return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, true); -} - -/** - * ixgbe_read_i2c_combined_generic_unlocked - Unlocked I2C read combined - * @hw: pointer to the hardware structure - * @addr: I2C bus address to read from - * @reg: I2C device register to read from - * @val: pointer to location to receive read value - * - * Returns an error code on error. - */ -s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 *val) -{ - return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, false); -} - -/** * ixgbe_write_i2c_combined_generic_int - Perform I2C write combined operation * @hw: pointer to the hardware structure * @addr: I2C bus address to write to @@ -217,8 +187,8 @@ s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, * * Returns an error code on error. */ -static s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 val, bool lock) +s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 val, bool lock) { u32 swfw_mask = hw->phy.phy_semaphore_mask; int max_retry = 1; @@ -273,36 +243,6 @@ fail: } /** - * ixgbe_write_i2c_combined_generic - Perform I2C write combined operation - * @hw: pointer to the hardware structure - * @addr: I2C bus address to write to - * @reg: I2C device register to write to - * @val: value to write - * - * Returns an error code on error. - */ -s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, - u8 addr, u16 reg, u16 val) -{ - return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, true); -} - -/** - * ixgbe_write_i2c_combined_generic_unlocked - Unlocked I2C write combined - * @hw: pointer to the hardware structure - * @addr: I2C bus address to write to - * @reg: I2C device register to write to - * @val: value to write - * - * Returns an error code on error. - */ -s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, - u8 addr, u16 reg, u16 val) -{ - return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, false); -} - -/** * ixgbe_identify_phy_generic - Get physical layer module * @hw: pointer to hardware structure * diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index cc735ec..ecf05f8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -195,12 +195,8 @@ s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *sff8472_data); s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 eeprom_data); -s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 *val); -s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 *val); -s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 val); -s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 val); +s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *, u8 addr, u16 reg, + u16 *val, bool lock); +s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *, u8 addr, u16 reg, + u16 val, bool lock); #endif /* _IXGBE_PHY_H_ */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index f2f60cc..fe9c6ae 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3396,16 +3396,28 @@ struct ixgbe_phy_operations { s32 (*read_i2c_sff8472)(struct ixgbe_hw *, u8 , u8 *); s32 (*read_i2c_eeprom)(struct ixgbe_hw *, u8 , u8 *); s32 (*write_i2c_eeprom)(struct ixgbe_hw *, u8, u8); - s32 (*read_i2c_combined)(struct ixgbe_hw *, u8 addr, u16 reg, u16 *val); - s32 (*write_i2c_combined)(struct ixgbe_hw *, u8 addr, u16 reg, u16 val); s32 (*check_overtemp)(struct ixgbe_hw *); s32 (*set_phy_power)(struct ixgbe_hw *, bool on); s32 (*enter_lplu)(struct ixgbe_hw *); s32 (*handle_lasi)(struct ixgbe_hw *hw); - s32 (*read_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, - u16 *value); - s32 (*write_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, - u16 value); + s32 (*read_i2c_byte_unlocked)(struct ixgbe_hw *, u8 offset, u8 addr, + u8 *value); + s32 (*write_i2c_byte_unlocked)(struct ixgbe_hw *, u8 offset, u8 addr, + u8 value); +}; + +struct ixgbe_link_operations { + s32 (*read_link)(struct ixgbe_hw *, u8 addr, u16 reg, u16 *val); + s32 (*read_link_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + u16 *val); + s32 (*write_link)(struct ixgbe_hw *, u8 addr, u16 reg, u16 val); + s32 (*write_link_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + u16 val); +}; + +struct ixgbe_link_info { + struct ixgbe_link_operations ops; + u8 addr; }; struct ixgbe_eeprom_info { @@ -3510,6 +3522,7 @@ struct ixgbe_hw { struct ixgbe_addr_filter_info addr_ctrl; struct ixgbe_fc_info fc; struct ixgbe_phy_info phy; + struct ixgbe_link_info link; struct ixgbe_eeprom_info eeprom; struct ixgbe_bus_info bus; struct ixgbe_mbx_info mbx; @@ -3533,6 +3546,7 @@ struct ixgbe_info { const struct ixgbe_eeprom_operations *eeprom_ops; const struct ixgbe_phy_operations *phy_ops; const struct ixgbe_mbx_operations *mbx_ops; + const struct ixgbe_link_operations *link_ops; const u32 *mvals; }; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index c6669c27..252e52d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -36,6 +36,23 @@ static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; struct ixgbe_phy_info *phy = &hw->phy; + struct ixgbe_link_info *link = &hw->link; + + /* Start with X540 invariants, since so simular */ + ixgbe_get_invariants_X540(hw); + + if (mac->ops.get_media_type(hw) != ixgbe_media_type_copper) + phy->ops.set_phy_power = NULL; + + link->addr = IXGBE_CS4227; + + return 0; +} + +static s32 ixgbe_get_invariants_X550_a(struct ixgbe_hw *hw) +{ + struct ixgbe_mac_info *mac = &hw->mac; + struct ixgbe_phy_info *phy = &hw->phy; /* Start with X540 invariants, since so simular */ ixgbe_get_invariants_X540(hw); @@ -72,8 +89,7 @@ static void ixgbe_setup_mux_ctl(struct ixgbe_hw *hw) */ static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value) { - return hw->phy.ops.read_i2c_combined_unlocked(hw, IXGBE_CS4227, reg, - value); + return hw->link.ops.read_link_unlocked(hw, hw->link.addr, reg, value); } /** @@ -86,8 +102,7 @@ static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value) */ static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value) { - return hw->phy.ops.write_i2c_combined_unlocked(hw, IXGBE_CS4227, reg, - value); + return hw->link.ops.write_link_unlocked(hw, hw->link.addr, reg, value); } /** @@ -325,6 +340,68 @@ static s32 ixgbe_write_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr, return IXGBE_NOT_IMPLEMENTED; } +/** + * ixgbe_read_i2c_combined_generic - Perform I2C read combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to read from + * @reg: I2C device register to read from + * @val: pointer to location to receive read value + * + * Returns an error code on error. + **/ +static s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val) +{ + return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, true); +} + +/** + * ixgbe_read_i2c_combined_generic_unlocked - Do I2C read combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to read from + * @reg: I2C device register to read from + * @val: pointer to location to receive read value + * + * Returns an error code on error. + **/ +static s32 +ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val) +{ + return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, false); +} + +/** + * ixgbe_write_i2c_combined_generic - Perform I2C write combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to write to + * @reg: I2C device register to write to + * @val: value to write + * + * Returns an error code on error. + **/ +static s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, + u8 addr, u16 reg, u16 val) +{ + return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, true); +} + +/** + * ixgbe_write_i2c_combined_generic_unlocked - Do I2C write combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to write to + * @reg: I2C device register to write to + * @val: value to write + * + * Returns an error code on error. + **/ +static s32 +ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, + u8 addr, u16 reg, u16 val) +{ + return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, false); +} + /** ixgbe_init_eeprom_params_X550 - Initialize EEPROM params * @hw: pointer to hardware structure * @@ -1338,8 +1415,10 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, reg_val = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 0x1; else reg_val = (IXGBE_CS4227_EDC_MODE_SR << 1) | 0x1; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, - reg_slice, reg_val); + + status = hw->link.ops.write_link(hw, hw->link.addr, reg_slice, + reg_val); + return status; } @@ -3216,11 +3295,6 @@ static const struct ixgbe_phy_operations phy_ops_X550EM_x = { .identify = &ixgbe_identify_phy_x550em, .read_reg = &ixgbe_read_phy_reg_generic, .write_reg = &ixgbe_write_phy_reg_generic, - .read_i2c_combined = &ixgbe_read_i2c_combined_generic, - .write_i2c_combined = &ixgbe_write_i2c_combined_generic, - .read_i2c_combined_unlocked = &ixgbe_read_i2c_combined_generic_unlocked, - .write_i2c_combined_unlocked = - &ixgbe_write_i2c_combined_generic_unlocked, }; static const struct ixgbe_phy_operations phy_ops_x550em_a = { @@ -3233,6 +3307,13 @@ static const struct ixgbe_phy_operations phy_ops_x550em_a = { .write_reg_mdi = &ixgbe_write_phy_reg_mdi, }; +static const struct ixgbe_link_operations link_ops_x550em_x = { + .read_link = &ixgbe_read_i2c_combined_generic, + .read_link_unlocked = &ixgbe_read_i2c_combined_generic_unlocked, + .write_link = &ixgbe_write_i2c_combined_generic, + .write_link_unlocked = &ixgbe_write_i2c_combined_generic_unlocked, +}; + static const u32 ixgbe_mvals_X550[IXGBE_MVALS_IDX_LIMIT] = { IXGBE_MVALS_INIT(X550) }; @@ -3263,11 +3344,12 @@ const struct ixgbe_info ixgbe_X550EM_x_info = { .phy_ops = &phy_ops_X550EM_x, .mbx_ops = &mbx_ops_generic, .mvals = ixgbe_mvals_X550EM_x, + .link_ops = &link_ops_x550em_x, }; const struct ixgbe_info ixgbe_x550em_a_info = { .mac = ixgbe_mac_x550em_a, - .get_invariants = &ixgbe_get_invariants_X550_x, + .get_invariants = &ixgbe_get_invariants_X550_a, .mac_ops = &mac_ops_x550em_a, .eeprom_ops = &eeprom_ops_X550EM_x, .phy_ops = &phy_ops_x550em_a, -- cgit v1.1 From 55570b6f5e2caa37a5bbd634cbe5ed126d3656f9 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 12 Oct 2016 12:34:25 -0700 Subject: ixgbe: set device if before calling get_invariants Fix an issue where set_phy_power was NULL for X550 copper devices because get_invariants was called before hw->device_id was set. Signed-off-by: Emil Tantilov Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 9a62b55..a582bea 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5624,7 +5624,8 @@ static void ixgbe_init_dcb(struct ixgbe_adapter *adapter) * Fields are initialized based on PCI device information and * OS network device settings (MTU size). **/ -static int ixgbe_sw_init(struct ixgbe_adapter *adapter) +static int ixgbe_sw_init(struct ixgbe_adapter *adapter, + const struct ixgbe_info *ii) { struct ixgbe_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; @@ -5640,6 +5641,9 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) hw->subsystem_vendor_id = pdev->subsystem_vendor; hw->subsystem_device_id = pdev->subsystem_device; + /* get_invariants needs the device IDs */ + ii->get_invariants(hw); + /* Set common capability flags and settings */ rss = min_t(int, ixgbe_max_rss_indices(adapter), num_online_cpus()); adapter->ring_feature[RING_F_RSS].limit = rss; @@ -9532,10 +9536,8 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->phy.mdio.mdio_read = ixgbe_mdio_read; hw->phy.mdio.mdio_write = ixgbe_mdio_write; - ii->get_invariants(hw); - /* setup the private structure */ - err = ixgbe_sw_init(adapter); + err = ixgbe_sw_init(adapter, ii); if (err) goto err_sw_init; -- cgit v1.1 From 470739b56386dd59df046e84e54a075d8f965b84 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Thu, 3 Nov 2016 21:01:37 -0400 Subject: ixgbe: Add X553 FW ALEF support This patch add X553 FW ALEF support for B0. ALEF is the new unified FW. This contains updated register defines for ALEF speed configuration. Likewise it also removes the AN_CNTL_8 usage from the native SFI flow as it is no longer supported by FW. Signed-off-by: Don Skidmore Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 96 +++++++---- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 17 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 239 +++++++++++++++++++------- 3 files changed, 255 insertions(+), 97 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 0c65814..5dadae6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -243,6 +243,42 @@ fail: } /** + * ixgbe_probe_phy - Probe a single address for a PHY + * @hw: pointer to hardware structure + * @phy_addr: PHY address to probe + * + * Returns true if PHY found + **/ +static bool ixgbe_probe_phy(struct ixgbe_hw *hw, u16 phy_addr) +{ + u16 ext_ability = 0; + + hw->phy.mdio.prtad = phy_addr; + if (mdio45_probe(&hw->phy.mdio, phy_addr) != 0) + return false; + + if (ixgbe_get_phy_id(hw)) + return false; + + hw->phy.type = ixgbe_get_phy_type_from_id(hw->phy.id); + + if (hw->phy.type == ixgbe_phy_unknown) { + hw->phy.ops.read_reg(hw, + MDIO_PMA_EXTABLE, + MDIO_MMD_PMAPMD, + &ext_ability); + if (ext_ability & + (MDIO_PMA_EXTABLE_10GBT | + MDIO_PMA_EXTABLE_1000BT)) + hw->phy.type = ixgbe_phy_cu_unknown; + else + hw->phy.type = ixgbe_phy_generic; + } + + return true; +} + +/** * ixgbe_identify_phy_generic - Get physical layer module * @hw: pointer to hardware structure * @@ -251,7 +287,7 @@ fail: s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw) { u32 phy_addr; - u16 ext_ability = 0; + u32 status = IXGBE_ERR_PHY_ADDR_INVALID; if (!hw->phy.phy_semaphore_mask) { if (hw->bus.lan_id) @@ -260,37 +296,34 @@ s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw) hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM; } - if (hw->phy.type == ixgbe_phy_unknown) { - for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) { - hw->phy.mdio.prtad = phy_addr; - if (mdio45_probe(&hw->phy.mdio, phy_addr) == 0) { - ixgbe_get_phy_id(hw); - hw->phy.type = - ixgbe_get_phy_type_from_id(hw->phy.id); - - if (hw->phy.type == ixgbe_phy_unknown) { - hw->phy.ops.read_reg(hw, - MDIO_PMA_EXTABLE, - MDIO_MMD_PMAPMD, - &ext_ability); - if (ext_ability & - (MDIO_PMA_EXTABLE_10GBT | - MDIO_PMA_EXTABLE_1000BT)) - hw->phy.type = - ixgbe_phy_cu_unknown; - else - hw->phy.type = - ixgbe_phy_generic; - } + if (hw->phy.type != ixgbe_phy_unknown) + return 0; - return 0; - } + if (hw->phy.nw_mng_if_sel) { + phy_addr = (hw->phy.nw_mng_if_sel & + IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD) >> + IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT; + if (ixgbe_probe_phy(hw, phy_addr)) + return 0; + else + return IXGBE_ERR_PHY_ADDR_INVALID; + } + + for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) { + if (ixgbe_probe_phy(hw, phy_addr)) { + status = 0; + break; } - /* indicate no PHY found */ - hw->phy.mdio.prtad = MDIO_PRTAD_NONE; - return IXGBE_ERR_PHY_ADDR_INVALID; } - return 0; + + /* Certain media types do not have a phy so an address will not + * be found and the code will take this path. Caller has to + * decide if it is an error or not. + */ + if (status) + hw->phy.mdio.prtad = MDIO_PRTAD_NONE; + + return status; } /** @@ -367,6 +400,7 @@ static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) phy_type = ixgbe_phy_nl; break; case X557_PHY_ID: + case X557_PHY_ID2: phy_type = ixgbe_phy_x550em_ext_t; break; default: @@ -417,8 +451,7 @@ s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw) */ for (i = 0; i < 30; i++) { msleep(100); - hw->phy.ops.read_reg(hw, MDIO_CTRL1, - MDIO_MMD_PHYXS, &ctrl); + hw->phy.ops.read_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS, &ctrl); if (!(ctrl & MDIO_CTRL1_RESET)) { udelay(2); break; @@ -769,6 +802,7 @@ static s32 ixgbe_get_copper_speeds_supported(struct ixgbe_hw *hw) hw->phy.speeds_supported |= IXGBE_LINK_SPEED_5GB_FULL; break; case ixgbe_mac_X550EM_x: + case ixgbe_mac_x550em_a: hw->phy.speeds_supported &= ~IXGBE_LINK_SPEED_100_FULL; break; default: diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index fe9c6ae..856152f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1379,6 +1379,7 @@ struct ixgbe_thermal_sensor_data { #define X540_PHY_ID 0x01540200 #define X550_PHY_ID 0x01540220 #define X557_PHY_ID 0x01540240 +#define X557_PHY_ID2 0x01540250 #define QT2022_PHY_ID 0x0043A400 #define ATH_PHY_ID 0x03429050 #define AQ_FW_REV 0x20 @@ -3604,9 +3605,25 @@ struct ixgbe_info { #define IXGBE_KRM_DSP_TXFFE_STATE_5(P) ((P) ? 0x8638 : 0x4638) #define IXGBE_KRM_RX_TRN_LINKUP_CTRL(P) ((P) ? 0x8B00 : 0x4B00) #define IXGBE_KRM_PMD_DFX_BURNIN(P) ((P) ? 0x8E00 : 0x4E00) +#define IXGBE_KRM_PMD_FLX_MASK_ST20(P) ((P) ? 0x9054 : 0x5054) #define IXGBE_KRM_TX_COEFF_CTRL_1(P) ((P) ? 0x9520 : 0x5520) #define IXGBE_KRM_RX_ANA_CTL(P) ((P) ? 0x9A00 : 0x5A00) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SFI_10G_DA ~(0x3 << 20) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SFI_10G_SR BIT(20) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SFI_10G_LR (0x2 << 20) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SGMII_EN BIT(25) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_AN37_EN BIT(26) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_AN_EN BIT(27) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_10M ~(0x7 << 28) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_100M BIT(28) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_1G (0x2 << 28) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_10G (0x3 << 28) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_AN (0x4 << 28) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_2_5G (0x7 << 28) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_MASK (0x7 << 28) +#define IXGBE_KRM_PMD_FLX_MASK_ST20_FW_AN_RESTART BIT(31) + #define IXGBE_KRM_PORT_CAR_GEN_CTRL_NELB_32B BIT(9) #define IXGBE_KRM_PORT_CAR_GEN_CTRL_NELB_KRPCS BIT(11) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 252e52d..11fb433 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1281,6 +1281,53 @@ static s32 ixgbe_setup_ixfi_x550em_x(struct ixgbe_hw *hw) return status; } +/** + * ixgbe_restart_an_internal_phy_x550em - restart autonegotiation for the + * internal PHY + * @hw: pointer to hardware structure + **/ +static s32 ixgbe_restart_an_internal_phy_x550em(struct ixgbe_hw *hw) +{ + s32 status; + u32 link_ctrl; + + /* Restart auto-negotiation. */ + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &link_ctrl); + + if (status) { + hw_dbg(hw, "Auto-negotiation did not complete\n"); + return status; + } + + link_ctrl |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; + status = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, link_ctrl); + + if (hw->mac.type == ixgbe_mac_x550em_a) { + u32 flx_mask_st20; + + /* Indicate to FW that AN restart has been asserted */ + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &flx_mask_st20); + + if (status) { + hw_dbg(hw, "Auto-negotiation did not complete\n"); + return status; + } + + flx_mask_st20 |= IXGBE_KRM_PMD_FLX_MASK_ST20_FW_AN_RESTART; + status = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, flx_mask_st20); + } + + return status; +} + /** ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI mode. * @hw: pointer to hardware structure * @speed: the link speed to force @@ -1330,16 +1377,7 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) } /* Toggle port SW reset by AN reset. */ - status = ixgbe_read_iosf_sb_reg_x550(hw, - IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); - if (status) - return status; - - reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; - status = ixgbe_write_iosf_sb_reg_x550(hw, - IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + status = ixgbe_restart_an_internal_phy_x550em(hw); return status; } @@ -1423,6 +1461,55 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, } /** + * ixgbe_setup_sfi_x550a - Configure the internal PHY for native SFI mode + * @hw: pointer to hardware structure + * @speed: the link speed to force + * + * Configures the integrated PHY for native SFI mode. Used to connect the + * internal PHY directly to an SFP cage, without autonegotiation. + **/ +static s32 ixgbe_setup_sfi_x550a(struct ixgbe_hw *hw, ixgbe_link_speed *speed) +{ + struct ixgbe_mac_info *mac = &hw->mac; + s32 status; + u32 reg_val; + + /* Disable all AN and force speed to 10G Serial. */ + status = mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + if (status) + return status; + + reg_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_AN_EN; + reg_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_AN37_EN; + reg_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_SGMII_EN; + reg_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_MASK; + + /* Select forced link speed for internal PHY. */ + switch (*speed) { + case IXGBE_LINK_SPEED_10GB_FULL: + reg_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_10G; + break; + case IXGBE_LINK_SPEED_1GB_FULL: + reg_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_1G; + break; + default: + /* Other link speeds are not supported by internal PHY. */ + return IXGBE_ERR_LINK_SETUP; + } + + status = mac->ops.write_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + + /* Toggle port SW reset by AN reset. */ + status = ixgbe_restart_an_internal_phy_x550em(hw); + + return status; +} + +/** * ixgbe_setup_mac_link_sfp_n - Setup internal PHY for native SFP * @hw: pointer to hardware structure * @@ -1434,45 +1521,39 @@ ixgbe_setup_mac_link_sfp_n(struct ixgbe_hw *hw, ixgbe_link_speed speed, { bool setup_linear = false; u32 reg_phy_int; - s32 rc; + s32 ret_val; /* Check if SFP module is supported and linear */ - rc = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); + ret_val = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); /* If no SFP module present, then return success. Return success since * SFP not present error is not excepted in the setup MAC link flow. */ - if (rc == IXGBE_ERR_SFP_NOT_PRESENT) + if (ret_val == IXGBE_ERR_SFP_NOT_PRESENT) return 0; - if (!rc) - return rc; + if (!ret_val) + return ret_val; - /* Configure internal PHY for native SFI */ - rc = hw->mac.ops.read_iosf_sb_reg(hw, - IXGBE_KRM_AN_CNTL_8(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, - ®_phy_int); - if (rc) - return rc; + /* Configure internal PHY for native SFI based on module type */ + ret_val = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_phy_int); + if (!ret_val) + return ret_val; - if (setup_linear) { - reg_phy_int &= ~IXGBE_KRM_AN_CNTL_8_LIMITING; - reg_phy_int |= IXGBE_KRM_AN_CNTL_8_LINEAR; - } else { - reg_phy_int |= IXGBE_KRM_AN_CNTL_8_LIMITING; - reg_phy_int &= ~IXGBE_KRM_AN_CNTL_8_LINEAR; - } + reg_phy_int &= IXGBE_KRM_PMD_FLX_MASK_ST20_SFI_10G_DA; + if (!setup_linear) + reg_phy_int |= IXGBE_KRM_PMD_FLX_MASK_ST20_SFI_10G_SR; - rc = hw->mac.ops.write_iosf_sb_reg(hw, - IXGBE_KRM_AN_CNTL_8(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, - reg_phy_int); - if (rc) - return rc; + ret_val = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_phy_int); + if (!ret_val) + return ret_val; - /* Setup XFI/SFI internal link */ - return ixgbe_setup_ixfi_x550em(hw, &speed); + /* Setup SFI internal link. */ + return ixgbe_setup_sfi_x550a(hw, &speed); } /** @@ -1488,19 +1569,19 @@ ixgbe_setup_mac_link_sfp_x550a(struct ixgbe_hw *hw, ixgbe_link_speed speed, u32 reg_slice, slice_offset; bool setup_linear = false; u16 reg_phy_ext; - s32 rc; + s32 ret_val; /* Check if SFP module is supported and linear */ - rc = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); + ret_val = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); /* If no SFP module present, then return success. Return success since * SFP not present error is not excepted in the setup MAC link flow. */ - if (rc == IXGBE_ERR_SFP_NOT_PRESENT) + if (ret_val == IXGBE_ERR_SFP_NOT_PRESENT) return 0; - if (!rc) - return rc; + if (!ret_val) + return ret_val; /* Configure internal PHY for KR/KX. */ ixgbe_setup_kr_speed_x550em(hw, speed); @@ -1509,10 +1590,10 @@ ixgbe_setup_mac_link_sfp_x550a(struct ixgbe_hw *hw, ixgbe_link_speed speed, return IXGBE_ERR_PHY_ADDR_INVALID; /* Get external PHY device id */ - rc = hw->phy.ops.read_reg(hw, IXGBE_CS4227_GLOBAL_ID_MSB, + ret_val = hw->phy.ops.read_reg(hw, IXGBE_CS4227_GLOBAL_ID_MSB, IXGBE_MDIO_ZERO_DEV_TYPE, ®_phy_ext); - if (rc) - return rc; + if (ret_val) + return ret_val; /* When configuring quad port CS4223, the MAC instance is part * of the slice offset. @@ -1625,7 +1706,7 @@ ixgbe_setup_sgmii(struct ixgbe_hw *hw, __always_unused ixgbe_link_speed speed, __always_unused bool autoneg_wait_to_complete) { struct ixgbe_mac_info *mac = &hw->mac; - u32 lval, sval; + u32 lval, sval, flx_val; s32 rc; rc = mac->ops.read_iosf_sb_reg(hw, @@ -1659,11 +1740,31 @@ ixgbe_setup_sgmii(struct ixgbe_hw *hw, __always_unused ixgbe_link_speed speed, if (rc) return rc; - lval |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; + rc = mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &flx_val); + if (rc) + return rc; + + rc = mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, &flx_val); + if (rc) + return rc; + + flx_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_MASK; + flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_1G; + flx_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_AN_EN; + flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SGMII_EN; + flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_AN37_EN; + rc = mac->ops.write_iosf_sb_reg(hw, - IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, lval); + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, flx_val); + if (rc) + return rc; + rc = ixgbe_restart_an_internal_phy_x550em(hw); return rc; } @@ -2020,13 +2121,31 @@ static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *hw, if (speed & IXGBE_LINK_SPEED_1GB_FULL) reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX; - /* Restart auto-negotiation. */ - reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; status = hw->mac.ops.write_iosf_sb_reg(hw, IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); - return status; + if (hw->mac.type == ixgbe_mac_x550em_a) { + /* Set lane mode to KR auto negotiation */ + status = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + + if (status) + return status; + + reg_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_MASK; + reg_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_AN; + reg_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_AN_EN; + reg_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_AN37_EN; + reg_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_SGMII_EN; + + status = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + } + + return ixgbe_restart_an_internal_phy_x550em(hw); } /** ixgbe_setup_kx4_x550em - Configure the KX4 PHY. @@ -2894,7 +3013,6 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw, static s32 ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *hw) { s32 status = 0; - u32 link_ctrl = 0; u32 an_cntl = 0; /* Validate the requested mode */ @@ -2965,18 +3083,7 @@ static s32 ixgbe_setup_fc_backplane_x550em_a(struct ixgbe_hw *hw) IXGBE_SB_IOSF_TARGET_KR_PHY, an_cntl); /* Restart auto-negotiation. */ - status = hw->mac.ops.read_iosf_sb_reg(hw, - IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, &link_ctrl); - if (status) { - hw_dbg(hw, "Auto-Negotiation did not complete\n"); - return status; - } - - link_ctrl |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; - status = hw->mac.ops.write_iosf_sb_reg(hw, - IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, link_ctrl); + status = ixgbe_restart_an_internal_phy_x550em(hw); return status; } @@ -3243,7 +3350,7 @@ static struct ixgbe_mac_operations mac_ops_x550em_a = { .acquire_swfw_sync = ixgbe_acquire_swfw_sync_x550em_a, .release_swfw_sync = ixgbe_release_swfw_sync_x550em_a, .setup_fc = ixgbe_setup_fc_x550em, - .fc_autoneg = NULL, /* defined later */ + .fc_autoneg = ixgbe_fc_autoneg, .read_iosf_sb_reg = ixgbe_read_iosf_sb_reg_x550a, .write_iosf_sb_reg = ixgbe_write_iosf_sb_reg_x550a, }; -- cgit v1.1 From 5f1c3589b0f0feb2c99b985d3a3bbda0dd790626 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Fri, 4 Nov 2016 16:46:16 -0400 Subject: ixgbe: Correct X550 phy ID We were using an old Alpha version of the X550 phy ID. This was leading to unnecessary queries of the PHY. I removed the old ID (which shouldn't be on any HW) and add the two that are. Signed-off-by: Don Skidmore Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 3 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 5dadae6..3b83620 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -389,7 +389,8 @@ static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) case TN1010_PHY_ID: phy_type = ixgbe_phy_tn; break; - case X550_PHY_ID: + case X550_PHY_ID2: + case X550_PHY_ID3: case X540_PHY_ID: phy_type = ixgbe_phy_aq; break; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 856152f..cf21273 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1377,7 +1377,8 @@ struct ixgbe_thermal_sensor_data { #define TN1010_PHY_ID 0x00A19410 #define TNX_FW_REV 0xB #define X540_PHY_ID 0x01540200 -#define X550_PHY_ID 0x01540220 +#define X550_PHY_ID2 0x01540223 +#define X550_PHY_ID3 0x01540221 #define X557_PHY_ID 0x01540240 #define X557_PHY_ID2 0x01540250 #define QT2022_PHY_ID 0x0043A400 -- cgit v1.1 From 520288218cf233f442aa815496a95622c2672c62 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Mon, 24 Oct 2016 15:36:38 -0700 Subject: ixgbe: ixgbe_atr() should access udp_hdr(skb) only for UDP packets Commit 9f12df906cd8 ("ixgbe: Store VXLAN port number in network order") incorrectly checks for hdr.ipv4->protocol != IPPROTO_UDP in ixgbe_atr(). This check should be for "==" instead. Signed-off-by: Sowmini Varadhan Reviewed-by: Alexander Duyck Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index a582bea..e4a3227 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7662,7 +7662,7 @@ static void ixgbe_atr(struct ixgbe_ring *ring, hdr.network = skb_network_header(skb); if (skb->encapsulation && first->protocol == htons(ETH_P_IP) && - hdr.ipv4->protocol != IPPROTO_UDP) { + hdr.ipv4->protocol == IPPROTO_UDP) { struct ixgbe_adapter *adapter = q_vector->adapter; /* verify the port is recognized as VXLAN */ -- cgit v1.1 From 9f3c7504fa005e2aab5ba9e1f2d8b4f4fcc6c077 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Mon, 24 Oct 2016 15:36:39 -0700 Subject: ixgbe: ixgbe_atr() compute l4_proto only if non-paged data has network/transport headers For some Tx paths (e.g., tpacket_snd()), ixgbe_atr may be passed down an sk_buff that has the network and transport header in the paged data, so it needs to make sure these headers are available in the headlen bytes to calculate the l4_proto. This patch expect that network and transport headers are already available in the non-paged header dat. The assumption is that the caller has set this up if l4_proto based Tx steering is desired. Signed-off-by: Sowmini Varadhan Reviewed-by: Alexander Duyck Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index e4a3227..2436984 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -54,6 +54,7 @@ #include #include #include +#include #include "ixgbe.h" #include "ixgbe_common.h" @@ -7660,11 +7661,17 @@ static void ixgbe_atr(struct ixgbe_ring *ring, /* snag network header to get L4 type and address */ skb = first->skb; hdr.network = skb_network_header(skb); + if (unlikely(hdr.network <= skb->data)) + return; if (skb->encapsulation && first->protocol == htons(ETH_P_IP) && hdr.ipv4->protocol == IPPROTO_UDP) { struct ixgbe_adapter *adapter = q_vector->adapter; + if (unlikely(skb_tail_pointer(skb) < hdr.network + + VXLAN_HEADROOM)) + return; + /* verify the port is recognized as VXLAN */ if (adapter->vxlan_port && udp_hdr(skb)->dest == adapter->vxlan_port) @@ -7675,6 +7682,12 @@ static void ixgbe_atr(struct ixgbe_ring *ring, hdr.network = skb_inner_network_header(skb); } + /* Make sure we have at least [minimum IPv4 header + TCP] + * or [IPv6 header] bytes + */ + if (unlikely(skb_tail_pointer(skb) < hdr.network + 40)) + return; + /* Currently only IPv4/IPv6 with TCP is supported */ switch (hdr.ipv4->version) { case IPVERSION: @@ -7694,6 +7707,10 @@ static void ixgbe_atr(struct ixgbe_ring *ring, if (l4_proto != IPPROTO_TCP) return; + if (unlikely(skb_tail_pointer(skb) < hdr.network + + hlen + sizeof(struct tcphdr))) + return; + th = (struct tcphdr *)(hdr.network + hlen); /* skip this packet since the socket is closing */ -- cgit v1.1 From eeffceee421b17a1d484679d738f278fbaa01384 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 28 Oct 2016 10:46:39 -0700 Subject: ixgbevf: Handle previously-freed msix_entries The msix_entries memory can be freed by a previous suspend or remove, so don't crash on close when it isn't there. Also only clear the interrupts when the interface is up, because there aren't any when it is not up. Signed-off-by: Mark Rustad Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index d2775f0..d316f50 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1498,6 +1498,9 @@ static void ixgbevf_free_irq(struct ixgbevf_adapter *adapter) { int i, q_vectors; + if (!adapter->msix_entries) + return; + q_vectors = adapter->num_msix_vectors; i = q_vectors - 1; @@ -2552,6 +2555,9 @@ static void ixgbevf_free_q_vectors(struct ixgbevf_adapter *adapter) **/ static void ixgbevf_reset_interrupt_capability(struct ixgbevf_adapter *adapter) { + if (!adapter->msix_entries) + return; + pci_disable_msix(adapter->pdev); kfree(adapter->msix_entries); adapter->msix_entries = NULL; @@ -3794,11 +3800,10 @@ static int ixgbevf_suspend(struct pci_dev *pdev, pm_message_t state) ixgbevf_free_irq(adapter); ixgbevf_free_all_tx_resources(adapter); ixgbevf_free_all_rx_resources(adapter); + ixgbevf_clear_interrupt_scheme(adapter); rtnl_unlock(); } - ixgbevf_clear_interrupt_scheme(adapter); - #ifdef CONFIG_PM retval = pci_save_state(pdev); if (retval) -- cgit v1.1 From b6e417977da87b246e356797c2af42002343aa9f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 2 Nov 2016 12:49:57 +0000 Subject: mlxsw: Remove unused including Remove including that don't need it. Signed-off-by: Wei Yongjun Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 - drivers/net/ethernet/mellanox/mlxsw/switchib.c | 1 - drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 8bca020..a5433e4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c index ec0b27e..1552594 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -43,7 +43,6 @@ #include #include #include -#include #include "pci.h" #include "core.h" diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 5208764..60f19fb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -45,7 +45,6 @@ #include #include #include -#include #include "pci.h" #include "core.h" -- cgit v1.1 From a4c48ad7370ba57ad899c2a4abd223ba7c6d9c32 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Fri, 4 Nov 2016 18:54:05 +0100 Subject: stmmac: dwmac-sti: remove useless of_node check Since dwmac-sti is a DT only driver checking for OF node is not necessary. Signed-off-by: Joachim Eastwood Acked-by: Giuseppe Cavallaro Tested-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index 58c05ac..075ed42 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -270,9 +270,6 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, struct regmap *regmap; int err; - if (!np) - return -EINVAL; - /* clk selection from extra syscfg register */ dwmac->clk_sel_reg = -ENXIO; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf"); -- cgit v1.1 From b211aa655d264bd2a1f8b05eb75aeda508bd0ade Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Fri, 4 Nov 2016 18:54:06 +0100 Subject: stmmac: dwmac-sti: remove clk NULL checks Since sti_dwmac_parse_data() sets dwmac->clk to NULL if not clock was provided in DT and NULL is a valid clock there is no need to check for NULL before using this clock. Signed-off-by: Joachim Eastwood Acked-by: Giuseppe Cavallaro Tested-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index 075ed42..f009bf4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -191,7 +191,7 @@ static void stih4xx_fix_retime_src(void *priv, u32 spd) } } - if (src == TX_RETIME_SRC_CLKGEN && dwmac->clk && freq) + if (src == TX_RETIME_SRC_CLKGEN && freq) clk_set_rate(dwmac->clk, freq); regmap_update_bits(dwmac->regmap, reg, STIH4XX_RETIME_SRC_MASK, @@ -222,7 +222,7 @@ static void stid127_fix_retime_src(void *priv, u32 spd) freq = DWMAC_2_5MHZ; } - if (dwmac->clk && freq) + if (freq) clk_set_rate(dwmac->clk, freq); regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val); @@ -238,8 +238,7 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv) u32 reg = dwmac->ctrl_reg; u32 val; - if (dwmac->clk) - clk_prepare_enable(dwmac->clk); + clk_prepare_enable(dwmac->clk); if (of_property_read_bool(np, "st,gmac_en")) regmap_update_bits(regmap, reg, EN_MASK, EN); @@ -258,8 +257,7 @@ static void sti_dwmac_exit(struct platform_device *pdev, void *priv) { struct sti_dwmac *dwmac = priv; - if (dwmac->clk) - clk_disable_unprepare(dwmac->clk); + clk_disable_unprepare(dwmac->clk); } static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, struct platform_device *pdev) -- cgit v1.1 From 2517cfd4ca78359dfc2bebbb121207d712c9b319 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Fri, 4 Nov 2016 18:54:07 +0100 Subject: stmmac: dwmac-sti: add PM ops and resume function Implement PM callbacks and driver remove in the driver instead of relying on the init/exit hooks in stmmac_platform. This gives the driver more flexibility in how the code is organized. Eventually the init/exit callbacks will be deprecated in favor of the standard PM callbacks and driver remove function. Signed-off-by: Joachim Eastwood Acked-by: Giuseppe Cavallaro Tested-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 47 +++++++++++++++++++------ 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index f009bf4..bd6db22 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -253,12 +253,6 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv) return 0; } -static void sti_dwmac_exit(struct platform_device *pdev, void *priv) -{ - struct sti_dwmac *dwmac = priv; - - clk_disable_unprepare(dwmac->clk); -} static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, struct platform_device *pdev) { @@ -352,8 +346,6 @@ static int sti_dwmac_probe(struct platform_device *pdev) dwmac->fix_retime_src = data->fix_retime_src; plat_dat->bsp_priv = dwmac; - plat_dat->init = sti_dwmac_init; - plat_dat->exit = sti_dwmac_exit; plat_dat->fix_mac_speed = data->fix_retime_src; ret = sti_dwmac_init(pdev, plat_dat->bsp_priv); @@ -363,6 +355,41 @@ static int sti_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); } +static int sti_dwmac_remove(struct platform_device *pdev) +{ + struct sti_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev); + int ret = stmmac_dvr_remove(&pdev->dev); + + clk_disable_unprepare(dwmac->clk); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int sti_dwmac_suspend(struct device *dev) +{ + struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); + int ret = stmmac_suspend(dev); + + clk_disable_unprepare(dwmac->clk); + + return ret; +} + +static int sti_dwmac_resume(struct device *dev) +{ + struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); + struct platform_device *pdev = to_platform_device(dev); + + sti_dwmac_init(pdev, dwmac); + + return stmmac_resume(dev); +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(sti_dwmac_pm_ops, sti_dwmac_suspend, + sti_dwmac_resume); + static const struct sti_dwmac_of_data stih4xx_dwmac_data = { .fix_retime_src = stih4xx_fix_retime_src, }; @@ -382,10 +409,10 @@ MODULE_DEVICE_TABLE(of, sti_dwmac_match); static struct platform_driver sti_dwmac_driver = { .probe = sti_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove = sti_dwmac_remove, .driver = { .name = "sti-dwmac", - .pm = &stmmac_pltfr_pm_ops, + .pm = &sti_dwmac_pm_ops, .of_match_table = sti_dwmac_match, }, }; -- cgit v1.1 From 06a6e829bdaf4df5616db2d47978e10e88f1dc32 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Fri, 4 Nov 2016 18:54:08 +0100 Subject: stmmac: dwmac-sti: move st, gmac_en parsing to sti_dwmac_parse_data The sti_dwmac_init() function is called both from probe and resume. Since DT properties doesn't change between suspend/resume cycles move parsing of this parameter into sti_dwmac_parse_data() where it belongs. Signed-off-by: Joachim Eastwood Acked-by: Giuseppe Cavallaro Tested-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index bd6db22..269c7a5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -128,6 +128,7 @@ struct sti_dwmac { int clk_sel_reg; /* GMAC ext clk selection register */ struct device *dev; struct regmap *regmap; + bool gmac_en; u32 speed; void (*fix_retime_src)(void *priv, unsigned int speed); }; @@ -233,14 +234,12 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv) struct sti_dwmac *dwmac = priv; struct regmap *regmap = dwmac->regmap; int iface = dwmac->interface; - struct device *dev = dwmac->dev; - struct device_node *np = dev->of_node; u32 reg = dwmac->ctrl_reg; u32 val; clk_prepare_enable(dwmac->clk); - if (of_property_read_bool(np, "st,gmac_en")) + if (dwmac->gmac_en) regmap_update_bits(regmap, reg, EN_MASK, EN); regmap_update_bits(regmap, reg, MII_PHY_SEL_MASK, phy_intf_sels[iface]); @@ -281,6 +280,7 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, dwmac->dev = dev; dwmac->interface = of_get_phy_mode(np); dwmac->regmap = regmap; + dwmac->gmac_en = of_property_read_bool(np, "st,gmac_en"); dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk"); dwmac->tx_retime_src = TX_RETIME_SRC_NA; dwmac->speed = SPEED_100; -- cgit v1.1 From b89cbfb01a2855b5c4e2f9121195439ac210d65b Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Fri, 4 Nov 2016 18:54:09 +0100 Subject: stmmac: dwmac-sti: move clk_prepare_enable out of init and add error handling Add clock error handling to probe and in the process move clock enabling out of sti_dwmac_init() to make this easier. Signed-off-by: Joachim Eastwood Acked-by: Giuseppe Cavallaro Tested-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index 269c7a5..b71888a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -237,8 +237,6 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv) u32 reg = dwmac->ctrl_reg; u32 val; - clk_prepare_enable(dwmac->clk); - if (dwmac->gmac_en) regmap_update_bits(regmap, reg, EN_MASK, EN); @@ -348,11 +346,23 @@ static int sti_dwmac_probe(struct platform_device *pdev) plat_dat->bsp_priv = dwmac; plat_dat->fix_mac_speed = data->fix_retime_src; - ret = sti_dwmac_init(pdev, plat_dat->bsp_priv); + ret = clk_prepare_enable(dwmac->clk); if (ret) return ret; - return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + ret = sti_dwmac_init(pdev, plat_dat->bsp_priv); + if (ret) + goto disable_clk; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + goto disable_clk; + + return 0; + +disable_clk: + clk_disable_unprepare(dwmac->clk); + return ret; } static int sti_dwmac_remove(struct platform_device *pdev) @@ -381,6 +391,7 @@ static int sti_dwmac_resume(struct device *dev) struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); struct platform_device *pdev = to_platform_device(dev); + clk_prepare_enable(dwmac->clk); sti_dwmac_init(pdev, dwmac); return stmmac_resume(dev); -- cgit v1.1 From 0eebedc2fd284e145fe054a87254b112f9b5ada4 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Fri, 4 Nov 2016 18:54:10 +0100 Subject: stmmac: dwmac-sti: clean up and rename sti_dwmac_init Rename sti_dwmac_init to sti_dwmac_set_mode which is a better description for what it really does. Signed-off-by: Joachim Eastwood Acked-by: Giuseppe Cavallaro Tested-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index b71888a..ac17bff 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -229,9 +229,8 @@ static void stid127_fix_retime_src(void *priv, u32 spd) regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val); } -static int sti_dwmac_init(struct platform_device *pdev, void *priv) +static int sti_dwmac_set_mode(struct sti_dwmac *dwmac) { - struct sti_dwmac *dwmac = priv; struct regmap *regmap = dwmac->regmap; int iface = dwmac->interface; u32 reg = dwmac->ctrl_reg; @@ -245,7 +244,7 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv) val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII; regmap_update_bits(regmap, reg, ENMII_MASK, val); - dwmac->fix_retime_src(priv, dwmac->speed); + dwmac->fix_retime_src(dwmac, dwmac->speed); return 0; } @@ -350,7 +349,7 @@ static int sti_dwmac_probe(struct platform_device *pdev) if (ret) return ret; - ret = sti_dwmac_init(pdev, plat_dat->bsp_priv); + ret = sti_dwmac_set_mode(dwmac); if (ret) goto disable_clk; @@ -389,10 +388,9 @@ static int sti_dwmac_suspend(struct device *dev) static int sti_dwmac_resume(struct device *dev) { struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); - struct platform_device *pdev = to_platform_device(dev); clk_prepare_enable(dwmac->clk); - sti_dwmac_init(pdev, dwmac); + sti_dwmac_set_mode(dwmac); return stmmac_resume(dev); } -- cgit v1.1 From cb42d4b64b1af1de70297c695644b178ff0dd5b7 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Fri, 4 Nov 2016 18:54:11 +0100 Subject: stmmac: dwmac-sti: remove unused priv dev member The dev member of struct sti_dwmac is not used anywhere in the driver so lets just remove it. Signed-off-by: Joachim Eastwood Acked-by: Giuseppe Cavallaro Tested-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index ac17bff..c9006ab 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -126,7 +126,6 @@ struct sti_dwmac { struct clk *clk; /* PHY clock */ u32 ctrl_reg; /* GMAC glue-logic control register */ int clk_sel_reg; /* GMAC ext clk selection register */ - struct device *dev; struct regmap *regmap; bool gmac_en; u32 speed; @@ -274,7 +273,6 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, return err; } - dwmac->dev = dev; dwmac->interface = of_get_phy_mode(np); dwmac->regmap = regmap; dwmac->gmac_en = of_property_read_bool(np, "st,gmac_en"); -- cgit v1.1 From 5b4e2900512321435a5cd7dd77f58f23f3109950 Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Fri, 4 Nov 2016 01:10:56 -0400 Subject: net: phy: broadcom: add bcm54xx_auxctl_read Add a helper function to read the AUXCTL register for the BCM54xx. This mirrors the bcm54xx_auxctl_write function already present in the code. Signed-off-by: Jon Mason Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/broadcom.c | 10 ++++++++++ include/linux/brcmphy.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 583ef8a..3a64b3d 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -30,6 +30,16 @@ MODULE_DESCRIPTION("Broadcom PHY driver"); MODULE_AUTHOR("Maciej W. Rozycki"); MODULE_LICENSE("GPL"); +static int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) +{ + /* The register must be written to both the Shadow Register Select and + * the Shadow Read Register Selector + */ + phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | + regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); + return phy_read(phydev, MII_BCM54XX_AUX_CTL); +} + static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) { return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 60def78..0ed6691 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -110,6 +110,7 @@ #define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200 #define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000 #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007 +#define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12 #define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007 -- cgit v1.1 From 3b9feb60e10e00830f86431b6717fab654664b6b Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Fri, 4 Nov 2016 01:10:57 -0400 Subject: Documentation: devicetree: add PHY lane swap binding Add the documentation for PHY lane swapping. This is a boolean entry to notify the phy device drivers that the TX/RX lanes need to be swapped. Signed-off-by: Jon Mason Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/phy.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt index bc1c3c8..4627da3 100644 --- a/Documentation/devicetree/bindings/net/phy.txt +++ b/Documentation/devicetree/bindings/net/phy.txt @@ -35,6 +35,10 @@ Optional Properties: - broken-turn-around: If set, indicates the PHY device does not correctly release the turn around line low at the end of a MDIO transaction. +- enet-phy-lane-swap: If set, indicates the PHY will swap the TX/RX lanes to + compensate for the board being designed with the lanes swapped. + + Example: ethernet-phy@0 { -- cgit v1.1 From b14995ac2527b43a75c9190fbd4efd43fb1f4562 Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Fri, 4 Nov 2016 01:10:58 -0400 Subject: net: phy: broadcom: Add BCM54810 PHY entry The BCM54810 PHY requires some semi-unique configuration, which results in some additional configuration in addition to the standard config. Also, some users of the BCM54810 require the PHY lanes to be swapped. Since there is no way to detect this, add a device tree query to see if it is applicable. Inspired-by: Vikas Soni Signed-off-by: Jon Mason Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 2 +- drivers/net/phy/broadcom.c | 58 +++++++++++++++++++++++++++++++++++++++++++++- include/linux/brcmphy.h | 9 +++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index ff31c10..d3fcfd2 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -217,7 +217,7 @@ config BROADCOM_PHY select BCM_NET_PHYLIB ---help--- Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464, - BCM5481 and BCM5482 PHYs. + BCM5481, BCM54810 and BCM5482 PHYs. config CICADA_PHY tristate "Cicada PHYs" diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 3a64b3d..b1e32e9 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -18,7 +18,7 @@ #include #include #include - +#include #define BRCM_PHY_MODEL(phydev) \ ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) @@ -45,6 +45,34 @@ static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); } +static int bcm54810_config(struct phy_device *phydev) +{ + int rc, val; + + val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); + val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; + rc = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, + val); + if (rc < 0) + return rc; + + val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); + val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; + val |= MII_BCM54XX_AUXCTL_MISC_WREN; + rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, + val); + if (rc < 0) + return rc; + + val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL); + val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; + rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); + if (rc < 0) + return rc; + + return 0; +} + /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */ static int bcm50610_a0_workaround(struct phy_device *phydev) { @@ -217,6 +245,12 @@ static int bcm54xx_config_init(struct phy_device *phydev) (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) bcm54xx_adjust_rxrefclk(phydev); + if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) { + err = bcm54810_config(phydev); + if (err) + return err; + } + bcm54xx_phydsp_config(phydev); return 0; @@ -314,6 +348,7 @@ static int bcm5482_read_status(struct phy_device *phydev) static int bcm5481_config_aneg(struct phy_device *phydev) { + struct device_node *np = phydev->mdio.dev.of_node; int ret; /* Aneg firsly. */ @@ -344,6 +379,14 @@ static int bcm5481_config_aneg(struct phy_device *phydev) phy_write(phydev, 0x18, reg); } + if (of_property_read_bool(np, "enet-phy-lane-swap")) { + /* Lane Swap - Undocumented register...magic! */ + ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9, + 0x11B); + if (ret < 0) + return ret; + } + return ret; } @@ -578,6 +621,18 @@ static struct phy_driver broadcom_drivers[] = { .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, }, { + .phy_id = PHY_ID_BCM54810, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM54810", + .features = PHY_GBIT_FEATURES | + SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, + .config_init = bcm54xx_config_init, + .config_aneg = bcm5481_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, +}, { .phy_id = PHY_ID_BCM5482, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5482", @@ -661,6 +716,7 @@ static struct mdio_device_id __maybe_unused broadcom_tbl[] = { { PHY_ID_BCM54616S, 0xfffffff0 }, { PHY_ID_BCM5464, 0xfffffff0 }, { PHY_ID_BCM5481, 0xfffffff0 }, + { PHY_ID_BCM54810, 0xfffffff0 }, { PHY_ID_BCM5482, 0xfffffff0 }, { PHY_ID_BCM50610, 0xfffffff0 }, { PHY_ID_BCM50610M, 0xfffffff0 }, diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 0ed6691..848dc50 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -13,6 +13,7 @@ #define PHY_ID_BCM5241 0x0143bc30 #define PHY_ID_BCMAC131 0x0143bc70 #define PHY_ID_BCM5481 0x0143bca0 +#define PHY_ID_BCM54810 0x03625d00 #define PHY_ID_BCM5482 0x0143bcb0 #define PHY_ID_BCM5411 0x00206070 #define PHY_ID_BCM5421 0x002060e0 @@ -56,6 +57,7 @@ #define PHY_BRCM_EXT_IBND_TX_ENABLE 0x00002000 #define PHY_BRCM_CLEAR_RGMII_MODE 0x00004000 #define PHY_BRCM_DIS_TXCRXC_NOENRGY 0x00008000 + /* Broadcom BCM7xxx specific workarounds */ #define PHY_BRCM_7XXX_REV(x) (((x) >> 8) & 0xff) #define PHY_BRCM_7XXX_PATCH(x) ((x) & 0xff) @@ -111,6 +113,7 @@ #define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000 #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007 #define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12 +#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN (1 << 8) #define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007 @@ -192,6 +195,12 @@ #define BCM5482_SSD_SGMII_SLAVE_EN 0x0002 /* Slave mode enable */ #define BCM5482_SSD_SGMII_SLAVE_AD 0x0001 /* Slave auto-detection */ +/* BCM54810 Registers */ +#define BCM54810_EXP_BROADREACH_LRE_MISC_CTL (MII_BCM54XX_EXP_SEL_ER + 0x90) +#define BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN (1 << 0) +#define BCM54810_SHD_CLK_CTL 0x3 +#define BCM54810_SHD_CLK_CTL_GTXCLK_EN (1 << 9) + /*****************************************************************************/ /* Fast Ethernet Transceiver definitions. */ -- cgit v1.1 From 0086f0944f8f2bfc3f029dcd4759b96dfb82a2af Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Fri, 4 Nov 2016 01:10:59 -0400 Subject: Documentation: devicetree: net: add NS2 bindings to amac Clean-up the documentation to the bgmac-amac driver, per suggestion by Rob Herring, and add details for NS2 support. Signed-off-by: Jon Mason Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/brcm,amac.txt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/net/brcm,amac.txt b/Documentation/devicetree/bindings/net/brcm,amac.txt index ba5ecc1..2fefa1a 100644 --- a/Documentation/devicetree/bindings/net/brcm,amac.txt +++ b/Documentation/devicetree/bindings/net/brcm,amac.txt @@ -2,11 +2,17 @@ Broadcom AMAC Ethernet Controller Device Tree Bindings ------------------------------------------------------------- Required properties: - - compatible: "brcm,amac" or "brcm,nsp-amac" - - reg: Address and length of the GMAC registers, - Address and length of the GMAC IDM registers - - reg-names: Names of the registers. Must have both "amac_base" and - "idm_base" + - compatible: "brcm,amac" + "brcm,nsp-amac" + "brcm,ns2-amac" + - reg: Address and length of the register set for the device. It + contains the information of registers in the same order as + described by reg-names + - reg-names: Names of the registers. + "amac_base": Address and length of the GMAC registers + "idm_base": Address and length of the GMAC IDM registers + "nicpm_base": Address and length of the NIC Port Manager + registers (required for Northstar2) - interrupts: Interrupt number Optional properties: -- cgit v1.1 From 1676aba5ef7ec8b9d360d33b559abc7db3a5a3a0 Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Fri, 4 Nov 2016 01:11:00 -0400 Subject: net: ethernet: bgmac: device tree phy enablement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the bgmac driver to allow for phy's defined by the device tree Signed-off-by: Jon Mason Acked-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bgmac-bcma.c | 22 +++++++++++++++++++ drivers/net/ethernet/broadcom/bgmac-platform.c | 22 ++++++++++++++++++- drivers/net/ethernet/broadcom/bgmac.c | 29 +++++--------------------- drivers/net/ethernet/broadcom/bgmac.h | 8 +++++++ 4 files changed, 56 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma.c b/drivers/net/ethernet/broadcom/bgmac-bcma.c index c16ec3a..4a4ffc0 100644 --- a/drivers/net/ethernet/broadcom/bgmac-bcma.c +++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c @@ -80,6 +80,24 @@ static void bcma_bgmac_cmn_maskset32(struct bgmac *bgmac, u16 offset, u32 mask, bcma_maskset32(bgmac->bcma.cmn, offset, mask, set); } +static int bcma_phy_connect(struct bgmac *bgmac) +{ + struct phy_device *phy_dev; + char bus_id[MII_BUS_ID_SIZE + 3]; + + /* Connect to the PHY */ + snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id, + bgmac->phyaddr); + phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link, + PHY_INTERFACE_MODE_MII); + if (IS_ERR(phy_dev)) { + dev_err(bgmac->dev, "PHY connection failed\n"); + return PTR_ERR(phy_dev); + } + + return 0; +} + static const struct bcma_device_id bgmac_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_4706_MAC_GBIT, BCMA_ANY_REV, BCMA_ANY_CLASS), @@ -275,6 +293,10 @@ static int bgmac_probe(struct bcma_device *core) bgmac->cco_ctl_maskset = bcma_bgmac_cco_ctl_maskset; bgmac->get_bus_clock = bcma_bgmac_get_bus_clock; bgmac->cmn_maskset32 = bcma_bgmac_cmn_maskset32; + if (bgmac->mii_bus) + bgmac->phy_connect = bcma_phy_connect; + else + bgmac->phy_connect = bgmac_phy_connect_direct; err = bgmac_enet_probe(bgmac); if (err) diff --git a/drivers/net/ethernet/broadcom/bgmac-platform.c b/drivers/net/ethernet/broadcom/bgmac-platform.c index be52f27..4642940 100644 --- a/drivers/net/ethernet/broadcom/bgmac-platform.c +++ b/drivers/net/ethernet/broadcom/bgmac-platform.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "bgmac.h" @@ -86,6 +87,20 @@ static void platform_bgmac_cmn_maskset32(struct bgmac *bgmac, u16 offset, WARN_ON(1); } +static int platform_phy_connect(struct bgmac *bgmac) +{ + struct phy_device *phy_dev; + + phy_dev = of_phy_get_and_connect(bgmac->net_dev, bgmac->dev->of_node, + bgmac_adjust_link); + if (!phy_dev) { + dev_err(bgmac->dev, "PHY connection failed\n"); + return -ENODEV; + } + + return 0; +} + static int bgmac_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -102,7 +117,6 @@ static int bgmac_probe(struct platform_device *pdev) /* Set the features of the 4707 family */ bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; bgmac->feature_flags |= BGMAC_FEAT_NO_RESET; - bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; bgmac->feature_flags |= BGMAC_FEAT_CMDCFG_SR_REV4; bgmac->feature_flags |= BGMAC_FEAT_TX_MASK_SETUP; bgmac->feature_flags |= BGMAC_FEAT_RX_MASK_SETUP; @@ -151,6 +165,12 @@ static int bgmac_probe(struct platform_device *pdev) bgmac->cco_ctl_maskset = platform_bgmac_cco_ctl_maskset; bgmac->get_bus_clock = platform_bgmac_get_bus_clock; bgmac->cmn_maskset32 = platform_bgmac_cmn_maskset32; + if (of_parse_phandle(np, "phy-handle", 0)) { + bgmac->phy_connect = platform_phy_connect; + } else { + bgmac->phy_connect = bgmac_phy_connect_direct; + bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; + } return bgmac_enet_probe(bgmac); } diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 31ca204..7f66ea7 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1388,7 +1388,7 @@ static const struct ethtool_ops bgmac_ethtool_ops = { * MII **************************************************/ -static void bgmac_adjust_link(struct net_device *net_dev) +void bgmac_adjust_link(struct net_device *net_dev) { struct bgmac *bgmac = netdev_priv(net_dev); struct phy_device *phy_dev = net_dev->phydev; @@ -1411,8 +1411,9 @@ static void bgmac_adjust_link(struct net_device *net_dev) phy_print_status(phy_dev); } } +EXPORT_SYMBOL_GPL(bgmac_adjust_link); -static int bgmac_phy_connect_direct(struct bgmac *bgmac) +int bgmac_phy_connect_direct(struct bgmac *bgmac) { struct fixed_phy_status fphy_status = { .link = 1, @@ -1437,24 +1438,7 @@ static int bgmac_phy_connect_direct(struct bgmac *bgmac) return err; } - -static int bgmac_phy_connect(struct bgmac *bgmac) -{ - struct phy_device *phy_dev; - char bus_id[MII_BUS_ID_SIZE + 3]; - - /* Connect to the PHY */ - snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id, - bgmac->phyaddr); - phy_dev = phy_connect(bgmac->net_dev, bus_id, &bgmac_adjust_link, - PHY_INTERFACE_MODE_MII); - if (IS_ERR(phy_dev)) { - dev_err(bgmac->dev, "PHY connection failed\n"); - return PTR_ERR(phy_dev); - } - - return 0; -} +EXPORT_SYMBOL_GPL(bgmac_phy_connect_direct); int bgmac_enet_probe(struct bgmac *info) { @@ -1507,10 +1491,7 @@ int bgmac_enet_probe(struct bgmac *info) netif_napi_add(net_dev, &bgmac->napi, bgmac_poll, BGMAC_WEIGHT); - if (!bgmac->mii_bus) - err = bgmac_phy_connect_direct(bgmac); - else - err = bgmac_phy_connect(bgmac); + err = bgmac_phy_connect(bgmac); if (err) { dev_err(bgmac->dev, "Cannot connect to phy\n"); goto err_dma_free; diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index 80836b4..bfd9f40 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -513,10 +513,13 @@ struct bgmac { u32 (*get_bus_clock)(struct bgmac *bgmac); void (*cmn_maskset32)(struct bgmac *bgmac, u16 offset, u32 mask, u32 set); + int (*phy_connect)(struct bgmac *bgmac); }; int bgmac_enet_probe(struct bgmac *info); void bgmac_enet_remove(struct bgmac *bgmac); +void bgmac_adjust_link(struct net_device *net_dev); +int bgmac_phy_connect_direct(struct bgmac *bgmac); struct mii_bus *bcma_mdio_mii_register(struct bcma_device *core, u8 phyaddr); void bcma_mdio_mii_unregister(struct mii_bus *mii_bus); @@ -583,4 +586,9 @@ static inline void bgmac_set(struct bgmac *bgmac, u16 offset, u32 set) { bgmac_maskset(bgmac, offset, ~0, set); } + +static inline int bgmac_phy_connect(struct bgmac *bgmac) +{ + return bgmac->phy_connect(bgmac); +} #endif /* _BGMAC_H */ -- cgit v1.1 From dd5c5d037f5e4701d6b4e463cdc140c053785fef Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Fri, 4 Nov 2016 01:11:01 -0400 Subject: net: ethernet: bgmac: add NS2 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the variant of amac hardware present in the Broadcom Northstar2 based SoCs. Northstar2 requires an additional register to be configured with the port speed/duplexity (NICPM). This can be added to the link callback to hide it from the instances that do not use this. Also, clearing of the pending interrupts on init is required due to observed issues on some platforms. Signed-off-by: Jon Mason Reviewed-by: Florian Fainelli Acked-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bgmac-platform.c | 56 +++++++++++++++++++++++++- drivers/net/ethernet/broadcom/bgmac.c | 3 ++ drivers/net/ethernet/broadcom/bgmac.h | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bgmac-platform.c b/drivers/net/ethernet/broadcom/bgmac-platform.c index 4642940..6f736c1 100644 --- a/drivers/net/ethernet/broadcom/bgmac-platform.c +++ b/drivers/net/ethernet/broadcom/bgmac-platform.c @@ -14,12 +14,21 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include #include #include "bgmac.h" +#define NICPM_IOMUX_CTRL 0x00000008 + +#define NICPM_IOMUX_CTRL_INIT_VAL 0x3196e000 +#define NICPM_IOMUX_CTRL_SPD_SHIFT 10 +#define NICPM_IOMUX_CTRL_SPD_10M 0 +#define NICPM_IOMUX_CTRL_SPD_100M 1 +#define NICPM_IOMUX_CTRL_SPD_1000M 2 + static u32 platform_bgmac_read(struct bgmac *bgmac, u16 offset) { return readl(bgmac->plat.base + offset); @@ -87,12 +96,46 @@ static void platform_bgmac_cmn_maskset32(struct bgmac *bgmac, u16 offset, WARN_ON(1); } +static void bgmac_nicpm_speed_set(struct net_device *net_dev) +{ + struct bgmac *bgmac = netdev_priv(net_dev); + u32 val; + + if (!bgmac->plat.nicpm_base) + return; + + val = NICPM_IOMUX_CTRL_INIT_VAL; + switch (bgmac->net_dev->phydev->speed) { + default: + netdev_err(net_dev, "Unsupported speed. Defaulting to 1000Mb\n"); + case SPEED_1000: + val |= NICPM_IOMUX_CTRL_SPD_1000M << NICPM_IOMUX_CTRL_SPD_SHIFT; + break; + case SPEED_100: + val |= NICPM_IOMUX_CTRL_SPD_100M << NICPM_IOMUX_CTRL_SPD_SHIFT; + break; + case SPEED_10: + val |= NICPM_IOMUX_CTRL_SPD_10M << NICPM_IOMUX_CTRL_SPD_SHIFT; + break; + } + + writel(val, bgmac->plat.nicpm_base + NICPM_IOMUX_CTRL); + + bgmac_adjust_link(bgmac->net_dev); +} + static int platform_phy_connect(struct bgmac *bgmac) { struct phy_device *phy_dev; - phy_dev = of_phy_get_and_connect(bgmac->net_dev, bgmac->dev->of_node, - bgmac_adjust_link); + if (bgmac->plat.nicpm_base) + phy_dev = of_phy_get_and_connect(bgmac->net_dev, + bgmac->dev->of_node, + bgmac_nicpm_speed_set); + else + phy_dev = of_phy_get_and_connect(bgmac->net_dev, + bgmac->dev->of_node, + bgmac_adjust_link); if (!phy_dev) { dev_err(bgmac->dev, "PHY connection failed\n"); return -ENODEV; @@ -156,6 +199,14 @@ static int bgmac_probe(struct platform_device *pdev) if (IS_ERR(bgmac->plat.idm_base)) return PTR_ERR(bgmac->plat.idm_base); + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nicpm_base"); + if (regs) { + bgmac->plat.nicpm_base = devm_ioremap_resource(&pdev->dev, + regs); + if (IS_ERR(bgmac->plat.nicpm_base)) + return PTR_ERR(bgmac->plat.nicpm_base); + } + bgmac->read = platform_bgmac_read; bgmac->write = platform_bgmac_write; bgmac->idm_read = platform_bgmac_idm_read; @@ -187,6 +238,7 @@ static int bgmac_remove(struct platform_device *pdev) static const struct of_device_id bgmac_of_enet_match[] = { {.compatible = "brcm,amac",}, {.compatible = "brcm,nsp-amac",}, + {.compatible = "brcm,ns2-amac",}, {}, }; diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 7f66ea7..a29787f 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1082,6 +1082,9 @@ static void bgmac_enable(struct bgmac *bgmac) /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipinit */ static void bgmac_chip_init(struct bgmac *bgmac) { + /* Clear any erroneously pending interrupts */ + bgmac_write(bgmac, BGMAC_INT_STATUS, ~0); + /* 1 interrupt per received frame */ bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT); diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index bfd9f40..71f493f 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -463,6 +463,7 @@ struct bgmac { struct { void *base; void *idm_base; + void *nicpm_base; } plat; struct { struct bcma_device *core; -- cgit v1.1 From dddc3c9d7d02e500feb041a85217b2f2dd8c188a Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Fri, 4 Nov 2016 01:11:02 -0400 Subject: arm64: dts: NS2: add AMAC ethernet support Add support for the AMAC ethernet to the Broadcom Northstar2 SoC device tree Signed-off-by: Jon Mason Signed-off-by: David S. Miller --- arch/arm64/boot/dts/broadcom/ns2-svk.dts | 5 +++++ arch/arm64/boot/dts/broadcom/ns2.dtsi | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/arch/arm64/boot/dts/broadcom/ns2-svk.dts b/arch/arm64/boot/dts/broadcom/ns2-svk.dts index b09f3bc..c4d5442 100644 --- a/arch/arm64/boot/dts/broadcom/ns2-svk.dts +++ b/arch/arm64/boot/dts/broadcom/ns2-svk.dts @@ -56,6 +56,10 @@ }; }; +&enet { + status = "ok"; +}; + &pci_phy0 { status = "ok"; }; @@ -174,6 +178,7 @@ &mdio_mux_iproc { mdio@10 { gphy0: eth-phy@10 { + enet-phy-lane-swap; reg = <0x10>; }; }; diff --git a/arch/arm64/boot/dts/broadcom/ns2.dtsi b/arch/arm64/boot/dts/broadcom/ns2.dtsi index d95dc40..773ed59 100644 --- a/arch/arm64/boot/dts/broadcom/ns2.dtsi +++ b/arch/arm64/boot/dts/broadcom/ns2.dtsi @@ -191,6 +191,18 @@ #include "ns2-clock.dtsi" + enet: ethernet@61000000 { + compatible = "brcm,ns2-amac"; + reg = <0x61000000 0x1000>, + <0x61090000 0x1000>, + <0x61030000 0x100>; + reg-names = "amac_base", "idm_base", "nicpm_base"; + interrupts = ; + phy-handle = <&gphy0>; + phy-mode = "rgmii"; + status = "disabled"; + }; + dma0: dma@61360000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x61360000 0x1000>; -- cgit v1.1 From cd2c0f454039ad303b54653c56ebc22ffd856200 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 3 Nov 2016 09:25:00 -0700 Subject: net: Update raw socket bind to consider l3 domain Binding a raw socket to a local address fails if the socket is bound to an L3 domain: $ vrf-test -s -l 10.100.1.2 -R -I red error binding socket: 99: Cannot assign requested address Update raw_bind to look consider if sk_bound_dev_if is bound to an L3 domain and use inet_addr_type_table to lookup the address. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/raw.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 838ea5e..2300fae 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -695,12 +695,20 @@ static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) { struct inet_sock *inet = inet_sk(sk); struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; + u32 tb_id = RT_TABLE_LOCAL; int ret = -EINVAL; int chk_addr_ret; if (sk->sk_state != TCP_CLOSE || addr_len < sizeof(struct sockaddr_in)) goto out; - chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); + + if (sk->sk_bound_dev_if) + tb_id = l3mdev_fib_table_by_index(sock_net(sk), + sk->sk_bound_dev_if) ? : tb_id; + + chk_addr_ret = inet_addr_type_table(sock_net(sk), addr->sin_addr.s_addr, + tb_id); + ret = -EADDRNOTAVAIL; if (addr->sin_addr.s_addr && chk_addr_ret != RTN_LOCAL && chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) -- cgit v1.1 From ad959036a70890bea121403c6a4e373dff5b7311 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 4 Nov 2016 11:28:58 +0100 Subject: net/sock: add an explicit sk argument for ip_cmsg_recv_offset() So that we can use it even after orphaining the skbuff. Suggested-by: Eric Dumazet Signed-off-by: Paolo Abeni Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip.h | 5 +++-- net/ipv4/ip_sockglue.c | 6 +++--- net/ipv4/udp.c | 2 +- net/ipv6/udp.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/net/ip.h b/include/net/ip.h index 55cdaac..f48c67c 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -579,7 +579,8 @@ int ip_options_rcv_srr(struct sk_buff *skb); */ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb); -void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int tlen, int offset); +void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb, int tlen, int offset); int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, bool allow_ipv6); int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, @@ -601,7 +602,7 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport, static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) { - ip_cmsg_recv_offset(msg, skb, 0, 0); + ip_cmsg_recv_offset(msg, skb->sk, skb, 0, 0); } bool icmp_global_allow(void); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index ecbaae2..8b13881 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -164,10 +164,10 @@ static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin); } -void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, - int tlen, int offset) +void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb, int tlen, int offset) { - struct inet_sock *inet = inet_sk(skb->sk); + struct inet_sock *inet = inet_sk(sk); unsigned int flags = inet->cmsg_flags; /* Ordered by supposed usage frequency */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index a32a890..28a0165 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1421,7 +1421,7 @@ try_again: *addr_len = sizeof(*sin); } if (inet->cmsg_flags) - ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr), off); + ip_cmsg_recv_offset(msg, sk, skb, sizeof(struct udphdr), off); err = copied; if (flags & MSG_TRUNC) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9103c5c..b5a23ce 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -425,7 +425,7 @@ try_again: if (is_udp4) { if (inet->cmsg_flags) - ip_cmsg_recv_offset(msg, skb, + ip_cmsg_recv_offset(msg, sk, skb, sizeof(struct udphdr), off); } else { if (np->rxopt.all) -- cgit v1.1 From 7c13f97ffde63cc792c49ec1513f3974f2f05229 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 4 Nov 2016 11:28:59 +0100 Subject: udp: do fwd memory scheduling on dequeue A new argument is added to __skb_recv_datagram to provide an explicit skb destructor, invoked under the receive queue lock. The UDP protocol uses such argument to perform memory reclaiming on dequeue, so that the UDP protocol does not set anymore skb->desctructor. Instead explicit memory reclaiming is performed at close() time and when skbs are removed from the receive queue. The in kernel UDP protocol users now need to call a skb_recv_udp() variant instead of skb_recv_datagram() to properly perform memory accounting on dequeue. Overall, this allows acquiring only once the receive queue lock on dequeue. Tested using pktgen with random src port, 64 bytes packet, wire-speed on a 10G link as sender and udp_sink as the receiver, using an l4 tuple rxhash to stress the contention, and one or more udp_sink instances with reuseport. nr sinks vanilla patched 1 440 560 3 2150 2300 6 3650 3800 9 4450 4600 12 6250 6450 v1 -> v2: - do rmem and allocated memory scheduling under the receive lock - do bulk scheduling in first_packet_length() and in udp_destruct_sock() - avoid the typdef for the dequeue callback Suggested-by: Eric Dumazet Acked-by: Hannes Frederic Sowa Signed-off-by: Paolo Abeni Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 4 ++++ include/net/udp.h | 15 +++++++++++++++ net/core/datagram.c | 17 ++++++++++++----- net/ipv4/udp.c | 42 ++++++++++++++++++++++++------------------ net/ipv6/udp.c | 3 +-- net/rxrpc/input.c | 7 +++---- net/sunrpc/svcsock.c | 2 +- net/sunrpc/xprtsock.c | 2 +- net/unix/af_unix.c | 4 ++-- 9 files changed, 63 insertions(+), 33 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index cc6e23e..a4aeeca 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3033,9 +3033,13 @@ static inline void skb_frag_list_init(struct sk_buff *skb) int __skb_wait_for_more_packets(struct sock *sk, int *err, long *timeo_p, const struct sk_buff *skb); struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned flags, + void (*destructor)(struct sock *sk, + struct sk_buff *skb), int *peeked, int *off, int *err, struct sk_buff **last); struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, + void (*destructor)(struct sock *sk, + struct sk_buff *skb), int *peeked, int *off, int *err); struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err); diff --git a/include/net/udp.h b/include/net/udp.h index 6134f37..e6e4e19 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -248,6 +248,21 @@ static inline __be16 udp_flow_src_port(struct net *net, struct sk_buff *skb, /* net/ipv4/udp.c */ void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len); int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb); +void udp_skb_destructor(struct sock *sk, struct sk_buff *skb); +static inline struct sk_buff * +__skb_recv_udp(struct sock *sk, unsigned int flags, int noblock, int *peeked, + int *off, int *err) +{ + return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), + udp_skb_destructor, peeked, off, err); +} +static inline struct sk_buff *skb_recv_udp(struct sock *sk, unsigned int flags, + int noblock, int *err) +{ + int peeked, off = 0; + + return __skb_recv_udp(sk, flags, noblock, &peeked, &off, err); +} void udp_v4_early_demux(struct sk_buff *skb); int udp_get_port(struct sock *sk, unsigned short snum, diff --git a/net/core/datagram.c b/net/core/datagram.c index bfb973a..49816af 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -165,6 +165,7 @@ done: * __skb_try_recv_datagram - Receive a datagram skbuff * @sk: socket * @flags: MSG_ flags + * @destructor: invoked under the receive lock on successful dequeue * @peeked: returns non-zero if this packet has been seen before * @off: an offset in bytes to peek skb from. Returns an offset * within an skb where data actually starts @@ -197,6 +198,8 @@ done: * the standard around please. */ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags, + void (*destructor)(struct sock *sk, + struct sk_buff *skb), int *peeked, int *off, int *err, struct sk_buff **last) { @@ -241,9 +244,11 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags, } atomic_inc(&skb->users); - } else + } else { __skb_unlink(skb, queue); - + if (destructor) + destructor(sk, skb); + } spin_unlock_irqrestore(&queue->lock, cpu_flags); *off = _off; return skb; @@ -262,6 +267,8 @@ no_packet: EXPORT_SYMBOL(__skb_try_recv_datagram); struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, + void (*destructor)(struct sock *sk, + struct sk_buff *skb), int *peeked, int *off, int *err) { struct sk_buff *skb, *last; @@ -270,8 +277,8 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); do { - skb = __skb_try_recv_datagram(sk, flags, peeked, off, err, - &last); + skb = __skb_try_recv_datagram(sk, flags, destructor, peeked, + off, err, &last); if (skb) return skb; @@ -290,7 +297,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned int flags, int peeked, off = 0; return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), - &peeked, &off, err); + NULL, &peeked, &off, err); } EXPORT_SYMBOL(skb_recv_datagram); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 28a0165..097b706 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1173,26 +1173,26 @@ out: return ret; } +/* fully reclaim rmem/fwd memory allocated for skb */ static void udp_rmem_release(struct sock *sk, int size, int partial) { int amt; atomic_sub(size, &sk->sk_rmem_alloc); - - spin_lock_bh(&sk->sk_receive_queue.lock); sk->sk_forward_alloc += size; amt = (sk->sk_forward_alloc - partial) & ~(SK_MEM_QUANTUM - 1); sk->sk_forward_alloc -= amt; - spin_unlock_bh(&sk->sk_receive_queue.lock); if (amt) __sk_mem_reduce_allocated(sk, amt >> SK_MEM_QUANTUM_SHIFT); } -static void udp_rmem_free(struct sk_buff *skb) +/* Note: called with sk_receive_queue.lock held */ +void udp_skb_destructor(struct sock *sk, struct sk_buff *skb) { - udp_rmem_release(skb->sk, skb->truesize, 1); + udp_rmem_release(sk, skb->truesize, 1); } +EXPORT_SYMBOL(udp_skb_destructor); int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) { @@ -1229,9 +1229,9 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) sk->sk_forward_alloc -= size; - /* the skb owner in now the udp socket */ - skb->sk = sk; - skb->destructor = udp_rmem_free; + /* no need to setup a destructor, we will explicitly release the + * forward allocated memory on dequeue + */ skb->dev = NULL; sock_skb_set_dropcount(sk, skb); @@ -1255,8 +1255,15 @@ EXPORT_SYMBOL_GPL(__udp_enqueue_schedule_skb); static void udp_destruct_sock(struct sock *sk) { /* reclaim completely the forward allocated memory */ - __skb_queue_purge(&sk->sk_receive_queue); - udp_rmem_release(sk, 0, 0); + unsigned int total = 0; + struct sk_buff *skb; + + while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { + total += skb->truesize; + kfree_skb(skb); + } + udp_rmem_release(sk, total, 0); + inet_sock_destruct(sk); } @@ -1288,12 +1295,11 @@ EXPORT_SYMBOL_GPL(skb_consume_udp); */ static int first_packet_length(struct sock *sk) { - struct sk_buff_head list_kill, *rcvq = &sk->sk_receive_queue; + struct sk_buff_head *rcvq = &sk->sk_receive_queue; struct sk_buff *skb; + int total = 0; int res; - __skb_queue_head_init(&list_kill); - spin_lock_bh(&rcvq->lock); while ((skb = skb_peek(rcvq)) != NULL && udp_lib_checksum_complete(skb)) { @@ -1303,12 +1309,13 @@ static int first_packet_length(struct sock *sk) IS_UDPLITE(sk)); atomic_inc(&sk->sk_drops); __skb_unlink(skb, rcvq); - __skb_queue_tail(&list_kill, skb); + total += skb->truesize; + kfree_skb(skb); } res = skb ? skb->len : -1; + if (total) + udp_rmem_release(sk, total, 1); spin_unlock_bh(&rcvq->lock); - - __skb_queue_purge(&list_kill); return res; } @@ -1363,8 +1370,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, try_again: peeking = off = sk_peek_offset(sk, flags); - skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), - &peeked, &off, &err); + skb = __skb_recv_udp(sk, flags, noblock, &peeked, &off, &err); if (!skb) return err; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b5a23ce..5313818 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -343,8 +343,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, try_again: peeking = off = sk_peek_offset(sk, flags); - skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), - &peeked, &off, &err); + skb = __skb_recv_udp(sk, flags, noblock, &peeked, &off, &err); if (!skb) return err; diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 44fb8d8..1d87b54 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1053,7 +1053,7 @@ void rxrpc_data_ready(struct sock *udp_sk) ASSERT(!irqs_disabled()); - skb = skb_recv_datagram(udp_sk, 0, 1, &ret); + skb = skb_recv_udp(udp_sk, 0, 1, &ret); if (!skb) { if (ret == -EAGAIN) return; @@ -1075,10 +1075,9 @@ void rxrpc_data_ready(struct sock *udp_sk) __UDP_INC_STATS(&init_net, UDP_MIB_INDATAGRAMS, 0); - /* The socket buffer we have is owned by UDP, with UDP's data all over - * it, but we really want our own data there. + /* The UDP protocol already released all skb resources; + * we are free to add our own data there. */ - skb_orphan(skb); sp = rxrpc_skb(skb); /* dig out the RxRPC connection details */ diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index e2a55dc..78da4ae 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -547,7 +547,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) err = kernel_recvmsg(svsk->sk_sock, &msg, NULL, 0, 0, MSG_PEEK | MSG_DONTWAIT); if (err >= 0) - skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err); + skb = skb_recv_udp(svsk->sk_sk, 0, 1, &err); if (skb == NULL) { if (err != -EAGAIN) { diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 1758665..7178d0a 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1080,7 +1080,7 @@ static void xs_udp_data_receive(struct sock_xprt *transport) if (sk == NULL) goto out; for (;;) { - skb = skb_recv_datagram(sk, 0, 1, &err); + skb = skb_recv_udp(sk, 0, 1, &err); if (skb != NULL) { xs_udp_data_read_skb(&transport->xprt, sk, skb); consume_skb(skb); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 145082e..8762018 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2113,8 +2113,8 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, mutex_lock(&u->iolock); skip = sk_peek_offset(sk, flags); - skb = __skb_try_recv_datagram(sk, flags, &peeked, &skip, &err, - &last); + skb = __skb_try_recv_datagram(sk, flags, NULL, &peeked, &skip, + &err, &last); if (skb) break; -- cgit v1.1 From d0a81f67cd6286d32f42a167d19c7a387c23db79 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 3 Nov 2016 14:56:01 +0100 Subject: net: make default TX queue length a defined constant The default TX queue length of Ethernet devices have been a magic constant of 1000, ever since the initial git import. Looking back in historical trees[1][2] the value used to be 100, with the same comment "Ethernet wants good queues". The commit[3] that changed this from 100 to 1000 didn't describe why, but from conversations with Robert Olsson it seems that it was changed when Ethernet devices went from 100Mbit/s to 1Gbit/s, because the link speed increased x10 the queue size were also adjusted. This value later caused much heartache for the bufferbloat community. This patch merely moves the value into a defined constant. [1] https://git.kernel.org/cgit/linux/kernel/git/davem/netdev-vger-cvs.git/ [2] https://git.kernel.org/cgit/linux/kernel/git/tglx/history.git/ [3] https://git.kernel.org/tglx/history/c/98921832c232 Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 2 ++ net/ethernet/eth.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index cd334c9..f1b76b8 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -6,6 +6,8 @@ #include #include +#define DEFAULT_TX_QUEUE_LEN 1000 + struct qdisc_walker { int stop; int skip; diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index d9e2fe1..8c5a479 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -62,6 +62,7 @@ #include #include #include +#include __setup("ether=", netdev_boot_setup); @@ -359,7 +360,7 @@ void ether_setup(struct net_device *dev) dev->min_mtu = ETH_MIN_MTU; dev->max_mtu = ETH_DATA_LEN; dev->addr_len = ETH_ALEN; - dev->tx_queue_len = 1000; /* Ethernet wants good queues */ + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; dev->flags = IFF_BROADCAST|IFF_MULTICAST; dev->priv_flags |= IFF_TX_SKB_SHARING; -- cgit v1.1 From 1159708432f7067b82388695e29d7105e79bd293 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 3 Nov 2016 14:56:06 +0100 Subject: net/qdisc: IFF_NO_QUEUE drivers should use consistent TX queue len The flag IFF_NO_QUEUE marks virtual device drivers that doesn't need a default qdisc attached, given they will be backed by physical device, that already have a qdisc attached for pushback. It is still supported to attach a qdisc to a IFF_NO_QUEUE device, as this can be useful for difference policy reasons (e.g. bandwidth limiting containers). For this to work, the tx_queue_len need to have a sane value, because some qdiscs inherit/copy the tx_queue_len (namely, pfifo, bfifo, gred, htb, plug and sfb). Commit a813104d9233 ("IFF_NO_QUEUE: Fix for drivers not calling ether_setup()") caught situations where some drivers didn't initialize tx_queue_len. The problem with the commit was choosing 1 as the fallback value. A qdisc queue length of 1 causes more harm than good, because it creates hard to debug situations for userspace. It gives userspace a false sense of a working config after attaching a qdisc. As low volume traffic (that doesn't activate the qdisc policy) works, like ping, while traffic that e.g. needs shaping cannot reach the configured policy levels, given the queue length is too small. This patch change the value to DEFAULT_TX_QUEUE_LEN, given other IFF_NO_QUEUE devices (that call ether_setup()) also use this value. Fixes: a813104d9233 ("IFF_NO_QUEUE: Fix for drivers not calling ether_setup()") Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index f23e286..0260ad3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -7651,7 +7651,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, if (!dev->tx_queue_len) { dev->priv_flags |= IFF_NO_QUEUE; - dev->tx_queue_len = 1; + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; } dev->num_tx_queues = txqs; -- cgit v1.1 From 84c46dd865384a1613e797b967552e4a428b5202 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 3 Nov 2016 14:56:11 +0100 Subject: qdisc: catch misconfig of attaching qdisc to tx_queue_len zero device It is a clear misconfiguration to attach a qdisc to a device with tx_queue_len zero, because some qdisc's (namely, pfifo, bfifo, gred, htb, plug and sfb) inherit/copy this value as their queue length. Why should the kernel catch such a misconfiguration? Because prior to introducing the IFF_NO_QUEUE device flag, userspace found a loophole in the qdisc config system that allowed them to achieve the equivalent of IFF_NO_QUEUE, which is to remove the qdisc code path entirely from a device. The loophole on older kernels is setting tx_queue_len=0, *prior* to device qdisc init (the config time is significant, simply setting tx_queue_len=0 doesn't trigger the loophole). This loophole is currently used by Docker[1] to get better performance and scalability out of the veth device. The Docker developers were warned[1] that they needed to adjust the tx_queue_len if ever attaching a qdisc. The OpenShift project didn't remember this warning and attached a qdisc, this were caught and fixed in[2]. [1] https://github.com/docker/libcontainer/pull/193 [2] https://github.com/openshift/origin/pull/11126 Instead of fixing every userspace program that used this loophole, and forgot to reset the tx_queue_len, prior to attaching a qdisc. Let's catch the misconfiguration on the kernel side. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- net/sched/sch_api.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 206dc24..f337f1b 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -960,6 +960,17 @@ static struct Qdisc *qdisc_create(struct net_device *dev, sch->handle = handle; + /* This exist to keep backward compatible with a userspace + * loophole, what allowed userspace to get IFF_NO_QUEUE + * facility on older kernels by setting tx_queue_len=0 (prior + * to qdisc init), and then forgot to reinit tx_queue_len + * before again attaching a qdisc. + */ + if ((dev->priv_flags & IFF_NO_QUEUE) && (dev->tx_queue_len == 0)) { + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; + netdev_info(dev, "Caught tx_queue_len zero misconfig\n"); + } + if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) { if (qdisc_is_percpu_stats(sch)) { sch->cpu_bstats = -- cgit v1.1 From f5f99309fa7481f59a500f0d08f3379cd6424c1f Mon Sep 17 00:00:00 2001 From: Soheil Hassas Yeganeh Date: Thu, 3 Nov 2016 18:24:27 -0400 Subject: sock: do not set sk_err in sock_dequeue_err_skb Do not set sk_err when dequeuing errors from the error queue. Doing so results in: a) Bugs: By overwriting existing sk_err values, it possibly hides legitimate errors. It is also incorrect when local errors are queued with ip_local_error. That happens in the context of a system call, which already returns the error code. b) Inconsistent behavior: When there are pending errors on the error queue, sk_err is sometimes 0 (e.g., for the first timestamp on the error queue) and sometimes set to an error code (after dequeuing the first timestamp). c) Suboptimality: Setting sk_err to ENOMSG on simple TX timestamps can abort parallel reads and writes. Removing this line doesn't break userspace. This is because userspace code cannot rely on sk_err for detecting whether there is something on the error queue. Except for ICMP messages received for UDP and RAW, sk_err is not set at enqueue time, and as a result sk_err can be 0 while there are plenty of errors on the error queue. For ICMP packets in UDP and RAW, sk_err is set when they are enqueued on the error queue, but that does not result in aborting reads and writes. For such cases, sk_err is only readable via getsockopt(SO_ERROR) which will reset the value of sk_err on its own. More importantly, prior to this patch, recvmsg(MSG_ERRQUEUE) has a race on setting sk_err (i.e., sk_err is set by sock_dequeue_err_skb without atomic ops or locks) which can store 0 in sk_err even when we have ICMP messages pending. Removing this line from sock_dequeue_err_skb eliminates that race. Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: Eric Dumazet Signed-off-by: Willem de Bruijn Signed-off-by: Neal Cardwell Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/core/skbuff.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1e3e008..0b2a6e9 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3725,7 +3725,6 @@ struct sk_buff *sock_dequeue_err_skb(struct sock *sk) err = SKB_EXT_ERR(skb_next)->ee.ee_errno; spin_unlock_irqrestore(&q->lock, flags); - sk->sk_err = err; if (err) sk->sk_error_report(sk); -- cgit v1.1 From b91a2543b4c15fed504e587d4441169c75d6646e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 17 Jul 2016 21:04:04 +0200 Subject: batman-adv: Consume skb in receive handlers Receiving functions in Linux consume the supplied skbuff. Doing the same in the batadv_rx_handler functions makes the behavior more similar to the rest of the Linux network code. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 22 ++++-- net/batman-adv/bat_v_elp.c | 30 ++++---- net/batman-adv/bat_v_ogm.c | 15 ++-- net/batman-adv/main.c | 11 +-- net/batman-adv/network-coding.c | 11 +-- net/batman-adv/routing.c | 149 +++++++++++++++++++++++++++------------- 6 files changed, 153 insertions(+), 85 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 310f391..bd39247 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1823,17 +1823,18 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, struct batadv_ogm_packet *ogm_packet; u8 *packet_pos; int ogm_offset; - bool ret; + bool res; + int ret = NET_RX_DROP; - ret = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN); - if (!ret) - return NET_RX_DROP; + res = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN); + if (!res) + goto free_skb; /* did we receive a B.A.T.M.A.N. IV OGM packet on an interface * that does not have B.A.T.M.A.N. IV enabled ? */ if (bat_priv->algo_ops->iface.enable != batadv_iv_ogm_iface_enable) - return NET_RX_DROP; + goto free_skb; batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX); batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES, @@ -1854,8 +1855,15 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, ogm_packet = (struct batadv_ogm_packet *)packet_pos; } - consume_skb(skb); - return NET_RX_SUCCESS; + ret = NET_RX_SUCCESS; + +free_skb: + if (ret == NET_RX_SUCCESS) + consume_skb(skb); + else + kfree_skb(skb); + + return ret; } #ifdef CONFIG_BATMAN_ADV_DEBUGFS diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index ee08540..54bdd41 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -492,20 +492,21 @@ int batadv_v_elp_packet_recv(struct sk_buff *skb, struct batadv_elp_packet *elp_packet; struct batadv_hard_iface *primary_if; struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); - bool ret; + bool res; + int ret = NET_RX_DROP; - ret = batadv_check_management_packet(skb, if_incoming, BATADV_ELP_HLEN); - if (!ret) - return NET_RX_DROP; + res = batadv_check_management_packet(skb, if_incoming, BATADV_ELP_HLEN); + if (!res) + goto free_skb; if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) - return NET_RX_DROP; + goto free_skb; /* did we receive a B.A.T.M.A.N. V ELP packet on an interface * that does not have B.A.T.M.A.N. V ELP enabled ? */ if (strcmp(bat_priv->algo_ops->name, "BATMAN_V") != 0) - return NET_RX_DROP; + goto free_skb; elp_packet = (struct batadv_elp_packet *)skb->data; @@ -516,14 +517,19 @@ int batadv_v_elp_packet_recv(struct sk_buff *skb, primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) - goto out; + goto free_skb; batadv_v_elp_neigh_update(bat_priv, ethhdr->h_source, if_incoming, elp_packet); -out: - if (primary_if) - batadv_hardif_put(primary_if); - consume_skb(skb); - return NET_RX_SUCCESS; + ret = NET_RX_SUCCESS; + batadv_hardif_put(primary_if); + +free_skb: + if (ret == NET_RX_SUCCESS) + consume_skb(skb); + else + kfree_skb(skb); + + return ret; } diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 9922ccd..38b9aab 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -810,18 +810,18 @@ int batadv_v_ogm_packet_recv(struct sk_buff *skb, * B.A.T.M.A.N. V enabled ? */ if (strcmp(bat_priv->algo_ops->name, "BATMAN_V") != 0) - return NET_RX_DROP; + goto free_skb; if (!batadv_check_management_packet(skb, if_incoming, BATADV_OGM2_HLEN)) - return NET_RX_DROP; + goto free_skb; if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) - return NET_RX_DROP; + goto free_skb; ogm_packet = (struct batadv_ogm2_packet *)skb->data; if (batadv_is_my_mac(bat_priv, ogm_packet->orig)) - return NET_RX_DROP; + goto free_skb; batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX); batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES, @@ -842,7 +842,12 @@ int batadv_v_ogm_packet_recv(struct sk_buff *skb, } ret = NET_RX_SUCCESS; - consume_skb(skb); + +free_skb: + if (ret == NET_RX_SUCCESS) + consume_skb(skb); + else + kfree_skb(skb); return ret; } diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 5e4e818..6b5dae6 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -402,6 +402,8 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset) static int batadv_recv_unhandled_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { + kfree_skb(skb); + return NET_RX_DROP; } @@ -416,7 +418,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, struct batadv_ogm_packet *batadv_ogm_packet; struct batadv_hard_iface *hard_iface; u8 idx; - int ret; hard_iface = container_of(ptype, struct batadv_hard_iface, batman_adv_ptype); @@ -466,14 +467,8 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, /* reset control block to avoid left overs from previous users */ memset(skb->cb, 0, sizeof(struct batadv_skb_cb)); - /* all receive handlers return whether they received or reused - * the supplied skb. if not, we have to free the skb. - */ idx = batadv_ogm_packet->packet_type; - ret = (*batadv_rx_handler[idx])(skb, hard_iface); - - if (ret == NET_RX_DROP) - kfree_skb(skb); + (*batadv_rx_handler[idx])(skb, hard_iface); batadv_hardif_put(hard_iface); diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index 3af66d3..ab5a3bf 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -1819,11 +1819,11 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb, /* Check if network coding is enabled */ if (!atomic_read(&bat_priv->network_coding)) - return NET_RX_DROP; + goto free_skb; /* Make sure we can access (and remove) header */ if (unlikely(!pskb_may_pull(skb, hdr_size))) - return NET_RX_DROP; + goto free_skb; coded_packet = (struct batadv_coded_packet *)skb->data; ethhdr = eth_hdr(skb); @@ -1831,7 +1831,7 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb, /* Verify frame is destined for us */ if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest) && !batadv_is_my_mac(bat_priv, coded_packet->second_dest)) - return NET_RX_DROP; + goto free_skb; /* Update stat counter */ if (batadv_is_my_mac(bat_priv, coded_packet->second_dest)) @@ -1841,7 +1841,7 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb, coded_packet); if (!nc_packet) { batadv_inc_counter(bat_priv, BATADV_CNT_NC_DECODE_FAILED); - return NET_RX_DROP; + goto free_skb; } /* Make skb's linear, because decoding accesses the entire buffer */ @@ -1867,6 +1867,9 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb, free_nc_packet: batadv_nc_packet_free(nc_packet, true); +free_skb: + kfree_skb(skb); + return NET_RX_DROP; } diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 4d2679a..105d4fc 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -262,8 +262,11 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, icmph->ttl = BATADV_TTL; res = batadv_send_skb_to_orig(skb, orig_node, NULL); - ret = NET_RX_SUCCESS; + if (res == NET_XMIT_SUCCESS) + ret = NET_RX_SUCCESS; + /* skb was consumed */ + skb = NULL; break; case BATADV_TP: if (!pskb_may_pull(skb, sizeof(struct batadv_icmp_tp_packet))) @@ -271,6 +274,8 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, batadv_tp_meter_recv(bat_priv, skb); ret = NET_RX_SUCCESS; + /* skb was consumed */ + skb = NULL; goto out; default: /* drop unknown type */ @@ -281,6 +286,9 @@ out: batadv_hardif_put(primary_if); if (orig_node) batadv_orig_node_put(orig_node); + + kfree_skb(skb); + return ret; } @@ -322,13 +330,20 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, icmp_packet->ttl = BATADV_TTL; res = batadv_send_skb_to_orig(skb, orig_node, NULL); - ret = NET_RX_SUCCESS; + if (res == NET_RX_SUCCESS) + ret = NET_XMIT_SUCCESS; + + /* skb was consumed */ + skb = NULL; out: if (primary_if) batadv_hardif_put(primary_if); if (orig_node) batadv_orig_node_put(orig_node); + + kfree_skb(skb); + return ret; } @@ -345,21 +360,21 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) - goto out; + goto free_skb; ethhdr = eth_hdr(skb); /* packet with unicast indication but broadcast recipient */ if (is_broadcast_ether_addr(ethhdr->h_dest)) - goto out; + goto free_skb; /* packet with broadcast sender address */ if (is_broadcast_ether_addr(ethhdr->h_source)) - goto out; + goto free_skb; /* not for me */ if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) - goto out; + goto free_skb; icmph = (struct batadv_icmp_header *)skb->data; @@ -368,17 +383,17 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, icmph->msg_type == BATADV_ECHO_REQUEST) && (skb->len >= sizeof(struct batadv_icmp_packet_rr))) { if (skb_linearize(skb) < 0) - goto out; + goto free_skb; /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) - goto out; + goto free_skb; ethhdr = eth_hdr(skb); icmph = (struct batadv_icmp_header *)skb->data; icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph; if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN) - goto out; + goto free_skb; ether_addr_copy(icmp_packet_rr->rr[icmp_packet_rr->rr_cur], ethhdr->h_dest); @@ -396,11 +411,11 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, /* get routing information */ orig_node = batadv_orig_hash_find(bat_priv, icmph->dst); if (!orig_node) - goto out; + goto free_skb; /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) - goto out; + goto put_orig_node; icmph = (struct batadv_icmp_header *)skb->data; @@ -409,11 +424,18 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, /* route it */ res = batadv_send_skb_to_orig(skb, orig_node, recv_if); - ret = NET_RX_SUCCESS; + if (res == NET_XMIT_SUCCESS) + ret = NET_RX_SUCCESS; -out: + /* skb was consumed */ + skb = NULL; + +put_orig_node: if (orig_node) batadv_orig_node_put(orig_node); +free_skb: + kfree_skb(skb); + return ret; } @@ -662,18 +684,18 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, if (unicast_packet->ttl < 2) { pr_debug("Warning - can't forward unicast packet from %pM to %pM: ttl exceeded\n", ethhdr->h_source, unicast_packet->dest); - goto out; + goto free_skb; } /* get routing information */ orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->dest); if (!orig_node) - goto out; + goto free_skb; /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) - goto out; + goto put_orig_node; /* decrement ttl */ unicast_packet = (struct batadv_unicast_packet *)skb->data; @@ -697,6 +719,11 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, len = skb->len; res = batadv_send_skb_to_orig(skb, orig_node, recv_if); + if (res == NET_XMIT_SUCCESS) + ret = NET_RX_SUCCESS; + + /* skb was consumed */ + skb = NULL; /* translate transmit result into receive result */ if (res == NET_XMIT_SUCCESS) { @@ -706,11 +733,11 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, len + ETH_HLEN); } - ret = NET_RX_SUCCESS; +put_orig_node: + batadv_orig_node_put(orig_node); +free_skb: + kfree_skb(skb); -out: - if (orig_node) - batadv_orig_node_put(orig_node); return ret; } @@ -895,14 +922,18 @@ int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb, check = batadv_check_unicast_packet(bat_priv, skb, hdr_size); if (check < 0) - return NET_RX_DROP; + goto free_skb; /* we don't know about this type, drop it. */ unicast_packet = (struct batadv_unicast_packet *)skb->data; if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) - return NET_RX_DROP; + goto free_skb; return batadv_route_unicast_packet(skb, recv_if); + +free_skb: + kfree_skb(skb); + return NET_RX_DROP; } int batadv_recv_unicast_packet(struct sk_buff *skb, @@ -916,6 +947,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, int check, hdr_size = sizeof(*unicast_packet); enum batadv_subtype subtype; bool is4addr; + int ret = NET_RX_DROP; unicast_packet = (struct batadv_unicast_packet *)skb->data; unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; @@ -935,9 +967,9 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, batadv_nc_skb_store_sniffed_unicast(bat_priv, skb); if (check < 0) - return NET_RX_DROP; + goto free_skb; if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size)) - return NET_RX_DROP; + goto free_skb; /* packet for me */ if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) { @@ -975,7 +1007,14 @@ rx_success: return NET_RX_SUCCESS; } - return batadv_route_unicast_packet(skb, recv_if); + ret = batadv_route_unicast_packet(skb, recv_if); + /* skb was consumed */ + skb = NULL; + +free_skb: + kfree_skb(skb); + + return ret; } /** @@ -997,15 +1036,15 @@ int batadv_recv_unicast_tvlv(struct sk_buff *skb, int ret = NET_RX_DROP; if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0) - return NET_RX_DROP; + goto free_skb; /* the header is likely to be modified while forwarding */ if (skb_cow(skb, hdr_size) < 0) - return NET_RX_DROP; + goto free_skb; /* packet needs to be linearized to access the tvlv content */ if (skb_linearize(skb) < 0) - return NET_RX_DROP; + goto free_skb; unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)skb->data; @@ -1013,17 +1052,21 @@ int batadv_recv_unicast_tvlv(struct sk_buff *skb, tvlv_buff_len = ntohs(unicast_tvlv_packet->tvlv_len); if (tvlv_buff_len > skb->len - hdr_size) - return NET_RX_DROP; + goto free_skb; ret = batadv_tvlv_containers_process(bat_priv, false, NULL, unicast_tvlv_packet->src, unicast_tvlv_packet->dst, tvlv_buff, tvlv_buff_len); - if (ret != NET_RX_SUCCESS) + if (ret != NET_RX_SUCCESS) { ret = batadv_route_unicast_packet(skb, recv_if); - else - consume_skb(skb); + /* skb was consumed */ + skb = NULL; + } + +free_skb: + kfree_skb(skb); return ret; } @@ -1049,20 +1092,22 @@ int batadv_recv_frag_packet(struct sk_buff *skb, if (batadv_check_unicast_packet(bat_priv, skb, sizeof(*frag_packet)) < 0) - goto out; + goto free_skb; frag_packet = (struct batadv_frag_packet *)skb->data; orig_node_src = batadv_orig_hash_find(bat_priv, frag_packet->orig); if (!orig_node_src) - goto out; + goto free_skb; skb->priority = frag_packet->priority + 256; /* Route the fragment if it is not for us and too big to be merged. */ if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { + /* skb was consumed */ + skb = NULL; ret = NET_RX_SUCCESS; - goto out; + goto put_orig_node; } batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX); @@ -1070,20 +1115,24 @@ int batadv_recv_frag_packet(struct sk_buff *skb, /* Add fragment to buffer and merge if possible. */ if (!batadv_frag_skb_buffer(&skb, orig_node_src)) - goto out; + goto put_orig_node; /* Deliver merged packet to the appropriate handler, if it was * merged */ - if (skb) + if (skb) { batadv_batman_skb_recv(skb, recv_if->net_dev, &recv_if->batman_adv_ptype, NULL); + /* skb was consumed */ + skb = NULL; + } ret = NET_RX_SUCCESS; -out: - if (orig_node_src) - batadv_orig_node_put(orig_node_src); +put_orig_node: + batadv_orig_node_put(orig_node_src); +free_skb: + kfree_skb(skb); return ret; } @@ -1102,35 +1151,35 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) - goto out; + goto free_skb; ethhdr = eth_hdr(skb); /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) - goto out; + goto free_skb; /* packet with broadcast sender address */ if (is_broadcast_ether_addr(ethhdr->h_source)) - goto out; + goto free_skb; /* ignore broadcasts sent by myself */ if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) - goto out; + goto free_skb; bcast_packet = (struct batadv_bcast_packet *)skb->data; /* ignore broadcasts originated by myself */ if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) - goto out; + goto free_skb; if (bcast_packet->ttl < 2) - goto out; + goto free_skb; orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); if (!orig_node) - goto out; + goto free_skb; spin_lock_bh(&orig_node->bcast_seqno_lock); @@ -1158,7 +1207,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, /* check whether this has been sent by another originator before */ if (batadv_bla_check_bcast_duplist(bat_priv, skb)) - goto out; + goto free_skb; batadv_skb_set_priority(skb, sizeof(struct batadv_bcast_packet)); @@ -1169,7 +1218,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, * from the same backbone. */ if (batadv_bla_is_backbone_gw(skb, orig_node, hdr_size)) - goto out; + goto free_skb; if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, hdr_size)) goto rx_success; @@ -1185,6 +1234,8 @@ rx_success: spin_unlock: spin_unlock_bh(&orig_node->bcast_seqno_lock); +free_skb: + kfree_skb(skb); out: if (orig_node) batadv_orig_node_put(orig_node); -- cgit v1.1 From 7d72d174c7b6e3641bf13d1816f672051f6480b4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 17 Jul 2016 21:04:05 +0200 Subject: batman-adv: Remove dev_queue_xmit return code exception No caller of batadv_send_skb_to_orig is expecting the results to be -1 (-EPERM) anymore when the skbuff was not consumed. They will instead expect that the skbuff is always consumed. Having such return code filter is therefore not needed anymore. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/send.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index b00aac7..9ea272e 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -64,8 +64,11 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work); * If neigh_node is NULL, then the packet is broadcasted using hard_iface, * otherwise it is sent as unicast to the given neighbor. * - * Return: NET_TX_DROP in case of error or the result of dev_queue_xmit(skb) - * otherwise + * Regardless of the return value, the skb is consumed. + * + * Return: A negative errno code is returned on a failure. A success does not + * guarantee the frame will be transmitted as it may be dropped due + * to congestion or traffic shaping. */ int batadv_send_skb_packet(struct sk_buff *skb, struct batadv_hard_iface *hard_iface, @@ -73,7 +76,6 @@ int batadv_send_skb_packet(struct sk_buff *skb, { struct batadv_priv *bat_priv; struct ethhdr *ethhdr; - int ret; bat_priv = netdev_priv(hard_iface->soft_iface); @@ -111,15 +113,8 @@ int batadv_send_skb_packet(struct sk_buff *skb, /* dev_queue_xmit() returns a negative result on error. However on * congestion and traffic shaping, it drops and returns NET_XMIT_DROP * (which is > 0). This will not be treated as an error. - * - * a negative value cannot be returned because it could be interepreted - * as not consumed skb by callers of batadv_send_skb_to_orig. */ - ret = dev_queue_xmit(skb); - if (ret < 0) - ret = NET_XMIT_DROP; - - return ret; + return dev_queue_xmit(skb); send_skb_err: kfree_skb(skb); return NET_XMIT_DROP; -- cgit v1.1 From 9f75c8e1c8cc868645f34152df5ffd3fe97d1620 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 6 Aug 2016 17:04:21 +0200 Subject: batman-adv: Disallow mcast src address for data frames The routing checks are validating the source mac address of the outer ethernet header. They reject every source mac address which is a broadcast address. But they also have to reject any multicast mac addresses. Signed-off-by: Sven Eckelmann [sw@simonwunderlich.de: fix commit message typo] Signed-off-by: Simon Wunderlich --- net/batman-adv/routing.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 105d4fc..c02897b 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -368,8 +368,8 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, if (is_broadcast_ether_addr(ethhdr->h_dest)) goto free_skb; - /* packet with broadcast sender address */ - if (is_broadcast_ether_addr(ethhdr->h_source)) + /* packet with broadcast/multicast sender address */ + if (is_multicast_ether_addr(ethhdr->h_source)) goto free_skb; /* not for me */ @@ -466,8 +466,8 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, if (is_broadcast_ether_addr(ethhdr->h_dest)) return -EBADR; - /* packet with broadcast sender address */ - if (is_broadcast_ether_addr(ethhdr->h_source)) + /* packet with broadcast/multicast sender address */ + if (is_multicast_ether_addr(ethhdr->h_source)) return -EBADR; /* not for me */ @@ -1159,8 +1159,8 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, if (!is_broadcast_ether_addr(ethhdr->h_dest)) goto free_skb; - /* packet with broadcast sender address */ - if (is_broadcast_ether_addr(ethhdr->h_source)) + /* packet with broadcast/multicast sender address */ + if (is_multicast_ether_addr(ethhdr->h_source)) goto free_skb; /* ignore broadcasts sent by myself */ -- cgit v1.1 From 92eef520d77d106d9ea06b17294c5079edefe95d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 6 Aug 2016 17:04:22 +0200 Subject: batman-adv: Disallow zero and mcast src address for mgmt frames The routing check for management frames is validating the source mac address in the outer ethernet header. It rejects every source mac address which is a broadcast address. But it also has to reject the zero-mac address and multicast mac addresses. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/routing.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index c02897b..4f034df 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -196,8 +196,8 @@ bool batadv_check_management_packet(struct sk_buff *skb, if (!is_broadcast_ether_addr(ethhdr->h_dest)) return false; - /* packet with broadcast sender address */ - if (is_broadcast_ether_addr(ethhdr->h_source)) + /* packet with invalid sender address */ + if (!is_valid_ether_addr(ethhdr->h_source)) return false; /* create a copy of the skb, if needed, to modify it. */ -- cgit v1.1 From 88ffc7d0e2cdcd3d8ba603d51b81a65712899c6c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 30 Sep 2016 15:21:00 +0200 Subject: batman-adv: Return non-const ptr in batadv_getlink_net The returned net_namespace of batadv_getlink_net may be used with functions that potentially modify the struct. Thus it must return the pointer as non-const like rtnl_link_ops::get_link_net does. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/hard-interface.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index a7a462e..fdbb47e 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -92,8 +92,8 @@ out: * * Return: result of rtnl_link_ops->get_link_net or @fallback_net */ -static const struct net *batadv_getlink_net(const struct net_device *netdev, - const struct net *fallback_net) +static struct net *batadv_getlink_net(const struct net_device *netdev, + struct net *fallback_net) { if (!netdev->rtnl_link_ops) return fallback_net; @@ -116,9 +116,9 @@ static const struct net *batadv_getlink_net(const struct net_device *netdev, * Return: true if the devices are each others parent, otherwise false */ static bool batadv_mutual_parents(const struct net_device *dev1, - const struct net *net1, + struct net *net1, const struct net_device *dev2, - const struct net *net2) + struct net *net2) { int dev1_parent_iflink = dev_get_iflink(dev1); int dev2_parent_iflink = dev_get_iflink(dev2); @@ -154,7 +154,7 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev) { struct net *net = dev_net(net_dev); struct net_device *parent_dev; - const struct net *parent_net; + struct net *parent_net; bool ret; /* check if this is a batman-adv mesh interface */ -- cgit v1.1 From 93bbaab455f30fd43911e0881a02107a17150a62 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 6 Aug 2016 17:04:23 +0200 Subject: batman-adv: Reject unicast packet with zero/mcast dst address An unicast batman-adv packet cannot be transmitted to a multicast or zero mac address. So reject incoming packets which still have these classes of addresses as destination mac address in the outer ethernet header. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/routing.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 4f034df..6713bdf 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -364,8 +364,8 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, ethhdr = eth_hdr(skb); - /* packet with unicast indication but broadcast recipient */ - if (is_broadcast_ether_addr(ethhdr->h_dest)) + /* packet with unicast indication but non-unicast recipient */ + if (!is_valid_ether_addr(ethhdr->h_dest)) goto free_skb; /* packet with broadcast/multicast sender address */ @@ -462,8 +462,8 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, ethhdr = eth_hdr(skb); - /* packet with unicast indication but broadcast recipient */ - if (is_broadcast_ether_addr(ethhdr->h_dest)) + /* packet with unicast indication but non-unicast recipient */ + if (!is_valid_ether_addr(ethhdr->h_dest)) return -EBADR; /* packet with broadcast/multicast sender address */ -- cgit v1.1 From f44a3ae9a2811cfd5321e0f72c02bc270410db64 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Fri, 30 Sep 2016 15:21:02 +0200 Subject: batman-adv: refactor wifi interface detection The ELP protocol requires cfg80211 to auto-detect the WiFi througput to a given neighbor. Use batadv_is_cfg80211_netdev() to determine whether or not an interface is eligible. Signed-off-by: Marek Lindner Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_v_elp.c | 29 ++++++++++++++--------------- net/batman-adv/hard-interface.c | 26 +++++++++++++++++++++----- net/batman-adv/hard-interface.h | 1 + 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index 54bdd41..e601def 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -90,22 +90,21 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) * cfg80211 API */ if (batadv_is_wifi_netdev(hard_iface->net_dev)) { - if (hard_iface->net_dev->ieee80211_ptr) { - ret = cfg80211_get_station(hard_iface->net_dev, - neigh->addr, &sinfo); - if (ret == -ENOENT) { - /* Node is not associated anymore! It would be - * possible to delete this neighbor. For now set - * the throughput metric to 0. - */ - return 0; - } - if (!ret) - return sinfo.expected_throughput / 100; + if (!batadv_is_cfg80211_netdev(hard_iface->net_dev)) + /* unsupported WiFi driver version */ + goto default_throughput; + + ret = cfg80211_get_station(hard_iface->net_dev, + neigh->addr, &sinfo); + if (ret == -ENOENT) { + /* Node is not associated anymore! It would be + * possible to delete this neighbor. For now set + * the throughput metric to 0. + */ + return 0; } - - /* unsupported WiFi driver version */ - goto default_throughput; + if (!ret) + return sinfo.expected_throughput / 100; } /* if not a wifi interface, check if this device provides data via diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index fdbb47e..c3fbc1b9 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -202,6 +202,26 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) } /** + * batadv_is_cfg80211_netdev - check if the given net_device struct is a + * cfg80211 wifi interface + * @net_device: the device to check + * + * Return: true if the net device is a cfg80211 wireless device, false + * otherwise. + */ +bool batadv_is_cfg80211_netdev(struct net_device *net_device) +{ + if (!net_device) + return false; + + /* cfg80211 drivers have to set ieee80211_ptr */ + if (net_device->ieee80211_ptr) + return true; + + return false; +} + +/** * batadv_is_wifi_netdev - check if the given net_device struct is a wifi * interface * @net_device: the device to check @@ -221,11 +241,7 @@ bool batadv_is_wifi_netdev(struct net_device *net_device) return true; #endif - /* cfg80211 drivers have to set ieee80211_ptr */ - if (net_device->ieee80211_ptr) - return true; - - return false; + return batadv_is_cfg80211_netdev(net_device); } /** diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index a043182..7f9acab 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -65,6 +65,7 @@ enum batadv_hard_if_cleanup { extern struct notifier_block batadv_hard_if_notifier; +bool batadv_is_cfg80211_netdev(struct net_device *net_device); bool batadv_is_wifi_netdev(struct net_device *net_device); bool batadv_is_wifi_iface(int ifindex); struct batadv_hard_iface* -- cgit v1.1 From 10b1bbb46c6c8e571458a9a8913514dbebbaf20c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 30 Sep 2016 15:21:03 +0200 Subject: batman-adv: Cache the type of wifi device for each hardif batman-adv is requiring the type of wifi device in different contexts. Some of them can take the rtnl semaphore and some of them already have the semaphore taken. But even others don't allow that the semaphore will be taken. The data has to be retrieved when the hardif is added to batman-adv because some of the wifi information for an hardif will only be available with rtnl lock. It can then be cached in the batadv_hard_iface and the functions is_wifi_netdev and is_cfg80211_netdev can just compare the correct bits without imposing extra locking requirements. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 2 +- net/batman-adv/bat_v_elp.c | 8 ++-- net/batman-adv/hard-interface.c | 82 +++++++++++++++++++++++++++++++------- net/batman-adv/hard-interface.h | 5 +-- net/batman-adv/translation-table.c | 10 ++++- net/batman-adv/types.h | 13 ++++++ 6 files changed, 95 insertions(+), 25 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index bd39247..36d8818 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1272,7 +1272,7 @@ static bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, */ tq_iface_penalty = BATADV_TQ_MAX_VALUE; if (if_outgoing && (if_incoming == if_outgoing) && - batadv_is_wifi_netdev(if_outgoing->net_dev)) + batadv_is_wifi_hardif(if_outgoing)) tq_iface_penalty = batadv_hop_penalty(BATADV_TQ_MAX_VALUE, bat_priv); diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index e601def..0d78689 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -89,8 +89,8 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) /* if this is a wireless device, then ask its throughput through * cfg80211 API */ - if (batadv_is_wifi_netdev(hard_iface->net_dev)) { - if (!batadv_is_cfg80211_netdev(hard_iface->net_dev)) + if (batadv_is_wifi_hardif(hard_iface)) { + if (!batadv_is_cfg80211_hardif(hard_iface)) /* unsupported WiFi driver version */ goto default_throughput; @@ -186,7 +186,7 @@ batadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh) int elp_skb_len; /* this probing routine is for Wifi neighbours only */ - if (!batadv_is_wifi_netdev(hard_iface->net_dev)) + if (!batadv_is_wifi_hardif(hard_iface)) return true; /* probe the neighbor only if no unicast packets have been sent @@ -351,7 +351,7 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) /* warn the user (again) if there is no throughput data is available */ hard_iface->bat_v.flags &= ~BATADV_WARNING_DEFAULT; - if (batadv_is_wifi_netdev(hard_iface->net_dev)) + if (batadv_is_wifi_hardif(hard_iface)) hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX; INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq, diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index c3fbc1b9..57e8912 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -202,6 +202,30 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) } /** + * batadv_is_wext_netdev - check if the given net_device struct is a + * wext wifi interface + * @net_device: the device to check + * + * Return: true if the net device is a wext wireless device, false + * otherwise. + */ +static bool batadv_is_wext_netdev(struct net_device *net_device) +{ + if (!net_device) + return false; + +#ifdef CONFIG_WIRELESS_EXT + /* pre-cfg80211 drivers have to implement WEXT, so it is possible to + * check for wireless_handlers != NULL + */ + if (net_device->wireless_handlers) + return true; +#endif + + return false; +} + +/** * batadv_is_cfg80211_netdev - check if the given net_device struct is a * cfg80211 wifi interface * @net_device: the device to check @@ -209,7 +233,7 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) * Return: true if the net device is a cfg80211 wireless device, false * otherwise. */ -bool batadv_is_cfg80211_netdev(struct net_device *net_device) +static bool batadv_is_cfg80211_netdev(struct net_device *net_device) { if (!net_device) return false; @@ -222,26 +246,53 @@ bool batadv_is_cfg80211_netdev(struct net_device *net_device) } /** - * batadv_is_wifi_netdev - check if the given net_device struct is a wifi - * interface + * batadv_wifi_flags_evaluate - calculate wifi flags for net_device * @net_device: the device to check * + * Return: batadv_hard_iface_wifi_flags flags of the device + */ +static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) +{ + u32 wifi_flags = 0; + + if (batadv_is_wext_netdev(net_device)) + wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT; + + if (batadv_is_cfg80211_netdev(net_device)) + wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + + return wifi_flags; +} + +/** + * batadv_is_cfg80211_hardif - check if the given hardif is a cfg80211 wifi + * interface + * @hard_iface: the device to check + * + * Return: true if the net device is a cfg80211 wireless device, false + * otherwise. + */ +bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface) +{ + u32 allowed_flags = 0; + + allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + + return !!(hard_iface->wifi_flags & allowed_flags); +} + +/** + * batadv_is_wifi_hardif - check if the given hardif is a wifi interface + * @hard_iface: the device to check + * * Return: true if the net device is a 802.11 wireless device, false otherwise. */ -bool batadv_is_wifi_netdev(struct net_device *net_device) +bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface) { - if (!net_device) + if (!hard_iface) return false; -#ifdef CONFIG_WIRELESS_EXT - /* pre-cfg80211 drivers have to implement WEXT, so it is possible to - * check for wireless_handlers != NULL - */ - if (net_device->wireless_handlers) - return true; -#endif - - return batadv_is_cfg80211_netdev(net_device); + return hard_iface->wifi_flags != 0; } /** @@ -765,7 +816,8 @@ batadv_hardif_add_interface(struct net_device *net_dev) kref_init(&hard_iface->refcount); hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT; - if (batadv_is_wifi_netdev(net_dev)) + hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); + if (batadv_is_wifi_hardif(hard_iface)) hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; batadv_v_hardif_init(hard_iface); diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 7f9acab..7281dd2 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -65,9 +65,8 @@ enum batadv_hard_if_cleanup { extern struct notifier_block batadv_hard_if_notifier; -bool batadv_is_cfg80211_netdev(struct net_device *net_device); -bool batadv_is_wifi_netdev(struct net_device *net_device); -bool batadv_is_wifi_iface(int ifindex); +bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface); +bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface); struct batadv_hard_iface* batadv_hardif_get_by_netdev(const struct net_device *net_dev); int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 3cae8f4f..447f949 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -646,6 +646,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, struct net *net = dev_net(soft_iface); struct batadv_softif_vlan *vlan; struct net_device *in_dev = NULL; + struct batadv_hard_iface *in_hardif = NULL; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry; int hash_added, table_size, packet_size_max; @@ -657,6 +658,9 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, if (ifindex != BATADV_NULL_IFINDEX) in_dev = dev_get_by_index(net, ifindex); + if (in_dev) + in_hardif = batadv_hardif_get_by_netdev(in_dev); + tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!is_multicast_ether_addr(addr)) @@ -730,7 +734,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, */ tt_local->common.flags = BATADV_TT_CLIENT_NEW; tt_local->common.vid = vid; - if (batadv_is_wifi_netdev(in_dev)) + if (batadv_is_wifi_hardif(in_hardif)) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; kref_init(&tt_local->common.refcount); tt_local->last_seen = jiffies; @@ -790,7 +794,7 @@ check_roaming: */ remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK; - if (batadv_is_wifi_netdev(in_dev)) + if (batadv_is_wifi_hardif(in_hardif)) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; else tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI; @@ -814,6 +818,8 @@ check_roaming: ret = true; out: + if (in_hardif) + batadv_hardif_put(in_hardif); if (in_dev) dev_put(in_dev); if (tt_local) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 98ebac0..1bcce32 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -119,11 +119,23 @@ struct batadv_hard_iface_bat_v { }; /** + * enum batadv_hard_iface_wifi_flags - Flags describing the wifi configuration + * of a batadv_hard_iface + * @BATADV_HARDIF_WIFI_WEXT_DIRECT: it is a wext wifi device + * @BATADV_HARDIF_WIFI_CFG80211_DIRECT: it is a cfg80211 wifi device + */ +enum batadv_hard_iface_wifi_flags { + BATADV_HARDIF_WIFI_WEXT_DIRECT = BIT(0), + BATADV_HARDIF_WIFI_CFG80211_DIRECT = BIT(1), +}; + +/** * struct batadv_hard_iface - network device known to batman-adv * @list: list node for batadv_hardif_list * @if_num: identificator of the interface * @if_status: status of the interface for batman-adv * @num_bcasts: number of payload re-broadcasts on this interface (ARQ) + * @wifi_flags: flags whether this is (directly or indirectly) a wifi interface * @net_dev: pointer to the net_device * @hardif_obj: kobject of the per interface sysfs "mesh" directory * @refcount: number of contexts the object is used @@ -142,6 +154,7 @@ struct batadv_hard_iface { s16 if_num; char if_status; u8 num_bcasts; + u32 wifi_flags; struct net_device *net_dev; struct kobject *hardif_obj; struct kref refcount; -- cgit v1.1 From 5ed4a460a1d359e1e9e7f54f6c78dfa9d1586b4b Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Fri, 30 Sep 2016 15:21:04 +0200 Subject: batman-adv: additional checks for virtual interfaces on top of WiFi In a few situations batman-adv tries to determine whether a given interface is a WiFi interface to enable specific WiFi optimizations. If the interface batman-adv has been configured with is a virtual interface (e.g. VLAN) it would not be properly detected as WiFi interface and thus not benefit from the special WiFi treatment. This patch changes that by peeking under the hood whenever a virtual interface is in play. Signed-off-by: Marek Lindner [sven.eckelmann@open-mesh.com: integrate in wifi_flags caching, retrieve namespace of link interface] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/hard-interface.c | 58 +++++++++++++++++++++++++++++++++++++++++ net/batman-adv/types.h | 4 +++ 2 files changed, 62 insertions(+) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 57e8912..df31f2f 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -202,6 +202,47 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) } /** + * batadv_get_real_netdevice - check if the given netdev struct is a virtual + * interface on top of another 'real' interface + * @netdev: the device to check + * + * Return: the 'real' net device or the original net device and NULL in case + * of an error. + */ +static struct net_device *batadv_get_real_netdevice(struct net_device *netdev) +{ + struct batadv_hard_iface *hard_iface = NULL; + struct net_device *real_netdev = NULL; + struct net *real_net; + struct net *net; + int ifindex; + + ASSERT_RTNL(); + + if (!netdev) + return NULL; + + if (netdev->ifindex == dev_get_iflink(netdev)) { + dev_hold(netdev); + return netdev; + } + + hard_iface = batadv_hardif_get_by_netdev(netdev); + if (!hard_iface || !hard_iface->soft_iface) + goto out; + + net = dev_net(hard_iface->soft_iface); + ifindex = dev_get_iflink(netdev); + real_net = batadv_getlink_net(netdev, net); + real_netdev = dev_get_by_index(real_net, ifindex); + +out: + if (hard_iface) + batadv_hardif_put(hard_iface); + return real_netdev; +} + +/** * batadv_is_wext_netdev - check if the given net_device struct is a * wext wifi interface * @net_device: the device to check @@ -254,6 +295,7 @@ static bool batadv_is_cfg80211_netdev(struct net_device *net_device) static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) { u32 wifi_flags = 0; + struct net_device *real_netdev; if (batadv_is_wext_netdev(net_device)) wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT; @@ -261,6 +303,21 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) if (batadv_is_cfg80211_netdev(net_device)) wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + real_netdev = batadv_get_real_netdevice(net_device); + if (!real_netdev) + return wifi_flags; + + if (real_netdev == net_device) + goto out; + + if (batadv_is_wext_netdev(real_netdev)) + wifi_flags |= BATADV_HARDIF_WIFI_WEXT_INDIRECT; + + if (batadv_is_cfg80211_netdev(real_netdev)) + wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT; + +out: + dev_put(real_netdev); return wifi_flags; } @@ -277,6 +334,7 @@ bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface) u32 allowed_flags = 0; allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT; return !!(hard_iface->wifi_flags & allowed_flags); } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 1bcce32..caa00a5 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -123,10 +123,14 @@ struct batadv_hard_iface_bat_v { * of a batadv_hard_iface * @BATADV_HARDIF_WIFI_WEXT_DIRECT: it is a wext wifi device * @BATADV_HARDIF_WIFI_CFG80211_DIRECT: it is a cfg80211 wifi device + * @BATADV_HARDIF_WIFI_WEXT_INDIRECT: link device is a wext wifi device + * @BATADV_HARDIF_WIFI_CFG80211_INDIRECT: link device is a cfg80211 wifi device */ enum batadv_hard_iface_wifi_flags { BATADV_HARDIF_WIFI_WEXT_DIRECT = BIT(0), BATADV_HARDIF_WIFI_CFG80211_DIRECT = BIT(1), + BATADV_HARDIF_WIFI_WEXT_INDIRECT = BIT(2), + BATADV_HARDIF_WIFI_CFG80211_INDIRECT = BIT(3), }; /** -- cgit v1.1 From 1942de1bbac30118a96255af1d14d1449aeb1a38 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Fri, 30 Sep 2016 15:21:05 +0200 Subject: batman-adv: retrieve B.A.T.M.A.N. V WiFi neighbor stats from real interface Signed-off-by: Marek Lindner [sven.eckelmann@open-mesh.com: re-add batadv_get_real_netdev to take rtnl semaphore for batadv_get_real_netdevice] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_v_elp.c | 10 ++++++++-- net/batman-adv/hard-interface.c | 22 ++++++++++++++++++++++ net/batman-adv/hard-interface.h | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index 0d78689..f2fb2f0 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -75,6 +75,7 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) { struct batadv_hard_iface *hard_iface = neigh->if_incoming; struct ethtool_link_ksettings link_settings; + struct net_device *real_netdev; struct station_info sinfo; u32 throughput; int ret; @@ -94,8 +95,13 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) /* unsupported WiFi driver version */ goto default_throughput; - ret = cfg80211_get_station(hard_iface->net_dev, - neigh->addr, &sinfo); + real_netdev = batadv_get_real_netdev(hard_iface->net_dev); + if (!real_netdev) + goto default_throughput; + + ret = cfg80211_get_station(real_netdev, neigh->addr, &sinfo); + + dev_put(real_netdev); if (ret == -ENOENT) { /* Node is not associated anymore! It would be * possible to delete this neighbor. For now set diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index df31f2f..ca743a5 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -206,6 +206,9 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) * interface on top of another 'real' interface * @netdev: the device to check * + * Callers must hold the rtnl semaphore. You may want batadv_get_real_netdev() + * instead of this. + * * Return: the 'real' net device or the original net device and NULL in case * of an error. */ @@ -243,6 +246,25 @@ out: } /** + * batadv_get_real_netdev - check if the given net_device struct is a virtual + * interface on top of another 'real' interface + * @net_device: the device to check + * + * Return: the 'real' net device or the original net device and NULL in case + * of an error. + */ +struct net_device *batadv_get_real_netdev(struct net_device *net_device) +{ + struct net_device *real_netdev; + + rtnl_lock(); + real_netdev = batadv_get_real_netdevice(net_device); + rtnl_unlock(); + + return real_netdev; +} + +/** * batadv_is_wext_netdev - check if the given net_device struct is a * wext wifi interface * @net_device: the device to check diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 7281dd2..d6309a4 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -65,6 +65,7 @@ enum batadv_hard_if_cleanup { extern struct notifier_block batadv_hard_if_notifier; +struct net_device *batadv_get_real_netdev(struct net_device *net_device); bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface); bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface); struct batadv_hard_iface* -- cgit v1.1 From ee3b5e9fe8400fac8e0d08db7c5c985df0234f64 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 30 Sep 2016 15:21:06 +0200 Subject: batman-adv: Update wifi flags on upper link change Things like VLANs don't have their link set when they are created. Thus the wifi flags have to be evaluated later to fix their contents for the link interface. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/hard-interface.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index ca743a5..61a431a 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -1006,6 +1006,11 @@ static int batadv_hard_if_event(struct notifier_block *this, if (hard_iface == primary_if) batadv_primary_if_update_addr(bat_priv, NULL); break; + case NETDEV_CHANGEUPPER: + hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); + if (batadv_is_wifi_hardif(hard_iface)) + hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; + break; default: break; } -- cgit v1.1 From 2c0c06ff442313c639018d97a8212dd825cb7b4c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 29 Oct 2016 13:56:23 +0200 Subject: batman-adv: Add module alias for batadv netlink family The batman-adv module has to be loaded to fulfill genl request by the userspace. When it is not loaded then requests will fail. It is therefore useful to get the module automatically loaded when such a request is made. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 6b5dae6..d46415e 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include "bat_algo.h" #include "bat_iv_ogm.h" @@ -648,3 +650,4 @@ MODULE_DESCRIPTION(BATADV_DRIVER_DESC); MODULE_SUPPORTED_DEVICE(BATADV_DRIVER_DEVICE); MODULE_VERSION(BATADV_SOURCE_VERSION); MODULE_ALIAS_RTNL_LINK("batadv"); +MODULE_ALIAS_GENL_FAMILY(BATADV_NL_NAME); -- cgit v1.1 From 9b4aec647a92a2464337db10507348aecf0f0fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 1 Nov 2016 09:44:44 +0100 Subject: batman-adv: fix rare race conditions on interface removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In rare cases during shutdown the following general protection fault can happen: general protection fault: 0000 [#1] SMP Modules linked in: batman_adv(O-) [...] CPU: 3 PID: 1714 Comm: rmmod Tainted: G O 4.6.0-rc6+ #1 [...] Call Trace: [] batadv_hardif_disable_interface+0x29a/0x3a6 [batman_adv] [] batadv_softif_destroy_netlink+0x4b/0xa4 [batman_adv] [] __rtnl_link_unregister+0x48/0x92 [] rtnl_link_unregister+0xc1/0xdb [] ? bit_waitqueue+0x87/0x87 [] batadv_exit+0x1a/0xf48 [batman_adv] [] SyS_delete_module+0x136/0x1b0 [] entry_SYSCALL_64_fastpath+0x18/0xa8 [] ? trace_hardirqs_off_caller+0x37/0xa6 Code: 89 f7 e8 21 bd 0d e1 4d 85 e4 75 0e 31 f6 48 c7 c7 50 d7 3b a0 e8 50 16 f2 e0 49 8b 9c 24 28 01 00 00 48 85 db 0f 84 b2 00 00 00 <48> 8b 03 4d 85 ed 48 89 45 c8 74 09 4c 39 ab f8 00 00 00 75 1c RIP [] batadv_purge_outstanding_packets+0x1c8/0x291 [batman_adv] RSP ---[ end trace 803b9bdc6a4a952b ]--- Kernel panic - not syncing: Fatal exception in interrupt Kernel Offset: disabled ---[ end Kernel panic - not syncing: Fatal exception in interrupt It does not happen often, but may potentially happen when frequently shutting down and reinitializing an interface. With some carefully placed msleep()s/mdelay()s it can be reproduced easily. The issue is, that on interface removal, any still running worker thread of a forwarding packet will race with the interface purging routine to free a forwarding packet. Temporarily giving up a spin-lock to be able to sleep in the purging routine is not safe. Furthermore, there is a potential general protection fault not just for the purging side shown above, but also on the worker side: Temporarily removing a forw_packet from the according forw_{bcast,bat}_list will make it impossible for the purging routine to catch and cancel it. # How this patch tries to fix it: With this patch we split the queue purging into three steps: Step 1), removing forward packets from the queue of an interface and by that claim it as our responsibility to free. Step 2), we are either lucky to cancel a pending worker before it starts to run. Or if it is already running, we wait and let it do its thing, except two things: Through the claiming in step 1) we prevent workers from a) re-arming themselves. And b) prevent workers from freeing packets which we still hold in the interface purging routine. Finally, step 3, we are sure that no forwarding packets are pending or even running anymore on the interface to remove. We can then safely free the claimed forwarding packets. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 19 +-- net/batman-adv/send.c | 283 +++++++++++++++++++++++++++++++++----------- net/batman-adv/send.h | 5 + net/batman-adv/types.h | 2 + 4 files changed, 227 insertions(+), 82 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 36d8818..f00f666 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -717,17 +717,10 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, if (direct_link) forw_packet_aggr->direct_link_flags |= 1; - /* add new packet to packet list */ - spin_lock_bh(&bat_priv->forw_bat_list_lock); - hlist_add_head(&forw_packet_aggr->list, &bat_priv->forw_bat_list); - spin_unlock_bh(&bat_priv->forw_bat_list_lock); - - /* start timer for this packet */ INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work, batadv_iv_send_outstanding_bat_ogm_packet); - queue_delayed_work(batadv_event_workqueue, - &forw_packet_aggr->delayed_work, - send_time - jiffies); + + batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time); } /* aggregate a new packet into the existing ogm packet */ @@ -1789,9 +1782,6 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work) forw_packet = container_of(delayed_work, struct batadv_forw_packet, delayed_work); bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface); - spin_lock_bh(&bat_priv->forw_bat_list_lock); - hlist_del(&forw_packet->list); - spin_unlock_bh(&bat_priv->forw_bat_list_lock); if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { dropped = true; @@ -1813,7 +1803,10 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work) batadv_iv_ogm_schedule(forw_packet->if_incoming); out: - batadv_forw_packet_free(forw_packet, dropped); + /* do we get something for free()? */ + if (batadv_forw_packet_steal(forw_packet, + &bat_priv->forw_bat_list_lock)) + batadv_forw_packet_free(forw_packet, dropped); } static int batadv_iv_ogm_receive(struct sk_buff *skb, diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 9ea272e..49021b7 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -19,6 +19,7 @@ #include "main.h" #include +#include #include #include #include @@ -520,6 +521,8 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, if (if_outgoing) kref_get(&if_outgoing->refcount); + INIT_HLIST_NODE(&forw_packet->list); + INIT_HLIST_NODE(&forw_packet->cleanup_list); forw_packet->skb = NULL; forw_packet->queue_left = queue_left; forw_packet->if_incoming = if_incoming; @@ -535,19 +538,191 @@ err: return NULL; } +/** + * batadv_forw_packet_was_stolen - check whether someone stole this packet + * @forw_packet: the forwarding packet to check + * + * This function checks whether the given forwarding packet was claimed by + * someone else for free(). + * + * Return: True if someone stole it, false otherwise. + */ +static bool +batadv_forw_packet_was_stolen(struct batadv_forw_packet *forw_packet) +{ + return !hlist_unhashed(&forw_packet->cleanup_list); +} + +/** + * batadv_forw_packet_steal - claim a forw_packet for free() + * @forw_packet: the forwarding packet to steal + * @lock: a key to the store to steal from (e.g. forw_{bat,bcast}_list_lock) + * + * This function tries to steal a specific forw_packet from global + * visibility for the purpose of getting it for free(). That means + * the caller is *not* allowed to requeue it afterwards. + * + * Return: True if stealing was successful. False if someone else stole it + * before us. + */ +bool batadv_forw_packet_steal(struct batadv_forw_packet *forw_packet, + spinlock_t *lock) +{ + /* did purging routine steal it earlier? */ + spin_lock_bh(lock); + if (batadv_forw_packet_was_stolen(forw_packet)) { + spin_unlock_bh(lock); + return false; + } + + hlist_del_init(&forw_packet->list); + + /* Just to spot misuse of this function */ + hlist_add_fake(&forw_packet->cleanup_list); + + spin_unlock_bh(lock); + return true; +} + +/** + * batadv_forw_packet_list_steal - claim a list of forward packets for free() + * @forw_list: the to be stolen forward packets + * @cleanup_list: a backup pointer, to be able to dispose the packet later + * @hard_iface: the interface to steal forward packets from + * + * This function claims responsibility to free any forw_packet queued on the + * given hard_iface. If hard_iface is NULL forwarding packets on all hard + * interfaces will be claimed. + * + * The packets are being moved from the forw_list to the cleanup_list and + * by that allows already running threads to notice the claiming. + */ static void -_batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, - struct batadv_forw_packet *forw_packet, - unsigned long send_time) +batadv_forw_packet_list_steal(struct hlist_head *forw_list, + struct hlist_head *cleanup_list, + const struct batadv_hard_iface *hard_iface) { - /* add new packet to packet list */ - spin_lock_bh(&bat_priv->forw_bcast_list_lock); - hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list); - spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + struct batadv_forw_packet *forw_packet; + struct hlist_node *safe_tmp_node; + + hlist_for_each_entry_safe(forw_packet, safe_tmp_node, + forw_list, list) { + /* if purge_outstanding_packets() was called with an argument + * we delete only packets belonging to the given interface + */ + if (hard_iface && + (forw_packet->if_incoming != hard_iface) && + (forw_packet->if_outgoing != hard_iface)) + continue; + + hlist_del(&forw_packet->list); + hlist_add_head(&forw_packet->cleanup_list, cleanup_list); + } +} + +/** + * batadv_forw_packet_list_free - free a list of forward packets + * @head: a list of to be freed forw_packets + * + * This function cancels the scheduling of any packet in the provided list, + * waits for any possibly running packet forwarding thread to finish and + * finally, safely frees this forward packet. + * + * This function might sleep. + */ +static void batadv_forw_packet_list_free(struct hlist_head *head) +{ + struct batadv_forw_packet *forw_packet; + struct hlist_node *safe_tmp_node; + + hlist_for_each_entry_safe(forw_packet, safe_tmp_node, head, + cleanup_list) { + cancel_delayed_work_sync(&forw_packet->delayed_work); - /* start timer for this packet */ - queue_delayed_work(batadv_event_workqueue, &forw_packet->delayed_work, - send_time); + hlist_del(&forw_packet->cleanup_list); + batadv_forw_packet_free(forw_packet, true); + } +} + +/** + * batadv_forw_packet_queue - try to queue a forwarding packet + * @forw_packet: the forwarding packet to queue + * @lock: a key to the store (e.g. forw_{bat,bcast}_list_lock) + * @head: the shelve to queue it on (e.g. forw_{bat,bcast}_list) + * @send_time: timestamp (jiffies) when the packet is to be sent + * + * This function tries to (re)queue a forwarding packet. Requeuing + * is prevented if the according interface is shutting down + * (e.g. if batadv_forw_packet_list_steal() was called for this + * packet earlier). + * + * Calling batadv_forw_packet_queue() after a call to + * batadv_forw_packet_steal() is forbidden! + * + * Caller needs to ensure that forw_packet->delayed_work was initialized. + */ +static void batadv_forw_packet_queue(struct batadv_forw_packet *forw_packet, + spinlock_t *lock, struct hlist_head *head, + unsigned long send_time) +{ + spin_lock_bh(lock); + + /* did purging routine steal it from us? */ + if (batadv_forw_packet_was_stolen(forw_packet)) { + /* If you got it for free() without trouble, then + * don't get back into the queue after stealing... + */ + WARN_ONCE(hlist_fake(&forw_packet->cleanup_list), + "Requeuing after batadv_forw_packet_steal() not allowed!\n"); + + spin_unlock_bh(lock); + return; + } + + hlist_del_init(&forw_packet->list); + hlist_add_head(&forw_packet->list, head); + + queue_delayed_work(batadv_event_workqueue, + &forw_packet->delayed_work, + send_time - jiffies); + spin_unlock_bh(lock); +} + +/** + * batadv_forw_packet_bcast_queue - try to queue a broadcast packet + * @bat_priv: the bat priv with all the soft interface information + * @forw_packet: the forwarding packet to queue + * @send_time: timestamp (jiffies) when the packet is to be sent + * + * This function tries to (re)queue a broadcast packet. + * + * Caller needs to ensure that forw_packet->delayed_work was initialized. + */ +static void +batadv_forw_packet_bcast_queue(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet, + unsigned long send_time) +{ + batadv_forw_packet_queue(forw_packet, &bat_priv->forw_bcast_list_lock, + &bat_priv->forw_bcast_list, send_time); +} + +/** + * batadv_forw_packet_ogmv1_queue - try to queue an OGMv1 packet + * @bat_priv: the bat priv with all the soft interface information + * @forw_packet: the forwarding packet to queue + * @send_time: timestamp (jiffies) when the packet is to be sent + * + * This function tries to (re)queue an OGMv1 packet. + * + * Caller needs to ensure that forw_packet->delayed_work was initialized. + */ +void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet, + unsigned long send_time) +{ + batadv_forw_packet_queue(forw_packet, &bat_priv->forw_bat_list_lock, + &bat_priv->forw_bat_list, send_time); } /** @@ -600,7 +775,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, INIT_DELAYED_WORK(&forw_packet->delayed_work, batadv_send_outstanding_bcast_packet); - _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, delay); + batadv_forw_packet_bcast_queue(bat_priv, forw_packet, jiffies + delay); return NETDEV_TX_OK; err_packet_free: @@ -619,6 +794,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) struct sk_buff *skb1; struct net_device *soft_iface; struct batadv_priv *bat_priv; + unsigned long send_time = jiffies + msecs_to_jiffies(5); bool dropped = false; u8 *neigh_addr; u8 *orig_neigh; @@ -630,10 +806,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) soft_iface = forw_packet->if_incoming->soft_iface; bat_priv = netdev_priv(soft_iface); - spin_lock_bh(&bat_priv->forw_bcast_list_lock); - hlist_del(&forw_packet->list); - spin_unlock_bh(&bat_priv->forw_bcast_list_lock); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { dropped = true; goto out; @@ -714,22 +886,34 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) /* if we still have some more bcasts to send */ if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) { - _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, - msecs_to_jiffies(5)); + batadv_forw_packet_bcast_queue(bat_priv, forw_packet, + send_time); return; } out: - batadv_forw_packet_free(forw_packet, dropped); + /* do we get something for free()? */ + if (batadv_forw_packet_steal(forw_packet, + &bat_priv->forw_bcast_list_lock)) + batadv_forw_packet_free(forw_packet, dropped); } +/** + * batadv_purge_outstanding_packets - stop/purge scheduled bcast/OGMv1 packets + * @bat_priv: the bat priv with all the soft interface information + * @hard_iface: the hard interface to cancel and purge bcast/ogm packets on + * + * This method cancels and purges any broadcast and OGMv1 packet on the given + * hard_iface. If hard_iface is NULL, broadcast and OGMv1 packets on all hard + * interfaces will be canceled and purged. + * + * This function might sleep. + */ void batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, const struct batadv_hard_iface *hard_iface) { - struct batadv_forw_packet *forw_packet; - struct hlist_node *safe_tmp_node; - bool pending; + struct hlist_head head = HLIST_HEAD_INIT; if (hard_iface) batadv_dbg(BATADV_DBG_BATMAN, bat_priv, @@ -739,57 +923,18 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "purge_outstanding_packets()\n"); - /* free bcast list */ + /* claim bcast list for free() */ spin_lock_bh(&bat_priv->forw_bcast_list_lock); - hlist_for_each_entry_safe(forw_packet, safe_tmp_node, - &bat_priv->forw_bcast_list, list) { - /* if purge_outstanding_packets() was called with an argument - * we delete only packets belonging to the given interface - */ - if ((hard_iface) && - (forw_packet->if_incoming != hard_iface) && - (forw_packet->if_outgoing != hard_iface)) - continue; - - spin_unlock_bh(&bat_priv->forw_bcast_list_lock); - - /* batadv_send_outstanding_bcast_packet() will lock the list to - * delete the item from the list - */ - pending = cancel_delayed_work_sync(&forw_packet->delayed_work); - spin_lock_bh(&bat_priv->forw_bcast_list_lock); - - if (pending) { - hlist_del(&forw_packet->list); - batadv_forw_packet_free(forw_packet, true); - } - } + batadv_forw_packet_list_steal(&bat_priv->forw_bcast_list, &head, + hard_iface); spin_unlock_bh(&bat_priv->forw_bcast_list_lock); - /* free batman packet list */ + /* claim batman packet list for free() */ spin_lock_bh(&bat_priv->forw_bat_list_lock); - hlist_for_each_entry_safe(forw_packet, safe_tmp_node, - &bat_priv->forw_bat_list, list) { - /* if purge_outstanding_packets() was called with an argument - * we delete only packets belonging to the given interface - */ - if ((hard_iface) && - (forw_packet->if_incoming != hard_iface) && - (forw_packet->if_outgoing != hard_iface)) - continue; - - spin_unlock_bh(&bat_priv->forw_bat_list_lock); - - /* send_outstanding_bat_packet() will lock the list to - * delete the item from the list - */ - pending = cancel_delayed_work_sync(&forw_packet->delayed_work); - spin_lock_bh(&bat_priv->forw_bat_list_lock); - - if (pending) { - hlist_del(&forw_packet->list); - batadv_forw_packet_free(forw_packet, true); - } - } + batadv_forw_packet_list_steal(&bat_priv->forw_bat_list, &head, + hard_iface); spin_unlock_bh(&bat_priv->forw_bat_list_lock); + + /* then cancel or wait for packet workers to finish and free */ + batadv_forw_packet_list_free(&head); } diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index c580194..a94e1e8 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -21,6 +21,7 @@ #include "main.h" #include +#include #include #include "packet.h" @@ -34,6 +35,10 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, atomic_t *queue_left, struct batadv_priv *bat_priv); +bool batadv_forw_packet_steal(struct batadv_forw_packet *packet, spinlock_t *l); +void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet, + unsigned long send_time); int batadv_send_skb_to_orig(struct sk_buff *skb, struct batadv_orig_node *orig_node, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index caa00a5..e913aee 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1385,6 +1385,7 @@ struct batadv_skb_cb { /** * struct batadv_forw_packet - structure for bcast packets to be sent/forwarded * @list: list node for batadv_priv::forw_{bat,bcast}_list + * @cleanup_list: list node for purging functions * @send_time: execution time for delayed_work (packet sending) * @own: bool for locally generated packets (local OGMs are re-scheduled after * sending) @@ -1401,6 +1402,7 @@ struct batadv_skb_cb { */ struct batadv_forw_packet { struct hlist_node list; + struct hlist_node cleanup_list; unsigned long send_time; u8 own; struct sk_buff *skb; -- cgit v1.1 From 9766cb709089fc460fda66c06a3da756af4a2930 Mon Sep 17 00:00:00 2001 From: Mathias Kresin Date: Fri, 26 Aug 2016 09:16:53 +0200 Subject: rt2x00: add support for mac addr from device tree On some devices the EEPROMs of Ralink Wi-Fi chips have a default Ralink MAC address set (RT3062F: 00:0C:43:30:62:00, RT3060F: 00:0C:43:30:60:00). Using multiple of these devices in the same network can cause nasty issues. Allow to override the MAC in the EEPROM with (a known good) one set in the device tree to bypass the issue. Signed-off-by: Mathias Kresin Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2400pci.c | 5 +---- drivers/net/wireless/ralink/rt2x00/rt2500pci.c | 5 +---- drivers/net/wireless/ralink/rt2x00/rt2500usb.c | 5 +---- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 5 +---- drivers/net/wireless/ralink/rt2x00/rt2x00.h | 1 + drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 17 +++++++++++++++++ drivers/net/wireless/ralink/rt2x00/rt61pci.c | 5 +---- drivers/net/wireless/ralink/rt2x00/rt73usb.c | 5 +---- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c index 155f343..085c5b4 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c @@ -1459,10 +1459,7 @@ static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } + rt2x00lib_set_mac_address(rt2x00dev, mac); rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c index 2553cdd..9832fd5 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c @@ -1585,10 +1585,7 @@ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } + rt2x00lib_set_mac_address(rt2x00dev, mac); rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c index 2d64611..cd3ab5a 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c @@ -1349,10 +1349,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } + rt2x00lib_set_mac_address(rt2x00dev, mac); rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index bf3f0a3..59c49af 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -6919,10 +6919,7 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) * Start validation of the data that has been read. */ mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } + rt2x00lib_set_mac_address(rt2x00dev, mac); rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word); if (word == 0xffff) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index f68d492..aa3d4cee 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -1403,6 +1403,7 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, */ u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif); +void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr); /* * Interrupt context handlers. diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 4e0c565..d659250 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "rt2x00.h" #include "rt2x00lib.h" @@ -931,6 +933,21 @@ static void rt2x00lib_rate(struct ieee80211_rate *entry, entry->flags |= IEEE80211_RATE_SHORT_PREAMBLE; } +void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr) +{ + const char *mac_addr; + + mac_addr = of_get_mac_address(rt2x00dev->dev->of_node); + if (mac_addr) + ether_addr_copy(eeprom_mac_addr, mac_addr); + + if (!is_valid_ether_addr(eeprom_mac_addr)) { + eth_random_addr(eeprom_mac_addr); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", eeprom_mac_addr); + } +} +EXPORT_SYMBOL_GPL(rt2x00lib_set_mac_address); + static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, struct hw_mode_spec *spec) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c index 03013eb..5306a3b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c @@ -2413,10 +2413,7 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } + rt2x00lib_set_mac_address(rt2x00dev, mac); rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c index c1397a6..1a29c4d 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c @@ -1766,10 +1766,7 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) * Start validation of the data that has been read. */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } + rt2x00lib_set_mac_address(rt2x00dev, mac); rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { -- cgit v1.1 From b073ac1fcf42376018f6db6acc885dfd2cc9ff02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 26 Sep 2016 23:51:44 +0200 Subject: brcmfmac: proto: add callback for queuing TX data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far our core code was calling brcmf_fws_process_skb which wasn't a proper thing to do. If case of devices using msgbuf protocol fwsignal shouldn't be used. It was an unnecessary extra layer simply calling a protocol specifix txdata function. Please note we already have txdata callback, but it's used for calls between bcdc and fwsignal so it couldn't be simply used there. This makes core code more generic (instead of bcdc/fwsignal specific). Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 12 ++++++++++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 8 +++++++- .../net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c | 15 +++++---------- .../net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 6 +++--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | 9 +++++++++ 7 files changed, 38 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 038a960..384b187 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -326,6 +326,17 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, return 0; } +static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx, + struct sk_buff *skb) +{ + struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx); + + if (!brcmf_fws_queue_skbs(drvr->fws)) + return brcmf_proto_txdata(drvr, ifidx, 0, skb); + + return brcmf_fws_process_skb(ifp, skb); +} + static int brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, struct sk_buff *pktbuf) @@ -375,6 +386,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull; drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; + drvr->proto->tx_queue_data = brcmf_proto_bcdc_tx_queue_data; drvr->proto->txdata = brcmf_proto_bcdc_txdata; drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode; drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 5eaac13..9e6f60a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -239,7 +239,13 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, if (eh->h_proto == htons(ETH_P_PAE)) atomic_inc(&ifp->pend_8021x_cnt); - ret = brcmf_fws_process_skb(ifp, skb); + /* determine the priority */ + if ((skb->priority == 0) || (skb->priority > 7)) + skb->priority = cfg80211_classify8021d(skb, NULL); + + ret = brcmf_proto_tx_queue_data(drvr, ifp->ifidx, skb); + if (ret < 0) + brcmf_txfinalize(ifp, skb, false); done: if (ret) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index a190f53..5f1a592 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -2100,16 +2100,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) int rc = 0; brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); - /* determine the priority */ - if ((skb->priority == 0) || (skb->priority > 7)) - skb->priority = cfg80211_classify8021d(skb, NULL); - - if (fws->avoid_queueing) { - rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); - if (rc < 0) - brcmf_txfinalize(ifp, skb, false); - return rc; - } /* set control buffer information */ skcb->if_flags = 0; @@ -2442,6 +2432,11 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr) kfree(fws); } +bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws) +{ + return !fws->avoid_queueing; +} + bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) { if (!fws->creditmap_received) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h index ef0ad85..96df660 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -20,6 +20,7 @@ int brcmf_fws_init(struct brcmf_pub *drvr); void brcmf_fws_deinit(struct brcmf_pub *drvr); +bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws); bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 2b9a2bc..7cc8851 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -782,8 +782,8 @@ static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid, } -static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, - u8 offset, struct sk_buff *skb) +static int brcmf_msgbuf_tx_queue_data(struct brcmf_pub *drvr, int ifidx, + struct sk_buff *skb) { struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; struct brcmf_flowring *flow = msgbuf->flow; @@ -1467,7 +1467,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) drvr->proto->hdrpull = brcmf_msgbuf_hdrpull; drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd; drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd; - drvr->proto->txdata = brcmf_msgbuf_txdata; + drvr->proto->tx_queue_data = brcmf_msgbuf_tx_queue_data; drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode; drvr->proto->delete_peer = brcmf_msgbuf_delete_peer; drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c index 26b68c3..d26ff21 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c @@ -51,7 +51,7 @@ int brcmf_proto_attach(struct brcmf_pub *drvr) drvr->bus_if->proto_type); goto fail; } - if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || + if (!proto->tx_queue_data || (proto->hdrpull == NULL) || (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || (proto->configure_addr_mode == NULL) || (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h index 57531f4..34b59fe 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h @@ -33,6 +33,8 @@ struct brcmf_proto { void *buf, uint len); int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); + int (*tx_queue_data)(struct brcmf_pub *drvr, int ifidx, + struct sk_buff *skb); int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, struct sk_buff *skb); void (*configure_addr_mode)(struct brcmf_pub *drvr, int ifidx, @@ -74,6 +76,13 @@ static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx, { return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); } + +static inline int brcmf_proto_tx_queue_data(struct brcmf_pub *drvr, int ifidx, + struct sk_buff *skb) +{ + return drvr->proto->tx_queue_data(drvr, ifidx, skb); +} + static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, struct sk_buff *skb) { -- cgit v1.1 From ec815dd2a5f110f627d7955e0027a3a008f68166 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 28 Sep 2016 18:18:21 +0530 Subject: mwifiex: prevent register accesses after host is sleeping Following is mwifiex driver-firmware host sleep handshake. It involves three threads. suspend handler, interrupt handler, interrupt processing in main work queue. 1) Enter suspend handler 2) Download HS_CFG command 3) Response from firmware for HS_CFG 4) Suspend thread waits until handshake completes(i.e hs_activate becomes true) 5) SLEEP from firmware 6) SLEEP confirm downloaded to firmware. 7) SLEEP confirm response from firmware 8) Driver processes SLEEP confirm response and set hs_activate to wake up suspend thread 9) Exit suspend handler 10) Read sleep cookie in loop and wait until it indicates firmware is sleep. 11) After processing SLEEP confirm response, we are at the end of interrupt processing routine. Recheck if there are interrupts received while we were processing them. During suspend-resume stress test, it's been observed that we may end up acessing PCIe hardware(in 10 and 11) when PCIe bus is closed which leads to a kernel crash. This patch solves the problem with below changes. a) action 10 above can be done before 8 b) Skip 11 if hs_activated is true. SLEEP confirm response is the last interrupt from firmware. No need to recheck for pending interrupts. c) Add flush_workqueue() in suspend handler. Signed-off-by: Amitkumar Karwar Reviewed-by: Brian Norris Tested-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 3c3c4f1..2833d47 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -118,6 +118,7 @@ static int mwifiex_pcie_suspend(struct device *dev) adapter = card->adapter; hs_actived = mwifiex_enable_hs(adapter); + flush_workqueue(adapter->workqueue); /* Indicate device suspended */ adapter->is_suspended = true; @@ -1669,9 +1670,6 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (!adapter->curr_cmd) { if (adapter->ps_state == PS_STATE_SLEEP_CFM) { - mwifiex_process_sleep_confirm_resp(adapter, skb->data, - skb->len); - mwifiex_pcie_enable_host_int(adapter); if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_SLEEP_CFM_DONE)) { @@ -1684,6 +1682,9 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) while (reg->sleep_cookie && (count++ < 10) && mwifiex_pcie_ok_to_access_hw(adapter)) usleep_range(50, 60); + mwifiex_pcie_enable_host_int(adapter); + mwifiex_process_sleep_confirm_resp(adapter, skb->data, + skb->len); } else { mwifiex_dbg(adapter, ERROR, "There is no command but got cmdrsp\n"); @@ -2322,6 +2323,8 @@ static int mwifiex_process_pcie_int(struct mwifiex_adapter *adapter) ret = mwifiex_pcie_process_cmd_complete(adapter); if (ret) return ret; + if (adapter->hs_activated) + return ret; } if (card->msi_enable) { -- cgit v1.1 From 5190f2e405919cd30ba2f12c58129fb2d71cd6b6 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 28 Sep 2016 18:18:22 +0530 Subject: mwifiex: report error to PCIe for suspend failure When host_sleep_config command fails, we should return an error to PCIe, instead of continuing (and possibly panicking, when we try to keep processing a timed-out ioctl after we return "successfully" from suspend). Signed-off-by: Amitkumar Karwar Reviewed-by: Brian Norris Tested-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 2833d47..063c707 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -101,7 +101,6 @@ static int mwifiex_pcie_suspend(struct device *dev) { struct mwifiex_adapter *adapter; struct pcie_service_card *card; - int hs_actived; struct pci_dev *pdev = to_pci_dev(dev); if (pdev) { @@ -117,7 +116,14 @@ static int mwifiex_pcie_suspend(struct device *dev) adapter = card->adapter; - hs_actived = mwifiex_enable_hs(adapter); + /* Enable the Host Sleep */ + if (!mwifiex_enable_hs(adapter)) { + mwifiex_dbg(adapter, ERROR, + "cmd: failed to suspend\n"); + adapter->hs_enabling = false; + return -EFAULT; + } + flush_workqueue(adapter->workqueue); /* Indicate device suspended */ -- cgit v1.1 From c44c040300d7afd79294710313a4989683e2afb1 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 28 Sep 2016 18:18:23 +0530 Subject: mwifiex: Fix NULL pointer dereference in skb_dequeue() At couple of places in cleanup path, we are just going through the skb queue and freeing them without unlinking. This leads to a crash when other thread tries to do skb_dequeue() and use already freed node. The problem is freed by unlinking skb before freeing it. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 4 +++- drivers/net/wireless/marvell/mwifiex/wmm.c | 12 +++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 39ce76a..e83861f 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3070,8 +3070,10 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) mwifiex_stop_net_dev_queue(priv->netdev, adapter); - skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) { + skb_unlink(skb, &priv->bypass_txq); mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + } if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index 0eb2465..dea2fe6 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -503,8 +503,10 @@ mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct sk_buff *skb, *tmp; - skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { + skb_unlink(skb, &ra_list->skb_head); mwifiex_write_data_complete(adapter, skb, 0, -1); + } } /* @@ -600,11 +602,15 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) priv->adapter->if_ops.clean_pcie_ring(priv->adapter); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { + skb_unlink(skb, &priv->tdls_txq); mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + } - skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) { + skb_unlink(skb, &priv->bypass_txq); mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + } atomic_set(&priv->adapter->bypass_tx_pending, 0); idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL); -- cgit v1.1 From 113630b581d6d423998d2113a8e892ed6e6af6f9 Mon Sep 17 00:00:00 2001 From: Karthik D A Date: Wed, 28 Sep 2016 18:18:24 +0530 Subject: mwifiex: vendor_ie length check for parse WMM IEs While copying the vendor_ie obtained from the cfg80211_find_vendor_ie() to the struct mwifiex_types_wmm_info, length/size was inappropriate. This patch corrects the required length needed to the mwifiex_types_wmm_info Signed-off-by: Karthik D A Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/uap_cmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c index a7e9f54..35d8636 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -404,7 +404,7 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv, struct cfg80211_ap_settings *params) { const u8 *vendor_ie; - struct ieee_types_header *wmm_ie; + const u8 *wmm_ie; u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, @@ -412,9 +412,9 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv, params->beacon.tail, params->beacon.tail_len); if (vendor_ie) { - wmm_ie = (struct ieee_types_header *)vendor_ie; - memcpy(&bss_cfg->wmm_info, wmm_ie + 1, - sizeof(bss_cfg->wmm_info)); + wmm_ie = vendor_ie; + memcpy(&bss_cfg->wmm_info, wmm_ie + + sizeof(struct ieee_types_header), *(wmm_ie + 1)); priv->wmm_enabled = 1; } else { memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info)); -- cgit v1.1 From a936ea543657d9764947eada7fa42bb570425118 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 28 Sep 2016 18:18:25 +0530 Subject: mwifiex: add memrw command information in README Support for this debugfs command is available in driver. This patch adds usage information in README file. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/README | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/README b/drivers/net/wireless/marvell/mwifiex/README index 24e649b..588fcbe 100644 --- a/drivers/net/wireless/marvell/mwifiex/README +++ b/drivers/net/wireless/marvell/mwifiex/README @@ -180,6 +180,29 @@ regrdwr echo "1 0xa060 0x12" > regrdwr : Write the MAC register echo "1 0xa794 0x80000000" > regrdwr : Write 0x80000000 to MAC register + +memrw + This command is used to read/write the firmware memory. + + Usage: + 1) For reading firmware memory location. + echo r
0 > /sys/kernel/debug/mwifiex/mlan0/memrw + cat /sys/kernel/debug/mwifiex/mlan0/memrw + 2) For writing value to firmware memory location. + echo w
[value] > /sys/kernel/debug/mwifiex/mlan0/memrw + + where the parameters are, +
: memory address + [value]: value to be written + + Examples: + echo r 0x4cf70 0 > /sys/kernel/debug/mwifiex/mlan0/memrw + cat /sys/kernel/debug/mwifiex/mlan0/memrw + : Read memory address 0x4cf70 + iwpriv mlan0 memrdwr -0x7fff6000 -0x40000000 + echo w 0x8000a000 0xc0000000 > /sys/kernel/debug/mwifiex/mlan0/memrw + : Write 0xc0000000 to memory address 0x8000a000 + rdeeprom This command is used to read the EEPROM contents of the card. -- cgit v1.1 From 77f486c8bb39546fe7e574b6ec371e83fe772ee5 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Wed, 28 Sep 2016 18:18:26 +0530 Subject: mwifiex: update tx_pkts_queued for requeued packets wmm.tx_pkts_queued and ralist's total_pkt_count should be updated in synchronization. They were not correctly updated in mwifiex_send_processed_packet(). Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/wmm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index dea2fe6..1ff3a87 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -1334,9 +1334,11 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, skb = skb_dequeue(&ptr->skb_head); if (adapter->data_sent || adapter->tx_lock_flag) { + ptr->total_pkt_count--; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); skb_queue_tail(&adapter->tx_data_q, skb); + atomic_dec(&priv->wmm.tx_pkts_queued); atomic_inc(&adapter->tx_queued); return; } @@ -1394,6 +1396,10 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, if (ret != -EBUSY) { mwifiex_rotate_priolists(priv, ptr, ptr_index); atomic_dec(&priv->wmm.tx_pkts_queued); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + ptr->total_pkt_count--; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); } } -- cgit v1.1 From 49abe5c83cd066f34f308d8c9e8abff7c2fb5d64 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Wed, 28 Sep 2016 18:18:27 +0530 Subject: mwifiex: fix command timeout problem seen in stress tests It is observed that if single tid 6 packet comes among with massive tid 0 packets, tid 6 packet may stay in it's queue and will never be transmited. This is because wmm.highest_queued_prio will be set to 2 during transmission of tid 0 packets As a result, main work thread keeps on looping without serving that packet. In this case, if command has downloaded to firmware, driver doesn't process it's response causing command timeout. This patch will reset highest_queued_prio if packets exist in data queue, and try to find a ra_list for current private. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.h | 1 + drivers/net/wireless/marvell/mwifiex/wmm.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 26df28f..d61fe3a 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -315,6 +315,7 @@ struct mwifiex_tid_tbl { #define WMM_HIGHEST_PRIORITY 7 #define HIGH_PRIO_TID 7 #define LOW_PRIO_TID 0 +#define NO_PKT_PRIO_TID -1 #define MWIFIEX_WMM_DRV_DELAY_MAX 510 struct mwifiex_wmm_desc { diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index 1ff3a87..28c2f6f 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -1105,6 +1105,7 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, &adapter->bss_prio_tbl[j].bss_prio_head, list) { +try_again: priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv; if (((priv_tmp->bss_mode != NL80211_IFTYPE_ADHOC) && @@ -1140,8 +1141,18 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, ra_list_spinlock, flags_ra); } - } + if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) != 0) { + atomic_set(&priv_tmp->wmm.highest_queued_prio, + HIGH_PRIO_TID); + /* Iterate current private once more, since + * there still exist packets in data queue + */ + goto try_again; + } else + atomic_set(&priv_tmp->wmm.highest_queued_prio, + NO_PKT_PRIO_TID); + } } return NULL; -- cgit v1.1 From 3d8bd85c2c9e47ed2c82348aa5b6029ed48376ae Mon Sep 17 00:00:00 2001 From: Karthik D A Date: Wed, 28 Sep 2016 18:18:28 +0530 Subject: mwifiex: fix p2p device doesn't find in scan problem Marvell p2p device disappears from the list of p2p peers on the other p2p device after disconnection. It happens due to a bug in driver. When interface is changed from p2p to station, certain variables(bss_type, bss_role etc.) aren't correctly updated. This patch corrects them to fix the issue. Signed-off-by: Karthik D A Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index e83861f..d9f5445 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -1203,6 +1203,12 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, priv->adapter->curr_iface_comb.p2p_intf--; priv->adapter->curr_iface_comb.sta_intf++; dev->ieee80211_ptr->iftype = type; + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; break; case NL80211_IFTYPE_ADHOC: if (mwifiex_cfg80211_deinit_p2p(priv)) -- cgit v1.1 From 424342ff0e033f5965a446fc6e37aba2c9cee248 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 29 Sep 2016 14:57:56 +0000 Subject: mwifiex: fix missing destroy_workqueue() on error in mwifiex_add_virtual_intf() Add the missing destroy_workqueue() before return from mwifiex_add_virtual_intf() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index d9f5445..c26469a 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3022,6 +3022,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->netdev = NULL; memset(&priv->wdev, 0, sizeof(priv->wdev)); priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + destroy_workqueue(priv->dfs_cac_workqueue); + priv->dfs_cac_workqueue = NULL; return ERR_PTR(-ENOMEM); } -- cgit v1.1 From 9afdd6128c39f42398041bb2e017d8df0dcebcd1 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 30 Sep 2016 14:39:17 +0200 Subject: cw1200: Don't leak memory if krealloc failes The call to krealloc() in wsm_buf_reserve() directly assigns the newly returned memory to buf->begin. This is all fine except when krealloc() failes we loose the ability to free the old memory pointed to by buf->begin. If we just create a temporary variable to assign memory to and assign the memory to it we can mitigate the memory leak. Signed-off-by: Johannes Thumshirn Cc: Johannes Berg Signed-off-by: Kalle Valo --- drivers/net/wireless/st/cw1200/wsm.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/st/cw1200/wsm.c b/drivers/net/wireless/st/cw1200/wsm.c index 680d60e..ffb245e 100644 --- a/drivers/net/wireless/st/cw1200/wsm.c +++ b/drivers/net/wireless/st/cw1200/wsm.c @@ -1807,16 +1807,18 @@ static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) { size_t pos = buf->data - buf->begin; size_t size = pos + extra_size; + u8 *tmp; size = round_up(size, FWLOAD_BLOCK_SIZE); - buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); - if (buf->begin) { - buf->data = &buf->begin[pos]; - buf->end = &buf->begin[size]; - return 0; - } else { - buf->end = buf->data = buf->begin; + tmp = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); + if (!tmp) { + wsm_buf_deinit(buf); return -ENOMEM; } + + buf->begin = tmp; + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; } -- cgit v1.1 From 1ac507d4ffbff578c388e85f9637dca19ff07b95 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Fri, 4 Nov 2016 10:04:11 -0700 Subject: RDS: TCP: report addr/port info based on TCP socket in rds-info The socket argument passed to rds_tcp_tc_info() is a PF_RDS socket, so it is incorrect to report the address port info based on rds_getname() as part of TCP state report. Invoke inet_getname() for the t_sock associated with the rds_tcp_connection instead. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/tcp.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/net/rds/tcp.c b/net/rds/tcp.c index fcddacc..3296a6a 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -220,7 +220,7 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp) write_unlock_bh(&sock->sk->sk_callback_lock); } -static void rds_tcp_tc_info(struct socket *sock, unsigned int len, +static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len, struct rds_info_iterator *iter, struct rds_info_lengths *lens) { @@ -229,6 +229,7 @@ static void rds_tcp_tc_info(struct socket *sock, unsigned int len, unsigned long flags; struct sockaddr_in sin; int sinlen; + struct socket *sock; spin_lock_irqsave(&rds_tcp_tc_list_lock, flags); @@ -237,12 +238,17 @@ static void rds_tcp_tc_info(struct socket *sock, unsigned int len, list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) { - sock->ops->getname(sock, (struct sockaddr *)&sin, &sinlen, 0); - tsinfo.local_addr = sin.sin_addr.s_addr; - tsinfo.local_port = sin.sin_port; - sock->ops->getname(sock, (struct sockaddr *)&sin, &sinlen, 1); - tsinfo.peer_addr = sin.sin_addr.s_addr; - tsinfo.peer_port = sin.sin_port; + sock = tc->t_sock; + if (sock) { + sock->ops->getname(sock, (struct sockaddr *)&sin, + &sinlen, 0); + tsinfo.local_addr = sin.sin_addr.s_addr; + tsinfo.local_port = sin.sin_port; + sock->ops->getname(sock, (struct sockaddr *)&sin, + &sinlen, 1); + tsinfo.peer_addr = sin.sin_addr.s_addr; + tsinfo.peer_port = sin.sin_port; + } tsinfo.hdr_rem = tc->t_tinc_hdr_rem; tsinfo.data_rem = tc->t_tinc_data_rem; -- cgit v1.1 From 117d15bbfde643b5bd0a173fddaa0e6c65f14d47 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Fri, 4 Nov 2016 10:04:12 -0700 Subject: RDS: TCP: start multipath acceptor loop at 0 The for() loop in rds_tcp_accept_one() assumes that the 0'th rds_tcp_conn_path is UP and starts multipath accepts at index 1. But this assumption may not always be true: if the 0'th path has failed (ERROR or DOWN state) an incoming connection request should be used to resurrect this path. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/tcp_listen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index e0b23fb..c9c4968 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -103,7 +103,7 @@ struct rds_tcp_connection *rds_tcp_accept_one_path(struct rds_connection *conn) if (!peer_is_smaller) return NULL; - for (i = 1; i < npaths; i++) { + for (i = 0; i < npaths; i++) { struct rds_conn_path *cp = &conn->c_path[i]; if (rds_conn_path_transition(cp, RDS_CONN_DOWN, -- cgit v1.1 From 9a4c803748250f34213e50f42db832c6b7aa05ea Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 4 Nov 2016 16:51:22 +0100 Subject: net: mdio-mux-mmioreg: Add support for 16bit and 32bit register sizes In order to support PHY switching on Amlogic GXL SoCs, add support for 16bit and 32bit registers sizes. Reviewed-by: Andrew Lunn Signed-off-by: Neil Armstrong Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- .../devicetree/bindings/net/mdio-mux-mmioreg.txt | 4 +- drivers/net/phy/mdio-mux-mmioreg.c | 60 +++++++++++++++++----- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt b/Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt index 8516929..065e8bd 100644 --- a/Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt +++ b/Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt @@ -3,7 +3,7 @@ Properties for an MDIO bus multiplexer controlled by a memory-mapped device This is a special case of a MDIO bus multiplexer. A memory-mapped device, like an FPGA, is used to control which child bus is connected. The mdio-mux node must be a child of the memory-mapped device. The driver currently only -supports devices with eight-bit registers. +supports devices with 8, 16 or 32-bit registers. Required properties in addition to the generic multiplexer properties: @@ -11,7 +11,7 @@ Required properties in addition to the generic multiplexer properties: - reg : integer, contains the offset of the register that controls the bus multiplexer. The size field in the 'reg' property is the size of - register, and must therefore be 1. + register, and must therefore be 1, 2, or 4. - mux-mask : integer, contains an eight-bit mask that specifies which bits in the register control the actual bus multiplexer. The diff --git a/drivers/net/phy/mdio-mux-mmioreg.c b/drivers/net/phy/mdio-mux-mmioreg.c index d0bed52..6a33646 100644 --- a/drivers/net/phy/mdio-mux-mmioreg.c +++ b/drivers/net/phy/mdio-mux-mmioreg.c @@ -21,7 +21,8 @@ struct mdio_mux_mmioreg_state { void *mux_handle; phys_addr_t phys; - uint8_t mask; + unsigned int iosize; + unsigned int mask; }; /* @@ -47,17 +48,47 @@ static int mdio_mux_mmioreg_switch_fn(int current_child, int desired_child, struct mdio_mux_mmioreg_state *s = data; if (current_child ^ desired_child) { - void __iomem *p = ioremap(s->phys, 1); - uint8_t x, y; - + void __iomem *p = ioremap(s->phys, s->iosize); if (!p) return -ENOMEM; - x = ioread8(p); - y = (x & ~s->mask) | desired_child; - if (x != y) { - iowrite8((x & ~s->mask) | desired_child, p); - pr_debug("%s: %02x -> %02x\n", __func__, x, y); + switch (s->iosize) { + case sizeof(uint8_t): { + uint8_t x, y; + + x = ioread8(p); + y = (x & ~s->mask) | desired_child; + if (x != y) { + iowrite8((x & ~s->mask) | desired_child, p); + pr_debug("%s: %02x -> %02x\n", __func__, x, y); + } + + break; + } + case sizeof(uint16_t): { + uint16_t x, y; + + x = ioread16(p); + y = (x & ~s->mask) | desired_child; + if (x != y) { + iowrite16((x & ~s->mask) | desired_child, p); + pr_debug("%s: %04x -> %04x\n", __func__, x, y); + } + + break; + } + case sizeof(uint32_t): { + uint32_t x, y; + + x = ioread32(p); + y = (x & ~s->mask) | desired_child; + if (x != y) { + iowrite32((x & ~s->mask) | desired_child, p); + pr_debug("%s: %08x -> %08x\n", __func__, x, y); + } + + break; + } } iounmap(p); @@ -88,8 +119,11 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev) } s->phys = res.start; - if (resource_size(&res) != sizeof(uint8_t)) { - dev_err(&pdev->dev, "only 8-bit registers are supported\n"); + s->iosize = resource_size(&res); + if (s->iosize != sizeof(uint8_t) && + s->iosize != sizeof(uint16_t) && + s->iosize != sizeof(uint32_t)) { + dev_err(&pdev->dev, "only 8/16/32-bit registers are supported\n"); return -EINVAL; } @@ -98,8 +132,8 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev) dev_err(&pdev->dev, "missing or invalid mux-mask property\n"); return -ENODEV; } - if (be32_to_cpup(iprop) > 255) { - dev_err(&pdev->dev, "only 8-bit registers are supported\n"); + if (be32_to_cpup(iprop) >= BIT(s->iosize * 8)) { + dev_err(&pdev->dev, "only 8/16/32-bit registers are supported\n"); return -EINVAL; } s->mask = be32_to_cpup(iprop); -- cgit v1.1 From 7334b3e47aeed5a84ccd6bbc3431a5f69651e107 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 4 Nov 2016 16:51:23 +0100 Subject: net: phy: Add Meson GXL Internal PHY driver Add driver for the Internal RMII PHY found in the Amlogic Meson GXL SoCs. This PHY seems to only implement some standard registers and need some workarounds to provide autoneg values from vendor registers. Some magic values are currently used to configure the PHY, and this a temporary setup until clarification about these registers names and registers fields are provided by Amlogic. Signed-off-by: Neil Armstrong Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 5 +++ drivers/net/phy/Makefile | 1 + drivers/net/phy/meson-gxl.c | 81 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 drivers/net/phy/meson-gxl.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index d3fcfd2..d361835 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -277,6 +277,11 @@ config MARVELL_PHY ---help--- Currently has a driver for the 88E1011S +config MESON_GXL_PHY + tristate "Amlogic Meson GXL Internal PHY" + ---help--- + Currently has a driver for the Amlogic Meson GXL Internal PHY + config MICREL_PHY tristate "Micrel PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 86d12cd..356859a 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MARVELL_PHY) += marvell.o +obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MICROCHIP_PHY) += microchip.o diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c new file mode 100644 index 0000000..1ea69b7 --- /dev/null +++ b/drivers/net/phy/meson-gxl.c @@ -0,0 +1,81 @@ +/* + * Amlogic Meson GXL Internal PHY Driver + * + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2016 BayLibre, SAS. All rights reserved. + * Author: Neil Armstrong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include +#include +#include +#include +#include +#include + +static int meson_gxl_config_init(struct phy_device *phydev) +{ + /* Enable Analog and DSP register Bank access by */ + phy_write(phydev, 0x14, 0x0000); + phy_write(phydev, 0x14, 0x0400); + phy_write(phydev, 0x14, 0x0000); + phy_write(phydev, 0x14, 0x0400); + + /* Write Analog register 23 */ + phy_write(phydev, 0x17, 0x8E0D); + phy_write(phydev, 0x14, 0x4417); + + /* Enable fractional PLL */ + phy_write(phydev, 0x17, 0x0005); + phy_write(phydev, 0x14, 0x5C1B); + + /* Program fraction FR_PLL_DIV1 */ + phy_write(phydev, 0x17, 0x029A); + phy_write(phydev, 0x14, 0x5C1D); + + /* Program fraction FR_PLL_DIV1 */ + phy_write(phydev, 0x17, 0xAAAA); + phy_write(phydev, 0x14, 0x5C1C); + + return 0; +} + +static struct phy_driver meson_gxl_phy[] = { + { + .phy_id = 0x01814400, + .phy_id_mask = 0xfffffff0, + .name = "Meson GXL Internal PHY", + .features = PHY_BASIC_FEATURES, + .flags = PHY_IS_INTERNAL, + .config_init = meson_gxl_config_init, + .config_aneg = genphy_config_aneg, + .aneg_done = genphy_aneg_done, + .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, +}; + +static struct mdio_device_id __maybe_unused meson_gxl_tbl[] = { + { 0x01814400, 0xfffffff0 }, + { } +}; + +module_phy_driver(meson_gxl_phy); + +MODULE_DEVICE_TABLE(mdio, meson_gxl_tbl); + +MODULE_DESCRIPTION("Amlogic Meson GXL Internal PHY driver"); +MODULE_AUTHOR("Baoqi wang"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_LICENSE("GPL"); -- cgit v1.1 From ccbf3bfaee0e4f1ddf8103884fd4bf9f35f31f08 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 4 Nov 2016 11:54:31 -0700 Subject: tcp: shortcut listeners in tcp_get_info() Being lockless in tcp_get_info() is hard, because we need to add specific synchronization in TCP fast path, like seqcount. Following patch will change inet_diag_dump_icsk() to no longer hold any lock for non listeners, so that we can properly acquire socket lock in get_tcp_info() and let it return more consistent counters. Signed-off-by: Eric Dumazet Signed-off-by: Yuchung Cheng Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3251fe7..117982b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2721,6 +2721,27 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_state = sk_state_load(sk); + /* Report meaningful fields for all TCP states, including listeners */ + rate = READ_ONCE(sk->sk_pacing_rate); + rate64 = rate != ~0U ? rate : ~0ULL; + put_unaligned(rate64, &info->tcpi_pacing_rate); + + rate = READ_ONCE(sk->sk_max_pacing_rate); + rate64 = rate != ~0U ? rate : ~0ULL; + put_unaligned(rate64, &info->tcpi_max_pacing_rate); + + info->tcpi_reordering = tp->reordering; + info->tcpi_snd_cwnd = tp->snd_cwnd; + + if (info->tcpi_state == TCP_LISTEN) { + /* listeners aliased fields : + * tcpi_unacked -> Number of children ready for accept() + * tcpi_sacked -> max backlog + */ + info->tcpi_unacked = sk->sk_ack_backlog; + info->tcpi_sacked = sk->sk_max_ack_backlog; + return; + } info->tcpi_ca_state = icsk->icsk_ca_state; info->tcpi_retransmits = icsk->icsk_retransmits; info->tcpi_probes = icsk->icsk_probes_out; @@ -2748,13 +2769,9 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_snd_mss = tp->mss_cache; info->tcpi_rcv_mss = icsk->icsk_ack.rcv_mss; - if (info->tcpi_state == TCP_LISTEN) { - info->tcpi_unacked = sk->sk_ack_backlog; - info->tcpi_sacked = sk->sk_max_ack_backlog; - } else { - info->tcpi_unacked = tp->packets_out; - info->tcpi_sacked = tp->sacked_out; - } + info->tcpi_unacked = tp->packets_out; + info->tcpi_sacked = tp->sacked_out; + info->tcpi_lost = tp->lost_out; info->tcpi_retrans = tp->retrans_out; info->tcpi_fackets = tp->fackets_out; @@ -2768,23 +2785,13 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_rtt = tp->srtt_us >> 3; info->tcpi_rttvar = tp->mdev_us >> 2; info->tcpi_snd_ssthresh = tp->snd_ssthresh; - info->tcpi_snd_cwnd = tp->snd_cwnd; info->tcpi_advmss = tp->advmss; - info->tcpi_reordering = tp->reordering; info->tcpi_rcv_rtt = jiffies_to_usecs(tp->rcv_rtt_est.rtt)>>3; info->tcpi_rcv_space = tp->rcvq_space.space; info->tcpi_total_retrans = tp->total_retrans; - rate = READ_ONCE(sk->sk_pacing_rate); - rate64 = rate != ~0U ? rate : ~0ULL; - put_unaligned(rate64, &info->tcpi_pacing_rate); - - rate = READ_ONCE(sk->sk_max_pacing_rate); - rate64 = rate != ~0U ? rate : ~0ULL; - put_unaligned(rate64, &info->tcpi_max_pacing_rate); - do { start = u64_stats_fetch_begin_irq(&tp->syncp); put_unaligned(tp->bytes_acked, &info->tcpi_bytes_acked); -- cgit v1.1 From 67db3e4bfbc90657c7be840aad5585be46240d6f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 4 Nov 2016 11:54:32 -0700 Subject: tcp: no longer hold ehash lock while calling tcp_get_info() We had various problems in the past in tcp_get_info() and used specific synchronization to avoid deadlocks. We would like to add more instrumentation points for TCP, and avoiding grabing socket lock in tcp_getinfo() was too costly. Being able to lock the socket allows to provide consistent set of fields. inet_diag_dump_icsk() can make sure ehash locks are not held any more when tcp_get_info() is called. We can remove syncp added in commit d654976cbf85 ("tcp: fix a potential deadlock in tcp_get_info()"), but we need to use lock_sock_fast() instead of spin_lock_bh() since TCP input path can now be run from process context. Signed-off-by: Eric Dumazet Signed-off-by: Yuchung Cheng Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 2 -- net/ipv4/inet_diag.c | 48 +++++++++++++++++++++++++++++++++--------------- net/ipv4/tcp.c | 20 +++++++++----------- net/ipv4/tcp_input.c | 4 ---- 4 files changed, 42 insertions(+), 32 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index a17ae7b..32a7c7e 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -176,8 +176,6 @@ struct tcp_sock { * sum(delta(snd_una)), or how many bytes * were acked. */ - struct u64_stats_sync syncp; /* protects 64bit vars (cf tcp_get_info()) */ - u32 snd_una; /* First byte we want an ack for */ u32 snd_sml; /* Last byte of the most recently transmitted small packet */ u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */ diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 3b34024..4dea33e 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -861,10 +861,11 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, struct netlink_callback *cb, const struct inet_diag_req_v2 *r, struct nlattr *bc) { + bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); struct net *net = sock_net(skb->sk); - int i, num, s_i, s_num; u32 idiag_states = r->idiag_states; - bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); + int i, num, s_i, s_num; + struct sock *sk; if (idiag_states & TCPF_SYN_RECV) idiag_states |= TCPF_NEW_SYN_RECV; @@ -877,7 +878,6 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, for (i = s_i; i < INET_LHTABLE_SIZE; i++) { struct inet_listen_hashbucket *ilb; - struct sock *sk; num = 0; ilb = &hashinfo->listening_hash[i]; @@ -922,13 +922,14 @@ skip_listen_ht: if (!(idiag_states & ~TCPF_LISTEN)) goto out; +#define SKARR_SZ 16 for (i = s_i; i <= hashinfo->ehash_mask; i++) { struct inet_ehash_bucket *head = &hashinfo->ehash[i]; spinlock_t *lock = inet_ehash_lockp(hashinfo, i); struct hlist_nulls_node *node; - struct sock *sk; - - num = 0; + struct sock *sk_arr[SKARR_SZ]; + int num_arr[SKARR_SZ]; + int idx, accum, res; if (hlist_nulls_empty(&head->chain)) continue; @@ -936,9 +937,12 @@ skip_listen_ht: if (i > s_i) s_num = 0; +next_chunk: + num = 0; + accum = 0; spin_lock_bh(lock); sk_nulls_for_each(sk, node, &head->chain) { - int state, res; + int state; if (!net_eq(sock_net(sk), net)) continue; @@ -962,21 +966,35 @@ skip_listen_ht: if (!inet_diag_bc_sk(bc, sk)) goto next_normal; - res = sk_diag_fill(sk, skb, r, + sock_hold(sk); + num_arr[accum] = num; + sk_arr[accum] = sk; + if (++accum == SKARR_SZ) + break; +next_normal: + ++num; + } + spin_unlock_bh(lock); + res = 0; + for (idx = 0; idx < accum; idx++) { + if (res >= 0) { + res = sk_diag_fill(sk_arr[idx], skb, r, sk_user_ns(NETLINK_CB(cb->skb).sk), NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh, net_admin); - if (res < 0) { - spin_unlock_bh(lock); - goto done; + if (res < 0) + num = num_arr[idx]; } -next_normal: - ++num; + sock_gen_put(sk_arr[idx]); } - - spin_unlock_bh(lock); + if (res < 0) + break; cond_resched(); + if (accum == SKARR_SZ) { + s_num = num + 1; + goto next_chunk; + } } done: diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 117982b..a7d54cb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -405,7 +405,6 @@ void tcp_init_sock(struct sock *sk) tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tp->snd_cwnd_clamp = ~0; tp->mss_cache = TCP_MSS_DEFAULT; - u64_stats_init(&tp->syncp); tp->reordering = sock_net(sk)->ipv4.sysctl_tcp_reordering; tcp_enable_early_retrans(tp); @@ -2710,9 +2709,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) const struct tcp_sock *tp = tcp_sk(sk); /* iff sk_type == SOCK_STREAM */ const struct inet_connection_sock *icsk = inet_csk(sk); u32 now = tcp_time_stamp, intv; - unsigned int start; - int notsent_bytes; u64 rate64; + bool slow; u32 rate; memset(info, 0, sizeof(*info)); @@ -2792,17 +2790,17 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_total_retrans = tp->total_retrans; - do { - start = u64_stats_fetch_begin_irq(&tp->syncp); - put_unaligned(tp->bytes_acked, &info->tcpi_bytes_acked); - put_unaligned(tp->bytes_received, &info->tcpi_bytes_received); - } while (u64_stats_fetch_retry_irq(&tp->syncp, start)); + slow = lock_sock_fast(sk); + + put_unaligned(tp->bytes_acked, &info->tcpi_bytes_acked); + put_unaligned(tp->bytes_received, &info->tcpi_bytes_received); + info->tcpi_notsent_bytes = max_t(int, 0, tp->write_seq - tp->snd_nxt); + + unlock_sock_fast(sk, slow); + info->tcpi_segs_out = tp->segs_out; info->tcpi_segs_in = tp->segs_in; - notsent_bytes = READ_ONCE(tp->write_seq) - READ_ONCE(tp->snd_nxt); - info->tcpi_notsent_bytes = max(0, notsent_bytes); - info->tcpi_min_rtt = tcp_min_rtt(tp); info->tcpi_data_segs_in = tp->data_segs_in; info->tcpi_data_segs_out = tp->data_segs_out; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index f2c59c8..a70046f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3351,9 +3351,7 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) u32 delta = ack - tp->snd_una; sock_owned_by_me((struct sock *)tp); - u64_stats_update_begin_raw(&tp->syncp); tp->bytes_acked += delta; - u64_stats_update_end_raw(&tp->syncp); tp->snd_una = ack; } @@ -3363,9 +3361,7 @@ static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq) u32 delta = seq - tp->rcv_nxt; sock_owned_by_me((struct sock *)tp); - u64_stats_update_begin_raw(&tp->syncp); tp->bytes_received += delta; - u64_stats_update_end_raw(&tp->syncp); tp->rcv_nxt = seq; } -- cgit v1.1 From 5619468a4136d595df880dc887eee0c2fa48e8aa Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 5 Nov 2016 14:04:49 +0100 Subject: stmmac: dwmac-rk: turn resume/suspend into standard PM callbacks Use standard PM resume/suspend callbacks instead of the hooks in stmmac_platform. This gives the driver more control and flexibility when implementing PM functionality. The hooks in stmmac_platform also doesn't buy us anything extra. Signed-off-by: Joachim Eastwood Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 59 ++++++++++++++------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 3740a44..d91acd5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -915,30 +915,6 @@ static void rk_gmac_exit(struct platform_device *pdev, void *priv) rk_gmac_powerdown(bsp_priv); } -static void rk_gmac_suspend(struct platform_device *pdev, void *priv) -{ - struct rk_priv_data *bsp_priv = priv; - - /* Keep the PHY up if we use Wake-on-Lan. */ - if (device_may_wakeup(&pdev->dev)) - return; - - rk_gmac_powerdown(bsp_priv); - bsp_priv->suspended = true; -} - -static void rk_gmac_resume(struct platform_device *pdev, void *priv) -{ - struct rk_priv_data *bsp_priv = priv; - - /* The PHY was up for Wake-on-Lan. */ - if (!bsp_priv->suspended) - return; - - rk_gmac_powerup(bsp_priv); - bsp_priv->suspended = false; -} - static void rk_fix_speed(void *priv, unsigned int speed) { struct rk_priv_data *bsp_priv = priv; @@ -977,8 +953,6 @@ static int rk_gmac_probe(struct platform_device *pdev) plat_dat->init = rk_gmac_init; plat_dat->exit = rk_gmac_exit; plat_dat->fix_mac_speed = rk_fix_speed; - plat_dat->suspend = rk_gmac_suspend; - plat_dat->resume = rk_gmac_resume; plat_dat->bsp_priv = rk_gmac_setup(pdev, data); if (IS_ERR(plat_dat->bsp_priv)) @@ -991,6 +965,37 @@ static int rk_gmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); } +#ifdef CONFIG_PM_SLEEP +static int rk_gmac_suspend(struct device *dev) +{ + struct rk_priv_data *bsp_priv = get_stmmac_bsp_priv(dev); + int ret = stmmac_suspend(dev); + + /* Keep the PHY up if we use Wake-on-Lan. */ + if (!device_may_wakeup(dev)) { + rk_gmac_powerdown(bsp_priv); + bsp_priv->suspended = true; + } + + return ret; +} + +static int rk_gmac_resume(struct device *dev) +{ + struct rk_priv_data *bsp_priv = get_stmmac_bsp_priv(dev); + + /* The PHY was up for Wake-on-Lan. */ + if (bsp_priv->suspended) { + rk_gmac_powerup(bsp_priv); + bsp_priv->suspended = false; + } + + return stmmac_resume(dev); +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(rk_gmac_pm_ops, rk_gmac_suspend, rk_gmac_resume); + static const struct of_device_id rk_gmac_dwmac_match[] = { { .compatible = "rockchip,rk3228-gmac", .data = &rk3228_ops }, { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops }, @@ -1006,7 +1011,7 @@ static struct platform_driver rk_gmac_dwmac_driver = { .remove = stmmac_pltfr_remove, .driver = { .name = "rk_gmac-dwmac", - .pm = &stmmac_pltfr_pm_ops, + .pm = &rk_gmac_pm_ops, .of_match_table = rk_gmac_dwmac_match, }, }; -- cgit v1.1 From 0de8c4c9a911a02ef968f7a2f4e8f8ffb7de58ca Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 5 Nov 2016 14:04:50 +0100 Subject: stmmac: dwmac-rk: turn exit into standard driver remove callback Convert the exit hook into a standard driver remove function as the hook doesn't really buy us anything extra. Eventually the exit hook will be deprecated in favor of the driver remove function. Signed-off-by: Joachim Eastwood Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index d91acd5..8506881 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -908,13 +908,6 @@ static int rk_gmac_init(struct platform_device *pdev, void *priv) return rk_gmac_powerup(bsp_priv); } -static void rk_gmac_exit(struct platform_device *pdev, void *priv) -{ - struct rk_priv_data *bsp_priv = priv; - - rk_gmac_powerdown(bsp_priv); -} - static void rk_fix_speed(void *priv, unsigned int speed) { struct rk_priv_data *bsp_priv = priv; @@ -951,7 +944,6 @@ static int rk_gmac_probe(struct platform_device *pdev) plat_dat->has_gmac = true; plat_dat->init = rk_gmac_init; - plat_dat->exit = rk_gmac_exit; plat_dat->fix_mac_speed = rk_fix_speed; plat_dat->bsp_priv = rk_gmac_setup(pdev, data); @@ -965,6 +957,16 @@ static int rk_gmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); } +static int rk_gmac_remove(struct platform_device *pdev) +{ + struct rk_priv_data *bsp_priv = get_stmmac_bsp_priv(&pdev->dev); + int ret = stmmac_dvr_remove(&pdev->dev); + + rk_gmac_powerdown(bsp_priv); + + return ret; +} + #ifdef CONFIG_PM_SLEEP static int rk_gmac_suspend(struct device *dev) { @@ -1008,7 +1010,7 @@ MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); static struct platform_driver rk_gmac_dwmac_driver = { .probe = rk_gmac_probe, - .remove = stmmac_pltfr_remove, + .remove = rk_gmac_remove, .driver = { .name = "rk_gmac-dwmac", .pm = &rk_gmac_pm_ops, -- cgit v1.1 From 07a5e76924a1a4549bc176f49ec9e37ca7315b43 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 5 Nov 2016 14:04:51 +0100 Subject: stmmac: dwmac-rk: absorb rk_gmac_init into probe Since the rk_gmac_init() only calls another function move this function call into probe so rk_gmac_init() can be removed. Since commit cecbc5563a02 ("stmmac: allow to split suspend/resume from init/exit callbacks") the init hook is no longer used in dwmac-rk so this can be removed. Signed-off-by: Joachim Eastwood Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 8506881..6b787d7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -901,13 +901,6 @@ static void rk_gmac_powerdown(struct rk_priv_data *gmac) gmac_clk_enable(gmac, false); } -static int rk_gmac_init(struct platform_device *pdev, void *priv) -{ - struct rk_priv_data *bsp_priv = priv; - - return rk_gmac_powerup(bsp_priv); -} - static void rk_fix_speed(void *priv, unsigned int speed) { struct rk_priv_data *bsp_priv = priv; @@ -943,14 +936,13 @@ static int rk_gmac_probe(struct platform_device *pdev) return PTR_ERR(plat_dat); plat_dat->has_gmac = true; - plat_dat->init = rk_gmac_init; plat_dat->fix_mac_speed = rk_fix_speed; plat_dat->bsp_priv = rk_gmac_setup(pdev, data); if (IS_ERR(plat_dat->bsp_priv)) return PTR_ERR(plat_dat->bsp_priv); - ret = rk_gmac_init(pdev, plat_dat->bsp_priv); + ret = rk_gmac_powerup(plat_dat->bsp_priv); if (ret) return ret; -- cgit v1.1 From 5a3c7805c444d9d55f302a4b3930e8758be13fab Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 5 Nov 2016 14:04:52 +0100 Subject: Revert "net: stmmac: allow to split suspend/resume from init/exit callbacks" Instead of adding hooks inside stmmac_platform it is better to just use the standard PM callbacks within the specific dwmac-driver. This only used by the dwmac-rk driver. This reverts commit cecbc5563a02 ("stmmac: allow to split suspend/resume from init/exit callbacks"). Signed-off-by: Joachim Eastwood Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 8 ++------ include/linux/stmmac.h | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 0a0d6a8..4d544c3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -417,9 +417,7 @@ static int stmmac_pltfr_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); ret = stmmac_suspend(dev); - if (priv->plat->suspend) - priv->plat->suspend(pdev, priv->plat->bsp_priv); - else if (priv->plat->exit) + if (priv->plat->exit) priv->plat->exit(pdev, priv->plat->bsp_priv); return ret; @@ -438,9 +436,7 @@ static int stmmac_pltfr_resume(struct device *dev) struct stmmac_priv *priv = netdev_priv(ndev); struct platform_device *pdev = to_platform_device(dev); - if (priv->plat->resume) - priv->plat->resume(pdev, priv->plat->bsp_priv); - else if (priv->plat->init) + if (priv->plat->init) priv->plat->init(pdev, priv->plat->bsp_priv); return stmmac_resume(dev); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 705840e..3537fb3 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -135,8 +135,6 @@ struct plat_stmmacenet_data { void (*bus_setup)(void __iomem *ioaddr); int (*init)(struct platform_device *pdev, void *priv); void (*exit)(struct platform_device *pdev, void *priv); - void (*suspend)(struct platform_device *pdev, void *priv); - void (*resume)(struct platform_device *pdev, void *priv); void *bsp_priv; struct stmmac_axi *axi; int has_gmac4; -- cgit v1.1 From f1cd5aa0780801dd885566956f07100ae0a7acd2 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 5 Nov 2016 14:05:39 +0100 Subject: net: adaptec: starfire: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/adaptec/starfire.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index 4a9a16e..3aaad33 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -1816,21 +1816,23 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } -static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct netdev_private *np = netdev_priv(dev); spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii_if, ecmd); + mii_ethtool_get_link_ksettings(&np->mii_if, cmd); spin_unlock_irq(&np->lock); return 0; } -static int set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct netdev_private *np = netdev_priv(dev); int res; spin_lock_irq(&np->lock); - res = mii_ethtool_sset(&np->mii_if, ecmd); + res = mii_ethtool_set_link_ksettings(&np->mii_if, cmd); spin_unlock_irq(&np->lock); check_duplex(dev); return res; @@ -1861,12 +1863,12 @@ static void set_msglevel(struct net_device *dev, u32 val) static const struct ethtool_ops ethtool_ops = { .begin = check_if_running, .get_drvinfo = get_drvinfo, - .get_settings = get_settings, - .set_settings = set_settings, .nway_reset = nway_reset, .get_link = get_link, .get_msglevel = get_msglevel, .set_msglevel = set_msglevel, + .get_link_ksettings = get_link_ksettings, + .set_link_ksettings = set_link_ksettings, }; static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -- cgit v1.1 From d17970d746b89e71253db4a60a684d5876bbf3c2 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 5 Nov 2016 16:17:54 +0100 Subject: net: alteon: acenic: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Acked-by: Jes Sorensen Signed-off-by: David S. Miller --- drivers/net/ethernet/alteon/acenic.c | 65 +++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c index a5c1e29..16f0c70 100644 --- a/drivers/net/ethernet/alteon/acenic.c +++ b/drivers/net/ethernet/alteon/acenic.c @@ -429,14 +429,16 @@ static const char version[] = "acenic.c: v0.92 08/05/2002 Jes Sorensen, linux-acenic@SunSITE.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; -static int ace_get_settings(struct net_device *, struct ethtool_cmd *); -static int ace_set_settings(struct net_device *, struct ethtool_cmd *); +static int ace_get_link_ksettings(struct net_device *, + struct ethtool_link_ksettings *); +static int ace_set_link_ksettings(struct net_device *, + const struct ethtool_link_ksettings *); static void ace_get_drvinfo(struct net_device *, struct ethtool_drvinfo *); static const struct ethtool_ops ace_ethtool_ops = { - .get_settings = ace_get_settings, - .set_settings = ace_set_settings, .get_drvinfo = ace_get_drvinfo, + .get_link_ksettings = ace_get_link_ksettings, + .set_link_ksettings = ace_set_link_ksettings, }; static void ace_watchdog(struct net_device *dev); @@ -2579,43 +2581,44 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu) return 0; } -static int ace_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int ace_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct ace_private *ap = netdev_priv(dev); struct ace_regs __iomem *regs = ap->regs; u32 link; + u32 supported; - memset(ecmd, 0, sizeof(struct ethtool_cmd)); - ecmd->supported = - (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg | SUPPORTED_FIBRE); + memset(cmd, 0, sizeof(struct ethtool_link_ksettings)); - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_INTERNAL; + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_FIBRE); + + cmd->base.port = PORT_FIBRE; link = readl(®s->GigLnkState); - if (link & LNK_1000MB) - ethtool_cmd_speed_set(ecmd, SPEED_1000); - else { + if (link & LNK_1000MB) { + cmd->base.speed = SPEED_1000; + } else { link = readl(®s->FastLnkState); if (link & LNK_100MB) - ethtool_cmd_speed_set(ecmd, SPEED_100); + cmd->base.speed = SPEED_100; else if (link & LNK_10MB) - ethtool_cmd_speed_set(ecmd, SPEED_10); + cmd->base.speed = SPEED_10; else - ethtool_cmd_speed_set(ecmd, 0); + cmd->base.speed = 0; } if (link & LNK_FULL_DUPLEX) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; if (link & LNK_NEGOTIATE) - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; else - ecmd->autoneg = AUTONEG_DISABLE; + cmd->base.autoneg = AUTONEG_DISABLE; #if 0 /* @@ -2626,13 +2629,15 @@ static int ace_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ecmd->txcoal = readl(®s->TuneTxCoalTicks); ecmd->rxcoal = readl(®s->TuneRxCoalTicks); #endif - ecmd->maxtxpkt = readl(®s->TuneMaxTxDesc); - ecmd->maxrxpkt = readl(®s->TuneMaxRxDesc); + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); return 0; } -static int ace_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int ace_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct ace_private *ap = netdev_priv(dev); struct ace_regs __iomem *regs = ap->regs; @@ -2655,11 +2660,11 @@ static int ace_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) LNK_RX_FLOW_CTL_Y | LNK_NEG_FCTL; if (!ACE_IS_TIGON_I(ap)) link |= LNK_TX_FLOW_CTL_Y; - if (ecmd->autoneg == AUTONEG_ENABLE) + if (cmd->base.autoneg == AUTONEG_ENABLE) link |= LNK_NEGOTIATE; - if (ethtool_cmd_speed(ecmd) != speed) { + if (cmd->base.speed != speed) { link &= ~(LNK_1000MB | LNK_100MB | LNK_10MB); - switch (ethtool_cmd_speed(ecmd)) { + switch (cmd->base.speed) { case SPEED_1000: link |= LNK_1000MB; break; @@ -2672,7 +2677,7 @@ static int ace_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) } } - if (ecmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) link |= LNK_FULL_DUPLEX; if (link != ap->link) { -- cgit v1.1 From 1435003c2c2dccad015c8b76199bc39d60343e3b Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 5 Nov 2016 20:17:03 +0100 Subject: net: amd8111e: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/amd8111e.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 84b4ffb..11cf1e3 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1421,21 +1421,23 @@ static void amd8111e_get_regs(struct net_device *dev, struct ethtool_regs *regs, amd8111e_read_regs(lp, buf); } -static int amd8111e_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int amd8111e_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct amd8111e_priv *lp = netdev_priv(dev); spin_lock_irq(&lp->lock); - mii_ethtool_gset(&lp->mii_if, ecmd); + mii_ethtool_get_link_ksettings(&lp->mii_if, cmd); spin_unlock_irq(&lp->lock); return 0; } -static int amd8111e_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int amd8111e_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct amd8111e_priv *lp = netdev_priv(dev); int res; spin_lock_irq(&lp->lock); - res = mii_ethtool_sset(&lp->mii_if, ecmd); + res = mii_ethtool_set_link_ksettings(&lp->mii_if, cmd); spin_unlock_irq(&lp->lock); return res; } @@ -1482,12 +1484,12 @@ static const struct ethtool_ops ops = { .get_drvinfo = amd8111e_get_drvinfo, .get_regs_len = amd8111e_get_regs_len, .get_regs = amd8111e_get_regs, - .get_settings = amd8111e_get_settings, - .set_settings = amd8111e_set_settings, .nway_reset = amd8111e_nway_reset, .get_link = amd8111e_get_link, .get_wol = amd8111e_get_wol, .set_wol = amd8111e_set_wol, + .get_link_ksettings = amd8111e_get_link_ksettings, + .set_link_ksettings = amd8111e_set_link_ksettings, }; /* This function handles all the ethtool ioctls. It gives driver info, -- cgit v1.1 From ea74df816f1daf83e3a8ff3197e4b2bbcb0582fc Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 6 Nov 2016 00:26:41 +0100 Subject: net: amd: pcnet32: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/pcnet32.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index adc7ab9..41e58cc 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -677,7 +677,8 @@ static void pcnet32_poll_controller(struct net_device *dev) } #endif -static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int pcnet32_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; @@ -685,14 +686,15 @@ static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) if (lp->mii) { spin_lock_irqsave(&lp->lock, flags); - mii_ethtool_gset(&lp->mii_if, cmd); + mii_ethtool_get_link_ksettings(&lp->mii_if, cmd); spin_unlock_irqrestore(&lp->lock, flags); r = 0; } return r; } -static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int pcnet32_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; @@ -700,7 +702,7 @@ static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) if (lp->mii) { spin_lock_irqsave(&lp->lock, flags); - r = mii_ethtool_sset(&lp->mii_if, cmd); + r = mii_ethtool_set_link_ksettings(&lp->mii_if, cmd); spin_unlock_irqrestore(&lp->lock, flags); } return r; @@ -1440,8 +1442,6 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, } static const struct ethtool_ops pcnet32_ethtool_ops = { - .get_settings = pcnet32_get_settings, - .set_settings = pcnet32_set_settings, .get_drvinfo = pcnet32_get_drvinfo, .get_msglevel = pcnet32_get_msglevel, .set_msglevel = pcnet32_set_msglevel, @@ -1455,6 +1455,8 @@ static const struct ethtool_ops pcnet32_ethtool_ops = { .get_regs_len = pcnet32_get_regs_len, .get_regs = pcnet32_get_regs, .get_sset_count = pcnet32_get_sset_count, + .get_link_ksettings = pcnet32_get_link_ksettings, + .set_link_ksettings = pcnet32_set_link_ksettings, }; /* only probes for non-PCI devices, the rest are handled by -- cgit v1.1 From 90fdd04e2c2c9090693922d855c267ddce77eff6 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 6 Nov 2016 14:57:04 +0100 Subject: net: xgbe: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Acked-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 68 +++++++++++++++------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 46c959b..920566a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -272,80 +272,86 @@ static int xgbe_set_pauseparam(struct net_device *netdev, return ret; } -static int xgbe_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) +static int xgbe_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - cmd->phy_address = pdata->phy.address; + cmd->base.phy_address = pdata->phy.address; - cmd->supported = pdata->phy.supported; - cmd->advertising = pdata->phy.advertising; - cmd->lp_advertising = pdata->phy.lp_advertising; + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + pdata->phy.supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + pdata->phy.advertising); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising, + pdata->phy.lp_advertising); - cmd->autoneg = pdata->phy.autoneg; - ethtool_cmd_speed_set(cmd, pdata->phy.speed); - cmd->duplex = pdata->phy.duplex; + cmd->base.autoneg = pdata->phy.autoneg; + cmd->base.speed = pdata->phy.speed; + cmd->base.duplex = pdata->phy.duplex; - cmd->port = PORT_NONE; - cmd->transceiver = XCVR_INTERNAL; + cmd->base.port = PORT_NONE; return 0; } -static int xgbe_set_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) +static int xgbe_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + u32 advertising; u32 speed; int ret; - speed = ethtool_cmd_speed(cmd); + speed = cmd->base.speed; - if (cmd->phy_address != pdata->phy.address) { + if (cmd->base.phy_address != pdata->phy.address) { netdev_err(netdev, "invalid phy address %hhu\n", - cmd->phy_address); + cmd->base.phy_address); return -EINVAL; } - if ((cmd->autoneg != AUTONEG_ENABLE) && - (cmd->autoneg != AUTONEG_DISABLE)) { + if ((cmd->base.autoneg != AUTONEG_ENABLE) && + (cmd->base.autoneg != AUTONEG_DISABLE)) { netdev_err(netdev, "unsupported autoneg %hhu\n", - cmd->autoneg); + cmd->base.autoneg); return -EINVAL; } - if (cmd->autoneg == AUTONEG_DISABLE) { + if (cmd->base.autoneg == AUTONEG_DISABLE) { if (!pdata->phy_if.phy_valid_speed(pdata, speed)) { netdev_err(netdev, "unsupported speed %u\n", speed); return -EINVAL; } - if (cmd->duplex != DUPLEX_FULL) { + if (cmd->base.duplex != DUPLEX_FULL) { netdev_err(netdev, "unsupported duplex %hhu\n", - cmd->duplex); + cmd->base.duplex); return -EINVAL; } } + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); + netif_dbg(pdata, link, netdev, "requested advertisement %#x, phy supported %#x\n", - cmd->advertising, pdata->phy.supported); + advertising, pdata->phy.supported); - cmd->advertising &= pdata->phy.supported; - if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) { + advertising &= pdata->phy.supported; + if ((cmd->base.autoneg == AUTONEG_ENABLE) && !advertising) { netdev_err(netdev, "unsupported requested advertisement\n"); return -EINVAL; } ret = 0; - pdata->phy.autoneg = cmd->autoneg; + pdata->phy.autoneg = cmd->base.autoneg; pdata->phy.speed = speed; - pdata->phy.duplex = cmd->duplex; - pdata->phy.advertising = cmd->advertising; + pdata->phy.duplex = cmd->base.duplex; + pdata->phy.advertising = advertising; - if (cmd->autoneg == AUTONEG_ENABLE) + if (cmd->base.autoneg == AUTONEG_ENABLE) pdata->phy.advertising |= ADVERTISED_Autoneg; else pdata->phy.advertising &= ~ADVERTISED_Autoneg; @@ -585,8 +591,6 @@ static int xgbe_get_ts_info(struct net_device *netdev, } static const struct ethtool_ops xgbe_ethtool_ops = { - .get_settings = xgbe_get_settings, - .set_settings = xgbe_set_settings, .get_drvinfo = xgbe_get_drvinfo, .get_msglevel = xgbe_get_msglevel, .set_msglevel = xgbe_set_msglevel, @@ -604,6 +608,8 @@ static const struct ethtool_ops xgbe_ethtool_ops = { .get_rxfh = xgbe_get_rxfh, .set_rxfh = xgbe_set_rxfh, .get_ts_info = xgbe_get_ts_info, + .get_link_ksettings = xgbe_get_link_ksettings, + .set_link_ksettings = xgbe_set_link_ksettings, }; const struct ethtool_ops *xgbe_get_ethtool_ops(void) -- cgit v1.1 From b12ab9b11966ee6c724e34b2f0f13ea76932b6bf Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 6 Nov 2016 15:02:32 +0100 Subject: net: 3com: typhoon: fix typhoon_get_link_ksettings When moving from typhoon_get_settings to typhoon_getlink_ksettings in the commit f7a5537cd2a5 ("net: 3com: typhoon: use new api ethtool_{get|set}_link_ksettings"), we use a local variable supported but we forgot to update the struct ethtool_link_ksettings with this value. We also initialize advertising to zero, because otherwise it may be uninitialized if no case of the switch (tp->xcvr_select) is used. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/typhoon.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index dbdf06f..a0cacbe 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -1000,7 +1000,7 @@ typhoon_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *cmd) { struct typhoon *tp = netdev_priv(dev); - u32 supported, advertising; + u32 supported, advertising = 0; supported = SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg; @@ -1049,6 +1049,11 @@ typhoon_get_link_ksettings(struct net_device *dev, else cmd->base.autoneg = AUTONEG_DISABLE; + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + return 0; } -- cgit v1.1 From bb48024284327768debd03a197d7c4179ec6ca05 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 6 Nov 2016 17:12:27 +0200 Subject: qed: Prevent stack corruption on MFW interaction Driver uses a union for copying data to & from management firmware when interacting with it. Problem is that the function always copies sizeof(union) while commit 2edbff8dcb5d ("qed: Learn resources from management firmware") is casting a union elements which is of smaller size [24-byte instead of 88-bytes]. Also, the union contains some inappropriate elements which increase its size [should have been 32-bytes]. While this shouldn't corrupt other PF messages to the MFW [as management firmware enforces permissions so that each PF is allowed to write only to its own mailbox] we fix this here as well. Fixes: 2edbff8dcb5d ("qed: Learn resources from management firmware") Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 1 - drivers/net/ethernet/qlogic/qed/qed_mcp.c | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 048f9a3..f5a4ebb 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -8581,7 +8581,6 @@ union drv_union_data { struct drv_version_stc drv_version; struct lan_stats_stc lan_stats; - u64 reserved_stats[11]; struct ocbb_data_stc ocbb_info; struct temperature_status_stc temp_info; struct resource_info resource; diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index d8e499e..6dd3ce4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1697,19 +1697,27 @@ int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, u32 *p_mcp_resp, u32 *p_mcp_param) { struct qed_mcp_mb_params mb_params; - union drv_union_data *p_union_data; + union drv_union_data union_data; int rc; memset(&mb_params, 0, sizeof(mb_params)); + memset(&union_data, 0, sizeof(union_data)); mb_params.cmd = DRV_MSG_GET_RESOURCE_ALLOC_MSG; mb_params.param = QED_RESC_ALLOC_VERSION; - p_union_data = (union drv_union_data *)p_resc_info; - mb_params.p_data_src = p_union_data; - mb_params.p_data_dst = p_union_data; + + /* Need to have a sufficient large struct, as the cmd_and_union + * is going to do memcpy from and to it. + */ + memcpy(&union_data.resource, p_resc_info, sizeof(*p_resc_info)); + + mb_params.p_data_src = &union_data; + mb_params.p_data_dst = &union_data; rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) return rc; + /* Copy the data back */ + memcpy(p_resc_info, &union_data.resource, sizeof(*p_resc_info)); *p_mcp_resp = mb_params.mcp_resp; *p_mcp_param = mb_params.mcp_param; -- cgit v1.1 From 35b80733b3d3ab620edc30f286606be775930843 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 7 Nov 2016 00:16:25 +0900 Subject: net: core: add missing check for uid_range in rule_exists. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this check, it is not possible to create two rules that are identical except for their UID ranges. For example: root@net-test:/# ip rule add prio 1000 lookup 300 root@net-test:/# ip rule add prio 1000 uidrange 100-200 lookup 300 RTNETLINK answers: File exists root@net-test:/# ip rule add prio 1000 uidrange 100-199 lookup 100 root@net-test:/# ip rule add prio 1000 uidrange 200-299 lookup 200 root@net-test:/# ip rule add prio 1000 uidrange 300-399 lookup 100 RTNETLINK answers: File exists Tested: https://android-review.googlesource.com/#/c/299980/ Signed-off-by: Lorenzo Colitti Acked-by: Maciej Å»enczykowski Signed-off-by: David S. Miller --- net/core/fib_rules.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 5de436a..b6791d9 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -343,6 +343,10 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, if (r->l3mdev != rule->l3mdev) continue; + if (!uid_eq(r->uid_range.start, rule->uid_range.start) || + !uid_eq(r->uid_range.end, rule->uid_range.end)) + continue; + if (!ops->compare(r, frh, tb)) continue; return 1; -- cgit v1.1 From 9ce183b4c4d24559467d7712e313f2b3f9277437 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:36 +0200 Subject: net/sched: act_tunnel_key: add helper inlines to access tcf_tunnel_key Needed for drivers to pick the relevant action when offloading tunnel key act. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/net/tc_act/tc_tunnel_key.h | 37 +++++++++++++++++++++++++++++++++++++ net/sched/act_tunnel_key.c | 1 - 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/net/tc_act/tc_tunnel_key.h b/include/net/tc_act/tc_tunnel_key.h index 253f8da..efef0b4 100644 --- a/include/net/tc_act/tc_tunnel_key.h +++ b/include/net/tc_act/tc_tunnel_key.h @@ -12,6 +12,8 @@ #define __NET_TC_TUNNEL_KEY_H #include +#include +#include struct tcf_tunnel_key_params { struct rcu_head rcu; @@ -27,4 +29,39 @@ struct tcf_tunnel_key { #define to_tunnel_key(a) ((struct tcf_tunnel_key *)a) +static inline bool is_tcf_tunnel_set(const struct tc_action *a) +{ +#ifdef CONFIG_NET_CLS_ACT + struct tcf_tunnel_key *t = to_tunnel_key(a); + struct tcf_tunnel_key_params *params = rtnl_dereference(t->params); + + if (a->ops && a->ops->type == TCA_ACT_TUNNEL_KEY) + return params->tcft_action == TCA_TUNNEL_KEY_ACT_SET; +#endif + return false; +} + +static inline bool is_tcf_tunnel_release(const struct tc_action *a) +{ +#ifdef CONFIG_NET_CLS_ACT + struct tcf_tunnel_key *t = to_tunnel_key(a); + struct tcf_tunnel_key_params *params = rtnl_dereference(t->params); + + if (a->ops && a->ops->type == TCA_ACT_TUNNEL_KEY) + return params->tcft_action == TCA_TUNNEL_KEY_ACT_RELEASE; +#endif + return false; +} + +static inline struct ip_tunnel_info *tcf_tunnel_info(const struct tc_action *a) +{ +#ifdef CONFIG_NET_CLS_ACT + struct tcf_tunnel_key *t = to_tunnel_key(a); + struct tcf_tunnel_key_params *params = rtnl_dereference(t->params); + + return ¶ms->tcft_enc_metadata->u.tun_info; +#else + return NULL; +#endif +} #endif /* __NET_TC_TUNNEL_KEY_H */ diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index af47bdf..cab1fd5 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include -- cgit v1.1 From 9ba6a9a9f7a42673e9fc08ff3594f64caae64d3c Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:37 +0200 Subject: flow_dissector: Add enums for encapsulation keys New encapsulation keys were added to the flower classifier, which allow classification according to outer (encapsulation) headers attributes such as key and IP addresses. In order to expose those attributes outside flower, add corresponding enums in the flow dissector. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/net/flow_dissector.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h index d953492..4e7cf38a 100644 --- a/include/net/flow_dissector.h +++ b/include/net/flow_dissector.h @@ -128,6 +128,10 @@ enum flow_dissector_key_id { FLOW_DISSECTOR_KEY_FLOW_LABEL, /* struct flow_dissector_key_flow_tags */ FLOW_DISSECTOR_KEY_GRE_KEYID, /* struct flow_dissector_key_keyid */ FLOW_DISSECTOR_KEY_MPLS_ENTROPY, /* struct flow_dissector_key_keyid */ + FLOW_DISSECTOR_KEY_ENC_KEYID, /* struct flow_dissector_key_keyid */ + FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, /* struct flow_dissector_key_ipv4_addrs */ + FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, /* struct flow_dissector_key_ipv6_addrs */ + FLOW_DISSECTOR_KEY_ENC_CONTROL, /* struct flow_dissector_key_control */ FLOW_DISSECTOR_KEY_MAX, }; -- cgit v1.1 From 519d10521cd3049f9500a1f4709ec1719a18540f Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:38 +0200 Subject: net/sched: cls_flower: Allow setting encapsulation fields as used key When encapsulation field is set, mark it as used key for the flow dissector. This will be used by offloading drivers. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index db4cd88..6369b74 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -567,6 +567,16 @@ static void fl_init_dissector(struct cls_fl_head *head, FLOW_DISSECTOR_KEY_PORTS, tp); FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, FLOW_DISSECTOR_KEY_VLAN, vlan); + FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, + FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); + FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, + FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4); + FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, + FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6); + if (FL_KEY_IS_MASKED(&mask->key, enc_ipv4) || + FL_KEY_IS_MASKED(&mask->key, enc_ipv6)) + FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, + enc_control); skb_flow_dissector_init(&head->dissector, keys, cnt); } -- cgit v1.1 From f4d997fd613001e612543339e0275c037f94ffe9 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:39 +0200 Subject: net/sched: cls_flower: Add UDP port to tunnel parameters The current IP tunneling classification supports only IP addresses and key. Enhance UDP based IP tunneling classification parameters by adding UDP src and dst port. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/net/flow_dissector.h | 1 + include/uapi/linux/pkt_cls.h | 5 +++++ net/sched/cls_flower.c | 29 ++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h index 4e7cf38a..c4f3166 100644 --- a/include/net/flow_dissector.h +++ b/include/net/flow_dissector.h @@ -132,6 +132,7 @@ enum flow_dissector_key_id { FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, /* struct flow_dissector_key_ipv4_addrs */ FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, /* struct flow_dissector_key_ipv6_addrs */ FLOW_DISSECTOR_KEY_ENC_CONTROL, /* struct flow_dissector_key_control */ + FLOW_DISSECTOR_KEY_ENC_PORTS, /* struct flow_dissector_key_ports */ FLOW_DISSECTOR_KEY_MAX, }; diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index eb94781..86786d4 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -452,6 +452,11 @@ enum { TCA_FLOWER_KEY_SCTP_SRC, /* be16 */ TCA_FLOWER_KEY_SCTP_DST, /* be16 */ + + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, /* be16 */ __TCA_FLOWER_MAX, }; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 6369b74..e8dd09a 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -43,6 +43,7 @@ struct fl_flow_key { struct flow_dissector_key_ipv4_addrs enc_ipv4; struct flow_dissector_key_ipv6_addrs enc_ipv6; }; + struct flow_dissector_key_ports enc_tp; } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ struct fl_flow_mask_range { @@ -155,6 +156,8 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, } skb_key.enc_key_id.keyid = tunnel_id_to_key32(key->tun_id); + skb_key.enc_tp.src = key->tp_src; + skb_key.enc_tp.dst = key->tp_dst; } skb_key.indev_ifindex = skb->skb_iif; @@ -348,6 +351,10 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, }; static void fl_set_key_val(struct nlattr **tb, @@ -500,6 +507,14 @@ static int fl_set_key(struct net *net, struct nlattr **tb, &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC, sizeof(key->enc_key_id.keyid)); + fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, + &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, + sizeof(key->enc_tp.src)); + + fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, + &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, + sizeof(key->enc_tp.dst)); + return 0; } @@ -577,6 +592,8 @@ static void fl_init_dissector(struct cls_fl_head *head, FL_KEY_IS_MASKED(&mask->key, enc_ipv6)) FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, enc_control); + FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, + FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp); skb_flow_dissector_init(&head->dissector, keys, cnt); } @@ -951,7 +968,17 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID, &mask->enc_key_id, TCA_FLOWER_UNSPEC, - sizeof(key->enc_key_id))) + sizeof(key->enc_key_id)) || + fl_dump_key_val(skb, &key->enc_tp.src, + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, + &mask->enc_tp.src, + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, + sizeof(key->enc_tp.src)) || + fl_dump_key_val(skb, &key->enc_tp.dst, + TCA_FLOWER_KEY_ENC_UDP_DST_PORT, + &mask->enc_tp.dst, + TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, + sizeof(key->enc_tp.dst))) goto nla_put_failure; nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags); -- cgit v1.1 From 24ba898d43e87f9ac87353c7a13eef4ee726cab7 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:40 +0200 Subject: net/dst: Add dst port to dst_metadata utility functions Add dst port parameter to __ip_tun_set_dst and __ipv6_tun_set_dst utility functions. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/net/dst_metadata.h | 10 ++++++---- net/sched/act_tunnel_key.c | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h index 6965c8f..701fc81 100644 --- a/include/net/dst_metadata.h +++ b/include/net/dst_metadata.h @@ -115,6 +115,7 @@ static inline struct ip_tunnel_info *skb_tunnel_info_unclone(struct sk_buff *skb static inline struct metadata_dst *__ip_tun_set_dst(__be32 saddr, __be32 daddr, __u8 tos, __u8 ttl, + __be16 tp_dst, __be16 flags, __be64 tunnel_id, int md_size) @@ -127,7 +128,7 @@ static inline struct metadata_dst *__ip_tun_set_dst(__be32 saddr, ip_tunnel_key_init(&tun_dst->u.tun_info.key, saddr, daddr, tos, ttl, - 0, 0, 0, tunnel_id, flags); + 0, 0, tp_dst, tunnel_id, flags); return tun_dst; } @@ -139,12 +140,13 @@ static inline struct metadata_dst *ip_tun_rx_dst(struct sk_buff *skb, const struct iphdr *iph = ip_hdr(skb); return __ip_tun_set_dst(iph->saddr, iph->daddr, iph->tos, iph->ttl, - flags, tunnel_id, md_size); + 0, flags, tunnel_id, md_size); } static inline struct metadata_dst *__ipv6_tun_set_dst(const struct in6_addr *saddr, const struct in6_addr *daddr, __u8 tos, __u8 ttl, + __be16 tp_dst, __be32 label, __be16 flags, __be64 tunnel_id, @@ -162,7 +164,7 @@ static inline struct metadata_dst *__ipv6_tun_set_dst(const struct in6_addr *sad info->key.tun_flags = flags; info->key.tun_id = tunnel_id; info->key.tp_src = 0; - info->key.tp_dst = 0; + info->key.tp_dst = tp_dst; info->key.u.ipv6.src = *saddr; info->key.u.ipv6.dst = *daddr; @@ -183,7 +185,7 @@ static inline struct metadata_dst *ipv6_tun_rx_dst(struct sk_buff *skb, return __ipv6_tun_set_dst(&ip6h->saddr, &ip6h->daddr, ipv6_get_dsfield(ip6h), ip6h->hop_limit, - ip6_flowlabel(ip6h), flags, tunnel_id, + 0, ip6_flowlabel(ip6h), flags, tunnel_id, md_size); } #endif /* __NET_DST_METADATA_H */ diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index cab1fd5..bd2f63e 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -119,7 +119,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, daddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]); metadata = __ip_tun_set_dst(saddr, daddr, 0, 0, - TUNNEL_KEY, key_id, 0); + 0, TUNNEL_KEY, key_id, 0); } else if (tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC] && tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]) { struct in6_addr saddr; @@ -129,7 +129,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, daddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]); metadata = __ipv6_tun_set_dst(&saddr, &daddr, 0, 0, 0, - TUNNEL_KEY, key_id, 0); + 0, TUNNEL_KEY, key_id, 0); } if (!metadata) { -- cgit v1.1 From 75bfbca01e48d2d62e8321609ae32aaf6c6fab0e Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:41 +0200 Subject: net/sched: act_tunnel_key: Add UDP dst port option The current tunnel set action supports only IP addresses and key options. Add UDP dst port option. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/uapi/linux/tc_act/tc_tunnel_key.h | 1 + net/sched/act_tunnel_key.c | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/tc_act/tc_tunnel_key.h b/include/uapi/linux/tc_act/tc_tunnel_key.h index 890106f..84ea55e 100644 --- a/include/uapi/linux/tc_act/tc_tunnel_key.h +++ b/include/uapi/linux/tc_act/tc_tunnel_key.h @@ -33,6 +33,7 @@ enum { TCA_TUNNEL_KEY_ENC_IPV6_DST, /* struct in6_addr */ TCA_TUNNEL_KEY_ENC_KEY_ID, /* be64 */ TCA_TUNNEL_KEY_PAD, + TCA_TUNNEL_KEY_ENC_DST_PORT, /* be16 */ __TCA_TUNNEL_KEY_MAX, }; diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index bd2f63e..edc720f 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -66,6 +66,7 @@ static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = { [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, [TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, [TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, + [TCA_TUNNEL_KEY_ENC_DST_PORT] = {.type = NLA_U16}, }; static int tunnel_key_init(struct net *net, struct nlattr *nla, @@ -80,6 +81,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, struct tc_tunnel_key *parm; struct tcf_tunnel_key *t; bool exists = false; + __be16 dst_port = 0; __be64 key_id; int ret = 0; int err; @@ -110,6 +112,9 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, key_id = key32_to_tunnel_id(nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID])); + if (tb[TCA_TUNNEL_KEY_ENC_DST_PORT]) + dst_port = nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_DST_PORT]); + if (tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC] && tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]) { __be32 saddr; @@ -119,7 +124,8 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, daddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]); metadata = __ip_tun_set_dst(saddr, daddr, 0, 0, - 0, TUNNEL_KEY, key_id, 0); + dst_port, TUNNEL_KEY, + key_id, 0); } else if (tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC] && tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]) { struct in6_addr saddr; @@ -129,7 +135,8 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, daddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]); metadata = __ipv6_tun_set_dst(&saddr, &daddr, 0, 0, 0, - 0, TUNNEL_KEY, key_id, 0); + dst_port, TUNNEL_KEY, + key_id, 0); } if (!metadata) { @@ -257,7 +264,8 @@ static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a, if (nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id) || tunnel_key_dump_addresses(skb, - ¶ms->tcft_enc_metadata->u.tun_info)) + ¶ms->tcft_enc_metadata->u.tun_info) || + nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT, key->tp_dst)) goto nla_put_failure; } -- cgit v1.1 From ae9f83ac24569e4bfc7c174be362d1835233c28f Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:42 +0200 Subject: net/mlx5: Move alloc/dealloc encap commands declarations to common header file The alloc and dealloc encap commands will be used in the mlx5e driver, as such, declare them in a common header file. Also, rename the functions: mlx5_cmd_{de}alloc_encap is replaced with mlx5_encap_{de}alloc. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 12 ++++++------ drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 7 ------- drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h | 6 ++++++ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 113c323..6c9d99a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -455,11 +455,11 @@ void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev, #define MAX_ENCAP_SIZE (128) -int mlx5_cmd_alloc_encap(struct mlx5_core_dev *dev, - int header_type, - size_t size, - void *encap_header, - u32 *encap_id) +int mlx5_encap_alloc(struct mlx5_core_dev *dev, + int header_type, + size_t size, + void *encap_header, + u32 *encap_id) { u32 out[MLX5_ST_SZ_DW(alloc_encap_header_out)]; u32 in[MLX5_ST_SZ_DW(alloc_encap_header_in) + @@ -488,7 +488,7 @@ int mlx5_cmd_alloc_encap(struct mlx5_core_dev *dev, return err; } -void mlx5_cmd_dealloc_encap(struct mlx5_core_dev *dev, u32 encap_id) +void mlx5_encap_dealloc(struct mlx5_core_dev *dev, u32 encap_id) { u32 in[MLX5_ST_SZ_DW(dealloc_encap_header_in)]; u32 out[MLX5_ST_SZ_DW(dealloc_encap_header_out)]; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index c5bc468..86bead1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -89,11 +89,4 @@ void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev, struct mlx5_cmd_fc_bulk *b, u16 id, u64 *packets, u64 *bytes); -int mlx5_cmd_alloc_encap(struct mlx5_core_dev *dev, - int header_type, - size_t size, - void *encap_header, - u32 *encap_id); -void mlx5_cmd_dealloc_encap(struct mlx5_core_dev *dev, u32 encap_id); - #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 1933b3c..4762bb9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -121,6 +121,12 @@ struct mlx5_core_dev *mlx5_get_next_phys_dev(struct mlx5_core_dev *dev); void mlx5_dev_list_lock(void); void mlx5_dev_list_unlock(void); int mlx5_dev_list_trylock(void); +int mlx5_encap_alloc(struct mlx5_core_dev *dev, + int header_type, + size_t size, + void *encap_header, + u32 *encap_id); +void mlx5_encap_dealloc(struct mlx5_core_dev *dev, u32 encap_id); bool mlx5_lag_intf_add(struct mlx5_interface *intf, struct mlx5_priv *priv); -- cgit v1.1 From 43f93839e359c8044200ec3f6fa455b47ba3aa2c Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:43 +0200 Subject: net/mlx5: Check max encap header size capability Instead of comparing to a const value, check the value of max encap header size capability as reported by the Firmware. Fixes: 575ddf5888ea ('net/mlx5: Introduce alloc_encap and dealloc_encap commands') Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 6c9d99a..301cec8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -453,27 +453,32 @@ void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev, *bytes = MLX5_GET64(traffic_counter, stats, octets); } -#define MAX_ENCAP_SIZE (128) - int mlx5_encap_alloc(struct mlx5_core_dev *dev, int header_type, size_t size, void *encap_header, u32 *encap_id) { + int max_encap_size = MLX5_CAP_ESW(dev, max_encap_header_size); u32 out[MLX5_ST_SZ_DW(alloc_encap_header_out)]; - u32 in[MLX5_ST_SZ_DW(alloc_encap_header_in) + - (MAX_ENCAP_SIZE / sizeof(u32))]; - void *encap_header_in = MLX5_ADDR_OF(alloc_encap_header_in, in, - encap_header); - void *header = MLX5_ADDR_OF(encap_header_in, encap_header_in, - encap_header); - int inlen = header - (void *)in + size; + void *encap_header_in; + void *header; + int inlen; int err; + u32 *in; - if (size > MAX_ENCAP_SIZE) + if (size > MLX5_CAP_ESW(dev, max_encap_header_size)) return -EINVAL; + in = kzalloc(MLX5_ST_SZ_BYTES(alloc_encap_header_in) + max_encap_size, + GFP_KERNEL); + if (!in) + return -ENOMEM; + + encap_header_in = MLX5_ADDR_OF(alloc_encap_header_in, in, encap_header); + header = MLX5_ADDR_OF(encap_header_in, encap_header_in, encap_header); + inlen = header - (void *)in + size; + memset(in, 0, inlen); MLX5_SET(alloc_encap_header_in, in, opcode, MLX5_CMD_OP_ALLOC_ENCAP_HEADER); @@ -485,6 +490,7 @@ int mlx5_encap_alloc(struct mlx5_core_dev *dev, err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); *encap_id = MLX5_GET(alloc_encap_header_out, out, encap_id); + kfree(in); return err; } -- cgit v1.1 From c9f1b073d0d750ccf8b30b272d1d76479f4cccbc Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:44 +0200 Subject: net/mlx5: Add creation flags when adding new flow table When creating flow tables, allow the caller to specify creation flags. Currently no flags are used and as such this patch doesn't add any new functionality. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 6 ++--- .../ethernet/mellanox/mlx5/core/en_fs_ethtool.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 2 +- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 7 +++--- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 7 +++++- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 28 +++++++++++++--------- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 1 + include/linux/mlx5/fs.h | 10 ++++++-- 12 files changed, 45 insertions(+), 26 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 292ae8b..9b16431 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1857,7 +1857,7 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev, ft = mlx5_create_auto_grouped_flow_table(ns, priority, num_entries, num_groups, - 0); + 0, 0); if (!IS_ERR(ft)) { prio->refcount = 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index 8ff22e8..677b238 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -324,7 +324,7 @@ static int arfs_create_table(struct mlx5e_priv *priv, int err; ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_ARFS_TABLE_SIZE, MLX5E_ARFS_FT_LEVEL); + MLX5E_ARFS_TABLE_SIZE, MLX5E_ARFS_FT_LEVEL, 0); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index bed544d..9617892 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -777,7 +777,7 @@ static int mlx5e_create_ttc_table(struct mlx5e_priv *priv) int err; ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_TTC_TABLE_SIZE, MLX5E_TTC_FT_LEVEL); + MLX5E_TTC_TABLE_SIZE, MLX5E_TTC_FT_LEVEL, 0); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; @@ -948,7 +948,7 @@ static int mlx5e_create_l2_table(struct mlx5e_priv *priv) ft->num_groups = 0; ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_L2_TABLE_SIZE, MLX5E_L2_FT_LEVEL); + MLX5E_L2_TABLE_SIZE, MLX5E_L2_FT_LEVEL, 0); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); @@ -1038,7 +1038,7 @@ static int mlx5e_create_vlan_table(struct mlx5e_priv *priv) ft->num_groups = 0; ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_VLAN_TABLE_SIZE, MLX5E_VLAN_FT_LEVEL); + MLX5E_VLAN_TABLE_SIZE, MLX5E_VLAN_FT_LEVEL, 0); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c index cf52c06..87bb3db 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c @@ -99,7 +99,7 @@ static struct mlx5e_ethtool_table *get_flow_table(struct mlx5e_priv *priv, MLX5E_ETHTOOL_NUM_ENTRIES); ft = mlx5_create_auto_grouped_flow_table(ns, prio, table_size, - MLX5E_ETHTOOL_NUM_GROUPS, 0); + MLX5E_ETHTOOL_NUM_GROUPS, 0, 0); if (IS_ERR(ft)) return (void *)ft; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 165682e..cdd4303 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -83,7 +83,7 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, MLX5E_TC_PRIO, MLX5E_TC_TABLE_NUM_ENTRIES, MLX5E_TC_TABLE_NUM_GROUPS, - 0); + 0, 0); if (IS_ERR(priv->fs.tc.t)) { netdev_err(priv->netdev, "Failed to create tc offload table\n"); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 9ee002e..27f21ac 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -361,7 +361,7 @@ static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw, int nvports) memset(flow_group_in, 0, inlen); table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); - fdb = mlx5_create_flow_table(root_ns, 0, table_size, 0); + fdb = mlx5_create_flow_table(root_ns, 0, table_size, 0, 0); if (IS_ERR(fdb)) { err = PTR_ERR(fdb); esw_warn(dev, "Failed to create FDB Table err %d\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 53d9d6c..b18f951 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -423,7 +423,8 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) fdb = mlx5_create_auto_grouped_flow_table(root_ns, FDB_FAST_PATH, ESW_OFFLOADS_NUM_ENTRIES, - ESW_OFFLOADS_NUM_GROUPS, 0); + ESW_OFFLOADS_NUM_GROUPS, 0, + 0); if (IS_ERR(fdb)) { err = PTR_ERR(fdb); esw_warn(dev, "Failed to create Fast path FDB Table err %d\n", err); @@ -432,7 +433,7 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) esw->fdb_table.fdb = fdb; table_size = nvports + MAX_PF_SQ + 1; - fdb = mlx5_create_flow_table(root_ns, FDB_SLOW_PATH, table_size, 0); + fdb = mlx5_create_flow_table(root_ns, FDB_SLOW_PATH, table_size, 0, 0); if (IS_ERR(fdb)) { err = PTR_ERR(fdb); esw_warn(dev, "Failed to create slow path FDB Table err %d\n", err); @@ -524,7 +525,7 @@ static int esw_create_offloads_table(struct mlx5_eswitch *esw) return -ENOMEM; } - ft_offloads = mlx5_create_flow_table(ns, 0, dev->priv.sriov.num_vfs + 2, 0); + ft_offloads = mlx5_create_flow_table(ns, 0, dev->priv.sriov.num_vfs + 2, 0, 0); if (IS_ERR(ft_offloads)) { err = PTR_ERR(ft_offloads); esw_warn(esw->dev, "Failed to create offloads table, err %d\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 301cec8..cc97bb2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -37,6 +37,7 @@ #include "fs_core.h" #include "fs_cmd.h" #include "mlx5_core.h" +#include "eswitch.h" int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft) @@ -61,8 +62,9 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, enum fs_flow_table_op_mod op_mod, enum fs_flow_table_type type, unsigned int level, unsigned int log_size, struct mlx5_flow_table - *next_ft, unsigned int *table_id) + *next_ft, unsigned int *table_id, u32 flags) { + int en_encap_decap = !!(flags & MLX5_FLOW_TABLE_TUNNEL_EN); u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {0}; u32 in[MLX5_ST_SZ_DW(create_flow_table_in)] = {0}; int err; @@ -78,6 +80,9 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, MLX5_SET(create_flow_table_in, in, other_vport, 1); } + MLX5_SET(create_flow_table_in, in, decap_en, en_encap_decap); + MLX5_SET(create_flow_table_in, in, encap_en, en_encap_decap); + switch (op_mod) { case FS_FT_OP_MOD_NORMAL: if (next_ft) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 86bead1..8fad806 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -38,7 +38,7 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, enum fs_flow_table_op_mod op_mod, enum fs_flow_table_type type, unsigned int level, unsigned int log_size, struct mlx5_flow_table - *next_ft, unsigned int *table_id); + *next_ft, unsigned int *table_id, u32 flags); int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index e65eabf..4d28c8d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -505,7 +505,8 @@ static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in) static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_fte, enum fs_flow_table_type table_type, - enum fs_flow_table_op_mod op_mod) + enum fs_flow_table_op_mod op_mod, + u32 flags) { struct mlx5_flow_table *ft; @@ -519,6 +520,7 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_ft ft->type = table_type; ft->vport = vport; ft->max_fte = max_fte; + ft->flags = flags; INIT_LIST_HEAD(&ft->fwd_rules); mutex_init(&ft->lock); @@ -777,7 +779,8 @@ static void list_add_flow_table(struct mlx5_flow_table *ft, static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespace *ns, enum fs_flow_table_op_mod op_mod, u16 vport, int prio, - int max_fte, u32 level) + int max_fte, u32 level, + u32 flags) { struct mlx5_flow_table *next_ft = NULL; struct mlx5_flow_table *ft; @@ -810,7 +813,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa vport, max_fte ? roundup_pow_of_two(max_fte) : 0, root->table_type, - op_mod); + op_mod, flags); if (!ft) { err = -ENOMEM; goto unlock_root; @@ -820,7 +823,8 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa log_table_sz = ft->max_fte ? ilog2(ft->max_fte) : 0; next_ft = find_next_chained_ft(fs_prio); err = mlx5_cmd_create_flow_table(root->dev, ft->vport, ft->op_mod, ft->type, - ft->level, log_table_sz, next_ft, &ft->id); + ft->level, log_table_sz, next_ft, &ft->id, + ft->flags); if (err) goto free_ft; @@ -845,10 +849,11 @@ unlock_root: struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, int prio, int max_fte, - u32 level) + u32 level, + u32 flags) { return __mlx5_create_flow_table(ns, FS_FT_OP_MOD_NORMAL, 0, prio, - max_fte, level); + max_fte, level, flags); } struct mlx5_flow_table *mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns, @@ -856,7 +861,7 @@ struct mlx5_flow_table *mlx5_create_vport_flow_table(struct mlx5_flow_namespace u32 level, u16 vport) { return __mlx5_create_flow_table(ns, FS_FT_OP_MOD_NORMAL, vport, prio, - max_fte, level); + max_fte, level, 0); } struct mlx5_flow_table *mlx5_create_lag_demux_flow_table( @@ -864,7 +869,7 @@ struct mlx5_flow_table *mlx5_create_lag_demux_flow_table( int prio, u32 level) { return __mlx5_create_flow_table(ns, FS_FT_OP_MOD_LAG_DEMUX, 0, prio, 0, - level); + level, 0); } EXPORT_SYMBOL(mlx5_create_lag_demux_flow_table); @@ -872,14 +877,15 @@ struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_nam int prio, int num_flow_table_entries, int max_num_groups, - u32 level) + u32 level, + u32 flags) { struct mlx5_flow_table *ft; if (max_num_groups > num_flow_table_entries) return ERR_PTR(-EINVAL); - ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries, level); + ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries, level, flags); if (IS_ERR(ft)) return ft; @@ -1822,7 +1828,7 @@ static int create_anchor_flow_table(struct mlx5_flow_steering *steering) ns = mlx5_get_flow_namespace(steering->dev, MLX5_FLOW_NAMESPACE_ANCHOR); if (!ns) return -EINVAL; - ft = mlx5_create_flow_table(ns, ANCHOR_PRIO, ANCHOR_SIZE, ANCHOR_LEVEL); + ft = mlx5_create_flow_table(ns, ANCHOR_PRIO, ANCHOR_SIZE, ANCHOR_LEVEL, 0); if (IS_ERR(ft)) { mlx5_core_err(steering->dev, "Failed to create last anchor flow table"); return PTR_ERR(ft); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index d515088..9f616ed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -117,6 +117,7 @@ struct mlx5_flow_table { struct mutex lock; /* FWD rules that point on this flow table */ struct list_head fwd_rules; + u32 flags; }; struct mlx5_fc_cache { diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 0dcd287..ab1a5fd 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -42,6 +42,10 @@ enum { MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO = 1 << 16, }; +enum { + MLX5_FLOW_TABLE_TUNNEL_EN = BIT(0), +}; + #define LEFTOVERS_RULE_NUM 2 static inline void build_leftovers_ft_param(int *priority, int *n_ent, @@ -97,13 +101,15 @@ mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns, int prio, int num_flow_table_entries, int max_num_groups, - u32 level); + u32 level, + u32 flags); struct mlx5_flow_table * mlx5_create_flow_table(struct mlx5_flow_namespace *ns, int prio, int num_flow_table_entries, - u32 level); + u32 level, + u32 flags); struct mlx5_flow_table * mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns, int prio, -- cgit v1.1 From 66958ed906b87816314c0517f05fe0b5766ec7fe Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:45 +0200 Subject: net/mlx5: Support encap id when setting new steering entry In order to support steering rules which add encapsulation headers, encap_id parameter is needed. Add new mlx5_flow_act struct which holds action related parameter: action, flow_tag and encap_id. Use mlx5_flow_act struct when adding a new steering rule. This patch doesn't change any functionality. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/main.c | 10 ++--- drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 17 +++++--- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 29 ++++++++----- .../ethernet/mellanox/mlx5/core/en_fs_ethtool.c | 10 ++--- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 9 ++-- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 23 ++++++----- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 25 ++++++----- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 48 ++++++++++------------ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 1 + include/linux/mlx5/fs.h | 9 +++- 11 files changed, 104 insertions(+), 78 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 9b16431..76ed57f 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1877,10 +1877,10 @@ static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev, { struct mlx5_flow_table *ft = ft_prio->flow_table; struct mlx5_ib_flow_handler *handler; + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_spec *spec; const void *ib_flow = (const void *)flow_attr + sizeof(*flow_attr); unsigned int spec_index; - u32 action; int err = 0; if (!is_valid_attr(flow_attr)) @@ -1905,12 +1905,12 @@ static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev, } spec->match_criteria_enable = get_match_criteria_enable(spec->match_criteria); - action = dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST : + flow_act.action = dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST : MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO; + flow_act.flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; handler->rule = mlx5_add_flow_rules(ft, spec, - action, - MLX5_FS_DEFAULT_FLOW_TAG, - dst, 1); + &flow_act, + dst, 1); if (IS_ERR(handler->rule)) { err = PTR_ERR(handler->rule); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index 677b238..68419a0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -174,6 +174,11 @@ static int arfs_add_default_rule(struct mlx5e_priv *priv, enum arfs_type type) { struct arfs_table *arfs_t = &priv->fs.arfs.arfs_tables[type]; + struct mlx5_flow_act flow_act = { + .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, + .encap_id = 0, + }; struct mlx5_flow_destination dest; struct mlx5e_tir *tir = priv->indir_tir; struct mlx5_flow_spec *spec; @@ -206,8 +211,7 @@ static int arfs_add_default_rule(struct mlx5e_priv *priv, } arfs_t->default_rule = mlx5_add_flow_rules(arfs_t->ft.t, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, + &flow_act, &dest, 1); if (IS_ERR(arfs_t->default_rule)) { err = PTR_ERR(arfs_t->default_rule); @@ -465,6 +469,11 @@ static struct arfs_table *arfs_get_table(struct mlx5e_arfs_tables *arfs, static struct mlx5_flow_handle *arfs_add_rule(struct mlx5e_priv *priv, struct arfs_rule *arfs_rule) { + struct mlx5_flow_act flow_act = { + .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, + .encap_id = 0, + }; struct mlx5e_arfs_tables *arfs = &priv->fs.arfs; struct arfs_tuple *tuple = &arfs_rule->tuple; struct mlx5_flow_handle *rule = NULL; @@ -544,9 +553,7 @@ static struct mlx5_flow_handle *arfs_add_rule(struct mlx5e_priv *priv, } dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; dest.tir_num = priv->direct_tir[arfs_rule->rxq].tirn; - rule = mlx5_add_flow_rules(ft, spec, MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - &dest, 1); + rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "%s: add rule(filter id=%d, rq idx=%d) failed, err=%d\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index 9617892..1fe80de 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -158,6 +158,11 @@ static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, enum mlx5e_vlan_rule_type rule_type, u16 vid, struct mlx5_flow_spec *spec) { + struct mlx5_flow_act flow_act = { + .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, + .encap_id = 0, + }; struct mlx5_flow_table *ft = priv->fs.vlan.ft.t; struct mlx5_flow_destination dest; struct mlx5_flow_handle **rule_p; @@ -187,10 +192,7 @@ static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, break; } - *rule_p = mlx5_add_flow_rules(ft, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - &dest, 1); + *rule_p = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); if (IS_ERR(*rule_p)) { err = PTR_ERR(*rule_p); @@ -623,6 +625,11 @@ mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, u16 etype, u8 proto) { + struct mlx5_flow_act flow_act = { + .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, + .encap_id = 0, + }; struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; int err = 0; @@ -644,10 +651,7 @@ mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, MLX5_SET(fte_match_param, spec->match_value, outer_headers.ethertype, etype); } - rule = mlx5_add_flow_rules(ft, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - dest, 1); + rule = mlx5_add_flow_rules(ft, spec, &flow_act, dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "%s: add rule failed\n", __func__); @@ -810,6 +814,11 @@ static void mlx5e_del_l2_flow_rule(struct mlx5e_priv *priv, static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, struct mlx5e_l2_rule *ai, int type) { + struct mlx5_flow_act flow_act = { + .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, + .encap_id = 0, + }; struct mlx5_flow_table *ft = priv->fs.l2.ft.t; struct mlx5_flow_destination dest; struct mlx5_flow_spec *spec; @@ -848,9 +857,7 @@ static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, break; } - ai->rule = mlx5_add_flow_rules(ft, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest, 1); + ai->rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); if (IS_ERR(ai->rule)) { netdev_err(priv->netdev, "%s: add l2 rule(mac:%pM) failed\n", __func__, mv_dmac); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c index 87bb3db..3691451 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c @@ -290,10 +290,10 @@ add_ethtool_flow_rule(struct mlx5e_priv *priv, struct ethtool_rx_flow_spec *fs) { struct mlx5_flow_destination *dst = NULL; + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_spec *spec; struct mlx5_flow_handle *rule; int err = 0; - u32 action; spec = mlx5_vzalloc(sizeof(*spec)); if (!spec) @@ -304,7 +304,7 @@ add_ethtool_flow_rule(struct mlx5e_priv *priv, goto free; if (fs->ring_cookie == RX_CLS_FLOW_DISC) { - action = MLX5_FLOW_CONTEXT_ACTION_DROP; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP; } else { dst = kzalloc(sizeof(*dst), GFP_KERNEL); if (!dst) { @@ -314,12 +314,12 @@ add_ethtool_flow_rule(struct mlx5e_priv *priv, dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR; dst->tir_num = priv->direct_tir[fs->ring_cookie].tirn; - action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; } spec->match_criteria_enable = (!outer_header_zero(spec->match_criteria)); - rule = mlx5_add_flow_rules(ft, spec, action, - MLX5_FS_DEFAULT_FLOW_TAG, dst, 1); + flow_act.flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; + rule = mlx5_add_flow_rules(ft, spec, &flow_act, dst, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "%s: failed to add ethtool steering rule: %d\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index cdd4303..35e38d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -61,6 +61,11 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, { struct mlx5_core_dev *dev = priv->mdev; struct mlx5_flow_destination dest = { 0 }; + struct mlx5_flow_act flow_act = { + .action = action, + .flow_tag = flow_tag, + .encap_id = 0, + }; struct mlx5_fc *counter = NULL; struct mlx5_flow_handle *rule; bool table_created = false; @@ -95,9 +100,7 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, } spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - rule = mlx5_add_flow_rules(priv->fs.tc.t, spec, - action, flow_tag, - &dest, 1); + rule = mlx5_add_flow_rules(priv->fs.tc.t, spec, &flow_act, &dest, 1); if (IS_ERR(rule)) goto err_add_rule; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 27f21ac..ae05d27 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -244,6 +244,7 @@ __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, int match_header = (is_zero_ether_addr(mac_c) ? 0 : MLX5_MATCH_OUTER_HEADERS); struct mlx5_flow_handle *flow_rule = NULL; + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_destination dest; struct mlx5_flow_spec *spec; void *mv_misc = NULL; @@ -285,10 +286,10 @@ __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n", dmac_v, dmac_c, vport); spec->match_criteria_enable = match_header; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; flow_rule = mlx5_add_flow_rules(esw->fdb_table.fdb, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest, 1); + &flow_act, &dest, 1); if (IS_ERR(flow_rule)) { esw_warn(esw->dev, "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n", @@ -1212,6 +1213,7 @@ static void esw_vport_disable_ingress_acl(struct mlx5_eswitch *esw, static int esw_vport_ingress_config(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_spec *spec; int err = 0; u8 *smac_v; @@ -1264,10 +1266,10 @@ static int esw_vport_ingress_config(struct mlx5_eswitch *esw, } spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW; vport->ingress.allow_rule = mlx5_add_flow_rules(vport->ingress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_ALLOW, - 0, NULL, 0); + &flow_act, NULL, 0); if (IS_ERR(vport->ingress.allow_rule)) { err = PTR_ERR(vport->ingress.allow_rule); esw_warn(esw->dev, @@ -1278,10 +1280,10 @@ static int esw_vport_ingress_config(struct mlx5_eswitch *esw, } memset(spec, 0, sizeof(*spec)); + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP; vport->ingress.drop_rule = mlx5_add_flow_rules(vport->ingress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_DROP, - 0, NULL, 0); + &flow_act, NULL, 0); if (IS_ERR(vport->ingress.drop_rule)) { err = PTR_ERR(vport->ingress.drop_rule); esw_warn(esw->dev, @@ -1301,6 +1303,7 @@ out: static int esw_vport_egress_config(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_spec *spec; int err = 0; @@ -1338,10 +1341,10 @@ static int esw_vport_egress_config(struct mlx5_eswitch *esw, MLX5_SET(fte_match_param, spec->match_value, outer_headers.first_vid, vport->info.vlan); spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW; vport->egress.allowed_vlan = mlx5_add_flow_rules(vport->egress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_ALLOW, - 0, NULL, 0); + &flow_act, NULL, 0); if (IS_ERR(vport->egress.allowed_vlan)) { err = PTR_ERR(vport->egress.allowed_vlan); esw_warn(esw->dev, @@ -1353,10 +1356,10 @@ static int esw_vport_egress_config(struct mlx5_eswitch *esw, /* Drop others rule (star rule) */ memset(spec, 0, sizeof(*spec)); + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP; vport->egress.drop_rule = mlx5_add_flow_rules(vport->egress.acl, spec, - MLX5_FLOW_CONTEXT_ACTION_DROP, - 0, NULL, 0); + &flow_act, NULL, 0); if (IS_ERR(vport->egress.drop_rule)) { err = PTR_ERR(vport->egress.drop_rule); esw_warn(esw->dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index b18f951..a390117 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -49,23 +49,23 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, struct mlx5_esw_flow_attr *attr) { struct mlx5_flow_destination dest[2] = {}; + struct mlx5_flow_act flow_act = {0}; struct mlx5_fc *counter = NULL; struct mlx5_flow_handle *rule; void *misc; - int action; int i = 0; if (esw->mode != SRIOV_OFFLOADS) return ERR_PTR(-EOPNOTSUPP); - action = attr->action; + flow_act.action = attr->action; - if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { + if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT; dest[i].vport_num = attr->out_rep->vport; i++; } - if (action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { + if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { counter = mlx5_fc_create(esw->dev, true); if (IS_ERR(counter)) return ERR_CAST(counter); @@ -84,7 +84,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, MLX5_MATCH_MISC_PARAMETERS; rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, - spec, action, 0, dest, i); + spec, &flow_act, dest, i); if (IS_ERR(rule)) mlx5_fc_destroy(esw->dev, counter); @@ -274,6 +274,7 @@ out: static struct mlx5_flow_handle * mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn) { + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_destination dest; struct mlx5_flow_handle *flow_rule; struct mlx5_flow_spec *spec; @@ -297,10 +298,10 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS; dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; dest.vport_num = vport; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest, 1); + &flow_act, &dest, 1); if (IS_ERR(flow_rule)) esw_warn(esw->dev, "FDB: Failed to add send to vport rule err %ld\n", PTR_ERR(flow_rule)); out: @@ -363,6 +364,7 @@ out_err: static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw) { + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_destination dest; struct mlx5_flow_handle *flow_rule = NULL; struct mlx5_flow_spec *spec; @@ -377,10 +379,10 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw) dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; dest.vport_num = 0; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest, 1); + &flow_act, &dest, 1); if (IS_ERR(flow_rule)) { err = PTR_ERR(flow_rule); esw_warn(esw->dev, "FDB: Failed to add miss flow rule err %d\n", err); @@ -591,6 +593,7 @@ static void esw_destroy_vport_rx_group(struct mlx5_eswitch *esw) struct mlx5_flow_handle * mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn) { + struct mlx5_flow_act flow_act = {0}; struct mlx5_flow_destination dest; struct mlx5_flow_handle *flow_rule; struct mlx5_flow_spec *spec; @@ -613,9 +616,9 @@ mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn) dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; dest.tir_num = tirn; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; flow_rule = mlx5_add_flow_rules(esw->offloads.ft_offloads, spec, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - 0, &dest, 1); + &flow_act, &dest, 1); if (IS_ERR(flow_rule)) { esw_warn(esw->dev, "fs offloads: Failed to add vport rx rule err %ld\n", PTR_ERR(flow_rule)); goto out; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index cc97bb2..c4478ec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -248,6 +248,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, MLX5_SET(flow_context, in_flow_context, group_id, group_id); MLX5_SET(flow_context, in_flow_context, flow_tag, fte->flow_tag); MLX5_SET(flow_context, in_flow_context, action, fte->action); + MLX5_SET(flow_context, in_flow_context, encap_id, fte->encap_id); in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, match_value); memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 4d28c8d..9adc766 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -460,8 +460,7 @@ static void del_flow_group(struct fs_node *node) fg->id, ft->id); } -static struct fs_fte *alloc_fte(u8 action, - u32 flow_tag, +static struct fs_fte *alloc_fte(struct mlx5_flow_act *flow_act, u32 *match_value, unsigned int index) { @@ -473,9 +472,10 @@ static struct fs_fte *alloc_fte(u8 action, memcpy(fte->val, match_value, sizeof(fte->val)); fte->node.type = FS_TYPE_FLOW_ENTRY; - fte->flow_tag = flow_tag; + fte->flow_tag = flow_act->flow_tag; fte->index = index; - fte->action = action; + fte->action = flow_act->action; + fte->encap_id = flow_act->encap_id; return fte; } @@ -1117,15 +1117,14 @@ static unsigned int get_free_fte_index(struct mlx5_flow_group *fg, /* prev is output, prev->next = new_fte */ static struct fs_fte *create_fte(struct mlx5_flow_group *fg, u32 *match_value, - u8 action, - u32 flow_tag, + struct mlx5_flow_act *flow_act, struct list_head **prev) { struct fs_fte *fte; int index; index = get_free_fte_index(fg, prev); - fte = alloc_fte(action, flow_tag, match_value, index); + fte = alloc_fte(flow_act, match_value, index); if (IS_ERR(fte)) return fte; @@ -1219,8 +1218,7 @@ static struct mlx5_flow_rule *find_flow_rule(struct fs_fte *fte, static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, u32 *match_value, - u8 action, - u32 flow_tag, + struct mlx5_flow_act *flow_act, struct mlx5_flow_destination *dest, int dest_num) { @@ -1234,12 +1232,13 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, fs_for_each_fte(fte, fg) { nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); if (compare_match_value(&fg->mask, match_value, &fte->val) && - (action & fte->action) && flow_tag == fte->flow_tag) { + (flow_act->action & fte->action) && + flow_act->flow_tag == fte->flow_tag) { int old_action = fte->action; - fte->action |= action; + fte->action |= flow_act->action; handle = add_rule_fte(fte, fg, dest, dest_num, - old_action != action); + old_action != flow_act->action); if (IS_ERR(handle)) { fte->action = old_action; goto unlock_fte; @@ -1255,7 +1254,7 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, goto unlock_fg; } - fte = create_fte(fg, match_value, action, flow_tag, &prev); + fte = create_fte(fg, match_value, flow_act, &prev); if (IS_ERR(fte)) { handle = (void *)fte; goto unlock_fg; @@ -1332,17 +1331,17 @@ static bool dest_is_valid(struct mlx5_flow_destination *dest, static struct mlx5_flow_handle * _mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_spec *spec, - u32 action, - u32 flow_tag, + struct mlx5_flow_act *flow_act, struct mlx5_flow_destination *dest, int dest_num) + { struct mlx5_flow_group *g; struct mlx5_flow_handle *rule; int i; for (i = 0; i < dest_num; i++) { - if (!dest_is_valid(&dest[i], action, ft)) + if (!dest_is_valid(&dest[i], flow_act->action, ft)) return ERR_PTR(-EINVAL); } @@ -1353,7 +1352,7 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, g->mask.match_criteria, spec->match_criteria)) { rule = add_rule_fg(g, spec->match_value, - action, flow_tag, dest, dest_num); + flow_act, dest, dest_num); if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) goto unlock; } @@ -1365,8 +1364,7 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, goto unlock; } - rule = add_rule_fg(g, spec->match_value, - action, flow_tag, dest, dest_num); + rule = add_rule_fg(g, spec->match_value, flow_act, dest, dest_num); if (IS_ERR(rule)) { /* Remove assumes refcount > 0 and autogroup creates a group * with a refcount = 0. @@ -1390,8 +1388,7 @@ static bool fwd_next_prio_supported(struct mlx5_flow_table *ft) struct mlx5_flow_handle * mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_spec *spec, - u32 action, - u32 flow_tag, + struct mlx5_flow_act *flow_act, struct mlx5_flow_destination *dest, int dest_num) { @@ -1399,11 +1396,11 @@ mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_destination gen_dest; struct mlx5_flow_table *next_ft = NULL; struct mlx5_flow_handle *handle = NULL; - u32 sw_action = action; + u32 sw_action = flow_act->action; struct fs_prio *prio; fs_get_obj(prio, ft->node.parent); - if (action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { + if (flow_act->action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { if (!fwd_next_prio_supported(ft)) return ERR_PTR(-EOPNOTSUPP); if (dest) @@ -1415,15 +1412,14 @@ mlx5_add_flow_rules(struct mlx5_flow_table *ft, gen_dest.ft = next_ft; dest = &gen_dest; dest_num = 1; - action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + flow_act->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; } else { mutex_unlock(&root->chain_lock); return ERR_PTR(-EOPNOTSUPP); } } - handle = _mlx5_add_flow_rules(ft, spec, action, flow_tag, dest, - dest_num); + handle = _mlx5_add_flow_rules(ft, spec, flow_act, dest, dest_num); if (sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { if (!IS_ERR_OR_NULL(handle) && diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 9f616ed..8e668c6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -151,6 +151,7 @@ struct fs_fte { u32 flow_tag; u32 index; u32 action; + u32 encap_id; enum fs_fte_status status; struct mlx5_fc *counter; }; diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index ab1a5fd..949b24b 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -130,14 +130,19 @@ struct mlx5_flow_group * mlx5_create_flow_group(struct mlx5_flow_table *ft, u32 *in); void mlx5_destroy_flow_group(struct mlx5_flow_group *fg); +struct mlx5_flow_act { + u32 action; + u32 flow_tag; + u32 encap_id; +}; + /* Single destination per rule. * Group ID is implied by the match criteria. */ struct mlx5_flow_handle * mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_spec *spec, - u32 action, - u32 flow_tag, + struct mlx5_flow_act *flow_act, struct mlx5_flow_destination *dest, int dest_num); void mlx5_del_flow_rules(struct mlx5_flow_handle *fr); -- cgit v1.1 From bbd00f7e2349e53345d0a151c04c8cb7e10bf2e7 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:46 +0200 Subject: net/mlx5e: Add TC tunnel release action for SRIOV offloads Enhance the parsing of offloaded TC rules to set HW matching on outer (encapsulation) headers. Parse TC tunnel release action and set it as mlx5 decap action when the required capabilities are supported. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 156 ++++++++++++++++++++- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 9 +- 2 files changed, 163 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 35e38d1..8946653 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -40,9 +40,11 @@ #include #include #include +#include #include "en.h" #include "en_tc.h" #include "eswitch.h" +#include "vxlan.h" struct mlx5e_tc_flow { struct rhash_head node; @@ -155,6 +157,121 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, } } +static void parse_vxlan_attr(struct mlx5_flow_spec *spec, + struct tc_cls_flower_offload *f) +{ + void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, + outer_headers); + void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, + outer_headers); + void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, + misc_parameters); + void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, + misc_parameters); + + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP); + + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) { + struct flow_dissector_key_keyid *key = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_ENC_KEYID, + f->key); + struct flow_dissector_key_keyid *mask = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_ENC_KEYID, + f->mask); + MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni, + be32_to_cpu(mask->keyid)); + MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni, + be32_to_cpu(key->keyid)); + } +} + +static int parse_tunnel_attr(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + struct tc_cls_flower_offload *f) +{ + void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, + outer_headers); + void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, + outer_headers); + + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) { + struct flow_dissector_key_ports *key = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_ENC_PORTS, + f->key); + struct flow_dissector_key_ports *mask = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_ENC_PORTS, + f->mask); + + /* Full udp dst port must be given */ + if (memchr_inv(&mask->dst, 0xff, sizeof(mask->dst))) + return -EOPNOTSUPP; + + /* udp src port isn't supported */ + if (memchr_inv(&mask->src, 0, sizeof(mask->src))) + return -EOPNOTSUPP; + + if (mlx5e_vxlan_lookup_port(priv, be16_to_cpu(key->dst)) && + MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap)) + parse_vxlan_attr(spec, f); + else + return -EOPNOTSUPP; + + MLX5_SET(fte_match_set_lyr_2_4, headers_c, + udp_dport, ntohs(mask->dst)); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, + udp_dport, ntohs(key->dst)); + + } else { /* udp dst port must be given */ + return -EOPNOTSUPP; + } + + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) { + struct flow_dissector_key_ipv4_addrs *key = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, + f->key); + struct flow_dissector_key_ipv4_addrs *mask = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, + f->mask); + MLX5_SET(fte_match_set_lyr_2_4, headers_c, + src_ipv4_src_ipv6.ipv4_layout.ipv4, + ntohl(mask->src)); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, + src_ipv4_src_ipv6.ipv4_layout.ipv4, + ntohl(key->src)); + + MLX5_SET(fte_match_set_lyr_2_4, headers_c, + dst_ipv4_dst_ipv6.ipv4_layout.ipv4, + ntohl(mask->dst)); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, + dst_ipv4_dst_ipv6.ipv4_layout.ipv4, + ntohl(key->dst)); + } + + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IP); + + /* Enforce DMAC when offloading incoming tunneled flows. + * Flow counters require a match on the DMAC. + */ + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_47_16); + MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_15_0); + ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, + dmac_47_16), priv->netdev->dev_addr); + + /* let software handle IP fragments */ + MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag, 0); + + return 0; +} + static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec, struct tc_cls_flower_offload *f) { @@ -172,12 +289,44 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec BIT(FLOW_DISSECTOR_KEY_VLAN) | BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | - BIT(FLOW_DISSECTOR_KEY_PORTS))) { + BIT(FLOW_DISSECTOR_KEY_PORTS) | + BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) | + BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_ENC_PORTS) | + BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL))) { netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n", f->dissector->used_keys); return -EOPNOTSUPP; } + if ((dissector_uses_key(f->dissector, + FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) || + dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID) || + dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) && + dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_CONTROL)) { + struct flow_dissector_key_control *key = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_ENC_CONTROL, + f->key); + switch (key->addr_type) { + case FLOW_DISSECTOR_KEY_IPV4_ADDRS: + if (parse_tunnel_attr(priv, spec, f)) + return -EOPNOTSUPP; + break; + default: + return -EOPNOTSUPP; + } + + /* In decap flow, header pointers should point to the inner + * headers, outer header were already set by parse_tunnel_attr + */ + headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, + inner_headers); + headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, + inner_headers); + } + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) { struct flow_dissector_key_control *key = skb_flow_dissector_target(f->dissector, @@ -442,6 +591,11 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, continue; } + if (is_tcf_tunnel_release(a)) { + attr->action |= MLX5_FLOW_CONTEXT_ACTION_DECAP; + continue; + } + return -EINVAL; } return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index a390117..c2dc470 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -82,6 +82,8 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS; + if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) + spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS; rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, spec, &flow_act, dest, i); @@ -409,6 +411,7 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) u32 *flow_group_in; void *match_criteria; int table_size, ix, err = 0; + u32 flags = 0; flow_group_in = mlx5_vzalloc(inlen); if (!flow_group_in) @@ -423,10 +426,14 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) esw_debug(dev, "Create offloads FDB table, log_max_size(%d)\n", MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); + if (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, encap) && + MLX5_CAP_ESW_FLOWTABLE_FDB(dev, decap)) + flags |= MLX5_FLOW_TABLE_TUNNEL_EN; + fdb = mlx5_create_auto_grouped_flow_table(root_ns, FDB_FAST_PATH, ESW_OFFLOADS_NUM_ENTRIES, ESW_OFFLOADS_NUM_GROUPS, 0, - 0); + flags); if (IS_ERR(fdb)) { err = PTR_ERR(fdb); esw_warn(dev, "Failed to create Fast path FDB Table err %d\n", err); -- cgit v1.1 From 4a25730eb202893cd79b885df8f6fa1e8174742f Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:47 +0200 Subject: net/mlx5e: Add ndo_udp_tunnel_add to VF representors By implementing this ndo, the host stack will set the vxlan udp port also to VF representor netdevices. This will allow the TC offload code in the driver when it gets a tunnel key set action to identify the UDP port as vxlan, and hence the rule will be a candidate for offloading. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 4 ++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 8 ++++---- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index ea8af47..ac09767 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -893,5 +893,9 @@ void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev); struct rtnl_link_stats64 * mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats); u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout); +void mlx5e_add_vxlan_port(struct net_device *netdev, + struct udp_tunnel_info *ti); +void mlx5e_del_vxlan_port(struct net_device *netdev, + struct udp_tunnel_info *ti); #endif /* __MLX5_EN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index ba0c774..313b765 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2995,8 +2995,8 @@ static int mlx5e_get_vf_stats(struct net_device *dev, vf_stats); } -static void mlx5e_add_vxlan_port(struct net_device *netdev, - struct udp_tunnel_info *ti) +void mlx5e_add_vxlan_port(struct net_device *netdev, + struct udp_tunnel_info *ti) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -3009,8 +3009,8 @@ static void mlx5e_add_vxlan_port(struct net_device *netdev, mlx5e_vxlan_queue_work(priv, ti->sa_family, be16_to_cpu(ti->port), 1); } -static void mlx5e_del_vxlan_port(struct net_device *netdev, - struct udp_tunnel_info *ti) +void mlx5e_del_vxlan_port(struct net_device *netdev, + struct udp_tunnel_info *ti) { struct mlx5e_priv *priv = netdev_priv(netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index c1a7b05..47dfd5b1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -256,6 +256,8 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = { .ndo_get_phys_port_name = mlx5e_rep_get_phys_port_name, .ndo_setup_tc = mlx5e_rep_ndo_setup_tc, .ndo_get_stats64 = mlx5e_get_stats, + .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, + .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, }; static void mlx5e_build_rep_netdev_priv(struct mlx5_core_dev *mdev, -- cgit v1.1 From a54e20b4fcae8730f51b1920ff190e2a6f1fcb04 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 7 Nov 2016 15:14:48 +0200 Subject: net/mlx5e: Add basic TC tunnel set action for SRIOV offloads In mlx5 HW, encapsulation is offloaded by the steering rule having index into an encapsulation table containing the entire set of headers to be added by the HW. The driver sets these headers in a buffer when we are offloading the action. The code maintains mlx5_encap_entry for each encap header it has encountered when attempted to offload TC tunnel set action. This entry maintains a linked list of all the flows sharing the same encap header, when the last flow is removed from the list the encap entry is removed. The actual encap_header is allocated by the driver in the hardware only if we have layer two neighbour info when the encap entry is created. While the flow is in the driver, the driver holds a reference on the neighbour. When a new flow with encap action is inserted, the code first checks if the required encap entry exists according to the tunnel set parameters. If it does the encap is shared, otherwise a new mlx5_encap_entry is created. TC action parsing implementation in the driver assumes that tunnel set action is provided in the same order set by the user, e.g before the mirred_redirect action. Signed-off-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 295 ++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 20 ++ .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 3 + 4 files changed, 312 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 8946653..9d133fc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "en.h" #include "en_tc.h" #include "eswitch.h" @@ -50,9 +51,15 @@ struct mlx5e_tc_flow { struct rhash_head node; u64 cookie; struct mlx5_flow_handle *rule; + struct list_head encap; /* flows sharing the same encap */ struct mlx5_esw_flow_attr *attr; }; +enum { + MLX5_HEADER_TYPE_VXLAN = 0x0, + MLX5_HEADER_TYPE_NVGRE = 0x1, +}; + #define MLX5E_TC_TABLE_NUM_ENTRIES 1024 #define MLX5E_TC_TABLE_NUM_GROUPS 4 @@ -538,11 +545,243 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return 0; } +static inline int cmp_encap_info(struct mlx5_encap_info *a, + struct mlx5_encap_info *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +static inline int hash_encap_info(struct mlx5_encap_info *info) +{ + return jhash(info, sizeof(*info), 0); +} + +static int mlx5e_route_lookup_ipv4(struct mlx5e_priv *priv, + struct net_device *mirred_dev, + struct net_device **out_dev, + struct flowi4 *fl4, + struct neighbour **out_n, + __be32 *saddr, + int *out_ttl) +{ + struct rtable *rt; + struct neighbour *n = NULL; + int ttl; + +#if IS_ENABLED(CONFIG_INET) + rt = ip_route_output_key(dev_net(mirred_dev), fl4); + if (IS_ERR(rt)) { + pr_warn("%s: no route to %pI4\n", __func__, &fl4->daddr); + return -EOPNOTSUPP; + } +#else + return -EOPNOTSUPP; +#endif + + if (!switchdev_port_same_parent_id(priv->netdev, rt->dst.dev)) { + pr_warn("%s: Can't offload the flow, netdevices aren't on the same HW e-switch\n", + __func__); + ip_rt_put(rt); + return -EOPNOTSUPP; + } + + ttl = ip4_dst_hoplimit(&rt->dst); + n = dst_neigh_lookup(&rt->dst, &fl4->daddr); + ip_rt_put(rt); + if (!n) + return -ENOMEM; + + *out_n = n; + *saddr = fl4->saddr; + *out_ttl = ttl; + *out_dev = rt->dst.dev; + + return 0; +} + +static int gen_vxlan_header_ipv4(struct net_device *out_dev, + char buf[], + unsigned char h_dest[ETH_ALEN], + int ttl, + __be32 daddr, + __be32 saddr, + __be16 udp_dst_port, + __be32 vx_vni) +{ + int encap_size = VXLAN_HLEN + sizeof(struct iphdr) + ETH_HLEN; + struct ethhdr *eth = (struct ethhdr *)buf; + struct iphdr *ip = (struct iphdr *)((char *)eth + sizeof(struct ethhdr)); + struct udphdr *udp = (struct udphdr *)((char *)ip + sizeof(struct iphdr)); + struct vxlanhdr *vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr)); + + memset(buf, 0, encap_size); + + ether_addr_copy(eth->h_dest, h_dest); + ether_addr_copy(eth->h_source, out_dev->dev_addr); + eth->h_proto = htons(ETH_P_IP); + + ip->daddr = daddr; + ip->saddr = saddr; + + ip->ttl = ttl; + ip->protocol = IPPROTO_UDP; + ip->version = 0x4; + ip->ihl = 0x5; + + udp->dest = udp_dst_port; + vxh->vx_flags = VXLAN_HF_VNI; + vxh->vx_vni = vxlan_vni_field(vx_vni); + + return encap_size; +} + +static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, + struct net_device *mirred_dev, + struct mlx5_encap_entry *e, + struct net_device **out_dev) +{ + int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); + struct flowi4 fl4 = {}; + struct neighbour *n; + char *encap_header; + int encap_size; + __be32 saddr; + int ttl; + int err; + + encap_header = kzalloc(max_encap_size, GFP_KERNEL); + if (!encap_header) + return -ENOMEM; + + switch (e->tunnel_type) { + case MLX5_HEADER_TYPE_VXLAN: + fl4.flowi4_proto = IPPROTO_UDP; + fl4.fl4_dport = e->tun_info.tp_dst; + break; + default: + err = -EOPNOTSUPP; + goto out; + } + fl4.daddr = e->tun_info.daddr; + + err = mlx5e_route_lookup_ipv4(priv, mirred_dev, out_dev, + &fl4, &n, &saddr, &ttl); + if (err) + goto out; + + e->n = n; + e->out_dev = *out_dev; + + if (!(n->nud_state & NUD_VALID)) { + err = -ENOTSUPP; + goto out; + } + + neigh_ha_snapshot(e->h_dest, n, *out_dev); + + switch (e->tunnel_type) { + case MLX5_HEADER_TYPE_VXLAN: + encap_size = gen_vxlan_header_ipv4(*out_dev, encap_header, + e->h_dest, ttl, + e->tun_info.daddr, + saddr, e->tun_info.tp_dst, + e->tun_info.tun_id); + break; + default: + err = -EOPNOTSUPP; + goto out; + } + + err = mlx5_encap_alloc(priv->mdev, e->tunnel_type, + encap_size, encap_header, &e->encap_id); +out: + kfree(encap_header); + return err; +} + +static int mlx5e_attach_encap(struct mlx5e_priv *priv, + struct ip_tunnel_info *tun_info, + struct net_device *mirred_dev, + struct mlx5_esw_flow_attr *attr) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + unsigned short family = ip_tunnel_info_af(tun_info); + struct ip_tunnel_key *key = &tun_info->key; + struct mlx5_encap_info info; + struct mlx5_encap_entry *e; + struct net_device *out_dev; + uintptr_t hash_key; + bool found = false; + int tunnel_type; + int err; + + /* udp dst port must be given */ + if (!memchr_inv(&key->tp_dst, 0, sizeof(key->tp_dst))) + return -EOPNOTSUPP; + + if (mlx5e_vxlan_lookup_port(priv, be16_to_cpu(key->tp_dst)) && + MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap)) { + info.tp_dst = key->tp_dst; + info.tun_id = tunnel_id_to_key32(key->tun_id); + tunnel_type = MLX5_HEADER_TYPE_VXLAN; + } else { + return -EOPNOTSUPP; + } + + switch (family) { + case AF_INET: + info.daddr = key->u.ipv4.dst; + break; + default: + return -EOPNOTSUPP; + } + + hash_key = hash_encap_info(&info); + + hash_for_each_possible_rcu(esw->offloads.encap_tbl, e, + encap_hlist, hash_key) { + if (!cmp_encap_info(&e->tun_info, &info)) { + found = true; + break; + } + } + + if (found) { + attr->encap = e; + return 0; + } + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) + return -ENOMEM; + + e->tun_info = info; + e->tunnel_type = tunnel_type; + INIT_LIST_HEAD(&e->flows); + + err = mlx5e_create_encap_header_ipv4(priv, mirred_dev, e, &out_dev); + if (err) + goto out_err; + + attr->encap = e; + hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key); + + return err; + +out_err: + kfree(e); + return err; +} + static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, - struct mlx5_esw_flow_attr *attr) + struct mlx5e_tc_flow *flow) { + struct mlx5_esw_flow_attr *attr = flow->attr; + struct ip_tunnel_info *info = NULL; const struct tc_action *a; LIST_HEAD(actions); + bool encap = false; + int err; if (tc_no_actions(exts)) return -EINVAL; @@ -565,16 +804,37 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, out_dev = __dev_get_by_index(dev_net(priv->netdev), ifindex); - if (!switchdev_port_same_parent_id(priv->netdev, out_dev)) { + if (switchdev_port_same_parent_id(priv->netdev, + out_dev)) { + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_COUNT; + out_priv = netdev_priv(out_dev); + attr->out_rep = out_priv->ppriv; + } else if (encap) { + err = mlx5e_attach_encap(priv, info, + out_dev, attr); + if (err) + return err; + list_add(&flow->encap, &attr->encap->flows); + attr->action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP | + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_COUNT; + out_priv = netdev_priv(attr->encap->out_dev); + attr->out_rep = out_priv->ppriv; + } else { pr_err("devices %s %s not on same switch HW, can't offload forwarding\n", priv->netdev->name, out_dev->name); return -EINVAL; } + continue; + } - attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_COUNT; - out_priv = netdev_priv(out_dev); - attr->out_rep = out_priv->ppriv; + if (is_tcf_tunnel_set(a)) { + info = tcf_tunnel_info(a); + if (info) + encap = true; + else + return -EOPNOTSUPP; continue; } @@ -644,7 +904,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, if (fdb_flow) { flow->attr = (struct mlx5_esw_flow_attr *)(flow + 1); - err = parse_tc_fdb_actions(priv, f->exts, flow->attr); + err = parse_tc_fdb_actions(priv, f->exts, flow); if (err < 0) goto err_free; flow->rule = mlx5e_tc_add_fdb_flow(priv, spec, flow->attr); @@ -681,6 +941,24 @@ out: return err; } +static void mlx5e_detach_encap(struct mlx5e_priv *priv, + struct mlx5e_tc_flow *flow) { + struct list_head *next = flow->encap.next; + + list_del(&flow->encap); + if (list_empty(next)) { + struct mlx5_encap_entry *e; + + e = list_entry(next, struct mlx5_encap_entry, flows); + if (e->n) { + mlx5_encap_dealloc(priv->mdev, e->encap_id); + neigh_release(e->n); + } + hlist_del_rcu(&e->encap_hlist); + kfree(e); + } +} + int mlx5e_delete_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f) { @@ -696,6 +974,9 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv, mlx5e_tc_del_flow(priv, flow->rule, flow->attr); + if (flow->attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) + mlx5e_detach_encap(priv, flow); + kfree(flow); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index ae05d27..9734ac8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1782,6 +1782,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) goto abort; } + hash_init(esw->offloads.encap_tbl); mutex_init(&esw->state_lock); for (vport_num = 0; vport_num < total_vports; vport_num++) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 6d414cb..40482e8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -199,6 +199,7 @@ struct mlx5_esw_offload { struct mlx5_flow_table *ft_offloads; struct mlx5_flow_group *vport_rx_group; struct mlx5_eswitch_rep *vport_reps; + DECLARE_HASHTABLE(encap_tbl, 8); }; struct mlx5_eswitch { @@ -272,6 +273,24 @@ enum { #define MLX5_FLOW_CONTEXT_ACTION_VLAN_POP 0x40 #define MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH 0x80 +struct mlx5_encap_info { + __be32 daddr; + __be32 tun_id; + __be16 tp_dst; +}; + +struct mlx5_encap_entry { + struct hlist_node encap_hlist; + struct list_head flows; + u32 encap_id; + struct neighbour *n; + struct mlx5_encap_info tun_info; + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + + struct net_device *out_dev; + int tunnel_type; +}; + struct mlx5_esw_flow_attr { struct mlx5_eswitch_rep *in_rep; struct mlx5_eswitch_rep *out_rep; @@ -279,6 +298,7 @@ struct mlx5_esw_flow_attr { int action; u16 vlan; bool vlan_handled; + struct mlx5_encap_entry *encap; }; int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index c2dc470..50fe8e8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -85,6 +85,9 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS; + if (attr->encap) + flow_act.encap_id = attr->encap->encap_id; + rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, spec, &flow_act, dest, i); if (IS_ERR(rule)) -- cgit v1.1 From a33a4c73810589f80b8a37477e1b28b4c1d61913 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 3 Nov 2016 22:12:27 +0000 Subject: sfc: enable 4-tuple RSS hashing for UDP This improves UDP spreading, and also slightly improves GRO performance of encapsulated TCP on 7000 series NICs. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 00279da..9f6d769 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2245,6 +2245,84 @@ static void efx_ef10_tx_write(struct efx_tx_queue *tx_queue) } } +#define RSS_MODE_HASH_ADDRS (1 << RSS_MODE_HASH_SRC_ADDR_LBN |\ + 1 << RSS_MODE_HASH_DST_ADDR_LBN) +#define RSS_MODE_HASH_PORTS (1 << RSS_MODE_HASH_SRC_PORT_LBN |\ + 1 << RSS_MODE_HASH_DST_PORT_LBN) +#define RSS_CONTEXT_FLAGS_DEFAULT (1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_IPV4_EN_LBN |\ + 1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_TCPV4_EN_LBN |\ + 1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_IPV6_EN_LBN |\ + 1 << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TOEPLITZ_TCPV6_EN_LBN |\ + (RSS_MODE_HASH_ADDRS | RSS_MODE_HASH_PORTS) << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TCP_IPV4_RSS_MODE_LBN |\ + RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV4_RSS_MODE_LBN |\ + RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_OTHER_IPV4_RSS_MODE_LBN |\ + (RSS_MODE_HASH_ADDRS | RSS_MODE_HASH_PORTS) << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_TCP_IPV6_RSS_MODE_LBN |\ + RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV6_RSS_MODE_LBN |\ + RSS_MODE_HASH_ADDRS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_OTHER_IPV6_RSS_MODE_LBN) + +static int efx_ef10_get_rss_flags(struct efx_nic *efx, u32 context, u32 *flags) +{ + /* Firmware had a bug (sfc bug 61952) where it would not actually + * fill in the flags field in the response to MC_CMD_RSS_CONTEXT_GET_FLAGS. + * This meant that it would always contain whatever was previously + * in the MCDI buffer. Fortunately, all firmware versions with + * this bug have the same default flags value for a newly-allocated + * RSS context, and the only time we want to get the flags is just + * after allocating. Moreover, the response has a 32-bit hole + * where the context ID would be in the request, so we can use an + * overlength buffer in the request and pre-fill the flags field + * with what we believe the default to be. Thus if the firmware + * has the bug, it will leave our pre-filled value in the flags + * field of the response, and we will get the right answer. + * + * However, this does mean that this function should NOT be used if + * the RSS context flags might not be their defaults - it is ONLY + * reliably correct for a newly-allocated RSS context. + */ + MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_LEN); + size_t outlen; + int rc; + + /* Check we have a hole for the context ID */ + BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_GET_FLAGS_IN_LEN != MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_FLAGS_OFST); + MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_FLAGS_IN_RSS_CONTEXT_ID, context); + MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_FLAGS_OUT_FLAGS, + RSS_CONTEXT_FLAGS_DEFAULT); + rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_GET_FLAGS, inbuf, + sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); + if (rc == 0) { + if (outlen < MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_LEN) + rc = -EIO; + else + *flags = MCDI_DWORD(outbuf, RSS_CONTEXT_GET_FLAGS_OUT_FLAGS); + } + return rc; +} + +/* Attempt to enable 4-tuple UDP hashing on the specified RSS context. + * If we fail, we just leave the RSS context at its default hash settings, + * which is safe but may slightly reduce performance. + * Defaults are 4-tuple for TCP and 2-tuple for UDP and other-IP, so we + * just need to set the UDP ports flags (for both IP versions). + */ +static void efx_ef10_set_rss_flags(struct efx_nic *efx, u32 context) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_SET_FLAGS_IN_LEN); + u32 flags; + + BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_SET_FLAGS_OUT_LEN != 0); + + if (efx_ef10_get_rss_flags(efx, context, &flags) != 0) + return; + MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_RSS_CONTEXT_ID, context); + flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV4_RSS_MODE_LBN; + flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV6_RSS_MODE_LBN; + MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_FLAGS, flags); + efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_FLAGS, inbuf, sizeof(inbuf), + NULL, 0, NULL); +} + static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context, bool exclusive, unsigned *context_size) { @@ -2290,6 +2368,10 @@ static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context, if (context_size) *context_size = rss_spread; + if (nic_data->datapath_caps & + 1 << MC_CMD_GET_CAPABILITIES_OUT_ADDITIONAL_RSS_MODES_LBN) + efx_ef10_set_rss_flags(efx, *context); + return 0; } -- cgit v1.1 From b718c88a62c9adf24d8d1a06902f06f0149efac7 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 3 Nov 2016 22:12:58 +0000 Subject: sfc: report 4-tuple UDP hashing to ethtool, if it's enabled Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 6 ++++-- drivers/net/ethernet/sfc/ethtool.c | 12 ++++++++---- drivers/net/ethernet/sfc/net_driver.h | 2 ++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 9f6d769..e61807e 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2319,8 +2319,10 @@ static void efx_ef10_set_rss_flags(struct efx_nic *efx, u32 context) flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV4_RSS_MODE_LBN; flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV6_RSS_MODE_LBN; MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_FLAGS, flags); - efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_FLAGS, inbuf, sizeof(inbuf), - NULL, 0, NULL); + if (!efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_FLAGS, inbuf, sizeof(inbuf), + NULL, 0, NULL)) + /* Succeeded, so UDP 4-tuple is now enabled */ + efx->rx_hash_udp_4tuple = true; } static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context, diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 445ccdb..bf126f9 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -968,20 +968,24 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, info->data = 0; switch (info->flow_type) { + case UDP_V4_FLOW: + if (efx->rx_hash_udp_4tuple) + /* fall through */ case TCP_V4_FLOW: - info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; /* fall through */ - case UDP_V4_FLOW: case SCTP_V4_FLOW: case AH_ESP_V4_FLOW: case IPV4_FLOW: info->data |= RXH_IP_SRC | RXH_IP_DST; min_revision = EFX_REV_FALCON_B0; break; + case UDP_V6_FLOW: + if (efx->rx_hash_udp_4tuple) + /* fall through */ case TCP_V6_FLOW: - info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; /* fall through */ - case UDP_V6_FLOW: case SCTP_V6_FLOW: case AH_ESP_V6_FLOW: case IPV6_FLOW: diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 99d8c82..fec51c4 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -853,6 +853,7 @@ struct vfdi_status; * @rx_hash_key: Toeplitz hash key for RSS * @rx_indir_table: Indirection table for RSS * @rx_scatter: Scatter mode enabled for receives + * @rx_hash_udp_4tuple: UDP 4-tuple hashing enabled * @int_error_count: Number of internal errors seen recently * @int_error_expire: Time at which error count will be expired * @irq_soft_enabled: Are IRQs soft-enabled? If not, IRQ handler will @@ -990,6 +991,7 @@ struct efx_nic { u8 rx_hash_key[40]; u32 rx_indir_table[128]; bool rx_scatter; + bool rx_hash_udp_4tuple; unsigned int_error_count; unsigned long int_error_expire; -- cgit v1.1 From f86dab3aa6fef724456ba7e3fae3e2f02414ae86 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Nov 2016 17:52:19 +0100 Subject: netfilter: nft_hash: get random bytes if seed is not specified If the user doesn't specify a seed, generate one at configuration time. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_hash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index baf694d..97ad8e3 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -57,7 +57,6 @@ static int nft_hash_init(const struct nft_ctx *ctx, if (!tb[NFTA_HASH_SREG] || !tb[NFTA_HASH_DREG] || !tb[NFTA_HASH_LEN] || - !tb[NFTA_HASH_SEED] || !tb[NFTA_HASH_MODULUS]) return -EINVAL; @@ -80,7 +79,10 @@ static int nft_hash_init(const struct nft_ctx *ctx, if (priv->offset + priv->modulus - 1 < priv->offset) return -EOVERFLOW; - priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED])); + if (tb[NFTA_HASH_SEED]) + priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED])); + else + get_random_bytes(&priv->seed, sizeof(priv->seed)); return nft_validate_register_load(priv->sreg, len) && nft_validate_register_store(ctx, priv->dreg, NULL, -- cgit v1.1 From 4e24877e61e8507c0843e4bddbc6ecccbfd2e87d Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sun, 6 Nov 2016 21:15:51 +0800 Subject: netfilter: nf_tables: simplify the basic expressions' init routine Some basic expressions are built into nf_tables.ko, such as nft_cmp, nft_lookup, nft_range and so on. But these basic expressions' init routine is a little ugly, too many goto errX labels, and we forget to call nft_range_module_exit in the exit routine, although it is harmless. Acctually, the init and exit routines of these basic expressions are same, i.e. do nft_register_expr in the init routine and do nft_unregister_expr in the exit routine. So it's better to arrange them into an array and deal with them together. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables_core.h | 33 ++++---------- net/netfilter/nf_tables_core.c | 80 +++++++++++----------------------- net/netfilter/nft_bitwise.c | 13 +----- net/netfilter/nft_byteorder.c | 13 +----- net/netfilter/nft_cmp.c | 13 +----- net/netfilter/nft_dynset.c | 13 +----- net/netfilter/nft_immediate.c | 13 +----- net/netfilter/nft_lookup.c | 13 +----- net/netfilter/nft_payload.c | 13 +----- net/netfilter/nft_range.c | 13 +----- 10 files changed, 43 insertions(+), 174 deletions(-) diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 00f4f6b..862373d 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -1,12 +1,18 @@ #ifndef _NET_NF_TABLES_CORE_H #define _NET_NF_TABLES_CORE_H +extern struct nft_expr_type nft_imm_type; +extern struct nft_expr_type nft_cmp_type; +extern struct nft_expr_type nft_lookup_type; +extern struct nft_expr_type nft_bitwise_type; +extern struct nft_expr_type nft_byteorder_type; +extern struct nft_expr_type nft_payload_type; +extern struct nft_expr_type nft_dynset_type; +extern struct nft_expr_type nft_range_type; + int nf_tables_core_module_init(void); void nf_tables_core_module_exit(void); -int nft_immediate_module_init(void); -void nft_immediate_module_exit(void); - struct nft_cmp_fast_expr { u32 data; enum nft_registers sreg:8; @@ -25,24 +31,6 @@ static inline u32 nft_cmp_fast_mask(unsigned int len) extern const struct nft_expr_ops nft_cmp_fast_ops; -int nft_cmp_module_init(void); -void nft_cmp_module_exit(void); - -int nft_range_module_init(void); -void nft_range_module_exit(void); - -int nft_lookup_module_init(void); -void nft_lookup_module_exit(void); - -int nft_dynset_module_init(void); -void nft_dynset_module_exit(void); - -int nft_bitwise_module_init(void); -void nft_bitwise_module_exit(void); - -int nft_byteorder_module_init(void); -void nft_byteorder_module_exit(void); - struct nft_payload { enum nft_payload_bases base:8; u8 offset; @@ -62,7 +50,4 @@ struct nft_payload_set { extern const struct nft_expr_ops nft_payload_fast_ops; extern struct static_key_false nft_trace_enabled; -int nft_payload_module_init(void); -void nft_payload_module_exit(void); - #endif /* _NET_NF_TABLES_CORE_H */ diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index b63b1ed..65dbead 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -232,68 +232,40 @@ next_rule: } EXPORT_SYMBOL_GPL(nft_do_chain); +static struct nft_expr_type *nft_basic_types[] = { + &nft_imm_type, + &nft_cmp_type, + &nft_lookup_type, + &nft_bitwise_type, + &nft_byteorder_type, + &nft_payload_type, + &nft_dynset_type, + &nft_range_type, +}; + int __init nf_tables_core_module_init(void) { - int err; - - err = nft_immediate_module_init(); - if (err < 0) - goto err1; - - err = nft_cmp_module_init(); - if (err < 0) - goto err2; - - err = nft_lookup_module_init(); - if (err < 0) - goto err3; - - err = nft_bitwise_module_init(); - if (err < 0) - goto err4; + int err, i; - err = nft_byteorder_module_init(); - if (err < 0) - goto err5; - - err = nft_payload_module_init(); - if (err < 0) - goto err6; - - err = nft_dynset_module_init(); - if (err < 0) - goto err7; - - err = nft_range_module_init(); - if (err < 0) - goto err8; + for (i = 0; i < ARRAY_SIZE(nft_basic_types); i++) { + err = nft_register_expr(nft_basic_types[i]); + if (err) + goto err; + } return 0; -err8: - nft_dynset_module_exit(); -err7: - nft_payload_module_exit(); -err6: - nft_byteorder_module_exit(); -err5: - nft_bitwise_module_exit(); -err4: - nft_lookup_module_exit(); -err3: - nft_cmp_module_exit(); -err2: - nft_immediate_module_exit(); -err1: + +err: + while (i-- > 0) + nft_unregister_expr(nft_basic_types[i]); return err; } void nf_tables_core_module_exit(void) { - nft_dynset_module_exit(); - nft_payload_module_exit(); - nft_byteorder_module_exit(); - nft_bitwise_module_exit(); - nft_lookup_module_exit(); - nft_cmp_module_exit(); - nft_immediate_module_exit(); + int i; + + i = ARRAY_SIZE(nft_basic_types); + while (i-- > 0) + nft_unregister_expr(nft_basic_types[i]); } diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 31c15ed..877d9ac 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -121,7 +121,6 @@ nla_put_failure: return -1; } -static struct nft_expr_type nft_bitwise_type; static const struct nft_expr_ops nft_bitwise_ops = { .type = &nft_bitwise_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise)), @@ -130,20 +129,10 @@ static const struct nft_expr_ops nft_bitwise_ops = { .dump = nft_bitwise_dump, }; -static struct nft_expr_type nft_bitwise_type __read_mostly = { +struct nft_expr_type nft_bitwise_type __read_mostly = { .name = "bitwise", .ops = &nft_bitwise_ops, .policy = nft_bitwise_policy, .maxattr = NFTA_BITWISE_MAX, .owner = THIS_MODULE, }; - -int __init nft_bitwise_module_init(void) -{ - return nft_register_expr(&nft_bitwise_type); -} - -void nft_bitwise_module_exit(void) -{ - nft_unregister_expr(&nft_bitwise_type); -} diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index ee63d98..13d4e42 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -169,7 +169,6 @@ nla_put_failure: return -1; } -static struct nft_expr_type nft_byteorder_type; static const struct nft_expr_ops nft_byteorder_ops = { .type = &nft_byteorder_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)), @@ -178,20 +177,10 @@ static const struct nft_expr_ops nft_byteorder_ops = { .dump = nft_byteorder_dump, }; -static struct nft_expr_type nft_byteorder_type __read_mostly = { +struct nft_expr_type nft_byteorder_type __read_mostly = { .name = "byteorder", .ops = &nft_byteorder_ops, .policy = nft_byteorder_policy, .maxattr = NFTA_BYTEORDER_MAX, .owner = THIS_MODULE, }; - -int __init nft_byteorder_module_init(void) -{ - return nft_register_expr(&nft_byteorder_type); -} - -void nft_byteorder_module_exit(void) -{ - nft_unregister_expr(&nft_byteorder_type); -} diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index e25b35d..2b96eff 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -107,7 +107,6 @@ nla_put_failure: return -1; } -static struct nft_expr_type nft_cmp_type; static const struct nft_expr_ops nft_cmp_ops = { .type = &nft_cmp_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), @@ -208,20 +207,10 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) return &nft_cmp_ops; } -static struct nft_expr_type nft_cmp_type __read_mostly = { +struct nft_expr_type nft_cmp_type __read_mostly = { .name = "cmp", .select_ops = nft_cmp_select_ops, .policy = nft_cmp_policy, .maxattr = NFTA_CMP_MAX, .owner = THIS_MODULE, }; - -int __init nft_cmp_module_init(void) -{ - return nft_register_expr(&nft_cmp_type); -} - -void nft_cmp_module_exit(void) -{ - nft_unregister_expr(&nft_cmp_type); -} diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 517f087..4339e3f1 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -261,7 +261,6 @@ nla_put_failure: return -1; } -static struct nft_expr_type nft_dynset_type; static const struct nft_expr_ops nft_dynset_ops = { .type = &nft_dynset_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_dynset)), @@ -271,20 +270,10 @@ static const struct nft_expr_ops nft_dynset_ops = { .dump = nft_dynset_dump, }; -static struct nft_expr_type nft_dynset_type __read_mostly = { +struct nft_expr_type nft_dynset_type __read_mostly = { .name = "dynset", .ops = &nft_dynset_ops, .policy = nft_dynset_policy, .maxattr = NFTA_DYNSET_MAX, .owner = THIS_MODULE, }; - -int __init nft_dynset_module_init(void) -{ - return nft_register_expr(&nft_dynset_type); -} - -void nft_dynset_module_exit(void) -{ - nft_unregister_expr(&nft_dynset_type); -} diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 4528ade..728baf8 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -102,7 +102,6 @@ static int nft_immediate_validate(const struct nft_ctx *ctx, return 0; } -static struct nft_expr_type nft_imm_type; static const struct nft_expr_ops nft_imm_ops = { .type = &nft_imm_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), @@ -113,20 +112,10 @@ static const struct nft_expr_ops nft_imm_ops = { .validate = nft_immediate_validate, }; -static struct nft_expr_type nft_imm_type __read_mostly = { +struct nft_expr_type nft_imm_type __read_mostly = { .name = "immediate", .ops = &nft_imm_ops, .policy = nft_immediate_policy, .maxattr = NFTA_IMMEDIATE_MAX, .owner = THIS_MODULE, }; - -int __init nft_immediate_module_init(void) -{ - return nft_register_expr(&nft_imm_type); -} - -void nft_immediate_module_exit(void) -{ - nft_unregister_expr(&nft_imm_type); -} diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index a8ce49b..d4f97fa 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -154,7 +154,6 @@ nla_put_failure: return -1; } -static struct nft_expr_type nft_lookup_type; static const struct nft_expr_ops nft_lookup_ops = { .type = &nft_lookup_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), @@ -164,20 +163,10 @@ static const struct nft_expr_ops nft_lookup_ops = { .dump = nft_lookup_dump, }; -static struct nft_expr_type nft_lookup_type __read_mostly = { +struct nft_expr_type nft_lookup_type __read_mostly = { .name = "lookup", .ops = &nft_lookup_ops, .policy = nft_lookup_policy, .maxattr = NFTA_LOOKUP_MAX, .owner = THIS_MODULE, }; - -int __init nft_lookup_module_init(void) -{ - return nft_register_expr(&nft_lookup_type); -} - -void nft_lookup_module_exit(void) -{ - nft_unregister_expr(&nft_lookup_type); -} diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index b2f8861..98fb5d7 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -148,7 +148,6 @@ nla_put_failure: return -1; } -static struct nft_expr_type nft_payload_type; static const struct nft_expr_ops nft_payload_ops = { .type = &nft_payload_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), @@ -320,20 +319,10 @@ nft_payload_select_ops(const struct nft_ctx *ctx, return &nft_payload_ops; } -static struct nft_expr_type nft_payload_type __read_mostly = { +struct nft_expr_type nft_payload_type __read_mostly = { .name = "payload", .select_ops = nft_payload_select_ops, .policy = nft_payload_policy, .maxattr = NFTA_PAYLOAD_MAX, .owner = THIS_MODULE, }; - -int __init nft_payload_module_init(void) -{ - return nft_register_expr(&nft_payload_type); -} - -void nft_payload_module_exit(void) -{ - nft_unregister_expr(&nft_payload_type); -} diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index fbc8800..0090626 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -122,7 +122,6 @@ nla_put_failure: return -1; } -static struct nft_expr_type nft_range_type; static const struct nft_expr_ops nft_range_ops = { .type = &nft_range_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_range_expr)), @@ -131,20 +130,10 @@ static const struct nft_expr_ops nft_range_ops = { .dump = nft_range_dump, }; -static struct nft_expr_type nft_range_type __read_mostly = { +struct nft_expr_type nft_range_type __read_mostly = { .name = "range", .ops = &nft_range_ops, .policy = nft_range_policy, .maxattr = NFTA_RANGE_MAX, .owner = THIS_MODULE, }; - -int __init nft_range_module_init(void) -{ - return nft_register_expr(&nft_range_type); -} - -void nft_range_module_exit(void) -{ - nft_unregister_expr(&nft_range_type); -} -- cgit v1.1 From 0e54d2179f650bac80d89a9def429dbdbed58c11 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Mon, 7 Nov 2016 18:31:17 +0100 Subject: netfilter: conntrack: simplify init/uninit of L4 protocol trackers modify registration and deregistration of layer-4 protocol trackers to facilitate inclusion of new elements into the current list of builtin protocols. Both builtin (TCP, UDP, ICMP) and non-builtin (DCCP, GRE, SCTP, UDPlite) layer-4 protocol trackers usually register/deregister themselves using consecutive calls to nf_ct_l4proto_{,pernet}_{,un}register(...). This sequence is interrupted and rolled back in case of error; in order to simplify addition of builtin protocols, the input of the above functions has been modified to allow registering/unregistering multiple protocols. Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 18 ++++-- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 76 +++++++---------------- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 78 ++++++++--------------- net/netfilter/nf_conntrack_proto.c | 85 ++++++++++++++++++++++---- net/netfilter/nf_conntrack_proto_dccp.c | 48 ++++----------- net/netfilter/nf_conntrack_proto_gre.c | 11 ++-- net/netfilter/nf_conntrack_proto_sctp.c | 50 ++++----------- net/netfilter/nf_conntrack_proto_udplite.c | 50 ++++----------- 8 files changed, 179 insertions(+), 237 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index de629f1..2152b70 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -125,14 +125,24 @@ struct nf_conntrack_l4proto *nf_ct_l4proto_find_get(u_int16_t l3proto, void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); /* Protocol pernet registration. */ +int nf_ct_l4proto_pernet_register_one(struct net *net, + struct nf_conntrack_l4proto *proto); +void nf_ct_l4proto_pernet_unregister_one(struct net *net, + struct nf_conntrack_l4proto *proto); int nf_ct_l4proto_pernet_register(struct net *net, - struct nf_conntrack_l4proto *proto); + struct nf_conntrack_l4proto *proto[], + unsigned int num_proto); void nf_ct_l4proto_pernet_unregister(struct net *net, - struct nf_conntrack_l4proto *proto); + struct nf_conntrack_l4proto *proto[], + unsigned int num_proto); /* Protocol global registration. */ -int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto); -void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto); +int nf_ct_l4proto_register_one(struct nf_conntrack_l4proto *proto); +void nf_ct_l4proto_unregister_one(struct nf_conntrack_l4proto *proto); +int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto[], + unsigned int num_proto); +void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto[], + unsigned int num_proto); /* Generic netlink helpers */ int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 713c09a..7130ed5 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -336,47 +336,34 @@ MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET)); MODULE_ALIAS("ip_conntrack"); MODULE_LICENSE("GPL"); +static struct nf_conntrack_l4proto *builtin_l4proto4[] = { + &nf_conntrack_l4proto_tcp4, + &nf_conntrack_l4proto_udp4, + &nf_conntrack_l4proto_icmp, +}; + static int ipv4_net_init(struct net *net) { int ret = 0; - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp4); - if (ret < 0) { - pr_err("nf_conntrack_tcp4: pernet registration failed\n"); - goto out_tcp; - } - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp4); - if (ret < 0) { - pr_err("nf_conntrack_udp4: pernet registration failed\n"); - goto out_udp; - } - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmp); - if (ret < 0) { - pr_err("nf_conntrack_icmp4: pernet registration failed\n"); - goto out_icmp; - } + ret = nf_ct_l4proto_pernet_register(net, builtin_l4proto4, + ARRAY_SIZE(builtin_l4proto4)); + if (ret < 0) + return ret; ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4); if (ret < 0) { pr_err("nf_conntrack_ipv4: pernet registration failed\n"); - goto out_ipv4; + nf_ct_l4proto_pernet_unregister(net, builtin_l4proto4, + ARRAY_SIZE(builtin_l4proto4)); } - return 0; -out_ipv4: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp); -out_icmp: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4); -out_udp: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4); -out_tcp: return ret; } static void ipv4_net_exit(struct net *net) { nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv4); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4); + nf_ct_l4proto_pernet_unregister(net, builtin_l4proto4, + ARRAY_SIZE(builtin_l4proto4)); } static struct pernet_operations ipv4_net_ops = { @@ -410,37 +397,21 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) goto cleanup_pernet; } - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4); - if (ret < 0) { - pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n"); + ret = nf_ct_l4proto_register(builtin_l4proto4, + ARRAY_SIZE(builtin_l4proto4)); + if (ret < 0) goto cleanup_hooks; - } - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4); - if (ret < 0) { - pr_err("nf_conntrack_ipv4: can't register udp4 proto.\n"); - goto cleanup_tcp4; - } - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp); - if (ret < 0) { - pr_err("nf_conntrack_ipv4: can't register icmpv4 proto.\n"); - goto cleanup_udp4; - } ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n"); - goto cleanup_icmpv4; + goto cleanup_l4proto; } return ret; - cleanup_icmpv4: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp); - cleanup_udp4: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4); - cleanup_tcp4: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4); +cleanup_l4proto: + nf_ct_l4proto_unregister(builtin_l4proto4, + ARRAY_SIZE(builtin_l4proto4)); cleanup_hooks: nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); cleanup_pernet: @@ -454,9 +425,8 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) { synchronize_net(); nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4); + nf_ct_l4proto_unregister(builtin_l4proto4, + ARRAY_SIZE(builtin_l4proto4)); nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); unregister_pernet_subsys(&ipv4_net_ops); nf_unregister_sockopt(&so_getorigdst); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 963ee38..500be28 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -336,47 +336,35 @@ static struct nf_sockopt_ops so_getorigdst6 = { .owner = THIS_MODULE, }; +static struct nf_conntrack_l4proto *builtin_l4proto6[] = { + &nf_conntrack_l4proto_tcp6, + &nf_conntrack_l4proto_udp6, + &nf_conntrack_l4proto_icmpv6, +}; + static int ipv6_net_init(struct net *net) { int ret = 0; - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp6); - if (ret < 0) { - pr_err("nf_conntrack_tcp6: pernet registration failed\n"); - goto out; - } - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp6); - if (ret < 0) { - pr_err("nf_conntrack_udp6: pernet registration failed\n"); - goto cleanup_tcp6; - } - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmpv6); - if (ret < 0) { - pr_err("nf_conntrack_icmp6: pernet registration failed\n"); - goto cleanup_udp6; - } + ret = nf_ct_l4proto_pernet_register(net, builtin_l4proto6, + ARRAY_SIZE(builtin_l4proto6)); + if (ret < 0) + return ret; + ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6); if (ret < 0) { pr_err("nf_conntrack_ipv6: pernet registration failed.\n"); - goto cleanup_icmpv6; + nf_ct_l4proto_pernet_unregister(net, builtin_l4proto6, + ARRAY_SIZE(builtin_l4proto6)); } - return 0; - cleanup_icmpv6: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6); - cleanup_udp6: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6); - cleanup_tcp6: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6); - out: return ret; } static void ipv6_net_exit(struct net *net) { nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6); + nf_ct_l4proto_pernet_unregister(net, builtin_l4proto6, + ARRAY_SIZE(builtin_l4proto6)); } static struct pernet_operations ipv6_net_ops = { @@ -409,37 +397,20 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) goto cleanup_pernet; } - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp6); - if (ret < 0) { - pr_err("nf_conntrack_ipv6: can't register tcp6 proto.\n"); + ret = nf_ct_l4proto_register(builtin_l4proto6, + ARRAY_SIZE(builtin_l4proto6)); + if (ret < 0) goto cleanup_hooks; - } - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp6); - if (ret < 0) { - pr_err("nf_conntrack_ipv6: can't register udp6 proto.\n"); - goto cleanup_tcp6; - } - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmpv6); - if (ret < 0) { - pr_err("nf_conntrack_ipv6: can't register icmpv6 proto.\n"); - goto cleanup_udp6; - } ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6); if (ret < 0) { pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n"); - goto cleanup_icmpv6; + goto cleanup_l4proto; } return ret; - - cleanup_icmpv6: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); - cleanup_udp6: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6); - cleanup_tcp6: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6); +cleanup_l4proto: + nf_ct_l4proto_unregister(builtin_l4proto6, + ARRAY_SIZE(builtin_l4proto6)); cleanup_hooks: nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); cleanup_pernet: @@ -453,9 +424,8 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) { synchronize_net(); nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); + nf_ct_l4proto_unregister(builtin_l4proto6, + ARRAY_SIZE(builtin_l4proto6)); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); unregister_pernet_subsys(&ipv6_net_ops); nf_unregister_sockopt(&so_getorigdst6); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 8d2c7d8..9bd3464 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -281,15 +281,15 @@ void nf_ct_l4proto_unregister_sysctl(struct net *net, /* FIXME: Allow NULL functions and sub in pointers to generic for them. --RR */ -int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto) +int nf_ct_l4proto_register_one(struct nf_conntrack_l4proto *l4proto) { int ret = 0; if (l4proto->l3proto >= PF_MAX) return -EBUSY; - if ((l4proto->to_nlattr && !l4proto->nlattr_size) - || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) + if ((l4proto->to_nlattr && !l4proto->nlattr_size) || + (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) return -EINVAL; mutex_lock(&nf_ct_proto_mutex); @@ -307,7 +307,8 @@ int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto) } for (i = 0; i < MAX_NF_CT_PROTO; i++) - RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic); + RCU_INIT_POINTER(proto_array[i], + &nf_conntrack_l4proto_generic); /* Before making proto_array visible to lockless readers, * we must make sure its content is committed to memory. @@ -335,10 +336,10 @@ out_unlock: mutex_unlock(&nf_ct_proto_mutex); return ret; } -EXPORT_SYMBOL_GPL(nf_ct_l4proto_register); +EXPORT_SYMBOL_GPL(nf_ct_l4proto_register_one); -int nf_ct_l4proto_pernet_register(struct net *net, - struct nf_conntrack_l4proto *l4proto) +int nf_ct_l4proto_pernet_register_one(struct net *net, + struct nf_conntrack_l4proto *l4proto) { int ret = 0; struct nf_proto_net *pn = NULL; @@ -361,9 +362,9 @@ int nf_ct_l4proto_pernet_register(struct net *net, out: return ret; } -EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register); +EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register_one); -void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) +void nf_ct_l4proto_unregister_one(struct nf_conntrack_l4proto *l4proto) { BUG_ON(l4proto->l3proto >= PF_MAX); @@ -378,10 +379,10 @@ void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) synchronize_rcu(); } -EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister); +EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister_one); -void nf_ct_l4proto_pernet_unregister(struct net *net, - struct nf_conntrack_l4proto *l4proto) +void nf_ct_l4proto_pernet_unregister_one(struct net *net, + struct nf_conntrack_l4proto *l4proto) { struct nf_proto_net *pn = NULL; @@ -395,6 +396,66 @@ void nf_ct_l4proto_pernet_unregister(struct net *net, /* Remove all contrack entries for this protocol */ nf_ct_iterate_cleanup(net, kill_l4proto, l4proto, 0, 0); } +EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister_one); + +int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto[], + unsigned int num_proto) +{ + int ret = -EINVAL, ver; + unsigned int i; + + for (i = 0; i < num_proto; i++) { + ret = nf_ct_l4proto_register_one(l4proto[i]); + if (ret < 0) + break; + } + if (i != num_proto) { + ver = l4proto[i]->l3proto == PF_INET6 ? 6 : 4; + pr_err("nf_conntrack_ipv%d: can't register %s%d proto.\n", + ver, l4proto[i]->name, ver); + nf_ct_l4proto_unregister(l4proto, i); + } + return ret; +} +EXPORT_SYMBOL_GPL(nf_ct_l4proto_register); + +int nf_ct_l4proto_pernet_register(struct net *net, + struct nf_conntrack_l4proto *l4proto[], + unsigned int num_proto) +{ + int ret = -EINVAL; + unsigned int i; + + for (i = 0; i < num_proto; i++) { + ret = nf_ct_l4proto_pernet_register_one(net, l4proto[i]); + if (ret < 0) + break; + } + if (i != num_proto) { + pr_err("nf_conntrack_%s%d: pernet registration failed\n", + l4proto[i]->name, + l4proto[i]->l3proto == PF_INET6 ? 6 : 4); + nf_ct_l4proto_pernet_unregister(net, l4proto, i); + } + return ret; +} +EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register); + +void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto[], + unsigned int num_proto) +{ + while (num_proto-- != 0) + nf_ct_l4proto_unregister_one(l4proto[num_proto]); +} +EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister); + +void nf_ct_l4proto_pernet_unregister(struct net *net, + struct nf_conntrack_l4proto *l4proto[], + unsigned int num_proto) +{ + while (num_proto-- != 0) + nf_ct_l4proto_pernet_unregister_one(net, l4proto[num_proto]); +} EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister); int nf_conntrack_proto_pernet_init(struct net *net) diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index a45bee5..ac89769 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -936,30 +936,21 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { .init_net = dccp_init_net, }; +static struct nf_conntrack_l4proto *dccp_proto[] = { + &dccp_proto4, + &dccp_proto6, +}; + static __net_init int dccp_net_init(struct net *net) { - int ret = 0; - ret = nf_ct_l4proto_pernet_register(net, &dccp_proto4); - if (ret < 0) { - pr_err("nf_conntrack_dccp4: pernet registration failed.\n"); - goto out; - } - ret = nf_ct_l4proto_pernet_register(net, &dccp_proto6); - if (ret < 0) { - pr_err("nf_conntrack_dccp6: pernet registration failed.\n"); - goto cleanup_dccp4; - } - return 0; -cleanup_dccp4: - nf_ct_l4proto_pernet_unregister(net, &dccp_proto4); -out: - return ret; + return nf_ct_l4proto_pernet_register(net, dccp_proto, + ARRAY_SIZE(dccp_proto)); } static __net_exit void dccp_net_exit(struct net *net) { - nf_ct_l4proto_pernet_unregister(net, &dccp_proto6); - nf_ct_l4proto_pernet_unregister(net, &dccp_proto4); + nf_ct_l4proto_pernet_unregister(net, dccp_proto, + ARRAY_SIZE(dccp_proto)); } static struct pernet_operations dccp_net_ops = { @@ -975,29 +966,16 @@ static int __init nf_conntrack_proto_dccp_init(void) ret = register_pernet_subsys(&dccp_net_ops); if (ret < 0) - goto out_pernet; - - ret = nf_ct_l4proto_register(&dccp_proto4); - if (ret < 0) - goto out_dccp4; - - ret = nf_ct_l4proto_register(&dccp_proto6); + return ret; + ret = nf_ct_l4proto_register(dccp_proto, ARRAY_SIZE(dccp_proto)); if (ret < 0) - goto out_dccp6; - - return 0; -out_dccp6: - nf_ct_l4proto_unregister(&dccp_proto4); -out_dccp4: - unregister_pernet_subsys(&dccp_net_ops); -out_pernet: + unregister_pernet_subsys(&dccp_net_ops); return ret; } static void __exit nf_conntrack_proto_dccp_fini(void) { - nf_ct_l4proto_unregister(&dccp_proto6); - nf_ct_l4proto_unregister(&dccp_proto4); + nf_ct_l4proto_unregister(dccp_proto, ARRAY_SIZE(dccp_proto)); unregister_pernet_subsys(&dccp_net_ops); } diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 9a715f8..ff405c9 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -396,7 +396,9 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { static int proto_gre_net_init(struct net *net) { int ret = 0; - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_gre4); + + ret = nf_ct_l4proto_pernet_register_one(net, + &nf_conntrack_l4proto_gre4); if (ret < 0) pr_err("nf_conntrack_gre4: pernet registration failed.\n"); return ret; @@ -404,7 +406,7 @@ static int proto_gre_net_init(struct net *net) static void proto_gre_net_exit(struct net *net) { - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_gre4); + nf_ct_l4proto_pernet_unregister_one(net, &nf_conntrack_l4proto_gre4); nf_ct_gre_keymap_flush(net); } @@ -422,8 +424,7 @@ static int __init nf_ct_proto_gre_init(void) ret = register_pernet_subsys(&proto_gre_net_ops); if (ret < 0) goto out_pernet; - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_gre4); + ret = nf_ct_l4proto_register_one(&nf_conntrack_l4proto_gre4); if (ret < 0) goto out_gre4; @@ -436,7 +437,7 @@ out_pernet: static void __exit nf_ct_proto_gre_fini(void) { - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_gre4); + nf_ct_l4proto_unregister_one(&nf_conntrack_l4proto_gre4); unregister_pernet_subsys(&proto_gre_net_ops); } diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 982ea62..17c0ade 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -816,32 +816,21 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .init_net = sctp_init_net, }; +static struct nf_conntrack_l4proto *sctp_proto[] = { + &nf_conntrack_l4proto_sctp4, + &nf_conntrack_l4proto_sctp6, +}; + static int sctp_net_init(struct net *net) { - int ret = 0; - - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp4); - if (ret < 0) { - pr_err("nf_conntrack_sctp4: pernet registration failed.\n"); - goto out; - } - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp6); - if (ret < 0) { - pr_err("nf_conntrack_sctp6: pernet registration failed.\n"); - goto cleanup_sctp4; - } - return 0; - -cleanup_sctp4: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4); -out: - return ret; + return nf_ct_l4proto_pernet_register(net, sctp_proto, + ARRAY_SIZE(sctp_proto)); } static void sctp_net_exit(struct net *net) { - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp6); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4); + nf_ct_l4proto_pernet_unregister(net, sctp_proto, + ARRAY_SIZE(sctp_proto)); } static struct pernet_operations sctp_net_ops = { @@ -857,29 +846,16 @@ static int __init nf_conntrack_proto_sctp_init(void) ret = register_pernet_subsys(&sctp_net_ops); if (ret < 0) - goto out_pernet; - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp4); - if (ret < 0) - goto out_sctp4; - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp6); + return ret; + ret = nf_ct_l4proto_register(sctp_proto, ARRAY_SIZE(sctp_proto)); if (ret < 0) - goto out_sctp6; - - return 0; -out_sctp6: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4); -out_sctp4: - unregister_pernet_subsys(&sctp_net_ops); -out_pernet: + unregister_pernet_subsys(&sctp_net_ops); return ret; } static void __exit nf_conntrack_proto_sctp_fini(void) { - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp6); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4); + nf_ct_l4proto_unregister(sctp_proto, ARRAY_SIZE(sctp_proto)); unregister_pernet_subsys(&sctp_net_ops); } diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 029206e8..8cdb4b1 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -336,32 +336,21 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = .init_net = udplite_init_net, }; +static struct nf_conntrack_l4proto *udplite_proto[] = { + &nf_conntrack_l4proto_udplite4, + &nf_conntrack_l4proto_udplite6, +}; + static int udplite_net_init(struct net *net) { - int ret = 0; - - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite4); - if (ret < 0) { - pr_err("nf_conntrack_udplite4: pernet registration failed.\n"); - goto out; - } - ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite6); - if (ret < 0) { - pr_err("nf_conntrack_udplite6: pernet registration failed.\n"); - goto cleanup_udplite4; - } - return 0; - -cleanup_udplite4: - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4); -out: - return ret; + return nf_ct_l4proto_pernet_register(net, udplite_proto, + ARRAY_SIZE(udplite_proto)); } static void udplite_net_exit(struct net *net) { - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite6); - nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4); + nf_ct_l4proto_pernet_unregister(net, udplite_proto, + ARRAY_SIZE(udplite_proto)); } static struct pernet_operations udplite_net_ops = { @@ -377,29 +366,16 @@ static int __init nf_conntrack_proto_udplite_init(void) ret = register_pernet_subsys(&udplite_net_ops); if (ret < 0) - goto out_pernet; - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite4); - if (ret < 0) - goto out_udplite4; - - ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite6); + return ret; + ret = nf_ct_l4proto_register(udplite_proto, ARRAY_SIZE(udplite_proto)); if (ret < 0) - goto out_udplite6; - - return 0; -out_udplite6: - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4); -out_udplite4: - unregister_pernet_subsys(&udplite_net_ops); -out_pernet: + unregister_pernet_subsys(&udplite_net_ops); return ret; } static void __exit nf_conntrack_proto_udplite_exit(void) { - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite6); - nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4); + nf_ct_l4proto_unregister(udplite_proto, ARRAY_SIZE(udplite_proto)); unregister_pernet_subsys(&udplite_net_ops); } -- cgit v1.1 From 30f581584873cca433c81cbcd4d8f3382348fbaa Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Nov 2016 14:28:18 +0100 Subject: udp: provide udp{4,6}_lib_lookup for nf_socket_ipv{4,6} Since commit ca065d0cf80f ("udp: no longer use SLAB_DESTROY_BY_RCU") the udp6_lib_lookup and udp4_lib_lookup functions are only provided when it is actually possible to call them. However, moving the callers now caused a link error: net/built-in.o: In function `nf_sk_lookup_slow_v6': (.text+0x131a39): undefined reference to `udp6_lib_lookup' net/ipv4/netfilter/nf_socket_ipv4.o: In function `nf_sk_lookup_slow_v4': nf_socket_ipv4.c:(.text.nf_sk_lookup_slow_v4+0x114): undefined reference to `udp4_lib_lookup' This extends the #ifdef so we also provide the functions when CONFIG_NF_SOCKET_IPV4 or CONFIG_NF_SOCKET_IPV6, respectively are set. Fixes: 8db4c5be88f6 ("netfilter: move socket lookup infrastructure to nf_socket_ipv{4,6}.c") Signed-off-by: Arnd Bergmann Signed-off-by: Pablo Neira Ayuso --- net/ipv4/udp.c | 3 ++- net/ipv6/udp.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 195992e..395361b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -580,7 +580,8 @@ EXPORT_SYMBOL_GPL(udp4_lib_lookup_skb); * Does increment socket refcount. */ #if IS_ENABLED(CONFIG_NETFILTER_XT_MATCH_SOCKET) || \ - IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TPROXY) + IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TPROXY) || \ + IS_ENABLED(CONFIG_NF_SOCKET_IPV4) struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, __be32 daddr, __be16 dport, int dif) { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a7700bbf..3e23258 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -302,7 +302,8 @@ EXPORT_SYMBOL_GPL(udp6_lib_lookup_skb); * Does increment socket refcount. */ #if IS_ENABLED(CONFIG_NETFILTER_XT_MATCH_SOCKET) || \ - IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TPROXY) + IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TPROXY) || \ + IS_ENABLED(CONFIG_NF_SOCKET_IPV6) struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport, const struct in6_addr *daddr, __be16 dport, int dif) { -- cgit v1.1 From 56a62e2218f544258bd1796889a42f4516ccd04a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Nov 2016 14:28:19 +0100 Subject: netfilter: conntrack: fix NF_REPEAT handling gcc correctly identified a theoretical uninitialized variable use: net/netfilter/nf_conntrack_core.c: In function 'nf_conntrack_in': net/netfilter/nf_conntrack_core.c:1125:14: error: 'l4proto' may be used uninitialized in this function [-Werror=maybe-uninitialized] This could only happen when we 'goto out' before looking up l4proto, and then enter the retry, implying that l3proto->get_l4proto() returned NF_REPEAT. This does not currently get returned in any code path and probably won't ever happen, but is not good to rely on. Moving the repeat handling up a little should have the same behavior as today but avoids the warning by making that case impossible to enter. [ I have mangled this original patch to remove the check for tmpl, we should inconditionally jump back to the repeat label in case we hit NF_REPEAT instead. I have also moved the comment that explains this where it belongs. --pablo ] Fixes: 08733a0cb7de ("netfilter: handle NF_REPEAT from nf_conntrack_in()") Signed-off-by: Arnd Bergmann Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index de4b8a7..e9ffe33 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1337,6 +1337,12 @@ repeat: NF_CT_STAT_INC_ATOMIC(net, invalid); if (ret == -NF_DROP) NF_CT_STAT_INC_ATOMIC(net, drop); + /* Special case: TCP tracker reports an attempt to reopen a + * closed/aborted connection. We have to go back and create a + * fresh conntrack. + */ + if (ret == -NF_REPEAT) + goto repeat; ret = -ret; goto out; } @@ -1344,16 +1350,8 @@ repeat: if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_REPLY, ct); out: - if (tmpl) { - /* Special case: TCP tracker reports an attempt to reopen a - * closed/aborted connection. We have to go back and create a - * fresh conntrack. - */ - if (ret == NF_REPEAT) - goto repeat; - else - nf_ct_put(tmpl); - } + if (tmpl) + nf_ct_put(tmpl); return ret; } -- cgit v1.1 From d61d072e87c8ee4938b2517818e7b6498923d6a4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 7 Nov 2016 11:12:27 -0800 Subject: net-gro: avoid reorders Receiving a GSO packet in dev_gro_receive() is not uncommon in stacked devices, or devices partially implementing LRO/GRO like bnx2x. GRO is implementing the aggregation the device was not able to do itself. Current code causes reorders, like in following case : For a given flow where sender sent 3 packets P1,P2,P3,P4 Receiver might receive P1 as a single packet, stored in GRO engine. Then P2-P4 are received as a single GSO packet, immediately given to upper stack, while P1 is held in GRO engine. This patch will make sure P1 is given to upper stack, then P2-P4 immediately after. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 0260ad3..c9837fa 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4482,7 +4482,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff if (!(skb->dev->features & NETIF_F_GRO)) goto normal; - if (skb_is_gso(skb) || skb_has_frag_list(skb) || skb->csum_bad) + if (skb->csum_bad) goto normal; gro_list_prepare(napi, skb); @@ -4495,7 +4495,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff skb_set_network_header(skb, skb_gro_offset(skb)); skb_reset_mac_len(skb); NAPI_GRO_CB(skb)->same_flow = 0; - NAPI_GRO_CB(skb)->flush = 0; + NAPI_GRO_CB(skb)->flush = skb_is_gso(skb) || skb_has_frag_list(skb); NAPI_GRO_CB(skb)->free = 0; NAPI_GRO_CB(skb)->encap_mark = 0; NAPI_GRO_CB(skb)->recursion_counter = 0; -- cgit v1.1 From 3f11ec045fecf2c0fb21f08f68ebc9237bd1d03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Mon, 7 Nov 2016 20:39:24 +0000 Subject: net: l2tp: change L2TP_ATTR_UDP_ZERO_CSUM6_{RX, TX} attribute types The attributes L2TP_ATTR_UDP_ZERO_CSUM6_RX and L2TP_ATTR_UDP_ZERO_CSUM6_TX are used as flags, but is defined as a u8 in a comment. This patch redocuments them as flags. Adding nla_policy entries would break API, so not doing that. CC: Tom Herbert Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- include/uapi/linux/l2tp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/l2tp.h b/include/uapi/linux/l2tp.h index 4bd27d0..5daa48e 100644 --- a/include/uapi/linux/l2tp.h +++ b/include/uapi/linux/l2tp.h @@ -124,8 +124,8 @@ enum { L2TP_ATTR_STATS, /* nested */ L2TP_ATTR_IP6_SADDR, /* struct in6_addr */ L2TP_ATTR_IP6_DADDR, /* struct in6_addr */ - L2TP_ATTR_UDP_ZERO_CSUM6_TX, /* u8 */ - L2TP_ATTR_UDP_ZERO_CSUM6_RX, /* u8 */ + L2TP_ATTR_UDP_ZERO_CSUM6_TX, /* flag */ + L2TP_ATTR_UDP_ZERO_CSUM6_RX, /* flag */ L2TP_ATTR_PAD, __L2TP_ATTR_MAX, }; -- cgit v1.1 From 7ff516ffe4ec723aad824d68d8a23751330e8f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Mon, 7 Nov 2016 20:39:25 +0000 Subject: net: l2tp: only set L2TP_ATTR_UDP_CSUM if AF_INET Only set L2TP_ATTR_UDP_CSUM in l2tp_nl_tunnel_send() when it's running over IPv4. This prepares the code to also have IPv6 specific attributes. Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- net/l2tp/l2tp_netlink.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 59aa2d2..2abd100 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -379,9 +379,14 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla switch (tunnel->encap) { case L2TP_ENCAPTYPE_UDP: + switch (sk->sk_family) { + case AF_INET: + if (nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx)) + goto nla_put_failure; + break; + } if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || - nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) || - nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx)) + nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport))) goto nla_put_failure; /* NOBREAK */ case L2TP_ENCAPTYPE_IP: -- cgit v1.1 From 97b7af097edb8b4d2456f16b75938cf119ed0c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Mon, 7 Nov 2016 20:39:26 +0000 Subject: net: l2tp: netlink: l2tp_nl_tunnel_send: set UDP6 checksum flags This patch causes the proper attribute flags to be set, in the case that IPv6 UDP checksums are disabled, so that userspace ie. `ip l2tp show tunnel` knows about it. Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- net/l2tp/l2tp_netlink.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 2abd100..494910d 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -384,6 +384,16 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla if (nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx)) goto nla_put_failure; break; +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + if (udp_get_no_check6_tx(sk) && + nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_TX)) + goto nla_put_failure; + if (udp_get_no_check6_rx(sk) && + nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_RX)) + goto nla_put_failure; + break; +#endif } if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport))) -- cgit v1.1 From 57ceb8611d85b7ee28c402383ba3fe8db4a083f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Mon, 7 Nov 2016 20:39:27 +0000 Subject: net: l2tp: cleanup: remove redundant condition These assignments follow this pattern: unsigned int foo:1; struct nlattr *nla = info->attrs[bar]; if (nla) foo = nla_get_flag(nla); /* expands to: foo = !!nla */ This could be simplified to: if (nla) foo = 1; but lets just remove the condition and use the macro, foo = nla_get_flag(nla); Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- net/l2tp/l2tp_netlink.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 494910d..3620fba 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -220,14 +220,14 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]); if (info->attrs[L2TP_ATTR_UDP_DPORT]) cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]); - if (info->attrs[L2TP_ATTR_UDP_CSUM]) - cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]); + cfg.use_udp_checksums = nla_get_flag( + info->attrs[L2TP_ATTR_UDP_CSUM]); #if IS_ENABLED(CONFIG_IPV6) - if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]) - cfg.udp6_zero_tx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]); - if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]) - cfg.udp6_zero_rx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]); + cfg.udp6_zero_tx_checksums = nla_get_flag( + info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]); + cfg.udp6_zero_rx_checksums = nla_get_flag( + info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]); #endif } -- cgit v1.1 From 3f9b9770b479986cb6acebaf8372f15c0cbd7f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Mon, 7 Nov 2016 20:39:28 +0000 Subject: net: l2tp: fix negative assignment to unsigned int recv_seq, send_seq and lns_mode mode are all defined as unsigned int foo:1; Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 2 +- net/l2tp/l2tp_ppp.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index a2ed3bd..85948c6 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -715,7 +715,7 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, l2tp_info(session, L2TP_MSG_SEQ, "%s: requested to enable seq numbers by LNS\n", session->name); - session->send_seq = -1; + session->send_seq = 1; l2tp_session_set_header_len(session, tunnel->version); } } else { diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 41d47bf..2ddfec1 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1272,7 +1272,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk, err = -EINVAL; break; } - session->recv_seq = val ? -1 : 0; + session->recv_seq = !!val; l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set recv_seq=%d\n", session->name, session->recv_seq); @@ -1283,7 +1283,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk, err = -EINVAL; break; } - session->send_seq = val ? -1 : 0; + session->send_seq = !!val; { struct sock *ssk = ps->sock; struct pppox_sock *po = pppox_sk(ssk); @@ -1301,7 +1301,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk, err = -EINVAL; break; } - session->lns_mode = val ? -1 : 0; + session->lns_mode = !!val; l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set lns_mode=%d\n", session->name, session->lns_mode); -- cgit v1.1 From 1af92836e53f66f8d2cddab2862cc7e1bb3a7cc3 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 7 Nov 2016 14:51:23 +0800 Subject: igmp: Document sysctl force_igmp_version There is some difference between force_igmp_version and force_mld_version. Add document to make users aware of this. Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 3db8c67..5af48dd 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -967,6 +967,21 @@ igmp_qrv - INTEGER Default: 2 (as specified by RFC2236 8.1) Minimum: 1 (as specified by RFC6636 4.5) +force_igmp_version - INTEGER + 0 - (default) No enforcement of a IGMP version, IGMPv1/v2 fallback + allowed. Will back to IGMPv3 mode again if all IGMPv1/v2 Querier + Present timer expires. + 1 - Enforce to use IGMP version 1. Will also reply IGMPv1 report if + receive IGMPv2/v3 query. + 2 - Enforce to use IGMP version 2. Will fallback to IGMPv1 if receive + IGMPv1 query message. Will reply report if receive IGMPv3 query. + 3 - Enforce to use IGMP version 3. The same react with default 0. + + Note: this is not the same with force_mld_version because IGMPv3 RFC3376 + Security Considerations does not have clear description that we could + ignore other version messages completely as MLDv2 RFC3810. So make + this value as default 0 is recommended. + conf/interface/* changes special settings per interface (where "interface" is the name of your network interface) -- cgit v1.1 From 6c27f99d35f4d14b7fc5e50ae1f822d7cca6a5af Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 8 Nov 2016 00:45:53 -0700 Subject: xen-netback: prefer xenbus_scanf() over xenbus_gather() For single items being collected this should be preferred as being more typesafe (as the compiler can check format string and to-be-written-to variable match) and more efficient (requiring one less parameter to be passed). Signed-off-by: Jan Beulich Reviewed-by: Paul Durrant Signed-off-by: David S. Miller --- drivers/net/xen-netback/xenbus.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 8674e18..55a4488 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -889,16 +889,16 @@ static int connect_ctrl_ring(struct backend_info *be) unsigned int evtchn; int err; - err = xenbus_gather(XBT_NIL, dev->otherend, - "ctrl-ring-ref", "%u", &val, NULL); - if (err) + err = xenbus_scanf(XBT_NIL, dev->otherend, + "ctrl-ring-ref", "%u", &val); + if (err < 0) goto done; /* The frontend does not have a control ring */ ring_ref = val; - err = xenbus_gather(XBT_NIL, dev->otherend, - "event-channel-ctrl", "%u", &val, NULL); - if (err) { + err = xenbus_scanf(XBT_NIL, dev->otherend, + "event-channel-ctrl", "%u", &val); + if (err < 0) { xenbus_dev_fatal(dev, err, "reading %s/event-channel-ctrl", dev->otherend); -- cgit v1.1 From dc0b2c9cb47a2176e1f341bfdc770745d24e072c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Nov 2016 14:31:38 +0100 Subject: net: mii: report 0 for unknown lp_advertising The newly introduced mii_ethtool_get_link_ksettings function sets lp_advertising to an uninitialized value when BMCR_ANENABLE is not set: drivers/net/mii.c: In function 'mii_ethtool_get_link_ksettings': drivers/net/mii.c:224:2: error: 'lp_advertising' may be used uninitialized in this function [-Werror=maybe-uninitialized] As documented in include/uapi/linux/ethtool.h, the value is expected to be zero when we don't know it, so let's initialize it to that. Fixes: bc8ee596afe8 ("net: mii: add generic function to support ksetting support") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/mii.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/mii.c b/drivers/net/mii.c index 0443546..6d953c5 100644 --- a/drivers/net/mii.c +++ b/drivers/net/mii.c @@ -213,6 +213,8 @@ int mii_ethtool_get_link_ksettings(struct mii_if_info *mii, SPEED_100 : SPEED_10)); cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; + + lp_advertising = 0; } mii->full_duplex = cmd->base.duplex; -- cgit v1.1 From 1ababeba4a21f3dba3da3523c670b207fb2feb62 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:57:39 +0100 Subject: ipv6: implement dataplane support for rthdr type 4 (Segment Routing Header) Implement minimal support for processing of SR-enabled packets as described in https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-02. This patch implements the following operations: - Intermediate segment endpoint: incrementation of active segment and rerouting. - Egress for SR-encapsulated packets: decapsulation of outer IPv6 header + SRH and routing of inner packet. - Cleanup flag support for SR-inlined packets: removal of SRH if we are the penultimate segment endpoint. A per-interface sysctl seg6_enabled is provided, to accept/deny SR-enabled packets. Default is deny. This patch does not provide support for HMAC-signed packets. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/linux/ipv6.h | 1 + include/linux/seg6.h | 6 ++ include/net/seg6.h | 36 ++++++++++ include/uapi/linux/ipv6.h | 2 + include/uapi/linux/seg6.h | 54 ++++++++++++++ net/ipv6/addrconf.c | 10 +++ net/ipv6/exthdrs.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 284 insertions(+) create mode 100644 include/linux/seg6.h create mode 100644 include/net/seg6.h create mode 100644 include/uapi/linux/seg6.h diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1afb6e8..68d3f71 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -64,6 +64,7 @@ struct ipv6_devconf { } stable_secret; __s32 use_oif_addrs_only; __s32 keep_addr_on_down; + __s32 seg6_enabled; struct ctl_table_header *sysctl_header; }; diff --git a/include/linux/seg6.h b/include/linux/seg6.h new file mode 100644 index 0000000..7a66d2b --- /dev/null +++ b/include/linux/seg6.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_SEG6_H +#define _LINUX_SEG6_H + +#include + +#endif diff --git a/include/net/seg6.h b/include/net/seg6.h new file mode 100644 index 0000000..4dd52a7 --- /dev/null +++ b/include/net/seg6.h @@ -0,0 +1,36 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _NET_SEG6_H +#define _NET_SEG6_H + +static inline void update_csum_diff4(struct sk_buff *skb, __be32 from, + __be32 to) +{ + __be32 diff[] = { ~from, to }; + + skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum); +} + +static inline void update_csum_diff16(struct sk_buff *skb, __be32 *from, + __be32 *to) +{ + __be32 diff[] = { + ~from[0], ~from[1], ~from[2], ~from[3], + to[0], to[1], to[2], to[3], + }; + + skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum); +} + +#endif diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 8c27723..7ff1d65 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -39,6 +39,7 @@ struct in6_ifreq { #define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */ #define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */ #define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */ +#define IPV6_SRCRT_TYPE_4 4 /* Segment Routing with IPv6 */ /* * routing header @@ -178,6 +179,7 @@ enum { DEVCONF_DROP_UNSOLICITED_NA, DEVCONF_KEEP_ADDR_ON_DOWN, DEVCONF_RTR_SOLICIT_MAX_INTERVAL, + DEVCONF_SEG6_ENABLED, DEVCONF_MAX }; diff --git a/include/uapi/linux/seg6.h b/include/uapi/linux/seg6.h new file mode 100644 index 0000000..c396a80 --- /dev/null +++ b/include/uapi/linux/seg6.h @@ -0,0 +1,54 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UAPI_LINUX_SEG6_H +#define _UAPI_LINUX_SEG6_H + +/* + * SRH + */ +struct ipv6_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + __u8 first_segment; + __u8 flag_1; + __u8 flag_2; + __u8 reserved; + + struct in6_addr segments[0]; +}; + +#define SR6_FLAG1_CLEANUP (1 << 7) +#define SR6_FLAG1_PROTECTED (1 << 6) +#define SR6_FLAG1_OAM (1 << 5) +#define SR6_FLAG1_ALERT (1 << 4) +#define SR6_FLAG1_HMAC (1 << 3) + +#define SR6_TLV_INGRESS 1 +#define SR6_TLV_EGRESS 2 +#define SR6_TLV_OPAQUE 3 +#define SR6_TLV_PADDING 4 +#define SR6_TLV_HMAC 5 + +#define sr_has_cleanup(srh) ((srh)->flag_1 & SR6_FLAG1_CLEANUP) +#define sr_has_hmac(srh) ((srh)->flag_1 & SR6_FLAG1_HMAC) + +struct sr6_tlv { + __u8 type; + __u8 len; + __u8 data[0]; +}; + +#endif diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 060dd99..2ac6cb4 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -238,6 +238,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .use_oif_addrs_only = 0, .ignore_routes_with_linkdown = 0, .keep_addr_on_down = 0, + .seg6_enabled = 0, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -284,6 +285,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .use_oif_addrs_only = 0, .ignore_routes_with_linkdown = 0, .keep_addr_on_down = 0, + .seg6_enabled = 0, }; /* Check if a valid qdisc is available */ @@ -4944,6 +4946,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast; array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na; array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down; + array[DEVCONF_SEG6_ENABLED] = cnf->seg6_enabled; } static inline size_t inet6_ifla6_size(void) @@ -6036,6 +6039,13 @@ static const struct ctl_table addrconf_sysctl[] = { }, { + .procname = "seg6_enabled", + .data = &ipv6_devconf.seg6_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { /* sentinel */ } }; diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 139ceb6..b8ba396 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -47,6 +47,8 @@ #if IS_ENABLED(CONFIG_IPV6_MIP6) #include #endif +#include +#include #include @@ -286,6 +288,175 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) return -1; } +static void seg6_update_csum(struct sk_buff *skb) +{ + struct ipv6_sr_hdr *hdr; + struct in6_addr *addr; + __be32 from, to; + + /* srh is at transport offset and seg_left is already decremented + * but daddr is not yet updated with next segment + */ + + hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb); + addr = hdr->segments + hdr->segments_left; + + hdr->segments_left++; + from = *(__be32 *)hdr; + + hdr->segments_left--; + to = *(__be32 *)hdr; + + /* update skb csum with diff resulting from seg_left decrement */ + + update_csum_diff4(skb, from, to); + + /* compute csum diff between current and next segment and update */ + + update_csum_diff16(skb, (__be32 *)(&ipv6_hdr(skb)->daddr), + (__be32 *)addr); +} + +static int ipv6_srh_rcv(struct sk_buff *skb) +{ + struct inet6_skb_parm *opt = IP6CB(skb); + struct net *net = dev_net(skb->dev); + struct ipv6_sr_hdr *hdr; + struct inet6_dev *idev; + struct in6_addr *addr; + bool cleanup = false; + int accept_seg6; + + hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb); + + idev = __in6_dev_get(skb->dev); + + accept_seg6 = net->ipv6.devconf_all->seg6_enabled; + if (accept_seg6 > idev->cnf.seg6_enabled) + accept_seg6 = idev->cnf.seg6_enabled; + + if (!accept_seg6) { + kfree_skb(skb); + return -1; + } + +looped_back: + if (hdr->segments_left > 0) { + if (hdr->nexthdr != NEXTHDR_IPV6 && hdr->segments_left == 1 && + sr_has_cleanup(hdr)) + cleanup = true; + } else { + if (hdr->nexthdr == NEXTHDR_IPV6) { + int offset = (hdr->hdrlen + 1) << 3; + + skb_postpull_rcsum(skb, skb_network_header(skb), + skb_network_header_len(skb)); + + if (!pskb_pull(skb, offset)) { + kfree_skb(skb); + return -1; + } + skb_postpull_rcsum(skb, skb_transport_header(skb), + offset); + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->encapsulation = 0; + + __skb_tunnel_rx(skb, skb->dev, net); + + netif_rx(skb); + return -1; + } + + opt->srcrt = skb_network_header_len(skb); + opt->lastopt = opt->srcrt; + skb->transport_header += (hdr->hdrlen + 1) << 3; + opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); + + return 1; + } + + if (hdr->segments_left >= (hdr->hdrlen >> 1)) { + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), + IPSTATS_MIB_INHDRERRORS); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + ((&hdr->segments_left) - + skb_network_header(skb))); + kfree_skb(skb); + return -1; + } + + if (skb_cloned(skb)) { + if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), + IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); + return -1; + } + } + + hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb); + + hdr->segments_left--; + addr = hdr->segments + hdr->segments_left; + + skb_push(skb, sizeof(struct ipv6hdr)); + + if (skb->ip_summed == CHECKSUM_COMPLETE) + seg6_update_csum(skb); + + ipv6_hdr(skb)->daddr = *addr; + + if (cleanup) { + int srhlen = (hdr->hdrlen + 1) << 3; + int nh = hdr->nexthdr; + + skb_pull_rcsum(skb, sizeof(struct ipv6hdr) + srhlen); + memmove(skb_network_header(skb) + srhlen, + skb_network_header(skb), + (unsigned char *)hdr - skb_network_header(skb)); + skb->network_header += srhlen; + ipv6_hdr(skb)->nexthdr = nh; + ipv6_hdr(skb)->payload_len = htons(skb->len - + sizeof(struct ipv6hdr)); + skb_push_rcsum(skb, sizeof(struct ipv6hdr)); + } + + skb_dst_drop(skb); + + ip6_route_input(skb); + + if (skb_dst(skb)->error) { + dst_input(skb); + return -1; + } + + if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) { + if (ipv6_hdr(skb)->hop_limit <= 1) { + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), + IPSTATS_MIB_INHDRERRORS); + icmpv6_send(skb, ICMPV6_TIME_EXCEED, + ICMPV6_EXC_HOPLIMIT, 0); + kfree_skb(skb); + return -1; + } + ipv6_hdr(skb)->hop_limit--; + + /* be sure that srh is still present before reinjecting */ + if (!cleanup) { + skb_pull(skb, sizeof(struct ipv6hdr)); + goto looped_back; + } + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); + } + + dst_input(skb); + + return -1; +} + /******************************** Routing header. ********************************/ @@ -326,6 +497,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb) return -1; } + /* segment routing */ + if (hdr->type == IPV6_SRCRT_TYPE_4) + return ipv6_srh_rcv(skb); + looped_back: if (hdr->segments_left == 0) { switch (hdr->type) { -- cgit v1.1 From 915d7e5e5930b4f01d0971d93b9b25ed17d221aa Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:57:40 +0100 Subject: ipv6: sr: add code base for control plane support of SR-IPv6 This patch adds the necessary hooks and structures to provide support for SR-IPv6 control plane, essentially the Generic Netlink commands that will be used for userspace control over the Segment Routing kernel structures. The genetlink commands provide control over two different structures: tunnel source and HMAC data. The tunnel source is the source address that will be used by default when encapsulating packets into an outer IPv6 header + SRH. If the tunnel source is set to :: then an address of the outgoing interface will be selected as the source. The HMAC commands currently just return ENOTSUPP and will be implemented in a future patch. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/linux/seg6_genl.h | 6 ++ include/net/netns/ipv6.h | 1 + include/net/seg6.h | 16 +++ include/uapi/linux/seg6_genl.h | 32 ++++++ net/ipv6/Makefile | 2 +- net/ipv6/af_inet6.c | 9 +- net/ipv6/seg6.c | 214 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 include/linux/seg6_genl.h create mode 100644 include/uapi/linux/seg6_genl.h create mode 100644 net/ipv6/seg6.c diff --git a/include/linux/seg6_genl.h b/include/linux/seg6_genl.h new file mode 100644 index 0000000..d6c3fb4f --- /dev/null +++ b/include/linux/seg6_genl.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_SEG6_GENL_H +#define _LINUX_SEG6_GENL_H + +#include + +#endif diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 10d0848..de7745e 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -85,6 +85,7 @@ struct netns_ipv6 { #endif atomic_t dev_addr_genid; atomic_t fib6_sernum; + struct seg6_pernet_data *seg6_data; }; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) diff --git a/include/net/seg6.h b/include/net/seg6.h index 4dd52a7..7c7b8ed 100644 --- a/include/net/seg6.h +++ b/include/net/seg6.h @@ -14,6 +14,9 @@ #ifndef _NET_SEG6_H #define _NET_SEG6_H +#include +#include + static inline void update_csum_diff4(struct sk_buff *skb, __be32 from, __be32 to) { @@ -33,4 +36,17 @@ static inline void update_csum_diff16(struct sk_buff *skb, __be32 *from, skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum); } +struct seg6_pernet_data { + struct mutex lock; + struct in6_addr __rcu *tun_src; +}; + +static inline struct seg6_pernet_data *seg6_pernet(struct net *net) +{ + return net->ipv6.seg6_data; +} + +extern int seg6_init(void); +extern void seg6_exit(void); + #endif diff --git a/include/uapi/linux/seg6_genl.h b/include/uapi/linux/seg6_genl.h new file mode 100644 index 0000000..fcf1c60 --- /dev/null +++ b/include/uapi/linux/seg6_genl.h @@ -0,0 +1,32 @@ +#ifndef _UAPI_LINUX_SEG6_GENL_H +#define _UAPI_LINUX_SEG6_GENL_H + +#define SEG6_GENL_NAME "SEG6" +#define SEG6_GENL_VERSION 0x1 + +enum { + SEG6_ATTR_UNSPEC, + SEG6_ATTR_DST, + SEG6_ATTR_DSTLEN, + SEG6_ATTR_HMACKEYID, + SEG6_ATTR_SECRET, + SEG6_ATTR_SECRETLEN, + SEG6_ATTR_ALGID, + SEG6_ATTR_HMACINFO, + __SEG6_ATTR_MAX, +}; + +#define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1) + +enum { + SEG6_CMD_UNSPEC, + SEG6_CMD_SETHMAC, + SEG6_CMD_DUMPHMAC, + SEG6_CMD_SET_TUNSRC, + SEG6_CMD_GET_TUNSRC, + __SEG6_CMD_MAX, +}; + +#define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1) + +#endif diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index c174ccb..c92010d 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -9,7 +9,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ - udp_offload.o + udp_offload.o seg6.o ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index c86911b..d424f3a 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -61,6 +61,7 @@ #include #endif #include +#include #include #include @@ -991,6 +992,10 @@ static int __init inet6_init(void) if (err) goto calipso_fail; + err = seg6_init(); + if (err) + goto seg6_fail; + #ifdef CONFIG_SYSCTL err = ipv6_sysctl_register(); if (err) @@ -1001,8 +1006,10 @@ out: #ifdef CONFIG_SYSCTL sysctl_fail: - calipso_exit(); + seg6_exit(); #endif +seg6_fail: + calipso_exit(); calipso_fail: pingv6_exit(); pingv6_fail: diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c new file mode 100644 index 0000000..e246b0b --- /dev/null +++ b/net/ipv6/seg6.c @@ -0,0 +1,214 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +static struct genl_family seg6_genl_family; + +static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = { + [SEG6_ATTR_DST] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [SEG6_ATTR_DSTLEN] = { .type = NLA_S32, }, + [SEG6_ATTR_HMACKEYID] = { .type = NLA_U32, }, + [SEG6_ATTR_SECRET] = { .type = NLA_BINARY, }, + [SEG6_ATTR_SECRETLEN] = { .type = NLA_U8, }, + [SEG6_ATTR_ALGID] = { .type = NLA_U8, }, + [SEG6_ATTR_HMACINFO] = { .type = NLA_NESTED, }, +}; + +static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) +{ + return -ENOTSUPP; +} + +static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct in6_addr *val, *t_old, *t_new; + struct seg6_pernet_data *sdata; + + sdata = seg6_pernet(net); + + if (!info->attrs[SEG6_ATTR_DST]) + return -EINVAL; + + val = nla_data(info->attrs[SEG6_ATTR_DST]); + t_new = kmemdup(val, sizeof(*val), GFP_KERNEL); + + mutex_lock(&sdata->lock); + + t_old = sdata->tun_src; + rcu_assign_pointer(sdata->tun_src, t_new); + + mutex_unlock(&sdata->lock); + + synchronize_net(); + kfree(t_old); + + return 0; +} + +static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct in6_addr *tun_src; + struct sk_buff *msg; + void *hdr; + + msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, + &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC); + if (!hdr) + goto free_msg; + + rcu_read_lock(); + tun_src = rcu_dereference(seg6_pernet(net)->tun_src); + + if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src)) + goto nla_put_failure; + + rcu_read_unlock(); + + genlmsg_end(msg, hdr); + genlmsg_reply(msg, info); + + return 0; + +nla_put_failure: + rcu_read_unlock(); + genlmsg_cancel(msg, hdr); +free_msg: + nlmsg_free(msg); + return -ENOMEM; +} + +static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) +{ + return -ENOTSUPP; +} + +static int __net_init seg6_net_init(struct net *net) +{ + struct seg6_pernet_data *sdata; + + sdata = kzalloc(sizeof(*sdata), GFP_KERNEL); + if (!sdata) + return -ENOMEM; + + mutex_init(&sdata->lock); + + sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL); + if (!sdata->tun_src) { + kfree(sdata); + return -ENOMEM; + } + + net->ipv6.seg6_data = sdata; + + return 0; +} + +static void __net_exit seg6_net_exit(struct net *net) +{ + struct seg6_pernet_data *sdata = seg6_pernet(net); + + kfree(sdata->tun_src); + kfree(sdata); +} + +static struct pernet_operations ip6_segments_ops = { + .init = seg6_net_init, + .exit = seg6_net_exit, +}; + +static const struct genl_ops seg6_genl_ops[] = { + { + .cmd = SEG6_CMD_SETHMAC, + .doit = seg6_genl_sethmac, + .policy = seg6_genl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = SEG6_CMD_DUMPHMAC, + .dumpit = seg6_genl_dumphmac, + .policy = seg6_genl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = SEG6_CMD_SET_TUNSRC, + .doit = seg6_genl_set_tunsrc, + .policy = seg6_genl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = SEG6_CMD_GET_TUNSRC, + .doit = seg6_genl_get_tunsrc, + .policy = seg6_genl_policy, + .flags = GENL_ADMIN_PERM, + }, +}; + +static struct genl_family seg6_genl_family __ro_after_init = { + .hdrsize = 0, + .name = SEG6_GENL_NAME, + .version = SEG6_GENL_VERSION, + .maxattr = SEG6_ATTR_MAX, + .netnsok = true, + .parallel_ops = true, + .ops = seg6_genl_ops, + .n_ops = ARRAY_SIZE(seg6_genl_ops), + .module = THIS_MODULE, +}; + +int __init seg6_init(void) +{ + int err = -ENOMEM; + + err = genl_register_family(&seg6_genl_family); + if (err) + goto out; + + err = register_pernet_subsys(&ip6_segments_ops); + if (err) + goto out_unregister_genl; + + pr_info("Segment Routing with IPv6\n"); + +out: + return err; +out_unregister_genl: + genl_unregister_family(&seg6_genl_family); + goto out; +} + +void seg6_exit(void) +{ + unregister_pernet_subsys(&ip6_segments_ops); + genl_unregister_family(&seg6_genl_family); +} -- cgit v1.1 From 6c8702c60b88651072460f3f4026c7dfe2521d12 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:57:41 +0100 Subject: ipv6: sr: add support for SRH encapsulation and injection with lwtunnels This patch creates a new type of interfaceless lightweight tunnel (SEG6), enabling the encapsulation and injection of SRH within locally emitted packets and forwarded packets. >From a configuration viewpoint, a seg6 tunnel would be configured as follows: ip -6 ro ad fc00::1/128 encap seg6 mode encap segs fc42::1,fc42::2,fc42::3 dev eth0 Any packet whose destination address is fc00::1 would thus be encapsulated within an outer IPv6 header containing the SRH with three segments, and would actually be routed to the first segment of the list. If `mode inline' was specified instead of `mode encap', then the SRH would be directly inserted after the IPv6 header without outer encapsulation. The inline mode is only available if CONFIG_IPV6_SEG6_INLINE is enabled. This feature was made configurable because direct header insertion may break several mechanisms such as PMTUD or IPSec AH. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/linux/seg6_iptunnel.h | 6 + include/net/seg6.h | 6 + include/uapi/linux/lwtunnel.h | 1 + include/uapi/linux/seg6_iptunnel.h | 44 ++++ net/core/lwtunnel.c | 2 + net/ipv6/Kconfig | 12 ++ net/ipv6/Makefile | 2 +- net/ipv6/seg6.c | 44 ++++ net/ipv6/seg6_iptunnel.c | 410 +++++++++++++++++++++++++++++++++++++ 9 files changed, 526 insertions(+), 1 deletion(-) create mode 100644 include/linux/seg6_iptunnel.h create mode 100644 include/uapi/linux/seg6_iptunnel.h create mode 100644 net/ipv6/seg6_iptunnel.c diff --git a/include/linux/seg6_iptunnel.h b/include/linux/seg6_iptunnel.h new file mode 100644 index 0000000..5377cf6 --- /dev/null +++ b/include/linux/seg6_iptunnel.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_SEG6_IPTUNNEL_H +#define _LINUX_SEG6_IPTUNNEL_H + +#include + +#endif diff --git a/include/net/seg6.h b/include/net/seg6.h index 7c7b8ed..ff5da0c 100644 --- a/include/net/seg6.h +++ b/include/net/seg6.h @@ -16,6 +16,8 @@ #include #include +#include +#include static inline void update_csum_diff4(struct sk_buff *skb, __be32 from, __be32 to) @@ -48,5 +50,9 @@ static inline struct seg6_pernet_data *seg6_pernet(struct net *net) extern int seg6_init(void); extern void seg6_exit(void); +extern int seg6_iptunnel_init(void); +extern void seg6_iptunnel_exit(void); + +extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); #endif diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h index a478fe8..453cc62 100644 --- a/include/uapi/linux/lwtunnel.h +++ b/include/uapi/linux/lwtunnel.h @@ -9,6 +9,7 @@ enum lwtunnel_encap_types { LWTUNNEL_ENCAP_IP, LWTUNNEL_ENCAP_ILA, LWTUNNEL_ENCAP_IP6, + LWTUNNEL_ENCAP_SEG6, __LWTUNNEL_ENCAP_MAX, }; diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h new file mode 100644 index 0000000..0f7dbd2 --- /dev/null +++ b/include/uapi/linux/seg6_iptunnel.h @@ -0,0 +1,44 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UAPI_LINUX_SEG6_IPTUNNEL_H +#define _UAPI_LINUX_SEG6_IPTUNNEL_H + +enum { + SEG6_IPTUNNEL_UNSPEC, + SEG6_IPTUNNEL_SRH, + __SEG6_IPTUNNEL_MAX, +}; +#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1) + +struct seg6_iptunnel_encap { + int mode; + struct ipv6_sr_hdr srh[0]; +}; + +#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3)) + +enum { + SEG6_IPTUN_MODE_INLINE, + SEG6_IPTUN_MODE_ENCAP, +}; + +static inline size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo) +{ + int encap = (tuninfo->mode == SEG6_IPTUN_MODE_ENCAP); + + return ((tuninfo->srh->hdrlen + 1) << 3) + + (encap * sizeof(struct ipv6hdr)); +} + +#endif diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 88fd642..03976e9 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -39,6 +39,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) return "MPLS"; case LWTUNNEL_ENCAP_ILA: return "ILA"; + case LWTUNNEL_ENCAP_SEG6: + return "SEG6"; case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP: case LWTUNNEL_ENCAP_NONE: diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 2343e4f..1123a00 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -289,4 +289,16 @@ config IPV6_PIMSM_V2 Support for IPv6 PIM multicast routing protocol PIM-SMv2. If unsure, say N. +config IPV6_SEG6_INLINE + bool "IPv6: direct Segment Routing Header insertion " + depends on IPV6 + ---help--- + Support for direct insertion of the Segment Routing Header, + also known as inline mode. Be aware that direct insertion of + extension headers (as opposed to encapsulation) may break + multiple mechanisms such as PMTUD or IPSec AH. Use this feature + only if you know exactly what you are doing. + + If unsure, say N. + endif # IPV6 diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index c92010d..59ee92f 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -9,7 +9,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ - udp_offload.o seg6.o + udp_offload.o seg6.o seg6_iptunnel.o ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index e246b0b..9c78053 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -26,6 +26,43 @@ #include #include +bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len) +{ + int trailing; + unsigned int tlv_offset; + + if (srh->type != IPV6_SRCRT_TYPE_4) + return false; + + if (((srh->hdrlen + 1) << 3) != len) + return false; + + if (srh->segments_left != srh->first_segment) + return false; + + tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4); + + trailing = len - tlv_offset; + if (trailing < 0) + return false; + + while (trailing) { + struct sr6_tlv *tlv; + unsigned int tlv_len; + + tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset); + tlv_len = sizeof(*tlv) + tlv->len; + + trailing -= tlv_len; + if (trailing < 0) + return false; + + tlv_offset += tlv_len; + } + + return true; +} + static struct genl_family seg6_genl_family; static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = { @@ -198,10 +235,16 @@ int __init seg6_init(void) if (err) goto out_unregister_genl; + err = seg6_iptunnel_init(); + if (err) + goto out_unregister_pernet; + pr_info("Segment Routing with IPv6\n"); out: return err; +out_unregister_pernet: + unregister_pernet_subsys(&ip6_segments_ops); out_unregister_genl: genl_unregister_family(&seg6_genl_family); goto out; @@ -209,6 +252,7 @@ out_unregister_genl: void seg6_exit(void) { + seg6_iptunnel_exit(); unregister_pernet_subsys(&ip6_segments_ops); genl_unregister_family(&seg6_genl_family); } diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c new file mode 100644 index 0000000..39762b2 --- /dev/null +++ b/net/ipv6/seg6_iptunnel.c @@ -0,0 +1,410 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DST_CACHE +#include +#endif + +struct seg6_lwt { +#ifdef CONFIG_DST_CACHE + struct dst_cache cache; +#endif + struct seg6_iptunnel_encap tuninfo[0]; +}; + +static inline struct seg6_lwt *seg6_lwt_lwtunnel(struct lwtunnel_state *lwt) +{ + return (struct seg6_lwt *)lwt->data; +} + +static inline struct seg6_iptunnel_encap * +seg6_encap_lwtunnel(struct lwtunnel_state *lwt) +{ + return seg6_lwt_lwtunnel(lwt)->tuninfo; +} + +static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = { + [SEG6_IPTUNNEL_SRH] = { .type = NLA_BINARY }, +}; + +int nla_put_srh(struct sk_buff *skb, int attrtype, + struct seg6_iptunnel_encap *tuninfo) +{ + struct seg6_iptunnel_encap *data; + struct nlattr *nla; + int len; + + len = SEG6_IPTUN_ENCAP_SIZE(tuninfo); + + nla = nla_reserve(skb, attrtype, len); + if (!nla) + return -EMSGSIZE; + + data = nla_data(nla); + memcpy(data, tuninfo, len); + + return 0; +} + +static void set_tun_src(struct net *net, struct net_device *dev, + struct in6_addr *daddr, struct in6_addr *saddr) +{ + struct seg6_pernet_data *sdata = seg6_pernet(net); + struct in6_addr *tun_src; + + rcu_read_lock(); + + tun_src = rcu_dereference(sdata->tun_src); + + if (!ipv6_addr_any(tun_src)) { + memcpy(saddr, tun_src, sizeof(struct in6_addr)); + } else { + ipv6_dev_get_saddr(net, dev, daddr, IPV6_PREFER_SRC_PUBLIC, + saddr); + } + + rcu_read_unlock(); +} + +/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ +static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) +{ + struct net *net = dev_net(skb_dst(skb)->dev); + struct ipv6hdr *hdr, *inner_hdr; + struct ipv6_sr_hdr *isrh; + int hdrlen, tot_len, err; + + hdrlen = (osrh->hdrlen + 1) << 3; + tot_len = hdrlen + sizeof(*hdr); + + err = pskb_expand_head(skb, tot_len, 0, GFP_ATOMIC); + if (unlikely(err)) + return err; + + inner_hdr = ipv6_hdr(skb); + + skb_push(skb, tot_len); + skb_reset_network_header(skb); + skb_mac_header_rebuild(skb); + hdr = ipv6_hdr(skb); + + /* inherit tc, flowlabel and hlim + * hlim will be decremented in ip6_forward() afterwards and + * decapsulation will overwrite inner hlim with outer hlim + */ + ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), + ip6_flowlabel(inner_hdr)); + hdr->hop_limit = inner_hdr->hop_limit; + hdr->nexthdr = NEXTHDR_ROUTING; + + isrh = (void *)hdr + sizeof(*hdr); + memcpy(isrh, osrh, hdrlen); + + isrh->nexthdr = NEXTHDR_IPV6; + + hdr->daddr = isrh->segments[isrh->first_segment]; + set_tun_src(net, skb->dev, &hdr->daddr, &hdr->saddr); + + skb_postpush_rcsum(skb, hdr, tot_len); + + return 0; +} + +/* insert an SRH within an IPv6 packet, just after the IPv6 header */ +#ifdef CONFIG_IPV6_SEG6_INLINE +static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) +{ + struct ipv6hdr *hdr, *oldhdr; + struct ipv6_sr_hdr *isrh; + int hdrlen, err; + + hdrlen = (osrh->hdrlen + 1) << 3; + + err = pskb_expand_head(skb, hdrlen, 0, GFP_ATOMIC); + if (unlikely(err)) + return err; + + oldhdr = ipv6_hdr(skb); + + skb_pull(skb, sizeof(struct ipv6hdr)); + skb_postpull_rcsum(skb, skb_network_header(skb), + sizeof(struct ipv6hdr)); + + skb_push(skb, sizeof(struct ipv6hdr) + hdrlen); + skb_reset_network_header(skb); + skb_mac_header_rebuild(skb); + + hdr = ipv6_hdr(skb); + + memmove(hdr, oldhdr, sizeof(*hdr)); + + isrh = (void *)hdr + sizeof(*hdr); + memcpy(isrh, osrh, hdrlen); + + isrh->nexthdr = hdr->nexthdr; + hdr->nexthdr = NEXTHDR_ROUTING; + + isrh->segments[0] = hdr->daddr; + hdr->daddr = isrh->segments[isrh->first_segment]; + + skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen); + + return 0; +} +#endif + +static int seg6_do_srh(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct seg6_iptunnel_encap *tinfo; + int err = 0; + + tinfo = seg6_encap_lwtunnel(dst->lwtstate); + + if (likely(!skb->encapsulation)) { + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + } + + switch (tinfo->mode) { +#ifdef CONFIG_IPV6_SEG6_INLINE + case SEG6_IPTUN_MODE_INLINE: + err = seg6_do_srh_inline(skb, tinfo->srh); + skb_reset_inner_headers(skb); + break; +#endif + case SEG6_IPTUN_MODE_ENCAP: + err = seg6_do_srh_encap(skb, tinfo->srh); + break; + } + + if (err) + return err; + + ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + + skb_set_inner_protocol(skb, skb->protocol); + + return 0; +} + +int seg6_input(struct sk_buff *skb) +{ + int err; + + err = seg6_do_srh(skb); + if (unlikely(err)) { + kfree_skb(skb); + return err; + } + + skb_dst_drop(skb); + ip6_route_input(skb); + + return dst_input(skb); +} + +int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + struct dst_entry *orig_dst = skb_dst(skb); + struct dst_entry *dst = NULL; + struct seg6_lwt *slwt; + int err = -EINVAL; + + err = seg6_do_srh(skb); + if (unlikely(err)) + goto drop; + + slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); + +#ifdef CONFIG_DST_CACHE + dst = dst_cache_get(&slwt->cache); +#endif + + if (unlikely(!dst)) { + struct ipv6hdr *hdr = ipv6_hdr(skb); + struct flowi6 fl6; + + fl6.daddr = hdr->daddr; + fl6.saddr = hdr->saddr; + fl6.flowlabel = ip6_flowinfo(hdr); + fl6.flowi6_mark = skb->mark; + fl6.flowi6_proto = hdr->nexthdr; + + dst = ip6_route_output(net, NULL, &fl6); + if (dst->error) { + err = dst->error; + dst_release(dst); + goto drop; + } + +#ifdef CONFIG_DST_CACHE + dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); +#endif + } + + skb_dst_drop(skb); + skb_dst_set(skb, dst); + + return dst_output(net, sk, skb); +drop: + kfree_skb(skb); + return err; +} + +static int seg6_build_state(struct net_device *dev, struct nlattr *nla, + unsigned int family, const void *cfg, + struct lwtunnel_state **ts) +{ + struct nlattr *tb[SEG6_IPTUNNEL_MAX + 1]; + struct seg6_iptunnel_encap *tuninfo; + struct lwtunnel_state *newts; + int tuninfo_len, min_size; + struct seg6_lwt *slwt; + int err; + + err = nla_parse_nested(tb, SEG6_IPTUNNEL_MAX, nla, + seg6_iptunnel_policy); + + if (err < 0) + return err; + + if (!tb[SEG6_IPTUNNEL_SRH]) + return -EINVAL; + + tuninfo = nla_data(tb[SEG6_IPTUNNEL_SRH]); + tuninfo_len = nla_len(tb[SEG6_IPTUNNEL_SRH]); + + /* tuninfo must contain at least the iptunnel encap structure, + * the SRH and one segment + */ + min_size = sizeof(*tuninfo) + sizeof(struct ipv6_sr_hdr) + + sizeof(struct in6_addr); + if (tuninfo_len < min_size) + return -EINVAL; + + switch (tuninfo->mode) { +#ifdef CONFIG_IPV6_SEG6_INLINE + case SEG6_IPTUN_MODE_INLINE: + break; +#endif + case SEG6_IPTUN_MODE_ENCAP: + break; + default: + return -EINVAL; + } + + /* verify that SRH is consistent */ + if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo))) + return -EINVAL; + + newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt)); + if (!newts) + return -ENOMEM; + + slwt = seg6_lwt_lwtunnel(newts); + +#ifdef CONFIG_DST_CACHE + err = dst_cache_init(&slwt->cache, GFP_KERNEL); + if (err) { + kfree(newts); + return err; + } +#endif + + memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); + + newts->type = LWTUNNEL_ENCAP_SEG6; + newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | + LWTUNNEL_STATE_INPUT_REDIRECT; + newts->headroom = seg6_lwt_headroom(tuninfo); + + *ts = newts; + + return 0; +} + +#ifdef CONFIG_DST_CACHE +static void seg6_destroy_state(struct lwtunnel_state *lwt) +{ + dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache); +} +#endif + +static int seg6_fill_encap_info(struct sk_buff *skb, + struct lwtunnel_state *lwtstate) +{ + struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); + + if (nla_put_srh(skb, SEG6_IPTUNNEL_SRH, tuninfo)) + return -EMSGSIZE; + + return 0; +} + +static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate) +{ + struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); + + return nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo)); +} + +static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) +{ + struct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a); + struct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b); + int len = SEG6_IPTUN_ENCAP_SIZE(a_hdr); + + if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr)) + return 1; + + return memcmp(a_hdr, b_hdr, len); +} + +static const struct lwtunnel_encap_ops seg6_iptun_ops = { + .build_state = seg6_build_state, +#ifdef CONFIG_DST_CACHE + .destroy_state = seg6_destroy_state, +#endif + .output = seg6_output, + .input = seg6_input, + .fill_encap = seg6_fill_encap_info, + .get_encap_size = seg6_encap_nlsize, + .cmp_encap = seg6_encap_cmp, +}; + +int __init seg6_iptunnel_init(void) +{ + return lwtunnel_encap_add_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); +} + +void seg6_iptunnel_exit(void) +{ + lwtunnel_encap_del_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); +} -- cgit v1.1 From bf355b8d2c30a289232042cacc1cfaea4923936c Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:57:42 +0100 Subject: ipv6: sr: add core files for SR HMAC support This patch adds the necessary functions to compute and check the HMAC signature of an SR-enabled packet. Two HMAC algorithms are supported: hmac(sha1) and hmac(sha256). In order to avoid dynamic memory allocation for each HMAC computation, a per-cpu ring buffer is allocated for this purpose. A new per-interface sysctl called seg6_require_hmac is added, allowing a user-defined policy for processing HMAC-signed SR-enabled packets. A value of -1 means that the HMAC field will always be ignored. A value of 0 means that if an HMAC field is present, its validity will be enforced (the packet is dropped is the signature is incorrect). Finally, a value of 1 means that any SR-enabled packet that does not contain an HMAC signature or whose signature is incorrect will be dropped. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/linux/ipv6.h | 3 + include/linux/seg6_hmac.h | 6 + include/net/seg6.h | 4 + include/net/seg6_hmac.h | 62 ++++++ include/uapi/linux/ipv6.h | 1 + include/uapi/linux/seg6_hmac.h | 21 ++ net/ipv6/Kconfig | 12 + net/ipv6/Makefile | 1 + net/ipv6/addrconf.c | 18 ++ net/ipv6/seg6_hmac.c | 484 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 612 insertions(+) create mode 100644 include/linux/seg6_hmac.h create mode 100644 include/net/seg6_hmac.h create mode 100644 include/uapi/linux/seg6_hmac.h create mode 100644 net/ipv6/seg6_hmac.c diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 68d3f71..9375658 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -65,6 +65,9 @@ struct ipv6_devconf { __s32 use_oif_addrs_only; __s32 keep_addr_on_down; __s32 seg6_enabled; +#ifdef CONFIG_IPV6_SEG6_HMAC + __s32 seg6_require_hmac; +#endif struct ctl_table_header *sysctl_header; }; diff --git a/include/linux/seg6_hmac.h b/include/linux/seg6_hmac.h new file mode 100644 index 0000000..da437eb --- /dev/null +++ b/include/linux/seg6_hmac.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_SEG6_HMAC_H +#define _LINUX_SEG6_HMAC_H + +#include + +#endif diff --git a/include/net/seg6.h b/include/net/seg6.h index ff5da0c..4e03575 100644 --- a/include/net/seg6.h +++ b/include/net/seg6.h @@ -18,6 +18,7 @@ #include #include #include +#include static inline void update_csum_diff4(struct sk_buff *skb, __be32 from, __be32 to) @@ -41,6 +42,9 @@ static inline void update_csum_diff16(struct sk_buff *skb, __be32 *from, struct seg6_pernet_data { struct mutex lock; struct in6_addr __rcu *tun_src; +#ifdef CONFIG_IPV6_SEG6_HMAC + struct rhashtable hmac_infos; +#endif }; static inline struct seg6_pernet_data *seg6_pernet(struct net *net) diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h new file mode 100644 index 0000000..69c3a10 --- /dev/null +++ b/include/net/seg6_hmac.h @@ -0,0 +1,62 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _NET_SEG6_HMAC_H +#define _NET_SEG6_HMAC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SEG6_HMAC_MAX_DIGESTSIZE 160 +#define SEG6_HMAC_RING_SIZE 256 + +struct seg6_hmac_info { + struct rhash_head node; + struct rcu_head rcu; + + u32 hmackeyid; + char secret[SEG6_HMAC_SECRET_LEN]; + u8 slen; + u8 alg_id; +}; + +struct seg6_hmac_algo { + u8 alg_id; + char name[64]; + struct crypto_shash * __percpu *tfms; + struct shash_desc * __percpu *shashs; +}; + +extern int seg6_hmac_compute(struct seg6_hmac_info *hinfo, + struct ipv6_sr_hdr *hdr, struct in6_addr *saddr, + u8 *output); +extern struct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key); +extern int seg6_hmac_info_add(struct net *net, u32 key, + struct seg6_hmac_info *hinfo); +extern int seg6_hmac_info_del(struct net *net, u32 key); +extern int seg6_push_hmac(struct net *net, struct in6_addr *saddr, + struct ipv6_sr_hdr *srh); +extern bool seg6_hmac_validate_skb(struct sk_buff *skb); +extern int seg6_hmac_init(void); +extern void seg6_hmac_exit(void); +extern int seg6_hmac_net_init(struct net *net); +extern void seg6_hmac_net_exit(struct net *net); + +#endif diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 7ff1d65..53561be 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -180,6 +180,7 @@ enum { DEVCONF_KEEP_ADDR_ON_DOWN, DEVCONF_RTR_SOLICIT_MAX_INTERVAL, DEVCONF_SEG6_ENABLED, + DEVCONF_SEG6_REQUIRE_HMAC, DEVCONF_MAX }; diff --git a/include/uapi/linux/seg6_hmac.h b/include/uapi/linux/seg6_hmac.h new file mode 100644 index 0000000..b652dfd --- /dev/null +++ b/include/uapi/linux/seg6_hmac.h @@ -0,0 +1,21 @@ +#ifndef _UAPI_LINUX_SEG6_HMAC_H +#define _UAPI_LINUX_SEG6_HMAC_H + +#include + +#define SEG6_HMAC_SECRET_LEN 64 +#define SEG6_HMAC_FIELD_LEN 32 + +struct sr6_tlv_hmac { + struct sr6_tlv tlvhdr; + __u16 reserved; + __be32 hmackeyid; + __u8 hmac[SEG6_HMAC_FIELD_LEN]; +}; + +enum { + SEG6_HMAC_ALGO_SHA1 = 1, + SEG6_HMAC_ALGO_SHA256 = 2, +}; + +#endif diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 1123a00..0f00811 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -301,4 +301,16 @@ config IPV6_SEG6_INLINE If unsure, say N. +config IPV6_SEG6_HMAC + bool "IPv6: Segment Routing HMAC support" + depends on IPV6 + select CRYPTO_HMAC + select CRYPTO_SHA1 + select CRYPTO_SHA256 + ---help--- + Support for HMAC signature generation and verification + of SR-enabled packets. + + If unsure, say N. + endif # IPV6 diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 59ee92f..129cad2 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_IPV6_SIT) += sit.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o obj-$(CONFIG_IPV6_GRE) += ip6_gre.o obj-$(CONFIG_IPV6_FOU) += fou6.o +obj-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o ip6_icmp.o obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 2ac6cb4..86219c0a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -239,6 +239,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .ignore_routes_with_linkdown = 0, .keep_addr_on_down = 0, .seg6_enabled = 0, +#ifdef CONFIG_IPV6_SEG6_HMAC + .seg6_require_hmac = 0, +#endif }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -286,6 +289,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .ignore_routes_with_linkdown = 0, .keep_addr_on_down = 0, .seg6_enabled = 0, +#ifdef CONFIG_IPV6_SEG6_HMAC + .seg6_require_hmac = 0, +#endif }; /* Check if a valid qdisc is available */ @@ -4947,6 +4953,9 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na; array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down; array[DEVCONF_SEG6_ENABLED] = cnf->seg6_enabled; +#ifdef CONFIG_IPV6_SEG6_HMAC + array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac; +#endif } static inline size_t inet6_ifla6_size(void) @@ -6045,6 +6054,15 @@ static const struct ctl_table addrconf_sysctl[] = { .mode = 0644, .proc_handler = proc_dointvec, }, +#ifdef CONFIG_IPV6_SEG6_HMAC + { + .procname = "seg6_require_hmac", + .data = &ipv6_devconf.seg6_require_hmac, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, +#endif { /* sentinel */ } diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c new file mode 100644 index 0000000..ef1c8a4 --- /dev/null +++ b/net/ipv6/seg6_hmac.c @@ -0,0 +1,484 @@ +/* + * SR-IPv6 implementation -- HMAC functions + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static char * __percpu *hmac_ring; + +static int seg6_hmac_cmpfn(struct rhashtable_compare_arg *arg, const void *obj) +{ + const struct seg6_hmac_info *hinfo = obj; + + return (hinfo->hmackeyid != *(__u32 *)arg->key); +} + +static inline void seg6_hinfo_release(struct seg6_hmac_info *hinfo) +{ + kfree_rcu(hinfo, rcu); +} + +static void seg6_free_hi(void *ptr, void *arg) +{ + struct seg6_hmac_info *hinfo = (struct seg6_hmac_info *)ptr; + + if (hinfo) + seg6_hinfo_release(hinfo); +} + +static const struct rhashtable_params rht_params = { + .head_offset = offsetof(struct seg6_hmac_info, node), + .key_offset = offsetof(struct seg6_hmac_info, hmackeyid), + .key_len = sizeof(u32), + .automatic_shrinking = true, + .obj_cmpfn = seg6_hmac_cmpfn, +}; + +static struct seg6_hmac_algo hmac_algos[] = { + { + .alg_id = SEG6_HMAC_ALGO_SHA1, + .name = "hmac(sha1)", + }, + { + .alg_id = SEG6_HMAC_ALGO_SHA256, + .name = "hmac(sha256)", + }, +}; + +static struct sr6_tlv_hmac *seg6_get_tlv_hmac(struct ipv6_sr_hdr *srh) +{ + struct sr6_tlv_hmac *tlv; + + if (srh->hdrlen < (srh->first_segment + 1) * 2 + 5) + return NULL; + + if (!sr_has_hmac(srh)) + return NULL; + + tlv = (struct sr6_tlv_hmac *) + ((char *)srh + ((srh->hdrlen + 1) << 3) - 40); + + if (tlv->tlvhdr.type != SR6_TLV_HMAC || tlv->tlvhdr.len != 38) + return NULL; + + return tlv; +} + +static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id) +{ + struct seg6_hmac_algo *algo; + int i, alg_count; + + alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo); + for (i = 0; i < alg_count; i++) { + algo = &hmac_algos[i]; + if (algo->alg_id == alg_id) + return algo; + } + + return NULL; +} + +static int __do_hmac(struct seg6_hmac_info *hinfo, const char *text, u8 psize, + u8 *output, int outlen) +{ + struct seg6_hmac_algo *algo; + struct crypto_shash *tfm; + struct shash_desc *shash; + int ret, dgsize; + + algo = __hmac_get_algo(hinfo->alg_id); + if (!algo) + return -ENOENT; + + tfm = *this_cpu_ptr(algo->tfms); + + dgsize = crypto_shash_digestsize(tfm); + if (dgsize > outlen) { + pr_debug("sr-ipv6: __do_hmac: digest size too big (%d / %d)\n", + dgsize, outlen); + return -ENOMEM; + } + + ret = crypto_shash_setkey(tfm, hinfo->secret, hinfo->slen); + if (ret < 0) { + pr_debug("sr-ipv6: crypto_shash_setkey failed: err %d\n", ret); + goto failed; + } + + shash = *this_cpu_ptr(algo->shashs); + shash->tfm = tfm; + + ret = crypto_shash_digest(shash, text, psize, output); + if (ret < 0) { + pr_debug("sr-ipv6: crypto_shash_digest failed: err %d\n", ret); + goto failed; + } + + return dgsize; + +failed: + return ret; +} + +int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, + struct in6_addr *saddr, u8 *output) +{ + __be32 hmackeyid = cpu_to_be32(hinfo->hmackeyid); + u8 tmp_out[SEG6_HMAC_MAX_DIGESTSIZE]; + int plen, i, dgsize, wrsize; + char *ring, *off; + + /* a 160-byte buffer for digest output allows to store highest known + * hash function (RadioGatun) with up to 1216 bits + */ + + /* saddr(16) + first_seg(1) + cleanup(1) + keyid(4) + seglist(16n) */ + plen = 16 + 1 + 1 + 4 + (hdr->first_segment + 1) * 16; + + /* this limit allows for 14 segments */ + if (plen >= SEG6_HMAC_RING_SIZE) + return -EMSGSIZE; + + /* Let's build the HMAC text on the ring buffer. The text is composed + * as follows, in order: + * + * 1. Source IPv6 address (128 bits) + * 2. first_segment value (8 bits) + * 3. cleanup flag (8 bits: highest bit is cleanup value, others are 0) + * 4. HMAC Key ID (32 bits) + * 5. All segments in the segments list (n * 128 bits) + */ + + local_bh_disable(); + ring = *this_cpu_ptr(hmac_ring); + off = ring; + + /* source address */ + memcpy(off, saddr, 16); + off += 16; + + /* first_segment value */ + *off++ = hdr->first_segment; + + /* cleanup flag */ + *off++ = !!(sr_has_cleanup(hdr)) << 7; + + /* HMAC Key ID */ + memcpy(off, &hmackeyid, 4); + off += 4; + + /* all segments in the list */ + for (i = 0; i < hdr->first_segment + 1; i++) { + memcpy(off, hdr->segments + i, 16); + off += 16; + } + + dgsize = __do_hmac(hinfo, ring, plen, tmp_out, + SEG6_HMAC_MAX_DIGESTSIZE); + local_bh_enable(); + + if (dgsize < 0) + return dgsize; + + wrsize = SEG6_HMAC_FIELD_LEN; + if (wrsize > dgsize) + wrsize = dgsize; + + memset(output, 0, SEG6_HMAC_FIELD_LEN); + memcpy(output, tmp_out, wrsize); + + return 0; +} +EXPORT_SYMBOL(seg6_hmac_compute); + +/* checks if an incoming SR-enabled packet's HMAC status matches + * the incoming policy. + * + * called with rcu_read_lock() + */ +bool seg6_hmac_validate_skb(struct sk_buff *skb) +{ + u8 hmac_output[SEG6_HMAC_FIELD_LEN]; + struct net *net = dev_net(skb->dev); + struct seg6_hmac_info *hinfo; + struct sr6_tlv_hmac *tlv; + struct ipv6_sr_hdr *srh; + struct inet6_dev *idev; + + idev = __in6_dev_get(skb->dev); + + srh = (struct ipv6_sr_hdr *)skb_transport_header(skb); + + tlv = seg6_get_tlv_hmac(srh); + + /* mandatory check but no tlv */ + if (idev->cnf.seg6_require_hmac > 0 && !tlv) + return false; + + /* no check */ + if (idev->cnf.seg6_require_hmac < 0) + return true; + + /* check only if present */ + if (idev->cnf.seg6_require_hmac == 0 && !tlv) + return true; + + /* now, seg6_require_hmac >= 0 && tlv */ + + hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid)); + if (!hinfo) + return false; + + if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output)) + return false; + + if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0) + return false; + + return true; +} +EXPORT_SYMBOL(seg6_hmac_validate_skb); + +/* called with rcu_read_lock() */ +struct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key) +{ + struct seg6_pernet_data *sdata = seg6_pernet(net); + struct seg6_hmac_info *hinfo; + + hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params); + + return hinfo; +} +EXPORT_SYMBOL(seg6_hmac_info_lookup); + +int seg6_hmac_info_add(struct net *net, u32 key, struct seg6_hmac_info *hinfo) +{ + struct seg6_pernet_data *sdata = seg6_pernet(net); + int err; + + err = rhashtable_lookup_insert_fast(&sdata->hmac_infos, &hinfo->node, + rht_params); + + return err; +} +EXPORT_SYMBOL(seg6_hmac_info_add); + +int seg6_hmac_info_del(struct net *net, u32 key) +{ + struct seg6_pernet_data *sdata = seg6_pernet(net); + struct seg6_hmac_info *hinfo; + int err = -ENOENT; + + hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params); + if (!hinfo) + goto out; + + err = rhashtable_remove_fast(&sdata->hmac_infos, &hinfo->node, + rht_params); + if (err) + goto out; + + seg6_hinfo_release(hinfo); + +out: + return err; +} +EXPORT_SYMBOL(seg6_hmac_info_del); + +int seg6_push_hmac(struct net *net, struct in6_addr *saddr, + struct ipv6_sr_hdr *srh) +{ + struct seg6_hmac_info *hinfo; + struct sr6_tlv_hmac *tlv; + int err = -ENOENT; + + tlv = seg6_get_tlv_hmac(srh); + if (!tlv) + return -EINVAL; + + rcu_read_lock(); + + hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid)); + if (!hinfo) + goto out; + + memset(tlv->hmac, 0, SEG6_HMAC_FIELD_LEN); + err = seg6_hmac_compute(hinfo, srh, saddr, tlv->hmac); + +out: + rcu_read_unlock(); + return err; +} +EXPORT_SYMBOL(seg6_push_hmac); + +static int seg6_hmac_init_ring(void) +{ + int i; + + hmac_ring = alloc_percpu(char *); + + if (!hmac_ring) + return -ENOMEM; + + for_each_possible_cpu(i) { + char *ring = kzalloc(SEG6_HMAC_RING_SIZE, GFP_KERNEL); + + if (!ring) + return -ENOMEM; + + *per_cpu_ptr(hmac_ring, i) = ring; + } + + return 0; +} + +static int seg6_hmac_init_algo(void) +{ + struct seg6_hmac_algo *algo; + struct crypto_shash *tfm; + struct shash_desc *shash; + int i, alg_count, cpu; + + alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo); + + for (i = 0; i < alg_count; i++) { + struct crypto_shash **p_tfm; + int shsize; + + algo = &hmac_algos[i]; + algo->tfms = alloc_percpu(struct crypto_shash *); + if (!algo->tfms) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + tfm = crypto_alloc_shash(algo->name, 0, GFP_KERNEL); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + p_tfm = per_cpu_ptr(algo->tfms, cpu); + *p_tfm = tfm; + } + + p_tfm = this_cpu_ptr(algo->tfms); + tfm = *p_tfm; + + shsize = sizeof(*shash) + crypto_shash_descsize(tfm); + + algo->shashs = alloc_percpu(struct shash_desc *); + if (!algo->shashs) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + shash = kzalloc(shsize, GFP_KERNEL); + if (!shash) + return -ENOMEM; + *per_cpu_ptr(algo->shashs, cpu) = shash; + } + } + + return 0; +} + +int __init seg6_hmac_init(void) +{ + int ret; + + ret = seg6_hmac_init_ring(); + if (ret < 0) + goto out; + + ret = seg6_hmac_init_algo(); + +out: + return ret; +} +EXPORT_SYMBOL(seg6_hmac_init); + +int __net_init seg6_hmac_net_init(struct net *net) +{ + struct seg6_pernet_data *sdata = seg6_pernet(net); + + rhashtable_init(&sdata->hmac_infos, &rht_params); + + return 0; +} +EXPORT_SYMBOL(seg6_hmac_net_init); + +void seg6_hmac_exit(void) +{ + struct seg6_hmac_algo *algo = NULL; + int i, alg_count, cpu; + + for_each_possible_cpu(i) { + char *ring = *per_cpu_ptr(hmac_ring, i); + + kfree(ring); + } + free_percpu(hmac_ring); + + alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo); + for (i = 0; i < alg_count; i++) { + algo = &hmac_algos[i]; + for_each_possible_cpu(cpu) { + struct crypto_shash *tfm; + struct shash_desc *shash; + + shash = *per_cpu_ptr(algo->shashs, cpu); + kfree(shash); + tfm = *per_cpu_ptr(algo->tfms, cpu); + crypto_free_shash(tfm); + } + free_percpu(algo->tfms); + free_percpu(algo->shashs); + } +} +EXPORT_SYMBOL(seg6_hmac_exit); + +void __net_exit seg6_hmac_net_exit(struct net *net) +{ + struct seg6_pernet_data *sdata = seg6_pernet(net); + + rhashtable_free_and_destroy(&sdata->hmac_infos, seg6_free_hi, NULL); +} +EXPORT_SYMBOL(seg6_hmac_net_exit); -- cgit v1.1 From 4f4853dc1c9c1994f6f756eabdcc25374ff271d9 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:59:18 +0100 Subject: ipv6: sr: implement API to control SR HMAC structure This patch provides an implementation of the genetlink commands to associate a given HMAC key identifier with an hashing algorithm and a secret. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 9c78053..50f6e06 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -25,6 +25,9 @@ #include #include #include +#ifdef CONFIG_IPV6_SEG6_HMAC +#include +#endif bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len) { @@ -76,11 +79,90 @@ static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = { [SEG6_ATTR_HMACINFO] = { .type = NLA_NESTED, }, }; +#ifdef CONFIG_IPV6_SEG6_HMAC + +static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct seg6_pernet_data *sdata; + struct seg6_hmac_info *hinfo; + u32 hmackeyid; + char *secret; + int err = 0; + u8 algid; + u8 slen; + + sdata = seg6_pernet(net); + + if (!info->attrs[SEG6_ATTR_HMACKEYID] || + !info->attrs[SEG6_ATTR_SECRETLEN] || + !info->attrs[SEG6_ATTR_ALGID]) + return -EINVAL; + + hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]); + slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]); + algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]); + + if (hmackeyid == 0) + return -EINVAL; + + if (slen > SEG6_HMAC_SECRET_LEN) + return -EINVAL; + + mutex_lock(&sdata->lock); + hinfo = seg6_hmac_info_lookup(net, hmackeyid); + + if (!slen) { + if (!hinfo) + err = -ENOENT; + + err = seg6_hmac_info_del(net, hmackeyid); + + goto out_unlock; + } + + if (!info->attrs[SEG6_ATTR_SECRET]) { + err = -EINVAL; + goto out_unlock; + } + + if (hinfo) { + err = seg6_hmac_info_del(net, hmackeyid); + if (err) + goto out_unlock; + } + + secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]); + + hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL); + if (!hinfo) { + err = -ENOMEM; + goto out_unlock; + } + + memcpy(hinfo->secret, secret, slen); + hinfo->slen = slen; + hinfo->alg_id = algid; + hinfo->hmackeyid = hmackeyid; + + err = seg6_hmac_info_add(net, hmackeyid, hinfo); + if (err) + kfree(hinfo); + +out_unlock: + mutex_unlock(&sdata->lock); + return err; +} + +#else + static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) { return -ENOTSUPP; } +#endif + static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info) { struct net *net = genl_info_net(info); @@ -145,11 +227,135 @@ free_msg: return -ENOMEM; } +#ifdef CONFIG_IPV6_SEG6_HMAC + +static int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo, + struct sk_buff *msg) +{ + if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) || + nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) || + nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) || + nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id)) + return -1; + + return 0; +} + +static int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo, + u32 portid, u32 seq, u32 flags, + struct sk_buff *skb, u8 cmd) +{ + void *hdr; + + hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd); + if (!hdr) + return -ENOMEM; + + if (__seg6_hmac_fill_info(hinfo, skb) < 0) + goto nla_put_failure; + + genlmsg_end(skb, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; +} + +static int seg6_genl_dumphmac_start(struct netlink_callback *cb) +{ + struct net *net = sock_net(cb->skb->sk); + struct seg6_pernet_data *sdata; + struct rhashtable_iter *iter; + + sdata = seg6_pernet(net); + iter = (struct rhashtable_iter *)cb->args[0]; + + if (!iter) { + iter = kmalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + cb->args[0] = (long)iter; + } + + rhashtable_walk_enter(&sdata->hmac_infos, iter); + + return 0; +} + +static int seg6_genl_dumphmac_done(struct netlink_callback *cb) +{ + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; + + rhashtable_walk_exit(iter); + + kfree(iter); + + return 0; +} + +static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; + struct net *net = sock_net(skb->sk); + struct seg6_pernet_data *sdata; + struct seg6_hmac_info *hinfo; + int ret; + + sdata = seg6_pernet(net); + + ret = rhashtable_walk_start(iter); + if (ret && ret != -EAGAIN) + goto done; + + for (;;) { + hinfo = rhashtable_walk_next(iter); + + if (IS_ERR(hinfo)) { + if (PTR_ERR(hinfo) == -EAGAIN) + continue; + ret = PTR_ERR(hinfo); + goto done; + } else if (!hinfo) { + break; + } + + ret = __seg6_genl_dumphmac_element(hinfo, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI, + skb, SEG6_CMD_DUMPHMAC); + if (ret) + goto done; + } + + ret = skb->len; + +done: + rhashtable_walk_stop(iter); + return ret; +} + +#else + +static int seg6_genl_dumphmac_start(struct netlink_callback *cb) +{ + return 0; +} + +static int seg6_genl_dumphmac_done(struct netlink_callback *cb) +{ + return 0; +} + static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) { return -ENOTSUPP; } +#endif + static int __net_init seg6_net_init(struct net *net) { struct seg6_pernet_data *sdata; @@ -168,6 +374,10 @@ static int __net_init seg6_net_init(struct net *net) net->ipv6.seg6_data = sdata; +#ifdef CONFIG_IPV6_SEG6_HMAC + seg6_hmac_net_init(net); +#endif + return 0; } @@ -175,6 +385,10 @@ static void __net_exit seg6_net_exit(struct net *net) { struct seg6_pernet_data *sdata = seg6_pernet(net); +#ifdef CONFIG_IPV6_SEG6_HMAC + seg6_hmac_net_exit(net); +#endif + kfree(sdata->tun_src); kfree(sdata); } @@ -193,7 +407,9 @@ static const struct genl_ops seg6_genl_ops[] = { }, { .cmd = SEG6_CMD_DUMPHMAC, + .start = seg6_genl_dumphmac_start, .dumpit = seg6_genl_dumphmac, + .done = seg6_genl_dumphmac_done, .policy = seg6_genl_policy, .flags = GENL_ADMIN_PERM, }, @@ -239,10 +455,20 @@ int __init seg6_init(void) if (err) goto out_unregister_pernet; +#ifdef CONFIG_IPV6_SEG6_HMAC + err = seg6_hmac_init(); + if (err) + goto out_unregister_iptun; +#endif + pr_info("Segment Routing with IPv6\n"); out: return err; +#ifdef CONFIG_IPV6_SEG6_HMAC +out_unregister_iptun: + seg6_iptunnel_exit(); +#endif out_unregister_pernet: unregister_pernet_subsys(&ip6_segments_ops); out_unregister_genl: @@ -252,6 +478,9 @@ out_unregister_genl: void seg6_exit(void) { +#ifdef CONFIG_IPV6_SEG6_HMAC + seg6_hmac_exit(); +#endif seg6_iptunnel_exit(); unregister_pernet_subsys(&ip6_segments_ops); genl_unregister_family(&seg6_genl_family); -- cgit v1.1 From 9baee83406d6a4b02222f5ee21511c3f4c19e39d Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:59:19 +0100 Subject: ipv6: sr: add calls to verify and insert HMAC signatures This patch enables the verification of the HMAC signature for transiting SR-enabled packets, and its insertion on encapsulated/injected SRH. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/exthdrs.c | 10 ++++++++++ net/ipv6/seg6_iptunnel.c | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index b8ba396..541cfa6 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -49,6 +49,9 @@ #endif #include #include +#ifdef CONFIG_IPV6_SEG6_HMAC +#include +#endif #include @@ -340,6 +343,13 @@ static int ipv6_srh_rcv(struct sk_buff *skb) return -1; } +#ifdef CONFIG_IPV6_SEG6_HMAC + if (!seg6_hmac_validate_skb(skb)) { + kfree_skb(skb); + return -1; + } +#endif + looped_back: if (hdr->segments_left > 0) { if (hdr->nexthdr != NEXTHDR_IPV6 && hdr->segments_left == 1 && diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 39762b2..bbfca22 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -29,6 +29,9 @@ #ifdef CONFIG_DST_CACHE #include #endif +#ifdef CONFIG_IPV6_SEG6_HMAC +#include +#endif struct seg6_lwt { #ifdef CONFIG_DST_CACHE @@ -130,6 +133,14 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) hdr->daddr = isrh->segments[isrh->first_segment]; set_tun_src(net, skb->dev, &hdr->daddr, &hdr->saddr); +#ifdef CONFIG_IPV6_SEG6_HMAC + if (sr_has_hmac(isrh)) { + err = seg6_push_hmac(net, &hdr->saddr, isrh); + if (unlikely(err)) + return err; + } +#endif + skb_postpush_rcsum(skb, hdr, tot_len); return 0; @@ -172,6 +183,16 @@ static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) isrh->segments[0] = hdr->daddr; hdr->daddr = isrh->segments[isrh->first_segment]; +#ifdef CONFIG_IPV6_SEG6_HMAC + if (sr_has_hmac(isrh)) { + struct net *net = dev_net(skb_dst(skb)->dev); + + err = seg6_push_hmac(net, &hdr->saddr, isrh); + if (unlikely(err)) + return err; + } +#endif + skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen); return 0; -- cgit v1.1 From 613fa3ca9e9e6af57927dab238121010c510fe4c Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:59:20 +0100 Subject: ipv6: add source address argument for ipv6_push_nfrag_opts This patch prepares for insertion of SRH through setsockopt(). The new source address argument is used when an HMAC field is present in the SRH, which must be filled. The HMAC signature process requires the source address as input text. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/net/ipv6.h | 3 ++- net/ipv6/exthdrs.c | 6 +++--- net/ipv6/ip6_output.c | 5 +++-- net/ipv6/ip6_tunnel.c | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 8fed1cd..0a3622b 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -932,7 +932,8 @@ int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb); */ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, - u8 *proto, struct in6_addr **daddr_p); + u8 *proto, struct in6_addr **daddr_p, + struct in6_addr *saddr); void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto); diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 541cfa6..72aadce 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -866,7 +866,7 @@ int ipv6_parse_hopopts(struct sk_buff *skb) static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, struct ipv6_rt_hdr *opt, - struct in6_addr **addr_p) + struct in6_addr **addr_p, struct in6_addr *saddr) { struct rt0_hdr *phdr, *ihdr; int hops; @@ -900,10 +900,10 @@ static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto, - struct in6_addr **daddr) + struct in6_addr **daddr, struct in6_addr *saddr) { if (opt->srcrt) { - ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); + ipv6_push_rthdr(skb, proto, opt->srcrt, daddr, saddr); /* * IPV6_RTHDRDSTOPTS is ignored * unless IPV6_RTHDR is set (RFC3542). diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 6001e78..ddc878d 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -203,7 +203,8 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, if (opt->opt_flen) ipv6_push_frag_opts(skb, opt, &proto); if (opt->opt_nflen) - ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop); + ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop, + &fl6->saddr); } skb_push(skb, sizeof(struct ipv6hdr)); @@ -1672,7 +1673,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, if (opt && opt->opt_flen) ipv6_push_frag_opts(skb, opt, &proto); if (opt && opt->opt_nflen) - ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst); + ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst, &fl6->saddr); skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index bfa889c..259e850 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1157,7 +1157,7 @@ route_lookup: if (encap_limit >= 0) { init_tel_txopt(&opt, encap_limit); - ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL); + ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL, NULL); } /* Calculate max headroom for all the headers and adjust -- cgit v1.1 From a149e7c7ce812561f0fdc7a86ddc42f294e5eb3e Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:59:21 +0100 Subject: ipv6: sr: add support for SRH injection through setsockopt This patch adds support for per-socket SRH injection with the setsockopt system call through the IPPROTO_IPV6, IPV6_RTHDR options. The SRH is pushed through the ipv6_push_nfrag_opts function. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/exthdrs.c | 79 +++++++++++++++++++++++++++++++++++++++++++++--- net/ipv6/ipv6_sockglue.c | 10 ++++++ 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 72aadce..926818c 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -864,9 +864,9 @@ int ipv6_parse_hopopts(struct sk_buff *skb) * for headers. */ -static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, - struct ipv6_rt_hdr *opt, - struct in6_addr **addr_p, struct in6_addr *saddr) +static void ipv6_push_rthdr0(struct sk_buff *skb, u8 *proto, + struct ipv6_rt_hdr *opt, + struct in6_addr **addr_p, struct in6_addr *saddr) { struct rt0_hdr *phdr, *ihdr; int hops; @@ -889,6 +889,62 @@ static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, *proto = NEXTHDR_ROUTING; } +static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto, + struct ipv6_rt_hdr *opt, + struct in6_addr **addr_p, struct in6_addr *saddr) +{ + struct ipv6_sr_hdr *sr_phdr, *sr_ihdr; + int plen, hops; + + sr_ihdr = (struct ipv6_sr_hdr *)opt; + plen = (sr_ihdr->hdrlen + 1) << 3; + + sr_phdr = (struct ipv6_sr_hdr *)skb_push(skb, plen); + memcpy(sr_phdr, sr_ihdr, sizeof(struct ipv6_sr_hdr)); + + hops = sr_ihdr->first_segment + 1; + memcpy(sr_phdr->segments + 1, sr_ihdr->segments + 1, + (hops - 1) * sizeof(struct in6_addr)); + + sr_phdr->segments[0] = **addr_p; + *addr_p = &sr_ihdr->segments[hops - 1]; + +#ifdef CONFIG_IPV6_SEG6_HMAC + if (sr_has_hmac(sr_phdr)) { + struct net *net = NULL; + + if (skb->dev) + net = dev_net(skb->dev); + else if (skb->sk) + net = sock_net(skb->sk); + + WARN_ON(!net); + + if (net) + seg6_push_hmac(net, saddr, sr_phdr); + } +#endif + + sr_phdr->nexthdr = *proto; + *proto = NEXTHDR_ROUTING; +} + +static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, + struct ipv6_rt_hdr *opt, + struct in6_addr **addr_p, struct in6_addr *saddr) +{ + switch (opt->type) { + case IPV6_SRCRT_TYPE_0: + ipv6_push_rthdr0(skb, proto, opt, addr_p, saddr); + break; + case IPV6_SRCRT_TYPE_4: + ipv6_push_rthdr4(skb, proto, opt, addr_p, saddr); + break; + default: + break; + } +} + static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt) { struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt)); @@ -1130,7 +1186,22 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6, return NULL; *orig = fl6->daddr; - fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr; + + switch (opt->srcrt->type) { + case IPV6_SRCRT_TYPE_0: + fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr; + break; + case IPV6_SRCRT_TYPE_4: + { + struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt; + + fl6->daddr = srh->segments[srh->first_segment]; + break; + } + default: + return NULL; + } + return orig; } EXPORT_SYMBOL_GPL(fl6_update_dst); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 6c12678..3ba5303 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -52,6 +52,7 @@ #include #include #include +#include #include @@ -430,6 +431,15 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, break; #endif + case IPV6_SRCRT_TYPE_4: + { + struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *) + opt->srcrt; + + if (!seg6_validate_srh(srh, optlen)) + goto sticky_done; + break; + } default: goto sticky_done; } -- cgit v1.1 From 8bc66a4423dba1ffafddd52b68ddad4adff39648 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 8 Nov 2016 14:59:22 +0100 Subject: ipv6: sr: add documentation file for per-interface sysctls This patch adds documentation for some SR-related per-interface sysctls. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- Documentation/networking/seg6-sysctl.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Documentation/networking/seg6-sysctl.txt diff --git a/Documentation/networking/seg6-sysctl.txt b/Documentation/networking/seg6-sysctl.txt new file mode 100644 index 0000000..bdbde23 --- /dev/null +++ b/Documentation/networking/seg6-sysctl.txt @@ -0,0 +1,18 @@ +/proc/sys/net/conf//seg6_* variables: + +seg6_enabled - BOOL + Accept or drop SR-enabled IPv6 packets on this interface. + + Relevant packets are those with SRH present and DA = local. + + 0 - disabled (default) + not 0 - enabled + +seg6_require_hmac - INTEGER + Define HMAC policy for ingress SR-enabled packets on this interface. + + -1 - Ignore HMAC field + 0 - Accept SR packets without HMAC, validate SR packets with HMAC + 1 - Drop SR packets without HMAC, validate SR packets with HMAC + + Default is 0. -- cgit v1.1 From f41cd11d64b2b21012eb4abffbe579bc0b90467f Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Tue, 8 Nov 2016 17:24:03 +0200 Subject: tc_act: Remove tcf_act macro tc_act macro addressed a non existing field, and was not used in the kernel source. Signed-off-by: Yotam Gigi Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/act_api.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/act_api.h b/include/net/act_api.h index 82f3c91..d8eae87 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -42,7 +42,6 @@ struct tc_action { struct gnet_stats_basic_cpu __percpu *cpu_bstats; struct gnet_stats_queue __percpu *cpu_qstats; }; -#define tcf_act common.tcfa_act #define tcf_head common.tcfa_head #define tcf_index common.tcfa_index #define tcf_refcnt common.tcfa_refcnt -- cgit v1.1 From de464375daf0d10f04fa5add2e889f42328d2ade Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 8 Nov 2016 16:40:28 +0100 Subject: bpf: Remove unused but set variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the unused but set variables min_set and max_set in adjust_reg_min_max_vals to fix the following warning when building with 'W=1': kernel/bpf/verifier.c:1483:7: warning: variable ‘min_set’ set but not used [-Wunused-but-set-variable] There is no warning about max_set being unused, but since it is only used in the assignment of min_set it can be removed as well. They were introduced in commit 484611357c19 ("bpf: allow access into map value arrays") but seem to have never been used. Cc: Josef Bacik Signed-off-by: Tobias Klauser Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/verifier.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9002575..89f787c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1499,7 +1499,6 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env, { struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg; u64 min_val = BPF_REGISTER_MIN_RANGE, max_val = BPF_REGISTER_MAX_RANGE; - bool min_set = false, max_set = false; u8 opcode = BPF_OP(insn->code); dst_reg = ®s[insn->dst_reg]; @@ -1522,7 +1521,6 @@ static void adjust_reg_min_max_vals(struct bpf_verifier_env *env, } else if (insn->imm < BPF_REGISTER_MAX_RANGE && (s64)insn->imm > BPF_REGISTER_MIN_RANGE) { min_val = max_val = insn->imm; - min_set = max_set = true; } /* We don't know anything about what was done to this register, mark it -- cgit v1.1 From ef8d759b5251ee9d6784fe53d90220bd91ee477f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 8 Nov 2016 11:06:53 -0800 Subject: bnxt_en: do not call napi_hash_add() This is automatically done from netif_napi_add(), and we want to not export napi_hash_add() anymore in the following patch. Signed-off-by: Eric Dumazet Cc: Michael Chan Acked-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index a042da1..d313b02 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4954,7 +4954,6 @@ static void bnxt_init_napi(struct bnxt *bp) bnapi = bp->bnapi[cp_nr_rings]; netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll_nitroa0, 64); - napi_hash_add(&bnapi->napi); } } else { bnapi = bp->bnapi[0]; -- cgit v1.1 From 149d6ad83663b4820ca09c9d40b1eea7f5c22c2b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 8 Nov 2016 11:07:28 -0800 Subject: net: napi_hash_add() is no longer exported There are no more users except from net/core/dev.c napi_hash_add() can now be static. Signed-off-by: Eric Dumazet Cc: Michael Chan Signed-off-by: David S. Miller --- include/linux/netdevice.h | 11 ----------- net/core/dev.c | 3 +-- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 66fd61c..d64135a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -468,17 +468,6 @@ static inline void napi_complete(struct napi_struct *n) } /** - * napi_hash_add - add a NAPI to global hashtable - * @napi: NAPI context - * - * Generate a new napi_id and store a @napi under it in napi_hash. - * Used for busy polling (CONFIG_NET_RX_BUSY_POLL). - * Note: This is normally automatically done from netif_napi_add(), - * so might disappear in a future Linux version. - */ -void napi_hash_add(struct napi_struct *napi); - -/** * napi_hash_del - remove a NAPI from global table * @napi: NAPI context * diff --git a/net/core/dev.c b/net/core/dev.c index c9837fa..7385c1a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5017,7 +5017,7 @@ EXPORT_SYMBOL(sk_busy_loop); #endif /* CONFIG_NET_RX_BUSY_POLL */ -void napi_hash_add(struct napi_struct *napi) +static void napi_hash_add(struct napi_struct *napi) { if (test_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state) || test_and_set_bit(NAPI_STATE_HASHED, &napi->state)) @@ -5037,7 +5037,6 @@ void napi_hash_add(struct napi_struct *napi) spin_unlock(&napi_hash_lock); } -EXPORT_SYMBOL_GPL(napi_hash_add); /* Warning : caller is responsible to make sure rcu grace period * is respected before freeing memory containing @napi -- cgit v1.1 From d8d26354191399627bac9cf0da0667b0f5178686 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 8 Nov 2016 22:49:16 +0100 Subject: ptp: Introduce a high resolution frequency adjustment method. The internal PTP Hardware Clock (PHC) interface limits the resolution for frequency adjustments to one part per billion. However, some hardware devices allow finer adjustment, and making use of the increased resolution improves synchronization measurably on such devices. This patch adds an alternative method that allows finer frequency tuning by passing the scaled ppm value to PHC drivers. This value comes from user space, and it has a resolution of about 0.015 ppb. We also deprecate the older method, anticipating its removal once existing drivers have been converted over. Signed-off-by: Richard Cochran Suggested-by: Ulrik De Bie Signed-off-by: David S. Miller --- drivers/ptp/ptp_clock.c | 5 ++++- include/linux/ptp_clock_kernel.h | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 86280b7..9c13381 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -153,7 +153,10 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx) s32 ppb = scaled_ppm_to_ppb(tx->freq); if (ppb > ops->max_adj || ppb < -ops->max_adj) return -ERANGE; - err = ops->adjfreq(ops, ppb); + if (ops->adjfine) + err = ops->adjfine(ops, tx->freq); + else + err = ops->adjfreq(ops, ppb); ptp->dialed_frequency = tx->freq; } else if (tx->modes == 0) { tx->freq = ptp->dialed_frequency; diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index 5ad54fc..b76d47a 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -58,7 +58,14 @@ struct system_device_crosststamp; * * clock operations * + * @adjfine: Adjusts the frequency of the hardware clock. + * parameter scaled_ppm: Desired frequency offset from + * nominal frequency in parts per million, but with a + * 16 bit binary fractional field. + * * @adjfreq: Adjusts the frequency of the hardware clock. + * This method is deprecated. New drivers should implement + * the @adjfine method instead. * parameter delta: Desired frequency offset from nominal frequency * in parts per billion * @@ -108,6 +115,7 @@ struct ptp_clock_info { int n_pins; int pps; struct ptp_pin_desc *pin_config; + int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm); int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts); -- cgit v1.1 From c79e975e1f4375b4524f4b901574c73b69316ead Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 8 Nov 2016 22:49:17 +0100 Subject: ptp: igb: Use the high resolution frequency method. The 82580 and related devices offer a frequency resolution of about 0.029 ppb. This patch lets users of the device benefit from the increased frequency resolution when tuning the clock. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/igb/igb_ptp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index a7895c4..c30eea8 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -226,7 +226,7 @@ static int igb_ptp_adjfreq_82576(struct ptp_clock_info *ptp, s32 ppb) return 0; } -static int igb_ptp_adjfreq_82580(struct ptp_clock_info *ptp, s32 ppb) +static int igb_ptp_adjfine_82580(struct ptp_clock_info *ptp, long scaled_ppm) { struct igb_adapter *igb = container_of(ptp, struct igb_adapter, ptp_caps); @@ -235,13 +235,13 @@ static int igb_ptp_adjfreq_82580(struct ptp_clock_info *ptp, s32 ppb) u64 rate; u32 inca; - if (ppb < 0) { + if (scaled_ppm < 0) { neg_adj = 1; - ppb = -ppb; + scaled_ppm = -scaled_ppm; } - rate = ppb; - rate <<= 26; - rate = div_u64(rate, 1953125); + rate = scaled_ppm; + rate <<= 13; + rate = div_u64(rate, 15625); inca = rate & INCVALUE_MASK; if (neg_adj) @@ -1103,7 +1103,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.max_adj = 62499999; adapter->ptp_caps.n_ext_ts = 0; adapter->ptp_caps.pps = 0; - adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82580; + adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580; adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576; adapter->ptp_caps.gettime64 = igb_ptp_gettime_82576; adapter->ptp_caps.settime64 = igb_ptp_settime_82576; @@ -1131,7 +1131,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.n_pins = IGB_N_SDP; adapter->ptp_caps.pps = 1; adapter->ptp_caps.pin_config = adapter->sdp_config; - adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82580; + adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580; adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210; adapter->ptp_caps.gettime64 = igb_ptp_gettime_i210; adapter->ptp_caps.settime64 = igb_ptp_settime_i210; -- cgit v1.1 From e4788b800f66718f0736bca385da548e48d39b8e Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 8 Nov 2016 22:49:18 +0100 Subject: ptp: dp83640: Use the high resolution frequency method. The dp83640 has a frequency resolution of about 0.029 ppb. This patch lets users of the device benefit from the increased frequency resolution when tuning the clock. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/phy/dp83640.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 7a240fc..e2460a5 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -375,7 +375,7 @@ static int periodic_output(struct dp83640_clock *clock, /* ptp clock methods */ -static int ptp_dp83640_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ptp_dp83640_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct dp83640_clock *clock = container_of(ptp, struct dp83640_clock, caps); @@ -384,13 +384,13 @@ static int ptp_dp83640_adjfreq(struct ptp_clock_info *ptp, s32 ppb) int neg_adj = 0; u16 hi, lo; - if (ppb < 0) { + if (scaled_ppm < 0) { neg_adj = 1; - ppb = -ppb; + scaled_ppm = -scaled_ppm; } - rate = ppb; - rate <<= 26; - rate = div_u64(rate, 1953125); + rate = scaled_ppm; + rate <<= 13; + rate = div_u64(rate, 15625); hi = (rate >> 16) & PTP_RATE_HI_MASK; if (neg_adj) @@ -1035,7 +1035,7 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus) clock->caps.n_per_out = N_PER_OUT; clock->caps.n_pins = DP83640_N_PINS; clock->caps.pps = 0; - clock->caps.adjfreq = ptp_dp83640_adjfreq; + clock->caps.adjfine = ptp_dp83640_adjfine; clock->caps.adjtime = ptp_dp83640_adjtime; clock->caps.gettime64 = ptp_dp83640_gettime; clock->caps.settime64 = ptp_dp83640_settime; -- cgit v1.1 From f522a5fcf6dbba92f05bee5bb07f70fa8e521ab2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 Nov 2016 11:24:22 -0800 Subject: tcp: remove unaligned accesses from tcp_get_info() After commit 6ed46d1247a5 ("sock_diag: align nlattr properly when needed"), tcp_get_info() gets 64bit aligned memory, so we can avoid the unaligned helpers. Suggested-by: David Miller Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index a7d54cb..f8f924c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -279,7 +279,6 @@ #include #include -#include #include int sysctl_tcp_min_tso_segs __read_mostly = 2; @@ -2722,11 +2721,11 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) /* Report meaningful fields for all TCP states, including listeners */ rate = READ_ONCE(sk->sk_pacing_rate); rate64 = rate != ~0U ? rate : ~0ULL; - put_unaligned(rate64, &info->tcpi_pacing_rate); + info->tcpi_pacing_rate = rate64; rate = READ_ONCE(sk->sk_max_pacing_rate); rate64 = rate != ~0U ? rate : ~0ULL; - put_unaligned(rate64, &info->tcpi_max_pacing_rate); + info->tcpi_max_pacing_rate = rate64; info->tcpi_reordering = tp->reordering; info->tcpi_snd_cwnd = tp->snd_cwnd; @@ -2792,8 +2791,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) slow = lock_sock_fast(sk); - put_unaligned(tp->bytes_acked, &info->tcpi_bytes_acked); - put_unaligned(tp->bytes_received, &info->tcpi_bytes_received); + info->tcpi_bytes_acked = tp->bytes_acked; + info->tcpi_bytes_received = tp->bytes_received; info->tcpi_notsent_bytes = max_t(int, 0, tp->write_seq - tp->snd_nxt); unlock_sock_fast(sk, slow); @@ -2811,7 +2810,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) if (rate && intv) { rate64 = (u64)rate * tp->mss_cache * USEC_PER_SEC; do_div(rate64, intv); - put_unaligned(rate64, &info->tcpi_delivery_rate); + info->tcpi_delivery_rate = rate64; } } EXPORT_SYMBOL_GPL(tcp_get_info); -- cgit v1.1 From 2da16a6948ca8f025e2c226ea4fc32baa6b90f27 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 11:17:25 +0100 Subject: netfilter: ipset: Remove extra whitespaces in ip_set.h Remove unnecessary whitespaces. Ported from a patch proposed by Sergey Popovich . Suggested-by: Sergey Popovich Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 83b9a2e..5b1fd09 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -336,14 +336,15 @@ ip_set_update_counter(struct ip_set_counter *counter, static inline void ip_set_get_skbinfo(struct ip_set_skbinfo *skbinfo, - const struct ip_set_ext *ext, - struct ip_set_ext *mext, u32 flags) + const struct ip_set_ext *ext, + struct ip_set_ext *mext, u32 flags) { - mext->skbmark = skbinfo->skbmark; - mext->skbmarkmask = skbinfo->skbmarkmask; - mext->skbprio = skbinfo->skbprio; - mext->skbqueue = skbinfo->skbqueue; + mext->skbmark = skbinfo->skbmark; + mext->skbmarkmask = skbinfo->skbmarkmask; + mext->skbprio = skbinfo->skbprio; + mext->skbqueue = skbinfo->skbqueue; } + static inline bool ip_set_put_skbinfo(struct sk_buff *skb, struct ip_set_skbinfo *skbinfo) { -- cgit v1.1 From da9fbfa76f32a031cb70b11e9fa650e30c85d040 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 11:24:15 +0100 Subject: netfilter: ipset: Mark some helper args as const. Mark some of the helpers arguments as const. Ported from a patch proposed by Sergey Popovich . Suggested-by: Sergey Popovich Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 4 ++-- include/linux/netfilter/ipset/ip_set_comment.h | 2 +- include/linux/netfilter/ipset/ip_set_timeout.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 5b1fd09..524467f 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -346,7 +346,7 @@ ip_set_get_skbinfo(struct ip_set_skbinfo *skbinfo, } static inline bool -ip_set_put_skbinfo(struct sk_buff *skb, struct ip_set_skbinfo *skbinfo) +ip_set_put_skbinfo(struct sk_buff *skb, const struct ip_set_skbinfo *skbinfo) { /* Send nonzero parameters only */ return ((skbinfo->skbmark || skbinfo->skbmarkmask) && @@ -373,7 +373,7 @@ ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo, } static inline bool -ip_set_put_counter(struct sk_buff *skb, struct ip_set_counter *counter) +ip_set_put_counter(struct sk_buff *skb, const struct ip_set_counter *counter) { return nla_put_net64(skb, IPSET_ATTR_BYTES, cpu_to_be64(ip_set_get_bytes(counter)), diff --git a/include/linux/netfilter/ipset/ip_set_comment.h b/include/linux/netfilter/ipset/ip_set_comment.h index 8d02485..bae5c76 100644 --- a/include/linux/netfilter/ipset/ip_set_comment.h +++ b/include/linux/netfilter/ipset/ip_set_comment.h @@ -43,7 +43,7 @@ ip_set_init_comment(struct ip_set_comment *comment, /* Used only when dumping a set, protected by rcu_read_lock_bh() */ static inline int -ip_set_put_comment(struct sk_buff *skb, struct ip_set_comment *comment) +ip_set_put_comment(struct sk_buff *skb, const struct ip_set_comment *comment) { struct ip_set_comment_rcu *c = rcu_dereference_bh(comment->c); diff --git a/include/linux/netfilter/ipset/ip_set_timeout.h b/include/linux/netfilter/ipset/ip_set_timeout.h index 1d6a935..bfb3531 100644 --- a/include/linux/netfilter/ipset/ip_set_timeout.h +++ b/include/linux/netfilter/ipset/ip_set_timeout.h @@ -40,7 +40,7 @@ ip_set_timeout_uget(struct nlattr *tb) } static inline bool -ip_set_timeout_expired(unsigned long *t) +ip_set_timeout_expired(const unsigned long *t) { return *t != IPSET_ELEM_PERMANENT && time_is_before_jiffies(*t); } @@ -63,7 +63,7 @@ ip_set_timeout_set(unsigned long *timeout, u32 value) } static inline u32 -ip_set_timeout_get(unsigned long *timeout) +ip_set_timeout_get(const unsigned long *timeout) { return *timeout == IPSET_ELEM_PERMANENT ? 0 : jiffies_to_msecs(*timeout - jiffies)/MSEC_PER_SEC; -- cgit v1.1 From 7ffea37957b900422ce8b82e9651f7a0a6fac733 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 11:31:03 +0100 Subject: netfilter: ipset: Headers file cleanup Group counter helper functions together. Ported from a patch proposed by Sergey Popovich . Suggested-by: Sergey Popovich Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 524467f..1ea28e3 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -334,6 +334,27 @@ ip_set_update_counter(struct ip_set_counter *counter, } } +static inline bool +ip_set_put_counter(struct sk_buff *skb, const struct ip_set_counter *counter) +{ + return nla_put_net64(skb, IPSET_ATTR_BYTES, + cpu_to_be64(ip_set_get_bytes(counter)), + IPSET_ATTR_PAD) || + nla_put_net64(skb, IPSET_ATTR_PACKETS, + cpu_to_be64(ip_set_get_packets(counter)), + IPSET_ATTR_PAD); +} + +static inline void +ip_set_init_counter(struct ip_set_counter *counter, + const struct ip_set_ext *ext) +{ + if (ext->bytes != ULLONG_MAX) + atomic64_set(&(counter)->bytes, (long long)(ext->bytes)); + if (ext->packets != ULLONG_MAX) + atomic64_set(&(counter)->packets, (long long)(ext->packets)); +} + static inline void ip_set_get_skbinfo(struct ip_set_skbinfo *skbinfo, const struct ip_set_ext *ext, @@ -372,27 +393,6 @@ ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo, skbinfo->skbqueue = ext->skbqueue; } -static inline bool -ip_set_put_counter(struct sk_buff *skb, const struct ip_set_counter *counter) -{ - return nla_put_net64(skb, IPSET_ATTR_BYTES, - cpu_to_be64(ip_set_get_bytes(counter)), - IPSET_ATTR_PAD) || - nla_put_net64(skb, IPSET_ATTR_PACKETS, - cpu_to_be64(ip_set_get_packets(counter)), - IPSET_ATTR_PAD); -} - -static inline void -ip_set_init_counter(struct ip_set_counter *counter, - const struct ip_set_ext *ext) -{ - if (ext->bytes != ULLONG_MAX) - atomic64_set(&(counter)->bytes, (long long)(ext->bytes)); - if (ext->packets != ULLONG_MAX) - atomic64_set(&(counter)->packets, (long long)(ext->packets)); -} - /* Netlink CB args */ enum { IPSET_CB_NET = 0, /* net namespace */ -- cgit v1.1 From bec810d973003b30bc477146904af6bd93fd2df8 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 5 May 2015 17:13:28 +0200 Subject: netfilter: ipset: Improve skbinfo get/init helpers Use struct ip_set_skbinfo in struct ip_set_ext instead of open coded fields and assign structure members in get/init helpers instead of copying members one by one. Explicitly note that struct ip_set_skbinfo must be padded to prevent non-aligned access in the extension blob. Ported from a patch proposed by Sergey Popovich . Suggested-by: Sergey Popovich Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 30 +++++++++++------------------- net/netfilter/ipset/ip_set_core.c | 12 ++++++------ net/netfilter/xt_set.c | 12 +++++++----- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 1ea28e3..7802621 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -92,17 +92,6 @@ struct ip_set_ext_type { extern const struct ip_set_ext_type ip_set_extensions[]; -struct ip_set_ext { - u64 packets; - u64 bytes; - u32 timeout; - u32 skbmark; - u32 skbmarkmask; - u32 skbprio; - u16 skbqueue; - char *comment; -}; - struct ip_set_counter { atomic64_t bytes; atomic64_t packets; @@ -122,6 +111,15 @@ struct ip_set_skbinfo { u32 skbmarkmask; u32 skbprio; u16 skbqueue; + u16 __pad; +}; + +struct ip_set_ext { + struct ip_set_skbinfo skbinfo; + u64 packets; + u64 bytes; + char *comment; + u32 timeout; }; struct ip_set; @@ -360,10 +358,7 @@ ip_set_get_skbinfo(struct ip_set_skbinfo *skbinfo, const struct ip_set_ext *ext, struct ip_set_ext *mext, u32 flags) { - mext->skbmark = skbinfo->skbmark; - mext->skbmarkmask = skbinfo->skbmarkmask; - mext->skbprio = skbinfo->skbprio; - mext->skbqueue = skbinfo->skbqueue; + mext->skbinfo = *skbinfo; } static inline bool @@ -387,10 +382,7 @@ static inline void ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo, const struct ip_set_ext *ext) { - skbinfo->skbmark = ext->skbmark; - skbinfo->skbmarkmask = ext->skbmarkmask; - skbinfo->skbprio = ext->skbprio; - skbinfo->skbqueue = ext->skbqueue; + *skbinfo = ext->skbinfo; } /* Netlink CB args */ diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 3f1b945..bfacccf 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -426,20 +426,20 @@ ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], if (!SET_WITH_SKBINFO(set)) return -IPSET_ERR_SKBINFO; fullmark = be64_to_cpu(nla_get_be64(tb[IPSET_ATTR_SKBMARK])); - ext->skbmark = fullmark >> 32; - ext->skbmarkmask = fullmark & 0xffffffff; + ext->skbinfo.skbmark = fullmark >> 32; + ext->skbinfo.skbmarkmask = fullmark & 0xffffffff; } if (tb[IPSET_ATTR_SKBPRIO]) { if (!SET_WITH_SKBINFO(set)) return -IPSET_ERR_SKBINFO; - ext->skbprio = be32_to_cpu(nla_get_be32( - tb[IPSET_ATTR_SKBPRIO])); + ext->skbinfo.skbprio = + be32_to_cpu(nla_get_be32(tb[IPSET_ATTR_SKBPRIO])); } if (tb[IPSET_ATTR_SKBQUEUE]) { if (!SET_WITH_SKBINFO(set)) return -IPSET_ERR_SKBINFO; - ext->skbqueue = be16_to_cpu(nla_get_be16( - tb[IPSET_ATTR_SKBQUEUE])); + ext->skbinfo.skbqueue = + be16_to_cpu(nla_get_be16(tb[IPSET_ATTR_SKBQUEUE])); } return 0; } diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 1bfede7..6428570 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -423,6 +423,8 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) /* Revision 3 target */ +#define MOPT(opt, member) ((opt).ext.skbinfo.member) + static unsigned int set_target_v3(struct sk_buff *skb, const struct xt_action_param *par) { @@ -453,14 +455,14 @@ set_target_v3(struct sk_buff *skb, const struct xt_action_param *par) if (!ret) return XT_CONTINUE; if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK) - skb->mark = (skb->mark & ~(map_opt.ext.skbmarkmask)) - ^ (map_opt.ext.skbmark); + skb->mark = (skb->mark & ~MOPT(map_opt,skbmarkmask)) + ^ MOPT(map_opt, skbmark); if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO) - skb->priority = map_opt.ext.skbprio; + skb->priority = MOPT(map_opt, skbprio); if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) && skb->dev && - skb->dev->real_num_tx_queues > map_opt.ext.skbqueue) - skb_set_queue_mapping(skb, map_opt.ext.skbqueue); + skb->dev->real_num_tx_queues > MOPT(map_opt, skbqueue)) + skb_set_queue_mapping(skb, MOPT(map_opt, skbqueue)); } return XT_CONTINUE; } -- cgit v1.1 From 1d0d6bd61d495d271b9774a15fbea93e4875474b Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 6 May 2015 07:27:28 +0200 Subject: netfilter: ipset: Use kmalloc() in comment extension helper Allocate memory with kmalloc() rather than kzalloc(): the string is immediately initialized so it is unnecessary to zero out the allocated memory area. Ported from a patch proposed by Sergey Popovich . Suggested-by: Sergey Popovich Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set_comment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/netfilter/ipset/ip_set_comment.h b/include/linux/netfilter/ipset/ip_set_comment.h index bae5c76..5444b1b 100644 --- a/include/linux/netfilter/ipset/ip_set_comment.h +++ b/include/linux/netfilter/ipset/ip_set_comment.h @@ -34,7 +34,7 @@ ip_set_init_comment(struct ip_set_comment *comment, return; if (unlikely(len > IPSET_MAX_COMMENT_SIZE)) len = IPSET_MAX_COMMENT_SIZE; - c = kzalloc(sizeof(*c) + len + 1, GFP_ATOMIC); + c = kmalloc(sizeof(*c) + len + 1, GFP_ATOMIC); if (unlikely(!c)) return; strlcpy(c->str, ext->comment, len + 1); -- cgit v1.1 From 57982edc2739b4473868e7579c0185270468bae1 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 10 Oct 2016 21:34:56 +0200 Subject: netfilter: ipset: Split extensions into separate files Cleanup to separate all extensions into individual files. Ported from a patch proposed by Sergey Popovich . Suggested-by: Sergey Popovich Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 95 +------------------------- include/linux/netfilter/ipset/ip_set_counter.h | 75 ++++++++++++++++++++ include/linux/netfilter/ipset/ip_set_skbinfo.h | 46 +++++++++++++ 3 files changed, 123 insertions(+), 93 deletions(-) create mode 100644 include/linux/netfilter/ipset/ip_set_counter.h create mode 100644 include/linux/netfilter/ipset/ip_set_skbinfo.h diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 7802621..b5bd0fb3 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -292,99 +292,6 @@ ip_set_put_flags(struct sk_buff *skb, struct ip_set *set) return nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(cadt_flags)); } -static inline void -ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter) -{ - atomic64_add((long long)bytes, &(counter)->bytes); -} - -static inline void -ip_set_add_packets(u64 packets, struct ip_set_counter *counter) -{ - atomic64_add((long long)packets, &(counter)->packets); -} - -static inline u64 -ip_set_get_bytes(const struct ip_set_counter *counter) -{ - return (u64)atomic64_read(&(counter)->bytes); -} - -static inline u64 -ip_set_get_packets(const struct ip_set_counter *counter) -{ - return (u64)atomic64_read(&(counter)->packets); -} - -static inline void -ip_set_update_counter(struct ip_set_counter *counter, - const struct ip_set_ext *ext, - struct ip_set_ext *mext, u32 flags) -{ - if (ext->packets != ULLONG_MAX && - !(flags & IPSET_FLAG_SKIP_COUNTER_UPDATE)) { - ip_set_add_bytes(ext->bytes, counter); - ip_set_add_packets(ext->packets, counter); - } - if (flags & IPSET_FLAG_MATCH_COUNTERS) { - mext->packets = ip_set_get_packets(counter); - mext->bytes = ip_set_get_bytes(counter); - } -} - -static inline bool -ip_set_put_counter(struct sk_buff *skb, const struct ip_set_counter *counter) -{ - return nla_put_net64(skb, IPSET_ATTR_BYTES, - cpu_to_be64(ip_set_get_bytes(counter)), - IPSET_ATTR_PAD) || - nla_put_net64(skb, IPSET_ATTR_PACKETS, - cpu_to_be64(ip_set_get_packets(counter)), - IPSET_ATTR_PAD); -} - -static inline void -ip_set_init_counter(struct ip_set_counter *counter, - const struct ip_set_ext *ext) -{ - if (ext->bytes != ULLONG_MAX) - atomic64_set(&(counter)->bytes, (long long)(ext->bytes)); - if (ext->packets != ULLONG_MAX) - atomic64_set(&(counter)->packets, (long long)(ext->packets)); -} - -static inline void -ip_set_get_skbinfo(struct ip_set_skbinfo *skbinfo, - const struct ip_set_ext *ext, - struct ip_set_ext *mext, u32 flags) -{ - mext->skbinfo = *skbinfo; -} - -static inline bool -ip_set_put_skbinfo(struct sk_buff *skb, const struct ip_set_skbinfo *skbinfo) -{ - /* Send nonzero parameters only */ - return ((skbinfo->skbmark || skbinfo->skbmarkmask) && - nla_put_net64(skb, IPSET_ATTR_SKBMARK, - cpu_to_be64((u64)skbinfo->skbmark << 32 | - skbinfo->skbmarkmask), - IPSET_ATTR_PAD)) || - (skbinfo->skbprio && - nla_put_net32(skb, IPSET_ATTR_SKBPRIO, - cpu_to_be32(skbinfo->skbprio))) || - (skbinfo->skbqueue && - nla_put_net16(skb, IPSET_ATTR_SKBQUEUE, - cpu_to_be16(skbinfo->skbqueue))); -} - -static inline void -ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo, - const struct ip_set_ext *ext) -{ - *skbinfo = ext->skbinfo; -} - /* Netlink CB args */ enum { IPSET_CB_NET = 0, /* net namespace */ @@ -539,6 +446,8 @@ bitmap_bytes(u32 a, u32 b) #include #include +#include +#include int ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set, diff --git a/include/linux/netfilter/ipset/ip_set_counter.h b/include/linux/netfilter/ipset/ip_set_counter.h new file mode 100644 index 0000000..bb6fba4 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_counter.h @@ -0,0 +1,75 @@ +#ifndef _IP_SET_COUNTER_H +#define _IP_SET_COUNTER_H + +/* Copyright (C) 2015 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifdef __KERNEL__ + +static inline void +ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter) +{ + atomic64_add((long long)bytes, &(counter)->bytes); +} + +static inline void +ip_set_add_packets(u64 packets, struct ip_set_counter *counter) +{ + atomic64_add((long long)packets, &(counter)->packets); +} + +static inline u64 +ip_set_get_bytes(const struct ip_set_counter *counter) +{ + return (u64)atomic64_read(&(counter)->bytes); +} + +static inline u64 +ip_set_get_packets(const struct ip_set_counter *counter) +{ + return (u64)atomic64_read(&(counter)->packets); +} + +static inline void +ip_set_update_counter(struct ip_set_counter *counter, + const struct ip_set_ext *ext, + struct ip_set_ext *mext, u32 flags) +{ + if (ext->packets != ULLONG_MAX && + !(flags & IPSET_FLAG_SKIP_COUNTER_UPDATE)) { + ip_set_add_bytes(ext->bytes, counter); + ip_set_add_packets(ext->packets, counter); + } + if (flags & IPSET_FLAG_MATCH_COUNTERS) { + mext->packets = ip_set_get_packets(counter); + mext->bytes = ip_set_get_bytes(counter); + } +} + +static inline bool +ip_set_put_counter(struct sk_buff *skb, const struct ip_set_counter *counter) +{ + return nla_put_net64(skb, IPSET_ATTR_BYTES, + cpu_to_be64(ip_set_get_bytes(counter)), + IPSET_ATTR_PAD) || + nla_put_net64(skb, IPSET_ATTR_PACKETS, + cpu_to_be64(ip_set_get_packets(counter)), + IPSET_ATTR_PAD); +} + +static inline void +ip_set_init_counter(struct ip_set_counter *counter, + const struct ip_set_ext *ext) +{ + if (ext->bytes != ULLONG_MAX) + atomic64_set(&(counter)->bytes, (long long)(ext->bytes)); + if (ext->packets != ULLONG_MAX) + atomic64_set(&(counter)->packets, (long long)(ext->packets)); +} + +#endif /* __KERNEL__ */ +#endif /* _IP_SET_COUNTER_H */ diff --git a/include/linux/netfilter/ipset/ip_set_skbinfo.h b/include/linux/netfilter/ipset/ip_set_skbinfo.h new file mode 100644 index 0000000..29d7ef2 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_skbinfo.h @@ -0,0 +1,46 @@ +#ifndef _IP_SET_SKBINFO_H +#define _IP_SET_SKBINFO_H + +/* Copyright (C) 2015 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifdef __KERNEL__ + +static inline void +ip_set_get_skbinfo(struct ip_set_skbinfo *skbinfo, + const struct ip_set_ext *ext, + struct ip_set_ext *mext, u32 flags) +{ + mext->skbinfo = *skbinfo; +} + +static inline bool +ip_set_put_skbinfo(struct sk_buff *skb, const struct ip_set_skbinfo *skbinfo) +{ + /* Send nonzero parameters only */ + return ((skbinfo->skbmark || skbinfo->skbmarkmask) && + nla_put_net64(skb, IPSET_ATTR_SKBMARK, + cpu_to_be64((u64)skbinfo->skbmark << 32 | + skbinfo->skbmarkmask), + IPSET_ATTR_PAD)) || + (skbinfo->skbprio && + nla_put_net32(skb, IPSET_ATTR_SKBPRIO, + cpu_to_be32(skbinfo->skbprio))) || + (skbinfo->skbqueue && + nla_put_net16(skb, IPSET_ATTR_SKBQUEUE, + cpu_to_be16(skbinfo->skbqueue))); +} + +static inline void +ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo, + const struct ip_set_ext *ext) +{ + *skbinfo = ext->skbinfo; +} + +#endif /* __KERNEL__ */ +#endif /* _IP_SET_SKBINFO_H */ -- cgit v1.1 From 722a94519a683d8ba5a8e75dec9622cc906273c1 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 10 Oct 2016 21:44:32 +0200 Subject: netfilter: ipset: Separate memsize calculation code into dedicated function Hash types already has it's memsize calculation code in separate functions. Clean up and do the same for *bitmap* and *list* sets. Ported from a patch proposed by Sergey Popovich . Suggested-by: Sergey Popovich Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_bitmap_gen.h | 11 ++++++++++- net/netfilter/ipset/ip_set_list_set.c | 23 +++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 2e8e7e5..4f07b90 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -22,6 +22,7 @@ #define mtype_kadt IPSET_TOKEN(MTYPE, _kadt) #define mtype_uadt IPSET_TOKEN(MTYPE, _uadt) #define mtype_destroy IPSET_TOKEN(MTYPE, _destroy) +#define mtype_memsize IPSET_TOKEN(MTYPE, _memsize) #define mtype_flush IPSET_TOKEN(MTYPE, _flush) #define mtype_head IPSET_TOKEN(MTYPE, _head) #define mtype_same_set IPSET_TOKEN(MTYPE, _same_set) @@ -84,12 +85,20 @@ mtype_flush(struct ip_set *set) memset(map->members, 0, map->memsize); } +/* Calculate the actual memory size of the set data */ +static size_t +mtype_memsize(const struct mtype *map, size_t dsize) +{ + return sizeof(*map) + map->memsize + + map->elements * dsize; +} + static int mtype_head(struct ip_set *set, struct sk_buff *skb) { const struct mtype *map = set->data; struct nlattr *nested; - size_t memsize = sizeof(*map) + map->memsize; + size_t memsize = mtype_memsize(map, set->dsize); nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index a2a89e4..462b0b1 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -441,12 +441,12 @@ list_set_destroy(struct ip_set *set) set->data = NULL; } -static int -list_set_head(struct ip_set *set, struct sk_buff *skb) +/* Calculate the actual memory size of the set data */ +static size_t +list_set_memsize(const struct list_set *map, size_t dsize) { - const struct list_set *map = set->data; - struct nlattr *nested; struct set_elem *e; + size_t memsize; u32 n = 0; rcu_read_lock(); @@ -454,13 +454,24 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) n++; rcu_read_unlock(); + memsize = sizeof(*map) + n * dsize; + + return memsize; +} + +static int +list_set_head(struct ip_set *set, struct sk_buff *skb) +{ + const struct list_set *map = set->data; + struct nlattr *nested; + size_t memsize = list_set_memsize(map, set->dsize); + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) || - nla_put_net32(skb, IPSET_ATTR_MEMSIZE, - htonl(sizeof(*map) + n * set->dsize))) + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) goto nla_put_failure; if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; -- cgit v1.1 From 837a90eab67edfa464dcc0ddef193449d23da408 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 10 Oct 2016 21:52:51 +0200 Subject: netfilter: ipset: Regroup ip_set_put_extensions and add extern Cleanup: group ip_set_put_extensions and ip_set_get_extensions together and add missing extern. Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index b5bd0fb3..7a218eb 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -331,6 +331,8 @@ extern size_t ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len, size_t align); extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], struct ip_set_ext *ext); +extern int ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set, + const void *e, bool active); static inline int ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr) @@ -449,10 +451,6 @@ bitmap_bytes(u32 a, u32 b) #include #include -int -ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set, - const void *e, bool active); - #define IP_SET_INIT_KEXT(skb, opt, set) \ { .bytes = (skb)->len, .packets = 1, \ .timeout = ip_set_adt_opt_timeout(opt, set) } -- cgit v1.1 From a54dad51a83ba08e951191ce3e1e9eb44750c0bb Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Mon, 10 Oct 2016 21:59:21 +0200 Subject: netfilter: ipset: Add element count to hash headers It would be useful for userspace to query the size of an ipset hash, however, this data is not exposed to userspace outside of counting the number of member entries. This patch uses the attribute IPSET_ATTR_ELEMENTS to indicate the size in the the header that is exported to userspace. This field is then printed by the userspace tool for hashes. Signed-off-by: Eric B Munson Cc: Pablo Neira Ayuso Cc: Josh Hunt Cc: netfilter-devel@vger.kernel.org Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index d32fd6b..f5acfb9 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1083,7 +1083,8 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) goto nla_put_failure; #endif if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) || - nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || + nla_put_net32(skb, IPSET_ATTR_ELEMENTS, htonl(h->elements))) goto nla_put_failure; if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; -- cgit v1.1 From 702b71e7c666a1c9be9d49e8cd173f0d4d1e859f Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 10 Oct 2016 22:07:41 +0200 Subject: netfilter: ipset: Add element count to all set types header It is better to list the set elements for all set types, thus the header information is uniform. Element counts are therefore added to the bitmap and list types. Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 2 ++ include/linux/netfilter/ipset/ip_set_bitmap.h | 2 +- net/netfilter/ipset/ip_set_bitmap_gen.h | 10 +++++++++- net/netfilter/ipset/ip_set_hash_gen.h | 21 ++++++++++----------- net/netfilter/ipset/ip_set_list_set.c | 6 +++++- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 7a218eb..4671d74 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -250,6 +250,8 @@ struct ip_set { u8 flags; /* Default timeout value, if enabled */ u32 timeout; + /* Number of elements (vs timeout) */ + u32 elements; /* Element data size */ size_t dsize; /* Offsets to extensions in elements */ diff --git a/include/linux/netfilter/ipset/ip_set_bitmap.h b/include/linux/netfilter/ipset/ip_set_bitmap.h index 5e4662a..366d6c0 100644 --- a/include/linux/netfilter/ipset/ip_set_bitmap.h +++ b/include/linux/netfilter/ipset/ip_set_bitmap.h @@ -6,8 +6,8 @@ #define IPSET_BITMAP_MAX_RANGE 0x0000FFFF enum { + IPSET_ADD_STORE_PLAIN_TIMEOUT = -1, IPSET_ADD_FAILED = 1, - IPSET_ADD_STORE_PLAIN_TIMEOUT, IPSET_ADD_START_STORED_TIMEOUT, }; diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 4f07b90..1810d1c 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -83,6 +83,7 @@ mtype_flush(struct ip_set *set) if (set->extensions & IPSET_EXT_DESTROY) mtype_ext_cleanup(set); memset(map->members, 0, map->memsize); + set->elements = 0; } /* Calculate the actual memory size of the set data */ @@ -105,7 +106,8 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) goto nla_put_failure; if (mtype_do_head(skb, map) || nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) || - nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || + nla_put_net32(skb, IPSET_ATTR_ELEMENTS, htonl(set->elements))) goto nla_put_failure; if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; @@ -149,6 +151,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (ret == IPSET_ADD_FAILED) { if (SET_WITH_TIMEOUT(set) && ip_set_timeout_expired(ext_timeout(x, set))) { + set->elements--; ret = 0; } else if (!(flags & IPSET_FLAG_EXIST)) { set_bit(e->id, map->members); @@ -157,6 +160,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, /* Element is re-added, cleanup extensions */ ip_set_ext_destroy(set, x); } + if (ret > 0) + set->elements--; if (SET_WITH_TIMEOUT(set)) #ifdef IP_SET_BITMAP_STORED_TIMEOUT @@ -174,6 +179,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, /* Activate element */ set_bit(e->id, map->members); + set->elements++; return 0; } @@ -190,6 +196,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, return -IPSET_ERR_EXIST; ip_set_ext_destroy(set, x); + set->elements--; if (SET_WITH_TIMEOUT(set) && ip_set_timeout_expired(ext_timeout(x, set))) return -IPSET_ERR_EXIST; @@ -285,6 +292,7 @@ mtype_gc(unsigned long ul_set) if (ip_set_timeout_expired(ext_timeout(x, set))) { clear_bit(id, map->members); ip_set_ext_destroy(set, x); + set->elements--; } } spin_unlock_bh(&set->lock); diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index f5acfb9..6e967f1 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -275,7 +275,6 @@ htable_bits(u32 hashsize) struct htype { struct htable __rcu *table; /* the hash table */ u32 maxelem; /* max elements in the hash */ - u32 elements; /* current element (vs timeout) */ u32 initval; /* random jhash init value */ #ifdef IP_SET_HASH_WITH_MARKMASK u32 markmask; /* markmask value for mark mask to store */ @@ -400,7 +399,7 @@ mtype_flush(struct ip_set *set) #ifdef IP_SET_HASH_WITH_NETS memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family)); #endif - h->elements = 0; + set->elements = 0; } /* Destroy the hashtable part of the set */ @@ -506,7 +505,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) nets_length, k); #endif ip_set_ext_destroy(set, data); - h->elements--; + set->elements--; d++; } } @@ -715,11 +714,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, bool deleted = false, forceadd = false, reuse = false; u32 key, multi = 0; - if (h->elements >= h->maxelem) { + if (set->elements >= h->maxelem) { if (SET_WITH_TIMEOUT(set)) /* FIXME: when set is full, we slow down here */ mtype_expire(set, h, NLEN(set->family), set->dsize); - if (h->elements >= h->maxelem && SET_WITH_FORCEADD(set)) + if (set->elements >= h->maxelem && SET_WITH_FORCEADD(set)) forceadd = true; } @@ -732,7 +731,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, pr_warn("Set %s is full, maxelem %u reached\n", set->name, h->maxelem); return -IPSET_ERR_HASH_FULL; - } else if (h->elements >= h->maxelem) { + } else if (set->elements >= h->maxelem) { goto set_full; } old = NULL; @@ -781,11 +780,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, NLEN(set->family), i); #endif ip_set_ext_destroy(set, data); - h->elements--; + set->elements--; } goto copy_data; } - if (h->elements >= h->maxelem) + if (set->elements >= h->maxelem) goto set_full; /* Create a new slot */ if (n->pos >= n->size) { @@ -810,7 +809,7 @@ copy_elem: j = n->pos++; data = ahash_data(n, j, set->dsize); copy_data: - h->elements++; + set->elements++; #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) mtype_add_cidr(h, NCIDR_PUT(DCIDR_GET(d->cidr, i)), @@ -883,7 +882,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, smp_mb__after_atomic(); if (i + 1 == n->pos) n->pos--; - h->elements--; + set->elements--; #ifdef IP_SET_HASH_WITH_NETS for (j = 0; j < IPSET_NET_COUNT; j++) mtype_del_cidr(h, NCIDR_PUT(DCIDR_GET(d->cidr, j)), @@ -1084,7 +1083,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) #endif if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) || nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || - nla_put_net32(skb, IPSET_ATTR_ELEMENTS, htonl(h->elements))) + nla_put_net32(skb, IPSET_ATTR_ELEMENTS, htonl(set->elements))) goto nla_put_failure; if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 462b0b1..c455166 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -166,6 +166,7 @@ __list_set_del_rcu(struct rcu_head * rcu) static inline void list_set_del(struct ip_set *set, struct set_elem *e) { + set->elements--; list_del_rcu(&e->list); call_rcu(&e->rcu, __list_set_del_rcu); } @@ -309,6 +310,7 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, list_add_rcu(&e->list, &prev->list); else list_add_tail_rcu(&e->list, &map->members); + set->elements++; return 0; } @@ -419,6 +421,7 @@ list_set_flush(struct ip_set *set) list_for_each_entry_safe(e, n, &map->members, list) list_set_del(set, e); + set->elements = 0; } static void @@ -471,7 +474,8 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) goto nla_put_failure; if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) || - nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || + nla_put_net32(skb, IPSET_ATTR_ELEMENTS, htonl(set->elements))) goto nla_put_failure; if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; -- cgit v1.1 From 9e41f26a505cca04b7122e65053cf6447007ea79 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 12:05:34 +0100 Subject: netfilter: ipset: Count non-static extension memory for userspace Non-static (i.e. comment) extension was not counted into the memory size. A new internal counter is introduced for this. In the case of the hash types the sizes of the arrays are counted there as well so that we can avoid to scan the whole set when just the header data is requested. Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 8 ++++++-- include/linux/netfilter/ipset/ip_set_comment.h | 7 +++++-- net/netfilter/ipset/ip_set_bitmap_gen.h | 5 +++-- net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/ipset/ip_set_hash_gen.h | 26 ++++++++++++++------------ net/netfilter/ipset/ip_set_list_set.c | 5 +++-- 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 4671d74..8e42253 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -79,10 +79,12 @@ enum ip_set_ext_id { IPSET_EXT_ID_MAX, }; +struct ip_set; + /* Extension type */ struct ip_set_ext_type { /* Destroy extension private data (can be NULL) */ - void (*destroy)(void *ext); + void (*destroy)(struct ip_set *set, void *ext); enum ip_set_extension type; enum ipset_cadt_flags flag; /* Size and minimal alignment */ @@ -252,6 +254,8 @@ struct ip_set { u32 timeout; /* Number of elements (vs timeout) */ u32 elements; + /* Size of the dynamic extensions (vs timeout) */ + size_t ext_size; /* Element data size */ size_t dsize; /* Offsets to extensions in elements */ @@ -268,7 +272,7 @@ ip_set_ext_destroy(struct ip_set *set, void *data) */ if (SET_WITH_COMMENT(set)) ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy( - ext_comment(data, set)); + set, ext_comment(data, set)); } static inline int diff --git a/include/linux/netfilter/ipset/ip_set_comment.h b/include/linux/netfilter/ipset/ip_set_comment.h index 5444b1b..8e2bab1 100644 --- a/include/linux/netfilter/ipset/ip_set_comment.h +++ b/include/linux/netfilter/ipset/ip_set_comment.h @@ -20,13 +20,14 @@ ip_set_comment_uget(struct nlattr *tb) * The kadt functions don't use the comment extensions in any way. */ static inline void -ip_set_init_comment(struct ip_set_comment *comment, +ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, const struct ip_set_ext *ext) { struct ip_set_comment_rcu *c = rcu_dereference_protected(comment->c, 1); size_t len = ext->comment ? strlen(ext->comment) : 0; if (unlikely(c)) { + set->ext_size -= sizeof(*c) + strlen(c->str) + 1; kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); } @@ -38,6 +39,7 @@ ip_set_init_comment(struct ip_set_comment *comment, if (unlikely(!c)) return; strlcpy(c->str, ext->comment, len + 1); + set->ext_size += sizeof(*c) + strlen(c->str) + 1; rcu_assign_pointer(comment->c, c); } @@ -58,13 +60,14 @@ ip_set_put_comment(struct sk_buff *skb, const struct ip_set_comment *comment) * of the set data anymore. */ static inline void -ip_set_comment_free(struct ip_set_comment *comment) +ip_set_comment_free(struct ip_set *set, struct ip_set_comment *comment) { struct ip_set_comment_rcu *c; c = rcu_dereference_protected(comment->c, 1); if (unlikely(!c)) return; + set->ext_size -= sizeof(*c) + strlen(c->str) + 1; kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); } diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 1810d1c..f8ea26c 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -84,6 +84,7 @@ mtype_flush(struct ip_set *set) mtype_ext_cleanup(set); memset(map->members, 0, map->memsize); set->elements = 0; + set->ext_size = 0; } /* Calculate the actual memory size of the set data */ @@ -99,7 +100,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) { const struct mtype *map = set->data; struct nlattr *nested; - size_t memsize = mtype_memsize(map, set->dsize); + size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) @@ -173,7 +174,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (SET_WITH_COUNTER(set)) ip_set_init_counter(ext_counter(x, set), ext); if (SET_WITH_COMMENT(set)) - ip_set_init_comment(ext_comment(x, set), ext); + ip_set_init_comment(set, ext_comment(x, set), ext); if (SET_WITH_SKBINFO(set)) ip_set_init_skbinfo(ext_skbinfo(x, set), ext); diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index bfacccf..23345d2 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -324,7 +324,7 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr) } EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); -typedef void (*destroyer)(void *); +typedef void (*destroyer)(struct ip_set *, void *); /* ipset data extension types, in size order */ const struct ip_set_ext_type ip_set_extensions[] = { diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 6e967f1..0746405 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -343,21 +343,13 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) /* Calculate the actual memory size of the set data */ static size_t mtype_ahash_memsize(const struct htype *h, const struct htable *t, - u8 nets_length, size_t dsize) + u8 nets_length) { - u32 i; - struct hbucket *n; size_t memsize = sizeof(*h) + sizeof(*t); #ifdef IP_SET_HASH_WITH_NETS memsize += sizeof(struct net_prefixes) * nets_length; #endif - for (i = 0; i < jhash_size(t->htable_bits); i++) { - n = rcu_dereference_bh(hbucket(t, i)); - if (!n) - continue; - memsize += sizeof(struct hbucket) + n->size * dsize; - } return memsize; } @@ -400,6 +392,7 @@ mtype_flush(struct ip_set *set) memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family)); #endif set->elements = 0; + set->ext_size = 0; } /* Destroy the hashtable part of the set */ @@ -531,6 +524,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) d++; } tmp->pos = d; + set->ext_size -= AHASH_INIT_SIZE * dsize; rcu_assign_pointer(hbucket(t, i), tmp); kfree_rcu(n, rcu); } @@ -562,7 +556,7 @@ mtype_resize(struct ip_set *set, bool retried) struct htype *h = set->data; struct htable *t, *orig; u8 htable_bits; - size_t dsize = set->dsize; + size_t extsize, dsize = set->dsize; #ifdef IP_SET_HASH_WITH_NETS u8 flags; struct mtype_elem *tmp; @@ -605,6 +599,7 @@ retry: /* There can't be another parallel resizing, but dumping is possible */ atomic_set(&orig->ref, 1); atomic_inc(&orig->uref); + extsize = 0; pr_debug("attempt to resize set %s from %u to %u, t %p\n", set->name, orig->htable_bits, htable_bits, orig); for (i = 0; i < jhash_size(orig->htable_bits); i++) { @@ -635,6 +630,7 @@ retry: goto cleanup; } m->size = AHASH_INIT_SIZE; + extsize = sizeof(*m) + AHASH_INIT_SIZE * dsize; RCU_INIT_POINTER(hbucket(t, key), m); } else if (m->pos >= m->size) { struct hbucket *ht; @@ -654,6 +650,7 @@ retry: memcpy(ht, m, sizeof(struct hbucket) + m->size * dsize); ht->size = m->size + AHASH_INIT_SIZE; + extsize += AHASH_INIT_SIZE * dsize; kfree(m); m = ht; RCU_INIT_POINTER(hbucket(t, key), ht); @@ -667,6 +664,7 @@ retry: } } rcu_assign_pointer(h->table, t); + set->ext_size = extsize; spin_unlock_bh(&set->lock); @@ -740,6 +738,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (!n) return -ENOMEM; n->size = AHASH_INIT_SIZE; + set->ext_size += sizeof(*n) + AHASH_INIT_SIZE * set->dsize; goto copy_elem; } for (i = 0; i < n->pos; i++) { @@ -803,6 +802,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, memcpy(n, old, sizeof(struct hbucket) + old->size * set->dsize); n->size = old->size + AHASH_INIT_SIZE; + set->ext_size += AHASH_INIT_SIZE * set->dsize; } copy_elem: @@ -823,7 +823,7 @@ overwrite_extensions: if (SET_WITH_COUNTER(set)) ip_set_init_counter(ext_counter(data, set), ext); if (SET_WITH_COMMENT(set)) - ip_set_init_comment(ext_comment(data, set), ext); + ip_set_init_comment(set, ext_comment(data, set), ext); if (SET_WITH_SKBINFO(set)) ip_set_init_skbinfo(ext_skbinfo(data, set), ext); /* Must come last for the case when timed out entry is reused */ @@ -895,6 +895,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, k++; } if (n->pos == 0 && k == 0) { + set->ext_size -= sizeof(*n) + n->size * dsize; rcu_assign_pointer(hbucket(t, key), NULL); kfree_rcu(n, rcu); } else if (k >= AHASH_INIT_SIZE) { @@ -913,6 +914,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, k++; } tmp->pos = k; + set->ext_size -= AHASH_INIT_SIZE * dsize; rcu_assign_pointer(hbucket(t, key), tmp); kfree_rcu(n, rcu); } @@ -1061,7 +1063,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) rcu_read_lock_bh(); t = rcu_dereference_bh_nfnl(h->table); - memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize); + memsize = mtype_ahash_memsize(h, t, NLEN(set->family)) + set->ext_size; htable_bits = t->htable_bits; rcu_read_unlock_bh(); diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index c455166..dede343 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -228,7 +228,7 @@ list_set_init_extensions(struct ip_set *set, const struct ip_set_ext *ext, if (SET_WITH_COUNTER(set)) ip_set_init_counter(ext_counter(e, set), ext); if (SET_WITH_COMMENT(set)) - ip_set_init_comment(ext_comment(e, set), ext); + ip_set_init_comment(set, ext_comment(e, set), ext); if (SET_WITH_SKBINFO(set)) ip_set_init_skbinfo(ext_skbinfo(e, set), ext); /* Update timeout last */ @@ -422,6 +422,7 @@ list_set_flush(struct ip_set *set) list_for_each_entry_safe(e, n, &map->members, list) list_set_del(set, e); set->elements = 0; + set->ext_size = 0; } static void @@ -467,7 +468,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) { const struct list_set *map = set->data; struct nlattr *nested; - size_t memsize = list_set_memsize(map, set->dsize); + size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) -- cgit v1.1 From 5fdb5f6938cf241d574f0ff5504d753c801dc4ae Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 12:12:25 +0100 Subject: netfilter: ipset: Remove redundant mtype_expire() arguments Remove redundant parameters nets_length and dsize, because they can be get from other parameters. Ported from a patch proposed by Sergey Popovich . Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 0746405..c4877b6 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -465,14 +465,15 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b) /* Delete expired elements from the hashtable */ static void -mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) +mtype_expire(struct ip_set *set, struct htype *h) { struct htable *t; struct hbucket *n, *tmp; struct mtype_elem *data; u32 i, j, d; + size_t dsize = set->dsize; #ifdef IP_SET_HASH_WITH_NETS - u8 k; + u8 k, nets_length = NLEN(set->family); #endif t = ipset_dereference_protected(h->table, set); @@ -539,7 +540,7 @@ mtype_gc(unsigned long ul_set) pr_debug("called\n"); spin_lock_bh(&set->lock); - mtype_expire(set, h, NLEN(set->family), set->dsize); + mtype_expire(set, h); spin_unlock_bh(&set->lock); h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; @@ -715,7 +716,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (set->elements >= h->maxelem) { if (SET_WITH_TIMEOUT(set)) /* FIXME: when set is full, we slow down here */ - mtype_expire(set, h, NLEN(set->family), set->dsize); + mtype_expire(set, h); if (set->elements >= h->maxelem && SET_WITH_FORCEADD(set)) forceadd = true; } -- cgit v1.1 From 509debc97587629dd5a6dca337afa57cb874b418 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 12:18:06 +0100 Subject: netfilter: ipset: Simplify mtype_expire() for hash types Remove one leve of intendation by using continue while iterating over elements in bucket. Ported from a patch proposed by Sergey Popovich . Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index c4877b6..7999e4c 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -487,21 +487,20 @@ mtype_expire(struct ip_set *set, struct htype *h) continue; } data = ahash_data(n, j, dsize); - if (ip_set_timeout_expired(ext_timeout(data, set))) { - pr_debug("expired %u/%u\n", i, j); - clear_bit(j, n->used); - smp_mb__after_atomic(); + if (!ip_set_timeout_expired(ext_timeout(data, set))) + continue; + pr_debug("expired %u/%u\n", i, j); + clear_bit(j, n->used); + smp_mb__after_atomic(); #ifdef IP_SET_HASH_WITH_NETS - for (k = 0; k < IPSET_NET_COUNT; k++) - mtype_del_cidr(h, - NCIDR_PUT(DCIDR_GET(data->cidr, - k)), - nets_length, k); + for (k = 0; k < IPSET_NET_COUNT; k++) + mtype_del_cidr(h, + NCIDR_PUT(DCIDR_GET(data->cidr, k)), + nets_length, k); #endif - ip_set_ext_destroy(set, data); - set->elements--; - d++; - } + ip_set_ext_destroy(set, data); + set->elements--; + d++; } if (d >= AHASH_INIT_SIZE) { if (d >= n->size) { -- cgit v1.1 From cee8b97b6c13b1331aa3a89a1aab2ca6a7bd06d0 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 12:24:10 +0100 Subject: netfilter: ipset: Make NLEN compile time constant for hash types Hash types define HOST_MASK before inclusion of ip_set_hash_gen.h and the only place where NLEN needed to be calculated at runtime is *_create() method. Ported from a patch proposed by Sergey Popovich . Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 51 ++++++++++++++++------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 7999e4c..6c88c20 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -150,20 +150,18 @@ htable_bits(u32 hashsize) #define INIT_CIDR(cidr, host_mask) \ DCIDR_PUT(((cidr) ? NCIDR_GET(cidr) : host_mask)) -#define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) - #ifdef IP_SET_HASH_WITH_NET0 -/* cidr from 0 to SET_HOST_MASK() value and c = cidr + 1 */ -#define NLEN(family) (SET_HOST_MASK(family) + 1) +/* cidr from 0 to HOST_MASK value and c = cidr + 1 */ +#define NLEN (HOST_MASK + 1) #define CIDR_POS(c) ((c) - 1) #else -/* cidr from 1 to SET_HOST_MASK() value and c = cidr + 1 */ -#define NLEN(family) SET_HOST_MASK(family) +/* cidr from 1 to HOST_MASK value and c = cidr + 1 */ +#define NLEN HOST_MASK #define CIDR_POS(c) ((c) - 2) #endif #else -#define NLEN(family) 0 +#define NLEN 0 #endif /* IP_SET_HASH_WITH_NETS */ #endif /* _IP_SET_HASH_GEN_H */ @@ -298,12 +296,12 @@ struct htype { * sized networks. cidr == real cidr + 1 to support /0. */ static void -mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) +mtype_add_cidr(struct htype *h, u8 cidr, u8 n) { int i, j; /* Add in increasing prefix order, so larger cidr first */ - for (i = 0, j = -1; i < nets_length && h->nets[i].cidr[n]; i++) { + for (i = 0, j = -1; i < NLEN && h->nets[i].cidr[n]; i++) { if (j != -1) { continue; } else if (h->nets[i].cidr[n] < cidr) { @@ -322,11 +320,11 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) } static void -mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) +mtype_del_cidr(struct htype *h, u8 cidr, u8 n) { - u8 i, j, net_end = nets_length - 1; + u8 i, j, net_end = NLEN - 1; - for (i = 0; i < nets_length; i++) { + for (i = 0; i < NLEN; i++) { if (h->nets[i].cidr[n] != cidr) continue; h->nets[CIDR_POS(cidr)].nets[n]--; @@ -342,13 +340,12 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) /* Calculate the actual memory size of the set data */ static size_t -mtype_ahash_memsize(const struct htype *h, const struct htable *t, - u8 nets_length) +mtype_ahash_memsize(const struct htype *h, const struct htable *t) { size_t memsize = sizeof(*h) + sizeof(*t); #ifdef IP_SET_HASH_WITH_NETS - memsize += sizeof(struct net_prefixes) * nets_length; + memsize += sizeof(struct net_prefixes) * NLEN; #endif return memsize; @@ -389,7 +386,7 @@ mtype_flush(struct ip_set *set) kfree_rcu(n, rcu); } #ifdef IP_SET_HASH_WITH_NETS - memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family)); + memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN); #endif set->elements = 0; set->ext_size = 0; @@ -473,7 +470,7 @@ mtype_expire(struct ip_set *set, struct htype *h) u32 i, j, d; size_t dsize = set->dsize; #ifdef IP_SET_HASH_WITH_NETS - u8 k, nets_length = NLEN(set->family); + u8 k; #endif t = ipset_dereference_protected(h->table, set); @@ -496,7 +493,7 @@ mtype_expire(struct ip_set *set, struct htype *h) for (k = 0; k < IPSET_NET_COUNT; k++) mtype_del_cidr(h, NCIDR_PUT(DCIDR_GET(data->cidr, k)), - nets_length, k); + k); #endif ip_set_ext_destroy(set, data); set->elements--; @@ -776,7 +773,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, for (i = 0; i < IPSET_NET_COUNT; i++) mtype_del_cidr(h, NCIDR_PUT(DCIDR_GET(data->cidr, i)), - NLEN(set->family), i); + i); #endif ip_set_ext_destroy(set, data); set->elements--; @@ -812,8 +809,7 @@ copy_data: set->elements++; #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) - mtype_add_cidr(h, NCIDR_PUT(DCIDR_GET(d->cidr, i)), - NLEN(set->family), i); + mtype_add_cidr(h, NCIDR_PUT(DCIDR_GET(d->cidr, i)), i); #endif memcpy(data, d, sizeof(struct mtype_elem)); overwrite_extensions: @@ -886,7 +882,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, #ifdef IP_SET_HASH_WITH_NETS for (j = 0; j < IPSET_NET_COUNT; j++) mtype_del_cidr(h, NCIDR_PUT(DCIDR_GET(d->cidr, j)), - NLEN(set->family), j); + j); #endif ip_set_ext_destroy(set, data); @@ -958,14 +954,13 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, int i, j = 0; #endif u32 key, multi = 0; - u8 nets_length = NLEN(set->family); pr_debug("test by nets\n"); - for (; j < nets_length && h->nets[j].cidr[0] && !multi; j++) { + for (; j < NLEN && h->nets[j].cidr[0] && !multi; j++) { #if IPSET_NET_COUNT == 2 mtype_data_reset_elem(d, &orig); mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0]), false); - for (k = 0; k < nets_length && h->nets[k].cidr[1] && !multi; + for (k = 0; k < NLEN && h->nets[k].cidr[1] && !multi; k++) { mtype_data_netmask(d, NCIDR_GET(h->nets[k].cidr[1]), true); @@ -1022,7 +1017,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, * try all possible network sizes */ for (i = 0; i < IPSET_NET_COUNT; i++) - if (DCIDR_GET(d->cidr, i) != SET_HOST_MASK(set->family)) + if (DCIDR_GET(d->cidr, i) != HOST_MASK) break; if (i == IPSET_NET_COUNT) { ret = mtype_test_cidrs(set, d, ext, mext, flags); @@ -1063,7 +1058,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) rcu_read_lock_bh(); t = rcu_dereference_bh_nfnl(h->table); - memsize = mtype_ahash_memsize(h, t, NLEN(set->family)) + set->ext_size; + memsize = mtype_ahash_memsize(h, t) + set->ext_size; htable_bits = t->htable_bits; rcu_read_unlock_bh(); @@ -1295,7 +1290,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, hsize = sizeof(*h); #ifdef IP_SET_HASH_WITH_NETS - hsize += sizeof(struct net_prefixes) * NLEN(set->family); + hsize += sizeof(struct net_prefixes) * NLEN; #endif h = kzalloc(hsize, GFP_KERNEL); if (!h) -- cgit v1.1 From 5a902e6d4b61de33f4a9d3fc5fbc9d339fee7ed3 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Fri, 26 Jun 2015 11:16:28 +0200 Subject: netfilter: ipset: Make sure element data size is a multiple of u32 Data for hashing required to be array of u32. Make sure that element data always multiple of u32. Ported from a patch proposed by Sergey Popovich . Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 6c88c20..34f115f 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -260,8 +260,14 @@ htable_bits(u32 hashsize) #endif #define HKEY(data, initval, htable_bits) \ -(jhash2((u32 *)(data), HKEY_DATALEN / sizeof(u32), initval) \ - & jhash_mask(htable_bits)) +({ \ + const u32 *__k = (const u32 *)data; \ + u32 __l = HKEY_DATALEN / sizeof(u32); \ + \ + BUILD_BUG_ON(HKEY_DATALEN % sizeof(u32) != 0); \ + \ + jhash2(__k, __l, initval) & jhash_mask(htable_bits); \ +}) #ifndef htype #ifndef HTYPE -- cgit v1.1 From 961509ac183ec1069616541938728fcb822f5dff Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 11 Oct 2016 07:25:00 +0200 Subject: netfilter: ipset: Optimize hash creation routine Exit as easly as possible on error and use RCU_INIT_POINTER() as set is not seen at creation time. Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 63 ++++++++++++++++------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 34f115f..de1d16f 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1241,41 +1241,35 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, struct htype *h; struct htable *t; + pr_debug("Create set %s with family %s\n", + set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6"); + #ifndef IP_SET_PROTO_UNDEF if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; #endif -#ifdef IP_SET_HASH_WITH_MARKMASK - markmask = 0xffffffff; -#endif -#ifdef IP_SET_HASH_WITH_NETMASK - netmask = set->family == NFPROTO_IPV4 ? 32 : 128; - pr_debug("Create set %s with family %s\n", - set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6"); -#endif - if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) return -IPSET_ERR_PROTOCOL; + #ifdef IP_SET_HASH_WITH_MARKMASK /* Separated condition in order to avoid directive in argument list */ if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_MARKMASK))) return -IPSET_ERR_PROTOCOL; -#endif - if (tb[IPSET_ATTR_HASHSIZE]) { - hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); - if (hashsize < IPSET_MIMINAL_HASHSIZE) - hashsize = IPSET_MIMINAL_HASHSIZE; + markmask = 0xffffffff; + if (tb[IPSET_ATTR_MARKMASK]) { + markmask = ntohl(nla_get_be32(tb[IPSET_ATTR_MARKMASK])); + if (markmask == 0) + return -IPSET_ERR_INVALID_MARKMASK; } - - if (tb[IPSET_ATTR_MAXELEM]) - maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); +#endif #ifdef IP_SET_HASH_WITH_NETMASK + netmask = set->family == NFPROTO_IPV4 ? 32 : 128; if (tb[IPSET_ATTR_NETMASK]) { netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); @@ -1285,14 +1279,15 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, return -IPSET_ERR_INVALID_NETMASK; } #endif -#ifdef IP_SET_HASH_WITH_MARKMASK - if (tb[IPSET_ATTR_MARKMASK]) { - markmask = ntohl(nla_get_be32(tb[IPSET_ATTR_MARKMASK])); - if (markmask == 0) - return -IPSET_ERR_INVALID_MARKMASK; + if (tb[IPSET_ATTR_HASHSIZE]) { + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; } -#endif + + if (tb[IPSET_ATTR_MAXELEM]) + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); hsize = sizeof(*h); #ifdef IP_SET_HASH_WITH_NETS @@ -1302,16 +1297,6 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, if (!h) return -ENOMEM; - h->maxelem = maxelem; -#ifdef IP_SET_HASH_WITH_NETMASK - h->netmask = netmask; -#endif -#ifdef IP_SET_HASH_WITH_MARKMASK - h->markmask = markmask; -#endif - get_random_bytes(&h->initval, sizeof(h->initval)); - set->timeout = IPSET_NO_TIMEOUT; - hbits = htable_bits(hashsize); hsize = htable_size(hbits); if (hsize == 0) { @@ -1323,8 +1308,17 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, kfree(h); return -ENOMEM; } + h->maxelem = maxelem; +#ifdef IP_SET_HASH_WITH_NETMASK + h->netmask = netmask; +#endif +#ifdef IP_SET_HASH_WITH_MARKMASK + h->markmask = markmask; +#endif + get_random_bytes(&h->initval, sizeof(h->initval)); + t->htable_bits = hbits; - rcu_assign_pointer(h->table, t); + RCU_INIT_POINTER(h->table, t); set->data = h; #ifndef IP_SET_PROTO_UNDEF @@ -1342,6 +1336,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, __alignof__(struct IPSET_TOKEN(HTYPE, 6_elem))); } #endif + set->timeout = IPSET_NO_TIMEOUT; if (tb[IPSET_ATTR_TIMEOUT]) { set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); #ifndef IP_SET_PROTO_UNDEF -- cgit v1.1 From 21956ab290f7f3f9203eb89adc0a1331e59b7f0f Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Fri, 26 Jun 2015 15:13:18 +0200 Subject: netfilter: ipset: Make struct htype per ipset family Before this patch struct htype created at the first source of ip_set_hash_gen.h and it is common for both IPv4 and IPv6 set variants. Make struct htype per ipset family and use NLEN to make nets array fixed size to simplify struct htype allocation. Ported from a patch proposed by Sergey Popovich . Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 51 +++++++++++----------------- net/netfilter/ipset/ip_set_hash_ip.c | 10 +++--- net/netfilter/ipset/ip_set_hash_ipmark.c | 10 +++--- net/netfilter/ipset/ip_set_hash_ipport.c | 6 ++-- net/netfilter/ipset/ip_set_hash_ipportip.c | 6 ++-- net/netfilter/ipset/ip_set_hash_ipportnet.c | 10 +++--- net/netfilter/ipset/ip_set_hash_net.c | 8 ++--- net/netfilter/ipset/ip_set_hash_netiface.c | 8 ++--- net/netfilter/ipset/ip_set_hash_netnet.c | 8 ++--- net/netfilter/ipset/ip_set_hash_netport.c | 10 +++--- net/netfilter/ipset/ip_set_hash_netportnet.c | 10 +++--- 11 files changed, 63 insertions(+), 74 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index de1d16f..c600f6d 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -166,6 +166,18 @@ htable_bits(u32 hashsize) #endif /* _IP_SET_HASH_GEN_H */ +#ifndef MTYPE +#error "MTYPE is not defined!" +#endif + +#ifndef HTYPE +#error "HTYPE is not defined!" +#endif + +#ifndef HOST_MASK +#error "HOST_MASK is not defined!" +#endif + /* Family dependent templates */ #undef ahash_data @@ -189,7 +201,6 @@ htable_bits(u32 hashsize) #undef mtype_same_set #undef mtype_kadt #undef mtype_uadt -#undef mtype #undef mtype_add #undef mtype_del @@ -205,6 +216,7 @@ htable_bits(u32 hashsize) #undef mtype_variant #undef mtype_data_match +#undef htype #undef HKEY #define mtype_data_equal IPSET_TOKEN(MTYPE, _data_equal) @@ -231,7 +243,6 @@ htable_bits(u32 hashsize) #define mtype_same_set IPSET_TOKEN(MTYPE, _same_set) #define mtype_kadt IPSET_TOKEN(MTYPE, _kadt) #define mtype_uadt IPSET_TOKEN(MTYPE, _uadt) -#define mtype MTYPE #define mtype_add IPSET_TOKEN(MTYPE, _add) #define mtype_del IPSET_TOKEN(MTYPE, _del) @@ -247,18 +258,12 @@ htable_bits(u32 hashsize) #define mtype_variant IPSET_TOKEN(MTYPE, _variant) #define mtype_data_match IPSET_TOKEN(MTYPE, _data_match) -#ifndef MTYPE -#error "MTYPE is not defined!" -#endif - -#ifndef HOST_MASK -#error "HOST_MASK is not defined!" -#endif - #ifndef HKEY_DATALEN #define HKEY_DATALEN sizeof(struct mtype_elem) #endif +#define htype MTYPE + #define HKEY(data, initval, htable_bits) \ ({ \ const u32 *__k = (const u32 *)data; \ @@ -269,33 +274,26 @@ htable_bits(u32 hashsize) jhash2(__k, __l, initval) & jhash_mask(htable_bits); \ }) -#ifndef htype -#ifndef HTYPE -#error "HTYPE is not defined!" -#endif /* HTYPE */ -#define htype HTYPE - /* The generic hash structure */ struct htype { struct htable __rcu *table; /* the hash table */ + struct timer_list gc; /* garbage collection when timeout enabled */ u32 maxelem; /* max elements in the hash */ u32 initval; /* random jhash init value */ #ifdef IP_SET_HASH_WITH_MARKMASK u32 markmask; /* markmask value for mark mask to store */ #endif - struct timer_list gc; /* garbage collection when timeout enabled */ - struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_MULTI u8 ahash_max; /* max elements in an array block */ #endif #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; /* netmask value for subnets to store */ #endif + struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_NETS - struct net_prefixes nets[0]; /* book-keeping of prefixes */ + struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */ #endif }; -#endif /* htype */ #ifdef IP_SET_HASH_WITH_NETS /* Network cidr size book keeping when the hash stores different @@ -348,13 +346,7 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 n) static size_t mtype_ahash_memsize(const struct htype *h, const struct htable *t) { - size_t memsize = sizeof(*h) + sizeof(*t); - -#ifdef IP_SET_HASH_WITH_NETS - memsize += sizeof(struct net_prefixes) * NLEN; -#endif - - return memsize; + return sizeof(*h) + sizeof(*t); } /* Get the ith element from the array block n */ @@ -392,7 +384,7 @@ mtype_flush(struct ip_set *set) kfree_rcu(n, rcu); } #ifdef IP_SET_HASH_WITH_NETS - memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN); + memset(h->nets, 0, sizeof(h->nets)); #endif set->elements = 0; set->ext_size = 0; @@ -1290,9 +1282,6 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); hsize = sizeof(*h); -#ifdef IP_SET_HASH_WITH_NETS - hsize += sizeof(struct net_prefixes) * NLEN; -#endif h = kzalloc(hsize, GFP_KERNEL); if (!h) return -ENOMEM; diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index 9d6bf19..20bfbd3 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -82,7 +82,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ip *h = set->data; + const struct hash_ip4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip4_elem e = { 0 }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -101,7 +101,7 @@ static int hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ip *h = set->data; + const struct hash_ip4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip4_elem e = { 0 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -199,7 +199,7 @@ nla_put_failure: } static inline void -hash_ip6_data_next(struct hash_ip4_elem *next, const struct hash_ip6_elem *e) +hash_ip6_data_next(struct hash_ip6_elem *next, const struct hash_ip6_elem *e) { } @@ -217,7 +217,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ip *h = set->data; + const struct hash_ip6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip6_elem e = { { .all = { 0 } } }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -234,7 +234,7 @@ static int hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ip *h = set->data; + const struct hash_ip6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip6_elem e = { { .all = { 0 } } }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); diff --git a/net/netfilter/ipset/ip_set_hash_ipmark.c b/net/netfilter/ipset/ip_set_hash_ipmark.c index a0695a2..b64cf14 100644 --- a/net/netfilter/ipset/ip_set_hash_ipmark.c +++ b/net/netfilter/ipset/ip_set_hash_ipmark.c @@ -85,7 +85,7 @@ hash_ipmark4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipmark *h = set->data; + const struct hash_ipmark4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipmark4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -101,7 +101,7 @@ static int hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipmark *h = set->data; + const struct hash_ipmark4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipmark4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -193,7 +193,7 @@ nla_put_failure: } static inline void -hash_ipmark6_data_next(struct hash_ipmark4_elem *next, +hash_ipmark6_data_next(struct hash_ipmark6_elem *next, const struct hash_ipmark6_elem *d) { } @@ -211,7 +211,7 @@ hash_ipmark6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipmark *h = set->data; + const struct hash_ipmark6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipmark6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -227,7 +227,7 @@ static int hash_ipmark6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipmark *h = set->data; + const struct hash_ipmark6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipmark6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 9d84b3d..f438740 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -108,7 +108,7 @@ static int hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipport *h = set->data; + const struct hash_ipport4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem e = { .ip = 0 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -231,7 +231,7 @@ nla_put_failure: } static inline void -hash_ipport6_data_next(struct hash_ipport4_elem *next, +hash_ipport6_data_next(struct hash_ipport6_elem *next, const struct hash_ipport6_elem *d) { next->port = d->port; @@ -266,7 +266,7 @@ static int hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipport *h = set->data; + const struct hash_ipport6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem e = { .ip = { .all = { 0 } } }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 215b7b9..6215fb8 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -111,7 +111,7 @@ static int hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipportip *h = set->data; + const struct hash_ipportip4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem e = { .ip = 0 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -241,7 +241,7 @@ nla_put_failure: } static inline void -hash_ipportip6_data_next(struct hash_ipportip4_elem *next, +hash_ipportip6_data_next(struct hash_ipportip6_elem *next, const struct hash_ipportip6_elem *d) { next->port = d->port; @@ -277,7 +277,7 @@ static int hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipportip *h = set->data; + const struct hash_ipportip6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip6_elem e = { .ip = { .all = { 0 } } }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 9ca7196..5ab1b99 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -138,7 +138,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipportnet *h = set->data; + const struct hash_ipportnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), @@ -163,7 +163,7 @@ static int hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipportnet *h = set->data; + const struct hash_ipportnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -370,7 +370,7 @@ nla_put_failure: } static inline void -hash_ipportnet6_data_next(struct hash_ipportnet4_elem *next, +hash_ipportnet6_data_next(struct hash_ipportnet6_elem *next, const struct hash_ipportnet6_elem *d) { next->port = d->port; @@ -389,7 +389,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipportnet *h = set->data; + const struct hash_ipportnet6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), @@ -414,7 +414,7 @@ static int hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_ipportnet *h = set->data; + const struct hash_ipportnet6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 3e4bffd..5d9e8954 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -117,7 +117,7 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_net *h = set->data; + const struct hash_net4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), @@ -139,7 +139,7 @@ static int hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_net *h = set->data; + const struct hash_net4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { .cidr = HOST_MASK }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -268,7 +268,7 @@ nla_put_failure: } static inline void -hash_net6_data_next(struct hash_net4_elem *next, +hash_net6_data_next(struct hash_net6_elem *next, const struct hash_net6_elem *d) { } @@ -286,7 +286,7 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_net *h = set->data; + const struct hash_net6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index aa1a776..44cf119 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -156,7 +156,7 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - struct hash_netiface *h = set->data; + struct hash_netiface4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), @@ -196,7 +196,7 @@ static int hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - struct hash_netiface *h = set->data; + struct hash_netiface4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -348,7 +348,7 @@ nla_put_failure: } static inline void -hash_netiface6_data_next(struct hash_netiface4_elem *next, +hash_netiface6_data_next(struct hash_netiface6_elem *next, const struct hash_netiface6_elem *d) { } @@ -367,7 +367,7 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - struct hash_netiface *h = set->data; + struct hash_netiface6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c index a93dfeb..db614e1 100644 --- a/net/netfilter/ipset/ip_set_hash_netnet.c +++ b/net/netfilter/ipset/ip_set_hash_netnet.c @@ -143,7 +143,7 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_netnet *h = set->data; + const struct hash_netnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -165,7 +165,7 @@ static int hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_netnet *h = set->data; + const struct hash_netnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -352,7 +352,7 @@ nla_put_failure: } static inline void -hash_netnet6_data_next(struct hash_netnet4_elem *next, +hash_netnet6_data_next(struct hash_netnet6_elem *next, const struct hash_netnet6_elem *d) { } @@ -377,7 +377,7 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_netnet *h = set->data; + const struct hash_netnet6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 731813e..54b64b6 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -133,7 +133,7 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_netport *h = set->data; + const struct hash_netport4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), @@ -157,7 +157,7 @@ static int hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_netport *h = set->data; + const struct hash_netport4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -329,7 +329,7 @@ nla_put_failure: } static inline void -hash_netport6_data_next(struct hash_netport4_elem *next, +hash_netport6_data_next(struct hash_netport6_elem *next, const struct hash_netport6_elem *d) { next->port = d->port; @@ -348,7 +348,7 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_netport *h = set->data; + const struct hash_netport6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), @@ -372,7 +372,7 @@ static int hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_netport *h = set->data; + const struct hash_netport6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { .cidr = HOST_MASK - 1 }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c index 9a14c23..aff8469 100644 --- a/net/netfilter/ipset/ip_set_hash_netportnet.c +++ b/net/netfilter/ipset/ip_set_hash_netportnet.c @@ -154,7 +154,7 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_netportnet *h = set->data; + const struct hash_netportnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netportnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -180,7 +180,7 @@ static int hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_netportnet *h = set->data; + const struct hash_netportnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netportnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); @@ -406,7 +406,7 @@ nla_put_failure: } static inline void -hash_netportnet6_data_next(struct hash_netportnet4_elem *next, +hash_netportnet6_data_next(struct hash_netportnet6_elem *next, const struct hash_netportnet6_elem *d) { next->port = d->port; @@ -432,7 +432,7 @@ hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_netportnet *h = set->data; + const struct hash_netportnet6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netportnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -458,7 +458,7 @@ static int hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_netportnet *h = set->data; + const struct hash_netportnet6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netportnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_UEXT(set); -- cgit v1.1 From 9be37d2acdc4fa452a9e250341cbb77668ab94e7 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 2 Nov 2015 20:27:58 +0100 Subject: netfilter: ipset: Collapse same condition body to a single one The set full case (with net_ratelimit()-ed pr_warn()) is already handled, simply jump there. Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index c600f6d..1c9b84e 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -719,14 +719,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, key = HKEY(value, h->initval, t->htable_bits); n = __ipset_dereference_protected(hbucket(t, key), 1); if (!n) { - if (forceadd) { - if (net_ratelimit()) - pr_warn("Set %s is full, maxelem %u reached\n", - set->name, h->maxelem); - return -IPSET_ERR_HASH_FULL; - } else if (set->elements >= h->maxelem) { + if (forceadd || set->elements >= h->maxelem) goto set_full; - } old = NULL; n = kzalloc(sizeof(*n) + AHASH_INIT_SIZE * set->dsize, GFP_ATOMIC); -- cgit v1.1 From a71bdbfa99272f0beb92b669d2bbc626871e00e5 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 4 Nov 2015 09:44:29 +0100 Subject: netfilter: ipset: Fix reported memory size for hash:* types The calculation of the full allocated memory did not take into account the size of the base hash bucket structure at some places. Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_gen.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 1c9b84e..88b70fc 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -85,6 +85,8 @@ struct htable { }; #define hbucket(h, i) ((h)->bucket[i]) +#define ext_size(n, dsize) \ + (sizeof(struct hbucket) + (n) * (dsize)) #ifndef IPSET_NET_COUNT #define IPSET_NET_COUNT 1 @@ -519,7 +521,7 @@ mtype_expire(struct ip_set *set, struct htype *h) d++; } tmp->pos = d; - set->ext_size -= AHASH_INIT_SIZE * dsize; + set->ext_size -= ext_size(AHASH_INIT_SIZE, dsize); rcu_assign_pointer(hbucket(t, i), tmp); kfree_rcu(n, rcu); } @@ -625,7 +627,7 @@ retry: goto cleanup; } m->size = AHASH_INIT_SIZE; - extsize = sizeof(*m) + AHASH_INIT_SIZE * dsize; + extsize = ext_size(AHASH_INIT_SIZE, dsize); RCU_INIT_POINTER(hbucket(t, key), m); } else if (m->pos >= m->size) { struct hbucket *ht; @@ -645,7 +647,7 @@ retry: memcpy(ht, m, sizeof(struct hbucket) + m->size * dsize); ht->size = m->size + AHASH_INIT_SIZE; - extsize += AHASH_INIT_SIZE * dsize; + extsize += ext_size(AHASH_INIT_SIZE, dsize); kfree(m); m = ht; RCU_INIT_POINTER(hbucket(t, key), ht); @@ -727,7 +729,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (!n) return -ENOMEM; n->size = AHASH_INIT_SIZE; - set->ext_size += sizeof(*n) + AHASH_INIT_SIZE * set->dsize; + set->ext_size += ext_size(AHASH_INIT_SIZE, set->dsize); goto copy_elem; } for (i = 0; i < n->pos; i++) { @@ -791,7 +793,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, memcpy(n, old, sizeof(struct hbucket) + old->size * set->dsize); n->size = old->size + AHASH_INIT_SIZE; - set->ext_size += AHASH_INIT_SIZE * set->dsize; + set->ext_size += ext_size(AHASH_INIT_SIZE, set->dsize); } copy_elem: @@ -883,7 +885,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, k++; } if (n->pos == 0 && k == 0) { - set->ext_size -= sizeof(*n) + n->size * dsize; + set->ext_size -= ext_size(n->size, dsize); rcu_assign_pointer(hbucket(t, key), NULL); kfree_rcu(n, rcu); } else if (k >= AHASH_INIT_SIZE) { @@ -902,7 +904,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, k++; } tmp->pos = k; - set->ext_size -= AHASH_INIT_SIZE * dsize; + set->ext_size -= ext_size(AHASH_INIT_SIZE, dsize); rcu_assign_pointer(hbucket(t, key), tmp); kfree_rcu(n, rcu); } -- cgit v1.1 From c0db95c7bcca956d444f233aee5c2b124392ef9a Mon Sep 17 00:00:00 2001 From: Tomasz Chilinski Date: Thu, 5 May 2016 07:21:26 +0200 Subject: netfilter: ipset: hash:ipmac type support added to ipset Introduce the hash:ipmac type. Signed-off-by: Tomasz Chili??ski Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/Kconfig | 9 + net/netfilter/ipset/Makefile | 1 + net/netfilter/ipset/ip_set_hash_ipmac.c | 315 ++++++++++++++++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 net/netfilter/ipset/ip_set_hash_ipmac.c diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index 234a8ec..4083a80 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -99,6 +99,15 @@ config IP_SET_HASH_IPPORTNET To compile it as a module, choose M here. If unsure, say N. +config IP_SET_HASH_IPMAC + tristate "hash:ip,mac set support" + depends on IP_SET + help + This option adds the hash:ip,mac set type support, by which + one can store IPv4/IPv6 address and MAC (ethernet address) pairs in a set. + + To compile it as a module, choose M here. If unsure, say N. + config IP_SET_HASH_MAC tristate "hash:mac set support" depends on IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index 3dbd5e9..28ec148 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_IP_SET_BITMAP_PORT) += ip_set_bitmap_port.o # hash types obj-$(CONFIG_IP_SET_HASH_IP) += ip_set_hash_ip.o +obj-$(CONFIG_IP_SET_HASH_IPMAC) += ip_set_hash_ipmac.o obj-$(CONFIG_IP_SET_HASH_IPMARK) += ip_set_hash_ipmark.o obj-$(CONFIG_IP_SET_HASH_IPPORT) += ip_set_hash_ipport.o obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o diff --git a/net/netfilter/ipset/ip_set_hash_ipmac.c b/net/netfilter/ipset/ip_set_hash_ipmac.c new file mode 100644 index 0000000..d9eb144 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_ipmac.c @@ -0,0 +1,315 @@ +/* Copyright (C) 2016 Tomasz Chilinski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the hash:ip,mac type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define IPSET_TYPE_REV_MIN 0 +#define IPSET_TYPE_REV_MAX 0 + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Chilinski "); +IP_SET_MODULE_DESC("hash:ip,mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +MODULE_ALIAS("ip_set_hash:ip,mac"); + +/* Type specific function prefix */ +#define HTYPE hash_ipmac + +/* Zero valued element is not supported */ +static const unsigned char invalid_ether[ETH_ALEN] = { 0 }; + +/* IPv4 variant */ + +/* Member elements */ +struct hash_ipmac4_elem { + /* Zero valued IP addresses cannot be stored */ + __be32 ip; + union { + unsigned char ether[ETH_ALEN]; + __be32 foo[2]; + }; +}; + +/* Common functions */ + +static inline bool +hash_ipmac4_data_equal(const struct hash_ipmac4_elem *e1, + const struct hash_ipmac4_elem *e2, + u32 *multi) +{ + return e1->ip == e2->ip && ether_addr_equal(e1->ether, e2->ether); +} + +static bool +hash_ipmac4_data_list(struct sk_buff *skb, const struct hash_ipmac4_elem *e) +{ + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, e->ip) || + nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, e->ether)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return 1; +} + +static inline void +hash_ipmac4_data_next(struct hash_ipmac4_elem *next, + const struct hash_ipmac4_elem *e) +{ + next->ip = e->ip; +} + +#define MTYPE hash_ipmac4 +#define PF 4 +#define HOST_MASK 32 +#define HKEY_DATALEN sizeof(struct hash_ipmac4_elem) +#include "ip_set_hash_gen.h" + +static int +hash_ipmac4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt) +{ + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipmac4_elem e = { .ip = 0, { .foo[0] = 0, .foo[1] = 0 } }; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + + /* MAC can be src only */ + if (!(opt->flags & IPSET_DIM_TWO_SRC)) + return 0; + + if (skb_mac_header(skb) < skb->head || + (skb_mac_header(skb) + ETH_HLEN) > skb->data) + return -EINVAL; + + memcpy(e.ether, eth_hdr(skb)->h_source, ETH_ALEN); + if (ether_addr_equal(e.ether, invalid_ether)) + return -EINVAL; + + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); + + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); +} + +static int +hash_ipmac4_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipmac4_elem e = { .ip = 0, { .foo[0] = 0, .foo[1] = 0 } }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + int ret; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !tb[IPSET_ATTR_ETHER] || + nla_len(tb[IPSET_ATTR_ETHER]) != ETH_ALEN || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBMARK) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBPRIO) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBQUEUE))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip) || + ip_set_get_extensions(set, tb, &ext); + if (ret) + return ret; + memcpy(e.ether, nla_data(tb[IPSET_ATTR_ETHER]), ETH_ALEN); + if (ether_addr_equal(e.ether, invalid_ether)) + return -IPSET_ERR_HASH_ELEM; + + return adtfn(set, &e, &ext, &ext, flags); +} + +/* IPv6 variant */ + +/* Member elements */ +struct hash_ipmac6_elem { + /* Zero valued IP addresses cannot be stored */ + union nf_inet_addr ip; + union { + unsigned char ether[ETH_ALEN]; + __be32 foo[2]; + }; +}; + +/* Common functions */ + +static inline bool +hash_ipmac6_data_equal(const struct hash_ipmac6_elem *e1, + const struct hash_ipmac6_elem *e2, + u32 *multi) +{ + return ipv6_addr_equal(&e1->ip.in6, &e2->ip.in6) && + ether_addr_equal(e1->ether, e2->ether); +} + +static bool +hash_ipmac6_data_list(struct sk_buff *skb, const struct hash_ipmac6_elem *e) +{ + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, e->ether)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return 1; +} + +static inline void +hash_ipmac6_data_next(struct hash_ipmac6_elem *next, + const struct hash_ipmac6_elem *e) +{ +} + +#undef MTYPE +#undef PF +#undef HOST_MASK +#undef HKEY_DATALEN + +#define MTYPE hash_ipmac6 +#define PF 6 +#define HOST_MASK 128 +#define HKEY_DATALEN sizeof(struct hash_ipmac6_elem) +#define IP_SET_EMIT_CREATE +#include "ip_set_hash_gen.h" + +static int +hash_ipmac6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt) +{ + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipmac6_elem e = { + { .all = { 0 } }, + { .foo[0] = 0, .foo[1] = 0 } + }; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + + /* MAC can be src only */ + if (!(opt->flags & IPSET_DIM_TWO_SRC)) + return 0; + + if (skb_mac_header(skb) < skb->head || + (skb_mac_header(skb) + ETH_HLEN) > skb->data) + return -EINVAL; + + memcpy(e.ether, eth_hdr(skb)->h_source, ETH_ALEN); + if (ether_addr_equal(e.ether, invalid_ether)) + return -EINVAL; + + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); + + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); +} + +static int +hash_ipmac6_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_ipmac6_elem e = { + { .all = { 0 } }, + { .foo[0] = 0, .foo[1] = 0 } + }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + int ret; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !tb[IPSET_ATTR_ETHER] || + nla_len(tb[IPSET_ATTR_ETHER]) != ETH_ALEN || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBMARK) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBPRIO) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBQUEUE))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) || + ip_set_get_extensions(set, tb, &ext); + if (ret) + return ret; + + memcpy(e.ether, nla_data(tb[IPSET_ATTR_ETHER]), ETH_ALEN); + if (ether_addr_equal(e.ether, invalid_ether)) + return -IPSET_ERR_HASH_ELEM; + + return adtfn(set, &e, &ext, &ext, flags); +} + +static struct ip_set_type hash_ipmac_type __read_mostly = { + .name = "hash:ip,mac", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_MAC, + .dimension = IPSET_DIM_TWO, + .family = NFPROTO_UNSPEC, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, + .create = hash_ipmac_create, + .create_policy = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + }, + .adt_policy = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, + .len = ETH_ALEN }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, + [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, + [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, + [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, + [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, + }, + .me = THIS_MODULE, +}; + +static int __init +hash_ipmac_init(void) +{ + return ip_set_type_register(&hash_ipmac_type); +} + +static void __exit +hash_ipmac_fini(void) +{ + ip_set_type_unregister(&hash_ipmac_type); +} + +module_init(hash_ipmac_init); +module_exit(hash_ipmac_fini); -- cgit v1.1 From fcb58a033271e448c3e49a3b3d3e9e0c0bcf61d9 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 10 Nov 2016 12:32:07 +0100 Subject: netfilter: ipset: use setup_timer() and mod_timer(). Use setup_timer() and instead of init_timer(), being the preferred way of setting up a timer. Also, quoting the mod_timer() function comment: -> mod_timer() is a more efficient way to update the expire field of an active timer (if the timer is inactive it will be activated). Use setup_timer() and mod_timer() to setup and arm a timer, making the code compact and easier to read. Signed-off-by: Muhammad Falak R Wani Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_bitmap_gen.h | 7 ++----- net/netfilter/ipset/ip_set_hash_gen.h | 7 ++----- net/netfilter/ipset/ip_set_list_set.c | 7 ++----- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index f8ea26c..6f09a99 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -41,11 +41,8 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) { struct mtype *map = set->data; - init_timer(&map->gc); - map->gc.data = (unsigned long)set; - map->gc.function = gc; - map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; - add_timer(&map->gc); + setup_timer(&map->gc, gc, (unsigned long)set); + mod_timer(&map->gc, jiffies + IPSET_GC_PERIOD(set->timeout) * HZ); } static void diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 88b70fc..1b05d4a 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -433,11 +433,8 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) { struct htype *h = set->data; - init_timer(&h->gc); - h->gc.data = (unsigned long)set; - h->gc.function = gc; - h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; - add_timer(&h->gc); + setup_timer(&h->gc, gc, (unsigned long)set); + mod_timer(&h->gc, jiffies + IPSET_GC_PERIOD(set->timeout) * HZ); pr_debug("gc initialized, run in every %u\n", IPSET_GC_PERIOD(set->timeout)); } diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index dede343..51077c5 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -586,11 +586,8 @@ list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) { struct list_set *map = set->data; - init_timer(&map->gc); - map->gc.data = (unsigned long)set; - map->gc.function = gc; - map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; - add_timer(&map->gc); + setup_timer(&map->gc, gc, (unsigned long)set); + mod_timer(&map->gc, jiffies + IPSET_GC_PERIOD(set->timeout) * HZ); } /* Create list:set type of sets */ -- cgit v1.1 From 737d387b75ec79d62b12fd8996c634b4635cc98f Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sat, 15 Oct 2016 09:13:14 +0800 Subject: netfilter: ipset: hash: fix boolreturn.cocci warnings net/netfilter/ipset/ip_set_hash_ipmac.c:70:8-9: WARNING: return of 0/1 in function 'hash_ipmac4_data_list' with return type bool net/netfilter/ipset/ip_set_hash_ipmac.c:178:8-9: WARNING: return of 0/1 in function 'hash_ipmac6_data_list' with return type bool Return statements in functions returning bool should use true/false instead of 1/0. Generated by: scripts/coccinelle/misc/boolreturn.cocci CC: Tomasz Chilinski Signed-off-by: Fengguang Wu Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_hash_ipmac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_ipmac.c b/net/netfilter/ipset/ip_set_hash_ipmac.c index d9eb144..1ab5ed2 100644 --- a/net/netfilter/ipset/ip_set_hash_ipmac.c +++ b/net/netfilter/ipset/ip_set_hash_ipmac.c @@ -67,10 +67,10 @@ hash_ipmac4_data_list(struct sk_buff *skb, const struct hash_ipmac4_elem *e) if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, e->ip) || nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, e->ether)) goto nla_put_failure; - return 0; + return false; nla_put_failure: - return 1; + return true; } static inline void @@ -175,10 +175,10 @@ hash_ipmac6_data_list(struct sk_buff *skb, const struct hash_ipmac6_elem *e) if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, e->ether)) goto nla_put_failure; - return 0; + return false; nla_put_failure: - return 1; + return true; } static inline void -- cgit v1.1 From edd9a29829e62d439b327cf189b44151570a604d Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Wed, 9 Nov 2016 18:13:45 +0000 Subject: Doc: hisi: hns adds mc-mac-mask property Since there is not enough tcam table entries for every vlan and multicast address, HNS needs to add support of fuzzy matching of TCAM tables. Adding the property to mask the bits to be fuzzy matched, so update the bindings document Signed-off-by: Kejian Yan Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt index d4b7f2e..abfbeec 100644 --- a/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt +++ b/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt @@ -45,6 +45,12 @@ Required properties: depends on the hardware user manual. - port-mode-offset: is offset of port mode field for each port in dsaf. Its value depends on the hardware user manual. +- mc-mac-mask: mask of multicast address, determines bit in multicast address + to set: + 1 stands for this bit will be precisely matched, TCAM will check this bit of + MAC address. + 0 stands for this bit will be fuzzy matched, TCAM won't care about this bit + of MAC address. [1] Documentation/devicetree/bindings/net/phy.txt @@ -74,10 +80,12 @@ dsaf0: dsa@c7000000 { reg = 0; phy-handle = <&phy0>; serdes-syscon = <&serdes>; + mc-mac-mask = [ff f0 00 00 00 00]; }; port@1 { reg = 1; serdes-syscon = <&serdes>; + mc-mac-mask = [ff f0 00 00 00 00]; }; }; -- cgit v1.1 From 153b1d48707eff876c1e0d72bb1e3dcd99613729 Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Wed, 9 Nov 2016 18:13:46 +0000 Subject: net: hns: add fuzzy match of tcam table for hns Since there is not enough tcam table entries for vlan and multicast address, HNSv2 needs to add support of fuzzy matching of TCAM tables. To add fuzzy match of TCAM, we Add the property to mask the bits to be fuzzy matched Signed-off-by: Kejian Yan Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 16 ++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 8 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 140 +++++++++++++++------ drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 6 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 2 + 5 files changed, 118 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 55cbb6c..2d64f53 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -335,8 +335,7 @@ static void hns_mac_param_get(struct mac_params *param, { param->vaddr = (void *)mac_cb->vaddr; param->mac_mode = hns_get_enet_interface(mac_cb); - memcpy(param->addr, mac_cb->addr_entry_idx[0].addr, - MAC_NUM_OCTETS_PER_ADDR); + ether_addr_copy(param->addr, mac_cb->addr_entry_idx[0].addr); param->mac_id = mac_cb->mac_id; param->dev = mac_cb->dev; } @@ -353,8 +352,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb, { int ret; struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; - u8 addr[MAC_NUM_OCTETS_PER_ADDR] - = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + u8 addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct dsaf_drv_mac_single_dest_entry mac_entry; /* directy return ok in debug network mode */ @@ -389,8 +387,7 @@ int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, bool enable) int ret; struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; u8 port_num; - u8 addr[MAC_NUM_OCTETS_PER_ADDR] - = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + u8 addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct mac_entry_idx *uc_mac_entry; struct dsaf_drv_mac_single_dest_entry mac_entry; @@ -868,6 +865,13 @@ static int hns_mac_get_info(struct hns_mac_cb *mac_cb) } } + if (fwnode_property_read_u8_array(mac_cb->fw_port, "mc-mac-mask", + mac_cb->mc_mask, ETH_ALEN)) { + dev_warn(mac_cb->dev, + "no mc-mac-mask property, set to default value.\n"); + eth_broadcast_addr(mac_cb->mc_mask); + } + return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index d3a1f72..1d941d5 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -56,9 +56,6 @@ struct dsaf_device; /*check mac addr multicast*/ #define MAC_IS_MULTICAST(p) ((*((u8 *)((p) + 0)) & 0x01) ? (1) : (0)) -/**< Number of octets (8-bit bytes) in an ethernet address */ -#define MAC_NUM_OCTETS_PER_ADDR 6 - struct mac_priv { void *mac; }; @@ -189,7 +186,7 @@ struct mac_statistics { /*mac para struct ,mac get param from nic or dsaf when initialize*/ struct mac_params { - char addr[MAC_NUM_OCTETS_PER_ADDR]; + char addr[ETH_ALEN]; void *vaddr; /*virtual address*/ struct device *dev; u8 mac_id; @@ -214,7 +211,7 @@ struct mac_info { }; struct mac_entry_idx { - u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u8 addr[ETH_ALEN]; u16 vlan_id:12; u16 valid:1; u16 qos:3; @@ -317,6 +314,7 @@ struct hns_mac_cb { u8 __iomem *serdes_vaddr; struct regmap *serdes_ctrl; struct regmap *cpld_ctrl; + char mc_mask[ETH_ALEN]; u32 cpld_ctrl_reg; u32 port_rst_off; u32 port_mode_off; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 8ea3d95..64f45ee 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -591,6 +591,16 @@ static void hns_dsaf_voq_bp_all_thrd_cfg(struct dsaf_device *dsaf_dev) } } +static void hns_dsaf_tbl_tcam_match_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_data *ptbl_tcam_data) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MATCH_CFG_L_REG, + ptbl_tcam_data->tbl_tcam_data_low); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MATCH_CFG_H_REG, + ptbl_tcam_data->tbl_tcam_data_high); +} + /** * hns_dsaf_tbl_tcam_data_cfg - tbl * @dsaf_id: dsa fabric id @@ -894,15 +904,16 @@ static void hns_dsaf_tcam_uc_cfg( } /** - * hns_dsaf_tcam_mc_cfg - INT - * @dsaf_id: dsa fabric id - * @address, - * @ptbl_tcam_data, - * @ptbl_tcam_mcast, + * hns_dsaf_tcam_mc_cfg - cfg the tcam for mc + * @dsaf_dev: dsa fabric device struct pointer + * @address: tcam index + * @ptbl_tcam_data: tcam data struct pointer + * @ptbl_tcam_mcast: tcam mask struct pointer, it must be null for HNSv1 */ static void hns_dsaf_tcam_mc_cfg( struct dsaf_device *dsaf_dev, u32 address, struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_data *ptbl_tcam_mask, struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast) { spin_lock_bh(&dsaf_dev->tcam_lock); @@ -913,7 +924,11 @@ static void hns_dsaf_tcam_mc_cfg( hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data); /*Write Tcam Mcast*/ hns_dsaf_tbl_tcam_mcast_cfg(dsaf_dev, ptbl_tcam_mcast); - /*Write Plus*/ + /* Write Match Data */ + if (ptbl_tcam_mask) + hns_dsaf_tbl_tcam_match_cfg(dsaf_dev, ptbl_tcam_mask); + + /* Write Puls */ hns_dsaf_tbl_tcam_data_mcast_pul(dsaf_dev); spin_unlock_bh(&dsaf_dev->tcam_lock); @@ -1625,7 +1640,7 @@ int hns_dsaf_set_mac_mc_entry( hns_dsaf_tcam_mc_cfg( dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + (struct dsaf_tbl_tcam_data *)(&mac_key), NULL, &mac_data); /* config software entry */ soft_mac_entry += entry_index; @@ -1636,6 +1651,16 @@ int hns_dsaf_set_mac_mc_entry( return 0; } +static void hns_dsaf_mc_mask_bit_clear(char *dst, const char *src) +{ + u16 *a = (u16 *)dst; + const u16 *b = (const u16 *)src; + + a[0] &= b[0]; + a[1] &= b[1]; + a[2] &= b[2]; +} + /** * hns_dsaf_add_mac_mc_port - add mac mc-port * @dsaf_dev: dsa fabric device struct pointer @@ -1646,11 +1671,14 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, { u16 entry_index = DSAF_INVALID_ENTRY_IDX; struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_drv_tbl_tcam_key mask_key; + struct dsaf_tbl_tcam_data *pmask_key = NULL; struct dsaf_tbl_tcam_mcast_cfg mac_data; - struct dsaf_drv_priv *priv = - (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev); struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; - struct dsaf_drv_tbl_tcam_key tmp_mac_key; + struct dsaf_tbl_tcam_data tcam_data; + u8 mc_addr[ETH_ALEN]; + u8 *mc_mask; int mskid; /*chechk mac addr */ @@ -1660,14 +1688,28 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, return -EINVAL; } + ether_addr_copy(mc_addr, mac_entry->addr); + mc_mask = dsaf_dev->mac_cb[mac_entry->in_port_num]->mc_mask; + if (!AE_IS_VER1(dsaf_dev->dsaf_ver)) { + /* prepare for key data setting */ + hns_dsaf_mc_mask_bit_clear(mc_addr, mc_mask); + + /* config key mask */ + hns_dsaf_set_mac_key(dsaf_dev, &mask_key, + 0x0, + 0xff, + mc_mask); + pmask_key = (struct dsaf_tbl_tcam_data *)(&mask_key); + } + /*config key */ hns_dsaf_set_mac_key( dsaf_dev, &mac_key, mac_entry->in_vlan_id, - mac_entry->in_port_num, mac_entry->addr); + mac_entry->in_port_num, mc_addr); memset(&mac_data, 0, sizeof(struct dsaf_tbl_tcam_mcast_cfg)); - /*check exist? */ + /* check if the tcam is exist */ entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); if (entry_index == DSAF_INVALID_ENTRY_IDX) { /*if hasnot , find a empty*/ @@ -1681,11 +1723,11 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, return -EINVAL; } } else { - /*if exist, add in */ - hns_dsaf_tcam_mc_get( - dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + /* if exist, add in */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, + &mac_data); } + /* config hardware entry */ if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { mskid = mac_entry->port_num; @@ -1708,9 +1750,12 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, dsaf_dev->ae_dev.name, mac_key.high.val, mac_key.low.val, entry_index); - hns_dsaf_tcam_mc_cfg( - dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + tcam_data.tbl_tcam_data_high = mac_key.high.val; + tcam_data.tbl_tcam_data_low = mac_key.low.val; + + /* config mc entry with mask */ + hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index, &tcam_data, + pmask_key, &mac_data); /*config software entry */ soft_mac_entry += entry_index; @@ -1782,15 +1827,18 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, { u16 entry_index = DSAF_INVALID_ENTRY_IDX; struct dsaf_drv_tbl_tcam_key mac_key; - struct dsaf_drv_priv *priv = - (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev); struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; u16 vlan_id; u8 in_port_num; struct dsaf_tbl_tcam_mcast_cfg mac_data; - struct dsaf_drv_tbl_tcam_key tmp_mac_key; + struct dsaf_tbl_tcam_data tcam_data; int mskid; const u8 empty_msk[sizeof(mac_data.tbl_mcast_port_msk)] = {0}; + struct dsaf_drv_tbl_tcam_key mask_key; + struct dsaf_tbl_tcam_data *pmask_key = NULL; + u8 mc_addr[ETH_ALEN]; + u8 *mc_mask; if (!(void *)mac_entry) { dev_err(dsaf_dev->dev, @@ -1798,10 +1846,6 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, return -EINVAL; } - /*get key info*/ - vlan_id = mac_entry->in_vlan_id; - in_port_num = mac_entry->in_port_num; - /*check mac addr */ if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { dev_err(dsaf_dev->dev, "del_port failed, addr %pM!\n", @@ -1809,11 +1853,28 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, return -EINVAL; } - /*config key */ - hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, - mac_entry->addr); + /* always mask vlan_id field */ + ether_addr_copy(mc_addr, mac_entry->addr); + mc_mask = dsaf_dev->mac_cb[mac_entry->in_port_num]->mc_mask; - /*check is exist? */ + if (!AE_IS_VER1(dsaf_dev->dsaf_ver)) { + /* prepare for key data setting */ + hns_dsaf_mc_mask_bit_clear(mc_addr, mc_mask); + + /* config key mask */ + hns_dsaf_set_mac_key(dsaf_dev, &mask_key, 0x00, 0xff, mc_addr); + + pmask_key = (struct dsaf_tbl_tcam_data *)(&mask_key); + } + + /* get key info */ + vlan_id = mac_entry->in_vlan_id; + in_port_num = mac_entry->in_port_num; + + /* config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, mc_addr); + + /* check if the tcam entry is exist */ entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); if (entry_index == DSAF_INVALID_ENTRY_IDX) { /*find none */ @@ -1829,10 +1890,8 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, dsaf_dev->ae_dev.name, mac_key.high.val, mac_key.low.val, entry_index); - /*read entry*/ - hns_dsaf_tcam_mc_get( - dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + /* read entry */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); /*del the port*/ if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { @@ -1857,10 +1916,13 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, /* del soft entry */ soft_mac_entry += entry_index; soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; - } else { /* not zer, just del port, updata*/ - hns_dsaf_tcam_mc_cfg( - dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + } else { /* not zero, just del port, update */ + tcam_data.tbl_tcam_data_high = mac_key.high.val; + tcam_data.tbl_tcam_data_low = mac_key.low.val; + + hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index, + &tcam_data, + pmask_key, &mac_data); } return 0; @@ -1976,7 +2038,7 @@ int hns_dsaf_get_mac_entry_by_index( struct dsaf_tbl_tcam_mcast_cfg mac_data; struct dsaf_tbl_tcam_ucast_cfg mac_uc_data; - char mac_addr[MAC_NUM_OCTETS_PER_ADDR] = {0}; + char mac_addr[ETH_ALEN] = {0}; if (entry_index >= DSAF_TCAM_SUM) { /* find none, del error */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index c494fc5..f832a32 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -35,8 +35,6 @@ struct hns_mac_cb; #define DSAF_CFG_READ_CNT 30 -#define MAC_NUM_OCTETS_PER_ADDR 6 - #define DSAF_DUMP_REGS_NUM 504 #define DSAF_STATIC_NUM 28 #define DSAF_V2_STATIC_NUM 44 @@ -165,7 +163,7 @@ enum dsaf_mode { /*mac entry, mc or uc entry*/ struct dsaf_drv_mac_single_dest_entry { /* mac addr, match the entry*/ - u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u8 addr[ETH_ALEN]; u16 in_vlan_id; /* value of VlanId */ /* the vld input port num, dsaf-mode fix 0, */ @@ -179,7 +177,7 @@ struct dsaf_drv_mac_single_dest_entry { /*only mc entry*/ struct dsaf_drv_mac_multi_dest_entry { /* mac addr, match the entry*/ - u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u8 addr[ETH_ALEN]; u16 in_vlan_id; /* this mac addr output port,*/ /* bit0-bit5 means Port0-Port5(1bit is vld)**/ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index 878950a..6826b28 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -297,6 +297,8 @@ #define DSAF_TBL_LKUP_NUM_I_0_REG 0x50C0 #define DSAF_TBL_LKUP_NUM_O_0_REG 0x50E0 #define DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG 0x510C +#define DSAF_TBL_TCAM_MATCH_CFG_H_REG 0x5130 +#define DSAF_TBL_TCAM_MATCH_CFG_L_REG 0x5134 #define DSAF_INODE_FIFO_WL_0_REG 0x6000 #define DSAF_ONODE_FIFO_WL_0_REG 0x6020 -- cgit v1.1 From 1f5fa2dd1cfa4c1356e83ff4bdd82561cad95278 Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Wed, 9 Nov 2016 18:13:47 +0000 Subject: net: hns: fix for promisc mode in HNS driver If set promisc mode when there is some traffic, The service nic will cause system halted. We reserve the last 6 tcam entry for the 6 ports. If promisc mode is enabled, we can config the relative tcam as fuzzy matching and set to be valid, or set the tcam to be invalid Signed-off-by: Kejian Yan Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 2 + drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 63 ++++++++++++++++++++-- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 3 ++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 3 ++ 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 2d64f53..fc90260 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -1085,6 +1085,8 @@ void hns_mac_set_promisc(struct hns_mac_cb *mac_cb, u8 en) { struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + hns_dsaf_set_promisc_tcam(mac_cb->dsaf_dev, mac_cb->mac_id, !!en); + if (mac_ctrl_drv->set_promiscuous) mac_ctrl_drv->set_promiscuous(mac_ctrl_drv, en); } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 64f45ee..2721458 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -765,7 +765,7 @@ static void hns_dsaf_tbl_tcam_data_ucast_pul( void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en) { - if (!HNS_DSAF_IS_DEBUG(dsaf_dev)) + if (AE_IS_VER1(dsaf_dev->dsaf_ver) && !HNS_DSAF_IS_DEBUG(dsaf_dev)) dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_MIX_MODE_S, !!en); } @@ -1384,6 +1384,12 @@ static int hns_dsaf_init(struct dsaf_device *dsaf_dev) if (HNS_DSAF_IS_DEBUG(dsaf_dev)) return 0; + if (AE_IS_VER1(dsaf_dev->dsaf_ver)) + dsaf_dev->tcam_max_num = DSAF_TCAM_SUM; + else + dsaf_dev->tcam_max_num = + DSAF_TCAM_SUM - DSAFV2_MAC_FUZZY_TCAM_NUM; + spin_lock_init(&dsaf_dev->tcam_lock); ret = hns_dsaf_init_hw(dsaf_dev); if (ret) @@ -1439,7 +1445,7 @@ static u16 hns_dsaf_find_soft_mac_entry( u32 i; soft_mac_entry = priv->soft_mac_tbl; - for (i = 0; i < DSAF_TCAM_SUM; i++) { + for (i = 0; i < dsaf_dev->tcam_max_num; i++) { /* invall tab entry */ if ((soft_mac_entry->index != DSAF_INVALID_ENTRY_IDX) && (soft_mac_entry->tcam_key.high.val == mac_key->high.val) && @@ -1464,7 +1470,7 @@ static u16 hns_dsaf_find_empty_mac_entry(struct dsaf_device *dsaf_dev) u32 i; soft_mac_entry = priv->soft_mac_tbl; - for (i = 0; i < DSAF_TCAM_SUM; i++) { + for (i = 0; i < dsaf_dev->tcam_max_num; i++) { /* inv all entry */ if (soft_mac_entry->index == DSAF_INVALID_ENTRY_IDX) /* return find result --soft index */ @@ -2040,7 +2046,7 @@ int hns_dsaf_get_mac_entry_by_index( struct dsaf_tbl_tcam_ucast_cfg mac_uc_data; char mac_addr[ETH_ALEN] = {0}; - if (entry_index >= DSAF_TCAM_SUM) { + if (entry_index >= dsaf_dev->tcam_max_num) { /* find none, del error */ dev_err(dsaf_dev->dev, "get_uc_entry failed, %s\n", dsaf_dev->ae_dev.name); @@ -2732,6 +2738,55 @@ int hns_dsaf_get_regs_count(void) return DSAF_DUMP_REGS_NUM; } +/* Reserve the last TCAM entry for promisc support */ +#define dsaf_promisc_tcam_entry(port) \ + (DSAF_TCAM_SUM - DSAFV2_MAC_FUZZY_TCAM_NUM + (port)) +void hns_dsaf_set_promisc_tcam(struct dsaf_device *dsaf_dev, + u32 port, bool enable) +{ + struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + u16 entry_index; + struct dsaf_drv_tbl_tcam_key tbl_tcam_data, tbl_tcam_mask; + struct dsaf_tbl_tcam_mcast_cfg mac_data = {0}; + + if ((AE_IS_VER1(dsaf_dev->dsaf_ver)) || HNS_DSAF_IS_DEBUG(dsaf_dev)) + return; + + /* find the tcam entry index for promisc */ + entry_index = dsaf_promisc_tcam_entry(port); + + /* config key mask */ + if (enable) { + memset(&tbl_tcam_data, 0, sizeof(tbl_tcam_data)); + memset(&tbl_tcam_mask, 0, sizeof(tbl_tcam_mask)); + tbl_tcam_data.low.bits.port = port; + tbl_tcam_mask.low.bits.port = 0xf; /* [3:0]: port id */ + + /* SUB_QID */ + dsaf_set_bit(mac_data.tbl_mcast_port_msk[0], + DSAF_SERVICE_NW_NUM, true); + mac_data.tbl_mcast_item_vld = true; /* item_vld bit */ + } else { + mac_data.tbl_mcast_item_vld = false; /* item_vld bit */ + } + + dev_dbg(dsaf_dev->dev, + "set_promisc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, tbl_tcam_data.high.val, + tbl_tcam_data.low.val, entry_index); + + /* config promisc entry with mask */ + hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&tbl_tcam_data, + (struct dsaf_tbl_tcam_data *)&tbl_tcam_mask, + &mac_data); + + /* config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = enable ? entry_index : DSAF_INVALID_ENTRY_IDX; +} + /** * dsaf_probe - probo dsaf dev * @pdev: dasf platform device diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index f832a32..2cae6eb 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -341,6 +341,7 @@ struct dsaf_device { enum hal_dsaf_mode dsaf_en; enum hal_dsaf_tc_mode dsaf_tc_mode; u32 dsaf_ver; + u16 tcam_max_num; /* max TCAM entry for user except promisc */ struct ppe_common_cb *ppe_common[DSAF_COMM_DEV_NUM]; struct rcb_common_cb *rcb_common[DSAF_COMM_DEV_NUM]; @@ -459,6 +460,8 @@ void hns_dsaf_get_strings(int stringset, u8 *data, int port, void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data); int hns_dsaf_get_regs_count(void); void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en); +void hns_dsaf_set_promisc_tcam(struct dsaf_device *dsaf_dev, + u32 port, bool enable); void hns_dsaf_get_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id, u32 *en); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index 6826b28..3eb8b4d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -41,6 +41,9 @@ #define DSAF_SW_PORT_NUM 8 #define DSAF_TOTAL_QUEUE_NUM 129 +/* reserved a tcam entry for each port to support promisc by fuzzy match */ +#define DSAFV2_MAC_FUZZY_TCAM_NUM DSAF_MAX_PORT_NUM + #define DSAF_TCAM_SUM 512 #define DSAF_LINE_SUM (2048 * 14) -- cgit v1.1 From 2e7c80577e40f0968840455d7cff613c2ded316b Mon Sep 17 00:00:00 2001 From: Daode Huang Date: Wed, 9 Nov 2016 18:13:48 +0000 Subject: net: hns: set default mac pause time to 0xffff The default mac pause time set to 0xff which is too short for pausing, this patch change it to the max value 0xffff. Signed-off-by: Daode Huang Reviewed-by: Yisen Zhuang Reviewed-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index 1d941d5..a9b0cb3 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -31,7 +31,7 @@ struct dsaf_device; #define MAC_MIN_MTU 68 #define MAC_MAX_MTU_DBG MAC_DEFAULT_MTU -#define MAC_DEFAULT_PAUSE_TIME 0xff +#define MAC_DEFAULT_PAUSE_TIME 0xffff #define MAC_GMAC_IDX 0 #define MAC_XGMAC_IDX 1 -- cgit v1.1 From da2ef1e5585bc733c4cccee8390259abeb27f0bf Mon Sep 17 00:00:00 2001 From: Daode Huang Date: Wed, 9 Nov 2016 18:13:49 +0000 Subject: net: hns: bug fix about restart auto-negotiation When set auto-negotiation off and duplex half, if run "ethtool -r ethX" on port with phy, then the port will be failed to work. It should forbid to start auto-negotiation when auto-negotiate is off. This patch add the limited condition. Reported-by: Jinchuang Tian Signed-off-by: Daode Huang Reviewed-by: Yisen Zhuang Reviewed-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 87d5c94..3ac2183 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -1178,7 +1178,8 @@ static int hns_nic_nway_reset(struct net_device *netdev) struct phy_device *phy = netdev->phydev; if (netif_running(netdev)) { - if (phy) + /* if autoneg is disabled, don't restart auto-negotiation */ + if (phy && phy->autoneg == AUTONEG_ENABLE) ret = genphy_restart_aneg(phy); } -- cgit v1.1 From f165e03b94e80be95d6f2cc21526c81906563046 Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:50 +0000 Subject: net: hns: delete redundant macro definition This patch deletes redundant macro definitions in hns drivers. And change the .h file containing relation to make the layers more clearly Signed-off-by: Qianqian Xie Signed-off-by: Weiwei Deng Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 3 --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 2 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 2d0cb60..193248f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -18,9 +18,6 @@ #include "hns_dsaf_rcb.h" #define AE_NAME_PORT_ID_IDX 6 -#define ETH_STATIC_REG 1 -#define ETH_DUMP_REG 5 -#define ETH_GSTRING_LEN 32 static struct hns_mac_cb *hns_get_mac_cb(struct hnae_handle *handle) { diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index a9b0cb3..ca7e175 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -407,7 +407,7 @@ struct mac_driver { }; struct mac_stats_string { - char desc[64]; + char desc[ETH_GSTRING_LEN]; unsigned long offset; }; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index 3eb8b4d..f5505e8 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -314,7 +314,6 @@ #define PPE_COM_INTEN_REG 0x110 #define PPE_COM_RINT_REG 0x114 #define PPE_COM_INTSTS_REG 0x118 -#define PPE_COM_COMMON_CNT_CLR_CE_REG 0x1120 #define PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG 0x300 #define PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG 0x600 #define PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG 0x900 -- cgit v1.1 From 28b3012400ad349ad633187c7e6e37352a22dd6e Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:51 +0000 Subject: net: hns: modify ethtool statistics value error This patch modify the gmac_rx_filt_pkt and gmac_rx_octets_total_filt statistics value. The two statistics is inconsistent with register, and just the opposite. Signed-off-by: Qianqian Xie Signed-off-by: Jun He Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c index 1e1eb92..3382441 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -37,8 +37,8 @@ static const struct mac_stats_string g_gmac_stats_string[] = { {"gmac_rx_very_long_err", MAC_STATS_FIELD_OFF(rx_long_err)}, {"gmac_rx_runt_err", MAC_STATS_FIELD_OFF(rx_minto64)}, {"gmac_rx_short_err", MAC_STATS_FIELD_OFF(rx_under_min)}, - {"gmac_rx_filt_pkt", MAC_STATS_FIELD_OFF(rx_filter_bytes)}, - {"gmac_rx_octets_total_filt", MAC_STATS_FIELD_OFF(rx_filter_pkts)}, + {"gmac_rx_filt_pkt", MAC_STATS_FIELD_OFF(rx_filter_pkts)}, + {"gmac_rx_octets_total_filt", MAC_STATS_FIELD_OFF(rx_filter_bytes)}, {"gmac_rx_overrun_cnt", MAC_STATS_FIELD_OFF(rx_fifo_overrun_err)}, {"gmac_rx_length_err", MAC_STATS_FIELD_OFF(rx_len_err)}, {"gmac_rx_fail_comma", MAC_STATS_FIELD_OFF(rx_comma_err)}, -- cgit v1.1 From 20b3385aaab5af5127fa957bcfcaf0bd92b61447 Mon Sep 17 00:00:00 2001 From: Daode Huang Date: Wed, 9 Nov 2016 18:13:52 +0000 Subject: net: hns: fix to intimate the link-status change by adding LF/RF method In current scenario, when the interface is disabled we reset the XGMAC RX/TX functionality. This operation does not affects the PHY layer/SFP and which appears UP to the remote end(this behaviour is unlike GMAC). The result is remote end keeps on sending the packets which gets partly processed by XMAC and dropped. Since these are partly processed these appears as errored packets in the packet counter statistics. This patch fixes this behaviour and adds local-fault and remote-fault functionality which can be used to intimate the remote peer whenever the state of the interface changes. This patch also removes the existing hns_dsaf_xge_core_srst_by_port function which was being used to reset the RX/TX functionality at XGE Core. Reported-by: Jun He Signed-off-by: Daode Huang Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 2 -- drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c | 31 ---------------- drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 7 ++-- .../net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c | 41 +++++++++++++++------- .../net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h | 5 +-- 5 files changed, 37 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index 2cae6eb..7f70f34 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -306,8 +306,6 @@ struct dsaf_misc_op { /* reset series function, it will be reset if the dereset is 0 */ void (*dsaf_reset)(struct dsaf_device *dsaf_dev, bool dereset); void (*xge_srst)(struct dsaf_device *dsaf_dev, u32 port, bool dereset); - void (*xge_core_srst)(struct dsaf_device *dsaf_dev, u32 port, - bool dereset); void (*ge_srst)(struct dsaf_device *dsaf_dev, u32 port, bool dereset); void (*ppe_srst)(struct dsaf_device *dsaf_dev, u32 port, bool dereset); void (*ppe_comm_srst)(struct dsaf_device *dsaf_dev, bool dereset); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c index 67accce..a2c22d0 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c @@ -23,7 +23,6 @@ enum _dsm_op_index { enum _dsm_rst_type { HNS_DSAF_RESET_FUNC = 0x1, HNS_PPE_RESET_FUNC = 0x2, - HNS_XGE_CORE_RESET_FUNC = 0x3, HNS_XGE_RESET_FUNC = 0x4, HNS_GE_RESET_FUNC = 0x5, HNS_DSAF_CHN_RESET_FUNC = 0x6, @@ -213,26 +212,6 @@ static void hns_dsaf_xge_srst_by_port_acpi(struct dsaf_device *dsaf_dev, HNS_XGE_RESET_FUNC, port, dereset); } -static void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev, - u32 port, bool dereset) -{ - u32 reg_val = 0; - u32 reg_addr; - - if (port >= DSAF_XGE_NUM) - return; - - reg_val |= XGMAC_TRX_CORE_SRST_M - << dsaf_dev->mac_cb[port]->port_rst_off; - - if (!dereset) - reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG; - else - reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG; - - dsaf_write_sub(dsaf_dev, reg_addr, reg_val); -} - /** * hns_dsaf_srst_chns - reset dsaf channels * @dsaf_dev: dsaf device struct pointer @@ -293,14 +272,6 @@ void hns_dsaf_roce_srst_acpi(struct dsaf_device *dsaf_dev, bool dereset) HNS_ROCE_RESET_FUNC, 0, dereset); } -static void -hns_dsaf_xge_core_srst_by_port_acpi(struct dsaf_device *dsaf_dev, - u32 port, bool dereset) -{ - hns_dsaf_acpi_srst_by_port(dsaf_dev, HNS_OP_RESET_FUNC, - HNS_XGE_CORE_RESET_FUNC, port, dereset); -} - static void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, bool dereset) { @@ -597,7 +568,6 @@ struct dsaf_misc_op *hns_misc_op_get(struct dsaf_device *dsaf_dev) misc_op->dsaf_reset = hns_dsaf_rst; misc_op->xge_srst = hns_dsaf_xge_srst_by_port; - misc_op->xge_core_srst = hns_dsaf_xge_core_srst_by_port; misc_op->ge_srst = hns_dsaf_ge_srst_by_port; misc_op->ppe_srst = hns_ppe_srst_by_port; misc_op->ppe_comm_srst = hns_ppe_com_srst; @@ -615,7 +585,6 @@ struct dsaf_misc_op *hns_misc_op_get(struct dsaf_device *dsaf_dev) misc_op->dsaf_reset = hns_dsaf_rst_acpi; misc_op->xge_srst = hns_dsaf_xge_srst_by_port_acpi; - misc_op->xge_core_srst = hns_dsaf_xge_core_srst_by_port_acpi; misc_op->ge_srst = hns_dsaf_ge_srst_by_port_acpi; misc_op->ppe_srst = hns_ppe_srst_by_port_acpi; misc_op->ppe_comm_srst = hns_ppe_com_srst; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index f5505e8..8722668 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -702,8 +702,6 @@ #define XGMAC_RX_SYMBOLERRPKTS 0x0210 #define XGMAC_RX_FCSERRPKTS 0x0218 -#define XGMAC_TRX_CORE_SRST_M 0x2080 - #define DSAF_SRAM_INIT_OVER_M 0xff #define DSAFV2_SRAM_INIT_OVER_M 0x3ff #define DSAF_SRAM_INIT_OVER_S 0 @@ -982,6 +980,11 @@ #define XGMAC_ENABLE_TX_B 0 #define XGMAC_ENABLE_RX_B 1 +#define XGMAC_UNIDIR_EN_B 0 +#define XGMAC_RF_TX_EN_B 1 +#define XGMAC_LF_RF_INSERT_S 2 +#define XGMAC_LF_RF_INSERT_M (0x3 << XGMAC_LF_RF_INSERT_S) + #define XGMAC_CTL_TX_FCS_B 0 #define XGMAC_CTL_TX_PAD_B 1 #define XGMAC_CTL_TX_PREAMBLE_TRANS_B 3 diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c index 8f4f0e8..aae830a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c @@ -108,6 +108,31 @@ static void hns_xgmac_rx_enable(struct mac_driver *drv, u32 value) } /** + * hns_xgmac_tx_lf_rf_insert - insert lf rf control about xgmac + * @mac_drv: mac driver + * @mode: inserf rf or lf + */ +static void hns_xgmac_lf_rf_insert(struct mac_driver *mac_drv, u32 mode) +{ + dsaf_set_dev_field(mac_drv, XGMAC_MAC_TX_LF_RF_CONTROL_REG, + XGMAC_LF_RF_INSERT_M, XGMAC_LF_RF_INSERT_S, mode); +} + +/** + * hns_xgmac__lf_rf_control_init - initial the lf rf control register + * @mac_drv: mac driver + */ +static void hns_xgmac_lf_rf_control_init(struct mac_driver *mac_drv) +{ + u32 val = 0; + + dsaf_set_bit(val, XGMAC_UNIDIR_EN_B, 0); + dsaf_set_bit(val, XGMAC_RF_TX_EN_B, 1); + dsaf_set_field(val, XGMAC_LF_RF_INSERT_M, XGMAC_LF_RF_INSERT_S, 0); + dsaf_write_reg(mac_drv, XGMAC_MAC_TX_LF_RF_CONTROL_REG, val); +} + +/** *hns_xgmac_enable - enable xgmac port *@drv: mac driver *@mode: mode of mac port @@ -115,12 +140,8 @@ static void hns_xgmac_rx_enable(struct mac_driver *drv, u32 value) static void hns_xgmac_enable(void *mac_drv, enum mac_commom_mode mode) { struct mac_driver *drv = (struct mac_driver *)mac_drv; - struct dsaf_device *dsaf_dev - = (struct dsaf_device *)dev_get_drvdata(drv->dev); - u32 port = drv->mac_id; - dsaf_dev->misc_op->xge_core_srst(dsaf_dev, port, 1); - mdelay(10); + hns_xgmac_lf_rf_insert(drv, HNS_XGMAC_NO_LF_RF_INSERT); /*enable XGE rX/tX */ if (mode == MAC_COMM_MODE_TX) { @@ -143,9 +164,6 @@ static void hns_xgmac_enable(void *mac_drv, enum mac_commom_mode mode) static void hns_xgmac_disable(void *mac_drv, enum mac_commom_mode mode) { struct mac_driver *drv = (struct mac_driver *)mac_drv; - struct dsaf_device *dsaf_dev - = (struct dsaf_device *)dev_get_drvdata(drv->dev); - u32 port = drv->mac_id; if (mode == MAC_COMM_MODE_TX) { hns_xgmac_tx_enable(drv, 0); @@ -155,9 +173,7 @@ static void hns_xgmac_disable(void *mac_drv, enum mac_commom_mode mode) hns_xgmac_tx_enable(drv, 0); hns_xgmac_rx_enable(drv, 0); } - - mdelay(10); - dsaf_dev->misc_op->xge_core_srst(dsaf_dev, port, 0); + hns_xgmac_lf_rf_insert(drv, HNS_XGMAC_LF_INSERT); } /** @@ -203,6 +219,7 @@ static void hns_xgmac_init(void *mac_drv) dsaf_dev->misc_op->xge_srst(dsaf_dev, port, 1); mdelay(100); + hns_xgmac_lf_rf_control_init(drv); hns_xgmac_exc_irq_en(drv, 0); hns_xgmac_pma_fec_enable(drv, 0x0, 0x0); @@ -788,7 +805,7 @@ static int hns_xgmac_get_sset_count(int stringset) */ static int hns_xgmac_get_regs_count(void) { - return ETH_XGMAC_DUMP_NUM; + return HNS_XGMAC_DUMP_NUM; } void *hns_xgmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h index 139f729..da6c534 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h @@ -10,6 +10,7 @@ #ifndef _HNS_XGMAC_H #define _HNS_XGMAC_H -#define ETH_XGMAC_DUMP_NUM (214) - +#define HNS_XGMAC_DUMP_NUM 214 +#define HNS_XGMAC_NO_LF_RF_INSERT 0x0 +#define HNS_XGMAC_LF_INSERT 0x2 #endif -- cgit v1.1 From d30721d459fb73a797f004b0d05e8eab2e7f1944 Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:53 +0000 Subject: net: hns: modify buffer format of cpu data to le64 Hardware ring buffer data is stored in Little-endian. Thus cpu data should be modified to Little-endian. Signed-off-by: Qianqian Xie Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index e093cbf..b0f0557 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -590,7 +590,7 @@ static inline int hnae_alloc_buffer_attach(struct hnae_ring *ring, int i) if (ret) return ret; - ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma); return 0; } @@ -621,14 +621,14 @@ static inline void hnae_replace_buffer(struct hnae_ring *ring, int i, bops->unmap_buffer(ring, &ring->desc_cb[i]); ring->desc_cb[i] = *res_cb; - ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma); ring->desc[i].rx.ipoff_bnum_pid_flag = 0; } static inline void hnae_reuse_buffer(struct hnae_ring *ring, int i) { ring->desc_cb[i].reuse_flag = 0; - ring->desc[i].addr = (__le64)(ring->desc_cb[i].dma + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma + ring->desc_cb[i].page_offset); ring->desc[i].rx.ipoff_bnum_pid_flag = 0; } -- cgit v1.1 From 5483bfcb169cf3e68196bb6f28a43107fb48022c Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:54 +0000 Subject: net: hns: modify tcam table and set mac key The current definition of dsaf_drv_tbl_tcam_key is only suitable for Little-endian. If data is stored in Big-endian, this may lead to error in data use. Shift operation can make it work normally in both Big-endian and Little-endian. Signed-off-by: Qianqian Xie Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 16 ++++++++++++---- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 13 ++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 2721458..db23eef 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -1509,8 +1509,12 @@ static void hns_dsaf_set_mac_key( mac_key->high.bits.mac_3 = addr[3]; mac_key->low.bits.mac_4 = addr[4]; mac_key->low.bits.mac_5 = addr[5]; - mac_key->low.bits.vlan = vlan_id; - mac_key->low.bits.port = port; + dsaf_set_field(mac_key->low.bits.port_vlan, DSAF_TBL_TCAM_KEY_VLAN_M, + DSAF_TBL_TCAM_KEY_VLAN_S, vlan_id); + dsaf_set_field(mac_key->low.bits.port_vlan, DSAF_TBL_TCAM_KEY_PORT_M, + DSAF_TBL_TCAM_KEY_PORT_S, port); + + mac_key->low.bits.port_vlan = le16_to_cpu(mac_key->low.bits.port_vlan); } /** @@ -2760,8 +2764,12 @@ void hns_dsaf_set_promisc_tcam(struct dsaf_device *dsaf_dev, if (enable) { memset(&tbl_tcam_data, 0, sizeof(tbl_tcam_data)); memset(&tbl_tcam_mask, 0, sizeof(tbl_tcam_mask)); - tbl_tcam_data.low.bits.port = port; - tbl_tcam_mask.low.bits.port = 0xf; /* [3:0]: port id */ + dsaf_set_field(tbl_tcam_data.low.bits.port_vlan, + DSAF_TBL_TCAM_KEY_PORT_M, + DSAF_TBL_TCAM_KEY_PORT_S, port); + dsaf_set_field(tbl_tcam_mask.low.bits.port_vlan, + DSAF_TBL_TCAM_KEY_PORT_M, + DSAF_TBL_TCAM_KEY_PORT_S, 0xf); /* SUB_QID */ dsaf_set_bit(mac_data.tbl_mcast_port_msk[0], diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index 7f70f34..c68555b 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -357,6 +357,11 @@ static inline void *hns_dsaf_dev_priv(const struct dsaf_device *dsaf_dev) return (void *)((u8 *)dsaf_dev + sizeof(*dsaf_dev)); } +#define DSAF_TBL_TCAM_KEY_PORT_S 0 +#define DSAF_TBL_TCAM_KEY_PORT_M (((1ULL << 4) - 1) << 0) +#define DSAF_TBL_TCAM_KEY_VLAN_S 4 +#define DSAF_TBL_TCAM_KEY_VLAN_M (((1ULL << 12) - 1) << 4) + struct dsaf_drv_tbl_tcam_key { union { struct { @@ -370,11 +375,9 @@ struct dsaf_drv_tbl_tcam_key { } high; union { struct { - u32 port:4; /* port id, */ - /* dsaf-mode fixed 0, non-dsaf-mode port id*/ - u32 vlan:12; /* vlan id */ - u32 mac_5:8; - u32 mac_4:8; + u16 port_vlan; + u8 mac_5; + u8 mac_4; } bits; u32 val; -- cgit v1.1 From c9c0b37072f5d75ec498539c90ab7113e02c26d6 Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:55 +0000 Subject: net: hns: modify tcam table of mac uc-entry The current definition of mac_uc_entry is only suitable for Little-endian. Thus it needs to modify tcam table of mac uc-entry to support both Little-endian and Big-endian. Signed-off-by: Qianqian Xie Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index db23eef..1713f8d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -1532,6 +1532,7 @@ int hns_dsaf_set_mac_uc_entry( struct dsaf_drv_priv *priv = (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_tbl_tcam_data tcam_data; /* mac addr check */ if (MAC_IS_ALL_ZEROS(mac_entry->addr) || @@ -1573,9 +1574,10 @@ int hns_dsaf_set_mac_uc_entry( /* default config dvc to 0 */ mac_data.tbl_ucast_dvc = 0; mac_data.tbl_ucast_out_port = mac_entry->port_num; - hns_dsaf_tcam_uc_cfg( - dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + tcam_data.tbl_tcam_data_high = cpu_to_le32(mac_key.high.val); + tcam_data.tbl_tcam_data_low = cpu_to_le32(mac_key.low.val); + + hns_dsaf_tcam_uc_cfg(dsaf_dev, entry_index, &tcam_data, &mac_data); /* config software entry */ soft_mac_entry += entry_index; @@ -1950,6 +1952,7 @@ int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, struct dsaf_drv_tbl_tcam_key mac_key; struct dsaf_tbl_tcam_ucast_cfg mac_data; + struct dsaf_tbl_tcam_data tcam_data; /* check macaddr */ if (MAC_IS_ALL_ZEROS(mac_entry->addr) || @@ -1978,9 +1981,12 @@ int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, dsaf_dev->ae_dev.name, mac_key.high.val, mac_key.low.val, entry_index); - /*read entry*/ - hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + /* read entry */ + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); + + mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); + mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); + mac_entry->port_num = mac_data.tbl_ucast_out_port; return 0; -- cgit v1.1 From 39a6c9ebcbc6b6c3f3aefbe5449eb45d78f2034f Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:56 +0000 Subject: net: hns: modify table index to get mac entry Big-endian is not supported by the current definition of table index to get mac entry. It needs to be modified to support both Little-endian and Big-endian. Signed-off-by: Qianqian Xie Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 1713f8d..bd6e5b0 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -2054,6 +2054,7 @@ int hns_dsaf_get_mac_entry_by_index( struct dsaf_tbl_tcam_mcast_cfg mac_data; struct dsaf_tbl_tcam_ucast_cfg mac_uc_data; + struct dsaf_tbl_tcam_data tcam_data; char mac_addr[ETH_ALEN] = {0}; if (entry_index >= dsaf_dev->tcam_max_num) { @@ -2064,8 +2065,10 @@ int hns_dsaf_get_mac_entry_by_index( } /* mc entry, do read opt */ - hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); + + mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); + mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; @@ -2082,9 +2085,12 @@ int hns_dsaf_get_mac_entry_by_index( /**mc donot do*/ } else { /*is not mc, just uc... */ - hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)&mac_key, + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, &tcam_data, &mac_uc_data); + + mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); + mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); + mac_entry->port_mask[0] = (1 << mac_uc_data.tbl_ucast_out_port); } -- cgit v1.1 From 928971b6bc95ae13d2d77c2b7b265c379bf0189d Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:57 +0000 Subject: net: hns: modify tcam table of mac mc-port Little-endian is only supported by current tcam table to add or delete mac mc-port. This patch makes it support both Little-endian and Big-endian. Signed-off-by: Qianqian Xie Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index bd6e5b0..27eb85c 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -1688,6 +1688,7 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, struct dsaf_tbl_tcam_mcast_cfg mac_data; struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev); struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; struct dsaf_tbl_tcam_data tcam_data; u8 mc_addr[ETH_ALEN]; u8 *mc_mask; @@ -1738,6 +1739,10 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, /* if exist, add in */ hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); + + tmp_mac_key.high.val = + le32_to_cpu(tcam_data.tbl_tcam_data_high); + tmp_mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); } /* config hardware entry */ @@ -1762,8 +1767,8 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, dsaf_dev->ae_dev.name, mac_key.high.val, mac_key.low.val, entry_index); - tcam_data.tbl_tcam_data_high = mac_key.high.val; - tcam_data.tbl_tcam_data_low = mac_key.low.val; + tcam_data.tbl_tcam_data_high = cpu_to_le32(mac_key.high.val); + tcam_data.tbl_tcam_data_low = cpu_to_le32(mac_key.low.val); /* config mc entry with mask */ hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index, &tcam_data, @@ -1847,7 +1852,7 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, struct dsaf_tbl_tcam_data tcam_data; int mskid; const u8 empty_msk[sizeof(mac_data.tbl_mcast_port_msk)] = {0}; - struct dsaf_drv_tbl_tcam_key mask_key; + struct dsaf_drv_tbl_tcam_key mask_key, tmp_mac_key; struct dsaf_tbl_tcam_data *pmask_key = NULL; u8 mc_addr[ETH_ALEN]; u8 *mc_mask; @@ -1905,6 +1910,9 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, /* read entry */ hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); + tmp_mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); + tmp_mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); + /*del the port*/ if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { mskid = mac_entry->port_num; @@ -1929,8 +1937,8 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, soft_mac_entry += entry_index; soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; } else { /* not zero, just del port, update */ - tcam_data.tbl_tcam_data_high = mac_key.high.val; - tcam_data.tbl_tcam_data_low = mac_key.low.val; + tcam_data.tbl_tcam_data_high = cpu_to_le32(mac_key.high.val); + tcam_data.tbl_tcam_data_low = cpu_to_le32(mac_key.low.val); hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index, &tcam_data, -- cgit v1.1 From 9d189b853cbca36453676d9152239715965c2a86 Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:58 +0000 Subject: net: hns: modify tcam table of mac mc-entry The current definition of mac_mc_entry is only suitable for Little-endian. Thus it needs to modify tcam table of mac mc-entry to support both Little-endian and Big-endian. Signed-off-by: Qianqian Xie Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 27eb85c..74ca53d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -1604,6 +1604,7 @@ int hns_dsaf_set_mac_mc_entry( (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; struct dsaf_drv_tbl_tcam_key tmp_mac_key; + struct dsaf_tbl_tcam_data tcam_data; /* mac addr check */ if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { @@ -1636,9 +1637,12 @@ int hns_dsaf_set_mac_mc_entry( 0, sizeof(mac_data.tbl_mcast_port_msk)); } else { /* config hardware entry */ - hns_dsaf_tcam_mc_get( - dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, + &mac_data); + + tmp_mac_key.high.val = + le32_to_cpu(tcam_data.tbl_tcam_data_high); + tmp_mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); } mac_data.tbl_mcast_old_en = 0; mac_data.tbl_mcast_item_vld = 1; @@ -1650,9 +1654,11 @@ int hns_dsaf_set_mac_mc_entry( dsaf_dev->ae_dev.name, mac_key.high.val, mac_key.low.val, entry_index); - hns_dsaf_tcam_mc_cfg( - dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)(&mac_key), NULL, &mac_data); + tcam_data.tbl_tcam_data_high = cpu_to_le32(mac_key.high.val); + tcam_data.tbl_tcam_data_low = cpu_to_le32(mac_key.low.val); + + hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index, &tcam_data, NULL, + &mac_data); /* config software entry */ soft_mac_entry += entry_index; @@ -2012,6 +2018,7 @@ int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, struct dsaf_drv_tbl_tcam_key mac_key; struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_tbl_tcam_data tcam_data; /*check mac addr */ if (MAC_IS_ALL_ZEROS(mac_entry->addr) || @@ -2041,8 +2048,10 @@ int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, mac_key.low.val, entry_index); /*read entry */ - hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, - (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); + + mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); + mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; return 0; -- cgit v1.1 From 590457f4ec3d8a7963be95f3fc4ae916ccd67533 Mon Sep 17 00:00:00 2001 From: Qianqian Xie Date: Wed, 9 Nov 2016 18:13:59 +0000 Subject: net: hns: modify tcam table of mask_key The packets of wrong mac address(only the last bit is different) can be received in Big-endian by current definition of mask_key. Thus it needs to be modified to support Big-endian and ensure Big-endian normal. Signed-off-by: Qianqian Xie Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 74ca53d..250e4a1 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -1718,6 +1718,10 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, 0x0, 0xff, mc_mask); + + mask_key.high.val = le32_to_cpu(mask_key.high.val); + mask_key.low.val = le32_to_cpu(mask_key.low.val); + pmask_key = (struct dsaf_tbl_tcam_data *)(&mask_key); } @@ -1887,6 +1891,9 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, /* config key mask */ hns_dsaf_set_mac_key(dsaf_dev, &mask_key, 0x00, 0xff, mc_addr); + mask_key.high.val = le32_to_cpu(mask_key.high.val); + mask_key.low.val = le32_to_cpu(mask_key.low.val); + pmask_key = (struct dsaf_tbl_tcam_data *)(&mask_key); } -- cgit v1.1 From ec2cafe68267d88a6290a734d780d680fee50b4f Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Wed, 9 Nov 2016 18:14:00 +0000 Subject: net: hns: add multicast tcam table clear There is no clear operation before add a new multicast tcam table, so the tcam table will be overflow when add more entries. Reported-by: Daode Huang Signed-off-by: Kejian Yan Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 3 + drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 11 +++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 12 ++++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 1 + drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 79 ++++++++++++++++++++++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 3 + drivers/net/ethernet/hisilicon/hns/hns_enet.c | 4 ++ 7 files changed, 113 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index b0f0557..55c0334 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -426,6 +426,8 @@ enum hnae_media_type { * get mac address * set_mac_addr() * set mac address + * clr_mc_addr() + * clear mcast tcam table * set_mc_addr() * set multicast mode * set_mtu() @@ -488,6 +490,7 @@ struct hnae_ae_ops { void (*set_promisc_mode)(struct hnae_handle *handle, u32 en); int (*get_mac_addr)(struct hnae_handle *handle, void **p); int (*set_mac_addr)(struct hnae_handle *handle, void *p); + int (*clr_mc_addr)(struct hnae_handle *handle); int (*set_mc_addr)(struct hnae_handle *handle, void *addr); int (*set_mtu)(struct hnae_handle *handle, int new_mtu); void (*set_tso_stats)(struct hnae_handle *handle, int enable); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 193248f..366b25b 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -232,6 +232,16 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr) return ret; } +static int hns_ae_clr_multicast(struct hnae_handle *handle) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return 0; + + return hns_mac_clr_multicast(mac_cb, handle->vf_id); +} + static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu) { struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); @@ -821,6 +831,7 @@ static struct hnae_ae_ops hns_dsaf_ops = { .set_promisc_mode = hns_ae_set_promisc_mode, .set_mac_addr = hns_ae_set_mac_address, .set_mc_addr = hns_ae_set_multicast_one, + .clr_mc_addr = hns_ae_clr_multicast, .set_mtu = hns_ae_set_mtu, .update_stats = hns_ae_update_stats, .set_tso_stats = hns_ae_set_tso_stats, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index fc90260..266417d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -330,6 +330,18 @@ int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac) return 0; } +int hns_mac_clr_multicast(struct hns_mac_cb *mac_cb, int vfn) +{ + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + u8 port_num; + int ret = hns_mac_get_inner_port_num(mac_cb, vfn, &port_num); + + if (ret) + return ret; + + return hns_dsaf_clr_mac_mc_port(dsaf_dev, mac_cb->mac_id, port_num); +} + static void hns_mac_param_get(struct mac_params *param, struct hns_mac_cb *mac_cb) { diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index ca7e175..d896452 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -461,5 +461,6 @@ int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, void hns_mac_set_promisc(struct hns_mac_cb *mac_cb, u8 en); int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb, u8 vmid, u8 *port_num); +int hns_mac_clr_multicast(struct hns_mac_cb *mac_cb, int vfn); #endif /* _HNS_DSAF_MAC_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 250e4a1..ebecbed 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -959,6 +959,16 @@ static void hns_dsaf_tcam_mc_invld(struct dsaf_device *dsaf_dev, u32 address) spin_unlock_bh(&dsaf_dev->tcam_lock); } +void hns_dsaf_tcam_addr_get(struct dsaf_drv_tbl_tcam_key *mac_key, u8 *addr) +{ + addr[0] = mac_key->high.bits.mac_0; + addr[1] = mac_key->high.bits.mac_1; + addr[2] = mac_key->high.bits.mac_2; + addr[3] = mac_key->high.bits.mac_3; + addr[4] = mac_key->low.bits.mac_4; + addr[5] = mac_key->low.bits.mac_5; +} + /** * hns_dsaf_tcam_uc_get - INT * @dsaf_id: dsa fabric id @@ -1961,6 +1971,75 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, return 0; } +int hns_dsaf_clr_mac_mc_port(struct dsaf_device *dsaf_dev, u8 mac_id, + u8 port_num) +{ + struct dsaf_drv_priv *priv = hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + int ret = 0, i; + + if (HNS_DSAF_IS_DEBUG(dsaf_dev)) + return 0; + + for (i = 0; i < DSAF_TCAM_SUM - DSAFV2_MAC_FUZZY_TCAM_NUM; i++) { + u8 addr[ETH_ALEN]; + u8 port; + + soft_mac_entry = priv->soft_mac_tbl + i; + + hns_dsaf_tcam_addr_get(&soft_mac_entry->tcam_key, addr); + port = dsaf_get_field( + soft_mac_entry->tcam_key.low.bits.port_vlan, + DSAF_TBL_TCAM_KEY_PORT_M, + DSAF_TBL_TCAM_KEY_PORT_S); + /* check valid tcam mc entry */ + if (soft_mac_entry->index != DSAF_INVALID_ENTRY_IDX && + port == mac_id && + is_multicast_ether_addr(addr) && + !is_broadcast_ether_addr(addr)) { + const u32 empty_msk[DSAF_PORT_MSK_NUM] = {0}; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + /* disable receiving of this multicast address for + * the VF. + */ + ether_addr_copy(mac_entry.addr, addr); + mac_entry.in_vlan_id = dsaf_get_field( + soft_mac_entry->tcam_key.low.bits.port_vlan, + DSAF_TBL_TCAM_KEY_VLAN_M, + DSAF_TBL_TCAM_KEY_VLAN_S); + mac_entry.in_port_num = mac_id; + mac_entry.port_num = port_num; + if (hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry)) { + ret = -EINVAL; + continue; + } + + /* disable receiving of this multicast address for + * the mac port if all VF are disable + */ + hns_dsaf_tcam_mc_get(dsaf_dev, i, + (struct dsaf_tbl_tcam_data *) + (&soft_mac_entry->tcam_key), + &mac_data); + dsaf_set_bit(mac_data.tbl_mcast_port_msk[mac_id / 32], + mac_id % 32, 0); + if (!memcmp(mac_data.tbl_mcast_port_msk, empty_msk, + sizeof(u32) * DSAF_PORT_MSK_NUM)) { + mac_entry.port_num = mac_id; + if (hns_dsaf_del_mac_mc_port(dsaf_dev, + &mac_entry)) { + ret = -EINVAL; + continue; + } + } + } + } + + return ret; +} + /** * hns_dsaf_get_mac_uc_entry - get mac uc entry * @dsaf_dev: dsa fabric device struct pointer diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index c68555b..901037c 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -468,5 +468,8 @@ void hns_dsaf_get_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id, u32 *en); int hns_dsaf_set_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id, u32 en); +int hns_dsaf_clr_mac_mc_port(struct dsaf_device *dsaf_dev, + u8 mac_id, u8 port_num); + #endif /* __HNS_DSAF_MAIN_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 60831a2..d22a39e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1511,6 +1511,10 @@ void hns_set_multicast_list(struct net_device *ndev) return; } + if (h->dev->ops->clr_mc_addr) + if (h->dev->ops->clr_mc_addr(h)) + netdev_err(ndev, "clear multicast address fail\n"); + if (h->dev->ops->set_mc_addr) { netdev_for_each_mc_addr(ha, ndev) if (h->dev->ops->set_mc_addr(h, ha->addr)) -- cgit v1.1 From 66355f52ca95926af0389121f1d3f51d991f0e02 Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Wed, 9 Nov 2016 18:14:01 +0000 Subject: net: hns: add the support to add/remove the ucast entry to/from table This patch adds the support to add or remove the unicast entries to the table and remove from the table. Reported-by: Daode Huang Signed-off-by: Kejian Yan Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 8 ++++ drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 24 +++++++++++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 40 ++++++++++++++++++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 4 ++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 49 ++++++++++++++++++++++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 4 ++ drivers/net/ethernet/hisilicon/hns/hns_enet.c | 26 ++++++++++++ 7 files changed, 155 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 55c0334..09602f1 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -430,6 +430,10 @@ enum hnae_media_type { * clear mcast tcam table * set_mc_addr() * set multicast mode + * add_uc_addr() + * add ucast address + * rm_uc_addr() + * remove ucast address * set_mtu() * set mtu * update_stats() @@ -490,6 +494,10 @@ struct hnae_ae_ops { void (*set_promisc_mode)(struct hnae_handle *handle, u32 en); int (*get_mac_addr)(struct hnae_handle *handle, void **p); int (*set_mac_addr)(struct hnae_handle *handle, void *p); + int (*add_uc_addr)(struct hnae_handle *handle, + const unsigned char *addr); + int (*rm_uc_addr)(struct hnae_handle *handle, + const unsigned char *addr); int (*clr_mc_addr)(struct hnae_handle *handle); int (*set_mc_addr)(struct hnae_handle *handle, void *addr); int (*set_mtu)(struct hnae_handle *handle, int new_mtu); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 366b25b..0a9cdf0 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -199,6 +199,28 @@ static int hns_ae_set_mac_address(struct hnae_handle *handle, void *p) return 0; } +static int hns_ae_add_uc_address(struct hnae_handle *handle, + const unsigned char *addr) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return -ENOSPC; + + return hns_mac_add_uc_addr(mac_cb, handle->vf_id, addr); +} + +static int hns_ae_rm_uc_address(struct hnae_handle *handle, + const unsigned char *addr) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return -ENOSPC; + + return hns_mac_rm_uc_addr(mac_cb, handle->vf_id, addr); +} + static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr) { int ret; @@ -830,6 +852,8 @@ static struct hnae_ae_ops hns_dsaf_ops = { .get_coalesce_range = hns_ae_get_coalesce_range, .set_promisc_mode = hns_ae_set_promisc_mode, .set_mac_addr = hns_ae_set_mac_address, + .add_uc_addr = hns_ae_add_uc_address, + .rm_uc_addr = hns_ae_rm_uc_address, .set_mc_addr = hns_ae_set_multicast_one, .clr_mc_addr = hns_ae_clr_multicast, .set_mtu = hns_ae_set_mtu, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 266417d..3239d27 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -263,6 +263,46 @@ int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, return 0; } +int hns_mac_add_uc_addr(struct hns_mac_cb *mac_cb, u8 vf_id, + const unsigned char *addr) +{ + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + int ret; + + if (HNS_DSAF_IS_DEBUG(dsaf_dev)) + return -ENOSPC; + + memset(&mac_entry, 0, sizeof(mac_entry)); + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, vf_id, &mac_entry.port_num); + if (ret) + return ret; + + return hns_dsaf_set_mac_uc_entry(dsaf_dev, &mac_entry); +} + +int hns_mac_rm_uc_addr(struct hns_mac_cb *mac_cb, u8 vf_id, + const unsigned char *addr) +{ + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + int ret; + + if (HNS_DSAF_IS_DEBUG(dsaf_dev)) + return -ENOSPC; + + memset(&mac_entry, 0, sizeof(mac_entry)); + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, vf_id, &mac_entry.port_num); + if (ret) + return ret; + + return hns_dsaf_rm_mac_addr(dsaf_dev, &mac_entry); +} + int hns_mac_set_multi(struct hns_mac_cb *mac_cb, u32 port_num, char *addr, bool enable) { diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index d896452..2bb3d1e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -461,6 +461,10 @@ int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, void hns_mac_set_promisc(struct hns_mac_cb *mac_cb, u8 en); int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb, u8 vmid, u8 *port_num); +int hns_mac_add_uc_addr(struct hns_mac_cb *mac_cb, u8 vf_id, + const unsigned char *addr); +int hns_mac_rm_uc_addr(struct hns_mac_cb *mac_cb, u8 vf_id, + const unsigned char *addr); int hns_mac_clr_multicast(struct hns_mac_cb *mac_cb, int vfn); #endif /* _HNS_DSAF_MAC_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index ebecbed..90dbda7 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -1598,6 +1598,55 @@ int hns_dsaf_set_mac_uc_entry( return 0; } +int hns_dsaf_rm_mac_addr( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_tbl_tcam_ucast_cfg mac_data; + struct dsaf_drv_tbl_tcam_key mac_key; + + /* mac addr check */ + if (!is_valid_ether_addr(mac_entry->addr)) { + dev_err(dsaf_dev->dev, "rm_uc_addr %s Mac %pM err!\n", + dsaf_dev->ae_dev.name, mac_entry->addr); + return -EINVAL; + } + + /* config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /* can not find the tcam entry, return 0 */ + dev_info(dsaf_dev->dev, + "rm_uc_addr no tcam, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return 0; + } + + dev_dbg(dsaf_dev->dev, + "rm_uc_addr, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + hns_dsaf_tcam_uc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, + &mac_data); + + /* unicast entry not used locally should not clear */ + if (mac_entry->port_num != mac_data.tbl_ucast_out_port) + return -EFAULT; + + return hns_dsaf_del_mac_entry(dsaf_dev, + mac_entry->in_vlan_id, + mac_entry->in_port_num, + mac_entry->addr); +} + /** * hns_dsaf_set_mac_mc_entry - set mac mc-entry * @dsaf_dev: dsa fabric device struct pointer diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index 901037c..cef6bf4 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -468,6 +468,10 @@ void hns_dsaf_get_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id, u32 *en); int hns_dsaf_set_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id, u32 en); +int hns_dsaf_rm_mac_addr( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); + int hns_dsaf_clr_mac_mc_port(struct dsaf_device *dsaf_dev, u8 mac_id, u8 port_num); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index d22a39e..776d81e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1493,6 +1493,29 @@ static netdev_features_t hns_nic_fix_features( return features; } +static int hns_nic_uc_sync(struct net_device *netdev, const unsigned char *addr) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + if (h->dev->ops->add_uc_addr) + return h->dev->ops->add_uc_addr(h, addr); + + return 0; +} + +static int hns_nic_uc_unsync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + if (h->dev->ops->rm_uc_addr) + return h->dev->ops->rm_uc_addr(h, addr); + + return 0; +} + /** * nic_set_multicast_list - set mutl mac address * @netdev: net device @@ -1535,6 +1558,9 @@ void hns_nic_set_rx_mode(struct net_device *ndev) } hns_set_multicast_list(ndev); + + if (__dev_uc_sync(ndev, hns_nic_uc_sync, hns_nic_uc_unsync)) + netdev_err(ndev, "sync uc address fail\n"); } struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev, -- cgit v1.1 From b602e491a518439abd98a25ccded85e1347b9a14 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Tue, 8 Nov 2016 15:16:05 +0200 Subject: net: ethernet: ti: davinci_cpdma: free memory while channel destroy While create/destroy channel operation memory is not freed. It was supposed that memory is freed while driver remove. But a channel can be created and destroyed many times while changing number of channels with ethtool. Based on net-next/master Signed-off-by: Ivan Khoronzhuk Reviewed-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index c3f35f1..8f7b420 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -533,7 +533,7 @@ int cpdma_chan_destroy(struct cpdma_chan *chan) cpdma_chan_stop(chan); ctlr->channels[chan->chan_num] = NULL; ctlr->chan_num--; - + devm_kfree(ctlr->dev, chan); cpdma_chan_split_pool(ctlr); spin_unlock_irqrestore(&ctlr->lock, flags); -- cgit v1.1 From c540594f864bb4645573c2c0a304919fabb3d7ea Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 9 Nov 2016 22:02:34 +0100 Subject: bpf, mlx4: fix prog refcount in mlx4_en_try_alloc_resources error path Commit 67f8b1dcb9ee ("net/mlx4_en: Refactor the XDP forwarding rings scheme") added a bug in that the prog's reference count is not dropped in the error path when mlx4_en_try_alloc_resources() is failing from mlx4_xdp_set(). We previously took bpf_prog_add(prog, priv->rx_ring_num - 1), that we need to release again. Earlier in the call path, dev_change_xdp_fd() itself holds a reference to the prog as well (hence the '- 1' in the bpf_prog_add()), so a simple atomic_sub() is safe to use here. When an error is propagated, then bpf_prog_put() is called eventually from dev_change_xdp_fd() Fixes: 67f8b1dcb9ee ("net/mlx4_en: Refactor the XDP forwarding rings scheme") Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 5 ++++- include/linux/bpf.h | 5 +++++ kernel/bpf/syscall.c | 11 +++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 0f6225c..9bf7320 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2747,8 +2747,11 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog) } err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof); - if (err) + if (err) { + if (prog) + bpf_prog_sub(prog, priv->rx_ring_num - 1); goto unlock_out; + } if (priv->port_up) { port_up = 1; diff --git a/include/linux/bpf.h b/include/linux/bpf.h index edcd96d..01c1487 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -234,6 +234,7 @@ void bpf_register_map_type(struct bpf_map_type_list *tl); struct bpf_prog *bpf_prog_get(u32 ufd); struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type); struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i); +void bpf_prog_sub(struct bpf_prog *prog, int i); struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog); void bpf_prog_put(struct bpf_prog *prog); @@ -303,6 +304,10 @@ static inline struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i) return ERR_PTR(-EOPNOTSUPP); } +static inline void bpf_prog_sub(struct bpf_prog *prog, int i) +{ +} + static inline void bpf_prog_put(struct bpf_prog *prog) { } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 228f962..23eb205 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -680,6 +680,17 @@ struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i) } EXPORT_SYMBOL_GPL(bpf_prog_add); +void bpf_prog_sub(struct bpf_prog *prog, int i) +{ + /* Only to be used for undoing previous bpf_prog_add() in some + * error path. We still know that another entity in our call + * path holds a reference to the program, thus atomic_sub() can + * be safely used in such cases! + */ + WARN_ON(atomic_sub_return(i, &prog->aux->refcnt) == 0); +} +EXPORT_SYMBOL_GPL(bpf_prog_sub); + struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog) { return bpf_prog_add(prog, 1); -- cgit v1.1 From 738314a084aae5f76ff760279034b39d52c42e8b Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:17 +0100 Subject: openvswitch: use hard_header_len instead of hardcoded ETH_HLEN On tx, use hard_header_len while deciding whether to refragment or drop the packet. That way, all combinations are calculated correctly: * L2 packet going to L2 interface (the L2 header len is subtracted), * L2 packet going to L3 interface (the L2 header is included in the packet lenght), * L3 packet going to L3 interface. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 3 ++- net/openvswitch/vport.c | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 1105c4e..49af167 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -791,7 +791,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, pskb_trim(skb, ETH_HLEN); } - if (likely(!mru || (skb->len <= mru + ETH_HLEN))) { + if (likely(!mru || + (skb->len <= mru + vport->dev->hard_header_len))) { ovs_vport_send(vport, skb); } else if (mru <= vport->dev->mtu) { struct net *net = read_pnet(&dp->net); diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 9bb85b3..3e131f6 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -464,9 +464,10 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, return 0; } -static unsigned int packet_length(const struct sk_buff *skb) +static unsigned int packet_length(const struct sk_buff *skb, + struct net_device *dev) { - unsigned int length = skb->len - ETH_HLEN; + unsigned int length = skb->len - dev->hard_header_len; if (!skb_vlan_tag_present(skb) && eth_type_vlan(skb->protocol)) @@ -484,10 +485,11 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb) { int mtu = vport->dev->mtu; - if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) { + if (unlikely(packet_length(skb, vport->dev) > mtu && + !skb_is_gso(skb))) { net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", vport->dev->name, - packet_length(skb), mtu); + packet_length(skb, vport->dev), mtu); vport->dev->stats.tx_errors++; goto drop; } -- cgit v1.1 From 329f45bc4f191c663dc156c510816411a4310578 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:18 +0100 Subject: openvswitch: add mac_proto field to the flow key Use a hole in the structure. We support only Ethernet so far and will add a support for L2-less packets shortly. We could use a bool to indicate whether the Ethernet header is present or not but the approach with the mac_proto field is more generic and occupies the same number of bytes in the struct, while allowing later extensibility. It also makes the code in the next patches more self explaining. It would be nice to use ARPHRD_ constants but those are u16 which would be waste. Thus define our own constants. Another upside of this is that we can overload this new field to also denote whether the flow key is valid. This has the advantage that on refragmentation, we don't have to reparse the packet but can rely on the stored eth.type. This is especially important for the next patches in this series - instead of adding another branch for L2-less packets before calling ovs_fragment, we can just remove all those branches completely. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 14 +++----------- net/openvswitch/flow.c | 1 + net/openvswitch/flow.h | 22 ++++++++++++++++++++++ net/openvswitch/flow_netlink.c | 5 +++++ 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 49af167..44144f9 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -137,12 +137,12 @@ static struct deferred_action *add_deferred_actions(struct sk_buff *skb, static void invalidate_flow_key(struct sw_flow_key *key) { - key->eth.type = htons(0); + key->mac_proto |= SW_FLOW_KEY_INVALID; } static bool is_flow_key_valid(const struct sw_flow_key *key) { - return !!key->eth.type; + return !(key->mac_proto & SW_FLOW_KEY_INVALID); } static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr, @@ -796,16 +796,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, ovs_vport_send(vport, skb); } else if (mru <= vport->dev->mtu) { struct net *net = read_pnet(&dp->net); - __be16 ethertype = key->eth.type; - if (!is_flow_key_valid(key)) { - if (eth_p_mpls(skb->protocol)) - ethertype = skb->inner_protocol; - else - ethertype = vlan_get_protocol(skb); - } - - ovs_fragment(net, vport, skb, mru, ethertype); + ovs_fragment(net, vport, skb, mru, key->eth.type); } else { kfree_skb(skb); } diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 2208706..96c8c47 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -751,6 +751,7 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, key->phy.skb_mark = skb->mark; ovs_ct_fill_key(skb, key); key->ovs_flow_hash = 0; + key->mac_proto = MAC_PROTO_ETHERNET; key->recirc_id = 0; return key_extract(skb, key); diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index ae783f5..f61cae7 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -37,6 +37,12 @@ struct sk_buff; +enum sw_flow_mac_proto { + MAC_PROTO_NONE = 0, + MAC_PROTO_ETHERNET, +}; +#define SW_FLOW_KEY_INVALID 0x80 + /* Store options at the end of the array if they are less than the * maximum size. This allows us to get the benefits of variable length * matching for small options. @@ -68,6 +74,7 @@ struct sw_flow_key { u32 skb_mark; /* SKB mark. */ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ } __packed phy; /* Safe when right after 'tun_key'. */ + u8 mac_proto; /* MAC layer protocol (e.g. Ethernet). */ u8 tun_proto; /* Protocol of encapsulating tunnel. */ u32 ovs_flow_hash; /* Datapath computed hash value. */ u32 recirc_id; /* Recirculation ID. */ @@ -206,6 +213,21 @@ struct arp_eth_header { unsigned char ar_tip[4]; /* target IP address */ } __packed; +static inline u8 ovs_key_mac_proto(const struct sw_flow_key *key) +{ + return key->mac_proto & ~SW_FLOW_KEY_INVALID; +} + +static inline u16 __ovs_mac_header_len(u8 mac_proto) +{ + return mac_proto == MAC_PROTO_ETHERNET ? ETH_HLEN : 0; +} + +static inline u16 ovs_mac_header_len(const struct sw_flow_key *key) +{ + return __ovs_mac_header_len(ovs_key_mac_proto(key)); +} + static inline bool ovs_identifier_is_ufid(const struct sw_flow_id *sfid) { return sfid->ufid_len; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index ae25ded..ccb9900 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -1059,6 +1059,11 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, sizeof(*cl), is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABELS); } + + /* Always exact match mac_proto */ + SW_FLOW_KEY_PUT(match, mac_proto, is_mask ? 0xff : MAC_PROTO_ETHERNET, + is_mask); + return 0; } -- cgit v1.1 From e2d9d8358cb961340ef88620b6a25ba4557033d5 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:19 +0100 Subject: openvswitch: pass mac_proto to ovs_vport_send We'll need it to alter packets sent to ARPHRD_NONE interfaces. Change do_output() to use the actual L2 header size of the packet when deciding on the minimum cutlen. The assumption here is that what matters is not the output interface hard_header_len but rather the L2 header of the particular packet. For example, ARPHRD_NONE tunnels that encapsulate Ethernet should get at least the Ethernet header. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 29 +++++++++++++++++------------ net/openvswitch/vport.c | 2 +- net/openvswitch/vport.h | 2 +- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 44144f9..dc8bb97 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -66,6 +66,7 @@ struct ovs_frag_data { u16 vlan_tci; __be16 vlan_proto; unsigned int l2_len; + u8 mac_proto; u8 l2_data[MAX_L2_LEN]; }; @@ -673,7 +674,7 @@ static int ovs_vport_output(struct net *net, struct sock *sk, struct sk_buff *sk skb_reset_mac_len(skb); } - ovs_vport_send(vport, skb); + ovs_vport_send(vport, skb, data->mac_proto); return 0; } @@ -692,7 +693,7 @@ static struct dst_ops ovs_dst_ops = { * ovs_vport_output(), which is called once per fragmented packet. */ static void prepare_frag(struct vport *vport, struct sk_buff *skb, - u16 orig_network_offset) + u16 orig_network_offset, u8 mac_proto) { unsigned int hlen = skb_network_offset(skb); struct ovs_frag_data *data; @@ -705,6 +706,7 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb, data->network_offset = orig_network_offset; data->vlan_tci = skb->vlan_tci; data->vlan_proto = skb->vlan_proto; + data->mac_proto = mac_proto; data->l2_len = hlen; memcpy(&data->l2_data, skb->data, hlen); @@ -713,7 +715,8 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb, } static void ovs_fragment(struct net *net, struct vport *vport, - struct sk_buff *skb, u16 mru, __be16 ethertype) + struct sk_buff *skb, u16 mru, + struct sw_flow_key *key) { u16 orig_network_offset = 0; @@ -727,11 +730,12 @@ static void ovs_fragment(struct net *net, struct vport *vport, goto err; } - if (ethertype == htons(ETH_P_IP)) { + if (key->eth.type == htons(ETH_P_IP)) { struct dst_entry ovs_dst; unsigned long orig_dst; - prepare_frag(vport, skb, orig_network_offset); + prepare_frag(vport, skb, orig_network_offset, + ovs_key_mac_proto(key)); dst_init(&ovs_dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); ovs_dst.dev = vport->dev; @@ -742,7 +746,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, ip_do_fragment(net, skb->sk, skb, ovs_vport_output); refdst_drop(orig_dst); - } else if (ethertype == htons(ETH_P_IPV6)) { + } else if (key->eth.type == htons(ETH_P_IPV6)) { const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops(); unsigned long orig_dst; struct rt6_info ovs_rt; @@ -751,7 +755,8 @@ static void ovs_fragment(struct net *net, struct vport *vport, goto err; } - prepare_frag(vport, skb, orig_network_offset); + prepare_frag(vport, skb, orig_network_offset, + ovs_key_mac_proto(key)); memset(&ovs_rt, 0, sizeof(ovs_rt)); dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); @@ -765,7 +770,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, refdst_drop(orig_dst); } else { WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.", - ovs_vport_name(vport), ntohs(ethertype), mru, + ovs_vport_name(vport), ntohs(key->eth.type), mru, vport->dev->mtu); goto err; } @@ -785,19 +790,19 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, u32 cutlen = OVS_CB(skb)->cutlen; if (unlikely(cutlen > 0)) { - if (skb->len - cutlen > ETH_HLEN) + if (skb->len - cutlen > ovs_mac_header_len(key)) pskb_trim(skb, skb->len - cutlen); else - pskb_trim(skb, ETH_HLEN); + pskb_trim(skb, ovs_mac_header_len(key)); } if (likely(!mru || (skb->len <= mru + vport->dev->hard_header_len))) { - ovs_vport_send(vport, skb); + ovs_vport_send(vport, skb, ovs_key_mac_proto(key)); } else if (mru <= vport->dev->mtu) { struct net *net = read_pnet(&dp->net); - ovs_fragment(net, vport, skb, mru, key->eth.type); + ovs_fragment(net, vport, skb, mru, key); } else { kfree_skb(skb); } diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 3e131f6..898ed37 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -481,7 +481,7 @@ static unsigned int packet_length(const struct sk_buff *skb, return length; } -void ovs_vport_send(struct vport *vport, struct sk_buff *skb) +void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto) { int mtu = vport->dev->mtu; diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 46e5b69..cda66c2 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -197,6 +197,6 @@ int __ovs_vport_ops_register(struct vport_ops *ops); }) void ovs_vport_ops_unregister(struct vport_ops *ops); -void ovs_vport_send(struct vport *vport, struct sk_buff *skb); +void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto); #endif /* vport.h */ -- cgit v1.1 From 1560a074df6297e76278e459ca3eb9ff83a6f878 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:20 +0100 Subject: openvswitch: support MPLS push and pop for L3 packets Update Ethernet header only if there is one. Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index dc8bb97..064cbcb 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -187,7 +187,8 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, skb_postpush_rcsum(skb, new_mpls_lse, MPLS_HLEN); - update_ethertype(skb, eth_hdr(skb), mpls->mpls_ethertype); + if (ovs_key_mac_proto(key) == MAC_PROTO_ETHERNET) + update_ethertype(skb, eth_hdr(skb), mpls->mpls_ethertype); skb->protocol = mpls->mpls_ethertype; invalidate_flow_key(key); @@ -197,7 +198,6 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key, const __be16 ethertype) { - struct ethhdr *hdr; int err; err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN); @@ -213,11 +213,15 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key, skb_reset_mac_header(skb); skb_set_network_header(skb, skb->mac_len); - /* mpls_hdr() is used to locate the ethertype field correctly in the - * presence of VLAN tags. - */ - hdr = (struct ethhdr *)((void *)mpls_hdr(skb) - ETH_HLEN); - update_ethertype(skb, hdr, ethertype); + if (ovs_key_mac_proto(key) == MAC_PROTO_ETHERNET) { + struct ethhdr *hdr; + + /* mpls_hdr() is used to locate the ethertype field correctly in the + * presence of VLAN tags. + */ + hdr = (struct ethhdr *)((void *)mpls_hdr(skb) - ETH_HLEN); + update_ethertype(skb, hdr, ethertype); + } if (eth_p_mpls(skb->protocol)) skb->protocol = ethertype; -- cgit v1.1 From 5108bbaddc37c1c8583f0cf2562d7d3463cd12cb Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:21 +0100 Subject: openvswitch: add processing of L3 packets Support receiving, extracting flow key and sending of L3 packets (packets without an Ethernet header). Note that even after this patch, non-Ethernet interfaces are still not allowed to be added to bridges. Similarly, netlink interface for sending and receiving L3 packets to/from user space is not in place yet. Based on previous versions by Lorand Jakab and Simon Horman. Signed-off-by: Lorand Jakab Signed-off-by: Simon Horman Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/datapath.c | 13 +----- net/openvswitch/flow.c | 106 ++++++++++++++++++++++++++++++++++----------- net/openvswitch/vport.c | 19 ++++++++ 3 files changed, 101 insertions(+), 37 deletions(-) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index fa87601..1402f1b 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -560,7 +560,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) struct sw_flow *flow; struct sw_flow_actions *sf_acts; struct datapath *dp; - struct ethhdr *eth; struct vport *input_vport; u16 mru = 0; int len; @@ -581,17 +580,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len); - skb_reset_mac_header(packet); - eth = eth_hdr(packet); - - /* Normally, setting the skb 'protocol' field would be handled by a - * call to eth_type_trans(), but it assumes there's a sending - * device, which we may not have. */ - if (eth_proto_is_802_3(eth->h_proto)) - packet->protocol = eth->h_proto; - else - packet->protocol = htons(ETH_P_802_2); - /* Set packet's mru */ if (a[OVS_PACKET_ATTR_MRU]) { mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]); @@ -618,6 +606,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) rcu_assign_pointer(flow->sf_acts, acts); packet->priority = flow->key.phy.priority; packet->mark = flow->key.phy.skb_mark; + packet->protocol = flow->key.eth.type; rcu_read_lock(); dp = get_dp_rcu(net, ovs_header->dp_ifindex); diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 96c8c47..08aa926 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -334,14 +334,17 @@ static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *key_vh) return 1; } -static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) +static void clear_vlan(struct sw_flow_key *key) { - int res; - key->eth.vlan.tci = 0; key->eth.vlan.tpid = 0; key->eth.cvlan.tci = 0; key->eth.cvlan.tpid = 0; +} + +static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) +{ + int res; if (skb_vlan_tag_present(skb)) { key->eth.vlan.tci = htons(skb->vlan_tci); @@ -483,17 +486,20 @@ invalid: * * Returns 0 if successful, otherwise a negative errno value. * - * Initializes @skb header pointers as follows: + * Initializes @skb header fields as follows: * - * - skb->mac_header: the Ethernet header. + * - skb->mac_header: the L2 header. * - * - skb->network_header: just past the Ethernet header, or just past the - * VLAN header, to the first byte of the Ethernet payload. + * - skb->network_header: just past the L2 header, or just past the + * VLAN header, to the first byte of the L2 payload. * * - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6 * on output, then just past the IP header, if one is present and * of a correct length, otherwise the same as skb->network_header. * For other key->eth.type values it is left untouched. + * + * - skb->protocol: the type of the data starting at skb->network_header. + * Equals to key->eth.type. */ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) { @@ -505,28 +511,35 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) skb_reset_mac_header(skb); - /* Link layer. We are guaranteed to have at least the 14 byte Ethernet - * header in the linear data area. - */ - eth = eth_hdr(skb); - ether_addr_copy(key->eth.src, eth->h_source); - ether_addr_copy(key->eth.dst, eth->h_dest); + /* Link layer. */ + clear_vlan(key); + if (key->mac_proto == MAC_PROTO_NONE) { + if (unlikely(eth_type_vlan(skb->protocol))) + return -EINVAL; - __skb_pull(skb, 2 * ETH_ALEN); - /* We are going to push all headers that we pull, so no need to - * update skb->csum here. - */ + skb_reset_network_header(skb); + } else { + eth = eth_hdr(skb); + ether_addr_copy(key->eth.src, eth->h_source); + ether_addr_copy(key->eth.dst, eth->h_dest); - if (unlikely(parse_vlan(skb, key))) - return -ENOMEM; + __skb_pull(skb, 2 * ETH_ALEN); + /* We are going to push all headers that we pull, so no need to + * update skb->csum here. + */ - key->eth.type = parse_ethertype(skb); - if (unlikely(key->eth.type == htons(0))) - return -ENOMEM; + if (unlikely(parse_vlan(skb, key))) + return -ENOMEM; + + skb->protocol = parse_ethertype(skb); + if (unlikely(skb->protocol == htons(0))) + return -ENOMEM; - skb_reset_network_header(skb); + skb_reset_network_header(skb); + __skb_push(skb, skb->data - skb_mac_header(skb)); + } skb_reset_mac_len(skb); - __skb_push(skb, skb->data - skb_mac_header(skb)); + key->eth.type = skb->protocol; /* Network layer. */ if (key->eth.type == htons(ETH_P_IP)) { @@ -721,9 +734,25 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key) return key_extract(skb, key); } +static int key_extract_mac_proto(struct sk_buff *skb) +{ + switch (skb->dev->type) { + case ARPHRD_ETHER: + return MAC_PROTO_ETHERNET; + case ARPHRD_NONE: + if (skb->protocol == htons(ETH_P_TEB)) + return MAC_PROTO_ETHERNET; + return MAC_PROTO_NONE; + } + WARN_ON_ONCE(1); + return -EINVAL; +} + int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, struct sk_buff *skb, struct sw_flow_key *key) { + int res; + /* Extract metadata from packet. */ if (tun_info) { key->tun_proto = ip_tunnel_info_af(tun_info); @@ -751,7 +780,10 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, key->phy.skb_mark = skb->mark; ovs_ct_fill_key(skb, key); key->ovs_flow_hash = 0; - key->mac_proto = MAC_PROTO_ETHERNET; + res = key_extract_mac_proto(skb); + if (res < 0) + return res; + key->mac_proto = res; key->recirc_id = 0; return key_extract(skb, key); @@ -768,5 +800,29 @@ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr, if (err) return err; + if (ovs_key_mac_proto(key) == MAC_PROTO_NONE) { + /* key_extract assumes that skb->protocol is set-up for + * layer 3 packets which is the case for other callers, + * in particular packets recieved from the network stack. + * Here the correct value can be set from the metadata + * extracted above. + */ + skb->protocol = key->eth.type; + } else { + struct ethhdr *eth; + + skb_reset_mac_header(skb); + eth = eth_hdr(skb); + + /* Normally, setting the skb 'protocol' field would be + * handled by a call to eth_type_trans(), but it assumes + * there's a sending device, which we may not have. + */ + if (eth_proto_is_802_3(eth->h_proto)) + skb->protocol = eth->h_proto; + else + skb->protocol = htons(ETH_P_802_2); + } + return key_extract(skb, key); } diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 898ed37..b6c8524 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -485,6 +485,25 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto) { int mtu = vport->dev->mtu; + switch (vport->dev->type) { + case ARPHRD_NONE: + if (mac_proto == MAC_PROTO_ETHERNET) { + skb_reset_network_header(skb); + skb_reset_mac_len(skb); + skb->protocol = htons(ETH_P_TEB); + } else if (mac_proto != MAC_PROTO_NONE) { + WARN_ON_ONCE(1); + goto drop; + } + break; + case ARPHRD_ETHER: + if (mac_proto != MAC_PROTO_ETHERNET) + goto drop; + break; + default: + goto drop; + } + if (unlikely(packet_length(skb, vport->dev) > mtu && !skb_is_gso(skb))) { net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", -- cgit v1.1 From 0a6410fbde597ebcf82dda4a0b0e889e82242678 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:22 +0100 Subject: openvswitch: netlink: support L3 packets Extend the ovs flow netlink protocol to support L3 packets. Packets without OVS_KEY_ATTR_ETHERNET attribute specify L3 packets; for those, the OVS_KEY_ATTR_ETHERTYPE attribute is mandatory. Push/pop vlan actions are only supported for Ethernet packets. Based on previous versions by Lorand Jakab and Simon Horman. Signed-off-by: Lorand Jakab Signed-off-by: Simon Horman Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/flow_netlink.c | 160 +++++++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 61 deletions(-) diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index ccb9900..c3d0cc43 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -123,7 +123,7 @@ static void update_range(struct sw_flow_match *match, static bool match_validate(const struct sw_flow_match *match, u64 key_attrs, u64 mask_attrs, bool log) { - u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET; + u64 key_expected = 0; u64 mask_allowed = key_attrs; /* At most allow all key attributes */ /* The following mask attributes allowed only if they @@ -969,10 +969,33 @@ static int parse_vlan_from_nlattrs(struct sw_flow_match *match, return 0; } +static int parse_eth_type_from_nlattrs(struct sw_flow_match *match, + u64 *attrs, const struct nlattr **a, + bool is_mask, bool log) +{ + __be16 eth_type; + + eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); + if (is_mask) { + /* Always exact match EtherType. */ + eth_type = htons(0xffff); + } else if (!eth_proto_is_802_3(eth_type)) { + OVS_NLERR(log, "EtherType %x is less than min %x", + ntohs(eth_type), ETH_P_802_3_MIN); + return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask); + *attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); + return 0; +} + static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, u64 *attrs, const struct nlattr **a, bool is_mask, bool log) { + u8 mac_proto = MAC_PROTO_ETHERNET; + if (*attrs & (1 << OVS_KEY_ATTR_DP_HASH)) { u32 hash_val = nla_get_u32(a[OVS_KEY_ATTR_DP_HASH]); @@ -1060,9 +1083,19 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABELS); } + /* For layer 3 packets the Ethernet type is provided + * and treated as metadata but no MAC addresses are provided. + */ + if (!(*attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) && + (*attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE))) + mac_proto = MAC_PROTO_NONE; + /* Always exact match mac_proto */ - SW_FLOW_KEY_PUT(match, mac_proto, is_mask ? 0xff : MAC_PROTO_ETHERNET, - is_mask); + SW_FLOW_KEY_PUT(match, mac_proto, is_mask ? 0xff : mac_proto, is_mask); + + if (mac_proto == MAC_PROTO_NONE) + return parse_eth_type_from_nlattrs(match, attrs, a, is_mask, + log); return 0; } @@ -1086,33 +1119,26 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match, SW_FLOW_KEY_MEMCPY(match, eth.dst, eth_key->eth_dst, ETH_ALEN, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET); - } - - if (attrs & (1 << OVS_KEY_ATTR_VLAN)) { - /* VLAN attribute is always parsed before getting here since it - * may occur multiple times. - */ - OVS_NLERR(log, "VLAN attribute unexpected."); - return -EINVAL; - } - - if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) { - __be16 eth_type; - eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); - if (is_mask) { - /* Always exact match EtherType. */ - eth_type = htons(0xffff); - } else if (!eth_proto_is_802_3(eth_type)) { - OVS_NLERR(log, "EtherType %x is less than min %x", - ntohs(eth_type), ETH_P_802_3_MIN); + if (attrs & (1 << OVS_KEY_ATTR_VLAN)) { + /* VLAN attribute is always parsed before getting here since it + * may occur multiple times. + */ + OVS_NLERR(log, "VLAN attribute unexpected."); return -EINVAL; } - SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); - } else if (!is_mask) { - SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); + if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) { + err = parse_eth_type_from_nlattrs(match, &attrs, a, is_mask, + log); + if (err) + return err; + } else if (!is_mask) { + SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); + } + } else if (!match->key->eth.type) { + OVS_NLERR(log, "Either Ethernet header or EtherType is required."); + return -EINVAL; } if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { @@ -1561,42 +1587,44 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey, if (ovs_ct_put_key(output, skb)) goto nla_put_failure; - nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); - if (!nla) - goto nla_put_failure; - - eth_key = nla_data(nla); - ether_addr_copy(eth_key->eth_src, output->eth.src); - ether_addr_copy(eth_key->eth_dst, output->eth.dst); - - if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) { - if (ovs_nla_put_vlan(skb, &output->eth.vlan, is_mask)) + if (ovs_key_mac_proto(swkey) == MAC_PROTO_ETHERNET) { + nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); + if (!nla) goto nla_put_failure; - encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); - if (!swkey->eth.vlan.tci) - goto unencap; - if (swkey->eth.cvlan.tci || eth_type_vlan(swkey->eth.type)) { - if (ovs_nla_put_vlan(skb, &output->eth.cvlan, is_mask)) + eth_key = nla_data(nla); + ether_addr_copy(eth_key->eth_src, output->eth.src); + ether_addr_copy(eth_key->eth_dst, output->eth.dst); + + if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) { + if (ovs_nla_put_vlan(skb, &output->eth.vlan, is_mask)) goto nla_put_failure; - in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); - if (!swkey->eth.cvlan.tci) + encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); + if (!swkey->eth.vlan.tci) goto unencap; + + if (swkey->eth.cvlan.tci || eth_type_vlan(swkey->eth.type)) { + if (ovs_nla_put_vlan(skb, &output->eth.cvlan, is_mask)) + goto nla_put_failure; + in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); + if (!swkey->eth.cvlan.tci) + goto unencap; + } } - } - if (swkey->eth.type == htons(ETH_P_802_2)) { - /* - * Ethertype 802.2 is represented in the netlink with omitted - * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and - * 0xffff in the mask attribute. Ethertype can also - * be wildcarded. - */ - if (is_mask && output->eth.type) - if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, - output->eth.type)) - goto nla_put_failure; - goto unencap; + if (swkey->eth.type == htons(ETH_P_802_2)) { + /* + * Ethertype 802.2 is represented in the netlink with omitted + * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and + * 0xffff in the mask attribute. Ethertype can also + * be wildcarded. + */ + if (is_mask && output->eth.type) + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, + output->eth.type)) + goto nla_put_failure; + goto unencap; + } } if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) @@ -2131,8 +2159,8 @@ static bool validate_masked(u8 *data, int len) static int validate_set(const struct nlattr *a, const struct sw_flow_key *flow_key, - struct sw_flow_actions **sfa, - bool *skip_copy, __be16 eth_type, bool masked, bool log) + struct sw_flow_actions **sfa, bool *skip_copy, + u8 mac_proto, __be16 eth_type, bool masked, bool log) { const struct nlattr *ovs_key = nla_data(a); int key_type = nla_type(ovs_key); @@ -2162,9 +2190,12 @@ static int validate_set(const struct nlattr *a, case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_CT_MARK: case OVS_KEY_ATTR_CT_LABELS: - case OVS_KEY_ATTR_ETHERNET: break; + case OVS_KEY_ATTR_ETHERNET: + if (mac_proto != MAC_PROTO_ETHERNET) + return -EINVAL; + case OVS_KEY_ATTR_TUNNEL: if (masked) return -EINVAL; /* Masked tunnel set not supported. */ @@ -2329,6 +2360,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, int depth, struct sw_flow_actions **sfa, __be16 eth_type, __be16 vlan_tci, bool log) { + u8 mac_proto = ovs_key_mac_proto(key); const struct nlattr *a; int rem, err; @@ -2399,10 +2431,14 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, } case OVS_ACTION_ATTR_POP_VLAN: + if (mac_proto != MAC_PROTO_ETHERNET) + return -EINVAL; vlan_tci = htons(0); break; case OVS_ACTION_ATTR_PUSH_VLAN: + if (mac_proto != MAC_PROTO_ETHERNET) + return -EINVAL; vlan = nla_data(a); if (!eth_type_vlan(vlan->vlan_tpid)) return -EINVAL; @@ -2452,14 +2488,16 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, case OVS_ACTION_ATTR_SET: err = validate_set(a, key, sfa, - &skip_copy, eth_type, false, log); + &skip_copy, mac_proto, eth_type, + false, log); if (err) return err; break; case OVS_ACTION_ATTR_SET_MASKED: err = validate_set(a, key, sfa, - &skip_copy, eth_type, true, log); + &skip_copy, mac_proto, eth_type, + true, log); if (err) return err; break; -- cgit v1.1 From 91820da6ae85904d95ed53bf3a83f9ec44a6b80a Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:23 +0100 Subject: openvswitch: add Ethernet push and pop actions It's not allowed to push Ethernet header in front of another Ethernet header. It's not allowed to pop Ethernet header if there's a vlan tag. This preserves the invariant that L3 packet never has a vlan tag. Based on previous versions by Lorand Jakab and Simon Horman. Signed-off-by: Lorand Jakab Signed-off-by: Simon Horman Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 15 ++++++++++++ net/openvswitch/actions.c | 49 ++++++++++++++++++++++++++++++++++++++++ net/openvswitch/flow_netlink.c | 18 +++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 59ed399..375d812 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -705,6 +705,15 @@ enum ovs_nat_attr { #define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1) +/* + * struct ovs_action_push_eth - %OVS_ACTION_ATTR_PUSH_ETH action argument. + * @addresses: Source and destination MAC addresses. + * @eth_type: Ethernet type + */ +struct ovs_action_push_eth { + struct ovs_key_ethernet addresses; +}; + /** * enum ovs_action_attr - Action types. * @@ -738,6 +747,10 @@ enum ovs_nat_attr { * is no MPLS label stack, as determined by ethertype, no action is taken. * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related * entries in the flow key. + * @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet header onto the + * packet. + * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the + * packet. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -765,6 +778,8 @@ enum ovs_action_attr { * bits. */ OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */ OVS_ACTION_ATTR_TRUNC, /* u32 struct ovs_action_trunc. */ + OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */ + OVS_ACTION_ATTR_POP_ETH, /* No argument. */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 064cbcb..514f7bc 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -317,6 +317,47 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key, return 0; } +/* pop_eth does not support VLAN packets as this action is never called + * for them. + */ +static int pop_eth(struct sk_buff *skb, struct sw_flow_key *key) +{ + skb_pull_rcsum(skb, ETH_HLEN); + skb_reset_mac_header(skb); + skb_reset_mac_len(skb); + + /* safe right before invalidate_flow_key */ + key->mac_proto = MAC_PROTO_NONE; + invalidate_flow_key(key); + return 0; +} + +static int push_eth(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_action_push_eth *ethh) +{ + struct ethhdr *hdr; + + /* Add the new Ethernet header */ + if (skb_cow_head(skb, ETH_HLEN) < 0) + return -ENOMEM; + + skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + skb_reset_mac_len(skb); + + hdr = eth_hdr(skb); + ether_addr_copy(hdr->h_source, ethh->addresses.eth_src); + ether_addr_copy(hdr->h_dest, ethh->addresses.eth_dst); + hdr->h_proto = skb->protocol; + + skb_postpush_rcsum(skb, hdr, ETH_HLEN); + + /* safe right before invalidate_flow_key */ + key->mac_proto = MAC_PROTO_ETHERNET; + invalidate_flow_key(key); + return 0; +} + static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh, __be32 addr, __be32 new_addr) { @@ -1200,6 +1241,14 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, if (err) return err == -EINPROGRESS ? 0 : err; break; + + case OVS_ACTION_ATTR_PUSH_ETH: + err = push_eth(skb, key, nla_data(a)); + break; + + case OVS_ACTION_ATTR_POP_ETH: + err = pop_eth(skb, key); + break; } if (unlikely(err)) { diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index c3d0cc43..d19044f 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2383,6 +2383,8 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash), [OVS_ACTION_ATTR_CT] = (u32)-1, [OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc), + [OVS_ACTION_ATTR_PUSH_ETH] = sizeof(struct ovs_action_push_eth), + [OVS_ACTION_ATTR_POP_ETH] = 0, }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); @@ -2517,6 +2519,22 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, skip_copy = true; break; + case OVS_ACTION_ATTR_PUSH_ETH: + /* Disallow pushing an Ethernet header if one + * is already present */ + if (mac_proto != MAC_PROTO_NONE) + return -EINVAL; + mac_proto = MAC_PROTO_NONE; + break; + + case OVS_ACTION_ATTR_POP_ETH: + if (mac_proto != MAC_PROTO_ETHERNET) + return -EINVAL; + if (vlan_tci & htons(VLAN_TAG_PRESENT)) + return -EINVAL; + mac_proto = MAC_PROTO_ETHERNET; + break; + default: OVS_NLERR(log, "Unknown Action type %d", type); return -EINVAL; -- cgit v1.1 From 217ac77a3c2524d999730b2a80b61fcc2d0f734a Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 10 Nov 2016 16:28:24 +0100 Subject: openvswitch: allow L3 netdev ports Allow ARPHRD_NONE interfaces to be added to ovs bridge. Based on previous versions by Lorand Jakab and Simon Horman. Signed-off-by: Lorand Jakab Signed-off-by: Simon Horman Signed-off-by: Jiri Benc Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/vport-netdev.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index e825753..0389398 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -57,8 +57,10 @@ static void netdev_port_receive(struct sk_buff *skb) if (unlikely(!skb)) return; - skb_push(skb, ETH_HLEN); - skb_postpush_rcsum(skb, skb->data, ETH_HLEN); + if (skb->dev->type == ARPHRD_ETHER) { + skb_push(skb, ETH_HLEN); + skb_postpush_rcsum(skb, skb->data, ETH_HLEN); + } ovs_vport_receive(vport, skb, skb_tunnel_info(skb)); return; error: @@ -97,7 +99,8 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name) } if (vport->dev->flags & IFF_LOOPBACK || - vport->dev->type != ARPHRD_ETHER || + (vport->dev->type != ARPHRD_ETHER && + vport->dev->type != ARPHRD_NONE) || ovs_is_internal_dev(vport->dev)) { err = -EINVAL; goto error_put; -- cgit v1.1 From 5ffc0335d5118bad9f98ea952e74eaf38334dd7f Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:09:29 -0600 Subject: amd-xgbe: Guard against incorrectly generated interrupts Due to a hardware issue, it is possible for interrupt events to be incorrectly generated when performing a soft reset. To guard against this, perform the soft reset twice. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 9037319..e3862bb 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1897,7 +1897,7 @@ static int xgbe_disable_int(struct xgbe_channel *channel, return 0; } -static int xgbe_exit(struct xgbe_prv_data *pdata) +static int __xgbe_exit(struct xgbe_prv_data *pdata) { unsigned int count = 2000; @@ -1919,6 +1919,20 @@ static int xgbe_exit(struct xgbe_prv_data *pdata) return 0; } +static int xgbe_exit(struct xgbe_prv_data *pdata) +{ + int ret; + + /* To guard against possible incorrectly generated interrupts, + * issue the software reset twice. + */ + ret = __xgbe_exit(pdata); + if (ret) + return ret; + + return __xgbe_exit(pdata); +} + static int xgbe_flush_tx_queues(struct xgbe_prv_data *pdata) { unsigned int i, count; -- cgit v1.1 From aba9777ae3ddea356874d46431183efaff0b3b1a Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:09:45 -0600 Subject: amd-xgbe: Add a workaround for Tx timestamp issue Update the reading of the Tx timestamp to account for a hardware issue on how the fields and interrupt are cleared. The "seconds" portion of the timestamp should be read first, followed by the "nanoseconds" portion. Reading the "nanoseconds" portion should clear the timestamp data and the interrupt. Because of an issue with the hardware this order is reversed and reading the "seconds" portion actually clears the timestamp. The code currently follows this workaround, but to guard against future versions where this is fixed add a field to the version data to indicate if the workaround is required or not. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 13 ++++++++++--- drivers/net/ethernet/amd/xgbe/xgbe-platform.c | 1 + drivers/net/ethernet/amd/xgbe/xgbe.h | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index e3862bb..81d4780 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1360,14 +1360,21 @@ static u64 xgbe_get_tstamp_time(struct xgbe_prv_data *pdata) static u64 xgbe_get_tx_tstamp(struct xgbe_prv_data *pdata) { - unsigned int tx_snr; + unsigned int tx_snr, tx_ssr; u64 nsec; - tx_snr = XGMAC_IOREAD(pdata, MAC_TXSNR); + if (pdata->vdata->tx_tstamp_workaround) { + tx_snr = XGMAC_IOREAD(pdata, MAC_TXSNR); + tx_ssr = XGMAC_IOREAD(pdata, MAC_TXSSR); + } else { + tx_ssr = XGMAC_IOREAD(pdata, MAC_TXSSR); + tx_snr = XGMAC_IOREAD(pdata, MAC_TXSNR); + } + if (XGMAC_GET_BITS(tx_snr, MAC_TXSNR, TXTSSTSMIS)) return 0; - nsec = XGMAC_IOREAD(pdata, MAC_TXSSR); + nsec = tx_ssr; nsec *= NSEC_PER_SEC; nsec += tx_snr; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c index 0edbcd5..7a701de 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c @@ -581,6 +581,7 @@ static const struct xgbe_version_data xgbe_v1 = { .xpcs_access = XGBE_XPCS_ACCESS_V1, .tx_max_fifo_size = 81920, .rx_max_fifo_size = 81920, + .tx_tstamp_workaround = 1, }; #ifdef CONFIG_ACPI diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 0779247..8523779 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -805,6 +805,7 @@ struct xgbe_version_data { unsigned int mmc_64bit; unsigned int tx_max_fifo_size; unsigned int rx_max_fifo_size; + unsigned int tx_tstamp_workaround; }; struct xgbe_prv_data { -- cgit v1.1 From 47f164deab22a02a2999f56ca51fdabab4565167 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:09:55 -0600 Subject: amd-xgbe: Add PCI device support Add support for new PCI devices to the driver. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/Kconfig | 4 +- drivers/net/ethernet/amd/xgbe/Makefile | 3 +- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 99 +++ drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c | 76 +++ drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 18 +- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 24 + drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 28 +- drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 523 ++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 854 ++++++++++++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe-platform.c | 9 +- drivers/net/ethernet/amd/xgbe/xgbe.h | 39 +- 11 files changed, 1657 insertions(+), 20 deletions(-) create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-pci.c create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 0038709..9255ce4 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -173,8 +173,8 @@ config SUNLANCE config AMD_XGBE tristate "AMD 10GbE Ethernet driver" - depends on ((OF_NET && OF_ADDRESS) || ACPI) && HAS_IOMEM && HAS_DMA - depends on ARM64 || COMPILE_TEST + depends on ((OF_NET && OF_ADDRESS) || ACPI || PCI) && HAS_IOMEM && HAS_DMA + depends on X86 || ARM64 || COMPILE_TEST select BITREVERSE select CRC32 select PTP_1588_CLOCK diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile index 217d59e..d1ce1c1 100644 --- a/drivers/net/ethernet/amd/xgbe/Makefile +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -3,8 +3,9 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ xgbe-ptp.o \ - xgbe-phy-v1.o \ + xgbe-phy-v1.o xgbe-phy-v2.o \ xgbe-platform.o +amd-xgbe-$(CONFIG_PCI) += xgbe-pci.o amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 8036ee5..b54862b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -858,8 +858,15 @@ /* PCS register offsets */ #define PCS_V1_WINDOW_SELECT 0x03fc +#define PCS_V2_WINDOW_DEF 0x9060 #define PCS_V2_WINDOW_SELECT 0x9064 +/* PCS register entry bit positions and sizes */ +#define PCS_V2_WINDOW_DEF_OFFSET_INDEX 6 +#define PCS_V2_WINDOW_DEF_OFFSET_WIDTH 14 +#define PCS_V2_WINDOW_DEF_SIZE_INDEX 2 +#define PCS_V2_WINDOW_DEF_SIZE_WIDTH 4 + /* SerDes integration register offsets */ #define SIR0_KR_RT_1 0x002c #define SIR0_STATUS 0x0040 @@ -902,6 +909,55 @@ #define RXTX_REG129_RXDFE_CONFIG_INDEX 14 #define RXTX_REG129_RXDFE_CONFIG_WIDTH 2 +/* MAC Control register offsets */ +#define XP_PROP_0 0x0000 +#define XP_PROP_1 0x0004 +#define XP_PROP_2 0x0008 +#define XP_PROP_3 0x000c +#define XP_PROP_4 0x0010 +#define XP_PROP_5 0x0014 +#define XP_MAC_ADDR_LO 0x0020 +#define XP_MAC_ADDR_HI 0x0024 +#define XP_DRIVER_INT_REQ 0x0060 +#define XP_DRIVER_INT_RO 0x0064 +#define XP_DRIVER_SCRATCH_0 0x0068 +#define XP_DRIVER_SCRATCH_1 0x006c +#define XP_INT_EN 0x0078 + +/* MAC Control register entry bit positions and sizes */ +#define XP_DRIVER_INT_REQ_REQUEST_INDEX 0 +#define XP_DRIVER_INT_REQ_REQUEST_WIDTH 1 +#define XP_DRIVER_INT_RO_STATUS_INDEX 0 +#define XP_DRIVER_INT_RO_STATUS_WIDTH 1 +#define XP_DRIVER_SCRATCH_0_COMMAND_INDEX 0 +#define XP_DRIVER_SCRATCH_0_COMMAND_WIDTH 8 +#define XP_DRIVER_SCRATCH_0_SUB_COMMAND_INDEX 8 +#define XP_DRIVER_SCRATCH_0_SUB_COMMAND_WIDTH 8 +#define XP_MAC_ADDR_HI_VALID_INDEX 31 +#define XP_MAC_ADDR_HI_VALID_WIDTH 1 +#define XP_PROP_0_CONN_TYPE_INDEX 28 +#define XP_PROP_0_CONN_TYPE_WIDTH 3 +#define XP_PROP_0_MDIO_ADDR_INDEX 16 +#define XP_PROP_0_MDIO_ADDR_WIDTH 5 +#define XP_PROP_0_PORT_ID_INDEX 0 +#define XP_PROP_0_PORT_ID_WIDTH 8 +#define XP_PROP_0_PORT_MODE_INDEX 8 +#define XP_PROP_0_PORT_MODE_WIDTH 4 +#define XP_PROP_0_PORT_SPEEDS_INDEX 23 +#define XP_PROP_0_PORT_SPEEDS_WIDTH 4 +#define XP_PROP_1_MAX_RX_DMA_INDEX 24 +#define XP_PROP_1_MAX_RX_DMA_WIDTH 5 +#define XP_PROP_1_MAX_RX_QUEUES_INDEX 8 +#define XP_PROP_1_MAX_RX_QUEUES_WIDTH 5 +#define XP_PROP_1_MAX_TX_DMA_INDEX 16 +#define XP_PROP_1_MAX_TX_DMA_WIDTH 5 +#define XP_PROP_1_MAX_TX_QUEUES_INDEX 0 +#define XP_PROP_1_MAX_TX_QUEUES_WIDTH 5 +#define XP_PROP_2_RX_FIFO_SIZE_INDEX 16 +#define XP_PROP_2_RX_FIFO_SIZE_WIDTH 16 +#define XP_PROP_2_TX_FIFO_SIZE_INDEX 0 +#define XP_PROP_2_TX_FIFO_SIZE_WIDTH 16 + /* Descriptor/Packet entry bit positions and sizes */ #define RX_PACKET_ERRORS_CRC_INDEX 2 #define RX_PACKET_ERRORS_CRC_WIDTH 1 @@ -1240,6 +1296,16 @@ do { \ /* Macros for building, reading or writing register values or bits * within the register values of XPCS registers. */ +#define XPCS_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XPCS_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + #define XPCS32_IOWRITE(_pdata, _off, _val) \ iowrite32(_val, (_pdata)->xpcs_regs + (_off)) @@ -1329,6 +1395,39 @@ do { \ } while (0) /* Macros for building, reading or writing register values or bits + * within the register values of MAC Control registers. + */ +#define XP_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XP_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +#define XP_IOREAD(_pdata, _reg) \ + ioread32((_pdata)->xprop_regs + (_reg)) + +#define XP_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XP_IOREAD((_pdata), (_reg)), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XP_IOWRITE(_pdata, _reg, _val) \ + iowrite32((_val), (_pdata)->xprop_regs + (_reg)) + +#define XP_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u32 reg_val = XP_IOREAD((_pdata), (_reg)); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XP_IOWRITE((_pdata), (_reg), reg_val); \ +} while (0) + +/* Macros for building, reading or writing register values or bits * using MDIO. Different from above because of the use of standardized * Linux include values. No shifting is performed with the bit * operations, everything works on mask values. diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 96f485a..951b5ab 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -316,6 +316,66 @@ static const struct file_operations xpcs_reg_value_fops = { .write = xpcs_reg_value_write, }; +static ssize_t xprop_reg_addr_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xprop_reg); +} + +static ssize_t xprop_reg_addr_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_write(buffer, count, ppos, + &pdata->debugfs_xprop_reg); +} + +static ssize_t xprop_reg_value_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + + value = XP_IOREAD(pdata, pdata->debugfs_xprop_reg); + + return xgbe_common_read(buffer, count, ppos, value); +} + +static ssize_t xprop_reg_value_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + ssize_t len; + + len = xgbe_common_write(buffer, count, ppos, &value); + if (len < 0) + return len; + + XP_IOWRITE(pdata, pdata->debugfs_xprop_reg, value); + + return len; +} + +static const struct file_operations xprop_reg_addr_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xprop_reg_addr_read, + .write = xprop_reg_addr_write, +}; + +static const struct file_operations xprop_reg_value_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xprop_reg_value_read, + .write = xprop_reg_value_write, +}; + void xgbe_debugfs_init(struct xgbe_prv_data *pdata) { struct dentry *pfile; @@ -367,6 +427,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata) if (!pfile) netdev_err(pdata->netdev, "debugfs_create_file failed\n"); + if (pdata->xprop_regs) { + pfile = debugfs_create_file("xprop_register", 0600, + pdata->xgbe_debugfs, pdata, + &xprop_reg_addr_fops); + if (!pfile) + netdev_err(pdata->netdev, + "debugfs_create_file failed\n"); + + pfile = debugfs_create_file("xprop_register_value", 0600, + pdata->xgbe_debugfs, pdata, + &xprop_reg_value_fops); + if (!pfile) + netdev_err(pdata->netdev, + "debugfs_create_file failed\n"); + } + kfree(buf); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index a43e9303..efa01a5 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -374,6 +374,10 @@ static irqreturn_t xgbe_isr(int irq, void *data) } } + /* If there is not a separate AN irq, handle it here */ + if (pdata->dev_irq == pdata->an_irq) + pdata->phy_if.an_isr(irq, pdata); + isr_done: return IRQ_HANDLED; } @@ -864,16 +868,16 @@ static int xgbe_start(struct xgbe_prv_data *pdata) hw_if->init(pdata); - ret = phy_if->phy_start(pdata); - if (ret) - goto err_phy; - xgbe_napi_enable(pdata, 1); ret = xgbe_request_irqs(pdata); if (ret) goto err_napi; + ret = phy_if->phy_start(pdata); + if (ret) + goto err_irqs; + hw_if->enable_tx(pdata); hw_if->enable_rx(pdata); @@ -886,12 +890,12 @@ static int xgbe_start(struct xgbe_prv_data *pdata) return 0; +err_irqs: + xgbe_free_irqs(pdata); + err_napi: xgbe_napi_disable(pdata, 1); - phy_if->phy_stop(pdata); - -err_phy: hw_if->exit(pdata); return ret; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index c7187fc..697483a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -291,6 +291,19 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_RX_DESC_CNT); pdata->rx_desc_count = XGBE_RX_DESC_CNT; + /* Adjust the number of queues based on interrupts assigned */ + if (pdata->channel_irq_count) { + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->channel_irq_count); + pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count, + pdata->channel_irq_count); + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, + "adjusted TX/RX DMA channel count = %u/%u\n", + pdata->tx_ring_count, pdata->rx_ring_count); + } + /* Set the number of queues */ ret = netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count); if (ret) { @@ -393,6 +406,11 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) xgbe_debugfs_init(pdata); + netif_dbg(pdata, drv, pdata->netdev, "%u Tx software queues\n", + pdata->tx_ring_count); + netif_dbg(pdata, drv, pdata->netdev, "%u Rx software queues\n", + pdata->rx_ring_count); + return 0; err_wq: @@ -431,11 +449,17 @@ static int __init xgbe_mod_init(void) if (ret) return ret; + ret = xgbe_pci_init(); + if (ret) + return ret; + return 0; } static void __exit xgbe_mod_exit(void) { + xgbe_pci_exit(); + xgbe_platform_exit(); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 723eb90..6d99943 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -640,6 +640,11 @@ static irqreturn_t xgbe_an_isr(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t xgbe_an_combined_isr(int irq, struct xgbe_prv_data *pdata) +{ + return xgbe_an_isr(irq, pdata); +} + static void xgbe_an_irq_work(struct work_struct *work) { struct xgbe_prv_data *pdata = container_of(work, @@ -1237,7 +1242,8 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) /* Disable auto-negotiation */ xgbe_an_disable_all(pdata); - devm_free_irq(pdata->dev, pdata->an_irq, pdata); + if (pdata->dev_irq != pdata->an_irq) + devm_free_irq(pdata->dev, pdata->an_irq, pdata); pdata->phy_if.phy_impl.stop(pdata); @@ -1258,12 +1264,15 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) if (ret) return ret; - ret = devm_request_irq(pdata->dev, pdata->an_irq, - xgbe_an_isr, 0, pdata->an_name, - pdata); - if (ret) { - netdev_err(netdev, "phy irq request failed\n"); - goto err_stop; + /* If we have a separate AN irq, enable it */ + if (pdata->dev_irq != pdata->an_irq) { + ret = devm_request_irq(pdata->dev, pdata->an_irq, + xgbe_an_isr, 0, pdata->an_name, + pdata); + if (ret) { + netdev_err(netdev, "phy irq request failed\n"); + goto err_stop; + } } /* Set initial mode - call the mode setting routines @@ -1289,7 +1298,8 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) return xgbe_phy_config_aneg(pdata); err_irq: - devm_free_irq(pdata->dev, pdata->an_irq, pdata); + if (pdata->dev_irq != pdata->an_irq) + devm_free_irq(pdata->dev, pdata->an_irq, pdata); err_stop: pdata->phy_if.phy_impl.stop(pdata); @@ -1442,4 +1452,6 @@ void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if) phy_if->phy_config_aneg = xgbe_phy_config_aneg; phy_if->phy_valid_speed = xgbe_phy_valid_speed; + + phy_if->an_isr = xgbe_an_combined_isr; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c new file mode 100644 index 0000000..fe77945 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -0,0 +1,523 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "xgbe.h" +#include "xgbe-common.h" + +static int xgbe_config_msi(struct xgbe_prv_data *pdata) +{ + unsigned int msi_count; + unsigned int i, j; + int ret; + + msi_count = XGBE_MSIX_BASE_COUNT; + msi_count += max(pdata->rx_ring_count, + pdata->tx_ring_count); + msi_count = roundup_pow_of_two(msi_count); + + ret = pci_enable_msi_exact(pdata->pcidev, msi_count); + if (ret < 0) { + dev_info(pdata->dev, "MSI request for %u interrupts failed\n", + msi_count); + + ret = pci_enable_msi(pdata->pcidev); + if (ret < 0) { + dev_info(pdata->dev, "MSI enablement failed\n"); + return ret; + } + + msi_count = 1; + } + + pdata->irq_count = msi_count; + + pdata->dev_irq = pdata->pcidev->irq; + + if (msi_count > 1) { + pdata->ecc_irq = pdata->pcidev->irq + 1; + pdata->i2c_irq = pdata->pcidev->irq + 2; + pdata->an_irq = pdata->pcidev->irq + 3; + + for (i = XGBE_MSIX_BASE_COUNT, j = 0; + (i < msi_count) && (j < XGBE_MAX_DMA_CHANNELS); + i++, j++) + pdata->channel_irq[j] = pdata->pcidev->irq + i; + pdata->channel_irq_count = j; + + pdata->per_channel_irq = 1; + } else { + pdata->ecc_irq = pdata->pcidev->irq; + pdata->i2c_irq = pdata->pcidev->irq; + pdata->an_irq = pdata->pcidev->irq; + } + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "MSI interrupts enabled\n"); + + return 0; +} + +static int xgbe_config_msix(struct xgbe_prv_data *pdata) +{ + unsigned int msix_count; + unsigned int i, j; + int ret; + + msix_count = XGBE_MSIX_BASE_COUNT; + msix_count += max(pdata->rx_ring_count, + pdata->tx_ring_count); + + pdata->msix_entries = devm_kcalloc(pdata->dev, msix_count, + sizeof(struct msix_entry), + GFP_KERNEL); + if (!pdata->msix_entries) + return -ENOMEM; + + for (i = 0; i < msix_count; i++) + pdata->msix_entries[i].entry = i; + + ret = pci_enable_msix_range(pdata->pcidev, pdata->msix_entries, + XGBE_MSIX_MIN_COUNT, msix_count); + if (ret < 0) { + dev_info(pdata->dev, "MSI-X enablement failed\n"); + devm_kfree(pdata->dev, pdata->msix_entries); + pdata->msix_entries = NULL; + return ret; + } + + pdata->irq_count = ret; + + pdata->dev_irq = pdata->msix_entries[0].vector; + pdata->ecc_irq = pdata->msix_entries[1].vector; + pdata->i2c_irq = pdata->msix_entries[2].vector; + pdata->an_irq = pdata->msix_entries[3].vector; + + for (i = XGBE_MSIX_BASE_COUNT, j = 0; i < ret; i++, j++) + pdata->channel_irq[j] = pdata->msix_entries[i].vector; + pdata->channel_irq_count = j; + + pdata->per_channel_irq = 1; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "MSI-X interrupts enabled\n"); + + return 0; +} + +static int xgbe_config_irqs(struct xgbe_prv_data *pdata) +{ + int ret; + + ret = xgbe_config_msix(pdata); + if (!ret) + goto out; + + ret = xgbe_config_msi(pdata); + if (!ret) + goto out; + + pdata->irq_count = 1; + pdata->irq_shared = 1; + + pdata->dev_irq = pdata->pcidev->irq; + pdata->ecc_irq = pdata->pcidev->irq; + pdata->i2c_irq = pdata->pcidev->irq; + pdata->an_irq = pdata->pcidev->irq; + +out: + if (netif_msg_probe(pdata)) { + unsigned int i; + + dev_dbg(pdata->dev, " dev irq=%d\n", pdata->dev_irq); + dev_dbg(pdata->dev, " ecc irq=%d\n", pdata->ecc_irq); + dev_dbg(pdata->dev, " i2c irq=%d\n", pdata->i2c_irq); + dev_dbg(pdata->dev, " an irq=%d\n", pdata->an_irq); + for (i = 0; i < pdata->channel_irq_count; i++) + dev_dbg(pdata->dev, " dma%u irq=%d\n", + i, pdata->channel_irq[i]); + } + + return 0; +} + +static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct xgbe_prv_data *pdata; + struct device *dev = &pdev->dev; + void __iomem * const *iomap_table; + unsigned int ma_lo, ma_hi; + unsigned int reg; + int bar_mask; + int ret; + + pdata = xgbe_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->pcidev = pdev; + pci_set_drvdata(pdev, pdata); + + /* Get the version data */ + pdata->vdata = (struct xgbe_version_data *)id->driver_data; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(dev, "pcim_enable_device failed\n"); + goto err_pci_enable; + } + + /* Obtain the mmio areas for the device */ + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + ret = pcim_iomap_regions(pdev, bar_mask, XGBE_DRV_NAME); + if (ret) { + dev_err(dev, "pcim_iomap_regions failed\n"); + goto err_pci_enable; + } + + iomap_table = pcim_iomap_table(pdev); + if (!iomap_table) { + dev_err(dev, "pcim_iomap_table failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->xgmac_regs = iomap_table[XGBE_XGMAC_BAR]; + if (!pdata->xgmac_regs) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + pdata->xprop_regs = pdata->xgmac_regs + XGBE_MAC_PROP_OFFSET; + pdata->xi2c_regs = pdata->xgmac_regs + XGBE_I2C_CTRL_OFFSET; + if (netif_msg_probe(pdata)) { + dev_dbg(dev, "xgmac_regs = %p\n", pdata->xgmac_regs); + dev_dbg(dev, "xprop_regs = %p\n", pdata->xprop_regs); + dev_dbg(dev, "xi2c_regs = %p\n", pdata->xi2c_regs); + } + + pdata->xpcs_regs = iomap_table[XGBE_XPCS_BAR]; + if (!pdata->xpcs_regs) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xpcs_regs = %p\n", pdata->xpcs_regs); + + /* Configure the PCS indirect addressing support */ + reg = XPCS32_IOREAD(pdata, PCS_V2_WINDOW_DEF); + pdata->xpcs_window = XPCS_GET_BITS(reg, PCS_V2_WINDOW_DEF, OFFSET); + pdata->xpcs_window <<= 6; + pdata->xpcs_window_size = XPCS_GET_BITS(reg, PCS_V2_WINDOW_DEF, SIZE); + pdata->xpcs_window_size = 1 << (pdata->xpcs_window_size + 7); + pdata->xpcs_window_mask = pdata->xpcs_window_size - 1; + if (netif_msg_probe(pdata)) { + dev_dbg(dev, "xpcs window = %#010x\n", + pdata->xpcs_window); + dev_dbg(dev, "xpcs window size = %#010x\n", + pdata->xpcs_window_size); + dev_dbg(dev, "xpcs window mask = %#010x\n", + pdata->xpcs_window_mask); + } + + pci_set_master(pdev); + + /* Enable all interrupts in the hardware */ + XP_IOWRITE(pdata, XP_INT_EN, 0x1fffff); + + /* Retrieve the MAC address */ + ma_lo = XP_IOREAD(pdata, XP_MAC_ADDR_LO); + ma_hi = XP_IOREAD(pdata, XP_MAC_ADDR_HI); + pdata->mac_addr[0] = ma_lo & 0xff; + pdata->mac_addr[1] = (ma_lo >> 8) & 0xff; + pdata->mac_addr[2] = (ma_lo >> 16) & 0xff; + pdata->mac_addr[3] = (ma_lo >> 24) & 0xff; + pdata->mac_addr[4] = ma_hi & 0xff; + pdata->mac_addr[5] = (ma_hi >> 8) & 0xff; + if (!XP_GET_BITS(ma_hi, XP_MAC_ADDR_HI, VALID) || + !is_valid_ether_addr(pdata->mac_addr)) { + dev_err(dev, "invalid mac address\n"); + ret = -EINVAL; + goto err_pci_enable; + } + + /* Clock settings */ + pdata->sysclk_rate = XGBE_V2_DMA_CLOCK_FREQ; + pdata->ptpclk_rate = XGBE_V2_PTP_CLOCK_FREQ; + + /* Set the DMA coherency values */ + pdata->coherent = 1; + pdata->axdomain = XGBE_DMA_OS_AXDOMAIN; + pdata->arcache = XGBE_DMA_OS_ARCACHE; + pdata->awcache = XGBE_DMA_OS_AWCACHE; + + /* Set the maximum channels and queues */ + reg = XP_IOREAD(pdata, XP_PROP_1); + pdata->tx_max_channel_count = XP_GET_BITS(reg, XP_PROP_1, MAX_TX_DMA); + pdata->rx_max_channel_count = XP_GET_BITS(reg, XP_PROP_1, MAX_RX_DMA); + pdata->tx_max_q_count = XP_GET_BITS(reg, XP_PROP_1, MAX_TX_QUEUES); + pdata->rx_max_q_count = XP_GET_BITS(reg, XP_PROP_1, MAX_RX_QUEUES); + if (netif_msg_probe(pdata)) { + dev_dbg(dev, "max tx/rx channel count = %u/%u\n", + pdata->tx_max_channel_count, + pdata->tx_max_channel_count); + dev_dbg(dev, "max tx/rx hw queue count = %u/%u\n", + pdata->tx_max_q_count, pdata->rx_max_q_count); + } + + /* Set the hardware channel and queue counts */ + xgbe_set_counts(pdata); + + /* Set the maximum fifo amounts */ + reg = XP_IOREAD(pdata, XP_PROP_2); + pdata->tx_max_fifo_size = XP_GET_BITS(reg, XP_PROP_2, TX_FIFO_SIZE); + pdata->tx_max_fifo_size *= 16384; + pdata->tx_max_fifo_size = min(pdata->tx_max_fifo_size, + pdata->vdata->tx_max_fifo_size); + pdata->rx_max_fifo_size = XP_GET_BITS(reg, XP_PROP_2, RX_FIFO_SIZE); + pdata->rx_max_fifo_size *= 16384; + pdata->rx_max_fifo_size = min(pdata->rx_max_fifo_size, + pdata->vdata->rx_max_fifo_size); + if (netif_msg_probe(pdata)) + dev_dbg(dev, "max tx/rx max fifo size = %u/%u\n", + pdata->tx_max_fifo_size, pdata->rx_max_fifo_size); + + /* Configure interrupt support */ + ret = xgbe_config_irqs(pdata); + if (ret) + goto err_pci_enable; + + /* Configure the netdev resource */ + ret = xgbe_config_netdev(pdata); + if (ret) + goto err_pci_enable; + + netdev_notice(pdata->netdev, "net device enabled\n"); + + return 0; + +err_pci_enable: + xgbe_free_pdata(pdata); + +err_alloc: + dev_notice(dev, "net device not enabled\n"); + + return ret; +} + +static void xgbe_pci_remove(struct pci_dev *pdev) +{ + struct xgbe_prv_data *pdata = pci_get_drvdata(pdev); + + xgbe_deconfig_netdev(pdata); + + xgbe_free_pdata(pdata); +} + +#ifdef CONFIG_PM +static int xgbe_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct xgbe_prv_data *pdata = pci_get_drvdata(pdev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + if (netif_running(netdev)) + ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT); + + pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + return ret; +} + +static int xgbe_pci_resume(struct pci_dev *pdev) +{ + struct xgbe_prv_data *pdata = pci_get_drvdata(pdev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + if (netif_running(netdev)) { + ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT); + + /* Schedule a restart in case the link or phy state changed + * while we were powered down. + */ + schedule_work(&pdata->restart_work); + } + + return ret; +} +#endif /* CONFIG_PM */ + +static const struct xgbe_version_data xgbe_v2a = { + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v2, + .xpcs_access = XGBE_XPCS_ACCESS_V2, + .mmc_64bit = 1, + .tx_max_fifo_size = 229376, + .rx_max_fifo_size = 229376, + .tx_tstamp_workaround = 1, +}; + +static const struct xgbe_version_data xgbe_v2b = { + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v2, + .xpcs_access = XGBE_XPCS_ACCESS_V2, + .mmc_64bit = 1, + .tx_max_fifo_size = 65536, + .rx_max_fifo_size = 65536, + .tx_tstamp_workaround = 1, +}; + +static const struct pci_device_id xgbe_pci_table[] = { + { PCI_VDEVICE(AMD, 0x1458), + .driver_data = (kernel_ulong_t)&xgbe_v2a }, + { PCI_VDEVICE(AMD, 0x1459), + .driver_data = (kernel_ulong_t)&xgbe_v2b }, + /* Last entry must be zero */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, xgbe_pci_table); + +static struct pci_driver xgbe_driver = { + .name = XGBE_DRV_NAME, + .id_table = xgbe_pci_table, + .probe = xgbe_pci_probe, + .remove = xgbe_pci_remove, +#ifdef CONFIG_PM + .suspend = xgbe_pci_suspend, + .resume = xgbe_pci_resume, +#endif +}; + +int xgbe_pci_init(void) +{ + return pci_register_driver(&xgbe_driver); +} + +void xgbe_pci_exit(void) +{ + pci_unregister_driver(&xgbe_driver); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c new file mode 100644 index 0000000..7ae0abc --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -0,0 +1,854 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_PHY_PORT_SPEED_100 BIT(0) +#define XGBE_PHY_PORT_SPEED_1000 BIT(1) +#define XGBE_PHY_PORT_SPEED_2500 BIT(2) +#define XGBE_PHY_PORT_SPEED_10000 BIT(3) + +/* Rate-change complete wait/retry count */ +#define XGBE_RATECHANGE_COUNT 500 + +enum xgbe_port_mode { + XGBE_PORT_MODE_RSVD = 0, + XGBE_PORT_MODE_BACKPLANE, + XGBE_PORT_MODE_BACKPLANE_2500, + XGBE_PORT_MODE_1000BASE_T, + XGBE_PORT_MODE_1000BASE_X, + XGBE_PORT_MODE_NBASE_T, + XGBE_PORT_MODE_10GBASE_T, + XGBE_PORT_MODE_10GBASE_R, + XGBE_PORT_MODE_SFP, + XGBE_PORT_MODE_MAX, +}; + +enum xgbe_conn_type { + XGBE_CONN_TYPE_NONE = 0, + XGBE_CONN_TYPE_SFP, + XGBE_CONN_TYPE_MDIO, + XGBE_CONN_TYPE_BACKPLANE, + XGBE_CONN_TYPE_MAX, +}; + +/* PHY related configuration information */ +struct xgbe_phy_data { + enum xgbe_port_mode port_mode; + + unsigned int port_id; + + unsigned int port_speeds; + + enum xgbe_conn_type conn_type; + + enum xgbe_mode cur_mode; + enum xgbe_mode start_mode; + + unsigned int rrc_count; +}; + +static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) +{ + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_Backplane; + + /* Compare Advertisement and Link Partner register 1 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + if (lp_reg & 0x400) + pdata->phy.lp_advertising |= ADVERTISED_Pause; + if (lp_reg & 0x800) + pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + + if (pdata->phy.pause_autoneg) { + /* Set flow control based on auto-negotiation result */ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (ad_reg & lp_reg & 0x400) { + pdata->phy.tx_pause = 1; + pdata->phy.rx_pause = 1; + } else if (ad_reg & lp_reg & 0x800) { + if (ad_reg & 0x400) + pdata->phy.rx_pause = 1; + else if (lp_reg & 0x400) + pdata->phy.tx_pause = 1; + } + } + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + if (lp_reg & 0x20) + pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + + ad_reg &= lp_reg; + if (ad_reg & 0x80) + mode = XGBE_MODE_KR; + else if (ad_reg & 0x20) + mode = XGBE_MODE_KX_1000; + else + mode = XGBE_MODE_UNKNOWN; + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + + return mode; +} + +static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + return XGBE_AN_MODE_CL73; + case XGBE_PORT_MODE_BACKPLANE_2500: + return XGBE_AN_MODE_NONE; + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + case XGBE_PORT_MODE_SFP: + default: + return XGBE_AN_MODE_NONE; + } +} + +static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata) +{ + if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) + return; + + /* Log if a previous command did not complete */ + netif_dbg(pdata, link, pdata->netdev, + "firmware mailbox not ready for command\n"); +} + +static void xgbe_phy_complete_ratechange(struct xgbe_prv_data *pdata) +{ + unsigned int wait; + + /* Wait for command to complete */ + wait = XGBE_RATECHANGE_COUNT; + while (wait--) { + if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) + return; + + usleep_range(1000, 2000); + } + + netif_dbg(pdata, link, pdata->netdev, + "firmware mailbox command did not complete\n"); +} + +static void xgbe_phy_rrc(struct xgbe_prv_data *pdata) +{ + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* Receiver Reset Cycle */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 5); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + netif_dbg(pdata, link, pdata->netdev, "receiver reset complete\n"); +} + +static void xgbe_phy_power_off(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_start_ratechange(pdata); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, 0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_UNKNOWN; + + netif_dbg(pdata, link, pdata->netdev, "phy powered off\n"); +} + +static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* 10G/KR */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 4); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_KR; + + netif_dbg(pdata, link, pdata->netdev, "10GbE KR mode set\n"); +} + +static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* 2.5G/KX */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 2); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_KX_2500; + + netif_dbg(pdata, link, pdata->netdev, "2.5GbE KX mode set\n"); +} + +static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* 1G/KX */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 1); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_KX_1000; + + netif_dbg(pdata, link, pdata->netdev, "1GbE KX mode set\n"); +} + +static enum xgbe_mode xgbe_phy_cur_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + return phy_data->cur_mode; +} + +static enum xgbe_mode xgbe_phy_switch_bp_2500_mode(struct xgbe_prv_data *pdata) +{ + return XGBE_MODE_KX_2500; +} + +static enum xgbe_mode xgbe_phy_switch_bp_mode(struct xgbe_prv_data *pdata) +{ + /* If we are in KR switch to KX, and vice-versa */ + switch (xgbe_phy_cur_mode(pdata)) { + case XGBE_MODE_KX_1000: + return XGBE_MODE_KR; + case XGBE_MODE_KR: + default: + return XGBE_MODE_KX_1000; + } +} + +static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + return xgbe_phy_switch_bp_mode(pdata); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_switch_bp_2500_mode(pdata); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + case XGBE_PORT_MODE_SFP: + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_bp_2500_mode(int speed) +{ + switch (speed) { + case SPEED_2500: + return XGBE_MODE_KX_2500; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_bp_mode(int speed) +{ + switch (speed) { + case SPEED_1000: + return XGBE_MODE_KX_1000; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata, + int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + return xgbe_phy_get_bp_mode(speed); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_get_bp_2500_mode(speed); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + case XGBE_PORT_MODE_SFP: + default: + return XGBE_MODE_UNKNOWN; + } +} + +static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_1000: + xgbe_phy_kx_1000_mode(pdata); + break; + case XGBE_MODE_KX_2500: + xgbe_phy_kx_2500_mode(pdata); + break; + case XGBE_MODE_KR: + xgbe_phy_kr_mode(pdata); + break; + default: + break; + } +} + +static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode, u32 advert) +{ + if (pdata->phy.autoneg == AUTONEG_ENABLE) { + if (pdata->phy.advertising & advert) + return true; + } else { + enum xgbe_mode cur_mode; + + cur_mode = xgbe_phy_get_mode(pdata, pdata->phy.speed); + if (cur_mode == mode) + return true; + } + + return false; +} + +static bool xgbe_phy_use_bp_2500_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_2500: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_2500baseX_Full); + default: + return false; + } +} + +static bool xgbe_phy_use_bp_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_1000: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseKX_Full); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseKR_Full); + default: + return false; + } +} + +static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + return xgbe_phy_use_bp_mode(pdata, mode); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_use_bp_2500_mode(pdata, mode); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + case XGBE_PORT_MODE_SFP: + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_bp_2500_mode(int speed) +{ + switch (speed) { + case SPEED_2500: + return true; + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_bp_mode(int speed) +{ + switch (speed) { + case SPEED_1000: + case SPEED_10000: + return true; + default: + return false; + } +} + +static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + return xgbe_phy_valid_speed_bp_mode(speed); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_valid_speed_bp_2500_mode(speed); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + case XGBE_PORT_MODE_SFP: + default: + return false; + } +} + +static int xgbe_phy_link_status(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + /* Link status is latched low, so read once to clear + * and then read again to get current state + */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + if (reg & MDIO_STAT1_LSTATUS) + return 1; + + /* No link, attempt a receiver reset cycle */ + if (phy_data->rrc_count++) { + phy_data->rrc_count = 0; + xgbe_phy_rrc(pdata); + } + + return 0; +} + +static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)) + return false; + break; + case XGBE_PORT_MODE_BACKPLANE_2500: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500) + return false; + break; + case XGBE_PORT_MODE_1000BASE_T: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000)) + return false; + break; + case XGBE_PORT_MODE_1000BASE_X: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + return false; + break; + case XGBE_PORT_MODE_NBASE_T: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500)) + return false; + break; + case XGBE_PORT_MODE_10GBASE_T: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)) + return false; + break; + case XGBE_PORT_MODE_10GBASE_R: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) + return false; + break; + case XGBE_PORT_MODE_SFP: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)) + return false; + break; + default: + break; + } + + return true; +} + +static bool xgbe_phy_conn_type_mismatch(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_2500: + if (phy_data->conn_type == XGBE_CONN_TYPE_BACKPLANE) + return false; + break; + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + if (phy_data->conn_type == XGBE_CONN_TYPE_MDIO) + return false; + break; + case XGBE_PORT_MODE_SFP: + if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) + return false; + break; + default: + break; + } + + return true; +} + +static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + reg = XP_IOREAD(pdata, XP_PROP_0); + if (!XP_GET_BITS(reg, XP_PROP_0, PORT_SPEEDS)) + return false; + if (!XP_GET_BITS(reg, XP_PROP_0, CONN_TYPE)) + return false; + + return true; +} + +static void xgbe_phy_stop(struct xgbe_prv_data *pdata) +{ + /* Power off the PHY */ + xgbe_phy_power_off(pdata); +} + +static int xgbe_phy_start(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* Start in highest supported mode */ + xgbe_phy_set_mode(pdata, phy_data->start_mode); + + return 0; +} + +static int xgbe_phy_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode cur_mode; + + /* Reset by power cycling the PHY */ + cur_mode = phy_data->cur_mode; + xgbe_phy_power_off(pdata); + xgbe_phy_set_mode(pdata, cur_mode); + + return 0; +} + +static void xgbe_phy_exit(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for exit */ +} + +static int xgbe_phy_init(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data; + unsigned int reg; + + /* Check if enabled */ + if (!xgbe_phy_port_enabled(pdata)) { + dev_info(pdata->dev, "device is not enabled\n"); + return -ENODEV; + } + + phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + pdata->phy_data = phy_data; + + reg = XP_IOREAD(pdata, XP_PROP_0); + phy_data->port_mode = XP_GET_BITS(reg, XP_PROP_0, PORT_MODE); + phy_data->port_id = XP_GET_BITS(reg, XP_PROP_0, PORT_ID); + phy_data->port_speeds = XP_GET_BITS(reg, XP_PROP_0, PORT_SPEEDS); + phy_data->conn_type = XP_GET_BITS(reg, XP_PROP_0, CONN_TYPE); + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "port mode=%u\n", phy_data->port_mode); + dev_dbg(pdata->dev, "port id=%u\n", phy_data->port_id); + dev_dbg(pdata->dev, "port speeds=%#x\n", phy_data->port_speeds); + dev_dbg(pdata->dev, "conn type=%u\n", phy_data->conn_type); + } + + /* Validate the connection requested */ + if (xgbe_phy_conn_type_mismatch(pdata)) { + dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n", + phy_data->port_mode, phy_data->conn_type); + } + + /* Validate the mode requested */ + if (xgbe_phy_port_mode_mismatch(pdata)) { + dev_err(pdata->dev, "phy mode/speed mismatch (%#x/%#x)\n", + phy_data->port_mode, phy_data->port_speeds); + return -EINVAL; + } + + /* Indicate current mode is unknown */ + phy_data->cur_mode = XGBE_MODE_UNKNOWN; + + /* Initialize supported features */ + pdata->phy.supported = 0; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + pdata->phy.supported |= SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_Backplane; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + pdata->phy.supported |= SUPPORTED_1000baseKX_Full; + phy_data->start_mode = XGBE_MODE_KX_1000; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + pdata->phy.supported |= SUPPORTED_10000baseKR_Full; + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + pdata->phy.supported |= + SUPPORTED_10000baseR_FEC; + phy_data->start_mode = XGBE_MODE_KR; + } + break; + case XGBE_PORT_MODE_BACKPLANE_2500: + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_Backplane; + pdata->phy.supported |= SUPPORTED_2500baseX_Full; + phy_data->start_mode = XGBE_MODE_KX_2500; + break; + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + case XGBE_PORT_MODE_SFP: + default: + return -EINVAL; + } + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phy supported=%#x\n", + pdata->phy.supported); + + return 0; +} + +void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if) +{ + struct xgbe_phy_impl_if *phy_impl = &phy_if->phy_impl; + + phy_impl->init = xgbe_phy_init; + phy_impl->exit = xgbe_phy_exit; + + phy_impl->reset = xgbe_phy_reset; + phy_impl->start = xgbe_phy_start; + phy_impl->stop = xgbe_phy_stop; + + phy_impl->link_status = xgbe_phy_link_status; + + phy_impl->valid_speed = xgbe_phy_valid_speed; + + phy_impl->use_mode = xgbe_phy_use_mode; + phy_impl->set_mode = xgbe_phy_set_mode; + phy_impl->get_mode = xgbe_phy_get_mode; + phy_impl->switch_mode = xgbe_phy_switch_mode; + phy_impl->cur_mode = xgbe_phy_cur_mode; + + phy_impl->an_mode = xgbe_phy_an_mode; + + phy_impl->an_outcome = xgbe_phy_an_outcome; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c index 7a701de..8d438c3 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c @@ -462,6 +462,9 @@ static int xgbe_platform_probe(struct platform_device *pdev) /* Set the hardware channel and queue counts */ xgbe_set_counts(pdata); + /* Always have XGMAC and XPCS (auto-negotiation) interrupts */ + pdata->irq_count = 2; + /* Get the device interrupt */ ret = platform_get_irq(pdev, 0); if (ret < 0) { @@ -485,6 +488,10 @@ static int xgbe_platform_probe(struct platform_device *pdev) pdata->channel_irq[i] = ret; } + + pdata->channel_irq_count = max; + + pdata->irq_count += max; } /* Get the auto-negotiation interrupt */ @@ -609,7 +616,7 @@ static SIMPLE_DEV_PM_OPS(xgbe_platform_pm_ops, static struct platform_driver xgbe_driver = { .driver = { - .name = "amd-xgbe", + .name = XGBE_DRV_NAME, #ifdef CONFIG_ACPI .acpi_match_table = xgbe_acpi_match, #endif diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 8523779..e22156e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -200,6 +200,20 @@ #define XGBE_ACPI_DMA_FREQ "amd,dma-freq" #define XGBE_ACPI_PTP_FREQ "amd,ptp-freq" +/* PCI BAR mapping */ +#define XGBE_XGMAC_BAR 0 +#define XGBE_XPCS_BAR 1 +#define XGBE_MAC_PROP_OFFSET 0x1d000 +#define XGBE_I2C_CTRL_OFFSET 0x1e000 + +/* PCI MSIx support */ +#define XGBE_MSIX_BASE_COUNT 4 +#define XGBE_MSIX_MIN_COUNT (XGBE_MSIX_BASE_COUNT + 1) + +/* PCI clock frequencies */ +#define XGBE_V2_DMA_CLOCK_FREQ 500000000 /* 500 MHz */ +#define XGBE_V2_PTP_CLOCK_FREQ 125000000 /* 125 MHz */ + /* Timestamp support - values based on 50MHz PTP clock * 50MHz => 20 nsec */ @@ -738,6 +752,9 @@ struct xgbe_phy_if { /* For PHY settings validation */ bool (*phy_valid_speed)(struct xgbe_prv_data *, int); + /* For single interrupt support */ + irqreturn_t (*an_isr)(int, struct xgbe_prv_data *); + /* PHY implementation specific services */ struct xgbe_phy_impl_if phy_impl; }; @@ -810,6 +827,7 @@ struct xgbe_version_data { struct xgbe_prv_data { struct net_device *netdev; + struct pci_dev *pcidev; struct platform_device *platdev; struct acpi_device *adev; struct device *dev; @@ -828,6 +846,8 @@ struct xgbe_prv_data { void __iomem *rxtx_regs; /* SerDes Rx/Tx CSRs */ void __iomem *sir0_regs; /* SerDes integration registers (1/2) */ void __iomem *sir1_regs; /* SerDes integration registers (2/2) */ + void __iomem *xprop_regs; /* XGBE property registers */ + void __iomem *xi2c_regs; /* XGBE I2C CSRs */ /* Overall device lock */ spinlock_t lock; @@ -844,10 +864,17 @@ struct xgbe_prv_data { /* Flags representing xgbe_state */ unsigned long dev_state; + struct msix_entry *msix_entries; int dev_irq; - unsigned int per_channel_irq; + int ecc_irq; + int i2c_irq; int channel_irq[XGBE_MAX_DMA_CHANNELS]; + unsigned int per_channel_irq; + unsigned int irq_shared; + unsigned int irq_count; + unsigned int channel_irq_count; + struct xgbe_hw_if hw_if; struct xgbe_phy_if phy_if; struct xgbe_desc_if desc_if; @@ -1009,6 +1036,8 @@ struct xgbe_prv_data { unsigned int debugfs_xpcs_mmd; unsigned int debugfs_xpcs_reg; + + unsigned int debugfs_xprop_reg; #endif }; @@ -1021,10 +1050,18 @@ void xgbe_deconfig_netdev(struct xgbe_prv_data *); int xgbe_platform_init(void); void xgbe_platform_exit(void); +#ifdef CONFIG_PCI +int xgbe_pci_init(void); +void xgbe_pci_exit(void); +#else +static inline int xgbe_pci_init(void) { return 0; } +static inline void xgbe_pci_exit(void) { } +#endif void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *); void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *); void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *); +void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *); void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *); const struct net_device_ops *xgbe_get_netdev_ops(void); const struct ethtool_ops *xgbe_get_ethtool_ops(void); -- cgit v1.1 From 8d6b2e92bdadc925c9ea4d8d0c4554918357ee35 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:10:05 -0600 Subject: amd-xgbe: Allow for a greater number of Rx queues Remove the call to netif_get_num_default_rss_queues() and replace it with num_online_cpus() to allow for the possibility of using all of the hardware DMA channels available. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 697483a..7655753 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -236,8 +236,7 @@ void xgbe_set_counts(struct xgbe_prv_data *pdata) pdata->tx_q_count = pdata->tx_ring_count; - pdata->rx_ring_count = min_t(unsigned int, - netif_get_num_default_rss_queues(), + pdata->rx_ring_count = min_t(unsigned int, num_online_cpus(), pdata->hw_feat.rx_ch_cnt); pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count, pdata->rx_max_channel_count); -- cgit v1.1 From 4c70dd8ac9ef88a1902b4d63dda987746a34ebc4 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:10:17 -0600 Subject: amd-xgbe: Add support for new DMA interrupt mode The current per channel DMA interrupt support is based on an edge triggered interrupt that is not maskable. This results in having to call the disable_irq/enable_irq functions in order to prevent interrupts during napi processing. The hardware now has a way to configure the per channel DMA interrupt that will allow for masking the interrupt which prevents calling disable_irq/enable_irq now. This patch makes use of this support. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 2 + drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 15 ++-- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 99 ++++++++++++++++++--------- drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 2 + drivers/net/ethernet/amd/xgbe/xgbe-platform.c | 4 +- drivers/net/ethernet/amd/xgbe/xgbe.h | 5 ++ 6 files changed, 91 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index b54862b..516345c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -159,6 +159,8 @@ #define DMA_ISR_MACIS_WIDTH 1 #define DMA_ISR_MTLIS_INDEX 16 #define DMA_ISR_MTLIS_WIDTH 1 +#define DMA_MR_INTM_INDEX 12 +#define DMA_MR_INTM_WIDTH 2 #define DMA_MR_SWR_INDEX 0 #define DMA_MR_SWR_WIDTH 1 #define DMA_SBMR_EAME_INDEX 11 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 81d4780..ff7f5ab 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -646,6 +646,11 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) unsigned int dma_ch_isr, dma_ch_ier; unsigned int i; + /* Set the interrupt mode if supported */ + if (pdata->channel_irq_mode) + XGMAC_IOWRITE_BITS(pdata, DMA_MR, INTM, + pdata->channel_irq_mode); + channel = pdata->channel; for (i = 0; i < pdata->channel_count; i++, channel++) { /* Clear all the interrupts which are set */ @@ -667,19 +672,21 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) if (channel->tx_ring) { /* Enable the following Tx interrupts * TIE - Transmit Interrupt Enable (unless using - * per channel interrupts) + * per channel interrupts in edge triggered + * mode) */ - if (!pdata->per_channel_irq) + if (!pdata->per_channel_irq || pdata->channel_irq_mode) XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1); } if (channel->rx_ring) { /* Enable following Rx interrupts * RBUE - Receive Buffer Unavailable Enable * RIE - Receive Interrupt Enable (unless using - * per channel interrupts) + * per channel interrupts in edge triggered + * mode) */ XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1); - if (!pdata->per_channel_irq) + if (!pdata->per_channel_irq || pdata->channel_irq_mode) XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index efa01a5..490fdb5 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -252,48 +252,60 @@ static int xgbe_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu) return rx_buf_size; } -static void xgbe_enable_rx_tx_ints(struct xgbe_prv_data *pdata) +static void xgbe_enable_rx_tx_int(struct xgbe_prv_data *pdata, + struct xgbe_channel *channel) { struct xgbe_hw_if *hw_if = &pdata->hw_if; - struct xgbe_channel *channel; enum xgbe_int int_id; + + if (channel->tx_ring && channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_RI; + else + return; + + hw_if->enable_int(channel, int_id); +} + +static void xgbe_enable_rx_tx_ints(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; unsigned int i; channel = pdata->channel; - for (i = 0; i < pdata->channel_count; i++, channel++) { - if (channel->tx_ring && channel->rx_ring) - int_id = XGMAC_INT_DMA_CH_SR_TI_RI; - else if (channel->tx_ring) - int_id = XGMAC_INT_DMA_CH_SR_TI; - else if (channel->rx_ring) - int_id = XGMAC_INT_DMA_CH_SR_RI; - else - continue; + for (i = 0; i < pdata->channel_count; i++, channel++) + xgbe_enable_rx_tx_int(pdata, channel); +} - hw_if->enable_int(channel, int_id); - } +static void xgbe_disable_rx_tx_int(struct xgbe_prv_data *pdata, + struct xgbe_channel *channel) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + enum xgbe_int int_id; + + if (channel->tx_ring && channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_RI; + else + return; + + hw_if->disable_int(channel, int_id); } static void xgbe_disable_rx_tx_ints(struct xgbe_prv_data *pdata) { - struct xgbe_hw_if *hw_if = &pdata->hw_if; struct xgbe_channel *channel; - enum xgbe_int int_id; unsigned int i; channel = pdata->channel; - for (i = 0; i < pdata->channel_count; i++, channel++) { - if (channel->tx_ring && channel->rx_ring) - int_id = XGMAC_INT_DMA_CH_SR_TI_RI; - else if (channel->tx_ring) - int_id = XGMAC_INT_DMA_CH_SR_TI; - else if (channel->rx_ring) - int_id = XGMAC_INT_DMA_CH_SR_RI; - else - continue; - - hw_if->disable_int(channel, int_id); - } + for (i = 0; i < pdata->channel_count; i++, channel++) + xgbe_disable_rx_tx_int(pdata, channel); } static irqreturn_t xgbe_isr(int irq, void *data) @@ -339,6 +351,13 @@ static irqreturn_t xgbe_isr(int irq, void *data) /* Turn on polling */ __napi_schedule_irqoff(&pdata->napi); } + } else { + /* Don't clear Rx/Tx status if doing per channel DMA + * interrupts, these will be cleared by the ISR for + * per channel DMA interrupts. + */ + XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, TI, 0); + XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, RI, 0); } if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU)) @@ -348,7 +367,7 @@ static irqreturn_t xgbe_isr(int irq, void *data) if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) schedule_work(&pdata->restart_work); - /* Clear all interrupt signals */ + /* Clear interrupt signals */ XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr); } @@ -385,18 +404,29 @@ isr_done: static irqreturn_t xgbe_dma_isr(int irq, void *data) { struct xgbe_channel *channel = data; + struct xgbe_prv_data *pdata = channel->pdata; + unsigned int dma_status; /* Per channel DMA interrupts are enabled, so we use the per * channel napi structure and not the private data napi structure */ if (napi_schedule_prep(&channel->napi)) { /* Disable Tx and Rx interrupts */ - disable_irq_nosync(channel->dma_irq); + if (pdata->channel_irq_mode) + xgbe_disable_rx_tx_int(pdata, channel); + else + disable_irq_nosync(channel->dma_irq); /* Turn on polling */ __napi_schedule_irqoff(&channel->napi); } + /* Clear Tx/Rx signals */ + dma_status = 0; + XGMAC_SET_BITS(dma_status, DMA_CH_SR, TI, 1); + XGMAC_SET_BITS(dma_status, DMA_CH_SR, RI, 1); + XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_status); + return IRQ_HANDLED; } @@ -413,7 +443,10 @@ static void xgbe_tx_timer(unsigned long data) if (napi_schedule_prep(napi)) { /* Disable Tx and Rx interrupts */ if (pdata->per_channel_irq) - disable_irq_nosync(channel->dma_irq); + if (pdata->channel_irq_mode) + xgbe_disable_rx_tx_int(pdata, channel); + else + disable_irq_nosync(channel->dma_irq); else xgbe_disable_rx_tx_ints(pdata); @@ -2030,6 +2063,7 @@ static int xgbe_one_poll(struct napi_struct *napi, int budget) { struct xgbe_channel *channel = container_of(napi, struct xgbe_channel, napi); + struct xgbe_prv_data *pdata = channel->pdata; int processed = 0; DBGPR("-->xgbe_one_poll: budget=%d\n", budget); @@ -2046,7 +2080,10 @@ static int xgbe_one_poll(struct napi_struct *napi, int budget) napi_complete_done(napi, processed); /* Enable Tx and Rx interrupts */ - enable_irq(channel->dma_irq); + if (pdata->channel_irq_mode) + xgbe_enable_rx_tx_int(pdata, channel); + else + enable_irq(channel->dma_irq); } DBGPR("<--xgbe_one_poll: received = %d\n", processed); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index fe77945..61eb6613 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -163,6 +163,7 @@ static int xgbe_config_msi(struct xgbe_prv_data *pdata) pdata->channel_irq_count = j; pdata->per_channel_irq = 1; + pdata->channel_irq_mode = XGBE_IRQ_MODE_LEVEL; } else { pdata->ecc_irq = pdata->pcidev->irq; pdata->i2c_irq = pdata->pcidev->irq; @@ -215,6 +216,7 @@ static int xgbe_config_msix(struct xgbe_prv_data *pdata) pdata->channel_irq_count = j; pdata->per_channel_irq = 1; + pdata->channel_irq_mode = XGBE_IRQ_MODE_LEVEL; if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "MSI-X interrupts enabled\n"); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c index 8d438c3..8c530dc 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c @@ -426,8 +426,10 @@ static int xgbe_platform_probe(struct platform_device *pdev) pdata->phy_mode = PHY_INTERFACE_MODE_XGMII; /* Check for per channel interrupt support */ - if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) + if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) { pdata->per_channel_irq = 1; + pdata->channel_irq_mode = XGBE_IRQ_MODE_EDGE; + } /* Obtain device settings unique to ACPI/OF */ if (pdata->use_acpi) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index e22156e..381144b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -171,6 +171,10 @@ #define XGBE_DMA_SYS_ARCACHE 0x0 #define XGBE_DMA_SYS_AWCACHE 0x0 +/* DMA channel interrupt modes */ +#define XGBE_IRQ_MODE_EDGE 0 +#define XGBE_IRQ_MODE_LEVEL 1 + #define XGBE_DMA_INTERRUPT_MASK 0x31c7 #define XGMAC_MIN_PACKET 60 @@ -874,6 +878,7 @@ struct xgbe_prv_data { unsigned int irq_shared; unsigned int irq_count; unsigned int channel_irq_count; + unsigned int channel_irq_mode; struct xgbe_hw_if hw_if; struct xgbe_phy_if phy_if; -- cgit v1.1 From e78332b2285d9fe631a093fc8ca2b604c48c33e6 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:10:26 -0600 Subject: amd-xgbe: Add ECC status support for the device memory Some versions of the amd-xgbe device are capable of reporting ECC error information back to the driver. Add support to process, track and report on this information. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/Kconfig | 5 + drivers/net/ethernet/amd/xgbe/xgbe-common.h | 40 +++++++ drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 68 +++++++++++ drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 180 +++++++++++++++++++++++++++- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 13 ++ drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 2 + drivers/net/ethernet/amd/xgbe/xgbe.h | 35 +++++- 7 files changed, 340 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 9255ce4..930636c 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -178,6 +178,7 @@ config AMD_XGBE select BITREVERSE select CRC32 select PTP_1588_CLOCK + select AMD_XGBE_HAVE_ECC if X86 ---help--- This driver supports the AMD 10GbE Ethernet device found on an AMD SoC. @@ -195,4 +196,8 @@ config AMD_XGBE_DCB If unsure, say N. +config AMD_XGBE_HAVE_ECC + bool + default n + endif # NET_VENDOR_AMD diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 516345c..f7527cd 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -920,6 +920,10 @@ #define XP_PROP_5 0x0014 #define XP_MAC_ADDR_LO 0x0020 #define XP_MAC_ADDR_HI 0x0024 +#define XP_ECC_ISR 0x0030 +#define XP_ECC_IER 0x0034 +#define XP_ECC_CNT0 0x003c +#define XP_ECC_CNT1 0x0040 #define XP_DRIVER_INT_REQ 0x0060 #define XP_DRIVER_INT_RO 0x0064 #define XP_DRIVER_SCRATCH_0 0x0068 @@ -935,6 +939,42 @@ #define XP_DRIVER_SCRATCH_0_COMMAND_WIDTH 8 #define XP_DRIVER_SCRATCH_0_SUB_COMMAND_INDEX 8 #define XP_DRIVER_SCRATCH_0_SUB_COMMAND_WIDTH 8 +#define XP_ECC_CNT0_RX_DED_INDEX 24 +#define XP_ECC_CNT0_RX_DED_WIDTH 8 +#define XP_ECC_CNT0_RX_SEC_INDEX 16 +#define XP_ECC_CNT0_RX_SEC_WIDTH 8 +#define XP_ECC_CNT0_TX_DED_INDEX 8 +#define XP_ECC_CNT0_TX_DED_WIDTH 8 +#define XP_ECC_CNT0_TX_SEC_INDEX 0 +#define XP_ECC_CNT0_TX_SEC_WIDTH 8 +#define XP_ECC_CNT1_DESC_DED_INDEX 8 +#define XP_ECC_CNT1_DESC_DED_WIDTH 8 +#define XP_ECC_CNT1_DESC_SEC_INDEX 0 +#define XP_ECC_CNT1_DESC_SEC_WIDTH 8 +#define XP_ECC_IER_DESC_DED_INDEX 0 +#define XP_ECC_IER_DESC_DED_WIDTH 1 +#define XP_ECC_IER_DESC_SEC_INDEX 1 +#define XP_ECC_IER_DESC_SEC_WIDTH 1 +#define XP_ECC_IER_RX_DED_INDEX 2 +#define XP_ECC_IER_RX_DED_WIDTH 1 +#define XP_ECC_IER_RX_SEC_INDEX 3 +#define XP_ECC_IER_RX_SEC_WIDTH 1 +#define XP_ECC_IER_TX_DED_INDEX 4 +#define XP_ECC_IER_TX_DED_WIDTH 1 +#define XP_ECC_IER_TX_SEC_INDEX 5 +#define XP_ECC_IER_TX_SEC_WIDTH 1 +#define XP_ECC_ISR_DESC_DED_INDEX 0 +#define XP_ECC_ISR_DESC_DED_WIDTH 1 +#define XP_ECC_ISR_DESC_SEC_INDEX 1 +#define XP_ECC_ISR_DESC_SEC_WIDTH 1 +#define XP_ECC_ISR_RX_DED_INDEX 2 +#define XP_ECC_ISR_RX_DED_WIDTH 1 +#define XP_ECC_ISR_RX_SEC_INDEX 3 +#define XP_ECC_ISR_RX_SEC_WIDTH 1 +#define XP_ECC_ISR_TX_DED_INDEX 4 +#define XP_ECC_ISR_TX_DED_WIDTH 1 +#define XP_ECC_ISR_TX_SEC_INDEX 5 +#define XP_ECC_ISR_TX_SEC_WIDTH 1 #define XP_MAC_ADDR_HI_VALID_INDEX 31 #define XP_MAC_ADDR_HI_VALID_WIDTH 1 #define XP_PROP_0_CONN_TYPE_INDEX 28 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index ff7f5ab..78a2063 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -724,6 +724,65 @@ static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata) XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xffffffff); } +static void xgbe_enable_ecc_interrupts(struct xgbe_prv_data *pdata) +{ + unsigned int ecc_isr, ecc_ier = 0; + + if (!pdata->vdata->ecc_support) + return; + + /* Clear all the interrupts which are set */ + ecc_isr = XP_IOREAD(pdata, XP_ECC_ISR); + XP_IOWRITE(pdata, XP_ECC_ISR, ecc_isr); + + /* Enable ECC interrupts */ + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_DED, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_SEC, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_DED, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_SEC, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_DED, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_SEC, 1); + + XP_IOWRITE(pdata, XP_ECC_IER, ecc_ier); +} + +static void xgbe_disable_ecc_ded(struct xgbe_prv_data *pdata) +{ + unsigned int ecc_ier; + + ecc_ier = XP_IOREAD(pdata, XP_ECC_IER); + + /* Disable ECC DED interrupts */ + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_DED, 0); + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_DED, 0); + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_DED, 0); + + XP_IOWRITE(pdata, XP_ECC_IER, ecc_ier); +} + +static void xgbe_disable_ecc_sec(struct xgbe_prv_data *pdata, + enum xgbe_ecc_sec sec) +{ + unsigned int ecc_ier; + + ecc_ier = XP_IOREAD(pdata, XP_ECC_IER); + + /* Disable ECC SEC interrupt */ + switch (sec) { + case XGBE_ECC_SEC_TX: + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_SEC, 0); + break; + case XGBE_ECC_SEC_RX: + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_SEC, 0); + break; + case XGBE_ECC_SEC_DESC: + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_SEC, 0); + break; + } + + XP_IOWRITE(pdata, XP_ECC_IER, ecc_ier); +} + static int xgbe_set_speed(struct xgbe_prv_data *pdata, int speed) { unsigned int ss; @@ -3294,6 +3353,11 @@ static int xgbe_init(struct xgbe_prv_data *pdata) xgbe_config_mmc(pdata); xgbe_enable_mac_interrupts(pdata); + /* + * Initialize ECC related features + */ + xgbe_enable_ecc_interrupts(pdata); + DBGPR("<--xgbe_init\n"); return 0; @@ -3399,5 +3463,9 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if) hw_if->set_rss_hash_key = xgbe_set_rss_hash_key; hw_if->set_rss_lookup_table = xgbe_set_rss_lookup_table; + /* For ECC */ + hw_if->disable_ecc_ded = xgbe_disable_ecc_ded; + hw_if->disable_ecc_sec = xgbe_disable_ecc_sec; + DBGPR("<--xgbe_init_function_ptrs\n"); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 490fdb5..fc3b703 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -114,6 +114,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -126,8 +127,35 @@ #include "xgbe.h" #include "xgbe-common.h" +static unsigned int ecc_sec_info_threshold = 10; +static unsigned int ecc_sec_warn_threshold = 10000; +static unsigned int ecc_sec_period = 600; +static unsigned int ecc_ded_threshold = 2; +static unsigned int ecc_ded_period = 600; + +#ifdef CONFIG_AMD_XGBE_HAVE_ECC +/* Only expose the ECC parameters if supported */ +module_param(ecc_sec_info_threshold, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(ecc_sec_info_threshold, + " ECC corrected error informational threshold setting"); + +module_param(ecc_sec_warn_threshold, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(ecc_sec_warn_threshold, + " ECC corrected error warning threshold setting"); + +module_param(ecc_sec_period, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(ecc_sec_period, " ECC corrected error period (in seconds)"); + +module_param(ecc_ded_threshold, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(ecc_ded_threshold, " ECC detected error threshold setting"); + +module_param(ecc_ded_period, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(ecc_ded_period, " ECC detected error period (in seconds)"); +#endif + static int xgbe_one_poll(struct napi_struct *, int); static int xgbe_all_poll(struct napi_struct *, int); +static void xgbe_stop(struct xgbe_prv_data *); static int xgbe_alloc_channels(struct xgbe_prv_data *pdata) { @@ -308,6 +336,107 @@ static void xgbe_disable_rx_tx_ints(struct xgbe_prv_data *pdata) xgbe_disable_rx_tx_int(pdata, channel); } +static bool xgbe_ecc_sec(struct xgbe_prv_data *pdata, unsigned long *period, + unsigned int *count, const char *area) +{ + if (time_before(jiffies, *period)) { + (*count)++; + } else { + *period = jiffies + (ecc_sec_period * HZ); + *count = 1; + } + + if (*count > ecc_sec_info_threshold) + dev_warn_once(pdata->dev, + "%s ECC corrected errors exceed informational threshold\n", + area); + + if (*count > ecc_sec_warn_threshold) { + dev_warn_once(pdata->dev, + "%s ECC corrected errors exceed warning threshold\n", + area); + return true; + } + + return false; +} + +static bool xgbe_ecc_ded(struct xgbe_prv_data *pdata, unsigned long *period, + unsigned int *count, const char *area) +{ + if (time_before(jiffies, *period)) { + (*count)++; + } else { + *period = jiffies + (ecc_ded_period * HZ); + *count = 1; + } + + if (*count > ecc_ded_threshold) { + netdev_alert(pdata->netdev, + "%s ECC detected errors exceed threshold\n", + area); + return true; + } + + return false; +} + +static irqreturn_t xgbe_ecc_isr(int irq, void *data) +{ + struct xgbe_prv_data *pdata = data; + unsigned int ecc_isr; + bool stop = false; + + /* Mask status with only the interrupts we care about */ + ecc_isr = XP_IOREAD(pdata, XP_ECC_ISR); + ecc_isr &= XP_IOREAD(pdata, XP_ECC_IER); + netif_dbg(pdata, intr, pdata->netdev, "ECC_ISR=%#010x\n", ecc_isr); + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, TX_DED)) { + stop |= xgbe_ecc_ded(pdata, &pdata->tx_ded_period, + &pdata->tx_ded_count, "TX fifo"); + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, RX_DED)) { + stop |= xgbe_ecc_ded(pdata, &pdata->rx_ded_period, + &pdata->rx_ded_count, "RX fifo"); + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, DESC_DED)) { + stop |= xgbe_ecc_ded(pdata, &pdata->desc_ded_period, + &pdata->desc_ded_count, + "descriptor cache"); + } + + if (stop) { + pdata->hw_if.disable_ecc_ded(pdata); + schedule_work(&pdata->stopdev_work); + goto out; + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, TX_SEC)) { + if (xgbe_ecc_sec(pdata, &pdata->tx_sec_period, + &pdata->tx_sec_count, "TX fifo")) + pdata->hw_if.disable_ecc_sec(pdata, XGBE_ECC_SEC_TX); + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, RX_SEC)) + if (xgbe_ecc_sec(pdata, &pdata->rx_sec_period, + &pdata->rx_sec_count, "RX fifo")) + pdata->hw_if.disable_ecc_sec(pdata, XGBE_ECC_SEC_RX); + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, DESC_SEC)) + if (xgbe_ecc_sec(pdata, &pdata->desc_sec_period, + &pdata->desc_sec_count, "descriptor cache")) + pdata->hw_if.disable_ecc_sec(pdata, XGBE_ECC_SEC_DESC); + +out: + /* Clear all ECC interrupts */ + XP_IOWRITE(pdata, XP_ECC_ISR, ecc_isr); + + return IRQ_HANDLED; +} + static irqreturn_t xgbe_isr(int irq, void *data) { struct xgbe_prv_data *pdata = data; @@ -397,6 +526,10 @@ static irqreturn_t xgbe_isr(int irq, void *data) if (pdata->dev_irq == pdata->an_irq) pdata->phy_if.an_isr(irq, pdata); + /* If there is not a separate ECC irq, handle it here */ + if (pdata->vdata->ecc_support && (pdata->dev_irq == pdata->ecc_irq)) + xgbe_ecc_isr(irq, pdata); + isr_done: return IRQ_HANDLED; } @@ -679,6 +812,16 @@ static int xgbe_request_irqs(struct xgbe_prv_data *pdata) return ret; } + if (pdata->vdata->ecc_support && (pdata->dev_irq != pdata->ecc_irq)) { + ret = devm_request_irq(pdata->dev, pdata->ecc_irq, xgbe_ecc_isr, + 0, pdata->ecc_name, pdata); + if (ret) { + netdev_alert(netdev, "error requesting ecc irq %d\n", + pdata->ecc_irq); + goto err_dev_irq; + } + } + if (!pdata->per_channel_irq) return 0; @@ -695,17 +838,21 @@ static int xgbe_request_irqs(struct xgbe_prv_data *pdata) if (ret) { netdev_alert(netdev, "error requesting irq %d\n", channel->dma_irq); - goto err_irq; + goto err_dma_irq; } } return 0; -err_irq: +err_dma_irq: /* Using an unsigned int, 'i' will go to UINT_MAX and exit */ for (i--, channel--; i < pdata->channel_count; i--, channel--) devm_free_irq(pdata->dev, channel->dma_irq, channel); + if (pdata->vdata->ecc_support && (pdata->dev_irq != pdata->ecc_irq)) + devm_free_irq(pdata->dev, pdata->ecc_irq, pdata); + +err_dev_irq: devm_free_irq(pdata->dev, pdata->dev_irq, pdata); return ret; @@ -718,6 +865,9 @@ static void xgbe_free_irqs(struct xgbe_prv_data *pdata) devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + if (pdata->vdata->ecc_support && (pdata->dev_irq != pdata->ecc_irq)) + devm_free_irq(pdata->dev, pdata->ecc_irq, pdata); + if (!pdata->per_channel_irq) return; @@ -919,6 +1069,8 @@ static int xgbe_start(struct xgbe_prv_data *pdata) xgbe_start_timers(pdata); queue_work(pdata->dev_workqueue, &pdata->service_work); + clear_bit(XGBE_STOPPED, &pdata->dev_state); + DBGPR("<--xgbe_start\n"); return 0; @@ -945,6 +1097,9 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) DBGPR("-->xgbe_stop\n"); + if (test_bit(XGBE_STOPPED, &pdata->dev_state)) + return; + netif_tx_stop_all_queues(netdev); xgbe_stop_timers(pdata); @@ -970,9 +1125,29 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) netdev_tx_reset_queue(txq); } + set_bit(XGBE_STOPPED, &pdata->dev_state); + DBGPR("<--xgbe_stop\n"); } +static void xgbe_stopdev(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + stopdev_work); + + rtnl_lock(); + + xgbe_stop(pdata); + + xgbe_free_tx_data(pdata); + xgbe_free_rx_data(pdata); + + rtnl_unlock(); + + netdev_alert(pdata->netdev, "device stopped\n"); +} + static void xgbe_restart_dev(struct xgbe_prv_data *pdata) { DBGPR("-->xgbe_restart_dev\n"); @@ -1355,6 +1530,7 @@ static int xgbe_open(struct net_device *netdev) INIT_WORK(&pdata->service_work, xgbe_service); INIT_WORK(&pdata->restart_work, xgbe_restart); + INIT_WORK(&pdata->stopdev_work, xgbe_stopdev); INIT_WORK(&pdata->tx_tstamp_work, xgbe_tx_tstamp); xgbe_init_timers(pdata); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 7655753..b16b7b6 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -190,6 +190,7 @@ struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev) pdata->msg_enable = netif_msg_init(debug, default_msg_level); set_bit(XGBE_DOWN, &pdata->dev_state); + set_bit(XGBE_STOPPED, &pdata->dev_state); return pdata; } @@ -263,6 +264,14 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) netdev->base_addr = (unsigned long)pdata->xgmac_regs; memcpy(netdev->dev_addr, pdata->mac_addr, netdev->addr_len); + /* Initialize ECC timestamps */ + pdata->tx_sec_period = jiffies; + pdata->tx_ded_period = jiffies; + pdata->rx_sec_period = jiffies; + pdata->rx_ded_period = jiffies; + pdata->desc_sec_period = jiffies; + pdata->desc_ded_period = jiffies; + /* Issue software reset to device */ pdata->hw_if.exit(pdata); @@ -384,6 +393,10 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) snprintf(pdata->an_name, sizeof(pdata->an_name) - 1, "%s-pcs", netdev_name(netdev)); + /* Create the ECC name based on netdev name */ + snprintf(pdata->ecc_name, sizeof(pdata->ecc_name) - 1, "%s-ecc", + netdev_name(netdev)); + /* Create workqueues */ pdata->dev_workqueue = create_singlethread_workqueue(netdev_name(netdev)); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index 61eb6613..798a65e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -482,6 +482,7 @@ static const struct xgbe_version_data xgbe_v2a = { .tx_max_fifo_size = 229376, .rx_max_fifo_size = 229376, .tx_tstamp_workaround = 1, + .ecc_support = 1, }; static const struct xgbe_version_data xgbe_v2b = { @@ -491,6 +492,7 @@ static const struct xgbe_version_data xgbe_v2b = { .tx_max_fifo_size = 65536, .rx_max_fifo_size = 65536, .tx_tstamp_workaround = 1, + .ecc_support = 1, }; static const struct pci_device_id xgbe_pci_table[] = { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 381144b..2f0b0b4 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -285,6 +285,9 @@ #define XGBE_SGMII_AN_LINK_SPEED_1000 0x08 #define XGBE_SGMII_AN_LINK_DUPLEX BIT(4) +/* ECC correctable error notification window (seconds) */ +#define XGBE_ECC_LIMIT 60 + struct xgbe_prv_data; struct xgbe_packet_data { @@ -461,6 +464,7 @@ enum xgbe_state { XGBE_DOWN, XGBE_LINK_INIT, XGBE_LINK_ERR, + XGBE_STOPPED, }; enum xgbe_int { @@ -480,6 +484,12 @@ enum xgbe_int_state { XGMAC_INT_STATE_RESTORE, }; +enum xgbe_ecc_sec { + XGBE_ECC_SEC_TX, + XGBE_ECC_SEC_RX, + XGBE_ECC_SEC_DESC, +}; + enum xgbe_speed { XGBE_SPEED_1000 = 0, XGBE_SPEED_2500, @@ -694,6 +704,10 @@ struct xgbe_hw_if { int (*disable_rss)(struct xgbe_prv_data *); int (*set_rss_hash_key)(struct xgbe_prv_data *, const u8 *); int (*set_rss_lookup_table)(struct xgbe_prv_data *, const u32 *); + + /* For ECC */ + void (*disable_ecc_ded)(struct xgbe_prv_data *); + void (*disable_ecc_sec)(struct xgbe_prv_data *, enum xgbe_ecc_sec); }; /* This structure represents implementation specific routines for an @@ -827,6 +841,7 @@ struct xgbe_version_data { unsigned int tx_max_fifo_size; unsigned int rx_max_fifo_size; unsigned int tx_tstamp_workaround; + unsigned int ecc_support; }; struct xgbe_prv_data { @@ -868,6 +883,21 @@ struct xgbe_prv_data { /* Flags representing xgbe_state */ unsigned long dev_state; + /* ECC support */ + unsigned long tx_sec_period; + unsigned long tx_ded_period; + unsigned long rx_sec_period; + unsigned long rx_ded_period; + unsigned long desc_sec_period; + unsigned long desc_ded_period; + + unsigned int tx_sec_count; + unsigned int tx_ded_count; + unsigned int rx_sec_count; + unsigned int rx_ded_count; + unsigned int desc_ded_count; + unsigned int desc_sec_count; + struct msix_entry *msix_entries; int dev_irq; int ecc_irq; @@ -880,6 +910,8 @@ struct xgbe_prv_data { unsigned int channel_irq_count; unsigned int channel_irq_mode; + char ecc_name[IFNAMSIZ + 32]; + struct xgbe_hw_if hw_if; struct xgbe_phy_if phy_if; struct xgbe_desc_if desc_if; @@ -990,8 +1022,9 @@ struct xgbe_prv_data { /* Hardware features of the device */ struct xgbe_hw_features hw_feat; - /* Device restart work structure */ + /* Device work structures */ struct work_struct restart_work; + struct work_struct stopdev_work; /* Keeps track of power mode */ unsigned int power_down; -- cgit v1.1 From 5ab1dcd58597c04f6d50980c3d5f3c2518301b31 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:10:36 -0600 Subject: amd-xgbe: Add I2C support for sideband communication Add support to initialize and use the I2C controller within the hardware in order to perform sideband communication, e.g. determine the SFP media type that is installed. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/Makefile | 2 +- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 92 +++++ drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c | 76 +++++ drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 4 + drivers/net/ethernet/amd/xgbe/xgbe-i2c.c | 492 +++++++++++++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe-main.c | 7 + drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 2 + drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 15 + drivers/net/ethernet/amd/xgbe/xgbe.h | 64 ++++ 9 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-i2c.c diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile index d1ce1c1..0dea8f5 100644 --- a/drivers/net/ethernet/amd/xgbe/Makefile +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ xgbe-ptp.o \ - xgbe-phy-v1.o xgbe-phy-v2.o \ + xgbe-i2c.o xgbe-phy-v1.o xgbe-phy-v2.o \ xgbe-platform.o amd-xgbe-$(CONFIG_PCI) += xgbe-pci.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index f7527cd..3b81b83 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -1000,6 +1000,65 @@ #define XP_PROP_2_TX_FIFO_SIZE_INDEX 0 #define XP_PROP_2_TX_FIFO_SIZE_WIDTH 16 +/* I2C Control register offsets */ +#define IC_CON 0x0000 +#define IC_TAR 0x0004 +#define IC_DATA_CMD 0x0010 +#define IC_INTR_STAT 0x002c +#define IC_INTR_MASK 0x0030 +#define IC_RAW_INTR_STAT 0x0034 +#define IC_CLR_INTR 0x0040 +#define IC_CLR_TX_ABRT 0x0054 +#define IC_CLR_STOP_DET 0x0060 +#define IC_ENABLE 0x006c +#define IC_TXFLR 0x0074 +#define IC_RXFLR 0x0078 +#define IC_TX_ABRT_SOURCE 0x0080 +#define IC_ENABLE_STATUS 0x009c +#define IC_COMP_PARAM_1 0x00f4 + +/* I2C Control register entry bit positions and sizes */ +#define IC_COMP_PARAM_1_MAX_SPEED_MODE_INDEX 2 +#define IC_COMP_PARAM_1_MAX_SPEED_MODE_WIDTH 2 +#define IC_COMP_PARAM_1_RX_BUFFER_DEPTH_INDEX 8 +#define IC_COMP_PARAM_1_RX_BUFFER_DEPTH_WIDTH 8 +#define IC_COMP_PARAM_1_TX_BUFFER_DEPTH_INDEX 16 +#define IC_COMP_PARAM_1_TX_BUFFER_DEPTH_WIDTH 8 +#define IC_CON_MASTER_MODE_INDEX 0 +#define IC_CON_MASTER_MODE_WIDTH 1 +#define IC_CON_RESTART_EN_INDEX 5 +#define IC_CON_RESTART_EN_WIDTH 1 +#define IC_CON_RX_FIFO_FULL_HOLD_INDEX 9 +#define IC_CON_RX_FIFO_FULL_HOLD_WIDTH 1 +#define IC_CON_SLAVE_DISABLE_INDEX 6 +#define IC_CON_SLAVE_DISABLE_WIDTH 1 +#define IC_CON_SPEED_INDEX 1 +#define IC_CON_SPEED_WIDTH 2 +#define IC_DATA_CMD_CMD_INDEX 8 +#define IC_DATA_CMD_CMD_WIDTH 1 +#define IC_DATA_CMD_STOP_INDEX 9 +#define IC_DATA_CMD_STOP_WIDTH 1 +#define IC_ENABLE_ABORT_INDEX 1 +#define IC_ENABLE_ABORT_WIDTH 1 +#define IC_ENABLE_EN_INDEX 0 +#define IC_ENABLE_EN_WIDTH 1 +#define IC_ENABLE_STATUS_EN_INDEX 0 +#define IC_ENABLE_STATUS_EN_WIDTH 1 +#define IC_INTR_MASK_TX_EMPTY_INDEX 4 +#define IC_INTR_MASK_TX_EMPTY_WIDTH 1 +#define IC_RAW_INTR_STAT_RX_FULL_INDEX 2 +#define IC_RAW_INTR_STAT_RX_FULL_WIDTH 1 +#define IC_RAW_INTR_STAT_STOP_DET_INDEX 9 +#define IC_RAW_INTR_STAT_STOP_DET_WIDTH 1 +#define IC_RAW_INTR_STAT_TX_ABRT_INDEX 6 +#define IC_RAW_INTR_STAT_TX_ABRT_WIDTH 1 +#define IC_RAW_INTR_STAT_TX_EMPTY_INDEX 4 +#define IC_RAW_INTR_STAT_TX_EMPTY_WIDTH 1 + +/* I2C Control register value */ +#define IC_TX_ABRT_7B_ADDR_NOACK 0x0001 +#define IC_TX_ABRT_ARB_LOST 0x1000 + /* Descriptor/Packet entry bit positions and sizes */ #define RX_PACKET_ERRORS_CRC_INDEX 2 #define RX_PACKET_ERRORS_CRC_WIDTH 1 @@ -1470,6 +1529,39 @@ do { \ } while (0) /* Macros for building, reading or writing register values or bits + * within the register values of I2C Control registers. + */ +#define XI2C_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XI2C_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +#define XI2C_IOREAD(_pdata, _reg) \ + ioread32((_pdata)->xi2c_regs + (_reg)) + +#define XI2C_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XI2C_IOREAD((_pdata), (_reg)), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XI2C_IOWRITE(_pdata, _reg, _val) \ + iowrite32((_val), (_pdata)->xi2c_regs + (_reg)) + +#define XI2C_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u32 reg_val = XI2C_IOREAD((_pdata), (_reg)); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XI2C_IOWRITE((_pdata), (_reg), reg_val); \ +} while (0) + +/* Macros for building, reading or writing register values or bits * using MDIO. Different from above because of the use of standardized * Linux include values. No shifting is performed with the bit * operations, everything works on mask values. diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 951b5ab..0c0140d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -376,6 +376,66 @@ static const struct file_operations xprop_reg_value_fops = { .write = xprop_reg_value_write, }; +static ssize_t xi2c_reg_addr_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xi2c_reg); +} + +static ssize_t xi2c_reg_addr_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_write(buffer, count, ppos, + &pdata->debugfs_xi2c_reg); +} + +static ssize_t xi2c_reg_value_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + + value = XI2C_IOREAD(pdata, pdata->debugfs_xi2c_reg); + + return xgbe_common_read(buffer, count, ppos, value); +} + +static ssize_t xi2c_reg_value_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + ssize_t len; + + len = xgbe_common_write(buffer, count, ppos, &value); + if (len < 0) + return len; + + XI2C_IOWRITE(pdata, pdata->debugfs_xi2c_reg, value); + + return len; +} + +static const struct file_operations xi2c_reg_addr_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xi2c_reg_addr_read, + .write = xi2c_reg_addr_write, +}; + +static const struct file_operations xi2c_reg_value_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xi2c_reg_value_read, + .write = xi2c_reg_value_write, +}; + void xgbe_debugfs_init(struct xgbe_prv_data *pdata) { struct dentry *pfile; @@ -443,6 +503,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata) "debugfs_create_file failed\n"); } + if (pdata->xi2c_regs) { + pfile = debugfs_create_file("xi2c_register", 0600, + pdata->xgbe_debugfs, pdata, + &xi2c_reg_addr_fops); + if (!pfile) + netdev_err(pdata->netdev, + "debugfs_create_file failed\n"); + + pfile = debugfs_create_file("xi2c_register_value", 0600, + pdata->xgbe_debugfs, pdata, + &xi2c_reg_value_fops); + if (!pfile) + netdev_err(pdata->netdev, + "debugfs_create_file failed\n"); + } + kfree(buf); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index fc3b703..7af358f 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -530,6 +530,10 @@ static irqreturn_t xgbe_isr(int irq, void *data) if (pdata->vdata->ecc_support && (pdata->dev_irq == pdata->ecc_irq)) xgbe_ecc_isr(irq, pdata); + /* If there is not a separate I2C irq, handle it here */ + if (pdata->vdata->i2c_support && (pdata->dev_irq == pdata->i2c_irq)) + pdata->i2c_if.i2c_isr(irq, pdata); + isr_done: return IRQ_HANDLED; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c new file mode 100644 index 0000000..0c7088a --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c @@ -0,0 +1,492 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_ABORT_COUNT 500 +#define XGBE_DISABLE_COUNT 1000 + +#define XGBE_STD_SPEED 1 + +#define XGBE_INTR_RX_FULL BIT(IC_RAW_INTR_STAT_RX_FULL_INDEX) +#define XGBE_INTR_TX_EMPTY BIT(IC_RAW_INTR_STAT_TX_EMPTY_INDEX) +#define XGBE_INTR_TX_ABRT BIT(IC_RAW_INTR_STAT_TX_ABRT_INDEX) +#define XGBE_INTR_STOP_DET BIT(IC_RAW_INTR_STAT_STOP_DET_INDEX) +#define XGBE_DEFAULT_INT_MASK (XGBE_INTR_RX_FULL | \ + XGBE_INTR_TX_EMPTY | \ + XGBE_INTR_TX_ABRT | \ + XGBE_INTR_STOP_DET) + +#define XGBE_I2C_READ BIT(8) +#define XGBE_I2C_STOP BIT(9) + +static int xgbe_i2c_abort(struct xgbe_prv_data *pdata) +{ + unsigned int wait = XGBE_ABORT_COUNT; + + /* Must be enabled to recognize the abort request */ + XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, 1); + + /* Issue the abort */ + XI2C_IOWRITE_BITS(pdata, IC_ENABLE, ABORT, 1); + + while (wait--) { + if (!XI2C_IOREAD_BITS(pdata, IC_ENABLE, ABORT)) + return 0; + + usleep_range(500, 600); + } + + return -EBUSY; +} + +static int xgbe_i2c_set_enable(struct xgbe_prv_data *pdata, bool enable) +{ + unsigned int wait = XGBE_DISABLE_COUNT; + unsigned int mode = enable ? 1 : 0; + + while (wait--) { + XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, mode); + if (XI2C_IOREAD_BITS(pdata, IC_ENABLE_STATUS, EN) == mode) + return 0; + + usleep_range(100, 110); + } + + return -EBUSY; +} + +static int xgbe_i2c_disable(struct xgbe_prv_data *pdata) +{ + unsigned int ret; + + ret = xgbe_i2c_set_enable(pdata, false); + if (ret) { + /* Disable failed, try an abort */ + ret = xgbe_i2c_abort(pdata); + if (ret) + return ret; + + /* Abort succeeded, try to disable again */ + ret = xgbe_i2c_set_enable(pdata, false); + } + + return ret; +} + +static int xgbe_i2c_enable(struct xgbe_prv_data *pdata) +{ + return xgbe_i2c_set_enable(pdata, true); +} + +static void xgbe_i2c_clear_all_interrupts(struct xgbe_prv_data *pdata) +{ + XI2C_IOREAD(pdata, IC_CLR_INTR); +} + +static void xgbe_i2c_disable_interrupts(struct xgbe_prv_data *pdata) +{ + XI2C_IOWRITE(pdata, IC_INTR_MASK, 0); +} + +static void xgbe_i2c_enable_interrupts(struct xgbe_prv_data *pdata) +{ + XI2C_IOWRITE(pdata, IC_INTR_MASK, XGBE_DEFAULT_INT_MASK); +} + +static void xgbe_i2c_write(struct xgbe_prv_data *pdata) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + unsigned int tx_slots; + unsigned int cmd; + + /* Configured to never receive Rx overflows, so fill up Tx fifo */ + tx_slots = pdata->i2c.tx_fifo_size - XI2C_IOREAD(pdata, IC_TXFLR); + while (tx_slots && state->tx_len) { + if (state->op->cmd == XGBE_I2C_CMD_READ) + cmd = XGBE_I2C_READ; + else + cmd = *state->tx_buf++; + + if (state->tx_len == 1) + XI2C_SET_BITS(cmd, IC_DATA_CMD, STOP, 1); + + XI2C_IOWRITE(pdata, IC_DATA_CMD, cmd); + + tx_slots--; + state->tx_len--; + } + + /* No more Tx operations, so ignore TX_EMPTY and return */ + if (!state->tx_len) + XI2C_IOWRITE_BITS(pdata, IC_INTR_MASK, TX_EMPTY, 0); +} + +static void xgbe_i2c_read(struct xgbe_prv_data *pdata) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + unsigned int rx_slots; + + /* Anything to be read? */ + if (state->op->cmd != XGBE_I2C_CMD_READ) + return; + + rx_slots = XI2C_IOREAD(pdata, IC_RXFLR); + while (rx_slots && state->rx_len) { + *state->rx_buf++ = XI2C_IOREAD(pdata, IC_DATA_CMD); + state->rx_len--; + rx_slots--; + } +} + +static void xgbe_i2c_clear_isr_interrupts(struct xgbe_prv_data *pdata, + unsigned int isr) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + + if (isr & XGBE_INTR_TX_ABRT) { + state->tx_abort_source = XI2C_IOREAD(pdata, IC_TX_ABRT_SOURCE); + XI2C_IOREAD(pdata, IC_CLR_TX_ABRT); + } + + if (isr & XGBE_INTR_STOP_DET) + XI2C_IOREAD(pdata, IC_CLR_STOP_DET); +} + +static irqreturn_t xgbe_i2c_isr(int irq, void *data) +{ + struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + unsigned int isr; + + isr = XI2C_IOREAD(pdata, IC_RAW_INTR_STAT); + netif_dbg(pdata, intr, pdata->netdev, + "I2C interrupt received: status=%#010x\n", isr); + + xgbe_i2c_clear_isr_interrupts(pdata, isr); + + if (isr & XGBE_INTR_TX_ABRT) { + netif_dbg(pdata, link, pdata->netdev, + "I2C TX_ABRT received (%#010x) for target %#04x\n", + state->tx_abort_source, state->op->target); + + xgbe_i2c_disable_interrupts(pdata); + + state->ret = -EIO; + goto out; + } + + /* Check for data in the Rx fifo */ + xgbe_i2c_read(pdata); + + /* Fill up the Tx fifo next */ + xgbe_i2c_write(pdata); + +out: + /* Complete on an error or STOP condition */ + if (state->ret || XI2C_GET_BITS(isr, IC_RAW_INTR_STAT, STOP_DET)) + complete(&pdata->i2c_complete); + + return IRQ_HANDLED; +} + +static void xgbe_i2c_set_mode(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + reg = XI2C_IOREAD(pdata, IC_CON); + XI2C_SET_BITS(reg, IC_CON, MASTER_MODE, 1); + XI2C_SET_BITS(reg, IC_CON, SLAVE_DISABLE, 1); + XI2C_SET_BITS(reg, IC_CON, RESTART_EN, 1); + XI2C_SET_BITS(reg, IC_CON, SPEED, XGBE_STD_SPEED); + XI2C_SET_BITS(reg, IC_CON, RX_FIFO_FULL_HOLD, 1); + XI2C_IOWRITE(pdata, IC_CON, reg); +} + +static void xgbe_i2c_get_features(struct xgbe_prv_data *pdata) +{ + struct xgbe_i2c *i2c = &pdata->i2c; + unsigned int reg; + + reg = XI2C_IOREAD(pdata, IC_COMP_PARAM_1); + i2c->max_speed_mode = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, + MAX_SPEED_MODE); + i2c->rx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, + RX_BUFFER_DEPTH); + i2c->tx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, + TX_BUFFER_DEPTH); + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "I2C features: %s=%u, %s=%u, %s=%u\n", + "MAX_SPEED_MODE", i2c->max_speed_mode, + "RX_BUFFER_DEPTH", i2c->rx_fifo_size, + "TX_BUFFER_DEPTH", i2c->tx_fifo_size); +} + +static void xgbe_i2c_set_target(struct xgbe_prv_data *pdata, unsigned int addr) +{ + XI2C_IOWRITE(pdata, IC_TAR, addr); +} + +static irqreturn_t xgbe_i2c_combined_isr(int irq, struct xgbe_prv_data *pdata) +{ + if (!XI2C_IOREAD(pdata, IC_RAW_INTR_STAT)) + return IRQ_HANDLED; + + return xgbe_i2c_isr(irq, pdata); +} + +static int xgbe_i2c_xfer(struct xgbe_prv_data *pdata, struct xgbe_i2c_op *op) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + int ret; + + mutex_lock(&pdata->i2c_mutex); + + reinit_completion(&pdata->i2c_complete); + + ret = xgbe_i2c_disable(pdata); + if (ret) { + netdev_err(pdata->netdev, "failed to disable i2c master\n"); + goto unlock; + } + + xgbe_i2c_set_target(pdata, op->target); + + memset(state, 0, sizeof(*state)); + state->op = op; + state->tx_len = op->len; + state->tx_buf = op->buf; + state->rx_len = op->len; + state->rx_buf = op->buf; + + xgbe_i2c_clear_all_interrupts(pdata); + ret = xgbe_i2c_enable(pdata); + if (ret) { + netdev_err(pdata->netdev, "failed to enable i2c master\n"); + goto unlock; + } + + /* Enabling the interrupts will cause the TX FIFO empty interrupt to + * fire and begin to process the command via the ISR. + */ + xgbe_i2c_enable_interrupts(pdata); + + if (!wait_for_completion_timeout(&pdata->i2c_complete, HZ)) { + netdev_err(pdata->netdev, "i2c operation timed out\n"); + ret = -ETIMEDOUT; + goto disable; + } + + ret = state->ret; + if (ret) { + if (state->tx_abort_source & IC_TX_ABRT_7B_ADDR_NOACK) + ret = -ENOTCONN; + else if (state->tx_abort_source & IC_TX_ABRT_ARB_LOST) + ret = -EAGAIN; + } + +disable: + xgbe_i2c_disable_interrupts(pdata); + xgbe_i2c_disable(pdata); + +unlock: + mutex_unlock(&pdata->i2c_mutex); + + return ret; +} + +static void xgbe_i2c_stop(struct xgbe_prv_data *pdata) +{ + if (!pdata->i2c.started) + return; + + netif_dbg(pdata, link, pdata->netdev, "stopping I2C\n"); + + pdata->i2c.started = 0; + + xgbe_i2c_disable_interrupts(pdata); + xgbe_i2c_disable(pdata); + xgbe_i2c_clear_all_interrupts(pdata); + + if (pdata->dev_irq != pdata->i2c_irq) + devm_free_irq(pdata->dev, pdata->i2c_irq, pdata); +} + +static int xgbe_i2c_start(struct xgbe_prv_data *pdata) +{ + int ret; + + if (pdata->i2c.started) + return 0; + + netif_dbg(pdata, link, pdata->netdev, "starting I2C\n"); + + /* If we have a separate I2C irq, enable it */ + if (pdata->dev_irq != pdata->i2c_irq) { + ret = devm_request_irq(pdata->dev, pdata->i2c_irq, + xgbe_i2c_isr, 0, pdata->i2c_name, + pdata); + if (ret) { + netdev_err(pdata->netdev, "i2c irq request failed\n"); + return ret; + } + } + + pdata->i2c.started = 1; + + return 0; +} + +static int xgbe_i2c_init(struct xgbe_prv_data *pdata) +{ + int ret; + + xgbe_i2c_disable_interrupts(pdata); + + ret = xgbe_i2c_disable(pdata); + if (ret) { + dev_err(pdata->dev, "failed to disable i2c master\n"); + return ret; + } + + xgbe_i2c_get_features(pdata); + + xgbe_i2c_set_mode(pdata); + + xgbe_i2c_clear_all_interrupts(pdata); + + return 0; +} + +void xgbe_init_function_ptrs_i2c(struct xgbe_i2c_if *i2c_if) +{ + i2c_if->i2c_init = xgbe_i2c_init; + + i2c_if->i2c_start = xgbe_i2c_start; + i2c_if->i2c_stop = xgbe_i2c_stop; + + i2c_if->i2c_xfer = xgbe_i2c_xfer; + + i2c_if->i2c_isr = xgbe_i2c_combined_isr; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index b16b7b6..385b7f6 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -161,6 +161,7 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata) { xgbe_init_function_ptrs_dev(&pdata->hw_if); xgbe_init_function_ptrs_phy(&pdata->phy_if); + xgbe_init_function_ptrs_i2c(&pdata->i2c_if); xgbe_init_function_ptrs_desc(&pdata->desc_if); pdata->vdata->init_function_ptrs_phy_impl(&pdata->phy_if); @@ -186,6 +187,8 @@ struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev) spin_lock_init(&pdata->xpcs_lock); mutex_init(&pdata->rss_mutex); spin_lock_init(&pdata->tstamp_lock); + mutex_init(&pdata->i2c_mutex); + init_completion(&pdata->i2c_complete); pdata->msg_enable = netif_msg_init(debug, default_msg_level); @@ -397,6 +400,10 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) snprintf(pdata->ecc_name, sizeof(pdata->ecc_name) - 1, "%s-ecc", netdev_name(netdev)); + /* Create the I2C name based on netdev name */ + snprintf(pdata->i2c_name, sizeof(pdata->i2c_name) - 1, "%s-i2c", + netdev_name(netdev)); + /* Create workqueues */ pdata->dev_workqueue = create_singlethread_workqueue(netdev_name(netdev)); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index 798a65e..e76b7f6 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -483,6 +483,7 @@ static const struct xgbe_version_data xgbe_v2a = { .rx_max_fifo_size = 229376, .tx_tstamp_workaround = 1, .ecc_support = 1, + .i2c_support = 1, }; static const struct xgbe_version_data xgbe_v2b = { @@ -493,6 +494,7 @@ static const struct xgbe_version_data xgbe_v2b = { .rx_max_fifo_size = 65536, .tx_tstamp_workaround = 1, .ecc_support = 1, + .i2c_support = 1, }; static const struct pci_device_id xgbe_pci_table[] = { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 7ae0abc..4bfe5c2 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -710,11 +710,20 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) { /* Power off the PHY */ xgbe_phy_power_off(pdata); + + /* Stop the I2C controller */ + pdata->i2c_if.i2c_stop(pdata); } static int xgbe_phy_start(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + /* Start the I2C controller */ + ret = pdata->i2c_if.i2c_start(pdata); + if (ret) + return ret; /* Start in highest supported mode */ xgbe_phy_set_mode(pdata, phy_data->start_mode); @@ -744,6 +753,7 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data; unsigned int reg; + int ret; /* Check if enabled */ if (!xgbe_phy_port_enabled(pdata)) { @@ -751,6 +761,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) return -ENODEV; } + /* Initialize the I2C controller */ + ret = pdata->i2c_if.i2c_init(pdata); + if (ret) + return ret; + phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL); if (!phy_data) return -ENOMEM; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 2f0b0b4..586154f 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -127,6 +127,7 @@ #include #include #include +#include #define XGBE_DRV_NAME "amd-xgbe" #define XGBE_DRV_VERSION "1.0.3" @@ -555,6 +556,43 @@ struct xgbe_phy { int rx_pause; }; +enum xgbe_i2c_cmd { + XGBE_I2C_CMD_READ = 0, + XGBE_I2C_CMD_WRITE, +}; + +struct xgbe_i2c_op { + enum xgbe_i2c_cmd cmd; + + unsigned int target; + + void *buf; + unsigned int len; +}; + +struct xgbe_i2c_op_state { + struct xgbe_i2c_op *op; + + unsigned int tx_len; + unsigned char *tx_buf; + + unsigned int rx_len; + unsigned char *rx_buf; + + unsigned int tx_abort_source; + + int ret; +}; + +struct xgbe_i2c { + unsigned int started; + unsigned int max_speed_mode; + unsigned int rx_fifo_size; + unsigned int tx_fifo_size; + + struct xgbe_i2c_op_state op_state; +}; + struct xgbe_mmc_stats { /* Tx Stats */ u64 txoctetcount_gb; @@ -777,6 +815,21 @@ struct xgbe_phy_if { struct xgbe_phy_impl_if phy_impl; }; +struct xgbe_i2c_if { + /* For initial I2C setup */ + int (*i2c_init)(struct xgbe_prv_data *); + + /* For I2C support when setting device up/down */ + int (*i2c_start)(struct xgbe_prv_data *); + void (*i2c_stop)(struct xgbe_prv_data *); + + /* For performing I2C operations */ + int (*i2c_xfer)(struct xgbe_prv_data *, struct xgbe_i2c_op *); + + /* For single interrupt support */ + irqreturn_t (*i2c_isr)(int, struct xgbe_prv_data *); +}; + struct xgbe_desc_if { int (*alloc_ring_resources)(struct xgbe_prv_data *); void (*free_ring_resources)(struct xgbe_prv_data *); @@ -842,6 +895,7 @@ struct xgbe_version_data { unsigned int rx_max_fifo_size; unsigned int tx_tstamp_workaround; unsigned int ecc_support; + unsigned int i2c_support; }; struct xgbe_prv_data { @@ -915,6 +969,7 @@ struct xgbe_prv_data { struct xgbe_hw_if hw_if; struct xgbe_phy_if phy_if; struct xgbe_desc_if desc_if; + struct xgbe_i2c_if i2c_if; /* AXI DMA settings */ unsigned int coherent; @@ -1065,6 +1120,12 @@ struct xgbe_prv_data { unsigned long an_start; enum xgbe_an_mode an_mode; + /* I2C support */ + struct xgbe_i2c i2c; + struct mutex i2c_mutex; + struct completion i2c_complete; + char i2c_name[IFNAMSIZ + 32]; + unsigned int lpm_ctrl; /* CTRL1 for resume */ #ifdef CONFIG_DEBUG_FS @@ -1076,6 +1137,8 @@ struct xgbe_prv_data { unsigned int debugfs_xpcs_reg; unsigned int debugfs_xprop_reg; + + unsigned int debugfs_xi2c_reg; #endif }; @@ -1101,6 +1164,7 @@ void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *); void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *); void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *); void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *); +void xgbe_init_function_ptrs_i2c(struct xgbe_i2c_if *); const struct net_device_ops *xgbe_get_netdev_ops(void); const struct ethtool_ops *xgbe_get_ethtool_ops(void); -- cgit v1.1 From 372788f964c95a6fa0f677c43d6153c27896ef42 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:10:46 -0600 Subject: net: phy: expose phy_aneg_done API for use by drivers Make phy_aneg_done() available to drivers so that the result of the auto-negotiation initiated by phy_start_aneg() can be determined. Remove the local implementation of phy_aneg_done() from the Aeroflex driver and use the phy library version. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/aeroflex/greth.c | 9 --------- drivers/net/phy/phy.c | 3 ++- include/linux/phy.h | 1 + 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index f8df824..93def92 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1290,15 +1290,6 @@ static int greth_mdio_probe(struct net_device *dev) return 0; } -static inline int phy_aneg_done(struct phy_device *phydev) -{ - int retval; - - retval = phy_read(phydev, MII_BMSR); - - return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE); -} - static int greth_mdio_init(struct greth_private *greth) { int ret; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 2f94c60..e6dd222 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -143,13 +143,14 @@ static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) * Returns > 0 on success or < 0 on error. 0 means that auto-negotiation * is still pending. */ -static inline int phy_aneg_done(struct phy_device *phydev) +int phy_aneg_done(struct phy_device *phydev) { if (phydev->drv->aneg_done) return phydev->drv->aneg_done(phydev); return genphy_aneg_done(phydev); } +EXPORT_SYMBOL(phy_aneg_done); /* A structure for mapping a particular speed and duplex * combination to a particular SUPPORTED and ADVERTISED value diff --git a/include/linux/phy.h b/include/linux/phy.h index e7e1fd3..9880d73 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -786,6 +786,7 @@ void phy_detach(struct phy_device *phydev); void phy_start(struct phy_device *phydev); void phy_stop(struct phy_device *phydev); int phy_start_aneg(struct phy_device *phydev); +int phy_aneg_done(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev); -- cgit v1.1 From abf0a1c2b26ad964d19b143ce46735e1b0667f29 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:10:58 -0600 Subject: amd-xgbe: Add support for SFP+ modules Add support for recognizing and using SFP+ modules directly. This includes using the I2C support to read and interpret the information returned from an SFP+ module and configuring things properly. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/Kconfig | 1 + drivers/net/ethernet/amd/xgbe/xgbe-common.h | 34 + drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 109 +- drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c | 12 +- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 1499 ++++++++++++++++++++++++++- drivers/net/ethernet/amd/xgbe/xgbe.h | 15 +- 6 files changed, 1652 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 930636c..7ab6efb 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -178,6 +178,7 @@ config AMD_XGBE select BITREVERSE select CRC32 select PTP_1588_CLOCK + select PHYLIB select AMD_XGBE_HAVE_ECC if X86 ---help--- This driver supports the AMD 10GbE Ethernet device found on an diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 3b81b83..b7140f9 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -929,6 +929,8 @@ #define XP_DRIVER_SCRATCH_0 0x0068 #define XP_DRIVER_SCRATCH_1 0x006c #define XP_INT_EN 0x0078 +#define XP_I2C_MUTEX 0x0080 +#define XP_MDIO_MUTEX 0x0084 /* MAC Control register entry bit positions and sizes */ #define XP_DRIVER_INT_REQ_REQUEST_INDEX 0 @@ -975,6 +977,12 @@ #define XP_ECC_ISR_TX_DED_WIDTH 1 #define XP_ECC_ISR_TX_SEC_INDEX 5 #define XP_ECC_ISR_TX_SEC_WIDTH 1 +#define XP_I2C_MUTEX_BUSY_INDEX 31 +#define XP_I2C_MUTEX_BUSY_WIDTH 1 +#define XP_I2C_MUTEX_ID_INDEX 29 +#define XP_I2C_MUTEX_ID_WIDTH 2 +#define XP_I2C_MUTEX_ACTIVE_INDEX 0 +#define XP_I2C_MUTEX_ACTIVE_WIDTH 1 #define XP_MAC_ADDR_HI_VALID_INDEX 31 #define XP_MAC_ADDR_HI_VALID_WIDTH 1 #define XP_PROP_0_CONN_TYPE_INDEX 28 @@ -999,6 +1007,24 @@ #define XP_PROP_2_RX_FIFO_SIZE_WIDTH 16 #define XP_PROP_2_TX_FIFO_SIZE_INDEX 0 #define XP_PROP_2_TX_FIFO_SIZE_WIDTH 16 +#define XP_PROP_3_GPIO_MASK_INDEX 28 +#define XP_PROP_3_GPIO_MASK_WIDTH 4 +#define XP_PROP_3_GPIO_MOD_ABS_INDEX 20 +#define XP_PROP_3_GPIO_MOD_ABS_WIDTH 4 +#define XP_PROP_3_GPIO_RATE_SELECT_INDEX 16 +#define XP_PROP_3_GPIO_RATE_SELECT_WIDTH 4 +#define XP_PROP_3_GPIO_RX_LOS_INDEX 24 +#define XP_PROP_3_GPIO_RX_LOS_WIDTH 4 +#define XP_PROP_3_GPIO_TX_FAULT_INDEX 12 +#define XP_PROP_3_GPIO_TX_FAULT_WIDTH 4 +#define XP_PROP_3_GPIO_ADDR_INDEX 8 +#define XP_PROP_3_GPIO_ADDR_WIDTH 3 +#define XP_PROP_4_MUX_ADDR_HI_INDEX 8 +#define XP_PROP_4_MUX_ADDR_HI_WIDTH 5 +#define XP_PROP_4_MUX_ADDR_LO_INDEX 0 +#define XP_PROP_4_MUX_ADDR_LO_WIDTH 3 +#define XP_PROP_4_MUX_CHAN_INDEX 4 +#define XP_PROP_4_MUX_CHAN_WIDTH 3 /* I2C Control register offsets */ #define IC_CON 0x0000 @@ -1235,6 +1261,14 @@ #define MDIO_VEND2_CTRL1_AN_RESTART BIT(9) #endif +#ifndef MDIO_VEND2_CTRL1_SS6 +#define MDIO_VEND2_CTRL1_SS6 BIT(6) +#endif + +#ifndef MDIO_VEND2_CTRL1_SS13 +#define MDIO_VEND2_CTRL1_SS13 BIT(13) +#endif + /* MDIO mask values */ #define XGBE_AN_CL73_INT_CMPLT BIT(0) #define XGBE_AN_CL73_INC_LINK BIT(1) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 6d99943..622675a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -252,6 +252,54 @@ static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata) pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_1000); } +static void xgbe_sfi_mode(struct xgbe_prv_data *pdata) +{ + /* Disable KR training */ + xgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 10G speed */ + pdata->hw_if.set_speed(pdata, SPEED_10000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SFI); +} + +static void xgbe_x_mode(struct xgbe_prv_data *pdata) +{ + /* Disable KR training */ + xgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_X); +} + +static void xgbe_sgmii_1000_mode(struct xgbe_prv_data *pdata) +{ + /* Disable KR training */ + xgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_1000); +} + +static void xgbe_sgmii_100_mode(struct xgbe_prv_data *pdata) +{ + /* Disable KR training */ + xgbe_an73_disable_kr_training(pdata); + + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_100); +} + static enum xgbe_mode xgbe_cur_mode(struct xgbe_prv_data *pdata) { return pdata->phy_if.phy_impl.cur_mode(pdata); @@ -275,6 +323,18 @@ static void xgbe_change_mode(struct xgbe_prv_data *pdata, case XGBE_MODE_KR: xgbe_kr_mode(pdata); break; + case XGBE_MODE_SGMII_100: + xgbe_sgmii_100_mode(pdata); + break; + case XGBE_MODE_SGMII_1000: + xgbe_sgmii_1000_mode(pdata); + break; + case XGBE_MODE_X: + xgbe_x_mode(pdata); + break; + case XGBE_MODE_SFI: + xgbe_sfi_mode(pdata); + break; case XGBE_MODE_UNKNOWN: break; default: @@ -972,6 +1032,8 @@ static const char *xgbe_phy_fc_string(struct xgbe_prv_data *pdata) static const char *xgbe_phy_speed_string(int speed) { switch (speed) { + case SPEED_100: + return "100Mbps"; case SPEED_1000: return "1Gbps"; case SPEED_2500: @@ -1057,6 +1119,10 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) case XGBE_MODE_KX_1000: case XGBE_MODE_KX_2500: case XGBE_MODE_KR: + case XGBE_MODE_SGMII_100: + case XGBE_MODE_SGMII_1000: + case XGBE_MODE_X: + case XGBE_MODE_SFI: break; case XGBE_MODE_UNKNOWN: default: @@ -1074,9 +1140,15 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) { + int ret; + set_bit(XGBE_LINK_INIT, &pdata->dev_state); pdata->link_check = jiffies; + ret = pdata->phy_if.phy_impl.an_config(pdata); + if (ret) + return ret; + if (pdata->phy.autoneg != AUTONEG_ENABLE) return xgbe_phy_config_fixed(pdata); @@ -1092,6 +1164,14 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) xgbe_set_mode(pdata, XGBE_MODE_KX_2500); } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { xgbe_set_mode(pdata, XGBE_MODE_KX_1000); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) { + xgbe_set_mode(pdata, XGBE_MODE_SFI); + } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) { + xgbe_set_mode(pdata, XGBE_MODE_X); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) { + xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) { + xgbe_set_mode(pdata, XGBE_MODE_SGMII_100); } else { enable_irq(pdata->an_irq); return -EINVAL; @@ -1167,13 +1247,19 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) mode = xgbe_phy_status_aneg(pdata); switch (mode) { + case XGBE_MODE_SGMII_100: + pdata->phy.speed = SPEED_100; + break; + case XGBE_MODE_X: case XGBE_MODE_KX_1000: + case XGBE_MODE_SGMII_1000: pdata->phy.speed = SPEED_1000; break; case XGBE_MODE_KX_2500: pdata->phy.speed = SPEED_2500; break; case XGBE_MODE_KR: + case XGBE_MODE_SFI: pdata->phy.speed = SPEED_10000; break; case XGBE_MODE_UNKNOWN: @@ -1189,6 +1275,7 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) static void xgbe_phy_status(struct xgbe_prv_data *pdata) { unsigned int link_aneg; + int an_restart; if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { netif_carrier_off(pdata->netdev); @@ -1199,7 +1286,13 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE); - pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata); + pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata, + &an_restart); + if (an_restart) { + xgbe_phy_config_aneg(pdata); + return; + } + if (pdata->phy.link) { if (link_aneg && !xgbe_phy_aneg_done(pdata)) { xgbe_check_link_timeout(pdata); @@ -1284,6 +1377,14 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) xgbe_kx_2500_mode(pdata); } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { xgbe_kx_1000_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) { + xgbe_sfi_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) { + xgbe_x_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) { + xgbe_sgmii_1000_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) { + xgbe_sgmii_100_mode(pdata); } else { ret = -EINVAL; goto err_irq; @@ -1367,10 +1468,16 @@ static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) { if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) return SPEED_10000; + else if (pdata->phy.advertising & ADVERTISED_10000baseT_Full) + return SPEED_10000; else if (pdata->phy.advertising & ADVERTISED_2500baseX_Full) return SPEED_2500; else if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full) return SPEED_1000; + else if (pdata->phy.advertising & ADVERTISED_1000baseT_Full) + return SPEED_1000; + else if (pdata->phy.advertising & ADVERTISED_100baseT_Full) + return SPEED_100; return SPEED_UNKNOWN; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c index 9c913a0..6c64d11 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -295,6 +295,12 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) return mode; } +static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for an configuration */ + return 0; +} + static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) { return XGBE_AN_MODE_CL73; @@ -607,10 +613,12 @@ static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) } } -static int xgbe_phy_link_status(struct xgbe_prv_data *pdata) +static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart) { unsigned int reg; + *an_restart = 0; + /* Link status is latched low, so read once to clear * and then read again to get current state */ @@ -821,6 +829,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if) phy_impl->an_mode = xgbe_phy_an_mode; + phy_impl->an_config = xgbe_phy_an_config; + phy_impl->an_outcome = xgbe_phy_an_outcome; phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 4bfe5c2..5d8cd8b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -115,6 +115,7 @@ */ #include +#include #include #include #include @@ -127,6 +128,22 @@ #define XGBE_PHY_PORT_SPEED_2500 BIT(2) #define XGBE_PHY_PORT_SPEED_10000 BIT(3) +#define XGBE_MUTEX_RELEASE 0x80000000 + +#define XGBE_SFP_DIRECT 7 + +/* I2C target addresses */ +#define XGBE_SFP_SERIAL_ID_ADDRESS 0x50 +#define XGBE_SFP_DIAG_INFO_ADDRESS 0x51 +#define XGBE_SFP_PHY_ADDRESS 0x56 +#define XGBE_GPIO_ADDRESS_PCA9555 0x20 + +/* SFP sideband signal indicators */ +#define XGBE_GPIO_NO_TX_FAULT BIT(0) +#define XGBE_GPIO_NO_RATE_SELECT BIT(1) +#define XGBE_GPIO_NO_MOD_ABSENT BIT(2) +#define XGBE_GPIO_NO_RX_LOS BIT(3) + /* Rate-change complete wait/retry count */ #define XGBE_RATECHANGE_COUNT 500 @@ -151,23 +168,1041 @@ enum xgbe_conn_type { XGBE_CONN_TYPE_MAX, }; -/* PHY related configuration information */ -struct xgbe_phy_data { - enum xgbe_port_mode port_mode; +/* SFP/SFP+ related definitions */ +enum xgbe_sfp_comm { + XGBE_SFP_COMM_DIRECT = 0, + XGBE_SFP_COMM_PCA9545, +}; + +enum xgbe_sfp_cable { + XGBE_SFP_CABLE_UNKNOWN = 0, + XGBE_SFP_CABLE_ACTIVE, + XGBE_SFP_CABLE_PASSIVE, +}; + +enum xgbe_sfp_base { + XGBE_SFP_BASE_UNKNOWN = 0, + XGBE_SFP_BASE_1000_T, + XGBE_SFP_BASE_1000_SX, + XGBE_SFP_BASE_1000_LX, + XGBE_SFP_BASE_1000_CX, + XGBE_SFP_BASE_10000_SR, + XGBE_SFP_BASE_10000_LR, + XGBE_SFP_BASE_10000_LRM, + XGBE_SFP_BASE_10000_ER, + XGBE_SFP_BASE_10000_CR, +}; + +enum xgbe_sfp_speed { + XGBE_SFP_SPEED_UNKNOWN = 0, + XGBE_SFP_SPEED_100_1000, + XGBE_SFP_SPEED_1000, + XGBE_SFP_SPEED_10000, +}; + +/* SFP Serial ID Base ID values relative to an offset of 0 */ +#define XGBE_SFP_BASE_ID 0 +#define XGBE_SFP_ID_SFP 0x03 + +#define XGBE_SFP_BASE_EXT_ID 1 +#define XGBE_SFP_EXT_ID_SFP 0x04 + +#define XGBE_SFP_BASE_10GBE_CC 3 +#define XGBE_SFP_BASE_10GBE_CC_SR BIT(4) +#define XGBE_SFP_BASE_10GBE_CC_LR BIT(5) +#define XGBE_SFP_BASE_10GBE_CC_LRM BIT(6) +#define XGBE_SFP_BASE_10GBE_CC_ER BIT(7) + +#define XGBE_SFP_BASE_1GBE_CC 6 +#define XGBE_SFP_BASE_1GBE_CC_SX BIT(0) +#define XGBE_SFP_BASE_1GBE_CC_LX BIT(1) +#define XGBE_SFP_BASE_1GBE_CC_CX BIT(2) +#define XGBE_SFP_BASE_1GBE_CC_T BIT(3) + +#define XGBE_SFP_BASE_CABLE 8 +#define XGBE_SFP_BASE_CABLE_PASSIVE BIT(2) +#define XGBE_SFP_BASE_CABLE_ACTIVE BIT(3) + +#define XGBE_SFP_BASE_BR 12 +#define XGBE_SFP_BASE_BR_1GBE_MIN 0x0a +#define XGBE_SFP_BASE_BR_1GBE_MAX 0x0d +#define XGBE_SFP_BASE_BR_10GBE_MIN 0x64 +#define XGBE_SFP_BASE_BR_10GBE_MAX 0x68 + +#define XGBE_SFP_BASE_CU_CABLE_LEN 18 + +#define XGBE_SFP_BASE_VENDOR_NAME 20 +#define XGBE_SFP_BASE_VENDOR_NAME_LEN 16 +#define XGBE_SFP_BASE_VENDOR_PN 40 +#define XGBE_SFP_BASE_VENDOR_PN_LEN 16 +#define XGBE_SFP_BASE_VENDOR_REV 56 +#define XGBE_SFP_BASE_VENDOR_REV_LEN 4 + +#define XGBE_SFP_BASE_CC 63 + +/* SFP Serial ID Extended ID values relative to an offset of 64 */ +#define XGBE_SFP_BASE_VENDOR_SN 4 +#define XGBE_SFP_BASE_VENDOR_SN_LEN 16 + +#define XGBE_SFP_EXTD_DIAG 28 +#define XGBE_SFP_EXTD_DIAG_ADDR_CHANGE BIT(2) + +#define XGBE_SFP_EXTD_SFF_8472 30 + +#define XGBE_SFP_EXTD_CC 31 + +struct xgbe_sfp_eeprom { + u8 base[64]; + u8 extd[32]; + u8 vendor[32]; +}; + +#define XGBE_BEL_FUSE_VENDOR "BEL-FUSE " +#define XGBE_BEL_FUSE_PARTNO "1GBT-SFP06 " + +struct xgbe_sfp_ascii { + union { + char vendor[XGBE_SFP_BASE_VENDOR_NAME_LEN + 1]; + char partno[XGBE_SFP_BASE_VENDOR_PN_LEN + 1]; + char rev[XGBE_SFP_BASE_VENDOR_REV_LEN + 1]; + char serno[XGBE_SFP_BASE_VENDOR_SN_LEN + 1]; + } u; +}; + +/* PHY related configuration information */ +struct xgbe_phy_data { + enum xgbe_port_mode port_mode; + + unsigned int port_id; + + unsigned int port_speeds; + + enum xgbe_conn_type conn_type; + + enum xgbe_mode cur_mode; + enum xgbe_mode start_mode; + + unsigned int rrc_count; + + unsigned int mdio_addr; + + unsigned int comm_owned; + + /* SFP Support */ + enum xgbe_sfp_comm sfp_comm; + unsigned int sfp_mux_address; + unsigned int sfp_mux_channel; + + unsigned int sfp_gpio_address; + unsigned int sfp_gpio_mask; + unsigned int sfp_gpio_rx_los; + unsigned int sfp_gpio_tx_fault; + unsigned int sfp_gpio_mod_absent; + unsigned int sfp_gpio_rate_select; + + unsigned int sfp_rx_los; + unsigned int sfp_tx_fault; + unsigned int sfp_mod_absent; + unsigned int sfp_diags; + unsigned int sfp_changed; + unsigned int sfp_phy_avail; + unsigned int sfp_cable_len; + enum xgbe_sfp_base sfp_base; + enum xgbe_sfp_cable sfp_cable; + enum xgbe_sfp_speed sfp_speed; + struct xgbe_sfp_eeprom sfp_eeprom; + + /* External PHY support */ + enum xgbe_mdio_mode phydev_mode; + struct mii_bus *mii; + struct phy_device *phydev; +}; + +/* I2C, MDIO and GPIO lines are muxed, so only one device at a time */ +static DEFINE_MUTEX(xgbe_phy_comm_lock); + +static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata); + +static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata, + struct xgbe_i2c_op *i2c_op) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* Be sure we own the bus */ + if (WARN_ON(!phy_data->comm_owned)) + return -EIO; + + return pdata->i2c_if.i2c_xfer(pdata, i2c_op); +} + +static int xgbe_phy_i2c_write(struct xgbe_prv_data *pdata, unsigned int target, + void *val, unsigned int val_len) +{ + struct xgbe_i2c_op i2c_op; + int retry, ret; + + retry = 1; +again: + /* Write the specfied register */ + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = target; + i2c_op.len = val_len; + i2c_op.buf = val; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if ((ret == -EAGAIN) && retry--) + goto again; + + return ret; +} + +static int xgbe_phy_i2c_read(struct xgbe_prv_data *pdata, unsigned int target, + void *reg, unsigned int reg_len, + void *val, unsigned int val_len) +{ + struct xgbe_i2c_op i2c_op; + int retry, ret; + + retry = 1; +again1: + /* Set the specified register to read */ + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = target; + i2c_op.len = reg_len; + i2c_op.buf = reg; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again1; + + return ret; + } + + retry = 1; +again2: + /* Read the specfied register */ + i2c_op.cmd = XGBE_I2C_CMD_READ; + i2c_op.target = target; + i2c_op.len = val_len; + i2c_op.buf = val; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if ((ret == -EAGAIN) && retry--) + goto again2; + + return ret; +} + +static int xgbe_phy_sfp_put_mux(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_i2c_op i2c_op; + u8 mux_channel; + + if (phy_data->sfp_comm == XGBE_SFP_COMM_DIRECT) + return 0; + + /* Select no mux channels */ + mux_channel = 0; + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->sfp_mux_address; + i2c_op.len = sizeof(mux_channel); + i2c_op.buf = &mux_channel; + + return xgbe_phy_i2c_xfer(pdata, &i2c_op); +} + +static int xgbe_phy_sfp_get_mux(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_i2c_op i2c_op; + u8 mux_channel; + + if (phy_data->sfp_comm == XGBE_SFP_COMM_DIRECT) + return 0; + + /* Select desired mux channel */ + mux_channel = 1 << phy_data->sfp_mux_channel; + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->sfp_mux_address; + i2c_op.len = sizeof(mux_channel); + i2c_op.buf = &mux_channel; + + return xgbe_phy_i2c_xfer(pdata, &i2c_op); +} + +static void xgbe_phy_put_comm_ownership(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + phy_data->comm_owned = 0; + + mutex_unlock(&xgbe_phy_comm_lock); +} + +static int xgbe_phy_get_comm_ownership(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned long timeout; + unsigned int mutex_id; + + if (phy_data->comm_owned) + return 0; + + /* The I2C and MDIO/GPIO bus is multiplexed between multiple devices, + * the driver needs to take the software mutex and then the hardware + * mutexes before being able to use the busses. + */ + mutex_lock(&xgbe_phy_comm_lock); + + /* Clear the mutexes */ + XP_IOWRITE(pdata, XP_I2C_MUTEX, XGBE_MUTEX_RELEASE); + XP_IOWRITE(pdata, XP_MDIO_MUTEX, XGBE_MUTEX_RELEASE); + + /* Mutex formats are the same for I2C and MDIO/GPIO */ + mutex_id = 0; + XP_SET_BITS(mutex_id, XP_I2C_MUTEX, ID, phy_data->port_id); + XP_SET_BITS(mutex_id, XP_I2C_MUTEX, ACTIVE, 1); + + timeout = jiffies + (5 * HZ); + while (time_before(jiffies, timeout)) { + /* Must be all zeroes in order to obtain the mutex */ + if (XP_IOREAD(pdata, XP_I2C_MUTEX) || + XP_IOREAD(pdata, XP_MDIO_MUTEX)) { + usleep_range(100, 200); + continue; + } + + /* Obtain the mutex */ + XP_IOWRITE(pdata, XP_I2C_MUTEX, mutex_id); + XP_IOWRITE(pdata, XP_MDIO_MUTEX, mutex_id); + + phy_data->comm_owned = 1; + return 0; + } + + mutex_unlock(&xgbe_phy_comm_lock); + + netdev_err(pdata->netdev, "unable to obtain hardware mutexes\n"); + + return -ETIMEDOUT; +} + +static int xgbe_phy_i2c_mii_write(struct xgbe_prv_data *pdata, int reg, u16 val) +{ + __be16 *mii_val; + u8 mii_data[3]; + int ret; + + ret = xgbe_phy_sfp_get_mux(pdata); + if (ret) + return ret; + + mii_data[0] = reg & 0xff; + mii_val = (__be16 *)&mii_data[1]; + *mii_val = cpu_to_be16(val); + + ret = xgbe_phy_i2c_write(pdata, XGBE_SFP_PHY_ADDRESS, + mii_data, sizeof(mii_data)); + + xgbe_phy_sfp_put_mux(pdata); + + return ret; +} + +static int xgbe_phy_mii_write(struct mii_bus *mii, int addr, int reg, u16 val) +{ + struct xgbe_prv_data *pdata = mii->priv; + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return ret; + + if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) + ret = xgbe_phy_i2c_mii_write(pdata, reg, val); + else + ret = -ENOTSUPP; + + xgbe_phy_put_comm_ownership(pdata); + + return ret; +} + +static int xgbe_phy_i2c_mii_read(struct xgbe_prv_data *pdata, int reg) +{ + __be16 mii_val; + u8 mii_reg; + int ret; + + ret = xgbe_phy_sfp_get_mux(pdata); + if (ret) + return ret; + + mii_reg = reg; + ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_PHY_ADDRESS, + &mii_reg, sizeof(mii_reg), + &mii_val, sizeof(mii_val)); + if (!ret) + ret = be16_to_cpu(mii_val); + + xgbe_phy_sfp_put_mux(pdata); + + return ret; +} + +static int xgbe_phy_mii_read(struct mii_bus *mii, int addr, int reg) +{ + struct xgbe_prv_data *pdata = mii->priv; + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return ret; + + if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) + ret = xgbe_phy_i2c_mii_read(pdata, reg); + else + ret = -ENOTSUPP; + + xgbe_phy_put_comm_ownership(pdata); + + return ret; +} + +static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (phy_data->sfp_mod_absent) { + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.advertising = pdata->phy.supported; + } + + pdata->phy.advertising &= ~ADVERTISED_Autoneg; + pdata->phy.advertising &= ~ADVERTISED_TP; + pdata->phy.advertising &= ~ADVERTISED_FIBRE; + pdata->phy.advertising &= ~ADVERTISED_100baseT_Full; + pdata->phy.advertising &= ~ADVERTISED_1000baseT_Full; + pdata->phy.advertising &= ~ADVERTISED_10000baseT_Full; + pdata->phy.advertising &= ~ADVERTISED_10000baseR_FEC; + + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.advertising |= ADVERTISED_Autoneg; + break; + case XGBE_SFP_BASE_10000_SR: + case XGBE_SFP_BASE_10000_LR: + case XGBE_SFP_BASE_10000_LRM: + case XGBE_SFP_BASE_10000_ER: + case XGBE_SFP_BASE_10000_CR: + default: + pdata->phy.speed = SPEED_10000; + pdata->phy.duplex = DUPLEX_FULL; + pdata->phy.autoneg = AUTONEG_DISABLE; + break; + } + + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + case XGBE_SFP_BASE_1000_CX: + case XGBE_SFP_BASE_10000_CR: + pdata->phy.advertising |= ADVERTISED_TP; + break; + default: + pdata->phy.advertising |= ADVERTISED_FIBRE; + } + + switch (phy_data->sfp_speed) { + case XGBE_SFP_SPEED_100_1000: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) + pdata->phy.advertising |= ADVERTISED_100baseT_Full; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + pdata->phy.advertising |= ADVERTISED_1000baseT_Full; + break; + case XGBE_SFP_SPEED_1000: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + pdata->phy.advertising |= ADVERTISED_1000baseT_Full; + break; + case XGBE_SFP_SPEED_10000: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) + pdata->phy.advertising |= ADVERTISED_10000baseT_Full; + break; + default: + /* Choose the fastest supported speed */ + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) + pdata->phy.advertising |= ADVERTISED_10000baseT_Full; + else if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + pdata->phy.advertising |= ADVERTISED_1000baseT_Full; + else if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) + pdata->phy.advertising |= ADVERTISED_100baseT_Full; + } +} + +static bool xgbe_phy_sfp_bit_rate(struct xgbe_sfp_eeprom *sfp_eeprom, + enum xgbe_sfp_speed sfp_speed) +{ + u8 *sfp_base, min, max; + + sfp_base = sfp_eeprom->base; + + switch (sfp_speed) { + case XGBE_SFP_SPEED_1000: + min = XGBE_SFP_BASE_BR_1GBE_MIN; + max = XGBE_SFP_BASE_BR_1GBE_MAX; + break; + case XGBE_SFP_SPEED_10000: + min = XGBE_SFP_BASE_BR_10GBE_MIN; + max = XGBE_SFP_BASE_BR_10GBE_MAX; + break; + default: + return false; + } + + return ((sfp_base[XGBE_SFP_BASE_BR] >= min) && + (sfp_base[XGBE_SFP_BASE_BR] <= max)); +} + +static void xgbe_phy_free_phy_device(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (phy_data->phydev) { + phy_detach(phy_data->phydev); + phy_device_remove(phy_data->phydev); + phy_device_free(phy_data->phydev); + phy_data->phydev = NULL; + } +} + +static bool xgbe_phy_finisar_phy_quirks(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int phy_id = phy_data->phydev->phy_id; + + if ((phy_id & 0xfffffff0) != 0x01ff0cc0) + return false; + + /* Enable Base-T AN */ + phy_write(phy_data->phydev, 0x16, 0x0001); + phy_write(phy_data->phydev, 0x00, 0x9140); + phy_write(phy_data->phydev, 0x16, 0x0000); + + /* Enable SGMII at 100Base-T/1000Base-T Full Duplex */ + phy_write(phy_data->phydev, 0x1b, 0x9084); + phy_write(phy_data->phydev, 0x09, 0x0e00); + phy_write(phy_data->phydev, 0x00, 0x8140); + phy_write(phy_data->phydev, 0x04, 0x0d01); + phy_write(phy_data->phydev, 0x00, 0x9140); + + phy_data->phydev->supported = PHY_GBIT_FEATURES; + phy_data->phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + phy_data->phydev->advertising = phy_data->phydev->supported; + + netif_dbg(pdata, drv, pdata->netdev, + "Finisar PHY quirk in place\n"); + + return true; +} + +static void xgbe_phy_external_phy_quirks(struct xgbe_prv_data *pdata) +{ + if (xgbe_phy_finisar_phy_quirks(pdata)) + return; +} + +static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct phy_device *phydev; + int ret; + + /* If we already have a PHY, just return */ + if (phy_data->phydev) + return 0; + + /* Check for the use of an external PHY */ + if (phy_data->phydev_mode == XGBE_MDIO_MODE_NONE) + return 0; + + /* For SFP, only use an external PHY if available */ + if ((phy_data->port_mode == XGBE_PORT_MODE_SFP) && + !phy_data->sfp_phy_avail) + return 0; + + /* Create and connect to the PHY device */ + phydev = get_phy_device(phy_data->mii, phy_data->mdio_addr, + (phy_data->phydev_mode == XGBE_MDIO_MODE_CL45)); + if (IS_ERR(phydev)) { + netdev_err(pdata->netdev, "get_phy_device failed\n"); + return -ENODEV; + } + netif_dbg(pdata, drv, pdata->netdev, "external PHY id is %#010x\n", + phydev->phy_id); + + /*TODO: If c45, add request_module based on one of the MMD ids? */ + + ret = phy_device_register(phydev); + if (ret) { + netdev_err(pdata->netdev, "phy_device_register failed\n"); + phy_device_free(phydev); + return ret; + } + + ret = phy_attach_direct(pdata->netdev, phydev, phydev->dev_flags, + PHY_INTERFACE_MODE_SGMII); + if (ret) { + netdev_err(pdata->netdev, "phy_attach_direct failed\n"); + phy_device_remove(phydev); + phy_device_free(phydev); + return ret; + } + phy_data->phydev = phydev; + + xgbe_phy_external_phy_quirks(pdata); + phydev->advertising &= pdata->phy.advertising; + + phy_start_aneg(phy_data->phydev); + + return 0; +} + +static void xgbe_phy_sfp_external_phy(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + if (!phy_data->sfp_changed) + return; + + phy_data->sfp_phy_avail = 0; + + if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) + return; + + /* Check access to the PHY by reading CTRL1 */ + ret = xgbe_phy_i2c_mii_read(pdata, MII_BMCR); + if (ret < 0) + return; + + /* Successfully accessed the PHY */ + phy_data->sfp_phy_avail = 1; +} + +static bool xgbe_phy_belfuse_parse_quirks(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom; + + if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_NAME], + XGBE_BEL_FUSE_VENDOR, XGBE_SFP_BASE_VENDOR_NAME_LEN)) + return false; + + if (!memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN], + XGBE_BEL_FUSE_PARTNO, XGBE_SFP_BASE_VENDOR_PN_LEN)) { + phy_data->sfp_base = XGBE_SFP_BASE_1000_SX; + phy_data->sfp_cable = XGBE_SFP_CABLE_ACTIVE; + phy_data->sfp_speed = XGBE_SFP_SPEED_1000; + if (phy_data->sfp_changed) + netif_dbg(pdata, drv, pdata->netdev, + "Bel-Fuse SFP quirk in place\n"); + return true; + } + + return false; +} + +static bool xgbe_phy_sfp_parse_quirks(struct xgbe_prv_data *pdata) +{ + if (xgbe_phy_belfuse_parse_quirks(pdata)) + return true; + + return false; +} + +static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom; + u8 *sfp_base; + + sfp_base = sfp_eeprom->base; + + if (sfp_base[XGBE_SFP_BASE_ID] != XGBE_SFP_ID_SFP) + return; + + if (sfp_base[XGBE_SFP_BASE_EXT_ID] != XGBE_SFP_EXT_ID_SFP) + return; + + if (xgbe_phy_sfp_parse_quirks(pdata)) + return; + + /* Assume ACTIVE cable unless told it is PASSIVE */ + if (sfp_base[XGBE_SFP_BASE_CABLE] & XGBE_SFP_BASE_CABLE_PASSIVE) { + phy_data->sfp_cable = XGBE_SFP_CABLE_PASSIVE; + phy_data->sfp_cable_len = sfp_base[XGBE_SFP_BASE_CU_CABLE_LEN]; + } else { + phy_data->sfp_cable = XGBE_SFP_CABLE_ACTIVE; + } + + /* Determine the type of SFP */ + if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_SR) + phy_data->sfp_base = XGBE_SFP_BASE_10000_SR; + else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_LR) + phy_data->sfp_base = XGBE_SFP_BASE_10000_LR; + else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_LRM) + phy_data->sfp_base = XGBE_SFP_BASE_10000_LRM; + else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_ER) + phy_data->sfp_base = XGBE_SFP_BASE_10000_ER; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_SX) + phy_data->sfp_base = XGBE_SFP_BASE_1000_SX; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_LX) + phy_data->sfp_base = XGBE_SFP_BASE_1000_LX; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_CX) + phy_data->sfp_base = XGBE_SFP_BASE_1000_CX; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_T) + phy_data->sfp_base = XGBE_SFP_BASE_1000_T; + else if ((phy_data->sfp_cable == XGBE_SFP_CABLE_PASSIVE) && + xgbe_phy_sfp_bit_rate(sfp_eeprom, XGBE_SFP_SPEED_10000)) + phy_data->sfp_base = XGBE_SFP_BASE_10000_CR; + + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + phy_data->sfp_speed = XGBE_SFP_SPEED_100_1000; + break; + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + phy_data->sfp_speed = XGBE_SFP_SPEED_1000; + break; + case XGBE_SFP_BASE_10000_SR: + case XGBE_SFP_BASE_10000_LR: + case XGBE_SFP_BASE_10000_LRM: + case XGBE_SFP_BASE_10000_ER: + case XGBE_SFP_BASE_10000_CR: + phy_data->sfp_speed = XGBE_SFP_SPEED_10000; + break; + default: + break; + } +} + +static void xgbe_phy_sfp_eeprom_info(struct xgbe_prv_data *pdata, + struct xgbe_sfp_eeprom *sfp_eeprom) +{ + struct xgbe_sfp_ascii sfp_ascii; + char *sfp_data = (char *)&sfp_ascii; + + netif_dbg(pdata, drv, pdata->netdev, "SFP detected:\n"); + memcpy(sfp_data, &sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_NAME], + XGBE_SFP_BASE_VENDOR_NAME_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_NAME_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " vendor: %s\n", + sfp_data); + + memcpy(sfp_data, &sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN], + XGBE_SFP_BASE_VENDOR_PN_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_PN_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " part number: %s\n", + sfp_data); + + memcpy(sfp_data, &sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_REV], + XGBE_SFP_BASE_VENDOR_REV_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_REV_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " revision level: %s\n", + sfp_data); + + memcpy(sfp_data, &sfp_eeprom->extd[XGBE_SFP_BASE_VENDOR_SN], + XGBE_SFP_BASE_VENDOR_SN_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_SN_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " serial number: %s\n", + sfp_data); +} + +static bool xgbe_phy_sfp_verify_eeprom(u8 cc_in, u8 *buf, unsigned int len) +{ + u8 cc; + + for (cc = 0; len; buf++, len--) + cc += *buf; + + return (cc == cc_in) ? true : false; +} + +static int xgbe_phy_sfp_read_eeprom(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_sfp_eeprom sfp_eeprom; + u8 eeprom_addr; + int ret; + + ret = xgbe_phy_sfp_get_mux(pdata); + if (ret) { + netdev_err(pdata->netdev, "I2C error setting SFP MUX\n"); + return ret; + } + + /* Read the SFP serial ID eeprom */ + eeprom_addr = 0; + ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_SERIAL_ID_ADDRESS, + &eeprom_addr, sizeof(eeprom_addr), + &sfp_eeprom, sizeof(sfp_eeprom)); + if (ret) { + netdev_err(pdata->netdev, "I2C error reading SFP EEPROM\n"); + goto put; + } + + /* Validate the contents read */ + if (!xgbe_phy_sfp_verify_eeprom(sfp_eeprom.base[XGBE_SFP_BASE_CC], + sfp_eeprom.base, + sizeof(sfp_eeprom.base) - 1)) { + ret = -EINVAL; + goto put; + } + + if (!xgbe_phy_sfp_verify_eeprom(sfp_eeprom.extd[XGBE_SFP_EXTD_CC], + sfp_eeprom.extd, + sizeof(sfp_eeprom.extd) - 1)) { + ret = -EINVAL; + goto put; + } + + /* Check for an added or changed SFP */ + if (memcmp(&phy_data->sfp_eeprom, &sfp_eeprom, sizeof(sfp_eeprom))) { + phy_data->sfp_changed = 1; + + if (netif_msg_drv(pdata)) + xgbe_phy_sfp_eeprom_info(pdata, &sfp_eeprom); + + memcpy(&phy_data->sfp_eeprom, &sfp_eeprom, sizeof(sfp_eeprom)); + + if (sfp_eeprom.extd[XGBE_SFP_EXTD_SFF_8472]) { + u8 diag_type = sfp_eeprom.extd[XGBE_SFP_EXTD_DIAG]; + + if (!(diag_type & XGBE_SFP_EXTD_DIAG_ADDR_CHANGE)) + phy_data->sfp_diags = 1; + } + + xgbe_phy_free_phy_device(pdata); + } else { + phy_data->sfp_changed = 0; + } + +put: + xgbe_phy_sfp_put_mux(pdata); + + return ret; +} + +static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int gpio_input; + u8 gpio_reg, gpio_ports[2]; + int ret; + + /* Read the input port registers */ + gpio_reg = 0; + ret = xgbe_phy_i2c_read(pdata, phy_data->sfp_gpio_address, + &gpio_reg, sizeof(gpio_reg), + gpio_ports, sizeof(gpio_ports)); + if (ret) { + netdev_err(pdata->netdev, "I2C error reading SFP GPIOs\n"); + return; + } + + gpio_input = (gpio_ports[1] << 8) | gpio_ports[0]; + + if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT) { + /* No GPIO, just assume the module is present for now */ + phy_data->sfp_mod_absent = 0; + } else { + if (!(gpio_input & (1 << phy_data->sfp_gpio_mod_absent))) + phy_data->sfp_mod_absent = 0; + } + + if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS) && + (gpio_input & (1 << phy_data->sfp_gpio_rx_los))) + phy_data->sfp_rx_los = 1; + + if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT) && + (gpio_input & (1 << phy_data->sfp_gpio_tx_fault))) + phy_data->sfp_tx_fault = 1; +} + +static void xgbe_phy_sfp_mod_absent(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_free_phy_device(pdata); + + phy_data->sfp_mod_absent = 1; + phy_data->sfp_phy_avail = 0; + memset(&phy_data->sfp_eeprom, 0, sizeof(phy_data->sfp_eeprom)); +} + +static void xgbe_phy_sfp_reset(struct xgbe_phy_data *phy_data) +{ + phy_data->sfp_rx_los = 0; + phy_data->sfp_tx_fault = 0; + phy_data->sfp_mod_absent = 1; + phy_data->sfp_diags = 0; + phy_data->sfp_base = XGBE_SFP_BASE_UNKNOWN; + phy_data->sfp_cable = XGBE_SFP_CABLE_UNKNOWN; + phy_data->sfp_speed = XGBE_SFP_SPEED_UNKNOWN; +} + +static void xgbe_phy_sfp_detect(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + /* Reset the SFP signals and info */ + xgbe_phy_sfp_reset(phy_data); + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return; + + /* Read the SFP signals and check for module presence */ + xgbe_phy_sfp_signals(pdata); + if (phy_data->sfp_mod_absent) { + xgbe_phy_sfp_mod_absent(pdata); + goto put; + } + + ret = xgbe_phy_sfp_read_eeprom(pdata); + if (ret) { + /* Treat any error as if there isn't an SFP plugged in */ + xgbe_phy_sfp_reset(phy_data); + xgbe_phy_sfp_mod_absent(pdata); + goto put; + } + + xgbe_phy_sfp_parse_eeprom(pdata); + + xgbe_phy_sfp_external_phy(pdata); + +put: + xgbe_phy_sfp_phy_settings(pdata); + + xgbe_phy_put_comm_ownership(pdata); +} + +static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_TP; + + if (pdata->phy.pause_autoneg && phy_data->phydev) { + /* Flow control is obtained from the attached PHY */ + u16 lcl_adv = 0, rmt_adv = 0; + u8 fc; + + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (phy_data->phydev->advertising & ADVERTISED_Pause) + lcl_adv |= ADVERTISE_PAUSE_CAP; + if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause) + lcl_adv |= ADVERTISE_PAUSE_ASYM; + + if (phy_data->phydev->pause) + rmt_adv |= LPA_PAUSE_CAP; + if (phy_data->phydev->asym_pause) + rmt_adv |= LPA_PAUSE_ASYM; + + fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + if (fc & FLOW_CTRL_TX) + pdata->phy.tx_pause = 1; + if (fc & FLOW_CTRL_RX) + pdata->phy.rx_pause = 1; + } + + switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) { + case XGBE_SGMII_AN_LINK_SPEED_100: + if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { + pdata->phy.lp_advertising |= ADVERTISED_100baseT_Full; + mode = XGBE_MODE_SGMII_100; + } else { + /* Half-duplex not supported */ + pdata->phy.lp_advertising |= ADVERTISED_100baseT_Half; + mode = XGBE_MODE_UNKNOWN; + } + break; + case XGBE_SGMII_AN_LINK_SPEED_1000: + if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { + pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Full; + mode = XGBE_MODE_SGMII_1000; + } else { + /* Half-duplex not supported */ + pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Half; + mode = XGBE_MODE_UNKNOWN; + } + break; + default: + mode = XGBE_MODE_UNKNOWN; + } + + return mode; +} + +static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) +{ + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_FIBRE; + + /* Compare Advertisement and Link Partner register */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_LP_ABILITY); + if (lp_reg & 0x100) + pdata->phy.lp_advertising |= ADVERTISED_Pause; + if (lp_reg & 0x80) + pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; - unsigned int port_id; + if (pdata->phy.pause_autoneg) { + /* Set flow control based on auto-negotiation result */ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; - unsigned int port_speeds; + if (ad_reg & lp_reg & 0x100) { + pdata->phy.tx_pause = 1; + pdata->phy.rx_pause = 1; + } else if (ad_reg & lp_reg & 0x80) { + if (ad_reg & 0x100) + pdata->phy.rx_pause = 1; + else if (lp_reg & 0x100) + pdata->phy.tx_pause = 1; + } + } - enum xgbe_conn_type conn_type; + if (lp_reg & 0x40) + pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Half; + if (lp_reg & 0x20) + pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Full; - enum xgbe_mode cur_mode; - enum xgbe_mode start_mode; + /* Half duplex is not supported */ + ad_reg &= lp_reg; + mode = (ad_reg & 0x20) ? XGBE_MODE_X : XGBE_MODE_UNKNOWN; - unsigned int rrc_count; -}; + return mode; +} -static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) +static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) { enum xgbe_mode mode; unsigned int ad_reg, lp_reg; @@ -224,6 +1259,60 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) return mode; } +static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) +{ + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + return xgbe_phy_an73_outcome(pdata); + case XGBE_AN_MODE_CL37: + return xgbe_phy_an37_outcome(pdata); + case XGBE_AN_MODE_CL37_SGMII: + return xgbe_phy_an37_sgmii_outcome(pdata); + default: + return XGBE_MODE_UNKNOWN; + } +} + +static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + ret = xgbe_phy_find_phy_device(pdata); + if (ret) + return ret; + + if (!phy_data->phydev) + return 0; + + phy_data->phydev->autoneg = pdata->phy.autoneg; + phy_data->phydev->advertising = phy_data->phydev->supported & + pdata->phy.advertising; + + if (pdata->phy.autoneg != AUTONEG_ENABLE) { + phy_data->phydev->speed = pdata->phy.speed; + phy_data->phydev->duplex = pdata->phy.duplex; + } + + ret = phy_start_aneg(phy_data->phydev); + + return ret; +} + +static enum xgbe_an_mode xgbe_phy_an_sfp_mode(struct xgbe_phy_data *phy_data) +{ + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + return XGBE_AN_MODE_CL37_SGMII; + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + return XGBE_AN_MODE_CL37; + default: + return XGBE_AN_MODE_NONE; + } +} + static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -238,7 +1327,9 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: case XGBE_PORT_MODE_10GBASE_R: + return XGBE_AN_MODE_NONE; case XGBE_PORT_MODE_SFP: + return xgbe_phy_an_sfp_mode(phy_data); default: return XGBE_AN_MODE_NONE; } @@ -310,6 +1401,113 @@ static void xgbe_phy_power_off(struct xgbe_prv_data *pdata) netif_dbg(pdata, link, pdata->netdev, "phy powered off\n"); } +static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* 10G/SFI */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 3); + if (phy_data->sfp_cable != XGBE_SFP_CABLE_PASSIVE) { + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0); + } else { + if (phy_data->sfp_cable_len <= 1) + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 1); + else if (phy_data->sfp_cable_len <= 3) + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 2); + else if (phy_data->sfp_cable_len <= 5) + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3); + else + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3); + } + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_SFI; + + netif_dbg(pdata, link, pdata->netdev, "10GbE SFI mode set\n"); +} + +static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* 1G/X */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 1); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_X; + + netif_dbg(pdata, link, pdata->netdev, "1GbE X mode set\n"); +} + +static void xgbe_phy_sgmii_1000_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* 1G/SGMII */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 1); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 2); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_SGMII_1000; + + netif_dbg(pdata, link, pdata->netdev, "1GbE SGMII mode set\n"); +} + +static void xgbe_phy_sgmii_100_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int s0; + + xgbe_phy_start_ratechange(pdata); + + /* 1G/SGMII */ + s0 = 0; + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 1); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 1); + + /* Call FW to make the change */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + xgbe_phy_complete_ratechange(pdata); + + phy_data->cur_mode = XGBE_MODE_SGMII_100; + + netif_dbg(pdata, link, pdata->netdev, "100MbE SGMII mode set\n"); +} + static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -420,7 +1618,29 @@ static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata) case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: case XGBE_PORT_MODE_10GBASE_R: + return XGBE_MODE_UNKNOWN; case XGBE_PORT_MODE_SFP: + /* No switching, so just return current mode */ + return xgbe_phy_cur_mode(pdata); + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_sfp_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + return XGBE_MODE_SGMII_100; + case SPEED_1000: + if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) + return XGBE_MODE_SGMII_1000; + else + return XGBE_MODE_X; + case SPEED_10000: + case SPEED_UNKNOWN: + return XGBE_MODE_SFI; default: return XGBE_MODE_UNKNOWN; } @@ -463,7 +1683,9 @@ static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata, case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: case XGBE_PORT_MODE_10GBASE_R: + return XGBE_MODE_UNKNOWN; case XGBE_PORT_MODE_SFP: + return xgbe_phy_get_sfp_mode(phy_data, speed); default: return XGBE_MODE_UNKNOWN; } @@ -481,6 +1703,18 @@ static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) case XGBE_MODE_KR: xgbe_phy_kr_mode(pdata); break; + case XGBE_MODE_SGMII_100: + xgbe_phy_sgmii_100_mode(pdata); + break; + case XGBE_MODE_SGMII_1000: + xgbe_phy_sgmii_1000_mode(pdata); + break; + case XGBE_MODE_X: + xgbe_phy_x_mode(pdata); + break; + case XGBE_MODE_SFI: + xgbe_phy_sfi_mode(pdata); + break; default: break; } @@ -503,6 +1737,35 @@ static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, return false; } +static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (mode) { + case XGBE_MODE_X: + if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) + return false; + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case XGBE_MODE_SGMII_100: + if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) + return false; + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_100baseT_Full); + case XGBE_MODE_SGMII_1000: + if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) + return false; + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case XGBE_MODE_SFI: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseT_Full); + default: + return false; + } +} + static bool xgbe_phy_use_bp_2500_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { @@ -544,7 +1807,25 @@ static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: case XGBE_PORT_MODE_10GBASE_R: + return false; case XGBE_PORT_MODE_SFP: + return xgbe_phy_use_sfp_mode(pdata, mode); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_sfp_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + return (phy_data->sfp_speed == XGBE_SFP_SPEED_100_1000); + case SPEED_1000: + return ((phy_data->sfp_speed == XGBE_SFP_SPEED_100_1000) || + (phy_data->sfp_speed == XGBE_SFP_SPEED_1000)); + case SPEED_10000: + return (phy_data->sfp_speed == XGBE_SFP_SPEED_10000); default: return false; } @@ -585,16 +1866,47 @@ static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: case XGBE_PORT_MODE_10GBASE_R: + return false; case XGBE_PORT_MODE_SFP: + return xgbe_phy_valid_speed_sfp_mode(phy_data, speed); default: return false; } } -static int xgbe_phy_link_status(struct xgbe_prv_data *pdata) +static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart) { struct xgbe_phy_data *phy_data = pdata->phy_data; - unsigned int reg; + unsigned int ret, reg; + + *an_restart = 0; + + if (phy_data->port_mode == XGBE_PORT_MODE_SFP) { + /* Check SFP signals */ + xgbe_phy_sfp_detect(pdata); + + if (phy_data->sfp_changed) { + *an_restart = 1; + return 0; + } + + if (phy_data->sfp_mod_absent || phy_data->sfp_rx_los) + return 0; + } + + if (phy_data->phydev) { + /* Check external PHY */ + ret = phy_read_status(phy_data->phydev); + if (ret < 0) + return 0; + + if ((pdata->phy.autoneg == AUTONEG_ENABLE) && + !phy_aneg_done(phy_data->phydev)) + return 0; + + if (!phy_data->phydev->link) + return 0; + } /* Link status is latched low, so read once to clear * and then read again to get current state @@ -613,6 +1925,73 @@ static int xgbe_phy_link_status(struct xgbe_prv_data *pdata) return 0; } +static void xgbe_phy_sfp_gpio_setup(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + reg = XP_IOREAD(pdata, XP_PROP_3); + + phy_data->sfp_gpio_address = XGBE_GPIO_ADDRESS_PCA9555 + + XP_GET_BITS(reg, XP_PROP_3, GPIO_ADDR); + + phy_data->sfp_gpio_mask = XP_GET_BITS(reg, XP_PROP_3, GPIO_MASK); + + phy_data->sfp_gpio_rx_los = XP_GET_BITS(reg, XP_PROP_3, + GPIO_RX_LOS); + phy_data->sfp_gpio_tx_fault = XP_GET_BITS(reg, XP_PROP_3, + GPIO_TX_FAULT); + phy_data->sfp_gpio_mod_absent = XP_GET_BITS(reg, XP_PROP_3, + GPIO_MOD_ABS); + phy_data->sfp_gpio_rate_select = XP_GET_BITS(reg, XP_PROP_3, + GPIO_RATE_SELECT); + + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "SFP: gpio_address=%#x\n", + phy_data->sfp_gpio_address); + dev_dbg(pdata->dev, "SFP: gpio_mask=%#x\n", + phy_data->sfp_gpio_mask); + dev_dbg(pdata->dev, "SFP: gpio_rx_los=%u\n", + phy_data->sfp_gpio_rx_los); + dev_dbg(pdata->dev, "SFP: gpio_tx_fault=%u\n", + phy_data->sfp_gpio_tx_fault); + dev_dbg(pdata->dev, "SFP: gpio_mod_absent=%u\n", + phy_data->sfp_gpio_mod_absent); + dev_dbg(pdata->dev, "SFP: gpio_rate_select=%u\n", + phy_data->sfp_gpio_rate_select); + } +} + +static void xgbe_phy_sfp_comm_setup(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg, mux_addr_hi, mux_addr_lo; + + reg = XP_IOREAD(pdata, XP_PROP_4); + + mux_addr_hi = XP_GET_BITS(reg, XP_PROP_4, MUX_ADDR_HI); + mux_addr_lo = XP_GET_BITS(reg, XP_PROP_4, MUX_ADDR_LO); + if (mux_addr_lo == XGBE_SFP_DIRECT) + return; + + phy_data->sfp_comm = XGBE_SFP_COMM_PCA9545; + phy_data->sfp_mux_address = (mux_addr_hi << 2) + mux_addr_lo; + phy_data->sfp_mux_channel = XP_GET_BITS(reg, XP_PROP_4, MUX_CHAN); + + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "SFP: mux_address=%#x\n", + phy_data->sfp_mux_address); + dev_dbg(pdata->dev, "SFP: mux_channel=%u\n", + phy_data->sfp_mux_channel); + } +} + +static void xgbe_phy_sfp_setup(struct xgbe_prv_data *pdata) +{ + xgbe_phy_sfp_comm_setup(pdata); + xgbe_phy_sfp_gpio_setup(pdata); +} + static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -708,6 +2087,15 @@ static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata) static void xgbe_phy_stop(struct xgbe_prv_data *pdata) { + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* If we have an external PHY, free it */ + xgbe_phy_free_phy_device(pdata); + + /* Reset SFP data */ + xgbe_phy_sfp_reset(phy_data); + xgbe_phy_sfp_mod_absent(pdata); + /* Power off the PHY */ xgbe_phy_power_off(pdata); @@ -728,7 +2116,26 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) /* Start in highest supported mode */ xgbe_phy_set_mode(pdata, phy_data->start_mode); + /* After starting the I2C controller, we can check for an SFP */ + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_SFP: + xgbe_phy_sfp_detect(pdata); + break; + default: + break; + } + + /* If we have an external PHY, start it */ + ret = xgbe_phy_find_phy_device(pdata); + if (ret) + goto err_i2c; + return 0; + +err_i2c: + pdata->i2c_if.i2c_stop(pdata); + + return ret; } static int xgbe_phy_reset(struct xgbe_prv_data *pdata) @@ -741,17 +2148,25 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata) xgbe_phy_power_off(pdata); xgbe_phy_set_mode(pdata, cur_mode); - return 0; + if (!phy_data->phydev) + return 0; + + /* Reset the external PHY */ + return phy_init_hw(phy_data->phydev); } static void xgbe_phy_exit(struct xgbe_prv_data *pdata) { - /* Nothing uniquely required for exit */ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* Unregister for driving external PHYs */ + mdiobus_unregister(phy_data->mii); } static int xgbe_phy_init(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data; + struct mii_bus *mii; unsigned int reg; int ret; @@ -776,11 +2191,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) phy_data->port_id = XP_GET_BITS(reg, XP_PROP_0, PORT_ID); phy_data->port_speeds = XP_GET_BITS(reg, XP_PROP_0, PORT_SPEEDS); phy_data->conn_type = XP_GET_BITS(reg, XP_PROP_0, CONN_TYPE); + phy_data->mdio_addr = XP_GET_BITS(reg, XP_PROP_0, MDIO_ADDR); if (netif_msg_probe(pdata)) { dev_dbg(pdata->dev, "port mode=%u\n", phy_data->port_mode); dev_dbg(pdata->dev, "port id=%u\n", phy_data->port_id); dev_dbg(pdata->dev, "port speeds=%#x\n", phy_data->port_speeds); dev_dbg(pdata->dev, "conn type=%u\n", phy_data->conn_type); + dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr); } /* Validate the connection requested */ @@ -818,19 +2235,48 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) SUPPORTED_10000baseR_FEC; phy_data->start_mode = XGBE_MODE_KR; } + + phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; break; case XGBE_PORT_MODE_BACKPLANE_2500: pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; pdata->phy.supported |= SUPPORTED_Backplane; pdata->phy.supported |= SUPPORTED_2500baseX_Full; phy_data->start_mode = XGBE_MODE_KX_2500; + + phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; break; case XGBE_PORT_MODE_1000BASE_T: case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: case XGBE_PORT_MODE_10GBASE_R: + return -ENODEV; case XGBE_PORT_MODE_SFP: + pdata->phy.supported |= SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_TP; + pdata->phy.supported |= SUPPORTED_FIBRE; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { + pdata->phy.supported |= SUPPORTED_100baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_100; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + pdata->phy.supported |= SUPPORTED_1000baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_1000; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + pdata->phy.supported |= SUPPORTED_10000baseT_Full; + phy_data->start_mode = XGBE_MODE_SFI; + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + pdata->phy.supported |= + SUPPORTED_10000baseR_FEC; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; + + xgbe_phy_sfp_setup(pdata); + break; default: return -EINVAL; } @@ -839,6 +2285,27 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) dev_dbg(pdata->dev, "phy supported=%#x\n", pdata->phy.supported); + /* Register for driving external PHYs */ + mii = devm_mdiobus_alloc(pdata->dev); + if (!mii) { + dev_err(pdata->dev, "mdiobus_alloc failed\n"); + return -ENOMEM; + } + + mii->priv = pdata; + mii->name = "amd-xgbe-mii"; + mii->read = xgbe_phy_mii_read; + mii->write = xgbe_phy_mii_write; + mii->parent = pdata->dev; + mii->phy_mask = ~0; + snprintf(mii->id, sizeof(mii->id), "%s", dev_name(pdata->dev)); + ret = mdiobus_register(mii); + if (ret) { + dev_err(pdata->dev, "mdiobus_register failed\n"); + return ret; + } + phy_data->mii = mii; + return 0; } @@ -865,5 +2332,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if) phy_impl->an_mode = xgbe_phy_an_mode; + phy_impl->an_config = xgbe_phy_an_config; + phy_impl->an_outcome = xgbe_phy_an_outcome; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 586154f..a691f84 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -530,6 +530,10 @@ enum xgbe_mode { XGBE_MODE_KX_1000 = 0, XGBE_MODE_KX_2500, XGBE_MODE_KR, + XGBE_MODE_X, + XGBE_MODE_SGMII_100, + XGBE_MODE_SGMII_1000, + XGBE_MODE_SFI, XGBE_MODE_UNKNOWN, }; @@ -538,6 +542,12 @@ enum xgbe_speedset { XGBE_SPEEDSET_2500_10000, }; +enum xgbe_mdio_mode { + XGBE_MDIO_MODE_NONE = 0, + XGBE_MDIO_MODE_CL22, + XGBE_MDIO_MODE_CL45, +}; + struct xgbe_phy { u32 supported; u32 advertising; @@ -764,7 +774,7 @@ struct xgbe_phy_impl_if { void (*stop)(struct xgbe_prv_data *); /* Return the link status */ - int (*link_status)(struct xgbe_prv_data *); + int (*link_status)(struct xgbe_prv_data *, int *); /* Indicate if a particular speed is valid */ bool (*valid_speed)(struct xgbe_prv_data *, int); @@ -783,6 +793,9 @@ struct xgbe_phy_impl_if { /* Retrieve current auto-negotiation mode */ enum xgbe_an_mode (*an_mode)(struct xgbe_prv_data *); + /* Configure auto-negotiation settings */ + int (*an_config)(struct xgbe_prv_data *); + /* Process results of auto-negotiation */ enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); -- cgit v1.1 From 732f2ab7afb975755dcfbdcbe6eafe42e8cdc1d4 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:11:14 -0600 Subject: amd-xgbe: Add support for MDIO attached PHYs Use the phylib support in the kernel to communicate with and control an MDIO attached PHY. Use the hardware's MDIO communication mechanism to communicate with the PHY. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 37 +++ drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 113 ++++++++ drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 19 +- drivers/net/ethernet/amd/xgbe/xgbe-main.c | 1 + drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 386 +++++++++++++++++++++++++++- drivers/net/ethernet/amd/xgbe/xgbe.h | 12 + 6 files changed, 557 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index b7140f9..ecd4f4d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -311,6 +311,11 @@ #define MAC_HWF0R 0x011c #define MAC_HWF1R 0x0120 #define MAC_HWF2R 0x0124 +#define MAC_MDIOSCAR 0x0200 +#define MAC_MDIOSCCDR 0x0204 +#define MAC_MDIOISR 0x0214 +#define MAC_MDIOIER 0x0218 +#define MAC_MDIOCL22R 0x0220 #define MAC_GPIOCR 0x0278 #define MAC_GPIOSR 0x027c #define MAC_MACA0HR 0x0300 @@ -411,10 +416,34 @@ #define MAC_ISR_MMCTXIS_WIDTH 1 #define MAC_ISR_PMTIS_INDEX 4 #define MAC_ISR_PMTIS_WIDTH 1 +#define MAC_ISR_SMI_INDEX 1 +#define MAC_ISR_SMI_WIDTH 1 #define MAC_ISR_TSIS_INDEX 12 #define MAC_ISR_TSIS_WIDTH 1 #define MAC_MACA1HR_AE_INDEX 31 #define MAC_MACA1HR_AE_WIDTH 1 +#define MAC_MDIOIER_SNGLCOMPIE_INDEX 12 +#define MAC_MDIOIER_SNGLCOMPIE_WIDTH 1 +#define MAC_MDIOISR_SNGLCOMPINT_INDEX 12 +#define MAC_MDIOISR_SNGLCOMPINT_WIDTH 1 +#define MAC_MDIOSCAR_DA_INDEX 21 +#define MAC_MDIOSCAR_DA_WIDTH 5 +#define MAC_MDIOSCAR_PA_INDEX 16 +#define MAC_MDIOSCAR_PA_WIDTH 5 +#define MAC_MDIOSCAR_RA_INDEX 0 +#define MAC_MDIOSCAR_RA_WIDTH 16 +#define MAC_MDIOSCAR_REG_INDEX 0 +#define MAC_MDIOSCAR_REG_WIDTH 21 +#define MAC_MDIOSCCDR_BUSY_INDEX 22 +#define MAC_MDIOSCCDR_BUSY_WIDTH 1 +#define MAC_MDIOSCCDR_CMD_INDEX 16 +#define MAC_MDIOSCCDR_CMD_WIDTH 2 +#define MAC_MDIOSCCDR_CR_INDEX 19 +#define MAC_MDIOSCCDR_CR_WIDTH 3 +#define MAC_MDIOSCCDR_DATA_INDEX 0 +#define MAC_MDIOSCCDR_DATA_WIDTH 16 +#define MAC_MDIOSCCDR_SADDR_INDEX 18 +#define MAC_MDIOSCCDR_SADDR_WIDTH 1 #define MAC_PFR_HMC_INDEX 2 #define MAC_PFR_HMC_WIDTH 1 #define MAC_PFR_HPF_INDEX 10 @@ -1019,6 +1048,14 @@ #define XP_PROP_3_GPIO_TX_FAULT_WIDTH 4 #define XP_PROP_3_GPIO_ADDR_INDEX 8 #define XP_PROP_3_GPIO_ADDR_WIDTH 3 +#define XP_PROP_3_MDIO_RESET_INDEX 0 +#define XP_PROP_3_MDIO_RESET_WIDTH 2 +#define XP_PROP_3_MDIO_RESET_I2C_ADDR_INDEX 8 +#define XP_PROP_3_MDIO_RESET_I2C_ADDR_WIDTH 3 +#define XP_PROP_3_MDIO_RESET_I2C_GPIO_INDEX 12 +#define XP_PROP_3_MDIO_RESET_I2C_GPIO_WIDTH 4 +#define XP_PROP_3_MDIO_RESET_INT_GPIO_INDEX 4 +#define XP_PROP_3_MDIO_RESET_INT_GPIO_WIDTH 2 #define XP_PROP_4_MUX_ADDR_HI_INDEX 8 #define XP_PROP_4_MUX_ADDR_HI_WIDTH 5 #define XP_PROP_4_MUX_ADDR_LO_INDEX 0 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 78a2063..30056e2 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -722,6 +722,9 @@ static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata) /* Enable all counter interrupts */ XGMAC_IOWRITE_BITS(pdata, MMC_RIER, ALL_INTERRUPTS, 0xffffffff); XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xffffffff); + + /* Enable MDIO single command completion interrupt */ + XGMAC_IOWRITE_BITS(pdata, MAC_MDIOIER, SNGLCOMPIE, 1); } static void xgbe_enable_ecc_interrupts(struct xgbe_prv_data *pdata) @@ -1092,6 +1095,36 @@ static int xgbe_config_rx_mode(struct xgbe_prv_data *pdata) return 0; } +static int xgbe_clr_gpio(struct xgbe_prv_data *pdata, unsigned int gpio) +{ + unsigned int reg; + + if (gpio > 16) + return -EINVAL; + + reg = XGMAC_IOREAD(pdata, MAC_GPIOSR); + + reg &= ~(1 << (gpio + 16)); + XGMAC_IOWRITE(pdata, MAC_GPIOSR, reg); + + return 0; +} + +static int xgbe_set_gpio(struct xgbe_prv_data *pdata, unsigned int gpio) +{ + unsigned int reg; + + if (gpio > 16) + return -EINVAL; + + reg = XGMAC_IOREAD(pdata, MAC_GPIOSR); + + reg |= (1 << (gpio + 16)); + XGMAC_IOWRITE(pdata, MAC_GPIOSR, reg); + + return 0; +} + static int xgbe_read_mmd_regs_v2(struct xgbe_prv_data *pdata, int prtad, int mmd_reg) { @@ -1236,6 +1269,79 @@ static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad, } } +static int xgbe_write_ext_mii_regs(struct xgbe_prv_data *pdata, int addr, + int reg, u16 val) +{ + unsigned int mdio_sca, mdio_sccd; + + reinit_completion(&pdata->mdio_complete); + + mdio_sca = 0; + XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, REG, reg); + XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, addr); + XGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca); + + mdio_sccd = 0; + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, val); + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 1); + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1); + XGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd); + + if (!wait_for_completion_timeout(&pdata->mdio_complete, HZ)) { + netdev_err(pdata->netdev, "mdio write operation timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int xgbe_read_ext_mii_regs(struct xgbe_prv_data *pdata, int addr, + int reg) +{ + unsigned int mdio_sca, mdio_sccd; + + reinit_completion(&pdata->mdio_complete); + + mdio_sca = 0; + XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, REG, reg); + XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, addr); + XGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca); + + mdio_sccd = 0; + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 3); + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1); + XGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd); + + if (!wait_for_completion_timeout(&pdata->mdio_complete, HZ)) { + netdev_err(pdata->netdev, "mdio read operation timed out\n"); + return -ETIMEDOUT; + } + + return XGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA); +} + +static int xgbe_set_ext_mii_mode(struct xgbe_prv_data *pdata, unsigned int port, + enum xgbe_mdio_mode mode) +{ + unsigned int reg_val = 0; + + switch (mode) { + case XGBE_MDIO_MODE_CL22: + if (port > XGMAC_MAX_C22_PORT) + return -EINVAL; + reg_val |= (1 << port); + break; + case XGBE_MDIO_MODE_CL45: + break; + default: + return -EINVAL; + } + + XGMAC_IOWRITE(pdata, MAC_MDIOCL22R, reg_val); + + return 0; +} + static int xgbe_tx_complete(struct xgbe_ring_desc *rdesc) { return !XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN); @@ -3386,6 +3492,13 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if) hw_if->set_speed = xgbe_set_speed; + hw_if->set_ext_mii_mode = xgbe_set_ext_mii_mode; + hw_if->read_ext_mii_regs = xgbe_read_ext_mii_regs; + hw_if->write_ext_mii_regs = xgbe_write_ext_mii_regs; + + hw_if->set_gpio = xgbe_set_gpio; + hw_if->clr_gpio = xgbe_clr_gpio; + hw_if->enable_tx = xgbe_enable_tx; hw_if->disable_tx = xgbe_disable_tx; hw_if->enable_rx = xgbe_enable_rx; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 7af358f..155190d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -443,7 +443,7 @@ static irqreturn_t xgbe_isr(int irq, void *data) struct xgbe_hw_if *hw_if = &pdata->hw_if; struct xgbe_channel *channel; unsigned int dma_isr, dma_ch_isr; - unsigned int mac_isr, mac_tssr; + unsigned int mac_isr, mac_tssr, mac_mdioisr; unsigned int i; /* The DMA interrupt status register also reports MAC and MTL @@ -503,6 +503,9 @@ static irqreturn_t xgbe_isr(int irq, void *data) if (XGMAC_GET_BITS(dma_isr, DMA_ISR, MACIS)) { mac_isr = XGMAC_IOREAD(pdata, MAC_ISR); + netif_dbg(pdata, intr, pdata->netdev, "MAC_ISR=%#010x\n", + mac_isr); + if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCTXIS)) hw_if->tx_mmc_int(pdata); @@ -512,6 +515,9 @@ static irqreturn_t xgbe_isr(int irq, void *data) if (XGMAC_GET_BITS(mac_isr, MAC_ISR, TSIS)) { mac_tssr = XGMAC_IOREAD(pdata, MAC_TSSR); + netif_dbg(pdata, intr, pdata->netdev, + "MAC_TSSR=%#010x\n", mac_tssr); + if (XGMAC_GET_BITS(mac_tssr, MAC_TSSR, TXTSC)) { /* Read Tx Timestamp to clear interrupt */ pdata->tx_tstamp = @@ -520,6 +526,17 @@ static irqreturn_t xgbe_isr(int irq, void *data) &pdata->tx_tstamp_work); } } + + if (XGMAC_GET_BITS(mac_isr, MAC_ISR, SMI)) { + mac_mdioisr = XGMAC_IOREAD(pdata, MAC_MDIOISR); + + netif_dbg(pdata, intr, pdata->netdev, + "MAC_MDIOISR=%#010x\n", mac_mdioisr); + + if (XGMAC_GET_BITS(mac_mdioisr, MAC_MDIOISR, + SNGLCOMPINT)) + complete(&pdata->mdio_complete); + } } /* If there is not a separate AN irq, handle it here */ diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 385b7f6..b87a899 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -189,6 +189,7 @@ struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev) spin_lock_init(&pdata->tstamp_lock); mutex_init(&pdata->i2c_mutex); init_completion(&pdata->i2c_complete); + init_completion(&pdata->mdio_complete); pdata->msg_enable = netif_msg_init(debug, default_msg_level); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 5d8cd8b..9848f74 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -269,6 +269,14 @@ struct xgbe_sfp_ascii { } u; }; +/* MDIO PHY reset types */ +enum xgbe_mdio_reset { + XGBE_MDIO_RESET_NONE = 0, + XGBE_MDIO_RESET_I2C_GPIO, + XGBE_MDIO_RESET_INT_GPIO, + XGBE_MDIO_RESET_MAX, +}; + /* PHY related configuration information */ struct xgbe_phy_data { enum xgbe_port_mode port_mode; @@ -316,6 +324,9 @@ struct xgbe_phy_data { enum xgbe_mdio_mode phydev_mode; struct mii_bus *mii; struct phy_device *phydev; + enum xgbe_mdio_reset mdio_reset; + unsigned int mdio_reset_addr; + unsigned int mdio_reset_gpio; }; /* I2C, MDIO and GPIO lines are muxed, so only one device at a time */ @@ -486,6 +497,22 @@ static int xgbe_phy_get_comm_ownership(struct xgbe_prv_data *pdata) return -ETIMEDOUT; } +static int xgbe_phy_mdio_mii_write(struct xgbe_prv_data *pdata, int addr, + int reg, u16 val) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (reg & MII_ADDR_C45) { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL45) + return -ENOTSUPP; + } else { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL22) + return -ENOTSUPP; + } + + return pdata->hw_if.write_ext_mii_regs(pdata, addr, reg, val); +} + static int xgbe_phy_i2c_mii_write(struct xgbe_prv_data *pdata, int reg, u16 val) { __be16 *mii_val; @@ -520,6 +547,8 @@ static int xgbe_phy_mii_write(struct mii_bus *mii, int addr, int reg, u16 val) if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) ret = xgbe_phy_i2c_mii_write(pdata, reg, val); + else if (phy_data->conn_type & XGBE_CONN_TYPE_MDIO) + ret = xgbe_phy_mdio_mii_write(pdata, addr, reg, val); else ret = -ENOTSUPP; @@ -528,6 +557,22 @@ static int xgbe_phy_mii_write(struct mii_bus *mii, int addr, int reg, u16 val) return ret; } +static int xgbe_phy_mdio_mii_read(struct xgbe_prv_data *pdata, int addr, + int reg) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (reg & MII_ADDR_C45) { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL45) + return -ENOTSUPP; + } else { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL22) + return -ENOTSUPP; + } + + return pdata->hw_if.read_ext_mii_regs(pdata, addr, reg); +} + static int xgbe_phy_i2c_mii_read(struct xgbe_prv_data *pdata, int reg) { __be16 mii_val; @@ -562,6 +607,8 @@ static int xgbe_phy_mii_read(struct mii_bus *mii, int addr, int reg) if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) ret = xgbe_phy_i2c_mii_read(pdata, reg); + else if (phy_data->conn_type & XGBE_CONN_TYPE_MDIO) + ret = xgbe_phy_mdio_mii_read(pdata, addr, reg); else ret = -ENOTSUPP; @@ -1323,9 +1370,13 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) case XGBE_PORT_MODE_BACKPLANE_2500: return XGBE_AN_MODE_NONE; case XGBE_PORT_MODE_1000BASE_T: + return XGBE_AN_MODE_CL37_SGMII; case XGBE_PORT_MODE_1000BASE_X: + return XGBE_AN_MODE_CL37; case XGBE_PORT_MODE_NBASE_T: + return XGBE_AN_MODE_CL37_SGMII; case XGBE_PORT_MODE_10GBASE_T: + return XGBE_AN_MODE_CL73; case XGBE_PORT_MODE_10GBASE_R: return XGBE_AN_MODE_NONE; case XGBE_PORT_MODE_SFP: @@ -1587,6 +1638,24 @@ static enum xgbe_mode xgbe_phy_cur_mode(struct xgbe_prv_data *pdata) return phy_data->cur_mode; } +static enum xgbe_mode xgbe_phy_switch_baset_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* No switching if not 10GBase-T */ + if (phy_data->port_mode != XGBE_PORT_MODE_10GBASE_T) + return xgbe_phy_cur_mode(pdata); + + switch (xgbe_phy_cur_mode(pdata)) { + case XGBE_MODE_SGMII_100: + case XGBE_MODE_SGMII_1000: + return XGBE_MODE_KR; + case XGBE_MODE_KR: + default: + return XGBE_MODE_SGMII_1000; + } +} + static enum xgbe_mode xgbe_phy_switch_bp_2500_mode(struct xgbe_prv_data *pdata) { return XGBE_MODE_KX_2500; @@ -1614,11 +1683,11 @@ static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata) case XGBE_PORT_MODE_BACKPLANE_2500: return xgbe_phy_switch_bp_2500_mode(pdata); case XGBE_PORT_MODE_1000BASE_T: - case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_switch_baset_mode(pdata); + case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_10GBASE_R: - return XGBE_MODE_UNKNOWN; case XGBE_PORT_MODE_SFP: /* No switching, so just return current mode */ return xgbe_phy_cur_mode(pdata); @@ -1627,6 +1696,34 @@ static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata) } } +static enum xgbe_mode xgbe_phy_get_basex_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_1000: + return XGBE_MODE_X; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_baset_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + return XGBE_MODE_SGMII_100; + case SPEED_1000: + return XGBE_MODE_SGMII_1000; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + static enum xgbe_mode xgbe_phy_get_sfp_mode(struct xgbe_phy_data *phy_data, int speed) { @@ -1679,11 +1776,12 @@ static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata, case XGBE_PORT_MODE_BACKPLANE_2500: return xgbe_phy_get_bp_2500_mode(speed); case XGBE_PORT_MODE_1000BASE_T: - case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_get_baset_mode(phy_data, speed); + case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_10GBASE_R: - return XGBE_MODE_UNKNOWN; + return xgbe_phy_get_basex_mode(phy_data, speed); case XGBE_PORT_MODE_SFP: return xgbe_phy_get_sfp_mode(phy_data, speed); default: @@ -1737,6 +1835,39 @@ static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, return false; } +static bool xgbe_phy_use_basex_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_X: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseT_Full); + default: + return false; + } +} + +static bool xgbe_phy_use_baset_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_SGMII_100: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_100baseT_Full); + case XGBE_MODE_SGMII_1000: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_1000baseT_Full); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + ADVERTISED_10000baseT_Full); + default: + return false; + } +} + static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { @@ -1803,11 +1934,12 @@ static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) case XGBE_PORT_MODE_BACKPLANE_2500: return xgbe_phy_use_bp_2500_mode(pdata, mode); case XGBE_PORT_MODE_1000BASE_T: - case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_use_baset_mode(pdata, mode); + case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_10GBASE_R: - return false; + return xgbe_phy_use_basex_mode(pdata, mode); case XGBE_PORT_MODE_SFP: return xgbe_phy_use_sfp_mode(pdata, mode); default: @@ -1815,6 +1947,33 @@ static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) } } +static bool xgbe_phy_valid_speed_basex_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_1000: + return (phy_data->port_mode == XGBE_PORT_MODE_1000BASE_X); + case SPEED_10000: + return (phy_data->port_mode == XGBE_PORT_MODE_10GBASE_R); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_baset_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + case SPEED_1000: + return true; + case SPEED_10000: + return (phy_data->port_mode == XGBE_PORT_MODE_10GBASE_T); + default: + return false; + } +} + static bool xgbe_phy_valid_speed_sfp_mode(struct xgbe_phy_data *phy_data, int speed) { @@ -1862,11 +2021,12 @@ static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) case XGBE_PORT_MODE_BACKPLANE_2500: return xgbe_phy_valid_speed_bp_2500_mode(speed); case XGBE_PORT_MODE_1000BASE_T: - case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_NBASE_T: case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_valid_speed_baset_mode(phy_data, speed); + case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_10GBASE_R: - return false; + return xgbe_phy_valid_speed_basex_mode(phy_data, speed); case XGBE_PORT_MODE_SFP: return xgbe_phy_valid_speed_sfp_mode(phy_data, speed); default: @@ -1992,6 +2152,121 @@ static void xgbe_phy_sfp_setup(struct xgbe_prv_data *pdata) xgbe_phy_sfp_gpio_setup(pdata); } +static int xgbe_phy_int_mdio_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int ret; + + ret = pdata->hw_if.set_gpio(pdata, phy_data->mdio_reset_gpio); + if (ret) + return ret; + + ret = pdata->hw_if.clr_gpio(pdata, phy_data->mdio_reset_gpio); + + return ret; +} + +static int xgbe_phy_i2c_mdio_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + u8 gpio_reg, gpio_ports[2], gpio_data[3]; + int ret; + + /* Read the output port registers */ + gpio_reg = 2; + ret = xgbe_phy_i2c_read(pdata, phy_data->mdio_reset_addr, + &gpio_reg, sizeof(gpio_reg), + gpio_ports, sizeof(gpio_ports)); + if (ret) + return ret; + + /* Prepare to write the GPIO data */ + gpio_data[0] = 2; + gpio_data[1] = gpio_ports[0]; + gpio_data[2] = gpio_ports[1]; + + /* Set the GPIO pin */ + if (phy_data->mdio_reset_gpio < 8) + gpio_data[1] |= (1 << (phy_data->mdio_reset_gpio % 8)); + else + gpio_data[2] |= (1 << (phy_data->mdio_reset_gpio % 8)); + + /* Write the output port registers */ + ret = xgbe_phy_i2c_write(pdata, phy_data->mdio_reset_addr, + gpio_data, sizeof(gpio_data)); + if (ret) + return ret; + + /* Clear the GPIO pin */ + if (phy_data->mdio_reset_gpio < 8) + gpio_data[1] &= ~(1 << (phy_data->mdio_reset_gpio % 8)); + else + gpio_data[2] &= ~(1 << (phy_data->mdio_reset_gpio % 8)); + + /* Write the output port registers */ + ret = xgbe_phy_i2c_write(pdata, phy_data->mdio_reset_addr, + gpio_data, sizeof(gpio_data)); + + return ret; +} + +static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + if (phy_data->conn_type != XGBE_CONN_TYPE_MDIO) + return 0; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return ret; + + if (phy_data->mdio_reset == XGBE_MDIO_RESET_I2C_GPIO) + ret = xgbe_phy_i2c_mdio_reset(pdata); + else if (phy_data->mdio_reset == XGBE_MDIO_RESET_INT_GPIO) + ret = xgbe_phy_int_mdio_reset(pdata); + + xgbe_phy_put_comm_ownership(pdata); + + return ret; +} + +static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + if (phy_data->conn_type != XGBE_CONN_TYPE_MDIO) + return 0; + + reg = XP_IOREAD(pdata, XP_PROP_3); + phy_data->mdio_reset = XP_GET_BITS(reg, XP_PROP_3, MDIO_RESET); + switch (phy_data->mdio_reset) { + case XGBE_MDIO_RESET_NONE: + case XGBE_MDIO_RESET_I2C_GPIO: + case XGBE_MDIO_RESET_INT_GPIO: + break; + default: + dev_err(pdata->dev, "unsupported MDIO reset (%#x)\n", + phy_data->mdio_reset); + return -EINVAL; + } + + if (phy_data->mdio_reset == XGBE_MDIO_RESET_I2C_GPIO) { + phy_data->mdio_reset_addr = XGBE_GPIO_ADDRESS_PCA9555 + + XP_GET_BITS(reg, XP_PROP_3, + MDIO_RESET_I2C_ADDR); + phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3, + MDIO_RESET_I2C_GPIO); + } else if (phy_data->mdio_reset == XGBE_MDIO_RESET_INT_GPIO) { + phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3, + MDIO_RESET_INT_GPIO); + } + + return 0; +} + static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -2022,7 +2297,8 @@ static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata) return false; break; case XGBE_PORT_MODE_10GBASE_T: - if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)) return false; break; @@ -2142,6 +2418,7 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; enum xgbe_mode cur_mode; + int ret; /* Reset by power cycling the PHY */ cur_mode = phy_data->cur_mode; @@ -2152,6 +2429,10 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata) return 0; /* Reset the external PHY */ + ret = xgbe_phy_mdio_reset(pdata); + if (ret) + return ret; + return phy_init_hw(phy_data->phydev); } @@ -2213,6 +2494,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) return -EINVAL; } + /* Check for and validate MDIO reset support */ + ret = xgbe_phy_mdio_reset_setup(pdata); + if (ret) + return ret; + /* Indicate current mode is unknown */ phy_data->cur_mode = XGBE_MODE_UNKNOWN; @@ -2220,6 +2506,7 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) pdata->phy.supported = 0; switch (phy_data->port_mode) { + /* Backplane support */ case XGBE_PORT_MODE_BACKPLANE: pdata->phy.supported |= SUPPORTED_Autoneg; pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; @@ -2246,12 +2533,91 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; break; + + /* MDIO 1GBase-T support */ case XGBE_PORT_MODE_1000BASE_T: + pdata->phy.supported |= SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_TP; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { + pdata->phy.supported |= SUPPORTED_100baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_100; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + pdata->phy.supported |= SUPPORTED_1000baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_1000; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; + break; + + /* MDIO Base-X support */ case XGBE_PORT_MODE_1000BASE_X: + pdata->phy.supported |= SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_FIBRE; + pdata->phy.supported |= SUPPORTED_1000baseT_Full; + phy_data->start_mode = XGBE_MODE_X; + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; + break; + + /* MDIO NBase-T support */ case XGBE_PORT_MODE_NBASE_T: + pdata->phy.supported |= SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_TP; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { + pdata->phy.supported |= SUPPORTED_100baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_100; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + pdata->phy.supported |= SUPPORTED_1000baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_1000; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500) { + pdata->phy.supported |= SUPPORTED_2500baseX_Full; + phy_data->start_mode = XGBE_MODE_KX_2500; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL45; + break; + + /* 10GBase-T support */ case XGBE_PORT_MODE_10GBASE_T: + pdata->phy.supported |= SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_TP; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { + pdata->phy.supported |= SUPPORTED_100baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_100; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + pdata->phy.supported |= SUPPORTED_1000baseT_Full; + phy_data->start_mode = XGBE_MODE_SGMII_1000; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + pdata->phy.supported |= SUPPORTED_10000baseT_Full; + phy_data->start_mode = XGBE_MODE_KR; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; + break; + + /* 10GBase-R support */ case XGBE_PORT_MODE_10GBASE_R: - return -ENODEV; + pdata->phy.supported |= SUPPORTED_Autoneg; + pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + pdata->phy.supported |= SUPPORTED_TP; + pdata->phy.supported |= SUPPORTED_10000baseT_Full; + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + pdata->phy.supported |= SUPPORTED_10000baseR_FEC; + phy_data->start_mode = XGBE_MODE_SFI; + + phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; + break; + + /* SFP support */ case XGBE_PORT_MODE_SFP: pdata->phy.supported |= SUPPORTED_Autoneg; pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index a691f84..34db470 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -289,6 +289,9 @@ /* ECC correctable error notification window (seconds) */ #define XGBE_ECC_LIMIT 60 +/* MDIO port types */ +#define XGMAC_MAX_C22_PORT 3 + struct xgbe_prv_data; struct xgbe_packet_data { @@ -675,6 +678,14 @@ struct xgbe_hw_if { void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int); int (*set_speed)(struct xgbe_prv_data *, int); + int (*set_ext_mii_mode)(struct xgbe_prv_data *, unsigned int, + enum xgbe_mdio_mode); + int (*read_ext_mii_regs)(struct xgbe_prv_data *, int, int); + int (*write_ext_mii_regs)(struct xgbe_prv_data *, int, int, u16); + + int (*set_gpio)(struct xgbe_prv_data *, unsigned int); + int (*clr_gpio)(struct xgbe_prv_data *, unsigned int); + void (*enable_tx)(struct xgbe_prv_data *); void (*disable_tx)(struct xgbe_prv_data *); void (*enable_rx)(struct xgbe_prv_data *); @@ -1111,6 +1122,7 @@ struct xgbe_prv_data { struct xgbe_phy phy; int mdio_mmd; unsigned long link_check; + struct completion mdio_complete; char an_name[IFNAMSIZ + 32]; struct workqueue_struct *an_workqueue; -- cgit v1.1 From d7445d1f0506fe26a877fac8dcdff7f4eff1328e Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 10 Nov 2016 17:11:41 -0600 Subject: amd-xgbe: Add support for a KR redriver This patch provides support for the presence of a KR redriver chip in between the device PCS and an external PHY. When a redriver chip is present the device must perform clause 73 auto-negotiation in order to set the redriver chip for the downstream connection. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-common.h | 10 + drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 45 ++- drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c | 7 + drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 427 ++++++++++++++++++++++++++-- drivers/net/ethernet/amd/xgbe/xgbe.h | 6 + 5 files changed, 458 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index ecd4f4d..5b7ba25 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -1062,6 +1062,16 @@ #define XP_PROP_4_MUX_ADDR_LO_WIDTH 3 #define XP_PROP_4_MUX_CHAN_INDEX 4 #define XP_PROP_4_MUX_CHAN_WIDTH 3 +#define XP_PROP_4_REDRV_ADDR_INDEX 16 +#define XP_PROP_4_REDRV_ADDR_WIDTH 7 +#define XP_PROP_4_REDRV_IF_INDEX 23 +#define XP_PROP_4_REDRV_IF_WIDTH 1 +#define XP_PROP_4_REDRV_LANE_INDEX 24 +#define XP_PROP_4_REDRV_LANE_WIDTH 3 +#define XP_PROP_4_REDRV_MODEL_INDEX 28 +#define XP_PROP_4_REDRV_MODEL_WIDTH 3 +#define XP_PROP_4_REDRV_PRESENT_INDEX 31 +#define XP_PROP_4_REDRV_PRESENT_WIDTH 1 /* I2C Control register offsets */ #define IC_CON 0x0000 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 622675a..0ecae70 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -179,6 +179,7 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata) { switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_enable_interrupts(pdata); break; case XGBE_AN_MODE_CL37: @@ -254,6 +255,10 @@ static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata) static void xgbe_sfi_mode(struct xgbe_prv_data *pdata) { + /* If a KR re-driver is present, change to KR mode instead */ + if (pdata->kr_redrv) + return xgbe_kr_mode(pdata); + /* Disable KR training */ xgbe_an73_disable_kr_training(pdata); @@ -433,6 +438,7 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata) { switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_restart(pdata); break; case XGBE_AN_MODE_CL37: @@ -448,6 +454,7 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata) { switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_disable(pdata); break; case XGBE_AN_MODE_CL37: @@ -687,6 +694,7 @@ static irqreturn_t xgbe_an_isr(int irq, void *data) switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_isr(pdata); break; case XGBE_AN_MODE_CL37: @@ -895,6 +903,7 @@ static void xgbe_an_state_machine(struct work_struct *work) switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_state_machine(pdata); break; case XGBE_AN_MODE_CL37: @@ -910,16 +919,18 @@ static void xgbe_an_state_machine(struct work_struct *work) static void xgbe_an37_init(struct xgbe_prv_data *pdata) { - unsigned int reg; + unsigned int advertising, reg; + + advertising = pdata->phy_if.phy_impl.an_advertising(pdata); /* Set up Advertisement register */ reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); - if (pdata->phy.advertising & ADVERTISED_Pause) + if (advertising & ADVERTISED_Pause) reg |= 0x100; else reg &= ~0x100; - if (pdata->phy.advertising & ADVERTISED_Asym_Pause) + if (advertising & ADVERTISED_Asym_Pause) reg |= 0x80; else reg &= ~0x80; @@ -954,11 +965,13 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata) static void xgbe_an73_init(struct xgbe_prv_data *pdata) { - unsigned int reg; + unsigned int advertising, reg; + + advertising = pdata->phy_if.phy_impl.an_advertising(pdata); /* Set up Advertisement register 3 first */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - if (pdata->phy.advertising & ADVERTISED_10000baseR_FEC) + if (advertising & ADVERTISED_10000baseR_FEC) reg |= 0xc000; else reg &= ~0xc000; @@ -967,13 +980,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 2 next */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); - if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) + if (advertising & ADVERTISED_10000baseKR_Full) reg |= 0x80; else reg &= ~0x80; - if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) || - (pdata->phy.advertising & ADVERTISED_2500baseX_Full)) + if ((advertising & ADVERTISED_1000baseKX_Full) || + (advertising & ADVERTISED_2500baseX_Full)) reg |= 0x20; else reg &= ~0x20; @@ -982,12 +995,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 1 last */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (pdata->phy.advertising & ADVERTISED_Pause) + if (advertising & ADVERTISED_Pause) reg |= 0x400; else reg &= ~0x400; - if (pdata->phy.advertising & ADVERTISED_Asym_Pause) + if (advertising & ADVERTISED_Asym_Pause) reg |= 0x800; else reg &= ~0x800; @@ -1006,6 +1019,7 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata) pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata); switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: xgbe_an73_init(pdata); break; case XGBE_AN_MODE_CL37: @@ -1149,10 +1163,15 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) if (ret) return ret; - if (pdata->phy.autoneg != AUTONEG_ENABLE) - return xgbe_phy_config_fixed(pdata); + if (pdata->phy.autoneg != AUTONEG_ENABLE) { + ret = xgbe_phy_config_fixed(pdata); + if (ret || !pdata->kr_redrv) + return ret; - netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n"); + netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n"); + } else { + netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n"); + } /* Disable auto-negotiation interrupt */ disable_irq(pdata->an_irq); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c index 6c64d11..c75edca 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -295,6 +295,11 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) return mode; } +static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) +{ + return pdata->phy.advertising; +} + static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) { /* Nothing uniquely required for an configuration */ @@ -831,6 +836,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if) phy_impl->an_config = xgbe_phy_an_config; + phy_impl->an_advertising = xgbe_phy_an_advertising; + phy_impl->an_outcome = xgbe_phy_an_outcome; phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 9848f74..4ba4332 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -277,6 +277,26 @@ enum xgbe_mdio_reset { XGBE_MDIO_RESET_MAX, }; +/* Re-driver related definitions */ +enum xgbe_phy_redrv_if { + XGBE_PHY_REDRV_IF_MDIO = 0, + XGBE_PHY_REDRV_IF_I2C, + XGBE_PHY_REDRV_IF_MAX, +}; + +enum xgbe_phy_redrv_model { + XGBE_PHY_REDRV_MODEL_4223 = 0, + XGBE_PHY_REDRV_MODEL_4227, + XGBE_PHY_REDRV_MODEL_MAX, +}; + +enum xgbe_phy_redrv_mode { + XGBE_PHY_REDRV_MODE_CX = 5, + XGBE_PHY_REDRV_MODE_SR = 9, +}; + +#define XGBE_PHY_REDRV_MODE_REG 0x12b0 + /* PHY related configuration information */ struct xgbe_phy_data { enum xgbe_port_mode port_mode; @@ -327,6 +347,13 @@ struct xgbe_phy_data { enum xgbe_mdio_reset mdio_reset; unsigned int mdio_reset_addr; unsigned int mdio_reset_gpio; + + /* Re-driver support */ + unsigned int redrv; + unsigned int redrv_if; + unsigned int redrv_addr; + unsigned int redrv_lane; + unsigned int redrv_model; }; /* I2C, MDIO and GPIO lines are muxed, so only one device at a time */ @@ -346,6 +373,68 @@ static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata, return pdata->i2c_if.i2c_xfer(pdata, i2c_op); } +static int xgbe_phy_redrv_write(struct xgbe_prv_data *pdata, unsigned int reg, + unsigned int val) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_i2c_op i2c_op; + __be16 *redrv_val; + u8 redrv_data[5], csum; + unsigned int i, retry; + int ret; + + /* High byte of register contains read/write indicator */ + redrv_data[0] = ((reg >> 8) & 0xff) << 1; + redrv_data[1] = reg & 0xff; + redrv_val = (__be16 *)&redrv_data[2]; + *redrv_val = cpu_to_be16(val); + + /* Calculate 1 byte checksum */ + csum = 0; + for (i = 0; i < 4; i++) { + csum += redrv_data[i]; + if (redrv_data[i] > csum) + csum++; + } + redrv_data[4] = ~csum; + + retry = 1; +again1: + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->redrv_addr; + i2c_op.len = sizeof(redrv_data); + i2c_op.buf = redrv_data; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again1; + + return ret; + } + + retry = 1; +again2: + i2c_op.cmd = XGBE_I2C_CMD_READ; + i2c_op.target = phy_data->redrv_addr; + i2c_op.len = 1; + i2c_op.buf = redrv_data; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again2; + + return ret; + } + + if (redrv_data[0] != 0xff) { + netif_dbg(pdata, drv, pdata->netdev, + "Redriver write checksum error\n"); + ret = -EIO; + } + + return ret; +} + static int xgbe_phy_i2c_write(struct xgbe_prv_data *pdata, unsigned int target, void *val, unsigned int val_len) { @@ -1144,38 +1233,49 @@ put: xgbe_phy_put_comm_ownership(pdata); } -static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) +static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; - enum xgbe_mode mode; + u16 lcl_adv = 0, rmt_adv = 0; + u8 fc; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_TP; + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; - if (pdata->phy.pause_autoneg && phy_data->phydev) { - /* Flow control is obtained from the attached PHY */ - u16 lcl_adv = 0, rmt_adv = 0; - u8 fc; + if (!phy_data->phydev) + return; - pdata->phy.tx_pause = 0; - pdata->phy.rx_pause = 0; + if (phy_data->phydev->advertising & ADVERTISED_Pause) + lcl_adv |= ADVERTISE_PAUSE_CAP; + if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause) + lcl_adv |= ADVERTISE_PAUSE_ASYM; - if (phy_data->phydev->advertising & ADVERTISED_Pause) - lcl_adv |= ADVERTISE_PAUSE_CAP; - if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause) - lcl_adv |= ADVERTISE_PAUSE_ASYM; + if (phy_data->phydev->pause) { + pdata->phy.lp_advertising |= ADVERTISED_Pause; + rmt_adv |= LPA_PAUSE_CAP; + } + if (phy_data->phydev->asym_pause) { + pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + rmt_adv |= LPA_PAUSE_ASYM; + } - if (phy_data->phydev->pause) - rmt_adv |= LPA_PAUSE_CAP; - if (phy_data->phydev->asym_pause) - rmt_adv |= LPA_PAUSE_ASYM; + fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + if (fc & FLOW_CTRL_TX) + pdata->phy.tx_pause = 1; + if (fc & FLOW_CTRL_RX) + pdata->phy.rx_pause = 1; +} - fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); - if (fc & FLOW_CTRL_TX) - pdata->phy.tx_pause = 1; - if (fc & FLOW_CTRL_RX) - pdata->phy.rx_pause = 1; - } +static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) +{ + enum xgbe_mode mode; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_TP; + + /* Use external PHY to determine flow control */ + if (pdata->phy.pause_autoneg) + xgbe_phy_phydev_flowctrl(pdata); switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) { case XGBE_SGMII_AN_LINK_SPEED_100: @@ -1249,6 +1349,83 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) return mode; } +static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + pdata->phy.lp_advertising |= ADVERTISED_Autoneg; + pdata->phy.lp_advertising |= ADVERTISED_Backplane; + + /* Use external PHY to determine flow control */ + if (pdata->phy.pause_autoneg) + xgbe_phy_phydev_flowctrl(pdata); + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + if (lp_reg & 0x20) + pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + + ad_reg &= lp_reg; + if (ad_reg & 0x80) { + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + mode = XGBE_MODE_KR; + break; + default: + mode = XGBE_MODE_SFI; + break; + } + } else if (ad_reg & 0x20) { + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + mode = XGBE_MODE_KX_1000; + break; + case XGBE_PORT_MODE_1000BASE_X: + mode = XGBE_MODE_X; + break; + case XGBE_PORT_MODE_SFP: + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + if (phy_data->phydev && + (phy_data->phydev->speed == SPEED_100)) + mode = XGBE_MODE_SGMII_100; + else + mode = XGBE_MODE_SGMII_1000; + break; + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + default: + mode = XGBE_MODE_X; + break; + } + break; + default: + if (phy_data->phydev && + (phy_data->phydev->speed == SPEED_100)) + mode = XGBE_MODE_SGMII_100; + else + mode = XGBE_MODE_SGMII_1000; + break; + } + } else { + mode = XGBE_MODE_UNKNOWN; + } + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + + return mode; +} + static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) { enum xgbe_mode mode; @@ -1311,6 +1488,8 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) switch (pdata->an_mode) { case XGBE_AN_MODE_CL73: return xgbe_phy_an73_outcome(pdata); + case XGBE_AN_MODE_CL73_REDRV: + return xgbe_phy_an73_redrv_outcome(pdata); case XGBE_AN_MODE_CL37: return xgbe_phy_an37_outcome(pdata); case XGBE_AN_MODE_CL37_SGMII: @@ -1320,6 +1499,63 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) } } +static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int advertising; + + /* Without a re-driver, just return current advertising */ + if (!phy_data->redrv) + return pdata->phy.advertising; + + /* With the KR re-driver we need to advertise a single speed */ + advertising = pdata->phy.advertising; + advertising &= ~ADVERTISED_1000baseKX_Full; + advertising &= ~ADVERTISED_10000baseKR_Full; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + advertising |= ADVERTISED_10000baseKR_Full; + break; + case XGBE_PORT_MODE_BACKPLANE_2500: + advertising |= ADVERTISED_1000baseKX_Full; + break; + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + advertising |= ADVERTISED_1000baseKX_Full; + break; + case XGBE_PORT_MODE_10GBASE_T: + if (phy_data->phydev && + (phy_data->phydev->speed == SPEED_10000)) + advertising |= ADVERTISED_10000baseKR_Full; + else + advertising |= ADVERTISED_1000baseKX_Full; + break; + case XGBE_PORT_MODE_10GBASE_R: + advertising |= ADVERTISED_10000baseKR_Full; + break; + case XGBE_PORT_MODE_SFP: + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + advertising |= ADVERTISED_1000baseKX_Full; + break; + default: + advertising |= ADVERTISED_10000baseKR_Full; + break; + } + break; + default: + advertising |= ADVERTISED_10000baseKR_Full; + break; + } + + return advertising; +} + static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -1364,6 +1600,10 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; + /* A KR re-driver will always require CL73 AN */ + if (phy_data->redrv) + return XGBE_AN_MODE_CL73_REDRV; + switch (phy_data->port_mode) { case XGBE_PORT_MODE_BACKPLANE: return XGBE_AN_MODE_CL73; @@ -1386,6 +1626,61 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) } } +static int xgbe_phy_set_redrv_mode_mdio(struct xgbe_prv_data *pdata, + enum xgbe_phy_redrv_mode mode) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + u16 redrv_reg, redrv_val; + + redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000); + redrv_val = (u16)mode; + + return pdata->hw_if.write_ext_mii_regs(pdata, phy_data->redrv_addr, + redrv_reg, redrv_val); +} + +static int xgbe_phy_set_redrv_mode_i2c(struct xgbe_prv_data *pdata, + enum xgbe_phy_redrv_mode mode) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int redrv_reg; + int ret; + + /* Calculate the register to write */ + redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000); + + ret = xgbe_phy_redrv_write(pdata, redrv_reg, mode); + + return ret; +} + +static void xgbe_phy_set_redrv_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_phy_redrv_mode mode; + int ret; + + if (!phy_data->redrv) + return; + + mode = XGBE_PHY_REDRV_MODE_CX; + if ((phy_data->port_mode == XGBE_PORT_MODE_SFP) && + (phy_data->sfp_base != XGBE_SFP_BASE_1000_CX) && + (phy_data->sfp_base != XGBE_SFP_BASE_10000_CR)) + mode = XGBE_PHY_REDRV_MODE_SR; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return; + + if (phy_data->redrv_if) + xgbe_phy_set_redrv_mode_i2c(pdata, mode); + else + xgbe_phy_set_redrv_mode_mdio(pdata, mode); + + xgbe_phy_put_comm_ownership(pdata); +} + static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata) { if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) @@ -1457,6 +1752,8 @@ static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 10G/SFI */ @@ -1492,6 +1789,8 @@ static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/X */ @@ -1516,6 +1815,8 @@ static void xgbe_phy_sgmii_1000_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/SGMII */ @@ -1540,6 +1841,8 @@ static void xgbe_phy_sgmii_100_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/SGMII */ @@ -1564,6 +1867,8 @@ static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 10G/KR */ @@ -1588,6 +1893,8 @@ static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 2.5G/KX */ @@ -1612,6 +1919,8 @@ static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata) struct xgbe_phy_data *phy_data = pdata->phy_data; unsigned int s0; + xgbe_phy_set_redrv_mode(pdata); + xgbe_phy_start_ratechange(pdata); /* 1G/KX */ @@ -2232,6 +2541,30 @@ static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata) return ret; } +static bool xgbe_phy_redrv_error(struct xgbe_phy_data *phy_data) +{ + if (!phy_data->redrv) + return false; + + if (phy_data->redrv_if >= XGBE_PHY_REDRV_IF_MAX) + return true; + + switch (phy_data->redrv_model) { + case XGBE_PHY_REDRV_MODEL_4223: + if (phy_data->redrv_lane > 3) + return true; + break; + case XGBE_PHY_REDRV_MODEL_4227: + if (phy_data->redrv_lane > 1) + return true; + break; + default: + return true; + } + + return false; +} + static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata) { struct xgbe_phy_data *phy_data = pdata->phy_data; @@ -2481,6 +2814,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr); } + reg = XP_IOREAD(pdata, XP_PROP_4); + phy_data->redrv = XP_GET_BITS(reg, XP_PROP_4, REDRV_PRESENT); + phy_data->redrv_if = XP_GET_BITS(reg, XP_PROP_4, REDRV_IF); + phy_data->redrv_addr = XP_GET_BITS(reg, XP_PROP_4, REDRV_ADDR); + phy_data->redrv_lane = XP_GET_BITS(reg, XP_PROP_4, REDRV_LANE); + phy_data->redrv_model = XP_GET_BITS(reg, XP_PROP_4, REDRV_MODEL); + if (phy_data->redrv && netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "redrv present\n"); + dev_dbg(pdata->dev, "redrv i/f=%u\n", phy_data->redrv_if); + dev_dbg(pdata->dev, "redrv addr=%#x\n", phy_data->redrv_addr); + dev_dbg(pdata->dev, "redrv lane=%u\n", phy_data->redrv_lane); + dev_dbg(pdata->dev, "redrv model=%u\n", phy_data->redrv_model); + } + /* Validate the connection requested */ if (xgbe_phy_conn_type_mismatch(pdata)) { dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n", @@ -2499,6 +2846,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) if (ret) return ret; + /* Validate the re-driver information */ + if (xgbe_phy_redrv_error(phy_data)) { + dev_err(pdata->dev, "phy re-driver settings error\n"); + return -EINVAL; + } + pdata->kr_redrv = phy_data->redrv; + /* Indicate current mode is unknown */ phy_data->cur_mode = XGBE_MODE_UNKNOWN; @@ -2651,6 +3005,29 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) dev_dbg(pdata->dev, "phy supported=%#x\n", pdata->phy.supported); + if ((phy_data->conn_type & XGBE_CONN_TYPE_MDIO) && + (phy_data->phydev_mode != XGBE_MDIO_MODE_NONE)) { + ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->mdio_addr, + phy_data->phydev_mode); + if (ret) { + dev_err(pdata->dev, + "mdio port/clause not compatible (%d/%u)\n", + phy_data->mdio_addr, phy_data->phydev_mode); + return -EINVAL; + } + } + + if (phy_data->redrv && !phy_data->redrv_if) { + ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->redrv_addr, + XGBE_MDIO_MODE_CL22); + if (ret) { + dev_err(pdata->dev, + "redriver mdio port not compatible (%u)\n", + phy_data->redrv_addr); + return -EINVAL; + } + } + /* Register for driving external PHYs */ mii = devm_mdiobus_alloc(pdata->dev); if (!mii) { @@ -2700,5 +3077,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if) phy_impl->an_config = xgbe_phy_an_config; + phy_impl->an_advertising = xgbe_phy_an_advertising; + phy_impl->an_outcome = xgbe_phy_an_outcome; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 34db470..f52a9bd 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -508,6 +508,7 @@ enum xgbe_xpcs_access { enum xgbe_an_mode { XGBE_AN_MODE_CL73 = 0, + XGBE_AN_MODE_CL73_REDRV, XGBE_AN_MODE_CL37, XGBE_AN_MODE_CL37_SGMII, XGBE_AN_MODE_NONE, @@ -807,6 +808,9 @@ struct xgbe_phy_impl_if { /* Configure auto-negotiation settings */ int (*an_config)(struct xgbe_prv_data *); + /* Set/override auto-negotiation advertisement settings */ + unsigned int (*an_advertising)(struct xgbe_prv_data *); + /* Process results of auto-negotiation */ enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); @@ -1124,6 +1128,8 @@ struct xgbe_prv_data { unsigned long link_check; struct completion mdio_complete; + unsigned int kr_redrv; + char an_name[IFNAMSIZ + 32]; struct workqueue_struct *an_workqueue; -- cgit v1.1 From b5718b5aeff1930ab06efe5683b54f1a3b2795fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 10 Nov 2016 15:03:01 +0100 Subject: net: phy: marvell: optimize logic for page changing during init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of remembering if the page was changed, just compare the current page to the saved one. This is easier and has the advantage to save a register write if the page was already restored. Signed-off-by: Uwe Kleine-König Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index c2dcf02..fa31f50 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -361,7 +361,7 @@ static int m88e1111_config_aneg(struct phy_device *phydev) static int marvell_of_reg_init(struct phy_device *phydev) { const __be32 *paddr; - int len, i, saved_page, current_page, page_changed, ret; + int len, i, saved_page, current_page, ret; if (!phydev->mdio.dev.of_node) return 0; @@ -374,7 +374,6 @@ static int marvell_of_reg_init(struct phy_device *phydev) saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE); if (saved_page < 0) return saved_page; - page_changed = 0; current_page = saved_page; ret = 0; @@ -388,7 +387,6 @@ static int marvell_of_reg_init(struct phy_device *phydev) if (reg_page != current_page) { current_page = reg_page; - page_changed = 1; ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page); if (ret < 0) goto err; @@ -411,7 +409,7 @@ static int marvell_of_reg_init(struct phy_device *phydev) } err: - if (page_changed) { + if (current_page != saved_page) { i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page); if (ret == 0) ret = i; -- cgit v1.1 From 98e4321b97cc790c6aac3fb7c92bbf13212452ee Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 13 Nov 2016 12:14:59 -0500 Subject: genetlink: Make family a signed integer. The idr_alloc(), idr_remove(), et al. routines all expect IDs to be signed integers. Therefore make the genl_family member 'id' signed too. Signed-off-by: David S. Miller --- include/net/genetlink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 3ec87ba..a34275b 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -48,7 +48,7 @@ struct genl_info; * @n_ops: number of operations supported by this family */ struct genl_family { - unsigned int id; /* private */ + int id; /* private */ unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; -- cgit v1.1 From f7ad3d4b83e694347cddc96d956143068bef32c9 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 11 Nov 2016 11:22:53 +0100 Subject: mlxsw: reg: Fix pwm_frequency field size in MFCR register The field is 7bit long. Fix it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index a61ce34..edad7cb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4424,7 +4424,7 @@ enum mlxsw_reg_mfcr_pwm_frequency { * Controls the frequency of the PWM signal. * Access: RW */ -MLXSW_ITEM32(reg, mfcr, pwm_frequency, 0x00, 0, 6); +MLXSW_ITEM32(reg, mfcr, pwm_frequency, 0x00, 0, 7); #define MLXSW_MFCR_TACHOS_MAX 10 -- cgit v1.1 From 991ddb1f6a94dcf2d0cdc9efd0a1f8fcd21cfedc Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Fri, 11 Nov 2016 15:45:24 +0200 Subject: net: ethernet: ti: davinci_cpdma: fix fixed prio cpdma ctlr configuration The dma ctlr is reseted to 0 while cpdma soft reset, thus cpdma ctlr cannot be configured after cpdma is stopped. So restoring content of cpdma ctlr while off/on procedure is needed. The cpdma ctlr off/on procedure is present while interface down/up and while changing number of channels with ethtool. In order to not restore content in many places, move it to cpdma_ctlr_start(). Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 4 -- drivers/net/ethernet/ti/davinci_cpdma.c | 102 +++++++++++++++++--------------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index b1ddf89..39d06e8 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1376,10 +1376,6 @@ static int cpsw_ndo_open(struct net_device *ndev) ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0); if (!cpsw_common_res_usage_state(cpsw)) { - /* setup tx dma to fixed prio and zero offset */ - cpdma_control_set(cpsw->dma, CPDMA_TX_PRIO_FIXED, 1); - cpdma_control_set(cpsw->dma, CPDMA_RX_BUFFER_OFFSET, 0); - /* disable priority elevation */ __raw_writel(0, &cpsw->regs->ptype); diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 8f7b420..56395ce 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -124,6 +124,29 @@ struct cpdma_chan { int int_set, int_clear, td; }; +struct cpdma_control_info { + u32 reg; + u32 shift, mask; + int access; +#define ACCESS_RO BIT(0) +#define ACCESS_WO BIT(1) +#define ACCESS_RW (ACCESS_RO | ACCESS_WO) +}; + +static struct cpdma_control_info controls[] = { + [CPDMA_CMD_IDLE] = {CPDMA_DMACONTROL, 3, 1, ACCESS_WO}, + [CPDMA_COPY_ERROR_FRAMES] = {CPDMA_DMACONTROL, 4, 1, ACCESS_RW}, + [CPDMA_RX_OFF_LEN_UPDATE] = {CPDMA_DMACONTROL, 2, 1, ACCESS_RW}, + [CPDMA_RX_OWNERSHIP_FLIP] = {CPDMA_DMACONTROL, 1, 1, ACCESS_RW}, + [CPDMA_TX_PRIO_FIXED] = {CPDMA_DMACONTROL, 0, 1, ACCESS_RW}, + [CPDMA_STAT_IDLE] = {CPDMA_DMASTATUS, 31, 1, ACCESS_RO}, + [CPDMA_STAT_TX_ERR_CODE] = {CPDMA_DMASTATUS, 20, 0xf, ACCESS_RW}, + [CPDMA_STAT_TX_ERR_CHAN] = {CPDMA_DMASTATUS, 16, 0x7, ACCESS_RW}, + [CPDMA_STAT_RX_ERR_CODE] = {CPDMA_DMASTATUS, 12, 0xf, ACCESS_RW}, + [CPDMA_STAT_RX_ERR_CHAN] = {CPDMA_DMASTATUS, 8, 0x7, ACCESS_RW}, + [CPDMA_RX_BUFFER_OFFSET] = {CPDMA_RXBUFFOFS, 0, 0xffff, ACCESS_RW}, +}; + #define tx_chan_num(chan) (chan) #define rx_chan_num(chan) ((chan) + CPDMA_MAX_CHANNELS) #define is_rx_chan(chan) ((chan)->chan_num >= CPDMA_MAX_CHANNELS) @@ -253,6 +276,31 @@ static void cpdma_desc_free(struct cpdma_desc_pool *pool, gen_pool_free(pool->gen_pool, (unsigned long)desc, pool->desc_size); } +static int _cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value) +{ + struct cpdma_control_info *info = &controls[control]; + u32 val; + + if (!ctlr->params.has_ext_regs) + return -ENOTSUPP; + + if (ctlr->state != CPDMA_STATE_ACTIVE) + return -EINVAL; + + if (control < 0 || control >= ARRAY_SIZE(controls)) + return -ENOENT; + + if ((info->access & ACCESS_WO) != ACCESS_WO) + return -EPERM; + + val = dma_reg_read(ctlr, info->reg); + val &= ~(info->mask << info->shift); + val |= (value & info->mask) << info->shift; + dma_reg_write(ctlr, info->reg, val); + + return 0; +} + struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params) { struct cpdma_ctlr *ctlr; @@ -324,6 +372,10 @@ int cpdma_ctlr_start(struct cpdma_ctlr *ctlr) if (ctlr->channels[i]) cpdma_chan_start(ctlr->channels[i]); } + + _cpdma_control_set(ctlr, CPDMA_TX_PRIO_FIXED, 1); + _cpdma_control_set(ctlr, CPDMA_RX_BUFFER_OFFSET, 0); + spin_unlock_irqrestore(&ctlr->lock, flags); return 0; } @@ -874,29 +926,6 @@ int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable) return 0; } -struct cpdma_control_info { - u32 reg; - u32 shift, mask; - int access; -#define ACCESS_RO BIT(0) -#define ACCESS_WO BIT(1) -#define ACCESS_RW (ACCESS_RO | ACCESS_WO) -}; - -static struct cpdma_control_info controls[] = { - [CPDMA_CMD_IDLE] = {CPDMA_DMACONTROL, 3, 1, ACCESS_WO}, - [CPDMA_COPY_ERROR_FRAMES] = {CPDMA_DMACONTROL, 4, 1, ACCESS_RW}, - [CPDMA_RX_OFF_LEN_UPDATE] = {CPDMA_DMACONTROL, 2, 1, ACCESS_RW}, - [CPDMA_RX_OWNERSHIP_FLIP] = {CPDMA_DMACONTROL, 1, 1, ACCESS_RW}, - [CPDMA_TX_PRIO_FIXED] = {CPDMA_DMACONTROL, 0, 1, ACCESS_RW}, - [CPDMA_STAT_IDLE] = {CPDMA_DMASTATUS, 31, 1, ACCESS_RO}, - [CPDMA_STAT_TX_ERR_CODE] = {CPDMA_DMASTATUS, 20, 0xf, ACCESS_RW}, - [CPDMA_STAT_TX_ERR_CHAN] = {CPDMA_DMASTATUS, 16, 0x7, ACCESS_RW}, - [CPDMA_STAT_RX_ERR_CODE] = {CPDMA_DMASTATUS, 12, 0xf, ACCESS_RW}, - [CPDMA_STAT_RX_ERR_CHAN] = {CPDMA_DMASTATUS, 8, 0x7, ACCESS_RW}, - [CPDMA_RX_BUFFER_OFFSET] = {CPDMA_RXBUFFOFS, 0, 0xffff, ACCESS_RW}, -}; - int cpdma_control_get(struct cpdma_ctlr *ctlr, int control) { unsigned long flags; @@ -931,35 +960,10 @@ unlock_ret: int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value) { unsigned long flags; - struct cpdma_control_info *info = &controls[control]; int ret; - u32 val; spin_lock_irqsave(&ctlr->lock, flags); - - ret = -ENOTSUPP; - if (!ctlr->params.has_ext_regs) - goto unlock_ret; - - ret = -EINVAL; - if (ctlr->state != CPDMA_STATE_ACTIVE) - goto unlock_ret; - - ret = -ENOENT; - if (control < 0 || control >= ARRAY_SIZE(controls)) - goto unlock_ret; - - ret = -EPERM; - if ((info->access & ACCESS_WO) != ACCESS_WO) - goto unlock_ret; - - val = dma_reg_read(ctlr, info->reg); - val &= ~(info->mask << info->shift); - val |= (value & info->mask) << info->shift; - dma_reg_write(ctlr, info->reg, val); - ret = 0; - -unlock_ret: + ret = _cpdma_control_set(ctlr, control, value); spin_unlock_irqrestore(&ctlr->lock, flags); return ret; } -- cgit v1.1 From b993eec0e1ec4ad1c9d04362afd4fd83261052c9 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Fri, 11 Nov 2016 16:10:47 +0200 Subject: net: ethernet: ti: davinci_cpdma: don't stop ctlr if it was stopped No need to stop ctlr if it was already stopped. It can cause timeout warns. Steps: - ifconfig eth0 down - ethtool -l eth0 rx 8 tx 8 - ethtool -l eth0 rx 1 tx 1 Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 56395ce..56708a7 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -387,7 +387,7 @@ int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr) int i; spin_lock_irqsave(&ctlr->lock, flags); - if (ctlr->state == CPDMA_STATE_TEARDOWN) { + if (ctlr->state != CPDMA_STATE_ACTIVE) { spin_unlock_irqrestore(&ctlr->lock, flags); return -EINVAL; } -- cgit v1.1 From 63fb571ee50320191a3cc2c4634c9c9f918a2c44 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 12 Nov 2016 23:16:51 +0100 Subject: net: atheros: atl1e: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. The previous implementation of set_settings was modifying the value of advertising, but with the new API, it's not possible. The structure ethtool_link_ksettings is defined as const. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c | 62 +++++++++++++--------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c index 8e3dbd4..cb489e7 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c @@ -26,73 +26,83 @@ #include "atl1e.h" -static int atl1e_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl1e_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; + u32 supported, advertising; - ecmd->supported = (SUPPORTED_10baseT_Half | + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); if (hw->nic_type == athr_l1e) - ecmd->supported |= SUPPORTED_1000baseT_Full; + supported |= SUPPORTED_1000baseT_Full; - ecmd->advertising = ADVERTISED_TP; + advertising = ADVERTISED_TP; - ecmd->advertising |= ADVERTISED_Autoneg; - ecmd->advertising |= hw->autoneg_advertised; + advertising |= ADVERTISED_Autoneg; + advertising |= hw->autoneg_advertised; - ecmd->port = PORT_TP; - ecmd->phy_address = 0; - ecmd->transceiver = XCVR_INTERNAL; + cmd->base.port = PORT_TP; + cmd->base.phy_address = 0; if (adapter->link_speed != SPEED_0) { - ethtool_cmd_speed_set(ecmd, adapter->link_speed); + cmd->base.speed = adapter->link_speed; if (adapter->link_duplex == FULL_DUPLEX) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + return 0; } -static int atl1e_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl1e_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct atl1e_adapter *adapter = netdev_priv(netdev); struct atl1e_hw *hw = &adapter->hw; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); - if (ecmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { u16 adv4, adv9; - if ((ecmd->advertising&ADVERTISE_1000_FULL)) { + if (advertising & ADVERTISE_1000_FULL) { if (hw->nic_type == athr_l1e) { hw->autoneg_advertised = - ecmd->advertising & AT_ADV_MASK; + advertising & AT_ADV_MASK; } else { clear_bit(__AT_RESETTING, &adapter->flags); return -EINVAL; } - } else if (ecmd->advertising&ADVERTISE_1000_HALF) { + } else if (advertising & ADVERTISE_1000_HALF) { clear_bit(__AT_RESETTING, &adapter->flags); return -EINVAL; } else { hw->autoneg_advertised = - ecmd->advertising & AT_ADV_MASK; + advertising & AT_ADV_MASK; } - ecmd->advertising = hw->autoneg_advertised | + advertising = hw->autoneg_advertised | ADVERTISED_TP | ADVERTISED_Autoneg; adv4 = hw->mii_autoneg_adv_reg & ~ADVERTISE_ALL; @@ -367,8 +377,6 @@ static int atl1e_nway_reset(struct net_device *netdev) } static const struct ethtool_ops atl1e_ethtool_ops = { - .get_settings = atl1e_get_settings, - .set_settings = atl1e_set_settings, .get_drvinfo = atl1e_get_drvinfo, .get_regs_len = atl1e_get_regs_len, .get_regs = atl1e_get_regs, @@ -380,6 +388,8 @@ static const struct ethtool_ops atl1e_ethtool_ops = { .get_eeprom_len = atl1e_get_eeprom_len, .get_eeprom = atl1e_get_eeprom, .set_eeprom = atl1e_set_eeprom, + .get_link_ksettings = atl1e_get_link_ksettings, + .set_link_ksettings = atl1e_set_link_ksettings, }; void atl1e_set_ethtool_ops(struct net_device *netdev) -- cgit v1.1 From 7e416ad7416307e22871a0ef0f0f14e2bb66a0d1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Nov 2016 14:17:01 +0100 Subject: netfilter: conntrack: remove unused netns_ct member since 23014011ba420 ('netfilter: conntrack: support a fixed size of 128 distinct labels') this isn't needed anymore. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netns/conntrack.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index e469e85..3d06d94 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -91,7 +91,6 @@ struct netns_ct { struct nf_ip_net nf_ct_proto; #if defined(CONFIG_NF_CONNTRACK_LABELS) unsigned int labels_used; - u8 label_words; #endif }; #endif -- cgit v1.1 From eb1a6bdc28268afa964e4c9f3399961dff9fd691 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 11 Nov 2016 13:32:38 +0100 Subject: netfilter: x_tables: simplify IS_ERR_OR_NULL to NULL test Since commit 7926dbfa4bc1 ("netfilter: don't use mutex_lock_interruptible()"), the function xt_find_table_lock can only return NULL on an error. Simplify the call sites and update the comment before the function. The semantic patch that change the code is as follows: (http://coccinelle.lip6.fr/) // @@ expression t,e; @@ t = \(xt_find_table_lock(...)\| try_then_request_module(xt_find_table_lock(...),...)\) ... when != t=e - ! IS_ERR_OR_NULL(t) + t @@ expression t,e; @@ t = \(xt_find_table_lock(...)\| try_then_request_module(xt_find_table_lock(...),...)\) ... when != t=e - IS_ERR_OR_NULL(t) + !t @@ expression t,e,e1; @@ t = \(xt_find_table_lock(...)\| try_then_request_module(xt_find_table_lock(...),...)\) ... when != t=e ?- t ? PTR_ERR(t) : e1 + e1 ... when any // Signed-off-by: Julia Lawall Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/arp_tables.c | 20 ++++++++++---------- net/ipv4/netfilter/ip_tables.c | 20 ++++++++++---------- net/ipv6/netfilter/ip6_tables.c | 20 ++++++++++---------- net/netfilter/x_tables.c | 2 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index e76ab23..39004da 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -805,7 +805,7 @@ static int get_info(struct net *net, void __user *user, #endif t = try_then_request_module(xt_find_table_lock(net, NFPROTO_ARP, name), "arptable_%s", name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { struct arpt_getinfo info; const struct xt_table_info *private = t->private; #ifdef CONFIG_COMPAT @@ -834,7 +834,7 @@ static int get_info(struct net *net, void __user *user, xt_table_unlock(t); module_put(t->me); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; #ifdef CONFIG_COMPAT if (compat) xt_compat_unlock(NFPROTO_ARP); @@ -859,7 +859,7 @@ static int get_entries(struct net *net, struct arpt_get_entries __user *uptr, get.name[sizeof(get.name) - 1] = '\0'; t = xt_find_table_lock(net, NFPROTO_ARP, get.name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { const struct xt_table_info *private = t->private; if (get.size == private->size) @@ -871,7 +871,7 @@ static int get_entries(struct net *net, struct arpt_get_entries __user *uptr, module_put(t->me); xt_table_unlock(t); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; return ret; } @@ -898,8 +898,8 @@ static int __do_replace(struct net *net, const char *name, t = try_then_request_module(xt_find_table_lock(net, NFPROTO_ARP, name), "arptable_%s", name); - if (IS_ERR_OR_NULL(t)) { - ret = t ? PTR_ERR(t) : -ENOENT; + if (!t) { + ret = -ENOENT; goto free_newinfo_counters_untrans; } @@ -1014,8 +1014,8 @@ static int do_add_counters(struct net *net, const void __user *user, return PTR_ERR(paddc); t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name); - if (IS_ERR_OR_NULL(t)) { - ret = t ? PTR_ERR(t) : -ENOENT; + if (!t) { + ret = -ENOENT; goto free; } @@ -1404,7 +1404,7 @@ static int compat_get_entries(struct net *net, xt_compat_lock(NFPROTO_ARP); t = xt_find_table_lock(net, NFPROTO_ARP, get.name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { const struct xt_table_info *private = t->private; struct xt_table_info info; @@ -1419,7 +1419,7 @@ static int compat_get_entries(struct net *net, module_put(t->me); xt_table_unlock(t); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; xt_compat_unlock(NFPROTO_ARP); return ret; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index de4fa03..46815c8 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -973,7 +973,7 @@ static int get_info(struct net *net, void __user *user, #endif t = try_then_request_module(xt_find_table_lock(net, AF_INET, name), "iptable_%s", name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { struct ipt_getinfo info; const struct xt_table_info *private = t->private; #ifdef CONFIG_COMPAT @@ -1003,7 +1003,7 @@ static int get_info(struct net *net, void __user *user, xt_table_unlock(t); module_put(t->me); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; #ifdef CONFIG_COMPAT if (compat) xt_compat_unlock(AF_INET); @@ -1028,7 +1028,7 @@ get_entries(struct net *net, struct ipt_get_entries __user *uptr, get.name[sizeof(get.name) - 1] = '\0'; t = xt_find_table_lock(net, AF_INET, get.name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { const struct xt_table_info *private = t->private; if (get.size == private->size) ret = copy_entries_to_user(private->size, @@ -1039,7 +1039,7 @@ get_entries(struct net *net, struct ipt_get_entries __user *uptr, module_put(t->me); xt_table_unlock(t); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; return ret; } @@ -1064,8 +1064,8 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, t = try_then_request_module(xt_find_table_lock(net, AF_INET, name), "iptable_%s", name); - if (IS_ERR_OR_NULL(t)) { - ret = t ? PTR_ERR(t) : -ENOENT; + if (!t) { + ret = -ENOENT; goto free_newinfo_counters_untrans; } @@ -1180,8 +1180,8 @@ do_add_counters(struct net *net, const void __user *user, return PTR_ERR(paddc); t = xt_find_table_lock(net, AF_INET, tmp.name); - if (IS_ERR_OR_NULL(t)) { - ret = t ? PTR_ERR(t) : -ENOENT; + if (!t) { + ret = -ENOENT; goto free; } @@ -1626,7 +1626,7 @@ compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr, xt_compat_lock(AF_INET); t = xt_find_table_lock(net, AF_INET, get.name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { const struct xt_table_info *private = t->private; struct xt_table_info info; ret = compat_table_info(private, &info); @@ -1640,7 +1640,7 @@ compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr, module_put(t->me); xt_table_unlock(t); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; xt_compat_unlock(AF_INET); return ret; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 7eac01d..6ff42b8 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1003,7 +1003,7 @@ static int get_info(struct net *net, void __user *user, #endif t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name), "ip6table_%s", name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { struct ip6t_getinfo info; const struct xt_table_info *private = t->private; #ifdef CONFIG_COMPAT @@ -1033,7 +1033,7 @@ static int get_info(struct net *net, void __user *user, xt_table_unlock(t); module_put(t->me); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; #ifdef CONFIG_COMPAT if (compat) xt_compat_unlock(AF_INET6); @@ -1059,7 +1059,7 @@ get_entries(struct net *net, struct ip6t_get_entries __user *uptr, get.name[sizeof(get.name) - 1] = '\0'; t = xt_find_table_lock(net, AF_INET6, get.name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { struct xt_table_info *private = t->private; if (get.size == private->size) ret = copy_entries_to_user(private->size, @@ -1070,7 +1070,7 @@ get_entries(struct net *net, struct ip6t_get_entries __user *uptr, module_put(t->me); xt_table_unlock(t); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; return ret; } @@ -1095,8 +1095,8 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name), "ip6table_%s", name); - if (IS_ERR_OR_NULL(t)) { - ret = t ? PTR_ERR(t) : -ENOENT; + if (!t) { + ret = -ENOENT; goto free_newinfo_counters_untrans; } @@ -1210,8 +1210,8 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len, if (IS_ERR(paddc)) return PTR_ERR(paddc); t = xt_find_table_lock(net, AF_INET6, tmp.name); - if (IS_ERR_OR_NULL(t)) { - ret = t ? PTR_ERR(t) : -ENOENT; + if (!t) { + ret = -ENOENT; goto free; } @@ -1647,7 +1647,7 @@ compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr, xt_compat_lock(AF_INET6); t = xt_find_table_lock(net, AF_INET6, get.name); - if (!IS_ERR_OR_NULL(t)) { + if (t) { const struct xt_table_info *private = t->private; struct xt_table_info info; ret = compat_table_info(private, &info); @@ -1661,7 +1661,7 @@ compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr, module_put(t->me); xt_table_unlock(t); } else - ret = t ? PTR_ERR(t) : -ENOENT; + ret = -ENOENT; xt_compat_unlock(AF_INET6); return ret; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index fc49774..ad818e5 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -982,7 +982,7 @@ void xt_free_table_info(struct xt_table_info *info) } EXPORT_SYMBOL(xt_free_table_info); -/* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ +/* Find table by name, grabs mutex & ref. Returns NULL on error. */ struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, const char *name) { -- cgit v1.1 From fedf18651b07f1701f9e8684b49f3b6c879577fc Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 10 Nov 2016 15:44:00 +0100 Subject: net: dsa: mv88e6xxx: Don't modify RGMII delays when not RGMII mode The RGMII modes delays can be set via strapping pings or EEPROM. Don't change them unless explicitly asked to change them. The recent refactoring of setting the MAC configuration changed this behaviours, in that CPU and DSA ports have any pre-configured RGMII delays removed. This breaks the Armada 370RD board. Restore the previous behaviour, in that RGMII delays are only applied/removed when explicitly asked for via an phy-mode being PHY_INTERFACE_MODE_RGMII* Fixes: 7340e5ecdbb1 ("net: dsa: mv88e6xxx: setup port's MAC") Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/port.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 18eeed0..e4978f6 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -63,9 +63,10 @@ static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK | PORT_PCS_CTRL_RGMII_DELAY_TXCLK; break; - default: - /* no delay */ + case PHY_INTERFACE_MODE_RGMII: break; + default: + return 0; } err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); -- cgit v1.1 From 94d66ae63151ab8db47fe468f5fe37a616f7bfeb Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 10 Nov 2016 15:44:01 +0100 Subject: net: dsa: mv88e6xxx: 6351 family also has RGMII delays The recent refactoring of setting the MAC configuration broke setting of RGMII delays, via the phy-mode, on the 6351 family. Add the missing ops to the structure. Fixes: 7340e5ecdbb1 ("net: dsa: mv88e6xxx: setup port's MAC") Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c8f824d..d6d9d66 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3196,6 +3196,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, }; @@ -3217,6 +3218,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, }; @@ -3281,6 +3283,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, }; @@ -3290,6 +3293,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, }; -- cgit v1.1 From 8d419324efeb1ac9b99a226643859a6636bc2aec Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 10 Nov 2016 12:10:29 +0100 Subject: mlxsw: spectrum_router: Add FIB abort warning Add a warning that the abort mechanism was triggered for device. Also avoid going through the procedure if abort was already done. Signed-off-by: Jiri Pirko Acked-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 348c773..df31f38 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1877,6 +1877,9 @@ static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) int i; int err; + if (mlxsw_sp->router.aborted) + return; + dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n"); for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { vr = &mlxsw_sp->router.vrs[i]; if (!vr->used) -- cgit v1.1 From d9dc8b0f8b4ec8cdc48ad5a20a3105387138be82 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 11 Nov 2016 10:20:50 -0800 Subject: net: fix sleeping for sk_wait_event() Similar to commit 14135f30e33c ("inet: fix sleeping inside inet_wait_for_connect()"), sk_wait_event() needs to fix too, because release_sock() is blocking, it changes the process state back to running after sleep, which breaks the previous prepare_to_wait(). Switch to the new wait API. Cc: Eric Dumazet Cc: Peter Zijlstra Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- crypto/algif_aead.c | 9 ++++----- crypto/algif_skcipher.c | 18 +++++++++--------- include/net/sock.h | 8 +++++--- net/core/sock.c | 8 ++++---- net/core/stream.c | 28 ++++++++++++++-------------- net/decnet/af_decnet.c | 16 ++++++++-------- net/llc/af_llc.c | 24 ++++++++++++------------ net/phonet/pep.c | 9 ++++----- net/tipc/socket.c | 24 ++++++++++++------------ net/vmw_vsock/virtio_transport_common.c | 10 +++++----- 10 files changed, 77 insertions(+), 77 deletions(-) diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 80a0f1a..8948392 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -132,28 +132,27 @@ static void aead_wmem_wakeup(struct sock *sk) static int aead_wait_for_data(struct sock *sk, unsigned flags) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct alg_sock *ask = alg_sk(sk); struct aead_ctx *ctx = ask->private; long timeout; - DEFINE_WAIT(wait); int err = -ERESTARTSYS; if (flags & MSG_DONTWAIT) return -EAGAIN; sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); - + add_wait_queue(sk_sleep(sk), &wait); for (;;) { if (signal_pending(current)) break; - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); timeout = MAX_SCHEDULE_TIMEOUT; - if (sk_wait_event(sk, &timeout, !ctx->more)) { + if (sk_wait_event(sk, &timeout, !ctx->more, &wait)) { err = 0; break; } } - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 28556fc..1e38aaa 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -199,26 +199,26 @@ static void skcipher_free_sgl(struct sock *sk) static int skcipher_wait_for_wmem(struct sock *sk, unsigned flags) { - long timeout; - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); int err = -ERESTARTSYS; + long timeout; if (flags & MSG_DONTWAIT) return -EAGAIN; sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); + add_wait_queue(sk_sleep(sk), &wait); for (;;) { if (signal_pending(current)) break; - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); timeout = MAX_SCHEDULE_TIMEOUT; - if (sk_wait_event(sk, &timeout, skcipher_writable(sk))) { + if (sk_wait_event(sk, &timeout, skcipher_writable(sk), &wait)) { err = 0; break; } } - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); return err; } @@ -242,10 +242,10 @@ static void skcipher_wmem_wakeup(struct sock *sk) static int skcipher_wait_for_data(struct sock *sk, unsigned flags) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct alg_sock *ask = alg_sk(sk); struct skcipher_ctx *ctx = ask->private; long timeout; - DEFINE_WAIT(wait); int err = -ERESTARTSYS; if (flags & MSG_DONTWAIT) { @@ -254,17 +254,17 @@ static int skcipher_wait_for_data(struct sock *sk, unsigned flags) sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); + add_wait_queue(sk_sleep(sk), &wait); for (;;) { if (signal_pending(current)) break; - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); timeout = MAX_SCHEDULE_TIMEOUT; - if (sk_wait_event(sk, &timeout, ctx->used)) { + if (sk_wait_event(sk, &timeout, ctx->used, &wait)) { err = 0; break; } } - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); diff --git a/include/net/sock.h b/include/net/sock.h index cf617ee..9d905ed 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -915,14 +915,16 @@ static inline void sock_rps_reset_rxhash(struct sock *sk) #endif } -#define sk_wait_event(__sk, __timeo, __condition) \ +#define sk_wait_event(__sk, __timeo, __condition, __wait) \ ({ int __rc; \ release_sock(__sk); \ __rc = __condition; \ if (!__rc) { \ - *(__timeo) = schedule_timeout(*(__timeo)); \ + *(__timeo) = wait_woken(__wait, \ + TASK_INTERRUPTIBLE, \ + *(__timeo)); \ } \ - sched_annotate_sleep(); \ + sched_annotate_sleep(); \ lock_sock(__sk); \ __rc = __condition; \ __rc; \ diff --git a/net/core/sock.c b/net/core/sock.c index 40dbc13..0397928 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2078,14 +2078,14 @@ void __sk_flush_backlog(struct sock *sk) */ int sk_wait_data(struct sock *sk, long *timeo, const struct sk_buff *skb) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); int rc; - DEFINE_WAIT(wait); - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + add_wait_queue(sk_sleep(sk), &wait); sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); - rc = sk_wait_event(sk, timeo, skb_peek_tail(&sk->sk_receive_queue) != skb); + rc = sk_wait_event(sk, timeo, skb_peek_tail(&sk->sk_receive_queue) != skb, &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); return rc; } EXPORT_SYMBOL(sk_wait_data); diff --git a/net/core/stream.c b/net/core/stream.c index 1086c8b..f575bcf 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -53,8 +53,8 @@ void sk_stream_write_space(struct sock *sk) */ int sk_stream_wait_connect(struct sock *sk, long *timeo_p) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct task_struct *tsk = current; - DEFINE_WAIT(wait); int done; do { @@ -68,13 +68,13 @@ int sk_stream_wait_connect(struct sock *sk, long *timeo_p) if (signal_pending(tsk)) return sock_intr_errno(*timeo_p); - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + add_wait_queue(sk_sleep(sk), &wait); sk->sk_write_pending++; done = sk_wait_event(sk, timeo_p, !sk->sk_err && !((1 << sk->sk_state) & - ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))); - finish_wait(sk_sleep(sk), &wait); + ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)), &wait); + remove_wait_queue(sk_sleep(sk), &wait); sk->sk_write_pending--; } while (!done); return 0; @@ -94,16 +94,16 @@ static inline int sk_stream_closing(struct sock *sk) void sk_stream_wait_close(struct sock *sk, long timeout) { if (timeout) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + + add_wait_queue(sk_sleep(sk), &wait); do { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (sk_wait_event(sk, &timeout, !sk_stream_closing(sk))) + if (sk_wait_event(sk, &timeout, !sk_stream_closing(sk), &wait)) break; } while (!signal_pending(current) && timeout); - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); } } EXPORT_SYMBOL(sk_stream_wait_close); @@ -119,16 +119,16 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p) long vm_wait = 0; long current_timeo = *timeo_p; bool noblock = (*timeo_p ? false : true); - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); if (sk_stream_memory_free(sk)) current_timeo = vm_wait = (prandom_u32() % (HZ / 5)) + 2; + add_wait_queue(sk_sleep(sk), &wait); + while (1) { sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) goto do_error; if (!*timeo_p) { @@ -147,7 +147,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p) sk_wait_event(sk, ¤t_timeo, sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN) || (sk_stream_memory_free(sk) && - !vm_wait)); + !vm_wait), &wait); sk->sk_write_pending--; if (vm_wait) { @@ -161,7 +161,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p) *timeo_p = current_timeo; } out: - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); return err; do_error: diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 13d6b1a..a90ed67 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -1718,7 +1718,7 @@ static int dn_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, * See if there is data ready to read, sleep if there isn't */ for(;;) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); if (sk->sk_err) goto out; @@ -1749,11 +1749,11 @@ static int dn_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, goto out; } - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + add_wait_queue(sk_sleep(sk), &wait); sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); - sk_wait_event(sk, &timeo, dn_data_ready(sk, queue, flags, target)); + sk_wait_event(sk, &timeo, dn_data_ready(sk, queue, flags, target), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); } skb_queue_walk_safe(queue, skb, n) { @@ -1999,19 +1999,19 @@ static int dn_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) * size. */ if (dn_queue_too_long(scp, queue, flags)) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); if (flags & MSG_DONTWAIT) { err = -EWOULDBLOCK; goto out; } - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + add_wait_queue(sk_sleep(sk), &wait); sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); sk_wait_event(sk, &timeo, - !dn_queue_too_long(scp, queue, flags)); + !dn_queue_too_long(scp, queue, flags), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); continue; } diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index db916cf..5e92963 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -532,12 +532,12 @@ out: static int llc_ui_wait_for_disc(struct sock *sk, long timeout) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); int rc = 0; + add_wait_queue(sk_sleep(sk), &wait); while (1) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - if (sk_wait_event(sk, &timeout, sk->sk_state == TCP_CLOSE)) + if (sk_wait_event(sk, &timeout, sk->sk_state == TCP_CLOSE, &wait)) break; rc = -ERESTARTSYS; if (signal_pending(current)) @@ -547,39 +547,39 @@ static int llc_ui_wait_for_disc(struct sock *sk, long timeout) break; rc = 0; } - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); return rc; } static bool llc_ui_wait_for_conn(struct sock *sk, long timeout) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + add_wait_queue(sk_sleep(sk), &wait); while (1) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - if (sk_wait_event(sk, &timeout, sk->sk_state != TCP_SYN_SENT)) + if (sk_wait_event(sk, &timeout, sk->sk_state != TCP_SYN_SENT, &wait)) break; if (signal_pending(current) || !timeout) break; } - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); return timeout; } static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct llc_sock *llc = llc_sk(sk); int rc; + add_wait_queue(sk_sleep(sk), &wait); while (1) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); rc = 0; if (sk_wait_event(sk, &timeout, (sk->sk_shutdown & RCV_SHUTDOWN) || (!llc_data_accept_state(llc->state) && !llc->remote_busy_flag && - !llc->p_flag))) + !llc->p_flag), &wait)) break; rc = -ERESTARTSYS; if (signal_pending(current)) @@ -588,7 +588,7 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout) if (!timeout) break; } - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); return rc; } diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 850a86c..8bad562 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -1167,7 +1167,7 @@ disabled: /* Wait until flow control allows TX */ done = atomic_read(&pn->tx_credits); while (!done) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); if (!timeo) { err = -EAGAIN; @@ -1178,10 +1178,9 @@ disabled: goto out; } - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits)); - finish_wait(sk_sleep(sk), &wait); + add_wait_queue(sk_sleep(sk), &wait); + done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits), &wait); + remove_wait_queue(sk_sleep(sk), &wait); if (sk->sk_state != TCP_ESTABLISHED) goto disabled; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 1493963..22d92f0 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -876,9 +876,9 @@ exit: static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); - DEFINE_WAIT(wait); int done; do { @@ -892,9 +892,9 @@ static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) if (signal_pending(current)) return sock_intr_errno(*timeo_p); - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - done = sk_wait_event(sk, timeo_p, !tsk->link_cong); - finish_wait(sk_sleep(sk), &wait); + add_wait_queue(sk_sleep(sk), &wait); + done = sk_wait_event(sk, timeo_p, !tsk->link_cong, &wait); + remove_wait_queue(sk_sleep(sk), &wait); } while (!done); return 0; } @@ -1031,9 +1031,9 @@ new_mtu: static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); - DEFINE_WAIT(wait); int done; do { @@ -1049,12 +1049,12 @@ static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) if (signal_pending(current)) return sock_intr_errno(*timeo_p); - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + add_wait_queue(sk_sleep(sk), &wait); done = sk_wait_event(sk, timeo_p, (!tsk->link_cong && !tsk_conn_cong(tsk)) || - !tipc_sk_connected(sk)); - finish_wait(sk_sleep(sk), &wait); + !tipc_sk_connected(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); } while (!done); return 0; } @@ -1929,8 +1929,8 @@ xmit: static int tipc_wait_for_connect(struct socket *sock, long *timeo_p) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct sock *sk = sock->sk; - DEFINE_WAIT(wait); int done; do { @@ -1942,10 +1942,10 @@ static int tipc_wait_for_connect(struct socket *sock, long *timeo_p) if (signal_pending(current)) return sock_intr_errno(*timeo_p); - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + add_wait_queue(sk_sleep(sk), &wait); done = sk_wait_event(sk, timeo_p, - sk->sk_state != TIPC_CONNECTING); - finish_wait(sk_sleep(sk), &wait); + sk->sk_state != TIPC_CONNECTING, &wait); + remove_wait_queue(sk_sleep(sk), &wait); } while (!done); return 0; } diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index a53b3a1..687e9fd 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -619,17 +619,17 @@ static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt) static void virtio_transport_wait_close(struct sock *sk, long timeout) { if (timeout) { - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + + add_wait_queue(sk_sleep(sk), &wait); do { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); if (sk_wait_event(sk, &timeout, - sock_flag(sk, SOCK_DONE))) + sock_flag(sk, SOCK_DONE), &wait)) break; } while (!signal_pending(current) && timeout); - finish_wait(sk_sleep(sk), &wait); + remove_wait_queue(sk_sleep(sk), &wait); } } -- cgit v1.1 From 36a4e690a7cb1925e16fd2202870b7a141509de6 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Fri, 11 Nov 2016 23:30:37 +0100 Subject: net: alx: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/ethtool.c | 59 ++++++++++++++++++------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/ethtool.c b/drivers/net/ethernet/atheros/alx/ethtool.c index 08e22df..2f4eabf 100644 --- a/drivers/net/ethernet/atheros/alx/ethtool.c +++ b/drivers/net/ethernet/atheros/alx/ethtool.c @@ -125,64 +125,75 @@ static u32 alx_get_supported_speeds(struct alx_hw *hw) return supported; } -static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int alx_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct alx_priv *alx = netdev_priv(netdev); struct alx_hw *hw = &alx->hw; + u32 supported, advertising; - ecmd->supported = SUPPORTED_Autoneg | + supported = SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_Pause | SUPPORTED_Asym_Pause; if (alx_hw_giga(hw)) - ecmd->supported |= SUPPORTED_1000baseT_Full; - ecmd->supported |= alx_get_supported_speeds(hw); + supported |= SUPPORTED_1000baseT_Full; + supported |= alx_get_supported_speeds(hw); - ecmd->advertising = ADVERTISED_TP; + advertising = ADVERTISED_TP; if (hw->adv_cfg & ADVERTISED_Autoneg) - ecmd->advertising |= hw->adv_cfg; + advertising |= hw->adv_cfg; - ecmd->port = PORT_TP; - ecmd->phy_address = 0; + cmd->base.port = PORT_TP; + cmd->base.phy_address = 0; if (hw->adv_cfg & ADVERTISED_Autoneg) - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; else - ecmd->autoneg = AUTONEG_DISABLE; - ecmd->transceiver = XCVR_INTERNAL; + cmd->base.autoneg = AUTONEG_DISABLE; if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) { if (hw->flowctrl & ALX_FC_RX) { - ecmd->advertising |= ADVERTISED_Pause; + advertising |= ADVERTISED_Pause; if (!(hw->flowctrl & ALX_FC_TX)) - ecmd->advertising |= ADVERTISED_Asym_Pause; + advertising |= ADVERTISED_Asym_Pause; } else if (hw->flowctrl & ALX_FC_TX) { - ecmd->advertising |= ADVERTISED_Asym_Pause; + advertising |= ADVERTISED_Asym_Pause; } } - ethtool_cmd_speed_set(ecmd, hw->link_speed); - ecmd->duplex = hw->duplex; + cmd->base.speed = hw->link_speed; + cmd->base.duplex = hw->duplex; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int alx_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct alx_priv *alx = netdev_priv(netdev); struct alx_hw *hw = &alx->hw; u32 adv_cfg; + u32 advertising; ASSERT_RTNL(); - if (ecmd->autoneg == AUTONEG_ENABLE) { - if (ecmd->advertising & ~alx_get_supported_speeds(hw)) + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); + + if (cmd->base.autoneg == AUTONEG_ENABLE) { + if (advertising & ~alx_get_supported_speeds(hw)) return -EINVAL; - adv_cfg = ecmd->advertising | ADVERTISED_Autoneg; + adv_cfg = advertising | ADVERTISED_Autoneg; } else { - adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd), - ecmd->duplex); + adv_cfg = alx_speed_to_ethadv(cmd->base.speed, + cmd->base.duplex); if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full) return -EINVAL; @@ -300,8 +311,6 @@ static int alx_get_sset_count(struct net_device *netdev, int sset) } const struct ethtool_ops alx_ethtool_ops = { - .get_settings = alx_get_settings, - .set_settings = alx_set_settings, .get_pauseparam = alx_get_pauseparam, .set_pauseparam = alx_set_pauseparam, .get_msglevel = alx_get_msglevel, @@ -310,4 +319,6 @@ const struct ethtool_ops alx_ethtool_ops = { .get_strings = alx_get_strings, .get_sset_count = alx_get_sset_count, .get_ethtool_stats = alx_get_ethtool_stats, + .get_link_ksettings = alx_get_link_ksettings, + .set_link_ksettings = alx_set_link_ksettings, }; -- cgit v1.1 From 58046c7043d0a9ca66ae77edd20397643d449cac Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 12 Nov 2016 17:32:49 +0100 Subject: net: atheros: atl1c: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c | 54 ++++++++++++---------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c index 872b7ab..cfe86a2 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c @@ -26,46 +26,52 @@ #include "atl1c.h" -static int atl1c_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl1c_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; + u32 supported, advertising; - ecmd->supported = (SUPPORTED_10baseT_Half | + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) - ecmd->supported |= SUPPORTED_1000baseT_Full; + supported |= SUPPORTED_1000baseT_Full; - ecmd->advertising = ADVERTISED_TP; + advertising = ADVERTISED_TP; - ecmd->advertising |= hw->autoneg_advertised; + advertising |= hw->autoneg_advertised; - ecmd->port = PORT_TP; - ecmd->phy_address = 0; - ecmd->transceiver = XCVR_INTERNAL; + cmd->base.port = PORT_TP; + cmd->base.phy_address = 0; if (adapter->link_speed != SPEED_0) { - ethtool_cmd_speed_set(ecmd, adapter->link_speed); + cmd->base.speed = adapter->link_speed; if (adapter->link_duplex == FULL_DUPLEX) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + return 0; } -static int atl1c_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl1c_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct atl1c_adapter *adapter = netdev_priv(netdev); struct atl1c_hw *hw = &adapter->hw; @@ -74,12 +80,12 @@ static int atl1c_set_settings(struct net_device *netdev, while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) msleep(1); - if (ecmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { autoneg_advertised = ADVERTISED_Autoneg; } else { - u32 speed = ethtool_cmd_speed(ecmd); + u32 speed = cmd->base.speed; if (speed == SPEED_1000) { - if (ecmd->duplex != DUPLEX_FULL) { + if (cmd->base.duplex != DUPLEX_FULL) { if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "1000M half is invalid\n"); @@ -88,12 +94,12 @@ static int atl1c_set_settings(struct net_device *netdev, } autoneg_advertised = ADVERTISED_1000baseT_Full; } else if (speed == SPEED_100) { - if (ecmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) autoneg_advertised = ADVERTISED_100baseT_Full; else autoneg_advertised = ADVERTISED_100baseT_Half; } else { - if (ecmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) autoneg_advertised = ADVERTISED_10baseT_Full; else autoneg_advertised = ADVERTISED_10baseT_Half; @@ -284,8 +290,6 @@ static int atl1c_nway_reset(struct net_device *netdev) } static const struct ethtool_ops atl1c_ethtool_ops = { - .get_settings = atl1c_get_settings, - .set_settings = atl1c_set_settings, .get_drvinfo = atl1c_get_drvinfo, .get_regs_len = atl1c_get_regs_len, .get_regs = atl1c_get_regs, @@ -297,6 +301,8 @@ static const struct ethtool_ops atl1c_ethtool_ops = { .get_link = ethtool_op_get_link, .get_eeprom_len = atl1c_get_eeprom_len, .get_eeprom = atl1c_get_eeprom, + .get_link_ksettings = atl1c_get_link_ksettings, + .set_link_ksettings = atl1c_set_link_ksettings, }; void atl1c_set_ethtool_ops(struct net_device *netdev) -- cgit v1.1 From 1dae02b3719a343f060412179a048833b78680c1 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 13 Nov 2016 18:35:14 +0100 Subject: net: atheros: atl1: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. The previous implementation of set_settings was modifying the value of advertising, but with the new API, it's not possible. The structure ethtool_link_ksettings is defined as const. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atlx/atl1.c | 66 +++++++++++++++++--------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index 9aede18..7dad8e4 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -3214,66 +3214,72 @@ static int atl1_get_sset_count(struct net_device *netdev, int sset) } } -static int atl1_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl1_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; + u32 supported, advertising; - ecmd->supported = (SUPPORTED_10baseT_Half | + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); - ecmd->advertising = ADVERTISED_TP; + advertising = ADVERTISED_TP; if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) { - ecmd->advertising |= ADVERTISED_Autoneg; + advertising |= ADVERTISED_Autoneg; if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) { - ecmd->advertising |= ADVERTISED_Autoneg; - ecmd->advertising |= + advertising |= ADVERTISED_Autoneg; + advertising |= (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Full); } else - ecmd->advertising |= (ADVERTISED_1000baseT_Full); + advertising |= (ADVERTISED_1000baseT_Full); } - ecmd->port = PORT_TP; - ecmd->phy_address = 0; - ecmd->transceiver = XCVR_INTERNAL; + cmd->base.port = PORT_TP; + cmd->base.phy_address = 0; if (netif_carrier_ok(adapter->netdev)) { u16 link_speed, link_duplex; atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex); - ethtool_cmd_speed_set(ecmd, link_speed); + cmd->base.speed = link_speed; if (link_duplex == FULL_DUPLEX) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || hw->media_type == MEDIA_TYPE_1000M_FULL) - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; else - ecmd->autoneg = AUTONEG_DISABLE; + cmd->base.autoneg = AUTONEG_DISABLE; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int atl1_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl1_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct atl1_adapter *adapter = netdev_priv(netdev); struct atl1_hw *hw = &adapter->hw; u16 phy_data; int ret_val = 0; u16 old_media_type = hw->media_type; + u32 advertising; if (netif_running(adapter->netdev)) { if (netif_msg_link(adapter)) @@ -3282,12 +3288,12 @@ static int atl1_set_settings(struct net_device *netdev, atl1_down(adapter); } - if (ecmd->autoneg == AUTONEG_ENABLE) + if (cmd->base.autoneg == AUTONEG_ENABLE) hw->media_type = MEDIA_TYPE_AUTO_SENSOR; else { - u32 speed = ethtool_cmd_speed(ecmd); + u32 speed = cmd->base.speed; if (speed == SPEED_1000) { - if (ecmd->duplex != DUPLEX_FULL) { + if (cmd->base.duplex != DUPLEX_FULL) { if (netif_msg_link(adapter)) dev_warn(&adapter->pdev->dev, "1000M half is invalid\n"); @@ -3296,12 +3302,12 @@ static int atl1_set_settings(struct net_device *netdev, } hw->media_type = MEDIA_TYPE_1000M_FULL; } else if (speed == SPEED_100) { - if (ecmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) hw->media_type = MEDIA_TYPE_100M_FULL; else hw->media_type = MEDIA_TYPE_100M_HALF; } else { - if (ecmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) hw->media_type = MEDIA_TYPE_10M_FULL; else hw->media_type = MEDIA_TYPE_10M_HALF; @@ -3309,7 +3315,7 @@ static int atl1_set_settings(struct net_device *netdev, } switch (hw->media_type) { case MEDIA_TYPE_AUTO_SENSOR: - ecmd->advertising = + advertising = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | @@ -3318,12 +3324,12 @@ static int atl1_set_settings(struct net_device *netdev, ADVERTISED_Autoneg | ADVERTISED_TP; break; case MEDIA_TYPE_1000M_FULL: - ecmd->advertising = + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_TP; break; default: - ecmd->advertising = 0; + advertising = 0; break; } if (atl1_phy_setup_autoneg_adv(hw)) { @@ -3663,8 +3669,6 @@ static int atl1_nway_reset(struct net_device *netdev) } static const struct ethtool_ops atl1_ethtool_ops = { - .get_settings = atl1_get_settings, - .set_settings = atl1_set_settings, .get_drvinfo = atl1_get_drvinfo, .get_wol = atl1_get_wol, .set_wol = atl1_set_wol, @@ -3681,6 +3685,8 @@ static const struct ethtool_ops atl1_ethtool_ops = { .nway_reset = atl1_nway_reset, .get_ethtool_stats = atl1_get_ethtool_stats, .get_sset_count = atl1_get_sset_count, + .get_link_ksettings = atl1_get_link_ksettings, + .set_link_ksettings = atl1_set_link_ksettings, }; module_pci_driver(atl1_driver); -- cgit v1.1 From a788859649b75a6e4514337a9a5d4a60fd709904 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 13 Nov 2016 18:53:28 +0100 Subject: net: atheros: atl2: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. The previous implementation of set_settings was modifying the value of advertising, but with the new API, it's not possible. The structure ethtool_link_ksettings is defined as const. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atlx/atl2.c | 66 +++++++++++++++++--------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 6911394..63f2dee 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -1737,81 +1737,87 @@ static void atl2_write_pci_cfg(struct atl2_hw *hw, u32 reg, u16 *value) pci_write_config_word(adapter->pdev, reg, *value); } -static int atl2_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl2_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; + u32 supported, advertising; - ecmd->supported = (SUPPORTED_10baseT_Half | + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP); - ecmd->advertising = ADVERTISED_TP; + advertising = ADVERTISED_TP; - ecmd->advertising |= ADVERTISED_Autoneg; - ecmd->advertising |= hw->autoneg_advertised; + advertising |= ADVERTISED_Autoneg; + advertising |= hw->autoneg_advertised; - ecmd->port = PORT_TP; - ecmd->phy_address = 0; - ecmd->transceiver = XCVR_INTERNAL; + cmd->base.port = PORT_TP; + cmd->base.phy_address = 0; if (adapter->link_speed != SPEED_0) { - ethtool_cmd_speed_set(ecmd, adapter->link_speed); + cmd->base.speed = adapter->link_speed; if (adapter->link_duplex == FULL_DUPLEX) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + return 0; } -static int atl2_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int atl2_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct atl2_adapter *adapter = netdev_priv(netdev); struct atl2_hw *hw = &adapter->hw; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags)) msleep(1); - if (ecmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { #define MY_ADV_MASK (ADVERTISE_10_HALF | \ ADVERTISE_10_FULL | \ ADVERTISE_100_HALF| \ ADVERTISE_100_FULL) - if ((ecmd->advertising & MY_ADV_MASK) == MY_ADV_MASK) { + if ((advertising & MY_ADV_MASK) == MY_ADV_MASK) { hw->MediaType = MEDIA_TYPE_AUTO_SENSOR; hw->autoneg_advertised = MY_ADV_MASK; - } else if ((ecmd->advertising & MY_ADV_MASK) == - ADVERTISE_100_FULL) { + } else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_FULL) { hw->MediaType = MEDIA_TYPE_100M_FULL; hw->autoneg_advertised = ADVERTISE_100_FULL; - } else if ((ecmd->advertising & MY_ADV_MASK) == - ADVERTISE_100_HALF) { + } else if ((advertising & MY_ADV_MASK) == ADVERTISE_100_HALF) { hw->MediaType = MEDIA_TYPE_100M_HALF; hw->autoneg_advertised = ADVERTISE_100_HALF; - } else if ((ecmd->advertising & MY_ADV_MASK) == - ADVERTISE_10_FULL) { + } else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_FULL) { hw->MediaType = MEDIA_TYPE_10M_FULL; hw->autoneg_advertised = ADVERTISE_10_FULL; - } else if ((ecmd->advertising & MY_ADV_MASK) == - ADVERTISE_10_HALF) { + } else if ((advertising & MY_ADV_MASK) == ADVERTISE_10_HALF) { hw->MediaType = MEDIA_TYPE_10M_HALF; hw->autoneg_advertised = ADVERTISE_10_HALF; } else { clear_bit(__ATL2_RESETTING, &adapter->flags); return -EINVAL; } - ecmd->advertising = hw->autoneg_advertised | + advertising = hw->autoneg_advertised | ADVERTISED_TP | ADVERTISED_Autoneg; } else { clear_bit(__ATL2_RESETTING, &adapter->flags); @@ -2080,8 +2086,6 @@ static int atl2_nway_reset(struct net_device *netdev) } static const struct ethtool_ops atl2_ethtool_ops = { - .get_settings = atl2_get_settings, - .set_settings = atl2_set_settings, .get_drvinfo = atl2_get_drvinfo, .get_regs_len = atl2_get_regs_len, .get_regs = atl2_get_regs, @@ -2094,6 +2098,8 @@ static const struct ethtool_ops atl2_ethtool_ops = { .get_eeprom_len = atl2_get_eeprom_len, .get_eeprom = atl2_get_eeprom, .set_eeprom = atl2_set_eeprom, + .get_link_ksettings = atl2_get_link_ksettings, + .set_link_ksettings = atl2_set_link_ksettings, }; #define LBYTESWAP(a) ((((a) & 0x00ff00ff) << 8) | \ -- cgit v1.1 From 535e7b4b5ef220be374b895684f274872aebd0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Sun, 13 Nov 2016 19:44:03 +0100 Subject: bpf: Use u64_to_user_ptr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the custom u64_to_ptr() function with the u64_to_user_ptr() macro. Signed-off-by: Mickaël Salaün Cc: Alexei Starovoitov Cc: Arnd Bergmann Cc: Daniel Borkmann Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/syscall.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 23eb205..cdc0654 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -17,6 +17,7 @@ #include #include #include +#include DEFINE_PER_CPU(int, bpf_prog_active); @@ -252,12 +253,6 @@ struct bpf_map *bpf_map_get_with_uref(u32 ufd) return map; } -/* helper to convert user pointers passed inside __aligned_u64 fields */ -static void __user *u64_to_ptr(__u64 val) -{ - return (void __user *) (unsigned long) val; -} - int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value) { return -ENOTSUPP; @@ -268,8 +263,8 @@ int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value) static int map_lookup_elem(union bpf_attr *attr) { - void __user *ukey = u64_to_ptr(attr->key); - void __user *uvalue = u64_to_ptr(attr->value); + void __user *ukey = u64_to_user_ptr(attr->key); + void __user *uvalue = u64_to_user_ptr(attr->value); int ufd = attr->map_fd; struct bpf_map *map; void *key, *value, *ptr; @@ -342,8 +337,8 @@ err_put: static int map_update_elem(union bpf_attr *attr) { - void __user *ukey = u64_to_ptr(attr->key); - void __user *uvalue = u64_to_ptr(attr->value); + void __user *ukey = u64_to_user_ptr(attr->key); + void __user *uvalue = u64_to_user_ptr(attr->value); int ufd = attr->map_fd; struct bpf_map *map; void *key, *value; @@ -420,7 +415,7 @@ err_put: static int map_delete_elem(union bpf_attr *attr) { - void __user *ukey = u64_to_ptr(attr->key); + void __user *ukey = u64_to_user_ptr(attr->key); int ufd = attr->map_fd; struct bpf_map *map; struct fd f; @@ -464,8 +459,8 @@ err_put: static int map_get_next_key(union bpf_attr *attr) { - void __user *ukey = u64_to_ptr(attr->key); - void __user *unext_key = u64_to_ptr(attr->next_key); + void __user *ukey = u64_to_user_ptr(attr->key); + void __user *unext_key = u64_to_user_ptr(attr->next_key); int ufd = attr->map_fd; struct bpf_map *map; void *key, *next_key; @@ -741,7 +736,7 @@ static int bpf_prog_load(union bpf_attr *attr) return -EINVAL; /* copy eBPF program license from user space */ - if (strncpy_from_user(license, u64_to_ptr(attr->license), + if (strncpy_from_user(license, u64_to_user_ptr(attr->license), sizeof(license) - 1) < 0) return -EFAULT; license[sizeof(license) - 1] = 0; @@ -771,7 +766,7 @@ static int bpf_prog_load(union bpf_attr *attr) prog->len = attr->insn_cnt; err = -EFAULT; - if (copy_from_user(prog->insns, u64_to_ptr(attr->insns), + if (copy_from_user(prog->insns, u64_to_user_ptr(attr->insns), prog->len * sizeof(struct bpf_insn)) != 0) goto free_prog; @@ -822,7 +817,7 @@ static int bpf_obj_pin(const union bpf_attr *attr) if (CHECK_ATTR(BPF_OBJ)) return -EINVAL; - return bpf_obj_pin_user(attr->bpf_fd, u64_to_ptr(attr->pathname)); + return bpf_obj_pin_user(attr->bpf_fd, u64_to_user_ptr(attr->pathname)); } static int bpf_obj_get(const union bpf_attr *attr) @@ -830,7 +825,7 @@ static int bpf_obj_get(const union bpf_attr *attr) if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0) return -EINVAL; - return bpf_obj_get_user(u64_to_ptr(attr->pathname)); + return bpf_obj_get_user(u64_to_user_ptr(attr->pathname)); } SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) -- cgit v1.1 From d94d02547ef9043ac04484088f7a6cd264ad7972 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Mon, 14 Nov 2016 08:24:19 +0800 Subject: driver: macvlan: Replace integer number with bool value The return value of function macvlan_addr_busy is used as bool value, so use bool value instead of integer number "1" and "0". Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index a064415..d0361f3 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -179,20 +179,20 @@ static void macvlan_hash_change_addr(struct macvlan_dev *vlan, macvlan_hash_add(vlan); } -static int macvlan_addr_busy(const struct macvlan_port *port, - const unsigned char *addr) +static bool macvlan_addr_busy(const struct macvlan_port *port, + const unsigned char *addr) { /* Test to see if the specified multicast address is * currently in use by the underlying device or * another macvlan. */ if (ether_addr_equal_64bits(port->dev->dev_addr, addr)) - return 1; + return true; if (macvlan_hash_lookup(port, addr)) - return 1; + return true; - return 0; + return false; } -- cgit v1.1 From eb2ca35f1814dad3ca547261eedfbbd0d65a0efc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 13 Nov 2016 19:01:17 -0800 Subject: mdio: Demote print from info to debug in mdio_driver_register While it is useful to know which MDIO driver is being registered, demote the pr_info() to a pr_debug(). Signed-off-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c index 9c88e67..43c8fd4 100644 --- a/drivers/net/phy/mdio_device.c +++ b/drivers/net/phy/mdio_device.c @@ -144,7 +144,7 @@ int mdio_driver_register(struct mdio_driver *drv) struct mdio_driver_common *mdiodrv = &drv->mdiodrv; int retval; - pr_info("mdio_driver_register: %s\n", mdiodrv->driver.name); + pr_debug("mdio_driver_register: %s\n", mdiodrv->driver.name); mdiodrv->driver.bus = &mdio_bus_type; mdiodrv->driver.probe = mdio_probe; -- cgit v1.1 From fe24a0c3a974f15945639edfc19f6b85f672145c Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Fri, 22 Jul 2016 07:55:41 +0800 Subject: ipvs: Use IS_ERR_OR_NULL(svc) instead of IS_ERR(svc) || svc == NULL This minor refactoring does not change the logic of function ip_vs_genl_dump_dests. Signed-off-by: Gao Feng Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- net/netfilter/ipvs/ip_vs_ctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 6b85ded..217e010 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3260,7 +3260,7 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb, svc = ip_vs_genl_find_service(ipvs, attrs[IPVS_CMD_ATTR_SERVICE]); - if (IS_ERR(svc) || svc == NULL) + if (IS_ERR_OR_NULL(svc)) goto out_err; /* Dump the destinations */ -- cgit v1.1 From 8d8e20e2d7bba8c50e64e0eca1cb83956f468e49 Mon Sep 17 00:00:00 2001 From: Dwip Banerjee Date: Wed, 26 Oct 2016 12:14:42 -0500 Subject: ipvs: Decrement ttl We decrement the IP ttl in all the modes in order to prevent infinite route loops. The changes were done based on Julian Anastasov's suggestions in a prior thread. The ttl based check/discard and the actual decrement are done in __ip_vs_get_out_rt() and in __ip_vs_get_out_rt_v6(), for the IPv6 case. decrement_ttl() implements the actual functionality for the two cases. Signed-off-by: Dwip Banerjee Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- net/netfilter/ipvs/ip_vs_xmit.c | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 01d3d89..4e1a98f 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -254,6 +254,54 @@ static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af, return true; } +static inline bool decrement_ttl(struct netns_ipvs *ipvs, + int skb_af, + struct sk_buff *skb) +{ + struct net *net = ipvs->net; + +#ifdef CONFIG_IP_VS_IPV6 + if (skb_af == AF_INET6) { + struct dst_entry *dst = skb_dst(skb); + + /* check and decrement ttl */ + if (ipv6_hdr(skb)->hop_limit <= 1) { + /* Force OUTPUT device used as source address */ + skb->dev = dst->dev; + icmpv6_send(skb, ICMPV6_TIME_EXCEED, + ICMPV6_EXC_HOPLIMIT, 0); + __IP6_INC_STATS(net, ip6_dst_idev(dst), + IPSTATS_MIB_INHDRERRORS); + + return false; + } + + /* don't propagate ttl change to cloned packets */ + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return false; + + ipv6_hdr(skb)->hop_limit--; + } else +#endif + { + if (ip_hdr(skb)->ttl <= 1) { + /* Tell the sender its packet died... */ + __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS); + icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0); + return false; + } + + /* don't propagate ttl change to cloned packets */ + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return false; + + /* Decrease ttl */ + ip_decrease_ttl(ip_hdr(skb)); + } + + return true; +} + /* Get route to destination or remote server */ static int __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, @@ -326,6 +374,9 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, return local; } + if (!decrement_ttl(ipvs, skb_af, skb)) + goto err_put; + if (likely(!(rt_mode & IP_VS_RT_MODE_TUNNEL))) { mtu = dst_mtu(&rt->dst); } else { @@ -473,6 +524,9 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, return local; } + if (!decrement_ttl(ipvs, skb_af, skb)) + goto err_put; + /* MTU checking */ if (likely(!(rt_mode & IP_VS_RT_MODE_TUNNEL))) mtu = dst_mtu(&rt->dst); -- cgit v1.1 From 79e57dd113d307a6c74773b8aaecf5442068988a Mon Sep 17 00:00:00 2001 From: "Vittorio Gambaletta (VittGam)" Date: Wed, 9 Nov 2016 03:40:56 +0200 Subject: ath9k: Really fix LED polarity for some Mini PCI AR9220 MB92 cards. The active_high LED of my Wistron DNMA-92 is still being recognized as active_low on 4.7.6 mainline. When I was preparing my former commit 0f9edcdd88a9 ("ath9k: Fix LED polarity for some Mini PCI AR9220 MB92 cards.") to fix that I must have somehow messed up with testing, because I tested the final version of that patch before sending it, and it was apparently working; but now it is not working on 4.7.6 mainline. I initially added the PCI_DEVICE_SUB section for 0x0029/0x2096 above the PCI_VDEVICE section for 0x0029; but then I moved the former below the latter after seeing how 0x002A sections were sorted in the file. This turned out to be wrong: if a generic PCI_VDEVICE entry (that has both subvendor and subdevice IDs set to PCI_ANY_ID) is put before a more specific one (PCI_DEVICE_SUB), then the generic PCI_VDEVICE entry will match first and will be used. With this patch, 0x0029/0x2096 has finally got active_high LED on 4.7.6. While I'm at it, let's fix 0x002A too by also moving its generic definition below its specific ones. Fixes: 0f9edcdd88a9 ("ath9k: Fix LED polarity for some Mini PCI AR9220 MB92 cards.") Cc: #4.7+ Signed-off-by: Vittorio Gambaletta [kvalo@qca.qualcomm.com: improve the commit log based on email discussions] Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/pci.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 0dd454a..aff473d 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -26,7 +26,6 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ - { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ #ifdef CONFIG_ATH9K_PCOEM /* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */ @@ -37,7 +36,7 @@ static const struct pci_device_id ath_pci_id_table[] = { .driver_data = ATH9K_PCI_LED_ACT_HI }, #endif - { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ #ifdef CONFIG_ATH9K_PCOEM { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, @@ -85,7 +84,11 @@ static const struct pci_device_id ath_pci_id_table[] = { 0x10CF, /* Fujitsu */ 0x1536), .driver_data = ATH9K_PCI_D3_L1_WAR }, +#endif + { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + +#ifdef CONFIG_ATH9K_PCOEM /* AR9285 card for Asus */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x002B, -- cgit v1.1 From fc383ffdb91a393086daf56c3d48a545e1cbd6e4 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 16 Oct 2016 22:59:05 +0200 Subject: Documentation: dt: net: add ath9k wireless device binding Add documentation how devicetree can be used to configure ath9k based devices. Signed-off-by: Martin Blumenstingl Acked-by: Rob Herring Signed-off-by: Kalle Valo --- .../devicetree/bindings/net/wireless/qca,ath9k.txt | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt diff --git a/Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt new file mode 100644 index 0000000..b7396c8 --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt @@ -0,0 +1,48 @@ +* Qualcomm Atheros ath9k wireless devices + +This node provides properties for configuring the ath9k wireless device. The +node is expected to be specified as a child node of the PCI controller to +which the wireless chip is connected. + +Required properties: +- compatible: For PCI and PCIe devices this should be an identifier following + the format as defined in "PCI Bus Binding to Open Firmware" + Revision 2.1. One of the possible formats is "pciVVVV,DDDD" + where VVVV is the PCI vendor ID and DDDD is PCI device ID. + Typically QCA's PCI vendor ID 168c is used while the PCI device + ID depends on the chipset - see the following (possibly + incomplete) list: + - 0023 for AR5416 + - 0024 for AR5418 + - 0027 for AR9160 + - 0029 for AR9220 and AR9223 + - 002a for AR9280 and AR9283 + - 002b for AR9285 + - 002c for AR2427 + - 002d for AR9227 + - 002e for AR9287 + - 0030 for AR9380, AR9381 and AR9382 + - 0032 for AR9485 + - 0033 for AR9580 and AR9590 + - 0034 for AR9462 + - 0036 for AR9565 + - 0037 for AR9485 +- reg: Address and length of the register set for the device. + +Optional properties: +- qca,no-eeprom: Indicates that there is no physical EEPROM connected to the + ath9k wireless chip (in this case the calibration / + EEPROM data will be loaded from userspace using the + kernel firmware loader). +- mac-address: See ethernet.txt in the parent directory +- local-mac-address: See ethernet.txt in the parent directory + + +In this example, the node is defined as child node of the PCI controller: +&pci0 { + wifi@168c,002d { + compatible = "pci168c,002d"; + reg = <0x7000 0 0 0 0x1000>; + qca,no-eeprom; + }; +}; -- cgit v1.1 From b40ded2ad75cc251e7ce4ae0db7098e5a0157200 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 16 Oct 2016 22:59:06 +0200 Subject: ath9k: add a helper to get the string representation of ath_bus_type This can be used when the ath_bus_type has to be presented in a log message or firmware filename. Signed-off-by: Martin Blumenstingl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath.h | 6 ++++++ drivers/net/wireless/ath/main.c | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index da7a7c8..f3f2784 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -327,4 +327,10 @@ static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode) } #endif +extern const char *ath_bus_type_strings[]; +static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype) +{ + return ath_bus_type_strings[bustype]; +} + #endif /* ATH_H */ diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c index 338d723..89f4b05 100644 --- a/drivers/net/wireless/ath/main.c +++ b/drivers/net/wireless/ath/main.c @@ -90,3 +90,10 @@ void ath_printk(const char *level, const struct ath_common* common, va_end(args); } EXPORT_SYMBOL(ath_printk); + +const char *ath_bus_type_strings[] = { + [ATH_PCI] = "pci", + [ATH_AHB] = "ahb", + [ATH_USB] = "usb", +}; +EXPORT_SYMBOL(ath_bus_type_strings); -- cgit v1.1 From 138b41253d9c9f9a06c8b086880cd3e839a23d69 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 16 Oct 2016 22:59:07 +0200 Subject: ath9k: parse the device configuration from an OF node This allows setting the MAC address and specifying that the firmware will be requested from userspace (because there might not be a hardware EEPROM connected to the chip) for ath9k based PCI devices using the device tree. There is some out-of-tree code to "convert devicetree to ath9k_platform_data" (for example in OpenWrt and LEDE) which becomes obsolete with this patch. Signed-off-by: Martin Blumenstingl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/init.c | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index cfa3fe8..b7c8ff9 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -555,6 +557,42 @@ static int ath9k_init_platform(struct ath_softc *sc) return 0; } +static int ath9k_of_init(struct ath_softc *sc) +{ + struct device_node *np = sc->dev->of_node; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + enum ath_bus_type bus_type = common->bus_ops->ath_bus_type; + const char *mac; + char eeprom_name[100]; + int ret; + + if (!of_device_is_available(np)) + return 0; + + ath_dbg(common, CONFIG, "parsing configuration from OF node\n"); + + if (of_property_read_bool(np, "qca,no-eeprom")) { + /* ath9k-eeprom--.bin */ + scnprintf(eeprom_name, sizeof(eeprom_name), + "ath9k-eeprom-%s-%s.bin", + ath_bus_type_to_string(bus_type), dev_name(ah->dev)); + + ret = ath9k_eeprom_request(sc, eeprom_name); + if (ret) + return ret; + } + + mac = of_get_mac_address(np); + if (mac) + ether_addr_copy(common->macaddr, mac); + + ah->ah_flags &= ~AH_USE_EEPROM; + ah->ah_flags |= AH_NO_EEP_SWAP; + + return 0; +} + static int ath9k_init_softc(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { @@ -611,6 +649,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (ret) return ret; + ret = ath9k_of_init(sc); + if (ret) + return ret; + if (ath9k_led_active_high != -1) ah->config.led_active_high = ath9k_led_active_high == 1; -- cgit v1.1 From 14acebc33e6dc69b31828c64ae069869d9e19f58 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 31 Oct 2016 09:12:47 -0600 Subject: ath9k_htc: fix minor mistakes in dev_err messages Add missing space in a dev_err message and join wrapped text so it does not span multiple lines. Fix spelling mistake on "unknown". Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/htc_hst.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index fd85f99..8e6dae2 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -244,8 +244,8 @@ int htc_connect_service(struct htc_target *target, /* Find an available endpoint */ endpoint = get_next_avail_ep(target->endpoint); if (!endpoint) { - dev_err(target->dev, "Endpoint is not available for" - "service %d\n", service_connreq->service_id); + dev_err(target->dev, "Endpoint is not available for service %d\n", + service_connreq->service_id); return -EINVAL; } @@ -382,7 +382,7 @@ static void ath9k_htc_fw_panic_report(struct htc_target *htc_handle, break; } default: - dev_err(htc_handle->dev, "ath: uknown panic pattern!\n"); + dev_err(htc_handle->dev, "ath: unknown panic pattern!\n"); break; } } -- cgit v1.1 From 50f08edf98096a68f01ff4566b605a25bf8e42ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Wed, 9 Nov 2016 12:31:49 +0100 Subject: ath9k: Switch to using mac80211 intermediate software queues. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This switches ath9k over to using the mac80211 intermediate software queueing mechanism for data packets. It removes the queueing inside the driver, except for the retry queue, and instead pulls from mac80211 when a packet is needed. The retry queue is used to store a packet that was pulled but can't be sent immediately. The old code path in ath_tx_start that would queue packets has been removed completely, as has the qlen limit tunables (since there's no longer a queue in the driver to limit). The mac80211 intermediate software queues offer significant latency reductions, and this patch allows ath9k to realise them. The exact gains from this varies with the test scenario, but in an access point scenario we have seen latency reductions ranging from 1/3 to as much as an order of magnitude. We also achieve slightly better aggregation. Median latency (ping) figures with this patch applied at the access point, with two high-rate stations and one low-rate station (HT20 5Ghz), running a Flent rtt_fair_var_up test with one TCP flow and one ping flow going to each station: Fast station Slow station Default pfifo_fast qdisc: 430.4 ms 638.7 ms fq_codel qdisc on iface: 35.5 ms 211.8 ms This patch set: 22.4 ms 38.2 ms Median aggregation sizes over the same test: Default pfifo_fast qdisc: 9.5 pkts 1.9 pkts fq_codel qdisc on iface: 11.2 pkts 1.9 pkts This patch set: 13.9 pkts 1.9 pkts This patch is based on Tim's original patch set, but reworked quite thoroughly. Cc: Tim Shepard Cc: Felix Fietkau Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/ath9k.h | 27 ++- drivers/net/wireless/ath/ath9k/channel.c | 2 - drivers/net/wireless/ath/ath9k/debug.c | 14 +- drivers/net/wireless/ath/ath9k/debug.h | 2 - drivers/net/wireless/ath/ath9k/debug_sta.c | 4 +- drivers/net/wireless/ath/ath9k/init.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 9 +- drivers/net/wireless/ath/ath9k/xmit.c | 338 ++++++++++++----------------- 8 files changed, 163 insertions(+), 235 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 26fc8ec..378d3458 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -91,7 +91,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_RXBUF 512 #define ATH_TXBUF 512 #define ATH_TXBUF_RESERVE 5 -#define ATH_MAX_QDEPTH (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE) #define ATH_TXMAXTRY 13 #define ATH_MAX_SW_RETRIES 30 @@ -145,7 +144,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define BAW_WITHIN(_start, _bawsz, _seqno) \ ((((_seqno) - (_start)) & 4095) < (_bawsz)) -#define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)]) +#define ATH_AN_2_TID(_an, _tidno) ath_node_to_tid(_an, _tidno) #define IS_HT_RATE(rate) (rate & 0x80) #define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e)) @@ -164,7 +163,6 @@ struct ath_txq { spinlock_t axq_lock; u32 axq_depth; u32 axq_ampdu_depth; - bool stopped; bool axq_tx_inprogress; struct list_head txq_fifo[ATH_TXFIFO_DEPTH]; u8 txq_headidx; @@ -232,7 +230,6 @@ struct ath_buf { struct ath_atx_tid { struct list_head list; - struct sk_buff_head buf_q; struct sk_buff_head retry_q; struct ath_node *an; struct ath_txq *txq; @@ -247,13 +244,13 @@ struct ath_atx_tid { s8 bar_index; bool active; bool clear_ps_filter; + bool has_queued; }; struct ath_node { struct ath_softc *sc; struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ - struct ath_atx_tid tid[IEEE80211_NUM_TIDS]; u16 maxampdu; u8 mpdudensity; @@ -276,7 +273,6 @@ struct ath_tx_control { struct ath_node *an; struct ieee80211_sta *sta; u8 paprd; - bool force_channel; }; @@ -293,7 +289,6 @@ struct ath_tx { struct ath_descdma txdma; struct ath_txq *txq_map[IEEE80211_NUM_ACS]; struct ath_txq *uapsdq; - u32 txq_max_pending[IEEE80211_NUM_ACS]; u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32]; }; @@ -421,6 +416,22 @@ struct ath_offchannel { int duration; }; +static inline struct ath_atx_tid * +ath_node_to_tid(struct ath_node *an, u8 tidno) +{ + struct ieee80211_sta *sta = an->sta; + struct ieee80211_vif *vif = an->vif; + struct ieee80211_txq *txq; + + BUG_ON(!vif); + if (sta) + txq = sta->txq[tidno % ARRAY_SIZE(sta->txq)]; + else + txq = vif->txq; + + return (struct ath_atx_tid *) txq->drv_priv; +} + #define case_rtn_string(val) case val: return #val #define ath_for_each_chanctx(_sc, _ctx) \ @@ -575,7 +586,6 @@ void ath_tx_edma_tasklet(struct ath_softc *sc); int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn); void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); -void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an); void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, @@ -585,6 +595,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, u16 tids, int nframes, enum ieee80211_frame_release_type reason, bool more_data); +void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue); /********/ /* VIFs */ diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 57e26a6..929dd70 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1010,7 +1010,6 @@ static void ath_scan_send_probe(struct ath_softc *sc, goto error; txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; - txctl.force_channel = true; if (ath_tx_start(sc->hw, skb, &txctl)) goto error; @@ -1133,7 +1132,6 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, memset(&txctl, 0, sizeof(txctl)); txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; txctl.sta = sta; - txctl.force_channel = true; if (ath_tx_start(sc->hw, skb, &txctl)) { ieee80211_free_txskb(sc->hw, skb); return false; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index c56e40f..89a94dd 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -600,7 +600,6 @@ static int read_file_xmit(struct seq_file *file, void *data) PR("MPDUs XRetried: ", xretries); PR("Aggregates: ", a_aggr); PR("AMPDUs Queued HW:", a_queued_hw); - PR("AMPDUs Queued SW:", a_queued_sw); PR("AMPDUs Completed:", a_completed); PR("AMPDUs Retried: ", a_retries); PR("AMPDUs XRetried: ", a_xretries); @@ -629,8 +628,7 @@ static void print_queue(struct ath_softc *sc, struct ath_txq *txq, seq_printf(file, "%s: %d ", "qnum", txq->axq_qnum); seq_printf(file, "%s: %2d ", "qdepth", txq->axq_depth); seq_printf(file, "%s: %2d ", "ampdu-depth", txq->axq_ampdu_depth); - seq_printf(file, "%s: %3d ", "pending", txq->pending_frames); - seq_printf(file, "%s: %d\n", "stopped", txq->stopped); + seq_printf(file, "%s: %3d\n", "pending", txq->pending_frames); ath_txq_unlock(sc, txq); } @@ -1208,7 +1206,6 @@ static const char ath9k_gstrings_stats[][ETH_GSTRING_LEN] = { AMKSTR(d_tx_mpdu_xretries), AMKSTR(d_tx_aggregates), AMKSTR(d_tx_ampdus_queued_hw), - AMKSTR(d_tx_ampdus_queued_sw), AMKSTR(d_tx_ampdus_completed), AMKSTR(d_tx_ampdu_retries), AMKSTR(d_tx_ampdu_xretries), @@ -1288,7 +1285,6 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw, AWDATA(xretries); AWDATA(a_aggr); AWDATA(a_queued_hw); - AWDATA(a_queued_sw); AWDATA(a_completed); AWDATA(a_retries); AWDATA(a_xretries); @@ -1346,14 +1342,6 @@ int ath9k_init_debug(struct ath_hw *ah) read_file_xmit); debugfs_create_devm_seqfile(sc->dev, "queues", sc->debug.debugfs_phy, read_file_queues); - debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[IEEE80211_AC_BK]); - debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[IEEE80211_AC_BE]); - debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[IEEE80211_AC_VI]); - debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[IEEE80211_AC_VO]); debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy, read_file_misc); debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index cd68c5f..a078cdd 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -147,7 +147,6 @@ struct ath_interrupt_stats { * @completed: Total MPDUs (non-aggr) completed * @a_aggr: Total no. of aggregates queued * @a_queued_hw: Total AMPDUs queued to hardware - * @a_queued_sw: Total AMPDUs queued to software queues * @a_completed: Total AMPDUs completed * @a_retries: No. of AMPDUs retried (SW) * @a_xretries: No. of AMPDUs dropped due to xretries @@ -174,7 +173,6 @@ struct ath_tx_stats { u32 xretries; u32 a_aggr; u32 a_queued_hw; - u32 a_queued_sw; u32 a_completed; u32 a_retries; u32 a_xretries; diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index b66cfa9..2a3a3c4 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -52,8 +52,8 @@ static ssize_t read_file_node_aggr(struct file *file, char __user *user_buf, "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); - for (tidno = 0, tid = &an->tid[tidno]; - tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { + tid = ath_node_to_tid(an, tidno); txq = tid->txq; ath_txq_lock(sc, txq); if (tid->active) { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index b7c8ff9..c0c8bf0 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -360,7 +360,6 @@ static int ath9k_init_queues(struct ath_softc *sc) for (i = 0; i < IEEE80211_NUM_ACS; i++) { sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); sc->tx.txq_map[i]->mac80211_qnum = i; - sc->tx.txq_max_pending[i] = ATH_MAX_QDEPTH; } return 0; } @@ -919,6 +918,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); + hw->txq_data_size = sizeof(struct ath_atx_tid); hw->extra_tx_headroom = 4; hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e9f32b5..59e3bd0 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1902,9 +1902,11 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, bool flush = false; int ret = 0; struct ieee80211_sta *sta = params->sta; + struct ath_node *an = (struct ath_node *)sta->drv_priv; enum ieee80211_ampdu_mlme_action action = params->action; u16 tid = params->tid; u16 *ssn = ¶ms->ssn; + struct ath_atx_tid *atid; mutex_lock(&sc->mutex); @@ -1937,9 +1939,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ath9k_ps_restore(sc); break; case IEEE80211_AMPDU_TX_OPERATIONAL: - ath9k_ps_wakeup(sc); - ath_tx_aggr_resume(sc, sta, tid); - ath9k_ps_restore(sc); + atid = ath_node_to_tid(an, tid); + atid->baw_size = IEEE80211_MIN_AMPDU_BUF << + sta->ht_cap.ampdu_factor; break; default: ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n"); @@ -2701,4 +2703,5 @@ struct ieee80211_ops ath9k_ops = { .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, .get_txpower = ath9k_get_txpower, + .wake_tx_queue = ath9k_wake_tx_queue, }; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 52bfbb9..486afa9 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -67,6 +67,8 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb); +static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath_tx_control *txctl); enum { MCS_HT20, @@ -137,6 +139,26 @@ static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq, list_add_tail(&tid->list, list); } +void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue) +{ + struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_atx_tid *tid = (struct ath_atx_tid *) queue->drv_priv; + struct ath_txq *txq = tid->txq; + + ath_dbg(common, QUEUE, "Waking TX queue: %pM (%d)\n", + queue->sta ? queue->sta->addr : queue->vif->addr, + tid->tidno); + + ath_txq_lock(sc, txq); + + tid->has_queued = true; + ath_tx_queue_tid(sc, txq, tid); + ath_txq_schedule(sc, txq); + + ath_txq_unlock(sc, txq); +} + static struct ath_frame_info *get_frame_info(struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); @@ -164,7 +186,6 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta, static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, struct sk_buff *skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ath_frame_info *fi = get_frame_info(skb); int q = fi->txq; @@ -175,14 +196,6 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; - if (txq->stopped && - txq->pending_frames < sc->tx.txq_max_pending[q]) { - if (ath9k_is_chanctx_enabled()) - ieee80211_wake_queue(sc->hw, info->hw_queue); - else - ieee80211_wake_queue(sc->hw, q); - txq->stopped = false; - } } static struct ath_atx_tid * @@ -192,9 +205,48 @@ ath_get_skb_tid(struct ath_softc *sc, struct ath_node *an, struct sk_buff *skb) return ATH_AN_2_TID(an, tidno); } +static struct sk_buff * +ath_tid_pull(struct ath_atx_tid *tid) +{ + struct ieee80211_txq *txq = container_of((void*)tid, struct ieee80211_txq, drv_priv); + struct ath_softc *sc = tid->an->sc; + struct ieee80211_hw *hw = sc->hw; + struct ath_tx_control txctl = { + .txq = tid->txq, + .sta = tid->an->sta, + }; + struct sk_buff *skb; + struct ath_frame_info *fi; + int q; + + if (!tid->has_queued) + return NULL; + + skb = ieee80211_tx_dequeue(hw, txq); + if (!skb) { + tid->has_queued = false; + return NULL; + } + + if (ath_tx_prepare(hw, skb, &txctl)) { + ieee80211_free_txskb(hw, skb); + return NULL; + } + + q = skb_get_queue_mapping(skb); + if (tid->txq == sc->tx.txq_map[q]) { + fi = get_frame_info(skb); + fi->txq = q; + ++tid->txq->pending_frames; + } + + return skb; + } + + static bool ath_tid_has_buffered(struct ath_atx_tid *tid) { - return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q); + return !skb_queue_empty(&tid->retry_q) || tid->has_queued; } static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid) @@ -203,46 +255,11 @@ static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid) skb = __skb_dequeue(&tid->retry_q); if (!skb) - skb = __skb_dequeue(&tid->buf_q); + skb = ath_tid_pull(tid); return skb; } -/* - * ath_tx_tid_change_state: - * - clears a-mpdu flag of previous session - * - force sequence number allocation to fix next BlockAck Window - */ -static void -ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid) -{ - struct ath_txq *txq = tid->txq; - struct ieee80211_tx_info *tx_info; - struct sk_buff *skb, *tskb; - struct ath_buf *bf; - struct ath_frame_info *fi; - - skb_queue_walk_safe(&tid->buf_q, skb, tskb) { - fi = get_frame_info(skb); - bf = fi->bf; - - tx_info = IEEE80211_SKB_CB(skb); - tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU; - - if (bf) - continue; - - bf = ath_tx_setup_buffer(sc, txq, tid, skb); - if (!bf) { - __skb_unlink(skb, &tid->buf_q); - ath_txq_skb_done(sc, txq, skb); - ieee80211_free_txskb(sc->hw, skb); - continue; - } - } - -} - static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) { struct ath_txq *txq = tid->txq; @@ -883,20 +900,16 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, static struct ath_buf * ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, - struct ath_atx_tid *tid, struct sk_buff_head **q) + struct ath_atx_tid *tid) { struct ieee80211_tx_info *tx_info; struct ath_frame_info *fi; - struct sk_buff *skb; + struct sk_buff *skb, *first_skb = NULL; struct ath_buf *bf; u16 seqno; while (1) { - *q = &tid->retry_q; - if (skb_queue_empty(*q)) - *q = &tid->buf_q; - - skb = skb_peek(*q); + skb = ath_tid_dequeue(tid); if (!skb) break; @@ -908,7 +921,6 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, bf->bf_state.stale = false; if (!bf) { - __skb_unlink(skb, *q); ath_txq_skb_done(sc, txq, skb); ieee80211_free_txskb(sc->hw, skb); continue; @@ -937,8 +949,20 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, seqno = bf->bf_state.seqno; /* do not step over block-ack window */ - if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) + if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) { + __skb_queue_tail(&tid->retry_q, skb); + + /* If there are other skbs in the retry q, they are + * probably within the BAW, so loop immediately to get + * one of them. Otherwise the queue can get stuck. */ + if (!skb_queue_is_first(&tid->retry_q, skb) && + !WARN_ON(skb == first_skb)) { + if(!first_skb) /* infinite loop prevention */ + first_skb = skb; + continue; + } break; + } if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) { struct ath_tx_status ts = {}; @@ -946,7 +970,6 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, INIT_LIST_HEAD(&bf_head); list_add(&bf->list, &bf_head); - __skb_unlink(skb, *q); ath_tx_update_baw(sc, tid, seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0); continue; @@ -958,11 +981,10 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, return NULL; } -static bool +static int ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct list_head *bf_q, - struct ath_buf *bf_first, struct sk_buff_head *tid_q, - int *aggr_len) + struct ath_buf *bf_first) { #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) struct ath_buf *bf = bf_first, *bf_prev = NULL; @@ -972,12 +994,13 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ieee80211_tx_info *tx_info; struct ath_frame_info *fi; struct sk_buff *skb; - bool closed = false; + bf = bf_first; aggr_limit = ath_lookup_rate(sc, bf, tid); - do { + while (bf) + { skb = bf->bf_mpdu; fi = get_frame_info(skb); @@ -986,12 +1009,12 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, if (nframes) { if (aggr_limit < al + bpad + al_delta || ath_lookup_legacy(bf) || nframes >= h_baw) - break; + goto stop; tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) || !(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) - break; + goto stop; } /* add padding for previous frame to aggregation length */ @@ -1013,20 +1036,18 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, ath_tx_addto_baw(sc, tid, bf); bf->bf_state.ndelim = ndelim; - __skb_unlink(skb, tid_q); list_add_tail(&bf->list, bf_q); if (bf_prev) bf_prev->bf_next = bf; bf_prev = bf; - bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); - if (!bf) { - closed = true; - break; - } - } while (ath_tid_has_buffered(tid)); - + bf = ath_tx_get_tid_subframe(sc, txq, tid); + } + goto finish; +stop: + __skb_queue_tail(&tid->retry_q, bf->bf_mpdu); +finish: bf = bf_first; bf->bf_lastbf = bf_prev; @@ -1037,9 +1058,7 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, TX_STAT_INC(txq->axq_qnum, a_aggr); } - *aggr_len = al; - - return closed; + return al; #undef PADBYTES } @@ -1416,18 +1435,15 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, static void ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct list_head *bf_q, - struct ath_buf *bf_first, struct sk_buff_head *tid_q) + struct ath_buf *bf_first) { struct ath_buf *bf = bf_first, *bf_prev = NULL; - struct sk_buff *skb; int nframes = 0; do { struct ieee80211_tx_info *tx_info; - skb = bf->bf_mpdu; nframes++; - __skb_unlink(skb, tid_q); list_add_tail(&bf->list, bf_q); if (bf_prev) bf_prev->bf_next = bf; @@ -1436,13 +1452,15 @@ ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq, if (nframes >= 2) break; - bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); + bf = ath_tx_get_tid_subframe(sc, txq, tid); if (!bf) break; tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); - if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { + __skb_queue_tail(&tid->retry_q, bf->bf_mpdu); break; + } ath_set_rates(tid->an->vif, tid->an->sta, bf); } while (1); @@ -1453,34 +1471,33 @@ static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, { struct ath_buf *bf; struct ieee80211_tx_info *tx_info; - struct sk_buff_head *tid_q; struct list_head bf_q; int aggr_len = 0; - bool aggr, last = true; + bool aggr; if (!ath_tid_has_buffered(tid)) return false; INIT_LIST_HEAD(&bf_q); - bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q); + bf = ath_tx_get_tid_subframe(sc, txq, tid); if (!bf) return false; tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU); if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) || - (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) { + (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) { + __skb_queue_tail(&tid->retry_q, bf->bf_mpdu); *stop = true; return false; } ath_set_rates(tid->an->vif, tid->an->sta, bf); if (aggr) - last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf, - tid_q, &aggr_len); + aggr_len = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf); else - ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q); + ath_tx_form_burst(sc, txq, tid, &bf_q, bf); if (list_empty(&bf_q)) return false; @@ -1523,9 +1540,6 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, an->mpdudensity = density; } - /* force sequence number allocation for pending frames */ - ath_tx_tid_change_state(sc, txtid); - txtid->active = true; *ssn = txtid->seq_start = txtid->seq_next; txtid->bar_index = -1; @@ -1550,7 +1564,6 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) ath_txq_lock(sc, txq); txtid->active = false; ath_tx_flush_tid(sc, txtid); - ath_tx_tid_change_state(sc, txtid); ath_txq_unlock_complete(sc, txq); } @@ -1560,14 +1573,12 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *tid; struct ath_txq *txq; - bool buffered; int tidno; ath_dbg(common, XMIT, "%s called\n", __func__); - for (tidno = 0, tid = &an->tid[tidno]; - tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { - + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { + tid = ath_node_to_tid(an, tidno); txq = tid->txq; ath_txq_lock(sc, txq); @@ -1577,13 +1588,12 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, continue; } - buffered = ath_tid_has_buffered(tid); + if (!skb_queue_empty(&tid->retry_q)) + ieee80211_sta_set_buffered(sta, tid->tidno, true); list_del_init(&tid->list); ath_txq_unlock(sc, txq); - - ieee80211_sta_set_buffered(sta, tidno, buffered); } } @@ -1596,49 +1606,20 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ath_dbg(common, XMIT, "%s called\n", __func__); - for (tidno = 0, tid = &an->tid[tidno]; - tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { - + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { + tid = ath_node_to_tid(an, tidno); txq = tid->txq; ath_txq_lock(sc, txq); tid->clear_ps_filter = true; - if (ath_tid_has_buffered(tid)) { ath_tx_queue_tid(sc, txq, tid); ath_txq_schedule(sc, txq); } - ath_txq_unlock_complete(sc, txq); } } -void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, - u16 tidno) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_atx_tid *tid; - struct ath_node *an; - struct ath_txq *txq; - - ath_dbg(common, XMIT, "%s called\n", __func__); - - an = (struct ath_node *)sta->drv_priv; - tid = ATH_AN_2_TID(an, tidno); - txq = tid->txq; - - ath_txq_lock(sc, txq); - - tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; - - if (ath_tid_has_buffered(tid)) { - ath_tx_queue_tid(sc, txq, tid); - ath_txq_schedule(sc, txq); - } - - ath_txq_unlock_complete(sc, txq); -} - void ath9k_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tids, int nframes, @@ -1651,7 +1632,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_tx_info *info; struct list_head bf_q; struct ath_buf *bf_tail = NULL, *bf; - struct sk_buff_head *tid_q; int sent = 0; int i; @@ -1666,11 +1646,10 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, ath_txq_lock(sc, tid->txq); while (nframes > 0) { - bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q); + bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid); if (!bf) break; - __skb_unlink(bf->bf_mpdu, tid_q); list_add_tail(&bf->list, &bf_q); ath_set_rates(tid->an->vif, tid->an->sta, bf); if (bf_isampdu(bf)) { @@ -1685,7 +1664,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, sent++; TX_STAT_INC(txq->axq_qnum, a_queued_hw); - if (an->sta && !ath_tid_has_buffered(tid)) + if (an->sta && skb_queue_empty(&tid->retry_q)) ieee80211_sta_set_buffered(an->sta, i, false); } ath_txq_unlock_complete(sc, tid->txq); @@ -1914,13 +1893,7 @@ bool ath_drain_all_txq(struct ath_softc *sc) if (!ATH_TXQ_SETUP(sc, i)) continue; - /* - * The caller will resume queues with ieee80211_wake_queues. - * Mark the queue as not stopped to prevent ath_tx_complete - * from waking the queue too early. - */ txq = &sc->tx.txq[i]; - txq->stopped = false; ath_draintxq(sc, txq); } @@ -2319,16 +2292,14 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_softc *sc = hw->priv; struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; + struct ath_node *an = NULL; struct ath_buf *bf; - bool queue, skip_uapsd = false, ps_resp; + bool ps_resp; int q, ret; if (vif) avp = (void *)vif->drv_priv; - if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) - txctl->force_channel = true; - ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE); ret = ath_tx_prepare(hw, skb, txctl); @@ -2343,63 +2314,18 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, q = skb_get_queue_mapping(skb); - ath_txq_lock(sc, txq); - if (txq == sc->tx.txq_map[q]) { - fi->txq = q; - if (++txq->pending_frames > sc->tx.txq_max_pending[q] && - !txq->stopped) { - if (ath9k_is_chanctx_enabled()) - ieee80211_stop_queue(sc->hw, info->hw_queue); - else - ieee80211_stop_queue(sc->hw, q); - txq->stopped = true; - } - } - - queue = ieee80211_is_data_present(hdr->frame_control); - - /* If chanctx, queue all null frames while NOA could be there */ - if (ath9k_is_chanctx_enabled() && - ieee80211_is_nullfunc(hdr->frame_control) && - !txctl->force_channel) - queue = true; - - /* Force queueing of all frames that belong to a virtual interface on - * a different channel context, to ensure that they are sent on the - * correct channel. - */ - if (((avp && avp->chanctx != sc->cur_chan) || - sc->cur_chan->stopped) && !txctl->force_channel) { - if (!txctl->an) - txctl->an = &avp->mcast_node; - queue = true; - skip_uapsd = true; - } - - if (txctl->an && queue) - tid = ath_get_skb_tid(sc, txctl->an, skb); - - if (!skip_uapsd && ps_resp) { - ath_txq_unlock(sc, txq); + if (ps_resp) txq = sc->tx.uapsdq; - ath_txq_lock(sc, txq); - } else if (txctl->an && queue) { - WARN_ON(tid->txq != txctl->txq); - if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) - tid->clear_ps_filter = true; - - /* - * Add this frame to software queue for scheduling later - * for aggregation. - */ - TX_STAT_INC(txq->axq_qnum, a_queued_sw); - __skb_queue_tail(&tid->buf_q, skb); - if (!txctl->an->sleeping) - ath_tx_queue_tid(sc, txq, tid); + if (txctl->sta) { + an = (struct ath_node *) sta->drv_priv; + tid = ath_get_skb_tid(sc, an, skb); + } - ath_txq_schedule(sc, txq); - goto out; + ath_txq_lock(sc, txq); + if (txq == sc->tx.txq_map[q]) { + fi->txq = q; + ++txq->pending_frames; } bf = ath_tx_setup_buffer(sc, txq, tid, skb); @@ -2892,9 +2818,8 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) struct ath_atx_tid *tid; int tidno, acno; - for (tidno = 0, tid = &an->tid[tidno]; - tidno < IEEE80211_NUM_TIDS; - tidno++, tid++) { + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { + tid = ath_node_to_tid(an, tidno); tid->an = an; tid->tidno = tidno; tid->seq_start = tid->seq_next = 0; @@ -2902,11 +2827,14 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) tid->baw_head = tid->baw_tail = 0; tid->active = false; tid->clear_ps_filter = true; - __skb_queue_head_init(&tid->buf_q); + tid->has_queued = false; __skb_queue_head_init(&tid->retry_q); INIT_LIST_HEAD(&tid->list); acno = TID_TO_WME_AC(tidno); tid->txq = sc->tx.txq_map[acno]; + + if (!an->sta) + break; /* just one multicast ath_atx_tid */ } } @@ -2916,9 +2844,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) struct ath_txq *txq; int tidno; - for (tidno = 0, tid = &an->tid[tidno]; - tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { - + for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { + tid = ath_node_to_tid(an, tidno); txq = tid->txq; ath_txq_lock(sc, txq); @@ -2930,6 +2857,9 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) tid->active = false; ath_txq_unlock(sc, txq); + + if (!an->sta) + break; /* just one multicast ath_atx_tid */ } } -- cgit v1.1 From fcf7cf1551cae54e747a771f5808240f2a37708f Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 9 Nov 2016 03:40:57 +0200 Subject: ath10k: fix failure to send NULL func frame for 10.4 This partially reverts 'commit 2cdce425aa33 ("ath10k: Fix broken NULL func data frame status for 10.4")' Unfortunately this breaks sending NULL func and the existing issue of obtaining proper tx status for NULL function will be fixed. Also update the comments for feature flag added to be useless and not working Fixes: 2cdce425aa33 "ath10k: Fix broken NULL func data frame status for 10.4" Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 6 ++---- drivers/net/wireless/ath/ath10k/mac.c | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 6e5aa2d..018fc22 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -556,10 +556,8 @@ enum ath10k_fw_features { */ ATH10K_FW_FEATURE_BTCOEX_PARAM = 14, - /* Older firmware with HTT delivers incorrect tx status for null func - * frames to driver, but this fixed in 10.2 and 10.4 firmware versions. - * Also this workaround results in reporting of incorrect null func - * status for 10.4. This flag is used to skip the workaround. + /* Unused flag and proven to be not working, enable this if you want + * to experiment sending NULL func data frames in HTT TX */ ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR = 15, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 691b7b5..144e0ef 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3257,8 +3257,6 @@ ath10k_mac_tx_h_get_txmode(struct ath10k *ar, if (ar->htt.target_version_major < 3 && (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) && !test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, - ar->running_fw->fw_file.fw_features) && - !test_bit(ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR, ar->running_fw->fw_file.fw_features)) return ATH10K_HW_TXRX_MGMT; -- cgit v1.1 From 19f338c6eb0cf69e8ee6d2f1382e6e6da21d65de Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 9 Nov 2016 03:40:58 +0200 Subject: ath10k: clean up HTT tx buffer allocation and free cleanup 'ath10k_htt_tx_alloc' by introducing the API's 'ath10k_htt_tx_alloc/free_{cont_txbuf, txdone_fifo} and re-use them whereever needed Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_tx.c | 75 +++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index ae5b33f..ab2ee90 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -229,6 +229,32 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) idr_remove(&htt->pending_tx, msdu_id); } +static void ath10k_htt_tx_free_cont_txbuf(struct ath10k_htt *htt) +{ + struct ath10k *ar = htt->ar; + size_t size; + + if (!htt->txbuf.vaddr) + return; + + size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf); + dma_free_coherent(ar->dev, size, htt->txbuf.vaddr, htt->txbuf.paddr); +} + +static int ath10k_htt_tx_alloc_cont_txbuf(struct ath10k_htt *htt) +{ + struct ath10k *ar = htt->ar; + size_t size; + + size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf); + htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size, &htt->txbuf.paddr, + GFP_KERNEL); + if (!htt->txbuf.vaddr) + return -ENOMEM; + + return 0; +} + static void ath10k_htt_tx_free_cont_frag_desc(struct ath10k_htt *htt) { size_t size; @@ -310,10 +336,26 @@ static int ath10k_htt_tx_alloc_txq(struct ath10k_htt *htt) return 0; } +static void ath10k_htt_tx_free_txdone_fifo(struct ath10k_htt *htt) +{ + WARN_ON(!kfifo_is_empty(&htt->txdone_fifo)); + kfifo_free(&htt->txdone_fifo); +} + +static int ath10k_htt_tx_alloc_txdone_fifo(struct ath10k_htt *htt) +{ + int ret; + size_t size; + + size = roundup_pow_of_two(htt->max_num_pending_tx); + ret = kfifo_alloc(&htt->txdone_fifo, size, GFP_KERNEL); + return ret; +} + int ath10k_htt_tx_alloc(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; - int ret, size; + int ret; ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", htt->max_num_pending_tx); @@ -321,13 +363,9 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) spin_lock_init(&htt->tx_lock); idr_init(&htt->pending_tx); - size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf); - htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size, - &htt->txbuf.paddr, - GFP_KERNEL); - if (!htt->txbuf.vaddr) { - ath10k_err(ar, "failed to alloc tx buffer\n"); - ret = -ENOMEM; + ret = ath10k_htt_tx_alloc_cont_txbuf(htt); + if (ret) { + ath10k_err(ar, "failed to alloc cont tx buffer: %d\n", ret); goto free_idr_pending_tx; } @@ -343,8 +381,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) goto free_frag_desc; } - size = roundup_pow_of_two(htt->max_num_pending_tx); - ret = kfifo_alloc(&htt->txdone_fifo, size, GFP_KERNEL); + ret = ath10k_htt_tx_alloc_txdone_fifo(htt); if (ret) { ath10k_err(ar, "failed to alloc txdone fifo: %d\n", ret); goto free_txq; @@ -359,10 +396,7 @@ free_frag_desc: ath10k_htt_tx_free_cont_frag_desc(htt); free_txbuf: - size = htt->max_num_pending_tx * - sizeof(struct ath10k_htt_txbuf); - dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr, - htt->txbuf.paddr); + ath10k_htt_tx_free_cont_txbuf(htt); free_idr_pending_tx: idr_destroy(&htt->pending_tx); @@ -388,22 +422,13 @@ static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx) void ath10k_htt_tx_free(struct ath10k_htt *htt) { - int size; - idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar); idr_destroy(&htt->pending_tx); - if (htt->txbuf.vaddr) { - size = htt->max_num_pending_tx * - sizeof(struct ath10k_htt_txbuf); - dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr, - htt->txbuf.paddr); - } - + ath10k_htt_tx_free_cont_txbuf(htt); ath10k_htt_tx_free_txq(htt); ath10k_htt_tx_free_cont_frag_desc(htt); - WARN_ON(!kfifo_is_empty(&htt->txdone_fifo)); - kfifo_free(&htt->txdone_fifo); + ath10k_htt_tx_free_txdone_fifo(htt); } void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) -- cgit v1.1 From f004e532a80a6368627e26503482508528e5c443 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 9 Nov 2016 03:40:59 +0200 Subject: ath10k: remove extraneous error message in tx alloc Remove extraneous error message in 'ath10k_htt_tx_alloc_cont_frag_desc' as the caller 'ath10k_htt_tx_alloc' already dumps a proper error message Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_tx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index ab2ee90..ccbc8c03 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -282,10 +282,8 @@ static int ath10k_htt_tx_alloc_cont_frag_desc(struct ath10k_htt *htt) htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size, &htt->frag_desc.paddr, GFP_KERNEL); - if (!htt->frag_desc.vaddr) { - ath10k_err(ar, "failed to alloc fragment desc memory\n"); + if (!htt->frag_desc.vaddr) return -ENOMEM; - } return 0; } -- cgit v1.1 From 31b239824ece321c09bdb8e61e1d14814eaba38b Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Sat, 29 Oct 2016 13:17:37 +0200 Subject: ath10k: use the right length of "background" The word "background" contains 10 characters so the third argument of strncmp() need to be 10 in order to match this prefix correctly. Signed-off-by: Nicolas Iooss Fixes: 855aed1220d2 ("ath10k: add spectral scan feature") Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/spectral.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index 7d9b0da..2ffc1fe 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -338,7 +338,7 @@ static ssize_t write_file_spec_scan_ctl(struct file *file, } else { res = -EINVAL; } - } else if (strncmp("background", buf, 9) == 0) { + } else if (strncmp("background", buf, 10) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND); } else if (strncmp("manual", buf, 6) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL); -- cgit v1.1 From 3a08c2fd763450a927d1130de078d6f9e74944fb Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 11 Nov 2016 10:55:06 -0800 Subject: bpf: LRU List Introduce bpf_lru_list which will provide LRU capability to the bpf_htab in the later patch. * General Thoughts: 1. Target use case. Read is more often than update. (i.e. bpf_lookup_elem() is more often than bpf_update_elem()). If bpf_prog does a bpf_lookup_elem() first and then an in-place update, it still counts as a read operation to the LRU list concern. 2. It may be useful to think of it as a LRU cache 3. Optimize the read case 3.1 No lock in read case 3.2 The LRU maintenance is only done during bpf_update_elem() 4. If there is a percpu LRU list, it will lose the system-wise LRU property. A completely isolated percpu LRU list has the best performance but the memory utilization is not ideal considering the work load may be imbalance. 5. Hence, this patch starts the LRU implementation with a global LRU list with batched operations before accessing the global LRU list. As a LRU cache, #read >> #update/#insert operations, it will work well. 6. There is a local list (for each cpu) which is named 'struct bpf_lru_locallist'. This local list is not used to sort the LRU property. Instead, the local list is to batch enough operations before acquiring the lock of the global LRU list. More details on this later. 7. In the later patch, it allows a percpu LRU list by specifying a map-attribute for scalability reason and for use cases that need to prepare for the worst (and pathological) case like DoS attack. The percpu LRU list is completely isolated from each other and the LRU nodes (including free nodes) cannot be moved across the list. The following description is for the global LRU list but mostly applicable to the percpu LRU list also. * Global LRU List: 1. It has three sub-lists: active-list, inactive-list and free-list. 2. The two list idea, active and inactive, is borrowed from the page cache. 3. All nodes are pre-allocated and all sit at the free-list (of the global LRU list) at the beginning. The pre-allocation reasoning is similar to the existing BPF_MAP_TYPE_HASH. However, opting-out prealloc (BPF_F_NO_PREALLOC) is not supported in the LRU map. * Active/Inactive List (of the global LRU list): 1. The active list, as its name says it, maintains the active set of the nodes. We can think of it as the working set or more frequently accessed nodes. The access frequency is approximated by a ref-bit. The ref-bit is set during the bpf_lookup_elem(). 2. The inactive list, as its name also says it, maintains a less active set of nodes. They are the candidates to be removed from the bpf_htab when we are running out of free nodes. 3. The ordering of these two lists is acting as a rough clock. The tail of the inactive list is the older nodes and should be released first if the bpf_htab needs free element. * Rotating the Active/Inactive List (of the global LRU list): 1. It is the basic operation to maintain the LRU property of the global list. 2. The active list is only rotated when the inactive list is running low. This idea is similar to the current page cache. Inactive running low is currently defined as "# of inactive < # of active". 3. The active list rotation always starts from the tail. It moves node without ref-bit set to the head of the inactive list. It moves node with ref-bit set back to the head of the active list and then clears its ref-bit. 4. The inactive rotation is pretty simply. It walks the inactive list and moves the nodes back to the head of active list if its ref-bit is set. The ref-bit is cleared after moving to the active list. If the node does not have ref-bit set, it just leave it as it is because it is already in the inactive list. * Shrinking the Inactive List (of the global LRU list): 1. Shrinking is the operation to get free nodes when the bpf_htab is full. 2. It usually only shrinks the inactive list to get free nodes. 3. During shrinking, it will walk the inactive list from the tail, delete the nodes without ref-bit set from bpf_htab. 4. If no free node found after step (3), it will forcefully get one node from the tail of inactive or active list. Forcefully is in the sense that it ignores the ref-bit. * Local List: 1. Each CPU has a 'struct bpf_lru_locallist'. The purpose is to batch enough operations before acquiring the lock of the global LRU. 2. A local list has two sub-lists, free-list and pending-list. 3. During bpf_update_elem(), it will try to get from the free-list of (the current CPU local list). 4. If the local free-list is empty, it will acquire from the global LRU list. The global LRU list can either satisfy it by its global free-list or by shrinking the global inactive list. Since we have acquired the global LRU list lock, it will try to get at most LOCAL_FREE_TARGET elements to the local free list. 5. When a new element is added to the bpf_htab, it will first sit at the pending-list (of the local list) first. The pending-list will be flushed to the global LRU list when it needs to acquire free nodes from the global list next time. * Lock Consideration: The LRU list has a lock (lru_lock). Each bucket of htab has a lock (buck_lock). If both locks need to be acquired together, the lock order is always lru_lock -> buck_lock and this only happens in the bpf_lru_list.c logic. In hashtab.c, both locks are not acquired together (i.e. one lock is always released first before acquiring another lock). Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/Makefile | 2 +- kernel/bpf/bpf_lru_list.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/bpf_lru_list.h | 80 +++++++ 3 files changed, 648 insertions(+), 1 deletion(-) create mode 100644 kernel/bpf/bpf_lru_list.c create mode 100644 kernel/bpf/bpf_lru_list.h diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index eed911d..c4d89d6 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -1,7 +1,7 @@ obj-y := core.o obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o -obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o +obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o ifeq ($(CONFIG_PERF_EVENTS),y) obj-$(CONFIG_BPF_SYSCALL) += stackmap.o endif diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c new file mode 100644 index 0000000..73f6709 --- /dev/null +++ b/kernel/bpf/bpf_lru_list.c @@ -0,0 +1,567 @@ +/* Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include + +#include "bpf_lru_list.h" + +#define LOCAL_FREE_TARGET (128) +#define LOCAL_NR_SCANS LOCAL_FREE_TARGET + +/* Helpers to get the local list index */ +#define LOCAL_LIST_IDX(t) ((t) - BPF_LOCAL_LIST_T_OFFSET) +#define LOCAL_FREE_LIST_IDX LOCAL_LIST_IDX(BPF_LRU_LOCAL_LIST_T_FREE) +#define LOCAL_PENDING_LIST_IDX LOCAL_LIST_IDX(BPF_LRU_LOCAL_LIST_T_PENDING) +#define IS_LOCAL_LIST_TYPE(t) ((t) >= BPF_LOCAL_LIST_T_OFFSET) + +static int get_next_cpu(int cpu) +{ + cpu = cpumask_next(cpu, cpu_possible_mask); + if (cpu >= nr_cpu_ids) + cpu = cpumask_first(cpu_possible_mask); + return cpu; +} + +/* Local list helpers */ +static struct list_head *local_free_list(struct bpf_lru_locallist *loc_l) +{ + return &loc_l->lists[LOCAL_FREE_LIST_IDX]; +} + +static struct list_head *local_pending_list(struct bpf_lru_locallist *loc_l) +{ + return &loc_l->lists[LOCAL_PENDING_LIST_IDX]; +} + +/* bpf_lru_node helpers */ +static bool bpf_lru_node_is_ref(const struct bpf_lru_node *node) +{ + return node->ref; +} + +static void bpf_lru_list_count_inc(struct bpf_lru_list *l, + enum bpf_lru_list_type type) +{ + if (type < NR_BPF_LRU_LIST_COUNT) + l->counts[type]++; +} + +static void bpf_lru_list_count_dec(struct bpf_lru_list *l, + enum bpf_lru_list_type type) +{ + if (type < NR_BPF_LRU_LIST_COUNT) + l->counts[type]--; +} + +static void __bpf_lru_node_move_to_free(struct bpf_lru_list *l, + struct bpf_lru_node *node, + struct list_head *free_list, + enum bpf_lru_list_type tgt_free_type) +{ + if (WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(node->type))) + return; + + /* If the removing node is the next_inactive_rotation candidate, + * move the next_inactive_rotation pointer also. + */ + if (&node->list == l->next_inactive_rotation) + l->next_inactive_rotation = l->next_inactive_rotation->prev; + + bpf_lru_list_count_dec(l, node->type); + + node->type = tgt_free_type; + list_move(&node->list, free_list); +} + +/* Move nodes from local list to the LRU list */ +static void __bpf_lru_node_move_in(struct bpf_lru_list *l, + struct bpf_lru_node *node, + enum bpf_lru_list_type tgt_type) +{ + if (WARN_ON_ONCE(!IS_LOCAL_LIST_TYPE(node->type)) || + WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(tgt_type))) + return; + + bpf_lru_list_count_inc(l, tgt_type); + node->type = tgt_type; + node->ref = 0; + list_move(&node->list, &l->lists[tgt_type]); +} + +/* Move nodes between or within active and inactive list (like + * active to inactive, inactive to active or tail of active back to + * the head of active). + */ +static void __bpf_lru_node_move(struct bpf_lru_list *l, + struct bpf_lru_node *node, + enum bpf_lru_list_type tgt_type) +{ + if (WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(node->type)) || + WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(tgt_type))) + return; + + if (node->type != tgt_type) { + bpf_lru_list_count_dec(l, node->type); + bpf_lru_list_count_inc(l, tgt_type); + node->type = tgt_type; + } + node->ref = 0; + + /* If the moving node is the next_inactive_rotation candidate, + * move the next_inactive_rotation pointer also. + */ + if (&node->list == l->next_inactive_rotation) + l->next_inactive_rotation = l->next_inactive_rotation->prev; + + list_move(&node->list, &l->lists[tgt_type]); +} + +static bool bpf_lru_list_inactive_low(const struct bpf_lru_list *l) +{ + return l->counts[BPF_LRU_LIST_T_INACTIVE] < + l->counts[BPF_LRU_LIST_T_ACTIVE]; +} + +/* Rotate the active list: + * 1. Start from tail + * 2. If the node has the ref bit set, it will be rotated + * back to the head of active list with the ref bit cleared. + * Give this node one more chance to survive in the active list. + * 3. If the ref bit is not set, move it to the head of the + * inactive list. + * 4. It will at most scan nr_scans nodes + */ +static void __bpf_lru_list_rotate_active(struct bpf_lru *lru, + struct bpf_lru_list *l) +{ + struct list_head *active = &l->lists[BPF_LRU_LIST_T_ACTIVE]; + struct bpf_lru_node *node, *tmp_node, *first_node; + unsigned int i = 0; + + first_node = list_first_entry(active, struct bpf_lru_node, list); + list_for_each_entry_safe_reverse(node, tmp_node, active, list) { + if (bpf_lru_node_is_ref(node)) + __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_ACTIVE); + else + __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_INACTIVE); + + if (++i == lru->nr_scans || node == first_node) + break; + } +} + +/* Rotate the inactive list. It starts from the next_inactive_rotation + * 1. If the node has ref bit set, it will be moved to the head + * of active list with the ref bit cleared. + * 2. If the node does not have ref bit set, it will leave it + * at its current location (i.e. do nothing) so that it can + * be considered during the next inactive_shrink. + * 3. It will at most scan nr_scans nodes + */ +static void __bpf_lru_list_rotate_inactive(struct bpf_lru *lru, + struct bpf_lru_list *l) +{ + struct list_head *inactive = &l->lists[BPF_LRU_LIST_T_INACTIVE]; + struct list_head *cur, *next, *last; + struct bpf_lru_node *node; + unsigned int i = 0; + + if (list_empty(inactive)) + return; + + last = l->next_inactive_rotation->next; + if (last == inactive) + last = last->next; + + cur = l->next_inactive_rotation; + while (i < lru->nr_scans) { + if (cur == inactive) { + cur = cur->prev; + continue; + } + + node = list_entry(cur, struct bpf_lru_node, list); + next = cur->prev; + if (bpf_lru_node_is_ref(node)) + __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_ACTIVE); + if (cur == last) + break; + cur = next; + i++; + } + + l->next_inactive_rotation = next; +} + +/* Shrink the inactive list. It starts from the tail of the + * inactive list and only move the nodes without the ref bit + * set to the designated free list. + */ +static unsigned int +__bpf_lru_list_shrink_inactive(struct bpf_lru *lru, + struct bpf_lru_list *l, + unsigned int tgt_nshrink, + struct list_head *free_list, + enum bpf_lru_list_type tgt_free_type) +{ + struct list_head *inactive = &l->lists[BPF_LRU_LIST_T_INACTIVE]; + struct bpf_lru_node *node, *tmp_node, *first_node; + unsigned int nshrinked = 0; + unsigned int i = 0; + + first_node = list_first_entry(inactive, struct bpf_lru_node, list); + list_for_each_entry_safe_reverse(node, tmp_node, inactive, list) { + if (bpf_lru_node_is_ref(node)) { + __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_ACTIVE); + } else if (lru->del_from_htab(lru->del_arg, node)) { + __bpf_lru_node_move_to_free(l, node, free_list, + tgt_free_type); + if (++nshrinked == tgt_nshrink) + break; + } + + if (++i == lru->nr_scans) + break; + } + + return nshrinked; +} + +/* 1. Rotate the active list (if needed) + * 2. Always rotate the inactive list + */ +static void __bpf_lru_list_rotate(struct bpf_lru *lru, struct bpf_lru_list *l) +{ + if (bpf_lru_list_inactive_low(l)) + __bpf_lru_list_rotate_active(lru, l); + + __bpf_lru_list_rotate_inactive(lru, l); +} + +/* Calls __bpf_lru_list_shrink_inactive() to shrink some + * ref-bit-cleared nodes and move them to the designated + * free list. + * + * If it cannot get a free node after calling + * __bpf_lru_list_shrink_inactive(). It will just remove + * one node from either inactive or active list without + * honoring the ref-bit. It prefers inactive list to active + * list in this situation. + */ +static unsigned int __bpf_lru_list_shrink(struct bpf_lru *lru, + struct bpf_lru_list *l, + unsigned int tgt_nshrink, + struct list_head *free_list, + enum bpf_lru_list_type tgt_free_type) + +{ + struct bpf_lru_node *node, *tmp_node; + struct list_head *force_shrink_list; + unsigned int nshrinked; + + nshrinked = __bpf_lru_list_shrink_inactive(lru, l, tgt_nshrink, + free_list, tgt_free_type); + if (nshrinked) + return nshrinked; + + /* Do a force shrink by ignoring the reference bit */ + if (!list_empty(&l->lists[BPF_LRU_LIST_T_INACTIVE])) + force_shrink_list = &l->lists[BPF_LRU_LIST_T_INACTIVE]; + else + force_shrink_list = &l->lists[BPF_LRU_LIST_T_ACTIVE]; + + list_for_each_entry_safe_reverse(node, tmp_node, force_shrink_list, + list) { + if (lru->del_from_htab(lru->del_arg, node)) { + __bpf_lru_node_move_to_free(l, node, free_list, + tgt_free_type); + return 1; + } + } + + return 0; +} + +/* Flush the nodes from the local pending list to the LRU list */ +static void __local_list_flush(struct bpf_lru_list *l, + struct bpf_lru_locallist *loc_l) +{ + struct bpf_lru_node *node, *tmp_node; + + list_for_each_entry_safe_reverse(node, tmp_node, + local_pending_list(loc_l), list) { + if (bpf_lru_node_is_ref(node)) + __bpf_lru_node_move_in(l, node, BPF_LRU_LIST_T_ACTIVE); + else + __bpf_lru_node_move_in(l, node, + BPF_LRU_LIST_T_INACTIVE); + } +} + +static void bpf_lru_list_push_free(struct bpf_lru_list *l, + struct bpf_lru_node *node) +{ + unsigned long flags; + + if (WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(node->type))) + return; + + raw_spin_lock_irqsave(&l->lock, flags); + __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE); + raw_spin_unlock_irqrestore(&l->lock, flags); +} + +static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, + struct bpf_lru_locallist *loc_l) +{ + struct bpf_lru_list *l = &lru->common_lru.lru_list; + struct bpf_lru_node *node, *tmp_node; + unsigned int nfree = 0; + + raw_spin_lock(&l->lock); + + __local_list_flush(l, loc_l); + + __bpf_lru_list_rotate(lru, l); + + list_for_each_entry_safe(node, tmp_node, &l->lists[BPF_LRU_LIST_T_FREE], + list) { + __bpf_lru_node_move_to_free(l, node, local_free_list(loc_l), + BPF_LRU_LOCAL_LIST_T_FREE); + if (++nfree == LOCAL_FREE_TARGET) + break; + } + + if (nfree < LOCAL_FREE_TARGET) + __bpf_lru_list_shrink(lru, l, LOCAL_FREE_TARGET - nfree, + local_free_list(loc_l), + BPF_LRU_LOCAL_LIST_T_FREE); + + raw_spin_unlock(&l->lock); +} + +static void __local_list_add_pending(struct bpf_lru *lru, + struct bpf_lru_locallist *loc_l, + int cpu, + struct bpf_lru_node *node, + u32 hash) +{ + *(u32 *)((void *)node + lru->hash_offset) = hash; + node->cpu = cpu; + node->type = BPF_LRU_LOCAL_LIST_T_PENDING; + node->ref = 0; + list_add(&node->list, local_pending_list(loc_l)); +} + +struct bpf_lru_node *__local_list_pop_free(struct bpf_lru_locallist *loc_l) +{ + struct bpf_lru_node *node; + + node = list_first_entry_or_null(local_free_list(loc_l), + struct bpf_lru_node, + list); + if (node) + list_del(&node->list); + + return node; +} + +struct bpf_lru_node *__local_list_pop_pending(struct bpf_lru *lru, + struct bpf_lru_locallist *loc_l) +{ + struct bpf_lru_node *node; + bool force = false; + +ignore_ref: + /* Get from the tail (i.e. older element) of the pending list. */ + list_for_each_entry_reverse(node, local_pending_list(loc_l), + list) { + if ((!bpf_lru_node_is_ref(node) || force) && + lru->del_from_htab(lru->del_arg, node)) { + list_del(&node->list); + return node; + } + } + + if (!force) { + force = true; + goto ignore_ref; + } + + return NULL; +} + +struct bpf_lru_node *bpf_lru_pop_free(struct bpf_lru *lru, u32 hash) +{ + struct bpf_lru_locallist *loc_l, *steal_loc_l; + struct bpf_common_lru *clru = &lru->common_lru; + struct bpf_lru_node *node; + int steal, first_steal; + unsigned long flags; + int cpu = raw_smp_processor_id(); + + loc_l = per_cpu_ptr(clru->local_list, cpu); + + raw_spin_lock_irqsave(&loc_l->lock, flags); + + node = __local_list_pop_free(loc_l); + if (!node) { + bpf_lru_list_pop_free_to_local(lru, loc_l); + node = __local_list_pop_free(loc_l); + } + + if (node) + __local_list_add_pending(lru, loc_l, cpu, node, hash); + + raw_spin_unlock_irqrestore(&loc_l->lock, flags); + + if (node) + return node; + + /* No free nodes found from the local free list and + * the global LRU list. + * + * Steal from the local free/pending list of the + * current CPU and remote CPU in RR. It starts + * with the loc_l->next_steal CPU. + */ + + first_steal = loc_l->next_steal; + steal = first_steal; + do { + steal_loc_l = per_cpu_ptr(clru->local_list, steal); + + raw_spin_lock_irqsave(&steal_loc_l->lock, flags); + + node = __local_list_pop_free(steal_loc_l); + if (!node) + node = __local_list_pop_pending(lru, steal_loc_l); + + raw_spin_unlock_irqrestore(&steal_loc_l->lock, flags); + + steal = get_next_cpu(steal); + } while (!node && steal != first_steal); + + loc_l->next_steal = steal; + + if (node) { + raw_spin_lock_irqsave(&loc_l->lock, flags); + __local_list_add_pending(lru, loc_l, cpu, node, hash); + raw_spin_unlock_irqrestore(&loc_l->lock, flags); + } + + return node; +} + +void bpf_lru_push_free(struct bpf_lru *lru, struct bpf_lru_node *node) +{ + unsigned long flags; + + if (WARN_ON_ONCE(node->type == BPF_LRU_LIST_T_FREE) || + WARN_ON_ONCE(node->type == BPF_LRU_LOCAL_LIST_T_FREE)) + return; + + if (node->type == BPF_LRU_LOCAL_LIST_T_PENDING) { + struct bpf_lru_locallist *loc_l; + + loc_l = per_cpu_ptr(lru->common_lru.local_list, node->cpu); + + raw_spin_lock_irqsave(&loc_l->lock, flags); + + if (unlikely(node->type != BPF_LRU_LOCAL_LIST_T_PENDING)) { + raw_spin_unlock_irqrestore(&loc_l->lock, flags); + goto check_lru_list; + } + + node->type = BPF_LRU_LOCAL_LIST_T_FREE; + node->ref = 0; + list_move(&node->list, local_free_list(loc_l)); + + raw_spin_unlock_irqrestore(&loc_l->lock, flags); + return; + } + +check_lru_list: + bpf_lru_list_push_free(&lru->common_lru.lru_list, node); +} + +void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, + u32 elem_size, u32 nr_elems) +{ + struct bpf_lru_list *l = &lru->common_lru.lru_list; + u32 i; + + for (i = 0; i < nr_elems; i++) { + struct bpf_lru_node *node; + + node = (struct bpf_lru_node *)(buf + node_offset); + node->type = BPF_LRU_LIST_T_FREE; + node->ref = 0; + list_add(&node->list, &l->lists[BPF_LRU_LIST_T_FREE]); + buf += elem_size; + } +} + +static void bpf_lru_locallist_init(struct bpf_lru_locallist *loc_l, int cpu) +{ + int i; + + for (i = 0; i < NR_BPF_LRU_LOCAL_LIST_T; i++) + INIT_LIST_HEAD(&loc_l->lists[i]); + + loc_l->next_steal = cpu; + + raw_spin_lock_init(&loc_l->lock); +} + +static void bpf_lru_list_init(struct bpf_lru_list *l) +{ + int i; + + for (i = 0; i < NR_BPF_LRU_LIST_T; i++) + INIT_LIST_HEAD(&l->lists[i]); + + for (i = 0; i < NR_BPF_LRU_LIST_COUNT; i++) + l->counts[i] = 0; + + l->next_inactive_rotation = &l->lists[BPF_LRU_LIST_T_INACTIVE]; + + raw_spin_lock_init(&l->lock); +} + +int bpf_lru_init(struct bpf_lru *lru, u32 hash_offset, + del_from_htab_func del_from_htab, void *del_arg) +{ + int cpu; + struct bpf_common_lru *clru = &lru->common_lru; + + clru->local_list = alloc_percpu(struct bpf_lru_locallist); + if (!clru->local_list) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + struct bpf_lru_locallist *loc_l; + + loc_l = per_cpu_ptr(clru->local_list, cpu); + bpf_lru_locallist_init(loc_l, cpu); + } + + bpf_lru_list_init(&clru->lru_list); + lru->nr_scans = LOCAL_NR_SCANS; + + lru->del_from_htab = del_from_htab; + lru->del_arg = del_arg; + lru->hash_offset = hash_offset; + + return 0; +} + +void bpf_lru_destroy(struct bpf_lru *lru) +{ + free_percpu(lru->common_lru.local_list); +} diff --git a/kernel/bpf/bpf_lru_list.h b/kernel/bpf/bpf_lru_list.h new file mode 100644 index 0000000..aaa2445 --- /dev/null +++ b/kernel/bpf/bpf_lru_list.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#ifndef __BPF_LRU_LIST_H_ +#define __BPF_LRU_LIST_H_ + +#include +#include + +#define NR_BPF_LRU_LIST_T (3) +#define NR_BPF_LRU_LIST_COUNT (2) +#define NR_BPF_LRU_LOCAL_LIST_T (2) +#define BPF_LOCAL_LIST_T_OFFSET NR_BPF_LRU_LIST_T + +enum bpf_lru_list_type { + BPF_LRU_LIST_T_ACTIVE, + BPF_LRU_LIST_T_INACTIVE, + BPF_LRU_LIST_T_FREE, + BPF_LRU_LOCAL_LIST_T_FREE, + BPF_LRU_LOCAL_LIST_T_PENDING, +}; + +struct bpf_lru_node { + struct list_head list; + u16 cpu; + u8 type; + u8 ref; +}; + +struct bpf_lru_list { + struct list_head lists[NR_BPF_LRU_LIST_T]; + unsigned int counts[NR_BPF_LRU_LIST_COUNT]; + /* The next inacitve list rotation starts from here */ + struct list_head *next_inactive_rotation; + + raw_spinlock_t lock ____cacheline_aligned_in_smp; +}; + +struct bpf_lru_locallist { + struct list_head lists[NR_BPF_LRU_LOCAL_LIST_T]; + u16 next_steal; + raw_spinlock_t lock; +}; + +struct bpf_common_lru { + struct bpf_lru_list lru_list; + struct bpf_lru_locallist __percpu *local_list; +}; + +typedef bool (*del_from_htab_func)(void *arg, struct bpf_lru_node *node); + +struct bpf_lru { + struct bpf_common_lru common_lru; + del_from_htab_func del_from_htab; + void *del_arg; + unsigned int hash_offset; + unsigned int nr_scans; +}; + +static inline void bpf_lru_node_set_ref(struct bpf_lru_node *node) +{ + /* ref is an approximation on access frequency. It does not + * have to be very accurate. Hence, no protection is used. + */ + node->ref = 1; +} + +int bpf_lru_init(struct bpf_lru *lru, u32 hash_offset, + del_from_htab_func del_from_htab, void *delete_arg); +void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, + u32 elem_size, u32 nr_elems); +void bpf_lru_destroy(struct bpf_lru *lru); +struct bpf_lru_node *bpf_lru_pop_free(struct bpf_lru *lru, u32 hash); +void bpf_lru_push_free(struct bpf_lru *lru, struct bpf_lru_node *node); +void bpf_lru_promote(struct bpf_lru *lru, struct bpf_lru_node *node); + +#endif -- cgit v1.1 From 961578b63474d13ad0e2f615fcc2901c5197dda6 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 11 Nov 2016 10:55:07 -0800 Subject: bpf: Add percpu LRU list Instead of having a common LRU list, this patch allows a percpu LRU list which can be selected by specifying a map attribute. The map attribute will be added in the later patch. While the common use case for LRU is #reads >> #updates, percpu LRU list allows bpf prog to absorb unusual #updates under pathological case (e.g. external traffic facing machine which could be under attack). Each percpu LRU is isolated from each other. The LRU nodes (including free nodes) cannot be moved across different LRU Lists. Here are the update performance comparison between common LRU list and percpu LRU list (the test code is at the last patch): [root@kerneltest003.31.prn1 ~]# for i in 1 4 8; do echo -n "$i cpus: "; \ ./map_perf_test 16 $i | awk '{r += $3}END{print r " updates"}'; done 1 cpus: 2934082 updates 4 cpus: 7391434 updates 8 cpus: 6500576 updates [root@kerneltest003.31.prn1 ~]# for i in 1 4 8; do echo -n "$i cpus: "; \ ./map_perf_test 32 $i | awk '{r += $3}END{printr " updates"}'; done 1 cpus: 2896553 updates 4 cpus: 9766395 updates 8 cpus: 17460553 updates Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/bpf_lru_list.c | 162 +++++++++++++++++++++++++++++++++++++++++----- kernel/bpf/bpf_lru_list.h | 8 ++- 2 files changed, 151 insertions(+), 19 deletions(-) diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c index 73f6709..bfebff0 100644 --- a/kernel/bpf/bpf_lru_list.c +++ b/kernel/bpf/bpf_lru_list.c @@ -13,6 +13,9 @@ #define LOCAL_FREE_TARGET (128) #define LOCAL_NR_SCANS LOCAL_FREE_TARGET +#define PERCPU_FREE_TARGET (16) +#define PERCPU_NR_SCANS PERCPU_FREE_TARGET + /* Helpers to get the local list index */ #define LOCAL_LIST_IDX(t) ((t) - BPF_LOCAL_LIST_T_OFFSET) #define LOCAL_FREE_LIST_IDX LOCAL_LIST_IDX(BPF_LRU_LOCAL_LIST_T_FREE) @@ -396,7 +399,40 @@ ignore_ref: return NULL; } -struct bpf_lru_node *bpf_lru_pop_free(struct bpf_lru *lru, u32 hash) +static struct bpf_lru_node *bpf_percpu_lru_pop_free(struct bpf_lru *lru, + u32 hash) +{ + struct list_head *free_list; + struct bpf_lru_node *node = NULL; + struct bpf_lru_list *l; + unsigned long flags; + int cpu = raw_smp_processor_id(); + + l = per_cpu_ptr(lru->percpu_lru, cpu); + + raw_spin_lock_irqsave(&l->lock, flags); + + __bpf_lru_list_rotate(lru, l); + + free_list = &l->lists[BPF_LRU_LIST_T_FREE]; + if (list_empty(free_list)) + __bpf_lru_list_shrink(lru, l, PERCPU_FREE_TARGET, free_list, + BPF_LRU_LIST_T_FREE); + + if (!list_empty(free_list)) { + node = list_first_entry(free_list, struct bpf_lru_node, list); + *(u32 *)((void *)node + lru->hash_offset) = hash; + node->ref = 0; + __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_INACTIVE); + } + + raw_spin_unlock_irqrestore(&l->lock, flags); + + return node; +} + +static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru, + u32 hash) { struct bpf_lru_locallist *loc_l, *steal_loc_l; struct bpf_common_lru *clru = &lru->common_lru; @@ -458,7 +494,16 @@ struct bpf_lru_node *bpf_lru_pop_free(struct bpf_lru *lru, u32 hash) return node; } -void bpf_lru_push_free(struct bpf_lru *lru, struct bpf_lru_node *node) +struct bpf_lru_node *bpf_lru_pop_free(struct bpf_lru *lru, u32 hash) +{ + if (lru->percpu) + return bpf_percpu_lru_pop_free(lru, hash); + else + return bpf_common_lru_pop_free(lru, hash); +} + +static void bpf_common_lru_push_free(struct bpf_lru *lru, + struct bpf_lru_node *node) { unsigned long flags; @@ -490,8 +535,31 @@ check_lru_list: bpf_lru_list_push_free(&lru->common_lru.lru_list, node); } -void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, - u32 elem_size, u32 nr_elems) +static void bpf_percpu_lru_push_free(struct bpf_lru *lru, + struct bpf_lru_node *node) +{ + struct bpf_lru_list *l; + unsigned long flags; + + l = per_cpu_ptr(lru->percpu_lru, node->cpu); + + raw_spin_lock_irqsave(&l->lock, flags); + + __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE); + + raw_spin_unlock_irqrestore(&l->lock, flags); +} + +void bpf_lru_push_free(struct bpf_lru *lru, struct bpf_lru_node *node) +{ + if (lru->percpu) + bpf_percpu_lru_push_free(lru, node); + else + bpf_common_lru_push_free(lru, node); +} + +void bpf_common_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, + u32 elem_size, u32 nr_elems) { struct bpf_lru_list *l = &lru->common_lru.lru_list; u32 i; @@ -507,6 +575,47 @@ void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, } } +void bpf_percpu_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, + u32 elem_size, u32 nr_elems) +{ + u32 i, pcpu_entries; + int cpu; + struct bpf_lru_list *l; + + pcpu_entries = nr_elems / num_possible_cpus(); + + i = 0; + + for_each_possible_cpu(cpu) { + struct bpf_lru_node *node; + + l = per_cpu_ptr(lru->percpu_lru, cpu); +again: + node = (struct bpf_lru_node *)(buf + node_offset); + node->cpu = cpu; + node->type = BPF_LRU_LIST_T_FREE; + node->ref = 0; + list_add(&node->list, &l->lists[BPF_LRU_LIST_T_FREE]); + i++; + buf += elem_size; + if (i == nr_elems) + break; + if (i % pcpu_entries) + goto again; + } +} + +void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, + u32 elem_size, u32 nr_elems) +{ + if (lru->percpu) + bpf_percpu_lru_populate(lru, buf, node_offset, elem_size, + nr_elems); + else + bpf_common_lru_populate(lru, buf, node_offset, elem_size, + nr_elems); +} + static void bpf_lru_locallist_init(struct bpf_lru_locallist *loc_l, int cpu) { int i; @@ -534,26 +643,42 @@ static void bpf_lru_list_init(struct bpf_lru_list *l) raw_spin_lock_init(&l->lock); } -int bpf_lru_init(struct bpf_lru *lru, u32 hash_offset, +int bpf_lru_init(struct bpf_lru *lru, bool percpu, u32 hash_offset, del_from_htab_func del_from_htab, void *del_arg) { int cpu; - struct bpf_common_lru *clru = &lru->common_lru; - clru->local_list = alloc_percpu(struct bpf_lru_locallist); - if (!clru->local_list) - return -ENOMEM; + if (percpu) { + lru->percpu_lru = alloc_percpu(struct bpf_lru_list); + if (!lru->percpu_lru) + return -ENOMEM; - for_each_possible_cpu(cpu) { - struct bpf_lru_locallist *loc_l; + for_each_possible_cpu(cpu) { + struct bpf_lru_list *l; - loc_l = per_cpu_ptr(clru->local_list, cpu); - bpf_lru_locallist_init(loc_l, cpu); - } + l = per_cpu_ptr(lru->percpu_lru, cpu); + bpf_lru_list_init(l); + } + lru->nr_scans = PERCPU_NR_SCANS; + } else { + struct bpf_common_lru *clru = &lru->common_lru; - bpf_lru_list_init(&clru->lru_list); - lru->nr_scans = LOCAL_NR_SCANS; + clru->local_list = alloc_percpu(struct bpf_lru_locallist); + if (!clru->local_list) + return -ENOMEM; + for_each_possible_cpu(cpu) { + struct bpf_lru_locallist *loc_l; + + loc_l = per_cpu_ptr(clru->local_list, cpu); + bpf_lru_locallist_init(loc_l, cpu); + } + + bpf_lru_list_init(&clru->lru_list); + lru->nr_scans = LOCAL_NR_SCANS; + } + + lru->percpu = percpu; lru->del_from_htab = del_from_htab; lru->del_arg = del_arg; lru->hash_offset = hash_offset; @@ -563,5 +688,8 @@ int bpf_lru_init(struct bpf_lru *lru, u32 hash_offset, void bpf_lru_destroy(struct bpf_lru *lru) { - free_percpu(lru->common_lru.local_list); + if (lru->percpu) + free_percpu(lru->percpu_lru); + else + free_percpu(lru->common_lru.local_list); } diff --git a/kernel/bpf/bpf_lru_list.h b/kernel/bpf/bpf_lru_list.h index aaa2445..5c35a98 100644 --- a/kernel/bpf/bpf_lru_list.h +++ b/kernel/bpf/bpf_lru_list.h @@ -53,11 +53,15 @@ struct bpf_common_lru { typedef bool (*del_from_htab_func)(void *arg, struct bpf_lru_node *node); struct bpf_lru { - struct bpf_common_lru common_lru; + union { + struct bpf_common_lru common_lru; + struct bpf_lru_list __percpu *percpu_lru; + }; del_from_htab_func del_from_htab; void *del_arg; unsigned int hash_offset; unsigned int nr_scans; + bool percpu; }; static inline void bpf_lru_node_set_ref(struct bpf_lru_node *node) @@ -68,7 +72,7 @@ static inline void bpf_lru_node_set_ref(struct bpf_lru_node *node) node->ref = 1; } -int bpf_lru_init(struct bpf_lru *lru, u32 hash_offset, +int bpf_lru_init(struct bpf_lru *lru, bool percpu, u32 hash_offset, del_from_htab_func del_from_htab, void *delete_arg); void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, u32 elem_size, u32 nr_elems); -- cgit v1.1 From fd91de7b3c69a7f108b92521e1115df3e058af55 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 11 Nov 2016 10:55:08 -0800 Subject: bpf: Refactor codes handling percpu map Refactor the codes that populate the value of a htab_elem in a BPF_MAP_TYPE_PERCPU_HASH typed bpf_map. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/hashtab.c | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index ad1bc67..b478d80 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -420,6 +420,24 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l) } } +static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, + void *value, bool onallcpus) +{ + if (!onallcpus) { + /* copy true value_size bytes */ + memcpy(this_cpu_ptr(pptr), value, htab->map.value_size); + } else { + u32 size = round_up(htab->map.value_size, 8); + int off = 0, cpu; + + for_each_possible_cpu(cpu) { + bpf_long_memcpy(per_cpu_ptr(pptr, cpu), + value + off, size); + off += size; + } + } +} + static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key, void *value, u32 key_size, u32 hash, bool percpu, bool onallcpus, @@ -479,18 +497,8 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key, } } - if (!onallcpus) { - /* copy true value_size bytes */ - memcpy(this_cpu_ptr(pptr), value, htab->map.value_size); - } else { - int off = 0, cpu; + pcpu_copy_value(htab, pptr, value, onallcpus); - for_each_possible_cpu(cpu) { - bpf_long_memcpy(per_cpu_ptr(pptr, cpu), - value + off, size); - off += size; - } - } if (!prealloc) htab_elem_set_ptr(l_new, key_size, pptr); } else { @@ -606,22 +614,9 @@ static int __htab_percpu_map_update_elem(struct bpf_map *map, void *key, goto err; if (l_old) { - void __percpu *pptr = htab_elem_get_ptr(l_old, key_size); - u32 size = htab->map.value_size; - /* per-cpu hash map can update value in-place */ - if (!onallcpus) { - memcpy(this_cpu_ptr(pptr), value, size); - } else { - int off = 0, cpu; - - size = round_up(size, 8); - for_each_possible_cpu(cpu) { - bpf_long_memcpy(per_cpu_ptr(pptr, cpu), - value + off, size); - off += size; - } - } + pcpu_copy_value(htab, htab_elem_get_ptr(l_old, key_size), + value, onallcpus); } else { l_new = alloc_htab_elem(htab, key, value, key_size, hash, true, onallcpus, false); -- cgit v1.1 From 29ba732acbeece1e34c68483d1ec1f3720fa1bb3 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 11 Nov 2016 10:55:09 -0800 Subject: bpf: Add BPF_MAP_TYPE_LRU_HASH Provide a LRU version of the existing BPF_MAP_TYPE_HASH. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 8 ++ kernel/bpf/hashtab.c | 266 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 260 insertions(+), 14 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index e2f38e0..ed8c679 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -85,6 +85,7 @@ enum bpf_map_type { BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_STACK_TRACE, BPF_MAP_TYPE_CGROUP_ARRAY, + BPF_MAP_TYPE_LRU_HASH, }; enum bpf_prog_type { @@ -106,6 +107,13 @@ enum bpf_prog_type { #define BPF_EXIST 2 /* update existing element */ #define BPF_F_NO_PREALLOC (1U << 0) +/* Instead of having one common LRU list in the + * BPF_MAP_TYPE_LRU_HASH map, use a percpu LRU list + * which can scale and perform better. + * Note, the LRU nodes (including free nodes) cannot be moved + * across different LRU lists. + */ +#define BPF_F_NO_COMMON_LRU (1U << 1) union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index b478d80..4a9e71a 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -15,6 +15,7 @@ #include #include #include "percpu_freelist.h" +#include "bpf_lru_list.h" struct bucket { struct hlist_head head; @@ -25,7 +26,10 @@ struct bpf_htab { struct bpf_map map; struct bucket *buckets; void *elems; - struct pcpu_freelist freelist; + union { + struct pcpu_freelist freelist; + struct bpf_lru lru; + }; void __percpu *extra_elems; atomic_t count; /* number of elements in this hashtable */ u32 n_buckets; /* number of hash buckets */ @@ -48,11 +52,19 @@ struct htab_elem { union { struct rcu_head rcu; enum extra_elem_state state; + struct bpf_lru_node lru_node; }; u32 hash; char key[0] __aligned(8); }; +static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node); + +static bool htab_is_lru(const struct bpf_htab *htab) +{ + return htab->map.map_type == BPF_MAP_TYPE_LRU_HASH; +} + static inline void htab_elem_set_ptr(struct htab_elem *l, u32 key_size, void __percpu *pptr) { @@ -87,7 +99,22 @@ free_elems: vfree(htab->elems); } -static int prealloc_elems_and_freelist(struct bpf_htab *htab) +static struct htab_elem *prealloc_lru_pop(struct bpf_htab *htab, void *key, + u32 hash) +{ + struct bpf_lru_node *node = bpf_lru_pop_free(&htab->lru, hash); + struct htab_elem *l; + + if (node) { + l = container_of(node, struct htab_elem, lru_node); + memcpy(l->key, key, htab->map.key_size); + return l; + } + + return NULL; +} + +static int prealloc_init(struct bpf_htab *htab) { int err = -ENOMEM, i; @@ -110,12 +137,27 @@ static int prealloc_elems_and_freelist(struct bpf_htab *htab) } skip_percpu_elems: - err = pcpu_freelist_init(&htab->freelist); + if (htab_is_lru(htab)) + err = bpf_lru_init(&htab->lru, + htab->map.map_flags & BPF_F_NO_COMMON_LRU, + offsetof(struct htab_elem, hash) - + offsetof(struct htab_elem, lru_node), + htab_lru_map_delete_node, + htab); + else + err = pcpu_freelist_init(&htab->freelist); + if (err) goto free_elems; - pcpu_freelist_populate(&htab->freelist, htab->elems, htab->elem_size, - htab->map.max_entries); + if (htab_is_lru(htab)) + bpf_lru_populate(&htab->lru, htab->elems, + offsetof(struct htab_elem, lru_node), + htab->elem_size, htab->map.max_entries); + else + pcpu_freelist_populate(&htab->freelist, htab->elems, + htab->elem_size, htab->map.max_entries); + return 0; free_elems: @@ -123,6 +165,16 @@ free_elems: return err; } +static void prealloc_destroy(struct bpf_htab *htab) +{ + htab_free_elems(htab); + + if (htab_is_lru(htab)) + bpf_lru_destroy(&htab->lru); + else + pcpu_freelist_destroy(&htab->freelist); +} + static int alloc_extra_elems(struct bpf_htab *htab) { void __percpu *pptr; @@ -144,14 +196,34 @@ static int alloc_extra_elems(struct bpf_htab *htab) static struct bpf_map *htab_map_alloc(union bpf_attr *attr) { bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_HASH; + bool lru = attr->map_type == BPF_MAP_TYPE_LRU_HASH; + /* percpu_lru means each cpu has its own LRU list. + * it is different from BPF_MAP_TYPE_PERCPU_HASH where + * the map's value itself is percpu. percpu_lru has + * nothing to do with the map's value. + */ + bool percpu_lru = (attr->map_flags & BPF_F_NO_COMMON_LRU); + bool prealloc = !(attr->map_flags & BPF_F_NO_PREALLOC); struct bpf_htab *htab; int err, i; u64 cost; - if (attr->map_flags & ~BPF_F_NO_PREALLOC) + if (lru && !capable(CAP_SYS_ADMIN)) + /* LRU implementation is much complicated than other + * maps. Hence, limit to CAP_SYS_ADMIN for now. + */ + return ERR_PTR(-EPERM); + + if (attr->map_flags & ~(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU)) /* reserved bits should not be used */ return ERR_PTR(-EINVAL); + if (!lru && percpu_lru) + return ERR_PTR(-EINVAL); + + if (lru && !prealloc) + return ERR_PTR(-ENOTSUPP); + htab = kzalloc(sizeof(*htab), GFP_USER); if (!htab) return ERR_PTR(-ENOMEM); @@ -171,6 +243,18 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) htab->map.value_size == 0) goto free_htab; + if (percpu_lru) { + /* ensure each CPU's lru list has >=1 elements. + * since we are at it, make each lru list has the same + * number of elements. + */ + htab->map.max_entries = roundup(attr->max_entries, + num_possible_cpus()); + if (htab->map.max_entries < attr->max_entries) + htab->map.max_entries = rounddown(attr->max_entries, + num_possible_cpus()); + } + /* hash table size must be power of 2 */ htab->n_buckets = roundup_pow_of_two(htab->map.max_entries); @@ -241,14 +325,17 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) raw_spin_lock_init(&htab->buckets[i].lock); } - if (!percpu) { + if (!percpu && !lru) { + /* lru itself can remove the least used element, so + * there is no need for an extra elem during map_update. + */ err = alloc_extra_elems(htab); if (err) goto free_buckets; } - if (!(attr->map_flags & BPF_F_NO_PREALLOC)) { - err = prealloc_elems_and_freelist(htab); + if (prealloc) { + err = prealloc_init(htab); if (err) goto free_extra_elems; } @@ -323,6 +410,46 @@ static void *htab_map_lookup_elem(struct bpf_map *map, void *key) return NULL; } +static void *htab_lru_map_lookup_elem(struct bpf_map *map, void *key) +{ + struct htab_elem *l = __htab_map_lookup_elem(map, key); + + if (l) { + bpf_lru_node_set_ref(&l->lru_node); + return l->key + round_up(map->key_size, 8); + } + + return NULL; +} + +/* It is called from the bpf_lru_list when the LRU needs to delete + * older elements from the htab. + */ +static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node) +{ + struct bpf_htab *htab = (struct bpf_htab *)arg; + struct htab_elem *l, *tgt_l; + struct hlist_head *head; + unsigned long flags; + struct bucket *b; + + tgt_l = container_of(node, struct htab_elem, lru_node); + b = __select_bucket(htab, tgt_l->hash); + head = &b->head; + + raw_spin_lock_irqsave(&b->lock, flags); + + hlist_for_each_entry_rcu(l, head, hash_node) + if (l == tgt_l) { + hlist_del_rcu(&l->hash_node); + break; + } + + raw_spin_unlock_irqrestore(&b->lock, flags); + + return l == tgt_l; +} + /* Called from syscall */ static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key) { @@ -579,6 +706,70 @@ err: return ret; } +static int htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value, + u64 map_flags) +{ + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); + struct htab_elem *l_new, *l_old = NULL; + struct hlist_head *head; + unsigned long flags; + struct bucket *b; + u32 key_size, hash; + int ret; + + if (unlikely(map_flags > BPF_EXIST)) + /* unknown flags */ + return -EINVAL; + + WARN_ON_ONCE(!rcu_read_lock_held()); + + key_size = map->key_size; + + hash = htab_map_hash(key, key_size); + + b = __select_bucket(htab, hash); + head = &b->head; + + /* For LRU, we need to alloc before taking bucket's + * spinlock because getting free nodes from LRU may need + * to remove older elements from htab and this removal + * operation will need a bucket lock. + */ + l_new = prealloc_lru_pop(htab, key, hash); + if (!l_new) + return -ENOMEM; + memcpy(l_new->key + round_up(map->key_size, 8), value, map->value_size); + + /* bpf_map_update_elem() can be called in_irq() */ + raw_spin_lock_irqsave(&b->lock, flags); + + l_old = lookup_elem_raw(head, hash, key, key_size); + + ret = check_flags(htab, l_old, map_flags); + if (ret) + goto err; + + /* add new element to the head of the list, so that + * concurrent search will find it before old elem + */ + hlist_add_head_rcu(&l_new->hash_node, head); + if (l_old) { + bpf_lru_node_set_ref(&l_new->lru_node); + hlist_del_rcu(&l_old->hash_node); + } + ret = 0; + +err: + raw_spin_unlock_irqrestore(&b->lock, flags); + + if (ret) + bpf_lru_push_free(&htab->lru, &l_new->lru_node); + else if (l_old) + bpf_lru_push_free(&htab->lru, &l_old->lru_node); + + return ret; +} + static int __htab_percpu_map_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags, bool onallcpus) @@ -671,6 +862,39 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key) return ret; } +static int htab_lru_map_delete_elem(struct bpf_map *map, void *key) +{ + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); + struct hlist_head *head; + struct bucket *b; + struct htab_elem *l; + unsigned long flags; + u32 hash, key_size; + int ret = -ENOENT; + + WARN_ON_ONCE(!rcu_read_lock_held()); + + key_size = map->key_size; + + hash = htab_map_hash(key, key_size); + b = __select_bucket(htab, hash); + head = &b->head; + + raw_spin_lock_irqsave(&b->lock, flags); + + l = lookup_elem_raw(head, hash, key, key_size); + + if (l) { + hlist_del_rcu(&l->hash_node); + ret = 0; + } + + raw_spin_unlock_irqrestore(&b->lock, flags); + if (l) + bpf_lru_push_free(&htab->lru, &l->lru_node); + return ret; +} + static void delete_all_elements(struct bpf_htab *htab) { int i; @@ -703,12 +927,11 @@ static void htab_map_free(struct bpf_map *map) * not have executed. Wait for them. */ rcu_barrier(); - if (htab->map.map_flags & BPF_F_NO_PREALLOC) { + if (htab->map.map_flags & BPF_F_NO_PREALLOC) delete_all_elements(htab); - } else { - htab_free_elems(htab); - pcpu_freelist_destroy(&htab->freelist); - } + else + prealloc_destroy(htab); + free_percpu(htab->extra_elems); kvfree(htab->buckets); kfree(htab); @@ -728,6 +951,20 @@ static struct bpf_map_type_list htab_type __read_mostly = { .type = BPF_MAP_TYPE_HASH, }; +static const struct bpf_map_ops htab_lru_ops = { + .map_alloc = htab_map_alloc, + .map_free = htab_map_free, + .map_get_next_key = htab_map_get_next_key, + .map_lookup_elem = htab_lru_map_lookup_elem, + .map_update_elem = htab_lru_map_update_elem, + .map_delete_elem = htab_lru_map_delete_elem, +}; + +static struct bpf_map_type_list htab_lru_type __read_mostly = { + .ops = &htab_lru_ops, + .type = BPF_MAP_TYPE_LRU_HASH, +}; + /* Called from eBPF program */ static void *htab_percpu_map_lookup_elem(struct bpf_map *map, void *key) { @@ -798,6 +1035,7 @@ static int __init register_htab_map(void) { bpf_register_map_type(&htab_type); bpf_register_map_type(&htab_percpu_type); + bpf_register_map_type(&htab_lru_type); return 0; } late_initcall(register_htab_map); -- cgit v1.1 From 8f8449384ec364ba2a654f11f94e754e4ff719e0 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 11 Nov 2016 10:55:10 -0800 Subject: bpf: Add BPF_MAP_TYPE_LRU_PERCPU_HASH Provide a LRU version of the existing BPF_MAP_TYPE_PERCPU_HASH Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 3 +- kernel/bpf/hashtab.c | 129 ++++++++++++++++++++++++++++++++++++++++++++--- kernel/bpf/syscall.c | 8 ++- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ed8c679..7d9b283 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -86,6 +86,7 @@ enum bpf_map_type { BPF_MAP_TYPE_STACK_TRACE, BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_LRU_HASH, + BPF_MAP_TYPE_LRU_PERCPU_HASH, }; enum bpf_prog_type { @@ -108,7 +109,7 @@ enum bpf_prog_type { #define BPF_F_NO_PREALLOC (1U << 0) /* Instead of having one common LRU list in the - * BPF_MAP_TYPE_LRU_HASH map, use a percpu LRU list + * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list * which can scale and perform better. * Note, the LRU nodes (including free nodes) cannot be moved * across different LRU lists. diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 4a9e71a..34debc1 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -62,7 +62,14 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node); static bool htab_is_lru(const struct bpf_htab *htab) { - return htab->map.map_type == BPF_MAP_TYPE_LRU_HASH; + return htab->map.map_type == BPF_MAP_TYPE_LRU_HASH || + htab->map.map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH; +} + +static bool htab_is_percpu(const struct bpf_htab *htab) +{ + return htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH || + htab->map.map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH; } static inline void htab_elem_set_ptr(struct htab_elem *l, u32 key_size, @@ -85,7 +92,7 @@ static void htab_free_elems(struct bpf_htab *htab) { int i; - if (htab->map.map_type != BPF_MAP_TYPE_PERCPU_HASH) + if (!htab_is_percpu(htab)) goto free_elems; for (i = 0; i < htab->map.max_entries; i++) { @@ -122,7 +129,7 @@ static int prealloc_init(struct bpf_htab *htab) if (!htab->elems) return -ENOMEM; - if (htab->map.map_type != BPF_MAP_TYPE_PERCPU_HASH) + if (!htab_is_percpu(htab)) goto skip_percpu_elems; for (i = 0; i < htab->map.max_entries; i++) { @@ -195,8 +202,10 @@ static int alloc_extra_elems(struct bpf_htab *htab) /* Called from syscall */ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) { - bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_HASH; - bool lru = attr->map_type == BPF_MAP_TYPE_LRU_HASH; + bool percpu = (attr->map_type == BPF_MAP_TYPE_PERCPU_HASH || + attr->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH); + bool lru = (attr->map_type == BPF_MAP_TYPE_LRU_HASH || + attr->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH); /* percpu_lru means each cpu has its own LRU list. * it is different from BPF_MAP_TYPE_PERCPU_HASH where * the map's value itself is percpu. percpu_lru has @@ -823,12 +832,84 @@ err: return ret; } +static int __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key, + void *value, u64 map_flags, + bool onallcpus) +{ + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); + struct htab_elem *l_new = NULL, *l_old; + struct hlist_head *head; + unsigned long flags; + struct bucket *b; + u32 key_size, hash; + int ret; + + if (unlikely(map_flags > BPF_EXIST)) + /* unknown flags */ + return -EINVAL; + + WARN_ON_ONCE(!rcu_read_lock_held()); + + key_size = map->key_size; + + hash = htab_map_hash(key, key_size); + + b = __select_bucket(htab, hash); + head = &b->head; + + /* For LRU, we need to alloc before taking bucket's + * spinlock because LRU's elem alloc may need + * to remove older elem from htab and this removal + * operation will need a bucket lock. + */ + if (map_flags != BPF_EXIST) { + l_new = prealloc_lru_pop(htab, key, hash); + if (!l_new) + return -ENOMEM; + } + + /* bpf_map_update_elem() can be called in_irq() */ + raw_spin_lock_irqsave(&b->lock, flags); + + l_old = lookup_elem_raw(head, hash, key, key_size); + + ret = check_flags(htab, l_old, map_flags); + if (ret) + goto err; + + if (l_old) { + bpf_lru_node_set_ref(&l_old->lru_node); + + /* per-cpu hash map can update value in-place */ + pcpu_copy_value(htab, htab_elem_get_ptr(l_old, key_size), + value, onallcpus); + } else { + pcpu_copy_value(htab, htab_elem_get_ptr(l_new, key_size), + value, onallcpus); + hlist_add_head_rcu(&l_new->hash_node, head); + l_new = NULL; + } + ret = 0; +err: + raw_spin_unlock_irqrestore(&b->lock, flags); + if (l_new) + bpf_lru_push_free(&htab->lru, &l_new->lru_node); + return ret; +} + static int htab_percpu_map_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags) { return __htab_percpu_map_update_elem(map, key, value, map_flags, false); } +static int htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key, + void *value, u64 map_flags) +{ + return __htab_lru_percpu_map_update_elem(map, key, value, map_flags, + false); +} + /* Called from syscall or from eBPF program */ static int htab_map_delete_elem(struct bpf_map *map, void *key) { @@ -976,8 +1057,21 @@ static void *htab_percpu_map_lookup_elem(struct bpf_map *map, void *key) return NULL; } +static void *htab_lru_percpu_map_lookup_elem(struct bpf_map *map, void *key) +{ + struct htab_elem *l = __htab_map_lookup_elem(map, key); + + if (l) { + bpf_lru_node_set_ref(&l->lru_node); + return this_cpu_ptr(htab_elem_get_ptr(l, map->key_size)); + } + + return NULL; +} + int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value) { + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); struct htab_elem *l; void __percpu *pptr; int ret = -ENOENT; @@ -993,6 +1087,8 @@ int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value) l = __htab_map_lookup_elem(map, key); if (!l) goto out; + if (htab_is_lru(htab)) + bpf_lru_node_set_ref(&l->lru_node); pptr = htab_elem_get_ptr(l, map->key_size); for_each_possible_cpu(cpu) { bpf_long_memcpy(value + off, @@ -1008,10 +1104,16 @@ out: int bpf_percpu_hash_update(struct bpf_map *map, void *key, void *value, u64 map_flags) { + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); int ret; rcu_read_lock(); - ret = __htab_percpu_map_update_elem(map, key, value, map_flags, true); + if (htab_is_lru(htab)) + ret = __htab_lru_percpu_map_update_elem(map, key, value, + map_flags, true); + else + ret = __htab_percpu_map_update_elem(map, key, value, map_flags, + true); rcu_read_unlock(); return ret; @@ -1031,11 +1133,26 @@ static struct bpf_map_type_list htab_percpu_type __read_mostly = { .type = BPF_MAP_TYPE_PERCPU_HASH, }; +static const struct bpf_map_ops htab_lru_percpu_ops = { + .map_alloc = htab_map_alloc, + .map_free = htab_map_free, + .map_get_next_key = htab_map_get_next_key, + .map_lookup_elem = htab_lru_percpu_map_lookup_elem, + .map_update_elem = htab_lru_percpu_map_update_elem, + .map_delete_elem = htab_lru_map_delete_elem, +}; + +static struct bpf_map_type_list htab_lru_percpu_type __read_mostly = { + .ops = &htab_lru_percpu_ops, + .type = BPF_MAP_TYPE_LRU_PERCPU_HASH, +}; + static int __init register_htab_map(void) { bpf_register_map_type(&htab_type); bpf_register_map_type(&htab_percpu_type); bpf_register_map_type(&htab_lru_type); + bpf_register_map_type(&htab_lru_percpu_type); return 0; } late_initcall(register_htab_map); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 233e3ac..ce1b7de 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -292,6 +292,7 @@ static int map_lookup_elem(union bpf_attr *attr) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || + map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) value_size = round_up(map->value_size, 8) * num_possible_cpus(); else @@ -302,7 +303,8 @@ static int map_lookup_elem(union bpf_attr *attr) if (!value) goto free_key; - if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH) { + if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || + map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { err = bpf_percpu_hash_copy(map, key, value); } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { err = bpf_percpu_array_copy(map, key, value); @@ -366,6 +368,7 @@ static int map_update_elem(union bpf_attr *attr) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || + map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) value_size = round_up(map->value_size, 8) * num_possible_cpus(); else @@ -385,7 +388,8 @@ static int map_update_elem(union bpf_attr *attr) */ preempt_disable(); __this_cpu_inc(bpf_prog_active); - if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH) { + if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || + map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { err = bpf_percpu_hash_update(map, key, value, attr->flags); } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { err = bpf_percpu_array_update(map, key, value, attr->flags); -- cgit v1.1 From 5db58faf989f16d1d6a3d661aac616f9ca7932aa Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 11 Nov 2016 10:55:11 -0800 Subject: bpf: Add tests for the LRU bpf_htab This patch has some unit tests and a test_lru_dist. The test_lru_dist reads in the numeric keys from a file. The files used here are generated by a modified fio-genzipf tool originated from the fio test suit. The sample data file can be found here: https://github.com/iamkafai/bpf-lru The zipf.* data files have 100k numeric keys and the key is also ranged from 1 to 100k. The test_lru_dist outputs the number of unique keys (nr_unique). F.e. The following means, 61239 of them is unique out of 100k keys. nr_misses means it cannot be found in the LRU map, so nr_misses must be >= nr_unique. test_lru_dist also simulates a perfect LRU map as a comparison: [root@arch-fb-vm1 ~]# ~/devshare/fb-kernel/linux/samples/bpf/test_lru_dist \ /root/zipf.100k.a1_01.out 4000 1 ... test_parallel_lru_dist (map_type:9 map_flags:0x0): task:0 BPF LRU: nr_unique:23093(/100000) nr_misses:31603(/100000) task:0 Perfect LRU: nr_unique:23093(/100000 nr_misses:34328(/100000) .... test_parallel_lru_dist (map_type:9 map_flags:0x2): task:0 BPF LRU: nr_unique:23093(/100000) nr_misses:31710(/100000) task:0 Perfect LRU: nr_unique:23093(/100000 nr_misses:34328(/100000) [root@arch-fb-vm1 ~]# ~/devshare/fb-kernel/linux/samples/bpf/test_lru_dist \ /root/zipf.100k.a0_01.out 40000 1 ... test_parallel_lru_dist (map_type:9 map_flags:0x0): task:0 BPF LRU: nr_unique:61239(/100000) nr_misses:67054(/100000) task:0 Perfect LRU: nr_unique:61239(/100000 nr_misses:66993(/100000) ... test_parallel_lru_dist (map_type:9 map_flags:0x2): task:0 BPF LRU: nr_unique:61239(/100000) nr_misses:67068(/100000) task:0 Perfect LRU: nr_unique:61239(/100000 nr_misses:66993(/100000) LRU map has also been added to map_perf_test: /* Global LRU */ [root@kerneltest003.31.prn1 ~]# for i in 1 4 8; do echo -n "$i cpus: "; \ ./map_perf_test 16 $i | awk '{r += $3}END{print r " updates"}'; done 1 cpus: 2934082 updates 4 cpus: 7391434 updates 8 cpus: 6500576 updates /* Percpu LRU */ [root@kerneltest003.31.prn1 ~]# for i in 1 4 8; do echo -n "$i cpus: "; \ ./map_perf_test 32 $i | awk '{r += $3}END{print r " updates"}'; done 1 cpus: 2896553 updates 4 cpus: 9766395 updates 8 cpus: 17460553 updates Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 2 + samples/bpf/map_perf_test_kern.c | 39 ++ samples/bpf/map_perf_test_user.c | 32 ++ samples/bpf/test_lru_dist.c | 538 ++++++++++++++++++++++++++ tools/testing/selftests/bpf/Makefile | 6 +- tools/testing/selftests/bpf/test_lru_map.c | 583 +++++++++++++++++++++++++++++ 6 files changed, 1197 insertions(+), 3 deletions(-) create mode 100644 samples/bpf/test_lru_dist.c create mode 100644 tools/testing/selftests/bpf/test_lru_map.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index ac87f9c..f394ac6 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -2,6 +2,7 @@ obj- := dummy.o # List of programs to build +hostprogs-y := test_lru_dist hostprogs-y += sock_example hostprogs-y += fds_example hostprogs-y += sockex1 @@ -28,6 +29,7 @@ hostprogs-y += trace_event hostprogs-y += sampleip hostprogs-y += tc_l2_redirect +test_lru_dist-objs := test_lru_dist.o libbpf.o sock_example-objs := sock_example.o libbpf.o fds_example-objs := bpf_load.o libbpf.o fds_example.o sockex1-objs := bpf_load.o libbpf.o sockex1_user.o diff --git a/samples/bpf/map_perf_test_kern.c b/samples/bpf/map_perf_test_kern.c index 311538e..7ee1574 100644 --- a/samples/bpf/map_perf_test_kern.c +++ b/samples/bpf/map_perf_test_kern.c @@ -19,6 +19,21 @@ struct bpf_map_def SEC("maps") hash_map = { .max_entries = MAX_ENTRIES, }; +struct bpf_map_def SEC("maps") lru_hash_map = { + .type = BPF_MAP_TYPE_LRU_HASH, + .key_size = sizeof(u32), + .value_size = sizeof(long), + .max_entries = 10000, +}; + +struct bpf_map_def SEC("maps") percpu_lru_hash_map = { + .type = BPF_MAP_TYPE_LRU_HASH, + .key_size = sizeof(u32), + .value_size = sizeof(long), + .max_entries = 10000, + .map_flags = BPF_F_NO_COMMON_LRU, +}; + struct bpf_map_def SEC("maps") percpu_hash_map = { .type = BPF_MAP_TYPE_PERCPU_HASH, .key_size = sizeof(u32), @@ -53,6 +68,7 @@ int stress_hmap(struct pt_regs *ctx) value = bpf_map_lookup_elem(&hash_map, &key); if (value) bpf_map_delete_elem(&hash_map, &key); + return 0; } @@ -96,5 +112,28 @@ int stress_percpu_hmap_alloc(struct pt_regs *ctx) bpf_map_delete_elem(&percpu_hash_map_alloc, &key); return 0; } + +SEC("kprobe/sys_getpid") +int stress_lru_hmap_alloc(struct pt_regs *ctx) +{ + u32 key = bpf_get_prandom_u32(); + long val = 1; + + bpf_map_update_elem(&lru_hash_map, &key, &val, BPF_ANY); + + return 0; +} + +SEC("kprobe/sys_getppid") +int stress_percpu_lru_hmap_alloc(struct pt_regs *ctx) +{ + u32 key = bpf_get_prandom_u32(); + long val = 1; + + bpf_map_update_elem(&percpu_lru_hash_map, &key, &val, BPF_ANY); + + return 0; +} + char _license[] SEC("license") = "GPL"; u32 _version SEC("version") = LINUX_VERSION_CODE; diff --git a/samples/bpf/map_perf_test_user.c b/samples/bpf/map_perf_test_user.c index 3147377..9505b4d 100644 --- a/samples/bpf/map_perf_test_user.c +++ b/samples/bpf/map_perf_test_user.c @@ -35,6 +35,8 @@ static __u64 time_get_ns(void) #define PERCPU_HASH_PREALLOC (1 << 1) #define HASH_KMALLOC (1 << 2) #define PERCPU_HASH_KMALLOC (1 << 3) +#define LRU_HASH_PREALLOC (1 << 4) +#define PERCPU_LRU_HASH_PREALLOC (1 << 5) static int test_flags = ~0; @@ -50,6 +52,30 @@ static void test_hash_prealloc(int cpu) cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); } +static void test_lru_hash_prealloc(int cpu) +{ + __u64 start_time; + int i; + + start_time = time_get_ns(); + for (i = 0; i < MAX_CNT; i++) + syscall(__NR_getpid); + printf("%d:lru_hash_map_perf pre-alloc %lld events per sec\n", + cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); +} + +static void test_percpu_lru_hash_prealloc(int cpu) +{ + __u64 start_time; + int i; + + start_time = time_get_ns(); + for (i = 0; i < MAX_CNT; i++) + syscall(__NR_getppid); + printf("%d:lru_hash_map_perf pre-alloc %lld events per sec\n", + cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); +} + static void test_percpu_hash_prealloc(int cpu) { __u64 start_time; @@ -105,6 +131,12 @@ static void loop(int cpu) if (test_flags & PERCPU_HASH_KMALLOC) test_percpu_hash_kmalloc(cpu); + + if (test_flags & LRU_HASH_PREALLOC) + test_lru_hash_prealloc(cpu); + + if (test_flags & PERCPU_LRU_HASH_PREALLOC) + test_percpu_lru_hash_prealloc(cpu); } static void run_perf_test(int tasks) diff --git a/samples/bpf/test_lru_dist.c b/samples/bpf/test_lru_dist.c new file mode 100644 index 0000000..2859977 --- /dev/null +++ b/samples/bpf/test_lru_dist.c @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbpf.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +static int nr_cpus; +static unsigned long long *dist_keys; +static unsigned int dist_key_counts; + +struct list_head { + struct list_head *next, *prev; +}; + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +struct pfect_lru_node { + struct list_head list; + unsigned long long key; +}; + +struct pfect_lru { + struct list_head list; + struct pfect_lru_node *free_nodes; + unsigned int cur_size; + unsigned int lru_size; + unsigned int nr_unique; + unsigned int nr_misses; + unsigned int total; + int map_fd; +}; + +static void pfect_lru_init(struct pfect_lru *lru, unsigned int lru_size, + unsigned int nr_possible_elems) +{ + lru->map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, + sizeof(unsigned long long), + sizeof(struct pfect_lru_node *), + nr_possible_elems, 0); + assert(lru->map_fd != -1); + + lru->free_nodes = malloc(lru_size * sizeof(struct pfect_lru_node)); + assert(lru->free_nodes); + + INIT_LIST_HEAD(&lru->list); + lru->cur_size = 0; + lru->lru_size = lru_size; + lru->nr_unique = lru->nr_misses = lru->total = 0; +} + +static void pfect_lru_destroy(struct pfect_lru *lru) +{ + close(lru->map_fd); + free(lru->free_nodes); +} + +static int pfect_lru_lookup_or_insert(struct pfect_lru *lru, + unsigned long long key) +{ + struct pfect_lru_node *node = NULL; + int seen = 0; + + lru->total++; + if (!bpf_lookup_elem(lru->map_fd, &key, &node)) { + if (node) { + list_move(&node->list, &lru->list); + return 1; + } + seen = 1; + } + + if (lru->cur_size < lru->lru_size) { + node = &lru->free_nodes[lru->cur_size++]; + INIT_LIST_HEAD(&node->list); + } else { + struct pfect_lru_node *null_node = NULL; + + node = list_last_entry(&lru->list, + struct pfect_lru_node, + list); + bpf_update_elem(lru->map_fd, &node->key, &null_node, BPF_EXIST); + } + + node->key = key; + list_move(&node->list, &lru->list); + + lru->nr_misses++; + if (seen) { + assert(!bpf_update_elem(lru->map_fd, &key, &node, BPF_EXIST)); + } else { + lru->nr_unique++; + assert(!bpf_update_elem(lru->map_fd, &key, &node, BPF_NOEXIST)); + } + + return seen; +} + +static unsigned int read_keys(const char *dist_file, + unsigned long long **keys) +{ + struct stat fst; + unsigned long long *retkeys; + unsigned int counts = 0; + int dist_fd; + char *b, *l; + int i; + + dist_fd = open(dist_file, 0); + assert(dist_fd != -1); + + assert(fstat(dist_fd, &fst) == 0); + b = malloc(fst.st_size); + assert(b); + + assert(read(dist_fd, b, fst.st_size) == fst.st_size); + close(dist_fd); + for (i = 0; i < fst.st_size; i++) { + if (b[i] == '\n') + counts++; + } + counts++; /* in case the last line has no \n */ + + retkeys = malloc(counts * sizeof(unsigned long long)); + assert(retkeys); + + counts = 0; + for (l = strtok(b, "\n"); l; l = strtok(NULL, "\n")) + retkeys[counts++] = strtoull(l, NULL, 10); + free(b); + + *keys = retkeys; + + return counts; +} + +static int create_map(int map_type, int map_flags, unsigned int size) +{ + int map_fd; + + map_fd = bpf_create_map(map_type, sizeof(unsigned long long), + sizeof(unsigned long long), size, map_flags); + + if (map_fd == -1) + perror("bpf_create_map"); + + return map_fd; +} + +static int sched_next_online(int pid, int next_to_try) +{ + cpu_set_t cpuset; + + if (next_to_try == nr_cpus) + return -1; + + while (next_to_try < nr_cpus) { + CPU_ZERO(&cpuset); + CPU_SET(next_to_try++, &cpuset); + if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset)) + break; + } + + return next_to_try; +} + +static void run_parallel(unsigned int tasks, void (*fn)(int i, void *data), + void *data) +{ + int next_sched_cpu = 0; + pid_t pid[tasks]; + int i; + + for (i = 0; i < tasks; i++) { + pid[i] = fork(); + if (pid[i] == 0) { + next_sched_cpu = sched_next_online(0, next_sched_cpu); + fn(i, data); + exit(0); + } else if (pid[i] == -1) { + printf("couldn't spawn #%d process\n", i); + exit(1); + } + /* It is mostly redundant and just allow the parent + * process to update next_shced_cpu for the next child + * process + */ + next_sched_cpu = sched_next_online(pid[i], next_sched_cpu); + } + for (i = 0; i < tasks; i++) { + int status; + + assert(waitpid(pid[i], &status, 0) == pid[i]); + assert(status == 0); + } +} + +static void do_test_lru_dist(int task, void *data) +{ + unsigned int nr_misses = 0; + struct pfect_lru pfect_lru; + unsigned long long key, value = 1234; + unsigned int i; + + unsigned int lru_map_fd = ((unsigned int *)data)[0]; + unsigned int lru_size = ((unsigned int *)data)[1]; + unsigned long long key_offset = task * dist_key_counts; + + pfect_lru_init(&pfect_lru, lru_size, dist_key_counts); + + for (i = 0; i < dist_key_counts; i++) { + key = dist_keys[i] + key_offset; + + pfect_lru_lookup_or_insert(&pfect_lru, key); + + if (!bpf_lookup_elem(lru_map_fd, &key, &value)) + continue; + + if (bpf_update_elem(lru_map_fd, &key, &value, BPF_NOEXIST)) { + printf("bpf_update_elem(lru_map_fd, %llu): errno:%d\n", + key, errno); + assert(0); + } + + nr_misses++; + } + + printf(" task:%d BPF LRU: nr_unique:%u(/%u) nr_misses:%u(/%u)\n", + task, pfect_lru.nr_unique, dist_key_counts, nr_misses, + dist_key_counts); + printf(" task:%d Perfect LRU: nr_unique:%u(/%u) nr_misses:%u(/%u)\n", + task, pfect_lru.nr_unique, pfect_lru.total, + pfect_lru.nr_misses, pfect_lru.total); + + pfect_lru_destroy(&pfect_lru); + close(lru_map_fd); +} + +static void test_parallel_lru_dist(int map_type, int map_flags, + int nr_tasks, unsigned int lru_size) +{ + int child_data[2]; + int lru_map_fd; + + printf("%s (map_type:%d map_flags:0x%X):\n", __func__, map_type, + map_flags); + + if (map_flags & BPF_F_NO_COMMON_LRU) + lru_map_fd = create_map(map_type, map_flags, + nr_cpus * lru_size); + else + lru_map_fd = create_map(map_type, map_flags, + nr_tasks * lru_size); + assert(lru_map_fd != -1); + + child_data[0] = lru_map_fd; + child_data[1] = lru_size; + + run_parallel(nr_tasks, do_test_lru_dist, child_data); + + close(lru_map_fd); +} + +static void test_lru_loss0(int map_type, int map_flags) +{ + unsigned long long key, value[nr_cpus]; + unsigned int old_unused_losses = 0; + unsigned int new_unused_losses = 0; + unsigned int used_losses = 0; + int map_fd; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, 0) != -1); + + if (map_flags & BPF_F_NO_COMMON_LRU) + map_fd = create_map(map_type, map_flags, 900 * nr_cpus); + else + map_fd = create_map(map_type, map_flags, 900); + + assert(map_fd != -1); + + value[0] = 1234; + + for (key = 1; key <= 1000; key++) { + int start_key, end_key; + + assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0); + + start_key = 101; + end_key = min(key, 900); + + while (start_key <= end_key) { + bpf_lookup_elem(map_fd, &start_key, value); + start_key++; + } + } + + for (key = 1; key <= 1000; key++) { + if (bpf_lookup_elem(map_fd, &key, value)) { + if (key <= 100) + old_unused_losses++; + else if (key <= 900) + used_losses++; + else + new_unused_losses++; + } + } + + close(map_fd); + + printf("older-elem-losses:%d(/100) active-elem-losses:%d(/800) " + "newer-elem-losses:%d(/100)\n", + old_unused_losses, used_losses, new_unused_losses); +} + +static void test_lru_loss1(int map_type, int map_flags) +{ + unsigned long long key, value[nr_cpus]; + int map_fd; + unsigned int nr_losses = 0; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, 0) != -1); + + if (map_flags & BPF_F_NO_COMMON_LRU) + map_fd = create_map(map_type, map_flags, 1000 * nr_cpus); + else + map_fd = create_map(map_type, map_flags, 1000); + + assert(map_fd != -1); + + value[0] = 1234; + + for (key = 1; key <= 1000; key++) + assert(!bpf_update_elem(map_fd, &key, value, BPF_NOEXIST)); + + for (key = 1; key <= 1000; key++) { + if (bpf_lookup_elem(map_fd, &key, value)) + nr_losses++; + } + + close(map_fd); + + printf("nr_losses:%d(/1000)\n", nr_losses); +} + +static void do_test_parallel_lru_loss(int task, void *data) +{ + const unsigned int nr_stable_elems = 1000; + const unsigned int nr_repeats = 100000; + + int map_fd = *(int *)data; + unsigned long long stable_base; + unsigned long long key, value[nr_cpus]; + unsigned long long next_ins_key; + unsigned int nr_losses = 0; + unsigned int i; + + stable_base = task * nr_repeats * 2 + 1; + next_ins_key = stable_base; + value[0] = 1234; + for (i = 0; i < nr_stable_elems; i++) { + assert(bpf_update_elem(map_fd, &next_ins_key, value, + BPF_NOEXIST) == 0); + next_ins_key++; + } + + for (i = 0; i < nr_repeats; i++) { + int rn; + + rn = rand(); + + if (rn % 10) { + key = rn % nr_stable_elems + stable_base; + bpf_lookup_elem(map_fd, &key, value); + } else { + bpf_update_elem(map_fd, &next_ins_key, value, + BPF_NOEXIST); + next_ins_key++; + } + } + + key = stable_base; + for (i = 0; i < nr_stable_elems; i++) { + if (bpf_lookup_elem(map_fd, &key, value)) + nr_losses++; + key++; + } + + printf(" task:%d nr_losses:%u\n", task, nr_losses); +} + +static void test_parallel_lru_loss(int map_type, int map_flags, int nr_tasks) +{ + int map_fd; + + printf("%s (map_type:%d map_flags:0x%X):\n", __func__, map_type, + map_flags); + + /* Give 20% more than the active working set */ + if (map_flags & BPF_F_NO_COMMON_LRU) + map_fd = create_map(map_type, map_flags, + nr_cpus * (1000 + 200)); + else + map_fd = create_map(map_type, map_flags, + nr_tasks * (1000 + 200)); + + assert(map_fd != -1); + + run_parallel(nr_tasks, do_test_parallel_lru_loss, &map_fd); + + close(map_fd); +} + +int main(int argc, char **argv) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + int map_flags[] = {0, BPF_F_NO_COMMON_LRU}; + const char *dist_file; + int nr_tasks = 1; + int lru_size; + int f; + + if (argc < 4) { + printf("Usage: %s \n", + argv[0]); + return -1; + } + + dist_file = argv[1]; + lru_size = atoi(argv[2]); + nr_tasks = atoi(argv[3]); + + setbuf(stdout, NULL); + + assert(!setrlimit(RLIMIT_MEMLOCK, &r)); + + srand(time(NULL)); + + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + assert(nr_cpus != -1); + printf("nr_cpus:%d\n\n", nr_cpus); + + nr_tasks = min(nr_tasks, nr_cpus); + + dist_key_counts = read_keys(dist_file, &dist_keys); + if (!dist_key_counts) { + printf("%s has no key\n", dist_file); + return -1; + } + + for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) { + test_lru_loss0(BPF_MAP_TYPE_LRU_HASH, map_flags[f]); + test_lru_loss1(BPF_MAP_TYPE_LRU_HASH, map_flags[f]); + test_parallel_lru_loss(BPF_MAP_TYPE_LRU_HASH, map_flags[f], + nr_tasks); + test_parallel_lru_dist(BPF_MAP_TYPE_LRU_HASH, map_flags[f], + nr_tasks, lru_size); + printf("\n"); + } + + free(dist_keys); + + return 0; +} diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 4761e2d6..7a5f245 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -1,8 +1,8 @@ -CFLAGS += -Wall -O2 +CFLAGS += -Wall -O2 -I../../../../usr/include -test_objs = test_verifier test_maps +test_objs = test_verifier test_maps test_lru_map -TEST_PROGS := test_verifier test_maps test_kmod.sh +TEST_PROGS := test_verifier test_maps test_lru_map test_kmod.sh TEST_FILES := $(test_objs) all: $(test_objs) diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c new file mode 100644 index 0000000..627757e --- /dev/null +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_sys.h" + +#define LOCAL_FREE_TARGET (128) +#define PERCPU_FREE_TARGET (16) + +static int nr_cpus; + +static int create_map(int map_type, int map_flags, unsigned int size) +{ + int map_fd; + + map_fd = bpf_map_create(map_type, sizeof(unsigned long long), + sizeof(unsigned long long), size, map_flags); + + if (map_fd == -1) + perror("bpf_map_create"); + + return map_fd; +} + +static int map_subset(int map0, int map1) +{ + unsigned long long next_key = 0; + unsigned long long value0[nr_cpus], value1[nr_cpus]; + int ret; + + while (!bpf_map_next_key(map1, &next_key, &next_key)) { + assert(!bpf_map_lookup(map1, &next_key, value1)); + ret = bpf_map_lookup(map0, &next_key, value0); + if (ret) { + printf("key:%llu not found from map. %s(%d)\n", + next_key, strerror(errno), errno); + return 0; + } + if (value0[0] != value1[0]) { + printf("key:%llu value0:%llu != value1:%llu\n", + next_key, value0[0], value1[0]); + return 0; + } + } + return 1; +} + +static int map_equal(int lru_map, int expected) +{ + return map_subset(lru_map, expected) && map_subset(expected, lru_map); +} + +static int sched_next_online(int pid, int next_to_try) +{ + cpu_set_t cpuset; + + if (next_to_try == nr_cpus) + return -1; + + while (next_to_try < nr_cpus) { + CPU_ZERO(&cpuset); + CPU_SET(next_to_try++, &cpuset); + if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset)) + break; + } + + return next_to_try; +} + +/* Size of the LRU amp is 2 + * Add key=1 (+1 key) + * Add key=2 (+1 key) + * Lookup Key=1 + * Add Key=3 + * => Key=2 will be removed by LRU + * Iterate map. Only found key=1 and key=3 + */ +static void test_lru_sanity0(int map_type, int map_flags) +{ + unsigned long long key, value[nr_cpus]; + int lru_map_fd, expected_map_fd; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, 0) != -1); + + if (map_flags & BPF_F_NO_COMMON_LRU) + lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus); + else + lru_map_fd = create_map(map_type, map_flags, 2); + assert(lru_map_fd != -1); + + expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2); + assert(expected_map_fd != -1); + + value[0] = 1234; + + /* insert key=1 element */ + + key = 1; + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update(expected_map_fd, &key, value, BPF_NOEXIST)); + + /* BPF_NOEXIST means: add new element if it doesn't exist */ + assert(bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST) == -1 && + /* key=1 already exists */ + errno == EEXIST); + + assert(bpf_map_update(lru_map_fd, &key, value, -1) == -1 && + errno == EINVAL); + + /* insert key=2 element */ + + /* check that key=2 is not found */ + key = 2; + assert(bpf_map_lookup(lru_map_fd, &key, value) == -1 && + errno == ENOENT); + + /* BPF_EXIST means: update existing element */ + assert(bpf_map_update(lru_map_fd, &key, value, BPF_EXIST) == -1 && + /* key=2 is not there */ + errno == ENOENT); + + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + + /* insert key=3 element */ + + /* check that key=3 is not found */ + key = 3; + assert(bpf_map_lookup(lru_map_fd, &key, value) == -1 && + errno == ENOENT); + + /* check that key=1 can be found and mark the ref bit to + * stop LRU from removing key=1 + */ + key = 1; + assert(!bpf_map_lookup(lru_map_fd, &key, value)); + assert(value[0] == 1234); + + key = 3; + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update(expected_map_fd, &key, value, BPF_NOEXIST)); + + /* key=2 has been removed from the LRU */ + key = 2; + assert(bpf_map_lookup(lru_map_fd, &key, value) == -1); + + assert(map_equal(lru_map_fd, expected_map_fd)); + + close(expected_map_fd); + close(lru_map_fd); + + printf("Pass\n"); +} + +/* Size of the LRU map is 1.5*tgt_free + * Insert 1 to tgt_free (+tgt_free keys) + * Lookup 1 to tgt_free/2 + * Insert 1+tgt_free to 2*tgt_free (+tgt_free keys) + * => 1+tgt_free/2 to LOCALFREE_TARGET will be removed by LRU + */ +static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) +{ + unsigned long long key, end_key, value[nr_cpus]; + int lru_map_fd, expected_map_fd; + unsigned int batch_size; + unsigned int map_size; + + if (map_flags & BPF_F_NO_COMMON_LRU) + /* Ther percpu lru list (i.e each cpu has its own LRU + * list) does not have a local free list. Hence, + * it will only free old nodes till there is no free + * from the LRU list. Hence, this test does not apply + * to BPF_F_NO_COMMON_LRU + */ + return; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, 0) != -1); + + batch_size = tgt_free / 2; + assert(batch_size * 2 == tgt_free); + + map_size = tgt_free + batch_size; + lru_map_fd = create_map(map_type, map_flags, map_size); + assert(lru_map_fd != -1); + + expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size); + assert(expected_map_fd != -1); + + value[0] = 1234; + + /* Insert 1 to tgt_free (+tgt_free keys) */ + end_key = 1 + tgt_free; + for (key = 1; key < end_key; key++) + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + + /* Lookup 1 to tgt_free/2 */ + end_key = 1 + batch_size; + for (key = 1; key < end_key; key++) { + assert(!bpf_map_lookup(lru_map_fd, &key, value)); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + /* Insert 1+tgt_free to 2*tgt_free + * => 1+tgt_free/2 to LOCALFREE_TARGET will be + * removed by LRU + */ + key = 1 + tgt_free; + end_key = key + tgt_free; + for (; key < end_key; key++) { + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + assert(map_equal(lru_map_fd, expected_map_fd)); + + close(expected_map_fd); + close(lru_map_fd); + + printf("Pass\n"); +} + +/* Size of the LRU map 1.5 * tgt_free + * Insert 1 to tgt_free (+tgt_free keys) + * Update 1 to tgt_free/2 + * => The original 1 to tgt_free/2 will be removed due to + * the LRU shrink process + * Re-insert 1 to tgt_free/2 again and do a lookup immeidately + * Insert 1+tgt_free to tgt_free*3/2 + * Insert 1+tgt_free*3/2 to tgt_free*5/2 + * => Key 1+tgt_free to tgt_free*3/2 + * will be removed from LRU because it has never + * been lookup and ref bit is not set + */ +static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) +{ + unsigned long long key, value[nr_cpus]; + unsigned long long end_key; + int lru_map_fd, expected_map_fd; + unsigned int batch_size; + unsigned int map_size; + + if (map_flags & BPF_F_NO_COMMON_LRU) + /* Ther percpu lru list (i.e each cpu has its own LRU + * list) does not have a local free list. Hence, + * it will only free old nodes till there is no free + * from the LRU list. Hence, this test does not apply + * to BPF_F_NO_COMMON_LRU + */ + return; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, 0) != -1); + + batch_size = tgt_free / 2; + assert(batch_size * 2 == tgt_free); + + map_size = tgt_free + batch_size; + if (map_flags & BPF_F_NO_COMMON_LRU) + lru_map_fd = create_map(map_type, map_flags, + map_size * nr_cpus); + else + lru_map_fd = create_map(map_type, map_flags, map_size); + assert(lru_map_fd != -1); + + expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size); + assert(expected_map_fd != -1); + + value[0] = 1234; + + /* Insert 1 to tgt_free (+tgt_free keys) */ + end_key = 1 + tgt_free; + for (key = 1; key < end_key; key++) + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + + /* Any bpf_map_update will require to acquire a new node + * from LRU first. + * + * The local list is running out of free nodes. + * It gets from the global LRU list which tries to + * shrink the inactive list to get tgt_free + * number of free nodes. + * + * Hence, the oldest key 1 to tgt_free/2 + * are removed from the LRU list. + */ + key = 1; + if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_delete(lru_map_fd, &key)); + } else { + assert(bpf_map_update(lru_map_fd, &key, value, BPF_EXIST)); + } + + /* Re-insert 1 to tgt_free/2 again and do a lookup + * immeidately. + */ + end_key = 1 + batch_size; + value[0] = 4321; + for (key = 1; key < end_key; key++) { + assert(bpf_map_lookup(lru_map_fd, &key, value)); + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_lookup(lru_map_fd, &key, value)); + assert(value[0] == 4321); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + value[0] = 1234; + + /* Insert 1+tgt_free to tgt_free*3/2 */ + end_key = 1 + tgt_free + batch_size; + for (key = 1 + tgt_free; key < end_key; key++) + /* These newly added but not referenced keys will be + * gone during the next LRU shrink. + */ + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + + /* Insert 1+tgt_free*3/2 to tgt_free*5/2 */ + end_key = key + tgt_free; + for (; key < end_key; key++) { + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + assert(map_equal(lru_map_fd, expected_map_fd)); + + close(expected_map_fd); + close(lru_map_fd); + + printf("Pass\n"); +} + +/* Size of the LRU map is 2*tgt_free + * It is to test the active/inactive list rotation + * Insert 1 to 2*tgt_free (+2*tgt_free keys) + * Lookup key 1 to tgt_free*3/2 + * Add 1+2*tgt_free to tgt_free*5/2 (+tgt_free/2 keys) + * => key 1+tgt_free*3/2 to 2*tgt_free are removed from LRU + */ +static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) +{ + unsigned long long key, end_key, value[nr_cpus]; + int lru_map_fd, expected_map_fd; + unsigned int batch_size; + unsigned int map_size; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, 0) != -1); + + batch_size = tgt_free / 2; + assert(batch_size * 2 == tgt_free); + + map_size = tgt_free * 2; + if (map_flags & BPF_F_NO_COMMON_LRU) + lru_map_fd = create_map(map_type, map_flags, + map_size * nr_cpus); + else + lru_map_fd = create_map(map_type, map_flags, map_size); + assert(lru_map_fd != -1); + + expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size); + assert(expected_map_fd != -1); + + value[0] = 1234; + + /* Insert 1 to 2*tgt_free (+2*tgt_free keys) */ + end_key = 1 + (2 * tgt_free); + for (key = 1; key < end_key; key++) + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + + /* Lookup key 1 to tgt_free*3/2 */ + end_key = tgt_free + batch_size; + for (key = 1; key < end_key; key++) { + assert(!bpf_map_lookup(lru_map_fd, &key, value)); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + /* Add 1+2*tgt_free to tgt_free*5/2 + * (+tgt_free/2 keys) + */ + key = 2 * tgt_free + 1; + end_key = key + batch_size; + for (; key < end_key; key++) { + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + assert(map_equal(lru_map_fd, expected_map_fd)); + + close(expected_map_fd); + close(lru_map_fd); + + printf("Pass\n"); +} + +/* Test deletion */ +static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free) +{ + int lru_map_fd, expected_map_fd; + unsigned long long key, value[nr_cpus]; + unsigned long long end_key; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, 0) != -1); + + if (map_flags & BPF_F_NO_COMMON_LRU) + lru_map_fd = create_map(map_type, map_flags, + 3 * tgt_free * nr_cpus); + else + lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free); + assert(lru_map_fd != -1); + + expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, + 3 * tgt_free); + assert(expected_map_fd != -1); + + value[0] = 1234; + + for (key = 1; key <= 2 * tgt_free; key++) + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + + key = 1; + assert(bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + + for (key = 1; key <= tgt_free; key++) { + assert(!bpf_map_lookup(lru_map_fd, &key, value)); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + for (; key <= 2 * tgt_free; key++) { + assert(!bpf_map_delete(lru_map_fd, &key)); + assert(bpf_map_delete(lru_map_fd, &key)); + } + + end_key = key + 2 * tgt_free; + for (; key < end_key; key++) { + assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + assert(map_equal(lru_map_fd, expected_map_fd)); + + close(expected_map_fd); + close(lru_map_fd); + + printf("Pass\n"); +} + +static void do_test_lru_sanity5(unsigned long long last_key, int map_fd) +{ + unsigned long long key, value[nr_cpus]; + + /* Ensure the last key inserted by previous CPU can be found */ + assert(!bpf_map_lookup(map_fd, &last_key, value)); + + value[0] = 1234; + + key = last_key + 1; + assert(!bpf_map_update(map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_lookup(map_fd, &key, value)); + + /* Cannot find the last key because it was removed by LRU */ + assert(bpf_map_lookup(map_fd, &last_key, value)); +} + +/* Test map with only one element */ +static void test_lru_sanity5(int map_type, int map_flags) +{ + unsigned long long key, value[nr_cpus]; + int next_sched_cpu = 0; + int map_fd; + int i; + + if (map_flags & BPF_F_NO_COMMON_LRU) + return; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + map_fd = create_map(map_type, map_flags, 1); + assert(map_fd != -1); + + value[0] = 1234; + key = 0; + assert(!bpf_map_update(map_fd, &key, value, BPF_NOEXIST)); + + for (i = 0; i < nr_cpus; i++) { + pid_t pid; + + pid = fork(); + if (pid == 0) { + next_sched_cpu = sched_next_online(0, next_sched_cpu); + if (next_sched_cpu != -1) + do_test_lru_sanity5(key, map_fd); + exit(0); + } else if (pid == -1) { + printf("couldn't spawn #%d process\n", i); + exit(1); + } else { + int status; + + /* It is mostly redundant and just allow the parent + * process to update next_shced_cpu for the next child + * process + */ + next_sched_cpu = sched_next_online(pid, next_sched_cpu); + + assert(waitpid(pid, &status, 0) == pid); + assert(status == 0); + key++; + } + } + + close(map_fd); + + printf("Pass\n"); +} + +int main(int argc, char **argv) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + int map_types[] = {BPF_MAP_TYPE_LRU_HASH, + BPF_MAP_TYPE_LRU_PERCPU_HASH}; + int map_flags[] = {0, BPF_F_NO_COMMON_LRU}; + int t, f; + + setbuf(stdout, NULL); + + assert(!setrlimit(RLIMIT_MEMLOCK, &r)); + + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + assert(nr_cpus != -1); + printf("nr_cpus:%d\n\n", nr_cpus); + + for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) { + unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ? + PERCPU_FREE_TARGET : LOCAL_FREE_TARGET; + + for (t = 0; t < sizeof(map_types) / sizeof(*map_types); t++) { + test_lru_sanity0(map_types[t], map_flags[f]); + test_lru_sanity1(map_types[t], map_flags[f], tgt_free); + test_lru_sanity2(map_types[t], map_flags[f], tgt_free); + test_lru_sanity3(map_types[t], map_flags[f], tgt_free); + test_lru_sanity4(map_types[t], map_flags[f], tgt_free); + test_lru_sanity5(map_types[t], map_flags[f]); + + printf("\n"); + } + } + + return 0; +} -- cgit v1.1 From c915fe13cbaae5c7aa7b44f367d05addd60c9008 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 15 Nov 2016 16:37:53 +0100 Subject: udplite: fix NULL pointer dereference The commit 850cbaddb52d ("udp: use it's own memory accounting schema") assumes that the socket proto has memory accounting enabled, but this is not the case for UDPLITE. Fix it enabling memory accounting for UDPLITE and performing fwd allocated memory reclaiming on socket shutdown. UDP and UDPLITE share now the same memory accounting limits. Also drop the backlog receive operation, since is no more needed. Fixes: 850cbaddb52d ("udp: use it's own memory accounting schema") Reported-by: Andrei Vagin Suggested-by: Eric Dumazet Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- include/net/udp.h | 1 + include/net/udplite.h | 1 + net/ipv4/udp.c | 3 ++- net/ipv4/udplite.c | 3 ++- net/ipv6/udplite.c | 3 ++- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/net/udp.h b/include/net/udp.h index e6e4e19..1661791 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -246,6 +246,7 @@ static inline __be16 udp_flow_src_port(struct net *net, struct sk_buff *skb, } /* net/ipv4/udp.c */ +void udp_destruct_sock(struct sock *sk); void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len); int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb); void udp_skb_destructor(struct sock *sk, struct sk_buff *skb); diff --git a/include/net/udplite.h b/include/net/udplite.h index 8076193..36097d3 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -27,6 +27,7 @@ static __inline__ int udplite_getfrag(void *from, char *to, int offset, static inline int udplite_sk_init(struct sock *sk) { udp_sk(sk)->pcflag = UDPLITE_BIT; + sk->sk_destruct = udp_destruct_sock; return 0; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c827e4e..9ae7c63 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1253,7 +1253,7 @@ drop: } EXPORT_SYMBOL_GPL(__udp_enqueue_schedule_skb); -static void udp_destruct_sock(struct sock *sk) +void udp_destruct_sock(struct sock *sk) { /* reclaim completely the forward allocated memory */ unsigned int total = 0; @@ -1267,6 +1267,7 @@ static void udp_destruct_sock(struct sock *sk) inet_sock_destruct(sk); } +EXPORT_SYMBOL_GPL(udp_destruct_sock); int udp_init_sock(struct sock *sk) { diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index af81715..59f10fe 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -50,10 +50,11 @@ struct proto udplite_prot = { .sendmsg = udp_sendmsg, .recvmsg = udp_recvmsg, .sendpage = udp_sendpage, - .backlog_rcv = udp_queue_rcv_skb, .hash = udp_lib_hash, .unhash = udp_lib_unhash, .get_port = udp_v4_get_port, + .memory_allocated = &udp_memory_allocated, + .sysctl_mem = sysctl_udp_mem, .obj_size = sizeof(struct udp_sock), .h.udp_table = &udplite_table, #ifdef CONFIG_COMPAT diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 47d0d2b..2784cc3 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -45,10 +45,11 @@ struct proto udplitev6_prot = { .getsockopt = udpv6_getsockopt, .sendmsg = udpv6_sendmsg, .recvmsg = udpv6_recvmsg, - .backlog_rcv = udpv6_queue_rcv_skb, .hash = udp_lib_hash, .unhash = udp_lib_unhash, .get_port = udp_v6_get_port, + .memory_allocated = &udp_memory_allocated, + .sysctl_mem = sysctl_udp_mem, .obj_size = sizeof(struct udp6_sock), .h.udp_table = &udplite_table, #ifdef CONFIG_COMPAT -- cgit v1.1 From 4a4f86cc7d6bc74522f581341a2cae3119d5a0f5 Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Sun, 13 Nov 2016 20:43:52 -0800 Subject: vxlan: avoid vlan processing in vxlan device. VxLan device does not have special handling for vlan taging on egress. Therefore it does not make sense to expose vlan offloading feature. This patch does not change vxlan functinality. Signed-off-by: Pravin B Shelar Acked-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 9 +-------- include/linux/if_vlan.h | 16 ---------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 5264c1a..7bebce1 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1750,18 +1750,13 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst, } min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len - + VXLAN_HLEN + iphdr_len - + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); + + VXLAN_HLEN + iphdr_len; /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); if (unlikely(err)) goto out_free; - skb = vlan_hwaccel_push_inside(skb); - if (WARN_ON(!skb)) - return -ENOMEM; - err = iptunnel_handle_offloads(skb, type); if (err) goto out_free; @@ -2529,10 +2524,8 @@ static void vxlan_setup(struct net_device *dev) dev->features |= NETIF_F_GSO_SOFTWARE; dev->vlan_features = dev->features; - dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; netif_keep_dst(dev); dev->priv_flags |= IFF_NO_QUEUE; diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 3319d97..8d5fcd6 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -399,22 +399,6 @@ static inline struct sk_buff *__vlan_hwaccel_push_inside(struct sk_buff *skb) skb->vlan_tci = 0; return skb; } -/* - * vlan_hwaccel_push_inside - pushes vlan tag to the payload - * @skb: skbuff to tag - * - * Checks is tag is present in @skb->vlan_tci and if it is, it pushes the - * VLAN tag from @skb->vlan_tci inside to the payload. - * - * Following the skb_unshare() example, in case of error, the calling function - * doesn't have to worry about freeing the original skb. - */ -static inline struct sk_buff *vlan_hwaccel_push_inside(struct sk_buff *skb) -{ - if (skb_vlan_tag_present(skb)) - skb = __vlan_hwaccel_push_inside(skb); - return skb; -} /** * __vlan_hwaccel_put_tag - hardware accelerated VLAN inserting -- cgit v1.1 From 03dc52a86d58c70577d9f62527f68db9116f61a5 Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Sun, 13 Nov 2016 20:43:53 -0800 Subject: vxlan: avoid checking socket multiple times. Check the vxlan socket in vxlan6_getroute(). Signed-off-by: Pravin B Shelar Acked-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 7bebce1..7730b33 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1832,6 +1832,7 @@ static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, #if IS_ENABLED(CONFIG_IPV6) static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, + struct vxlan_sock *sock6, struct sk_buff *skb, int oif, u8 tos, __be32 label, const struct in6_addr *daddr, @@ -1839,7 +1840,6 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, struct dst_cache *dst_cache, const struct ip_tunnel_info *info) { - struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct dst_entry *ndst; struct flowi6 fl6; @@ -2071,11 +2071,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct dst_entry *ndst; u32 rt6i_flags; - if (!sock6) - goto drop; - sk = sock6->sock->sk; - - ndst = vxlan6_get_route(vxlan, skb, + ndst = vxlan6_get_route(vxlan, sock6, skb, rdst ? rdst->remote_ifindex : 0, tos, label, &dst->sin6.sin6_addr, &src->sin6.sin6_addr, @@ -2095,6 +2091,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto tx_error; } + sk = sock6->sock->sk; /* Bypass encapsulation if the destination is local */ rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; if (!info && rt6i_flags & RTF_LOCAL && @@ -2434,9 +2431,10 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) ip_rt_put(rt); } else { #if IS_ENABLED(CONFIG_IPV6) + struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); struct dst_entry *ndst; - ndst = vxlan6_get_route(vxlan, skb, 0, info->key.tos, + ndst = vxlan6_get_route(vxlan, sock6, skb, 0, info->key.tos, info->key.label, &info->key.u.ipv6.dst, &info->key.u.ipv6.src, NULL, info); if (IS_ERR(ndst)) -- cgit v1.1 From c46b7897ad5ba49b215882fa6a1e4c28d21acf4f Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Sun, 13 Nov 2016 20:43:54 -0800 Subject: vxlan: simplify exception handling vxlan egress path error handling has became complicated, it need to handle IPv4 and IPv6 tunnel cases. Earlier patch removes vlan handling from vxlan_build_skb(), so vxlan_build_skb does not need to free skb and we can simplify the xmit path by having single error handling for both type of tunnels. Signed-off-by: Pravin B Shelar Acked-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 7730b33..40ea9e0 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1755,11 +1755,11 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst, /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); if (unlikely(err)) - goto out_free; + return err; err = iptunnel_handle_offloads(skb, type); if (err) - goto out_free; + return err; vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); vxh->vx_flags = VXLAN_HF_VNI; @@ -1783,16 +1783,12 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst, if (vxflags & VXLAN_F_GPE) { err = vxlan_build_gpe_hdr(vxh, vxflags, skb->protocol); if (err < 0) - goto out_free; + return err; inner_protocol = skb->protocol; } skb_set_inner_protocol(skb, inner_protocol); return 0; - -out_free: - kfree_skb(skb); - return err; } static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, @@ -1929,13 +1925,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct ip_tunnel_info *info; struct vxlan_dev *vxlan = netdev_priv(dev); struct sock *sk; - struct rtable *rt = NULL; const struct iphdr *old_iph; union vxlan_addr *dst; union vxlan_addr remote_ip, local_ip; union vxlan_addr *src; struct vxlan_metadata _md; struct vxlan_metadata *md = &_md; + struct dst_entry *ndst = NULL; __be16 src_port = 0, dst_port; __be32 vni, label; __be16 df = 0; @@ -2011,6 +2007,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (dst->sa.sa_family == AF_INET) { struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); + struct rtable *rt; if (!sock4) goto drop; @@ -2032,7 +2029,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, netdev_dbg(dev, "circular route to %pI4\n", &dst->sin.sin_addr.s_addr); dev->stats.collisions++; - goto rt_tx_error; + ip_rt_put(rt); + goto tx_error; } /* Bypass encapsulation if the destination is local */ @@ -2055,12 +2053,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) df = htons(IP_DF); + ndst = &rt->dst; tos = ip_tunnel_ecn_encap(tos, old_iph, skb); ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); - err = vxlan_build_skb(skb, &rt->dst, sizeof(struct iphdr), + err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr), vni, md, flags, udp_sum); if (err < 0) - goto xmit_tx_error; + goto tx_error; udp_tunnel_xmit_skb(rt, sk, skb, src->sin.sin_addr.s_addr, dst->sin.sin_addr.s_addr, tos, ttl, df, @@ -2068,7 +2067,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, #if IS_ENABLED(CONFIG_IPV6) } else { struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); - struct dst_entry *ndst; u32 rt6i_flags; ndst = vxlan6_get_route(vxlan, sock6, skb, @@ -2080,13 +2078,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, netdev_dbg(dev, "no route to %pI6\n", &dst->sin6.sin6_addr); dev->stats.tx_carrier_errors++; + ndst = NULL; goto tx_error; } if (ndst->dev == dev) { netdev_dbg(dev, "circular route to %pI6\n", &dst->sin6.sin6_addr); - dst_release(ndst); dev->stats.collisions++; goto tx_error; } @@ -2098,12 +2096,12 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, !(rt6i_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) { struct vxlan_dev *dst_vxlan; - dst_release(ndst); dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst->sa.sa_family, dst_port, vxlan->flags); if (!dst_vxlan) goto tx_error; + dst_release(ndst); vxlan_encap_bypass(skb, vxlan, dst_vxlan); return; } @@ -2116,11 +2114,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, skb_scrub_packet(skb, xnet); err = vxlan_build_skb(skb, ndst, sizeof(struct ipv6hdr), vni, md, flags, udp_sum); - if (err < 0) { - dst_release(ndst); - dev->stats.tx_errors++; - return; - } + if (err < 0) + goto tx_error; + udp_tunnel6_xmit_skb(ndst, sk, skb, dev, &src->sin6.sin6_addr, &dst->sin6.sin6_addr, tos, ttl, @@ -2132,17 +2128,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, drop: dev->stats.tx_dropped++; - goto tx_free; + dev_kfree_skb(skb); + return; -xmit_tx_error: - /* skb is already freed. */ - skb = NULL; -rt_tx_error: - ip_rt_put(rt); tx_error: + dst_release(ndst); dev->stats.tx_errors++; -tx_free: - dev_kfree_skb(skb); + kfree_skb(skb); } /* Transmit local packets over Vxlan -- cgit v1.1 From 655c3de16540b8496273514658a6ad4cb31e01b4 Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Sun, 13 Nov 2016 20:43:55 -0800 Subject: vxlan: improve vxlan route lookup checks. Move route sanity check to respective vxlan[4/6]_get_route functions. This allows us to perform all sanity checks before caching the dst so that we can avoid these checks on subsequent packets. This give move accurate metadata information for packet from fill_metadata_dst(). Signed-off-by: Pravin B Shelar Acked-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 77 ++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 40ea9e0..d8fe4b8 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1791,7 +1791,8 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst, return 0; } -static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, +static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, struct net_device *dev, + struct vxlan_sock *sock4, struct sk_buff *skb, int oif, u8 tos, __be32 daddr, __be32 *saddr, struct dst_cache *dst_cache, @@ -1801,6 +1802,9 @@ static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, struct rtable *rt = NULL; struct flowi4 fl4; + if (!sock4) + return ERR_PTR(-EIO); + if (tos && !info) use_cache = false; if (use_cache) { @@ -1818,16 +1822,26 @@ static struct rtable *vxlan_get_route(struct vxlan_dev *vxlan, fl4.saddr = *saddr; rt = ip_route_output_key(vxlan->net, &fl4); - if (!IS_ERR(rt)) { + if (likely(!IS_ERR(rt))) { + if (rt->dst.dev == dev) { + netdev_dbg(dev, "circular route to %pI4\n", &daddr); + ip_rt_put(rt); + return ERR_PTR(-ELOOP); + } + *saddr = fl4.saddr; if (use_cache) dst_cache_set_ip4(dst_cache, &rt->dst, fl4.saddr); + } else { + netdev_dbg(dev, "no route to %pI4\n", &daddr); + return ERR_PTR(-ENETUNREACH); } return rt; } #if IS_ENABLED(CONFIG_IPV6) static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, + struct net_device *dev, struct vxlan_sock *sock6, struct sk_buff *skb, int oif, u8 tos, __be32 label, @@ -1863,8 +1877,16 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, err = ipv6_stub->ipv6_dst_lookup(vxlan->net, sock6->sock->sk, &ndst, &fl6); - if (err < 0) - return ERR_PTR(err); + if (unlikely(err < 0)) { + netdev_dbg(dev, "no route to %pI6\n", daddr); + return ERR_PTR(-ENETUNREACH); + } + + if (unlikely(ndst->dev == dev)) { + netdev_dbg(dev, "circular route to %pI6\n", daddr); + dst_release(ndst); + return ERR_PTR(-ELOOP); + } *saddr = fl6.saddr; if (use_cache) @@ -1931,8 +1953,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, union vxlan_addr *src; struct vxlan_metadata _md; struct vxlan_metadata *md = &_md; - struct dst_entry *ndst = NULL; __be16 src_port = 0, dst_port; + struct dst_entry *ndst = NULL; __be32 vni, label; __be16 df = 0; __u8 tos, ttl; @@ -2009,29 +2031,14 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); struct rtable *rt; - if (!sock4) - goto drop; - sk = sock4->sock->sk; - - rt = vxlan_get_route(vxlan, skb, + rt = vxlan_get_route(vxlan, dev, sock4, skb, rdst ? rdst->remote_ifindex : 0, tos, dst->sin.sin_addr.s_addr, &src->sin.sin_addr.s_addr, dst_cache, info); - if (IS_ERR(rt)) { - netdev_dbg(dev, "no route to %pI4\n", - &dst->sin.sin_addr.s_addr); - dev->stats.tx_carrier_errors++; - goto tx_error; - } - - if (rt->dst.dev == dev) { - netdev_dbg(dev, "circular route to %pI4\n", - &dst->sin.sin_addr.s_addr); - dev->stats.collisions++; - ip_rt_put(rt); + if (IS_ERR(rt)) goto tx_error; - } + sk = sock4->sock->sk; /* Bypass encapsulation if the destination is local */ if (!info && rt->rt_flags & RTCF_LOCAL && @@ -2069,27 +2076,17 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); u32 rt6i_flags; - ndst = vxlan6_get_route(vxlan, sock6, skb, + ndst = vxlan6_get_route(vxlan, dev, sock6, skb, rdst ? rdst->remote_ifindex : 0, tos, label, &dst->sin6.sin6_addr, &src->sin6.sin6_addr, dst_cache, info); if (IS_ERR(ndst)) { - netdev_dbg(dev, "no route to %pI6\n", - &dst->sin6.sin6_addr); - dev->stats.tx_carrier_errors++; ndst = NULL; goto tx_error; } - - if (ndst->dev == dev) { - netdev_dbg(dev, "circular route to %pI6\n", - &dst->sin6.sin6_addr); - dev->stats.collisions++; - goto tx_error; - } - sk = sock6->sock->sk; + /* Bypass encapsulation if the destination is local */ rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; if (!info && rt6i_flags & RTF_LOCAL && @@ -2132,6 +2129,10 @@ drop: return; tx_error: + if (err == -ELOOP) + dev->stats.collisions++; + else if (err == -ENETUNREACH) + dev->stats.tx_carrier_errors++; dst_release(ndst); dev->stats.tx_errors++; kfree_skb(skb); @@ -2413,9 +2414,7 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); struct rtable *rt; - if (!sock4) - return -EINVAL; - rt = vxlan_get_route(vxlan, skb, 0, info->key.tos, + rt = vxlan_get_route(vxlan, dev, sock4, skb, 0, info->key.tos, info->key.u.ipv4.dst, &info->key.u.ipv4.src, NULL, info); if (IS_ERR(rt)) @@ -2426,7 +2425,7 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); struct dst_entry *ndst; - ndst = vxlan6_get_route(vxlan, sock6, skb, 0, info->key.tos, + ndst = vxlan6_get_route(vxlan, dev, sock6, skb, 0, info->key.tos, info->key.label, &info->key.u.ipv6.dst, &info->key.u.ipv6.src, NULL, info); if (IS_ERR(ndst)) -- cgit v1.1 From fee1fad7c73dd006f50115ddc538a2e20f3b0c6c Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Sun, 13 Nov 2016 20:43:56 -0800 Subject: vxlan: simplify RTF_LOCAL handling. Avoid code duplicate code for handling RTF_LOCAL routes. Signed-off-by: Pravin B Shelar Acked-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 85 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index d8fe4b8..3f952cf 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1940,6 +1940,40 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, } } +static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, + struct vxlan_dev *vxlan, union vxlan_addr *daddr, + __be32 dst_port, __be32 vni, struct dst_entry *dst, + u32 rt_flags) +{ +#if IS_ENABLED(CONFIG_IPV6) + /* IPv6 rt-flags are checked against RTF_LOCAL, but the value of + * RTF_LOCAL is equal to RTCF_LOCAL. So to keep code simple + * we can use RTCF_LOCAL which works for ipv4 and ipv6 route entry. + */ + BUILD_BUG_ON(RTCF_LOCAL != RTF_LOCAL); +#endif + /* Bypass encapsulation if the destination is local */ + if (rt_flags & RTCF_LOCAL && + !(rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) { + struct vxlan_dev *dst_vxlan; + + dst_release(dst); + dst_vxlan = vxlan_find_vni(vxlan->net, vni, + daddr->sa.sa_family, dst_port, + vxlan->flags); + if (!dst_vxlan) { + dev->stats.tx_errors++; + kfree_skb(skb); + + return -ENOENT; + } + vxlan_encap_bypass(skb, vxlan, dst_vxlan); + return 1; + } + + return 0; +} + static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_rdst *rdst, bool did_rsc) { @@ -2038,27 +2072,19 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, dst_cache, info); if (IS_ERR(rt)) goto tx_error; - sk = sock4->sock->sk; + sk = sock4->sock->sk; /* Bypass encapsulation if the destination is local */ - if (!info && rt->rt_flags & RTCF_LOCAL && - !(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) { - struct vxlan_dev *dst_vxlan; - - ip_rt_put(rt); - dst_vxlan = vxlan_find_vni(vxlan->net, vni, - dst->sa.sa_family, dst_port, - vxlan->flags); - if (!dst_vxlan) - goto tx_error; - vxlan_encap_bypass(skb, vxlan, dst_vxlan); - return; - } - - if (!info) + if (!info) { + err = encap_bypass_if_local(skb, dev, vxlan, dst, + dst_port, vni, &rt->dst, + rt->rt_flags); + if (err) + return; udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM_TX); - else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) + } else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) { df = htons(IP_DF); + } ndst = &rt->dst; tos = ip_tunnel_ecn_encap(tos, old_iph, skb); @@ -2074,7 +2100,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, #if IS_ENABLED(CONFIG_IPV6) } else { struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); - u32 rt6i_flags; ndst = vxlan6_get_route(vxlan, dev, sock6, skb, rdst ? rdst->remote_ifindex : 0, tos, @@ -2087,24 +2112,16 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, } sk = sock6->sock->sk; - /* Bypass encapsulation if the destination is local */ - rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; - if (!info && rt6i_flags & RTF_LOCAL && - !(rt6i_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) { - struct vxlan_dev *dst_vxlan; - - dst_vxlan = vxlan_find_vni(vxlan->net, vni, - dst->sa.sa_family, dst_port, - vxlan->flags); - if (!dst_vxlan) - goto tx_error; - dst_release(ndst); - vxlan_encap_bypass(skb, vxlan, dst_vxlan); - return; - } + if (!info) { + u32 rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; - if (!info) + err = encap_bypass_if_local(skb, dev, vxlan, dst, + dst_port, vni, ndst, + rt6i_flags); + if (err) + return; udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); + } tos = ip_tunnel_ecn_encap(tos, old_iph, skb); ttl = ttl ? : ip6_dst_hoplimit(ndst); -- cgit v1.1 From 0770b53bd276a7b7846a185ec8a8f1188f135f7f Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Sun, 13 Nov 2016 20:43:57 -0800 Subject: vxlan: simplify vxlan xmit Existing vxlan xmit function handles two distinct cases. 1. vxlan net device 2. vxlan lwt device. By seperating initialization these two cases the egress path looks better. Signed-off-by: Pravin B Shelar Acked-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 78 +++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 3f952cf..d536a93 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1980,8 +1980,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct dst_cache *dst_cache; struct ip_tunnel_info *info; struct vxlan_dev *vxlan = netdev_priv(dev); - struct sock *sk; - const struct iphdr *old_iph; + const struct iphdr *old_iph = ip_hdr(skb); union vxlan_addr *dst; union vxlan_addr remote_ip, local_ip; union vxlan_addr *src; @@ -1990,7 +1989,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, __be16 src_port = 0, dst_port; struct dst_entry *ndst = NULL; __be32 vni, label; - __be16 df = 0; __u8 tos, ttl; int err; u32 flags = vxlan->flags; @@ -2000,19 +1998,40 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, info = skb_tunnel_info(skb); if (rdst) { + dst = &rdst->remote_ip; + if (vxlan_addr_any(dst)) { + if (did_rsc) { + /* short-circuited back to local bridge */ + vxlan_encap_bypass(skb, vxlan, vxlan); + return; + } + goto drop; + } + dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port; vni = rdst->remote_vni; - dst = &rdst->remote_ip; src = &vxlan->cfg.saddr; dst_cache = &rdst->dst_cache; + md->gbp = skb->mark; + ttl = vxlan->cfg.ttl; + if (!ttl && vxlan_addr_multicast(dst)) + ttl = 1; + + tos = vxlan->cfg.tos; + if (tos == 1) + tos = ip_tunnel_get_dsfield(old_iph, skb); + + if (dst->sa.sa_family == AF_INET) + udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM_TX); + else + udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); + label = vxlan->cfg.label; } else { if (!info) { WARN_ONCE(1, "%s: Missing encapsulation instructions\n", dev->name); goto drop; } - dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; - vni = tunnel_id_to_key32(info->key.tun_id); remote_ip.sa.sa_family = ip_tunnel_info_af(info); if (remote_ip.sa.sa_family == AF_INET) { remote_ip.sin.sin_addr.s_addr = info->key.u.ipv4.dst; @@ -2022,48 +2041,24 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, local_ip.sin6.sin6_addr = info->key.u.ipv6.src; } dst = &remote_ip; + dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; + vni = tunnel_id_to_key32(info->key.tun_id); src = &local_ip; dst_cache = &info->dst_cache; - } - - if (vxlan_addr_any(dst)) { - if (did_rsc) { - /* short-circuited back to local bridge */ - vxlan_encap_bypass(skb, vxlan, vxlan); - return; - } - goto drop; - } - - old_iph = ip_hdr(skb); - - ttl = vxlan->cfg.ttl; - if (!ttl && vxlan_addr_multicast(dst)) - ttl = 1; - - tos = vxlan->cfg.tos; - if (tos == 1) - tos = ip_tunnel_get_dsfield(old_iph, skb); - - label = vxlan->cfg.label; - src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, - vxlan->cfg.port_max, true); - - if (info) { + if (info->options_len) + md = ip_tunnel_info_opts(info); ttl = info->key.ttl; tos = info->key.tos; label = info->key.label; udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); - - if (info->options_len) - md = ip_tunnel_info_opts(info); - } else { - md->gbp = skb->mark; } + src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, + vxlan->cfg.port_max, true); if (dst->sa.sa_family == AF_INET) { struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); struct rtable *rt; + __be16 df = 0; rt = vxlan_get_route(vxlan, dev, sock4, skb, rdst ? rdst->remote_ifindex : 0, tos, @@ -2073,7 +2068,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (IS_ERR(rt)) goto tx_error; - sk = sock4->sock->sk; /* Bypass encapsulation if the destination is local */ if (!info) { err = encap_bypass_if_local(skb, dev, vxlan, dst, @@ -2081,7 +2075,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, rt->rt_flags); if (err) return; - udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM_TX); } else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) { df = htons(IP_DF); } @@ -2094,7 +2087,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (err < 0) goto tx_error; - udp_tunnel_xmit_skb(rt, sk, skb, src->sin.sin_addr.s_addr, + udp_tunnel_xmit_skb(rt, sock4->sock->sk, skb, src->sin.sin_addr.s_addr, dst->sin.sin_addr.s_addr, tos, ttl, df, src_port, dst_port, xnet, !udp_sum); #if IS_ENABLED(CONFIG_IPV6) @@ -2110,7 +2103,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ndst = NULL; goto tx_error; } - sk = sock6->sock->sk; if (!info) { u32 rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; @@ -2120,7 +2112,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, rt6i_flags); if (err) return; - udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); } tos = ip_tunnel_ecn_encap(tos, old_iph, skb); @@ -2131,13 +2122,12 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (err < 0) goto tx_error; - udp_tunnel6_xmit_skb(ndst, sk, skb, dev, + udp_tunnel6_xmit_skb(ndst, sock6->sock->sk, skb, dev, &src->sin6.sin6_addr, &dst->sin6.sin6_addr, tos, ttl, label, src_port, dst_port, !udp_sum); #endif } - return; drop: -- cgit v1.1 From 9efdb92d68c726e70066e5b4189c1186c9b6f90c Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Sun, 13 Nov 2016 20:43:58 -0800 Subject: vxlan: remove unsed vxlan_dev_dst_port() Signed-off-by: Pravin B Shelar Acked-by: Jiri Benc Signed-off-by: David S. Miller --- include/net/vxlan.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 308adc4..49a5920 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -281,16 +281,6 @@ struct vxlan_dev { struct net_device *vxlan_dev_create(struct net *net, const char *name, u8 name_assign_type, struct vxlan_config *conf); -static inline __be16 vxlan_dev_dst_port(struct vxlan_dev *vxlan, - unsigned short family) -{ -#if IS_ENABLED(CONFIG_IPV6) - if (family == AF_INET6) - return inet_sk(vxlan->vn6_sock->sock->sk)->inet_sport; -#endif - return inet_sk(vxlan->vn4_sock->sock->sk)->inet_sport; -} - static inline netdev_features_t vxlan_features_check(struct sk_buff *skb, netdev_features_t features) { -- cgit v1.1 From 8ebd115bb23ac42f14a5e242500be61d8606b82d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Nov 2016 16:32:11 -0500 Subject: vxlan: Fix uninitialized variable warnings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/net/vxlan.c: In function ‘vxlan_xmit_one’: drivers/net/vxlan.c:2141:10: warning: ‘err’ may be used uninitialized in this function [-Wmaybe-uninitialized] Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index d536a93..0a3fd67 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2065,8 +2065,10 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, dst->sin.sin_addr.s_addr, &src->sin.sin_addr.s_addr, dst_cache, info); - if (IS_ERR(rt)) + if (IS_ERR(rt)) { + err = PTR_ERR(rt); goto tx_error; + } /* Bypass encapsulation if the destination is local */ if (!info) { @@ -2100,6 +2102,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, &src->sin6.sin6_addr, dst_cache, info); if (IS_ERR(ndst)) { + err = PTR_ERR(ndst); ndst = NULL; goto tx_error; } -- cgit v1.1 From e86a8987e458a1826f509c41494b0b29a61144a7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:30 -0800 Subject: net: phy: Add phy_ethtool_nway_reset This function just calls into genphy_restart_aneg() to perform an autonegotation restart. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 11 +++++++++++ include/linux/phy.h | 1 + 2 files changed, 12 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e6dd222..73adbaa 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1441,3 +1441,14 @@ int phy_ethtool_set_link_ksettings(struct net_device *ndev, return phy_ethtool_ksettings_set(phydev, cmd); } EXPORT_SYMBOL(phy_ethtool_set_link_ksettings); + +int phy_ethtool_nway_reset(struct net_device *ndev) +{ + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + return genphy_restart_aneg(phydev); +} +EXPORT_SYMBOL(phy_ethtool_nway_reset); diff --git a/include/linux/phy.h b/include/linux/phy.h index 9880d73..b9bd3b4 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -860,6 +860,7 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev, struct ethtool_link_ksettings *cmd); int phy_ethtool_set_link_ksettings(struct net_device *ndev, const struct ethtool_link_ksettings *cmd); +int phy_ethtool_nway_reset(struct net_device *ndev); int __init mdio_bus_init(void); void mdio_bus_exit(void); -- cgit v1.1 From b3de7f36de1d5693cf481051cf72e0e8a5cfe7e6 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:31 -0800 Subject: net: nb8800: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/aurora/nb8800.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c index b59aa35..07ff649 100644 --- a/drivers/net/ethernet/aurora/nb8800.c +++ b/drivers/net/ethernet/aurora/nb8800.c @@ -1037,16 +1037,6 @@ static const struct net_device_ops nb8800_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; -static int nb8800_nway_reset(struct net_device *dev) -{ - struct phy_device *phydev = dev->phydev; - - if (!phydev) - return -ENODEV; - - return genphy_restart_aneg(phydev); -} - static void nb8800_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pp) { @@ -1165,7 +1155,7 @@ static void nb8800_get_ethtool_stats(struct net_device *dev, } static const struct ethtool_ops nb8800_ethtool_ops = { - .nway_reset = nb8800_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_pauseparam = nb8800_get_pauseparam, .set_pauseparam = nb8800_set_pauseparam, -- cgit v1.1 From 42469bf5d9bb9fe5e2ab28f18a5d39776797f026 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:32 -0800 Subject: net: bcm63xx_enet: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 5c7acef..a43ab90 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1434,11 +1434,8 @@ static int bcm_enet_nway_reset(struct net_device *dev) struct bcm_enet_priv *priv; priv = netdev_priv(dev); - if (priv->has_phy) { - if (!dev->phydev) - return -ENODEV; - return genphy_restart_aneg(dev->phydev); - } + if (priv->has_phy) + return phy_ethtool_nway_reset(dev), return -EOPNOTSUPP; } -- cgit v1.1 From fc5e353cd1a7d51cdf4159c71b5c367a2413abc3 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:33 -0800 Subject: net: mv643xx_eth: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 81b08d7..5f62c3d 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1639,14 +1639,6 @@ static void mv643xx_eth_get_drvinfo(struct net_device *dev, strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); } -static int mv643xx_eth_nway_reset(struct net_device *dev) -{ - if (!dev->phydev) - return -EINVAL; - - return genphy_restart_aneg(dev->phydev); -} - static int mv643xx_eth_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) { @@ -1770,7 +1762,7 @@ static int mv643xx_eth_get_sset_count(struct net_device *dev, int sset) static const struct ethtool_ops mv643xx_eth_ethtool_ops = { .get_drvinfo = mv643xx_eth_get_drvinfo, - .nway_reset = mv643xx_eth_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_coalesce = mv643xx_eth_get_coalesce, .set_coalesce = mv643xx_eth_set_coalesce, -- cgit v1.1 From fe1eb9c5bbb1c8ba026da162e45317fa9ed6101f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:34 -0800 Subject: net: smsc911x: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smsc911x.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index cdb343f..be09573 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1956,11 +1956,6 @@ static void smsc911x_ethtool_getdrvinfo(struct net_device *dev, sizeof(info->bus_info)); } -static int smsc911x_ethtool_nwayreset(struct net_device *dev) -{ - return phy_start_aneg(dev->phydev); -} - static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev) { struct smsc911x_data *pdata = netdev_priv(dev); @@ -2132,7 +2127,7 @@ static int smsc911x_ethtool_set_eeprom(struct net_device *dev, static const struct ethtool_ops smsc911x_ethtool_ops = { .get_link = ethtool_op_get_link, .get_drvinfo = smsc911x_ethtool_getdrvinfo, - .nway_reset = smsc911x_ethtool_nwayreset, + .nway_reset = phy_ethtool_nway_reset, .get_msglevel = smsc911x_ethtool_getmsglevel, .set_msglevel = smsc911x_ethtool_setmsglevel, .get_regs_len = smsc911x_ethtool_getregslen, -- cgit v1.1 From 8fd9ce0b760b5a974a264e24b95b49dd15e27891 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:35 -0800 Subject: net: ethernet: smsc9420: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smsc9420.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index b7bfed4b..3174aeb 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -254,14 +254,6 @@ static void smsc9420_ethtool_set_msglevel(struct net_device *netdev, u32 data) pd->msg_enable = data; } -static int smsc9420_ethtool_nway_reset(struct net_device *netdev) -{ - if (!netdev->phydev) - return -ENODEV; - - return phy_start_aneg(netdev->phydev); -} - static int smsc9420_ethtool_getregslen(struct net_device *dev) { /* all smsc9420 registers plus all phy registers */ @@ -417,7 +409,7 @@ static const struct ethtool_ops smsc9420_ethtool_ops = { .get_drvinfo = smsc9420_ethtool_get_drvinfo, .get_msglevel = smsc9420_ethtool_get_msglevel, .set_msglevel = smsc9420_ethtool_set_msglevel, - .nway_reset = smsc9420_ethtool_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_eeprom_len = smsc9420_ethtool_get_eeprom_len, .get_eeprom = smsc9420_ethtool_get_eeprom, -- cgit v1.1 From 16250527b3437eae92fcbac922aa24445b5c699a Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:36 -0800 Subject: net: ethernet: ll_temac: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/ll_temac_main.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index a9bd665..bcd7b76 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -967,13 +967,8 @@ static const struct attribute_group temac_attr_group = { }; /* ethtool support */ -static int temac_nway_reset(struct net_device *ndev) -{ - return phy_start_aneg(ndev->phydev); -} - static const struct ethtool_ops temac_ethtool_ops = { - .nway_reset = temac_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_ts_info = ethtool_op_get_ts_info, .get_link_ksettings = phy_ethtool_get_link_ksettings, -- cgit v1.1 From 17fc4637fc713eb395c005e08a6906786e053ffe Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:37 -0800 Subject: net: ethernet: ixp4xx_eth: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/xscale/ixp4xx_eth.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 07d862d..aee55c0 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1001,11 +1001,6 @@ static void ixp4xx_get_drvinfo(struct net_device *dev, strlcpy(info->bus_info, "internal", sizeof(info->bus_info)); } -static int ixp4xx_nway_reset(struct net_device *dev) -{ - return phy_start_aneg(dev->phydev); -} - int ixp46x_phc_index = -1; EXPORT_SYMBOL_GPL(ixp46x_phc_index); @@ -1037,7 +1032,7 @@ static int ixp4xx_get_ts_info(struct net_device *dev, static const struct ethtool_ops ixp4xx_ethtool_ops = { .get_drvinfo = ixp4xx_get_drvinfo, - .nway_reset = ixp4xx_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_ts_info = ixp4xx_get_ts_info, .get_link_ksettings = phy_ethtool_get_link_ksettings, -- cgit v1.1 From 016e770d87d44206c42ca9dd9710a2fa12161183 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:38 -0800 Subject: net: bcmgenet: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 4464bc5..33638dc 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -971,13 +971,6 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e) return phy_ethtool_set_eee(priv->phydev, e); } -static int bcmgenet_nway_reset(struct net_device *dev) -{ - struct bcmgenet_priv *priv = netdev_priv(dev); - - return genphy_restart_aneg(priv->phydev); -} - /* standard ethtool support functions. */ static const struct ethtool_ops bcmgenet_ethtool_ops = { .get_strings = bcmgenet_get_strings, @@ -991,7 +984,7 @@ static const struct ethtool_ops bcmgenet_ethtool_ops = { .set_wol = bcmgenet_set_wol, .get_eee = bcmgenet_get_eee, .set_eee = bcmgenet_set_eee, - .nway_reset = bcmgenet_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_coalesce = bcmgenet_get_coalesce, .set_coalesce = bcmgenet_set_coalesce, .get_link_ksettings = bcmgenet_get_link_ksettings, -- cgit v1.1 From 7c5ad3d9a823e204868b5da1fea57d772d068ba9 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:39 -0800 Subject: net: fs_enet: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index 44f50e1..34843c1 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -807,11 +807,6 @@ static void fs_get_regs(struct net_device *dev, struct ethtool_regs *regs, regs->version = 0; } -static int fs_nway_reset(struct net_device *dev) -{ - return 0; -} - static u32 fs_get_msglevel(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); @@ -865,7 +860,7 @@ static int fs_set_tunable(struct net_device *dev, static const struct ethtool_ops fs_ethtool_ops = { .get_drvinfo = fs_get_drvinfo, .get_regs_len = fs_get_regs_len, - .nway_reset = fs_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_msglevel = fs_get_msglevel, .set_msglevel = fs_set_msglevel, -- cgit v1.1 From 11d592890c17ddca002dacf703678f38235d5176 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:40 -0800 Subject: net: fec: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 01aebc0..1aabe4b 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2344,16 +2344,6 @@ static int fec_enet_get_sset_count(struct net_device *dev, int sset) } #endif /* !defined(CONFIG_M5272) */ -static int fec_enet_nway_reset(struct net_device *dev) -{ - struct phy_device *phydev = dev->phydev; - - if (!phydev) - return -ENODEV; - - return genphy_restart_aneg(phydev); -} - /* ITR clock source is enet system clock (clk_ahb). * TCTT unit is cycle_ns * 64 cycle * So, the ICTT value = X us / (cycle_ns * 64) @@ -2553,7 +2543,7 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { .get_drvinfo = fec_enet_get_drvinfo, .get_regs_len = fec_enet_get_regs_len, .get_regs = fec_enet_get_regs, - .nway_reset = fec_enet_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_coalesce = fec_enet_get_coalesce, .set_coalesce = fec_enet_set_coalesce, -- cgit v1.1 From e06986fab3fc75f4070267efd566a10f080198f6 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:41 -0800 Subject: net: ethernet: ucc: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/ucc_geth_ethtool.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index 812a968..8ba636f 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -332,13 +332,6 @@ static void uec_get_ethtool_stats(struct net_device *netdev, } } -static int uec_nway_reset(struct net_device *netdev) -{ - struct ucc_geth_private *ugeth = netdev_priv(netdev); - - return phy_start_aneg(ugeth->phydev); -} - /* Report driver information */ static void uec_get_drvinfo(struct net_device *netdev, @@ -394,7 +387,7 @@ static const struct ethtool_ops uec_ethtool_ops = { .get_regs = uec_get_regs, .get_msglevel = uec_get_msglevel, .set_msglevel = uec_set_msglevel, - .nway_reset = uec_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_ringparam = uec_get_ringparam, .set_ringparam = uec_set_ringparam, -- cgit v1.1 From e3979ce98bd9e26c1830b9e23b5d9189de7c1c7d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:42 -0800 Subject: net: ethernet: lantiq_etop: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Reviewed-by: Thomas Langer Signed-off-by: David S. Miller --- drivers/net/ethernet/lantiq_etop.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 1a739d7..9d93137 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -303,15 +303,9 @@ ltq_etop_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } -static int -ltq_etop_nway_reset(struct net_device *dev) -{ - return phy_start_aneg(dev->phydev); -} - static const struct ethtool_ops ltq_etop_ethtool_ops = { .get_drvinfo = ltq_etop_get_drvinfo, - .nway_reset = ltq_etop_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, }; -- cgit v1.1 From eea16da82cccd974b1105001fb247f652b74759c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:43 -0800 Subject: net: usb: ax88172x: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/usb/ax88172a.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index 49a3bc1..6308386 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -149,14 +149,6 @@ static const struct net_device_ops ax88172a_netdev_ops = { .ndo_set_rx_mode = asix_set_multicast, }; -static int ax88172a_nway_reset(struct net_device *net) -{ - if (!net->phydev) - return -ENODEV; - - return phy_start_aneg(net->phydev); -} - static const struct ethtool_ops ax88172a_ethtool_ops = { .get_drvinfo = asix_get_drvinfo, .get_link = usbnet_get_link, @@ -167,7 +159,7 @@ static const struct ethtool_ops ax88172a_ethtool_ops = { .get_eeprom_len = asix_get_eeprom_len, .get_eeprom = asix_get_eeprom, .set_eeprom = asix_set_eeprom, - .nway_reset = ax88172a_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, }; -- cgit v1.1 From 860ce4b4015c4d54d8cd6fa9158d5b4a6476dffc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 10:06:44 -0800 Subject: net: usb: lan78xx: Utilize phy_ethtool_nway_reset Signed-off-by: Florian Fainelli Acked-by: Woojung Huh Signed-off-by: David S. Miller --- drivers/net/usb/lan78xx.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index bcd9010..cf2857f 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1447,11 +1447,6 @@ static u32 lan78xx_get_link(struct net_device *net) return net->phydev->link; } -static int lan78xx_nway_reset(struct net_device *net) -{ - return phy_start_aneg(net->phydev); -} - static void lan78xx_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { @@ -1655,7 +1650,7 @@ exit: static const struct ethtool_ops lan78xx_ethtool_ops = { .get_link = lan78xx_get_link, - .nway_reset = lan78xx_nway_reset, + .nway_reset = phy_ethtool_nway_reset, .get_drvinfo = lan78xx_get_drvinfo, .get_msglevel = lan78xx_get_msglevel, .set_msglevel = lan78xx_set_msglevel, -- cgit v1.1 From 08e10d4dd159035747610846f5185b30df943173 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 13 Nov 2016 23:33:46 +0100 Subject: net: bnx2: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 74 ++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 7baf300..9960a92 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -6904,12 +6904,14 @@ bnx2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats) /* All ethtool functions called with rtnl_lock */ static int -bnx2_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +bnx2_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct bnx2 *bp = netdev_priv(dev); int support_serdes = 0, support_copper = 0; + u32 supported, advertising; - cmd->supported = SUPPORTED_Autoneg; + supported = SUPPORTED_Autoneg; if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) { support_serdes = 1; support_copper = 1; @@ -6919,56 +6921,59 @@ bnx2_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) support_copper = 1; if (support_serdes) { - cmd->supported |= SUPPORTED_1000baseT_Full | + supported |= SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE; if (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) - cmd->supported |= SUPPORTED_2500baseX_Full; - + supported |= SUPPORTED_2500baseX_Full; } if (support_copper) { - cmd->supported |= SUPPORTED_10baseT_Half | + supported |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_TP; - } spin_lock_bh(&bp->phy_lock); - cmd->port = bp->phy_port; - cmd->advertising = bp->advertising; + cmd->base.port = bp->phy_port; + advertising = bp->advertising; if (bp->autoneg & AUTONEG_SPEED) { - cmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; } else { - cmd->autoneg = AUTONEG_DISABLE; + cmd->base.autoneg = AUTONEG_DISABLE; } if (netif_carrier_ok(dev)) { - ethtool_cmd_speed_set(cmd, bp->line_speed); - cmd->duplex = bp->duplex; + cmd->base.speed = bp->line_speed; + cmd->base.duplex = bp->duplex; if (!(bp->phy_flags & BNX2_PHY_FLAG_SERDES)) { if (bp->phy_flags & BNX2_PHY_FLAG_MDIX) - cmd->eth_tp_mdix = ETH_TP_MDI_X; + cmd->base.eth_tp_mdix = ETH_TP_MDI_X; else - cmd->eth_tp_mdix = ETH_TP_MDI; + cmd->base.eth_tp_mdix = ETH_TP_MDI; } } else { - ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); - cmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } spin_unlock_bh(&bp->phy_lock); - cmd->transceiver = XCVR_INTERNAL; - cmd->phy_address = bp->phy_addr; + cmd->base.phy_address = bp->phy_addr; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } static int -bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +bnx2_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct bnx2 *bp = netdev_priv(dev); u8 autoneg = bp->autoneg; @@ -6979,24 +6984,26 @@ bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) spin_lock_bh(&bp->phy_lock); - if (cmd->port != PORT_TP && cmd->port != PORT_FIBRE) + if (cmd->base.port != PORT_TP && cmd->base.port != PORT_FIBRE) goto err_out_unlock; - if (cmd->port != bp->phy_port && + if (cmd->base.port != bp->phy_port && !(bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP)) goto err_out_unlock; /* If device is down, we can store the settings only if the user * is setting the currently active port. */ - if (!netif_running(dev) && cmd->port != bp->phy_port) + if (!netif_running(dev) && cmd->base.port != bp->phy_port) goto err_out_unlock; - if (cmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { autoneg |= AUTONEG_SPEED; - advertising = cmd->advertising; - if (cmd->port == PORT_TP) { + ethtool_convert_link_mode_to_legacy_u32( + &advertising, cmd->link_modes.advertising); + + if (cmd->base.port == PORT_TP) { advertising &= ETHTOOL_ALL_COPPER_SPEED; if (!advertising) advertising = ETHTOOL_ALL_COPPER_SPEED; @@ -7008,11 +7015,12 @@ bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) advertising |= ADVERTISED_Autoneg; } else { - u32 speed = ethtool_cmd_speed(cmd); - if (cmd->port == PORT_FIBRE) { + u32 speed = cmd->base.speed; + + if (cmd->base.port == PORT_FIBRE) { if ((speed != SPEED_1000 && speed != SPEED_2500) || - (cmd->duplex != DUPLEX_FULL)) + (cmd->base.duplex != DUPLEX_FULL)) goto err_out_unlock; if (speed == SPEED_2500 && @@ -7023,7 +7031,7 @@ bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) autoneg &= ~AUTONEG_SPEED; req_line_speed = speed; - req_duplex = cmd->duplex; + req_duplex = cmd->base.duplex; advertising = 0; } @@ -7037,7 +7045,7 @@ bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) * brought up. */ if (netif_running(dev)) - err = bnx2_setup_phy(bp, cmd->port); + err = bnx2_setup_phy(bp, cmd->base.port); err_out_unlock: spin_unlock_bh(&bp->phy_lock); @@ -7822,8 +7830,6 @@ static int bnx2_set_channels(struct net_device *dev, } static const struct ethtool_ops bnx2_ethtool_ops = { - .get_settings = bnx2_get_settings, - .set_settings = bnx2_set_settings, .get_drvinfo = bnx2_get_drvinfo, .get_regs_len = bnx2_get_regs_len, .get_regs = bnx2_get_regs, @@ -7847,6 +7853,8 @@ static const struct ethtool_ops bnx2_ethtool_ops = { .get_sset_count = bnx2_get_sset_count, .get_channels = bnx2_get_channels, .set_channels = bnx2_set_channels, + .get_link_ksettings = bnx2_get_link_ksettings, + .set_link_ksettings = bnx2_set_link_ksettings, }; /* Called with rtnl_lock */ -- cgit v1.1 From 0fa1dfd6b92b31eed5446e907049d5b0062b8860 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 18:21:09 -0800 Subject: net: bcm63xx_enet: Fix build failure with phy_ethtool_nway_reset Introduced a typo making the driver no longer build, *sigh*. Fixes: 42469bf5d9bb ("net: bcm63xx_enet: Utilize phy_ethtool_nway_reset") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index a43ab90..3b14d51 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1435,7 +1435,7 @@ static int bcm_enet_nway_reset(struct net_device *dev) priv = netdev_priv(dev); if (priv->has_phy) - return phy_ethtool_nway_reset(dev), + return phy_ethtool_nway_reset(dev); return -EOPNOTSUPP; } -- cgit v1.1 From 4780566784b3968ab9fd6cc94bab72421813f004 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 14 Nov 2016 16:42:01 +0100 Subject: dctcp: update cwnd on congestion event draft-ietf-tcpm-dctcp-02 says: ... when the sender receives an indication of congestion (ECE), the sender SHOULD update cwnd as follows: cwnd = cwnd * (1 - DCTCP.Alpha / 2) So, lets do this and reduce cwnd more smoothly (and faster), as per current congestion estimate. Cc: Lawrence Brakmo Cc: Andrew Shewmaker Cc: Glenn Judd Cc: Daniel Borkmann Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv4/tcp_dctcp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c index ab37c67..5113917 100644 --- a/net/ipv4/tcp_dctcp.c +++ b/net/ipv4/tcp_dctcp.c @@ -188,8 +188,8 @@ static void dctcp_ce_state_1_to_0(struct sock *sk) static void dctcp_update_alpha(struct sock *sk, u32 flags) { - const struct tcp_sock *tp = tcp_sk(sk); struct dctcp *ca = inet_csk_ca(sk); + struct tcp_sock *tp = tcp_sk(sk); u32 acked_bytes = tp->snd_una - ca->prior_snd_una; /* If ack did not advance snd_una, count dupack as MSS size. @@ -229,6 +229,13 @@ static void dctcp_update_alpha(struct sock *sk, u32 flags) WRITE_ONCE(ca->dctcp_alpha, alpha); dctcp_reset(tp, ca); } + + if (flags & CA_ACK_ECE) { + unsigned int cwnd = dctcp_ssthresh(sk); + + if (cwnd != tp->snd_cwnd) + tp->snd_cwnd = cwnd; + } } static void dctcp_state(struct sock *sk, u8 new_state) -- cgit v1.1 From f8be0d78be6e7f199116a5e644ee20ff0ce413f7 Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Mon, 14 Nov 2016 18:58:05 +0100 Subject: net: ethernet: stmmac: change dma descriptors to __le32 The stmmac driver does not take into account the processor may be big endian when writing the DMA descriptors. This causes the ethernet interface not to be initialised correctly when running a big-endian kernel. Change the descriptors for DMA to use __le32 and ensure they are suitably swapped before writing. Tested successfully on the Cubieboard2. Signed-off-by: Michael Weiser Cc: Giuseppe Cavallaro Cc: Alexandre Torgue Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 55 ++++++++++---------- drivers/net/ethernet/stmicro/stmmac/descs.h | 20 ++++---- drivers/net/ethernet/stmicro/stmmac/descs_com.h | 48 +++++++++-------- drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 60 +++++++++++----------- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 55 ++++++++++---------- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 48 ++++++++--------- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 39 +++++++------- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 51 +++++++++--------- 8 files changed, 195 insertions(+), 181 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index b3e669a..026e8e9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -34,7 +34,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) unsigned int entry = priv->cur_tx; struct dma_desc *desc = priv->dma_tx + entry; unsigned int nopaged_len = skb_headlen(skb); - unsigned int bmax; + unsigned int bmax, des2; unsigned int i = 1, len; if (priv->plat->enh_desc) @@ -44,11 +44,12 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) len = nopaged_len - bmax; - desc->des2 = dma_map_single(priv->device, skb->data, - bmax, DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, desc->des2)) + des2 = dma_map_single(priv->device, skb->data, + bmax, DMA_TO_DEVICE); + desc->des2 = cpu_to_le32(des2); + if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = desc->des2; + priv->tx_skbuff_dma[entry].buf = des2; priv->tx_skbuff_dma[entry].len = bmax; /* do not close the descriptor and do not set own bit */ priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE, @@ -60,12 +61,13 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc = priv->dma_tx + entry; if (len > bmax) { - desc->des2 = dma_map_single(priv->device, - (skb->data + bmax * i), - bmax, DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, desc->des2)) + des2 = dma_map_single(priv->device, + (skb->data + bmax * i), + bmax, DMA_TO_DEVICE); + desc->des2 = cpu_to_le32(des2); + if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = desc->des2; + priv->tx_skbuff_dma[entry].buf = des2; priv->tx_skbuff_dma[entry].len = bmax; priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, STMMAC_CHAIN_MODE, 1, @@ -73,12 +75,13 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) len -= bmax; i++; } else { - desc->des2 = dma_map_single(priv->device, - (skb->data + bmax * i), len, - DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, desc->des2)) + des2 = dma_map_single(priv->device, + (skb->data + bmax * i), len, + DMA_TO_DEVICE); + desc->des2 = cpu_to_le32(des2); + if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = desc->des2; + priv->tx_skbuff_dma[entry].buf = des2; priv->tx_skbuff_dma[entry].len = len; /* last descriptor can be set now */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, @@ -119,19 +122,19 @@ static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr, struct dma_extended_desc *p = (struct dma_extended_desc *)des; for (i = 0; i < (size - 1); i++) { dma_phy += sizeof(struct dma_extended_desc); - p->basic.des3 = (unsigned int)dma_phy; + p->basic.des3 = cpu_to_le32((unsigned int)dma_phy); p++; } - p->basic.des3 = (unsigned int)phy_addr; + p->basic.des3 = cpu_to_le32((unsigned int)phy_addr); } else { struct dma_desc *p = (struct dma_desc *)des; for (i = 0; i < (size - 1); i++) { dma_phy += sizeof(struct dma_desc); - p->des3 = (unsigned int)dma_phy; + p->des3 = cpu_to_le32((unsigned int)dma_phy); p++; } - p->des3 = (unsigned int)phy_addr; + p->des3 = cpu_to_le32((unsigned int)phy_addr); } } @@ -144,10 +147,10 @@ static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = (unsigned int)(priv->dma_rx_phy + - (((priv->dirty_rx) + 1) % - DMA_RX_SIZE) * - sizeof(struct dma_desc)); + p->des3 = cpu_to_le32((unsigned int)(priv->dma_rx_phy + + (((priv->dirty_rx) + 1) % + DMA_RX_SIZE) * + sizeof(struct dma_desc))); } static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) @@ -161,9 +164,9 @@ static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = (unsigned int)((priv->dma_tx_phy + - ((priv->dirty_tx + 1) % DMA_TX_SIZE)) - * sizeof(struct dma_desc)); + p->des3 = cpu_to_le32((unsigned int)((priv->dma_tx_phy + + ((priv->dirty_tx + 1) % DMA_TX_SIZE)) + * sizeof(struct dma_desc))); } const struct stmmac_mode_ops chain_mode_ops = { diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h index 2e4c171..4000af4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs.h @@ -87,7 +87,7 @@ #define TDES0_ERROR_SUMMARY BIT(15) #define TDES0_IP_HEADER_ERROR BIT(16) #define TDES0_TIME_STAMP_STATUS BIT(17) -#define TDES0_OWN BIT(31) +#define TDES0_OWN ((u32)BIT(31)) /* silence sparse */ /* TDES1 */ #define TDES1_BUFFER1_SIZE_MASK GENMASK(10, 0) #define TDES1_BUFFER2_SIZE_MASK GENMASK(21, 11) @@ -130,7 +130,7 @@ #define ETDES0_FIRST_SEGMENT BIT(28) #define ETDES0_LAST_SEGMENT BIT(29) #define ETDES0_INTERRUPT BIT(30) -#define ETDES0_OWN BIT(31) +#define ETDES0_OWN ((u32)BIT(31)) /* silence sparse */ /* TDES1 */ #define ETDES1_BUFFER1_SIZE_MASK GENMASK(12, 0) #define ETDES1_BUFFER2_SIZE_MASK GENMASK(28, 16) @@ -166,19 +166,19 @@ /* Basic descriptor structure for normal and alternate descriptors */ struct dma_desc { - unsigned int des0; - unsigned int des1; - unsigned int des2; - unsigned int des3; + __le32 des0; + __le32 des1; + __le32 des2; + __le32 des3; }; /* Extended descriptor structure (e.g. >= databook 3.50a) */ struct dma_extended_desc { struct dma_desc basic; /* Basic descriptors */ - unsigned int des4; /* Extended Status */ - unsigned int des5; /* Reserved */ - unsigned int des6; /* Tx/Rx Timestamp Low */ - unsigned int des7; /* Tx/Rx Timestamp High */ + __le32 des4; /* Extended Status */ + __le32 des5; /* Reserved */ + __le32 des6; /* Tx/Rx Timestamp Low */ + __le32 des7; /* Tx/Rx Timestamp High */ }; /* Transmit checksum insertion control */ diff --git a/drivers/net/ethernet/stmicro/stmmac/descs_com.h b/drivers/net/ethernet/stmicro/stmmac/descs_com.h index 7635a46..1d181e2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs_com.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs_com.h @@ -35,47 +35,50 @@ /* Enhanced descriptors */ static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end) { - p->des1 |= ((BUF_SIZE_8KiB - 1) << ERDES1_BUFFER2_SIZE_SHIFT) - & ERDES1_BUFFER2_SIZE_MASK; + p->des1 |= cpu_to_le32(((BUF_SIZE_8KiB - 1) + << ERDES1_BUFFER2_SIZE_SHIFT) + & ERDES1_BUFFER2_SIZE_MASK); if (end) - p->des1 |= ERDES1_END_RING; + p->des1 |= cpu_to_le32(ERDES1_END_RING); } static inline void enh_desc_end_tx_desc_on_ring(struct dma_desc *p, int end) { if (end) - p->des0 |= ETDES0_END_RING; + p->des0 |= cpu_to_le32(ETDES0_END_RING); else - p->des0 &= ~ETDES0_END_RING; + p->des0 &= cpu_to_le32(~ETDES0_END_RING); } static inline void enh_set_tx_desc_len_on_ring(struct dma_desc *p, int len) { if (unlikely(len > BUF_SIZE_4KiB)) { - p->des1 |= (((len - BUF_SIZE_4KiB) << ETDES1_BUFFER2_SIZE_SHIFT) + p->des1 |= cpu_to_le32((((len - BUF_SIZE_4KiB) + << ETDES1_BUFFER2_SIZE_SHIFT) & ETDES1_BUFFER2_SIZE_MASK) | (BUF_SIZE_4KiB - & ETDES1_BUFFER1_SIZE_MASK); + & ETDES1_BUFFER1_SIZE_MASK)); } else - p->des1 |= (len & ETDES1_BUFFER1_SIZE_MASK); + p->des1 |= cpu_to_le32((len & ETDES1_BUFFER1_SIZE_MASK)); } /* Normal descriptors */ static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end) { - p->des1 |= ((BUF_SIZE_2KiB - 1) << RDES1_BUFFER2_SIZE_SHIFT) - & RDES1_BUFFER2_SIZE_MASK; + p->des1 |= cpu_to_le32(((BUF_SIZE_2KiB - 1) + << RDES1_BUFFER2_SIZE_SHIFT) + & RDES1_BUFFER2_SIZE_MASK); if (end) - p->des1 |= RDES1_END_RING; + p->des1 |= cpu_to_le32(RDES1_END_RING); } static inline void ndesc_end_tx_desc_on_ring(struct dma_desc *p, int end) { if (end) - p->des1 |= TDES1_END_RING; + p->des1 |= cpu_to_le32(TDES1_END_RING); else - p->des1 &= ~TDES1_END_RING; + p->des1 &= cpu_to_le32(~TDES1_END_RING); } static inline void norm_set_tx_desc_len_on_ring(struct dma_desc *p, int len) @@ -83,10 +86,11 @@ static inline void norm_set_tx_desc_len_on_ring(struct dma_desc *p, int len) if (unlikely(len > BUF_SIZE_2KiB)) { unsigned int buffer1 = (BUF_SIZE_2KiB - 1) & TDES1_BUFFER1_SIZE_MASK; - p->des1 |= ((((len - buffer1) << TDES1_BUFFER2_SIZE_SHIFT) - & TDES1_BUFFER2_SIZE_MASK) | buffer1); + p->des1 |= cpu_to_le32((((len - buffer1) + << TDES1_BUFFER2_SIZE_SHIFT) + & TDES1_BUFFER2_SIZE_MASK) | buffer1); } else - p->des1 |= (len & TDES1_BUFFER1_SIZE_MASK); + p->des1 |= cpu_to_le32((len & TDES1_BUFFER1_SIZE_MASK)); } /* Specific functions used for Chain mode */ @@ -94,32 +98,32 @@ static inline void norm_set_tx_desc_len_on_ring(struct dma_desc *p, int len) /* Enhanced descriptors */ static inline void ehn_desc_rx_set_on_chain(struct dma_desc *p) { - p->des1 |= ERDES1_SECOND_ADDRESS_CHAINED; + p->des1 |= cpu_to_le32(ERDES1_SECOND_ADDRESS_CHAINED); } static inline void enh_desc_end_tx_desc_on_chain(struct dma_desc *p) { - p->des0 |= ETDES0_SECOND_ADDRESS_CHAINED; + p->des0 |= cpu_to_le32(ETDES0_SECOND_ADDRESS_CHAINED); } static inline void enh_set_tx_desc_len_on_chain(struct dma_desc *p, int len) { - p->des1 |= (len & ETDES1_BUFFER1_SIZE_MASK); + p->des1 |= cpu_to_le32(len & ETDES1_BUFFER1_SIZE_MASK); } /* Normal descriptors */ static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int end) { - p->des1 |= RDES1_SECOND_ADDRESS_CHAINED; + p->des1 |= cpu_to_le32(RDES1_SECOND_ADDRESS_CHAINED); } static inline void ndesc_tx_set_on_chain(struct dma_desc *p) { - p->des1 |= TDES1_SECOND_ADDRESS_CHAINED; + p->des1 |= cpu_to_le32(TDES1_SECOND_ADDRESS_CHAINED); } static inline void norm_set_tx_desc_len_on_chain(struct dma_desc *p, int len) { - p->des1 |= len & TDES1_BUFFER1_SIZE_MASK; + p->des1 |= cpu_to_le32(len & TDES1_BUFFER1_SIZE_MASK); } #endif /* __DESC_COM_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c index a1b17cd..bec72d3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c @@ -23,7 +23,7 @@ static int dwmac4_wrback_get_tx_status(void *data, struct stmmac_extra_stats *x, unsigned int tdes3; int ret = tx_done; - tdes3 = p->des3; + tdes3 = le32_to_cpu(p->des3); /* Get tx owner first */ if (unlikely(tdes3 & TDES3_OWN)) @@ -77,9 +77,9 @@ static int dwmac4_wrback_get_rx_status(void *data, struct stmmac_extra_stats *x, struct dma_desc *p) { struct net_device_stats *stats = (struct net_device_stats *)data; - unsigned int rdes1 = p->des1; - unsigned int rdes2 = p->des2; - unsigned int rdes3 = p->des3; + unsigned int rdes1 = le32_to_cpu(p->des1); + unsigned int rdes2 = le32_to_cpu(p->des2); + unsigned int rdes3 = le32_to_cpu(p->des3); int message_type; int ret = good_frame; @@ -169,42 +169,43 @@ static int dwmac4_wrback_get_rx_status(void *data, struct stmmac_extra_stats *x, static int dwmac4_rd_get_tx_len(struct dma_desc *p) { - return (p->des2 & TDES2_BUFFER1_SIZE_MASK); + return (le32_to_cpu(p->des2) & TDES2_BUFFER1_SIZE_MASK); } static int dwmac4_get_tx_owner(struct dma_desc *p) { - return (p->des3 & TDES3_OWN) >> TDES3_OWN_SHIFT; + return (le32_to_cpu(p->des3) & TDES3_OWN) >> TDES3_OWN_SHIFT; } static void dwmac4_set_tx_owner(struct dma_desc *p) { - p->des3 |= TDES3_OWN; + p->des3 |= cpu_to_le32(TDES3_OWN); } static void dwmac4_set_rx_owner(struct dma_desc *p) { - p->des3 |= RDES3_OWN; + p->des3 |= cpu_to_le32(RDES3_OWN); } static int dwmac4_get_tx_ls(struct dma_desc *p) { - return (p->des3 & TDES3_LAST_DESCRIPTOR) >> TDES3_LAST_DESCRIPTOR_SHIFT; + return (le32_to_cpu(p->des3) & TDES3_LAST_DESCRIPTOR) + >> TDES3_LAST_DESCRIPTOR_SHIFT; } static int dwmac4_wrback_get_rx_frame_len(struct dma_desc *p, int rx_coe) { - return (p->des3 & RDES3_PACKET_SIZE_MASK); + return (le32_to_cpu(p->des3) & RDES3_PACKET_SIZE_MASK); } static void dwmac4_rd_enable_tx_timestamp(struct dma_desc *p) { - p->des2 |= TDES2_TIMESTAMP_ENABLE; + p->des2 |= cpu_to_le32(TDES2_TIMESTAMP_ENABLE); } static int dwmac4_wrback_get_tx_timestamp_status(struct dma_desc *p) { - return (p->des3 & TDES3_TIMESTAMP_STATUS) + return (le32_to_cpu(p->des3) & TDES3_TIMESTAMP_STATUS) >> TDES3_TIMESTAMP_STATUS_SHIFT; } @@ -216,9 +217,9 @@ static u64 dwmac4_wrback_get_timestamp(void *desc, u32 ats) struct dma_desc *p = (struct dma_desc *)desc; u64 ns; - ns = p->des0; + ns = le32_to_cpu(p->des0); /* convert high/sec time stamp value to nanosecond */ - ns += p->des1 * 1000000000ULL; + ns += le32_to_cpu(p->des1) * 1000000000ULL; return ns; } @@ -227,17 +228,17 @@ static int dwmac4_context_get_rx_timestamp_status(void *desc, u32 ats) { struct dma_desc *p = (struct dma_desc *)desc; - return (p->des1 & RDES1_TIMESTAMP_AVAILABLE) + return (le32_to_cpu(p->des1) & RDES1_TIMESTAMP_AVAILABLE) >> RDES1_TIMESTAMP_AVAILABLE_SHIFT; } static void dwmac4_rd_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, int end) { - p->des3 = RDES3_OWN | RDES3_BUFFER1_VALID_ADDR; + p->des3 = cpu_to_le32(RDES3_OWN | RDES3_BUFFER1_VALID_ADDR); if (!disable_rx_ic) - p->des3 |= RDES3_INT_ON_COMPLETION_EN; + p->des3 |= cpu_to_le32(RDES3_INT_ON_COMPLETION_EN); } static void dwmac4_rd_init_tx_desc(struct dma_desc *p, int mode, int end) @@ -252,9 +253,9 @@ static void dwmac4_rd_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, bool ls) { - unsigned int tdes3 = p->des3; + unsigned int tdes3 = le32_to_cpu(p->des3); - p->des2 |= (len & TDES2_BUFFER1_SIZE_MASK); + p->des2 |= cpu_to_le32(len & TDES2_BUFFER1_SIZE_MASK); if (is_fs) tdes3 |= TDES3_FIRST_DESCRIPTOR; @@ -282,7 +283,7 @@ static void dwmac4_rd_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, */ wmb(); - p->des3 = tdes3; + p->des3 = cpu_to_le32(tdes3); } static void dwmac4_rd_prepare_tso_tx_desc(struct dma_desc *p, int is_fs, @@ -290,14 +291,14 @@ static void dwmac4_rd_prepare_tso_tx_desc(struct dma_desc *p, int is_fs, bool ls, unsigned int tcphdrlen, unsigned int tcppayloadlen) { - unsigned int tdes3 = p->des3; + unsigned int tdes3 = le32_to_cpu(p->des3); if (len1) - p->des2 |= (len1 & TDES2_BUFFER1_SIZE_MASK); + p->des2 |= cpu_to_le32((len1 & TDES2_BUFFER1_SIZE_MASK)); if (len2) - p->des2 |= (len2 << TDES2_BUFFER2_SIZE_MASK_SHIFT) - & TDES2_BUFFER2_SIZE_MASK; + p->des2 |= cpu_to_le32((len2 << TDES2_BUFFER2_SIZE_MASK_SHIFT) + & TDES2_BUFFER2_SIZE_MASK); if (is_fs) { tdes3 |= TDES3_FIRST_DESCRIPTOR | @@ -325,7 +326,7 @@ static void dwmac4_rd_prepare_tso_tx_desc(struct dma_desc *p, int is_fs, */ wmb(); - p->des3 = tdes3; + p->des3 = cpu_to_le32(tdes3); } static void dwmac4_release_tx_desc(struct dma_desc *p, int mode) @@ -336,7 +337,7 @@ static void dwmac4_release_tx_desc(struct dma_desc *p, int mode) static void dwmac4_rd_set_tx_ic(struct dma_desc *p) { - p->des2 |= TDES2_INTERRUPT_ON_COMPLETION; + p->des2 |= cpu_to_le32(TDES2_INTERRUPT_ON_COMPLETION); } static void dwmac4_display_ring(void *head, unsigned int size, bool rx) @@ -349,7 +350,8 @@ static void dwmac4_display_ring(void *head, unsigned int size, bool rx) for (i = 0; i < size; i++) { pr_info("%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n", i, (unsigned int)virt_to_phys(p), - p->des0, p->des1, p->des2, p->des3); + le32_to_cpu(p->des0), le32_to_cpu(p->des1), + le32_to_cpu(p->des2), le32_to_cpu(p->des3)); p++; } } @@ -358,8 +360,8 @@ static void dwmac4_set_mss_ctxt(struct dma_desc *p, unsigned int mss) { p->des0 = 0; p->des1 = 0; - p->des2 = mss; - p->des3 = TDES3_CONTEXT_TYPE | TDES3_CTXT_TCMSSV; + p->des2 = cpu_to_le32(mss); + p->des3 = cpu_to_le32(TDES3_CONTEXT_TYPE | TDES3_CTXT_TCMSSV); } const struct stmmac_desc_ops dwmac4_desc_ops = { diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 38f19c9..8295aa9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -30,7 +30,7 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, struct dma_desc *p, void __iomem *ioaddr) { struct net_device_stats *stats = (struct net_device_stats *)data; - unsigned int tdes0 = p->des0; + unsigned int tdes0 = le32_to_cpu(p->des0); int ret = tx_done; /* Get tx owner first */ @@ -95,7 +95,7 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, static int enh_desc_get_tx_len(struct dma_desc *p) { - return (p->des1 & ETDES1_BUFFER1_SIZE_MASK); + return (le32_to_cpu(p->des1) & ETDES1_BUFFER1_SIZE_MASK); } static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) @@ -134,8 +134,8 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) static void enh_desc_get_ext_status(void *data, struct stmmac_extra_stats *x, struct dma_extended_desc *p) { - unsigned int rdes0 = p->basic.des0; - unsigned int rdes4 = p->des4; + unsigned int rdes0 = le32_to_cpu(p->basic.des0); + unsigned int rdes4 = le32_to_cpu(p->des4); if (unlikely(rdes0 & ERDES0_RX_MAC_ADDR)) { int message_type = (rdes4 & ERDES4_MSG_TYPE_MASK) >> 8; @@ -191,7 +191,7 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, struct dma_desc *p) { struct net_device_stats *stats = (struct net_device_stats *)data; - unsigned int rdes0 = p->des0; + unsigned int rdes0 = le32_to_cpu(p->des0); int ret = good_frame; if (unlikely(rdes0 & RDES0_OWN)) @@ -257,8 +257,8 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, int end) { - p->des0 |= RDES0_OWN; - p->des1 |= ((BUF_SIZE_8KiB - 1) & ERDES1_BUFFER1_SIZE_MASK); + p->des0 |= cpu_to_le32(RDES0_OWN); + p->des1 |= cpu_to_le32((BUF_SIZE_8KiB - 1) & ERDES1_BUFFER1_SIZE_MASK); if (mode == STMMAC_CHAIN_MODE) ehn_desc_rx_set_on_chain(p); @@ -266,12 +266,12 @@ static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, ehn_desc_rx_set_on_ring(p, end); if (disable_rx_ic) - p->des1 |= ERDES1_DISABLE_IC; + p->des1 |= cpu_to_le32(ERDES1_DISABLE_IC); } static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end) { - p->des0 &= ~ETDES0_OWN; + p->des0 &= cpu_to_le32(~ETDES0_OWN); if (mode == STMMAC_CHAIN_MODE) enh_desc_end_tx_desc_on_chain(p); else @@ -280,27 +280,27 @@ static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end) static int enh_desc_get_tx_owner(struct dma_desc *p) { - return (p->des0 & ETDES0_OWN) >> 31; + return (le32_to_cpu(p->des0) & ETDES0_OWN) >> 31; } static void enh_desc_set_tx_owner(struct dma_desc *p) { - p->des0 |= ETDES0_OWN; + p->des0 |= cpu_to_le32(ETDES0_OWN); } static void enh_desc_set_rx_owner(struct dma_desc *p) { - p->des0 |= RDES0_OWN; + p->des0 |= cpu_to_le32(RDES0_OWN); } static int enh_desc_get_tx_ls(struct dma_desc *p) { - return (p->des0 & ETDES0_LAST_SEGMENT) >> 29; + return (le32_to_cpu(p->des0) & ETDES0_LAST_SEGMENT) >> 29; } static void enh_desc_release_tx_desc(struct dma_desc *p, int mode) { - int ter = (p->des0 & ETDES0_END_RING) >> 21; + int ter = (le32_to_cpu(p->des0) & ETDES0_END_RING) >> 21; memset(p, 0, offsetof(struct dma_desc, des2)); if (mode == STMMAC_CHAIN_MODE) @@ -313,7 +313,7 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, bool ls) { - unsigned int tdes0 = p->des0; + unsigned int tdes0 = le32_to_cpu(p->des0); if (mode == STMMAC_CHAIN_MODE) enh_set_tx_desc_len_on_chain(p, len); @@ -344,12 +344,12 @@ static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, */ wmb(); - p->des0 = tdes0; + p->des0 = cpu_to_le32(tdes0); } static void enh_desc_set_tx_ic(struct dma_desc *p) { - p->des0 |= ETDES0_INTERRUPT; + p->des0 |= cpu_to_le32(ETDES0_INTERRUPT); } static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) @@ -364,18 +364,18 @@ static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) if (rx_coe_type == STMMAC_RX_COE_TYPE1) csum = 2; - return (((p->des0 & RDES0_FRAME_LEN_MASK) >> RDES0_FRAME_LEN_SHIFT) - - csum); + return (((le32_to_cpu(p->des0) & RDES0_FRAME_LEN_MASK) + >> RDES0_FRAME_LEN_SHIFT) - csum); } static void enh_desc_enable_tx_timestamp(struct dma_desc *p) { - p->des0 |= ETDES0_TIME_STAMP_ENABLE; + p->des0 |= cpu_to_le32(ETDES0_TIME_STAMP_ENABLE); } static int enh_desc_get_tx_timestamp_status(struct dma_desc *p) { - return (p->des0 & ETDES0_TIME_STAMP_STATUS) >> 17; + return (le32_to_cpu(p->des0) & ETDES0_TIME_STAMP_STATUS) >> 17; } static u64 enh_desc_get_timestamp(void *desc, u32 ats) @@ -384,13 +384,13 @@ static u64 enh_desc_get_timestamp(void *desc, u32 ats) if (ats) { struct dma_extended_desc *p = (struct dma_extended_desc *)desc; - ns = p->des6; + ns = le32_to_cpu(p->des6); /* convert high/sec time stamp value to nanosecond */ - ns += p->des7 * 1000000000ULL; + ns += le32_to_cpu(p->des7) * 1000000000ULL; } else { struct dma_desc *p = (struct dma_desc *)desc; - ns = p->des2; - ns += p->des3 * 1000000000ULL; + ns = le32_to_cpu(p->des2); + ns += le32_to_cpu(p->des3) * 1000000000ULL; } return ns; @@ -400,10 +400,11 @@ static int enh_desc_get_rx_timestamp_status(void *desc, u32 ats) { if (ats) { struct dma_extended_desc *p = (struct dma_extended_desc *)desc; - return (p->basic.des0 & RDES0_IPC_CSUM_ERROR) >> 7; + return (le32_to_cpu(p->basic.des0) & RDES0_IPC_CSUM_ERROR) >> 7; } else { struct dma_desc *p = (struct dma_desc *)desc; - if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) + if ((le32_to_cpu(p->des2) == 0xffffffff) && + (le32_to_cpu(p->des3) == 0xffffffff)) /* timestamp is corrupted, hence don't store it */ return 0; else diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 2beacd0..fd78406 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -30,8 +30,8 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x, struct dma_desc *p, void __iomem *ioaddr) { struct net_device_stats *stats = (struct net_device_stats *)data; - unsigned int tdes0 = p->des0; - unsigned int tdes1 = p->des1; + unsigned int tdes0 = le32_to_cpu(p->des0); + unsigned int tdes1 = le32_to_cpu(p->des1); int ret = tx_done; /* Get tx owner first */ @@ -77,7 +77,7 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x, static int ndesc_get_tx_len(struct dma_desc *p) { - return (p->des1 & RDES1_BUFFER1_SIZE_MASK); + return (le32_to_cpu(p->des1) & RDES1_BUFFER1_SIZE_MASK); } /* This function verifies if each incoming frame has some errors @@ -88,7 +88,7 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x, struct dma_desc *p) { int ret = good_frame; - unsigned int rdes0 = p->des0; + unsigned int rdes0 = le32_to_cpu(p->des0); struct net_device_stats *stats = (struct net_device_stats *)data; if (unlikely(rdes0 & RDES0_OWN)) @@ -141,8 +141,8 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x, static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, int end) { - p->des0 |= RDES0_OWN; - p->des1 |= (BUF_SIZE_2KiB - 1) & RDES1_BUFFER1_SIZE_MASK; + p->des0 |= cpu_to_le32(RDES0_OWN); + p->des1 |= cpu_to_le32((BUF_SIZE_2KiB - 1) & RDES1_BUFFER1_SIZE_MASK); if (mode == STMMAC_CHAIN_MODE) ndesc_rx_set_on_chain(p, end); @@ -150,12 +150,12 @@ static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, ndesc_rx_set_on_ring(p, end); if (disable_rx_ic) - p->des1 |= RDES1_DISABLE_IC; + p->des1 |= cpu_to_le32(RDES1_DISABLE_IC); } static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end) { - p->des0 &= ~TDES0_OWN; + p->des0 &= cpu_to_le32(~TDES0_OWN); if (mode == STMMAC_CHAIN_MODE) ndesc_tx_set_on_chain(p); else @@ -164,27 +164,27 @@ static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end) static int ndesc_get_tx_owner(struct dma_desc *p) { - return (p->des0 & TDES0_OWN) >> 31; + return (le32_to_cpu(p->des0) & TDES0_OWN) >> 31; } static void ndesc_set_tx_owner(struct dma_desc *p) { - p->des0 |= TDES0_OWN; + p->des0 |= cpu_to_le32(TDES0_OWN); } static void ndesc_set_rx_owner(struct dma_desc *p) { - p->des0 |= RDES0_OWN; + p->des0 |= cpu_to_le32(RDES0_OWN); } static int ndesc_get_tx_ls(struct dma_desc *p) { - return (p->des1 & TDES1_LAST_SEGMENT) >> 30; + return (le32_to_cpu(p->des1) & TDES1_LAST_SEGMENT) >> 30; } static void ndesc_release_tx_desc(struct dma_desc *p, int mode) { - int ter = (p->des1 & TDES1_END_RING) >> 25; + int ter = (le32_to_cpu(p->des1) & TDES1_END_RING) >> 25; memset(p, 0, offsetof(struct dma_desc, des2)); if (mode == STMMAC_CHAIN_MODE) @@ -197,7 +197,7 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, bool ls) { - unsigned int tdes1 = p->des1; + unsigned int tdes1 = le32_to_cpu(p->des1); if (is_fs) tdes1 |= TDES1_FIRST_SEGMENT; @@ -212,7 +212,7 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, if (ls) tdes1 |= TDES1_LAST_SEGMENT; - p->des1 = tdes1; + p->des1 = cpu_to_le32(tdes1); if (mode == STMMAC_CHAIN_MODE) norm_set_tx_desc_len_on_chain(p, len); @@ -220,12 +220,12 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, norm_set_tx_desc_len_on_ring(p, len); if (tx_own) - p->des0 |= TDES0_OWN; + p->des0 |= cpu_to_le32(TDES0_OWN); } static void ndesc_set_tx_ic(struct dma_desc *p) { - p->des1 |= TDES1_INTERRUPT; + p->des1 |= cpu_to_le32(TDES1_INTERRUPT); } static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) @@ -241,19 +241,20 @@ static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) if (rx_coe_type == STMMAC_RX_COE_TYPE1) csum = 2; - return (((p->des0 & RDES0_FRAME_LEN_MASK) >> RDES0_FRAME_LEN_SHIFT) - + return (((le32_to_cpu(p->des0) & RDES0_FRAME_LEN_MASK) + >> RDES0_FRAME_LEN_SHIFT) - csum); } static void ndesc_enable_tx_timestamp(struct dma_desc *p) { - p->des1 |= TDES1_TIME_STAMP_ENABLE; + p->des1 |= cpu_to_le32(TDES1_TIME_STAMP_ENABLE); } static int ndesc_get_tx_timestamp_status(struct dma_desc *p) { - return (p->des0 & TDES0_TIME_STAMP_STATUS) >> 17; + return (le32_to_cpu(p->des0) & TDES0_TIME_STAMP_STATUS) >> 17; } static u64 ndesc_get_timestamp(void *desc, u32 ats) @@ -261,9 +262,9 @@ static u64 ndesc_get_timestamp(void *desc, u32 ats) struct dma_desc *p = (struct dma_desc *)desc; u64 ns; - ns = p->des2; + ns = le32_to_cpu(p->des2); /* convert high/sec time stamp value to nanosecond */ - ns += p->des3 * 1000000000ULL; + ns += le32_to_cpu(p->des3) * 1000000000ULL; return ns; } @@ -272,7 +273,8 @@ static int ndesc_get_rx_timestamp_status(void *desc, u32 ats) { struct dma_desc *p = (struct dma_desc *)desc; - if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) + if ((le32_to_cpu(p->des2) == 0xffffffff) && + (le32_to_cpu(p->des3) == 0xffffffff)) /* timestamp is corrupted, hence don't store it */ return 0; else diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index 7723b5d..9983ce9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -34,7 +34,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) unsigned int entry = priv->cur_tx; struct dma_desc *desc; unsigned int nopaged_len = skb_headlen(skb); - unsigned int bmax, len; + unsigned int bmax, len, des2; if (priv->extend_desc) desc = (struct dma_desc *)(priv->dma_etx + entry); @@ -50,16 +50,17 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) if (nopaged_len > BUF_SIZE_8KiB) { - desc->des2 = dma_map_single(priv->device, skb->data, - bmax, DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, desc->des2)) + des2 = dma_map_single(priv->device, skb->data, bmax, + DMA_TO_DEVICE); + desc->des2 = cpu_to_le32(des2); + if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = desc->des2; + priv->tx_skbuff_dma[entry].buf = des2; priv->tx_skbuff_dma[entry].len = bmax; priv->tx_skbuff_dma[entry].is_jumbo = true; - desc->des3 = desc->des2 + BUF_SIZE_4KiB; + desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_RING_MODE, 0, false); priv->tx_skbuff[entry] = NULL; @@ -70,26 +71,28 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) else desc = priv->dma_tx + entry; - desc->des2 = dma_map_single(priv->device, skb->data + bmax, - len, DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, desc->des2)) + des2 = dma_map_single(priv->device, skb->data + bmax, len, + DMA_TO_DEVICE); + desc->des2 = cpu_to_le32(des2); + if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = desc->des2; + priv->tx_skbuff_dma[entry].buf = des2; priv->tx_skbuff_dma[entry].len = len; priv->tx_skbuff_dma[entry].is_jumbo = true; - desc->des3 = desc->des2 + BUF_SIZE_4KiB; + desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, STMMAC_RING_MODE, 1, true); } else { - desc->des2 = dma_map_single(priv->device, skb->data, - nopaged_len, DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, desc->des2)) + des2 = dma_map_single(priv->device, skb->data, + nopaged_len, DMA_TO_DEVICE); + desc->des2 = cpu_to_le32(des2); + if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = desc->des2; + priv->tx_skbuff_dma[entry].buf = des2; priv->tx_skbuff_dma[entry].len = nopaged_len; priv->tx_skbuff_dma[entry].is_jumbo = true; - desc->des3 = desc->des2 + BUF_SIZE_4KiB; + desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum, STMMAC_RING_MODE, 0, true); } @@ -115,13 +118,13 @@ static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) /* Fill DES3 in case of RING mode */ if (priv->dma_buf_sz >= BUF_SIZE_8KiB) - p->des3 = p->des2 + BUF_SIZE_8KiB; + p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB); } /* In ring mode we need to fill the desc3 because it is used as buffer */ static void stmmac_init_desc3(struct dma_desc *p) { - p->des3 = p->des2 + BUF_SIZE_8KiB; + p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB); } static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 03dbf8e..8eb12353 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -990,9 +990,9 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, } if (priv->synopsys_id >= DWMAC_CORE_4_00) - p->des0 = priv->rx_skbuff_dma[i]; + p->des0 = cpu_to_le32(priv->rx_skbuff_dma[i]); else - p->des2 = priv->rx_skbuff_dma[i]; + p->des2 = cpu_to_le32(priv->rx_skbuff_dma[i]); if ((priv->hw->mode->init_desc3) && (priv->dma_buf_sz == BUF_SIZE_16KiB)) @@ -1946,7 +1946,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des, priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); desc = priv->dma_tx + priv->cur_tx; - desc->des0 = des + (total_len - tmp_len); + desc->des0 = cpu_to_le32(des + (total_len - tmp_len)); buff_size = tmp_len >= TSO_MAX_BUFF_SIZE ? TSO_MAX_BUFF_SIZE : tmp_len; @@ -2048,11 +2048,11 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) priv->tx_skbuff_dma[first_entry].len = skb_headlen(skb); priv->tx_skbuff[first_entry] = skb; - first->des0 = des; + first->des0 = cpu_to_le32(des); /* Fill start of payload in buff2 of first descriptor */ if (pay_len) - first->des1 = des + proto_hdr_len; + first->des1 = cpu_to_le32(des + proto_hdr_len); /* If needed take extra descriptors to fill the remaining payload */ tmp_pay_len = pay_len - TSO_MAX_BUFF_SIZE; @@ -2241,13 +2241,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) priv->tx_skbuff[entry] = NULL; - if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) { - desc->des0 = des; - priv->tx_skbuff_dma[entry].buf = desc->des0; - } else { - desc->des2 = des; - priv->tx_skbuff_dma[entry].buf = desc->des2; - } + priv->tx_skbuff_dma[entry].buf = des; + if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) + desc->des0 = cpu_to_le32(des); + else + desc->des2 = cpu_to_le32(des); priv->tx_skbuff_dma[entry].map_as_page = true; priv->tx_skbuff_dma[entry].len = len; @@ -2318,13 +2316,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) { - first->des0 = des; - priv->tx_skbuff_dma[first_entry].buf = first->des0; - } else { - first->des2 = des; - priv->tx_skbuff_dma[first_entry].buf = first->des2; - } + priv->tx_skbuff_dma[first_entry].buf = des; + if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) + first->des0 = cpu_to_le32(des); + else + first->des2 = cpu_to_le32(des); priv->tx_skbuff_dma[first_entry].len = nopaged_len; priv->tx_skbuff_dma[first_entry].last_segment = last_segment; @@ -2438,10 +2434,10 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) } if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) { - p->des0 = priv->rx_skbuff_dma[entry]; + p->des0 = cpu_to_le32(priv->rx_skbuff_dma[entry]); p->des1 = 0; } else { - p->des2 = priv->rx_skbuff_dma[entry]; + p->des2 = cpu_to_le32(priv->rx_skbuff_dma[entry]); } if (priv->hw->mode->refill_desc3) priv->hw->mode->refill_desc3(priv, p); @@ -2542,9 +2538,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) unsigned int des; if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) - des = p->des0; + des = le32_to_cpu(p->des0); else - des = p->des2; + des = le32_to_cpu(p->des2); frame_len = priv->hw->desc->get_rx_frame_len(p, coe); @@ -2901,14 +2897,17 @@ static void sysfs_display_ring(void *head, int size, int extend_desc, x = *(u64 *) ep; seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n", i, (unsigned int)virt_to_phys(ep), - ep->basic.des0, ep->basic.des1, - ep->basic.des2, ep->basic.des3); + le32_to_cpu(ep->basic.des0), + le32_to_cpu(ep->basic.des1), + le32_to_cpu(ep->basic.des2), + le32_to_cpu(ep->basic.des3)); ep++; } else { x = *(u64 *) p; seq_printf(seq, "%d [0x%x]: 0x%x 0x%x 0x%x 0x%x\n", i, (unsigned int)virt_to_phys(ep), - p->des0, p->des1, p->des2, p->des3); + le32_to_cpu(p->des0), le32_to_cpu(p->des1), + le32_to_cpu(p->des2), le32_to_cpu(p->des3)); p++; } seq_printf(seq, "\n"); -- cgit v1.1 From b8ca3387fe94ad5a7142f0b3de5aedf7389234c3 Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Mon, 14 Nov 2016 18:58:06 +0100 Subject: net: ethernet: sun4i-emac: Allow to enable netif messages sun4i-emac has the ability to print a number of diagnostic messages using dev_dbg depending on message level settings implemented using netif_msg_* macros. But there's no way to actually enable them. Add the ability to switch diagnostic messages on using either a module parameter debug or ethtool -s msglvl . Signed-off-by: Michael Weiser Cc: Maxime Ripard Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- drivers/net/ethernet/allwinner/sun4i-emac.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index af27f9d..7401219 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -37,6 +37,11 @@ #define EMAC_MAX_FRAME_LEN 0x0600 +#define EMAC_DEFAULT_MSG_ENABLE 0x0000 +static int debug = -1; /* defaults above */; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "debug message flags"); + /* Transmit timeout, default 5 seconds. */ static int watchdog = 5000; module_param(watchdog, int, 0400); @@ -225,11 +230,27 @@ static void emac_get_drvinfo(struct net_device *dev, strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); } +static u32 emac_get_msglevel(struct net_device *dev) +{ + struct emac_board_info *db = netdev_priv(dev); + + return db->msg_enable; +} + +static void emac_set_msglevel(struct net_device *dev, u32 value) +{ + struct emac_board_info *db = netdev_priv(dev); + + db->msg_enable = value; +} + static const struct ethtool_ops emac_ethtool_ops = { .get_drvinfo = emac_get_drvinfo, .get_link = ethtool_op_get_link, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_msglevel = emac_get_msglevel, + .set_msglevel = emac_set_msglevel, }; static unsigned int emac_setup(struct net_device *ndev) @@ -804,6 +825,7 @@ static int emac_probe(struct platform_device *pdev) db->dev = &pdev->dev; db->ndev = ndev; db->pdev = pdev; + db->msg_enable = netif_msg_init(debug, EMAC_DEFAULT_MSG_ENABLE); spin_lock_init(&db->lock); -- cgit v1.1 From 934d0048d07d14916b5c6f48709470d96f98d9c3 Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Mon, 14 Nov 2016 18:58:07 +0100 Subject: net: ethernet: sun4i-emac: Read rxhdr in CPU byte-order The EMAC EMAC_RX_IO_DATA_REG data register is dual-purpose: On one hand it is used to move actual packet data off the wire. This will be in wire-format and accepted as such by higher layers such as IP. Therefore it is correctly read as-is (i.e. raw) using readsl. On the other hand it provides metadata about incoming transfers to the driver such as length and checksum validation status. This data is little-endian, always and it is interpreted by the driver. Therefore it needs to be swapped to CPU endianness to make sense to the driver. This is already done for the "receive header" but not rxhdr. Read rxhdr using readl in order for sun4i-emac to work correctly when running a big-endian kernel. Signed-off-by: Michael Weiser Cc: Maxime Ripard Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- drivers/net/ethernet/allwinner/sun4i-emac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 7401219..c8f4d26 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -592,8 +592,7 @@ static void emac_rx(struct net_device *dev) /* A packet ready now & Get status/length */ good_packet = true; - emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG, - &rxhdr, sizeof(rxhdr)); + rxhdr = readl(db->membase + EMAC_RX_IO_DATA_REG); if (netif_msg_rx_status(db)) dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr))); -- cgit v1.1 From e6fbd47add0d13189bacb1881fe006db5027536e Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Mon, 14 Nov 2016 16:39:07 -0600 Subject: amd-xgbe: Fix mask appliciation for Clause 37 register The application of a mask to clear an area of a clause 37 register value was not properly applied. Update the code to do the proper application of the mask. Reported-by: Marion & Christophe JAILLET Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 0ecae70..4c5b90e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -943,8 +943,8 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata) /* Set up the Control register */ reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); - reg &= XGBE_AN_CL37_TX_CONFIG_MASK; - reg &= XGBE_AN_CL37_PCS_MODE_MASK; + reg &= ~XGBE_AN_CL37_TX_CONFIG_MASK; + reg &= ~XGBE_AN_CL37_PCS_MODE_MASK; switch (pdata->an_mode) { case XGBE_AN_MODE_CL37: -- cgit v1.1 From 8c5385cbb0361dd6538b5b422996c3f1e1c6989d Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Mon, 14 Nov 2016 16:39:16 -0600 Subject: amd-xgbe: Fix up some coccinelle identified warnings Fix up some warnings that were identified by coccinelle: Clean up an if/else block that can look confusing since the same statement is executed in an "else if" check and the final "else" statement. Change a variable from unsigned int to int since it is used in an if statement checking the value to be less than 0. Reported-by: Julia Lawall Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 4ba4332..348cc8c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -1766,8 +1766,6 @@ static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata) XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 1); else if (phy_data->sfp_cable_len <= 3) XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 2); - else if (phy_data->sfp_cable_len <= 5) - XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3); else XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3); } @@ -2346,7 +2344,8 @@ static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart) { struct xgbe_phy_data *phy_data = pdata->phy_data; - unsigned int ret, reg; + unsigned int reg; + int ret; *an_restart = 0; -- cgit v1.1 From d13520c7ca7761346d2787fff1b3b112f87da77a Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:39 -0800 Subject: liquidio CN23XX: HW config for VF support Adds support for configuring HW for creating VFs. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_pf_device.c | 90 ++++++++++++++-------- .../net/ethernet/cavium/liquidio/octeon_config.h | 6 ++ .../net/ethernet/cavium/liquidio/octeon_device.h | 12 ++- 3 files changed, 74 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 380a641..832d710 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -40,11 +40,6 @@ */ #define CN23XX_INPUT_JABBER 64600 -#define LIOLUT_RING_DISTRIBUTION 9 -const int liolut_num_vfs_to_rings_per_vf[LIOLUT_RING_DISTRIBUTION] = { - 0, 8, 4, 2, 2, 2, 1, 1, 1 -}; - void cn23xx_dump_pf_initialized_regs(struct octeon_device *oct) { int i = 0; @@ -309,9 +304,10 @@ u32 cn23xx_pf_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us) static void cn23xx_setup_global_mac_regs(struct octeon_device *oct) { - u64 reg_val; u16 mac_no = oct->pcie_port; u16 pf_num = oct->pf_num; + u64 reg_val; + u64 temp; /* programming SRN and TRS for each MAC(0..3) */ @@ -333,6 +329,14 @@ static void cn23xx_setup_global_mac_regs(struct octeon_device *oct) /* setting TRS <23:16> */ reg_val = reg_val | (oct->sriov_info.trs << CN23XX_PKT_MAC_CTL_RINFO_TRS_BIT_POS); + /* setting RPVF <39:32> */ + temp = oct->sriov_info.rings_per_vf & 0xff; + reg_val |= (temp << CN23XX_PKT_MAC_CTL_RINFO_RPVF_BIT_POS); + + /* setting NVFS <55:48> */ + temp = oct->sriov_info.max_vfs & 0xff; + reg_val |= (temp << CN23XX_PKT_MAC_CTL_RINFO_NVFS_BIT_POS); + /* write these settings to MAC register */ octeon_write_csr64(oct, CN23XX_SLI_PKT_MAC_RINFO64(mac_no, pf_num), reg_val); @@ -399,11 +403,12 @@ static int cn23xx_reset_io_queues(struct octeon_device *oct) static int cn23xx_pf_setup_global_input_regs(struct octeon_device *oct) { + struct octeon_cn23xx_pf *cn23xx = (struct octeon_cn23xx_pf *)oct->chip; + struct octeon_instr_queue *iq; + u64 intr_threshold, reg_val; u32 q_no, ern, srn; u64 pf_num; - u64 intr_threshold, reg_val; - struct octeon_instr_queue *iq; - struct octeon_cn23xx_pf *cn23xx = (struct octeon_cn23xx_pf *)oct->chip; + u64 vf_num; pf_num = oct->pf_num; @@ -420,6 +425,16 @@ static int cn23xx_pf_setup_global_input_regs(struct octeon_device *oct) */ for (q_no = 0; q_no < ern; q_no++) { reg_val = oct->pcie_port << CN23XX_PKT_INPUT_CTL_MAC_NUM_POS; + + /* for VF assigned queues. */ + if (q_no < oct->sriov_info.pf_srn) { + vf_num = q_no / oct->sriov_info.rings_per_vf; + vf_num += 1; /* VF1, VF2,........ */ + } else { + vf_num = 0; + } + + reg_val |= vf_num << CN23XX_PKT_INPUT_CTL_VF_NUM_POS; reg_val |= pf_num << CN23XX_PKT_INPUT_CTL_PF_NUM_POS; octeon_write_csr64(oct, CN23XX_SLI_IQ_PKT_CONTROL64(q_no), @@ -1048,50 +1063,59 @@ static void cn23xx_setup_reg_address(struct octeon_device *oct) static int cn23xx_sriov_config(struct octeon_device *oct) { - u32 total_rings; struct octeon_cn23xx_pf *cn23xx = (struct octeon_cn23xx_pf *)oct->chip; - /* num_vfs is already filled for us */ + u32 max_rings, total_rings, max_vfs, rings_per_vf; u32 pf_srn, num_pf_rings; + u32 max_possible_vfs; cn23xx->conf = - (struct octeon_config *)oct_get_config_info(oct, LIO_23XX); + (struct octeon_config *)oct_get_config_info(oct, LIO_23XX); switch (oct->rev_id) { case OCTEON_CN23XX_REV_1_0: - total_rings = CN23XX_MAX_RINGS_PER_PF_PASS_1_0; + max_rings = CN23XX_MAX_RINGS_PER_PF_PASS_1_0; + max_possible_vfs = CN23XX_MAX_VFS_PER_PF_PASS_1_0; break; case OCTEON_CN23XX_REV_1_1: - total_rings = CN23XX_MAX_RINGS_PER_PF_PASS_1_1; + max_rings = CN23XX_MAX_RINGS_PER_PF_PASS_1_1; + max_possible_vfs = CN23XX_MAX_VFS_PER_PF_PASS_1_1; break; default: - total_rings = CN23XX_MAX_RINGS_PER_PF; + max_rings = CN23XX_MAX_RINGS_PER_PF; + max_possible_vfs = CN23XX_MAX_VFS_PER_PF; break; } - if (!oct->sriov_info.num_pf_rings) { - if (total_rings > num_present_cpus()) - num_pf_rings = num_present_cpus(); - else - num_pf_rings = total_rings; - } else { - num_pf_rings = oct->sriov_info.num_pf_rings; - if (num_pf_rings > total_rings) { - dev_warn(&oct->pci_dev->dev, - "num_queues_per_pf requested %u is more than available rings. Reducing to %u\n", - num_pf_rings, total_rings); - num_pf_rings = total_rings; - } - } + if (max_rings <= num_present_cpus()) + num_pf_rings = 1; + else + num_pf_rings = num_present_cpus(); + +#ifdef CONFIG_PCI_IOV + max_vfs = min_t(u32, + (max_rings - num_pf_rings), max_possible_vfs); + rings_per_vf = 1; +#else + max_vfs = 0; + rings_per_vf = 0; +#endif + + total_rings = num_pf_rings + max_vfs; - total_rings = num_pf_rings; /* the first ring of the pf */ pf_srn = total_rings - num_pf_rings; oct->sriov_info.trs = total_rings; + oct->sriov_info.max_vfs = max_vfs; + oct->sriov_info.rings_per_vf = rings_per_vf; oct->sriov_info.pf_srn = pf_srn; oct->sriov_info.num_pf_rings = num_pf_rings; - dev_dbg(&oct->pci_dev->dev, "trs:%d pf_srn:%d num_pf_rings:%d\n", - oct->sriov_info.trs, oct->sriov_info.pf_srn, - oct->sriov_info.num_pf_rings); + dev_notice(&oct->pci_dev->dev, "trs:%d max_vfs:%d rings_per_vf:%d pf_srn:%d num_pf_rings:%d\n", + oct->sriov_info.trs, oct->sriov_info.max_vfs, + oct->sriov_info.rings_per_vf, oct->sriov_info.pf_srn, + oct->sriov_info.num_pf_rings); + + oct->sriov_info.sriov_enabled = 0; + return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_config.h b/drivers/net/ethernet/cavium/liquidio/octeon_config.h index c765568..512bca5 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_config.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_config.h @@ -65,9 +65,15 @@ #define DEFAULT_NUM_NIC_PORTS_68XX_210NV 2 /* CN23xx IQ configuration macros */ +#define CN23XX_MAX_VFS_PER_PF_PASS_1_0 8 +#define CN23XX_MAX_VFS_PER_PF_PASS_1_1 31 +#define CN23XX_MAX_VFS_PER_PF 63 +#define CN23XX_MAX_RINGS_PER_VF 8 + #define CN23XX_MAX_RINGS_PER_PF_PASS_1_0 12 #define CN23XX_MAX_RINGS_PER_PF_PASS_1_1 32 #define CN23XX_MAX_RINGS_PER_PF 64 +#define CN23XX_MAX_RINGS_PER_VF 8 #define CN23XX_MAX_INPUT_QUEUES CN23XX_MAX_RINGS_PER_PF #define CN23XX_MAX_IQ_DESCRIPTORS 2048 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index da15c2a..751d3b6 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -322,11 +322,21 @@ struct octeon_pf_vf_hs_word { }; struct octeon_sriov_info { + /* Number of rings assigned to VF */ + u32 rings_per_vf; + + /** Max Number of VF devices that can be enabled. This variable can + * specified during load time or it will be derived after allocating + * PF queues. When max_vfs is derived then each VF will get one queue + **/ + u32 max_vfs; + /* Actual rings left for PF device */ u32 num_pf_rings; - /* SRN of PF usable IO queues */ + /* SRN of PF usable IO queues */ u32 pf_srn; + /* total pf rings */ u32 trs; -- cgit v1.1 From ca6139ffc67ee6ef0459f81e37894ed411083855 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:40 -0800 Subject: liquidio CN23XX: sysfs VF config support Adds sysfs based support for enabling or disabling VFs. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 106 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/octeon_config.h | 3 + .../net/ethernet/cavium/liquidio/octeon_device.h | 8 ++ 3 files changed, 117 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 71d01a7..afce659 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -180,6 +180,10 @@ struct octeon_device_priv { unsigned long napi_mask; }; +#ifdef CONFIG_PCI_IOV +static int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs); +#endif + static int octeon_device_init(struct octeon_device *); static int liquidio_stop(struct net_device *netdev); static void liquidio_remove(struct pci_dev *pdev); @@ -518,6 +522,9 @@ static struct pci_driver liquidio_pci_driver = { .suspend = liquidio_suspend, .resume = liquidio_resume, #endif +#ifdef CONFIG_PCI_IOV + .sriov_configure = liquidio_enable_sriov, +#endif }; /** @@ -1472,6 +1479,10 @@ static void octeon_destroy_resources(struct octeon_device *oct) continue; octeon_delete_instr_queue(oct, i); } +#ifdef CONFIG_PCI_IOV + if (oct->sriov_info.sriov_enabled) + pci_disable_sriov(oct->pci_dev); +#endif /* fallthrough */ case OCT_DEV_SC_BUFF_POOL_INIT_DONE: octeon_free_sc_buffer_pool(oct); @@ -3990,6 +4001,101 @@ setup_nic_wait_intr: return -ENODEV; } +#ifdef CONFIG_PCI_IOV +static int octeon_enable_sriov(struct octeon_device *oct) +{ + unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced; + struct pci_dev *vfdev; + int err; + u32 u; + + if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) { + err = pci_enable_sriov(oct->pci_dev, + oct->sriov_info.num_vfs_alloced); + if (err) { + dev_err(&oct->pci_dev->dev, + "OCTEON: Failed to enable PCI sriov: %d\n", + err); + oct->sriov_info.num_vfs_alloced = 0; + return err; + } + oct->sriov_info.sriov_enabled = 1; + + /* init lookup table that maps DPI ring number to VF pci_dev + * struct pointer + */ + u = 0; + vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + OCTEON_CN23XX_VF_VID, NULL); + while (vfdev) { + if (vfdev->is_virtfn && + (vfdev->physfn == oct->pci_dev)) { + oct->sriov_info.dpiring_to_vfpcidev_lut[u] = + vfdev; + u += oct->sriov_info.rings_per_vf; + } + vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + OCTEON_CN23XX_VF_VID, vfdev); + } + } + + return num_vfs_alloced; +} + +static int lio_pci_sriov_disable(struct octeon_device *oct) +{ + int u; + + if (pci_vfs_assigned(oct->pci_dev)) { + dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n"); + return -EPERM; + } + + pci_disable_sriov(oct->pci_dev); + + u = 0; + while (u < MAX_POSSIBLE_VFS) { + oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL; + u += oct->sriov_info.rings_per_vf; + } + + oct->sriov_info.num_vfs_alloced = 0; + dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n", + oct->pf_num); + + return 0; +} + +static int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs) +{ + struct octeon_device *oct = pci_get_drvdata(dev); + int ret = 0; + + if ((num_vfs == oct->sriov_info.num_vfs_alloced) && + (oct->sriov_info.sriov_enabled)) { + dev_info(&oct->pci_dev->dev, "oct->pf_num:%d already enabled num_vfs:%d\n", + oct->pf_num, num_vfs); + return 0; + } + + if (!num_vfs) { + ret = lio_pci_sriov_disable(oct); + } else if (num_vfs > oct->sriov_info.max_vfs) { + dev_err(&oct->pci_dev->dev, + "OCTEON: Max allowed VFs:%d user requested:%d", + oct->sriov_info.max_vfs, num_vfs); + ret = -EPERM; + } else { + oct->sriov_info.num_vfs_alloced = num_vfs; + ret = octeon_enable_sriov(oct); + dev_info(&oct->pci_dev->dev, "oct->pf_num:%d num_vfs:%d\n", + oct->pf_num, num_vfs); + } + + return ret; +} +#endif + /** * \brief initialize the NIC * @param oct octeon device diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_config.h b/drivers/net/ethernet/cavium/liquidio/octeon_config.h index 512bca5..71daf82 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_config.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_config.h @@ -472,4 +472,7 @@ struct octeon_config { #define MAX_POSSIBLE_OCTEON_INSTR_QUEUES CN23XX_MAX_INPUT_QUEUES #define MAX_POSSIBLE_OCTEON_OUTPUT_QUEUES CN23XX_MAX_OUTPUT_QUEUES + +#define MAX_POSSIBLE_VFS 64 + #endif /* __OCTEON_CONFIG_H__ */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 751d3b6..cfd12ec 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -38,6 +38,7 @@ #define OCTEON_CN68XX 0x0091 #define OCTEON_CN66XX 0x0092 #define OCTEON_CN23XX_PF_VID 0x9702 +#define OCTEON_CN23XX_VF_VID 0x9712 /**RevisionId for the chips */ #define OCTEON_CN23XX_REV_1_0 0x00 @@ -331,6 +332,9 @@ struct octeon_sriov_info { **/ u32 max_vfs; + /** Number of VF devices enabled using sysfs. */ + u32 num_vfs_alloced; + /* Actual rings left for PF device */ u32 num_pf_rings; @@ -340,6 +344,10 @@ struct octeon_sriov_info { /* total pf rings */ u32 trs; + u32 sriov_enabled; + + /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/ + struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS]; }; struct octeon_ioq_vector { -- cgit v1.1 From 8c978d059224deea079f0ee13640bcb99faa8061 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:41 -0800 Subject: liquidio CN23XX: Mailbox support Adds support for mailbox communication between PF and VF. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/Makefile | 1 + drivers/net/ethernet/cavium/liquidio/lio_core.c | 31 ++ .../net/ethernet/cavium/liquidio/liquidio_common.h | 6 +- .../net/ethernet/cavium/liquidio/octeon_device.h | 4 + .../net/ethernet/cavium/liquidio/octeon_mailbox.c | 318 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/octeon_mailbox.h | 115 ++++++++ drivers/net/ethernet/cavium/liquidio/octeon_main.h | 2 +- 7 files changed, 474 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c create mode 100644 drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h diff --git a/drivers/net/ethernet/cavium/liquidio/Makefile b/drivers/net/ethernet/cavium/liquidio/Makefile index 5a27b2a..14958de 100644 --- a/drivers/net/ethernet/cavium/liquidio/Makefile +++ b/drivers/net/ethernet/cavium/liquidio/Makefile @@ -11,6 +11,7 @@ liquidio-$(CONFIG_LIQUIDIO) += lio_ethtool.o \ cn66xx_device.o \ cn68xx_device.o \ cn23xx_pf_device.o \ + octeon_mailbox.o \ octeon_mem_ops.o \ octeon_droq.o \ octeon_nic.o diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 201eddb..4997ec2 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -264,3 +264,34 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) nctrl->ncmd.s.cmd); } } + +void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac) +{ + bool macaddr_changed = false; + struct net_device *netdev; + struct lio *lio; + + rtnl_lock(); + + netdev = oct->props[0].netdev; + lio = GET_LIO(netdev); + + lio->linfo.macaddr_is_admin_asgnd = true; + + if (!ether_addr_equal(netdev->dev_addr, mac)) { + macaddr_changed = true; + ether_addr_copy(netdev->dev_addr, mac); + ether_addr_copy(((u8 *)&lio->linfo.hw_addr) + 2, mac); + call_netdevice_notifiers(NETDEV_CHANGEADDR, netdev); + } + + rtnl_unlock(); + + if (macaddr_changed) + dev_info(&oct->pci_dev->dev, + "PF changed VF's MAC address to %pM\n", mac); + + /* no need to notify the firmware of the macaddr change because + * the PF did that already + */ +} diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 0d990ac..caeff9a 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -731,13 +731,15 @@ struct oct_link_info { #ifdef __BIG_ENDIAN_BITFIELD u64 gmxport:16; - u64 rsvd:32; + u64 macaddr_is_admin_asgnd:1; + u64 rsvd:31; u64 num_txpciq:8; u64 num_rxpciq:8; #else u64 num_rxpciq:8; u64 num_txpciq:8; - u64 rsvd:32; + u64 rsvd:31; + u64 macaddr_is_admin_asgnd:1; u64 gmxport:16; #endif diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index cfd12ec..77a6eb7 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -492,6 +492,9 @@ struct octeon_device { int msix_on; + /** Mail Box details of each octeon queue. */ + struct octeon_mbox *mbox[MAX_POSSIBLE_VFS]; + /** IOq information of it's corresponding MSI-X interrupt. */ struct octeon_ioq_vector *ioq_vector; @@ -511,6 +514,7 @@ struct octeon_device { #define OCTEON_CN6XXX(oct) ((oct->chip_id == OCTEON_CN66XX) || \ (oct->chip_id == OCTEON_CN68XX)) #define OCTEON_CN23XX_PF(oct) (oct->chip_id == OCTEON_CN23XX_PF_VID) +#define OCTEON_CN23XX_VF(oct) ((oct)->chip_id == OCTEON_CN23XX_VF_VID) #define CHIP_FIELD(oct, TYPE, field) \ (((struct octeon_ ## TYPE *)(oct->chip))->field) diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c new file mode 100644 index 0000000..5309384 --- /dev/null +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c @@ -0,0 +1,318 @@ +/********************************************************************** + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ +#include +#include +#include "liquidio_common.h" +#include "octeon_droq.h" +#include "octeon_iq.h" +#include "response_manager.h" +#include "octeon_device.h" +#include "octeon_main.h" +#include "octeon_mailbox.h" + +/** + * octeon_mbox_read: + * @oct: Pointer mailbox + * + * Reads the 8-bytes of data from the mbox register + * Writes back the acknowldgement inidcating completion of read + */ +int octeon_mbox_read(struct octeon_mbox *mbox) +{ + union octeon_mbox_message msg; + int ret = 0; + + spin_lock(&mbox->lock); + + msg.u64 = readq(mbox->mbox_read_reg); + + if ((msg.u64 == OCTEON_PFVFACK) || (msg.u64 == OCTEON_PFVFSIG)) { + spin_unlock(&mbox->lock); + return 0; + } + + if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVING) { + mbox->mbox_req.data[mbox->mbox_req.recv_len - 1] = msg.u64; + mbox->mbox_req.recv_len++; + } else { + if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVING) { + mbox->mbox_resp.data[mbox->mbox_resp.recv_len - 1] = + msg.u64; + mbox->mbox_resp.recv_len++; + } else { + if ((mbox->state & OCTEON_MBOX_STATE_IDLE) && + (msg.s.type == OCTEON_MBOX_REQUEST)) { + mbox->state &= ~OCTEON_MBOX_STATE_IDLE; + mbox->state |= + OCTEON_MBOX_STATE_REQUEST_RECEIVING; + mbox->mbox_req.msg.u64 = msg.u64; + mbox->mbox_req.q_no = mbox->q_no; + mbox->mbox_req.recv_len = 1; + } else { + if ((mbox->state & + OCTEON_MBOX_STATE_RESPONSE_PENDING) && + (msg.s.type == OCTEON_MBOX_RESPONSE)) { + mbox->state &= + ~OCTEON_MBOX_STATE_RESPONSE_PENDING; + mbox->state |= + OCTEON_MBOX_STATE_RESPONSE_RECEIVING + ; + mbox->mbox_resp.msg.u64 = msg.u64; + mbox->mbox_resp.q_no = mbox->q_no; + mbox->mbox_resp.recv_len = 1; + } else { + writeq(OCTEON_PFVFERR, + mbox->mbox_read_reg); + mbox->state |= OCTEON_MBOX_STATE_ERROR; + spin_unlock(&mbox->lock); + return 1; + } + } + } + } + + if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVING) { + if (mbox->mbox_req.recv_len < msg.s.len) { + ret = 0; + } else { + mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVING; + mbox->state |= OCTEON_MBOX_STATE_REQUEST_RECEIVED; + ret = 1; + } + } else { + if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVING) { + if (mbox->mbox_resp.recv_len < msg.s.len) { + ret = 0; + } else { + mbox->state &= + ~OCTEON_MBOX_STATE_RESPONSE_RECEIVING; + mbox->state |= + OCTEON_MBOX_STATE_RESPONSE_RECEIVED; + ret = 1; + } + } else { + WARN_ON(1); + } + } + + writeq(OCTEON_PFVFACK, mbox->mbox_read_reg); + + spin_unlock(&mbox->lock); + + return ret; +} + +/** + * octeon_mbox_write: + * @oct: Pointer Octeon Device + * @mbox_cmd: Cmd to send to mailbox. + * + * Populates the queue specific mbox structure + * with cmd information. + * Write the cmd to mbox register + */ +int octeon_mbox_write(struct octeon_device *oct, + struct octeon_mbox_cmd *mbox_cmd) +{ + struct octeon_mbox *mbox = oct->mbox[mbox_cmd->q_no]; + u32 count, i, ret = OCTEON_MBOX_STATUS_SUCCESS; + unsigned long flags; + + spin_lock_irqsave(&mbox->lock, flags); + + if ((mbox_cmd->msg.s.type == OCTEON_MBOX_RESPONSE) && + !(mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVED)) { + spin_unlock_irqrestore(&mbox->lock, flags); + return OCTEON_MBOX_STATUS_FAILED; + } + + if ((mbox_cmd->msg.s.type == OCTEON_MBOX_REQUEST) && + !(mbox->state & OCTEON_MBOX_STATE_IDLE)) { + spin_unlock_irqrestore(&mbox->lock, flags); + return OCTEON_MBOX_STATUS_BUSY; + } + + if (mbox_cmd->msg.s.type == OCTEON_MBOX_REQUEST) { + memcpy(&mbox->mbox_resp, mbox_cmd, + sizeof(struct octeon_mbox_cmd)); + mbox->state = OCTEON_MBOX_STATE_RESPONSE_PENDING; + } + + spin_unlock_irqrestore(&mbox->lock, flags); + + count = 0; + + while (readq(mbox->mbox_write_reg) != OCTEON_PFVFSIG) { + schedule_timeout_uninterruptible(LIO_MBOX_WRITE_WAIT_TIME); + if (count++ == LIO_MBOX_WRITE_WAIT_CNT) { + ret = OCTEON_MBOX_STATUS_FAILED; + break; + } + } + + if (ret == OCTEON_MBOX_STATUS_SUCCESS) { + writeq(mbox_cmd->msg.u64, mbox->mbox_write_reg); + for (i = 0; i < (u32)(mbox_cmd->msg.s.len - 1); i++) { + count = 0; + while (readq(mbox->mbox_write_reg) != + OCTEON_PFVFACK) { + schedule_timeout_uninterruptible(10); + if (count++ == LIO_MBOX_WRITE_WAIT_CNT) { + ret = OCTEON_MBOX_STATUS_FAILED; + break; + } + } + writeq(mbox_cmd->data[i], mbox->mbox_write_reg); + } + } + + spin_lock_irqsave(&mbox->lock, flags); + if (mbox_cmd->msg.s.type == OCTEON_MBOX_RESPONSE) { + mbox->state = OCTEON_MBOX_STATE_IDLE; + writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); + } else { + if ((!mbox_cmd->msg.s.resp_needed) || + (ret == OCTEON_MBOX_STATUS_FAILED)) { + mbox->state &= ~OCTEON_MBOX_STATE_RESPONSE_PENDING; + if (!(mbox->state & + (OCTEON_MBOX_STATE_REQUEST_RECEIVING | + OCTEON_MBOX_STATE_REQUEST_RECEIVED))) + mbox->state = OCTEON_MBOX_STATE_IDLE; + } + } + spin_unlock_irqrestore(&mbox->lock, flags); + + return ret; +} + +/** + * octeon_mbox_process_cmd: + * @mbox: Pointer mailbox + * @mbox_cmd: Pointer to command received + * + * Process the cmd received in mbox + */ +static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, + struct octeon_mbox_cmd *mbox_cmd) +{ + struct octeon_device *oct = mbox->oct_dev; + + switch (mbox_cmd->msg.s.cmd) { + case OCTEON_VF_ACTIVE: + dev_dbg(&oct->pci_dev->dev, "got vfactive sending data back\n"); + mbox_cmd->msg.s.type = OCTEON_MBOX_RESPONSE; + mbox_cmd->msg.s.resp_needed = 1; + mbox_cmd->msg.s.len = 2; + mbox_cmd->data[0] = 0; /* VF version is in mbox_cmd->data[0] */ + ((struct lio_version *)&mbox_cmd->data[0])->major = + LIQUIDIO_BASE_MAJOR_VERSION; + ((struct lio_version *)&mbox_cmd->data[0])->minor = + LIQUIDIO_BASE_MINOR_VERSION; + ((struct lio_version *)&mbox_cmd->data[0])->micro = + LIQUIDIO_BASE_MICRO_VERSION; + memcpy(mbox_cmd->msg.s.params, (uint8_t *)&oct->pfvf_hsword, 6); + /* Sending core cofig info to the corresponding active VF.*/ + octeon_mbox_write(oct, mbox_cmd); + break; + + case OCTEON_VF_FLR_REQUEST: + dev_info(&oct->pci_dev->dev, + "got a request for FLR from VF that owns DPI ring %u\n", + mbox->q_no); + pcie_capability_set_word( + oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no], + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); + break; + + case OCTEON_PF_CHANGED_VF_MACADDR: + if (OCTEON_CN23XX_VF(oct)) + octeon_pf_changed_vf_macaddr(oct, + mbox_cmd->msg.s.params); + break; + + default: + break; + } + return 0; +} + +/** + *octeon_mbox_process_message: + * + * Process the received mbox message. + */ +int octeon_mbox_process_message(struct octeon_mbox *mbox) +{ + struct octeon_mbox_cmd mbox_cmd; + unsigned long flags; + + spin_lock_irqsave(&mbox->lock, flags); + + if (mbox->state & OCTEON_MBOX_STATE_ERROR) { + if (mbox->state & (OCTEON_MBOX_STATE_RESPONSE_PENDING | + OCTEON_MBOX_STATE_RESPONSE_RECEIVING)) { + memcpy(&mbox_cmd, &mbox->mbox_resp, + sizeof(struct octeon_mbox_cmd)); + mbox->state = OCTEON_MBOX_STATE_IDLE; + writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); + spin_unlock_irqrestore(&mbox->lock, flags); + mbox_cmd.recv_status = 1; + if (mbox_cmd.fn) + mbox_cmd.fn(mbox->oct_dev, &mbox_cmd, + mbox_cmd.fn_arg); + return 0; + } + + mbox->state = OCTEON_MBOX_STATE_IDLE; + writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); + spin_unlock_irqrestore(&mbox->lock, flags); + return 0; + } + + if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVED) { + memcpy(&mbox_cmd, &mbox->mbox_resp, + sizeof(struct octeon_mbox_cmd)); + mbox->state = OCTEON_MBOX_STATE_IDLE; + writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); + spin_unlock_irqrestore(&mbox->lock, flags); + mbox_cmd.recv_status = 0; + if (mbox_cmd.fn) + mbox_cmd.fn(mbox->oct_dev, &mbox_cmd, mbox_cmd.fn_arg); + return 0; + } + + if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVED) { + memcpy(&mbox_cmd, &mbox->mbox_req, + sizeof(struct octeon_mbox_cmd)); + if (!mbox_cmd.msg.s.resp_needed) { + mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVED; + if (!(mbox->state && + OCTEON_MBOX_STATE_RESPONSE_PENDING)) + mbox->state = OCTEON_MBOX_STATE_IDLE; + writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); + } + + spin_unlock_irqrestore(&mbox->lock, flags); + octeon_mbox_process_cmd(mbox, &mbox_cmd); + return 0; + } + + WARN_ON(1); + + return 0; +} diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h new file mode 100644 index 0000000..fe60a3e --- /dev/null +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h @@ -0,0 +1,115 @@ +/********************************************************************** + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ +#ifndef __MAILBOX_H__ +#define __MAILBOX_H__ + +/* Macros for Mail Box Communication */ + +#define OCTEON_MBOX_DATA_MAX 32 + +#define OCTEON_VF_ACTIVE 0x1 +#define OCTEON_VF_FLR_REQUEST 0x2 +#define OCTEON_PF_CHANGED_VF_MACADDR 0x4 + +/*Macro for Read acknowldgement*/ +#define OCTEON_PFVFACK 0xffffffffffffffff +#define OCTEON_PFVFSIG 0x1122334455667788 +#define OCTEON_PFVFERR 0xDEADDEADDEADDEAD + +#define LIO_MBOX_WRITE_WAIT_CNT 1000 +#define LIO_MBOX_WRITE_WAIT_TIME 10 + +enum octeon_mbox_cmd_status { + OCTEON_MBOX_STATUS_SUCCESS = 0, + OCTEON_MBOX_STATUS_FAILED = 1, + OCTEON_MBOX_STATUS_BUSY = 2 +}; + +enum octeon_mbox_message_type { + OCTEON_MBOX_REQUEST = 0, + OCTEON_MBOX_RESPONSE = 1 +}; + +union octeon_mbox_message { + u64 u64; + struct { + u16 type : 1; + u16 resp_needed : 1; + u16 cmd : 6; + u16 len : 8; + u8 params[6]; + } s; +}; + +typedef void (*octeon_mbox_callback_t)(void *, void *, void *); + +struct octeon_mbox_cmd { + union octeon_mbox_message msg; + u64 data[OCTEON_MBOX_DATA_MAX]; + u32 q_no; + u32 recv_len; + u32 recv_status; + octeon_mbox_callback_t fn; + void *fn_arg; +}; + +enum octeon_mbox_state { + OCTEON_MBOX_STATE_IDLE = 1, + OCTEON_MBOX_STATE_REQUEST_RECEIVING = 2, + OCTEON_MBOX_STATE_REQUEST_RECEIVED = 4, + OCTEON_MBOX_STATE_RESPONSE_PENDING = 8, + OCTEON_MBOX_STATE_RESPONSE_RECEIVING = 16, + OCTEON_MBOX_STATE_RESPONSE_RECEIVED = 16, + OCTEON_MBOX_STATE_ERROR = 32 +}; + +struct octeon_mbox { + /** A spinlock to protect access to this q_mbox. */ + spinlock_t lock; + + struct octeon_device *oct_dev; + + u32 q_no; + + enum octeon_mbox_state state; + + struct cavium_wk mbox_poll_wk; + + /** SLI_MAC_PF_MBOX_INT for PF, SLI_PKT_MBOX_INT for VF. */ + void *mbox_int_reg; + + /** SLI_PKT_PF_VF_MBOX_SIG(0) for PF, SLI_PKT_PF_VF_MBOX_SIG(1) for VF. + */ + void *mbox_write_reg; + + /** SLI_PKT_PF_VF_MBOX_SIG(1) for PF, SLI_PKT_PF_VF_MBOX_SIG(0) for VF. + */ + void *mbox_read_reg; + + struct octeon_mbox_cmd mbox_req; + + struct octeon_mbox_cmd mbox_resp; + +}; + +int octeon_mbox_read(struct octeon_mbox *mbox); +int octeon_mbox_write(struct octeon_device *oct, + struct octeon_mbox_cmd *mbox_cmd); +int octeon_mbox_process_message(struct octeon_mbox *mbox); + +#endif diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h index 366298f..3cddf28 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h @@ -66,7 +66,7 @@ void octeon_update_tx_completion_counters(void *buf, int reqtype, unsigned int *bytes_compl); void octeon_report_tx_completion_to_bql(void *txq, unsigned int pkts_compl, unsigned int bytes_compl); - +void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac); /** Swap 8B blocks */ static inline void octeon_swap_8B_data(u64 *data, u32 blocks) { -- cgit v1.1 From 5d65556beb36682175b6bceaecadae226c7358be Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:42 -0800 Subject: liquidio CN23XX: mailbox interrupt processing Adds support for mailbox interrupt processing of various commands. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_pf_device.c | 157 +++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 12 ++ .../net/ethernet/cavium/liquidio/octeon_device.c | 1 + .../net/ethernet/cavium/liquidio/octeon_device.h | 21 ++- 4 files changed, 184 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 832d710..ffc94ac 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -30,6 +30,7 @@ #include "octeon_device.h" #include "cn23xx_pf_device.h" #include "octeon_main.h" +#include "octeon_mailbox.h" #define RESET_NOTDONE 0 #define RESET_DONE 1 @@ -677,6 +678,118 @@ static void cn23xx_setup_oq_regs(struct octeon_device *oct, u32 oq_no) } } +static void cn23xx_pf_mbox_thread(struct work_struct *work) +{ + struct cavium_wk *wk = (struct cavium_wk *)work; + struct octeon_mbox *mbox = (struct octeon_mbox *)wk->ctxptr; + struct octeon_device *oct = mbox->oct_dev; + u64 mbox_int_val, val64; + u32 q_no, i; + + if (oct->rev_id < OCTEON_CN23XX_REV_1_1) { + /*read and clear by writing 1*/ + mbox_int_val = readq(mbox->mbox_int_reg); + writeq(mbox_int_val, mbox->mbox_int_reg); + + for (i = 0; i < oct->sriov_info.num_vfs_alloced; i++) { + q_no = i * oct->sriov_info.rings_per_vf; + + val64 = readq(oct->mbox[q_no]->mbox_write_reg); + + if (val64 && (val64 != OCTEON_PFVFACK)) { + if (octeon_mbox_read(oct->mbox[q_no])) + octeon_mbox_process_message( + oct->mbox[q_no]); + } + } + + schedule_delayed_work(&wk->work, msecs_to_jiffies(10)); + } else { + octeon_mbox_process_message(mbox); + } +} + +static int cn23xx_setup_pf_mbox(struct octeon_device *oct) +{ + struct octeon_mbox *mbox = NULL; + u16 mac_no = oct->pcie_port; + u16 pf_num = oct->pf_num; + u32 q_no, i; + + if (!oct->sriov_info.max_vfs) + return 0; + + for (i = 0; i < oct->sriov_info.max_vfs; i++) { + q_no = i * oct->sriov_info.rings_per_vf; + + mbox = vmalloc(sizeof(*mbox)); + if (!mbox) + goto free_mbox; + + memset(mbox, 0, sizeof(struct octeon_mbox)); + + spin_lock_init(&mbox->lock); + + mbox->oct_dev = oct; + + mbox->q_no = q_no; + + mbox->state = OCTEON_MBOX_STATE_IDLE; + + /* PF mbox interrupt reg */ + mbox->mbox_int_reg = (u8 *)oct->mmio[0].hw_addr + + CN23XX_SLI_MAC_PF_MBOX_INT(mac_no, pf_num); + + /* PF writes into SIG0 reg */ + mbox->mbox_write_reg = (u8 *)oct->mmio[0].hw_addr + + CN23XX_SLI_PKT_PF_VF_MBOX_SIG(q_no, 0); + + /* PF reads from SIG1 reg */ + mbox->mbox_read_reg = (u8 *)oct->mmio[0].hw_addr + + CN23XX_SLI_PKT_PF_VF_MBOX_SIG(q_no, 1); + + /*Mail Box Thread creation*/ + INIT_DELAYED_WORK(&mbox->mbox_poll_wk.work, + cn23xx_pf_mbox_thread); + mbox->mbox_poll_wk.ctxptr = (void *)mbox; + + oct->mbox[q_no] = mbox; + + writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); + } + + if (oct->rev_id < OCTEON_CN23XX_REV_1_1) + schedule_delayed_work(&oct->mbox[0]->mbox_poll_wk.work, + msecs_to_jiffies(0)); + + return 0; + +free_mbox: + while (i) { + i--; + vfree(oct->mbox[i]); + } + + return 1; +} + +static int cn23xx_free_pf_mbox(struct octeon_device *oct) +{ + u32 q_no, i; + + if (!oct->sriov_info.max_vfs) + return 0; + + for (i = 0; i < oct->sriov_info.max_vfs; i++) { + q_no = i * oct->sriov_info.rings_per_vf; + cancel_delayed_work_sync( + &oct->mbox[q_no]->mbox_poll_wk.work); + vfree(oct->mbox[q_no]); + } + + return 0; +} + static int cn23xx_enable_io_queues(struct octeon_device *oct) { u64 reg_val; @@ -871,6 +984,29 @@ static u64 cn23xx_pf_msix_interrupt_handler(void *dev) return ret; } +static void cn23xx_handle_pf_mbox_intr(struct octeon_device *oct) +{ + struct delayed_work *work; + u64 mbox_int_val; + u32 i, q_no; + + mbox_int_val = readq(oct->mbox[0]->mbox_int_reg); + + for (i = 0; i < oct->sriov_info.num_vfs_alloced; i++) { + q_no = i * oct->sriov_info.rings_per_vf; + + if (mbox_int_val & BIT_ULL(q_no)) { + writeq(BIT_ULL(q_no), + oct->mbox[0]->mbox_int_reg); + if (octeon_mbox_read(oct->mbox[q_no])) { + work = &oct->mbox[q_no]->mbox_poll_wk.work; + schedule_delayed_work(work, + msecs_to_jiffies(0)); + } + } + } +} + static irqreturn_t cn23xx_interrupt_handler(void *dev) { struct octeon_device *oct = (struct octeon_device *)dev; @@ -886,6 +1022,10 @@ static irqreturn_t cn23xx_interrupt_handler(void *dev) dev_err(&oct->pci_dev->dev, "OCTEON[%d]: Error Intr: 0x%016llx\n", oct->octeon_id, CVM_CAST64(intr64)); + /* When VFs write into MBOX_SIG2 reg,these intr is set in PF */ + if (intr64 & CN23XX_INTR_VF_MBOX) + cn23xx_handle_pf_mbox_intr(oct); + if (oct->msix_on != LIO_FLAG_MSIX_ENABLED) { if (intr64 & CN23XX_INTR_PKT_DATA) oct->int_status |= OCT_DEV_INTR_PKT_DATA; @@ -976,6 +1116,13 @@ static void cn23xx_enable_pf_interrupt(struct octeon_device *oct, u8 intr_flag) intr_val = readq(cn23xx->intr_enb_reg64); intr_val |= CN23XX_INTR_PKT_DATA; writeq(intr_val, cn23xx->intr_enb_reg64); + } else if ((intr_flag & OCTEON_MBOX_INTR) && + (oct->sriov_info.max_vfs > 0)) { + if (oct->rev_id >= OCTEON_CN23XX_REV_1_1) { + intr_val = readq(cn23xx->intr_enb_reg64); + intr_val |= CN23XX_INTR_VF_MBOX; + writeq(intr_val, cn23xx->intr_enb_reg64); + } } } @@ -991,6 +1138,13 @@ static void cn23xx_disable_pf_interrupt(struct octeon_device *oct, u8 intr_flag) intr_val = readq(cn23xx->intr_enb_reg64); intr_val &= ~CN23XX_INTR_PKT_DATA; writeq(intr_val, cn23xx->intr_enb_reg64); + } else if ((intr_flag & OCTEON_MBOX_INTR) && + (oct->sriov_info.max_vfs > 0)) { + if (oct->rev_id >= OCTEON_CN23XX_REV_1_1) { + intr_val = readq(cn23xx->intr_enb_reg64); + intr_val &= ~CN23XX_INTR_VF_MBOX; + writeq(intr_val, cn23xx->intr_enb_reg64); + } } } @@ -1143,6 +1297,9 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) oct->fn_list.setup_iq_regs = cn23xx_setup_iq_regs; oct->fn_list.setup_oq_regs = cn23xx_setup_oq_regs; + oct->fn_list.setup_mbox = cn23xx_setup_pf_mbox; + oct->fn_list.free_mbox = cn23xx_free_pf_mbox; + oct->fn_list.process_interrupt_regs = cn23xx_interrupt_handler; oct->fn_list.msix_interrupt_handler = cn23xx_pf_msix_interrupt_handler; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index afce659..aebf342 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1445,6 +1445,12 @@ static void octeon_destroy_resources(struct octeon_device *oct) if (OCTEON_CN23XX_PF(oct)) octeon_free_ioq_vector(oct); + + /* fallthrough */ + case OCT_DEV_MBOX_SETUP_DONE: + if (OCTEON_CN23XX_PF(oct)) + oct->fn_list.free_mbox(oct); + /* fallthrough */ case OCT_DEV_IN_RESET: case OCT_DEV_DROQ_INIT_DONE: @@ -4315,6 +4321,12 @@ static int octeon_device_init(struct octeon_device *octeon_dev) atomic_set(&octeon_dev->status, OCT_DEV_DROQ_INIT_DONE); if (OCTEON_CN23XX_PF(octeon_dev)) { + if (octeon_dev->fn_list.setup_mbox(octeon_dev)) { + dev_err(&octeon_dev->pci_dev->dev, "OCTEON: Mailbox setup failed\n"); + return 1; + } + atomic_set(&octeon_dev->status, OCT_DEV_MBOX_SETUP_DONE); + if (octeon_allocate_ioq_vector(octeon_dev)) { dev_err(&octeon_dev->pci_dev->dev, "OCTEON: ioq vector allocation failed\n"); return 1; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 586b688..7f532fd 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -767,6 +767,7 @@ octeon_allocate_ioq_vector(struct octeon_device *oct) ioq_vector->oct_dev = oct; ioq_vector->iq_index = i; ioq_vector->droq_index = i; + ioq_vector->mbox = oct->mbox[i]; cpu_num = i % num_online_cpus(); cpumask_set_cpu(cpu_num, &ioq_vector->affinity_mask); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 77a6eb7..bdb10a0 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -54,6 +54,7 @@ enum octeon_pci_swap_mode { }; #define OCTEON_OUTPUT_INTR (2) +#define OCTEON_MBOX_INTR (4) #define OCTEON_ALL_INTR 0xff /*--------------- PCI BAR1 index registers -------------*/ @@ -77,13 +78,14 @@ enum octeon_pci_swap_mode { #define OCT_DEV_SC_BUFF_POOL_INIT_DONE 0x4 #define OCT_DEV_RESP_LIST_INIT_DONE 0x5 #define OCT_DEV_DROQ_INIT_DONE 0x6 -#define OCT_DEV_IO_QUEUES_DONE 0x7 -#define OCT_DEV_CONSOLE_INIT_DONE 0x8 -#define OCT_DEV_HOST_OK 0x9 -#define OCT_DEV_CORE_OK 0xa -#define OCT_DEV_RUNNING 0xb -#define OCT_DEV_IN_RESET 0xc -#define OCT_DEV_STATE_INVALID 0xd +#define OCT_DEV_MBOX_SETUP_DONE 0x8 +#define OCT_DEV_IO_QUEUES_DONE 0x9 +#define OCT_DEV_CONSOLE_INIT_DONE 0xa +#define OCT_DEV_HOST_OK 0xb +#define OCT_DEV_CORE_OK 0xc +#define OCT_DEV_RUNNING 0xd +#define OCT_DEV_IN_RESET 0xe +#define OCT_DEV_STATE_INVALID 0xf #define OCT_DEV_STATES OCT_DEV_STATE_INVALID @@ -209,6 +211,10 @@ struct octeon_fn_list { irqreturn_t (*process_interrupt_regs)(void *); u64 (*msix_interrupt_handler)(void *); + + int (*setup_mbox)(struct octeon_device *); + int (*free_mbox)(struct octeon_device *); + int (*soft_reset)(struct octeon_device *); int (*setup_device_regs)(struct octeon_device *); void (*bar1_idx_setup)(struct octeon_device *, u64, u32, int); @@ -355,6 +361,7 @@ struct octeon_ioq_vector { int iq_index; int droq_index; int vector; + struct octeon_mbox *mbox; struct cpumask affinity_mask; u32 ioq_num; }; -- cgit v1.1 From 86dea55b9aef494b349c5e77e7793a8b2c7d8f1f Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:43 -0800 Subject: liquidio CN23XX: VF related operations Adds support for VF related operations like mac address vlan and link changes. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_pf_device.c | 22 +++ .../ethernet/cavium/liquidio/cn23xx_pf_device.h | 5 + drivers/net/ethernet/cavium/liquidio/lio_main.c | 211 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/liquidio_common.h | 5 + .../net/ethernet/cavium/liquidio/octeon_device.h | 8 + 5 files changed, 251 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index ffc94ac..d01b00b 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "liquidio_common.h" #include "octeon_droq.h" #include "octeon_iq.h" @@ -1416,3 +1417,24 @@ int cn23xx_fw_loaded(struct octeon_device *oct) val = octeon_read_csr64(oct, CN23XX_SLI_SCRATCH1); return (val >> 1) & 1ULL; } + +void cn23xx_tell_vf_its_macaddr_changed(struct octeon_device *oct, int vfidx, + u8 *mac) +{ + if (oct->sriov_info.vf_drv_loaded_mask & BIT_ULL(vfidx)) { + struct octeon_mbox_cmd mbox_cmd; + + mbox_cmd.msg.u64 = 0; + mbox_cmd.msg.s.type = OCTEON_MBOX_REQUEST; + mbox_cmd.msg.s.resp_needed = 0; + mbox_cmd.msg.s.cmd = OCTEON_PF_CHANGED_VF_MACADDR; + mbox_cmd.msg.s.len = 1; + mbox_cmd.recv_len = 0; + mbox_cmd.recv_status = 0; + mbox_cmd.fn = NULL; + mbox_cmd.fn_arg = 0; + ether_addr_copy(mbox_cmd.msg.s.params, mac); + mbox_cmd.q_no = vfidx * oct->sriov_info.rings_per_vf; + octeon_mbox_write(oct, &mbox_cmd); + } +} diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h index 21b5c90..cee346a 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h @@ -29,6 +29,8 @@ #include "cn23xx_pf_regs.h" +#define LIO_CMD_WAIT_TM 100 + /* Register address and configuration for a CN23XX devices. * If device specific changes need to be made then add a struct to include * device specific fields as shown in the commented section @@ -56,4 +58,7 @@ u32 cn23xx_pf_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us); void cn23xx_dump_pf_initialized_regs(struct octeon_device *oct); int cn23xx_fw_loaded(struct octeon_device *oct); + +void cn23xx_tell_vf_its_macaddr_changed(struct octeon_device *oct, int vfidx, + u8 *mac); #endif diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index aebf342..8c82cd3 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3573,6 +3573,151 @@ static void liquidio_del_vxlan_port(struct net_device *netdev, OCTNET_CMD_VXLAN_PORT_DEL); } +static int __liquidio_set_vf_mac(struct net_device *netdev, int vfidx, + u8 *mac, bool is_admin_assigned) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; + + if (vfidx < 0 || vfidx >= oct->sriov_info.max_vfs) + return -EINVAL; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MACADDR; + /* vfidx is 0 based, but vf_num (param1) is 1 based */ + nctrl.ncmd.s.param1 = vfidx + 1; + nctrl.ncmd.s.param2 = (is_admin_assigned ? 1 : 0); + nctrl.ncmd.s.more = 1; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.cb_fn = 0; + nctrl.wait_time = LIO_CMD_WAIT_TM; + + nctrl.udd[0] = 0; + /* The MAC Address is presented in network byte order. */ + ether_addr_copy((u8 *)&nctrl.udd[0] + 2, mac); + + oct->sriov_info.vf_macaddr[vfidx] = nctrl.udd[0]; + + octnet_send_nic_ctrl_pkt(oct, &nctrl); + + return 0; +} + +static int liquidio_set_vf_mac(struct net_device *netdev, int vfidx, u8 *mac) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + int retval; + + retval = __liquidio_set_vf_mac(netdev, vfidx, mac, true); + if (!retval) + cn23xx_tell_vf_its_macaddr_changed(oct, vfidx, mac); + + return retval; +} + +static int liquidio_set_vf_vlan(struct net_device *netdev, int vfidx, + u16 vlan, u8 qos, __be16 vlan_proto) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + u16 vlantci; + + if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced) + return -EINVAL; + + if (vlan_proto != htons(ETH_P_8021Q)) + return -EPROTONOSUPPORT; + + if (vlan >= VLAN_N_VID || qos > 7) + return -EINVAL; + + if (vlan) + vlantci = vlan | (u16)qos << VLAN_PRIO_SHIFT; + else + vlantci = 0; + + if (oct->sriov_info.vf_vlantci[vfidx] == vlantci) + return 0; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + if (vlan) + nctrl.ncmd.s.cmd = OCTNET_CMD_ADD_VLAN_FILTER; + else + nctrl.ncmd.s.cmd = OCTNET_CMD_DEL_VLAN_FILTER; + + nctrl.ncmd.s.param1 = vlantci; + nctrl.ncmd.s.param2 = + vfidx + 1; /* vfidx is 0 based, but vf_num (param2) is 1 based */ + nctrl.ncmd.s.more = 0; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.cb_fn = 0; + nctrl.wait_time = LIO_CMD_WAIT_TM; + + octnet_send_nic_ctrl_pkt(oct, &nctrl); + + oct->sriov_info.vf_vlantci[vfidx] = vlantci; + + return 0; +} + +static int liquidio_get_vf_config(struct net_device *netdev, int vfidx, + struct ifla_vf_info *ivi) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + u8 *macaddr; + + if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced) + return -EINVAL; + + ivi->vf = vfidx; + macaddr = 2 + (u8 *)&oct->sriov_info.vf_macaddr[vfidx]; + ether_addr_copy(&ivi->mac[0], macaddr); + ivi->vlan = oct->sriov_info.vf_vlantci[vfidx] & VLAN_VID_MASK; + ivi->qos = oct->sriov_info.vf_vlantci[vfidx] >> VLAN_PRIO_SHIFT; + ivi->linkstate = oct->sriov_info.vf_linkstate[vfidx]; + return 0; +} + +static int liquidio_set_vf_link_state(struct net_device *netdev, int vfidx, + int linkstate) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + + if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced) + return -EINVAL; + + if (oct->sriov_info.vf_linkstate[vfidx] == linkstate) + return 0; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + nctrl.ncmd.s.cmd = OCTNET_CMD_SET_VF_LINKSTATE; + nctrl.ncmd.s.param1 = + vfidx + 1; /* vfidx is 0 based, but vf_num (param1) is 1 based */ + nctrl.ncmd.s.param2 = linkstate; + nctrl.ncmd.s.more = 0; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.cb_fn = 0; + nctrl.wait_time = LIO_CMD_WAIT_TM; + + octnet_send_nic_ctrl_pkt(oct, &nctrl); + + oct->sriov_info.vf_linkstate[vfidx] = linkstate; + + return 0; +} + static struct net_device_ops lionetdevops = { .ndo_open = liquidio_open, .ndo_stop = liquidio_stop, @@ -3590,6 +3735,10 @@ static struct net_device_ops lionetdevops = { .ndo_set_features = liquidio_set_features, .ndo_udp_tunnel_add = liquidio_add_vxlan_port, .ndo_udp_tunnel_del = liquidio_del_vxlan_port, + .ndo_set_vf_mac = liquidio_set_vf_mac, + .ndo_set_vf_vlan = liquidio_set_vf_vlan, + .ndo_get_vf_config = liquidio_get_vf_config, + .ndo_set_vf_link_state = liquidio_set_vf_link_state, }; /** \brief Entry point for the liquidio module @@ -3912,6 +4061,19 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) "if%d gmx: %d hw_addr: 0x%llx\n", i, lio->linfo.gmxport, CVM_CAST64(lio->linfo.hw_addr)); + for (j = 0; j < octeon_dev->sriov_info.max_vfs; j++) { + u8 vfmac[ETH_ALEN]; + + random_ether_addr(&vfmac[0]); + if (__liquidio_set_vf_mac(netdev, j, + &vfmac[0], false)) { + dev_err(&octeon_dev->pci_dev->dev, + "Error setting VF%d MAC address\n", + j); + goto setup_nic_dev_fail; + } + } + /* 64-bit swap required on LE machines */ octeon_swap_8B_data(&lio->linfo.hw_addr, 1); for (j = 0; j < 6; j++) @@ -4207,6 +4369,52 @@ static void nic_starter(struct work_struct *work) complete(&handshake[oct->octeon_id].started); } +static int +octeon_recv_vf_drv_notice(struct octeon_recv_info *recv_info, void *buf) +{ + struct octeon_device *oct = (struct octeon_device *)buf; + struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt; + int i, notice, vf_idx; + u64 *data, vf_num; + + notice = recv_pkt->rh.r.ossp; + data = (u64 *)get_rbd(recv_pkt->buffer_ptr[0]); + + /* the first 64-bit word of data is the vf_num */ + vf_num = data[0]; + octeon_swap_8B_data(&vf_num, 1); + vf_idx = (int)vf_num - 1; + + if (notice == VF_DRV_LOADED) { + if (!(oct->sriov_info.vf_drv_loaded_mask & BIT_ULL(vf_idx))) { + oct->sriov_info.vf_drv_loaded_mask |= BIT_ULL(vf_idx); + dev_info(&oct->pci_dev->dev, + "driver for VF%d was loaded\n", vf_idx); + try_module_get(THIS_MODULE); + } + } else if (notice == VF_DRV_REMOVED) { + if (oct->sriov_info.vf_drv_loaded_mask & BIT_ULL(vf_idx)) { + oct->sriov_info.vf_drv_loaded_mask &= ~BIT_ULL(vf_idx); + dev_info(&oct->pci_dev->dev, + "driver for VF%d was removed\n", vf_idx); + module_put(THIS_MODULE); + } + } else if (notice == VF_DRV_MACADDR_CHANGED) { + u8 *b = (u8 *)&data[1]; + + oct->sriov_info.vf_macaddr[vf_idx] = data[1]; + dev_info(&oct->pci_dev->dev, + "VF driver changed VF%d's MAC address to %pM\n", + vf_idx, b + 2); + } + + for (i = 0; i < recv_pkt->buffer_count; i++) + recv_buffer_free(recv_pkt->buffer_ptr[i]); + octeon_free_recv_info(recv_info); + + return 0; +} + /** * \brief Device initialization for each Octeon device that is probed * @param octeon_dev octeon device @@ -4265,6 +4473,9 @@ static int octeon_device_init(struct octeon_device *octeon_dev) octeon_core_drv_init, octeon_dev); + octeon_register_dispatch_fn(octeon_dev, OPCODE_NIC, + OPCODE_NIC_VF_DRV_NOTICE, + octeon_recv_vf_drv_notice, octeon_dev); INIT_DELAYED_WORK(&octeon_dev->nic_poll_work.work, nic_starter); octeon_dev->nic_poll_work.ctxptr = (void *)octeon_dev; schedule_delayed_work(&octeon_dev->nic_poll_work.work, diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index caeff9a..edf1282 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -89,6 +89,10 @@ enum octeon_tag_type { #define OPCODE_NIC_TIMESTAMP 0x07 #define OPCODE_NIC_INTRMOD_CFG 0x08 #define OPCODE_NIC_IF_CFG 0x09 +#define OPCODE_NIC_VF_DRV_NOTICE 0x0A +#define VF_DRV_LOADED 1 +#define VF_DRV_REMOVED -1 +#define VF_DRV_MACADDR_CHANGED 2 #define CORE_DRV_TEST_SCATTER_OP 0xFFF5 @@ -235,6 +239,7 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_ID_ACTIVE 0x1a +#define OCTNET_CMD_SET_VF_LINKSTATE 0x1c #define OCTNET_CMD_VXLAN_PORT_ADD 0x0 #define OCTNET_CMD_VXLAN_PORT_DEL 0x1 #define OCTNET_CMD_RXCSUM_ENABLE 0x0 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index bdb10a0..70a0d98 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -354,6 +354,14 @@ struct octeon_sriov_info { /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/ struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS]; + + u64 vf_macaddr[MAX_POSSIBLE_VFS]; + + u16 vf_vlantci[MAX_POSSIBLE_VFS]; + + int vf_linkstate[MAX_POSSIBLE_VFS]; + + u64 vf_drv_loaded_mask; }; struct octeon_ioq_vector { -- cgit v1.1 From 515e752d2b877f79eefb9c512d7ab63f082bbe3a Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:44 -0800 Subject: liquidio CN23XX: device states Cleaned up resource leaks during destroy resources by introducing more device states. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 33 ++++++++++++++++------ .../net/ethernet/cavium/liquidio/octeon_device.c | 6 +++- .../net/ethernet/cavium/liquidio/octeon_device.h | 29 ++++++++++--------- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 13 +++++---- drivers/net/ethernet/cavium/liquidio/octeon_main.h | 8 ++++-- .../net/ethernet/cavium/liquidio/request_manager.c | 6 +++- 6 files changed, 64 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 8c82cd3..8aa862b 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -770,6 +770,7 @@ static void delete_glists(struct lio *lio) } kfree((void *)lio->glist); + kfree((void *)lio->glist_lock); } /** @@ -1329,6 +1330,7 @@ liquidio_probe(struct pci_dev *pdev, complete(&first_stage); if (octeon_device_init(oct_dev)) { + complete(&hs->init); liquidio_remove(pdev); return -ENOMEM; } @@ -1353,7 +1355,15 @@ liquidio_probe(struct pci_dev *pdev, oct_dev->watchdog_task = kthread_create( liquidio_watchdog, oct_dev, "liowd/%02hhx:%02hhx.%hhx", bus, device, function); - wake_up_process(oct_dev->watchdog_task); + if (!IS_ERR(oct_dev->watchdog_task)) { + wake_up_process(oct_dev->watchdog_task); + } else { + oct_dev->watchdog_task = NULL; + dev_err(&oct_dev->pci_dev->dev, + "failed to create kernel_thread\n"); + liquidio_remove(pdev); + return -1; + } } } @@ -1417,6 +1427,8 @@ static void octeon_destroy_resources(struct octeon_device *oct) if (lio_wait_for_oq_pkts(oct)) dev_err(&oct->pci_dev->dev, "OQ had pending packets\n"); + /* fallthrough */ + case OCT_DEV_INTR_SET_DONE: /* Disable interrupts */ oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); @@ -1443,6 +1455,8 @@ static void octeon_destroy_resources(struct octeon_device *oct) pci_disable_msi(oct->pci_dev); } + /* fallthrough */ + case OCT_DEV_MSIX_ALLOC_VECTOR_DONE: if (OCTEON_CN23XX_PF(oct)) octeon_free_ioq_vector(oct); @@ -1508,10 +1522,13 @@ static void octeon_destroy_resources(struct octeon_device *oct) octeon_unmap_pci_barx(oct, 1); /* fallthrough */ - case OCT_DEV_BEGIN_STATE: + case OCT_DEV_PCI_ENABLE_DONE: + pci_clear_master(oct->pci_dev); /* Disable the device, releasing the PCI INT */ pci_disable_device(oct->pci_dev); + /* fallthrough */ + case OCT_DEV_BEGIN_STATE: /* Nothing to be done here either */ break; } /* end switch (oct->status) */ @@ -1781,6 +1798,7 @@ static int octeon_pci_os_setup(struct octeon_device *oct) if (dma_set_mask_and_coherent(&oct->pci_dev->dev, DMA_BIT_MASK(64))) { dev_err(&oct->pci_dev->dev, "Unexpected DMA device capability\n"); + pci_disable_device(oct->pci_dev); return 1; } @@ -4434,6 +4452,8 @@ static int octeon_device_init(struct octeon_device *octeon_dev) if (octeon_pci_os_setup(octeon_dev)) return 1; + atomic_set(&octeon_dev->status, OCT_DEV_PCI_ENABLE_DONE); + /* Identify the Octeon type and map the BAR address space. */ if (octeon_chip_specific_setup(octeon_dev)) { dev_err(&octeon_dev->pci_dev->dev, "Chip specific setup failed\n"); @@ -4505,9 +4525,6 @@ static int octeon_device_init(struct octeon_device *octeon_dev) if (octeon_setup_instr_queues(octeon_dev)) { dev_err(&octeon_dev->pci_dev->dev, "instruction queue initialization failed\n"); - /* On error, release any previously allocated queues */ - for (j = 0; j < octeon_dev->num_iqs; j++) - octeon_delete_instr_queue(octeon_dev, j); return 1; } atomic_set(&octeon_dev->status, OCT_DEV_INSTR_QUEUE_INIT_DONE); @@ -4523,9 +4540,6 @@ static int octeon_device_init(struct octeon_device *octeon_dev) if (octeon_setup_output_queues(octeon_dev)) { dev_err(&octeon_dev->pci_dev->dev, "Output queue initialization failed\n"); - /* Release any previously allocated queues */ - for (j = 0; j < octeon_dev->num_oqs; j++) - octeon_delete_droq(octeon_dev, j); return 1; } @@ -4542,6 +4556,7 @@ static int octeon_device_init(struct octeon_device *octeon_dev) dev_err(&octeon_dev->pci_dev->dev, "OCTEON: ioq vector allocation failed\n"); return 1; } + atomic_set(&octeon_dev->status, OCT_DEV_MSIX_ALLOC_VECTOR_DONE); } else { /* The input and output queue registers were setup earlier (the @@ -4569,6 +4584,8 @@ static int octeon_device_init(struct octeon_device *octeon_dev) /* Enable Octeon device interrupts */ octeon_dev->fn_list.enable_interrupt(octeon_dev, OCTEON_ALL_INTR); + atomic_set(&octeon_dev->status, OCT_DEV_INTR_SET_DONE); + /* Enable the input and output queues for this Octeon device */ ret = octeon_dev->fn_list.enable_io_queues(octeon_dev); if (ret) { diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 7f532fd..295e2ca 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -822,6 +822,7 @@ int octeon_setup_instr_queues(struct octeon_device *oct) if (octeon_init_instr_queue(oct, txpciq, num_descs)) { /* prevent memory leak */ vfree(oct->instr_queue[0]); + oct->instr_queue[0] = NULL; return 1; } @@ -854,8 +855,11 @@ int octeon_setup_output_queues(struct octeon_device *oct) if (!oct->droq[0]) return 1; - if (octeon_init_droq(oct, oq_no, num_descs, desc_size, NULL)) + if (octeon_init_droq(oct, oq_no, num_descs, desc_size, NULL)) { + vfree(oct->droq[oq_no]); + oct->droq[oq_no] = NULL; return 1; + } oct->num_oqs++; return 0; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 70a0d98..6c18baa 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -72,20 +72,23 @@ enum octeon_pci_swap_mode { * as it is initialized. */ #define OCT_DEV_BEGIN_STATE 0x0 -#define OCT_DEV_PCI_MAP_DONE 0x1 -#define OCT_DEV_DISPATCH_INIT_DONE 0x2 -#define OCT_DEV_INSTR_QUEUE_INIT_DONE 0x3 -#define OCT_DEV_SC_BUFF_POOL_INIT_DONE 0x4 -#define OCT_DEV_RESP_LIST_INIT_DONE 0x5 -#define OCT_DEV_DROQ_INIT_DONE 0x6 +#define OCT_DEV_PCI_ENABLE_DONE 0x1 +#define OCT_DEV_PCI_MAP_DONE 0x2 +#define OCT_DEV_DISPATCH_INIT_DONE 0x3 +#define OCT_DEV_INSTR_QUEUE_INIT_DONE 0x4 +#define OCT_DEV_SC_BUFF_POOL_INIT_DONE 0x5 +#define OCT_DEV_RESP_LIST_INIT_DONE 0x6 +#define OCT_DEV_DROQ_INIT_DONE 0x7 #define OCT_DEV_MBOX_SETUP_DONE 0x8 -#define OCT_DEV_IO_QUEUES_DONE 0x9 -#define OCT_DEV_CONSOLE_INIT_DONE 0xa -#define OCT_DEV_HOST_OK 0xb -#define OCT_DEV_CORE_OK 0xc -#define OCT_DEV_RUNNING 0xd -#define OCT_DEV_IN_RESET 0xe -#define OCT_DEV_STATE_INVALID 0xf +#define OCT_DEV_MSIX_ALLOC_VECTOR_DONE 0x9 +#define OCT_DEV_INTR_SET_DONE 0xa +#define OCT_DEV_IO_QUEUES_DONE 0xb +#define OCT_DEV_CONSOLE_INIT_DONE 0xc +#define OCT_DEV_HOST_OK 0xd +#define OCT_DEV_CORE_OK 0xe +#define OCT_DEV_RUNNING 0xf +#define OCT_DEV_IN_RESET 0x10 +#define OCT_DEV_STATE_INVALID 0x11 #define OCT_DEV_STATES OCT_DEV_STATE_INVALID diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index f60e532..886987f 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -988,7 +988,8 @@ int octeon_create_droq(struct octeon_device *oct, if (!droq) droq = vmalloc(sizeof(*droq)); if (!droq) - goto create_droq_fail; + return -1; + memset(droq, 0, sizeof(struct octeon_droq)); /*Disable the pkt o/p for this Q */ @@ -996,7 +997,11 @@ int octeon_create_droq(struct octeon_device *oct, oct->droq[q_no] = droq; /* Initialize the Droq */ - octeon_init_droq(oct, q_no, num_descs, desc_size, app_ctx); + if (octeon_init_droq(oct, q_no, num_descs, desc_size, app_ctx)) { + vfree(oct->droq[q_no]); + oct->droq[q_no] = NULL; + return -1; + } oct->num_oqs++; @@ -1009,8 +1014,4 @@ int octeon_create_droq(struct octeon_device *oct, * the same time. */ return 0; - -create_droq_fail: - octeon_delete_droq(oct, q_no); - return -ENOMEM; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h index 3cddf28..3afa61e 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h @@ -116,7 +116,7 @@ static inline int octeon_map_pci_barx(struct octeon_device *oct, mapped_len = oct->mmio[baridx].len; if (!mapped_len) - return 1; + goto err_release_region; if (max_map_len && (mapped_len > max_map_len)) mapped_len = max_map_len; @@ -132,11 +132,15 @@ static inline int octeon_map_pci_barx(struct octeon_device *oct, if (!oct->mmio[baridx].hw_addr) { dev_err(&oct->pci_dev->dev, "error ioremap for bar %d\n", baridx); - return 1; + goto err_release_region; } oct->mmio[baridx].done = 1; return 0; + +err_release_region: + pci_release_region(oct->pci_dev, baridx * 2); + return 1; } static inline void * diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 90866bb..a73560c 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -157,6 +157,8 @@ int octeon_init_instr_queue(struct octeon_device *oct, WQ_MEM_RECLAIM, 0); if (!oct->check_db_wq[iq_no].wq) { + vfree(iq->request_list); + iq->request_list = NULL; lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma); dev_err(&oct->pci_dev->dev, "check db wq create failed for iq %d\n", iq_no); @@ -749,8 +751,10 @@ int octeon_setup_sc_buffer_pool(struct octeon_device *oct) lio_dma_alloc(oct, SOFT_COMMAND_BUFFER_SIZE, (dma_addr_t *)&dma_addr); - if (!sc) + if (!sc) { + octeon_free_sc_buffer_pool(oct); return 1; + } sc->dma_addr = dma_addr; sc->size = SOFT_COMMAND_BUFFER_SIZE; -- cgit v1.1 From 763185a38b7f520d1421eef782b32eec9281b2a1 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:45 -0800 Subject: liquidio CN23XX: code cleanup Cleaned up unnecessary comments and added some minor macros. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/cn66xx_device.c | 13 ++++--------- drivers/net/ethernet/cavium/liquidio/cn66xx_device.h | 4 ++-- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 14 ++++---------- drivers/net/ethernet/cavium/liquidio/lio_main.c | 16 +++++----------- drivers/net/ethernet/cavium/liquidio/liquidio_common.h | 2 -- drivers/net/ethernet/cavium/liquidio/octeon_device.c | 8 ++++---- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 2 +- drivers/net/ethernet/cavium/liquidio/octeon_droq.h | 1 - drivers/net/ethernet/cavium/liquidio/octeon_iq.h | 1 - drivers/net/ethernet/cavium/liquidio/octeon_main.h | 18 ------------------ drivers/net/ethernet/cavium/liquidio/request_manager.c | 7 ++----- .../net/ethernet/cavium/liquidio/response_manager.c | 6 +----- .../net/ethernet/cavium/liquidio/response_manager.h | 1 - 13 files changed, 23 insertions(+), 70 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c index e779af8..1ebc225 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c @@ -275,7 +275,6 @@ void lio_cn6xxx_setup_iq_regs(struct octeon_device *oct, u32 iq_no) { struct octeon_instr_queue *iq = oct->instr_queue[iq_no]; - /* Disable Packet-by-Packet mode; No Parse Mode or Skip length */ octeon_write_csr64(oct, CN6XXX_SLI_IQ_PKT_INSTR_HDR64(iq_no), 0); /* Write the start of the input queue's ring and its size */ @@ -378,7 +377,7 @@ void lio_cn6xxx_disable_io_queues(struct octeon_device *oct) /* Reset the doorbell register for each Input queue. */ for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { - if (!(oct->io_qmask.iq & (1ULL << i))) + if (!(oct->io_qmask.iq & BIT_ULL(i))) continue; octeon_write_csr(oct, CN6XXX_SLI_IQ_DOORBELL(i), 0xFFFFFFFF); d32 = octeon_read_csr(oct, CN6XXX_SLI_IQ_DOORBELL(i)); @@ -400,9 +399,8 @@ void lio_cn6xxx_disable_io_queues(struct octeon_device *oct) ; /* Reset the doorbell register for each Output queue. */ - /* for (i = 0; i < oct->num_oqs; i++) { */ for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { - if (!(oct->io_qmask.oq & (1ULL << i))) + if (!(oct->io_qmask.oq & BIT_ULL(i))) continue; octeon_write_csr(oct, CN6XXX_SLI_OQ_PKTS_CREDIT(i), 0xFFFFFFFF); d32 = octeon_read_csr(oct, CN6XXX_SLI_OQ_PKTS_CREDIT(i)); @@ -537,15 +535,14 @@ static int lio_cn6xxx_process_droq_intr_regs(struct octeon_device *oct) oct->droq_intr = 0; - /* for (oq_no = 0; oq_no < oct->num_oqs; oq_no++) { */ for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); oq_no++) { - if (!(droq_mask & (1ULL << oq_no))) + if (!(droq_mask & BIT_ULL(oq_no))) continue; droq = oct->droq[oq_no]; pkt_count = octeon_droq_check_hw_for_pkts(droq); if (pkt_count) { - oct->droq_intr |= (1ULL << oq_no); + oct->droq_intr |= BIT_ULL(oq_no); if (droq->ops.poll_mode) { u32 value; u32 reg; @@ -721,8 +718,6 @@ int lio_setup_cn66xx_octeon_device(struct octeon_device *oct) int lio_validate_cn6xxx_config_info(struct octeon_device *oct, struct octeon_config *conf6xxx) { - /* int total_instrs = 0; */ - if (CFG_GET_IQ_MAX_Q(conf6xxx) > CN6XXX_MAX_INPUT_QUEUES) { dev_err(&oct->pci_dev->dev, "%s: Num IQ (%d) exceeds Max (%d)\n", __func__, CFG_GET_IQ_MAX_Q(conf6xxx), diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h index a40a913..32fbbb2 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h @@ -96,8 +96,8 @@ void lio_cn6xxx_setup_reg_address(struct octeon_device *oct, void *chip, struct octeon_reg_list *reg_list); u32 lio_cn6xxx_coprocessor_clock(struct octeon_device *oct); u32 lio_cn6xxx_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us); -int lio_setup_cn66xx_octeon_device(struct octeon_device *); +int lio_setup_cn66xx_octeon_device(struct octeon_device *oct); int lio_validate_cn6xxx_config_info(struct octeon_device *oct, - struct octeon_config *); + struct octeon_config *conf6xxx); #endif diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index f163e0a..1674522 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -757,9 +757,6 @@ lio_get_ethtool_stats(struct net_device *netdev, /*sum of oct->instr_queue[iq_no]->stats.tx_dropped */ data[i++] = CVM_CAST64(netstats->tx_dropped); - /*data[i++] = CVM_CAST64(stats->multicast); */ - /*data[i++] = CVM_CAST64(stats->collisions); */ - /* firmware tx stats */ /*per_core_stats[cvmx_get_core_num()].link_stats[mdata->from_ifidx]. *fromhost.fw_total_sent @@ -910,9 +907,8 @@ lio_get_ethtool_stats(struct net_device *netdev, /*lio->link_changes*/ data[i++] = CVM_CAST64(lio->link_changes); - /* TX -- lio_update_stats(lio); */ for (j = 0; j < MAX_OCTEON_INSTR_QUEUES(oct_dev); j++) { - if (!(oct_dev->io_qmask.iq & (1ULL << j))) + if (!(oct_dev->io_qmask.iq & BIT_ULL(j))) continue; /*packets to network port*/ /*# of packets tx to network */ @@ -954,9 +950,8 @@ lio_get_ethtool_stats(struct net_device *netdev, } /* RX */ - /* for (j = 0; j < oct_dev->num_oqs; j++) { */ for (j = 0; j < MAX_OCTEON_OUTPUT_QUEUES(oct_dev); j++) { - if (!(oct_dev->io_qmask.oq & (1ULL << j))) + if (!(oct_dev->io_qmask.oq & BIT_ULL(j))) continue; /*packets send to TCP/IP network stack */ @@ -1030,7 +1025,7 @@ static void lio_get_strings(struct net_device *netdev, u32 stringset, u8 *data) num_iq_stats = ARRAY_SIZE(oct_iq_stats_strings); for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct_dev); i++) { - if (!(oct_dev->io_qmask.iq & (1ULL << i))) + if (!(oct_dev->io_qmask.iq & BIT_ULL(i))) continue; for (j = 0; j < num_iq_stats; j++) { sprintf(data, "tx-%d-%s", i, @@ -1040,9 +1035,8 @@ static void lio_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } num_oq_stats = ARRAY_SIZE(oct_droq_stats_strings); - /* for (i = 0; i < oct_dev->num_oqs; i++) { */ for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct_dev); i++) { - if (!(oct_dev->io_qmask.oq & (1ULL << i))) + if (!(oct_dev->io_qmask.oq & BIT_ULL(i))) continue; for (j = 0; j < num_oq_stats; j++) { sprintf(data, "rx-%d-%s", i, diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 8aa862b..e206323 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -19,10 +19,8 @@ * This file may also be available under a different license from Cavium. * Contact Cavium, Inc. for more information **********************************************************************/ -#include #include #include -#include #include #include #include "liquidio_common.h" @@ -201,9 +199,8 @@ static void octeon_droq_bh(unsigned long pdev) struct octeon_device_priv *oct_priv = (struct octeon_device_priv *)oct->priv; - /* for (q_no = 0; q_no < oct->num_oqs; q_no++) { */ for (q_no = 0; q_no < MAX_OCTEON_OUTPUT_QUEUES(oct); q_no++) { - if (!(oct->io_qmask.oq & (1ULL << q_no))) + if (!(oct->io_qmask.oq & BIT_ULL(q_no))) continue; reschedule |= octeon_droq_process_packets(oct, oct->droq[q_no], MAX_PACKET_BUDGET); @@ -238,7 +235,7 @@ static int lio_wait_for_oq_pkts(struct octeon_device *oct) pending_pkts = 0; for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { - if (!(oct->io_qmask.oq & (1ULL << i))) + if (!(oct->io_qmask.oq & BIT_ULL(i))) continue; pkt_cnt += octeon_droq_check_hw_for_pkts(oct->droq[i]); } @@ -320,7 +317,7 @@ static inline void pcierror_quiesce_device(struct octeon_device *oct) for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { struct octeon_instr_queue *iq; - if (!(oct->io_qmask.iq & (1ULL << i))) + if (!(oct->io_qmask.iq & BIT_ULL(i))) continue; iq = oct->instr_queue[i]; @@ -386,7 +383,6 @@ static void stop_pci_io(struct octeon_device *oct) dev_dbg(&oct->pci_dev->dev, "Device state is now %s\n", lio_get_state_string(&oct->status)); - /* cn63xx_cleanup_aer_uncorrect_error_status(oct->pci_dev); */ /* making it a common function for all OCTEON models */ cleanup_aer_uncorrect_error_status(oct->pci_dev); } @@ -941,7 +937,6 @@ static inline void update_link_status(struct net_device *netdev, if (lio->linfo.link.s.link_up) { netif_carrier_on(netdev); - /* start_txq(netdev); */ txqs_wake(netdev); } else { netif_carrier_off(netdev); @@ -1019,7 +1014,7 @@ static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) if (oct->int_status & OCT_DEV_INTR_PKT_DATA) { for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); oq_no++) { - if (!(oct->droq_intr & (1ULL << oq_no))) + if (!(oct->droq_intr & BIT_ULL(oq_no))) continue; droq = oct->droq[oq_no]; @@ -1468,7 +1463,7 @@ static void octeon_destroy_resources(struct octeon_device *oct) /* fallthrough */ case OCT_DEV_IN_RESET: case OCT_DEV_DROQ_INIT_DONE: - /*atomic_set(&oct->status, OCT_DEV_DROQ_INIT_DONE);*/ + /* Wait for any pending operations */ mdelay(100); for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { if (!(oct->io_qmask.oq & BIT_ULL(i))) @@ -2461,7 +2456,6 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget) * Return back if tx_done is false. */ update_txq_status(oct, iq_no); - /*tx_done = (iq->flush_index == iq->octeon_read_index);*/ } else { dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", __func__, iq_no); diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index edf1282..1d2eba7 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -68,8 +68,6 @@ enum octeon_tag_type { */ #define OPCODE_CORE 0 /* used for generic core operations */ #define OPCODE_NIC 1 /* used for NIC operations */ -#define OPCODE_LAST OPCODE_NIC - /* Subcodes are used by host driver/apps to identify the sub-operation * for the core. They only need to by unique for a given subsystem. */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 295e2ca..278c880 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -649,12 +649,12 @@ void octeon_free_device_mem(struct octeon_device *oct) int i; for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { - if (oct->io_qmask.oq & (1ULL << i)) + if (oct->io_qmask.oq & BIT_ULL(i)) vfree(oct->droq[i]); } for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { - if (oct->io_qmask.iq & (1ULL << i)) + if (oct->io_qmask.iq & BIT_ULL(i)) vfree(oct->instr_queue[i]); } @@ -1148,7 +1148,7 @@ int octeon_get_tx_qsize(struct octeon_device *oct, u32 q_no) { if (oct && (q_no < MAX_OCTEON_INSTR_QUEUES(oct)) && - (oct->io_qmask.iq & (1ULL << q_no))) + (oct->io_qmask.iq & BIT_ULL(q_no))) return oct->instr_queue[q_no]->max_count; return -1; @@ -1157,7 +1157,7 @@ int octeon_get_tx_qsize(struct octeon_device *oct, u32 q_no) int octeon_get_rx_qsize(struct octeon_device *oct, u32 q_no) { if (oct && (q_no < MAX_OCTEON_OUTPUT_QUEUES(oct)) && - (oct->io_qmask.oq & (1ULL << q_no))) + (oct->io_qmask.oq & BIT_ULL(q_no))) return oct->droq[q_no]->max_count; return -1; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 886987f..8f6fc95 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -337,7 +337,7 @@ int octeon_init_droq(struct octeon_device *oct, /* For 56xx Pass1, this function won't be called, so no checks. */ oct->fn_list.setup_oq_regs(oct, q_no); - oct->io_qmask.oq |= (1ULL << q_no); + oct->io_qmask.oq |= BIT_ULL(q_no); return 0; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h index 5be002d..d591f5f 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h @@ -121,7 +121,6 @@ struct oct_droq_stats { /** Num of Packets dropped due to receive path failures. */ u64 rx_dropped; - /** Num of vxlan packets received; */ u64 rx_vxlan; /** Num of failures of recv_buffer_alloc() */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h index e4d426b..b493e67 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h @@ -69,7 +69,6 @@ struct oct_iq_stats { u64 tx_vxlan; /* tunnel */ u64 tx_dmamap_fail; u64 tx_restart; - /*u64 tx_timeout_count;*/ }; #define OCT_IQ_STATS_SIZE (sizeof(struct oct_iq_stats)) diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h index 3afa61e..db86c63 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h @@ -207,24 +207,6 @@ out: return errno; } -static inline void -sleep_atomic_cond(wait_queue_head_t *waitq, atomic_t *pcond) -{ - wait_queue_t we; - - init_waitqueue_entry(&we, current); - add_wait_queue(waitq, &we); - while (!atomic_read(pcond)) { - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) - goto out; - schedule(); - } -out: - set_current_state(TASK_RUNNING); - remove_wait_queue(waitq, &we); -} - /* Gives up the CPU for a timeout period. * Check that the condition is not true before we go to sleep for a * timeout period. diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index a73560c..f6e1616 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -145,7 +145,7 @@ int octeon_init_instr_queue(struct octeon_device *oct, spin_lock_init(&iq->iq_flush_running_lock); - oct->io_qmask.iq |= (1ULL << iq_no); + oct->io_qmask.iq |= BIT_ULL(iq_no); /* Set the 32B/64B mode for each input queue */ oct->io_qmask.iq64B |= ((conf->instr_type == 64) << iq_no); @@ -252,9 +252,8 @@ int lio_wait_for_instr_fetch(struct octeon_device *oct) do { instr_cnt = 0; - /*for (i = 0; i < oct->num_iqs; i++) {*/ for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { - if (!(oct->io_qmask.iq & (1ULL << i))) + if (!(oct->io_qmask.iq & BIT_ULL(i))) continue; pending = atomic_read(&oct-> @@ -579,8 +578,6 @@ octeon_send_command(struct octeon_device *oct, u32 iq_no, /* This is only done here to expedite packets being flushed * for cases where there are no IQ completion interrupts. */ - /*if (iq->do_auto_flush)*/ - /* octeon_flush_iq(oct, iq, 2, 0);*/ return st.status; } diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c index be52178d..9e496c1 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c @@ -81,11 +81,7 @@ int lio_process_ordered_list(struct octeon_device *octeon_dev, spin_lock_bh(&ordered_sc_list->lock); if (ordered_sc_list->head.next == &ordered_sc_list->head) { - /* ordered_sc_list is empty; there is - * nothing to process - */ - spin_unlock_bh - (&ordered_sc_list->lock); + spin_unlock_bh(&ordered_sc_list->lock); return 1; } diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.h b/drivers/net/ethernet/cavium/liquidio/response_manager.h index 7a48752..d377faf 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.h +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.h @@ -85,7 +85,6 @@ enum { /** A value of 0x00000000 indicates no error i.e. success */ #define DRIVER_ERROR_NONE 0x00000000 -/** (Major number: 0x0000; Minor Number: 0x0001) */ #define DRIVER_ERROR_REQ_PENDING 0x00000001 #define DRIVER_ERROR_REQ_TIMEOUT 0x00000003 #define DRIVER_ERROR_REQ_EINTR 0x00000004 -- cgit v1.1 From 50579d3d951f7297e3e5af71832baacaa529de8b Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:46 -0800 Subject: liquidio CN23XX: copyrights changes and alignment Updated copyrights comments and also changed some other comments alignments. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_pf_device.c | 53 ++++++++++------------ .../ethernet/cavium/liquidio/cn23xx_pf_device.h | 39 +++++++--------- .../net/ethernet/cavium/liquidio/cn23xx_pf_regs.h | 39 +++++++--------- .../net/ethernet/cavium/liquidio/cn66xx_device.c | 36 +++++++-------- .../net/ethernet/cavium/liquidio/cn66xx_device.h | 37 +++++++-------- drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h | 37 +++++++-------- .../net/ethernet/cavium/liquidio/cn68xx_device.c | 36 +++++++-------- .../net/ethernet/cavium/liquidio/cn68xx_device.h | 37 +++++++-------- drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h | 37 +++++++-------- drivers/net/ethernet/cavium/liquidio/lio_core.c | 36 +++++++-------- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 42 ++++++++--------- drivers/net/ethernet/cavium/liquidio/lio_main.c | 36 +++++++-------- .../net/ethernet/cavium/liquidio/liquidio_common.h | 37 +++++++-------- .../net/ethernet/cavium/liquidio/liquidio_image.h | 36 +++++++-------- .../net/ethernet/cavium/liquidio/octeon_config.h | 37 +++++++-------- .../net/ethernet/cavium/liquidio/octeon_console.c | 43 ++++++++---------- .../net/ethernet/cavium/liquidio/octeon_device.c | 36 +++++++-------- .../net/ethernet/cavium/liquidio/octeon_device.h | 45 ++++++++---------- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 36 +++++++-------- drivers/net/ethernet/cavium/liquidio/octeon_droq.h | 17 +++---- drivers/net/ethernet/cavium/liquidio/octeon_iq.h | 21 ++++----- drivers/net/ethernet/cavium/liquidio/octeon_main.h | 19 +++----- .../net/ethernet/cavium/liquidio/octeon_mem_ops.c | 5 +- .../net/ethernet/cavium/liquidio/octeon_mem_ops.h | 5 +- .../net/ethernet/cavium/liquidio/octeon_network.h | 5 +- drivers/net/ethernet/cavium/liquidio/octeon_nic.c | 5 +- drivers/net/ethernet/cavium/liquidio/octeon_nic.h | 5 +- .../net/ethernet/cavium/liquidio/request_manager.c | 5 +- .../ethernet/cavium/liquidio/response_manager.c | 5 +- .../ethernet/cavium/liquidio/response_manager.h | 5 +- 30 files changed, 352 insertions(+), 480 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index d01b00b..962dcbc 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1,27 +1,21 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include -#include #include #include #include "liquidio_common.h" @@ -421,10 +415,10 @@ static int cn23xx_pf_setup_global_input_regs(struct octeon_device *oct) return -1; /** Set the MAC_NUM and PVF_NUM in IQ_PKT_CONTROL reg - * for all queues.Only PF can set these bits. - * bits 29:30 indicate the MAC num. - * bits 32:47 indicate the PVF num. - */ + * for all queues.Only PF can set these bits. + * bits 29:30 indicate the MAC num. + * bits 32:47 indicate the PVF num. + */ for (q_no = 0; q_no < ern; q_no++) { reg_val = oct->pcie_port << CN23XX_PKT_INPUT_CTL_MAC_NUM_POS; @@ -547,8 +541,8 @@ static void cn23xx_pf_setup_global_output_regs(struct octeon_device *oct) writeq(0x40, (u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_OQ_WMARK); /** Disabling setting OQs in reset when ring has no dorebells - * enabling this will cause of head of line blocking - */ + * enabling this will cause of head of line blocking + */ /* Do it only for pass1.1. and pass1.2 */ if ((oct->rev_id == OCTEON_CN23XX_REV_1_0) || (oct->rev_id == OCTEON_CN23XX_REV_1_1)) @@ -1391,8 +1385,7 @@ void cn23xx_dump_iq_regs(struct octeon_device *oct) dev_dbg(&oct->pci_dev->dev, "SLI_PKT[%d]_INPUT_CTL [0x%x]: 0x%016llx\n", q_no, CN23XX_SLI_IQ_PKT_CONTROL64(q_no), CVM_CAST64(octeon_read_csr64 - (oct, - CN23XX_SLI_IQ_PKT_CONTROL64(q_no)))); + (oct, CN23XX_SLI_IQ_PKT_CONTROL64(q_no)))); } pci_read_config_dword(oct->pci_dev, CN23XX_CONFIG_PCIE_DEVCTL, ®val); diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h index cee346a..2fedd91 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h @@ -1,28 +1,23 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file cn23xx_device.h * \brief Host Driver: Routines that perform CN23XX specific operations. -*/ + */ #ifndef __CN23XX_PF_DEVICE_H__ #define __CN23XX_PF_DEVICE_H__ diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h index 03d79d9..680a405 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h @@ -1,29 +1,24 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file cn23xx_regs.h * \brief Host Driver: Register Address and Register Mask values for * Octeon CN23XX devices. -*/ + */ #ifndef __CN23XX_PF_REGS_H__ #define __CN23XX_PF_REGS_H__ diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c index 1ebc225..bdec051 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include #include #include "liquidio_common.h" diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h index 32fbbb2..8ed57134 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file cn66xx_device.h * \brief Host Driver: Routines that perform CN66XX specific operations. */ diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h b/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h index 5e3aff2..23152c0 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h +++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file cn66xx_regs.h * \brief Host Driver: Register Address and Register Mask values for * Octeon CN66XX devices. diff --git a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c index dbf3566..1e40f1a 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include #include #include "liquidio_common.h" diff --git a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h index ea7bdcc..66b8d6b 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file cn68xx_device.h * \brief Host Driver: Routines that perform CN68XX specific operations. */ diff --git a/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h b/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h index d45a0f4..0b742f0 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h +++ b/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file cn68xx_regs.h * \brief Host Driver: Register Address and Register Mask values for * Octeon CN68XX devices. The register map for CN66XX is the same diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 4997ec2..403bcaa 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include #include #include "liquidio_common.h" diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 1674522..c7c39a0 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include #include #include @@ -87,9 +83,9 @@ static const char oct_stats_strings[][ETH_GSTRING_LEN] = { "tx_bytes", "rx_errors", /*jabber_err+l2_err+frame_err */ "tx_errors", /*fw_err_pko+fw_err_link+fw_err_drop */ - "rx_dropped", /*st->fromwire.total_rcvd - st->fromwire.fw_total_rcvd - *+st->fromwire.dmac_drop + st->fromwire.fw_err_drop - */ + "rx_dropped", /*st->fromwire.total_rcvd - st->fromwire.fw_total_rcvd + + *st->fromwire.dmac_drop + st->fromwire.fw_err_drop + */ "tx_dropped", "tx_total_sent", diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index e206323..0d4c584 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include #include #include diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 1d2eba7..82a4eed 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file liquidio_common.h * \brief Common: Structures and macros used in PCI-NIC package by core and * host driver. diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_image.h b/drivers/net/ethernet/cavium/liquidio/liquidio_image.h index 93819bd..78a3685 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_image.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_image.h @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #ifndef _LIQUIDIO_IMAGE_H_ #define _LIQUIDIO_IMAGE_H_ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_config.h b/drivers/net/ethernet/cavium/liquidio/octeon_config.h index 71daf82..1cb3514 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_config.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_config.h @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file octeon_config.h * \brief Host Driver: Configuration data structures for the host driver. */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index 01a50f3..c0089ed 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /** * @file octeon_console.c */ @@ -76,9 +71,9 @@ MODULE_PARM_DESC(console_bitmask, #define OCTEON_CONSOLE_POLL_INTERVAL_MS 100 /* 10 times per second */ /* First three members of cvmx_bootmem_desc are left in original -** positions for backwards compatibility. -** Assumes big endian target -*/ + * positions for backwards compatibility. + * Assumes big endian target + */ struct cvmx_bootmem_desc { /** spinlock to control access to list */ u32 lock; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 278c880..4a30d7c 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include #include #include diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 6c18baa..c59b77e 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -1,25 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ - + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file octeon_device.h * \brief Host Driver: This file defines the octeon device structure. */ @@ -93,9 +88,9 @@ enum octeon_pci_swap_mode { #define OCT_DEV_STATES OCT_DEV_STATE_INVALID /** Octeon Device interrupts - * These interrupt bits are set in int_status filed of - * octeon_device structure - */ + * These interrupt bits are set in int_status filed of + * octeon_device structure + */ #define OCT_DEV_INTR_DMA0_FORCE 0x01 #define OCT_DEV_INTR_DMA1_FORCE 0x02 #define OCT_DEV_INTR_PKT_DATA 0x04 @@ -548,7 +543,7 @@ struct oct_intrmod_cmd { void octeon_init_device_list(int conf_type); /** Free memory for Input and Output queue structures for a octeon device */ -void octeon_free_device_mem(struct octeon_device *); +void octeon_free_device_mem(struct octeon_device *oct); /* Look up a free entry in the octeon_device table and allocate resources * for the octeon_device structure for an octeon device. Called at init diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 8f6fc95..d7ba000 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -1,24 +1,20 @@ /********************************************************************** -* Author: Cavium, Inc. -* -* Contact: support@cavium.com -* Please include "LiquidIO" in the subject. -* -* Copyright (c) 2003-2015 Cavium, Inc. -* -* This file is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License, Version 2, as -* published by the Free Software Foundation. -* -* This file is distributed in the hope that it will be useful, but -* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or -* NONINFRINGEMENT. See the GNU General Public License for more -* details. -* -* This file may also be available under a different license from Cavium. -* Contact Cavium, Inc. for more information -**********************************************************************/ + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ #include #include #include diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h index d591f5f..e620740 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -13,13 +13,8 @@ * This file is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information - **********************************************************************/ - + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file octeon_droq.h * \brief Implementation of Octeon Output queues. "Output" is with * respect to the Octeon device on the NIC. From this driver's point of @@ -81,7 +76,7 @@ struct octeon_skb_page_info { * the Octeon device. Since the descriptor ring keeps physical (bus) * addresses, this field is required for the driver to keep track of * the virtual address pointers. -*/ + */ struct octeon_recv_buffer { /** Packet buffer, including metadata. */ void *buffer; @@ -358,7 +353,7 @@ struct octeon_droq { * @param q_no - droq no. ranges from 0 - 3. * @param app_ctx - pointer to application context * @return Success: 0 Failure: 1 -*/ + */ int octeon_init_droq(struct octeon_device *oct_dev, u32 q_no, u32 num_descs, @@ -371,7 +366,7 @@ int octeon_init_droq(struct octeon_device *oct_dev, * @param oct_dev - pointer to the octeon device structure * @param q_no - droq no. ranges from 0 - 3. * @return: Success: 0 Failure: 1 -*/ + */ int octeon_delete_droq(struct octeon_device *oct_dev, u32 q_no); /** Register a change in droq operations. The ops field has a pointer to a diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h index b493e67..c67152a 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -13,13 +13,8 @@ * This file is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information - **********************************************************************/ - + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file octeon_iq.h * \brief Host Driver: Implementation of Octeon input queues. "Input" is * with respect to the Octeon device on the NIC. From this driver's @@ -77,7 +72,7 @@ struct oct_iq_stats { * The input queue is used to post raw (instruction) mode data or packet * data to Octeon device from the host. Each input queue (upto 4) for * a Octeon device has one such structure to represent it. -*/ + */ struct octeon_instr_queue { struct octeon_device *oct_dev; @@ -117,8 +112,8 @@ struct octeon_instr_queue { u32 octeon_read_index; /** This index aids in finding the window in the queue where Octeon - * has read the commands. - */ + * has read the commands. + */ u32 flush_index; /** This field keeps track of the instructions pending in this queue. */ @@ -149,8 +144,8 @@ struct octeon_instr_queue { u64 last_db_time; /** The doorbell timeout. If the doorbell was not rung for this time and - * fill_cnt is non-zero, ring the doorbell again. - */ + * fill_cnt is non-zero, ring the doorbell again. + */ u32 db_timeout; /** Statistics for this input queue. */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h index db86c63..8cd3891 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -13,13 +13,8 @@ * This file is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information - **********************************************************************/ - + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ /*! \file octeon_main.h * \brief Host Driver: This file is included by all host driver source files * to include common definitions. @@ -78,10 +73,10 @@ static inline void octeon_swap_8B_data(u64 *data, u32 blocks) } /** - * \brief unmaps a PCI BAR - * @param oct Pointer to Octeon device - * @param baridx bar index - */ + * \brief unmaps a PCI BAR + * @param oct Pointer to Octeon device + * @param baridx bar index + */ static inline void octeon_unmap_pci_barx(struct octeon_device *oct, int baridx) { dev_dbg(&oct->pci_dev->dev, "Freeing PCI mapped regions for Bar%d\n", diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c index 0dc081a..98d8066d 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ #include #include "liquidio_common.h" diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h index 11b1833..bae2fdd 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ /*! \file octeon_mem_ops.h diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 54b9665..172b70e 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ /*! \file octeon_network.h diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_nic.c b/drivers/net/ethernet/cavium/liquidio/octeon_nic.c index 40ac1fe..c3d6a82 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_nic.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_nic.c @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ #include #include diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h index 4b8da67b..b059979 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ /*! \file octeon_nic.h diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index f6e1616..0cc1583 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ #include #include diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c index 9e496c1..fdaf742 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ #include #include diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.h b/drivers/net/ethernet/cavium/liquidio/response_manager.h index d377faf..cbb2d84 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.h +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.h @@ -4,7 +4,7 @@ * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * - * Copyright (c) 2003-2015 Cavium, Inc. + * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as @@ -15,9 +15,6 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium, Inc. for more information **********************************************************************/ /*! \file response_manager.h -- cgit v1.1 From 97a2532660c41118ba7bf9ddc11f6cb233fd6234 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 14 Nov 2016 15:54:47 -0800 Subject: liquidio CN23XX: fix for new check patch errors New checkpatch script shows some errors with pre-existing driver. This patch provides fix for those errors. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../net/ethernet/cavium/liquidio/cn23xx_pf_regs.h | 12 +-- drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h | 12 +-- .../net/ethernet/cavium/liquidio/cn68xx_device.c | 2 +- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 9 +- drivers/net/ethernet/cavium/liquidio/lio_main.c | 15 +-- .../net/ethernet/cavium/liquidio/liquidio_common.h | 50 ++++----- .../net/ethernet/cavium/liquidio/octeon_console.c | 113 ++++++++++----------- .../net/ethernet/cavium/liquidio/octeon_device.c | 28 ++--- .../net/ethernet/cavium/liquidio/octeon_device.h | 25 +++-- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 40 ++++---- drivers/net/ethernet/cavium/liquidio/octeon_iq.h | 3 + .../net/ethernet/cavium/liquidio/octeon_mem_ops.c | 2 +- .../net/ethernet/cavium/liquidio/octeon_network.h | 6 +- drivers/net/ethernet/cavium/liquidio/octeon_nic.h | 2 +- .../net/ethernet/cavium/liquidio/request_manager.c | 16 ++- 15 files changed, 155 insertions(+), 180 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h index 680a405..e6d4ad9 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h @@ -58,7 +58,7 @@ #define CN23XX_CONFIG_SRIOV_BAR_START 0x19C #define CN23XX_CONFIG_SRIOV_BARX(i) \ - (CN23XX_CONFIG_SRIOV_BAR_START + (i * 4)) + (CN23XX_CONFIG_SRIOV_BAR_START + ((i) * 4)) #define CN23XX_CONFIG_SRIOV_BAR_PF 0x08 #define CN23XX_CONFIG_SRIOV_BAR_64BIT 0x04 #define CN23XX_CONFIG_SRIOV_BAR_IO 0x01 @@ -508,7 +508,7 @@ /* 4 Registers (64 - bit) */ #define CN23XX_SLI_S2M_PORT_CTL_START 0x23D80 #define CN23XX_SLI_S2M_PORTX_CTL(port) \ - (CN23XX_SLI_S2M_PORT_CTL_START + (port * 0x10)) + (CN23XX_SLI_S2M_PORT_CTL_START + ((port) * 0x10)) #define CN23XX_SLI_MAC_NUMBER 0x20050 @@ -549,26 +549,26 @@ * Provides DMA Engine Queue Enable */ #define CN23XX_DPI_DMA_ENG0_ENB 0x0001df0000000080ULL -#define CN23XX_DPI_DMA_ENG_ENB(eng) (CN23XX_DPI_DMA_ENG0_ENB + (eng * 8)) +#define CN23XX_DPI_DMA_ENG_ENB(eng) (CN23XX_DPI_DMA_ENG0_ENB + ((eng) * 8)) /* 8 register (64-bit) - DPI_DMA(0..7)_REQQ_CTL * Provides control bits for transaction on 8 Queues */ #define CN23XX_DPI_DMA_REQQ0_CTL 0x0001df0000000180ULL #define CN23XX_DPI_DMA_REQQ_CTL(q_no) \ - (CN23XX_DPI_DMA_REQQ0_CTL + (q_no * 8)) + (CN23XX_DPI_DMA_REQQ0_CTL + ((q_no) * 8)) /* 6 register (64-bit) - DPI_ENG(0..5)_BUF * Provides DMA Engine FIFO (Queue) Size */ #define CN23XX_DPI_DMA_ENG0_BUF 0x0001df0000000880ULL #define CN23XX_DPI_DMA_ENG_BUF(eng) \ - (CN23XX_DPI_DMA_ENG0_BUF + (eng * 8)) + (CN23XX_DPI_DMA_ENG0_BUF + ((eng) * 8)) /* 4 Registers (64-bit) */ #define CN23XX_DPI_SLI_PRT_CFG_START 0x0001df0000000900ULL #define CN23XX_DPI_SLI_PRTX_CFG(port) \ - (CN23XX_DPI_SLI_PRT_CFG_START + (port * 0x8)) + (CN23XX_DPI_SLI_PRT_CFG_START + ((port) * 0x8)) /* Masks for DPI_DMA_CONTROL Register */ #define CN23XX_DPI_DMA_COMMIT_MODE BIT_ULL(58) diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h b/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h index 23152c0..b248966 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h +++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h @@ -438,10 +438,10 @@ #define CN6XXX_SLI_S2M_PORT0_CTL 0x3D80 #define CN6XXX_SLI_S2M_PORT1_CTL 0x3D90 #define CN6XXX_SLI_S2M_PORTX_CTL(port) \ - (CN6XXX_SLI_S2M_PORT0_CTL + (port * 0x10)) + (CN6XXX_SLI_S2M_PORT0_CTL + ((port) * 0x10)) #define CN6XXX_SLI_INT_ENB64(port) \ - (CN6XXX_SLI_INT_ENB64_PORT0 + (port * 0x10)) + (CN6XXX_SLI_INT_ENB64_PORT0 + ((port) * 0x10)) #define CN6XXX_SLI_MAC_NUMBER 0x3E00 @@ -453,7 +453,7 @@ #define CN6XXX_PCI_BAR1_OFFSET 0x8 #define CN6XXX_BAR1_REG(idx, port) \ - (CN6XXX_BAR1_INDEX_START + (port * CN6XXX_PEM_OFFSET) + \ + (CN6XXX_BAR1_INDEX_START + ((port) * CN6XXX_PEM_OFFSET) + \ (CN6XXX_PCI_BAR1_OFFSET * (idx))) /*############################ DPI #########################*/ @@ -471,17 +471,17 @@ #define CN6XXX_DPI_DMA_ENG0_ENB 0x0001df0000000080ULL #define CN6XXX_DPI_DMA_ENG_ENB(q_no) \ - (CN6XXX_DPI_DMA_ENG0_ENB + (q_no * 8)) + (CN6XXX_DPI_DMA_ENG0_ENB + ((q_no) * 8)) #define CN6XXX_DPI_DMA_ENG0_BUF 0x0001df0000000880ULL #define CN6XXX_DPI_DMA_ENG_BUF(q_no) \ - (CN6XXX_DPI_DMA_ENG0_BUF + (q_no * 8)) + (CN6XXX_DPI_DMA_ENG0_BUF + ((q_no) * 8)) #define CN6XXX_DPI_SLI_PRT0_CFG 0x0001df0000000900ULL #define CN6XXX_DPI_SLI_PRT1_CFG 0x0001df0000000908ULL #define CN6XXX_DPI_SLI_PRTX_CFG(port) \ - (CN6XXX_DPI_SLI_PRT0_CFG + (port * 0x10)) + (CN6XXX_DPI_SLI_PRT0_CFG + ((port) * 0x10)) #define CN6XXX_DPI_DMA_COMMIT_MODE BIT_ULL(58) #define CN6XXX_DPI_DMA_PKT_HP BIT_ULL(57) diff --git a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c index 1e40f1a..50b533f 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c @@ -72,7 +72,7 @@ static void lio_cn68xx_setup_pkt_ctl_regs(struct octeon_device *oct) pktctl = octeon_read_csr64(oct, CN6XXX_SLI_PKT_CTL); /* 68XX specific */ - max_oqs = CFG_GET_OQ_MAX_Q(CHIP_FIELD(oct, cn6xxx, conf)); + max_oqs = CFG_GET_OQ_MAX_Q(CHIP_CONF(oct, cn6xxx)); tx_pipe = octeon_read_csr64(oct, CN68XX_SLI_TX_PIPE); tx_pipe &= 0xffffffffff00ffffULL; /* clear out NUMP field */ tx_pipe |= max_oqs << 16; /* put max_oqs in NUMP field */ diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index c7c39a0..e233796 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -70,7 +70,6 @@ enum { INTERFACE_MODE_MIXED, }; -#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) #define OCT_ETHTOOL_REGDUMP_LEN 4096 #define OCT_ETHTOOL_REGDUMP_LEN_23XX (4096 * 11) #define OCT_ETHTOOL_REGSVER 1 @@ -255,14 +254,14 @@ lio_ethtool_get_channels(struct net_device *dev, u32 max_rx = 0, max_tx = 0, tx_count = 0, rx_count = 0; if (OCTEON_CN6XXX(oct)) { - struct octeon_config *conf6x = CHIP_FIELD(oct, cn6xxx, conf); + struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); max_rx = CFG_GET_OQ_MAX_Q(conf6x); max_tx = CFG_GET_IQ_MAX_Q(conf6x); rx_count = CFG_GET_NUM_RXQS_NIC_IF(conf6x, lio->ifidx); tx_count = CFG_GET_NUM_TXQS_NIC_IF(conf6x, lio->ifidx); } else if (OCTEON_CN23XX_PF(oct)) { - struct octeon_config *conf23 = CHIP_FIELD(oct, cn23xx_pf, conf); + struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf); max_rx = CFG_GET_OQ_MAX_Q(conf23); max_tx = CFG_GET_IQ_MAX_Q(conf23); @@ -585,14 +584,14 @@ lio_ethtool_get_ringparam(struct net_device *netdev, rx_pending = 0; if (OCTEON_CN6XXX(oct)) { - struct octeon_config *conf6x = CHIP_FIELD(oct, cn6xxx, conf); + struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); tx_max_pending = CN6XXX_MAX_IQ_DESCRIPTORS; rx_max_pending = CN6XXX_MAX_OQ_DESCRIPTORS; rx_pending = CFG_GET_NUM_RX_DESCS_NIC_IF(conf6x, lio->ifidx); tx_pending = CFG_GET_NUM_TX_DESCS_NIC_IF(conf6x, lio->ifidx); } else if (OCTEON_CN23XX_PF(oct)) { - struct octeon_config *conf23 = CHIP_FIELD(oct, cn23xx_pf, conf); + struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf); tx_max_pending = CN23XX_MAX_IQ_DESCRIPTORS; rx_max_pending = CN23XX_MAX_OQ_DESCRIPTORS; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 0d4c584..3d05b2f 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -48,9 +48,6 @@ MODULE_PARM_DESC(ddr_timeout, #define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) -#define INCR_INSTRQUEUE_PKT_COUNT(octeon_dev_ptr, iq_no, field, count) \ - (octeon_dev_ptr->instr_queue[iq_no]->stats.field += count) - static int debug = -1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "NETIF_MSG debug bits"); @@ -59,10 +56,6 @@ static char fw_type[LIO_MAX_FW_TYPE_LEN]; module_param_string(fw_type, fw_type, sizeof(fw_type), 0000); MODULE_PARM_DESC(fw_type, "Type of firmware to be loaded. Default \"nic\""); -static int conf_type; -module_param(conf_type, int, 0); -MODULE_PARM_DESC(conf_type, "select octeon configuration 0 default 1 ovs"); - static int ptp_enable = 1; /* Bit mask values for lio->ifstate */ @@ -3726,7 +3719,7 @@ static int liquidio_set_vf_link_state(struct net_device *netdev, int vfidx, return 0; } -static struct net_device_ops lionetdevops = { +static const struct net_device_ops lionetdevops = { .ndo_open = liquidio_open, .ndo_stop = liquidio_stop, .ndo_start_xmit = liquidio_xmit, @@ -3747,6 +3740,7 @@ static struct net_device_ops lionetdevops = { .ndo_set_vf_vlan = liquidio_set_vf_vlan, .ndo_get_vf_config = liquidio_get_vf_config, .ndo_set_vf_link_state = liquidio_set_vf_link_state, + .ndo_select_queue = select_q }; /** \brief Entry point for the liquidio module @@ -3758,7 +3752,7 @@ static int __init liquidio_init(void) init_completion(&first_stage); - octeon_init_device_list(conf_type); + octeon_init_device_list(OCTEON_CONFIG_TYPE_DEFAULT); if (liquidio_init_pci()) return -EINVAL; @@ -3979,9 +3973,6 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) SET_NETDEV_DEV(netdev, &octeon_dev->pci_dev->dev); - if (num_iqueues > 1) - lionetdevops.ndo_select_queue = select_q; - /* Associate the routines that will handle different * netdev tasks. */ diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 82a4eed..f308ee4 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -66,7 +66,7 @@ enum octeon_tag_type { /* Subcodes are used by host driver/apps to identify the sub-operation * for the core. They only need to by unique for a given subsystem. */ -#define OPCODE_SUBCODE(op, sub) (((op & 0x0f) << 8) | ((sub) & 0x7f)) +#define OPCODE_SUBCODE(op, sub) ((((op) & 0x0f) << 8) | ((sub) & 0x7f)) /** OPCODE_CORE subcodes. For future use. */ @@ -89,10 +89,6 @@ enum octeon_tag_type { #define CORE_DRV_TEST_SCATTER_OP 0xFFF5 -#define OPCODE_SLOW_PATH(rh) \ - (OPCODE_SUBCODE(rh->r.opcode, rh->r.subcode) != \ - OPCODE_SUBCODE(OPCODE_NIC, OPCODE_NIC_NW_DATA)) - /* Application codes advertised by the core driver initialization packet. */ #define CVM_DRV_APP_START 0x0 #define CVM_DRV_NO_APP 0 @@ -102,31 +98,15 @@ enum octeon_tag_type { #define CVM_DRV_INVALID_APP (CVM_DRV_APP_START + 0x2) #define CVM_DRV_APP_END (CVM_DRV_INVALID_APP - 1) -/* Macro to increment index. - * Index is incremented by count; if the sum exceeds - * max, index is wrapped-around to the start. - */ -#define INCR_INDEX(index, count, max) \ -do { \ - if (((index) + (count)) >= (max)) \ - index = ((index) + (count)) - (max); \ - else \ - index += (count); \ -} while (0) - -#define INCR_INDEX_BY1(index, max) \ -do { \ - if ((++(index)) == (max)) \ - index = 0; \ -} while (0) - -#define DECR_INDEX(index, count, max) \ -do { \ - if ((count) > (index)) \ - index = ((max) - ((count - index))); \ - else \ - index -= count; \ -} while (0) +static inline u32 incr_index(u32 index, u32 count, u32 max) +{ + if ((index + count) >= max) + index = index + count - max; + else + index += count; + + return index; +} #define OCT_BOARD_NAME 32 #define OCT_SERIAL_LEN 64 @@ -827,6 +807,16 @@ struct oct_link_stats { }; +static inline int opcode_slow_path(union octeon_rh *rh) +{ + u16 subcode1, subcode2; + + subcode1 = OPCODE_SUBCODE((rh)->r.opcode, (rh)->r.subcode); + subcode2 = OPCODE_SUBCODE(OPCODE_NIC, OPCODE_NIC_NW_DATA); + + return (subcode2 != subcode1); +} + #define LIO68XX_LED_CTRL_ADDR 0x3501 #define LIO68XX_LED_CTRL_CFGON 0x1f #define LIO68XX_LED_CTRL_CFGOFF 0x100 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index c0089ed..3265e0b 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -138,46 +138,6 @@ struct octeon_pci_console_desc { }; /** - * This macro returns the size of a member of a structure. - * Logically it is the same as "sizeof(s::field)" in C++, but - * C lacks the "::" operator. - */ -#define SIZEOF_FIELD(s, field) sizeof(((s *)NULL)->field) - -/** - * This macro returns a member of the cvmx_bootmem_desc - * structure. These members can't be directly addressed as - * they might be in memory not directly reachable. In the case - * where bootmem is compiled with LINUX_HOST, the structure - * itself might be located on a remote Octeon. The argument - * "field" is the member name of the cvmx_bootmem_desc to read. - * Regardless of the type of the field, the return type is always - * a u64. - */ -#define CVMX_BOOTMEM_DESC_GET_FIELD(oct, field) \ - __cvmx_bootmem_desc_get(oct, oct->bootmem_desc_addr, \ - offsetof(struct cvmx_bootmem_desc, field), \ - SIZEOF_FIELD(struct cvmx_bootmem_desc, field)) - -#define __cvmx_bootmem_lock(flags) (flags = flags) -#define __cvmx_bootmem_unlock(flags) (flags = flags) - -/** - * This macro returns a member of the - * cvmx_bootmem_named_block_desc structure. These members can't - * be directly addressed as they might be in memory not directly - * reachable. In the case where bootmem is compiled with - * LINUX_HOST, the structure itself might be located on a remote - * Octeon. The argument "field" is the member name of the - * cvmx_bootmem_named_block_desc to read. Regardless of the type - * of the field, the return type is always a u64. The "addr" - * parameter is the physical address of the structure. - */ -#define CVMX_BOOTMEM_NAMED_GET_FIELD(oct, addr, field) \ - __cvmx_bootmem_desc_get(oct, addr, \ - offsetof(struct cvmx_bootmem_named_block_desc, field), \ - SIZEOF_FIELD(struct cvmx_bootmem_named_block_desc, field)) -/** * \brief determines if a given console has debug enabled. * @param console console to check * @returns 1 = enabled. 0 otherwise @@ -258,10 +218,15 @@ static int __cvmx_bootmem_check_version(struct octeon_device *oct, oct->bootmem_desc_addr = octeon_read_device_mem64(oct, BOOTLOADER_PCI_READ_DESC_ADDR); - major_version = - (u32)CVMX_BOOTMEM_DESC_GET_FIELD(oct, major_version); - minor_version = - (u32)CVMX_BOOTMEM_DESC_GET_FIELD(oct, minor_version); + major_version = (u32)__cvmx_bootmem_desc_get( + oct, oct->bootmem_desc_addr, + offsetof(struct cvmx_bootmem_desc, major_version), + FIELD_SIZEOF(struct cvmx_bootmem_desc, major_version)); + minor_version = (u32)__cvmx_bootmem_desc_get( + oct, oct->bootmem_desc_addr, + offsetof(struct cvmx_bootmem_desc, minor_version), + FIELD_SIZEOF(struct cvmx_bootmem_desc, minor_version)); + dev_dbg(&oct->pci_dev->dev, "%s: major_version=%d\n", __func__, major_version); if ((major_version > 3) || @@ -284,10 +249,20 @@ static const struct cvmx_bootmem_named_block_desc u64 named_addr = cvmx_bootmem_phy_named_block_find(oct, name, flags); if (named_addr) { - desc->base_addr = CVMX_BOOTMEM_NAMED_GET_FIELD(oct, named_addr, - base_addr); - desc->size = - CVMX_BOOTMEM_NAMED_GET_FIELD(oct, named_addr, size); + desc->base_addr = __cvmx_bootmem_desc_get( + oct, named_addr, + offsetof(struct cvmx_bootmem_named_block_desc, + base_addr), + FIELD_SIZEOF( + struct cvmx_bootmem_named_block_desc, + base_addr)); + desc->size = __cvmx_bootmem_desc_get(oct, named_addr, + offsetof(struct cvmx_bootmem_named_block_desc, + size), + FIELD_SIZEOF( + struct cvmx_bootmem_named_block_desc, + size)); + strncpy(desc->name, name, sizeof(desc->name)); desc->name[sizeof(desc->name) - 1] = 0; return &oct->bootmem_named_block_desc; @@ -302,22 +277,41 @@ static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, { u64 result = 0; - __cvmx_bootmem_lock(flags); if (!__cvmx_bootmem_check_version(oct, 3)) { u32 i; - u64 named_block_array_addr = - CVMX_BOOTMEM_DESC_GET_FIELD(oct, - named_block_array_addr); - u32 num_blocks = (u32) - CVMX_BOOTMEM_DESC_GET_FIELD(oct, nb_num_blocks); - u32 name_length = (u32) - CVMX_BOOTMEM_DESC_GET_FIELD(oct, named_block_name_len); + + u64 named_block_array_addr = __cvmx_bootmem_desc_get( + oct, oct->bootmem_desc_addr, + offsetof(struct cvmx_bootmem_desc, + named_block_array_addr), + FIELD_SIZEOF(struct cvmx_bootmem_desc, + named_block_array_addr)); + u32 num_blocks = (u32)__cvmx_bootmem_desc_get( + oct, oct->bootmem_desc_addr, + offsetof(struct cvmx_bootmem_desc, + nb_num_blocks), + FIELD_SIZEOF(struct cvmx_bootmem_desc, + nb_num_blocks)); + + u32 name_length = (u32)__cvmx_bootmem_desc_get( + oct, oct->bootmem_desc_addr, + offsetof(struct cvmx_bootmem_desc, + named_block_name_len), + FIELD_SIZEOF(struct cvmx_bootmem_desc, + named_block_name_len)); + u64 named_addr = named_block_array_addr; for (i = 0; i < num_blocks; i++) { - u64 named_size = - CVMX_BOOTMEM_NAMED_GET_FIELD(oct, named_addr, - size); + u64 named_size = __cvmx_bootmem_desc_get( + oct, named_addr, + offsetof( + struct cvmx_bootmem_named_block_desc, + size), + FIELD_SIZEOF( + struct cvmx_bootmem_named_block_desc, + size)); + if (name && named_size) { char *name_tmp = kmalloc(name_length + 1, GFP_KERNEL); @@ -342,7 +336,6 @@ static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, sizeof(struct cvmx_bootmem_named_block_desc); } } - __cvmx_bootmem_unlock(flags); return result; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 4a30d7c..79c8875 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -516,11 +516,6 @@ static struct octeon_config default_cn23xx_conf = { } }; -enum { - OCTEON_CONFIG_TYPE_DEFAULT = 0, - NUM_OCTEON_CONFS, -}; - static struct octeon_config_ptr { u32 conf_type; } oct_conf_info[MAX_OCTEON_DEVICES] = { @@ -792,10 +787,9 @@ int octeon_setup_instr_queues(struct octeon_device *oct) if (OCTEON_CN6XXX(oct)) num_descs = - CFG_GET_NUM_DEF_TX_DESCS(CHIP_FIELD(oct, cn6xxx, conf)); + CFG_GET_NUM_DEF_TX_DESCS(CHIP_CONF(oct, cn6xxx)); else if (OCTEON_CN23XX_PF(oct)) - num_descs = CFG_GET_NUM_DEF_TX_DESCS(CHIP_FIELD(oct, cn23xx_pf, - conf)); + num_descs = CFG_GET_NUM_DEF_TX_DESCS(CHIP_CONF(oct, cn23xx_pf)); oct->num_iqs = 0; @@ -835,14 +829,12 @@ int octeon_setup_output_queues(struct octeon_device *oct) if (OCTEON_CN6XXX(oct)) { num_descs = - CFG_GET_NUM_DEF_RX_DESCS(CHIP_FIELD(oct, cn6xxx, conf)); + CFG_GET_NUM_DEF_RX_DESCS(CHIP_CONF(oct, cn6xxx)); desc_size = - CFG_GET_DEF_RX_BUF_SIZE(CHIP_FIELD(oct, cn6xxx, conf)); + CFG_GET_DEF_RX_BUF_SIZE(CHIP_CONF(oct, cn6xxx)); } else if (OCTEON_CN23XX_PF(oct)) { - num_descs = CFG_GET_NUM_DEF_RX_DESCS(CHIP_FIELD(oct, cn23xx_pf, - conf)); - desc_size = CFG_GET_DEF_RX_BUF_SIZE(CHIP_FIELD(oct, cn23xx_pf, - conf)); + num_descs = CFG_GET_NUM_DEF_RX_DESCS(CHIP_CONF(oct, cn23xx_pf)); + desc_size = CFG_GET_DEF_RX_BUF_SIZE(CHIP_CONF(oct, cn23xx_pf)); } oct->num_oqs = 0; oct->droq[0] = vmalloc_node(sizeof(*oct->droq[0]), numa_node); @@ -1071,10 +1063,10 @@ int octeon_core_drv_init(struct octeon_recv_info *recv_info, void *buf) if (OCTEON_CN6XXX(oct)) num_nic_ports = - CFG_GET_NUM_NIC_PORTS(CHIP_FIELD(oct, cn6xxx, conf)); + CFG_GET_NUM_NIC_PORTS(CHIP_CONF(oct, cn6xxx)); else if (OCTEON_CN23XX_PF(oct)) num_nic_ports = - CFG_GET_NUM_NIC_PORTS(CHIP_FIELD(oct, cn23xx_pf, conf)); + CFG_GET_NUM_NIC_PORTS(CHIP_CONF(oct, cn23xx_pf)); if (atomic_read(&oct->status) >= OCT_DEV_RUNNING) { dev_err(&oct->pci_dev->dev, "Received CORE OK when device state is 0x%x\n", @@ -1169,10 +1161,10 @@ struct octeon_config *octeon_get_conf(struct octeon_device *oct) if (OCTEON_CN6XXX(oct)) { default_oct_conf = - (struct octeon_config *)(CHIP_FIELD(oct, cn6xxx, conf)); + (struct octeon_config *)(CHIP_CONF(oct, cn6xxx)); } else if (OCTEON_CN23XX_PF(oct)) { default_oct_conf = (struct octeon_config *) - (CHIP_FIELD(oct, cn23xx_pf, conf)); + (CHIP_CONF(oct, cn23xx_pf)); } return default_oct_conf; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index c59b77e..5ce2048 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -48,6 +48,11 @@ enum octeon_pci_swap_mode { OCTEON_PCI_32BIT_LW_SWAP = 3 }; +enum { + OCTEON_CONFIG_TYPE_DEFAULT = 0, + NUM_OCTEON_CONFS, +}; + #define OCTEON_OUTPUT_INTR (2) #define OCTEON_MBOX_INTR (4) #define OCTEON_ALL_INTR 0xff @@ -524,12 +529,14 @@ struct octeon_device { #define OCT_DRV_ONLINE 1 #define OCT_DRV_OFFLINE 2 -#define OCTEON_CN6XXX(oct) ((oct->chip_id == OCTEON_CN66XX) || \ - (oct->chip_id == OCTEON_CN68XX)) -#define OCTEON_CN23XX_PF(oct) (oct->chip_id == OCTEON_CN23XX_PF_VID) +#define OCTEON_CN6XXX(oct) ({ \ + typeof(oct) _oct = (oct); \ + ((_oct->chip_id == OCTEON_CN66XX) || \ + (_oct->chip_id == OCTEON_CN68XX)); }) +#define OCTEON_CN23XX_PF(oct) ((oct)->chip_id == OCTEON_CN23XX_PF_VID) #define OCTEON_CN23XX_VF(oct) ((oct)->chip_id == OCTEON_CN23XX_VF_VID) -#define CHIP_FIELD(oct, TYPE, field) \ - (((struct octeon_ ## TYPE *)(oct->chip))->field) +#define CHIP_CONF(oct, TYPE) \ + (((struct octeon_ ## TYPE *)((oct)->chip))->conf) struct oct_intrmod_cmd { struct octeon_device *oct_dev; @@ -641,16 +648,16 @@ void lio_pci_writeq(struct octeon_device *oct, u64 val, u64 addr); /* Routines for reading and writing CSRs */ #define octeon_write_csr(oct_dev, reg_off, value) \ - writel(value, oct_dev->mmio[0].hw_addr + reg_off) + writel(value, (oct_dev)->mmio[0].hw_addr + (reg_off)) #define octeon_write_csr64(oct_dev, reg_off, val64) \ - writeq(val64, oct_dev->mmio[0].hw_addr + reg_off) + writeq(val64, (oct_dev)->mmio[0].hw_addr + (reg_off)) #define octeon_read_csr(oct_dev, reg_off) \ - readl(oct_dev->mmio[0].hw_addr + reg_off) + readl((oct_dev)->mmio[0].hw_addr + (reg_off)) #define octeon_read_csr64(oct_dev, reg_off) \ - readq(oct_dev->mmio[0].hw_addr + reg_off) + readq((oct_dev)->mmio[0].hw_addr + (reg_off)) /** * Checks if memory access is okay diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index d7ba000..8bf1ac76 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -29,9 +29,6 @@ #include "cn66xx_device.h" #include "cn23xx_pf_device.h" -#define CVM_MIN(d1, d2) (((d1) < (d2)) ? (d1) : (d2)) -#define CVM_MAX(d1, d2) (((d1) > (d2)) ? (d1) : (d2)) - struct niclist { struct list_head list; void *ptr; @@ -254,13 +251,13 @@ int octeon_init_droq(struct octeon_device *oct, c_num_descs = num_descs; c_buf_size = desc_size; if (OCTEON_CN6XXX(oct)) { - struct octeon_config *conf6x = CHIP_FIELD(oct, cn6xxx, conf); + struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf6x); c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf6x); } else if (OCTEON_CN23XX_PF(oct)) { - struct octeon_config *conf23 = CHIP_FIELD(oct, cn23xx_pf, conf); + struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf); c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf23); c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf23); @@ -405,7 +402,7 @@ static inline struct octeon_recv_info *octeon_create_recv_info( recv_pkt->buffer_ptr[i] = droq->recv_buf_list[idx].buffer; droq->recv_buf_list[idx].buffer = NULL; - INCR_INDEX_BY1(idx, droq->max_count); + idx = incr_index(idx, 1, droq->max_count); bytes_left -= droq->buffer_size; i++; buf_cnt--; @@ -436,14 +433,15 @@ octeon_droq_refill_pullup_descs(struct octeon_droq *droq, droq->recv_buf_list[refill_index].buffer = NULL; desc_ring[refill_index].buffer_ptr = 0; do { - INCR_INDEX_BY1(droq->refill_idx, - droq->max_count); + droq->refill_idx = incr_index(droq->refill_idx, + 1, + droq->max_count); desc_refilled++; droq->refill_count--; } while (droq->recv_buf_list[droq->refill_idx]. buffer); } - INCR_INDEX_BY1(refill_index, droq->max_count); + refill_index = incr_index(refill_index, 1, droq->max_count); } /* while */ return desc_refilled; } @@ -510,7 +508,8 @@ octeon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq) /* Reset any previous values in the length field. */ droq->info_list[droq->refill_idx].length = 0; - INCR_INDEX_BY1(droq->refill_idx, droq->max_count); + droq->refill_idx = incr_index(droq->refill_idx, 1, + droq->max_count); desc_refilled++; droq->refill_count--; } @@ -595,7 +594,8 @@ static inline void octeon_droq_drop_packets(struct octeon_device *oct, buf_cnt = 1; } - INCR_INDEX(droq->read_idx, buf_cnt, droq->max_count); + droq->read_idx = incr_index(droq->read_idx, buf_cnt, + droq->max_count); droq->refill_count += buf_cnt; } } @@ -635,11 +635,12 @@ octeon_droq_fast_process_packets(struct octeon_device *oct, rh = &info->rh; total_len += (u32)info->length; - if (OPCODE_SLOW_PATH(rh)) { + if (opcode_slow_path(rh)) { u32 buf_cnt; buf_cnt = octeon_droq_dispatch_pkt(oct, droq, rh, info); - INCR_INDEX(droq->read_idx, buf_cnt, droq->max_count); + droq->read_idx = incr_index(droq->read_idx, + buf_cnt, droq->max_count); droq->refill_count += buf_cnt; } else { if (info->length <= droq->buffer_size) { @@ -653,7 +654,8 @@ octeon_droq_fast_process_packets(struct octeon_device *oct, droq->recv_buf_list[droq->read_idx].buffer = NULL; - INCR_INDEX_BY1(droq->read_idx, droq->max_count); + droq->read_idx = incr_index(droq->read_idx, 1, + droq->max_count); droq->refill_count++; } else { nicbuf = octeon_fast_packet_alloc((u32) @@ -685,8 +687,9 @@ octeon_droq_fast_process_packets(struct octeon_device *oct, } pkt_len += cpy_len; - INCR_INDEX_BY1(droq->read_idx, - droq->max_count); + droq->read_idx = + incr_index(droq->read_idx, 1, + droq->max_count); droq->refill_count++; } } @@ -800,9 +803,8 @@ octeon_droq_process_poll_pkts(struct octeon_device *oct, while (total_pkts_processed < budget) { octeon_droq_check_hw_for_pkts(droq); - pkts_available = - CVM_MIN((budget - total_pkts_processed), - (u32)(atomic_read(&droq->pkts_pending))); + pkts_available = min((budget - total_pkts_processed), + (u32)(atomic_read(&droq->pkts_pending))); if (pkts_available == 0) break; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h index c67152a..e04ca8f 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h @@ -303,6 +303,9 @@ struct octeon_sc_buffer_pool { atomic_t alloc_buf_count; }; +#define INCR_INSTRQUEUE_PKT_COUNT(octeon_dev_ptr, iq_no, field, count) \ + (((octeon_dev_ptr)->instr_queue[iq_no]->stats.field) += count) + int octeon_setup_sc_buffer_pool(struct octeon_device *oct); int octeon_free_sc_buffer_pool(struct octeon_device *oct); struct octeon_soft_command * diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c index 98d8066d..13a18c9 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c @@ -36,7 +36,7 @@ octeon_toggle_bar1_swapmode(struct octeon_device *oct, u32 idx) oct->fn_list.bar1_idx_write(oct, idx, mask); } #else -#define octeon_toggle_bar1_swapmode(oct, idx) (oct = oct) +#define octeon_toggle_bar1_swapmode(oct, idx) #endif static void diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 172b70e..e94edc8 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -128,7 +128,7 @@ struct lio { #define LIO_SIZE (sizeof(struct lio)) #define GET_LIO(netdev) ((struct lio *)netdev_priv(netdev)) -#define CIU3_WDOG(c) (0x1010000020000ULL + (c << 3)) +#define CIU3_WDOG(c) (0x1010000020000ULL + ((c) << 3)) #define CIU3_WDOG_MASK 12ULL #define LIO_MONITOR_WDOG_EXPIRE 1 #define LIO_MONITOR_CORE_STUCK_MSGD 2 @@ -339,9 +339,9 @@ static inline void tx_buffer_free(void *buffer) } #define lio_dma_alloc(oct, size, dma_addr) \ - dma_alloc_coherent(&oct->pci_dev->dev, size, dma_addr, GFP_KERNEL) + dma_alloc_coherent(&(oct)->pci_dev->dev, size, dma_addr, GFP_KERNEL) #define lio_dma_free(oct, size, virt_addr, dma_addr) \ - dma_free_coherent(&oct->pci_dev->dev, size, virt_addr, dma_addr) + dma_free_coherent(&(oct)->pci_dev->dev, size, virt_addr, dma_addr) static inline void *get_rbd(struct sk_buff *skb) diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h index b059979..0c7a5c9 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h @@ -64,7 +64,7 @@ struct octnic_ctrl_pkt { octnic_ctrl_pkt_cb_fn_t cb_fn; }; -#define MAX_UDD_SIZE(nctrl) (sizeof(nctrl->udd)) +#define MAX_UDD_SIZE(nctrl) (sizeof((nctrl)->udd)) /** Structure of data information passed by the NIC module to the OSI * layer when forwarding data to Octeon device software. diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 0cc1583..8531a00 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -29,9 +29,6 @@ #include "cn66xx_device.h" #include "cn23xx_pf_device.h" -#define INCR_INSTRQUEUE_PKT_COUNT(octeon_dev_ptr, iq_no, field, count) \ - (octeon_dev_ptr->instr_queue[iq_no]->stats.field += count) - struct iq_post_status { int status; int index; @@ -68,9 +65,9 @@ int octeon_init_instr_queue(struct octeon_device *oct, int numa_node = cpu_to_node(iq_no % num_online_cpus()); if (OCTEON_CN6XXX(oct)) - conf = &(CFG_GET_IQ_CFG(CHIP_FIELD(oct, cn6xxx, conf))); + conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn6xxx))); else if (OCTEON_CN23XX_PF(oct)) - conf = &(CFG_GET_IQ_CFG(CHIP_FIELD(oct, cn23xx_pf, conf))); + conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_pf))); if (!conf) { dev_err(&oct->pci_dev->dev, "Unsupported Chip %x\n", oct->chip_id); @@ -182,10 +179,10 @@ int octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no) if (OCTEON_CN6XXX(oct)) desc_size = - CFG_GET_IQ_INSTR_TYPE(CHIP_FIELD(oct, cn6xxx, conf)); + CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn6xxx)); else if (OCTEON_CN23XX_PF(oct)) desc_size = - CFG_GET_IQ_INSTR_TYPE(CHIP_FIELD(oct, cn23xx_pf, conf)); + CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_pf)); vfree(iq->request_list); @@ -317,7 +314,8 @@ __post_command2(struct octeon_instr_queue *iq, u8 *cmd) /* "index" is returned, host_write_index is modified. */ st.index = iq->host_write_index; - INCR_INDEX_BY1(iq->host_write_index, iq->max_count); + iq->host_write_index = incr_index(iq->host_write_index, 1, + iq->max_count); iq->fill_cnt++; /* Flush the command into memory. We need to be sure the data is in @@ -432,7 +430,7 @@ lio_process_iq_request_list(struct octeon_device *oct, skip_this: inst_count++; - INCR_INDEX_BY1(old, iq->max_count); + old = incr_index(old, 1, iq->max_count); if ((napi_budget) && (inst_count >= napi_budget)) break; -- cgit v1.1 From 319b0534b9588124cdc7128e121f3f85daaab556 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 14 Nov 2016 18:15:14 -0800 Subject: tcp: allow to enable the repair mode for non-listening sockets The repair mode is used to get and restore sequence numbers and data from queues. It used to checkpoint/restore connections. Currently the repair mode can be enabled for sockets in the established and closed states, but for other states we have to dump the same socket properties, so lets allow to enable repair mode for these sockets. The repair mode reveals nothing more for sockets in other states. Signed-off-by: Andrei Vagin Acked-by: Pavel Emelyanov Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b025a69..913f9bb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2300,7 +2300,7 @@ EXPORT_SYMBOL(tcp_disconnect); static inline bool tcp_can_repair_sock(const struct sock *sk) { return ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN) && - ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); + (sk->sk_state != TCP_LISTEN); } static int tcp_repair_set_window(struct tcp_sock *tp, char __user *optbuf, int len) -- cgit v1.1 From ff86aae3b4112b85d2231c23bccbc49589df1c06 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:01 +0200 Subject: devres: add devm_alloc_percpu() Introduce managed counterparts for alloc_percpu() and free_percpu(). Add devm_alloc_percpu() and devm_free_percpu() into the managed interfaces list. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- Documentation/driver-model/devres.txt | 4 +++ drivers/base/devres.c | 66 +++++++++++++++++++++++++++++++++++ include/linux/device.h | 19 ++++++++++ 3 files changed, 89 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 1670708..ca9d1eb 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -332,6 +332,10 @@ MEM MFD devm_mfd_add_devices() +PER-CPU MEM + devm_alloc_percpu() + devm_free_percpu() + PCI pcim_enable_device() : after success, all PCI ops become managed pcim_pin_device() : keep PCI device enabled after release diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 8fc654f..71d5770 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "base.h" @@ -985,3 +986,68 @@ void devm_free_pages(struct device *dev, unsigned long addr) &devres)); } EXPORT_SYMBOL_GPL(devm_free_pages); + +static void devm_percpu_release(struct device *dev, void *pdata) +{ + void __percpu *p; + + p = *(void __percpu **)pdata; + free_percpu(p); +} + +static int devm_percpu_match(struct device *dev, void *data, void *p) +{ + struct devres *devr = container_of(data, struct devres, data); + + return *(void **)devr->data == p; +} + +/** + * __devm_alloc_percpu - Resource-managed alloc_percpu + * @dev: Device to allocate per-cpu memory for + * @size: Size of per-cpu memory to allocate + * @align: Alignment of per-cpu memory to allocate + * + * Managed alloc_percpu. Per-cpu memory allocated with this function is + * automatically freed on driver detach. + * + * RETURNS: + * Pointer to allocated memory on success, NULL on failure. + */ +void __percpu *__devm_alloc_percpu(struct device *dev, size_t size, + size_t align) +{ + void *p; + void __percpu *pcpu; + + pcpu = __alloc_percpu(size, align); + if (!pcpu) + return NULL; + + p = devres_alloc(devm_percpu_release, sizeof(void *), GFP_KERNEL); + if (!p) { + free_percpu(pcpu); + return NULL; + } + + *(void __percpu **)p = pcpu; + + devres_add(dev, p); + + return pcpu; +} +EXPORT_SYMBOL_GPL(__devm_alloc_percpu); + +/** + * devm_free_percpu - Resource-managed free_percpu + * @dev: Device this memory belongs to + * @pdata: Per-cpu memory to free + * + * Free memory allocated with devm_alloc_percpu(). + */ +void devm_free_percpu(struct device *dev, void __percpu *pdata) +{ + WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match, + (void *)pdata)); +} +EXPORT_SYMBOL_GPL(devm_free_percpu); diff --git a/include/linux/device.h b/include/linux/device.h index bc41e87..a00105c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -698,6 +698,25 @@ static inline int devm_add_action_or_reset(struct device *dev, return ret; } +/** + * devm_alloc_percpu - Resource-managed alloc_percpu + * @dev: Device to allocate per-cpu memory for + * @type: Type to allocate per-cpu memory for + * + * Managed alloc_percpu. Per-cpu memory allocated with this function is + * automatically freed on driver detach. + * + * RETURNS: + * Pointer to allocated memory on success, NULL on failure. + */ +#define devm_alloc_percpu(dev, type) \ + ((typeof(type) __percpu *)__devm_alloc_percpu((dev), sizeof(type), \ + __alignof__(type))) + +void __percpu *__devm_alloc_percpu(struct device *dev, size_t size, + size_t align); +void devm_free_percpu(struct device *dev, void __percpu *pdata); + struct device_dma_parameters { /* * a low level driver may set these to teach IOMMU code about -- cgit v1.1 From 9ad1a37493338cacf04e2c93acf44d151a7adda8 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:02 +0200 Subject: dpaa_eth: add support for DPAA Ethernet This introduces the Freescale Data Path Acceleration Architecture (DPAA) Ethernet driver (dpaa_eth) that builds upon the DPAA QMan, BMan, PAMU and FMan drivers to deliver Ethernet connectivity on the Freescale DPAA QorIQ platforms. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/Kconfig | 2 + drivers/net/ethernet/freescale/Makefile | 1 + drivers/net/ethernet/freescale/dpaa/Kconfig | 10 + drivers/net/ethernet/freescale/dpaa/Makefile | 11 + drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2682 ++++++++++++++++++++++++ drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 144 ++ 6 files changed, 2850 insertions(+) create mode 100644 drivers/net/ethernet/freescale/dpaa/Kconfig create mode 100644 drivers/net/ethernet/freescale/dpaa/Makefile create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth.c create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth.h diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index d1ca45f..aa3f615 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -93,4 +93,6 @@ config GIANFAR and MPC86xx family of chips, the eTSEC on LS1021A and the FEC on the 8540. +source "drivers/net/ethernet/freescale/dpaa/Kconfig" + endif # NET_VENDOR_FREESCALE diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index cbe21dc..4a13115 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o obj-$(CONFIG_FSL_FMAN) += fman/ +obj-$(CONFIG_FSL_DPAA_ETH) += dpaa/ diff --git a/drivers/net/ethernet/freescale/dpaa/Kconfig b/drivers/net/ethernet/freescale/dpaa/Kconfig new file mode 100644 index 0000000..f3a3454 --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa/Kconfig @@ -0,0 +1,10 @@ +menuconfig FSL_DPAA_ETH + tristate "DPAA Ethernet" + depends on FSL_SOC && FSL_DPAA && FSL_FMAN + select PHYLIB + select FSL_FMAN_MAC + ---help--- + Data Path Acceleration Architecture Ethernet driver, + supporting the Freescale QorIQ chips. + Depends on Freescale Buffer Manager and Queue Manager + driver and Frame Manager Driver. diff --git a/drivers/net/ethernet/freescale/dpaa/Makefile b/drivers/net/ethernet/freescale/dpaa/Makefile new file mode 100644 index 0000000..fc76029 --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the Freescale DPAA Ethernet controllers +# + +# Include FMan headers +FMAN = $(srctree)/drivers/net/ethernet/freescale/fman +ccflags-y += -I$(FMAN) + +obj-$(CONFIG_FSL_DPAA_ETH) += fsl_dpa.o + +fsl_dpa-objs += dpaa_eth.o diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c new file mode 100644 index 0000000..8b9b0720f --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -0,0 +1,2682 @@ +/* Copyright 2008 - 2016 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fman.h" +#include "fman_port.h" +#include "mac.h" +#include "dpaa_eth.h" + +static int debug = -1; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Module/Driver verbosity level (0=none,...,16=all)"); + +static u16 tx_timeout = 1000; +module_param(tx_timeout, ushort, 0444); +MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms"); + +#define FM_FD_STAT_RX_ERRORS \ + (FM_FD_ERR_DMA | FM_FD_ERR_PHYSICAL | \ + FM_FD_ERR_SIZE | FM_FD_ERR_CLS_DISCARD | \ + FM_FD_ERR_EXTRACTION | FM_FD_ERR_NO_SCHEME | \ + FM_FD_ERR_PRS_TIMEOUT | FM_FD_ERR_PRS_ILL_INSTRUCT | \ + FM_FD_ERR_PRS_HDR_ERR) + +#define FM_FD_STAT_TX_ERRORS \ + (FM_FD_ERR_UNSUPPORTED_FORMAT | \ + FM_FD_ERR_LENGTH | FM_FD_ERR_DMA) + +#define DPAA_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | NETIF_MSG_IFUP | \ + NETIF_MSG_IFDOWN) + +#define DPAA_INGRESS_CS_THRESHOLD 0x10000000 +/* Ingress congestion threshold on FMan ports + * The size in bytes of the ingress tail-drop threshold on FMan ports. + * Traffic piling up above this value will be rejected by QMan and discarded + * by FMan. + */ + +/* Size in bytes of the FQ taildrop threshold */ +#define DPAA_FQ_TD 0x200000 + +#define DPAA_CS_THRESHOLD_1G 0x06000000 +/* Egress congestion threshold on 1G ports, range 0x1000 .. 0x10000000 + * The size in bytes of the egress Congestion State notification threshold on + * 1G ports. The 1G dTSECs can quite easily be flooded by cores doing Tx in a + * tight loop (e.g. by sending UDP datagrams at "while(1) speed"), + * and the larger the frame size, the more acute the problem. + * So we have to find a balance between these factors: + * - avoiding the device staying congested for a prolonged time (risking + * the netdev watchdog to fire - see also the tx_timeout module param); + * - affecting performance of protocols such as TCP, which otherwise + * behave well under the congestion notification mechanism; + * - preventing the Tx cores from tightly-looping (as if the congestion + * threshold was too low to be effective); + * - running out of memory if the CS threshold is set too high. + */ + +#define DPAA_CS_THRESHOLD_10G 0x10000000 +/* The size in bytes of the egress Congestion State notification threshold on + * 10G ports, range 0x1000 .. 0x10000000 + */ + +/* Largest value that the FQD's OAL field can hold */ +#define FSL_QMAN_MAX_OAL 127 + +/* Default alignment for start of data in an Rx FD */ +#define DPAA_FD_DATA_ALIGNMENT 16 + +/* Values for the L3R field of the FM Parse Results + */ +/* L3 Type field: First IP Present IPv4 */ +#define FM_L3_PARSE_RESULT_IPV4 0x8000 +/* L3 Type field: First IP Present IPv6 */ +#define FM_L3_PARSE_RESULT_IPV6 0x4000 +/* Values for the L4R field of the FM Parse Results */ +/* L4 Type field: UDP */ +#define FM_L4_PARSE_RESULT_UDP 0x40 +/* L4 Type field: TCP */ +#define FM_L4_PARSE_RESULT_TCP 0x20 + +#define DPAA_SGT_MAX_ENTRIES 16 /* maximum number of entries in SG Table */ +#define DPAA_BUFF_RELEASE_MAX 8 /* maximum number of buffers released at once */ + +#define FSL_DPAA_BPID_INV 0xff +#define FSL_DPAA_ETH_MAX_BUF_COUNT 128 +#define FSL_DPAA_ETH_REFILL_THRESHOLD 80 + +#define DPAA_TX_PRIV_DATA_SIZE 16 +#define DPAA_PARSE_RESULTS_SIZE sizeof(struct fman_prs_result) +#define DPAA_TIME_STAMP_SIZE 8 +#define DPAA_HASH_RESULTS_SIZE 8 +#define DPAA_RX_PRIV_DATA_SIZE (u16)(DPAA_TX_PRIV_DATA_SIZE + \ + dpaa_rx_extra_headroom) + +#define DPAA_ETH_RX_QUEUES 128 + +#define DPAA_ENQUEUE_RETRIES 100000 + +enum port_type {RX, TX}; + +struct fm_port_fqs { + struct dpaa_fq *tx_defq; + struct dpaa_fq *tx_errq; + struct dpaa_fq *rx_defq; + struct dpaa_fq *rx_errq; +}; + +/* All the dpa bps in use at any moment */ +static struct dpaa_bp *dpaa_bp_array[BM_MAX_NUM_OF_POOLS]; + +/* The raw buffer size must be cacheline aligned */ +#define DPAA_BP_RAW_SIZE 4096 +/* When using more than one buffer pool, the raw sizes are as follows: + * 1 bp: 4KB + * 2 bp: 2KB, 4KB + * 3 bp: 1KB, 2KB, 4KB + * 4 bp: 1KB, 2KB, 4KB, 8KB + */ +static inline size_t bpool_buffer_raw_size(u8 index, u8 cnt) +{ + size_t res = DPAA_BP_RAW_SIZE / 4; + u8 i; + + for (i = (cnt < 3) ? cnt : 3; i < 3 + index; i++) + res *= 2; + return res; +} + +/* FMan-DMA requires 16-byte alignment for Rx buffers, but SKB_DATA_ALIGN is + * even stronger (SMP_CACHE_BYTES-aligned), so we just get away with that, + * via SKB_WITH_OVERHEAD(). We can't rely on netdev_alloc_frag() giving us + * half-page-aligned buffers, so we reserve some more space for start-of-buffer + * alignment. + */ +#define dpaa_bp_size(raw_size) SKB_WITH_OVERHEAD((raw_size) - SMP_CACHE_BYTES) + +static int dpaa_max_frm; + +static int dpaa_rx_extra_headroom; + +#define dpaa_get_max_mtu() \ + (dpaa_max_frm - (VLAN_ETH_HLEN + ETH_FCS_LEN)) + +static int dpaa_netdev_init(struct net_device *net_dev, + const struct net_device_ops *dpaa_ops, + u16 tx_timeout) +{ + struct dpaa_priv *priv = netdev_priv(net_dev); + struct device *dev = net_dev->dev.parent; + struct dpaa_percpu_priv *percpu_priv; + const u8 *mac_addr; + int i, err; + + /* Although we access another CPU's private data here + * we do it at initialization so it is safe + */ + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + percpu_priv->net_dev = net_dev; + } + + net_dev->netdev_ops = dpaa_ops; + mac_addr = priv->mac_dev->addr; + + net_dev->mem_start = priv->mac_dev->res->start; + net_dev->mem_end = priv->mac_dev->res->end; + + net_dev->min_mtu = ETH_MIN_MTU; + net_dev->max_mtu = dpaa_get_max_mtu(); + + net_dev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_LLTX); + + net_dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA; + /* The kernels enables GSO automatically, if we declare NETIF_F_SG. + * For conformity, we'll still declare GSO explicitly. + */ + net_dev->features |= NETIF_F_GSO; + + net_dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + /* we do not want shared skbs on TX */ + net_dev->priv_flags &= ~IFF_TX_SKB_SHARING; + + net_dev->features |= net_dev->hw_features; + net_dev->vlan_features = net_dev->features; + + memcpy(net_dev->perm_addr, mac_addr, net_dev->addr_len); + memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); + + net_dev->needed_headroom = priv->tx_headroom; + net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); + + /* start without the RUNNING flag, phylib controls it later */ + netif_carrier_off(net_dev); + + err = register_netdev(net_dev); + if (err < 0) { + dev_err(dev, "register_netdev() = %d\n", err); + return err; + } + + return 0; +} + +static int dpaa_stop(struct net_device *net_dev) +{ + struct mac_device *mac_dev; + struct dpaa_priv *priv; + int i, err, error; + + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + + netif_tx_stop_all_queues(net_dev); + /* Allow the Fman (Tx) port to process in-flight frames before we + * try switching it off. + */ + usleep_range(5000, 10000); + + err = mac_dev->stop(mac_dev); + if (err < 0) + netif_err(priv, ifdown, net_dev, "mac_dev->stop() = %d\n", + err); + + for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { + error = fman_port_disable(mac_dev->port[i]); + if (error) + err = error; + } + + if (net_dev->phydev) + phy_disconnect(net_dev->phydev); + net_dev->phydev = NULL; + + return err; +} + +static void dpaa_tx_timeout(struct net_device *net_dev) +{ + struct dpaa_percpu_priv *percpu_priv; + const struct dpaa_priv *priv; + + priv = netdev_priv(net_dev); + percpu_priv = this_cpu_ptr(priv->percpu_priv); + + netif_crit(priv, timer, net_dev, "Transmit timeout latency: %u ms\n", + jiffies_to_msecs(jiffies - dev_trans_start(net_dev))); + + percpu_priv->stats.tx_errors++; +} + +/* Calculates the statistics for the given device by adding the statistics + * collected by each CPU. + */ +static struct rtnl_link_stats64 *dpaa_get_stats64(struct net_device *net_dev, + struct rtnl_link_stats64 *s) +{ + int numstats = sizeof(struct rtnl_link_stats64) / sizeof(u64); + struct dpaa_priv *priv = netdev_priv(net_dev); + struct dpaa_percpu_priv *percpu_priv; + u64 *netstats = (u64 *)s; + u64 *cpustats; + int i, j; + + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + + cpustats = (u64 *)&percpu_priv->stats; + + /* add stats from all CPUs */ + for (j = 0; j < numstats; j++) + netstats[j] += cpustats[j]; + } + + return s; +} + +static struct mac_device *dpaa_mac_dev_get(struct platform_device *pdev) +{ + struct platform_device *of_dev; + struct dpaa_eth_data *eth_data; + struct device *dpaa_dev, *dev; + struct device_node *mac_node; + struct mac_device *mac_dev; + + dpaa_dev = &pdev->dev; + eth_data = dpaa_dev->platform_data; + if (!eth_data) + return ERR_PTR(-ENODEV); + + mac_node = eth_data->mac_node; + + of_dev = of_find_device_by_node(mac_node); + if (!of_dev) { + dev_err(dpaa_dev, "of_find_device_by_node(%s) failed\n", + mac_node->full_name); + of_node_put(mac_node); + return ERR_PTR(-EINVAL); + } + of_node_put(mac_node); + + dev = &of_dev->dev; + + mac_dev = dev_get_drvdata(dev); + if (!mac_dev) { + dev_err(dpaa_dev, "dev_get_drvdata(%s) failed\n", + dev_name(dev)); + return ERR_PTR(-EINVAL); + } + + return mac_dev; +} + +static int dpaa_set_mac_address(struct net_device *net_dev, void *addr) +{ + const struct dpaa_priv *priv; + struct mac_device *mac_dev; + struct sockaddr old_addr; + int err; + + priv = netdev_priv(net_dev); + + memcpy(old_addr.sa_data, net_dev->dev_addr, ETH_ALEN); + + err = eth_mac_addr(net_dev, addr); + if (err < 0) { + netif_err(priv, drv, net_dev, "eth_mac_addr() = %d\n", err); + return err; + } + + mac_dev = priv->mac_dev; + + err = mac_dev->change_addr(mac_dev->fman_mac, + (enet_addr_t *)net_dev->dev_addr); + if (err < 0) { + netif_err(priv, drv, net_dev, "mac_dev->change_addr() = %d\n", + err); + /* reverting to previous address */ + eth_mac_addr(net_dev, &old_addr); + + return err; + } + + return 0; +} + +static void dpaa_set_rx_mode(struct net_device *net_dev) +{ + const struct dpaa_priv *priv; + int err; + + priv = netdev_priv(net_dev); + + if (!!(net_dev->flags & IFF_PROMISC) != priv->mac_dev->promisc) { + priv->mac_dev->promisc = !priv->mac_dev->promisc; + err = priv->mac_dev->set_promisc(priv->mac_dev->fman_mac, + priv->mac_dev->promisc); + if (err < 0) + netif_err(priv, drv, net_dev, + "mac_dev->set_promisc() = %d\n", + err); + } + + err = priv->mac_dev->set_multi(net_dev, priv->mac_dev); + if (err < 0) + netif_err(priv, drv, net_dev, "mac_dev->set_multi() = %d\n", + err); +} + +static struct dpaa_bp *dpaa_bpid2pool(int bpid) +{ + if (WARN_ON(bpid < 0 || bpid >= BM_MAX_NUM_OF_POOLS)) + return NULL; + + return dpaa_bp_array[bpid]; +} + +/* checks if this bpool is already allocated */ +static bool dpaa_bpid2pool_use(int bpid) +{ + if (dpaa_bpid2pool(bpid)) { + atomic_inc(&dpaa_bp_array[bpid]->refs); + return true; + } + + return false; +} + +/* called only once per bpid by dpaa_bp_alloc_pool() */ +static void dpaa_bpid2pool_map(int bpid, struct dpaa_bp *dpaa_bp) +{ + dpaa_bp_array[bpid] = dpaa_bp; + atomic_set(&dpaa_bp->refs, 1); +} + +static int dpaa_bp_alloc_pool(struct dpaa_bp *dpaa_bp) +{ + int err; + + if (dpaa_bp->size == 0 || dpaa_bp->config_count == 0) { + pr_err("%s: Buffer pool is not properly initialized! Missing size or initial number of buffers\n", + __func__); + return -EINVAL; + } + + /* If the pool is already specified, we only create one per bpid */ + if (dpaa_bp->bpid != FSL_DPAA_BPID_INV && + dpaa_bpid2pool_use(dpaa_bp->bpid)) + return 0; + + if (dpaa_bp->bpid == FSL_DPAA_BPID_INV) { + dpaa_bp->pool = bman_new_pool(); + if (!dpaa_bp->pool) { + pr_err("%s: bman_new_pool() failed\n", + __func__); + return -ENODEV; + } + + dpaa_bp->bpid = (u8)bman_get_bpid(dpaa_bp->pool); + } + + if (dpaa_bp->seed_cb) { + err = dpaa_bp->seed_cb(dpaa_bp); + if (err) + goto pool_seed_failed; + } + + dpaa_bpid2pool_map(dpaa_bp->bpid, dpaa_bp); + + return 0; + +pool_seed_failed: + pr_err("%s: pool seeding failed\n", __func__); + bman_free_pool(dpaa_bp->pool); + + return err; +} + +/* remove and free all the buffers from the given buffer pool */ +static void dpaa_bp_drain(struct dpaa_bp *bp) +{ + u8 num = 8; + int ret; + + do { + struct bm_buffer bmb[8]; + int i; + + ret = bman_acquire(bp->pool, bmb, num); + if (ret < 0) { + if (num == 8) { + /* we have less than 8 buffers left; + * drain them one by one + */ + num = 1; + ret = 1; + continue; + } else { + /* Pool is fully drained */ + break; + } + } + + if (bp->free_buf_cb) + for (i = 0; i < num; i++) + bp->free_buf_cb(bp, &bmb[i]); + } while (ret > 0); +} + +static void dpaa_bp_free(struct dpaa_bp *dpaa_bp) +{ + struct dpaa_bp *bp = dpaa_bpid2pool(dpaa_bp->bpid); + + /* the mapping between bpid and dpaa_bp is done very late in the + * allocation procedure; if something failed before the mapping, the bp + * was not configured, therefore we don't need the below instructions + */ + if (!bp) + return; + + if (!atomic_dec_and_test(&bp->refs)) + return; + + if (bp->free_buf_cb) + dpaa_bp_drain(bp); + + dpaa_bp_array[bp->bpid] = NULL; + bman_free_pool(bp->pool); +} + +static void dpaa_bps_free(struct dpaa_priv *priv) +{ + int i; + + for (i = 0; i < DPAA_BPS_NUM; i++) + dpaa_bp_free(priv->dpaa_bps[i]); +} + +/* Use multiple WQs for FQ assignment: + * - Tx Confirmation queues go to WQ1. + * - Rx Error and Tx Error queues go to WQ2 (giving them a better chance + * to be scheduled, in case there are many more FQs in WQ3). + * - Rx Default and Tx queues go to WQ3 (no differentiation between + * Rx and Tx traffic). + * This ensures that Tx-confirmed buffers are timely released. In particular, + * it avoids congestion on the Tx Confirm FQs, which can pile up PFDRs if they + * are greatly outnumbered by other FQs in the system, while + * dequeue scheduling is round-robin. + */ +static inline void dpaa_assign_wq(struct dpaa_fq *fq) +{ + switch (fq->fq_type) { + case FQ_TYPE_TX_CONFIRM: + case FQ_TYPE_TX_CONF_MQ: + fq->wq = 1; + break; + case FQ_TYPE_RX_ERROR: + case FQ_TYPE_TX_ERROR: + fq->wq = 2; + break; + case FQ_TYPE_RX_DEFAULT: + case FQ_TYPE_TX: + fq->wq = 3; + break; + default: + WARN(1, "Invalid FQ type %d for FQID %d!\n", + fq->fq_type, fq->fqid); + } +} + +static struct dpaa_fq *dpaa_fq_alloc(struct device *dev, + u32 start, u32 count, + struct list_head *list, + enum dpaa_fq_type fq_type) +{ + struct dpaa_fq *dpaa_fq; + int i; + + dpaa_fq = devm_kzalloc(dev, sizeof(*dpaa_fq) * count, + GFP_KERNEL); + if (!dpaa_fq) + return NULL; + + for (i = 0; i < count; i++) { + dpaa_fq[i].fq_type = fq_type; + dpaa_fq[i].fqid = start ? start + i : 0; + list_add_tail(&dpaa_fq[i].list, list); + } + + for (i = 0; i < count; i++) + dpaa_assign_wq(dpaa_fq + i); + + return dpaa_fq; +} + +static int dpaa_alloc_all_fqs(struct device *dev, struct list_head *list, + struct fm_port_fqs *port_fqs) +{ + struct dpaa_fq *dpaa_fq; + + dpaa_fq = dpaa_fq_alloc(dev, 0, 1, list, FQ_TYPE_RX_ERROR); + if (!dpaa_fq) + goto fq_alloc_failed; + + port_fqs->rx_errq = &dpaa_fq[0]; + + dpaa_fq = dpaa_fq_alloc(dev, 0, 1, list, FQ_TYPE_RX_DEFAULT); + if (!dpaa_fq) + goto fq_alloc_failed; + + port_fqs->rx_defq = &dpaa_fq[0]; + + if (!dpaa_fq_alloc(dev, 0, DPAA_ETH_TXQ_NUM, list, FQ_TYPE_TX_CONF_MQ)) + goto fq_alloc_failed; + + dpaa_fq = dpaa_fq_alloc(dev, 0, 1, list, FQ_TYPE_TX_ERROR); + if (!dpaa_fq) + goto fq_alloc_failed; + + port_fqs->tx_errq = &dpaa_fq[0]; + + dpaa_fq = dpaa_fq_alloc(dev, 0, 1, list, FQ_TYPE_TX_CONFIRM); + if (!dpaa_fq) + goto fq_alloc_failed; + + port_fqs->tx_defq = &dpaa_fq[0]; + + if (!dpaa_fq_alloc(dev, 0, DPAA_ETH_TXQ_NUM, list, FQ_TYPE_TX)) + goto fq_alloc_failed; + + return 0; + +fq_alloc_failed: + dev_err(dev, "dpaa_fq_alloc() failed\n"); + return -ENOMEM; +} + +static u32 rx_pool_channel; +static DEFINE_SPINLOCK(rx_pool_channel_init); + +static int dpaa_get_channel(void) +{ + spin_lock(&rx_pool_channel_init); + if (!rx_pool_channel) { + u32 pool; + int ret; + + ret = qman_alloc_pool(&pool); + + if (!ret) + rx_pool_channel = pool; + } + spin_unlock(&rx_pool_channel_init); + if (!rx_pool_channel) + return -ENOMEM; + return rx_pool_channel; +} + +static void dpaa_release_channel(void) +{ + qman_release_pool(rx_pool_channel); +} + +static void dpaa_eth_add_channel(u16 channel) +{ + u32 pool = QM_SDQCR_CHANNELS_POOL_CONV(channel); + const cpumask_t *cpus = qman_affine_cpus(); + struct qman_portal *portal; + int cpu; + + for_each_cpu(cpu, cpus) { + portal = qman_get_affine_portal(cpu); + qman_p_static_dequeue_add(portal, pool); + } +} + +/* Congestion group state change notification callback. + * Stops the device's egress queues while they are congested and + * wakes them upon exiting congested state. + * Also updates some CGR-related stats. + */ +static void dpaa_eth_cgscn(struct qman_portal *qm, struct qman_cgr *cgr, + int congested) +{ + struct dpaa_priv *priv = (struct dpaa_priv *)container_of(cgr, + struct dpaa_priv, cgr_data.cgr); + + if (congested) + netif_tx_stop_all_queues(priv->net_dev); + else + netif_tx_wake_all_queues(priv->net_dev); +} + +static int dpaa_eth_cgr_init(struct dpaa_priv *priv) +{ + struct qm_mcc_initcgr initcgr; + u32 cs_th; + int err; + + err = qman_alloc_cgrid(&priv->cgr_data.cgr.cgrid); + if (err < 0) { + if (netif_msg_drv(priv)) + pr_err("%s: Error %d allocating CGR ID\n", + __func__, err); + goto out_error; + } + priv->cgr_data.cgr.cb = dpaa_eth_cgscn; + + /* Enable Congestion State Change Notifications and CS taildrop */ + initcgr.we_mask = QM_CGR_WE_CSCN_EN | QM_CGR_WE_CS_THRES; + initcgr.cgr.cscn_en = QM_CGR_EN; + + /* Set different thresholds based on the MAC speed. + * This may turn suboptimal if the MAC is reconfigured at a speed + * lower than its max, e.g. if a dTSEC later negotiates a 100Mbps link. + * In such cases, we ought to reconfigure the threshold, too. + */ + if (priv->mac_dev->if_support & SUPPORTED_10000baseT_Full) + cs_th = DPAA_CS_THRESHOLD_10G; + else + cs_th = DPAA_CS_THRESHOLD_1G; + qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1); + + initcgr.we_mask |= QM_CGR_WE_CSTD_EN; + initcgr.cgr.cstd_en = QM_CGR_EN; + + err = qman_create_cgr(&priv->cgr_data.cgr, QMAN_CGR_FLAG_USE_INIT, + &initcgr); + if (err < 0) { + if (netif_msg_drv(priv)) + pr_err("%s: Error %d creating CGR with ID %d\n", + __func__, err, priv->cgr_data.cgr.cgrid); + qman_release_cgrid(priv->cgr_data.cgr.cgrid); + goto out_error; + } + if (netif_msg_drv(priv)) + pr_debug("Created CGR %d for netdev with hwaddr %pM on QMan channel %d\n", + priv->cgr_data.cgr.cgrid, priv->mac_dev->addr, + priv->cgr_data.cgr.chan); + +out_error: + return err; +} + +static inline void dpaa_setup_ingress(const struct dpaa_priv *priv, + struct dpaa_fq *fq, + const struct qman_fq *template) +{ + fq->fq_base = *template; + fq->net_dev = priv->net_dev; + + fq->flags = QMAN_FQ_FLAG_NO_ENQUEUE; + fq->channel = priv->channel; +} + +static inline void dpaa_setup_egress(const struct dpaa_priv *priv, + struct dpaa_fq *fq, + struct fman_port *port, + const struct qman_fq *template) +{ + fq->fq_base = *template; + fq->net_dev = priv->net_dev; + + if (port) { + fq->flags = QMAN_FQ_FLAG_TO_DCPORTAL; + fq->channel = (u16)fman_port_get_qman_channel_id(port); + } else { + fq->flags = QMAN_FQ_FLAG_NO_MODIFY; + } +} + +static void dpaa_fq_setup(struct dpaa_priv *priv, + const struct dpaa_fq_cbs *fq_cbs, + struct fman_port *tx_port) +{ + int egress_cnt = 0, conf_cnt = 0, num_portals = 0, cpu; + const cpumask_t *affine_cpus = qman_affine_cpus(); + u16 portals[NR_CPUS]; + struct dpaa_fq *fq; + + for_each_cpu(cpu, affine_cpus) + portals[num_portals++] = qman_affine_channel(cpu); + if (num_portals == 0) + dev_err(priv->net_dev->dev.parent, + "No Qman software (affine) channels found"); + + /* Initialize each FQ in the list */ + list_for_each_entry(fq, &priv->dpaa_fq_list, list) { + switch (fq->fq_type) { + case FQ_TYPE_RX_DEFAULT: + dpaa_setup_ingress(priv, fq, &fq_cbs->rx_defq); + break; + case FQ_TYPE_RX_ERROR: + dpaa_setup_ingress(priv, fq, &fq_cbs->rx_errq); + break; + case FQ_TYPE_TX: + dpaa_setup_egress(priv, fq, tx_port, + &fq_cbs->egress_ern); + /* If we have more Tx queues than the number of cores, + * just ignore the extra ones. + */ + if (egress_cnt < DPAA_ETH_TXQ_NUM) + priv->egress_fqs[egress_cnt++] = &fq->fq_base; + break; + case FQ_TYPE_TX_CONF_MQ: + priv->conf_fqs[conf_cnt++] = &fq->fq_base; + /* fall through */ + case FQ_TYPE_TX_CONFIRM: + dpaa_setup_ingress(priv, fq, &fq_cbs->tx_defq); + break; + case FQ_TYPE_TX_ERROR: + dpaa_setup_ingress(priv, fq, &fq_cbs->tx_errq); + break; + default: + dev_warn(priv->net_dev->dev.parent, + "Unknown FQ type detected!\n"); + break; + } + } + + /* Make sure all CPUs receive a corresponding Tx queue. */ + while (egress_cnt < DPAA_ETH_TXQ_NUM) { + list_for_each_entry(fq, &priv->dpaa_fq_list, list) { + if (fq->fq_type != FQ_TYPE_TX) + continue; + priv->egress_fqs[egress_cnt++] = &fq->fq_base; + if (egress_cnt == DPAA_ETH_TXQ_NUM) + break; + } + } +} + +static inline int dpaa_tx_fq_to_id(const struct dpaa_priv *priv, + struct qman_fq *tx_fq) +{ + int i; + + for (i = 0; i < DPAA_ETH_TXQ_NUM; i++) + if (priv->egress_fqs[i] == tx_fq) + return i; + + return -EINVAL; +} + +static int dpaa_fq_init(struct dpaa_fq *dpaa_fq, bool td_enable) +{ + const struct dpaa_priv *priv; + struct qman_fq *confq = NULL; + struct qm_mcc_initfq initfq; + struct device *dev; + struct qman_fq *fq; + int queue_id; + int err; + + priv = netdev_priv(dpaa_fq->net_dev); + dev = dpaa_fq->net_dev->dev.parent; + + if (dpaa_fq->fqid == 0) + dpaa_fq->flags |= QMAN_FQ_FLAG_DYNAMIC_FQID; + + dpaa_fq->init = !(dpaa_fq->flags & QMAN_FQ_FLAG_NO_MODIFY); + + err = qman_create_fq(dpaa_fq->fqid, dpaa_fq->flags, &dpaa_fq->fq_base); + if (err) { + dev_err(dev, "qman_create_fq() failed\n"); + return err; + } + fq = &dpaa_fq->fq_base; + + if (dpaa_fq->init) { + memset(&initfq, 0, sizeof(initfq)); + + initfq.we_mask = QM_INITFQ_WE_FQCTRL; + /* Note: we may get to keep an empty FQ in cache */ + initfq.fqd.fq_ctrl = QM_FQCTRL_PREFERINCACHE; + + /* Try to reduce the number of portal interrupts for + * Tx Confirmation FQs. + */ + if (dpaa_fq->fq_type == FQ_TYPE_TX_CONFIRM) + initfq.fqd.fq_ctrl |= QM_FQCTRL_HOLDACTIVE; + + /* FQ placement */ + initfq.we_mask |= QM_INITFQ_WE_DESTWQ; + + qm_fqd_set_destwq(&initfq.fqd, dpaa_fq->channel, dpaa_fq->wq); + + /* Put all egress queues in a congestion group of their own. + * Sensu stricto, the Tx confirmation queues are Rx FQs, + * rather than Tx - but they nonetheless account for the + * memory footprint on behalf of egress traffic. We therefore + * place them in the netdev's CGR, along with the Tx FQs. + */ + if (dpaa_fq->fq_type == FQ_TYPE_TX || + dpaa_fq->fq_type == FQ_TYPE_TX_CONFIRM || + dpaa_fq->fq_type == FQ_TYPE_TX_CONF_MQ) { + initfq.we_mask |= QM_INITFQ_WE_CGID; + initfq.fqd.fq_ctrl |= QM_FQCTRL_CGE; + initfq.fqd.cgid = (u8)priv->cgr_data.cgr.cgrid; + /* Set a fixed overhead accounting, in an attempt to + * reduce the impact of fixed-size skb shells and the + * driver's needed headroom on system memory. This is + * especially the case when the egress traffic is + * composed of small datagrams. + * Unfortunately, QMan's OAL value is capped to an + * insufficient value, but even that is better than + * no overhead accounting at all. + */ + initfq.we_mask |= QM_INITFQ_WE_OAC; + qm_fqd_set_oac(&initfq.fqd, QM_OAC_CG); + qm_fqd_set_oal(&initfq.fqd, + min(sizeof(struct sk_buff) + + priv->tx_headroom, + (size_t)FSL_QMAN_MAX_OAL)); + } + + if (td_enable) { + initfq.we_mask |= QM_INITFQ_WE_TDTHRESH; + qm_fqd_set_taildrop(&initfq.fqd, DPAA_FQ_TD, 1); + initfq.fqd.fq_ctrl = QM_FQCTRL_TDE; + } + + if (dpaa_fq->fq_type == FQ_TYPE_TX) { + queue_id = dpaa_tx_fq_to_id(priv, &dpaa_fq->fq_base); + if (queue_id >= 0) + confq = priv->conf_fqs[queue_id]; + if (confq) { + initfq.we_mask |= QM_INITFQ_WE_CONTEXTA; + /* ContextA: OVOM=1(use contextA2 bits instead of ICAD) + * A2V=1 (contextA A2 field is valid) + * A0V=1 (contextA A0 field is valid) + * B0V=1 (contextB field is valid) + * ContextA A2: EBD=1 (deallocate buffers inside FMan) + * ContextB B0(ASPID): 0 (absolute Virtual Storage ID) + */ + initfq.fqd.context_a.hi = 0x1e000000; + initfq.fqd.context_a.lo = 0x80000000; + } + } + + /* Put all the ingress queues in our "ingress CGR". */ + if (priv->use_ingress_cgr && + (dpaa_fq->fq_type == FQ_TYPE_RX_DEFAULT || + dpaa_fq->fq_type == FQ_TYPE_RX_ERROR)) { + initfq.we_mask |= QM_INITFQ_WE_CGID; + initfq.fqd.fq_ctrl |= QM_FQCTRL_CGE; + initfq.fqd.cgid = (u8)priv->ingress_cgr.cgrid; + /* Set a fixed overhead accounting, just like for the + * egress CGR. + */ + initfq.we_mask |= QM_INITFQ_WE_OAC; + qm_fqd_set_oac(&initfq.fqd, QM_OAC_CG); + qm_fqd_set_oal(&initfq.fqd, + min(sizeof(struct sk_buff) + + priv->tx_headroom, + (size_t)FSL_QMAN_MAX_OAL)); + } + + /* Initialization common to all ingress queues */ + if (dpaa_fq->flags & QMAN_FQ_FLAG_NO_ENQUEUE) { + initfq.we_mask |= QM_INITFQ_WE_CONTEXTA; + initfq.fqd.fq_ctrl |= + QM_FQCTRL_HOLDACTIVE; + initfq.fqd.context_a.stashing.exclusive = + QM_STASHING_EXCL_DATA | QM_STASHING_EXCL_CTX | + QM_STASHING_EXCL_ANNOTATION; + qm_fqd_set_stashing(&initfq.fqd, 1, 2, + DIV_ROUND_UP(sizeof(struct qman_fq), + 64)); + } + + err = qman_init_fq(fq, QMAN_INITFQ_FLAG_SCHED, &initfq); + if (err < 0) { + dev_err(dev, "qman_init_fq(%u) = %d\n", + qman_fq_fqid(fq), err); + qman_destroy_fq(fq); + return err; + } + } + + dpaa_fq->fqid = qman_fq_fqid(fq); + + return 0; +} + +static int dpaa_fq_free_entry(struct device *dev, struct qman_fq *fq) +{ + const struct dpaa_priv *priv; + struct dpaa_fq *dpaa_fq; + int err, error; + + err = 0; + + dpaa_fq = container_of(fq, struct dpaa_fq, fq_base); + priv = netdev_priv(dpaa_fq->net_dev); + + if (dpaa_fq->init) { + err = qman_retire_fq(fq, NULL); + if (err < 0 && netif_msg_drv(priv)) + dev_err(dev, "qman_retire_fq(%u) = %d\n", + qman_fq_fqid(fq), err); + + error = qman_oos_fq(fq); + if (error < 0 && netif_msg_drv(priv)) { + dev_err(dev, "qman_oos_fq(%u) = %d\n", + qman_fq_fqid(fq), error); + if (err >= 0) + err = error; + } + } + + qman_destroy_fq(fq); + list_del(&dpaa_fq->list); + + return err; +} + +static int dpaa_fq_free(struct device *dev, struct list_head *list) +{ + struct dpaa_fq *dpaa_fq, *tmp; + int err, error; + + err = 0; + list_for_each_entry_safe(dpaa_fq, tmp, list, list) { + error = dpaa_fq_free_entry(dev, (struct qman_fq *)dpaa_fq); + if (error < 0 && err >= 0) + err = error; + } + + return err; +} + +static void dpaa_eth_init_tx_port(struct fman_port *port, struct dpaa_fq *errq, + struct dpaa_fq *defq, + struct dpaa_buffer_layout *buf_layout) +{ + struct fman_buffer_prefix_content buf_prefix_content; + struct fman_port_params params; + int err; + + memset(¶ms, 0, sizeof(params)); + memset(&buf_prefix_content, 0, sizeof(buf_prefix_content)); + + buf_prefix_content.priv_data_size = buf_layout->priv_data_size; + buf_prefix_content.pass_prs_result = true; + buf_prefix_content.pass_hash_result = true; + buf_prefix_content.pass_time_stamp = false; + buf_prefix_content.data_align = DPAA_FD_DATA_ALIGNMENT; + + params.specific_params.non_rx_params.err_fqid = errq->fqid; + params.specific_params.non_rx_params.dflt_fqid = defq->fqid; + + err = fman_port_config(port, ¶ms); + if (err) + pr_err("%s: fman_port_config failed\n", __func__); + + err = fman_port_cfg_buf_prefix_content(port, &buf_prefix_content); + if (err) + pr_err("%s: fman_port_cfg_buf_prefix_content failed\n", + __func__); + + err = fman_port_init(port); + if (err) + pr_err("%s: fm_port_init failed\n", __func__); +} + +static void dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, + size_t count, struct dpaa_fq *errq, + struct dpaa_fq *defq, + struct dpaa_buffer_layout *buf_layout) +{ + struct fman_buffer_prefix_content buf_prefix_content; + struct fman_port_rx_params *rx_p; + struct fman_port_params params; + int i, err; + + memset(¶ms, 0, sizeof(params)); + memset(&buf_prefix_content, 0, sizeof(buf_prefix_content)); + + buf_prefix_content.priv_data_size = buf_layout->priv_data_size; + buf_prefix_content.pass_prs_result = true; + buf_prefix_content.pass_hash_result = true; + buf_prefix_content.pass_time_stamp = false; + buf_prefix_content.data_align = DPAA_FD_DATA_ALIGNMENT; + + rx_p = ¶ms.specific_params.rx_params; + rx_p->err_fqid = errq->fqid; + rx_p->dflt_fqid = defq->fqid; + + count = min(ARRAY_SIZE(rx_p->ext_buf_pools.ext_buf_pool), count); + rx_p->ext_buf_pools.num_of_pools_used = (u8)count; + for (i = 0; i < count; i++) { + rx_p->ext_buf_pools.ext_buf_pool[i].id = bps[i]->bpid; + rx_p->ext_buf_pools.ext_buf_pool[i].size = (u16)bps[i]->size; + } + + err = fman_port_config(port, ¶ms); + if (err) + pr_err("%s: fman_port_config failed\n", __func__); + + err = fman_port_cfg_buf_prefix_content(port, &buf_prefix_content); + if (err) + pr_err("%s: fman_port_cfg_buf_prefix_content failed\n", + __func__); + + err = fman_port_init(port); + if (err) + pr_err("%s: fm_port_init failed\n", __func__); +} + +static void dpaa_eth_init_ports(struct mac_device *mac_dev, + struct dpaa_bp **bps, size_t count, + struct fm_port_fqs *port_fqs, + struct dpaa_buffer_layout *buf_layout, + struct device *dev) +{ + struct fman_port *rxport = mac_dev->port[RX]; + struct fman_port *txport = mac_dev->port[TX]; + + dpaa_eth_init_tx_port(txport, port_fqs->tx_errq, + port_fqs->tx_defq, &buf_layout[TX]); + dpaa_eth_init_rx_port(rxport, bps, count, port_fqs->rx_errq, + port_fqs->rx_defq, &buf_layout[RX]); +} + +static int dpaa_bman_release(const struct dpaa_bp *dpaa_bp, + struct bm_buffer *bmb, int cnt) +{ + int err; + + err = bman_release(dpaa_bp->pool, bmb, cnt); + /* Should never occur, address anyway to avoid leaking the buffers */ + if (unlikely(WARN_ON(err)) && dpaa_bp->free_buf_cb) + while (cnt-- > 0) + dpaa_bp->free_buf_cb(dpaa_bp, &bmb[cnt]); + + return cnt; +} + +static void dpaa_release_sgt_members(struct qm_sg_entry *sgt) +{ + struct bm_buffer bmb[DPAA_BUFF_RELEASE_MAX]; + struct dpaa_bp *dpaa_bp; + int i = 0, j; + + memset(bmb, 0, sizeof(bmb)); + + do { + dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); + if (!dpaa_bp) + return; + + j = 0; + do { + WARN_ON(qm_sg_entry_is_ext(&sgt[i])); + + bm_buffer_set64(&bmb[j], qm_sg_entry_get64(&sgt[i])); + + j++; i++; + } while (j < ARRAY_SIZE(bmb) && + !qm_sg_entry_is_final(&sgt[i - 1]) && + sgt[i - 1].bpid == sgt[i].bpid); + + dpaa_bman_release(dpaa_bp, bmb, j); + } while (!qm_sg_entry_is_final(&sgt[i - 1])); +} + +static void dpaa_fd_release(const struct net_device *net_dev, + const struct qm_fd *fd) +{ + struct qm_sg_entry *sgt; + struct dpaa_bp *dpaa_bp; + struct bm_buffer bmb; + dma_addr_t addr; + void *vaddr; + + bmb.data = 0; + bm_buffer_set64(&bmb, qm_fd_addr(fd)); + + dpaa_bp = dpaa_bpid2pool(fd->bpid); + if (!dpaa_bp) + return; + + if (qm_fd_get_format(fd) == qm_fd_sg) { + vaddr = phys_to_virt(qm_fd_addr(fd)); + sgt = vaddr + qm_fd_get_offset(fd); + + dma_unmap_single(dpaa_bp->dev, qm_fd_addr(fd), dpaa_bp->size, + DMA_FROM_DEVICE); + + dpaa_release_sgt_members(sgt); + + addr = dma_map_single(dpaa_bp->dev, vaddr, dpaa_bp->size, + DMA_FROM_DEVICE); + if (dma_mapping_error(dpaa_bp->dev, addr)) { + dev_err(dpaa_bp->dev, "DMA mapping failed"); + return; + } + bm_buffer_set64(&bmb, addr); + } + + dpaa_bman_release(dpaa_bp, &bmb, 1); +} + +/* Turn on HW checksum computation for this outgoing frame. + * If the current protocol is not something we support in this regard + * (or if the stack has already computed the SW checksum), we do nothing. + * + * Returns 0 if all goes well (or HW csum doesn't apply), and a negative value + * otherwise. + * + * Note that this function may modify the fd->cmd field and the skb data buffer + * (the Parse Results area). + */ +static int dpaa_enable_tx_csum(struct dpaa_priv *priv, + struct sk_buff *skb, + struct qm_fd *fd, + char *parse_results) +{ + struct fman_prs_result *parse_result; + u16 ethertype = ntohs(skb->protocol); + struct ipv6hdr *ipv6h = NULL; + struct iphdr *iph; + int retval = 0; + u8 l4_proto; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + /* Note: L3 csum seems to be already computed in sw, but we can't choose + * L4 alone from the FM configuration anyway. + */ + + /* Fill in some fields of the Parse Results array, so the FMan + * can find them as if they came from the FMan Parser. + */ + parse_result = (struct fman_prs_result *)parse_results; + + /* If we're dealing with VLAN, get the real Ethernet type */ + if (ethertype == ETH_P_8021Q) { + /* We can't always assume the MAC header is set correctly + * by the stack, so reset to beginning of skb->data + */ + skb_reset_mac_header(skb); + ethertype = ntohs(vlan_eth_hdr(skb)->h_vlan_encapsulated_proto); + } + + /* Fill in the relevant L3 parse result fields + * and read the L4 protocol type + */ + switch (ethertype) { + case ETH_P_IP: + parse_result->l3r = cpu_to_be16(FM_L3_PARSE_RESULT_IPV4); + iph = ip_hdr(skb); + WARN_ON(!iph); + l4_proto = iph->protocol; + break; + case ETH_P_IPV6: + parse_result->l3r = cpu_to_be16(FM_L3_PARSE_RESULT_IPV6); + ipv6h = ipv6_hdr(skb); + WARN_ON(!ipv6h); + l4_proto = ipv6h->nexthdr; + break; + default: + /* We shouldn't even be here */ + if (net_ratelimit()) + netif_alert(priv, tx_err, priv->net_dev, + "Can't compute HW csum for L3 proto 0x%x\n", + ntohs(skb->protocol)); + retval = -EIO; + goto return_error; + } + + /* Fill in the relevant L4 parse result fields */ + switch (l4_proto) { + case IPPROTO_UDP: + parse_result->l4r = FM_L4_PARSE_RESULT_UDP; + break; + case IPPROTO_TCP: + parse_result->l4r = FM_L4_PARSE_RESULT_TCP; + break; + default: + if (net_ratelimit()) + netif_alert(priv, tx_err, priv->net_dev, + "Can't compute HW csum for L4 proto 0x%x\n", + l4_proto); + retval = -EIO; + goto return_error; + } + + /* At index 0 is IPOffset_1 as defined in the Parse Results */ + parse_result->ip_off[0] = (u8)skb_network_offset(skb); + parse_result->l4_off = (u8)skb_transport_offset(skb); + + /* Enable L3 (and L4, if TCP or UDP) HW checksum. */ + fd->cmd |= FM_FD_CMD_RPD | FM_FD_CMD_DTC; + + /* On P1023 and similar platforms fd->cmd interpretation could + * be disabled by setting CONTEXT_A bit ICMD; currently this bit + * is not set so we do not need to check; in the future, if/when + * using context_a we need to check this bit + */ + +return_error: + return retval; +} + +static int dpaa_bp_add_8_bufs(const struct dpaa_bp *dpaa_bp) +{ + struct device *dev = dpaa_bp->dev; + struct bm_buffer bmb[8]; + dma_addr_t addr; + void *new_buf; + u8 i; + + for (i = 0; i < 8; i++) { + new_buf = netdev_alloc_frag(dpaa_bp->raw_size); + if (unlikely(!new_buf)) { + dev_err(dev, "netdev_alloc_frag() failed, size %zu\n", + dpaa_bp->raw_size); + goto release_previous_buffs; + } + new_buf = PTR_ALIGN(new_buf, SMP_CACHE_BYTES); + + addr = dma_map_single(dev, new_buf, + dpaa_bp->size, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, addr))) { + dev_err(dpaa_bp->dev, "DMA map failed"); + goto release_previous_buffs; + } + + bmb[i].data = 0; + bm_buffer_set64(&bmb[i], addr); + } + +release_bufs: + return dpaa_bman_release(dpaa_bp, bmb, i); + +release_previous_buffs: + WARN_ONCE(1, "dpaa_eth: failed to add buffers on Rx\n"); + + bm_buffer_set64(&bmb[i], 0); + /* Avoid releasing a completely null buffer; bman_release() requires + * at least one buffer. + */ + if (likely(i)) + goto release_bufs; + + return 0; +} + +static int dpaa_bp_seed(struct dpaa_bp *dpaa_bp) +{ + int i; + + /* Give each CPU an allotment of "config_count" buffers */ + for_each_possible_cpu(i) { + int *count_ptr = per_cpu_ptr(dpaa_bp->percpu_count, i); + int j; + + /* Although we access another CPU's counters here + * we do it at boot time so it is safe + */ + for (j = 0; j < dpaa_bp->config_count; j += 8) + *count_ptr += dpaa_bp_add_8_bufs(dpaa_bp); + } + return 0; +} + +/* Add buffers/(pages) for Rx processing whenever bpool count falls below + * REFILL_THRESHOLD. + */ +static int dpaa_eth_refill_bpool(struct dpaa_bp *dpaa_bp, int *countptr) +{ + int count = *countptr; + int new_bufs; + + if (unlikely(count < FSL_DPAA_ETH_REFILL_THRESHOLD)) { + do { + new_bufs = dpaa_bp_add_8_bufs(dpaa_bp); + if (unlikely(!new_bufs)) { + /* Avoid looping forever if we've temporarily + * run out of memory. We'll try again at the + * next NAPI cycle. + */ + break; + } + count += new_bufs; + } while (count < FSL_DPAA_ETH_MAX_BUF_COUNT); + + *countptr = count; + if (unlikely(count < FSL_DPAA_ETH_MAX_BUF_COUNT)) + return -ENOMEM; + } + + return 0; +} + +static int dpaa_eth_refill_bpools(struct dpaa_priv *priv) +{ + struct dpaa_bp *dpaa_bp; + int *countptr; + int res, i; + + for (i = 0; i < DPAA_BPS_NUM; i++) { + dpaa_bp = priv->dpaa_bps[i]; + if (!dpaa_bp) + return -EINVAL; + countptr = this_cpu_ptr(dpaa_bp->percpu_count); + res = dpaa_eth_refill_bpool(dpaa_bp, countptr); + if (res) + return res; + } + return 0; +} + +/* Cleanup function for outgoing frame descriptors that were built on Tx path, + * either contiguous frames or scatter/gather ones. + * Skb freeing is not handled here. + * + * This function may be called on error paths in the Tx function, so guard + * against cases when not all fd relevant fields were filled in. + * + * Return the skb backpointer, since for S/G frames the buffer containing it + * gets freed here. + */ +static struct sk_buff *dpaa_cleanup_tx_fd(const struct dpaa_priv *priv, + const struct qm_fd *fd) +{ + const enum dma_data_direction dma_dir = DMA_TO_DEVICE; + struct device *dev = priv->net_dev->dev.parent; + dma_addr_t addr = qm_fd_addr(fd); + const struct qm_sg_entry *sgt; + struct sk_buff **skbh, *skb; + int nr_frags, i; + + skbh = (struct sk_buff **)phys_to_virt(addr); + skb = *skbh; + + if (unlikely(qm_fd_get_format(fd) == qm_fd_sg)) { + nr_frags = skb_shinfo(skb)->nr_frags; + dma_unmap_single(dev, addr, qm_fd_get_offset(fd) + + sizeof(struct qm_sg_entry) * (1 + nr_frags), + dma_dir); + + /* The sgt buffer has been allocated with netdev_alloc_frag(), + * it's from lowmem. + */ + sgt = phys_to_virt(addr + qm_fd_get_offset(fd)); + + /* sgt[0] is from lowmem, was dma_map_single()-ed */ + dma_unmap_single(dev, qm_sg_addr(&sgt[0]), + qm_sg_entry_get_len(&sgt[0]), dma_dir); + + /* remaining pages were mapped with skb_frag_dma_map() */ + for (i = 1; i < nr_frags; i++) { + WARN_ON(qm_sg_entry_is_ext(&sgt[i])); + + dma_unmap_page(dev, qm_sg_addr(&sgt[i]), + qm_sg_entry_get_len(&sgt[i]), dma_dir); + } + + /* Free the page frag that we allocated on Tx */ + skb_free_frag(phys_to_virt(addr)); + } else { + dma_unmap_single(dev, addr, + skb_tail_pointer(skb) - (u8 *)skbh, dma_dir); + } + + return skb; +} + +/* Build a linear skb around the received buffer. + * We are guaranteed there is enough room at the end of the data buffer to + * accommodate the shared info area of the skb. + */ +static struct sk_buff *contig_fd_to_skb(const struct dpaa_priv *priv, + const struct qm_fd *fd) +{ + ssize_t fd_off = qm_fd_get_offset(fd); + dma_addr_t addr = qm_fd_addr(fd); + struct dpaa_bp *dpaa_bp; + struct sk_buff *skb; + void *vaddr; + + vaddr = phys_to_virt(addr); + WARN_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); + + dpaa_bp = dpaa_bpid2pool(fd->bpid); + if (!dpaa_bp) + goto free_buffer; + + skb = build_skb(vaddr, dpaa_bp->size + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); + if (unlikely(!skb)) { + WARN_ONCE(1, "Build skb failure on Rx\n"); + goto free_buffer; + } + WARN_ON(fd_off != priv->rx_headroom); + skb_reserve(skb, fd_off); + skb_put(skb, qm_fd_get_length(fd)); + + skb->ip_summed = CHECKSUM_NONE; + + return skb; + +free_buffer: + skb_free_frag(vaddr); + return NULL; +} + +/* Build an skb with the data of the first S/G entry in the linear portion and + * the rest of the frame as skb fragments. + * + * The page fragment holding the S/G Table is recycled here. + */ +static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, + const struct qm_fd *fd) +{ + ssize_t fd_off = qm_fd_get_offset(fd); + dma_addr_t addr = qm_fd_addr(fd); + const struct qm_sg_entry *sgt; + struct page *page, *head_page; + struct dpaa_bp *dpaa_bp; + void *vaddr, *sg_vaddr; + int frag_off, frag_len; + struct sk_buff *skb; + dma_addr_t sg_addr; + int page_offset; + unsigned int sz; + int *count_ptr; + int i; + + vaddr = phys_to_virt(addr); + WARN_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); + + /* Iterate through the SGT entries and add data buffers to the skb */ + sgt = vaddr + fd_off; + for (i = 0; i < DPAA_SGT_MAX_ENTRIES; i++) { + /* Extension bit is not supported */ + WARN_ON(qm_sg_entry_is_ext(&sgt[i])); + + sg_addr = qm_sg_addr(&sgt[i]); + sg_vaddr = phys_to_virt(sg_addr); + WARN_ON(!IS_ALIGNED((unsigned long)sg_vaddr, + SMP_CACHE_BYTES)); + + /* We may use multiple Rx pools */ + dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); + if (!dpaa_bp) + goto free_buffers; + + count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); + dma_unmap_single(dpaa_bp->dev, sg_addr, dpaa_bp->size, + DMA_FROM_DEVICE); + if (i == 0) { + sz = dpaa_bp->size + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + skb = build_skb(sg_vaddr, sz); + if (WARN_ON(unlikely(!skb))) + goto free_buffers; + + skb->ip_summed = CHECKSUM_NONE; + + /* Make sure forwarded skbs will have enough space + * on Tx, if extra headers are added. + */ + WARN_ON(fd_off != priv->rx_headroom); + skb_reserve(skb, fd_off); + skb_put(skb, qm_sg_entry_get_len(&sgt[i])); + } else { + /* Not the first S/G entry; all data from buffer will + * be added in an skb fragment; fragment index is offset + * by one since first S/G entry was incorporated in the + * linear part of the skb. + * + * Caution: 'page' may be a tail page. + */ + page = virt_to_page(sg_vaddr); + head_page = virt_to_head_page(sg_vaddr); + + /* Compute offset in (possibly tail) page */ + page_offset = ((unsigned long)sg_vaddr & + (PAGE_SIZE - 1)) + + (page_address(page) - page_address(head_page)); + /* page_offset only refers to the beginning of sgt[i]; + * but the buffer itself may have an internal offset. + */ + frag_off = qm_sg_entry_get_off(&sgt[i]) + page_offset; + frag_len = qm_sg_entry_get_len(&sgt[i]); + /* skb_add_rx_frag() does no checking on the page; if + * we pass it a tail page, we'll end up with + * bad page accounting and eventually with segafults. + */ + skb_add_rx_frag(skb, i - 1, head_page, frag_off, + frag_len, dpaa_bp->size); + } + /* Update the pool count for the current {cpu x bpool} */ + (*count_ptr)--; + + if (qm_sg_entry_is_final(&sgt[i])) + break; + } + WARN_ONCE(i == DPAA_SGT_MAX_ENTRIES, "No final bit on SGT\n"); + + /* free the SG table buffer */ + skb_free_frag(vaddr); + + return skb; + +free_buffers: + /* compensate sw bpool counter changes */ + for (i--; i > 0; i--) { + dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); + if (dpaa_bp) { + count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); + (*count_ptr)++; + } + } + /* free all the SG entries */ + for (i = 0; i < DPAA_SGT_MAX_ENTRIES ; i++) { + sg_addr = qm_sg_addr(&sgt[i]); + sg_vaddr = phys_to_virt(sg_addr); + skb_free_frag(sg_vaddr); + dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); + if (dpaa_bp) { + count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); + (*count_ptr)--; + } + + if (qm_sg_entry_is_final(&sgt[i])) + break; + } + /* free the SGT fragment */ + skb_free_frag(vaddr); + + return NULL; +} + +static int skb_to_contig_fd(struct dpaa_priv *priv, + struct sk_buff *skb, struct qm_fd *fd, + int *offset) +{ + struct net_device *net_dev = priv->net_dev; + struct device *dev = net_dev->dev.parent; + enum dma_data_direction dma_dir; + unsigned char *buffer_start; + struct sk_buff **skbh; + dma_addr_t addr; + int err; + + /* We are guaranteed to have at least tx_headroom bytes + * available, so just use that for offset. + */ + fd->bpid = FSL_DPAA_BPID_INV; + buffer_start = skb->data - priv->tx_headroom; + dma_dir = DMA_TO_DEVICE; + + skbh = (struct sk_buff **)buffer_start; + *skbh = skb; + + /* Enable L3/L4 hardware checksum computation. + * + * We must do this before dma_map_single(DMA_TO_DEVICE), because we may + * need to write into the skb. + */ + err = dpaa_enable_tx_csum(priv, skb, fd, + ((char *)skbh) + DPAA_TX_PRIV_DATA_SIZE); + if (unlikely(err < 0)) { + if (net_ratelimit()) + netif_err(priv, tx_err, net_dev, "HW csum error: %d\n", + err); + return err; + } + + /* Fill in the rest of the FD fields */ + qm_fd_set_contig(fd, priv->tx_headroom, skb->len); + fd->cmd |= FM_FD_CMD_FCO; + + /* Map the entire buffer size that may be seen by FMan, but no more */ + addr = dma_map_single(dev, skbh, + skb_tail_pointer(skb) - buffer_start, dma_dir); + if (unlikely(dma_mapping_error(dev, addr))) { + if (net_ratelimit()) + netif_err(priv, tx_err, net_dev, "dma_map_single() failed\n"); + return -EINVAL; + } + qm_fd_addr_set64(fd, addr); + + return 0; +} + +static int skb_to_sg_fd(struct dpaa_priv *priv, + struct sk_buff *skb, struct qm_fd *fd) +{ + const enum dma_data_direction dma_dir = DMA_TO_DEVICE; + const int nr_frags = skb_shinfo(skb)->nr_frags; + struct net_device *net_dev = priv->net_dev; + struct device *dev = net_dev->dev.parent; + struct qm_sg_entry *sgt; + struct sk_buff **skbh; + int i, j, err, sz; + void *buffer_start; + skb_frag_t *frag; + dma_addr_t addr; + size_t frag_len; + void *sgt_buf; + + /* get a page frag to store the SGTable */ + sz = SKB_DATA_ALIGN(priv->tx_headroom + + sizeof(struct qm_sg_entry) * (1 + nr_frags)); + sgt_buf = netdev_alloc_frag(sz); + if (unlikely(!sgt_buf)) { + netdev_err(net_dev, "netdev_alloc_frag() failed for size %d\n", + sz); + return -ENOMEM; + } + + /* Enable L3/L4 hardware checksum computation. + * + * We must do this before dma_map_single(DMA_TO_DEVICE), because we may + * need to write into the skb. + */ + err = dpaa_enable_tx_csum(priv, skb, fd, + sgt_buf + DPAA_TX_PRIV_DATA_SIZE); + if (unlikely(err < 0)) { + if (net_ratelimit()) + netif_err(priv, tx_err, net_dev, "HW csum error: %d\n", + err); + goto csum_failed; + } + + sgt = (struct qm_sg_entry *)(sgt_buf + priv->tx_headroom); + qm_sg_entry_set_len(&sgt[0], skb_headlen(skb)); + sgt[0].bpid = FSL_DPAA_BPID_INV; + sgt[0].offset = 0; + addr = dma_map_single(dev, skb->data, + skb_headlen(skb), dma_dir); + if (unlikely(dma_mapping_error(dev, addr))) { + dev_err(dev, "DMA mapping failed"); + err = -EINVAL; + goto sg0_map_failed; + } + qm_sg_entry_set64(&sgt[0], addr); + + /* populate the rest of SGT entries */ + frag = &skb_shinfo(skb)->frags[0]; + frag_len = frag->size; + for (i = 1; i <= nr_frags; i++, frag++) { + WARN_ON(!skb_frag_page(frag)); + addr = skb_frag_dma_map(dev, frag, 0, + frag_len, dma_dir); + if (unlikely(dma_mapping_error(dev, addr))) { + dev_err(dev, "DMA mapping failed"); + err = -EINVAL; + goto sg_map_failed; + } + + qm_sg_entry_set_len(&sgt[i], frag_len); + sgt[i].bpid = FSL_DPAA_BPID_INV; + sgt[i].offset = 0; + + /* keep the offset in the address */ + qm_sg_entry_set64(&sgt[i], addr); + frag_len = frag->size; + } + qm_sg_entry_set_f(&sgt[i - 1], frag_len); + + qm_fd_set_sg(fd, priv->tx_headroom, skb->len); + + /* DMA map the SGT page */ + buffer_start = (void *)sgt - priv->tx_headroom; + skbh = (struct sk_buff **)buffer_start; + *skbh = skb; + + addr = dma_map_single(dev, buffer_start, priv->tx_headroom + + sizeof(struct qm_sg_entry) * (1 + nr_frags), + dma_dir); + if (unlikely(dma_mapping_error(dev, addr))) { + dev_err(dev, "DMA mapping failed"); + err = -EINVAL; + goto sgt_map_failed; + } + + fd->bpid = FSL_DPAA_BPID_INV; + fd->cmd |= FM_FD_CMD_FCO; + qm_fd_addr_set64(fd, addr); + + return 0; + +sgt_map_failed: +sg_map_failed: + for (j = 0; j < i; j++) + dma_unmap_page(dev, qm_sg_addr(&sgt[j]), + qm_sg_entry_get_len(&sgt[j]), dma_dir); +sg0_map_failed: +csum_failed: + skb_free_frag(sgt_buf); + + return err; +} + +static inline int dpaa_xmit(struct dpaa_priv *priv, + struct rtnl_link_stats64 *percpu_stats, + int queue, + struct qm_fd *fd) +{ + struct qman_fq *egress_fq; + int err, i; + + egress_fq = priv->egress_fqs[queue]; + if (fd->bpid == FSL_DPAA_BPID_INV) + fd->cmd |= qman_fq_fqid(priv->conf_fqs[queue]); + + for (i = 0; i < DPAA_ENQUEUE_RETRIES; i++) { + err = qman_enqueue(egress_fq, fd); + if (err != -EBUSY) + break; + } + + if (unlikely(err < 0)) { + percpu_stats->tx_errors++; + percpu_stats->tx_fifo_errors++; + return err; + } + + percpu_stats->tx_packets++; + percpu_stats->tx_bytes += qm_fd_get_length(fd); + + return 0; +} + +static int dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev) +{ + const int queue_mapping = skb_get_queue_mapping(skb); + bool nonlinear = skb_is_nonlinear(skb); + struct rtnl_link_stats64 *percpu_stats; + struct dpaa_percpu_priv *percpu_priv; + struct dpaa_priv *priv; + struct qm_fd fd; + int offset = 0; + int err = 0; + + priv = netdev_priv(net_dev); + percpu_priv = this_cpu_ptr(priv->percpu_priv); + percpu_stats = &percpu_priv->stats; + + qm_fd_clear_fd(&fd); + + if (!nonlinear) { + /* We're going to store the skb backpointer at the beginning + * of the data buffer, so we need a privately owned skb + * + * We've made sure skb is not shared in dev->priv_flags, + * we need to verify the skb head is not cloned + */ + if (skb_cow_head(skb, priv->tx_headroom)) + goto enomem; + + WARN_ON(skb_is_nonlinear(skb)); + } + + /* MAX_SKB_FRAGS is equal or larger than our dpaa_SGT_MAX_ENTRIES; + * make sure we don't feed FMan with more fragments than it supports. + */ + if (nonlinear && + likely(skb_shinfo(skb)->nr_frags < DPAA_SGT_MAX_ENTRIES)) { + /* Just create a S/G fd based on the skb */ + err = skb_to_sg_fd(priv, skb, &fd); + } else { + /* If the egress skb contains more fragments than we support + * we have no choice but to linearize it ourselves. + */ + if (unlikely(nonlinear) && __skb_linearize(skb)) + goto enomem; + + /* Finally, create a contig FD from this skb */ + err = skb_to_contig_fd(priv, skb, &fd, &offset); + } + if (unlikely(err < 0)) + goto skb_to_fd_failed; + + if (likely(dpaa_xmit(priv, percpu_stats, queue_mapping, &fd) == 0)) + return NETDEV_TX_OK; + + dpaa_cleanup_tx_fd(priv, &fd); +skb_to_fd_failed: +enomem: + percpu_stats->tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static void dpaa_rx_error(struct net_device *net_dev, + const struct dpaa_priv *priv, + struct dpaa_percpu_priv *percpu_priv, + const struct qm_fd *fd, + u32 fqid) +{ + if (net_ratelimit()) + netif_err(priv, hw, net_dev, "Err FD status = 0x%08x\n", + fd->status & FM_FD_STAT_RX_ERRORS); + + percpu_priv->stats.rx_errors++; + + dpaa_fd_release(net_dev, fd); +} + +static void dpaa_tx_error(struct net_device *net_dev, + const struct dpaa_priv *priv, + struct dpaa_percpu_priv *percpu_priv, + const struct qm_fd *fd, + u32 fqid) +{ + struct sk_buff *skb; + + if (net_ratelimit()) + netif_warn(priv, hw, net_dev, "FD status = 0x%08x\n", + fd->status & FM_FD_STAT_TX_ERRORS); + + percpu_priv->stats.tx_errors++; + + skb = dpaa_cleanup_tx_fd(priv, fd); + dev_kfree_skb(skb); +} + +static int dpaa_eth_poll(struct napi_struct *napi, int budget) +{ + struct dpaa_napi_portal *np = + container_of(napi, struct dpaa_napi_portal, napi); + + int cleaned = qman_p_poll_dqrr(np->p, budget); + + if (cleaned < budget) { + napi_complete(napi); + qman_p_irqsource_add(np->p, QM_PIRQ_DQRI); + + } else if (np->down) { + qman_p_irqsource_add(np->p, QM_PIRQ_DQRI); + } + + return cleaned; +} + +static void dpaa_tx_conf(struct net_device *net_dev, + const struct dpaa_priv *priv, + struct dpaa_percpu_priv *percpu_priv, + const struct qm_fd *fd, + u32 fqid) +{ + struct sk_buff *skb; + + if (unlikely(fd->status & FM_FD_STAT_TX_ERRORS) != 0) { + if (net_ratelimit()) + netif_warn(priv, hw, net_dev, "FD status = 0x%08x\n", + fd->status & FM_FD_STAT_TX_ERRORS); + + percpu_priv->stats.tx_errors++; + } + + skb = dpaa_cleanup_tx_fd(priv, fd); + + consume_skb(skb); +} + +static inline int dpaa_eth_napi_schedule(struct dpaa_percpu_priv *percpu_priv, + struct qman_portal *portal) +{ + if (unlikely(in_irq() || !in_serving_softirq())) { + /* Disable QMan IRQ and invoke NAPI */ + qman_p_irqsource_remove(portal, QM_PIRQ_DQRI); + + percpu_priv->np.p = portal; + napi_schedule(&percpu_priv->np.napi); + return 1; + } + return 0; +} + +static enum qman_cb_dqrr_result rx_error_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct dpaa_fq *dpaa_fq = container_of(fq, struct dpaa_fq, fq_base); + struct dpaa_percpu_priv *percpu_priv; + struct net_device *net_dev; + struct dpaa_bp *dpaa_bp; + struct dpaa_priv *priv; + + net_dev = dpaa_fq->net_dev; + priv = netdev_priv(net_dev); + dpaa_bp = dpaa_bpid2pool(dq->fd.bpid); + if (!dpaa_bp) + return qman_cb_dqrr_consume; + + percpu_priv = this_cpu_ptr(priv->percpu_priv); + + if (dpaa_eth_napi_schedule(percpu_priv, portal)) + return qman_cb_dqrr_stop; + + if (dpaa_eth_refill_bpools(priv)) + /* Unable to refill the buffer pool due to insufficient + * system memory. Just release the frame back into the pool, + * otherwise we'll soon end up with an empty buffer pool. + */ + dpaa_fd_release(net_dev, &dq->fd); + else + dpaa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); + + return qman_cb_dqrr_consume; +} + +static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct rtnl_link_stats64 *percpu_stats; + struct dpaa_percpu_priv *percpu_priv; + const struct qm_fd *fd = &dq->fd; + dma_addr_t addr = qm_fd_addr(fd); + enum qm_fd_format fd_format; + struct net_device *net_dev; + u32 fd_status = fd->status; + struct dpaa_bp *dpaa_bp; + struct dpaa_priv *priv; + unsigned int skb_len; + struct sk_buff *skb; + int *count_ptr; + + net_dev = ((struct dpaa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + dpaa_bp = dpaa_bpid2pool(dq->fd.bpid); + if (!dpaa_bp) + return qman_cb_dqrr_consume; + + percpu_priv = this_cpu_ptr(priv->percpu_priv); + percpu_stats = &percpu_priv->stats; + + if (unlikely(dpaa_eth_napi_schedule(percpu_priv, portal))) + return qman_cb_dqrr_stop; + + /* Make sure we didn't run out of buffers */ + if (unlikely(dpaa_eth_refill_bpools(priv))) { + /* Unable to refill the buffer pool due to insufficient + * system memory. Just release the frame back into the pool, + * otherwise we'll soon end up with an empty buffer pool. + */ + dpaa_fd_release(net_dev, &dq->fd); + return qman_cb_dqrr_consume; + } + + if (unlikely(fd_status & FM_FD_STAT_RX_ERRORS) != 0) { + if (net_ratelimit()) + netif_warn(priv, hw, net_dev, "FD status = 0x%08x\n", + fd_status & FM_FD_STAT_RX_ERRORS); + + percpu_stats->rx_errors++; + dpaa_fd_release(net_dev, fd); + return qman_cb_dqrr_consume; + } + + dpaa_bp = dpaa_bpid2pool(fd->bpid); + if (!dpaa_bp) + return qman_cb_dqrr_consume; + + dma_unmap_single(dpaa_bp->dev, addr, dpaa_bp->size, DMA_FROM_DEVICE); + + /* prefetch the first 64 bytes of the frame or the SGT start */ + prefetch(phys_to_virt(addr) + qm_fd_get_offset(fd)); + + fd_format = qm_fd_get_format(fd); + /* The only FD types that we may receive are contig and S/G */ + WARN_ON((fd_format != qm_fd_contig) && (fd_format != qm_fd_sg)); + + /* Account for either the contig buffer or the SGT buffer (depending on + * which case we were in) having been removed from the pool. + */ + count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); + (*count_ptr)--; + + if (likely(fd_format == qm_fd_contig)) + skb = contig_fd_to_skb(priv, fd); + else + skb = sg_fd_to_skb(priv, fd); + if (!skb) + return qman_cb_dqrr_consume; + + skb->protocol = eth_type_trans(skb, net_dev); + + skb_len = skb->len; + + if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) + return qman_cb_dqrr_consume; + + percpu_stats->rx_packets++; + percpu_stats->rx_bytes += skb_len; + + return qman_cb_dqrr_consume; +} + +static enum qman_cb_dqrr_result conf_error_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct dpaa_percpu_priv *percpu_priv; + struct net_device *net_dev; + struct dpaa_priv *priv; + + net_dev = ((struct dpaa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + + percpu_priv = this_cpu_ptr(priv->percpu_priv); + + if (dpaa_eth_napi_schedule(percpu_priv, portal)) + return qman_cb_dqrr_stop; + + dpaa_tx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); + + return qman_cb_dqrr_consume; +} + +static enum qman_cb_dqrr_result conf_dflt_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct dpaa_percpu_priv *percpu_priv; + struct net_device *net_dev; + struct dpaa_priv *priv; + + net_dev = ((struct dpaa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + + percpu_priv = this_cpu_ptr(priv->percpu_priv); + + if (dpaa_eth_napi_schedule(percpu_priv, portal)) + return qman_cb_dqrr_stop; + + dpaa_tx_conf(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); + + return qman_cb_dqrr_consume; +} + +static void egress_ern(struct qman_portal *portal, + struct qman_fq *fq, + const union qm_mr_entry *msg) +{ + const struct qm_fd *fd = &msg->ern.fd; + struct dpaa_percpu_priv *percpu_priv; + const struct dpaa_priv *priv; + struct net_device *net_dev; + struct sk_buff *skb; + + net_dev = ((struct dpaa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + percpu_priv = this_cpu_ptr(priv->percpu_priv); + + percpu_priv->stats.tx_dropped++; + percpu_priv->stats.tx_fifo_errors++; + + skb = dpaa_cleanup_tx_fd(priv, fd); + dev_kfree_skb_any(skb); +} + +static const struct dpaa_fq_cbs dpaa_fq_cbs = { + .rx_defq = { .cb = { .dqrr = rx_default_dqrr } }, + .tx_defq = { .cb = { .dqrr = conf_dflt_dqrr } }, + .rx_errq = { .cb = { .dqrr = rx_error_dqrr } }, + .tx_errq = { .cb = { .dqrr = conf_error_dqrr } }, + .egress_ern = { .cb = { .ern = egress_ern } } +}; + +static void dpaa_eth_napi_enable(struct dpaa_priv *priv) +{ + struct dpaa_percpu_priv *percpu_priv; + int i; + + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + + percpu_priv->np.down = 0; + napi_enable(&percpu_priv->np.napi); + } +} + +static void dpaa_eth_napi_disable(struct dpaa_priv *priv) +{ + struct dpaa_percpu_priv *percpu_priv; + int i; + + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + + percpu_priv->np.down = 1; + napi_disable(&percpu_priv->np.napi); + } +} + +static int dpaa_open(struct net_device *net_dev) +{ + struct mac_device *mac_dev; + struct dpaa_priv *priv; + int err, i; + + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + dpaa_eth_napi_enable(priv); + + net_dev->phydev = mac_dev->init_phy(net_dev, priv->mac_dev); + if (!net_dev->phydev) { + netif_err(priv, ifup, net_dev, "init_phy() failed\n"); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { + err = fman_port_enable(mac_dev->port[i]); + if (err) + goto mac_start_failed; + } + + err = priv->mac_dev->start(mac_dev); + if (err < 0) { + netif_err(priv, ifup, net_dev, "mac_dev->start() = %d\n", err); + goto mac_start_failed; + } + + netif_tx_start_all_queues(net_dev); + + return 0; + +mac_start_failed: + for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) + fman_port_disable(mac_dev->port[i]); + + dpaa_eth_napi_disable(priv); + + return err; +} + +static int dpaa_eth_stop(struct net_device *net_dev) +{ + struct dpaa_priv *priv; + int err; + + err = dpaa_stop(net_dev); + + priv = netdev_priv(net_dev); + dpaa_eth_napi_disable(priv); + + return err; +} + +static const struct net_device_ops dpaa_ops = { + .ndo_open = dpaa_open, + .ndo_start_xmit = dpaa_start_xmit, + .ndo_stop = dpaa_eth_stop, + .ndo_tx_timeout = dpaa_tx_timeout, + .ndo_get_stats64 = dpaa_get_stats64, + .ndo_set_mac_address = dpaa_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = dpaa_set_rx_mode, +}; + +static int dpaa_napi_add(struct net_device *net_dev) +{ + struct dpaa_priv *priv = netdev_priv(net_dev); + struct dpaa_percpu_priv *percpu_priv; + int cpu; + + for_each_possible_cpu(cpu) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, cpu); + + netif_napi_add(net_dev, &percpu_priv->np.napi, + dpaa_eth_poll, NAPI_POLL_WEIGHT); + } + + return 0; +} + +static void dpaa_napi_del(struct net_device *net_dev) +{ + struct dpaa_priv *priv = netdev_priv(net_dev); + struct dpaa_percpu_priv *percpu_priv; + int cpu; + + for_each_possible_cpu(cpu) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, cpu); + + netif_napi_del(&percpu_priv->np.napi); + } +} + +static inline void dpaa_bp_free_pf(const struct dpaa_bp *bp, + struct bm_buffer *bmb) +{ + dma_addr_t addr = bm_buf_addr(bmb); + + dma_unmap_single(bp->dev, addr, bp->size, DMA_FROM_DEVICE); + + skb_free_frag(phys_to_virt(addr)); +} + +/* Alloc the dpaa_bp struct and configure default values */ +static struct dpaa_bp *dpaa_bp_alloc(struct device *dev) +{ + struct dpaa_bp *dpaa_bp; + + dpaa_bp = devm_kzalloc(dev, sizeof(*dpaa_bp), GFP_KERNEL); + if (!dpaa_bp) + return ERR_PTR(-ENOMEM); + + dpaa_bp->bpid = FSL_DPAA_BPID_INV; + dpaa_bp->percpu_count = devm_alloc_percpu(dev, *dpaa_bp->percpu_count); + dpaa_bp->config_count = FSL_DPAA_ETH_MAX_BUF_COUNT; + + dpaa_bp->seed_cb = dpaa_bp_seed; + dpaa_bp->free_buf_cb = dpaa_bp_free_pf; + + return dpaa_bp; +} + +/* Place all ingress FQs (Rx Default, Rx Error) in a dedicated CGR. + * We won't be sending congestion notifications to FMan; for now, we just use + * this CGR to generate enqueue rejections to FMan in order to drop the frames + * before they reach our ingress queues and eat up memory. + */ +static int dpaa_ingress_cgr_init(struct dpaa_priv *priv) +{ + struct qm_mcc_initcgr initcgr; + u32 cs_th; + int err; + + err = qman_alloc_cgrid(&priv->ingress_cgr.cgrid); + if (err < 0) { + if (netif_msg_drv(priv)) + pr_err("Error %d allocating CGR ID\n", err); + goto out_error; + } + + /* Enable CS TD, but disable Congestion State Change Notifications. */ + initcgr.we_mask = QM_CGR_WE_CS_THRES; + initcgr.cgr.cscn_en = QM_CGR_EN; + cs_th = DPAA_INGRESS_CS_THRESHOLD; + qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1); + + initcgr.we_mask |= QM_CGR_WE_CSTD_EN; + initcgr.cgr.cstd_en = QM_CGR_EN; + + /* This CGR will be associated with the SWP affined to the current CPU. + * However, we'll place all our ingress FQs in it. + */ + err = qman_create_cgr(&priv->ingress_cgr, QMAN_CGR_FLAG_USE_INIT, + &initcgr); + if (err < 0) { + if (netif_msg_drv(priv)) + pr_err("Error %d creating ingress CGR with ID %d\n", + err, priv->ingress_cgr.cgrid); + qman_release_cgrid(priv->ingress_cgr.cgrid); + goto out_error; + } + if (netif_msg_drv(priv)) + pr_debug("Created ingress CGR %d for netdev with hwaddr %pM\n", + priv->ingress_cgr.cgrid, priv->mac_dev->addr); + + priv->use_ingress_cgr = true; + +out_error: + return err; +} + +static const struct of_device_id dpaa_match[]; + +static inline u16 dpaa_get_headroom(struct dpaa_buffer_layout *bl) +{ + u16 headroom; + + /* The frame headroom must accommodate: + * - the driver private data area + * - parse results, hash results, timestamp if selected + * If either hash results or time stamp are selected, both will + * be copied to/from the frame headroom, as TS is located between PR and + * HR in the IC and IC copy size has a granularity of 16bytes + * (see description of FMBM_RICP and FMBM_TICP registers in DPAARM) + * + * Also make sure the headroom is a multiple of data_align bytes + */ + headroom = (u16)(bl->priv_data_size + DPAA_PARSE_RESULTS_SIZE + + DPAA_TIME_STAMP_SIZE + DPAA_HASH_RESULTS_SIZE); + + return DPAA_FD_DATA_ALIGNMENT ? ALIGN(headroom, + DPAA_FD_DATA_ALIGNMENT) : + headroom; +} + +static int dpaa_eth_probe(struct platform_device *pdev) +{ + struct dpaa_bp *dpaa_bps[DPAA_BPS_NUM] = {NULL}; + struct dpaa_percpu_priv *percpu_priv; + struct net_device *net_dev = NULL; + struct dpaa_fq *dpaa_fq, *tmp; + struct dpaa_priv *priv = NULL; + struct fm_port_fqs port_fqs; + struct mac_device *mac_dev; + int err = 0, i, channel; + struct device *dev; + + dev = &pdev->dev; + + /* Allocate this early, so we can store relevant information in + * the private area + */ + net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA_ETH_TXQ_NUM); + if (!net_dev) { + dev_err(dev, "alloc_etherdev_mq() failed\n"); + goto alloc_etherdev_mq_failed; + } + + /* Do this here, so we can be verbose early */ + SET_NETDEV_DEV(net_dev, dev); + dev_set_drvdata(dev, net_dev); + + priv = netdev_priv(net_dev); + priv->net_dev = net_dev; + + priv->msg_enable = netif_msg_init(debug, DPAA_MSG_DEFAULT); + + mac_dev = dpaa_mac_dev_get(pdev); + if (IS_ERR(mac_dev)) { + dev_err(dev, "dpaa_mac_dev_get() failed\n"); + err = PTR_ERR(mac_dev); + goto mac_probe_failed; + } + + /* If fsl_fm_max_frm is set to a higher value than the all-common 1500, + * we choose conservatively and let the user explicitly set a higher + * MTU via ifconfig. Otherwise, the user may end up with different MTUs + * in the same LAN. + * If on the other hand fsl_fm_max_frm has been chosen below 1500, + * start with the maximum allowed. + */ + net_dev->mtu = min(dpaa_get_max_mtu(), ETH_DATA_LEN); + + netdev_dbg(net_dev, "Setting initial MTU on net device: %d\n", + net_dev->mtu); + + priv->buf_layout[RX].priv_data_size = DPAA_RX_PRIV_DATA_SIZE; /* Rx */ + priv->buf_layout[TX].priv_data_size = DPAA_TX_PRIV_DATA_SIZE; /* Tx */ + + /* device used for DMA mapping */ + arch_setup_dma_ops(dev, 0, 0, NULL, false); + err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(40)); + if (err) { + dev_err(dev, "dma_coerce_mask_and_coherent() failed\n"); + goto dev_mask_failed; + } + + /* bp init */ + for (i = 0; i < DPAA_BPS_NUM; i++) { + int err; + + dpaa_bps[i] = dpaa_bp_alloc(dev); + if (IS_ERR(dpaa_bps[i])) + return PTR_ERR(dpaa_bps[i]); + /* the raw size of the buffers used for reception */ + dpaa_bps[i]->raw_size = bpool_buffer_raw_size(i, DPAA_BPS_NUM); + /* avoid runtime computations by keeping the usable size here */ + dpaa_bps[i]->size = dpaa_bp_size(dpaa_bps[i]->raw_size); + dpaa_bps[i]->dev = dev; + + err = dpaa_bp_alloc_pool(dpaa_bps[i]); + if (err < 0) { + dpaa_bps_free(priv); + priv->dpaa_bps[i] = NULL; + goto bp_create_failed; + } + priv->dpaa_bps[i] = dpaa_bps[i]; + } + + INIT_LIST_HEAD(&priv->dpaa_fq_list); + + memset(&port_fqs, 0, sizeof(port_fqs)); + + err = dpaa_alloc_all_fqs(dev, &priv->dpaa_fq_list, &port_fqs); + if (err < 0) { + dev_err(dev, "dpaa_alloc_all_fqs() failed\n"); + goto fq_probe_failed; + } + + priv->mac_dev = mac_dev; + + channel = dpaa_get_channel(); + if (channel < 0) { + dev_err(dev, "dpaa_get_channel() failed\n"); + err = channel; + goto get_channel_failed; + } + + priv->channel = (u16)channel; + + /* Start a thread that will walk the CPUs with affine portals + * and add this pool channel to each's dequeue mask. + */ + dpaa_eth_add_channel(priv->channel); + + dpaa_fq_setup(priv, &dpaa_fq_cbs, priv->mac_dev->port[TX]); + + /* Create a congestion group for this netdev, with + * dynamically-allocated CGR ID. + * Must be executed after probing the MAC, but before + * assigning the egress FQs to the CGRs. + */ + err = dpaa_eth_cgr_init(priv); + if (err < 0) { + dev_err(dev, "Error initializing CGR\n"); + goto tx_cgr_init_failed; + } + + err = dpaa_ingress_cgr_init(priv); + if (err < 0) { + dev_err(dev, "Error initializing ingress CGR\n"); + goto rx_cgr_init_failed; + } + + /* Add the FQs to the interface, and make them active */ + list_for_each_entry_safe(dpaa_fq, tmp, &priv->dpaa_fq_list, list) { + err = dpaa_fq_init(dpaa_fq, false); + if (err < 0) + goto fq_alloc_failed; + } + + priv->tx_headroom = dpaa_get_headroom(&priv->buf_layout[TX]); + priv->rx_headroom = dpaa_get_headroom(&priv->buf_layout[RX]); + + /* All real interfaces need their ports initialized */ + dpaa_eth_init_ports(mac_dev, dpaa_bps, DPAA_BPS_NUM, &port_fqs, + &priv->buf_layout[0], dev); + + priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); + if (!priv->percpu_priv) { + dev_err(dev, "devm_alloc_percpu() failed\n"); + err = -ENOMEM; + goto alloc_percpu_failed; + } + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + memset(percpu_priv, 0, sizeof(*percpu_priv)); + } + + /* Initialize NAPI */ + err = dpaa_napi_add(net_dev); + if (err < 0) + goto napi_add_failed; + + err = dpaa_netdev_init(net_dev, &dpaa_ops, tx_timeout); + if (err < 0) + goto netdev_init_failed; + + netif_info(priv, probe, net_dev, "Probed interface %s\n", + net_dev->name); + + return 0; + +netdev_init_failed: +napi_add_failed: + dpaa_napi_del(net_dev); +alloc_percpu_failed: + dpaa_fq_free(dev, &priv->dpaa_fq_list); +fq_alloc_failed: + qman_delete_cgr_safe(&priv->ingress_cgr); + qman_release_cgrid(priv->ingress_cgr.cgrid); +rx_cgr_init_failed: + qman_delete_cgr_safe(&priv->cgr_data.cgr); + qman_release_cgrid(priv->cgr_data.cgr.cgrid); +tx_cgr_init_failed: +get_channel_failed: + dpaa_bps_free(priv); +bp_create_failed: +fq_probe_failed: +dev_mask_failed: +mac_probe_failed: + dev_set_drvdata(dev, NULL); + free_netdev(net_dev); +alloc_etherdev_mq_failed: + for (i = 0; i < DPAA_BPS_NUM && dpaa_bps[i]; i++) { + if (atomic_read(&dpaa_bps[i]->refs) == 0) + devm_kfree(dev, dpaa_bps[i]); + } + return err; +} + +static int dpaa_remove(struct platform_device *pdev) +{ + struct net_device *net_dev; + struct dpaa_priv *priv; + struct device *dev; + int err; + + dev = &pdev->dev; + net_dev = dev_get_drvdata(dev); + + priv = netdev_priv(net_dev); + + dev_set_drvdata(dev, NULL); + unregister_netdev(net_dev); + + err = dpaa_fq_free(dev, &priv->dpaa_fq_list); + + qman_delete_cgr_safe(&priv->ingress_cgr); + qman_release_cgrid(priv->ingress_cgr.cgrid); + qman_delete_cgr_safe(&priv->cgr_data.cgr); + qman_release_cgrid(priv->cgr_data.cgr.cgrid); + + dpaa_napi_del(net_dev); + + dpaa_bps_free(priv); + + free_netdev(net_dev); + + return err; +} + +static struct platform_device_id dpaa_devtype[] = { + { + .name = "dpaa-ethernet", + .driver_data = 0, + }, { + } +}; +MODULE_DEVICE_TABLE(platform, dpaa_devtype); + +static struct platform_driver dpaa_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .id_table = dpaa_devtype, + .probe = dpaa_eth_probe, + .remove = dpaa_remove +}; + +static int __init dpaa_load(void) +{ + int err; + + pr_debug("FSL DPAA Ethernet driver\n"); + + /* initialize dpaa_eth mirror values */ + dpaa_rx_extra_headroom = fman_get_rx_extra_headroom(); + dpaa_max_frm = fman_get_max_frm(); + + err = platform_driver_register(&dpaa_driver); + if (err < 0) + pr_err("Error, platform_driver_register() = %d\n", err); + + return err; +} +module_init(dpaa_load); + +static void __exit dpaa_unload(void) +{ + platform_driver_unregister(&dpaa_driver); + + /* Only one channel is used and needs to be released after all + * interfaces are removed + */ + dpaa_release_channel(); +} +module_exit(dpaa_unload); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("FSL DPAA Ethernet driver"); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h new file mode 100644 index 0000000..fe98e08 --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -0,0 +1,144 @@ +/* Copyright 2008 - 2016 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DPAA_H +#define __DPAA_H + +#include +#include +#include + +#include "fman.h" +#include "mac.h" + +#define DPAA_ETH_TXQ_NUM NR_CPUS + +#define DPAA_BPS_NUM 3 /* number of bpools per interface */ + +/* More detailed FQ types - used for fine-grained WQ assignments */ +enum dpaa_fq_type { + FQ_TYPE_RX_DEFAULT = 1, /* Rx Default FQs */ + FQ_TYPE_RX_ERROR, /* Rx Error FQs */ + FQ_TYPE_TX, /* "Real" Tx FQs */ + FQ_TYPE_TX_CONFIRM, /* Tx default Conf FQ (actually an Rx FQ) */ + FQ_TYPE_TX_CONF_MQ, /* Tx conf FQs (one for each Tx FQ) */ + FQ_TYPE_TX_ERROR, /* Tx Error FQs (these are actually Rx FQs) */ +}; + +struct dpaa_fq { + struct qman_fq fq_base; + struct list_head list; + struct net_device *net_dev; + bool init; + u32 fqid; + u32 flags; + u16 channel; + u8 wq; + enum dpaa_fq_type fq_type; +}; + +struct dpaa_fq_cbs { + struct qman_fq rx_defq; + struct qman_fq tx_defq; + struct qman_fq rx_errq; + struct qman_fq tx_errq; + struct qman_fq egress_ern; +}; + +struct dpaa_bp { + /* device used in the DMA mapping operations */ + struct device *dev; + /* current number of buffers in the buffer pool alloted to each CPU */ + int __percpu *percpu_count; + /* all buffers allocated for this pool have this raw size */ + size_t raw_size; + /* all buffers in this pool have this same usable size */ + size_t size; + /* the buffer pools are initialized with config_count buffers for each + * CPU; at runtime the number of buffers per CPU is constantly brought + * back to this level + */ + u16 config_count; + u8 bpid; + struct bman_pool *pool; + /* bpool can be seeded before use by this cb */ + int (*seed_cb)(struct dpaa_bp *); + /* bpool can be emptied before freeing by this cb */ + void (*free_buf_cb)(const struct dpaa_bp *, struct bm_buffer *); + atomic_t refs; +}; + +struct dpaa_napi_portal { + struct napi_struct napi; + struct qman_portal *p; + bool down; +}; + +struct dpaa_percpu_priv { + struct net_device *net_dev; + struct dpaa_napi_portal np; + struct rtnl_link_stats64 stats; +}; + +struct dpaa_buffer_layout { + u16 priv_data_size; +}; + +struct dpaa_priv { + struct dpaa_percpu_priv __percpu *percpu_priv; + struct dpaa_bp *dpaa_bps[DPAA_BPS_NUM]; + /* Store here the needed Tx headroom for convenience and speed + * (even though it can be computed based on the fields of buf_layout) + */ + u16 tx_headroom; + struct net_device *net_dev; + struct mac_device *mac_dev; + struct qman_fq *egress_fqs[DPAA_ETH_TXQ_NUM]; + struct qman_fq *conf_fqs[DPAA_ETH_TXQ_NUM]; + + u16 channel; + struct list_head dpaa_fq_list; + + u32 msg_enable; /* net_device message level */ + + struct { + /* All egress queues to a given net device belong to one + * (and the same) congestion group. + */ + struct qman_cgr cgr; + } cgr_data; + /* Use a per-port CGR for ingress traffic. */ + bool use_ingress_cgr; + struct qman_cgr ingress_cgr; + + struct dpaa_buffer_layout buf_layout[2]; + u16 rx_headroom; +}; +#endif /* __DPAA_H */ -- cgit v1.1 From b0cdb1682b2112d259dcd90b35eeaec40590e62f Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:03 +0200 Subject: dpaa_eth: add ethtool functionality Add support for basic ethtool operations. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/Makefile | 2 +- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 + drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 3 + drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c | 218 +++++++++++++++++++++ 4 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c diff --git a/drivers/net/ethernet/freescale/dpaa/Makefile b/drivers/net/ethernet/freescale/dpaa/Makefile index fc76029..43a4cfd 100644 --- a/drivers/net/ethernet/freescale/dpaa/Makefile +++ b/drivers/net/ethernet/freescale/dpaa/Makefile @@ -8,4 +8,4 @@ ccflags-y += -I$(FMAN) obj-$(CONFIG_FSL_DPAA_ETH) += fsl_dpa.o -fsl_dpa-objs += dpaa_eth.o +fsl_dpa-objs += dpaa_eth.o dpaa_ethtool.o diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 8b9b0720f..0e7f1c7 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -240,6 +240,8 @@ static int dpaa_netdev_init(struct net_device *net_dev, memcpy(net_dev->perm_addr, mac_addr, net_dev->addr_len); memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); + net_dev->ethtool_ops = &dpaa_ethtool_ops; + net_dev->needed_headroom = priv->tx_headroom; net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index fe98e08..d6ab335 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -141,4 +141,7 @@ struct dpaa_priv { struct dpaa_buffer_layout buf_layout[2]; u16 rx_headroom; }; + +/* from dpaa_ethtool.c */ +extern const struct ethtool_ops dpaa_ethtool_ops; #endif /* __DPAA_H */ diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c new file mode 100644 index 0000000..3580a62 --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -0,0 +1,218 @@ +/* Copyright 2008-2016 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include "dpaa_eth.h" +#include "mac.h" + +static int dpaa_get_settings(struct net_device *net_dev, + struct ethtool_cmd *et_cmd) +{ + int err; + + if (!net_dev->phydev) { + netdev_dbg(net_dev, "phy device not initialized\n"); + return 0; + } + + err = phy_ethtool_gset(net_dev->phydev, et_cmd); + + return err; +} + +static int dpaa_set_settings(struct net_device *net_dev, + struct ethtool_cmd *et_cmd) +{ + int err; + + if (!net_dev->phydev) { + netdev_err(net_dev, "phy device not initialized\n"); + return -ENODEV; + } + + err = phy_ethtool_sset(net_dev->phydev, et_cmd); + if (err < 0) + netdev_err(net_dev, "phy_ethtool_sset() = %d\n", err); + + return err; +} + +static void dpaa_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *drvinfo) +{ + int len; + + strlcpy(drvinfo->driver, KBUILD_MODNAME, + sizeof(drvinfo->driver)); + len = snprintf(drvinfo->version, sizeof(drvinfo->version), + "%X", 0); + len = snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%X", 0); + + if (len >= sizeof(drvinfo->fw_version)) { + /* Truncated output */ + netdev_notice(net_dev, "snprintf() = %d\n", len); + } + strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), + sizeof(drvinfo->bus_info)); +} + +static u32 dpaa_get_msglevel(struct net_device *net_dev) +{ + return ((struct dpaa_priv *)netdev_priv(net_dev))->msg_enable; +} + +static void dpaa_set_msglevel(struct net_device *net_dev, + u32 msg_enable) +{ + ((struct dpaa_priv *)netdev_priv(net_dev))->msg_enable = msg_enable; +} + +static int dpaa_nway_reset(struct net_device *net_dev) +{ + int err; + + if (!net_dev->phydev) { + netdev_err(net_dev, "phy device not initialized\n"); + return -ENODEV; + } + + err = 0; + if (net_dev->phydev->autoneg) { + err = phy_start_aneg(net_dev->phydev); + if (err < 0) + netdev_err(net_dev, "phy_start_aneg() = %d\n", + err); + } + + return err; +} + +static void dpaa_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *epause) +{ + struct mac_device *mac_dev; + struct dpaa_priv *priv; + + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + + if (!net_dev->phydev) { + netdev_err(net_dev, "phy device not initialized\n"); + return; + } + + epause->autoneg = mac_dev->autoneg_pause; + epause->rx_pause = mac_dev->rx_pause_active; + epause->tx_pause = mac_dev->tx_pause_active; +} + +static int dpaa_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *epause) +{ + struct mac_device *mac_dev; + struct phy_device *phydev; + bool rx_pause, tx_pause; + struct dpaa_priv *priv; + u32 newadv, oldadv; + int err; + + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + + phydev = net_dev->phydev; + if (!phydev) { + netdev_err(net_dev, "phy device not initialized\n"); + return -ENODEV; + } + + if (!(phydev->supported & SUPPORTED_Pause) || + (!(phydev->supported & SUPPORTED_Asym_Pause) && + (epause->rx_pause != epause->tx_pause))) + return -EINVAL; + + /* The MAC should know how to handle PAUSE frame autonegotiation before + * adjust_link is triggered by a forced renegotiation of sym/asym PAUSE + * settings. + */ + mac_dev->autoneg_pause = !!epause->autoneg; + mac_dev->rx_pause_req = !!epause->rx_pause; + mac_dev->tx_pause_req = !!epause->tx_pause; + + /* Determine the sym/asym advertised PAUSE capabilities from the desired + * rx/tx pause settings. + */ + newadv = 0; + if (epause->rx_pause) + newadv = ADVERTISED_Pause | ADVERTISED_Asym_Pause; + if (epause->tx_pause) + newadv |= ADVERTISED_Asym_Pause; + + oldadv = phydev->advertising & + (ADVERTISED_Pause | ADVERTISED_Asym_Pause); + + /* If there are differences between the old and the new advertised + * values, restart PHY autonegotiation and advertise the new values. + */ + if (oldadv != newadv) { + phydev->advertising &= ~(ADVERTISED_Pause + | ADVERTISED_Asym_Pause); + phydev->advertising |= newadv; + if (phydev->autoneg) { + err = phy_start_aneg(phydev); + if (err < 0) + netdev_err(net_dev, "phy_start_aneg() = %d\n", + err); + } + } + + fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); + err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); + if (err < 0) + netdev_err(net_dev, "set_mac_active_pause() = %d\n", err); + + return err; +} + +const struct ethtool_ops dpaa_ethtool_ops = { + .get_settings = dpaa_get_settings, + .set_settings = dpaa_set_settings, + .get_drvinfo = dpaa_get_drvinfo, + .get_msglevel = dpaa_get_msglevel, + .set_msglevel = dpaa_set_msglevel, + .nway_reset = dpaa_nway_reset, + .get_pauseparam = dpaa_get_pauseparam, + .set_pauseparam = dpaa_set_pauseparam, + .get_link = ethtool_op_get_link, +}; -- cgit v1.1 From b0ce0d02e44d3bd92b4cb6544d8ff9c90d730d07 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:04 +0200 Subject: dpaa_eth: add ethtool statistics Add a series of counters to be exported through ethtool: - add detailed counters for reception errors; - add detailed counters for QMan enqueue reject events; - count the number of fragmented skbs received from the stack; - count all frames received on the Tx confirmation path; - add congestion group statistics; - count the number of interrupts for each CPU. Signed-off-by: Ioana Ciornei Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 54 +++++- drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 33 ++++ drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c | 199 +++++++++++++++++++++ 3 files changed, 284 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 0e7f1c7..fcb9cac 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -700,10 +700,15 @@ static void dpaa_eth_cgscn(struct qman_portal *qm, struct qman_cgr *cgr, struct dpaa_priv *priv = (struct dpaa_priv *)container_of(cgr, struct dpaa_priv, cgr_data.cgr); - if (congested) + if (congested) { + priv->cgr_data.congestion_start_jiffies = jiffies; netif_tx_stop_all_queues(priv->net_dev); - else + priv->cgr_data.cgr_congested_count++; + } else { + priv->cgr_data.congested_jiffies += + (jiffies - priv->cgr_data.congestion_start_jiffies); netif_tx_wake_all_queues(priv->net_dev); + } } static int dpaa_eth_cgr_init(struct dpaa_priv *priv) @@ -1217,6 +1222,37 @@ static void dpaa_fd_release(const struct net_device *net_dev, dpaa_bman_release(dpaa_bp, &bmb, 1); } +static void count_ern(struct dpaa_percpu_priv *percpu_priv, + const union qm_mr_entry *msg) +{ + switch (msg->ern.rc & QM_MR_RC_MASK) { + case QM_MR_RC_CGR_TAILDROP: + percpu_priv->ern_cnt.cg_tdrop++; + break; + case QM_MR_RC_WRED: + percpu_priv->ern_cnt.wred++; + break; + case QM_MR_RC_ERROR: + percpu_priv->ern_cnt.err_cond++; + break; + case QM_MR_RC_ORPWINDOW_EARLY: + percpu_priv->ern_cnt.early_window++; + break; + case QM_MR_RC_ORPWINDOW_LATE: + percpu_priv->ern_cnt.late_window++; + break; + case QM_MR_RC_FQ_TAILDROP: + percpu_priv->ern_cnt.fq_tdrop++; + break; + case QM_MR_RC_ORPWINDOW_RETIRED: + percpu_priv->ern_cnt.fq_retired++; + break; + case QM_MR_RC_ORP_ZERO: + percpu_priv->ern_cnt.orp_zero++; + break; + } +} + /* Turn on HW checksum computation for this outgoing frame. * If the current protocol is not something we support in this regard * (or if the stack has already computed the SW checksum), we do nothing. @@ -1882,6 +1918,7 @@ static int dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev) likely(skb_shinfo(skb)->nr_frags < DPAA_SGT_MAX_ENTRIES)) { /* Just create a S/G fd based on the skb */ err = skb_to_sg_fd(priv, skb, &fd); + percpu_priv->tx_frag_skbuffs++; } else { /* If the egress skb contains more fragments than we support * we have no choice but to linearize it ourselves. @@ -1918,6 +1955,15 @@ static void dpaa_rx_error(struct net_device *net_dev, percpu_priv->stats.rx_errors++; + if (fd->status & FM_FD_ERR_DMA) + percpu_priv->rx_errors.dme++; + if (fd->status & FM_FD_ERR_PHYSICAL) + percpu_priv->rx_errors.fpe++; + if (fd->status & FM_FD_ERR_SIZE) + percpu_priv->rx_errors.fse++; + if (fd->status & FM_FD_ERR_PRS_HDR_ERR) + percpu_priv->rx_errors.phe++; + dpaa_fd_release(net_dev, fd); } @@ -1973,6 +2019,8 @@ static void dpaa_tx_conf(struct net_device *net_dev, percpu_priv->stats.tx_errors++; } + percpu_priv->tx_confirm++; + skb = dpaa_cleanup_tx_fd(priv, fd); consume_skb(skb); @@ -1987,6 +2035,7 @@ static inline int dpaa_eth_napi_schedule(struct dpaa_percpu_priv *percpu_priv, percpu_priv->np.p = portal; napi_schedule(&percpu_priv->np.napi); + percpu_priv->in_interrupt++; return 1; } return 0; @@ -2171,6 +2220,7 @@ static void egress_ern(struct qman_portal *portal, percpu_priv->stats.tx_dropped++; percpu_priv->stats.tx_fifo_errors++; + count_ern(percpu_priv, msg); skb = dpaa_cleanup_tx_fd(priv, fd); dev_kfree_skb_any(skb); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index d6ab335..711fb06 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -95,6 +95,25 @@ struct dpaa_bp { atomic_t refs; }; +struct dpaa_rx_errors { + u64 dme; /* DMA Error */ + u64 fpe; /* Frame Physical Error */ + u64 fse; /* Frame Size Error */ + u64 phe; /* Header Error */ +}; + +/* Counters for QMan ERN frames - one counter per rejection code */ +struct dpaa_ern_cnt { + u64 cg_tdrop; /* Congestion group taildrop */ + u64 wred; /* WRED congestion */ + u64 err_cond; /* Error condition */ + u64 early_window; /* Order restoration, frame too early */ + u64 late_window; /* Order restoration, frame too late */ + u64 fq_tdrop; /* FQ taildrop */ + u64 fq_retired; /* FQ is retired */ + u64 orp_zero; /* ORP disabled */ +}; + struct dpaa_napi_portal { struct napi_struct napi; struct qman_portal *p; @@ -104,7 +123,13 @@ struct dpaa_napi_portal { struct dpaa_percpu_priv { struct net_device *net_dev; struct dpaa_napi_portal np; + u64 in_interrupt; + u64 tx_confirm; + /* fragmented (non-linear) skbuffs received from the stack */ + u64 tx_frag_skbuffs; struct rtnl_link_stats64 stats; + struct dpaa_rx_errors rx_errors; + struct dpaa_ern_cnt ern_cnt; }; struct dpaa_buffer_layout { @@ -133,6 +158,14 @@ struct dpaa_priv { * (and the same) congestion group. */ struct qman_cgr cgr; + /* If congested, when it began. Used for performance stats. */ + u32 congestion_start_jiffies; + /* Number of jiffies the Tx port was congested. */ + u32 congested_jiffies; + /* Counter for the number of times the CGR + * entered congestion state + */ + u32 cgr_congested_count; } cgr_data; /* Use a per-port CGR for ingress traffic. */ bool use_ingress_cgr; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c index 3580a62..27e7044 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -36,6 +36,42 @@ #include "dpaa_eth.h" #include "mac.h" +static const char dpaa_stats_percpu[][ETH_GSTRING_LEN] = { + "interrupts", + "rx packets", + "tx packets", + "tx confirm", + "tx S/G", + "tx error", + "rx error", +}; + +static char dpaa_stats_global[][ETH_GSTRING_LEN] = { + /* dpa rx errors */ + "rx dma error", + "rx frame physical error", + "rx frame size error", + "rx header error", + + /* demultiplexing errors */ + "qman cg_tdrop", + "qman wred", + "qman error cond", + "qman early window", + "qman late window", + "qman fq tdrop", + "qman fq retired", + "qman orp disabled", + + /* congestion related stats */ + "congestion time (ms)", + "entered congestion", + "congested (0/1)" +}; + +#define DPAA_STATS_PERCPU_LEN ARRAY_SIZE(dpaa_stats_percpu) +#define DPAA_STATS_GLOBAL_LEN ARRAY_SIZE(dpaa_stats_global) + static int dpaa_get_settings(struct net_device *net_dev, struct ethtool_cmd *et_cmd) { @@ -205,6 +241,166 @@ static int dpaa_set_pauseparam(struct net_device *net_dev, return err; } +static int dpaa_get_sset_count(struct net_device *net_dev, int type) +{ + unsigned int total_stats, num_stats; + + num_stats = num_online_cpus() + 1; + total_stats = num_stats * (DPAA_STATS_PERCPU_LEN + DPAA_BPS_NUM) + + DPAA_STATS_GLOBAL_LEN; + + switch (type) { + case ETH_SS_STATS: + return total_stats; + default: + return -EOPNOTSUPP; + } +} + +static void copy_stats(struct dpaa_percpu_priv *percpu_priv, int num_cpus, + int crr_cpu, u64 *bp_count, u64 *data) +{ + int num_values = num_cpus + 1; + int crr = 0, j; + + /* update current CPU's stats and also add them to the total values */ + data[crr * num_values + crr_cpu] = percpu_priv->in_interrupt; + data[crr++ * num_values + num_cpus] += percpu_priv->in_interrupt; + + data[crr * num_values + crr_cpu] = percpu_priv->stats.rx_packets; + data[crr++ * num_values + num_cpus] += percpu_priv->stats.rx_packets; + + data[crr * num_values + crr_cpu] = percpu_priv->stats.tx_packets; + data[crr++ * num_values + num_cpus] += percpu_priv->stats.tx_packets; + + data[crr * num_values + crr_cpu] = percpu_priv->tx_confirm; + data[crr++ * num_values + num_cpus] += percpu_priv->tx_confirm; + + data[crr * num_values + crr_cpu] = percpu_priv->tx_frag_skbuffs; + data[crr++ * num_values + num_cpus] += percpu_priv->tx_frag_skbuffs; + + data[crr * num_values + crr_cpu] = percpu_priv->stats.tx_errors; + data[crr++ * num_values + num_cpus] += percpu_priv->stats.tx_errors; + + data[crr * num_values + crr_cpu] = percpu_priv->stats.rx_errors; + data[crr++ * num_values + num_cpus] += percpu_priv->stats.rx_errors; + + for (j = 0; j < DPAA_BPS_NUM; j++) { + data[crr * num_values + crr_cpu] = bp_count[j]; + data[crr++ * num_values + num_cpus] += bp_count[j]; + } +} + +static void dpaa_get_ethtool_stats(struct net_device *net_dev, + struct ethtool_stats *stats, u64 *data) +{ + u64 bp_count[DPAA_BPS_NUM], cg_time, cg_num; + struct dpaa_percpu_priv *percpu_priv; + struct dpaa_rx_errors rx_errors; + unsigned int num_cpus, offset; + struct dpaa_ern_cnt ern_cnt; + struct dpaa_bp *dpaa_bp; + struct dpaa_priv *priv; + int total_stats, i, j; + bool cg_status; + + total_stats = dpaa_get_sset_count(net_dev, ETH_SS_STATS); + priv = netdev_priv(net_dev); + num_cpus = num_online_cpus(); + + memset(&bp_count, 0, sizeof(bp_count)); + memset(&rx_errors, 0, sizeof(struct dpaa_rx_errors)); + memset(&ern_cnt, 0, sizeof(struct dpaa_ern_cnt)); + memset(data, 0, total_stats * sizeof(u64)); + + for_each_online_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + for (j = 0; j < DPAA_BPS_NUM; j++) { + dpaa_bp = priv->dpaa_bps[j]; + if (!dpaa_bp->percpu_count) + continue; + bp_count[j] = *(per_cpu_ptr(dpaa_bp->percpu_count, i)); + } + rx_errors.dme += percpu_priv->rx_errors.dme; + rx_errors.fpe += percpu_priv->rx_errors.fpe; + rx_errors.fse += percpu_priv->rx_errors.fse; + rx_errors.phe += percpu_priv->rx_errors.phe; + + ern_cnt.cg_tdrop += percpu_priv->ern_cnt.cg_tdrop; + ern_cnt.wred += percpu_priv->ern_cnt.wred; + ern_cnt.err_cond += percpu_priv->ern_cnt.err_cond; + ern_cnt.early_window += percpu_priv->ern_cnt.early_window; + ern_cnt.late_window += percpu_priv->ern_cnt.late_window; + ern_cnt.fq_tdrop += percpu_priv->ern_cnt.fq_tdrop; + ern_cnt.fq_retired += percpu_priv->ern_cnt.fq_retired; + ern_cnt.orp_zero += percpu_priv->ern_cnt.orp_zero; + + copy_stats(percpu_priv, num_cpus, i, bp_count, data); + } + + offset = (num_cpus + 1) * (DPAA_STATS_PERCPU_LEN + DPAA_BPS_NUM); + memcpy(data + offset, &rx_errors, sizeof(struct dpaa_rx_errors)); + + offset += sizeof(struct dpaa_rx_errors) / sizeof(u64); + memcpy(data + offset, &ern_cnt, sizeof(struct dpaa_ern_cnt)); + + /* gather congestion related counters */ + cg_num = 0; + cg_status = 0; + cg_time = jiffies_to_msecs(priv->cgr_data.congested_jiffies); + if (qman_query_cgr_congested(&priv->cgr_data.cgr, &cg_status) == 0) { + cg_num = priv->cgr_data.cgr_congested_count; + + /* reset congestion stats (like QMan API does */ + priv->cgr_data.congested_jiffies = 0; + priv->cgr_data.cgr_congested_count = 0; + } + + offset += sizeof(struct dpaa_ern_cnt) / sizeof(u64); + data[offset++] = cg_time; + data[offset++] = cg_num; + data[offset++] = cg_status; +} + +static void dpaa_get_strings(struct net_device *net_dev, u32 stringset, + u8 *data) +{ + unsigned int i, j, num_cpus, size; + char string_cpu[ETH_GSTRING_LEN]; + u8 *strings; + + memset(string_cpu, 0, sizeof(string_cpu)); + strings = data; + num_cpus = num_online_cpus(); + size = DPAA_STATS_GLOBAL_LEN * ETH_GSTRING_LEN; + + for (i = 0; i < DPAA_STATS_PERCPU_LEN; i++) { + for (j = 0; j < num_cpus; j++) { + snprintf(string_cpu, ETH_GSTRING_LEN, "%s [CPU %d]", + dpaa_stats_percpu[i], j); + memcpy(strings, string_cpu, ETH_GSTRING_LEN); + strings += ETH_GSTRING_LEN; + } + snprintf(string_cpu, ETH_GSTRING_LEN, "%s [TOTAL]", + dpaa_stats_percpu[i]); + memcpy(strings, string_cpu, ETH_GSTRING_LEN); + strings += ETH_GSTRING_LEN; + } + for (i = 0; i < DPAA_BPS_NUM; i++) { + for (j = 0; j < num_cpus; j++) { + snprintf(string_cpu, ETH_GSTRING_LEN, + "bpool %c [CPU %d]", 'a' + i, j); + memcpy(strings, string_cpu, ETH_GSTRING_LEN); + strings += ETH_GSTRING_LEN; + } + snprintf(string_cpu, ETH_GSTRING_LEN, "bpool %c [TOTAL]", + 'a' + i); + memcpy(strings, string_cpu, ETH_GSTRING_LEN); + strings += ETH_GSTRING_LEN; + } + memcpy(strings, dpaa_stats_global, size); +} + const struct ethtool_ops dpaa_ethtool_ops = { .get_settings = dpaa_get_settings, .set_settings = dpaa_set_settings, @@ -215,4 +411,7 @@ const struct ethtool_ops dpaa_ethtool_ops = { .get_pauseparam = dpaa_get_pauseparam, .set_pauseparam = dpaa_set_pauseparam, .get_link = ethtool_op_get_link, + .get_sset_count = dpaa_get_sset_count, + .get_ethtool_stats = dpaa_get_ethtool_stats, + .get_strings = dpaa_get_strings, }; -- cgit v1.1 From 846a86e20123b0f95df40b58f43a729e017e5f7b Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:05 +0200 Subject: dpaa_eth: add sysfs exports Export Frame Queue and Buffer Pool IDs through sysfs. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/Makefile | 2 +- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 4 + drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 4 + .../net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c | 165 +++++++++++++++++++++ 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c diff --git a/drivers/net/ethernet/freescale/dpaa/Makefile b/drivers/net/ethernet/freescale/dpaa/Makefile index 43a4cfd..bfb03d4 100644 --- a/drivers/net/ethernet/freescale/dpaa/Makefile +++ b/drivers/net/ethernet/freescale/dpaa/Makefile @@ -8,4 +8,4 @@ ccflags-y += -I$(FMAN) obj-$(CONFIG_FSL_DPAA_ETH) += fsl_dpa.o -fsl_dpa-objs += dpaa_eth.o dpaa_ethtool.o +fsl_dpa-objs += dpaa_eth.o dpaa_ethtool.o dpaa_eth_sysfs.o diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index fcb9cac..07ec2fe 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -2619,6 +2619,8 @@ static int dpaa_eth_probe(struct platform_device *pdev) if (err < 0) goto netdev_init_failed; + dpaa_eth_sysfs_init(&net_dev->dev); + netif_info(priv, probe, net_dev, "Probed interface %s\n", net_dev->name); @@ -2664,6 +2666,8 @@ static int dpaa_remove(struct platform_device *pdev) priv = netdev_priv(net_dev); + dpaa_eth_sysfs_remove(dev); + dev_set_drvdata(dev, NULL); unregister_netdev(net_dev); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index 711fb06..44323e2 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -177,4 +177,8 @@ struct dpaa_priv { /* from dpaa_ethtool.c */ extern const struct ethtool_ops dpaa_ethtool_ops; + +/* from dpaa_eth_sysfs.c */ +void dpaa_eth_sysfs_remove(struct device *dev); +void dpaa_eth_sysfs_init(struct device *dev); #endif /* __DPAA_H */ diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c new file mode 100644 index 0000000..ec75d1c --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c @@ -0,0 +1,165 @@ +/* Copyright 2008-2016 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include "dpaa_eth.h" +#include "mac.h" + +static ssize_t dpaa_eth_show_addr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dpaa_priv *priv = netdev_priv(to_net_dev(dev)); + struct mac_device *mac_dev = priv->mac_dev; + + if (mac_dev) + return sprintf(buf, "%llx", + (unsigned long long)mac_dev->res->start); + else + return sprintf(buf, "none"); +} + +static ssize_t dpaa_eth_show_fqids(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dpaa_priv *priv = netdev_priv(to_net_dev(dev)); + struct dpaa_fq *prev = NULL; + char *prevstr = NULL; + struct dpaa_fq *tmp; + struct dpaa_fq *fq; + u32 first_fqid = 0; + u32 last_fqid = 0; + ssize_t bytes = 0; + char *str; + int i = 0; + + list_for_each_entry_safe(fq, tmp, &priv->dpaa_fq_list, list) { + switch (fq->fq_type) { + case FQ_TYPE_RX_DEFAULT: + str = "Rx default"; + break; + case FQ_TYPE_RX_ERROR: + str = "Rx error"; + break; + case FQ_TYPE_TX_CONFIRM: + str = "Tx default confirmation"; + break; + case FQ_TYPE_TX_CONF_MQ: + str = "Tx confirmation (mq)"; + break; + case FQ_TYPE_TX_ERROR: + str = "Tx error"; + break; + case FQ_TYPE_TX: + str = "Tx"; + break; + default: + str = "Unknown"; + } + + if (prev && (abs(fq->fqid - prev->fqid) != 1 || + str != prevstr)) { + if (last_fqid == first_fqid) + bytes += sprintf(buf + bytes, + "%s: %d\n", prevstr, prev->fqid); + else + bytes += sprintf(buf + bytes, + "%s: %d - %d\n", prevstr, + first_fqid, last_fqid); + } + + if (prev && abs(fq->fqid - prev->fqid) == 1 && + str == prevstr) { + last_fqid = fq->fqid; + } else { + first_fqid = fq->fqid; + last_fqid = fq->fqid; + } + + prev = fq; + prevstr = str; + i++; + } + + if (prev) { + if (last_fqid == first_fqid) + bytes += sprintf(buf + bytes, "%s: %d\n", prevstr, + prev->fqid); + else + bytes += sprintf(buf + bytes, "%s: %d - %d\n", prevstr, + first_fqid, last_fqid); + } + + return bytes; +} + +static ssize_t dpaa_eth_show_bpids(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dpaa_priv *priv = netdev_priv(to_net_dev(dev)); + ssize_t bytes = 0; + int i = 0; + + for (i = 0; i < DPAA_BPS_NUM; i++) + bytes += snprintf(buf + bytes, PAGE_SIZE - bytes, "%u\n", + priv->dpaa_bps[i]->bpid); + + return bytes; +} + +static struct device_attribute dpaa_eth_attrs[] = { + __ATTR(device_addr, 0444, dpaa_eth_show_addr, NULL), + __ATTR(fqids, 0444, dpaa_eth_show_fqids, NULL), + __ATTR(bpids, 0444, dpaa_eth_show_bpids, NULL), +}; + +void dpaa_eth_sysfs_init(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dpaa_eth_attrs); i++) + if (device_create_file(dev, &dpaa_eth_attrs[i])) { + dev_err(dev, "Error creating sysfs file\n"); + while (i > 0) + device_remove_file(dev, &dpaa_eth_attrs[--i]); + return; + } +} + +void dpaa_eth_sysfs_remove(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dpaa_eth_attrs); i++) + device_remove_file(dev, &dpaa_eth_attrs[i]); +} -- cgit v1.1 From eb11ddf36eb87f3d568248370be4fdaaefb5043c Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:06 +0200 Subject: dpaa_eth: add trace points Add trace points on the hot processing path. Signed-off-by: Ruxandra Ioana Radulescu Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/Makefile | 1 + drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 15 +++ drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 1 + .../net/ethernet/freescale/dpaa/dpaa_eth_trace.h | 141 +++++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth_trace.h diff --git a/drivers/net/ethernet/freescale/dpaa/Makefile b/drivers/net/ethernet/freescale/dpaa/Makefile index bfb03d4..7db50bc 100644 --- a/drivers/net/ethernet/freescale/dpaa/Makefile +++ b/drivers/net/ethernet/freescale/dpaa/Makefile @@ -9,3 +9,4 @@ ccflags-y += -I$(FMAN) obj-$(CONFIG_FSL_DPAA_ETH) += fsl_dpa.o fsl_dpa-objs += dpaa_eth.o dpaa_ethtool.o dpaa_eth_sysfs.o +CFLAGS_dpaa_eth.o := -I$(src) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 07ec2fe..3c48a84 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -59,6 +59,12 @@ #include "mac.h" #include "dpaa_eth.h" +/* CREATE_TRACE_POINTS only needs to be defined once. Other dpaa files + * using trace events only need to #include + */ +#define CREATE_TRACE_POINTS +#include "dpaa_eth_trace.h" + static int debug = -1; module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Module/Driver verbosity level (0=none,...,16=all)"); @@ -1863,6 +1869,9 @@ static inline int dpaa_xmit(struct dpaa_priv *priv, if (fd->bpid == FSL_DPAA_BPID_INV) fd->cmd |= qman_fq_fqid(priv->conf_fqs[queue]); + /* Trace this Tx fd */ + trace_dpaa_tx_fd(priv->net_dev, egress_fq, fd); + for (i = 0; i < DPAA_ENQUEUE_RETRIES; i++) { err = qman_enqueue(egress_fq, fd); if (err != -EBUSY) @@ -2097,6 +2106,9 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, if (!dpaa_bp) return qman_cb_dqrr_consume; + /* Trace the Rx fd */ + trace_dpaa_rx_fd(net_dev, fq, &dq->fd); + percpu_priv = this_cpu_ptr(priv->percpu_priv); percpu_stats = &percpu_priv->stats; @@ -2194,6 +2206,9 @@ static enum qman_cb_dqrr_result conf_dflt_dqrr(struct qman_portal *portal, net_dev = ((struct dpaa_fq *)fq)->net_dev; priv = netdev_priv(net_dev); + /* Trace the fd */ + trace_dpaa_tx_conf_fd(net_dev, fq, &dq->fd); + percpu_priv = this_cpu_ptr(priv->percpu_priv); if (dpaa_eth_napi_schedule(percpu_priv, portal)) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index 44323e2..1f9aebf 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -37,6 +37,7 @@ #include "fman.h" #include "mac.h" +#include "dpaa_eth_trace.h" #define DPAA_ETH_TXQ_NUM NR_CPUS diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_trace.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_trace.h new file mode 100644 index 0000000..409c1dc --- /dev/null +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_trace.h @@ -0,0 +1,141 @@ +/* Copyright 2013-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM dpaa_eth + +#if !defined(_DPAA_ETH_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _DPAA_ETH_TRACE_H + +#include +#include +#include "dpaa_eth.h" +#include + +#define fd_format_name(format) { qm_fd_##format, #format } +#define fd_format_list \ + fd_format_name(contig), \ + fd_format_name(sg) + +/* This is used to declare a class of events. + * individual events of this type will be defined below. + */ + +/* Store details about a frame descriptor and the FQ on which it was + * transmitted/received. + */ +DECLARE_EVENT_CLASS(dpaa_eth_fd, + /* Trace function prototype */ + TP_PROTO(struct net_device *netdev, + struct qman_fq *fq, + const struct qm_fd *fd), + + /* Repeat argument list here */ + TP_ARGS(netdev, fq, fd), + + /* A structure containing the relevant information we want to record. + * Declare name and type for each normal element, name, type and size + * for arrays. Use __string for variable length strings. + */ + TP_STRUCT__entry( + __field(u32, fqid) + __field(u64, fd_addr) + __field(u8, fd_format) + __field(u16, fd_offset) + __field(u32, fd_length) + __field(u32, fd_status) + __string(name, netdev->name) + ), + + /* The function that assigns values to the above declared fields */ + TP_fast_assign( + __entry->fqid = fq->fqid; + __entry->fd_addr = qm_fd_addr_get64(fd); + __entry->fd_format = qm_fd_get_format(fd); + __entry->fd_offset = qm_fd_get_offset(fd); + __entry->fd_length = qm_fd_get_length(fd); + __entry->fd_status = fd->status; + __assign_str(name, netdev->name); + ), + + /* This is what gets printed when the trace event is triggered */ + TP_printk("[%s] fqid=%d, fd: addr=0x%llx, format=%s, off=%u, len=%u, status=0x%08x", + __get_str(name), __entry->fqid, __entry->fd_addr, + __print_symbolic(__entry->fd_format, fd_format_list), + __entry->fd_offset, __entry->fd_length, __entry->fd_status) +); + +/* Now declare events of the above type. Format is: + * DEFINE_EVENT(class, name, proto, args), with proto and args same as for class + */ + +/* Tx (egress) fd */ +DEFINE_EVENT(dpaa_eth_fd, dpaa_tx_fd, + + TP_PROTO(struct net_device *netdev, + struct qman_fq *fq, + const struct qm_fd *fd), + + TP_ARGS(netdev, fq, fd) +); + +/* Rx fd */ +DEFINE_EVENT(dpaa_eth_fd, dpaa_rx_fd, + + TP_PROTO(struct net_device *netdev, + struct qman_fq *fq, + const struct qm_fd *fd), + + TP_ARGS(netdev, fq, fd) +); + +/* Tx confirmation fd */ +DEFINE_EVENT(dpaa_eth_fd, dpaa_tx_conf_fd, + + TP_PROTO(struct net_device *netdev, + struct qman_fq *fq, + const struct qm_fd *fd), + + TP_ARGS(netdev, fq, fd) +); + +/* If only one event of a certain type needs to be declared, use TRACE_EVENT(). + * The syntax is the same as for DECLARE_EVENT_CLASS(). + */ + +#endif /* _DPAA_ETH_TRACE_H */ + +/* This must be outside ifdef _DPAA_ETH_TRACE_H */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE dpaa_eth_trace +#include -- cgit v1.1 From 8f2840197a9df9ce40db9dc4ef45ce7758337a35 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:07 +0200 Subject: arch/powerpc: Enable FSL_PAMU Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- arch/powerpc/configs/dpaa.config | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/configs/dpaa.config b/arch/powerpc/configs/dpaa.config index efa99c0..f124ee1 100644 --- a/arch/powerpc/configs/dpaa.config +++ b/arch/powerpc/configs/dpaa.config @@ -1 +1,2 @@ CONFIG_FSL_DPAA=y +CONFIG_FSL_PAMU=y -- cgit v1.1 From 9256f21e503f776f6c04c239e5b22d0d3635d533 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:08 +0200 Subject: arch/powerpc: Enable FSL_FMAN Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- arch/powerpc/configs/dpaa.config | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/configs/dpaa.config b/arch/powerpc/configs/dpaa.config index f124ee1..9ad9bc0 100644 --- a/arch/powerpc/configs/dpaa.config +++ b/arch/powerpc/configs/dpaa.config @@ -1,2 +1,3 @@ CONFIG_FSL_DPAA=y CONFIG_FSL_PAMU=y +CONFIG_FSL_FMAN=y -- cgit v1.1 From e0bd03d0eaa472e2b253c7c112740a8682f5f584 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 15 Nov 2016 10:41:09 +0200 Subject: arch/powerpc: Enable dpaa_eth Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- arch/powerpc/configs/dpaa.config | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/configs/dpaa.config b/arch/powerpc/configs/dpaa.config index 9ad9bc0..2fe76f5 100644 --- a/arch/powerpc/configs/dpaa.config +++ b/arch/powerpc/configs/dpaa.config @@ -1,3 +1,4 @@ CONFIG_FSL_DPAA=y CONFIG_FSL_PAMU=y CONFIG_FSL_FMAN=y +CONFIG_FSL_DPAA_ETH=y -- cgit v1.1 From 8c2a4c8efb0c22cb68fcecf84f3ac7af25ebf542 Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:08 +0100 Subject: alx: refactor descriptor allocation Split the allocation of descriptor memory and the buffer allocation into a tx and rx function. This is in preparation for multiple queues where we need to iterate over the new functions. While at it drop the unneeded casting on the rx side. Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 97 ++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index eccbacd..8935766 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -573,19 +573,41 @@ static int alx_set_mac_address(struct net_device *netdev, void *data) return 0; } -static int alx_alloc_descriptors(struct alx_priv *alx) +static int alx_alloc_tx_ring(struct alx_priv *alx, struct alx_tx_queue *txq, + int offset) { - alx->txq.bufs = kcalloc(alx->tx_ringsz, - sizeof(struct alx_buffer), - GFP_KERNEL); - if (!alx->txq.bufs) + txq->bufs = kcalloc(alx->tx_ringsz, sizeof(struct alx_buffer), GFP_KERNEL); + if (!txq->bufs) return -ENOMEM; - alx->rxq.bufs = kcalloc(alx->rx_ringsz, - sizeof(struct alx_buffer), - GFP_KERNEL); - if (!alx->rxq.bufs) - goto out_free; + txq->tpd = alx->descmem.virt + offset; + txq->tpd_dma = alx->descmem.dma + offset; + offset += sizeof(struct alx_txd) * alx->tx_ringsz; + + return offset; +} + +static int alx_alloc_rx_ring(struct alx_priv *alx, struct alx_rx_queue *rxq, + int offset) +{ + rxq->bufs = kcalloc(alx->rx_ringsz, sizeof(struct alx_buffer), GFP_KERNEL); + if (!rxq->bufs) + return -ENOMEM; + + rxq->rrd = alx->descmem.virt + offset; + rxq->rrd_dma = alx->descmem.dma + offset; + offset += sizeof(struct alx_rrd) * alx->rx_ringsz; + + rxq->rfd = alx->descmem.virt + offset; + rxq->rfd_dma = alx->descmem.dma + offset; + offset += sizeof(struct alx_rfd) * alx->rx_ringsz; + + return offset; +} + +static int alx_alloc_rings(struct alx_priv *alx) +{ + int offset = 0; /* physical tx/rx ring descriptors * @@ -601,45 +623,23 @@ static int alx_alloc_descriptors(struct alx_priv *alx) &alx->descmem.dma, GFP_KERNEL); if (!alx->descmem.virt) - goto out_free; - - alx->txq.tpd = alx->descmem.virt; - alx->txq.tpd_dma = alx->descmem.dma; + return -ENOMEM; - /* alignment requirement for next block */ + /* alignment requirements */ BUILD_BUG_ON(sizeof(struct alx_txd) % 8); - - alx->rxq.rrd = - (void *)((u8 *)alx->descmem.virt + - sizeof(struct alx_txd) * alx->tx_ringsz); - alx->rxq.rrd_dma = alx->descmem.dma + - sizeof(struct alx_txd) * alx->tx_ringsz; - - /* alignment requirement for next block */ BUILD_BUG_ON(sizeof(struct alx_rrd) % 8); - alx->rxq.rfd = - (void *)((u8 *)alx->descmem.virt + - sizeof(struct alx_txd) * alx->tx_ringsz + - sizeof(struct alx_rrd) * alx->rx_ringsz); - alx->rxq.rfd_dma = alx->descmem.dma + - sizeof(struct alx_txd) * alx->tx_ringsz + - sizeof(struct alx_rrd) * alx->rx_ringsz; - - return 0; -out_free: - kfree(alx->txq.bufs); - kfree(alx->rxq.bufs); - return -ENOMEM; -} - -static int alx_alloc_rings(struct alx_priv *alx) -{ - int err; + offset = alx_alloc_tx_ring(alx, &alx->txq, offset); + if (offset < 0) { + netdev_err(alx->dev, "Allocation of tx buffer failed!\n"); + goto out_free; + } - err = alx_alloc_descriptors(alx); - if (err) - return err; + offset = alx_alloc_rx_ring(alx, &alx->rxq, offset); + if (offset < 0) { + netdev_err(alx->dev, "Allocation of rx buffer failed!\n"); + goto out_free; + } alx->int_mask &= ~ALX_ISR_ALL_QUEUES; alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; @@ -647,7 +647,16 @@ static int alx_alloc_rings(struct alx_priv *alx) netif_napi_add(alx->dev, &alx->napi, alx_poll, 64); alx_reinit_rings(alx); + return 0; +out_free: + kfree(alx->txq.bufs); + kfree(alx->rxq.bufs); + dma_free_coherent(&alx->hw.pdev->dev, + alx->descmem.size, + alx->descmem.virt, + alx->descmem.dma); + return -ENOMEM; } static void alx_free_rings(struct alx_priv *alx) -- cgit v1.1 From bccffcf7154420c1cbd9e3c2702e2ec4ff0bc319 Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:09 +0100 Subject: alx: extend data structures for multi queue support Extend the driver data structures to be able to handle multiple queues. Based on the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/alx.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/net/ethernet/atheros/alx/alx.h b/drivers/net/ethernet/atheros/alx/alx.h index 6cac919..0859053 100644 --- a/drivers/net/ethernet/atheros/alx/alx.h +++ b/drivers/net/ethernet/atheros/alx/alx.h @@ -50,6 +50,10 @@ struct alx_buffer { }; struct alx_rx_queue { + struct net_device *netdev; + struct device *dev; + struct alx_napi *np; + struct alx_rrd *rrd; dma_addr_t rrd_dma; @@ -58,16 +62,26 @@ struct alx_rx_queue { struct alx_buffer *bufs; + u16 count; u16 write_idx, read_idx; u16 rrd_read_idx; + u16 queue_idx; }; #define ALX_RX_ALLOC_THRESH 32 struct alx_tx_queue { + struct net_device *netdev; + struct device *dev; + struct alx_txd *tpd; dma_addr_t tpd_dma; + struct alx_buffer *bufs; + + u16 count; u16 write_idx, read_idx; + u16 queue_idx; + u16 p_reg, c_reg; }; #define ALX_DEFAULT_TX_WORK 128 @@ -76,6 +90,18 @@ enum alx_device_quirks { ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG = BIT(0), }; +struct alx_napi { + struct napi_struct napi; + struct alx_priv *alx; + struct alx_rx_queue *rxq; + struct alx_tx_queue *txq; + int vec_idx; + u32 vec_mask; + char irq_lbl[IFNAMSIZ + 8]; +}; + +#define ALX_MAX_NAPIS 8 + #define ALX_FLAG_USING_MSIX BIT(0) #define ALX_FLAG_USING_MSI BIT(1) @@ -96,6 +122,11 @@ struct alx_priv { unsigned int size; } descmem; + struct alx_napi *qnapi[ALX_MAX_NAPIS]; + int num_txq; + int num_rxq; + int num_napi; + /* protect int_mask updates */ spinlock_t irq_lock; u32 int_mask; -- cgit v1.1 From b0999223f224187318c67f1de653e34fb1f04f6b Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:10 +0100 Subject: alx: add ability to allocate and free alx_napi structures Add new functions to allocate and free the alx_napi structures and use them in __alx_open and __alx_stop. We only allocate one of these structures for now, as the rest of the driver is not yet ready for multiple queues. We switch over the setup of the interrupt mask and the call to netif_napi_add to the new function because we must adjust these later on a per queue basis. Based on the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 99 ++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 8935766..193da67 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -632,45 +632,96 @@ static int alx_alloc_rings(struct alx_priv *alx) offset = alx_alloc_tx_ring(alx, &alx->txq, offset); if (offset < 0) { netdev_err(alx->dev, "Allocation of tx buffer failed!\n"); - goto out_free; + return -ENOMEM; } offset = alx_alloc_rx_ring(alx, &alx->rxq, offset); if (offset < 0) { netdev_err(alx->dev, "Allocation of rx buffer failed!\n"); - goto out_free; + return -ENOMEM; } - alx->int_mask &= ~ALX_ISR_ALL_QUEUES; - alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; - - netif_napi_add(alx->dev, &alx->napi, alx_poll, 64); - alx_reinit_rings(alx); return 0; -out_free: - kfree(alx->txq.bufs); - kfree(alx->rxq.bufs); - dma_free_coherent(&alx->hw.pdev->dev, - alx->descmem.size, - alx->descmem.virt, - alx->descmem.dma); - return -ENOMEM; } static void alx_free_rings(struct alx_priv *alx) { - netif_napi_del(&alx->napi); alx_free_buffers(alx); kfree(alx->txq.bufs); kfree(alx->rxq.bufs); - dma_free_coherent(&alx->hw.pdev->dev, - alx->descmem.size, - alx->descmem.virt, - alx->descmem.dma); + if (!alx->descmem.virt) + dma_free_coherent(&alx->hw.pdev->dev, + alx->descmem.size, + alx->descmem.virt, + alx->descmem.dma); +} + +static void alx_free_napis(struct alx_priv *alx) +{ + struct alx_napi *np; + + np = alx->qnapi[0]; + if (!np) + return; + + netif_napi_del(&alx->napi); + kfree(np->txq); + kfree(np->rxq); + kfree(np); + alx->qnapi[0] = NULL; +} + +static int alx_alloc_napis(struct alx_priv *alx) +{ + struct alx_napi *np; + struct alx_rx_queue *rxq; + struct alx_tx_queue *txq; + + alx->int_mask &= ~ALX_ISR_ALL_QUEUES; + alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; + + /* allocate alx_napi structures */ + np = kzalloc(sizeof(struct alx_napi), GFP_KERNEL); + if (!np) + goto err_out; + + np->alx = alx; + netif_napi_add(alx->dev, &alx->napi, alx_poll, 64); + alx->qnapi[0] = np; + + /* allocate tx queues */ + np = alx->qnapi[0]; + txq = kzalloc(sizeof(*txq), GFP_KERNEL); + if (!txq) + goto err_out; + + np->txq = txq; + txq->count = alx->tx_ringsz; + txq->netdev = alx->dev; + txq->dev = &alx->hw.pdev->dev; + + /* allocate rx queues */ + np = alx->qnapi[0]; + rxq = kzalloc(sizeof(*rxq), GFP_KERNEL); + if (!rxq) + goto err_out; + + np->rxq = rxq; + rxq->np = alx->qnapi[0]; + rxq->count = alx->rx_ringsz; + rxq->netdev = alx->dev; + rxq->dev = &alx->hw.pdev->dev; + + return 0; + +err_out: + netdev_err(alx->dev, "error allocating internal structures\n"); + alx_free_napis(alx); + return -ENOMEM; } static void alx_config_vector_mapping(struct alx_priv *alx) @@ -1031,10 +1082,14 @@ static int __alx_open(struct alx_priv *alx, bool resume) if (!resume) netif_carrier_off(alx->dev); - err = alx_alloc_rings(alx); + err = alx_alloc_napis(alx); if (err) goto out_disable_adv_intr; + err = alx_alloc_rings(alx); + if (err) + goto out_free_rings; + alx_configure(alx); err = alx_request_irq(alx); @@ -1054,6 +1109,7 @@ static int __alx_open(struct alx_priv *alx, bool resume) out_free_rings: alx_free_rings(alx); + alx_free_napis(alx); out_disable_adv_intr: alx_disable_advanced_intr(alx); return err; @@ -1064,6 +1120,7 @@ static void __alx_stop(struct alx_priv *alx) alx_halt(alx); alx_free_irq(alx); alx_free_rings(alx); + alx_free_napis(alx); } static const char *alx_speed_desc(struct alx_hw *hw) -- cgit v1.1 From 702e84185f472457912c641d8c0cc0cc786310eb Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:11 +0100 Subject: alx: switch to per queue data structures Remove the tx and rx queue structures from the alx_priv structure and switch everything over to the queue pointers in the alx_napi structure. Based on the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/alx.h | 5 - drivers/net/ethernet/atheros/alx/main.c | 183 ++++++++++++++++---------------- 2 files changed, 93 insertions(+), 95 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/alx.h b/drivers/net/ethernet/atheros/alx/alx.h index 0859053..d4a4091 100644 --- a/drivers/net/ethernet/atheros/alx/alx.h +++ b/drivers/net/ethernet/atheros/alx/alx.h @@ -113,7 +113,6 @@ struct alx_priv { /* msi-x vectors */ int num_vec; struct msix_entry *msix_entries; - char irq_lbl[IFNAMSIZ + 8]; /* all descriptor memory */ struct { @@ -135,10 +134,6 @@ struct alx_priv { unsigned int rx_ringsz; unsigned int rxbuf_size; - struct napi_struct napi; - struct alx_tx_queue txq; - struct alx_rx_queue rxq; - struct work_struct link_check_wk; struct work_struct reset_wk; diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 193da67..a9fc446 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -55,12 +55,12 @@ static bool msix = false; module_param(msix, bool, 0); MODULE_PARM_DESC(msix, "Enable msi-x interrupt support"); -static void alx_free_txbuf(struct alx_priv *alx, int entry) +static void alx_free_txbuf(struct alx_tx_queue *txq, int entry) { - struct alx_buffer *txb = &alx->txq.bufs[entry]; + struct alx_buffer *txb = &txq->bufs[entry]; if (dma_unmap_len(txb, size)) { - dma_unmap_single(&alx->hw.pdev->dev, + dma_unmap_single(txq->dev, dma_unmap_addr(txb, dma), dma_unmap_len(txb, size), DMA_TO_DEVICE); @@ -75,7 +75,7 @@ static void alx_free_txbuf(struct alx_priv *alx, int entry) static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp) { - struct alx_rx_queue *rxq = &alx->rxq; + struct alx_rx_queue *rxq = alx->qnapi[0]->rxq; struct sk_buff *skb; struct alx_buffer *cur_buf; dma_addr_t dma; @@ -143,22 +143,22 @@ static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp) return count; } -static inline int alx_tpd_avail(struct alx_priv *alx) +static inline int alx_tpd_avail(struct alx_tx_queue *txq) { - struct alx_tx_queue *txq = &alx->txq; - if (txq->write_idx >= txq->read_idx) - return alx->tx_ringsz + txq->read_idx - txq->write_idx - 1; + return txq->count + txq->read_idx - txq->write_idx - 1; return txq->read_idx - txq->write_idx - 1; } -static bool alx_clean_tx_irq(struct alx_priv *alx) +static bool alx_clean_tx_irq(struct alx_tx_queue *txq) { - struct alx_tx_queue *txq = &alx->txq; + struct alx_priv *alx; u16 hw_read_idx, sw_read_idx; unsigned int total_bytes = 0, total_packets = 0; int budget = ALX_DEFAULT_TX_WORK; + alx = netdev_priv(txq->netdev); + sw_read_idx = txq->read_idx; hw_read_idx = alx_read_mem16(&alx->hw, ALX_TPD_PRI0_CIDX); @@ -173,19 +173,19 @@ static bool alx_clean_tx_irq(struct alx_priv *alx) budget--; } - alx_free_txbuf(alx, sw_read_idx); + alx_free_txbuf(txq, sw_read_idx); - if (++sw_read_idx == alx->tx_ringsz) + if (++sw_read_idx == txq->count) sw_read_idx = 0; } txq->read_idx = sw_read_idx; - netdev_completed_queue(alx->dev, total_packets, total_bytes); + netdev_completed_queue(txq->netdev, total_packets, total_bytes); } - if (netif_queue_stopped(alx->dev) && netif_carrier_ok(alx->dev) && - alx_tpd_avail(alx) > alx->tx_ringsz/4) - netif_wake_queue(alx->dev); + if (netif_queue_stopped(txq->netdev) && netif_carrier_ok(txq->netdev) && + alx_tpd_avail(txq) > txq->count / 4) + netif_wake_queue(txq->netdev); return sw_read_idx == hw_read_idx; } @@ -200,15 +200,17 @@ static void alx_schedule_reset(struct alx_priv *alx) schedule_work(&alx->reset_wk); } -static int alx_clean_rx_irq(struct alx_priv *alx, int budget) +static int alx_clean_rx_irq(struct alx_rx_queue *rxq, int budget) { - struct alx_rx_queue *rxq = &alx->rxq; + struct alx_priv *alx; struct alx_rrd *rrd; struct alx_buffer *rxb; struct sk_buff *skb; u16 length, rfd_cleaned = 0; int work = 0; + alx = netdev_priv(rxq->netdev); + while (work < budget) { rrd = &rxq->rrd[rxq->rrd_read_idx]; if (!(rrd->word3 & cpu_to_le32(1 << RRD_UPDATED_SHIFT))) @@ -224,7 +226,7 @@ static int alx_clean_rx_irq(struct alx_priv *alx, int budget) } rxb = &rxq->bufs[rxq->read_idx]; - dma_unmap_single(&alx->hw.pdev->dev, + dma_unmap_single(rxq->dev, dma_unmap_addr(rxb, dma), dma_unmap_len(rxb, size), DMA_FROM_DEVICE); @@ -242,7 +244,7 @@ static int alx_clean_rx_irq(struct alx_priv *alx, int budget) length = ALX_GET_FIELD(le32_to_cpu(rrd->word3), RRD_PKTLEN) - ETH_FCS_LEN; skb_put(skb, length); - skb->protocol = eth_type_trans(skb, alx->dev); + skb->protocol = eth_type_trans(skb, rxq->netdev); skb_checksum_none_assert(skb); if (alx->dev->features & NETIF_F_RXCSUM && @@ -259,13 +261,13 @@ static int alx_clean_rx_irq(struct alx_priv *alx, int budget) } } - napi_gro_receive(&alx->napi, skb); + napi_gro_receive(&rxq->np->napi, skb); work++; next_pkt: - if (++rxq->read_idx == alx->rx_ringsz) + if (++rxq->read_idx == rxq->count) rxq->read_idx = 0; - if (++rxq->rrd_read_idx == alx->rx_ringsz) + if (++rxq->rrd_read_idx == rxq->count) rxq->rrd_read_idx = 0; if (++rfd_cleaned > ALX_RX_ALLOC_THRESH) @@ -280,19 +282,20 @@ next_pkt: static int alx_poll(struct napi_struct *napi, int budget) { - struct alx_priv *alx = container_of(napi, struct alx_priv, napi); + struct alx_napi *np = container_of(napi, struct alx_napi, napi); + struct alx_priv *alx = np->alx; struct alx_hw *hw = &alx->hw; unsigned long flags; bool tx_complete; int work; - tx_complete = alx_clean_tx_irq(alx); - work = alx_clean_rx_irq(alx, budget); + tx_complete = alx_clean_tx_irq(np->txq); + work = alx_clean_rx_irq(np->rxq, budget); if (!tx_complete || work == budget) return budget; - napi_complete(&alx->napi); + napi_complete(&np->napi); /* enable interrupt */ if (alx->flags & ALX_FLAG_USING_MSIX) { @@ -350,7 +353,7 @@ static irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr) goto out; if (intr & (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0)) { - napi_schedule(&alx->napi); + napi_schedule(&alx->qnapi[0]->napi); /* mask rx/tx interrupt, enable them when napi complete */ alx->int_mask &= ~ALX_ISR_ALL_QUEUES; alx_write_mem32(hw, ALX_IMR, alx->int_mask); @@ -365,15 +368,15 @@ static irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr) static irqreturn_t alx_intr_msix_ring(int irq, void *data) { - struct alx_priv *alx = data; - struct alx_hw *hw = &alx->hw; + struct alx_napi *np = data; + struct alx_hw *hw = &np->alx->hw; /* mask interrupt to ACK chip */ alx_mask_msix(hw, 1, true); /* clear interrupt status */ alx_write_mem32(hw, ALX_ISR, (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0)); - napi_schedule(&alx->napi); + napi_schedule(&np->napi); return IRQ_HANDLED; } @@ -428,59 +431,58 @@ static void alx_init_ring_ptrs(struct alx_priv *alx) { struct alx_hw *hw = &alx->hw; u32 addr_hi = ((u64)alx->descmem.dma) >> 32; + struct alx_napi *np = alx->qnapi[0]; - alx->rxq.read_idx = 0; - alx->rxq.write_idx = 0; - alx->rxq.rrd_read_idx = 0; + np->rxq->read_idx = 0; + np->rxq->write_idx = 0; + np->rxq->rrd_read_idx = 0; alx_write_mem32(hw, ALX_RX_BASE_ADDR_HI, addr_hi); - alx_write_mem32(hw, ALX_RRD_ADDR_LO, alx->rxq.rrd_dma); + alx_write_mem32(hw, ALX_RRD_ADDR_LO, np->rxq->rrd_dma); alx_write_mem32(hw, ALX_RRD_RING_SZ, alx->rx_ringsz); - alx_write_mem32(hw, ALX_RFD_ADDR_LO, alx->rxq.rfd_dma); + alx_write_mem32(hw, ALX_RFD_ADDR_LO, np->rxq->rfd_dma); alx_write_mem32(hw, ALX_RFD_RING_SZ, alx->rx_ringsz); alx_write_mem32(hw, ALX_RFD_BUF_SZ, alx->rxbuf_size); - alx->txq.read_idx = 0; - alx->txq.write_idx = 0; + np->txq->read_idx = 0; + np->txq->write_idx = 0; alx_write_mem32(hw, ALX_TX_BASE_ADDR_HI, addr_hi); - alx_write_mem32(hw, ALX_TPD_PRI0_ADDR_LO, alx->txq.tpd_dma); + alx_write_mem32(hw, ALX_TPD_PRI0_ADDR_LO, np->txq->tpd_dma); alx_write_mem32(hw, ALX_TPD_RING_SZ, alx->tx_ringsz); /* load these pointers into the chip */ alx_write_mem32(hw, ALX_SRAM9, ALX_SRAM_LOAD_PTR); } -static void alx_free_txring_buf(struct alx_priv *alx) +static void alx_free_txring_buf(struct alx_tx_queue *txq) { - struct alx_tx_queue *txq = &alx->txq; int i; if (!txq->bufs) return; - for (i = 0; i < alx->tx_ringsz; i++) - alx_free_txbuf(alx, i); + for (i = 0; i < txq->count; i++) + alx_free_txbuf(txq, i); - memset(txq->bufs, 0, alx->tx_ringsz * sizeof(struct alx_buffer)); - memset(txq->tpd, 0, alx->tx_ringsz * sizeof(struct alx_txd)); + memset(txq->bufs, 0, txq->count * sizeof(struct alx_buffer)); + memset(txq->tpd, 0, txq->count * sizeof(struct alx_txd)); txq->write_idx = 0; txq->read_idx = 0; - netdev_reset_queue(alx->dev); + netdev_reset_queue(txq->netdev); } -static void alx_free_rxring_buf(struct alx_priv *alx) +static void alx_free_rxring_buf(struct alx_rx_queue *rxq) { - struct alx_rx_queue *rxq = &alx->rxq; struct alx_buffer *cur_buf; u16 i; if (rxq == NULL) return; - for (i = 0; i < alx->rx_ringsz; i++) { + for (i = 0; i < rxq->count; i++) { cur_buf = rxq->bufs + i; if (cur_buf->skb) { - dma_unmap_single(&alx->hw.pdev->dev, + dma_unmap_single(rxq->dev, dma_unmap_addr(cur_buf, dma), dma_unmap_len(cur_buf, size), DMA_FROM_DEVICE); @@ -498,8 +500,8 @@ static void alx_free_rxring_buf(struct alx_priv *alx) static void alx_free_buffers(struct alx_priv *alx) { - alx_free_txring_buf(alx); - alx_free_rxring_buf(alx); + alx_free_txring_buf(alx->qnapi[0]->txq); + alx_free_rxring_buf(alx->qnapi[0]->rxq); } static int alx_reinit_rings(struct alx_priv *alx) @@ -576,13 +578,13 @@ static int alx_set_mac_address(struct net_device *netdev, void *data) static int alx_alloc_tx_ring(struct alx_priv *alx, struct alx_tx_queue *txq, int offset) { - txq->bufs = kcalloc(alx->tx_ringsz, sizeof(struct alx_buffer), GFP_KERNEL); + txq->bufs = kcalloc(txq->count, sizeof(struct alx_buffer), GFP_KERNEL); if (!txq->bufs) return -ENOMEM; txq->tpd = alx->descmem.virt + offset; txq->tpd_dma = alx->descmem.dma + offset; - offset += sizeof(struct alx_txd) * alx->tx_ringsz; + offset += sizeof(struct alx_txd) * txq->count; return offset; } @@ -590,17 +592,17 @@ static int alx_alloc_tx_ring(struct alx_priv *alx, struct alx_tx_queue *txq, static int alx_alloc_rx_ring(struct alx_priv *alx, struct alx_rx_queue *rxq, int offset) { - rxq->bufs = kcalloc(alx->rx_ringsz, sizeof(struct alx_buffer), GFP_KERNEL); + rxq->bufs = kcalloc(rxq->count, sizeof(struct alx_buffer), GFP_KERNEL); if (!rxq->bufs) return -ENOMEM; rxq->rrd = alx->descmem.virt + offset; rxq->rrd_dma = alx->descmem.dma + offset; - offset += sizeof(struct alx_rrd) * alx->rx_ringsz; + offset += sizeof(struct alx_rrd) * rxq->count; rxq->rfd = alx->descmem.virt + offset; rxq->rfd_dma = alx->descmem.dma + offset; - offset += sizeof(struct alx_rfd) * alx->rx_ringsz; + offset += sizeof(struct alx_rfd) * rxq->count; return offset; } @@ -629,13 +631,13 @@ static int alx_alloc_rings(struct alx_priv *alx) BUILD_BUG_ON(sizeof(struct alx_txd) % 8); BUILD_BUG_ON(sizeof(struct alx_rrd) % 8); - offset = alx_alloc_tx_ring(alx, &alx->txq, offset); + offset = alx_alloc_tx_ring(alx, alx->qnapi[0]->txq, offset); if (offset < 0) { netdev_err(alx->dev, "Allocation of tx buffer failed!\n"); return -ENOMEM; } - offset = alx_alloc_rx_ring(alx, &alx->rxq, offset); + offset = alx_alloc_rx_ring(alx, alx->qnapi[0]->rxq, offset); if (offset < 0) { netdev_err(alx->dev, "Allocation of rx buffer failed!\n"); return -ENOMEM; @@ -648,10 +650,11 @@ static int alx_alloc_rings(struct alx_priv *alx) static void alx_free_rings(struct alx_priv *alx) { + alx_free_buffers(alx); - kfree(alx->txq.bufs); - kfree(alx->rxq.bufs); + kfree(alx->qnapi[0]->txq->bufs); + kfree(alx->qnapi[0]->rxq->bufs); if (!alx->descmem.virt) dma_free_coherent(&alx->hw.pdev->dev, @@ -668,7 +671,7 @@ static void alx_free_napis(struct alx_priv *alx) if (!np) return; - netif_napi_del(&alx->napi); + netif_napi_del(&np->napi); kfree(np->txq); kfree(np->rxq); kfree(np); @@ -690,7 +693,7 @@ static int alx_alloc_napis(struct alx_priv *alx) goto err_out; np->alx = alx; - netif_napi_add(alx->dev, &alx->napi, alx_poll, 64); + netif_napi_add(alx->dev, &np->napi, alx_poll, 64); alx->qnapi[0] = np; /* allocate tx queues */ @@ -768,6 +771,7 @@ static int alx_request_msix(struct alx_priv *alx) { struct net_device *netdev = alx->dev; int i, err, vector = 0, free_vector = 0; + struct alx_napi *np = alx->qnapi[0]; err = request_irq(alx->msix_entries[0].vector, alx_intr_msix_misc, 0, netdev->name, alx); @@ -775,10 +779,10 @@ static int alx_request_msix(struct alx_priv *alx) goto out_err; vector++; - sprintf(alx->irq_lbl, "%s-TxRx-0", netdev->name); + sprintf(np->irq_lbl, "%s-TxRx-0", netdev->name); err = request_irq(alx->msix_entries[vector].vector, - alx_intr_msix_ring, 0, alx->irq_lbl, alx); + alx_intr_msix_ring, 0, np->irq_lbl, np); if (err) goto out_free; @@ -789,7 +793,7 @@ out_free: vector--; for (i = 0; i < vector; i++) - free_irq(alx->msix_entries[free_vector++].vector, alx); + free_irq(alx->msix_entries[free_vector++].vector, alx->qnapi[0]); out_err: return err; @@ -905,12 +909,12 @@ out: static void alx_free_irq(struct alx_priv *alx) { struct pci_dev *pdev = alx->hw.pdev; - int i; + int i, vector = 0; if (alx->flags & ALX_FLAG_USING_MSIX) { /* we have only 2 vectors without multi queue support */ - for (i = 0; i < 2; i++) - free_irq(alx->msix_entries[i].vector, alx); + free_irq(alx->msix_entries[vector++].vector, alx); + free_irq(alx->msix_entries[vector++].vector, alx->qnapi[0]); } else { free_irq(pdev->irq, alx); } @@ -999,7 +1003,7 @@ static void alx_netif_stop(struct alx_priv *alx) if (netif_carrier_ok(alx->dev)) { netif_carrier_off(alx->dev); netif_tx_disable(alx->dev); - napi_disable(&alx->napi); + napi_disable(&alx->qnapi[0]->napi); } } @@ -1069,7 +1073,7 @@ static int alx_change_mtu(struct net_device *netdev, int mtu) static void alx_netif_start(struct alx_priv *alx) { netif_tx_wake_all_queues(alx->dev); - napi_enable(&alx->napi); + napi_enable(&alx->qnapi[0]->napi); netif_carrier_on(alx->dev); } @@ -1303,9 +1307,8 @@ static int alx_tso(struct sk_buff *skb, struct alx_txd *first) return 1; } -static int alx_map_tx_skb(struct alx_priv *alx, struct sk_buff *skb) +static int alx_map_tx_skb(struct alx_tx_queue *txq, struct sk_buff *skb) { - struct alx_tx_queue *txq = &alx->txq; struct alx_txd *tpd, *first_tpd; dma_addr_t dma; int maplen, f, first_idx = txq->write_idx; @@ -1314,7 +1317,7 @@ static int alx_map_tx_skb(struct alx_priv *alx, struct sk_buff *skb) tpd = first_tpd; if (tpd->word1 & (1 << TPD_LSO_V2_SHIFT)) { - if (++txq->write_idx == alx->tx_ringsz) + if (++txq->write_idx == txq->count) txq->write_idx = 0; tpd = &txq->tpd[txq->write_idx]; @@ -1324,9 +1327,9 @@ static int alx_map_tx_skb(struct alx_priv *alx, struct sk_buff *skb) } maplen = skb_headlen(skb); - dma = dma_map_single(&alx->hw.pdev->dev, skb->data, maplen, + dma = dma_map_single(txq->dev, skb->data, maplen, DMA_TO_DEVICE); - if (dma_mapping_error(&alx->hw.pdev->dev, dma)) + if (dma_mapping_error(txq->dev, dma)) goto err_dma; dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen); @@ -1340,16 +1343,16 @@ static int alx_map_tx_skb(struct alx_priv *alx, struct sk_buff *skb) frag = &skb_shinfo(skb)->frags[f]; - if (++txq->write_idx == alx->tx_ringsz) + if (++txq->write_idx == txq->count) txq->write_idx = 0; tpd = &txq->tpd[txq->write_idx]; tpd->word1 = first_tpd->word1; maplen = skb_frag_size(frag); - dma = skb_frag_dma_map(&alx->hw.pdev->dev, frag, 0, + dma = skb_frag_dma_map(txq->dev, frag, 0, maplen, DMA_TO_DEVICE); - if (dma_mapping_error(&alx->hw.pdev->dev, dma)) + if (dma_mapping_error(txq->dev, dma)) goto err_dma; dma_unmap_len_set(&txq->bufs[txq->write_idx], size, maplen); dma_unmap_addr_set(&txq->bufs[txq->write_idx], dma, dma); @@ -1362,7 +1365,7 @@ static int alx_map_tx_skb(struct alx_priv *alx, struct sk_buff *skb) tpd->word1 |= cpu_to_le32(1 << TPD_EOP_SHIFT); txq->bufs[txq->write_idx].skb = skb; - if (++txq->write_idx == alx->tx_ringsz) + if (++txq->write_idx == txq->count) txq->write_idx = 0; return 0; @@ -1370,8 +1373,8 @@ static int alx_map_tx_skb(struct alx_priv *alx, struct sk_buff *skb) err_dma: f = first_idx; while (f != txq->write_idx) { - alx_free_txbuf(alx, f); - if (++f == alx->tx_ringsz) + alx_free_txbuf(txq, f); + if (++f == txq->count) f = 0; } return -ENOMEM; @@ -1381,12 +1384,12 @@ static netdev_tx_t alx_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct alx_priv *alx = netdev_priv(netdev); - struct alx_tx_queue *txq = &alx->txq; + struct alx_tx_queue *txq = alx->qnapi[0]->txq; struct alx_txd *first; int tso; - if (alx_tpd_avail(alx) < alx_tpd_req(skb)) { - netif_stop_queue(alx->dev); + if (alx_tpd_avail(txq) < alx_tpd_req(skb)) { + netif_stop_queue(txq->netdev); goto drop; } @@ -1399,17 +1402,17 @@ static netdev_tx_t alx_start_xmit(struct sk_buff *skb, else if (!tso && alx_tx_csum(skb, first)) goto drop; - if (alx_map_tx_skb(alx, skb) < 0) + if (alx_map_tx_skb(txq, skb) < 0) goto drop; - netdev_sent_queue(alx->dev, skb->len); + netdev_sent_queue(txq->netdev, skb->len); /* flush updates before updating hardware */ wmb(); alx_write_mem16(&alx->hw, ALX_TPD_PRI0_PIDX, txq->write_idx); - if (alx_tpd_avail(alx) < alx->tx_ringsz/8) - netif_stop_queue(alx->dev); + if (alx_tpd_avail(txq) < txq->count / 8) + netif_stop_queue(txq->netdev); return NETDEV_TX_OK; @@ -1478,7 +1481,7 @@ static void alx_poll_controller(struct net_device *netdev) if (alx->flags & ALX_FLAG_USING_MSIX) { alx_intr_msix_misc(0, alx); - alx_intr_msix_ring(0, alx); + alx_intr_msix_ring(0, alx->qnapi[0]); } else if (alx->flags & ALX_FLAG_USING_MSI) alx_intr_msi(0, alx); else -- cgit v1.1 From e0eac25460902a4eda07c0e9b3e749d198e074fa Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:12 +0100 Subject: alx: prepare interrupt functions for multiple queues Extend the interrupt bringup code and the interrupt handler for msi-x interrupts in order to handle multiple queues. We must change the poll function because with multiple queues it is possible that an alx_napi structure has only a tx or only a rx queue pointer. Based on the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 132 +++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index a9fc446..aeb4212 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -286,11 +286,13 @@ static int alx_poll(struct napi_struct *napi, int budget) struct alx_priv *alx = np->alx; struct alx_hw *hw = &alx->hw; unsigned long flags; - bool tx_complete; - int work; + bool tx_complete = true; + int work = 0; - tx_complete = alx_clean_tx_irq(np->txq); - work = alx_clean_rx_irq(np->rxq, budget); + if (np->txq) + tx_complete = alx_clean_tx_irq(np->txq); + if (np->rxq) + work = alx_clean_rx_irq(np->rxq, budget); if (!tx_complete || work == budget) return budget; @@ -299,7 +301,7 @@ static int alx_poll(struct napi_struct *napi, int budget) /* enable interrupt */ if (alx->flags & ALX_FLAG_USING_MSIX) { - alx_mask_msix(hw, 1, false); + alx_mask_msix(hw, np->vec_idx, false); } else { spin_lock_irqsave(&alx->irq_lock, flags); alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; @@ -372,9 +374,9 @@ static irqreturn_t alx_intr_msix_ring(int irq, void *data) struct alx_hw *hw = &np->alx->hw; /* mask interrupt to ACK chip */ - alx_mask_msix(hw, 1, true); + alx_mask_msix(hw, np->vec_idx, true); /* clear interrupt status */ - alx_write_mem32(hw, ALX_ISR, (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0)); + alx_write_mem32(hw, ALX_ISR, np->vec_mask); napi_schedule(&np->napi); @@ -678,6 +680,13 @@ static void alx_free_napis(struct alx_priv *alx) alx->qnapi[0] = NULL; } +static const u32 tx_vect_mask[] = {ALX_ISR_TX_Q0, ALX_ISR_TX_Q1, + ALX_ISR_TX_Q2, ALX_ISR_TX_Q3}; +static const u32 rx_vect_mask[] = {ALX_ISR_RX_Q0, ALX_ISR_RX_Q1, + ALX_ISR_RX_Q2, ALX_ISR_RX_Q3, + ALX_ISR_RX_Q4, ALX_ISR_RX_Q5, + ALX_ISR_RX_Q6, ALX_ISR_RX_Q7}; + static int alx_alloc_napis(struct alx_priv *alx) { struct alx_napi *np; @@ -685,7 +694,6 @@ static int alx_alloc_napis(struct alx_priv *alx) struct alx_tx_queue *txq; alx->int_mask &= ~ALX_ISR_ALL_QUEUES; - alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; /* allocate alx_napi structures */ np = kzalloc(sizeof(struct alx_napi), GFP_KERNEL); @@ -703,9 +711,12 @@ static int alx_alloc_napis(struct alx_priv *alx) goto err_out; np->txq = txq; + txq->queue_idx = 0; txq->count = alx->tx_ringsz; txq->netdev = alx->dev; txq->dev = &alx->hw.pdev->dev; + np->vec_mask |= tx_vect_mask[0]; + alx->int_mask |= tx_vect_mask[0]; /* allocate rx queues */ np = alx->qnapi[0]; @@ -715,9 +726,12 @@ static int alx_alloc_napis(struct alx_priv *alx) np->rxq = rxq; rxq->np = alx->qnapi[0]; + rxq->queue_idx = 0; rxq->count = alx->rx_ringsz; rxq->netdev = alx->dev; rxq->dev = &alx->hw.pdev->dev; + np->vec_mask |= rx_vect_mask[0]; + alx->int_mask |= rx_vect_mask[0]; return 0; @@ -727,24 +741,43 @@ err_out: return -ENOMEM; } +static const int txq_vec_mapping_shift[] = { + 0, ALX_MSI_MAP_TBL1_TXQ0_SHIFT, + 0, ALX_MSI_MAP_TBL1_TXQ1_SHIFT, + 1, ALX_MSI_MAP_TBL2_TXQ2_SHIFT, + 1, ALX_MSI_MAP_TBL2_TXQ3_SHIFT, +}; + static void alx_config_vector_mapping(struct alx_priv *alx) { struct alx_hw *hw = &alx->hw; - u32 tbl = 0; + u32 tbl[2] = {0, 0}; + int i, vector, idx, shift; if (alx->flags & ALX_FLAG_USING_MSIX) { - tbl |= 1 << ALX_MSI_MAP_TBL1_TXQ0_SHIFT; - tbl |= 1 << ALX_MSI_MAP_TBL1_RXQ0_SHIFT; + /* tx mappings */ + for (i = 0, vector = 1; i < alx->num_txq; i++, vector++) { + idx = txq_vec_mapping_shift[i * 2]; + shift = txq_vec_mapping_shift[i * 2 + 1]; + tbl[idx] |= vector << shift; + } + + /* rx mapping */ + tbl[0] |= 1 << ALX_MSI_MAP_TBL1_RXQ0_SHIFT; } - alx_write_mem32(hw, ALX_MSI_MAP_TBL1, tbl); - alx_write_mem32(hw, ALX_MSI_MAP_TBL2, 0); + alx_write_mem32(hw, ALX_MSI_MAP_TBL1, tbl[0]); + alx_write_mem32(hw, ALX_MSI_MAP_TBL2, tbl[1]); alx_write_mem32(hw, ALX_MSI_ID_MAP, 0); } static bool alx_enable_msix(struct alx_priv *alx) { - int i, err, num_vec = 2; + int i, err, num_vec, num_txq, num_rxq; + + num_txq = 1; + num_rxq = 1; + num_vec = max_t(int, num_txq, num_rxq) + 1; alx->msix_entries = kcalloc(num_vec, sizeof(struct msix_entry), GFP_KERNEL); @@ -764,6 +797,10 @@ static bool alx_enable_msix(struct alx_priv *alx) } alx->num_vec = num_vec; + alx->num_napi = num_vec - 1; + alx->num_txq = num_txq; + alx->num_rxq = num_rxq; + return true; } @@ -771,21 +808,35 @@ static int alx_request_msix(struct alx_priv *alx) { struct net_device *netdev = alx->dev; int i, err, vector = 0, free_vector = 0; - struct alx_napi *np = alx->qnapi[0]; err = request_irq(alx->msix_entries[0].vector, alx_intr_msix_misc, 0, netdev->name, alx); if (err) goto out_err; - vector++; - sprintf(np->irq_lbl, "%s-TxRx-0", netdev->name); - - err = request_irq(alx->msix_entries[vector].vector, - alx_intr_msix_ring, 0, np->irq_lbl, np); + for (i = 0; i < alx->num_napi; i++) { + struct alx_napi *np = alx->qnapi[i]; + + vector++; + + if (np->txq && np->rxq) + sprintf(np->irq_lbl, "%s-TxRx-%u", netdev->name, + np->txq->queue_idx); + else if (np->txq) + sprintf(np->irq_lbl, "%s-tx-%u", netdev->name, + np->txq->queue_idx); + else if (np->rxq) + sprintf(np->irq_lbl, "%s-rx-%u", netdev->name, + np->rxq->queue_idx); + else + sprintf(np->irq_lbl, "%s-unused", netdev->name); + + np->vec_idx = vector; + err = request_irq(alx->msix_entries[vector].vector, + alx_intr_msix_ring, 0, np->irq_lbl, np); if (err) goto out_free; - + } return 0; out_free: @@ -793,7 +844,8 @@ out_free: vector--; for (i = 0; i < vector; i++) - free_irq(alx->msix_entries[free_vector++].vector, alx->qnapi[0]); + free_irq(alx->msix_entries[free_vector++].vector, + alx->qnapi[i]); out_err: return err; @@ -808,6 +860,9 @@ static void alx_init_intr(struct alx_priv *alx, bool msix) if (!(alx->flags & ALX_FLAG_USING_MSIX)) { alx->num_vec = 1; + alx->num_napi = 1; + alx->num_txq = 1; + alx->num_rxq = 1; if (!pci_enable_msi(alx->hw.pdev)) alx->flags |= ALX_FLAG_USING_MSI; @@ -863,6 +918,25 @@ static void alx_irq_disable(struct alx_priv *alx) } } +static int alx_realloc_resources(struct alx_priv *alx) +{ + int err; + + alx_free_rings(alx); + alx_free_napis(alx); + alx_disable_advanced_intr(alx); + + err = alx_alloc_napis(alx); + if (err) + return err; + + err = alx_alloc_rings(alx); + if (err) + return err; + + return 0; +} + static int alx_request_irq(struct alx_priv *alx) { struct pci_dev *pdev = alx->hw.pdev; @@ -879,8 +953,9 @@ static int alx_request_irq(struct alx_priv *alx) goto out; /* msix request failed, realloc resources */ - alx_disable_advanced_intr(alx); - alx_init_intr(alx, false); + err = alx_realloc_resources(alx); + if (err) + goto out; } if (alx->flags & ALX_FLAG_USING_MSI) { @@ -912,9 +987,10 @@ static void alx_free_irq(struct alx_priv *alx) int i, vector = 0; if (alx->flags & ALX_FLAG_USING_MSIX) { - /* we have only 2 vectors without multi queue support */ free_irq(alx->msix_entries[vector++].vector, alx); - free_irq(alx->msix_entries[vector++].vector, alx->qnapi[0]); + for (i = 0; i < alx->num_napi; i++) + free_irq(alx->msix_entries[vector++].vector, + alx->qnapi[i]); } else { free_irq(pdev->irq, alx); } @@ -1478,10 +1554,12 @@ static int alx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) static void alx_poll_controller(struct net_device *netdev) { struct alx_priv *alx = netdev_priv(netdev); + int i; if (alx->flags & ALX_FLAG_USING_MSIX) { alx_intr_msix_misc(0, alx); - alx_intr_msix_ring(0, alx->qnapi[0]); + for (i = 0; i < alx->num_txq; i++) + alx_intr_msix_ring(0, alx->qnapi[i]); } else if (alx->flags & ALX_FLAG_USING_MSI) alx_intr_msi(0, alx); else -- cgit v1.1 From a4076d347f9a27cdd85186bef2f4207b6187c35e Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:13 +0100 Subject: alx: prepare resource allocation for multi queue support Allocate, initialise and free alx_tx_queue structs based on the number of alx_napi structures. Also increase the size of the descriptor memory based on the number of tx queues in use. Based on the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 149 +++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index aeb4212..c5f09d2 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -429,28 +429,45 @@ static irqreturn_t alx_intr_legacy(int irq, void *data) return alx_intr_handle(alx, intr); } +static const u16 txring_header_reg[] = {ALX_TPD_PRI0_ADDR_LO, + ALX_TPD_PRI1_ADDR_LO, + ALX_TPD_PRI2_ADDR_LO, + ALX_TPD_PRI3_ADDR_LO}; + static void alx_init_ring_ptrs(struct alx_priv *alx) { struct alx_hw *hw = &alx->hw; u32 addr_hi = ((u64)alx->descmem.dma) >> 32; - struct alx_napi *np = alx->qnapi[0]; + struct alx_napi *np; + int i; + + for (i = 0; i < alx->num_napi; i++) { + np = alx->qnapi[i]; + if (np->txq) { + np->txq->read_idx = 0; + np->txq->write_idx = 0; + alx_write_mem32(hw, + txring_header_reg[np->txq->queue_idx], + np->txq->tpd_dma); + } + + if (np->rxq) { + np->rxq->read_idx = 0; + np->rxq->write_idx = 0; + np->rxq->rrd_read_idx = 0; + alx_write_mem32(hw, ALX_RRD_ADDR_LO, np->rxq->rrd_dma); + alx_write_mem32(hw, ALX_RFD_ADDR_LO, np->rxq->rfd_dma); + } + } + + alx_write_mem32(hw, ALX_TX_BASE_ADDR_HI, addr_hi); + alx_write_mem32(hw, ALX_TPD_RING_SZ, alx->tx_ringsz); - np->rxq->read_idx = 0; - np->rxq->write_idx = 0; - np->rxq->rrd_read_idx = 0; alx_write_mem32(hw, ALX_RX_BASE_ADDR_HI, addr_hi); - alx_write_mem32(hw, ALX_RRD_ADDR_LO, np->rxq->rrd_dma); alx_write_mem32(hw, ALX_RRD_RING_SZ, alx->rx_ringsz); - alx_write_mem32(hw, ALX_RFD_ADDR_LO, np->rxq->rfd_dma); alx_write_mem32(hw, ALX_RFD_RING_SZ, alx->rx_ringsz); alx_write_mem32(hw, ALX_RFD_BUF_SZ, alx->rxbuf_size); - np->txq->read_idx = 0; - np->txq->write_idx = 0; - alx_write_mem32(hw, ALX_TX_BASE_ADDR_HI, addr_hi); - alx_write_mem32(hw, ALX_TPD_PRI0_ADDR_LO, np->txq->tpd_dma); - alx_write_mem32(hw, ALX_TPD_RING_SZ, alx->tx_ringsz); - /* load these pointers into the chip */ alx_write_mem32(hw, ALX_SRAM9, ALX_SRAM_LOAD_PTR); } @@ -478,7 +495,7 @@ static void alx_free_rxring_buf(struct alx_rx_queue *rxq) struct alx_buffer *cur_buf; u16 i; - if (rxq == NULL) + if (!rxq->bufs) return; for (i = 0; i < rxq->count; i++) { @@ -502,8 +519,14 @@ static void alx_free_rxring_buf(struct alx_rx_queue *rxq) static void alx_free_buffers(struct alx_priv *alx) { - alx_free_txring_buf(alx->qnapi[0]->txq); - alx_free_rxring_buf(alx->qnapi[0]->rxq); + int i; + + for (i = 0; i < alx->num_txq; i++) + if (alx->qnapi[i] && alx->qnapi[i]->txq) + alx_free_txring_buf(alx->qnapi[i]->txq); + + if (alx->qnapi[0] && alx->qnapi[0]->rxq) + alx_free_rxring_buf(alx->qnapi[0]->rxq); } static int alx_reinit_rings(struct alx_priv *alx) @@ -611,7 +634,7 @@ static int alx_alloc_rx_ring(struct alx_priv *alx, struct alx_rx_queue *rxq, static int alx_alloc_rings(struct alx_priv *alx) { - int offset = 0; + int i, offset = 0; /* physical tx/rx ring descriptors * @@ -619,7 +642,8 @@ static int alx_alloc_rings(struct alx_priv *alx) * 4G boundary (hardware has a single register for high 32 bits * of addresses only) */ - alx->descmem.size = sizeof(struct alx_txd) * alx->tx_ringsz + + alx->descmem.size = sizeof(struct alx_txd) * alx->tx_ringsz * + alx->num_txq + sizeof(struct alx_rrd) * alx->rx_ringsz + sizeof(struct alx_rfd) * alx->rx_ringsz; alx->descmem.virt = dma_zalloc_coherent(&alx->hw.pdev->dev, @@ -633,10 +657,12 @@ static int alx_alloc_rings(struct alx_priv *alx) BUILD_BUG_ON(sizeof(struct alx_txd) % 8); BUILD_BUG_ON(sizeof(struct alx_rrd) % 8); - offset = alx_alloc_tx_ring(alx, alx->qnapi[0]->txq, offset); - if (offset < 0) { - netdev_err(alx->dev, "Allocation of tx buffer failed!\n"); - return -ENOMEM; + for (i = 0; i < alx->num_txq; i++) { + offset = alx_alloc_tx_ring(alx, alx->qnapi[i]->txq, offset); + if (offset < 0) { + netdev_err(alx->dev, "Allocation of tx buffer failed!\n"); + return -ENOMEM; + } } offset = alx_alloc_rx_ring(alx, alx->qnapi[0]->rxq, offset); @@ -652,11 +678,16 @@ static int alx_alloc_rings(struct alx_priv *alx) static void alx_free_rings(struct alx_priv *alx) { + int i; alx_free_buffers(alx); - kfree(alx->qnapi[0]->txq->bufs); - kfree(alx->qnapi[0]->rxq->bufs); + for (i = 0; i < alx->num_txq; i++) + if (alx->qnapi[i] && alx->qnapi[i]->txq) + kfree(alx->qnapi[i]->txq->bufs); + + if (alx->qnapi[0] && alx->qnapi[0]->rxq) + kfree(alx->qnapi[0]->rxq->bufs); if (!alx->descmem.virt) dma_free_coherent(&alx->hw.pdev->dev, @@ -668,16 +699,19 @@ static void alx_free_rings(struct alx_priv *alx) static void alx_free_napis(struct alx_priv *alx) { struct alx_napi *np; + int i; - np = alx->qnapi[0]; - if (!np) - return; - - netif_napi_del(&np->napi); - kfree(np->txq); - kfree(np->rxq); - kfree(np); - alx->qnapi[0] = NULL; + for (i = 0; i < alx->num_napi; i++) { + np = alx->qnapi[i]; + if (!np) + continue; + + netif_napi_del(&np->napi); + kfree(np->txq); + kfree(np->rxq); + kfree(np); + alx->qnapi[i] = NULL; + } } static const u32 tx_vect_mask[] = {ALX_ISR_TX_Q0, ALX_ISR_TX_Q1, @@ -692,31 +726,36 @@ static int alx_alloc_napis(struct alx_priv *alx) struct alx_napi *np; struct alx_rx_queue *rxq; struct alx_tx_queue *txq; + int i; alx->int_mask &= ~ALX_ISR_ALL_QUEUES; /* allocate alx_napi structures */ - np = kzalloc(sizeof(struct alx_napi), GFP_KERNEL); - if (!np) - goto err_out; + for (i = 0; i < alx->num_napi; i++) { + np = kzalloc(sizeof(struct alx_napi), GFP_KERNEL); + if (!np) + goto err_out; - np->alx = alx; - netif_napi_add(alx->dev, &np->napi, alx_poll, 64); - alx->qnapi[0] = np; + np->alx = alx; + netif_napi_add(alx->dev, &np->napi, alx_poll, 64); + alx->qnapi[i] = np; + } /* allocate tx queues */ - np = alx->qnapi[0]; - txq = kzalloc(sizeof(*txq), GFP_KERNEL); - if (!txq) - goto err_out; - - np->txq = txq; - txq->queue_idx = 0; - txq->count = alx->tx_ringsz; - txq->netdev = alx->dev; - txq->dev = &alx->hw.pdev->dev; - np->vec_mask |= tx_vect_mask[0]; - alx->int_mask |= tx_vect_mask[0]; + for (i = 0; i < alx->num_txq; i++) { + np = alx->qnapi[i]; + txq = kzalloc(sizeof(*txq), GFP_KERNEL); + if (!txq) + goto err_out; + + np->txq = txq; + txq->queue_idx = i; + txq->count = alx->tx_ringsz; + txq->netdev = alx->dev; + txq->dev = &alx->hw.pdev->dev; + np->vec_mask |= tx_vect_mask[i]; + alx->int_mask |= tx_vect_mask[i]; + } /* allocate rx queues */ np = alx->qnapi[0]; @@ -1075,11 +1114,14 @@ static netdev_features_t alx_fix_features(struct net_device *netdev, static void alx_netif_stop(struct alx_priv *alx) { + int i; + netif_trans_update(alx->dev); if (netif_carrier_ok(alx->dev)) { netif_carrier_off(alx->dev); netif_tx_disable(alx->dev); - napi_disable(&alx->qnapi[0]->napi); + for (i = 0; i < alx->num_napi; i++) + napi_disable(&alx->qnapi[i]->napi); } } @@ -1148,8 +1190,11 @@ static int alx_change_mtu(struct net_device *netdev, int mtu) static void alx_netif_start(struct alx_priv *alx) { + int i; + netif_tx_wake_all_queues(alx->dev); - napi_enable(&alx->qnapi[0]->napi); + for (i = 0; i < alx->num_napi; i++) + napi_enable(&alx->qnapi[i]->napi); netif_carrier_on(alx->dev); } -- cgit v1.1 From 2e06826bc659e0ff664b709719d746b7b088827b Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:14 +0100 Subject: alx: prepare tx path for multi queue support This patch prepares the tx path to send data on multiple tx queues. It introduces per queue register adresses and uses them in the alx_tx_queue structs. There are new helper functions for the queue mapping in the tx path. Based on the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 58 +++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index c5f09d2..0e773a2 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -143,6 +143,22 @@ static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp) return count; } +static struct alx_tx_queue *alx_tx_queue_mapping(struct alx_priv *alx, + struct sk_buff *skb) +{ + unsigned int r_idx = skb->queue_mapping; + + if (r_idx >= alx->num_txq) + r_idx = r_idx % alx->num_txq; + + return alx->qnapi[r_idx]->txq; +} + +static struct netdev_queue *alx_get_tx_queue(const struct alx_tx_queue *txq) +{ + return netdev_get_tx_queue(txq->netdev, txq->queue_idx); +} + static inline int alx_tpd_avail(struct alx_tx_queue *txq) { if (txq->write_idx >= txq->read_idx) @@ -153,14 +169,16 @@ static inline int alx_tpd_avail(struct alx_tx_queue *txq) static bool alx_clean_tx_irq(struct alx_tx_queue *txq) { struct alx_priv *alx; + struct netdev_queue *tx_queue; u16 hw_read_idx, sw_read_idx; unsigned int total_bytes = 0, total_packets = 0; int budget = ALX_DEFAULT_TX_WORK; alx = netdev_priv(txq->netdev); + tx_queue = alx_get_tx_queue(txq); sw_read_idx = txq->read_idx; - hw_read_idx = alx_read_mem16(&alx->hw, ALX_TPD_PRI0_CIDX); + hw_read_idx = alx_read_mem16(&alx->hw, txq->c_reg); if (sw_read_idx != hw_read_idx) { while (sw_read_idx != hw_read_idx && budget > 0) { @@ -180,12 +198,12 @@ static bool alx_clean_tx_irq(struct alx_tx_queue *txq) } txq->read_idx = sw_read_idx; - netdev_completed_queue(txq->netdev, total_packets, total_bytes); + netdev_tx_completed_queue(tx_queue, total_packets, total_bytes); } - if (netif_queue_stopped(txq->netdev) && netif_carrier_ok(txq->netdev) && + if (netif_tx_queue_stopped(tx_queue) && netif_carrier_ok(alx->dev) && alx_tpd_avail(txq) > txq->count / 4) - netif_wake_queue(txq->netdev); + netif_tx_wake_queue(tx_queue); return sw_read_idx == hw_read_idx; } @@ -487,7 +505,7 @@ static void alx_free_txring_buf(struct alx_tx_queue *txq) txq->write_idx = 0; txq->read_idx = 0; - netdev_reset_queue(txq->netdev); + netdev_tx_reset_queue(alx_get_tx_queue(txq)); } static void alx_free_rxring_buf(struct alx_rx_queue *rxq) @@ -714,6 +732,10 @@ static void alx_free_napis(struct alx_priv *alx) } } +static const u16 tx_pidx_reg[] = {ALX_TPD_PRI0_PIDX, ALX_TPD_PRI1_PIDX, + ALX_TPD_PRI2_PIDX, ALX_TPD_PRI3_PIDX}; +static const u16 tx_cidx_reg[] = {ALX_TPD_PRI0_CIDX, ALX_TPD_PRI1_CIDX, + ALX_TPD_PRI2_CIDX, ALX_TPD_PRI3_CIDX}; static const u32 tx_vect_mask[] = {ALX_ISR_TX_Q0, ALX_ISR_TX_Q1, ALX_ISR_TX_Q2, ALX_ISR_TX_Q3}; static const u32 rx_vect_mask[] = {ALX_ISR_RX_Q0, ALX_ISR_RX_Q1, @@ -749,6 +771,8 @@ static int alx_alloc_napis(struct alx_priv *alx) goto err_out; np->txq = txq; + txq->p_reg = tx_pidx_reg[i]; + txq->c_reg = tx_cidx_reg[i]; txq->queue_idx = i; txq->count = alx->tx_ringsz; txq->netdev = alx->dev; @@ -1501,16 +1525,17 @@ err_dma: return -ENOMEM; } -static netdev_tx_t alx_start_xmit(struct sk_buff *skb, - struct net_device *netdev) +static netdev_tx_t alx_start_xmit_ring(struct sk_buff *skb, + struct alx_tx_queue *txq) { - struct alx_priv *alx = netdev_priv(netdev); - struct alx_tx_queue *txq = alx->qnapi[0]->txq; + struct alx_priv *alx; struct alx_txd *first; int tso; + alx = netdev_priv(txq->netdev); + if (alx_tpd_avail(txq) < alx_tpd_req(skb)) { - netif_stop_queue(txq->netdev); + netif_tx_stop_queue(alx_get_tx_queue(txq)); goto drop; } @@ -1526,14 +1551,14 @@ static netdev_tx_t alx_start_xmit(struct sk_buff *skb, if (alx_map_tx_skb(txq, skb) < 0) goto drop; - netdev_sent_queue(txq->netdev, skb->len); + netdev_tx_sent_queue(alx_get_tx_queue(txq), skb->len); /* flush updates before updating hardware */ wmb(); - alx_write_mem16(&alx->hw, ALX_TPD_PRI0_PIDX, txq->write_idx); + alx_write_mem16(&alx->hw, txq->p_reg, txq->write_idx); if (alx_tpd_avail(txq) < txq->count / 8) - netif_stop_queue(txq->netdev); + netif_tx_stop_queue(alx_get_tx_queue(txq)); return NETDEV_TX_OK; @@ -1542,6 +1567,13 @@ drop: return NETDEV_TX_OK; } +static netdev_tx_t alx_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct alx_priv *alx = netdev_priv(netdev); + return alx_start_xmit_ring(skb, alx_tx_queue_mapping(alx, skb)); +} + static void alx_tx_timeout(struct net_device *dev) { struct alx_priv *alx = netdev_priv(dev); -- cgit v1.1 From f58e0f77471b913c18fc253c90fd7bb8ee6fca3f Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:15 +0100 Subject: alx: enable msi-x interrupts by default Remove the module parameter to enable msi-x support and enable msi-x interrupts unconditionally by default. This is a preparatory step to enable multi queue support by default, because this is only working with msi-x interrupts. Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 0e773a2..91c9149 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -51,10 +51,6 @@ const char alx_drv_name[] = "alx"; -static bool msix = false; -module_param(msix, bool, 0); -MODULE_PARM_DESC(msix, "Enable msi-x interrupt support"); - static void alx_free_txbuf(struct alx_tx_queue *txq, int entry) { struct alx_buffer *txb = &txq->bufs[entry]; @@ -1226,7 +1222,7 @@ static int __alx_open(struct alx_priv *alx, bool resume) { int err; - alx_init_intr(alx, msix); + alx_init_intr(alx, true); if (!resume) netif_carrier_off(alx->dev); -- cgit v1.1 From d768319cd4277ef1cfa1e04792adb0aeea40cca6 Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Tue, 15 Nov 2016 12:43:16 +0100 Subject: alx: enable multiple tx queues Enable multiple tx queues by default based on the number of online cpus. The hardware supports up to four tx queues. Based on the downstream driver at github.com/qca/alx Signed-off-by: Tobias Regnery Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 91c9149..c8f5255 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -834,7 +834,7 @@ static bool alx_enable_msix(struct alx_priv *alx) { int i, err, num_vec, num_txq, num_rxq; - num_txq = 1; + num_txq = min_t(int, num_online_cpus(), ALX_MAX_TX_QUEUES); num_rxq = 1; num_vec = max_t(int, num_txq, num_rxq) + 1; @@ -1241,6 +1241,9 @@ static int __alx_open(struct alx_priv *alx, bool resume) if (err) goto out_free_rings; + netif_set_real_num_tx_queues(alx->dev, alx->num_txq); + netif_set_real_num_rx_queues(alx->dev, alx->num_rxq); + /* clear old interrupts */ alx_write_mem32(&alx->hw, ALX_ISR, ~(u32)ALX_ISR_DIS); @@ -1749,7 +1752,8 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_pci_release; } - netdev = alloc_etherdev(sizeof(*alx)); + netdev = alloc_etherdev_mqs(sizeof(*alx), + ALX_MAX_TX_QUEUES, 1); if (!netdev) { err = -ENOMEM; goto out_pci_release; -- cgit v1.1 From 46738b1317e169b281ad74690276916e24d1be6d Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Tue, 15 Nov 2016 16:14:04 +0100 Subject: ipv6: sr: add option to control lwtunnel support This patch adds a new option CONFIG_IPV6_SEG6_LWTUNNEL to enable/disable support of encapsulation with the lightweight tunnels. When this option is enabled, CONFIG_LWTUNNEL is automatically selected. Fix commit 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels") Without a proper option to control lwtunnel support for SR-IPv6, if CONFIG_LWTUNNEL=n then the IPv6 initialization fails as a consequence of seg6_iptunnel_init() failure with EOPNOTSUPP: NET: Registered protocol family 10 IPv6: Attempt to unregister permanent protocol 6 IPv6: Attempt to unregister permanent protocol 136 IPv6: Attempt to unregister permanent protocol 17 NET: Unregistered protocol family 10 Tested (compiling, booting, and loading ipv6 module when relevant) with possible combinations of CONFIG_IPV6={y,m,n}, CONFIG_IPV6_SEG6_LWTUNNEL={y,n} and CONFIG_LWTUNNEL={y,n}. Reported-by: Lorenzo Colitti Suggested-by: Roopa Prabhu Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/Kconfig | 13 ++++++++++++- net/ipv6/Makefile | 5 +++-- net/ipv6/seg6.c | 8 ++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 0f00811..ec1267e 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -289,9 +289,20 @@ config IPV6_PIMSM_V2 Support for IPv6 PIM multicast routing protocol PIM-SMv2. If unsure, say N. +config IPV6_SEG6_LWTUNNEL + bool "IPv6: Segment Routing Header encapsulation support" + depends on IPV6 + select LWTUNNEL + ---help--- + Support for encapsulation of packets within an outer IPv6 + header and a Segment Routing Header using the lightweight + tunnels mechanism. + + If unsure, say N. + config IPV6_SEG6_INLINE bool "IPv6: direct Segment Routing Header insertion " - depends on IPV6 + depends on IPV6_SEG6_LWTUNNEL ---help--- Support for direct insertion of the Segment Routing Header, also known as inline mode. Be aware that direct insertion of diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 129cad2..a9e9fec 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -9,7 +9,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ - udp_offload.o seg6.o seg6_iptunnel.o + udp_offload.o seg6.o ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o @@ -23,6 +23,8 @@ ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o ipv6-$(CONFIG_PROC_FS) += proc.o ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o ipv6-$(CONFIG_NETLABEL) += calipso.o +ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o +ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o ipv6-objs += $(ipv6-y) @@ -44,7 +46,6 @@ obj-$(CONFIG_IPV6_SIT) += sit.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o obj-$(CONFIG_IPV6_GRE) += ip6_gre.o obj-$(CONFIG_IPV6_FOU) += fou6.o -obj-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o ip6_icmp.o obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 50f6e06..b172d85 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -451,9 +451,11 @@ int __init seg6_init(void) if (err) goto out_unregister_genl; +#ifdef CONFIG_IPV6_SEG6_LWTUNNEL err = seg6_iptunnel_init(); if (err) goto out_unregister_pernet; +#endif #ifdef CONFIG_IPV6_SEG6_HMAC err = seg6_hmac_init(); @@ -467,10 +469,14 @@ out: return err; #ifdef CONFIG_IPV6_SEG6_HMAC out_unregister_iptun: +#ifdef CONFIG_IPV6_SEG6_LWTUNNEL seg6_iptunnel_exit(); #endif +#endif +#ifdef CONFIG_IPV6_SEG6_LWTUNNEL out_unregister_pernet: unregister_pernet_subsys(&ip6_segments_ops); +#endif out_unregister_genl: genl_unregister_family(&seg6_genl_family); goto out; @@ -481,7 +487,9 @@ void seg6_exit(void) #ifdef CONFIG_IPV6_SEG6_HMAC seg6_hmac_exit(); #endif +#ifdef CONFIG_IPV6_SEG6_LWTUNNEL seg6_iptunnel_exit(); +#endif unregister_pernet_subsys(&ip6_segments_ops); genl_unregister_family(&seg6_genl_family); } -- cgit v1.1 From 2874aa2e467dbc0b4f7cb0ee5dc872e98e000a47 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 15 Nov 2016 11:00:04 -0800 Subject: bpf: Fix compilation warning in __bpf_lru_list_rotate_inactive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc-6.2.1 gives the following warning: kernel/bpf/bpf_lru_list.c: In function ‘__bpf_lru_list_rotate_inactive.isra.3’: kernel/bpf/bpf_lru_list.c:201:28: warning: ‘next’ may be used uninitialized in this function [-Wmaybe-uninitialized] The "next" is currently initialized in the while() loop which must have >=1 iterations. This patch initializes next to get rid of the compiler warning. Fixes: 3a08c2fd7634 ("bpf: LRU List") Reported-by: David Miller Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/bpf_lru_list.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c index bfebff0..89b7ef4 100644 --- a/kernel/bpf/bpf_lru_list.c +++ b/kernel/bpf/bpf_lru_list.c @@ -170,7 +170,7 @@ static void __bpf_lru_list_rotate_inactive(struct bpf_lru *lru, struct bpf_lru_list *l) { struct list_head *inactive = &l->lists[BPF_LRU_LIST_T_INACTIVE]; - struct list_head *cur, *next, *last; + struct list_head *cur, *last, *next = inactive; struct bpf_lru_node *node; unsigned int i = 0; -- cgit v1.1 From 217f6974368188fd8bd7804bf5a036aa5762c5e4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2016 10:15:11 -0800 Subject: net: busy-poll: allow preemption in sk_busy_loop() After commit 4cd13c21b207 ("softirq: Let ksoftirqd do its job"), sk_busy_loop() needs a bit of care : softirqs might be delayed since we do not allow preemption yet. This patch adds preemptiom points in sk_busy_loop(), and makes sure no unnecessary cache line dirtying or atomic operations are done while looping. A new flag is added into napi->state : NAPI_STATE_IN_BUSY_POLL This prevents napi_complete_done() from clearing NAPIF_STATE_SCHED, so that sk_busy_loop() does not have to grab it again. Similarly, netpoll_poll_lock() is done one time. This gives about 10 to 20 % improvement in various busy polling tests, especially when many threads are busy polling in configurations with large number of NIC queues. This should allow experimenting with bigger delays without hurting overall latencies. Tested: On a 40Gb mlx4 NIC, 32 RX/TX queues. echo 70 >/proc/sys/net/core/busy_read for i in `seq 1 40`; do echo -n $i: ; ./super_netperf $i -H lpaa24 -t UDP_RR -- -N -n; done Before: After: 1: 90072 92819 2: 157289 184007 3: 235772 213504 4: 344074 357513 5: 394755 458267 6: 461151 487819 7: 549116 625963 8: 544423 716219 9: 720460 738446 10: 794686 837612 11: 915998 923960 12: 937507 925107 13: 1019677 971506 14: 1046831 1113650 15: 1114154 1148902 16: 1105221 1179263 17: 1266552 1299585 18: 1258454 1383817 19: 1341453 1312194 20: 1363557 1488487 21: 1387979 1501004 22: 1417552 1601683 23: 1550049 1642002 24: 1568876 1601915 25: 1560239 1683607 26: 1640207 1745211 27: 1706540 1723574 28: 1638518 1722036 29: 1734309 1757447 30: 1782007 1855436 31: 1724806 1888539 32: 1717716 1944297 33: 1778716 1869118 34: 1805738 1983466 35: 1815694 2020758 36: 1893059 2035632 37: 1843406 2034653 38: 1888830 2086580 39: 1972827 2143567 40: 1877729 2181851 Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Adam Belay Cc: Tariq Toukan Cc: Yuval Mintz Cc: Ariel Elior Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 +++++ net/core/dev.c | 102 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 92 insertions(+), 20 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 86bacf6..e71de66 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -334,6 +334,16 @@ enum { NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */ NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */ NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */ + NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */ +}; + +enum { + NAPIF_STATE_SCHED = (1UL << NAPI_STATE_SCHED), + NAPIF_STATE_DISABLE = (1UL << NAPI_STATE_DISABLE), + NAPIF_STATE_NPSVC = (1UL << NAPI_STATE_NPSVC), + NAPIF_STATE_HASHED = (1UL << NAPI_STATE_HASHED), + NAPIF_STATE_NO_BUSY_POLL = (1UL << NAPI_STATE_NO_BUSY_POLL), + NAPIF_STATE_IN_BUSY_POLL = (1UL << NAPI_STATE_IN_BUSY_POLL), }; enum gro_result { diff --git a/net/core/dev.c b/net/core/dev.c index 6deba68..369dcc8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4902,6 +4902,12 @@ void __napi_complete(struct napi_struct *n) { BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state)); + /* Some drivers call us directly, instead of calling + * napi_complete_done(). + */ + if (unlikely(test_bit(NAPI_STATE_IN_BUSY_POLL, &n->state))) + return; + list_del_init(&n->poll_list); smp_mb__before_atomic(); clear_bit(NAPI_STATE_SCHED, &n->state); @@ -4913,10 +4919,13 @@ void napi_complete_done(struct napi_struct *n, int work_done) unsigned long flags; /* - * don't let napi dequeue from the cpu poll list - * just in case its running on a different cpu + * 1) Don't let napi dequeue from the cpu poll list + * just in case its running on a different cpu. + * 2) If we are busy polling, do nothing here, we have + * the guarantee we will be called later. */ - if (unlikely(test_bit(NAPI_STATE_NPSVC, &n->state))) + if (unlikely(n->state & (NAPIF_STATE_NPSVC | + NAPIF_STATE_IN_BUSY_POLL))) return; if (n->gro_list) { @@ -4956,13 +4965,41 @@ static struct napi_struct *napi_by_id(unsigned int napi_id) } #if defined(CONFIG_NET_RX_BUSY_POLL) + #define BUSY_POLL_BUDGET 8 + +static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock) +{ + int rc; + + clear_bit(NAPI_STATE_IN_BUSY_POLL, &napi->state); + + local_bh_disable(); + + /* All we really want here is to re-enable device interrupts. + * Ideally, a new ndo_busy_poll_stop() could avoid another round. + */ + rc = napi->poll(napi, BUSY_POLL_BUDGET); + netpoll_poll_unlock(have_poll_lock); + if (rc == BUSY_POLL_BUDGET) + __napi_schedule(napi); + local_bh_enable(); + if (local_softirq_pending()) + do_softirq(); +} + bool sk_busy_loop(struct sock *sk, int nonblock) { unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0; + int (*napi_poll)(struct napi_struct *napi, int budget); int (*busy_poll)(struct napi_struct *dev); + void *have_poll_lock = NULL; struct napi_struct *napi; - int rc = false; + int rc; + +restart: + rc = false; + napi_poll = NULL; rcu_read_lock(); @@ -4973,24 +5010,33 @@ bool sk_busy_loop(struct sock *sk, int nonblock) /* Note: ndo_busy_poll method is optional in linux-4.5 */ busy_poll = napi->dev->netdev_ops->ndo_busy_poll; - do { + preempt_disable(); + for (;;) { rc = 0; local_bh_disable(); if (busy_poll) { rc = busy_poll(napi); - } else if (napi_schedule_prep(napi)) { - void *have = netpoll_poll_lock(napi); - - if (test_bit(NAPI_STATE_SCHED, &napi->state)) { - rc = napi->poll(napi, BUSY_POLL_BUDGET); - trace_napi_poll(napi, rc, BUSY_POLL_BUDGET); - if (rc == BUSY_POLL_BUDGET) { - napi_complete_done(napi, rc); - napi_schedule(napi); - } - } - netpoll_poll_unlock(have); + goto count; } + if (!napi_poll) { + unsigned long val = READ_ONCE(napi->state); + + /* If multiple threads are competing for this napi, + * we avoid dirtying napi->state as much as we can. + */ + if (val & (NAPIF_STATE_DISABLE | NAPIF_STATE_SCHED | + NAPIF_STATE_IN_BUSY_POLL)) + goto count; + if (cmpxchg(&napi->state, val, + val | NAPIF_STATE_IN_BUSY_POLL | + NAPIF_STATE_SCHED) != val) + goto count; + have_poll_lock = netpoll_poll_lock(napi); + napi_poll = napi->poll; + } + rc = napi_poll(napi, BUSY_POLL_BUDGET); + trace_napi_poll(napi, rc, BUSY_POLL_BUDGET); +count: if (rc > 0) __NET_ADD_STATS(sock_net(sk), LINUX_MIB_BUSYPOLLRXPACKETS, rc); @@ -4999,10 +5045,26 @@ bool sk_busy_loop(struct sock *sk, int nonblock) if (rc == LL_FLUSH_FAILED) break; /* permanent failure */ - cpu_relax(); - } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) && - !need_resched() && !busy_loop_timeout(end_time)); + if (nonblock || !skb_queue_empty(&sk->sk_receive_queue) || + busy_loop_timeout(end_time)) + break; + if (unlikely(need_resched())) { + if (napi_poll) + busy_poll_stop(napi, have_poll_lock); + preempt_enable(); + rcu_read_unlock(); + cond_resched(); + rc = !skb_queue_empty(&sk->sk_receive_queue); + if (rc || busy_loop_timeout(end_time)) + return rc; + goto restart; + } + cpu_relax_lowlatency(); + } + if (napi_poll) + busy_poll_stop(napi, have_poll_lock); + preempt_enable(); rc = !skb_queue_empty(&sk->sk_receive_queue); out: rcu_read_unlock(); -- cgit v1.1 From 21cb84c48ca0619181106f0f44f3802a989de024 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2016 10:15:12 -0800 Subject: net: busy-poll: remove need_resched() from sk_can_busy_loop() Now sk_busy_loop() can schedule by itself, we can remove need_resched() check from sk_can_busy_loop() Also add a const to its struct sock parameter. Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Adam Belay Cc: Tariq Toukan Cc: Yuval Mintz Cc: Ariel Elior Signed-off-by: David S. Miller --- include/net/busy_poll.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index 2fbeb13..965e52b 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -58,10 +58,9 @@ static inline unsigned long busy_loop_end_time(void) return busy_loop_us_clock() + ACCESS_ONCE(sysctl_net_busy_poll); } -static inline bool sk_can_busy_loop(struct sock *sk) +static inline bool sk_can_busy_loop(const struct sock *sk) { - return sk->sk_ll_usec && sk->sk_napi_id && - !need_resched() && !signal_pending(current); + return sk->sk_ll_usec && sk->sk_napi_id && !signal_pending(current); } -- cgit v1.1 From 364b6055738b4c752c30ccaaf25c624e69d76195 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2016 10:15:13 -0800 Subject: net: busy-poll: return busypolling status to drivers NAPI drivers use napi_complete_done() or napi_complete() when they drained RX ring and right before re-enabling device interrupts. In busy polling, we can avoid interrupts being delivered since we are polling RX ring in a controlled loop. Drivers can chose to use napi_complete_done() return value to reduce interrupts overhead while busy polling is active. This is optional, legacy drivers should work fine even if not updated. Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Adam Belay Cc: Tariq Toukan Cc: Yuval Mintz Cc: Ariel Elior Signed-off-by: David S. Miller --- include/linux/netdevice.h | 7 ++++--- net/core/dev.c | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e71de66..bcddf95 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -463,16 +463,17 @@ static inline bool napi_reschedule(struct napi_struct *napi) return false; } -void __napi_complete(struct napi_struct *n); -void napi_complete_done(struct napi_struct *n, int work_done); +bool __napi_complete(struct napi_struct *n); +bool napi_complete_done(struct napi_struct *n, int work_done); /** * napi_complete - NAPI processing complete * @n: NAPI context * * Mark NAPI processing as complete. * Consider using napi_complete_done() instead. + * Return false if device should avoid rearming interrupts. */ -static inline void napi_complete(struct napi_struct *n) +static inline bool napi_complete(struct napi_struct *n) { return napi_complete_done(n, 0); } diff --git a/net/core/dev.c b/net/core/dev.c index 369dcc8..edba9ef 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4898,7 +4898,7 @@ void __napi_schedule_irqoff(struct napi_struct *n) } EXPORT_SYMBOL(__napi_schedule_irqoff); -void __napi_complete(struct napi_struct *n) +bool __napi_complete(struct napi_struct *n) { BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state)); @@ -4906,15 +4906,16 @@ void __napi_complete(struct napi_struct *n) * napi_complete_done(). */ if (unlikely(test_bit(NAPI_STATE_IN_BUSY_POLL, &n->state))) - return; + return false; list_del_init(&n->poll_list); smp_mb__before_atomic(); clear_bit(NAPI_STATE_SCHED, &n->state); + return true; } EXPORT_SYMBOL(__napi_complete); -void napi_complete_done(struct napi_struct *n, int work_done) +bool napi_complete_done(struct napi_struct *n, int work_done) { unsigned long flags; @@ -4926,7 +4927,7 @@ void napi_complete_done(struct napi_struct *n, int work_done) */ if (unlikely(n->state & (NAPIF_STATE_NPSVC | NAPIF_STATE_IN_BUSY_POLL))) - return; + return false; if (n->gro_list) { unsigned long timeout = 0; @@ -4948,6 +4949,7 @@ void napi_complete_done(struct napi_struct *n, int work_done) __napi_complete(n); local_irq_restore(flags); } + return true; } EXPORT_SYMBOL(napi_complete_done); -- cgit v1.1 From 2e713283751f494596655d9125c168aeb913f71d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2016 10:15:14 -0800 Subject: net/mlx4_en: use napi_complete_done() return value Do not rearm interrupts if we are busy polling. mlx4 uses separate CQ for TX and RX, so number of TX interrupts does not change, unfortunately. Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Adam Belay Cc: Tariq Toukan Cc: Yuval Mintz Cc: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 2cc9100..22f08f9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1137,8 +1137,8 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) done = 0; } /* Done for now */ - napi_complete_done(napi, done); - mlx4_en_arm_cq(priv, cq); + if (napi_complete_done(napi, done)) + mlx4_en_arm_cq(priv, cq); return done; } -- cgit v1.1 From 80f1c21c53dc5906944eac52d643f63e82ad1673 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2016 10:15:15 -0800 Subject: bnx2x: switch to napi_complete_done() Switch from napi_complete() to napi_complete_done() for better GRO support (gro_flush_timeout) and core NAPI features. Do not rearm interrupts if we are busy polling, to reduce bus and interrupts overhead. Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Adam Belay Cc: Tariq Toukan Cc: Yuval Mintz Cc: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index ed42c10..3fd36b4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3248,13 +3248,14 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) rmb(); if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) { - napi_complete(napi); - /* Re-enable interrupts */ - DP(NETIF_MSG_RX_STATUS, - "Update index to %d\n", fp->fp_hc_idx); - bnx2x_ack_sb(bp, fp->igu_sb_id, USTORM_ID, - le16_to_cpu(fp->fp_hc_idx), - IGU_INT_ENABLE, 1); + if (napi_complete_done(napi, rx_work_done)) { + /* Re-enable interrupts */ + DP(NETIF_MSG_RX_STATUS, + "Update index to %d\n", fp->fp_hc_idx); + bnx2x_ack_sb(bp, fp->igu_sb_id, USTORM_ID, + le16_to_cpu(fp->fp_hc_idx), + IGU_INT_ENABLE, 1); + } } else { rx_work_done = budget; } -- cgit v1.1 From cab247e9dfae4da574b2b2a5ff22da9c3a94f1d7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 11:19:45 -0800 Subject: net: stmmac: Implement ethtool::nway_reset Utilize the generic phy_ethtool_nway_reset() helper function to implement an autonegotiation restart. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 3fe9340..0290d52 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -870,6 +870,7 @@ static const struct ethtool_ops stmmac_ethtool_ops = { .get_regs = stmmac_ethtool_gregs, .get_regs_len = stmmac_ethtool_get_regs_len, .get_link = ethtool_op_get_link, + .nway_reset = phy_ethtool_nway_reset, .get_pauseparam = stmmac_get_pauseparam, .set_pauseparam = stmmac_set_pauseparam, .get_ethtool_stats = stmmac_get_ethtool_stats, -- cgit v1.1 From 3d3ba5685eed01058b4aa1d9c7c1d338100cc028 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 11:19:46 -0800 Subject: net: ethoc: Implement ethtool::nway_reset Implement ethtool::nway_reset using phy_ethtool_nway_reset. We are already using dev->phydev all over the place so this comes for free. Signed-off-by: Florian Fainelli Reviewed-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/ethoc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index c044667..6456c18 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -966,6 +966,7 @@ static int ethoc_set_ringparam(struct net_device *dev, const struct ethtool_ops ethoc_ethtool_ops = { .get_regs_len = ethoc_get_regs_len, .get_regs = ethoc_get_regs, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_ringparam = ethoc_get_ringparam, .set_ringparam = ethoc_set_ringparam, -- cgit v1.1 From 5489ee897685d4bfa8de13b4e13c6da7da2d8607 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 11:19:47 -0800 Subject: net: ethernet: mvneta: Implement ethtool::nway_reset Implement ethtool::nway_reset using phy_ethtool_nway_reset. We are already using dev->phydev all over the place so this comes for free. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index b85819e..87274d4 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3811,6 +3811,7 @@ static const struct net_device_ops mvneta_netdev_ops = { }; const struct ethtool_ops mvneta_eth_tool_ops = { + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .set_coalesce = mvneta_ethtool_set_coalesce, .get_coalesce = mvneta_ethtool_get_coalesce, -- cgit v1.1 From 00606c498664a8575afc6c1835de1e3cf582445f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 11:19:48 -0800 Subject: net: ethernet: mvpp2: Implement ethtool::nway_reset Implement ethtool::nway_reset using phy_ethtool_nway_reset. We are already using dev->phydev all over the place so this comes for free. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index c8bf155..ee85786 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5923,6 +5923,7 @@ static const struct net_device_ops mvpp2_netdev_ops = { }; static const struct ethtool_ops mvpp2_eth_tool_ops = { + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .set_coalesce = mvpp2_ethtool_set_coalesce, .get_coalesce = mvpp2_ethtool_get_coalesce, -- cgit v1.1 From 13f0ac41095587a23176f169b7f4557418017ade Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Nov 2016 11:19:49 -0800 Subject: net: ethernet: marvell: pxa168_eth: Implement ethtool::nway_reset Implement ethtool::nway_reset using phy_ethtool_nway_reset. We are already using dev->phydev all over the place so this comes for free. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/pxa168_eth.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index b78a838..3af2814 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1393,6 +1393,7 @@ static void pxa168_get_drvinfo(struct net_device *dev, static const struct ethtool_ops pxa168_ethtool_ops = { .get_drvinfo = pxa168_get_drvinfo, + .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_ts_info = ethtool_op_get_ts_info, .get_link_ksettings = pxa168_get_link_ksettings, -- cgit v1.1 From 5e4cbaa7fb69609737c57b42378e91a82e2960e0 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Tue, 15 Nov 2016 16:11:05 -0600 Subject: amd-xgbe: Fix possible uninitialized variable The debugfs support in the driver uses a common routine to write the debugfs values. In this routine, if the input file position is non-zero then the write routine will not return an error and an output parameter will not have been set. Because an error isn't returned an uninitialized value will be written into a register. Fix the common write routine to return an error if the input file position is non-zero, which will propagate back to the caller. Reported-by: Dan Carpenter Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 0c0140d..7546b66 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -153,7 +153,7 @@ static ssize_t xgbe_common_write(const char __user *buffer, size_t count, int ret; if (*ppos != 0) - return 0; + return -EINVAL; if (count >= sizeof(workarea)) return -ENOSPC; -- cgit v1.1 From 1c1f619e451bdbf29c3be9ed11c77d488b56dfd9 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Tue, 15 Nov 2016 16:11:15 -0600 Subject: amd-xgbe: Fix maximum GPIO value check The GPIO support in the hardware allows for up to 16 GPIO pins, enumerated from 0 to 15. The driver uses the wrong value (16) to validate the GPIO pin range in the routines to set and clear the GPIO output pins. Update the code to use the correct value (15). Reported-by: Colin Ian King Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 30056e2..aaf0350 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1099,7 +1099,7 @@ static int xgbe_clr_gpio(struct xgbe_prv_data *pdata, unsigned int gpio) { unsigned int reg; - if (gpio > 16) + if (gpio > 15) return -EINVAL; reg = XGMAC_IOREAD(pdata, MAC_GPIOSR); @@ -1114,7 +1114,7 @@ static int xgbe_set_gpio(struct xgbe_prv_data *pdata, unsigned int gpio) { unsigned int reg; - if (gpio > 16) + if (gpio > 15) return -EINVAL; reg = XGMAC_IOREAD(pdata, MAC_GPIOSR); -- cgit v1.1 From 0b6e3d0322b39a5f3a974a0d3cb8677aeb8106a5 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 16 Nov 2016 04:26:48 +0100 Subject: net: dsa: mv88e6xxx: Respect SPEED_UNFORCED, don't set force bit The SPEED_UNFORCED indicates the MAC & PHY should perform auto-negotiation to determine a speed which works. If this is called for, don't set the force bit. If it is set, the MAC actually does 10Gbps, why the internal PHYs don't support. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index e4978f6..af4772d8 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -213,7 +213,7 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, reg &= ~PORT_PCS_CTRL_ALTSPEED; if (force_bit) { reg &= ~PORT_PCS_CTRL_FORCE_SPEED; - if (speed) + if (speed != SPEED_UNFORCED) ctrl |= PORT_PCS_CTRL_FORCE_SPEED; } reg |= ctrl; -- cgit v1.1 From d331d30360d7d10c4150a5a6452bc8d9aeb5784f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 16 Nov 2016 09:51:58 +0100 Subject: mlxsw: spectrum_router: Adjust placement of FIB abort warning The recent merge commit bb598c1b8c9b ("Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net") would cause the FIB abort warning to fire whenever we flush the FIB tables - either during module removal or actual abort. Move it back to its rightful location in the FIB abort function. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 164bd30..683f045 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1872,9 +1872,6 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) struct mlxsw_sp_vr *vr; int i; - if (mlxsw_sp->router.aborted) - return; - dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n"); for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { vr = &mlxsw_sp->router.vrs[i]; @@ -1899,6 +1896,9 @@ static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) { int err; + if (mlxsw_sp->router.aborted) + return; + dev_warn(mlxsw_sp->bus_info->dev, "FIB abort triggered. Note that FIB entries are no longer being offloaded to this device.\n"); mlxsw_sp_router_fib_flush(mlxsw_sp); mlxsw_sp->router.aborted = true; err = mlxsw_sp_router_set_abort_trap(mlxsw_sp); -- cgit v1.1 From a23a8f5bc17e6209eefadcd1cb3d3e28a2cdf530 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Wed, 16 Nov 2016 10:05:46 +0100 Subject: lwtunnel: subtract tunnel headroom from mtu on output redirect This patch changes the lwtunnel_headroom() function which is called in ipv4_mtu() and ip6_mtu(), to also return the correct headroom value when the lwtunnel state is OUTPUT_REDIRECT. This patch enables e.g. SR-IPv6 encapsulations to work without manually setting the route mtu. Acked-by: Roopa Prabhu Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/net/lwtunnel.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h index 82e76fe..d4c1c75 100644 --- a/include/net/lwtunnel.h +++ b/include/net/lwtunnel.h @@ -94,7 +94,8 @@ static inline bool lwtunnel_xmit_redirect(struct lwtunnel_state *lwtstate) static inline unsigned int lwtunnel_headroom(struct lwtunnel_state *lwtstate, unsigned int mtu) { - if (lwtunnel_xmit_redirect(lwtstate) && lwtstate->headroom < mtu) + if ((lwtunnel_xmit_redirect(lwtstate) || + lwtunnel_output_redirect(lwtstate)) && lwtstate->headroom < mtu) return lwtstate->headroom; return 0; -- cgit v1.1 From 973334a1d3bb4ee8b4e761e322f116f0403b26b5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 16 Nov 2016 06:01:47 -0800 Subject: sfc: remove napi_hash_del() call Calling napi_hash_del() after netif_napi_del() is pointless. Signed-off-by: Eric Dumazet Cc: Edward Cree Cc: Bert Kenward Acked-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 649bc50..52ca040 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -2113,10 +2113,9 @@ static void efx_init_napi(struct efx_nic *efx) static void efx_fini_napi_channel(struct efx_channel *channel) { - if (channel->napi_dev) { + if (channel->napi_dev) netif_napi_del(&channel->napi_str); - napi_hash_del(&channel->napi_str); - } + channel->napi_dev = NULL; } -- cgit v1.1 From 08348995c47ec0e6ad3712c08eb88fb4df2e8180 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 16 Nov 2016 15:10:49 +0100 Subject: netronome: don't access real_num_rx_queues directly The netdev->real_num_rx_queues setting is only available if CONFIG_SYSFS is enabled, so we now get a build failure when that is turned off: netronome/nfp/nfp_net_common.c: In function 'nfp_net_ring_swap_enable': netronome/nfp/nfp_net_common.c:2489:18: error: 'struct net_device' has no member named 'real_num_rx_queues'; did you mean 'real_num_tx_queues'? As far as I can tell, the check here is only used as an optimization that we can skip in order to fix the compilation. If sysfs is disabled, the following netif_set_real_num_rx_queues() has no effect. Fixes: 164d1e9e5d52 ("nfp: add support for ethtool .set_channels") Signed-off-by: Arnd Bergmann Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 99edb9f..eb37157 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2486,15 +2486,13 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, for (r = 0; r < nn->max_r_vecs; r++) nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); - if (nn->netdev->real_num_rx_queues != nn->num_rx_rings) { - if (!netif_is_rxfh_configured(nn->netdev)) - nfp_net_rss_init_itbl(nn); + if (!netif_is_rxfh_configured(nn->netdev)) + nfp_net_rss_init_itbl(nn); - err = netif_set_real_num_rx_queues(nn->netdev, - nn->num_rx_rings); - if (err) - return err; - } + err = netif_set_real_num_rx_queues(nn->netdev, + nn->num_rx_rings); + if (err) + return err; if (nn->netdev->real_num_tx_queues != nn->num_stack_tx_rings) { err = netif_set_real_num_tx_queues(nn->netdev, -- cgit v1.1 From 1629dd4f763cc15ac3b2711ac65dab153b738c6d Mon Sep 17 00:00:00 2001 From: Rafal Ozieblo Date: Wed, 16 Nov 2016 10:02:34 +0000 Subject: cadence: Add LSO support. New Cadence GEM hardware support Large Segment Offload (LSO): TCP segmentation offload (TSO) as well as UDP fragmentation offload (UFO). Support for those features was added to the driver. Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 138 ++++++++++++++++++++++++++++++++---- drivers/net/ethernet/cadence/macb.h | 14 ++++ 2 files changed, 140 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index e1847ce..654b5bf 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -32,7 +32,9 @@ #include #include #include - +#include +#include +#include #include "macb.h" #define MACB_RX_BUFFER_SIZE 128 @@ -60,10 +62,13 @@ | MACB_BIT(TXERR)) #define MACB_TX_INT_FLAGS (MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP)) -#define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1)) -#define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1)) +/* Max length of transmit frame must be a multiple of 8 bytes */ +#define MACB_TX_LEN_ALIGN 8 +#define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1))) +#define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1))) #define GEM_MTU_MIN_SIZE ETH_MIN_MTU +#define MACB_NETIF_LSO (NETIF_F_TSO | NETIF_F_UFO) #define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0) #define MACB_WOL_ENABLED (0x1 << 1) @@ -1223,7 +1228,8 @@ static void macb_poll_controller(struct net_device *dev) static unsigned int macb_tx_map(struct macb *bp, struct macb_queue *queue, - struct sk_buff *skb) + struct sk_buff *skb, + unsigned int hdrlen) { dma_addr_t mapping; unsigned int len, entry, i, tx_head = queue->tx_head; @@ -1231,14 +1237,27 @@ static unsigned int macb_tx_map(struct macb *bp, struct macb_dma_desc *desc; unsigned int offset, size, count = 0; unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; - unsigned int eof = 1; - u32 ctrl; + unsigned int eof = 1, mss_mfs = 0; + u32 ctrl, lso_ctrl = 0, seq_ctrl = 0; + + /* LSO */ + if (skb_shinfo(skb)->gso_size != 0) { + if (ip_hdr(skb)->protocol == IPPROTO_UDP) + /* UDP - UFO */ + lso_ctrl = MACB_LSO_UFO_ENABLE; + else + /* TCP - TSO */ + lso_ctrl = MACB_LSO_TSO_ENABLE; + } /* First, map non-paged data */ len = skb_headlen(skb); + + /* first buffer length */ + size = hdrlen; + offset = 0; while (len) { - size = min(len, bp->max_tx_length); entry = macb_tx_ring_wrap(bp, tx_head); tx_skb = &queue->tx_skb[entry]; @@ -1258,6 +1277,8 @@ static unsigned int macb_tx_map(struct macb *bp, offset += size; count++; tx_head++; + + size = min(len, bp->max_tx_length); } /* Then, map paged data from fragments */ @@ -1311,6 +1332,21 @@ static unsigned int macb_tx_map(struct macb *bp, desc = &queue->tx_ring[entry]; desc->ctrl = ctrl; + if (lso_ctrl) { + if (lso_ctrl == MACB_LSO_UFO_ENABLE) + /* include header and FCS in value given to h/w */ + mss_mfs = skb_shinfo(skb)->gso_size + + skb_transport_offset(skb) + + ETH_FCS_LEN; + else /* TSO */ { + mss_mfs = skb_shinfo(skb)->gso_size; + /* TCP Sequence Number Source Select + * can be set only for TSO + */ + seq_ctrl = 0; + } + } + do { i--; entry = macb_tx_ring_wrap(bp, i); @@ -1325,6 +1361,16 @@ static unsigned int macb_tx_map(struct macb *bp, if (unlikely(entry == (bp->tx_ring_size - 1))) ctrl |= MACB_BIT(TX_WRAP); + /* First descriptor is header descriptor */ + if (i == queue->tx_head) { + ctrl |= MACB_BF(TX_LSO, lso_ctrl); + ctrl |= MACB_BF(TX_TCP_SEQ_SRC, seq_ctrl); + } else + /* Only set MSS/MFS on payload descriptors + * (second or later descriptor) + */ + ctrl |= MACB_BF(MSS_MFS, mss_mfs); + /* Set TX buffer descriptor */ macb_set_addr(desc, tx_skb->mapping); /* desc->addr must be visible to hardware before clearing @@ -1350,6 +1396,43 @@ dma_error: return 0; } +static netdev_features_t macb_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + unsigned int nr_frags, f; + unsigned int hdrlen; + + /* Validate LSO compatibility */ + + /* there is only one buffer */ + if (!skb_is_nonlinear(skb)) + return features; + + /* length of header */ + hdrlen = skb_transport_offset(skb); + if (ip_hdr(skb)->protocol == IPPROTO_TCP) + hdrlen += tcp_hdrlen(skb); + + /* For LSO: + * When software supplies two or more payload buffers all payload buffers + * apart from the last must be a multiple of 8 bytes in size. + */ + if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, MACB_TX_LEN_ALIGN)) + return features & ~MACB_NETIF_LSO; + + nr_frags = skb_shinfo(skb)->nr_frags; + /* No need to check last fragment */ + nr_frags--; + for (f = 0; f < nr_frags; f++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + + if (!IS_ALIGNED(skb_frag_size(frag), MACB_TX_LEN_ALIGN)) + return features & ~MACB_NETIF_LSO; + } + return features; +} + static inline int macb_clear_csum(struct sk_buff *skb) { /* no change for packets without checksum offloading */ @@ -1374,7 +1457,28 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) struct macb *bp = netdev_priv(dev); struct macb_queue *queue = &bp->queues[queue_index]; unsigned long flags; - unsigned int count, nr_frags, frag_size, f; + unsigned int desc_cnt, nr_frags, frag_size, f; + unsigned int hdrlen; + bool is_lso, is_udp = 0; + + is_lso = (skb_shinfo(skb)->gso_size != 0); + + if (is_lso) { + is_udp = !!(ip_hdr(skb)->protocol == IPPROTO_UDP); + + /* length of headers */ + if (is_udp) + /* only queue eth + ip headers separately for UDP */ + hdrlen = skb_transport_offset(skb); + else + hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + if (skb_headlen(skb) < hdrlen) { + netdev_err(bp->dev, "Error - LSO headers fragmented!!!\n"); + /* if this is required, would need to copy to single buffer */ + return NETDEV_TX_BUSY; + } + } else + hdrlen = min(skb_headlen(skb), bp->max_tx_length); #if defined(DEBUG) && defined(VERBOSE_DEBUG) netdev_vdbg(bp->dev, @@ -1389,18 +1493,22 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) * socket buffer: skb fragments of jumbo frames may need to be * split into many buffer descriptors. */ - count = DIV_ROUND_UP(skb_headlen(skb), bp->max_tx_length); + if (is_lso && (skb_headlen(skb) > hdrlen)) + /* extra header descriptor if also payload in first buffer */ + desc_cnt = DIV_ROUND_UP((skb_headlen(skb) - hdrlen), bp->max_tx_length) + 1; + else + desc_cnt = DIV_ROUND_UP(skb_headlen(skb), bp->max_tx_length); nr_frags = skb_shinfo(skb)->nr_frags; for (f = 0; f < nr_frags; f++) { frag_size = skb_frag_size(&skb_shinfo(skb)->frags[f]); - count += DIV_ROUND_UP(frag_size, bp->max_tx_length); + desc_cnt += DIV_ROUND_UP(frag_size, bp->max_tx_length); } spin_lock_irqsave(&bp->lock, flags); /* This is a hard error, log it. */ if (CIRC_SPACE(queue->tx_head, queue->tx_tail, - bp->tx_ring_size) < count) { + bp->tx_ring_size) < desc_cnt) { netif_stop_subqueue(dev, queue_index); spin_unlock_irqrestore(&bp->lock, flags); netdev_dbg(bp->dev, "tx_head = %u, tx_tail = %u\n", @@ -1414,7 +1522,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) } /* Map socket buffer for DMA transfer */ - if (!macb_tx_map(bp, queue, skb)) { + if (!macb_tx_map(bp, queue, skb, hdrlen)) { dev_kfree_skb_any(skb); goto unlock; } @@ -2354,6 +2462,7 @@ static const struct net_device_ops macb_netdev_ops = { .ndo_poll_controller = macb_poll_controller, #endif .ndo_set_features = macb_set_features, + .ndo_features_check = macb_features_check, }; /* Configure peripheral capabilities according to device tree @@ -2560,6 +2669,11 @@ static int macb_init(struct platform_device *pdev) /* Set features */ dev->hw_features = NETIF_F_SG; + + /* Check LSO capability */ + if (GEM_BFEXT(PBUF_LSO, gem_readl(bp, DCFG6))) + dev->hw_features |= MACB_NETIF_LSO; + /* Checksum offload is only available on gem with packet buffer */ if (macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE)) dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 1216950..d67adad 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -382,6 +382,10 @@ #define GEM_TX_PKT_BUFF_OFFSET 21 #define GEM_TX_PKT_BUFF_SIZE 1 +/* Bitfields in DCFG6. */ +#define GEM_PBUF_LSO_OFFSET 27 +#define GEM_PBUF_LSO_SIZE 1 + /* Constants for CLK */ #define MACB_CLK_DIV8 0 #define MACB_CLK_DIV16 1 @@ -414,6 +418,10 @@ #define MACB_CAPS_SG_DISABLED 0x40000000 #define MACB_CAPS_MACB_IS_GEM 0x80000000 +/* LSO settings */ +#define MACB_LSO_UFO_ENABLE 0x01 +#define MACB_LSO_TSO_ENABLE 0x02 + /* Bit manipulation macros */ #define MACB_BIT(name) \ (1 << MACB_##name##_OFFSET) @@ -545,6 +553,12 @@ struct macb_dma_desc { #define MACB_TX_LAST_SIZE 1 #define MACB_TX_NOCRC_OFFSET 16 #define MACB_TX_NOCRC_SIZE 1 +#define MACB_MSS_MFS_OFFSET 16 +#define MACB_MSS_MFS_SIZE 14 +#define MACB_TX_LSO_OFFSET 17 +#define MACB_TX_LSO_SIZE 2 +#define MACB_TX_TCP_SEQ_SRC_OFFSET 19 +#define MACB_TX_TCP_SEQ_SRC_SIZE 1 #define MACB_TX_BUF_EXHAUSTED_OFFSET 27 #define MACB_TX_BUF_EXHAUSTED_SIZE 1 #define MACB_TX_UNDERRUN_OFFSET 28 -- cgit v1.1 From 89c4b442b78bdba388337cc746fe63caba85f46c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 16 Nov 2016 14:54:50 -0800 Subject: netpoll: more efficient locking Callers of netpoll_poll_lock() own NAPI_STATE_SCHED Callers of netpoll_poll_unlock() have BH blocked between the NAPI_STATE_SCHED being cleared and poll_lock is released. We can avoid the spinlock which has no contention, and use cmpxchg() on poll_owner which we need to set anyway. This removes a possible lockdep violation after the cited commit, since sk_busy_loop() re-enables BH before calling busy_poll_stop() Fixes: 217f69743681 ("net: busy-poll: allow preemption in sk_busy_loop()") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - include/linux/netpoll.h | 13 +++++++------ net/core/dev.c | 1 - net/core/netpoll.c | 6 +++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bcddf95..e84800e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -316,7 +316,6 @@ struct napi_struct { unsigned int gro_count; int (*poll)(struct napi_struct *, int); #ifdef CONFIG_NETPOLL - spinlock_t poll_lock; int poll_owner; #endif struct net_device *dev; diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index b25ee9f..1828900 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -78,8 +78,11 @@ static inline void *netpoll_poll_lock(struct napi_struct *napi) struct net_device *dev = napi->dev; if (dev && dev->npinfo) { - spin_lock(&napi->poll_lock); - napi->poll_owner = smp_processor_id(); + int owner = smp_processor_id(); + + while (cmpxchg(&napi->poll_owner, -1, owner) != -1) + cpu_relax(); + return napi; } return NULL; @@ -89,10 +92,8 @@ static inline void netpoll_poll_unlock(void *have) { struct napi_struct *napi = have; - if (napi) { - napi->poll_owner = -1; - spin_unlock(&napi->poll_lock); - } + if (napi) + smp_store_release(&napi->poll_owner, -1); } static inline bool netpoll_tx_running(struct net_device *dev) diff --git a/net/core/dev.c b/net/core/dev.c index edba9ef..f71b34a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5143,7 +5143,6 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi, list_add(&napi->dev_list, &dev->napi_list); napi->dev = dev; #ifdef CONFIG_NETPOLL - spin_lock_init(&napi->poll_lock); napi->poll_owner = -1; #endif set_bit(NAPI_STATE_SCHED, &napi->state); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 53599bd..9424673 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -171,12 +171,12 @@ static void poll_one_napi(struct napi_struct *napi) static void poll_napi(struct net_device *dev) { struct napi_struct *napi; + int cpu = smp_processor_id(); list_for_each_entry(napi, &dev->napi_list, dev_list) { - if (napi->poll_owner != smp_processor_id() && - spin_trylock(&napi->poll_lock)) { + if (cmpxchg(&napi->poll_owner, -1, cpu) == -1) { poll_one_napi(napi); - spin_unlock(&napi->poll_lock); + smp_store_release(&napi->poll_owner, -1); } } } -- cgit v1.1 From 16d663a69f4a1f3534e780e35d50142b98cf1279 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 16 Nov 2016 21:13:07 -0500 Subject: bnxt_en: Update firmware interface spec to 1.5.4. Use the new FORCE_LINK_DWN bit to shutdown link during close. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 ++- drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 91 +++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 27a2dd9..1332577 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5346,7 +5346,7 @@ static int bnxt_hwrm_shutdown_link(struct bnxt *bp) return 0; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_CFG, -1, -1); - req.flags = cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DOWN); + req.flags = cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DWN); return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); } @@ -5409,6 +5409,12 @@ static int bnxt_update_phy_setting(struct bnxt *bp) update_link = true; } + /* The last close may have shutdown the link, so need to call + * PHY_CFG to bring it back up. + */ + if (!netif_carrier_ok(bp->dev)) + update_link = true; + if (!bnxt_eee_config_ok(bp)) update_eee = true; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 04a96cc..0456d5b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -215,6 +215,9 @@ struct hwrm_async_event_cmpl_dcb_config_change { __le16 event_id; #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_ID_DCB_CONFIG_CHANGE 0x3UL __le32 event_data2; + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_ETS 0x1UL + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_PFC 0x2UL + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_APP 0x4UL u8 opaque_v; #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_MASK 0xfeUL @@ -224,6 +227,14 @@ struct hwrm_async_event_cmpl_dcb_config_change { __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0 + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_MASK 0xff0000UL + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_SFT 16 + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_NONE (0xffUL << 16) + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_LAST HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_NONE + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_MASK 0xff000000UL + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_SFT 24 + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_NONE (0xffUL << 24) + #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_LAST HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_NONE }; /* HWRM Asynchronous Event Completion Record for port connection not allowed (16 bytes) */ @@ -485,12 +496,12 @@ struct hwrm_async_event_cmpl_hwrm_error { #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA1_TIMESTAMP 0x1UL }; -/* HW Resource Manager Specification 1.5.1 */ +/* HW Resource Manager Specification 1.5.4 */ #define HWRM_VERSION_MAJOR 1 #define HWRM_VERSION_MINOR 5 -#define HWRM_VERSION_UPDATE 1 +#define HWRM_VERSION_UPDATE 4 -#define HWRM_VERSION_STR "1.5.1" +#define HWRM_VERSION_STR "1.5.4" /* * Following is the signature for HWRM message field that indicates not * applicable (All F's). Need to cast it the size of the field if needed. @@ -612,6 +623,9 @@ struct cmd_nums { #define HWRM_FW_QSTATUS (0xc1UL) #define HWRM_FW_SET_TIME (0xc8UL) #define HWRM_FW_GET_TIME (0xc9UL) + #define HWRM_FW_SET_STRUCTURED_DATA (0xcaUL) + #define HWRM_FW_GET_STRUCTURED_DATA (0xcbUL) + #define HWRM_FW_IPC_MAILBOX (0xccUL) #define HWRM_EXEC_FWD_RESP (0xd0UL) #define HWRM_REJECT_FWD_RESP (0xd1UL) #define HWRM_FWD_RESP (0xd2UL) @@ -626,6 +640,8 @@ struct cmd_nums { #define HWRM_DBG_WRITE_DIRECT (0xff12UL) #define HWRM_DBG_WRITE_INDIRECT (0xff13UL) #define HWRM_DBG_DUMP (0xff14UL) + #define HWRM_NVM_GET_VARIABLE (0xfff1UL) + #define HWRM_NVM_SET_VARIABLE (0xfff2UL) #define HWRM_NVM_INSTALL_UPDATE (0xfff3UL) #define HWRM_NVM_MODIFY (0xfff4UL) #define HWRM_NVM_VERIFY_UPDATE (0xfff5UL) @@ -1399,6 +1415,7 @@ struct hwrm_func_drv_rgtr_input { #define FUNC_DRV_RGTR_REQ_OS_TYPE_ESXI 0x68UL #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN864 0x73UL #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN2012R2 0x74UL + #define FUNC_DRV_RGTR_REQ_OS_TYPE_UEFI 0x8000UL u8 ver_maj; u8 ver_min; u8 ver_upd; @@ -1549,7 +1566,7 @@ struct hwrm_port_phy_cfg_input { __le64 resp_addr; __le32 flags; #define PORT_PHY_CFG_REQ_FLAGS_RESET_PHY 0x1UL - #define PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DOWN 0x2UL + #define PORT_PHY_CFG_REQ_FLAGS_DEPRECATED 0x2UL #define PORT_PHY_CFG_REQ_FLAGS_FORCE 0x4UL #define PORT_PHY_CFG_REQ_FLAGS_RESTART_AUTONEG 0x8UL #define PORT_PHY_CFG_REQ_FLAGS_EEE_ENABLE 0x10UL @@ -1562,6 +1579,7 @@ struct hwrm_port_phy_cfg_input { #define PORT_PHY_CFG_REQ_FLAGS_FEC_CLAUSE74_DISABLE 0x800UL #define PORT_PHY_CFG_REQ_FLAGS_FEC_CLAUSE91_ENABLE 0x1000UL #define PORT_PHY_CFG_REQ_FLAGS_FEC_CLAUSE91_DISABLE 0x2000UL + #define PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DWN 0x4000UL __le32 enables; #define PORT_PHY_CFG_REQ_ENABLES_AUTO_MODE 0x1UL #define PORT_PHY_CFG_REQ_ENABLES_AUTO_DUPLEX 0x2UL @@ -4023,6 +4041,71 @@ struct hwrm_fw_set_time_output { u8 valid; }; +/* hwrm_fw_set_structured_data */ +/* Input (32 bytes) */ +struct hwrm_fw_set_structured_data_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 src_data_addr; + __le16 data_len; + u8 hdr_cnt; + u8 unused_0[5]; +}; + +/* Output (16 bytes) */ +struct hwrm_fw_set_structured_data_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_fw_get_structured_data */ +/* Input (32 bytes) */ +struct hwrm_fw_get_structured_data_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 dest_data_addr; + __le16 data_len; + __le16 structure_id; + __le16 subtype; + #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_ALL 0xffffUL + #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_NEAR_BRIDGE_ADMIN 0x100UL + #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_NEAR_BRIDGE_PEER 0x101UL + #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_NEAR_BRIDGE_OPERATIONAL 0x102UL + #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_NON_TPMR_ADMIN 0x200UL + #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_NON_TPMR_PEER 0x201UL + #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_NON_TPMR_OPERATIONAL 0x202UL + u8 count; + u8 unused_0; +}; + +/* Output (16 bytes) */ +struct hwrm_fw_get_structured_data_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 hdr_cnt; + u8 unused_0; + __le16 unused_1; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 valid; +}; + /* hwrm_exec_fwd_resp */ /* Input (128 bytes) */ struct hwrm_exec_fwd_resp_input { -- cgit v1.1 From 286ef9d64ea7435a1e323d12b44a309e15cbff0e Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 16 Nov 2016 21:13:08 -0500 Subject: bnxt_en: Enhance autoneg support. On some dual port NICs, the speed setting on one port can affect the available speed on the other port. Add logic to detect these changes and adjust the advertised speed settings when necessary. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 23 +++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 24 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 1332577..4db50e8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1499,6 +1499,7 @@ static int bnxt_async_event_process(struct bnxt *bp, netdev_warn(bp->dev, "Link speed %d no longer supported\n", speed); } + set_bit(BNXT_LINK_SPEED_CHNG_SP_EVENT, &bp->sp_event); /* fall thru */ } case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE: @@ -5095,6 +5096,7 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state) struct hwrm_port_phy_qcfg_input req = {0}; struct hwrm_port_phy_qcfg_output *resp = bp->hwrm_cmd_resp_addr; u8 link_up = link_info->link_up; + u16 diff; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_QCFG, -1, -1); @@ -5182,6 +5184,23 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state) link_info->link_up = 0; } mutex_unlock(&bp->hwrm_cmd_lock); + + diff = link_info->support_auto_speeds ^ link_info->advertising; + if ((link_info->support_auto_speeds | diff) != + link_info->support_auto_speeds) { + /* An advertised speed is no longer supported, so we need to + * update the advertisement settings. See bnxt_reset() for + * comments about the rtnl_lock() sequence below. + */ + clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state); + rtnl_lock(); + link_info->advertising = link_info->support_auto_speeds; + if (test_bit(BNXT_STATE_OPEN, &bp->state) && + (link_info->autoneg & BNXT_AUTONEG_SPEED)) + bnxt_hwrm_set_link_setting(bp, true, false); + set_bit(BNXT_STATE_IN_SP_TASK, &bp->state); + rtnl_unlock(); + } return 0; } @@ -6108,6 +6127,10 @@ static void bnxt_sp_task(struct work_struct *work) if (test_and_clear_bit(BNXT_RX_NTP_FLTR_SP_EVENT, &bp->sp_event)) bnxt_cfg_ntp_filters(bp); if (test_and_clear_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event)) { + if (test_and_clear_bit(BNXT_LINK_SPEED_CHNG_SP_EVENT, + &bp->sp_event)) + bnxt_hwrm_phy_qcaps(bp); + rc = bnxt_update_link(bp, true); if (rc) netdev_err(bp->dev, "SP task can't update link (rc: %x)\n", diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 51b164a..666bc06 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1089,6 +1089,7 @@ struct bnxt { #define BNXT_RESET_TASK_SILENT_SP_EVENT 11 #define BNXT_GENEVE_ADD_PORT_SP_EVENT 12 #define BNXT_GENEVE_DEL_PORT_SP_EVENT 13 +#define BNXT_LINK_SPEED_CHNG_SP_EVENT 14 struct bnxt_pf_info pf; #ifdef CONFIG_BNXT_SRIOV -- cgit v1.1 From 87da7f796d5e44311ea69afb6f4220d43a89382e Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 16 Nov 2016 21:13:09 -0500 Subject: bnxt_en: Add UDP RSS support for 57X1X chips. The newer chips have proper support for 4-tuple UDP RSS. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 21 ++++++++++++++------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 ++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4db50e8..7401c90 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -3425,13 +3425,7 @@ static int bnxt_hwrm_vnic_set_rss(struct bnxt *bp, u16 vnic_id, bool set_rss) bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_VNIC_RSS_CFG, -1, -1); if (set_rss) { - vnic->hash_type = VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4 | - VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4 | - VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 | - VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6; - - req.hash_type = cpu_to_le32(vnic->hash_type); - + req.hash_type = cpu_to_le32(bp->rss_hash_cfg); if (vnic->flags & BNXT_VNIC_RSS_FLAG) { if (BNXT_CHIP_TYPE_NITRO_A0(bp)) max_rings = bp->rx_nr_rings - 1; @@ -6940,6 +6934,19 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #endif bnxt_set_dflt_rings(bp); + /* Default RSS hash cfg. */ + bp->rss_hash_cfg = VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4 | + VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4 | + VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 | + VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6; + if (!BNXT_CHIP_NUM_57X0X(bp->chip_num) && + !BNXT_CHIP_TYPE_NITRO_A0(bp) && + bp->hwrm_spec_code >= 0x10501) { + bp->flags |= BNXT_FLAG_UDP_RSS_CAP; + bp->rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4 | + VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6; + } + if (BNXT_PF(bp) && !BNXT_CHIP_TYPE_NITRO_A0(bp)) { dev->hw_features |= NETIF_F_NTUPLE; if (bnxt_rfs_capable(bp)) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 666bc06..47be789 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -700,7 +700,6 @@ struct bnxt_vnic_info { u8 *uc_list; u16 *fw_grp_ids; - u16 hash_type; dma_addr_t rss_table_dma_addr; __le16 *rss_table; dma_addr_t rss_hash_key_dma_addr; @@ -952,6 +951,7 @@ struct bnxt { #define BNXT_FLAG_RFS 0x100 #define BNXT_FLAG_SHARED_RINGS 0x200 #define BNXT_FLAG_PORT_STATS 0x400 + #define BNXT_FLAG_UDP_RSS_CAP 0x800 #define BNXT_FLAG_EEE_CAP 0x1000 #define BNXT_FLAG_CHIP_NITRO_A0 0x1000000 @@ -1007,6 +1007,7 @@ struct bnxt { struct bnxt_ring_grp_info *grp_info; struct bnxt_vnic_info *vnic_info; int nr_vnics; + u32 rss_hash_cfg; u8 max_tc; struct bnxt_queue_info q_info[BNXT_MAX_QUEUE]; -- cgit v1.1 From a011952a1a465258ab006a8613a41aa5367d2274 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 16 Nov 2016 21:13:10 -0500 Subject: bnxt_en: Add ethtool -n|-N rx-flow-hash support. To display and modify the RSS hash. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 167 +++++++++++++++++++++- 1 file changed, 164 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index a7e04ff..fa6125e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -542,6 +542,146 @@ fltr_err: return rc; } +#endif + +static u64 get_ethtool_ipv4_rss(struct bnxt *bp) +{ + if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4) + return RXH_IP_SRC | RXH_IP_DST; + return 0; +} + +static u64 get_ethtool_ipv6_rss(struct bnxt *bp) +{ + if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6) + return RXH_IP_SRC | RXH_IP_DST; + return 0; +} + +static int bnxt_grxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) +{ + cmd->data = 0; + switch (cmd->flow_type) { + case TCP_V4_FLOW: + if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4) + cmd->data |= RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + cmd->data |= get_ethtool_ipv4_rss(bp); + break; + case UDP_V4_FLOW: + if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4) + cmd->data |= RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* fall through */ + case SCTP_V4_FLOW: + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case IPV4_FLOW: + cmd->data |= get_ethtool_ipv4_rss(bp); + break; + + case TCP_V6_FLOW: + if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6) + cmd->data |= RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + cmd->data |= get_ethtool_ipv6_rss(bp); + break; + case UDP_V6_FLOW: + if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6) + cmd->data |= RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* fall through */ + case SCTP_V6_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IPV6_FLOW: + cmd->data |= get_ethtool_ipv6_rss(bp); + break; + } + return 0; +} + +#define RXH_4TUPLE (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3) +#define RXH_2TUPLE (RXH_IP_SRC | RXH_IP_DST) + +static int bnxt_srxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) +{ + u32 rss_hash_cfg = bp->rss_hash_cfg; + int tuple, rc = 0; + + if (cmd->data == RXH_4TUPLE) + tuple = 4; + else if (cmd->data == RXH_2TUPLE) + tuple = 2; + else if (!cmd->data) + tuple = 0; + else + return -EINVAL; + + if (cmd->flow_type == TCP_V4_FLOW) { + rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4; + if (tuple == 4) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4; + } else if (cmd->flow_type == UDP_V4_FLOW) { + if (tuple == 4 && !(bp->flags & BNXT_FLAG_UDP_RSS_CAP)) + return -EINVAL; + rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4; + if (tuple == 4) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4; + } else if (cmd->flow_type == TCP_V6_FLOW) { + rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6; + if (tuple == 4) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6; + } else if (cmd->flow_type == UDP_V6_FLOW) { + if (tuple == 4 && !(bp->flags & BNXT_FLAG_UDP_RSS_CAP)) + return -EINVAL; + rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6; + if (tuple == 4) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6; + } else if (tuple == 4) { + return -EINVAL; + } + + switch (cmd->flow_type) { + case TCP_V4_FLOW: + case UDP_V4_FLOW: + case SCTP_V4_FLOW: + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case IPV4_FLOW: + if (tuple == 2) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4; + else if (!tuple) + rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4; + break; + + case TCP_V6_FLOW: + case UDP_V6_FLOW: + case SCTP_V6_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IPV6_FLOW: + if (tuple == 2) + rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6; + else if (!tuple) + rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6; + break; + } + + if (bp->rss_hash_cfg == rss_hash_cfg) + return 0; + + bp->rss_hash_cfg = rss_hash_cfg; + if (netif_running(bp->dev)) { + bnxt_close_nic(bp, false, false); + rc = bnxt_open_nic(bp, false, false); + } + return rc; +} static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, u32 *rule_locs) @@ -550,6 +690,7 @@ static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, int rc = 0; switch (cmd->cmd) { +#ifdef CONFIG_RFS_ACCEL case ETHTOOL_GRXRINGS: cmd->data = bp->rx_nr_rings; break; @@ -566,6 +707,11 @@ static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, case ETHTOOL_GRXCLSRULE: rc = bnxt_grxclsrule(bp, cmd); break; +#endif + + case ETHTOOL_GRXFH: + rc = bnxt_grxfh(bp, cmd); + break; default: rc = -EOPNOTSUPP; @@ -574,7 +720,23 @@ static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, return rc; } -#endif + +static int bnxt_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + struct bnxt *bp = netdev_priv(dev); + int rc; + + switch (cmd->cmd) { + case ETHTOOL_SRXFH: + rc = bnxt_srxfh(bp, cmd); + break; + + default: + rc = -EOPNOTSUPP; + break; + } + return rc; +} static u32 bnxt_get_rxfh_indir_size(struct net_device *dev) { @@ -1885,9 +2047,8 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_ringparam = bnxt_get_ringparam, .get_channels = bnxt_get_channels, .set_channels = bnxt_set_channels, -#ifdef CONFIG_RFS_ACCEL .get_rxnfc = bnxt_get_rxnfc, -#endif + .set_rxnfc = bnxt_set_rxnfc, .get_rxfh_indir_size = bnxt_get_rxfh_indir_size, .get_rxfh_key_size = bnxt_get_rxfh_key_size, .get_rxfh = bnxt_get_rxfh, -- cgit v1.1 From 7fda702f9315e6f4a74fee155c540750788a2d66 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 15 Nov 2016 23:23:11 +0800 Subject: sctp: use new rhlist interface on sctp transport rhashtable Now sctp transport rhashtable uses hash(lport, dport, daddr) as the key to hash a node to one chain. If in one host thousands of assocs connect to one server with the same lport and different laddrs (although it's not a normal case), all the transports would be hashed into the same chain. It may cause to keep returning -EBUSY when inserting a new node, as the chain is too long and sctp inserts a transport node in a loop, which could even lead to system hangs there. The new rhlist interface works for this case that there are many nodes with the same key in one chain. It puts them into a list then makes this list be as a node of the chain. This patch is to replace rhashtable_ interface with rhltable_ interface. Since a chain would not be too long and it would not return -EBUSY with this fix when inserting a node, the reinsert loop is also removed here. Signed-off-by: Xin Long Acked-by: Neil Horman Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 2 +- include/net/sctp/structs.h | 4 +- net/sctp/associola.c | 8 +++- net/sctp/input.c | 93 ++++++++++++++++++++++++++-------------------- net/sctp/socket.c | 7 +--- 5 files changed, 64 insertions(+), 50 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 31acc3f..f0dcaeb 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -164,7 +164,7 @@ void sctp_backlog_migrate(struct sctp_association *assoc, struct sock *oldsk, struct sock *newsk); int sctp_transport_hashtable_init(void); void sctp_transport_hashtable_destroy(void); -void sctp_hash_transport(struct sctp_transport *t); +int sctp_hash_transport(struct sctp_transport *t); void sctp_unhash_transport(struct sctp_transport *t); struct sctp_transport *sctp_addrs_lookup_transport( struct net *net, diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index bd4a3de..92daabd 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -124,7 +124,7 @@ extern struct sctp_globals { /* This is the sctp port control hash. */ struct sctp_bind_hashbucket *port_hashtable; /* This is the hash of all transports. */ - struct rhashtable transport_hashtable; + struct rhltable transport_hashtable; /* Sizes of above hashtables. */ int ep_hashsize; @@ -761,7 +761,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet) struct sctp_transport { /* A list of transports. */ struct list_head transports; - struct rhash_head node; + struct rhlist_head node; /* Reference counting. */ atomic_t refcnt; diff --git a/net/sctp/associola.c b/net/sctp/associola.c index f10d339..68428e1 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -700,11 +700,15 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, /* Set the peer's active state. */ peer->state = peer_state; + /* Add this peer into the transport hashtable */ + if (sctp_hash_transport(peer)) { + sctp_transport_free(peer); + return NULL; + } + /* Attach the remote transport to our asoc. */ list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list); asoc->peer.transport_count++; - /* Add this peer into the transport hashtable */ - sctp_hash_transport(peer); /* If we do not yet have a primary path, set one. */ if (!asoc->peer.primary_path) { diff --git a/net/sctp/input.c b/net/sctp/input.c index a01a56e..458e506 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -790,10 +790,9 @@ hit: /* rhashtable for transport */ struct sctp_hash_cmp_arg { - const struct sctp_endpoint *ep; - const union sctp_addr *laddr; - const union sctp_addr *paddr; - const struct net *net; + const union sctp_addr *paddr; + const struct net *net; + u16 lport; }; static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg, @@ -801,7 +800,6 @@ static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg, { struct sctp_transport *t = (struct sctp_transport *)ptr; const struct sctp_hash_cmp_arg *x = arg->key; - struct sctp_association *asoc; int err = 1; if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr)) @@ -809,19 +807,10 @@ static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg, if (!sctp_transport_hold(t)) return err; - asoc = t->asoc; - if (!net_eq(sock_net(asoc->base.sk), x->net)) + if (!net_eq(sock_net(t->asoc->base.sk), x->net)) + goto out; + if (x->lport != htons(t->asoc->base.bind_addr.port)) goto out; - if (x->ep) { - if (x->ep != asoc->ep) - goto out; - } else { - if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port)) - goto out; - if (!sctp_bind_addr_match(&asoc->base.bind_addr, - x->laddr, sctp_sk(asoc->base.sk))) - goto out; - } err = 0; out: @@ -851,11 +840,9 @@ static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed) const struct sctp_hash_cmp_arg *x = data; const union sctp_addr *paddr = x->paddr; const struct net *net = x->net; - u16 lport; + u16 lport = x->lport; u32 addr; - lport = x->ep ? htons(x->ep->base.bind_addr.port) : - x->laddr->v4.sin_port; if (paddr->sa.sa_family == AF_INET6) addr = jhash(&paddr->v6.sin6_addr, 16, seed); else @@ -875,29 +862,32 @@ static const struct rhashtable_params sctp_hash_params = { int sctp_transport_hashtable_init(void) { - return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params); + return rhltable_init(&sctp_transport_hashtable, &sctp_hash_params); } void sctp_transport_hashtable_destroy(void) { - rhashtable_destroy(&sctp_transport_hashtable); + rhltable_destroy(&sctp_transport_hashtable); } -void sctp_hash_transport(struct sctp_transport *t) +int sctp_hash_transport(struct sctp_transport *t) { struct sctp_hash_cmp_arg arg; + int err; if (t->asoc->temp) - return; + return 0; - arg.ep = t->asoc->ep; - arg.paddr = &t->ipaddr; arg.net = sock_net(t->asoc->base.sk); + arg.paddr = &t->ipaddr; + arg.lport = htons(t->asoc->base.bind_addr.port); -reinsert: - if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg, - &t->node, sctp_hash_params) == -EBUSY) - goto reinsert; + err = rhltable_insert_key(&sctp_transport_hashtable, &arg, + &t->node, sctp_hash_params); + if (err) + pr_err_once("insert transport fail, errno %d\n", err); + + return err; } void sctp_unhash_transport(struct sctp_transport *t) @@ -905,39 +895,62 @@ void sctp_unhash_transport(struct sctp_transport *t) if (t->asoc->temp) return; - rhashtable_remove_fast(&sctp_transport_hashtable, &t->node, - sctp_hash_params); + rhltable_remove(&sctp_transport_hashtable, &t->node, + sctp_hash_params); } +/* return a transport with holding it */ struct sctp_transport *sctp_addrs_lookup_transport( struct net *net, const union sctp_addr *laddr, const union sctp_addr *paddr) { + struct rhlist_head *tmp, *list; + struct sctp_transport *t; struct sctp_hash_cmp_arg arg = { - .ep = NULL, - .laddr = laddr, .paddr = paddr, .net = net, + .lport = laddr->v4.sin_port, }; - return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg, - sctp_hash_params); + list = rhltable_lookup(&sctp_transport_hashtable, &arg, + sctp_hash_params); + + rhl_for_each_entry_rcu(t, tmp, list, node) { + if (!sctp_transport_hold(t)) + continue; + + if (sctp_bind_addr_match(&t->asoc->base.bind_addr, + laddr, sctp_sk(t->asoc->base.sk))) + return t; + sctp_transport_put(t); + } + + return NULL; } +/* return a transport without holding it, as it's only used under sock lock */ struct sctp_transport *sctp_epaddr_lookup_transport( const struct sctp_endpoint *ep, const union sctp_addr *paddr) { struct net *net = sock_net(ep->base.sk); + struct rhlist_head *tmp, *list; + struct sctp_transport *t; struct sctp_hash_cmp_arg arg = { - .ep = ep, .paddr = paddr, .net = net, + .lport = htons(ep->base.bind_addr.port), }; - return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg, - sctp_hash_params); + list = rhltable_lookup(&sctp_transport_hashtable, &arg, + sctp_hash_params); + + rhl_for_each_entry_rcu(t, tmp, list, node) + if (ep == t->asoc->ep) + return t; + + return NULL; } /* Look up an association. */ @@ -951,7 +964,7 @@ static struct sctp_association *__sctp_lookup_association( struct sctp_association *asoc = NULL; t = sctp_addrs_lookup_transport(net, local, peer); - if (!t || !sctp_transport_hold(t)) + if (!t) goto out; asoc = t->asoc; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index f23ad91..d5f4b4a 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4392,10 +4392,7 @@ int sctp_transport_walk_start(struct rhashtable_iter *iter) { int err; - err = rhashtable_walk_init(&sctp_transport_hashtable, iter, - GFP_KERNEL); - if (err) - return err; + rhltable_walk_enter(&sctp_transport_hashtable, iter); err = rhashtable_walk_start(iter); if (err && err != -EAGAIN) { @@ -4479,7 +4476,7 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *), rcu_read_lock(); transport = sctp_addrs_lookup_transport(net, laddr, paddr); - if (!transport || !sctp_transport_hold(transport)) + if (!transport) goto out; rcu_read_unlock(); -- cgit v1.1 From d01eb808c7b8bf2faf89901f1fddfa836d330057 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 16 Nov 2016 11:43:33 +0100 Subject: net: netcp: replace IS_ERR_OR_NULL by IS_ERR knav_queue_open always returns an ERR_PTR value, never NULL. This can be confirmed by unfolding the function calls and conforms to the function's documentation. Thus, replace IS_ERR_OR_NULL by IS_ERR in error checks. The change is made using the following semantic patch: (http://coccinelle.lip6.fr/) // @@ expression x; statement S; @@ x = knav_queue_open(...); if ( - IS_ERR_OR_NULL + IS_ERR (x)) S // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 78b4c83..7981b99 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1568,7 +1568,7 @@ static int netcp_setup_navigator_resources(struct net_device *ndev) /* open Tx completion queue */ snprintf(name, sizeof(name), "tx-compl-%s", ndev->name); netcp->tx_compl_q = knav_queue_open(name, netcp->tx_compl_qid, 0); - if (IS_ERR_OR_NULL(netcp->tx_compl_q)) { + if (IS_ERR(netcp->tx_compl_q)) { ret = PTR_ERR(netcp->tx_compl_q); goto fail; } @@ -1588,7 +1588,7 @@ static int netcp_setup_navigator_resources(struct net_device *ndev) /* open Rx completion queue */ snprintf(name, sizeof(name), "rx-compl-%s", ndev->name); netcp->rx_queue = knav_queue_open(name, netcp->rx_queue_id, 0); - if (IS_ERR_OR_NULL(netcp->rx_queue)) { + if (IS_ERR(netcp->rx_queue)) { ret = PTR_ERR(netcp->rx_queue); goto fail; } @@ -1610,7 +1610,7 @@ static int netcp_setup_navigator_resources(struct net_device *ndev) ++i) { snprintf(name, sizeof(name), "rx-fdq-%s-%d", ndev->name, i); netcp->rx_fdq[i] = knav_queue_open(name, KNAV_QUEUE_GP, 0); - if (IS_ERR_OR_NULL(netcp->rx_fdq[i])) { + if (IS_ERR(netcp->rx_fdq[i])) { ret = PTR_ERR(netcp->rx_fdq[i]); goto fail; } -- cgit v1.1 From c711e27a1851c5efe9eb43365d6a3ffaf6d5f37f Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 16 Nov 2016 15:20:43 +0100 Subject: mlxsw: Add bus capability flag The mlxsw core infrastructure currently assumes that communication with the ASIC is always possible using Ethernet management datagrams (EMADs), but this is only possible when the PCI bus is used. The bus capability flag is added to indicate EMAD support and make core initialize EMAD communication only when it's set. Otherwise, register access is done using command interface. Signed-off-by: Vadim Pasternak Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 6 ++++++ drivers/net/ethernet/mellanox/mlxsw/core.h | 3 +++ drivers/net/ethernet/mellanox/mlxsw/pci.c | 1 + 3 files changed, 10 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 6004817..4f7c478 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -598,6 +598,9 @@ static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) u64 tid; int err; + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return 0; + /* Set the upper 32 bits of the transaction ID field to a random * number. This allows us to discard EMADs addressed to other * devices. @@ -634,6 +637,9 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core) { char hpkt_pl[MLXSW_REG_HPKT_LEN]; + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return; + mlxsw_core->emad.use_emad = false; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, MLXSW_TRAP_ID_ETHEMAD); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index c0acc1b..f7a4d838 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -268,6 +268,8 @@ u64 mlxsw_core_res_get(struct mlxsw_core *mlxsw_core, #define MLXSW_CORE_RES_GET(res, short_res_id) \ mlxsw_core_res_get(res, MLXSW_RES_ID_##short_res_id) +#define MLXSW_BUS_F_TXRX BIT(0) + struct mlxsw_bus { const char *kind; int (*init)(void *bus_priv, struct mlxsw_core *mlxsw_core, @@ -283,6 +285,7 @@ struct mlxsw_bus { char *in_mbox, size_t in_mbox_size, char *out_mbox, size_t out_mbox_size, u8 *p_status); + u8 features; }; struct mlxsw_bus_info { diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index d5cf1ea..a223c85 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -1755,6 +1755,7 @@ static const struct mlxsw_bus mlxsw_pci_bus = { .skb_transmit_busy = mlxsw_pci_skb_transmit_busy, .skb_transmit = mlxsw_pci_skb_transmit, .cmd_exec = mlxsw_pci_cmd_exec, + .features = MLXSW_BUS_F_TXRX, }; static int mlxsw_pci_sw_reset(struct mlxsw_pci *mlxsw_pci, -- cgit v1.1 From 6882b0aee180f2797b8803bdf699aa45c2e5f2d6 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 16 Nov 2016 15:20:44 +0100 Subject: mlxsw: Introduce support for I2C bus Add I2C bus implementation for Mellanox Technologies Switch ASICs. This includes command interface implementation using input / out mailboxes, whose location is retrieved from the firmware during probe time. Signed-off-by: Vadim Pasternak Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 10 + drivers/net/ethernet/mellanox/mlxsw/Makefile | 2 + drivers/net/ethernet/mellanox/mlxsw/i2c.c | 582 +++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/i2c.h | 60 +++ 4 files changed, 654 insertions(+) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/i2c.c create mode 100644 drivers/net/ethernet/mellanox/mlxsw/i2c.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index bac2e5e..f0bc7b3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -29,6 +29,16 @@ config MLXSW_PCI To compile this driver as a module, choose M here: the module will be called mlxsw_pci. +config MLXSW_I2C + tristate "I2C bus implementation for Mellanox Technologies Switch ASICs" + depends on I2C && MLXSW_CORE + default m + ---help--- + This is I2C bus implementation for Mellanox Technologies Switch ASICs. + + To compile this driver as a module, choose M here: the + module will be called mlxsw_i2c. + config MLXSW_SWITCHIB tristate "Mellanox Technologies SwitchIB and SwitchIB-2 support" depends on MLXSW_CORE && NET_SWITCHDEV diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index badef87..4623ede 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -3,6 +3,8 @@ mlxsw_core-objs := core.o mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o mlxsw_pci-objs := pci.o +obj-$(CONFIG_MLXSW_I2C) += mlxsw_i2c.o +mlxsw_i2c-objs := i2c.o obj-$(CONFIG_MLXSW_SWITCHIB) += mlxsw_switchib.o mlxsw_switchib-objs := switchib.o obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/i2c.c b/drivers/net/ethernet/mellanox/mlxsw/i2c.c new file mode 100644 index 0000000..e50c8db --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/i2c.c @@ -0,0 +1,582 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/i2c.c + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Vadim Pasternak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "core.h" +#include "i2c.h" + +static const char mlxsw_i2c_driver_name[] = "mlxsw_i2c"; + +#define MLXSW_I2C_CIR2_BASE 0x72000 +#define MLXSW_I2C_CIR_STATUS_OFF 0x18 +#define MLXSW_I2C_CIR2_OFF_STATUS (MLXSW_I2C_CIR2_BASE + \ + MLXSW_I2C_CIR_STATUS_OFF) +#define MLXSW_I2C_OPMOD_SHIFT 12 +#define MLXSW_I2C_GO_BIT_SHIFT 23 +#define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT 24 +#define MLXSW_I2C_GO_BIT BIT(MLXSW_I2C_GO_BIT_SHIFT) +#define MLXSW_I2C_GO_OPMODE BIT(MLXSW_I2C_OPMOD_SHIFT) +#define MLXSW_I2C_SET_IMM_CMD (MLXSW_I2C_GO_OPMODE | \ + MLXSW_CMD_OPCODE_QUERY_FW) +#define MLXSW_I2C_PUSH_IMM_CMD (MLXSW_I2C_GO_BIT | \ + MLXSW_I2C_SET_IMM_CMD) +#define MLXSW_I2C_SET_CMD (MLXSW_CMD_OPCODE_ACCESS_REG) +#define MLXSW_I2C_PUSH_CMD (MLXSW_I2C_GO_BIT | MLXSW_I2C_SET_CMD) +#define MLXSW_I2C_TLV_HDR_SIZE 0x10 +#define MLXSW_I2C_ADDR_WIDTH 4 +#define MLXSW_I2C_PUSH_CMD_SIZE (MLXSW_I2C_ADDR_WIDTH + 4) +#define MLXSW_I2C_READ_SEMA_SIZE 4 +#define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28) +#define MLXSW_I2C_MBOX_SIZE 20 +#define MLXSW_I2C_MBOX_OUT_PARAM_OFF 12 +#define MLXSW_I2C_MAX_BUFF_SIZE 32 +#define MLXSW_I2C_MBOX_OFFSET_BITS 20 +#define MLXSW_I2C_MBOX_SIZE_BITS 12 +#define MLXSW_I2C_ADDR_BUF_SIZE 4 +#define MLXSW_I2C_BLK_MAX 32 +#define MLXSW_I2C_RETRY 5 +#define MLXSW_I2C_TIMEOUT_MSECS 5000 + +/** + * struct mlxsw_i2c - device private data: + * @cmd.mb_size_in: input mailbox size; + * @cmd.mb_off_in: input mailbox offset in register space; + * @cmd.mb_size_out: output mailbox size; + * @cmd.mb_off_out: output mailbox offset in register space; + * @cmd.lock: command execution lock; + * @dev: I2C device; + * @core: switch core pointer; + * @bus_info: bus info block; + */ +struct mlxsw_i2c { + struct { + u32 mb_size_in; + u32 mb_off_in; + u32 mb_size_out; + u32 mb_off_out; + struct mutex lock; + } cmd; + struct device *dev; + struct mlxsw_core *core; + struct mlxsw_bus_info bus_info; +}; + +#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ + { .addr = (_client)->addr, \ + .buf = (_addr_buf), \ + .len = MLXSW_I2C_ADDR_BUF_SIZE, \ + .flags = 0 }, \ + { .addr = (_client)->addr, \ + .buf = (_buf), \ + .len = (_len), \ + .flags = I2C_M_RD } } + +#define MLXSW_I2C_WRITE_MSG(_client, _buf, _len) \ + { .addr = (_client)->addr, \ + .buf = (u8 *)(_buf), \ + .len = (_len), \ + .flags = 0 } + +/* Routine converts in and out mail boxes offset and size. */ +static inline void +mlxsw_i2c_convert_mbox(struct mlxsw_i2c *mlxsw_i2c, u8 *buf) +{ + u32 tmp; + + /* Local in/out mailboxes: 20 bits for offset, 12 for size */ + tmp = be32_to_cpup((__be32 *) buf); + mlxsw_i2c->cmd.mb_off_in = tmp & + GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); + mlxsw_i2c->cmd.mb_size_in = (tmp & GENMASK(31, + MLXSW_I2C_MBOX_OFFSET_BITS)) >> + MLXSW_I2C_MBOX_OFFSET_BITS; + + tmp = be32_to_cpup((__be32 *) (buf + MLXSW_I2C_ADDR_WIDTH)); + mlxsw_i2c->cmd.mb_off_out = tmp & + GENMASK(MLXSW_I2C_MBOX_OFFSET_BITS - 1, 0); + mlxsw_i2c->cmd.mb_size_out = (tmp & GENMASK(31, + MLXSW_I2C_MBOX_OFFSET_BITS)) >> + MLXSW_I2C_MBOX_OFFSET_BITS; +} + +/* Routine obtains register size from mail box buffer. */ +static inline int mlxsw_i2c_get_reg_size(u8 *in_mbox) +{ + u16 tmp = be16_to_cpup((__be16 *) (in_mbox + MLXSW_I2C_TLV_HDR_SIZE)); + + return (tmp & 0x7ff) * 4 + MLXSW_I2C_TLV_HDR_SIZE; +} + +/* Routine sets I2C device internal offset in the transaction buffer. */ +static inline void mlxsw_i2c_set_slave_addr(u8 *buf, u32 off) +{ + __be32 *val = (__be32 *) buf; + + *val = htonl(off); +} + +/* Routine waits until go bit is cleared. */ +static int mlxsw_i2c_wait_go_bit(struct i2c_client *client, + struct mlxsw_i2c *mlxsw_i2c, u8 *p_status) +{ + u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; + u8 buf[MLXSW_I2C_READ_SEMA_SIZE]; + int len = MLXSW_I2C_READ_SEMA_SIZE; + struct i2c_msg read_sema[] = + MLXSW_I2C_READ_MSG(client, addr_buf, buf, len); + bool wait_done = false; + unsigned long end; + int i = 0, err; + + mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_OFF_STATUS); + + end = jiffies + msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); + do { + u32 ctrl; + + err = i2c_transfer(client->adapter, read_sema, + ARRAY_SIZE(read_sema)); + + ctrl = be32_to_cpu(*(__be32 *) buf); + if (err == ARRAY_SIZE(read_sema)) { + if (!(ctrl & MLXSW_I2C_GO_BIT)) { + wait_done = true; + *p_status = ctrl >> + MLXSW_I2C_CIR_CTRL_STATUS_SHIFT; + break; + } + } + cond_resched(); + } while ((time_before(jiffies, end)) || (i++ < MLXSW_I2C_RETRY)); + + if (wait_done) { + if (*p_status) + err = -EIO; + } else { + return -ETIMEDOUT; + } + + return err > 0 ? 0 : err; +} + +/* Routine posts a command to ASIC though mail box. */ +static int mlxsw_i2c_write_cmd(struct i2c_client *client, + struct mlxsw_i2c *mlxsw_i2c, + int immediate) +{ + __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = { + 0, cpu_to_be32(MLXSW_I2C_PUSH_IMM_CMD) + }; + __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = { + 0, 0, 0, 0, 0, 0, + cpu_to_be32(client->adapter->nr & 0xffff), + cpu_to_be32(MLXSW_I2C_SET_IMM_CMD) + }; + struct i2c_msg push_cmd = + MLXSW_I2C_WRITE_MSG(client, push_cmd_buf, + MLXSW_I2C_PUSH_CMD_SIZE); + struct i2c_msg prep_cmd = + MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE); + int err; + + if (!immediate) { + push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_CMD); + prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_SET_CMD); + } + mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf, + MLXSW_I2C_CIR2_BASE); + mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf, + MLXSW_I2C_CIR2_OFF_STATUS); + + /* Prepare Command Interface Register for transaction */ + err = i2c_transfer(client->adapter, &prep_cmd, 1); + if (err < 0) + return err; + else if (err != 1) + return -EIO; + + /* Write out Command Interface Register GO bit to push transaction */ + err = i2c_transfer(client->adapter, &push_cmd, 1); + if (err < 0) + return err; + else if (err != 1) + return -EIO; + + return 0; +} + +/* Routine obtains mail box offsets from ASIC register space. */ +static int mlxsw_i2c_get_mbox(struct i2c_client *client, + struct mlxsw_i2c *mlxsw_i2c) +{ + u8 addr_buf[MLXSW_I2C_ADDR_BUF_SIZE]; + u8 buf[MLXSW_I2C_MBOX_SIZE]; + struct i2c_msg mbox_cmd[] = + MLXSW_I2C_READ_MSG(client, addr_buf, buf, MLXSW_I2C_MBOX_SIZE); + int err; + + /* Read mail boxes offsets. */ + mlxsw_i2c_set_slave_addr(addr_buf, MLXSW_I2C_CIR2_BASE); + err = i2c_transfer(client->adapter, mbox_cmd, 2); + if (err != 2) { + dev_err(&client->dev, "Could not obtain mail boxes\n"); + if (!err) + return -EIO; + else + return err; + } + + /* Convert mail boxes. */ + mlxsw_i2c_convert_mbox(mlxsw_i2c, &buf[MLXSW_I2C_MBOX_OUT_PARAM_OFF]); + + return err; +} + +/* Routine sends I2C write transaction to ASIC device. */ +static int +mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num, + u8 *p_status) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); + unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); + u8 tran_buf[MLXSW_I2C_MAX_BUFF_SIZE + MLXSW_I2C_ADDR_BUF_SIZE]; + int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j; + unsigned long end; + struct i2c_msg write_tran = + MLXSW_I2C_WRITE_MSG(client, tran_buf, MLXSW_I2C_PUSH_CMD_SIZE); + int err; + + for (i = 0; i < num; i++) { + chunk_size = (in_mbox_size > MLXSW_I2C_BLK_MAX) ? + MLXSW_I2C_BLK_MAX : in_mbox_size; + write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size; + mlxsw_i2c_set_slave_addr(tran_buf, off); + memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox + + chunk_size * i, chunk_size); + + j = 0; + end = jiffies + timeout; + do { + err = i2c_transfer(client->adapter, &write_tran, 1); + if (err == 1) + break; + + cond_resched(); + } while ((time_before(jiffies, end)) || + (j++ < MLXSW_I2C_RETRY)); + + if (err != 1) { + if (!err) + err = -EIO; + return err; + } + + off += chunk_size; + in_mbox_size -= chunk_size; + } + + /* Prepare and write out Command Interface Register for transaction. */ + err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0); + if (err) { + dev_err(&client->dev, "Could not start transaction"); + return -EIO; + } + + /* Wait until go bit is cleared. */ + err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status); + if (err) { + dev_err(&client->dev, "HW semaphore is not released"); + return err; + } + + /* Validate transaction completion status. */ + if (*p_status) { + dev_err(&client->dev, "Bad transaction completion status %x\n", + *p_status); + return -EIO; + } + + return err > 0 ? 0 : err; +} + +/* Routine executes I2C command. */ +static int +mlxsw_i2c_cmd(struct device *dev, size_t in_mbox_size, u8 *in_mbox, + size_t out_mbox_size, u8 *out_mbox, u8 *status) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); + unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); + u8 tran_buf[MLXSW_I2C_ADDR_BUF_SIZE]; + int num, chunk_size, reg_size, i, j; + int off = mlxsw_i2c->cmd.mb_off_out; + unsigned long end; + struct i2c_msg read_tran[] = + MLXSW_I2C_READ_MSG(client, tran_buf, NULL, 0); + int err; + + WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32)); + + reg_size = mlxsw_i2c_get_reg_size(in_mbox); + num = reg_size / MLXSW_I2C_BLK_MAX; + if (reg_size % MLXSW_I2C_BLK_MAX) + num++; + + if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { + dev_err(&client->dev, "Could not acquire lock"); + return -EINVAL; + } + + err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status); + if (err) + goto cmd_fail; + + /* No out mailbox is case of write transaction. */ + if (!out_mbox) { + mutex_unlock(&mlxsw_i2c->cmd.lock); + return 0; + } + + /* Send read transaction to get output mailbox content. */ + read_tran[1].buf = out_mbox; + for (i = 0; i < num; i++) { + chunk_size = (reg_size > MLXSW_I2C_BLK_MAX) ? + MLXSW_I2C_BLK_MAX : reg_size; + read_tran[1].len = chunk_size; + mlxsw_i2c_set_slave_addr(tran_buf, off); + + j = 0; + end = jiffies + timeout; + do { + err = i2c_transfer(client->adapter, read_tran, + ARRAY_SIZE(read_tran)); + if (err == ARRAY_SIZE(read_tran)) + break; + + cond_resched(); + } while ((time_before(jiffies, end)) || + (j++ < MLXSW_I2C_RETRY)); + + if (err != ARRAY_SIZE(read_tran)) { + if (!err) + err = -EIO; + + goto cmd_fail; + } + + off += chunk_size; + reg_size -= chunk_size; + read_tran[1].buf += chunk_size; + } + + mutex_unlock(&mlxsw_i2c->cmd.lock); + + return 0; + +cmd_fail: + mutex_unlock(&mlxsw_i2c->cmd.lock); + return err; +} + +static int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod, + u32 in_mod, bool out_mbox_direct, + char *in_mbox, size_t in_mbox_size, + char *out_mbox, size_t out_mbox_size, + u8 *status) +{ + struct mlxsw_i2c *mlxsw_i2c = bus_priv; + + return mlxsw_i2c_cmd(mlxsw_i2c->dev, in_mbox_size, in_mbox, + out_mbox_size, out_mbox, status); +} + +static bool mlxsw_i2c_skb_transmit_busy(void *bus_priv, + const struct mlxsw_tx_info *tx_info) +{ + return false; +} + +static int mlxsw_i2c_skb_transmit(void *bus_priv, struct sk_buff *skb, + const struct mlxsw_tx_info *tx_info) +{ + return 0; +} + +static int +mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core, + const struct mlxsw_config_profile *profile, + struct mlxsw_res *resources) +{ + struct mlxsw_i2c *mlxsw_i2c = bus_priv; + + mlxsw_i2c->core = mlxsw_core; + + return 0; +} + +static void mlxsw_i2c_fini(void *bus_priv) +{ + struct mlxsw_i2c *mlxsw_i2c = bus_priv; + + mlxsw_i2c->core = NULL; +} + +static const struct mlxsw_bus mlxsw_i2c_bus = { + .kind = "i2c", + .init = mlxsw_i2c_init, + .fini = mlxsw_i2c_fini, + .skb_transmit_busy = mlxsw_i2c_skb_transmit_busy, + .skb_transmit = mlxsw_i2c_skb_transmit, + .cmd_exec = mlxsw_i2c_cmd_exec, +}; + +static int mlxsw_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mlxsw_i2c *mlxsw_i2c; + u8 status; + int err; + + mlxsw_i2c = devm_kzalloc(&client->dev, sizeof(*mlxsw_i2c), GFP_KERNEL); + if (!mlxsw_i2c) + return -ENOMEM; + + i2c_set_clientdata(client, mlxsw_i2c); + mutex_init(&mlxsw_i2c->cmd.lock); + + /* In order to use mailboxes through the i2c, special area is reserved + * on the i2c address space that can be used for input and output + * mailboxes. Such mailboxes are called local mailboxes. When using a + * local mailbox, software should specify 0 as the Input/Output + * parameters. The location of the Local Mailbox addresses on the i2c + * space can be retrieved through the QUERY_FW command. + * For this purpose QUERY_FW is to be issued with opcode modifier equal + * 0x01. For such command the output parameter is an immediate value. + * Here QUERY_FW command is invoked for ASIC probing and for getting + * local mailboxes addresses from immedate output parameters. + */ + + /* Prepare and write out Command Interface Register for transaction */ + err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 1); + if (err) { + dev_err(&client->dev, "Could not start transaction"); + goto errout; + } + + /* Wait until go bit is cleared. */ + err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status); + if (err) { + dev_err(&client->dev, "HW semaphore is not released"); + goto errout; + } + + /* Validate transaction completion status. */ + if (status) { + dev_err(&client->dev, "Bad transaction completion status %x\n", + status); + err = -EIO; + goto errout; + } + + /* Get mailbox offsets. */ + err = mlxsw_i2c_get_mbox(client, mlxsw_i2c); + if (err < 0) { + dev_err(&client->dev, "Fail to get mailboxes\n"); + goto errout; + } + + dev_info(&client->dev, "%s mb size=%x off=0x%08x out mb size=%x off=0x%08x\n", + id->name, mlxsw_i2c->cmd.mb_size_in, + mlxsw_i2c->cmd.mb_off_in, mlxsw_i2c->cmd.mb_size_out, + mlxsw_i2c->cmd.mb_off_out); + + /* Register device bus. */ + mlxsw_i2c->bus_info.device_kind = id->name; + mlxsw_i2c->bus_info.device_name = client->name; + mlxsw_i2c->bus_info.dev = &client->dev; + mlxsw_i2c->dev = &client->dev; + + err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, + &mlxsw_i2c_bus, mlxsw_i2c); + if (err) { + dev_err(&client->dev, "Fail to register core bus\n"); + return err; + } + + return 0; + +errout: + i2c_set_clientdata(client, NULL); + + return err; +} + +static int mlxsw_i2c_remove(struct i2c_client *client) +{ + struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); + + mlxsw_core_bus_device_unregister(mlxsw_i2c->core); + mutex_destroy(&mlxsw_i2c->cmd.lock); + + return 0; +} + +int mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver) +{ + i2c_driver->probe = mlxsw_i2c_probe; + i2c_driver->remove = mlxsw_i2c_remove; + return i2c_add_driver(i2c_driver); +} +EXPORT_SYMBOL(mlxsw_i2c_driver_register); + +void mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver) +{ + i2c_del_driver(i2c_driver); +} +EXPORT_SYMBOL(mlxsw_i2c_driver_unregister); + +MODULE_AUTHOR("Vadim Pasternak "); +MODULE_DESCRIPTION("Mellanox switch I2C interface driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/mellanox/mlxsw/i2c.h b/drivers/net/ethernet/mellanox/mlxsw/i2c.h new file mode 100644 index 0000000..daa24b2 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/i2c.h @@ -0,0 +1,60 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/i2c.h + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Vadim Pasternak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MLXSW_I2C_H +#define _MLXSW_I2C_H + +#include + +#if IS_ENABLED(CONFIG_MLXSW_I2C) + +int mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver); +void mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver); + +#else + +static inline int +mlxsw_i2c_driver_register(struct i2c_driver *i2c_driver) +{ + return -ENODEV; +} + +static inline void +mlxsw_i2c_driver_unregister(struct i2c_driver *i2c_driver) +{ +} + +#endif + +#endif -- cgit v1.1 From 4ebd00bc5ffc747f07fe23b1993adc9e6220ba2f Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 16 Nov 2016 15:20:45 +0100 Subject: mlxsw: Invoke driver's init/fini methods only if defined We are going to add a minimal driver on top of the mlxsw core infrastructure, which will be mainly used for hardware monitoring in Baseboard management controller (BMC) installations. Unlike the switch drivers (e.g., spectrum, switchx2), this driver does not initialize the ASIC and therefore doesn't need to implement the init() and fini() methods in its 'mlxsw_driver' struct. Signed-off-by: Vadim Pasternak Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 4f7c478..763752f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1162,9 +1162,11 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_hwmon_init; - err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info); - if (err) - goto err_driver_init; + if (mlxsw_driver->init) { + err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info); + if (err) + goto err_driver_init; + } err = mlxsw_core_debugfs_init(mlxsw_core); if (err) @@ -1173,7 +1175,8 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, return 0; err_debugfs_init: - mlxsw_core->driver->fini(mlxsw_core); + if (mlxsw_core->driver->fini) + mlxsw_core->driver->fini(mlxsw_core); err_driver_init: err_hwmon_init: devlink_unregister(devlink); @@ -1199,7 +1202,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) struct devlink *devlink = priv_to_devlink(mlxsw_core); mlxsw_core_debugfs_fini(mlxsw_core); - mlxsw_core->driver->fini(mlxsw_core); + if (mlxsw_core->driver->fini) + mlxsw_core->driver->fini(mlxsw_core); devlink_unregister(devlink); mlxsw_emad_fini(mlxsw_core); mlxsw_core->bus->fini(mlxsw_core->bus_priv); -- cgit v1.1 From d556e929164fdc6c94848374fc98c40852f97adb Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 16 Nov 2016 15:20:46 +0100 Subject: mlxsw: minimal: Add I2C support for Mellanox ASICs Add I2C access support for Mellanox ASICs: - Virtual Protocol Interconnect switches SwitchX, SwitchX2, providing InfiniBand, Ethernet and Fibre Channel connectivity; - Infiniband switches SwitchIB, SwitchIB2: - Ethernet switch Spectrum. Example of probing activation: echo mlxsw_minimal 0x48 > /sys/bus/i2c/devices/i2c-2/new_device Signed-off-by: Vadim Pasternak Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 11 +++ drivers/net/ethernet/mellanox/mlxsw/Makefile | 2 + drivers/net/ethernet/mellanox/mlxsw/minimal.c | 97 +++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/minimal.c diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index f0bc7b3..c9822e6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -79,3 +79,14 @@ config MLXSW_SPECTRUM_DCB ---help--- Say Y here if you want to use Data Center Bridging (DCB) in the driver. + +config MLXSW_MINIMAL + tristate "Mellanox Technologies minimal I2C support" + depends on MLXSW_CORE && MLXSW_I2C + default m + ---help--- + This driver supports I2C access for Mellanox Technologies Switch + ASICs. + + To compile this driver as a module, choose M here: the + module will be called mlxsw_minimal. diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 4623ede..2722942 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -14,3 +14,5 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ spectrum_switchdev.o spectrum_router.o \ spectrum_kvdl.o mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o +obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o +mlxsw_minimal-objs := minimal.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c new file mode 100644 index 0000000..3dd1626 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -0,0 +1,97 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/minimal.c + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Vadim Pasternak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "core.h" +#include "i2c.h" + +static const char mlxsw_minimal_driver_name[] = "mlxsw_minimal"; + +static const struct mlxsw_config_profile mlxsw_minimal_config_profile; + +static struct mlxsw_driver mlxsw_minimal_driver = { + .kind = mlxsw_minimal_driver_name, + .priv_size = 1, + .profile = &mlxsw_minimal_config_profile, +}; + +static const struct i2c_device_id mlxsw_minimal_i2c_id[] = { + { "mlxsw_minimal", 0}, + { }, +}; + +static struct i2c_driver mlxsw_minimal_i2c_driver = { + .driver.name = "mlxsw_minimal", + .class = I2C_CLASS_HWMON, + .id_table = mlxsw_minimal_i2c_id, +}; + +static int __init mlxsw_minimal_module_init(void) +{ + int err; + + err = mlxsw_core_driver_register(&mlxsw_minimal_driver); + if (err) + return err; + + err = mlxsw_i2c_driver_register(&mlxsw_minimal_i2c_driver); + if (err) + goto err_i2c_driver_register; + + return 0; + +err_i2c_driver_register: + mlxsw_core_driver_unregister(&mlxsw_minimal_driver); + + return err; +} + +static void __exit mlxsw_minimal_module_exit(void) +{ + mlxsw_i2c_driver_unregister(&mlxsw_minimal_i2c_driver); + mlxsw_core_driver_unregister(&mlxsw_minimal_driver); +} + +module_init(mlxsw_minimal_module_init); +module_exit(mlxsw_minimal_module_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Vadim Pasternak "); +MODULE_DESCRIPTION("Mellanox minimal driver"); +MODULE_DEVICE_TABLE(i2c, mlxsw_minimal_i2c_id); -- cgit v1.1 From 42c7372a111630dab200c2f959424f5ec3bf79a4 Mon Sep 17 00:00:00 2001 From: Maxim Altshul Date: Sun, 21 Aug 2016 14:24:24 +0300 Subject: wlcore: Pass win_size taken from ieee80211_sta to FW When starting a new BA session, we must pass the win_size to the FW. To do this we take max_rx_aggregation_subframes (BA RX win size) which is stored in ieee80211_sta structure (e.g per link and not per HW) We will use the value stored per link when passing the win_size to firmware through the ACX_BA_SESSION_RX_SETUP command. Signed-off-by: Maxim Altshul Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wlcore/acx.c | 5 +++-- drivers/net/wireless/ti/wlcore/acx.h | 3 ++- drivers/net/wireless/ti/wlcore/main.c | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index 26cc23f..a485999 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1419,7 +1419,8 @@ out: /* setup BA session receiver setting in the FW. */ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, - u16 ssn, bool enable, u8 peer_hlid) + u16 ssn, bool enable, u8 peer_hlid, + u8 win_size) { struct wl1271_acx_ba_receiver_setup *acx; int ret; @@ -1435,7 +1436,7 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, acx->hlid = peer_hlid; acx->tid = tid_index; acx->enable = enable; - acx->win_size = wl->conf.ht.rx_ba_win_size; + acx->win_size = win_size; acx->ssn = ssn; ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx, diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 6321ed4..f46d7fd 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -1113,7 +1113,8 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl, int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, - u16 ssn, bool enable, u8 peer_hlid); + u16 ssn, bool enable, u8 peer_hlid, + u8 win_size); int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, u64 *mactime); int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 471521a..5438975 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5285,7 +5285,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true, - hlid); + hlid, + params->buf_size); + if (!ret) { *ba_bitmap |= BIT(tid); wl->ba_rx_session_count++; @@ -5306,7 +5308,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false, - hlid); + hlid, 0); if (!ret) { *ba_bitmap &= ~BIT(tid); wl->ba_rx_session_count--; -- cgit v1.1 From e7ee74b56f23ba447d3124f2eccc32033cca501d Mon Sep 17 00:00:00 2001 From: Maxim Altshul Date: Sun, 21 Aug 2016 14:24:25 +0300 Subject: wlcore: Add RX_BA_WIN_SIZE_CHANGE_EVENT event This event is used by the Firmware to limit the RX BA win size for a specific link. The event handler updates the new size in the mac's sta->sta struct. BA sessions opened for that link will use the new restricted win_size. This limitation remains until a new update is received or until the link is closed. Signed-off-by: Maxim Altshul Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wl18xx/event.c | 28 ++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/event.h | 1 + drivers/net/wireless/ti/wl18xx/main.c | 3 ++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index b36ce18..86fa0fc 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -218,5 +218,33 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) if (vector & FW_LOGGER_INDICATION) wlcore_event_fw_logger(wl); + if (vector & RX_BA_WIN_SIZE_CHANGE_EVENT_ID) { + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + u8 link_id = mbox->rx_ba_link_id; + u8 win_size = mbox->rx_ba_win_size; + const u8 *addr; + + wlvif = wl->links[link_id].wlvif; + vif = wl12xx_wlvif_to_vif(wlvif); + + /* Update RX aggregation window size and call + * MAC routine to stop active RX aggregations for this link + */ + if (wlvif->bss_type != BSS_TYPE_AP_BSS) + addr = vif->bss_conf.bssid; + else + addr = wl->links[link_id].addr; + + sta = ieee80211_find_sta(vif, addr); + if (sta) { + sta->max_rx_aggregation_subframes = win_size; + ieee80211_stop_rx_ba_session(vif, + wl->links[link_id].ba_bitmap, + addr); + } + } + return 0; } diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index ce8ea9c0..4af297f 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -38,6 +38,7 @@ enum { REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18), DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19), PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20), + RX_BA_WIN_SIZE_CHANGE_EVENT_ID = BIT(21), SMART_CONFIG_SYNC_EVENT_ID = BIT(22), SMART_CONFIG_DECODE_EVENT_ID = BIT(23), TIME_SYNC_EVENT_ID = BIT(24), diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 06d6943..5bdf7a0 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1041,7 +1041,8 @@ static int wl18xx_boot(struct wl1271 *wl) SMART_CONFIG_SYNC_EVENT_ID | SMART_CONFIG_DECODE_EVENT_ID | TIME_SYNC_EVENT_ID | - FW_LOGGER_INDICATION; + FW_LOGGER_INDICATION | + RX_BA_WIN_SIZE_CHANGE_EVENT_ID; wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID; -- cgit v1.1 From df566a481ee8fb4ce90c1d498bc8fab5c0ec887a Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Oct 2016 13:37:42 -0700 Subject: mwifiex: report wakeup for wowlan Register the WLAN device as a wakeup source since it can wake the system via wake-on-wireless-lan. In an actual wowlan event, notify the PM core that we are the current wakeup source. This allows the PM core to update the wakeup attributes in /sys. This was causing wakeup issues on chromeos as the system was apparently confused about the wakeup source. Signed-off-by: Wei-Ning Huang Signed-off-by: Rajat Jain Tested-by: Wei-Ning Huang Reviewed-by: Eric Caruso Acked-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 8 ++++++++ drivers/net/wireless/marvell/mwifiex/sdio.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 8718950..807af13 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -89,6 +89,9 @@ static irqreturn_t mwifiex_wake_irq_wifi(int irq, void *priv) disable_irq_nosync(irq); } + /* Notify PM core we are wakeup source */ + pm_wakeup_event(cfg->dev, 0); + return IRQ_HANDLED; } @@ -112,6 +115,7 @@ static int mwifiex_sdio_probe_of(struct device *dev, struct sdio_mmc_card *card) GFP_KERNEL); cfg = card->plt_wake_cfg; if (cfg && card->plt_of_node) { + cfg->dev = dev; cfg->irq_wifi = irq_of_parse_and_map(card->plt_of_node, 0); if (!cfg->irq_wifi) { dev_dbg(dev, @@ -132,6 +136,10 @@ static int mwifiex_sdio_probe_of(struct device *dev, struct sdio_mmc_card *card) } } + ret = device_init_wakeup(dev, true); + if (ret) + dev_err(dev, "fail to init wakeup for mwifiex"); + return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h index db837f1..07cdd23 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.h +++ b/drivers/net/wireless/marvell/mwifiex/sdio.h @@ -155,6 +155,7 @@ } while (0) struct mwifiex_plt_wake_cfg { + struct device *dev; int irq_wifi; bool wake_by_wifi; }; -- cgit v1.1 From 0d3d253b2040e86b2174cad502925fedaedb29ad Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:03 -0500 Subject: rtlwifi: rtl8192de: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192de/def.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/reg.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h | 4 ---- 20 files changed, 80 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/def.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/def.h index 0a443ed..cb7b9b7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/def.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/def.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.c index 7c1db7e..ac6d554 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.h index f2d318c..5d346ec 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/dm.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c index 8de29cc..17f6903 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h index 8a38daa..6b43523 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c index d91f8bb..fcb14c5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.h index 1bc7b1a..24b03b9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c index 811ba57..c22b8a2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.h index a29df30..9874519 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/led.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c index 2a1edfd..424f54b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h index 8115bf4..58b56b5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/reg.h index 315a298..b354b95 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/reg.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/reg.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.c index 2f479d3..9dc9e91 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.h index 7303d12..c650a8d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/rf.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c index 0538a4d..a9c39eb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.h index 0e6035b..fd7d036 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.c index 8ea6f52..4badb18 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.h index 8b724a8..7fefc48 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/table.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c index e998e98..5fb3756 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h index 194d99f..9bb6cc6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 22beafeaf324f962d3d74feb9ac5db9d810ca8e8 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:04 -0500 Subject: rtlwifi: rtl8192se: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192se/def.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/reg.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.h | 4 ---- 20 files changed, 80 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/def.h index 41466f9..b5ba055 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/def.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/def.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.c index 9bae5a9..2c073a7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.h index de6ac79..3af07ef 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/dm.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c index 331b158..32f9207 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.h index b1e44b8..5827aa3 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c index 52e4430..26e06b2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.h index 4cacee1..86bce1b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c index 9849cb9..8700078 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.h index 2182dbe..90e265d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/led.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c index 4bb7558..fcb9216 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.h index 8acf476..7a3b6b6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/phy.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/reg.h index e130434..5d445c2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/reg.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/reg.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c index 34e88a3..bd2fa77 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.h index 8a29eb9..e9ba283 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/rf.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c index 5e8e02d..a652d45 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.h index 2eb8886..af449d6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.c index f1a73f7..162578f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.h index 2feb73b..aa3c768 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/table.h @@ -6,10 +6,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c index d53bbf6..9a5a113 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.h index 5a13f17..7285891 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 6456d4d35e9b8439ed0a6efdc9c27fdc99d18bbe Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:05 -0500 Subject: rtlwifi: rtl8192ce: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/def.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/reg.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.h | 4 ---- 18 files changed, 72 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/def.h index 690a7a1..b90aaf1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/def.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/def.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.c index 09898cf..2c8205e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.h index 38ba707..9761d0c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/dm.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c index a47be73..4483d40 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.h index 98a0868..877f138 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c index 24e483b..833193b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.h index c576106..f6edb9c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/led.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c index 46d0d94..d1b6a8f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.h index dadc02b..93f3bc0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/phy.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/reg.h index dc8460c..1bb7ed3 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/reg.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/reg.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.c index a9c406f..7cae635 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.h index ebd72ca..22c5e6f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/rf.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c index 87aa209..5c46a98 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.h index d2367a5..9a1c89c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.c index 752f943..98b06d4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.h index 8b79161..51e4e07 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/table.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c index 781af1b..2ab4a00 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.h index 6073045..66291fc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 7d63d36b92869c4bcfda2766cc1a032d29aa84e2 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:06 -0500 Subject: rtlwifi: rtl8192cu: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/reg.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h | 4 ---- 20 files changed, 80 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h index 74a479a..316fe99 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/def.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.c index c16209a..00fc068 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.h index fafa6ba..ce71433 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/dm.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c index ae8f055..5c7da0c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.h index 6758808..932f056 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/hw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c index 8514ab65..c624081 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.h index 0f37227..551deb8 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/led.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c index 68ca734..cf212f6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.h index 20a49ec..8573b7e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.c index 4b29764..f35f435 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.h index 42b0686..a422c4d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/phy.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/reg.h index 8b81465..8185886 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/reg.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/reg.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c index ec2ea56..5e31830 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.h index 6f987de..07aec0b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c index 7c6f7f0..92588e0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.h index a1310ab..4ea2cb2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.c index 7903c15..b3ac981 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.h index 4b020e9..851bf53 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/table.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c index 95880fe..1ea878f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h index fd8051d..df88e39 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 20ac950f20a0f4fd16724778d1744e03107126cf Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:07 -0500 Subject: rtlwifi: rtl8723ae: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c | 4 ---- 4 files changed, 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.h index 5711105..a113780 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h index 9d1fe25..2e668fc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.h index bcd64a2..45719fd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/hal_bt_coexist.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c index c7be934..77c1004 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/led.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 044a609a1dc98cda6d1147036942f78fcc21164f Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:08 -0500 Subject: rtlwifi: rtl8188ee: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c | 4 ---- 4 files changed, 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.h b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.h index 071ccee..0fd2bac 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/dm.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h index 21bd4a5..b884c30 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.h index 1850fde..d38dbca 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c index f05c2c6..6ea7fd7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/led.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 61a288fcdc9dae2008fe2e8e436d8f11c317d0a6 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:09 -0500 Subject: rtlwifi: rtl8192c: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192c/main.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.h | 4 ---- 6 files changed, 24 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c index 316be5f..bdc132b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.h index 6a72d0c..441604f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h index 864806c..c5fa14b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/main.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/main.c index 918b1d1..889bd13 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/main.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/main.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.c index 27e3d5f..94dd25c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.h index 2024125..d11261e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/phy_common.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 1b4893264e4faab0a6b42799fe1cec755ff97680 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 4 Oct 2016 23:08:10 -0500 Subject: rtlwifi: Remove address of Free Software Foundation Since this driver was added to the kernel, the checkpatch script was modified to request that the address of the FSF not be included. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/pci.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/pci.h | 4 ---- drivers/net/wireless/realtek/rtlwifi/usb.c | 4 ---- drivers/net/wireless/realtek/rtlwifi/usb.h | 4 ---- 4 files changed, 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 0dfa9ea..d044b23 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.h b/drivers/net/wireless/realtek/rtlwifi/pci.h index b951eba..578b1d9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.h +++ b/drivers/net/wireless/realtek/rtlwifi/pci.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index 32aa5c1..0a508649 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h index 685273c..a6d43d2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.h +++ b/drivers/net/wireless/realtek/rtlwifi/usb.h @@ -11,10 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * -- cgit v1.1 From 87016f5eb1a39251f982ae7a6f2843f82307ae47 Mon Sep 17 00:00:00 2001 From: James Minor Date: Fri, 7 Oct 2016 10:45:57 -0500 Subject: wlcore: Allow scans when in AP mode When in AP mode, scans can be done without changing firmware to the multi-role firmware. Allow the interface to scan if forced in the scan request. Signed-off-by: James Minor Signed-off-by: Xander Huff Reviewed-by: Ben Shelton Reviewed-by: Jaeden Amero Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wlcore/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 5438975..2f056923 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -6122,6 +6122,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) WIPHY_FLAG_SUPPORTS_SCHED_SCAN | WIPHY_FLAG_HAS_CHANNEL_SWITCH; + wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; + /* make sure all our channels fit in the scanned_ch bitmask */ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + ARRAY_SIZE(wl1271_channels_5ghz) > -- cgit v1.1 From b022539db49a31bd55cb8167dec5a67967ba8a2c Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Thu, 13 Oct 2016 16:55:46 +0530 Subject: rsi: Fix memory leak in module unload Observed crash when module is unloaded if CONFIG_RSI_DEBUGSFS is not set. Fix: Debugfs entry removal moved inside CONFIG_RSI_DEBUGSFS flag in function rsi_mac80211_detach() Memory leak found and fixed for below structures in function rsi_mac80211_detach() * channel list for each supported band * rsi debugfs info Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index dbb2389..58779c0 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -194,6 +194,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band) void rsi_mac80211_detach(struct rsi_hw *adapter) { struct ieee80211_hw *hw = adapter->hw; + enum nl80211_band band; if (hw) { ieee80211_stop_queues(hw); @@ -201,7 +202,17 @@ void rsi_mac80211_detach(struct rsi_hw *adapter) ieee80211_free_hw(hw); } + for (band = 0; band < NUM_NL80211_BANDS; band++) { + struct ieee80211_supported_band *sband = + &adapter->sbands[band]; + + kfree(sband->channels); + } + +#ifdef CONFIG_RSI_DEBUGFS rsi_remove_dbgfs(adapter); + kfree(adapter->dfsentry); +#endif } EXPORT_SYMBOL_GPL(rsi_mac80211_detach); -- cgit v1.1 From 77364aae88d7b261bb0433fb1900598a59a95dc8 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Thu, 13 Oct 2016 16:56:45 +0530 Subject: rsi: Host to device command frame vap_capabilites modified with new field vap status * Command frame vap_capabilites is modified to use for vap add, vap delete and vap update in firmware, hence new filed vap status is added. * When interface is down this frame needs to be send with vap status delete. Otherwise it is considered as wrong frame for the same interface in firmware. * vap_update status is reserved for future. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 8 ++++++-- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 5 ++++- drivers/net/wireless/rsi/rsi_mgmt.h | 9 ++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 58779c0..aaca258 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -315,7 +315,9 @@ static int rsi_mac80211_add_interface(struct ieee80211_hw *hw, if (!adapter->sc_nvifs) { ++adapter->sc_nvifs; adapter->vifs[0] = vif; - ret = rsi_set_vap_capabilities(common, STA_OPMODE); + ret = rsi_set_vap_capabilities(common, + STA_OPMODE, + VAP_ADD); } break; default: @@ -343,8 +345,10 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw, struct rsi_common *common = adapter->priv; mutex_lock(&common->mutex); - if (vif->type == NL80211_IFTYPE_STATION) + if (vif->type == NL80211_IFTYPE_STATION) { adapter->sc_nvifs--; + rsi_set_vap_capabilities(common, STA_OPMODE, VAP_DELETE); + } if (!memcmp(adapter->vifs[0], vif, sizeof(struct ieee80211_vif))) adapter->vifs[0] = NULL; diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 35c14cc..30b9d44 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -617,7 +617,9 @@ static int rsi_program_bb_rf(struct rsi_common *common) * * Return: 0 on success, corresponding negative error code on failure. */ -int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode) +int rsi_set_vap_capabilities(struct rsi_common *common, + enum opmode mode, + u8 vap_status) { struct sk_buff *skb = NULL; struct rsi_vap_caps *vap_caps; @@ -642,6 +644,7 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode) FRAME_DESC_SZ) | (RSI_WIFI_MGMT_Q << 12)); vap_caps->desc_word[1] = cpu_to_le16(VAP_CAPABILITIES); + vap_caps->desc_word[2] = cpu_to_le16(vap_status << 8); vap_caps->desc_word[4] = cpu_to_le16(mode | (common->channel_width << 8)); vap_caps->desc_word[7] = cpu_to_le16((vap_id << 8) | diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 3741173..d155358 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -145,6 +145,12 @@ enum opmode { AP_OPMODE = 2 }; +enum vap_status { + VAP_ADD = 1, + VAP_DELETE = 2, + VAP_UPDATE = 3 +}; + extern struct ieee80211_rate rsi_rates[12]; extern const u16 rsi_mcsrates[8]; @@ -287,7 +293,8 @@ static inline u8 rsi_get_channel(u8 *addr) } int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg); -int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode); +int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode, + u8 vap_status); int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, u16 ssn, u8 buf_size, u8 event); int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, -- cgit v1.1 From e1c122d55f9ec5608ca98a9a846fd39cdf3ed7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 14 Oct 2016 09:45:59 +0200 Subject: brcmfmac: print name of connect status event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies debugging. Format %s (%u) comes from similar debugging message in brcmf_fweh_event_worker. Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 3 ++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index b777e1b..1e7c6f0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5506,7 +5506,8 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, u32 reason = e->reason; struct station_info sinfo; - brcmf_dbg(CONN, "event %d, reason %d\n", event, reason); + brcmf_dbg(CONN, "event %s (%u), reason %d\n", + brcmf_fweh_event_name(event), event, reason); if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS && ndev != cfg_to_ndev(cfg)) { brcmf_dbg(CONN, "AP mode link down\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index 79c081f..c79306b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -69,7 +69,7 @@ static struct brcmf_fweh_event_name fweh_event_names[] = { * * @code: code to lookup. */ -static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) +const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) { int i; for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) { @@ -79,7 +79,7 @@ static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) return "unknown"; } #else -static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) +const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) { return "nodebug"; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h index 26ff5a9..5fba4b4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h @@ -287,6 +287,8 @@ struct brcmf_fweh_info { void *data); }; +const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code); + void brcmf_fweh_attach(struct brcmf_pub *drvr); void brcmf_fweh_detach(struct brcmf_pub *drvr); int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, -- cgit v1.1 From 6f17581788206444cbbcdbc107498f85e9765e3d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 14 Oct 2016 11:23:09 +0200 Subject: p54: memset(0) whole array gcc 7 complains: drivers/net/wireless/intersil/p54/fwio.c: In function 'p54_scan': drivers/net/wireless/intersil/p54/fwio.c:491:4: warning: 'memset' used with length equal to number of elements without multiplication by element size [-Wmemset-elt-size] Fix that by passing the correct size to memset. Signed-off-by: Jiri Slaby Cc: Christian Lamparter Cc: Kalle Valo Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Acked-by: Christian Lamparter Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/p54/fwio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c index 257a9ea..4ac6764 100644 --- a/drivers/net/wireless/intersil/p54/fwio.c +++ b/drivers/net/wireless/intersil/p54/fwio.c @@ -488,7 +488,7 @@ int p54_scan(struct p54_common *priv, u16 mode, u16 dwell) entry += sizeof(__le16); chan->pa_points_per_curve = 8; - memset(chan->curve_data, 0, sizeof(*chan->curve_data)); + memset(chan->curve_data, 0, sizeof(chan->curve_data)); memcpy(chan->curve_data, entry, sizeof(struct p54_pa_curve_data_sample) * min((u8)8, curve_data->points_per_channel)); -- cgit v1.1 From 10f3366b4d894ac6eb9f31931bd4c99895935f26 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 24 Oct 2016 17:38:35 +0200 Subject: wireless: fix bogus maybe-uninitialized warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hostap_80211_rx() function is supposed to set up the mac addresses for four possible cases, based on two bits of input data. For some reason, gcc decides that it's possible that none of the these four cases apply and the addresses remain uninitialized: drivers/net/wireless/intersil/hostap/hostap_80211_rx.c: In function ‘hostap_80211_rx’: arch/x86/include/asm/string_32.h:77:14: warning: ‘src’ may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/net/wireless/intel/ipw2x00/libipw_rx.c: In function ‘libipw_rx’: arch/x86/include/asm/string_32.h:77:14: error: ‘dst’ may be used uninitialized in this function [-Werror=maybe-uninitialized] arch/x86/include/asm/string_32.h:78:22: error: ‘*((void *)&dst+4)’ may be used uninitialized in this function [-Werror=maybe-uninitialized] This warning is clearly nonsense, but changing the last case into 'default' makes it obvious to the compiler too, which avoids the warning and probably leads to better object code too. The same code is duplicated several times in the kernel, so this patch uses the same workaround for all copies. The exact configuration was hit only very rarely in randconfig builds and I only saw it in three drivers, but I assume that all of them are potentially affected, and it's better to keep the code consistent. Signed-off-by: Arnd Bergmann Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 8 ++++---- drivers/net/wireless/intel/ipw2x00/libipw_rx.c | 2 +- drivers/net/wireless/intersil/hostap/hostap_80211_rx.c | 2 +- net/wireless/lib80211_crypt_tkip.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 3fd1cc9..84a6d12 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -421,10 +421,6 @@ int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb) switch ((le16_to_cpu(wh.frame_control)) & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { - case 0: - memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN); - memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN); - break; case IEEE80211_FCTL_TODS: memcpy(eth_hdr.h_dest, wh.addr3, ETH_ALEN); memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN); @@ -435,6 +431,10 @@ int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb) break; case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: break; + default: + memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN); + memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN); + break; } skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr)); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c index cef7f7d..1c1ec7b 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c @@ -507,7 +507,7 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, memcpy(dst, hdr->addr3, ETH_ALEN); memcpy(src, hdr->addr4, ETH_ALEN); break; - case 0: + default: memcpy(dst, hdr->addr1, ETH_ALEN); memcpy(src, hdr->addr2, ETH_ALEN); break; diff --git a/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c index 599f30f..34dbddb 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c @@ -855,7 +855,7 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, memcpy(dst, hdr->addr3, ETH_ALEN); memcpy(src, hdr->addr4, ETH_ALEN); break; - case 0: + default: memcpy(dst, hdr->addr1, ETH_ALEN); memcpy(src, hdr->addr2, ETH_ALEN); break; diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index 71447cf..ba0a1f3 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -556,7 +556,7 @@ static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ break; - case 0: + default: memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ break; -- cgit v1.1 From 7fc1503c906f0fac62d3506a6e993e49fb996248 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Oct 2016 22:21:04 +0200 Subject: cw1200: fix bogus maybe-uninitialized warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On x86, the cw1200 driver produces a rather silly warning about the possible use of the 'ret' variable without an initialization presumably after being confused by the architecture specific definition of WARN_ON: drivers/net/wireless/st/cw1200/wsm.c: In function ‘wsm_handle_rx’: drivers/net/wireless/st/cw1200/wsm.c:1457:9: error: ‘ret’ may be used uninitialized in this function [-Werror=maybe-uninitialized] We have already checked that 'count' is larger than 0 here, so we know that 'ret' is initialized. Changing the 'for' loop into do/while also makes this clear to the compiler. Suggested-by: David Laight Signed-off-by: Arnd Bergmann Signed-off-by: Kalle Valo --- drivers/net/wireless/st/cw1200/wsm.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/st/cw1200/wsm.c b/drivers/net/wireless/st/cw1200/wsm.c index ffb245e..be4c22e 100644 --- a/drivers/net/wireless/st/cw1200/wsm.c +++ b/drivers/net/wireless/st/cw1200/wsm.c @@ -379,7 +379,6 @@ static int wsm_multi_tx_confirm(struct cw1200_common *priv, { int ret; int count; - int i; count = WSM_GET32(buf); if (WARN_ON(count <= 0)) @@ -395,11 +394,10 @@ static int wsm_multi_tx_confirm(struct cw1200_common *priv, } cw1200_debug_txed_multi(priv, count); - for (i = 0; i < count; ++i) { + do { ret = wsm_tx_confirm(priv, buf, link_id); - if (ret) - return ret; - } + } while (!ret && --count); + return ret; underflow: -- cgit v1.1 From e49656147359411fb7529a211e852e587e5970dd Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 1 Nov 2016 14:48:40 +0000 Subject: rtlwifi: Use dev_kfree_skb_irq instead of kfree_skb It is not allowed to call kfree_skb() from hardware interrupt context or with interrupts being disabled, spin_lock_irqsave() make sure always in irq disable context. So the kfree_skb() should be replaced with dev_kfree_skb_irq(). This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index f95760c..a11e8f5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1832,7 +1832,7 @@ bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); pskb = __skb_dequeue(&ring->queue); - kfree_skb(pskb); + dev_kfree_skb_irq(pskb); /*this is wrong, fill_tx_cmddesc needs update*/ pdesc = &ring->desc[0]; -- cgit v1.1 From 8052d7245b6089992343c80b38b14dbbd8354651 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 5 Nov 2016 14:08:57 -0500 Subject: ssb: Fix error routine when fallback SPROM fails When there is a CRC error in the SPROM read from the device, the code attempts to handle a fallback SPROM. When this also fails, the driver returns zero rather than an error code. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/ssb/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 0f28c08..77b551d 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -909,6 +909,7 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, if (err) { ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n", err); + goto out_free; } else { ssb_dbg("Using SPROM revision %d provided by platform\n", sprom->revision); -- cgit v1.1 From 5ff2622293788fc7495fa169b4e4374a669be4e6 Mon Sep 17 00:00:00 2001 From: Ricky Liang Date: Wed, 9 Nov 2016 11:37:28 +0800 Subject: mwifiex: fix memory leak in mwifiex_save_hidden_ssid_channels() kmemleak reports memory leak in mwifiex_save_hidden_ssid_channels(): unreferenced object 0xffffffc0a2914780 (size 192): comm "ksdioirqd/mmc2", pid 2004, jiffies 4307182506 (age 820.684s) hex dump (first 32 bytes): 00 06 47 49 4e 2d 32 67 01 03 c8 60 6c 03 01 40 ..GIN-2g...`l..@ 07 10 54 57 20 34 04 1e 64 05 24 84 03 24 95 04 ..TW 4..d.$..$.. backtrace: [] create_object+0x164/0x2b4 [] kmemleak_alloc+0x50/0x88 [] __kmalloc_track_caller+0x1bc/0x264 [] kmemdup+0x38/0x64 [] mwifiex_fill_new_bss_desc+0x3c/0x130 [mwifiex] [] mwifiex_save_curr_bcn+0x4ec/0x640 [mwifiex] [] mwifiex_handle_event_ext_scan_report+0x1d4/0x268 [mwifiex] [] mwifiex_process_sta_event+0x378/0x898 [mwifiex] [] mwifiex_process_event+0x1a8/0x1e8 [mwifiex] [] mwifiex_main_process+0x258/0x534 [mwifiex] [] 0xffffffbffc258858 [] process_sdio_pending_irqs+0xf8/0x160 [] sdio_irq_thread+0x9c/0x1a4 [] kthread+0xf4/0x100 [] ret_from_fork+0xc/0x50 [] 0xffffffffffffffff Signed-off-by: Ricky Liang Acked-by: Amitkumar Karwar Reviewed-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/scan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 97c9765..98ce072 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -1671,6 +1671,10 @@ static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv, } done: + /* beacon_ie buffer was allocated in function + * mwifiex_fill_new_bss_desc(). Free it now. + */ + kfree(bss_desc->beacon_buf); kfree(bss_desc); return 0; } -- cgit v1.1 From bb07fafaec70183f1dcd3d4a1459abbc409753e2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 16 Nov 2016 05:49:22 -0800 Subject: net/mlx4_en: remove napi_hash_del() call There is no need calling napi_hash_del()+synchronize_rcu() before calling netif_napi_del() netif_napi_del() does this already. Using napi_hash_del() in a driver is useful only when dealing with a batch of NAPI structures, so that a single synchronize_rcu() can be used. mlx4_en_deactivate_cq() is deactivating a single NAPI. Signed-off-by: Eric Dumazet Cc: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_cq.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 03f05c4..09dd377 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -185,10 +185,6 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq) void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) { napi_disable(&cq->napi); - if (cq->type == RX) { - napi_hash_del(&cq->napi); - synchronize_rcu(); - } netif_napi_del(&cq->napi); mlx4_cq_free(priv->mdev->dev, &cq->mcq); -- cgit v1.1 From d30d9ccbfac7cf9a12a088d57aaf0891732e2bca Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 16 Nov 2016 06:21:34 -0800 Subject: net/mlx5e: remove napi_hash_del() calls Calling napi_hash_del() after netif_napi_del() is pointless. Signed-off-by: Eric Dumazet Cc: Saeed Mahameed Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 52b4fd5..00579ce 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1533,7 +1533,6 @@ err_close_icosq_cq: err_napi_del: netif_napi_del(&c->napi); - napi_hash_del(&c->napi); kfree(c); return err; @@ -1554,9 +1553,6 @@ static void mlx5e_close_channel(struct mlx5e_channel *c) mlx5e_close_cq(&c->icosq.cq); netif_napi_del(&c->napi); - napi_hash_del(&c->napi); - synchronize_rcu(); - kfree(c); } -- cgit v1.1 From 29c58472ec2af073c4f858a61236fbedfc2923ac Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 17 Nov 2016 09:48:30 -0800 Subject: net_sched: sch_fq: use hash_ptr() When I wrote sch_fq.c, hash_ptr() on 64bit arches was awful, and I chose hash_32(). Linus Torvalds and George Spelvin fixed this issue, so we can use hash_ptr() to get more entropy on 64bit arches with Terabytes of memory, and avoid the cast games. Signed-off-by: Eric Dumazet Cc: Hugh Dickins Signed-off-by: David S. Miller --- net/sched/sch_fq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index 18e7524..86309a3 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -245,7 +245,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) skb_orphan(skb); } - root = &q->fq_root[hash_32((u32)(long)sk, q->fq_trees_log)]; + root = &q->fq_root[hash_ptr(sk, q->fq_trees_log)]; if (q->flows >= (2U << q->fq_trees_log) && q->inactive_flows > q->flows/2) @@ -599,7 +599,7 @@ static void fq_rehash(struct fq_sched_data *q, kmem_cache_free(fq_flow_cachep, of); continue; } - nroot = &new_array[hash_32((u32)(long)of->sk, new_log)]; + nroot = &new_array[hash_ptr(of->sk, new_log)]; np = &nroot->rb_node; parent = NULL; -- cgit v1.1 From 38ddc59d65b6d97b760ba34aa164700d0fc2e933 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Wed, 16 Nov 2016 20:09:39 +0100 Subject: net: stmmac: replace all pr_xxx by their netdev_xxx counterpart The stmmac driver use lots of pr_xxx functions to print information. This is bad since we cannot know which device logs the information. (moreover if two stmmac device are present) Furthermore, it seems that it assumes wrongly that all logs will always be subsequent by using a dev_xxx then some indented pr_xxx like this: kernel: sun7i-dwmac 1c50000.ethernet: no reset control found kernel: Ring mode enabled kernel: No HW DMA feature register supported kernel: Normal descriptors kernel: TX Checksum insertion supported So this patch replace all pr_xxx by their netdev_xxx counterpart. Excepts for some printing where netdev "cause" unpretty output like: sun7i-dwmac 1c50000.ethernet (unnamed net_device) (uninitialized): no reset control found In those case, I keep dev_xxx. In the same time I remove some "stmmac:" print since this will be a duplicate with that dev_xxx displays. Signed-off-by: Corentin Labbe Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 204 ++++++++++++---------- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 14 +- 2 files changed, 123 insertions(+), 95 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 8eb12353..791daf4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -305,7 +305,7 @@ bool stmmac_eee_init(struct stmmac_priv *priv) */ spin_lock_irqsave(&priv->lock, flags); if (priv->eee_active) { - pr_debug("stmmac: disable EEE\n"); + netdev_dbg(priv->dev, "disable EEE\n"); del_timer_sync(&priv->eee_ctrl_timer); priv->hw->mac->set_eee_timer(priv->hw, 0, tx_lpi_timer); @@ -334,7 +334,7 @@ bool stmmac_eee_init(struct stmmac_priv *priv) ret = true; spin_unlock_irqrestore(&priv->lock, flags); - pr_debug("stmmac: Energy-Efficient Ethernet initialized\n"); + netdev_dbg(priv->dev, "Energy-Efficient Ethernet initialized\n"); } out: return ret; @@ -456,8 +456,8 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) sizeof(struct hwtstamp_config))) return -EFAULT; - pr_debug("%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n", - __func__, config.flags, config.tx_type, config.rx_filter); + netdev_dbg(priv->dev, "%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n", + __func__, config.flags, config.tx_type, config.rx_filter); /* reserved for future extensions */ if (config.flags) @@ -756,8 +756,9 @@ static void stmmac_adjust_link(struct net_device *dev) break; default: if (netif_msg_link(priv)) - pr_warn("%s: Speed (%d) not 10/100\n", - dev->name, phydev->speed); + netdev_warn(priv->dev, + "Speed (%d) not 10/100\n", + phydev->speed); break; } @@ -810,10 +811,10 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) (interface == PHY_INTERFACE_MODE_RGMII_ID) || (interface == PHY_INTERFACE_MODE_RGMII_RXID) || (interface == PHY_INTERFACE_MODE_RGMII_TXID)) { - pr_debug("STMMAC: PCS RGMII support enable\n"); + netdev_dbg(priv->dev, "PCS RGMII support enabled\n"); priv->hw->pcs = STMMAC_PCS_RGMII; } else if (interface == PHY_INTERFACE_MODE_SGMII) { - pr_debug("STMMAC: PCS SGMII support enable\n"); + netdev_dbg(priv->dev, "PCS SGMII support enabled\n"); priv->hw->pcs = STMMAC_PCS_SGMII; } } @@ -848,15 +849,15 @@ static int stmmac_init_phy(struct net_device *dev) snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, priv->plat->phy_addr); - pr_debug("stmmac_init_phy: trying to attach to %s\n", - phy_id_fmt); + netdev_dbg(priv->dev, "stmmac_init_phy: trying to attach to %s\n", + phy_id_fmt); phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface); } if (IS_ERR_OR_NULL(phydev)) { - pr_err("%s: Could not attach to PHY\n", dev->name); + netdev_err(priv->dev, "Could not attach to PHY\n"); if (!phydev) return -ENODEV; @@ -889,8 +890,9 @@ static int stmmac_init_phy(struct net_device *dev) if (phydev->is_pseudo_fixed_link) phydev->irq = PHY_POLL; - pr_debug("stmmac_init_phy: %s: attached to PHY (UID 0x%x)" - " Link = %d\n", dev->name, phydev->phy_id, phydev->link); + netdev_dbg(priv->dev, + "stmmac_init_phy: attached to PHY (UID 0x%x) Link = %d\n", + phydev->phy_id, phydev->link); return 0; } @@ -976,7 +978,8 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); if (!skb) { - pr_err("%s: Rx init fails; skb is NULL\n", __func__); + netdev_err(priv->dev, + "%s: Rx init fails; skb is NULL\n", __func__); return -ENOMEM; } priv->rx_skbuff[i] = skb; @@ -984,7 +987,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, priv->dma_buf_sz, DMA_FROM_DEVICE); if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[i])) { - pr_err("%s: DMA mapping error\n", __func__); + netdev_err(priv->dev, "%s: DMA mapping error\n", __func__); dev_kfree_skb_any(skb); return -EINVAL; } @@ -1035,11 +1038,12 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) priv->dma_buf_sz = bfsize; if (netif_msg_probe(priv)) { - pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__, - (u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy); + netdev_dbg(priv->dev, "(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", + __func__, (u32)priv->dma_rx_phy, + (u32)priv->dma_tx_phy); /* RX INITIALIZATION */ - pr_debug("\tSKB addresses:\nskb\t\tskb data\tdma data\n"); + netdev_dbg(priv->dev, "SKB addresses:\nskb\t\tskb data\tdma data\n"); } for (i = 0; i < DMA_RX_SIZE; i++) { struct dma_desc *p; @@ -1053,7 +1057,8 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) goto err_init_rx_buffers; if (netif_msg_probe(priv)) - pr_debug("[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i], + netdev_dbg(priv->dev, "[%p]\t[%p]\t[%x]\n", + priv->rx_skbuff[i], priv->rx_skbuff[i]->data, (unsigned int)priv->rx_skbuff_dma[i]); } @@ -1386,7 +1391,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) if (netif_queue_stopped(priv->dev) && stmmac_tx_avail(priv) > STMMAC_TX_THRESH) { if (netif_msg_tx_done(priv)) - pr_debug("%s: restart transmit\n", __func__); + netdev_dbg(priv->dev, "%s: restart transmit\n", + __func__); netif_wake_queue(priv->dev); } netif_tx_unlock(priv->dev); @@ -1497,7 +1503,7 @@ static void stmmac_mmc_setup(struct stmmac_priv *priv) dwmac_mmc_ctrl(priv->mmcaddr, mode); memset(&priv->mmc, 0, sizeof(struct stmmac_counters)); } else - pr_info(" No MAC Management Counters available\n"); + netdev_info(priv->dev, "No MAC Management Counters available\n"); } /** @@ -1510,18 +1516,18 @@ static void stmmac_mmc_setup(struct stmmac_priv *priv) static void stmmac_selec_desc_mode(struct stmmac_priv *priv) { if (priv->plat->enh_desc) { - pr_info(" Enhanced/Alternate descriptors\n"); + dev_info(priv->device, "Enhanced/Alternate descriptors\n"); /* GMAC older than 3.50 has no extended descriptors */ if (priv->synopsys_id >= DWMAC_CORE_3_50) { - pr_info("\tEnabled extended descriptors\n"); + dev_info(priv->device, "Enabled extended descriptors\n"); priv->extend_desc = 1; } else - pr_warn("Extended descriptors not supported\n"); + dev_warn(priv->device, "Extended descriptors not supported\n"); priv->hw->desc = &enh_desc_ops; } else { - pr_info(" Normal descriptors\n"); + dev_info(priv->device, "Normal descriptors\n"); priv->hw->desc = &ndesc_ops; } } @@ -1562,8 +1568,8 @@ static void stmmac_check_ether_addr(struct stmmac_priv *priv) priv->dev->dev_addr, 0); if (!is_valid_ether_addr(priv->dev->dev_addr)) eth_hw_addr_random(priv->dev); - pr_info("%s: device MAC address %pM\n", priv->dev->name, - priv->dev->dev_addr); + netdev_info(priv->dev, "device MAC address %pM\n", + priv->dev->dev_addr); } } @@ -1671,7 +1677,8 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) /* DMA initialization and SW reset */ ret = stmmac_init_dma_engine(priv); if (ret < 0) { - pr_err("%s: DMA engine initialization failed\n", __func__); + netdev_err(priv->dev, "%s: DMA engine initialization failed\n", + __func__); return ret; } @@ -1700,7 +1707,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) ret = priv->hw->mac->rx_ipc(priv->hw); if (!ret) { - pr_warn(" RX IPC Checksum Offload disabled\n"); + netdev_warn(priv->dev, "RX IPC Checksum Offload disabled\n"); priv->plat->rx_coe = STMMAC_RX_COE_NONE; priv->hw->rx_csum = 0; } @@ -1725,10 +1732,11 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) #ifdef CONFIG_DEBUG_FS ret = stmmac_init_fs(dev); if (ret < 0) - pr_warn("%s: failed debugFS registration\n", __func__); + netdev_warn(priv->dev, "%s: failed debugFS registration\n", + __func__); #endif /* Start the ball rolling... */ - pr_debug("%s: DMA RX/TX processes started...\n", dev->name); + netdev_dbg(priv->dev, "DMA RX/TX processes started...\n"); priv->hw->dma->start_tx(priv->ioaddr); priv->hw->dma->start_rx(priv->ioaddr); @@ -1783,8 +1791,9 @@ static int stmmac_open(struct net_device *dev) priv->hw->pcs != STMMAC_PCS_RTBI) { ret = stmmac_init_phy(dev); if (ret) { - pr_err("%s: Cannot attach to PHY (error: %d)\n", - __func__, ret); + netdev_err(priv->dev, + "%s: Cannot attach to PHY (error: %d)\n", + __func__, ret); return ret; } } @@ -1798,19 +1807,21 @@ static int stmmac_open(struct net_device *dev) ret = alloc_dma_desc_resources(priv); if (ret < 0) { - pr_err("%s: DMA descriptors allocation failed\n", __func__); + netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", + __func__); goto dma_desc_error; } ret = init_dma_desc_rings(dev, GFP_KERNEL); if (ret < 0) { - pr_err("%s: DMA descriptors initialization failed\n", __func__); + netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", + __func__); goto init_error; } ret = stmmac_hw_setup(dev, true); if (ret < 0) { - pr_err("%s: Hw setup failed\n", __func__); + netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); goto init_error; } @@ -1823,8 +1834,9 @@ static int stmmac_open(struct net_device *dev) ret = request_irq(dev->irq, stmmac_interrupt, IRQF_SHARED, dev->name, dev); if (unlikely(ret < 0)) { - pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n", - __func__, dev->irq, ret); + netdev_err(priv->dev, + "%s: ERROR: allocating the IRQ %d (error: %d)\n", + __func__, dev->irq, ret); goto init_error; } @@ -1833,8 +1845,9 @@ static int stmmac_open(struct net_device *dev) ret = request_irq(priv->wol_irq, stmmac_interrupt, IRQF_SHARED, dev->name, dev); if (unlikely(ret < 0)) { - pr_err("%s: ERROR: allocating the WoL IRQ %d (%d)\n", - __func__, priv->wol_irq, ret); + netdev_err(priv->dev, + "%s: ERROR: allocating the WoL IRQ %d (%d)\n", + __func__, priv->wol_irq, ret); goto wolirq_error; } } @@ -1844,8 +1857,9 @@ static int stmmac_open(struct net_device *dev) ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED, dev->name, dev); if (unlikely(ret < 0)) { - pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n", - __func__, priv->lpi_irq, ret); + netdev_err(priv->dev, + "%s: ERROR: allocating the LPI IRQ %d (%d)\n", + __func__, priv->lpi_irq, ret); goto lpiirq_error; } } @@ -2008,7 +2022,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (!netif_queue_stopped(dev)) { netif_stop_queue(dev); /* This is a hard error, log it. */ - pr_err("%s: Tx Ring full when queue awake\n", __func__); + netdev_err(priv->dev, + "%s: Tx Ring full when queue awake\n", + __func__); } spin_unlock(&priv->tx_lock); return NETDEV_TX_BUSY; @@ -2082,7 +2098,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { if (netif_msg_hw(priv)) - pr_debug("%s: stop transmitted packets\n", __func__); + netdev_dbg(priv->dev, "%s: stop transmitted packets\n", + __func__); netif_stop_queue(dev); } @@ -2188,7 +2205,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (!netif_queue_stopped(dev)) { netif_stop_queue(dev); /* This is a hard error, log it. */ - pr_err("%s: Tx Ring full when queue awake\n", __func__); + netdev_err(priv->dev, + "%s: Tx Ring full when queue awake\n", + __func__); } return NETDEV_TX_BUSY; } @@ -2263,9 +2282,10 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (netif_msg_pktdata(priv)) { void *tx_head; - pr_debug("%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d", - __func__, priv->cur_tx, priv->dirty_tx, first_entry, - entry, first, nfrags); + netdev_dbg(priv->dev, + "%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d", + __func__, priv->cur_tx, priv->dirty_tx, first_entry, + entry, first, nfrags); if (priv->extend_desc) tx_head = (void *)priv->dma_etx; @@ -2274,13 +2294,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->desc->display_ring(tx_head, DMA_TX_SIZE, false); - pr_debug(">>> frame to be transmitted: "); + netdev_dbg(priv->dev, ">>> frame to be transmitted: "); print_pkt(skb->data, skb->len); } if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { if (netif_msg_hw(priv)) - pr_debug("%s: stop transmitted packets\n", __func__); + netdev_dbg(priv->dev, + "%s: stop transmitted packets\n", __func__); netif_stop_queue(dev); } @@ -2357,7 +2378,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) dma_map_err: spin_unlock(&priv->tx_lock); - dev_err(priv->device, "Tx dma map failed\n"); + netdev_err(priv->dev, "Tx DMA map failed\n"); dev_kfree_skb(skb); priv->dev->stats.tx_dropped++; return NETDEV_TX_OK; @@ -2428,7 +2449,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) DMA_FROM_DEVICE); if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[entry])) { - dev_err(priv->device, "Rx dma map failed\n"); + netdev_err(priv->dev, "Rx DMA map failed\n"); dev_kfree_skb(skb); break; } @@ -2446,7 +2467,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) priv->rx_zeroc_thresh--; if (netif_msg_rx_status(priv)) - pr_debug("\trefill entry #%d\n", entry); + netdev_dbg(priv->dev, + "refill entry #%d\n", entry); } wmb(); @@ -2479,7 +2501,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) if (netif_msg_rx_status(priv)) { void *rx_head; - pr_debug("%s: descriptor ring:\n", __func__); + netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__); if (priv->extend_desc) rx_head = (void *)priv->dma_erx; else @@ -2549,9 +2571,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) * ignored */ if (frame_len > priv->dma_buf_sz) { - pr_err("%s: len %d larger than size (%d)\n", - priv->dev->name, frame_len, - priv->dma_buf_sz); + netdev_err(priv->dev, + "len %d larger than size (%d)\n", + frame_len, priv->dma_buf_sz); priv->dev->stats.rx_length_errors++; break; } @@ -2563,11 +2585,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) frame_len -= ETH_FCS_LEN; if (netif_msg_rx_status(priv)) { - pr_debug("\tdesc: %p [entry %d] buff=0x%x\n", - p, entry, des); + netdev_dbg(priv->dev, "\tdesc: %p [entry %d] buff=0x%x\n", + p, entry, des); if (frame_len > ETH_FRAME_LEN) - pr_debug("\tframe size %d, COE: %d\n", - frame_len, status); + netdev_dbg(priv->dev, "frame size %d, COE: %d\n", + frame_len, status); } /* The zero-copy is always used for all the sizes @@ -2604,8 +2626,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) } else { skb = priv->rx_skbuff[entry]; if (unlikely(!skb)) { - pr_err("%s: Inconsistent Rx chain\n", - priv->dev->name); + netdev_err(priv->dev, + "%s: Inconsistent Rx chain\n", + priv->dev->name); priv->dev->stats.rx_dropped++; break; } @@ -2623,7 +2646,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) stmmac_get_rx_hwtstamp(priv, entry, skb); if (netif_msg_pktdata(priv)) { - pr_debug("frame received (%dbytes)", frame_len); + netdev_dbg(priv->dev, "frame received (%dbytes)", + frame_len); print_pkt(skb->data, frame_len); } @@ -2720,8 +2744,10 @@ static void stmmac_set_rx_mode(struct net_device *dev) */ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) { + struct stmmac_priv *priv = netdev_priv(dev); + if (netif_running(dev)) { - pr_err("%s: must be stopped to change its MTU\n", dev->name); + netdev_err(priv->dev, "must be stopped to change its MTU\n"); return -EBUSY; } @@ -2800,7 +2826,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) pm_wakeup_event(priv->device, 0); if (unlikely(!dev)) { - pr_err("%s: invalid dev pointer\n", __func__); + netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__); return IRQ_NONE; } @@ -3032,8 +3058,7 @@ static int stmmac_init_fs(struct net_device *dev) priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir); if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) { - pr_err("ERROR %s/%s, debugfs create directory failed\n", - STMMAC_RESOURCE_NAME, dev->name); + netdev_err(priv->dev, "ERROR failed to create debugfs directory\n"); return -ENOMEM; } @@ -3045,7 +3070,7 @@ static int stmmac_init_fs(struct net_device *dev) &stmmac_rings_status_fops); if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) { - pr_info("ERROR creating stmmac ring debugfs file\n"); + netdev_err(priv->dev, "ERROR creating stmmac ring debugfs file\n"); debugfs_remove_recursive(priv->dbgfs_dir); return -ENOMEM; @@ -3057,7 +3082,7 @@ static int stmmac_init_fs(struct net_device *dev) dev, &stmmac_dma_cap_fops); if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) { - pr_info("ERROR creating stmmac MMC debugfs file\n"); + netdev_err(priv->dev, "ERROR creating stmmac MMC debugfs file\n"); debugfs_remove_recursive(priv->dbgfs_dir); return -ENOMEM; @@ -3129,11 +3154,11 @@ static int stmmac_hw_init(struct stmmac_priv *priv) } else { if (chain_mode) { priv->hw->mode = &chain_mode_ops; - pr_info(" Chain mode enabled\n"); + dev_info(priv->device, "Chain mode enabled\n"); priv->mode = STMMAC_CHAIN_MODE; } else { priv->hw->mode = &ring_mode_ops; - pr_info(" Ring mode enabled\n"); + dev_info(priv->device, "Ring mode enabled\n"); priv->mode = STMMAC_RING_MODE; } } @@ -3141,7 +3166,7 @@ static int stmmac_hw_init(struct stmmac_priv *priv) /* Get the HW capability (new GMAC newer than 3.50a) */ priv->hw_cap_support = stmmac_get_hw_features(priv); if (priv->hw_cap_support) { - pr_info(" DMA HW capability register supported"); + dev_info(priv->device, "DMA HW capability register supported\n"); /* We can override some gmac/dma configuration fields: e.g. * enh_desc, tx_coe (e.g. that are passed through the @@ -3166,8 +3191,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv) else if (priv->dma_cap.rx_coe_type1) priv->plat->rx_coe = STMMAC_RX_COE_TYPE1; - } else - pr_info(" No HW DMA feature register supported"); + } else { + dev_info(priv->device, "No HW DMA feature register supported\n"); + } /* To use alternate (extended), normal or GMAC4 descriptor structures */ if (priv->synopsys_id >= DWMAC_CORE_4_00) @@ -3177,20 +3203,20 @@ static int stmmac_hw_init(struct stmmac_priv *priv) if (priv->plat->rx_coe) { priv->hw->rx_csum = priv->plat->rx_coe; - pr_info(" RX Checksum Offload Engine supported\n"); + dev_info(priv->device, "RX Checksum Offload Engine supported\n"); if (priv->synopsys_id < DWMAC_CORE_4_00) - pr_info("\tCOE Type %d\n", priv->hw->rx_csum); + dev_info(priv->device, "COE Type %d\n", priv->hw->rx_csum); } if (priv->plat->tx_coe) - pr_info(" TX Checksum insertion supported\n"); + dev_info(priv->device, "TX Checksum insertion supported\n"); if (priv->plat->pmt) { - pr_info(" Wake-Up On Lan supported\n"); + dev_info(priv->device, "Wake-Up On Lan supported\n"); device_set_wakeup_capable(priv->device, 1); } if (priv->dma_cap.tsoen) - pr_info(" TSO supported\n"); + dev_info(priv->device, "TSO supported\n"); return 0; } @@ -3249,8 +3275,8 @@ int stmmac_dvr_probe(struct device *device, priv->stmmac_clk = devm_clk_get(priv->device, STMMAC_RESOURCE_NAME); if (IS_ERR(priv->stmmac_clk)) { - dev_warn(priv->device, "%s: warning: cannot get CSR clock\n", - __func__); + netdev_warn(priv->dev, "%s: warning: cannot get CSR clock\n", + __func__); /* If failed to obtain stmmac_clk and specific clk_csr value * is NOT passed from the platform, probe fail. */ @@ -3299,7 +3325,7 @@ int stmmac_dvr_probe(struct device *device, if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) { ndev->hw_features |= NETIF_F_TSO; priv->tso = true; - pr_info(" TSO feature enabled\n"); + dev_info(priv->device, "TSO feature enabled\n"); } ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA; ndev->watchdog_timeo = msecs_to_jiffies(watchdog); @@ -3328,7 +3354,7 @@ int stmmac_dvr_probe(struct device *device, */ if ((priv->synopsys_id >= DWMAC_CORE_3_50) && (!priv->plat->riwt_off)) { priv->use_riwt = 1; - pr_info(" Enable RX Mitigation via HW Watchdog Timer\n"); + netdev_info(priv->dev, "Enable RX Mitigation via HW Watchdog Timer\n"); } netif_napi_add(ndev, &priv->napi, stmmac_poll, 64); @@ -3338,7 +3364,8 @@ int stmmac_dvr_probe(struct device *device, ret = register_netdev(ndev); if (ret) { - pr_err("%s: ERROR %i registering the device\n", __func__, ret); + netdev_err(priv->dev, "%s: ERROR %i registering the device\n", + __func__, ret); goto error_netdev_register; } @@ -3361,8 +3388,9 @@ int stmmac_dvr_probe(struct device *device, /* MDIO bus Registration */ ret = stmmac_mdio_register(ndev); if (ret < 0) { - pr_debug("%s: MDIO bus (id: %d) registration failed", - __func__, priv->plat->bus_id); + netdev_err(priv->dev, + "%s: MDIO bus (id: %d) registration failed", + __func__, priv->plat->bus_id); goto error_mdio_register; } } @@ -3395,7 +3423,7 @@ int stmmac_dvr_remove(struct device *dev) struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); - pr_info("%s:\n\tremoving driver", __func__); + netdev_info(priv->dev, "%s: removing driver", __func__); priv->hw->dma->stop_rx(priv->ioaddr); priv->hw->dma->stop_tx(priv->ioaddr); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index ec29585..e3216e5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -260,7 +260,7 @@ int stmmac_mdio_reset(struct mii_bus *bus) #endif if (data->phy_reset) { - pr_debug("stmmac_mdio_reset: calling phy_reset\n"); + netdev_dbg(ndev, "stmmac_mdio_reset: calling phy_reset\n"); data->phy_reset(priv->plat->bsp_priv); } @@ -325,7 +325,7 @@ int stmmac_mdio_register(struct net_device *ndev) else err = mdiobus_register(new_bus); if (err != 0) { - pr_err("%s: Cannot register as MDIO bus\n", new_bus->name); + netdev_err(ndev, "Cannot register the MDIO bus\n"); goto bus_register_fail; } @@ -372,16 +372,16 @@ int stmmac_mdio_register(struct net_device *ndev) irq_str = irq_num; break; } - pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n", - ndev->name, phydev->phy_id, addr, - irq_str, phydev_name(phydev), - act ? " active" : ""); + netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n", + phydev->phy_id, addr, + irq_str, phydev_name(phydev), + act ? " active" : ""); found = 1; } } if (!found && !mdio_node) { - pr_warn("%s: No PHY found\n", ndev->name); + netdev_warn(ndev, "No PHY found\n"); mdiobus_unregister(new_bus); mdiobus_free(new_bus); return -ENODEV; -- cgit v1.1 From de9a2165a576ca262d8cb68d8c9c016b8ab3678f Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Wed, 16 Nov 2016 20:09:40 +0100 Subject: net: stmmac: replace hardcoded function name by __func__ Some printing have the function name hardcoded. It is better to use __func__ instead. Signed-off-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 791daf4..d160bdb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -849,7 +849,7 @@ static int stmmac_init_phy(struct net_device *dev) snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, priv->plat->phy_addr); - netdev_dbg(priv->dev, "stmmac_init_phy: trying to attach to %s\n", + netdev_dbg(priv->dev, "%s: trying to attach to %s\n", __func__, phy_id_fmt); phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, @@ -890,9 +890,8 @@ static int stmmac_init_phy(struct net_device *dev) if (phydev->is_pseudo_fixed_link) phydev->irq = PHY_POLL; - netdev_dbg(priv->dev, - "stmmac_init_phy: attached to PHY (UID 0x%x) Link = %d\n", - phydev->phy_id, phydev->link); + netdev_dbg(priv->dev, "%s: attached to PHY (UID 0x%x) Link = %d\n", + __func__, phydev->phy_id, phydev->link); return 0; } -- cgit v1.1 From b3e51069627e2b9439757590d7b82be0d22c7779 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Wed, 16 Nov 2016 20:09:41 +0100 Subject: net: stmmac: replace if (netif_msg_type) by their netif_xxx counterpart As sugested by Joe Perches, we could replace all if (netif_msg_type(priv)) dev_xxx(priv->devices, ...) by the simpler macro netif_xxx(priv, hw, priv->dev, ...) Signed-off-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 49 ++++++++++------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d160bdb..fbd1cd7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -755,10 +755,9 @@ static void stmmac_adjust_link(struct net_device *dev) stmmac_hw_fix_mac_speed(priv); break; default: - if (netif_msg_link(priv)) - netdev_warn(priv->dev, - "Speed (%d) not 10/100\n", - phydev->speed); + netif_warn(priv, link, priv->dev, + "Speed (%d) not 10/100\n", + phydev->speed); break; } @@ -1036,14 +1035,14 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) priv->dma_buf_sz = bfsize; - if (netif_msg_probe(priv)) { - netdev_dbg(priv->dev, "(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", - __func__, (u32)priv->dma_rx_phy, - (u32)priv->dma_tx_phy); + netif_dbg(priv, probe, priv->dev, + "(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", + __func__, (u32)priv->dma_rx_phy, (u32)priv->dma_tx_phy); + + /* RX INITIALIZATION */ + netif_dbg(priv, probe, priv->dev, + "SKB addresses:\nskb\t\tskb data\tdma data\n"); - /* RX INITIALIZATION */ - netdev_dbg(priv->dev, "SKB addresses:\nskb\t\tskb data\tdma data\n"); - } for (i = 0; i < DMA_RX_SIZE; i++) { struct dma_desc *p; if (priv->extend_desc) @@ -1055,11 +1054,9 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) if (ret) goto err_init_rx_buffers; - if (netif_msg_probe(priv)) - netdev_dbg(priv->dev, "[%p]\t[%p]\t[%x]\n", - priv->rx_skbuff[i], - priv->rx_skbuff[i]->data, - (unsigned int)priv->rx_skbuff_dma[i]); + netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n", + priv->rx_skbuff[i], priv->rx_skbuff[i]->data, + (unsigned int)priv->rx_skbuff_dma[i]); } priv->cur_rx = 0; priv->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); @@ -1389,9 +1386,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) netif_tx_lock(priv->dev); if (netif_queue_stopped(priv->dev) && stmmac_tx_avail(priv) > STMMAC_TX_THRESH) { - if (netif_msg_tx_done(priv)) - netdev_dbg(priv->dev, "%s: restart transmit\n", - __func__); + netif_dbg(priv, tx_done, priv->dev, + "%s: restart transmit\n", __func__); netif_wake_queue(priv->dev); } netif_tx_unlock(priv->dev); @@ -2096,9 +2092,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { - if (netif_msg_hw(priv)) - netdev_dbg(priv->dev, "%s: stop transmitted packets\n", - __func__); + netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", + __func__); netif_stop_queue(dev); } @@ -2298,9 +2293,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) } if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { - if (netif_msg_hw(priv)) - netdev_dbg(priv->dev, - "%s: stop transmitted packets\n", __func__); + netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", + __func__); netif_stop_queue(dev); } @@ -2465,9 +2459,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) if (priv->rx_zeroc_thresh > 0) priv->rx_zeroc_thresh--; - if (netif_msg_rx_status(priv)) - netdev_dbg(priv->dev, - "refill entry #%d\n", entry); + netif_dbg(priv, rx_status, priv->dev, + "refill entry #%d\n", entry); } wmb(); -- cgit v1.1 From 315ca6d98ed3fd7abe235637c28dd2f9f0a77795 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 16 Nov 2016 13:29:48 -0800 Subject: RDS: TCP: set RDS_FLAG_RETRANSMITTED in cp_retrans list As noted in rds_recv_incoming() sequence numbers on data packets can decreas for the failover case, and the Rx path is equipped to recover from this, if the RDS_FLAG_RETRANSMITTED is set on the rds header of an incoming message with a suspect sequence number. The RDS_FLAG_RETRANSMITTED is predicated on the RDS_FLAG_RETRANSMITTED flag in the rds_message, so make sure the flag is set on messages queued for retransmission. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/tcp_send.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c index 89d09b4..dcf4742 100644 --- a/net/rds/tcp_send.c +++ b/net/rds/tcp_send.c @@ -100,6 +100,9 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, set_bit(RDS_MSG_HAS_ACK_SEQ, &rm->m_flags); tc->t_last_expected_una = rm->m_ack_seq + 1; + if (test_bit(RDS_MSG_RETRANSMITTED, &rm->m_flags)) + rm->m_inc.i_hdr.h_flags |= RDS_FLAG_RETRANSMITTED; + rdsdebug("rm %p tcp nxt %u ack_seq %llu\n", rm, rds_tcp_snd_nxt(tc), (unsigned long long)rm->m_ack_seq); -- cgit v1.1 From 905dd4184e0732de41d6ee3c7b06e0cfdd9f0aad Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 16 Nov 2016 13:29:49 -0800 Subject: RDS: TCP: Track peer's connection generation number The RDS transport has to be able to distinguish between two types of failure events: (a) when the transport fails (e.g., TCP connection reset) but the RDS socket/connection layer on both sides stays the same (b) when the peer's RDS layer itself resets (e.g., due to module reload or machine reboot at the peer) In case (a) both sides must reconnect and continue the RDS messaging without any message loss or disruption to the message sequence numbers, and this is achieved by rds_send_path_reset(). In case (b) we should reset all rds_connection state to the new incarnation of the peer. Examples of state that needs to be reset are next expected rx sequence number from, or messages to be retransmitted to, the new incarnation of the peer. To achieve this, the RDS handshake probe added as part of commit 5916e2c1554f ("RDS: TCP: Enable multipath RDS for TCP") is enhanced so that sender and receiver of the RDS ping-probe will add a generation number as part of the RDS_EXTHDR_GEN_NUM extension header. Each peer stores local and remote generation numbers as part of each rds_connection. Changes in generation number will be detected via incoming handshake probe ping request or response and will allow the receiver to reset rds_connection state. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/af_rds.c | 4 ++++ net/rds/connection.c | 2 ++ net/rds/message.c | 1 + net/rds/rds.h | 8 +++++++- net/rds/recv.c | 36 ++++++++++++++++++++++++++++++++++++ net/rds/send.c | 9 +++++++-- 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index 6beaeb1..2ac1e61 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -605,10 +605,14 @@ static void rds_exit(void) } module_exit(rds_exit); +u32 rds_gen_num; + static int rds_init(void) { int ret; + net_get_random_once(&rds_gen_num, sizeof(rds_gen_num)); + ret = rds_bind_lock_init(); if (ret) goto out; diff --git a/net/rds/connection.c b/net/rds/connection.c index 13f459d..b86e188 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -269,6 +269,8 @@ static struct rds_connection *__rds_conn_create(struct net *net, kmem_cache_free(rds_conn_slab, conn); conn = found; } else { + conn->c_my_gen_num = rds_gen_num; + conn->c_peer_gen_num = 0; hlist_add_head_rcu(&conn->c_hash_node, head); rds_cong_add_conn(conn); rds_conn_count++; diff --git a/net/rds/message.c b/net/rds/message.c index 6cb9106..49bfb51 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -42,6 +42,7 @@ static unsigned int rds_exthdr_size[__RDS_EXTHDR_MAX] = { [RDS_EXTHDR_RDMA] = sizeof(struct rds_ext_header_rdma), [RDS_EXTHDR_RDMA_DEST] = sizeof(struct rds_ext_header_rdma_dest), [RDS_EXTHDR_NPATHS] = sizeof(u16), +[RDS_EXTHDR_GEN_NUM] = sizeof(u32), }; diff --git a/net/rds/rds.h b/net/rds/rds.h index 4121e18..ebbf909 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -151,6 +151,9 @@ struct rds_connection { struct rds_conn_path c_path[RDS_MPATH_WORKERS]; wait_queue_head_t c_hs_waitq; /* handshake waitq */ + + u32 c_my_gen_num; + u32 c_peer_gen_num; }; static inline @@ -243,7 +246,8 @@ struct rds_ext_header_rdma_dest { /* Extension header announcing number of paths. * Implicit length = 2 bytes. */ -#define RDS_EXTHDR_NPATHS 4 +#define RDS_EXTHDR_NPATHS 5 +#define RDS_EXTHDR_GEN_NUM 6 #define __RDS_EXTHDR_MAX 16 /* for now */ @@ -338,6 +342,7 @@ static inline u32 rds_rdma_cookie_offset(rds_rdma_cookie_t cookie) #define RDS_MSG_RETRANSMITTED 5 #define RDS_MSG_MAPPED 6 #define RDS_MSG_PAGEVEC 7 +#define RDS_MSG_FLUSH 8 struct rds_message { atomic_t m_refcount; @@ -664,6 +669,7 @@ void rds_cong_exit(void); struct rds_message *rds_cong_update_alloc(struct rds_connection *conn); /* conn.c */ +extern u32 rds_gen_num; int rds_conn_init(void); void rds_conn_exit(void); struct rds_connection *rds_conn_create(struct net *net, diff --git a/net/rds/recv.c b/net/rds/recv.c index cbfabdf..9d0666e 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -120,6 +120,36 @@ static void rds_recv_rcvbuf_delta(struct rds_sock *rs, struct sock *sk, /* do nothing if no change in cong state */ } +static void rds_conn_peer_gen_update(struct rds_connection *conn, + u32 peer_gen_num) +{ + int i; + struct rds_message *rm, *tmp; + unsigned long flags; + + WARN_ON(conn->c_trans->t_type != RDS_TRANS_TCP); + if (peer_gen_num != 0) { + if (conn->c_peer_gen_num != 0 && + peer_gen_num != conn->c_peer_gen_num) { + for (i = 0; i < RDS_MPATH_WORKERS; i++) { + struct rds_conn_path *cp; + + cp = &conn->c_path[i]; + spin_lock_irqsave(&cp->cp_lock, flags); + cp->cp_next_tx_seq = 1; + cp->cp_next_rx_seq = 0; + list_for_each_entry_safe(rm, tmp, + &cp->cp_retrans, + m_conn_item) { + set_bit(RDS_MSG_FLUSH, &rm->m_flags); + } + spin_unlock_irqrestore(&cp->cp_lock, flags); + } + } + conn->c_peer_gen_num = peer_gen_num; + } +} + /* * Process all extension headers that come with this message. */ @@ -163,7 +193,9 @@ static void rds_recv_hs_exthdrs(struct rds_header *hdr, union { struct rds_ext_header_version version; u16 rds_npaths; + u32 rds_gen_num; } buffer; + u32 new_peer_gen_num = 0; while (1) { len = sizeof(buffer); @@ -176,6 +208,9 @@ static void rds_recv_hs_exthdrs(struct rds_header *hdr, conn->c_npaths = min_t(int, RDS_MPATH_WORKERS, buffer.rds_npaths); break; + case RDS_EXTHDR_GEN_NUM: + new_peer_gen_num = buffer.rds_gen_num; + break; default: pr_warn_ratelimited("ignoring unknown exthdr type " "0x%x\n", type); @@ -183,6 +218,7 @@ static void rds_recv_hs_exthdrs(struct rds_header *hdr, } /* if RDS_EXTHDR_NPATHS was not found, default to a single-path */ conn->c_npaths = max_t(int, conn->c_npaths, 1); + rds_conn_peer_gen_update(conn, new_peer_gen_num); } /* rds_start_mprds() will synchronously start multiple paths when appropriate. diff --git a/net/rds/send.c b/net/rds/send.c index 896626b..77c8c6e 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -259,8 +259,9 @@ restart: * connection. * Therefore, we never retransmit messages with RDMA ops. */ - if (rm->rdma.op_active && - test_bit(RDS_MSG_RETRANSMITTED, &rm->m_flags)) { + if (test_bit(RDS_MSG_FLUSH, &rm->m_flags) || + (rm->rdma.op_active && + test_bit(RDS_MSG_RETRANSMITTED, &rm->m_flags))) { spin_lock_irqsave(&cp->cp_lock, flags); if (test_and_clear_bit(RDS_MSG_ON_CONN, &rm->m_flags)) list_move(&rm->m_conn_item, &to_be_dropped); @@ -1209,6 +1210,10 @@ rds_send_probe(struct rds_conn_path *cp, __be16 sport, rds_message_add_extension(&rm->m_inc.i_hdr, RDS_EXTHDR_NPATHS, &npaths, sizeof(npaths)); + rds_message_add_extension(&rm->m_inc.i_hdr, + RDS_EXTHDR_GEN_NUM, + &cp->cp_conn->c_my_gen_num, + sizeof(u32)); } spin_unlock_irqrestore(&cp->cp_lock, flags); -- cgit v1.1 From 1a0e100fb2c9667cea2a7d755faaa83569942f05 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 16 Nov 2016 13:29:50 -0800 Subject: RDS: TCP: Force every connection to be initiated by numerically smaller IP address When 2 RDS peers initiate an RDS-TCP connection simultaneously, there is a potential for "duelling syns" on either/both sides. See commit 241b271952eb ("RDS-TCP: Reset tcp callbacks if re-using an outgoing socket in rds_tcp_accept_one()") for a description of this condition, and the arbitration logic which ensures that the numerically large IP address in the TCP connection is bound to the RDS_TCP_PORT ("canonical ordering"). The rds_connection should not be marked as RDS_CONN_UP until the arbitration logic has converged for the following reason. The sender may start transmitting RDS datagrams as soon as RDS_CONN_UP is set, and since the sender removes all datagrams from the rds_connection's cp_retrans queue based on TCP acks. If the TCP ack was sent from a tcp socket that got reset as part of duel aribitration (but before data was delivered to the receivers RDS socket layer), the sender may end up prematurely freeing the datagram, and the datagram is no longer reliably deliverable. This patch remedies that condition by making sure that, upon receipt of 3WH completion state change notification of TCP_ESTABLISHED in rds_tcp_state_change, we mark the rds_connection as RDS_CONN_UP if, and only if, the IP addresses and ports for the connection are canonically ordered. In all other cases, rds_tcp_state_change will force an rds_conn_path_drop(), and rds_queue_reconnect() on both peers will restart the connection to ensure canonical ordering. A side-effect of enforcing this condition in rds_tcp_state_change() is that rds_tcp_accept_one_path() can now be refactored for simplicity. It is also no longer possible to encounter an RDS_CONN_UP connection in the arbitration logic in rds_tcp_accept_one(). Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/connection.c | 1 + net/rds/tcp_connect.c | 14 +++++++++++++- net/rds/tcp_listen.c | 29 ++++++++++++----------------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/net/rds/connection.c b/net/rds/connection.c index b86e188..fe9d31c 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -683,6 +683,7 @@ void rds_conn_path_connect_if_down(struct rds_conn_path *cp) !test_and_set_bit(RDS_RECONNECT_PENDING, &cp->cp_flags)) queue_delayed_work(rds_wq, &cp->cp_conn_w, 0); } +EXPORT_SYMBOL_GPL(rds_conn_path_connect_if_down); void rds_conn_connect_if_down(struct rds_connection *conn) { diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c index 05f61c5..d6839d9 100644 --- a/net/rds/tcp_connect.c +++ b/net/rds/tcp_connect.c @@ -60,7 +60,19 @@ void rds_tcp_state_change(struct sock *sk) case TCP_SYN_RECV: break; case TCP_ESTABLISHED: - rds_connect_path_complete(cp, RDS_CONN_CONNECTING); + /* Force the peer to reconnect so that we have the + * TCP ports going from . to + * .. We avoid marking the + * RDS connection as RDS_CONN_UP until the reconnect, + * to avoid RDS datagram loss. + */ + if (cp->cp_conn->c_laddr > cp->cp_conn->c_faddr && + rds_conn_path_transition(cp, RDS_CONN_CONNECTING, + RDS_CONN_ERROR)) { + rds_conn_path_drop(cp); + } else { + rds_connect_path_complete(cp, RDS_CONN_CONNECTING); + } break; case TCP_CLOSE_WAIT: case TCP_CLOSE: diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index c9c4968..f74bab3 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -83,25 +83,20 @@ struct rds_tcp_connection *rds_tcp_accept_one_path(struct rds_connection *conn) { int i; bool peer_is_smaller = (conn->c_faddr < conn->c_laddr); - int npaths = conn->c_npaths; - - if (npaths <= 1) { - struct rds_conn_path *cp = &conn->c_path[0]; - int ret; - - ret = rds_conn_path_transition(cp, RDS_CONN_DOWN, - RDS_CONN_CONNECTING); - if (!ret) - rds_conn_path_transition(cp, RDS_CONN_ERROR, - RDS_CONN_CONNECTING); - return cp->cp_transport_data; - } + int npaths = max_t(int, 1, conn->c_npaths); - /* for mprds, paths with cp_index > 0 MUST be initiated by the peer + /* for mprds, all paths MUST be initiated by the peer * with the smaller address. */ - if (!peer_is_smaller) + if (!peer_is_smaller) { + /* Make sure we initiate at least one path if this + * has not already been done; rds_start_mprds() will + * take care of additional paths, if necessary. + */ + if (npaths == 1) + rds_conn_path_connect_if_down(&conn->c_path[0]); return NULL; + } for (i = 0; i < npaths; i++) { struct rds_conn_path *cp = &conn->c_path[i]; @@ -171,8 +166,8 @@ int rds_tcp_accept_one(struct socket *sock) mutex_lock(&rs_tcp->t_conn_path_lock); cp = rs_tcp->t_cpath; conn_state = rds_conn_path_state(cp); - if (conn_state != RDS_CONN_CONNECTING && conn_state != RDS_CONN_UP && - conn_state != RDS_CONN_ERROR) + WARN_ON(conn_state == RDS_CONN_UP); + if (conn_state != RDS_CONN_CONNECTING && conn_state != RDS_CONN_ERROR) goto rst_nsk; if (rs_tcp->t_sock) { /* Need to resolve a duelling SYN between peers. -- cgit v1.1 From 6a7b1910c3c6185b42509ea12201e900a12a22b2 Mon Sep 17 00:00:00 2001 From: Shengzhen Li Date: Fri, 14 Oct 2016 15:50:09 +0530 Subject: mwifiex: add power save parameters in hs_cfg cmd This patch adds power save parameters(hs_wake_interval and hs_inactivity_timeout) in host sleep cfg cmd. Signed-off-by: Shengzhen Li Signed-off-by: Cathy Luo Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/fw.h | 10 +++++++++ drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 29 ++++++++++++++++++++------ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 4b1894b..9b3ccea 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -181,6 +181,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) #define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) #define TLV_TYPE_REPEAT_COUNT (PROPRIETARY_TLV_BASE_ID + 176) +#define TLV_TYPE_PS_PARAMS_IN_HS (PROPRIETARY_TLV_BASE_ID + 181) #define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) #define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) #define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) @@ -986,6 +987,15 @@ struct mwifiex_ps_param { __le16 delay_to_ps; }; +#define HS_DEF_WAKE_INTERVAL 100 +#define HS_DEF_INACTIVITY_TIMEOUT 50 + +struct mwifiex_ps_param_in_hs { + struct mwifiex_ie_types_header header; + __le32 hs_wake_int; + __le32 hs_inact_timeout; +}; + #define BITMAP_AUTO_DS 0x01 #define BITMAP_STA_PS 0x10 diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 2a162c3..11ddfcc 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -368,7 +368,10 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; + u8 *tlv = (u8 *)hs_cfg + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); + struct mwifiex_ps_param_in_hs *psparam_tlv = NULL; bool hs_activate = false; + u16 size; if (!hscfg_param) /* New Activate command */ @@ -385,13 +388,14 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, memcpy(((u8 *) hs_cfg) + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), adapter->arp_filter, adapter->arp_filter_size); - cmd->size = cpu_to_le16 - (adapter->arp_filter_size + - sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) - + S_DS_GEN); + size = adapter->arp_filter_size + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + S_DS_GEN; + tlv = (u8 *)hs_cfg + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + adapter->arp_filter_size; } else { - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct - host_cmd_ds_802_11_hs_cfg_enh)); + size = S_DS_GEN + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); } if (hs_activate) { hs_cfg->action = cpu_to_le16(HS_ACTIVATE); @@ -401,12 +405,25 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, hs_cfg->params.hs_config.conditions = hscfg_param->conditions; hs_cfg->params.hs_config.gpio = hscfg_param->gpio; hs_cfg->params.hs_config.gap = hscfg_param->gap; + + size += sizeof(struct mwifiex_ps_param_in_hs); + psparam_tlv = (struct mwifiex_ps_param_in_hs *)tlv; + psparam_tlv->header.type = + cpu_to_le16(TLV_TYPE_PS_PARAMS_IN_HS); + psparam_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_ps_param_in_hs) + - sizeof(struct mwifiex_ie_types_header)); + psparam_tlv->hs_wake_int = cpu_to_le32(HS_DEF_WAKE_INTERVAL); + psparam_tlv->hs_inact_timeout = + cpu_to_le32(HS_DEF_INACTIVITY_TIMEOUT); + mwifiex_dbg(adapter, CMD, "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", hs_cfg->params.hs_config.conditions, hs_cfg->params.hs_config.gpio, hs_cfg->params.hs_config.gap); } + cmd->size = cpu_to_le16(size); return 0; } -- cgit v1.1 From 14e5e937592697d830537b7f6a59d45a6aa3d51b Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 21 Oct 2016 20:45:48 +0530 Subject: mwifiex: ignore calibration data failure Firmware may reject calibration data from host for certain OTP settings. In that case, we should continue initialisation ignoring the failure. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 11ddfcc..0a54e21 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -2245,19 +2245,13 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) adapter->hs_cfg.gpio = data; } - ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, - "marvell,caldata"); - if (ret) - return -1; + mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, + "marvell,caldata"); } - if (adapter->cal_data) { - ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - if (ret) - return -1; - } + if (adapter->cal_data) + mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, NULL, true); /* Read MAC address from HW */ ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, -- cgit v1.1 From 66b9c182538e2ed11d31120e853321e4ea6f3e5a Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 26 Oct 2016 16:29:20 -0700 Subject: mwifiex: don't do unbalanced free()'ing in cleanup_if() The cleanup_if() callback is the inverse of init_if(). We allocate our 'card' interface structure in the probe() function, but we free it in cleanup_if(). That gives a few problems: (a) we leak this memory if probe() fails before we reach init_if() (b) we can't safely utilize 'card' after cleanup_if() -- namely, in remove() or suspend(), both of which might race with the cleanup paths in our asynchronous FW initialization path Solution: just use devm_kzalloc(), which will free this structure properly when the device is removed -- and drop the set_drvdata(..., NULL), since the driver core does this for us. This also removes the temptation to use drvdata == NULL as a hack for checking if the device has been "cleaned up." I *do* leave the set_drvdata(..., NULL) for the hacky SDIO mwifiex_recreate_adapter(), since the device core won't be able to clear that one for us. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 5 +---- drivers/net/wireless/marvell/mwifiex/sdio.c | 16 ++++++++++------ drivers/net/wireless/marvell/mwifiex/usb.c | 7 +------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 063c707..3047c1a 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -189,7 +189,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", pdev->vendor, pdev->device, pdev->revision); - card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL); + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; @@ -2815,7 +2815,6 @@ err_req_region0: err_set_dma_mask: pci_disable_device(pdev); err_enable_dev: - pci_set_drvdata(pdev, NULL); return ret; } @@ -2849,9 +2848,7 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) pci_disable_device(pdev); pci_release_region(pdev, 2); pci_release_region(pdev, 0); - pci_set_drvdata(pdev, NULL); } - kfree(card); } static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 807af13..e25a7f0 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -160,7 +160,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", func->vendor, func->device, func->class, func->num); - card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; @@ -193,7 +193,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) if (ret) { dev_err(&func->dev, "failed to enable function\n"); - goto err_free; + return ret; } /* device tree node parsing and platform specific configuration*/ @@ -218,8 +218,6 @@ err_disable: sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); -err_free: - kfree(card); return ret; } @@ -2248,8 +2246,6 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) kfree(card->mpa_rx.len_arr); kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); - sdio_set_drvdata(card->func, NULL); - kfree(card); } /* @@ -2299,6 +2295,14 @@ static void mwifiex_recreate_adapter(struct sdio_mmc_card *card) mwifiex_sdio_remove(func); + /* + * Normally, we would let the driver core take care of releasing these. + * But we're not letting the driver core handle this one. See above + * TODO. + */ + sdio_set_drvdata(func, NULL); + devm_kfree(&func->dev, card); + /* power cycle the adapter */ sdio_claim_host(func); mmc_hw_reset(func->card->host); diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 73eb084..57ed834 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -382,7 +382,7 @@ static int mwifiex_usb_probe(struct usb_interface *intf, struct usb_card_rec *card; u16 id_vendor, id_product, bcd_device, bcd_usb; - card = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + card = devm_kzalloc(&intf->dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; @@ -480,7 +480,6 @@ static int mwifiex_usb_probe(struct usb_interface *intf, if (ret) { pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); usb_reset_device(udev); - kfree(card); return ret; } @@ -630,11 +629,7 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) "%s: removing card\n", __func__); mwifiex_remove_card(adapter, &add_remove_card_sem); - usb_set_intfdata(intf, NULL); usb_put_dev(interface_to_usbdev(intf)); - kfree(card); - - return; } static struct usb_driver mwifiex_usb_driver = { -- cgit v1.1 From e68b6e50fa359cc5aad4d2f8ac2bdbc1a8f4fd59 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 16 Nov 2016 09:10:42 -0800 Subject: udp: enable busy polling for all sockets UDP busy polling is restricted to connected UDP sockets. This is because sk_busy_loop() only takes care of one NAPI context. There are cases where it could be extended. 1) Some hosts receive traffic on a single NIC, with one RX queue. 2) Some applications use SO_REUSEPORT and associated BPF filter to split the incoming traffic on one UDP socket per RX queue/thread/cpu 3) Some UDP sockets are used to send/receive traffic for one flow, but they do not bother with connect() This patch records the napi_id of first received skb, giving more reach to busy polling. Tested: lpaa23:~# echo 70 >/proc/sys/net/core/busy_read lpaa24:~# echo 70 >/proc/sys/net/core/busy_read lpaa23:~# for f in `seq 1 10`; do ./super_netperf 1 -H lpaa24 -t UDP_RR -l 5; done Before patch : 27867 28870 37324 41060 41215 36764 36838 44455 41282 43843 After patch : 73920 73213 70147 74845 71697 68315 68028 75219 70082 73707 Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Signed-off-by: David S. Miller --- include/net/busy_poll.h | 28 +++++++++++++++++++--------- net/ipv4/udp.c | 2 ++ net/ipv6/udp.c | 2 ++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index 965e52b..d73b849 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -80,11 +80,6 @@ static inline void skb_mark_napi_id(struct sk_buff *skb, skb->napi_id = napi->napi_id; } -/* used in the protocol hanlder to propagate the napi_id to the socket */ -static inline void sk_mark_napi_id(struct sock *sk, struct sk_buff *skb) -{ - sk->sk_napi_id = skb->napi_id; -} #else /* CONFIG_NET_RX_BUSY_POLL */ static inline unsigned long net_busy_loop_on(void) @@ -107,10 +102,6 @@ static inline void skb_mark_napi_id(struct sk_buff *skb, { } -static inline void sk_mark_napi_id(struct sock *sk, struct sk_buff *skb) -{ -} - static inline bool busy_loop_timeout(unsigned long end_time) { return true; @@ -122,4 +113,23 @@ static inline bool sk_busy_loop(struct sock *sk, int nonblock) } #endif /* CONFIG_NET_RX_BUSY_POLL */ + +/* used in the protocol hanlder to propagate the napi_id to the socket */ +static inline void sk_mark_napi_id(struct sock *sk, const struct sk_buff *skb) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + sk->sk_napi_id = skb->napi_id; +#endif +} + +/* variant used for unconnected sockets */ +static inline void sk_mark_napi_id_once(struct sock *sk, + const struct sk_buff *skb) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + if (!sk->sk_napi_id) + sk->sk_napi_id = skb->napi_id; +#endif +} + #endif /* _LINUX_NET_BUSY_POLL_H */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 9ae7c63..e1fc011 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1569,6 +1569,8 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) sock_rps_save_rxhash(sk, skb); sk_mark_napi_id(sk, skb); sk_incoming_cpu_update(sk); + } else { + sk_mark_napi_id_once(sk, skb); } rc = __udp_enqueue_schedule_skb(sk, skb); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 86a8cac..4f99417 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -519,6 +519,8 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) sock_rps_save_rxhash(sk, skb); sk_mark_napi_id(sk, skb); sk_incoming_cpu_update(sk); + } else { + sk_mark_napi_id_once(sk, skb); } rc = __udp_enqueue_schedule_skb(sk, skb); -- cgit v1.1 From c7d03a00b56fc23c3a01a8353789ad257363e281 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 17 Nov 2016 04:58:21 +0300 Subject: netns: make struct pernet_operations::id unsigned int Make struct pernet_operations::id unsigned. There are 2 reasons to do so: 1) This field is really an index into an zero based array and thus is unsigned entity. Using negative value is out-of-bound access by definition. 2) On x86_64 unsigned 32-bit data which are mixed with pointers via array indexing or offsets added or subtracted to pointers are preffered to signed 32-bit data. "int" being used as an array index needs to be sign-extended to 64-bit before being used. void f(long *p, int i) { g(p[i]); } roughly translates to movsx rsi, esi mov rdi, [rsi+...] call g MOVSX is 3 byte instruction which isn't necessary if the variable is unsigned because x86_64 is zero extending by default. Now, there is net_generic() function which, you guessed it right, uses "int" as an array index: static inline void *net_generic(const struct net *net, int id) { ... ptr = ng->ptr[id - 1]; ... } And this function is used a lot, so those sign extensions add up. Patch snipes ~1730 bytes on allyesconfig kernel (without all junk messing with code generation): add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730) Unfortunately some functions actually grow bigger. This is a semmingly random artefact of code generation with register allocator being used differently. gcc decides that some variable needs to live in new r8+ registers and every access now requires REX prefix. Or it is shifted into r12, so [r12+0] addressing mode has to be used which is longer than [r8] However, overall balance is in negative direction: add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730) function old new delta nfsd4_lock 3886 3959 +73 tipc_link_build_proto_msg 1096 1140 +44 mac80211_hwsim_new_radio 2776 2808 +32 tipc_mon_rcv 1032 1058 +26 svcauth_gss_legacy_init 1413 1429 +16 tipc_bcbase_select_primary 379 392 +13 nfsd4_exchange_id 1247 1260 +13 nfsd4_setclientid_confirm 782 793 +11 ... put_client_renew_locked 494 480 -14 ip_set_sockfn_get 730 716 -14 geneve_sock_add 829 813 -16 nfsd4_sequence_done 721 703 -18 nlmclnt_lookup_host 708 686 -22 nfsd4_lockt 1085 1063 -22 nfs_get_client 1077 1050 -27 tcf_bpf_init 1106 1076 -30 nfsd4_encode_fattr 5997 5930 -67 Total: Before=154856051, After=154854321, chg -0.00% Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- drivers/infiniband/core/cma.c | 2 +- drivers/net/bonding/bond_main.c | 2 +- drivers/net/geneve.c | 2 +- drivers/net/gtp.c | 2 +- drivers/net/ppp/ppp_generic.c | 2 +- drivers/net/ppp/pppoe.c | 2 +- drivers/net/vxlan.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 2 +- fs/lockd/netns.h | 2 +- fs/lockd/svc.c | 2 +- fs/nfs/inode.c | 2 +- fs/nfs/netns.h | 2 +- fs/nfs_common/grace.c | 2 +- fs/nfsd/netns.h | 2 +- fs/nfsd/nfsctl.c | 2 +- include/net/bonding.h | 2 +- include/net/ip_tunnels.h | 6 +++--- include/net/net_namespace.h | 2 +- include/net/netfilter/nf_conntrack_l4proto.h | 2 +- include/net/netfilter/nf_conntrack_synproxy.h | 2 +- include/net/netns/generic.h | 2 +- kernel/audit.c | 2 +- net/8021q/vlan.c | 2 +- net/8021q/vlan.h | 2 +- net/bridge/br_netfilter_hooks.c | 2 +- net/caif/caif_dev.c | 2 +- net/core/net_namespace.c | 7 +++---- net/core/pktgen.c | 2 +- net/ipv4/ip_gre.c | 4 ++-- net/ipv4/ip_tunnel.c | 4 ++-- net/ipv4/ip_vti.c | 2 +- net/ipv4/ipip.c | 2 +- net/ipv4/netfilter/ipt_CLUSTERIP.c | 2 +- net/ipv6/ip6_gre.c | 2 +- net/ipv6/ip6_tunnel.c | 2 +- net/ipv6/ip6_vti.c | 2 +- net/ipv6/sit.c | 2 +- net/ipv6/xfrm6_tunnel.c | 2 +- net/key/af_key.c | 2 +- net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/ipvs/ip_vs_core.c | 2 +- net/netfilter/nf_conntrack_proto_dccp.c | 2 +- net/netfilter/nf_conntrack_proto_gre.c | 2 +- net/netfilter/nf_conntrack_proto_sctp.c | 2 +- net/netfilter/nf_conntrack_proto_udplite.c | 2 +- net/netfilter/nf_synproxy_core.c | 2 +- net/netfilter/nfnetlink_log.c | 2 +- net/netfilter/nfnetlink_queue.c | 2 +- net/netfilter/xt_hashlimit.c | 2 +- net/netfilter/xt_recent.c | 2 +- net/openvswitch/datapath.c | 2 +- net/openvswitch/datapath.h | 2 +- net/phonet/pn_dev.c | 2 +- net/rds/tcp.c | 2 +- net/sched/act_bpf.c | 2 +- net/sched/act_connmark.c | 2 +- net/sched/act_csum.c | 2 +- net/sched/act_gact.c | 2 +- net/sched/act_ife.c | 2 +- net/sched/act_ipt.c | 4 ++-- net/sched/act_mirred.c | 2 +- net/sched/act_nat.c | 2 +- net/sched/act_pedit.c | 2 +- net/sched/act_police.c | 2 +- net/sched/act_simple.c | 2 +- net/sched/act_skbedit.c | 2 +- net/sched/act_skbmod.c | 2 +- net/sched/act_tunnel_key.c | 2 +- net/sched/act_vlan.c | 2 +- net/sunrpc/netns.h | 2 +- net/sunrpc/sunrpc_syms.c | 2 +- net/tipc/core.c | 2 +- net/tipc/core.h | 2 +- 73 files changed, 80 insertions(+), 81 deletions(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 89a6b05..c68f4fe 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -116,7 +116,7 @@ static LIST_HEAD(dev_list); static LIST_HEAD(listen_any_list); static DEFINE_MUTEX(lock); static struct workqueue_struct *cma_wq; -static int cma_pernet_id; +static unsigned int cma_pernet_id; struct cma_pernet { struct idr tcp_ps; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 5708f17..8029dd4 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -199,7 +199,7 @@ MODULE_PARM_DESC(lp_interval, "The number of seconds between instances where " atomic_t netpoll_block_tx = ATOMIC_INIT(0); #endif -int bond_net_id __read_mostly; +unsigned int bond_net_id __read_mostly; static __be32 arp_target[BOND_MAX_ARP_TARGETS]; static int arp_ip_count; diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 85a423a..90dc6b1 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -43,7 +43,7 @@ struct geneve_net { struct list_head sock_list; }; -static int geneve_net_id; +static unsigned int geneve_net_id; union geneve_addr { struct sockaddr_in sin; diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 719d19f..98f10c2 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -77,7 +77,7 @@ struct gtp_dev { struct hlist_head *addr_hash; }; -static int gtp_net_id __read_mostly; +static unsigned int gtp_net_id __read_mostly; struct gtp_net { struct list_head gtp_dev_list; diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 5489c0e..3d3b1f4 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -204,7 +204,7 @@ static atomic_t ppp_unit_count = ATOMIC_INIT(0); static atomic_t channel_count = ATOMIC_INIT(0); /* per-net private data for this module */ -static int ppp_net_id __read_mostly; +static unsigned int ppp_net_id __read_mostly; struct ppp_net { /* units to ppp mapping */ struct idr units_idr; diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 4ddae81..f017c72 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -95,7 +95,7 @@ static const struct proto_ops pppoe_ops; static const struct ppp_channel_ops pppoe_chan_ops; /* per-net private data for this module */ -static int pppoe_net_id __read_mostly; +static unsigned int pppoe_net_id __read_mostly; struct pppoe_net { /* * we could use _single_ hash table for all diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 0a3fd67..21e92be 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -52,7 +52,7 @@ static bool log_ecn_error = true; module_param(log_ecn_error, bool, 0644); MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); -static int vxlan_net_id; +static unsigned int vxlan_net_id; static struct rtnl_link_ops vxlan_link_ops; static const u8 all_zeros_mac[ETH_ALEN + 2]; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 8f366cc..1293f84 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -250,7 +250,7 @@ static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c) cp->magic = 0; } -static int hwsim_net_id; +static unsigned int hwsim_net_id; static int hwsim_netgroup; diff --git a/fs/lockd/netns.h b/fs/lockd/netns.h index 5426189..fb8cac8 100644 --- a/fs/lockd/netns.h +++ b/fs/lockd/netns.h @@ -15,6 +15,6 @@ struct lockd_net { struct list_head nsm_handles; }; -extern int lockd_net_id; +extern unsigned int lockd_net_id; #endif diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index fc4084e..1c13dd8 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -57,7 +57,7 @@ static struct task_struct *nlmsvc_task; static struct svc_rqst *nlmsvc_rqst; unsigned long nlmsvc_timeout; -int lockd_net_id; +unsigned int lockd_net_id; /* * These can be set at insmod time (useful for NFS as root filesystem), diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index bf4ec5e..ce42dd0 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -2015,7 +2015,7 @@ static void nfsiod_stop(void) destroy_workqueue(wq); } -int nfs_net_id; +unsigned int nfs_net_id; EXPORT_SYMBOL_GPL(nfs_net_id); static int nfs_net_init(struct net *net) diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h index fbce0d8..5fbd2bd 100644 --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h @@ -35,6 +35,6 @@ struct nfs_net { #endif }; -extern int nfs_net_id; +extern unsigned int nfs_net_id; #endif diff --git a/fs/nfs_common/grace.c b/fs/nfs_common/grace.c index fd8c9a5..420d3a0 100644 --- a/fs/nfs_common/grace.c +++ b/fs/nfs_common/grace.c @@ -9,7 +9,7 @@ #include #include -static int grace_net_id; +static unsigned int grace_net_id; static DEFINE_SPINLOCK(grace_lock); /** diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index ee36efd..3714231 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -124,5 +124,5 @@ struct nfsd_net { /* Simple check to find out if a given net was properly initialized */ #define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl) -extern int nfsd_net_id; +extern unsigned int nfsd_net_id; #endif /* __NFSD_NETNS_H__ */ diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 36b2af9..2857e46 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1201,7 +1201,7 @@ static int create_proc_exports_entry(void) } #endif -int nfsd_net_id; +unsigned int nfsd_net_id; static __net_init int nfsd_init_net(struct net *net) { diff --git a/include/net/bonding.h b/include/net/bonding.h index f32f7ef..3c85777 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -681,7 +681,7 @@ static inline int bond_get_targets_ip(__be32 *targets, __be32 ip) } /* exported from bond_main.c */ -extern int bond_net_id; +extern unsigned int bond_net_id; extern const struct bond_parm_tbl bond_lacp_tbl[]; extern const struct bond_parm_tbl xmit_hashtype_tbl[]; extern const struct bond_parm_tbl arp_validate_tbl[]; diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 59557c0..e893fe4 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -129,7 +129,7 @@ struct ip_tunnel { #endif struct ip_tunnel_prl_entry __rcu *prl; /* potential router list */ unsigned int prl_count; /* # of entries in PRL */ - int ip_tnl_net_id; + unsigned int ip_tnl_net_id; struct gro_cells gro_cells; bool collect_md; bool ignore_df; @@ -248,7 +248,7 @@ void ip_tunnel_uninit(struct net_device *dev); void ip_tunnel_dellink(struct net_device *dev, struct list_head *head); struct net *ip_tunnel_get_link_net(const struct net_device *dev); int ip_tunnel_get_iflink(const struct net_device *dev); -int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, +int ip_tunnel_init_net(struct net *net, unsigned int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname); void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops); @@ -275,7 +275,7 @@ int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm *p); int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm *p); -void ip_tunnel_setup(struct net_device *dev, int net_id); +void ip_tunnel_setup(struct net_device *dev, unsigned int net_id); struct ip_tunnel_encap_ops { size_t (*encap_hlen)(struct ip_tunnel_encap *e); diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index fc4f757..d7149e9 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -291,7 +291,7 @@ struct pernet_operations { int (*init)(struct net *net); void (*exit)(struct net *net); void (*exit_batch)(struct list_head *net_exit_list); - int *id; + unsigned int *id; size_t size; }; diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 2152b70..e7b8365 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -98,7 +98,7 @@ struct nf_conntrack_l4proto { const struct nla_policy *nla_policy; } ctnl_timeout; #endif - int *net_id; + unsigned int *net_id; /* Init l4proto pernet data */ int (*init_net)(struct net *net, u_int16_t proto); diff --git a/include/net/netfilter/nf_conntrack_synproxy.h b/include/net/netfilter/nf_conntrack_synproxy.h index e693731..b0ca402 100644 --- a/include/net/netfilter/nf_conntrack_synproxy.h +++ b/include/net/netfilter/nf_conntrack_synproxy.h @@ -54,7 +54,7 @@ struct synproxy_net { struct synproxy_stats __percpu *stats; }; -extern int synproxy_net_id; +extern unsigned int synproxy_net_id; static inline struct synproxy_net *synproxy_pernet(struct net *net) { return net_generic(net, synproxy_net_id); diff --git a/include/net/netns/generic.h b/include/net/netns/generic.h index 70e1585..d315786 100644 --- a/include/net/netns/generic.h +++ b/include/net/netns/generic.h @@ -31,7 +31,7 @@ struct net_generic { void *ptr[0]; }; -static inline void *net_generic(const struct net *net, int id) +static inline void *net_generic(const struct net *net, unsigned int id) { struct net_generic *ng; void *ptr; diff --git a/kernel/audit.c b/kernel/audit.c index f1ca116..92c463d 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -126,7 +126,7 @@ static atomic_t audit_lost = ATOMIC_INIT(0); /* The netlink socket. */ static struct sock *audit_sock; -static int audit_net_id; +static unsigned int audit_net_id; /* Hash for inode-based rules */ struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index a793655..691f0ad 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -44,7 +44,7 @@ /* Global VLAN variables */ -int vlan_net_id __read_mostly; +unsigned int vlan_net_id __read_mostly; const char vlan_fullname[] = "802.1Q VLAN Support"; const char vlan_version[] = DRV_VERSION; diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index cc15579..df8bd65 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -159,7 +159,7 @@ void vlan_netlink_fini(void); extern struct rtnl_link_ops vlan_link_ops; -extern int vlan_net_id; +extern unsigned int vlan_net_id; struct proc_dir_entry; diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 8155bd2..83d937f 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -46,7 +46,7 @@ #include #endif -static int brnf_net_id __read_mostly; +static unsigned int brnf_net_id __read_mostly; struct brnf_net { bool enabled; diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index d730a0f..2d38b6e 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -52,7 +52,7 @@ struct caif_net { struct caif_device_entry_list caifdevs; }; -static int caif_net_id; +static unsigned int caif_net_id; static int q_high = 50; /* Percent */ struct cfcnfg *get_cfcnfg(struct net *net) diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 1309d78..35d37b1 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -55,7 +55,7 @@ static struct net_generic *net_alloc_generic(void) return ng; } -static int net_assign_generic(struct net *net, int id, void *data) +static int net_assign_generic(struct net *net, unsigned int id, void *data) { struct net_generic *ng, *old_ng; @@ -122,8 +122,7 @@ out: static void ops_free(const struct pernet_operations *ops, struct net *net) { if (ops->id && ops->size) { - int id = *ops->id; - kfree(net_generic(net, id)); + kfree(net_generic(net, *ops->id)); } } @@ -881,7 +880,7 @@ again: } return error; } - max_gen_ptrs = max_t(unsigned int, max_gen_ptrs, *ops->id); + max_gen_ptrs = max(max_gen_ptrs, *ops->id); } error = __register_pernet_operations(list, ops); if (error) { diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 306b8f0..8e69ce4 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -413,7 +413,7 @@ struct pktgen_hdr { }; -static int pg_net_id __read_mostly; +static unsigned int pg_net_id __read_mostly; struct pktgen_net { struct net *net; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 576f705..78fd620 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -113,8 +113,8 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); static struct rtnl_link_ops ipgre_link_ops __read_mostly; static int ipgre_tunnel_init(struct net_device *dev); -static int ipgre_net_id __read_mostly; -static int gre_tap_net_id __read_mostly; +static unsigned int ipgre_net_id __read_mostly; +static unsigned int gre_tap_net_id __read_mostly; static void ipgre_err(struct sk_buff *skb, u32 info, const struct tnl_ptk_info *tpi) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 12a92e3..823abae 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -994,7 +994,7 @@ int ip_tunnel_get_iflink(const struct net_device *dev) } EXPORT_SYMBOL(ip_tunnel_get_iflink); -int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, +int ip_tunnel_init_net(struct net *net, unsigned int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname) { struct ip_tunnel_net *itn = net_generic(net, ip_tnl_net_id); @@ -1196,7 +1196,7 @@ void ip_tunnel_uninit(struct net_device *dev) EXPORT_SYMBOL_GPL(ip_tunnel_uninit); /* Do least required initialization, rest of init is done in tunnel_init call */ -void ip_tunnel_setup(struct net_device *dev, int net_id) +void ip_tunnel_setup(struct net_device *dev, unsigned int net_id) { struct ip_tunnel *tunnel = netdev_priv(dev); tunnel->ip_tnl_net_id = net_id; diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 5d7944f..8b14f14 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -46,7 +46,7 @@ static struct rtnl_link_ops vti_link_ops __read_mostly; -static int vti_net_id __read_mostly; +static unsigned int vti_net_id __read_mostly; static int vti_tunnel_init(struct net_device *dev); static int vti_input(struct sk_buff *skb, int nexthdr, __be32 spi, diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index c939258..79489f0 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -121,7 +121,7 @@ static bool log_ecn_error = true; module_param(log_ecn_error, bool, 0644); MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); -static int ipip_net_id __read_mostly; +static unsigned int ipip_net_id __read_mostly; static int ipip_tunnel_init(struct net_device *dev); static struct rtnl_link_ops ipip_link_ops __read_mostly; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 4a9e6db..e6e206f 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -62,7 +62,7 @@ struct clusterip_config { static const struct file_operations clusterip_proc_fops; #endif -static int clusterip_net_id __read_mostly; +static unsigned int clusterip_net_id __read_mostly; struct clusterip_net { struct list_head configs; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 710bc79..75b6108 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -64,7 +64,7 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); #define IP6_GRE_HASH_SIZE_SHIFT 5 #define IP6_GRE_HASH_SIZE (1 << IP6_GRE_HASH_SIZE_SHIFT) -static int ip6gre_net_id __read_mostly; +static unsigned int ip6gre_net_id __read_mostly; struct ip6gre_net { struct ip6_tnl __rcu *tunnels[4][IP6_GRE_HASH_SIZE]; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 259e850..d3c619e 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -83,7 +83,7 @@ static int ip6_tnl_dev_init(struct net_device *dev); static void ip6_tnl_dev_setup(struct net_device *dev); static struct rtnl_link_ops ip6_link_ops __read_mostly; -static int ip6_tnl_net_id __read_mostly; +static unsigned int ip6_tnl_net_id __read_mostly; struct ip6_tnl_net { /* the IPv6 tunnel fallback device */ struct net_device *fb_tnl_dev; diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index af3f0e0..c476bb8 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -64,7 +64,7 @@ static int vti6_dev_init(struct net_device *dev); static void vti6_dev_setup(struct net_device *dev); static struct rtnl_link_ops vti6_link_ops __read_mostly; -static int vti6_net_id __read_mostly; +static unsigned int vti6_net_id __read_mostly; struct vti6_net { /* the vti6 tunnel fallback device */ struct net_device *fb_tnl_dev; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index dc7a344..0355231 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -76,7 +76,7 @@ static bool check_6rd(struct ip_tunnel *tunnel, const struct in6_addr *v6dst, __be32 *v4dst); static struct rtnl_link_ops sit_link_ops __read_mostly; -static int sit_net_id __read_mostly; +static unsigned int sit_net_id __read_mostly; struct sit_net { struct ip_tunnel __rcu *tunnels_r_l[IP6_SIT_HASH_SIZE]; struct ip_tunnel __rcu *tunnels_r[IP6_SIT_HASH_SIZE]; diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index e1c0bbe..d7b731a 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -44,7 +44,7 @@ struct xfrm6_tunnel_net { u32 spi; }; -static int xfrm6_tunnel_net_id __read_mostly; +static unsigned int xfrm6_tunnel_net_id __read_mostly; static inline struct xfrm6_tunnel_net *xfrm6_tunnel_pernet(struct net *net) { return net_generic(net, xfrm6_tunnel_net_id); diff --git a/net/key/af_key.c b/net/key/af_key.c index f9c9ecb..c6252ed 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -36,7 +36,7 @@ #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x)) #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x)) -static int pfkey_net_id __read_mostly; +static unsigned int pfkey_net_id __read_mostly; struct netns_pfkey { /* List of all pfkey sockets. */ struct hlist_head table; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 23345d2..c296f9b 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -36,7 +36,7 @@ struct ip_set_net { bool is_destroyed; /* all sets are destroyed */ }; -static int ip_set_net_id __read_mostly; +static unsigned int ip_set_net_id __read_mostly; static inline struct ip_set_net *ip_set_pernet(struct net *net) { diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 2c1b498..db40050 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -70,7 +70,7 @@ EXPORT_SYMBOL(ip_vs_get_debug_level); #endif EXPORT_SYMBOL(ip_vs_new_conn_out); -static int ip_vs_net_id __read_mostly; +static unsigned int ip_vs_net_id __read_mostly; /* netns cnt used for uniqueness */ static atomic_t ipvs_netns_cnt = ATOMIC_INIT(0); diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index ac89769..073b047 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -385,7 +385,7 @@ dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = }; /* this module per-net specifics */ -static int dccp_net_id __read_mostly; +static unsigned int dccp_net_id __read_mostly; struct dccp_net { struct nf_proto_net pn; int dccp_loose; diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index ff405c9..87bb40a 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -53,7 +53,7 @@ static unsigned int gre_timeouts[GRE_CT_MAX] = { [GRE_CT_REPLIED] = 180*HZ, }; -static int proto_gre_net_id __read_mostly; +static unsigned int proto_gre_net_id __read_mostly; struct netns_proto_gre { struct nf_proto_net nf; rwlock_t keymap_lock; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 17c0ade..d096c2d 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -144,7 +144,7 @@ static const u8 sctp_conntracks[2][11][SCTP_CONNTRACK_MAX] = { } }; -static int sctp_net_id __read_mostly; +static unsigned int sctp_net_id __read_mostly; struct sctp_net { struct nf_proto_net pn; unsigned int timeouts[SCTP_CONNTRACK_MAX]; diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 8cdb4b1..7808604 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -35,7 +35,7 @@ static unsigned int udplite_timeouts[UDPLITE_CT_MAX] = { [UDPLITE_CT_REPLIED] = 180*HZ, }; -static int udplite_net_id __read_mostly; +static unsigned int udplite_net_id __read_mostly; struct udplite_net { struct nf_proto_net pn; unsigned int timeouts[UDPLITE_CT_MAX]; diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index c8a4a48..7c6d1fb 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -24,7 +24,7 @@ #include #include -int synproxy_net_id; +unsigned int synproxy_net_id; EXPORT_SYMBOL_GPL(synproxy_net_id); bool diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 7435505..763cb4d 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -80,7 +80,7 @@ struct nfulnl_instance { #define INSTANCE_BUCKETS 16 -static int nfnl_log_net_id __read_mostly; +static unsigned int nfnl_log_net_id __read_mostly; struct nfnl_log_net { spinlock_t instances_lock; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 1e33115b..be7627b 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -77,7 +77,7 @@ struct nfqnl_instance { typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); -static int nfnl_queue_net_id __read_mostly; +static unsigned int nfnl_queue_net_id __read_mostly; #define INSTANCE_BUCKETS 16 struct nfnl_queue_net { diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index b89b688..1006340 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -49,7 +49,7 @@ struct hashlimit_net { struct proc_dir_entry *ip6t_hashlimit; }; -static int hashlimit_net_id; +static unsigned int hashlimit_net_id; static inline struct hashlimit_net *hashlimit_pernet(struct net *net) { return net_generic(net, hashlimit_net_id); diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index bf25000..1d89a4e 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -95,7 +95,7 @@ struct recent_net { #endif }; -static int recent_net_id __read_mostly; +static unsigned int recent_net_id __read_mostly; static inline struct recent_net *recent_pernet(struct net *net) { diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 1402f1b..2d4c4d3 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -58,7 +58,7 @@ #include "vport-internal_dev.h" #include "vport-netdev.h" -int ovs_net_id __read_mostly; +unsigned int ovs_net_id __read_mostly; static struct genl_family dp_packet_genl_family; static struct genl_family dp_flow_genl_family; diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index ab85c1c..1c6e937 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -144,7 +144,7 @@ struct ovs_net { bool xt_label; }; -extern int ovs_net_id; +extern unsigned int ovs_net_id; void ovs_lock(void); void ovs_unlock(void); diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index a586800..2cb4c5d 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -44,7 +44,7 @@ struct phonet_net { struct phonet_routes routes; }; -static int phonet_net_id __read_mostly; +static unsigned int phonet_net_id __read_mostly; static struct phonet_net *phonet_pernet(struct net *net) { diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 3296a6a..1a0399d 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -366,7 +366,7 @@ struct rds_transport rds_tcp_transport = { .t_mp_capable = 1, }; -static int rds_tcp_netid; +static unsigned int rds_tcp_netid; /* per-network namespace private data for this module */ struct rds_tcp_net { diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 9ff06cf..1aa4ecf 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -33,7 +33,7 @@ struct tcf_bpf_cfg { bool is_ebpf; }; -static int bpf_net_id; +static unsigned int bpf_net_id; static struct tc_action_ops act_bpf_ops; static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act, diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c index eae07a2..ab80629 100644 --- a/net/sched/act_connmark.c +++ b/net/sched/act_connmark.c @@ -30,7 +30,7 @@ #define CONNMARK_TAB_MASK 3 -static int connmark_net_id; +static unsigned int connmark_net_id; static struct tc_action_ops act_connmark_ops; static int tcf_connmark(struct sk_buff *skb, const struct tc_action *a, diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index e0defce..a0edd80 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -42,7 +42,7 @@ static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, }; -static int csum_net_id; +static unsigned int csum_net_id; static struct tc_action_ops act_csum_ops; static int tcf_csum_init(struct net *net, struct nlattr *nla, diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index e0aa30f..e6c874a 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -25,7 +25,7 @@ #define GACT_TAB_MASK 15 -static int gact_net_id; +static unsigned int gact_net_id; static struct tc_action_ops act_gact_ops; #ifdef CONFIG_GACT_PROB diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index 95c463c..80b848d 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -35,7 +35,7 @@ #define IFE_TAB_MASK 15 -static int ife_net_id; +static unsigned int ife_net_id; static int max_metacnt = IFE_META_MAX + 1; static struct tc_action_ops act_ife_ops; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index ce7ea6c..992ef8d 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -30,10 +30,10 @@ #define IPT_TAB_MASK 15 -static int ipt_net_id; +static unsigned int ipt_net_id; static struct tc_action_ops act_ipt_ops; -static int xt_net_id; +static unsigned int xt_net_id; static struct tc_action_ops act_xt_ops; static int ipt_init_target(struct xt_entry_target *t, char *table, diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 6073a11..b2d417b 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -70,7 +70,7 @@ static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { [TCA_MIRRED_PARMS] = { .len = sizeof(struct tc_mirred) }, }; -static int mirred_net_id; +static unsigned int mirred_net_id; static struct tc_action_ops act_mirred_ops; static bool dev_is_mac_header_xmit(const struct net_device *dev) diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 8e8b0cc..9b6aec6 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -31,7 +31,7 @@ #define NAT_TAB_MASK 15 -static int nat_net_id; +static unsigned int nat_net_id; static struct tc_action_ops act_nat_ops; static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index b54d56d..eda3220 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -25,7 +25,7 @@ #define PEDIT_TAB_MASK 15 -static int pedit_net_id; +static unsigned int pedit_net_id; static struct tc_action_ops act_pedit_ops; static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = { diff --git a/net/sched/act_police.c b/net/sched/act_police.c index d1bd248..c990b73 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -55,7 +55,7 @@ struct tc_police_compat { /* Each policer is serialized by its individual spinlock */ -static int police_net_id; +static unsigned int police_net_id; static struct tc_action_ops act_police_ops; static int tcf_act_police_walker(struct net *net, struct sk_buff *skb, diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 289af6f..823a73a 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -26,7 +26,7 @@ #define SIMP_TAB_MASK 7 -static int simp_net_id; +static unsigned int simp_net_id; static struct tc_action_ops act_simp_ops; #define SIMP_MAX_DATA 32 diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 024f3a3..06ccae3 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -29,7 +29,7 @@ #define SKBEDIT_TAB_MASK 15 -static int skbedit_net_id; +static unsigned int skbedit_net_id; static struct tc_action_ops act_skbedit_ops; static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c index e7d9638..3b7074e 100644 --- a/net/sched/act_skbmod.c +++ b/net/sched/act_skbmod.c @@ -22,7 +22,7 @@ #define SKBMOD_TAB_MASK 15 -static int skbmod_net_id; +static unsigned int skbmod_net_id; static struct tc_action_ops act_skbmod_ops; #define MAX_EDIT_LEN ETH_HLEN diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index edc720f..7af7125 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -22,7 +22,7 @@ #define TUNNEL_KEY_TAB_MASK 15 -static int tunnel_key_net_id; +static unsigned int tunnel_key_net_id; static struct tc_action_ops act_tunnel_key_ops; static int tunnel_key_act(struct sk_buff *skb, const struct tc_action *a, diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index b57fcbc..19e0dba 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c @@ -21,7 +21,7 @@ #define VLAN_TAB_MASK 15 -static int vlan_net_id; +static unsigned int vlan_net_id; static struct tc_action_ops act_vlan_ops; static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h index df58268..394ce52 100644 --- a/net/sunrpc/netns.h +++ b/net/sunrpc/netns.h @@ -34,7 +34,7 @@ struct sunrpc_net { struct proc_dir_entry *use_gssp_proc; }; -extern int sunrpc_net_id; +extern unsigned int sunrpc_net_id; int ip_map_cache_create(struct net *); void ip_map_cache_destroy(struct net *); diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index ee5d3d2..d1c330a 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -24,7 +24,7 @@ #include "netns.h" -int sunrpc_net_id; +unsigned int sunrpc_net_id; EXPORT_SYMBOL_GPL(sunrpc_net_id); static __net_init int sunrpc_init_net(struct net *net) diff --git a/net/tipc/core.c b/net/tipc/core.c index 236b043..0b982d0 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -47,7 +47,7 @@ #include /* configurable TIPC parameters */ -int tipc_net_id __read_mostly; +unsigned int tipc_net_id __read_mostly; int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */ static int __net_init tipc_init_net(struct net *net) diff --git a/net/tipc/core.h b/net/tipc/core.h index a1845fb..5cc5398 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -74,7 +74,7 @@ struct tipc_monitor; #define MAX_BEARERS 3 #define TIPC_DEF_MON_THRESHOLD 32 -extern int tipc_net_id __read_mostly; +extern unsigned int tipc_net_id __read_mostly; extern int sysctl_tipc_rmem[3] __read_mostly; extern int sysctl_tipc_named_timeout __read_mostly; -- cgit v1.1 From ece0cc17939287730f7b7337b51338b9cf0331cb Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 17 Nov 2016 10:51:30 +0000 Subject: sfc: Update MCDI protocol definitions Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mcdi_pcol.h | 483 +++++++++++++++++++++++++++++++++-- 1 file changed, 462 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/sfc/mcdi_pcol.h b/drivers/net/ethernet/sfc/mcdi_pcol.h index ccceafc..35cc3d4 100644 --- a/drivers/net/ethernet/sfc/mcdi_pcol.h +++ b/drivers/net/ethernet/sfc/mcdi_pcol.h @@ -276,6 +276,9 @@ /* The clock whose frequency you've attempted to set set * doesn't exist on this NIC */ #define MC_CMD_ERR_NO_CLOCK 0x1015 +/* Returned by MC_CMD_TESTASSERT if the action that should + * have caused an assertion failed to do so. */ +#define MC_CMD_ERR_UNREACHABLE 0x1016 #define MC_CMD_ERR_CODE_OFST 0 @@ -933,6 +936,8 @@ #define MC_CMD_COPYCODE_IN_BOOT_MAGIC_SKIP_BOOT_ICORE_SYNC_WIDTH 1 #define MC_CMD_COPYCODE_IN_BOOT_MAGIC_FORCE_STANDALONE_LBN 5 #define MC_CMD_COPYCODE_IN_BOOT_MAGIC_FORCE_STANDALONE_WIDTH 1 +#define MC_CMD_COPYCODE_IN_BOOT_MAGIC_DISABLE_XIP_LBN 6 +#define MC_CMD_COPYCODE_IN_BOOT_MAGIC_DISABLE_XIP_WIDTH 1 /* Destination address */ #define MC_CMD_COPYCODE_IN_DEST_ADDR_OFST 4 #define MC_CMD_COPYCODE_IN_NUMWORDS_OFST 8 @@ -1659,6 +1664,8 @@ #define MC_CMD_PTP_OUT_GET_ATTRIBUTES_CAPABILITIES_OFST 8 #define MC_CMD_PTP_OUT_GET_ATTRIBUTES_REPORT_SYNC_STATUS_LBN 0 #define MC_CMD_PTP_OUT_GET_ATTRIBUTES_REPORT_SYNC_STATUS_WIDTH 1 +#define MC_CMD_PTP_OUT_GET_ATTRIBUTES_RX_TSTAMP_OOB_LBN 1 +#define MC_CMD_PTP_OUT_GET_ATTRIBUTES_RX_TSTAMP_OOB_WIDTH 1 #define MC_CMD_PTP_OUT_GET_ATTRIBUTES_RESERVED0_OFST 12 #define MC_CMD_PTP_OUT_GET_ATTRIBUTES_RESERVED1_OFST 16 #define MC_CMD_PTP_OUT_GET_ATTRIBUTES_RESERVED2_OFST 20 @@ -2211,6 +2218,10 @@ #define MC_CMD_FW_HIGH_TX_RATE 0x3 /* enum: Reserved value */ #define MC_CMD_FW_PACKED_STREAM_HASH_MODE_1 0x4 +/* enum: Prefer to use firmware with additional "rules engine" filtering + * support + */ +#define MC_CMD_FW_RULES_ENGINE 0x5 /* enum: Only this option is allowed for non-admin functions */ #define MC_CMD_FW_DONT_CARE 0xffffffff @@ -3654,12 +3665,27 @@ #define MC_CMD_0x38_PRIVILEGE_CTG SRIOV_CTG_ADMIN -/* MC_CMD_NVRAM_UPDATE_START_IN msgrequest */ +/* MC_CMD_NVRAM_UPDATE_START_IN msgrequest: Legacy NVRAM_UPDATE_START request. + * Use NVRAM_UPDATE_START_V2_IN in new code + */ #define MC_CMD_NVRAM_UPDATE_START_IN_LEN 4 #define MC_CMD_NVRAM_UPDATE_START_IN_TYPE_OFST 0 /* Enum values, see field(s): */ /* MC_CMD_NVRAM_TYPES/MC_CMD_NVRAM_TYPES_OUT/TYPES */ +/* MC_CMD_NVRAM_UPDATE_START_V2_IN msgrequest: Extended NVRAM_UPDATE_START + * request with additional flags indicating version of command in use. See + * MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT for details of extended functionality. Use + * paired up with NVRAM_UPDATE_FINISH_V2_IN. + */ +#define MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN 8 +#define MC_CMD_NVRAM_UPDATE_START_V2_IN_TYPE_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_NVRAM_TYPES/MC_CMD_NVRAM_TYPES_OUT/TYPES */ +#define MC_CMD_NVRAM_UPDATE_START_V2_IN_FLAGS_OFST 4 +#define MC_CMD_NVRAM_UPDATE_START_V2_IN_FLAG_REPORT_VERIFY_RESULT_LBN 0 +#define MC_CMD_NVRAM_UPDATE_START_V2_IN_FLAG_REPORT_VERIFY_RESULT_WIDTH 1 + /* MC_CMD_NVRAM_UPDATE_START_OUT msgresponse */ #define MC_CMD_NVRAM_UPDATE_START_OUT_LEN 0 @@ -3784,16 +3810,81 @@ #define MC_CMD_0x3c_PRIVILEGE_CTG SRIOV_CTG_ADMIN -/* MC_CMD_NVRAM_UPDATE_FINISH_IN msgrequest */ +/* MC_CMD_NVRAM_UPDATE_FINISH_IN msgrequest: Legacy NVRAM_UPDATE_FINISH + * request. Use NVRAM_UPDATE_FINISH_V2_IN in new code + */ #define MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN 8 #define MC_CMD_NVRAM_UPDATE_FINISH_IN_TYPE_OFST 0 /* Enum values, see field(s): */ /* MC_CMD_NVRAM_TYPES/MC_CMD_NVRAM_TYPES_OUT/TYPES */ #define MC_CMD_NVRAM_UPDATE_FINISH_IN_REBOOT_OFST 4 -/* MC_CMD_NVRAM_UPDATE_FINISH_OUT msgresponse */ +/* MC_CMD_NVRAM_UPDATE_FINISH_V2_IN msgrequest: Extended NVRAM_UPDATE_FINISH + * request with additional flags indicating version of NVRAM_UPDATE commands in + * use. See MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT for details of extended + * functionality. Use paired up with NVRAM_UPDATE_START_V2_IN. + */ +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN 12 +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_TYPE_OFST 0 +/* Enum values, see field(s): */ +/* MC_CMD_NVRAM_TYPES/MC_CMD_NVRAM_TYPES_OUT/TYPES */ +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_REBOOT_OFST 4 +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_FLAGS_OFST 8 +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT_LBN 0 +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT_WIDTH 1 + +/* MC_CMD_NVRAM_UPDATE_FINISH_OUT msgresponse: Legacy NVRAM_UPDATE_FINISH + * response. Use NVRAM_UPDATE_FINISH_V2_OUT in new code + */ #define MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN 0 +/* MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT msgresponse: + * + * Extended NVRAM_UPDATE_FINISH response that communicates the result of secure + * firmware validation where applicable back to the host. + * + * Medford only: For signed firmware images, such as those for medford, the MC + * firmware verifies the signature before marking the firmware image as valid. + * This process takes a few seconds to complete. So is likely to take more than + * the MCDI timeout. Hence signature verification is initiated when + * MC_CMD_NVRAM_UPDATE_FINISH_V2_IN is received by the firmware, however, the + * MCDI command returns immediately with error code EAGAIN. Subsequent + * NVRAM_UPDATE_FINISH_V2_IN requests also return EAGAIN if the verification is + * in progress. Once the verification has completed, this response payload + * includes the results of the signature verification. Note that the nvram lock + * in firmware is only released after the verification has completed and the + * host has read back the result code from firmware. + */ +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN 4 +/* Result of nvram update completion processing */ +#define MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE_OFST 0 +/* enum: Verify succeeded without any errors. */ +#define MC_CMD_NVRAM_VERIFY_RC_SUCCESS 0x1 +/* enum: CMS format verification failed due to an internal error. */ +#define MC_CMD_NVRAM_VERIFY_RC_CMS_CHECK_FAILED 0x2 +/* enum: Invalid CMS format in image metadata. */ +#define MC_CMD_NVRAM_VERIFY_RC_INVALID_CMS_FORMAT 0x3 +/* enum: Message digest verification failed due to an internal error. */ +#define MC_CMD_NVRAM_VERIFY_RC_MESSAGE_DIGEST_CHECK_FAILED 0x4 +/* enum: Error in message digest calculated over the reflash-header, payload + * and reflash-trailer. + */ +#define MC_CMD_NVRAM_VERIFY_RC_BAD_MESSAGE_DIGEST 0x5 +/* enum: Signature verification failed due to an internal error. */ +#define MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHECK_FAILED 0x6 +/* enum: There are no valid signatures in the image. */ +#define MC_CMD_NVRAM_VERIFY_RC_NO_VALID_SIGNATURES 0x7 +/* enum: Trusted approvers verification failed due to an internal error. */ +#define MC_CMD_NVRAM_VERIFY_RC_TRUSTED_APPROVERS_CHECK_FAILED 0x8 +/* enum: The Trusted approver's list is empty. */ +#define MC_CMD_NVRAM_VERIFY_RC_NO_TRUSTED_APPROVERS 0x9 +/* enum: Signature chain verification failed due to an internal error. */ +#define MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHAIN_CHECK_FAILED 0xa +/* enum: The signers of the signatures in the image are not listed in the + * Trusted approver's list. + */ +#define MC_CMD_NVRAM_VERIFY_RC_NO_SIGNATURE_MATCH 0xb + /***********************************/ /* MC_CMD_REBOOT @@ -4356,6 +4447,28 @@ /* MC_CMD_TESTASSERT_OUT msgresponse */ #define MC_CMD_TESTASSERT_OUT_LEN 0 +/* MC_CMD_TESTASSERT_V2_IN msgrequest */ +#define MC_CMD_TESTASSERT_V2_IN_LEN 4 +/* How to provoke the assertion */ +#define MC_CMD_TESTASSERT_V2_IN_TYPE_OFST 0 +/* enum: Assert using the FAIL_ASSERTION_WITH_USEFUL_VALUES macro. Unless + * you're testing firmware, this is what you want. + */ +#define MC_CMD_TESTASSERT_V2_IN_FAIL_ASSERTION_WITH_USEFUL_VALUES 0x0 +/* enum: Assert using assert(0); */ +#define MC_CMD_TESTASSERT_V2_IN_ASSERT_FALSE 0x1 +/* enum: Deliberately trigger a watchdog */ +#define MC_CMD_TESTASSERT_V2_IN_WATCHDOG 0x2 +/* enum: Deliberately trigger a trap by loading from an invalid address */ +#define MC_CMD_TESTASSERT_V2_IN_LOAD_TRAP 0x3 +/* enum: Deliberately trigger a trap by storing to an invalid address */ +#define MC_CMD_TESTASSERT_V2_IN_STORE_TRAP 0x4 +/* enum: Jump to an invalid address */ +#define MC_CMD_TESTASSERT_V2_IN_JUMP_TRAP 0x5 + +/* MC_CMD_TESTASSERT_V2_OUT msgresponse */ +#define MC_CMD_TESTASSERT_V2_OUT_LEN 0 + /***********************************/ /* MC_CMD_WORKAROUND @@ -4421,6 +4534,7 @@ * (GET_PHY_CFG_OUT_MEDIA_TYPE); the valid 'page number' input values, and the * output data, are interpreted on a per-type basis. For SFP+: PAGE=0 or 1 * returns a 128-byte block read from module I2C address 0xA0 offset 0 or 0x80. + * Anything else: currently undefined. Locks required: None. Return code: 0. */ #define MC_CMD_GET_PHY_MEDIA_INFO 0x4b @@ -5362,12 +5476,14 @@ #define NVRAM_PARTITION_TYPE_EXPANSION_UEFI 0xd00 /* enum: Spare partition 0 */ #define NVRAM_PARTITION_TYPE_SPARE_0 0x1000 -/* enum: Spare partition 1 */ -#define NVRAM_PARTITION_TYPE_SPARE_1 0x1100 +/* enum: Used for XIP code of shmbooted images */ +#define NVRAM_PARTITION_TYPE_XIP_SCRATCH 0x1100 /* enum: Spare partition 2 */ #define NVRAM_PARTITION_TYPE_SPARE_2 0x1200 -/* enum: Spare partition 3 */ -#define NVRAM_PARTITION_TYPE_SPARE_3 0x1300 +/* enum: Manufacturing partition. Used during manufacture to pass information + * between XJTAG and Manftest. + */ +#define NVRAM_PARTITION_TYPE_MANUFACTURING 0x1300 /* enum: Spare partition 4 */ #define NVRAM_PARTITION_TYPE_SPARE_4 0x1400 /* enum: Spare partition 5 */ @@ -5402,6 +5518,14 @@ #define LICENSED_APP_ID_CAPTURE_SOLARSYSTEM 0x40 /* enum: Network Access Control */ #define LICENSED_APP_ID_NETWORK_ACCESS_CONTROL 0x80 +/* enum: TCP Direct */ +#define LICENSED_APP_ID_TCP_DIRECT 0x100 +/* enum: Low Latency */ +#define LICENSED_APP_ID_LOW_LATENCY 0x200 +/* enum: SolarCapture Tap */ +#define LICENSED_APP_ID_SOLARCAPTURE_TAP 0x400 +/* enum: Capture SolarSystem 40G */ +#define LICENSED_APP_ID_CAPTURE_SOLARSYSTEM_40G 0x800 #define LICENSED_APP_ID_ID_LBN 0 #define LICENSED_APP_ID_ID_WIDTH 32 @@ -5458,6 +5582,14 @@ #define LICENSED_V3_APPS_CAPTURE_SOLARSYSTEM_WIDTH 1 #define LICENSED_V3_APPS_NETWORK_ACCESS_CONTROL_LBN 7 #define LICENSED_V3_APPS_NETWORK_ACCESS_CONTROL_WIDTH 1 +#define LICENSED_V3_APPS_TCP_DIRECT_LBN 8 +#define LICENSED_V3_APPS_TCP_DIRECT_WIDTH 1 +#define LICENSED_V3_APPS_LOW_LATENCY_LBN 9 +#define LICENSED_V3_APPS_LOW_LATENCY_WIDTH 1 +#define LICENSED_V3_APPS_SOLARCAPTURE_TAP_LBN 10 +#define LICENSED_V3_APPS_SOLARCAPTURE_TAP_WIDTH 1 +#define LICENSED_V3_APPS_CAPTURE_SOLARSYSTEM_40G_LBN 11 +#define LICENSED_V3_APPS_CAPTURE_SOLARSYSTEM_40G_WIDTH 1 #define LICENSED_V3_APPS_MASK_LBN 0 #define LICENSED_V3_APPS_MASK_WIDTH 64 @@ -5988,6 +6120,8 @@ #define MC_CMD_INIT_TXQ_EXT_IN_FLAG_INNER_TCP_CSUM_EN_WIDTH 1 #define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TSOV2_EN_LBN 12 #define MC_CMD_INIT_TXQ_EXT_IN_FLAG_TSOV2_EN_WIDTH 1 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_CTPIO_LBN 13 +#define MC_CMD_INIT_TXQ_EXT_IN_FLAG_CTPIO_WIDTH 1 /* Owner ID to use if in buffer mode (zero if physical) */ #define MC_CMD_INIT_TXQ_EXT_IN_OWNER_ID_OFST 20 /* The port ID associated with the v-adaptor which should contain this DMAQ. */ @@ -7728,6 +7862,8 @@ * tests (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7 +/* enum: Rules engine RX PD production firmware */ +#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8 /* enum: RX PD firmware for GUE parsing prototype (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe /* enum: RX PD firmware parsing but not filtering network overlay tunnel @@ -7763,6 +7899,8 @@ * tests (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7 +/* enum: Rules engine TX PD production firmware */ +#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8 /* enum: RX PD firmware for GUE parsing prototype (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe /* Hardware capabilities of NIC */ @@ -7913,6 +8051,8 @@ * tests (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7 +/* enum: Rules engine RX PD production firmware */ +#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8 /* enum: RX PD firmware for GUE parsing prototype (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe /* enum: RX PD firmware parsing but not filtering network overlay tunnel @@ -7948,6 +8088,8 @@ * tests (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7 +/* enum: Rules engine TX PD production firmware */ +#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8 /* enum: RX PD firmware for GUE parsing prototype (Medford development only) */ #define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe /* Hardware capabilities of NIC */ @@ -7980,6 +8122,8 @@ #define MC_CMD_GET_CAPABILITIES_V2_OUT_RX_SNIFF_WIDTH 1 #define MC_CMD_GET_CAPABILITIES_V2_OUT_TX_SNIFF_LBN 11 #define MC_CMD_GET_CAPABILITIES_V2_OUT_TX_SNIFF_WIDTH 1 +#define MC_CMD_GET_CAPABILITIES_V2_OUT_NVRAM_UPDATE_REPORT_VERIFY_RESULT_LBN 12 +#define MC_CMD_GET_CAPABILITIES_V2_OUT_NVRAM_UPDATE_REPORT_VERIFY_RESULT_WIDTH 1 /* Number of FATSOv2 contexts per datapath supported by this NIC. Not present * on older firmware (check the length). */ @@ -8247,6 +8391,8 @@ #define MC_CMD_GET_CAPABILITIES_V3_OUT_RX_SNIFF_WIDTH 1 #define MC_CMD_GET_CAPABILITIES_V3_OUT_TX_SNIFF_LBN 11 #define MC_CMD_GET_CAPABILITIES_V3_OUT_TX_SNIFF_WIDTH 1 +#define MC_CMD_GET_CAPABILITIES_V3_OUT_NVRAM_UPDATE_REPORT_VERIFY_RESULT_LBN 12 +#define MC_CMD_GET_CAPABILITIES_V3_OUT_NVRAM_UPDATE_REPORT_VERIFY_RESULT_WIDTH 1 /* Number of FATSOv2 contexts per datapath supported by this NIC. Not present * on older firmware (check the length). */ @@ -8304,7 +8450,7 @@ #define MC_CMD_GET_CAPABILITIES_V3_OUT_SIZE_PIO_BUFF_LEN 2 /* On chips later than Medford the amount of address space assigned to each VI * is configurable. This is a global setting that the driver must query to - * discover the VI to address mapping. Cut-through PIO (CTPIO) in not available + * discover the VI to address mapping. Cut-through PIO (CTPIO) is not available * with 8k VI windows. */ #define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_OFST 72 @@ -10283,6 +10429,8 @@ * more data is returned. */ #define MC_CMD_PCIE_TUNE_IN_POLL_EYE_PLOT 0x6 +/* enum: Enable the SERDES BIST and set it to generate a 200MHz square wave */ +#define MC_CMD_PCIE_TUNE_IN_BIST_SQUARE_WAVE 0x7 /* Align the arguments to 32 bits */ #define MC_CMD_PCIE_TUNE_IN_PCIE_TUNE_RSVD_OFST 1 #define MC_CMD_PCIE_TUNE_IN_PCIE_TUNE_RSVD_LEN 3 @@ -10468,6 +10616,12 @@ #define MC_CMD_PCIE_TUNE_POLL_EYE_PLOT_OUT_SAMPLES_MINNUM 0 #define MC_CMD_PCIE_TUNE_POLL_EYE_PLOT_OUT_SAMPLES_MAXNUM 126 +/* MC_CMD_PCIE_TUNE_BIST_SQUARE_WAVE_IN msgrequest */ +#define MC_CMD_PCIE_TUNE_BIST_SQUARE_WAVE_IN_LEN 0 + +/* MC_CMD_PCIE_TUNE_BIST_SQUARE_WAVE_OUT msgrequest */ +#define MC_CMD_PCIE_TUNE_BIST_SQUARE_WAVE_OUT_LEN 0 + /***********************************/ /* MC_CMD_LICENSING @@ -10783,29 +10937,45 @@ #define MC_CMD_0xd4_PRIVILEGE_CTG SRIOV_CTG_GENERAL /* MC_CMD_LICENSED_V3_VALIDATE_APP_IN msgrequest */ -#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_LEN 72 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_LEN 56 +/* challenge for validation (384 bits) */ +#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_CHALLENGE_OFST 0 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_CHALLENGE_LEN 48 /* application ID expressed as a single bit mask */ -#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_APP_ID_OFST 0 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_APP_ID_OFST 48 #define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_APP_ID_LEN 8 -#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_APP_ID_LO_OFST 0 -#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_APP_ID_HI_OFST 4 -/* challenge for validation */ -#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_CHALLENGE_OFST 8 -#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_CHALLENGE_LEN 64 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_APP_ID_LO_OFST 48 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_IN_APP_ID_HI_OFST 52 /* MC_CMD_LICENSED_V3_VALIDATE_APP_OUT msgresponse */ -#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_LEN 72 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_LEN 116 +/* validation response to challenge in the form of ECDSA signature consisting + * of two 384-bit integers, r and s, in big-endian order. The signature signs a + * SHA-384 digest of a message constructed from the concatenation of the input + * message and the remaining fields of this output message, e.g. challenge[48 + * bytes] ... expiry_time[4 bytes] ... + */ +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_RESPONSE_OFST 0 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_RESPONSE_LEN 96 /* application expiry time */ -#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_TIME_OFST 0 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_TIME_OFST 96 /* application expiry units */ -#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNITS_OFST 4 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNITS_OFST 100 /* enum: expiry units are accounting units */ #define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNIT_ACC 0x0 /* enum: expiry units are calendar days */ #define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNIT_DAYS 0x1 -/* validation response to challenge */ -#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_RESPONSE_OFST 8 -#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_RESPONSE_LEN 64 +/* base MAC address of the NIC stored in NVRAM (note that this is a constant + * value for a given NIC regardless which function is calling, effectively this + * is PF0 base MAC address) + */ +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_BASE_MACADDR_OFST 104 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_BASE_MACADDR_LEN 6 +/* MAC address of v-adaptor associated with the client. If no such v-adapator + * exists, then the field is filled with 0xFF. + */ +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_VADAPTOR_MACADDR_OFST 110 +#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_VADAPTOR_MACADDR_LEN 6 /***********************************/ @@ -10835,6 +11005,70 @@ /***********************************/ +/* MC_CMD_LICENSING_V3_TEMPORARY + * Perform operations to support installation of a single temporary license in + * the adapter, in addition to those found in the licensing partition. See + * SF-116124-SW for an overview of how this could be used. The license is + * stored in MC persistent data and so will survive a MC reboot, but will be + * erased when the adapter is power cycled + */ +#define MC_CMD_LICENSING_V3_TEMPORARY 0xd6 + +#define MC_CMD_0xd6_PRIVILEGE_CTG SRIOV_CTG_GENERAL + +/* MC_CMD_LICENSING_V3_TEMPORARY_IN msgrequest */ +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_LEN 4 +/* operation code */ +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_OP_OFST 0 +/* enum: install a new license, overwriting any existing temporary license. + * This is an asynchronous operation owing to the time taken to validate an + * ECDSA license + */ +#define MC_CMD_LICENSING_V3_TEMPORARY_SET 0x0 +/* enum: clear the license immediately rather than waiting for the next power + * cycle + */ +#define MC_CMD_LICENSING_V3_TEMPORARY_CLEAR 0x1 +/* enum: get the status of the asynchronous MC_CMD_LICENSING_V3_TEMPORARY_SET + * operation + */ +#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS 0x2 + +/* MC_CMD_LICENSING_V3_TEMPORARY_IN_SET msgrequest */ +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_SET_LEN 164 +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_SET_OP_OFST 0 +/* ECDSA license and signature */ +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_SET_LICENSE_OFST 4 +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_SET_LICENSE_LEN 160 + +/* MC_CMD_LICENSING_V3_TEMPORARY_IN_CLEAR msgrequest */ +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_CLEAR_LEN 4 +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_CLEAR_OP_OFST 0 + +/* MC_CMD_LICENSING_V3_TEMPORARY_IN_STATUS msgrequest */ +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_STATUS_LEN 4 +#define MC_CMD_LICENSING_V3_TEMPORARY_IN_STATUS_OP_OFST 0 + +/* MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS msgresponse */ +#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_LEN 12 +/* status code */ +#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_STATUS_OFST 0 +/* enum: finished validating and installing license */ +#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_OK 0x0 +/* enum: license validation and installation in progress */ +#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_IN_PROGRESS 0x1 +/* enum: licensing error. More specific error messages are not provided to + * avoid exposing details of the licensing system to the client + */ +#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_ERROR 0x2 +/* bitmask of licensed features */ +#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_LICENSED_FEATURES_OFST 4 +#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_LICENSED_FEATURES_LEN 8 +#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_LICENSED_FEATURES_LO_OFST 4 +#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_LICENSED_FEATURES_HI_OFST 8 + + +/***********************************/ /* MC_CMD_SET_PORT_SNIFF_CONFIG * Configure RX port sniffing for the physical port associated with the calling * function. Only a privileged function may change the port sniffing @@ -11705,6 +11939,66 @@ /* MC_CMD_RX_BALANCING_OUT msgresponse */ #define MC_CMD_RX_BALANCING_OUT_LEN 0 + +/***********************************/ +/* MC_CMD_NVRAM_PRIVATE_APPEND + * Append a single TLV to the MC_USAGE_TLV partition. Returns MC_CMD_ERR_EEXIST + * if the tag is already present. + */ +#define MC_CMD_NVRAM_PRIVATE_APPEND 0x11c + +#define MC_CMD_0x11c_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_NVRAM_PRIVATE_APPEND_IN msgrequest */ +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_LENMIN 9 +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_LENMAX 252 +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_LEN(num) (8+1*(num)) +/* The tag to be appended */ +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_TAG_OFST 0 +/* The length of the data */ +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_LENGTH_OFST 4 +/* The data to be contained in the TLV structure */ +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_DATA_BUFFER_OFST 8 +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_DATA_BUFFER_LEN 1 +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_DATA_BUFFER_MINNUM 1 +#define MC_CMD_NVRAM_PRIVATE_APPEND_IN_DATA_BUFFER_MAXNUM 244 + +/* MC_CMD_NVRAM_PRIVATE_APPEND_OUT msgresponse */ +#define MC_CMD_NVRAM_PRIVATE_APPEND_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_XPM_VERIFY_CONTENTS + * Verify that the contents of the XPM memory is correct (Medford only). This + * is used during manufacture to check that the XPM memory has been programmed + * correctly at ATE. + */ +#define MC_CMD_XPM_VERIFY_CONTENTS 0x11b + +#define MC_CMD_0x11b_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_XPM_VERIFY_CONTENTS_IN msgrequest */ +#define MC_CMD_XPM_VERIFY_CONTENTS_IN_LEN 4 +/* Data type to be checked */ +#define MC_CMD_XPM_VERIFY_CONTENTS_IN_DATA_TYPE_OFST 0 + +/* MC_CMD_XPM_VERIFY_CONTENTS_OUT msgresponse */ +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_LENMIN 12 +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_LENMAX 252 +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_LEN(num) (12+1*(num)) +/* Number of sectors found (test builds only) */ +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_NUM_SECTORS_OFST 0 +/* Number of bytes found (test builds only) */ +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_NUM_BYTES_OFST 4 +/* Length of signature */ +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_SIG_LENGTH_OFST 8 +/* Signature */ +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_SIGNATURE_OFST 12 +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_SIGNATURE_LEN 1 +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_SIGNATURE_MINNUM 0 +#define MC_CMD_XPM_VERIFY_CONTENTS_OUT_SIGNATURE_MAXNUM 240 + + /***********************************/ /* MC_CMD_SET_EVQ_TMR * Update the timer load, timer reload and timer mode values for a given EVQ. @@ -11798,4 +12092,151 @@ */ #define MC_CMD_GET_EVQ_TMR_PROPERTIES_OUT_BUG35388_TMR_STEP_OFST 32 + +/***********************************/ +/* MC_CMD_ALLOCATE_TX_VFIFO_CP + * When we use the TX_vFIFO_ULL mode, we can allocate common pools using the + * non used switch buffers. + */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP 0x11d + +#define MC_CMD_0x11d_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_ALLOCATE_TX_VFIFO_CP_IN msgrequest */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_LEN 20 +/* Desired instance. Must be set to a specific instance, which is a function + * local queue index. + */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_INSTANCE_OFST 0 +/* Will the common pool be used as TX_vFIFO_ULL (1) */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_MODE_OFST 4 +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_ENABLED 0x1 /* enum */ +/* enum: Using this interface without TX_vFIFO_ULL is not supported for now */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_DISABLED 0x0 +/* Number of buffers to reserve for the common pool */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_SIZE_OFST 8 +/* TX datapath to which the Common Pool is connected to. */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_INGRESS_OFST 12 +/* enum: Extracts information from function */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_USE_FUNCTION_VALUE -0x1 +/* Network port or RX Engine to which the common pool connects. */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_EGRESS_OFST 16 +/* enum: Extracts information from function */ +/* MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_USE_FUNCTION_VALUE -0x1 */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT0 0x0 /* enum */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT1 0x1 /* enum */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT2 0x2 /* enum */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT3 0x3 /* enum */ +/* enum: To enable Switch loopback with Rx engine 0 */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_RX_ENGINE0 0x4 +/* enum: To enable Switch loopback with Rx engine 1 */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_RX_ENGINE1 0x5 + +/* MC_CMD_ALLOCATE_TX_VFIFO_CP_OUT msgresponse */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_OUT_LEN 4 +/* ID of the common pool allocated */ +#define MC_CMD_ALLOCATE_TX_VFIFO_CP_OUT_CP_ID_OFST 0 + + +/***********************************/ +/* MC_CMD_ALLOCATE_TX_VFIFO_VFIFO + * When we use the TX_vFIFO_ULL mode, we can allocate vFIFOs using the + * previously allocated common pools. + */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO 0x11e + +#define MC_CMD_0x11e_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN msgrequest */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_LEN 20 +/* Common pool previously allocated to which the new vFIFO will be associated + */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_CP_OFST 0 +/* Port or RX engine to associate the vFIFO egress */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_EGRESS_OFST 4 +/* enum: Extracts information from common pool */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_USE_CP_VALUE -0x1 +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT0 0x0 /* enum */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT1 0x1 /* enum */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT2 0x2 /* enum */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT3 0x3 /* enum */ +/* enum: To enable Switch loopback with Rx engine 0 */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_RX_ENGINE0 0x4 +/* enum: To enable Switch loopback with Rx engine 1 */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_RX_ENGINE1 0x5 +/* Minimum number of buffers that the pool must have */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_SIZE_OFST 8 +/* enum: Do not check the space available */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_NO_MINIMUM 0x0 +/* Will the vFIFO be used as TX_vFIFO_ULL */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_MODE_OFST 12 +/* Network priority of the vFIFO,if applicable */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PRIORITY_OFST 16 +/* enum: Search for the lowest unused priority */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_LOWEST_AVAILABLE -0x1 + +/* MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_OUT msgresponse */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_OUT_LEN 8 +/* Short vFIFO ID */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_OUT_VID_OFST 0 +/* Network priority of the vFIFO */ +#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_OUT_PRIORITY_OFST 4 + + +/***********************************/ +/* MC_CMD_TEARDOWN_TX_VFIFO_VF + * This interface clears the configuration of the given vFIFO and leaves it + * ready to be re-used. + */ +#define MC_CMD_TEARDOWN_TX_VFIFO_VF 0x11f + +#define MC_CMD_0x11f_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_TEARDOWN_TX_VFIFO_VF_IN msgrequest */ +#define MC_CMD_TEARDOWN_TX_VFIFO_VF_IN_LEN 4 +/* Short vFIFO ID */ +#define MC_CMD_TEARDOWN_TX_VFIFO_VF_IN_VFIFO_OFST 0 + +/* MC_CMD_TEARDOWN_TX_VFIFO_VF_OUT msgresponse */ +#define MC_CMD_TEARDOWN_TX_VFIFO_VF_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_DEALLOCATE_TX_VFIFO_CP + * This interface clears the configuration of the given common pool and leaves + * it ready to be re-used. + */ +#define MC_CMD_DEALLOCATE_TX_VFIFO_CP 0x121 + +#define MC_CMD_0x121_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_DEALLOCATE_TX_VFIFO_CP_IN msgrequest */ +#define MC_CMD_DEALLOCATE_TX_VFIFO_CP_IN_LEN 4 +/* Common pool ID given when pool allocated */ +#define MC_CMD_DEALLOCATE_TX_VFIFO_CP_IN_POOL_ID_OFST 0 + +/* MC_CMD_DEALLOCATE_TX_VFIFO_CP_OUT msgresponse */ +#define MC_CMD_DEALLOCATE_TX_VFIFO_CP_OUT_LEN 0 + + +/***********************************/ +/* MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS + * This interface allows the host to find out how many common pool buffers are + * not yet assigned. + */ +#define MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS 0x124 + +#define MC_CMD_0x124_PRIVILEGE_CTG SRIOV_CTG_ADMIN + +/* MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_IN msgrequest */ +#define MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_IN_LEN 0 + +/* MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_OUT msgresponse */ +#define MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_OUT_LEN 8 +/* Available buffers for the ENG to NET vFIFOs. */ +#define MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_OUT_NET_OFST 0 +/* Available buffers for the ENG to ENG and NET to ENG vFIFOs. */ +#define MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_OUT_ENG_OFST 4 + + #endif /* MCDI_PCOL_H */ -- cgit v1.1 From e17705c43b4273d2587297d5f8168ee432d9c94a Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 17 Nov 2016 10:51:39 +0000 Subject: sfc: Update EF10 register definitions Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10_regs.h | 103 ++++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10_regs.h b/drivers/net/ethernet/sfc/ef10_regs.h index 62a55dd..2c4bf94 100644 --- a/drivers/net/ethernet/sfc/ef10_regs.h +++ b/drivers/net/ethernet/sfc/ef10_regs.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare network controllers and boards - * Copyright 2012-2013 Solarflare Communications Inc. + * Copyright 2012-2015 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -147,8 +147,14 @@ #define ESF_DZ_RX_OVERRIDE_HOLDOFF_WIDTH 1 #define ESF_DZ_RX_DROP_EVENT_LBN 58 #define ESF_DZ_RX_DROP_EVENT_WIDTH 1 -#define ESF_DZ_RX_EV_RSVD2_LBN 54 -#define ESF_DZ_RX_EV_RSVD2_WIDTH 4 +#define ESF_DD_RX_EV_RSVD2_LBN 54 +#define ESF_DD_RX_EV_RSVD2_WIDTH 4 +#define ESF_EZ_RX_TCP_UDP_INNER_CHKSUM_ERR_LBN 57 +#define ESF_EZ_RX_TCP_UDP_INNER_CHKSUM_ERR_WIDTH 1 +#define ESF_EZ_RX_IP_INNER_CHKSUM_ERR_LBN 56 +#define ESF_EZ_RX_IP_INNER_CHKSUM_ERR_WIDTH 1 +#define ESF_EZ_RX_EV_RSVD2_LBN 54 +#define ESF_EZ_RX_EV_RSVD2_WIDTH 2 #define ESF_DZ_RX_EV_SOFT2_LBN 52 #define ESF_DZ_RX_EV_SOFT2_WIDTH 2 #define ESF_DZ_RX_DSC_PTR_LBITS_LBN 48 @@ -192,12 +198,21 @@ #define ESF_DZ_RX_MAC_CLASS_WIDTH 1 #define ESE_DZ_MAC_CLASS_MCAST 1 #define ESE_DZ_MAC_CLASS_UCAST 0 -#define ESF_DZ_RX_EV_SOFT1_LBN 32 -#define ESF_DZ_RX_EV_SOFT1_WIDTH 3 -#define ESF_DZ_RX_EV_RSVD1_LBN 31 -#define ESF_DZ_RX_EV_RSVD1_WIDTH 1 -#define ESF_DZ_RX_ABORT_LBN 30 -#define ESF_DZ_RX_ABORT_WIDTH 1 +#define ESF_DD_RX_EV_SOFT1_LBN 32 +#define ESF_DD_RX_EV_SOFT1_WIDTH 3 +#define ESF_EZ_RX_EV_SOFT1_LBN 34 +#define ESF_EZ_RX_EV_SOFT1_WIDTH 1 +#define ESF_EZ_RX_ENCAP_HDR_LBN 32 +#define ESF_EZ_RX_ENCAP_HDR_WIDTH 2 +#define ESE_EZ_ENCAP_HDR_GRE 2 +#define ESE_EZ_ENCAP_HDR_VXLAN 1 +#define ESE_EZ_ENCAP_HDR_NONE 0 +#define ESF_DD_RX_EV_RSVD1_LBN 30 +#define ESF_DD_RX_EV_RSVD1_WIDTH 2 +#define ESF_EZ_RX_EV_RSVD1_LBN 31 +#define ESF_EZ_RX_EV_RSVD1_WIDTH 1 +#define ESF_EZ_RX_ABORT_LBN 30 +#define ESF_EZ_RX_ABORT_WIDTH 1 #define ESF_DZ_RX_ECC_ERR_LBN 29 #define ESF_DZ_RX_ECC_ERR_WIDTH 1 #define ESF_DZ_RX_CRC1_ERR_LBN 28 @@ -235,6 +250,12 @@ #define ESE_DZ_TX_OPTION_DESC_TSO 7 #define ESE_DZ_TX_OPTION_DESC_VLAN 6 #define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_OPTION_TS_AT_TXDP_LBN 8 +#define ESF_DZ_TX_OPTION_TS_AT_TXDP_WIDTH 1 +#define ESF_DZ_TX_OPTION_INNER_UDP_TCP_CSUM_LBN 7 +#define ESF_DZ_TX_OPTION_INNER_UDP_TCP_CSUM_WIDTH 1 +#define ESF_DZ_TX_OPTION_INNER_IP_CSUM_LBN 6 +#define ESF_DZ_TX_OPTION_INNER_IP_CSUM_WIDTH 1 #define ESF_DZ_TX_TIMESTAMP_LBN 5 #define ESF_DZ_TX_TIMESTAMP_WIDTH 1 #define ESF_DZ_TX_OPTION_CRC_MODE_LBN 2 @@ -257,14 +278,22 @@ #define ESF_DZ_TX_OVERRIDE_HOLDOFF_WIDTH 1 #define ESF_DZ_TX_DROP_EVENT_LBN 58 #define ESF_DZ_TX_DROP_EVENT_WIDTH 1 -#define ESF_DZ_TX_EV_RSVD_LBN 48 -#define ESF_DZ_TX_EV_RSVD_WIDTH 10 +#define ESF_DD_TX_EV_RSVD_LBN 48 +#define ESF_DD_TX_EV_RSVD_WIDTH 10 +#define ESF_EZ_TCP_UDP_INNER_CHKSUM_ERR_LBN 57 +#define ESF_EZ_TCP_UDP_INNER_CHKSUM_ERR_WIDTH 1 +#define ESF_EZ_IP_INNER_CHKSUM_ERR_LBN 56 +#define ESF_EZ_IP_INNER_CHKSUM_ERR_WIDTH 1 +#define ESF_EZ_TX_EV_RSVD_LBN 48 +#define ESF_EZ_TX_EV_RSVD_WIDTH 8 #define ESF_DZ_TX_SOFT2_LBN 32 #define ESF_DZ_TX_SOFT2_WIDTH 16 -#define ESF_DZ_TX_CAN_MERGE_LBN 31 -#define ESF_DZ_TX_CAN_MERGE_WIDTH 1 -#define ESF_DZ_TX_SOFT1_LBN 24 -#define ESF_DZ_TX_SOFT1_WIDTH 7 +#define ESF_DD_TX_SOFT1_LBN 24 +#define ESF_DD_TX_SOFT1_WIDTH 8 +#define ESF_EZ_TX_CAN_MERGE_LBN 31 +#define ESF_EZ_TX_CAN_MERGE_WIDTH 1 +#define ESF_EZ_TX_SOFT1_LBN 24 +#define ESF_EZ_TX_SOFT1_WIDTH 7 #define ESF_DZ_TX_QLABEL_LBN 16 #define ESF_DZ_TX_QLABEL_WIDTH 5 #define ESF_DZ_TX_DESCR_INDX_LBN 0 @@ -301,6 +330,10 @@ #define ESE_DZ_TX_OPTION_DESC_TSO 7 #define ESE_DZ_TX_OPTION_DESC_VLAN 6 #define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_TSO_OPTION_TYPE_LBN 56 +#define ESF_DZ_TX_TSO_OPTION_TYPE_WIDTH 4 +#define ESE_DZ_TX_TSO_OPTION_DESC_ENCAP 1 +#define ESE_DZ_TX_TSO_OPTION_DESC_NORMAL 0 #define ESF_DZ_TX_TSO_TCP_FLAGS_LBN 48 #define ESF_DZ_TX_TSO_TCP_FLAGS_WIDTH 8 #define ESF_DZ_TX_TSO_IP_ID_LBN 32 @@ -308,6 +341,46 @@ #define ESF_DZ_TX_TSO_TCP_SEQNO_LBN 0 #define ESF_DZ_TX_TSO_TCP_SEQNO_WIDTH 32 +/* TX_TSO_FATSO2A_DESC */ +#define ESF_DZ_TX_DESC_IS_OPT_LBN 63 +#define ESF_DZ_TX_DESC_IS_OPT_WIDTH 1 +#define ESF_DZ_TX_OPTION_TYPE_LBN 60 +#define ESF_DZ_TX_OPTION_TYPE_WIDTH 3 +#define ESE_DZ_TX_OPTION_DESC_TSO 7 +#define ESE_DZ_TX_OPTION_DESC_VLAN 6 +#define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_TSO_OPTION_TYPE_LBN 56 +#define ESF_DZ_TX_TSO_OPTION_TYPE_WIDTH 4 +#define ESE_DZ_TX_TSO_OPTION_DESC_FATSO2B 3 +#define ESE_DZ_TX_TSO_OPTION_DESC_FATSO2A 2 +#define ESE_DZ_TX_TSO_OPTION_DESC_ENCAP 1 +#define ESE_DZ_TX_TSO_OPTION_DESC_NORMAL 0 +#define ESF_DZ_TX_TSO_IP_ID_LBN 32 +#define ESF_DZ_TX_TSO_IP_ID_WIDTH 16 +#define ESF_DZ_TX_TSO_TCP_SEQNO_LBN 0 +#define ESF_DZ_TX_TSO_TCP_SEQNO_WIDTH 32 + + +/* TX_TSO_FATSO2B_DESC */ +#define ESF_DZ_TX_DESC_IS_OPT_LBN 63 +#define ESF_DZ_TX_DESC_IS_OPT_WIDTH 1 +#define ESF_DZ_TX_OPTION_TYPE_LBN 60 +#define ESF_DZ_TX_OPTION_TYPE_WIDTH 3 +#define ESE_DZ_TX_OPTION_DESC_TSO 7 +#define ESE_DZ_TX_OPTION_DESC_VLAN 6 +#define ESE_DZ_TX_OPTION_DESC_CRC_CSUM 0 +#define ESF_DZ_TX_TSO_OPTION_TYPE_LBN 56 +#define ESF_DZ_TX_TSO_OPTION_TYPE_WIDTH 4 +#define ESE_DZ_TX_TSO_OPTION_DESC_FATSO2B 3 +#define ESE_DZ_TX_TSO_OPTION_DESC_FATSO2A 2 +#define ESE_DZ_TX_TSO_OPTION_DESC_ENCAP 1 +#define ESE_DZ_TX_TSO_OPTION_DESC_NORMAL 0 +#define ESF_DZ_TX_TSO_OUTER_IP_ID_LBN 0 +#define ESF_DZ_TX_TSO_OUTER_IP_ID_WIDTH 16 +#define ESF_DZ_TX_TSO_TCP_MSS_LBN 32 +#define ESF_DZ_TX_TSO_TCP_MSS_WIDTH 16 + + /*************************************************************************/ /* TX_DESC_UPD_REG: Transmit descriptor update register. -- cgit v1.1 From e9117e5099ea29592c2a6180f368951948837a8b Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Thu, 17 Nov 2016 10:51:54 +0000 Subject: sfc: Firmware-Assisted TSO version 2 Add support for FATSOv2 to the driver. FATSOv2 offloads far more of the task of TCP segmentation to the firmware, such that we now just pass a single super-packet to the NIC. This means TSO has a great deal in common with a normal DMA transmit, apart from adding a couple of option descriptors. NIC-specific checks have been moved off the fast path and in to initialisation where possible. This also moves FATSOv1/SWTSO to a new file (tx_tso.c). The end of transmit and some error handling is now outside TSO, since it is common with other code. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/Makefile | 2 +- drivers/net/ethernet/sfc/ef10.c | 123 +++- drivers/net/ethernet/sfc/ethtool.c | 1 + drivers/net/ethernet/sfc/falcon.c | 2 + drivers/net/ethernet/sfc/farch.c | 15 + drivers/net/ethernet/sfc/net_driver.h | 47 +- drivers/net/ethernet/sfc/nic.h | 2 + drivers/net/ethernet/sfc/siena.c | 1 + drivers/net/ethernet/sfc/tx.c | 1013 ++++++++------------------------- drivers/net/ethernet/sfc/tx.h | 27 + drivers/net/ethernet/sfc/tx_tso.c | 570 +++++++++++++++++++ 11 files changed, 1035 insertions(+), 768 deletions(-) create mode 100644 drivers/net/ethernet/sfc/tx.h create mode 100644 drivers/net/ethernet/sfc/tx_tso.c diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index ce8470fe7..b3b620f 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -1,7 +1,7 @@ sfc-y += efx.o nic.o farch.o falcon.o siena.o ef10.o tx.o \ rx.o selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ tenxpress.o txc43128_phy.o falcon_boards.o \ - mcdi.o mcdi_port.o mcdi_mon.o ptp.o + mcdi.o mcdi_port.o mcdi_mon.o ptp.o tx_tso.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o siena_sriov.o ef10_sriov.o diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index e61807e..4b69ed9 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2086,6 +2086,78 @@ static inline void efx_ef10_push_tx_desc(struct efx_tx_queue *tx_queue, ER_DZ_TX_DESC_UPD, tx_queue->queue); } +/* Add Firmware-Assisted TSO v2 option descriptors to a queue. + */ +static int efx_ef10_tx_tso_desc(struct efx_tx_queue *tx_queue, + struct sk_buff *skb, + bool *data_mapped) +{ + struct efx_tx_buffer *buffer; + struct tcphdr *tcp; + struct iphdr *ip; + + u16 ipv4_id; + u32 seqnum; + u32 mss; + + EFX_BUG_ON_PARANOID(tx_queue->tso_version != 2); + + mss = skb_shinfo(skb)->gso_size; + + if (unlikely(mss < 4)) { + WARN_ONCE(1, "MSS of %u is too small for TSO v2\n", mss); + return -EINVAL; + } + + ip = ip_hdr(skb); + if (ip->version == 4) { + /* Modify IPv4 header if needed. */ + ip->tot_len = 0; + ip->check = 0; + ipv4_id = ip->id; + } else { + /* Modify IPv6 header if needed. */ + struct ipv6hdr *ipv6 = ipv6_hdr(skb); + + ipv6->payload_len = 0; + ipv4_id = 0; + } + + tcp = tcp_hdr(skb); + seqnum = ntohl(tcp->seq); + + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + + buffer->flags = EFX_TX_BUF_OPTION; + buffer->len = 0; + buffer->unmap_len = 0; + EFX_POPULATE_QWORD_5(buffer->option, + ESF_DZ_TX_DESC_IS_OPT, 1, + ESF_DZ_TX_OPTION_TYPE, ESE_DZ_TX_OPTION_DESC_TSO, + ESF_DZ_TX_TSO_OPTION_TYPE, + ESE_DZ_TX_TSO_OPTION_DESC_FATSO2A, + ESF_DZ_TX_TSO_IP_ID, ipv4_id, + ESF_DZ_TX_TSO_TCP_SEQNO, seqnum + ); + ++tx_queue->insert_count; + + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + + buffer->flags = EFX_TX_BUF_OPTION; + buffer->len = 0; + buffer->unmap_len = 0; + EFX_POPULATE_QWORD_4(buffer->option, + ESF_DZ_TX_DESC_IS_OPT, 1, + ESF_DZ_TX_OPTION_TYPE, ESE_DZ_TX_OPTION_DESC_TSO, + ESF_DZ_TX_TSO_OPTION_TYPE, + ESE_DZ_TX_TSO_OPTION_DESC_FATSO2B, + ESF_DZ_TX_TSO_TCP_MSS, mss + ); + ++tx_queue->insert_count; + + return 0; +} + static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) { MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_TXQ_IN_LEN(EFX_MAX_DMAQ_SIZE * 8 / @@ -2095,6 +2167,7 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) struct efx_channel *channel = tx_queue->channel; struct efx_nic *efx = tx_queue->efx; struct efx_ef10_nic_data *nic_data = efx->nic_data; + bool tso_v2 = false; size_t inlen; dma_addr_t dma_addr; efx_qword_t *txd; @@ -2102,13 +2175,33 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) int i; BUILD_BUG_ON(MC_CMD_INIT_TXQ_OUT_LEN != 0); + /* TSOv2 is a limited resource that can only be configured on a limited + * number of queues. TSO without checksum offload is not really a thing, + * so we only enable it for those queues. + * + * TODO: handle failure to allocate this in the case where we've used + * all the queues. + */ + if (csum_offload && (nic_data->datapath_caps2 & + (1 << MC_CMD_GET_CAPABILITIES_V2_OUT_TX_TSO_V2_LBN))) { + tso_v2 = true; + netif_dbg(efx, hw, efx->net_dev, "Using TSOv2 for channel %u\n", + channel->channel); + } + MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_SIZE, tx_queue->ptr_mask + 1); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_TARGET_EVQ, channel->channel); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_LABEL, tx_queue->queue); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_INSTANCE, tx_queue->queue); - MCDI_POPULATE_DWORD_2(inbuf, INIT_TXQ_IN_FLAGS, + MCDI_POPULATE_DWORD_3(inbuf, INIT_TXQ_IN_FLAGS, + /* This flag was removed from mcdi_pcol.h for + * the non-_EXT version of INIT_TXQ. However, + * firmware still honours it. + */ + INIT_TXQ_EXT_IN_FLAG_TSOV2_EN, tso_v2, INIT_TXQ_IN_FLAG_IP_CSUM_DIS, !csum_offload, INIT_TXQ_IN_FLAG_TCP_CSUM_DIS, !csum_offload); + MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_OWNER_ID, 0); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_PORT_ID, nic_data->vport_id); @@ -2146,8 +2239,11 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) ESF_DZ_TX_OPTION_IP_CSUM, csum_offload); tx_queue->write_count = 1; - if (nic_data->datapath_caps & - (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN)) { + if (tso_v2) { + tx_queue->handle_tso = efx_ef10_tx_tso_desc; + tx_queue->tso_version = 2; + } else if (nic_data->datapath_caps & + (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN)) { tx_queue->tso_version = 1; } @@ -2202,6 +2298,25 @@ static inline void efx_ef10_notify_tx_desc(struct efx_tx_queue *tx_queue) ER_DZ_TX_DESC_UPD_DWORD, tx_queue->queue); } +#define EFX_EF10_MAX_TX_DESCRIPTOR_LEN 0x3fff + +static unsigned int efx_ef10_tx_limit_len(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len) +{ + if (len > EFX_EF10_MAX_TX_DESCRIPTOR_LEN) { + /* If we need to break across multiple descriptors we should + * stop at a page boundary. This assumes the length limit is + * greater than the page size. + */ + dma_addr_t end = dma_addr + EFX_EF10_MAX_TX_DESCRIPTOR_LEN; + + BUILD_BUG_ON(EFX_EF10_MAX_TX_DESCRIPTOR_LEN < EFX_PAGE_SIZE); + len = (end & (~(EFX_PAGE_SIZE - 1))) - dma_addr; + } + + return len; +} + static void efx_ef10_tx_write(struct efx_tx_queue *tx_queue) { unsigned int old_write_count = tx_queue->write_count; @@ -5469,6 +5584,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .tx_init = efx_ef10_tx_init, .tx_remove = efx_ef10_tx_remove, .tx_write = efx_ef10_tx_write, + .tx_limit_len = efx_ef10_tx_limit_len, .rx_push_rss_config = efx_ef10_vf_rx_push_rss_config, .rx_probe = efx_ef10_rx_probe, .rx_init = efx_ef10_rx_init, @@ -5575,6 +5691,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .tx_init = efx_ef10_tx_init, .tx_remove = efx_ef10_tx_remove, .tx_write = efx_ef10_tx_write, + .tx_limit_len = efx_ef10_tx_limit_len, .rx_push_rss_config = efx_ef10_pf_rx_push_rss_config, .rx_probe = efx_ef10_rx_probe, .rx_init = efx_ef10_rx_init, diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index bf126f9..bd5edd6 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -71,6 +71,7 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = { EFX_ETHTOOL_UINT_TXQ_STAT(tso_packets), EFX_ETHTOOL_UINT_TXQ_STAT(pushes), EFX_ETHTOOL_UINT_TXQ_STAT(pio_packets), + EFX_ETHTOOL_UINT_TXQ_STAT(cb_packets), EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), diff --git a/drivers/net/ethernet/sfc/falcon.c b/drivers/net/ethernet/sfc/falcon.c index 1a70926..6a1e74b 100644 --- a/drivers/net/ethernet/sfc/falcon.c +++ b/drivers/net/ethernet/sfc/falcon.c @@ -2750,6 +2750,7 @@ const struct efx_nic_type falcon_a1_nic_type = { .tx_init = efx_farch_tx_init, .tx_remove = efx_farch_tx_remove, .tx_write = efx_farch_tx_write, + .tx_limit_len = efx_farch_tx_limit_len, .rx_push_rss_config = dummy_rx_push_rss_config, .rx_probe = efx_farch_rx_probe, .rx_init = efx_farch_rx_init, @@ -2849,6 +2850,7 @@ const struct efx_nic_type falcon_b0_nic_type = { .tx_init = efx_farch_tx_init, .tx_remove = efx_farch_tx_remove, .tx_write = efx_farch_tx_write, + .tx_limit_len = efx_farch_tx_limit_len, .rx_push_rss_config = falcon_b0_rx_push_rss_config, .rx_probe = efx_farch_rx_probe, .rx_init = efx_farch_rx_init, diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c index 4762ec4..3d5b91b 100644 --- a/drivers/net/ethernet/sfc/farch.c +++ b/drivers/net/ethernet/sfc/farch.c @@ -356,6 +356,21 @@ void efx_farch_tx_write(struct efx_tx_queue *tx_queue) } } +unsigned int efx_farch_tx_limit_len(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len) +{ + /* Don't cross 4K boundaries with descriptors. */ + unsigned int limit = (~dma_addr & (EFX_PAGE_SIZE - 1)) + 1; + + len = min(limit, len); + + if (EFX_WORKAROUND_5391(tx_queue->efx) && (dma_addr & 0xf)) + len = min_t(unsigned int, len, 512 - (dma_addr & 0xf)); + + return len; +} + + /* Allocate hardware resources for a TX queue */ int efx_farch_tx_probe(struct efx_tx_queue *tx_queue) { diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index fec51c4..2da3e8f 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -189,13 +189,17 @@ struct efx_tx_buffer { * @channel: The associated channel * @core_txq: The networking core TX queue structure * @buffer: The software buffer ring - * @tsoh_page: Array of pages of TSO header buffers + * @cb_page: Array of pages of copy buffers. Carved up according to + * %EFX_TX_CB_ORDER into %EFX_TX_CB_SIZE-sized chunks. * @txd: The hardware descriptor ring * @ptr_mask: The size of the ring minus 1. * @piobuf: PIO buffer region for this TX queue (shared with its partner). * Size of the region is efx_piobuf_size. * @piobuf_offset: Buffer offset to be specified in PIO descriptors * @initialised: Has hardware queue been initialised? + * @tx_min_size: Minimum transmit size for this queue. Depends on HW. + * @handle_tso: TSO xmit preparation handler. Sets up the TSO metadata and + * may also map tx data, depending on the nature of the TSO implementation. * @read_count: Current read pointer. * This is the number of buffers that have been removed from both rings. * @old_write_count: The value of @write_count when last checked. @@ -224,6 +228,7 @@ struct efx_tx_buffer { * @pushes: Number of times the TX push feature has been used * @pio_packets: Number of times the TX PIO feature has been used * @xmit_more_available: Are any packets waiting to be pushed to the NIC + * @cb_packets: Number of times the TX copybreak feature has been used * @empty_read_count: If the completion path has seen the queue as empty * and the transmission path has not yet checked this, the value of * @read_count bitwise-added to %EFX_EMPTY_COUNT_VALID; otherwise 0. @@ -236,12 +241,16 @@ struct efx_tx_queue { struct efx_channel *channel; struct netdev_queue *core_txq; struct efx_tx_buffer *buffer; - struct efx_buffer *tsoh_page; + struct efx_buffer *cb_page; struct efx_special_buffer txd; unsigned int ptr_mask; void __iomem *piobuf; unsigned int piobuf_offset; bool initialised; + unsigned int tx_min_size; + + /* Function pointers used in the fast path. */ + int (*handle_tso)(struct efx_tx_queue*, struct sk_buff*, bool *); /* Members used mainly on the completion path */ unsigned int read_count ____cacheline_aligned_in_smp; @@ -260,6 +269,7 @@ struct efx_tx_queue { unsigned int pushes; unsigned int pio_packets; bool xmit_more_available; + unsigned int cb_packets; /* Statistics to supplement MAC stats */ unsigned long tx_packets; @@ -269,6 +279,9 @@ struct efx_tx_queue { atomic_t flush_outstanding; }; +#define EFX_TX_CB_ORDER 7 +#define EFX_TX_CB_SIZE (1 << EFX_TX_CB_ORDER) - NET_IP_ALIGN + /** * struct efx_rx_buffer - An Efx RX data buffer * @dma_addr: DMA base address of the buffer @@ -1288,6 +1301,8 @@ struct efx_nic_type { void (*tx_init)(struct efx_tx_queue *tx_queue); void (*tx_remove)(struct efx_tx_queue *tx_queue); void (*tx_write)(struct efx_tx_queue *tx_queue); + unsigned int (*tx_limit_len)(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len); int (*rx_push_rss_config)(struct efx_nic *efx, bool user, const u32 *rx_indir_table); int (*rx_probe)(struct efx_rx_queue *rx_queue); @@ -1545,4 +1560,32 @@ static inline netdev_features_t efx_supported_features(const struct efx_nic *efx return net_dev->features | net_dev->hw_features; } +/* Get the current TX queue insert index. */ +static inline unsigned int +efx_tx_queue_get_insert_index(const struct efx_tx_queue *tx_queue) +{ + return tx_queue->insert_count & tx_queue->ptr_mask; +} + +/* Get a TX buffer. */ +static inline struct efx_tx_buffer * +__efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue) +{ + return &tx_queue->buffer[efx_tx_queue_get_insert_index(tx_queue)]; +} + +/* Get a TX buffer, checking it's not currently in use. */ +static inline struct efx_tx_buffer * +efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue) +{ + struct efx_tx_buffer *buffer = + __efx_tx_queue_get_insert_buffer(tx_queue); + + EFX_BUG_ON_PARANOID(buffer->len); + EFX_BUG_ON_PARANOID(buffer->flags); + EFX_BUG_ON_PARANOID(buffer->unmap_len); + + return buffer; +} + #endif /* EFX_NET_DRIVER_H */ diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 73bee7e..06dd96e 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -681,6 +681,8 @@ void efx_farch_tx_init(struct efx_tx_queue *tx_queue); void efx_farch_tx_fini(struct efx_tx_queue *tx_queue); void efx_farch_tx_remove(struct efx_tx_queue *tx_queue); void efx_farch_tx_write(struct efx_tx_queue *tx_queue); +unsigned int efx_farch_tx_limit_len(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len); int efx_farch_rx_probe(struct efx_rx_queue *rx_queue); void efx_farch_rx_init(struct efx_rx_queue *rx_queue); void efx_farch_rx_fini(struct efx_rx_queue *rx_queue); diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 04ed1b4..3975cad 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -977,6 +977,7 @@ const struct efx_nic_type siena_a0_nic_type = { .tx_init = efx_farch_tx_init, .tx_remove = efx_farch_tx_remove, .tx_write = efx_farch_tx_write, + .tx_limit_len = efx_farch_tx_limit_len, .rx_push_rss_config = siena_rx_push_rss_config, .rx_probe = efx_farch_rx_probe, .rx_init = efx_farch_rx_init, diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 2337789..3089e88 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -22,6 +22,7 @@ #include "efx.h" #include "io.h" #include "nic.h" +#include "tx.h" #include "workarounds.h" #include "ef10_regs.h" @@ -33,29 +34,30 @@ unsigned int efx_piobuf_size __read_mostly = EFX_PIOBUF_SIZE_DEF; #endif /* EFX_USE_PIO */ -static inline unsigned int -efx_tx_queue_get_insert_index(const struct efx_tx_queue *tx_queue) +static inline u8 *efx_tx_get_copy_buffer(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer) { - return tx_queue->insert_count & tx_queue->ptr_mask; -} + unsigned int index = efx_tx_queue_get_insert_index(tx_queue); + struct efx_buffer *page_buf = + &tx_queue->cb_page[index >> (PAGE_SHIFT - EFX_TX_CB_ORDER)]; + unsigned int offset = + ((index << EFX_TX_CB_ORDER) + NET_IP_ALIGN) & (PAGE_SIZE - 1); -static inline struct efx_tx_buffer * -__efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue) -{ - return &tx_queue->buffer[efx_tx_queue_get_insert_index(tx_queue)]; + if (unlikely(!page_buf->addr) && + efx_nic_alloc_buffer(tx_queue->efx, page_buf, PAGE_SIZE, + GFP_ATOMIC)) + return NULL; + buffer->dma_addr = page_buf->dma_addr + offset; + buffer->unmap_len = 0; + return (u8 *)page_buf->addr + offset; } -static inline struct efx_tx_buffer * -efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue) +u8 *efx_tx_get_copy_buffer_limited(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer, size_t len) { - struct efx_tx_buffer *buffer = - __efx_tx_queue_get_insert_buffer(tx_queue); - - EFX_BUG_ON_PARANOID(buffer->len); - EFX_BUG_ON_PARANOID(buffer->flags); - EFX_BUG_ON_PARANOID(buffer->unmap_len); - - return buffer; + if (len > EFX_TX_CB_SIZE) + return NULL; + return efx_tx_get_copy_buffer(tx_queue, buffer); } static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, @@ -90,27 +92,6 @@ static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, buffer->flags = 0; } -static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, - struct sk_buff *skb); - -static inline unsigned -efx_max_tx_len(struct efx_nic *efx, dma_addr_t dma_addr) -{ - /* Depending on the NIC revision, we can use descriptor - * lengths up to 8K or 8K-1. However, since PCI Express - * devices must split read requests at 4K boundaries, there is - * little benefit from using descriptors that cross those - * boundaries and we keep things simple by not doing so. - */ - unsigned len = (~dma_addr & (EFX_PAGE_SIZE - 1)) + 1; - - /* Work around hardware bug for unaligned buffers. */ - if (EFX_WORKAROUND_5391(efx) && (dma_addr & 0xf)) - len = min_t(unsigned, len, 512 - (dma_addr & 0xf)); - - return len; -} - unsigned int efx_tx_max_skb_descs(struct efx_nic *efx) { /* Header and payload descriptor for each output segment, plus @@ -173,6 +154,39 @@ static void efx_tx_maybe_stop_queue(struct efx_tx_queue *txq1) } } +static int efx_enqueue_skb_copy(struct efx_tx_queue *tx_queue, + struct sk_buff *skb) +{ + unsigned int min_len = tx_queue->tx_min_size; + unsigned int copy_len = skb->len; + struct efx_tx_buffer *buffer; + u8 *copy_buffer; + int rc; + + EFX_BUG_ON_PARANOID(copy_len > EFX_TX_CB_SIZE); + + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + + copy_buffer = efx_tx_get_copy_buffer(tx_queue, buffer); + if (unlikely(!copy_buffer)) + return -ENOMEM; + + rc = skb_copy_bits(skb, 0, copy_buffer, copy_len); + EFX_WARN_ON_PARANOID(rc); + if (unlikely(copy_len < min_len)) { + memset(copy_buffer + copy_len, 0, min_len - copy_len); + buffer->len = min_len; + } else { + buffer->len = copy_len; + } + + buffer->skb = skb; + buffer->flags = EFX_TX_BUF_SKB; + + ++tx_queue->insert_count; + return rc; +} + #ifdef EFX_USE_PIO struct efx_short_copy_buffer { @@ -267,8 +281,8 @@ static void efx_skb_copy_bits_to_pio(struct efx_nic *efx, struct sk_buff *skb, EFX_BUG_ON_PARANOID(skb_shinfo(skb)->frag_list); } -static struct efx_tx_buffer * -efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +static int efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, + struct sk_buff *skb) { struct efx_tx_buffer *buffer = efx_tx_queue_get_insert_buffer(tx_queue); @@ -292,7 +306,7 @@ efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb) efx_flush_copy_buffer(tx_queue->efx, piobuf, ©_buf); } else { /* Pad the write to the size of a cache line. - * We can do this because we know the skb_shared_info sruct is + * We can do this because we know the skb_shared_info struct is * after the source, and the destination buffer is big enough. */ BUILD_BUG_ON(L1_CACHE_BYTES > @@ -301,6 +315,9 @@ efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb) ALIGN(skb->len, L1_CACHE_BYTES) >> 3); } + buffer->skb = skb; + buffer->flags = EFX_TX_BUF_SKB | EFX_TX_BUF_OPTION; + EFX_POPULATE_QWORD_5(buffer->option, ESF_DZ_TX_DESC_IS_OPT, 1, ESF_DZ_TX_OPTION_TYPE, ESE_DZ_TX_OPTION_DESC_PIO, @@ -308,127 +325,192 @@ efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb) ESF_DZ_TX_PIO_BYTE_CNT, skb->len, ESF_DZ_TX_PIO_BUF_ADDR, tx_queue->piobuf_offset); - ++tx_queue->pio_packets; ++tx_queue->insert_count; - return buffer; + return 0; } #endif /* EFX_USE_PIO */ -/* - * Add a socket buffer to a TX queue - * - * This maps all fragments of a socket buffer for DMA and adds them to - * the TX queue. The queue's insert pointer will be incremented by - * the number of fragments in the socket buffer. - * - * If any DMA mapping fails, any mapped fragments will be unmapped, - * the queue's insert pointer will be restored to its original value. - * - * This function is split out from efx_hard_start_xmit to allow the - * loopback test to direct packets via specific TX queues. - * - * Returns NETDEV_TX_OK. - * You must hold netif_tx_lock() to call this function. - */ -netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +static struct efx_tx_buffer *efx_tx_map_chunk(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, + size_t len) { - struct efx_nic *efx = tx_queue->efx; - struct device *dma_dev = &efx->pci_dev->dev; + const struct efx_nic_type *nic_type = tx_queue->efx->type; struct efx_tx_buffer *buffer; - unsigned int old_insert_count = tx_queue->insert_count; - skb_frag_t *fragment; - unsigned int len, unmap_len = 0; - dma_addr_t dma_addr, unmap_addr = 0; unsigned int dma_len; - unsigned short dma_flags; - int i = 0; - if (skb_shinfo(skb)->gso_size) - return efx_enqueue_skb_tso(tx_queue, skb); + /* Map the fragment taking account of NIC-dependent DMA limits. */ + do { + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + dma_len = nic_type->tx_limit_len(tx_queue, dma_addr, len); - /* Get size of the initial fragment */ - len = skb_headlen(skb); + buffer->len = dma_len; + buffer->dma_addr = dma_addr; + buffer->flags = EFX_TX_BUF_CONT; + len -= dma_len; + dma_addr += dma_len; + ++tx_queue->insert_count; + } while (len); - /* Pad if necessary */ - if (EFX_WORKAROUND_15592(efx) && skb->len <= 32) { - EFX_BUG_ON_PARANOID(skb->data_len); - len = 32 + 1; - if (skb_pad(skb, len - skb->len)) - return NETDEV_TX_OK; - } + return buffer; +} - /* Consider using PIO for short packets */ -#ifdef EFX_USE_PIO - if (skb->len <= efx_piobuf_size && !skb->xmit_more && - efx_nic_may_tx_pio(tx_queue)) { - buffer = efx_enqueue_skb_pio(tx_queue, skb); - dma_flags = EFX_TX_BUF_OPTION; - goto finish_packet; - } -#endif +/* Map all data from an SKB for DMA and create descriptors on the queue. + */ +static int efx_tx_map_data(struct efx_tx_queue *tx_queue, struct sk_buff *skb, + unsigned int segment_count) +{ + struct efx_nic *efx = tx_queue->efx; + struct device *dma_dev = &efx->pci_dev->dev; + unsigned int frag_index, nr_frags; + dma_addr_t dma_addr, unmap_addr; + unsigned short dma_flags; + size_t len, unmap_len; - /* Map for DMA. Use dma_map_single rather than dma_map_page - * since this is more efficient on machines with sparse - * memory. - */ - dma_flags = EFX_TX_BUF_MAP_SINGLE; - dma_addr = dma_map_single(dma_dev, skb->data, len, PCI_DMA_TODEVICE); + nr_frags = skb_shinfo(skb)->nr_frags; + frag_index = 0; - /* Process all fragments */ - while (1) { - if (unlikely(dma_mapping_error(dma_dev, dma_addr))) - goto dma_err; + /* Map header data. */ + len = skb_headlen(skb); + dma_addr = dma_map_single(dma_dev, skb->data, len, DMA_TO_DEVICE); + dma_flags = EFX_TX_BUF_MAP_SINGLE; + unmap_len = len; + unmap_addr = dma_addr; - /* Store fields for marking in the per-fragment final - * descriptor */ - unmap_len = len; - unmap_addr = dma_addr; + if (unlikely(dma_mapping_error(dma_dev, dma_addr))) + return -EIO; - /* Add to TX queue, splitting across DMA boundaries */ - do { - buffer = efx_tx_queue_get_insert_buffer(tx_queue); + if (segment_count) { + /* For TSO we need to put the header in to a separate + * descriptor. Map this separately if necessary. + */ + size_t header_len = skb_transport_header(skb) - skb->data + + (tcp_hdr(skb)->doff << 2u); + + if (header_len != len) { + tx_queue->tso_long_headers++; + efx_tx_map_chunk(tx_queue, dma_addr, header_len); + len -= header_len; + dma_addr += header_len; + } + } - dma_len = efx_max_tx_len(efx, dma_addr); - if (likely(dma_len >= len)) - dma_len = len; + /* Add descriptors for each fragment. */ + do { + struct efx_tx_buffer *buffer; + skb_frag_t *fragment; - /* Fill out per descriptor fields */ - buffer->len = dma_len; - buffer->dma_addr = dma_addr; - buffer->flags = EFX_TX_BUF_CONT; - len -= dma_len; - dma_addr += dma_len; - ++tx_queue->insert_count; - } while (len); + buffer = efx_tx_map_chunk(tx_queue, dma_addr, len); - /* Transfer ownership of the unmapping to the final buffer */ + /* The final descriptor for a fragment is responsible for + * unmapping the whole fragment. + */ buffer->flags = EFX_TX_BUF_CONT | dma_flags; buffer->unmap_len = unmap_len; buffer->dma_offset = buffer->dma_addr - unmap_addr; - unmap_len = 0; - /* Get address and size of next fragment */ - if (i >= skb_shinfo(skb)->nr_frags) - break; - fragment = &skb_shinfo(skb)->frags[i]; + if (frag_index >= nr_frags) { + /* Store SKB details with the final buffer for + * the completion. + */ + buffer->skb = skb; + buffer->flags = EFX_TX_BUF_SKB | dma_flags; + return 0; + } + + /* Move on to the next fragment. */ + fragment = &skb_shinfo(skb)->frags[frag_index++]; len = skb_frag_size(fragment); - i++; - /* Map for DMA */ + dma_addr = skb_frag_dma_map(dma_dev, fragment, + 0, len, DMA_TO_DEVICE); dma_flags = 0; - dma_addr = skb_frag_dma_map(dma_dev, fragment, 0, len, - DMA_TO_DEVICE); + unmap_len = len; + unmap_addr = dma_addr; + + if (unlikely(dma_mapping_error(dma_dev, dma_addr))) + return -EIO; + } while (1); +} + +/* Remove buffers put into a tx_queue. None of the buffers must have + * an skb attached. + */ +static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) +{ + struct efx_tx_buffer *buffer; + + /* Work backwards until we hit the original insert pointer value */ + while (tx_queue->insert_count != tx_queue->write_count) { + --tx_queue->insert_count; + buffer = __efx_tx_queue_get_insert_buffer(tx_queue); + efx_dequeue_buffer(tx_queue, buffer, NULL, NULL); } +} + +static int efx_tx_tso_sw(struct efx_tx_queue *tx_queue, struct sk_buff *skb, + bool *data_mapped) +{ + return efx_enqueue_skb_tso(tx_queue, skb, data_mapped); +} + +/* + * Add a socket buffer to a TX queue + * + * This maps all fragments of a socket buffer for DMA and adds them to + * the TX queue. The queue's insert pointer will be incremented by + * the number of fragments in the socket buffer. + * + * If any DMA mapping fails, any mapped fragments will be unmapped, + * the queue's insert pointer will be restored to its original value. + * + * This function is split out from efx_hard_start_xmit to allow the + * loopback test to direct packets via specific TX queues. + * + * Returns NETDEV_TX_OK. + * You must hold netif_tx_lock() to call this function. + */ +netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + bool data_mapped = false; + unsigned int segments; + unsigned int skb_len; - /* Transfer ownership of the skb to the final buffer */ + skb_len = skb->len; + segments = skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 0; + if (segments == 1) + segments = 0; /* Don't use TSO for a single segment. */ + + /* Handle TSO first - it's *possible* (although unlikely) that we might + * be passed a packet to segment that's smaller than the copybreak/PIO + * size limit. + */ + if (segments) { + EFX_BUG_ON_PARANOID(!tx_queue->handle_tso); + if (tx_queue->handle_tso(tx_queue, skb, &data_mapped)) + goto err; #ifdef EFX_USE_PIO -finish_packet: + } else if (skb_len <= efx_piobuf_size && !skb->xmit_more && + efx_nic_may_tx_pio(tx_queue)) { + /* Use PIO for short packets with an empty queue. */ + if (efx_enqueue_skb_pio(tx_queue, skb)) + goto err; + tx_queue->pio_packets++; + data_mapped = true; #endif - buffer->skb = skb; - buffer->flags = EFX_TX_BUF_SKB | dma_flags; + } else if (skb_len < tx_queue->tx_min_size || + (skb->data_len && skb_len <= EFX_TX_CB_SIZE)) { + /* Pad short packets or coalesce short fragmented packets. */ + if (efx_enqueue_skb_copy(tx_queue, skb)) + goto err; + tx_queue->cb_packets++; + data_mapped = true; + } - netdev_tx_sent_queue(tx_queue->core_txq, skb->len); + /* Map for DMA and create descriptors if we haven't done so already. */ + if (!data_mapped && (efx_tx_map_data(tx_queue, skb, segments))) + goto err; - efx_tx_maybe_stop_queue(tx_queue); + /* Update BQL */ + netdev_tx_sent_queue(tx_queue->core_txq, skb_len); /* Pass off to hardware */ if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq)) { @@ -446,37 +528,22 @@ finish_packet: tx_queue->xmit_more_available = skb->xmit_more; } - tx_queue->tx_packets++; + if (segments) { + tx_queue->tso_bursts++; + tx_queue->tso_packets += segments; + tx_queue->tx_packets += segments; + } else { + tx_queue->tx_packets++; + } + + efx_tx_maybe_stop_queue(tx_queue); return NETDEV_TX_OK; - dma_err: - netif_err(efx, tx_err, efx->net_dev, - " TX queue %d could not map skb with %d bytes %d " - "fragments for DMA\n", tx_queue->queue, skb->len, - skb_shinfo(skb)->nr_frags + 1); - /* Mark the packet as transmitted, and free the SKB ourselves */ +err: + efx_enqueue_unwind(tx_queue); dev_kfree_skb_any(skb); - - /* Work backwards until we hit the original insert pointer value */ - while (tx_queue->insert_count != old_insert_count) { - unsigned int pkts_compl = 0, bytes_compl = 0; - --tx_queue->insert_count; - buffer = __efx_tx_queue_get_insert_buffer(tx_queue); - efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl); - } - - /* Free the fragment we were mid-way through pushing */ - if (unmap_len) { - if (dma_flags & EFX_TX_BUF_MAP_SINGLE) - dma_unmap_single(dma_dev, unmap_addr, unmap_len, - DMA_TO_DEVICE); - else - dma_unmap_page(dma_dev, unmap_addr, unmap_len, - DMA_TO_DEVICE); - } - return NETDEV_TX_OK; } @@ -667,19 +734,9 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) } } -/* Size of page-based TSO header buffers. Larger blocks must be - * allocated from the heap. - */ -#define TSOH_STD_SIZE 128 -#define TSOH_PER_PAGE (PAGE_SIZE / TSOH_STD_SIZE) - -/* At most half the descriptors in the queue at any time will refer to - * a TSO header buffer, since they must always be followed by a - * payload descriptor referring to an skb. - */ -static unsigned int efx_tsoh_page_count(struct efx_tx_queue *tx_queue) +static unsigned int efx_tx_cb_page_count(struct efx_tx_queue *tx_queue) { - return DIV_ROUND_UP(tx_queue->ptr_mask + 1, 2 * TSOH_PER_PAGE); + return DIV_ROUND_UP(tx_queue->ptr_mask + 1, PAGE_SIZE >> EFX_TX_CB_ORDER); } int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) @@ -703,14 +760,11 @@ int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) if (!tx_queue->buffer) return -ENOMEM; - if (tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD) { - tx_queue->tsoh_page = - kcalloc(efx_tsoh_page_count(tx_queue), - sizeof(tx_queue->tsoh_page[0]), GFP_KERNEL); - if (!tx_queue->tsoh_page) { - rc = -ENOMEM; - goto fail1; - } + tx_queue->cb_page = kcalloc(efx_tx_cb_page_count(tx_queue), + sizeof(tx_queue->cb_page[0]), GFP_KERNEL); + if (!tx_queue->cb_page) { + rc = -ENOMEM; + goto fail1; } /* Allocate hardware ring */ @@ -721,8 +775,8 @@ int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) return 0; fail2: - kfree(tx_queue->tsoh_page); - tx_queue->tsoh_page = NULL; + kfree(tx_queue->cb_page); + tx_queue->cb_page = NULL; fail1: kfree(tx_queue->buffer); tx_queue->buffer = NULL; @@ -731,7 +785,9 @@ fail1: void efx_init_tx_queue(struct efx_tx_queue *tx_queue) { - netif_dbg(tx_queue->efx, drv, tx_queue->efx->net_dev, + struct efx_nic *efx = tx_queue->efx; + + netif_dbg(efx, drv, efx->net_dev, "initialising TX queue %d\n", tx_queue->queue); tx_queue->insert_count = 0; @@ -742,6 +798,14 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue) tx_queue->empty_read_count = 0 | EFX_EMPTY_COUNT_VALID; tx_queue->xmit_more_available = false; + /* Set up default function pointers. These may get replaced by + * efx_nic_init_tx() based off NIC/queue capabilities. + */ + tx_queue->handle_tso = efx_tx_tso_sw; + + /* Some older hardware requires Tx writes larger than 32. */ + tx_queue->tx_min_size = EFX_WORKAROUND_15592(efx) ? 33 : 0; + /* Set up TX descriptor ring */ efx_nic_init_tx(tx_queue); @@ -781,589 +845,14 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) "destroying TX queue %d\n", tx_queue->queue); efx_nic_remove_tx(tx_queue); - if (tx_queue->tsoh_page) { - for (i = 0; i < efx_tsoh_page_count(tx_queue); i++) + if (tx_queue->cb_page) { + for (i = 0; i < efx_tx_cb_page_count(tx_queue); i++) efx_nic_free_buffer(tx_queue->efx, - &tx_queue->tsoh_page[i]); - kfree(tx_queue->tsoh_page); - tx_queue->tsoh_page = NULL; + &tx_queue->cb_page[i]); + kfree(tx_queue->cb_page); + tx_queue->cb_page = NULL; } kfree(tx_queue->buffer); tx_queue->buffer = NULL; } - - -/* Efx TCP segmentation acceleration. - * - * Why? Because by doing it here in the driver we can go significantly - * faster than the GSO. - * - * Requires TX checksum offload support. - */ - -#define PTR_DIFF(p1, p2) ((u8 *)(p1) - (u8 *)(p2)) - -/** - * struct tso_state - TSO state for an SKB - * @out_len: Remaining length in current segment - * @seqnum: Current sequence number - * @ipv4_id: Current IPv4 ID, host endian - * @packet_space: Remaining space in current packet - * @dma_addr: DMA address of current position - * @in_len: Remaining length in current SKB fragment - * @unmap_len: Length of SKB fragment - * @unmap_addr: DMA address of SKB fragment - * @dma_flags: TX buffer flags for DMA mapping - %EFX_TX_BUF_MAP_SINGLE or 0 - * @protocol: Network protocol (after any VLAN header) - * @ip_off: Offset of IP header - * @tcp_off: Offset of TCP header - * @header_len: Number of bytes of header - * @ip_base_len: IPv4 tot_len or IPv6 payload_len, before TCP payload - * @header_dma_addr: Header DMA address, when using option descriptors - * @header_unmap_len: Header DMA mapped length, or 0 if not using option - * descriptors - * - * The state used during segmentation. It is put into this data structure - * just to make it easy to pass into inline functions. - */ -struct tso_state { - /* Output position */ - unsigned out_len; - unsigned seqnum; - u16 ipv4_id; - unsigned packet_space; - - /* Input position */ - dma_addr_t dma_addr; - unsigned in_len; - unsigned unmap_len; - dma_addr_t unmap_addr; - unsigned short dma_flags; - - __be16 protocol; - unsigned int ip_off; - unsigned int tcp_off; - unsigned header_len; - unsigned int ip_base_len; - dma_addr_t header_dma_addr; - unsigned int header_unmap_len; -}; - - -/* - * Verify that our various assumptions about sk_buffs and the conditions - * under which TSO will be attempted hold true. Return the protocol number. - */ -static __be16 efx_tso_check_protocol(struct sk_buff *skb) -{ - __be16 protocol = skb->protocol; - - EFX_BUG_ON_PARANOID(((struct ethhdr *)skb->data)->h_proto != - protocol); - if (protocol == htons(ETH_P_8021Q)) { - struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; - protocol = veh->h_vlan_encapsulated_proto; - } - - if (protocol == htons(ETH_P_IP)) { - EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); - } else { - EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6)); - EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); - } - EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) - + (tcp_hdr(skb)->doff << 2u)) > - skb_headlen(skb)); - - return protocol; -} - -static u8 *efx_tsoh_get_buffer(struct efx_tx_queue *tx_queue, - struct efx_tx_buffer *buffer, unsigned int len) -{ - u8 *result; - - EFX_BUG_ON_PARANOID(buffer->len); - EFX_BUG_ON_PARANOID(buffer->flags); - EFX_BUG_ON_PARANOID(buffer->unmap_len); - - if (likely(len <= TSOH_STD_SIZE - NET_IP_ALIGN)) { - unsigned index = - (tx_queue->insert_count & tx_queue->ptr_mask) / 2; - struct efx_buffer *page_buf = - &tx_queue->tsoh_page[index / TSOH_PER_PAGE]; - unsigned offset = - TSOH_STD_SIZE * (index % TSOH_PER_PAGE) + NET_IP_ALIGN; - - if (unlikely(!page_buf->addr) && - efx_nic_alloc_buffer(tx_queue->efx, page_buf, PAGE_SIZE, - GFP_ATOMIC)) - return NULL; - - result = (u8 *)page_buf->addr + offset; - buffer->dma_addr = page_buf->dma_addr + offset; - buffer->flags = EFX_TX_BUF_CONT; - } else { - tx_queue->tso_long_headers++; - - buffer->heap_buf = kmalloc(NET_IP_ALIGN + len, GFP_ATOMIC); - if (unlikely(!buffer->heap_buf)) - return NULL; - result = (u8 *)buffer->heap_buf + NET_IP_ALIGN; - buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_HEAP; - } - - buffer->len = len; - - return result; -} - -/** - * efx_tx_queue_insert - push descriptors onto the TX queue - * @tx_queue: Efx TX queue - * @dma_addr: DMA address of fragment - * @len: Length of fragment - * @final_buffer: The final buffer inserted into the queue - * - * Push descriptors onto the TX queue. - */ -static void efx_tx_queue_insert(struct efx_tx_queue *tx_queue, - dma_addr_t dma_addr, unsigned len, - struct efx_tx_buffer **final_buffer) -{ - struct efx_tx_buffer *buffer; - struct efx_nic *efx = tx_queue->efx; - unsigned dma_len; - - EFX_BUG_ON_PARANOID(len <= 0); - - while (1) { - buffer = efx_tx_queue_get_insert_buffer(tx_queue); - ++tx_queue->insert_count; - - EFX_BUG_ON_PARANOID(tx_queue->insert_count - - tx_queue->read_count >= - efx->txq_entries); - - buffer->dma_addr = dma_addr; - - dma_len = efx_max_tx_len(efx, dma_addr); - - /* If there is enough space to send then do so */ - if (dma_len >= len) - break; - - buffer->len = dma_len; - buffer->flags = EFX_TX_BUF_CONT; - dma_addr += dma_len; - len -= dma_len; - } - - EFX_BUG_ON_PARANOID(!len); - buffer->len = len; - *final_buffer = buffer; -} - - -/* - * Put a TSO header into the TX queue. - * - * This is special-cased because we know that it is small enough to fit in - * a single fragment, and we know it doesn't cross a page boundary. It - * also allows us to not worry about end-of-packet etc. - */ -static int efx_tso_put_header(struct efx_tx_queue *tx_queue, - struct efx_tx_buffer *buffer, u8 *header) -{ - if (unlikely(buffer->flags & EFX_TX_BUF_HEAP)) { - buffer->dma_addr = dma_map_single(&tx_queue->efx->pci_dev->dev, - header, buffer->len, - DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(&tx_queue->efx->pci_dev->dev, - buffer->dma_addr))) { - kfree(buffer->heap_buf); - buffer->len = 0; - buffer->flags = 0; - return -ENOMEM; - } - buffer->unmap_len = buffer->len; - buffer->dma_offset = 0; - buffer->flags |= EFX_TX_BUF_MAP_SINGLE; - } - - ++tx_queue->insert_count; - return 0; -} - - -/* Remove buffers put into a tx_queue. None of the buffers must have - * an skb attached. - */ -static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue, - unsigned int insert_count) -{ - struct efx_tx_buffer *buffer; - - /* Work backwards until we hit the original insert pointer value */ - while (tx_queue->insert_count != insert_count) { - --tx_queue->insert_count; - buffer = __efx_tx_queue_get_insert_buffer(tx_queue); - efx_dequeue_buffer(tx_queue, buffer, NULL, NULL); - } -} - - -/* Parse the SKB header and initialise state. */ -static int tso_start(struct tso_state *st, struct efx_nic *efx, - struct efx_tx_queue *tx_queue, - const struct sk_buff *skb) -{ - struct device *dma_dev = &efx->pci_dev->dev; - unsigned int header_len, in_len; - bool use_opt_desc = false; - dma_addr_t dma_addr; - - if (tx_queue->tso_version == 1) - use_opt_desc = true; - - st->ip_off = skb_network_header(skb) - skb->data; - st->tcp_off = skb_transport_header(skb) - skb->data; - header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u); - in_len = skb_headlen(skb) - header_len; - st->header_len = header_len; - st->in_len = in_len; - if (st->protocol == htons(ETH_P_IP)) { - st->ip_base_len = st->header_len - st->ip_off; - st->ipv4_id = ntohs(ip_hdr(skb)->id); - } else { - st->ip_base_len = st->header_len - st->tcp_off; - st->ipv4_id = 0; - } - st->seqnum = ntohl(tcp_hdr(skb)->seq); - - EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); - EFX_BUG_ON_PARANOID(tcp_hdr(skb)->syn); - EFX_BUG_ON_PARANOID(tcp_hdr(skb)->rst); - - st->out_len = skb->len - header_len; - - if (!use_opt_desc) { - st->header_unmap_len = 0; - - if (likely(in_len == 0)) { - st->dma_flags = 0; - st->unmap_len = 0; - return 0; - } - - dma_addr = dma_map_single(dma_dev, skb->data + header_len, - in_len, DMA_TO_DEVICE); - st->dma_flags = EFX_TX_BUF_MAP_SINGLE; - st->dma_addr = dma_addr; - st->unmap_addr = dma_addr; - st->unmap_len = in_len; - } else { - dma_addr = dma_map_single(dma_dev, skb->data, - skb_headlen(skb), DMA_TO_DEVICE); - st->header_dma_addr = dma_addr; - st->header_unmap_len = skb_headlen(skb); - st->dma_flags = 0; - st->dma_addr = dma_addr + header_len; - st->unmap_len = 0; - } - - return unlikely(dma_mapping_error(dma_dev, dma_addr)) ? -ENOMEM : 0; -} - -static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, - skb_frag_t *frag) -{ - st->unmap_addr = skb_frag_dma_map(&efx->pci_dev->dev, frag, 0, - skb_frag_size(frag), DMA_TO_DEVICE); - if (likely(!dma_mapping_error(&efx->pci_dev->dev, st->unmap_addr))) { - st->dma_flags = 0; - st->unmap_len = skb_frag_size(frag); - st->in_len = skb_frag_size(frag); - st->dma_addr = st->unmap_addr; - return 0; - } - return -ENOMEM; -} - - -/** - * tso_fill_packet_with_fragment - form descriptors for the current fragment - * @tx_queue: Efx TX queue - * @skb: Socket buffer - * @st: TSO state - * - * Form descriptors for the current fragment, until we reach the end - * of fragment or end-of-packet. - */ -static void tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue, - const struct sk_buff *skb, - struct tso_state *st) -{ - struct efx_tx_buffer *buffer; - int n; - - if (st->in_len == 0) - return; - if (st->packet_space == 0) - return; - - EFX_BUG_ON_PARANOID(st->in_len <= 0); - EFX_BUG_ON_PARANOID(st->packet_space <= 0); - - n = min(st->in_len, st->packet_space); - - st->packet_space -= n; - st->out_len -= n; - st->in_len -= n; - - efx_tx_queue_insert(tx_queue, st->dma_addr, n, &buffer); - - if (st->out_len == 0) { - /* Transfer ownership of the skb */ - buffer->skb = skb; - buffer->flags = EFX_TX_BUF_SKB; - } else if (st->packet_space != 0) { - buffer->flags = EFX_TX_BUF_CONT; - } - - if (st->in_len == 0) { - /* Transfer ownership of the DMA mapping */ - buffer->unmap_len = st->unmap_len; - buffer->dma_offset = buffer->unmap_len - buffer->len; - buffer->flags |= st->dma_flags; - st->unmap_len = 0; - } - - st->dma_addr += n; -} - - -/** - * tso_start_new_packet - generate a new header and prepare for the new packet - * @tx_queue: Efx TX queue - * @skb: Socket buffer - * @st: TSO state - * - * Generate a new header and prepare for the new packet. Return 0 on - * success, or -%ENOMEM if failed to alloc header. - */ -static int tso_start_new_packet(struct efx_tx_queue *tx_queue, - const struct sk_buff *skb, - struct tso_state *st) -{ - struct efx_tx_buffer *buffer = - efx_tx_queue_get_insert_buffer(tx_queue); - bool is_last = st->out_len <= skb_shinfo(skb)->gso_size; - u8 tcp_flags_clear; - - if (!is_last) { - st->packet_space = skb_shinfo(skb)->gso_size; - tcp_flags_clear = 0x09; /* mask out FIN and PSH */ - } else { - st->packet_space = st->out_len; - tcp_flags_clear = 0x00; - } - - if (!st->header_unmap_len) { - /* Allocate and insert a DMA-mapped header buffer. */ - struct tcphdr *tsoh_th; - unsigned ip_length; - u8 *header; - int rc; - - header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len); - if (!header) - return -ENOMEM; - - tsoh_th = (struct tcphdr *)(header + st->tcp_off); - - /* Copy and update the headers. */ - memcpy(header, skb->data, st->header_len); - - tsoh_th->seq = htonl(st->seqnum); - ((u8 *)tsoh_th)[13] &= ~tcp_flags_clear; - - ip_length = st->ip_base_len + st->packet_space; - - if (st->protocol == htons(ETH_P_IP)) { - struct iphdr *tsoh_iph = - (struct iphdr *)(header + st->ip_off); - - tsoh_iph->tot_len = htons(ip_length); - tsoh_iph->id = htons(st->ipv4_id); - } else { - struct ipv6hdr *tsoh_iph = - (struct ipv6hdr *)(header + st->ip_off); - - tsoh_iph->payload_len = htons(ip_length); - } - - rc = efx_tso_put_header(tx_queue, buffer, header); - if (unlikely(rc)) - return rc; - } else { - /* Send the original headers with a TSO option descriptor - * in front - */ - u8 tcp_flags = ((u8 *)tcp_hdr(skb))[13] & ~tcp_flags_clear; - - buffer->flags = EFX_TX_BUF_OPTION; - buffer->len = 0; - buffer->unmap_len = 0; - EFX_POPULATE_QWORD_5(buffer->option, - ESF_DZ_TX_DESC_IS_OPT, 1, - ESF_DZ_TX_OPTION_TYPE, - ESE_DZ_TX_OPTION_DESC_TSO, - ESF_DZ_TX_TSO_TCP_FLAGS, tcp_flags, - ESF_DZ_TX_TSO_IP_ID, st->ipv4_id, - ESF_DZ_TX_TSO_TCP_SEQNO, st->seqnum); - ++tx_queue->insert_count; - - /* We mapped the headers in tso_start(). Unmap them - * when the last segment is completed. - */ - buffer = efx_tx_queue_get_insert_buffer(tx_queue); - buffer->dma_addr = st->header_dma_addr; - buffer->len = st->header_len; - if (is_last) { - buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_MAP_SINGLE; - buffer->unmap_len = st->header_unmap_len; - buffer->dma_offset = 0; - /* Ensure we only unmap them once in case of a - * later DMA mapping error and rollback - */ - st->header_unmap_len = 0; - } else { - buffer->flags = EFX_TX_BUF_CONT; - buffer->unmap_len = 0; - } - ++tx_queue->insert_count; - } - - st->seqnum += skb_shinfo(skb)->gso_size; - - /* Linux leaves suitable gaps in the IP ID space for us to fill. */ - ++st->ipv4_id; - - ++tx_queue->tso_packets; - - ++tx_queue->tx_packets; - - return 0; -} - - -/** - * efx_enqueue_skb_tso - segment and transmit a TSO socket buffer - * @tx_queue: Efx TX queue - * @skb: Socket buffer - * - * Context: You must hold netif_tx_lock() to call this function. - * - * Add socket buffer @skb to @tx_queue, doing TSO or return != 0 if - * @skb was not enqueued. In all cases @skb is consumed. Return - * %NETDEV_TX_OK. - */ -static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, - struct sk_buff *skb) -{ - struct efx_nic *efx = tx_queue->efx; - unsigned int old_insert_count = tx_queue->insert_count; - int frag_i, rc; - struct tso_state state; - - /* Find the packet protocol and sanity-check it */ - state.protocol = efx_tso_check_protocol(skb); - - rc = tso_start(&state, efx, tx_queue, skb); - if (rc) - goto mem_err; - - if (likely(state.in_len == 0)) { - /* Grab the first payload fragment. */ - EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags < 1); - frag_i = 0; - rc = tso_get_fragment(&state, efx, - skb_shinfo(skb)->frags + frag_i); - if (rc) - goto mem_err; - } else { - /* Payload starts in the header area. */ - frag_i = -1; - } - - if (tso_start_new_packet(tx_queue, skb, &state) < 0) - goto mem_err; - - while (1) { - tso_fill_packet_with_fragment(tx_queue, skb, &state); - - /* Move onto the next fragment? */ - if (state.in_len == 0) { - if (++frag_i >= skb_shinfo(skb)->nr_frags) - /* End of payload reached. */ - break; - rc = tso_get_fragment(&state, efx, - skb_shinfo(skb)->frags + frag_i); - if (rc) - goto mem_err; - } - - /* Start at new packet? */ - if (state.packet_space == 0 && - tso_start_new_packet(tx_queue, skb, &state) < 0) - goto mem_err; - } - - netdev_tx_sent_queue(tx_queue->core_txq, skb->len); - - efx_tx_maybe_stop_queue(tx_queue); - - /* Pass off to hardware */ - if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq)) { - struct efx_tx_queue *txq2 = efx_tx_queue_partner(tx_queue); - - /* There could be packets left on the partner queue if those - * SKBs had skb->xmit_more set. If we do not push those they - * could be left for a long time and cause a netdev watchdog. - */ - if (txq2->xmit_more_available) - efx_nic_push_buffers(txq2); - - efx_nic_push_buffers(tx_queue); - } else { - tx_queue->xmit_more_available = skb->xmit_more; - } - - tx_queue->tso_bursts++; - return NETDEV_TX_OK; - - mem_err: - netif_err(efx, tx_err, efx->net_dev, - "Out of memory for TSO headers, or DMA mapping error\n"); - dev_kfree_skb_any(skb); - - /* Free the DMA mapping we were in the process of writing out */ - if (state.unmap_len) { - if (state.dma_flags & EFX_TX_BUF_MAP_SINGLE) - dma_unmap_single(&efx->pci_dev->dev, state.unmap_addr, - state.unmap_len, DMA_TO_DEVICE); - else - dma_unmap_page(&efx->pci_dev->dev, state.unmap_addr, - state.unmap_len, DMA_TO_DEVICE); - } - - /* Free the header DMA mapping, if using option descriptors */ - if (state.header_unmap_len) - dma_unmap_single(&efx->pci_dev->dev, state.header_dma_addr, - state.header_unmap_len, DMA_TO_DEVICE); - - efx_enqueue_unwind(tx_queue, old_insert_count); - return NETDEV_TX_OK; -} diff --git a/drivers/net/ethernet/sfc/tx.h b/drivers/net/ethernet/sfc/tx.h new file mode 100644 index 0000000..1cccc97 --- /dev/null +++ b/drivers/net/ethernet/sfc/tx.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2015 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_TX_H +#define EFX_TX_H + +#include + +/* Driver internal tx-path related declarations. */ + +unsigned int efx_tx_limit_len(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len); + +u8 *efx_tx_get_copy_buffer_limited(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer, size_t len); + +int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, struct sk_buff *skb, + bool *data_mapped); + +#endif /* EFX_TX_H */ diff --git a/drivers/net/ethernet/sfc/tx_tso.c b/drivers/net/ethernet/sfc/tx_tso.c new file mode 100644 index 0000000..99936d7 --- /dev/null +++ b/drivers/net/ethernet/sfc/tx_tso.c @@ -0,0 +1,570 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2015 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "efx.h" +#include "io.h" +#include "nic.h" +#include "tx.h" +#include "workarounds.h" +#include "ef10_regs.h" + +/* Efx legacy TCP segmentation acceleration. + * + * Why? Because by doing it here in the driver we can go significantly + * faster than the GSO. + * + * Requires TX checksum offload support. + */ + +#define PTR_DIFF(p1, p2) ((u8 *)(p1) - (u8 *)(p2)) + +/** + * struct tso_state - TSO state for an SKB + * @out_len: Remaining length in current segment + * @seqnum: Current sequence number + * @ipv4_id: Current IPv4 ID, host endian + * @packet_space: Remaining space in current packet + * @dma_addr: DMA address of current position + * @in_len: Remaining length in current SKB fragment + * @unmap_len: Length of SKB fragment + * @unmap_addr: DMA address of SKB fragment + * @dma_flags: TX buffer flags for DMA mapping - %EFX_TX_BUF_MAP_SINGLE or 0 + * @protocol: Network protocol (after any VLAN header) + * @ip_off: Offset of IP header + * @tcp_off: Offset of TCP header + * @header_len: Number of bytes of header + * @ip_base_len: IPv4 tot_len or IPv6 payload_len, before TCP payload + * @header_dma_addr: Header DMA address, when using option descriptors + * @header_unmap_len: Header DMA mapped length, or 0 if not using option + * descriptors + * + * The state used during segmentation. It is put into this data structure + * just to make it easy to pass into inline functions. + */ +struct tso_state { + /* Output position */ + unsigned int out_len; + unsigned int seqnum; + u16 ipv4_id; + unsigned int packet_space; + + /* Input position */ + dma_addr_t dma_addr; + unsigned int in_len; + unsigned int unmap_len; + dma_addr_t unmap_addr; + unsigned short dma_flags; + + __be16 protocol; + unsigned int ip_off; + unsigned int tcp_off; + unsigned int header_len; + unsigned int ip_base_len; + dma_addr_t header_dma_addr; + unsigned int header_unmap_len; +}; + +static inline void prefetch_ptr(struct efx_tx_queue *tx_queue) +{ + unsigned int insert_ptr = efx_tx_queue_get_insert_index(tx_queue); + char *ptr; + + ptr = (char *) (tx_queue->buffer + insert_ptr); + prefetch(ptr); + prefetch(ptr + 0x80); + + ptr = (char *) (((efx_qword_t *)tx_queue->txd.buf.addr) + insert_ptr); + prefetch(ptr); + prefetch(ptr + 0x80); +} + +/** + * efx_tx_queue_insert - push descriptors onto the TX queue + * @tx_queue: Efx TX queue + * @dma_addr: DMA address of fragment + * @len: Length of fragment + * @final_buffer: The final buffer inserted into the queue + * + * Push descriptors onto the TX queue. + */ +static void efx_tx_queue_insert(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len, + struct efx_tx_buffer **final_buffer) +{ + struct efx_tx_buffer *buffer; + unsigned int dma_len; + + EFX_BUG_ON_PARANOID(len <= 0); + + while (1) { + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + ++tx_queue->insert_count; + + EFX_BUG_ON_PARANOID(tx_queue->insert_count - + tx_queue->read_count >= + tx_queue->efx->txq_entries); + + buffer->dma_addr = dma_addr; + + dma_len = tx_queue->efx->type->tx_limit_len(tx_queue, + dma_addr, len); + + /* If there's space for everything this is our last buffer. */ + if (dma_len >= len) + break; + + buffer->len = dma_len; + buffer->flags = EFX_TX_BUF_CONT; + dma_addr += dma_len; + len -= dma_len; + } + + EFX_BUG_ON_PARANOID(!len); + buffer->len = len; + *final_buffer = buffer; +} + +/* + * Verify that our various assumptions about sk_buffs and the conditions + * under which TSO will be attempted hold true. Return the protocol number. + */ +static __be16 efx_tso_check_protocol(struct sk_buff *skb) +{ + __be16 protocol = skb->protocol; + + EFX_BUG_ON_PARANOID(((struct ethhdr *)skb->data)->h_proto != + protocol); + if (protocol == htons(ETH_P_8021Q)) { + struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; + + protocol = veh->h_vlan_encapsulated_proto; + } + + if (protocol == htons(ETH_P_IP)) { + EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); + } else { + EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6)); + EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); + } + EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) + + (tcp_hdr(skb)->doff << 2u)) > + skb_headlen(skb)); + + return protocol; +} + +static u8 *efx_tsoh_get_buffer(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer, unsigned int len) +{ + u8 *result; + + EFX_BUG_ON_PARANOID(buffer->len); + EFX_BUG_ON_PARANOID(buffer->flags); + EFX_BUG_ON_PARANOID(buffer->unmap_len); + + result = efx_tx_get_copy_buffer_limited(tx_queue, buffer, len); + + if (result) { + buffer->flags = EFX_TX_BUF_CONT; + } else { + buffer->heap_buf = kmalloc(NET_IP_ALIGN + len, GFP_ATOMIC); + if (unlikely(!buffer->heap_buf)) + return NULL; + tx_queue->tso_long_headers++; + result = (u8 *)buffer->heap_buf + NET_IP_ALIGN; + buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_HEAP; + } + + buffer->len = len; + + return result; +} + +/* + * Put a TSO header into the TX queue. + * + * This is special-cased because we know that it is small enough to fit in + * a single fragment, and we know it doesn't cross a page boundary. It + * also allows us to not worry about end-of-packet etc. + */ +static int efx_tso_put_header(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer, u8 *header) +{ + if (unlikely(buffer->flags & EFX_TX_BUF_HEAP)) { + buffer->dma_addr = dma_map_single(&tx_queue->efx->pci_dev->dev, + header, buffer->len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(&tx_queue->efx->pci_dev->dev, + buffer->dma_addr))) { + kfree(buffer->heap_buf); + buffer->len = 0; + buffer->flags = 0; + return -ENOMEM; + } + buffer->unmap_len = buffer->len; + buffer->dma_offset = 0; + buffer->flags |= EFX_TX_BUF_MAP_SINGLE; + } + + ++tx_queue->insert_count; + return 0; +} + + +/* Parse the SKB header and initialise state. */ +static int tso_start(struct tso_state *st, struct efx_nic *efx, + struct efx_tx_queue *tx_queue, + const struct sk_buff *skb) +{ + struct device *dma_dev = &efx->pci_dev->dev; + unsigned int header_len, in_len; + bool use_opt_desc = false; + dma_addr_t dma_addr; + + if (tx_queue->tso_version == 1) + use_opt_desc = true; + + st->ip_off = skb_network_header(skb) - skb->data; + st->tcp_off = skb_transport_header(skb) - skb->data; + header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u); + in_len = skb_headlen(skb) - header_len; + st->header_len = header_len; + st->in_len = in_len; + if (st->protocol == htons(ETH_P_IP)) { + st->ip_base_len = st->header_len - st->ip_off; + st->ipv4_id = ntohs(ip_hdr(skb)->id); + } else { + st->ip_base_len = st->header_len - st->tcp_off; + st->ipv4_id = 0; + } + st->seqnum = ntohl(tcp_hdr(skb)->seq); + + EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); + EFX_BUG_ON_PARANOID(tcp_hdr(skb)->syn); + EFX_BUG_ON_PARANOID(tcp_hdr(skb)->rst); + + st->out_len = skb->len - header_len; + + if (!use_opt_desc) { + st->header_unmap_len = 0; + + if (likely(in_len == 0)) { + st->dma_flags = 0; + st->unmap_len = 0; + return 0; + } + + dma_addr = dma_map_single(dma_dev, skb->data + header_len, + in_len, DMA_TO_DEVICE); + st->dma_flags = EFX_TX_BUF_MAP_SINGLE; + st->dma_addr = dma_addr; + st->unmap_addr = dma_addr; + st->unmap_len = in_len; + } else { + dma_addr = dma_map_single(dma_dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + st->header_dma_addr = dma_addr; + st->header_unmap_len = skb_headlen(skb); + st->dma_flags = 0; + st->dma_addr = dma_addr + header_len; + st->unmap_len = 0; + } + + return unlikely(dma_mapping_error(dma_dev, dma_addr)) ? -ENOMEM : 0; +} + +static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, + skb_frag_t *frag) +{ + st->unmap_addr = skb_frag_dma_map(&efx->pci_dev->dev, frag, 0, + skb_frag_size(frag), DMA_TO_DEVICE); + if (likely(!dma_mapping_error(&efx->pci_dev->dev, st->unmap_addr))) { + st->dma_flags = 0; + st->unmap_len = skb_frag_size(frag); + st->in_len = skb_frag_size(frag); + st->dma_addr = st->unmap_addr; + return 0; + } + return -ENOMEM; +} + + +/** + * tso_fill_packet_with_fragment - form descriptors for the current fragment + * @tx_queue: Efx TX queue + * @skb: Socket buffer + * @st: TSO state + * + * Form descriptors for the current fragment, until we reach the end + * of fragment or end-of-packet. + */ +static void tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue, + const struct sk_buff *skb, + struct tso_state *st) +{ + struct efx_tx_buffer *buffer; + int n; + + if (st->in_len == 0) + return; + if (st->packet_space == 0) + return; + + EFX_BUG_ON_PARANOID(st->in_len <= 0); + EFX_BUG_ON_PARANOID(st->packet_space <= 0); + + n = min(st->in_len, st->packet_space); + + st->packet_space -= n; + st->out_len -= n; + st->in_len -= n; + + efx_tx_queue_insert(tx_queue, st->dma_addr, n, &buffer); + + if (st->out_len == 0) { + /* Transfer ownership of the skb */ + buffer->skb = skb; + buffer->flags = EFX_TX_BUF_SKB; + } else if (st->packet_space != 0) { + buffer->flags = EFX_TX_BUF_CONT; + } + + if (st->in_len == 0) { + /* Transfer ownership of the DMA mapping */ + buffer->unmap_len = st->unmap_len; + buffer->dma_offset = buffer->unmap_len - buffer->len; + buffer->flags |= st->dma_flags; + st->unmap_len = 0; + } + + st->dma_addr += n; +} + + +#define TCP_FLAGS_OFFSET 13 + +/** + * tso_start_new_packet - generate a new header and prepare for the new packet + * @tx_queue: Efx TX queue + * @skb: Socket buffer + * @st: TSO state + * + * Generate a new header and prepare for the new packet. Return 0 on + * success, or -%ENOMEM if failed to alloc header. + */ +static int tso_start_new_packet(struct efx_tx_queue *tx_queue, + const struct sk_buff *skb, + struct tso_state *st) +{ + struct efx_tx_buffer *buffer = + efx_tx_queue_get_insert_buffer(tx_queue); + bool is_last = st->out_len <= skb_shinfo(skb)->gso_size; + u8 tcp_flags_mask; + + if (!is_last) { + st->packet_space = skb_shinfo(skb)->gso_size; + tcp_flags_mask = 0x09; /* mask out FIN and PSH */ + } else { + st->packet_space = st->out_len; + tcp_flags_mask = 0x00; + } + + if (!st->header_unmap_len) { + /* Allocate and insert a DMA-mapped header buffer. */ + struct tcphdr *tsoh_th; + unsigned int ip_length; + u8 *header; + int rc; + + header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len); + if (!header) + return -ENOMEM; + + tsoh_th = (struct tcphdr *)(header + st->tcp_off); + + /* Copy and update the headers. */ + memcpy(header, skb->data, st->header_len); + + tsoh_th->seq = htonl(st->seqnum); + ((u8 *)tsoh_th)[TCP_FLAGS_OFFSET] &= ~tcp_flags_mask; + + ip_length = st->ip_base_len + st->packet_space; + + if (st->protocol == htons(ETH_P_IP)) { + struct iphdr *tsoh_iph = + (struct iphdr *)(header + st->ip_off); + + tsoh_iph->tot_len = htons(ip_length); + tsoh_iph->id = htons(st->ipv4_id); + } else { + struct ipv6hdr *tsoh_iph = + (struct ipv6hdr *)(header + st->ip_off); + + tsoh_iph->payload_len = htons(ip_length); + } + + rc = efx_tso_put_header(tx_queue, buffer, header); + if (unlikely(rc)) + return rc; + } else { + /* Send the original headers with a TSO option descriptor + * in front + */ + u8 tcp_flags = ((u8 *)tcp_hdr(skb))[TCP_FLAGS_OFFSET] & + ~tcp_flags_mask; + + buffer->flags = EFX_TX_BUF_OPTION; + buffer->len = 0; + buffer->unmap_len = 0; + EFX_POPULATE_QWORD_5(buffer->option, + ESF_DZ_TX_DESC_IS_OPT, 1, + ESF_DZ_TX_OPTION_TYPE, + ESE_DZ_TX_OPTION_DESC_TSO, + ESF_DZ_TX_TSO_TCP_FLAGS, tcp_flags, + ESF_DZ_TX_TSO_IP_ID, st->ipv4_id, + ESF_DZ_TX_TSO_TCP_SEQNO, st->seqnum); + ++tx_queue->insert_count; + + /* We mapped the headers in tso_start(). Unmap them + * when the last segment is completed. + */ + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + buffer->dma_addr = st->header_dma_addr; + buffer->len = st->header_len; + if (is_last) { + buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_MAP_SINGLE; + buffer->unmap_len = st->header_unmap_len; + buffer->dma_offset = 0; + /* Ensure we only unmap them once in case of a + * later DMA mapping error and rollback + */ + st->header_unmap_len = 0; + } else { + buffer->flags = EFX_TX_BUF_CONT; + buffer->unmap_len = 0; + } + ++tx_queue->insert_count; + } + + st->seqnum += skb_shinfo(skb)->gso_size; + + /* Linux leaves suitable gaps in the IP ID space for us to fill. */ + ++st->ipv4_id; + + return 0; +} + +/** + * efx_enqueue_skb_tso - segment and transmit a TSO socket buffer + * @tx_queue: Efx TX queue + * @skb: Socket buffer + * @data_mapped: Did we map the data? Always set to true + * by this on success. + * + * Context: You must hold netif_tx_lock() to call this function. + * + * Add socket buffer @skb to @tx_queue, doing TSO or return != 0 if + * @skb was not enqueued. In all cases @skb is consumed. Return + * %NETDEV_TX_OK. + */ +int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, + struct sk_buff *skb, + bool *data_mapped) +{ + struct efx_nic *efx = tx_queue->efx; + int frag_i, rc; + struct tso_state state; + + prefetch(skb->data); + + /* Find the packet protocol and sanity-check it */ + state.protocol = efx_tso_check_protocol(skb); + + EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); + + rc = tso_start(&state, efx, tx_queue, skb); + if (rc) + goto mem_err; + + if (likely(state.in_len == 0)) { + /* Grab the first payload fragment. */ + EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags < 1); + frag_i = 0; + rc = tso_get_fragment(&state, efx, + skb_shinfo(skb)->frags + frag_i); + if (rc) + goto mem_err; + } else { + /* Payload starts in the header area. */ + frag_i = -1; + } + + if (tso_start_new_packet(tx_queue, skb, &state) < 0) + goto mem_err; + + prefetch_ptr(tx_queue); + + while (1) { + tso_fill_packet_with_fragment(tx_queue, skb, &state); + + /* Move onto the next fragment? */ + if (state.in_len == 0) { + if (++frag_i >= skb_shinfo(skb)->nr_frags) + /* End of payload reached. */ + break; + rc = tso_get_fragment(&state, efx, + skb_shinfo(skb)->frags + frag_i); + if (rc) + goto mem_err; + } + + /* Start at new packet? */ + if (state.packet_space == 0 && + tso_start_new_packet(tx_queue, skb, &state) < 0) + goto mem_err; + } + + *data_mapped = true; + + return 0; + + mem_err: + netif_err(efx, tx_err, efx->net_dev, + "Out of memory for TSO headers, or DMA mapping error\n"); + + /* Free the DMA mapping we were in the process of writing out */ + if (state.unmap_len) { + if (state.dma_flags & EFX_TX_BUF_MAP_SINGLE) + dma_unmap_single(&efx->pci_dev->dev, state.unmap_addr, + state.unmap_len, DMA_TO_DEVICE); + else + dma_unmap_page(&efx->pci_dev->dev, state.unmap_addr, + state.unmap_len, DMA_TO_DEVICE); + } + + /* Free the header DMA mapping, if using option descriptors */ + if (state.header_unmap_len) + dma_unmap_single(&efx->pci_dev->dev, state.header_dma_addr, + state.header_unmap_len, DMA_TO_DEVICE); + + return -ENOMEM; +} -- cgit v1.1 From e638ee1d0a6aa10a1a32d9161e88758ecb8d1823 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 17 Nov 2016 10:52:07 +0000 Subject: sfc: handle failure to allocate TSOv2 contexts If we fail to init the TXQ because of insufficient TSOv2 contexts, try again with TSOv2 disabled. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 4b69ed9..9cbe573 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2178,9 +2178,6 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) /* TSOv2 is a limited resource that can only be configured on a limited * number of queues. TSO without checksum offload is not really a thing, * so we only enable it for those queues. - * - * TODO: handle failure to allocate this in the case where we've used - * all the queues. */ if (csum_offload && (nic_data->datapath_caps2 & (1 << MC_CMD_GET_CAPABILITIES_V2_OUT_TX_TSO_V2_LBN))) { @@ -2193,15 +2190,6 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_TARGET_EVQ, channel->channel); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_LABEL, tx_queue->queue); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_INSTANCE, tx_queue->queue); - MCDI_POPULATE_DWORD_3(inbuf, INIT_TXQ_IN_FLAGS, - /* This flag was removed from mcdi_pcol.h for - * the non-_EXT version of INIT_TXQ. However, - * firmware still honours it. - */ - INIT_TXQ_EXT_IN_FLAG_TSOV2_EN, tso_v2, - INIT_TXQ_IN_FLAG_IP_CSUM_DIS, !csum_offload, - INIT_TXQ_IN_FLAG_TCP_CSUM_DIS, !csum_offload); - MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_OWNER_ID, 0); MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_PORT_ID, nic_data->vport_id); @@ -2217,10 +2205,30 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) inlen = MC_CMD_INIT_TXQ_IN_LEN(entries); - rc = efx_mcdi_rpc(efx, MC_CMD_INIT_TXQ, inbuf, inlen, - NULL, 0, NULL); - if (rc) - goto fail; + do { + MCDI_POPULATE_DWORD_3(inbuf, INIT_TXQ_IN_FLAGS, + /* This flag was removed from mcdi_pcol.h for + * the non-_EXT version of INIT_TXQ. However, + * firmware still honours it. + */ + INIT_TXQ_EXT_IN_FLAG_TSOV2_EN, tso_v2, + INIT_TXQ_IN_FLAG_IP_CSUM_DIS, !csum_offload, + INIT_TXQ_IN_FLAG_TCP_CSUM_DIS, !csum_offload); + + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_INIT_TXQ, inbuf, inlen, + NULL, 0, NULL); + if (rc == -ENOSPC && tso_v2) { + /* Retry without TSOv2 if we're short on contexts. */ + tso_v2 = false; + netif_warn(efx, probe, efx->net_dev, + "TSOv2 context not available to segment in hardware. TCP performance may be reduced.\n"); + } else if (rc) { + efx_mcdi_display_error(efx, MC_CMD_INIT_TXQ, + MC_CMD_INIT_TXQ_EXT_IN_LEN, + NULL, 0, rc); + goto fail; + } + } while (rc); /* A previous user of this TX queue might have set us up the * bomb by writing a descriptor to the TX push collector but -- cgit v1.1 From 46d1efd852ccbc94e8c4f8c41cfd84147a103436 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 17 Nov 2016 10:52:36 +0000 Subject: sfc: remove Software TSO It gives no advantage over GSO now that xmit_more exists. If we find ourselves unable to handle a TSO skb (because our TXQ doesn't have a TSOv2 context and the NIC doesn't support TSOv1), hand it back to GSO. Also do that if the TSO handler fails with EINVAL for any other reason. As Falcon-architecture NICs don't support any firmware-assisted TSO, they no longer advertise TSO feature flags at all. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 15 ++ drivers/net/ethernet/sfc/efx.c | 38 ++--- drivers/net/ethernet/sfc/ethtool.c | 1 + drivers/net/ethernet/sfc/net_driver.h | 5 + drivers/net/ethernet/sfc/tx.c | 46 +++++- drivers/net/ethernet/sfc/tx_tso.c | 260 ++++++++++------------------------ 6 files changed, 154 insertions(+), 211 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 9cbe573..0f58ea8 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2158,6 +2158,20 @@ static int efx_ef10_tx_tso_desc(struct efx_tx_queue *tx_queue, return 0; } +static u32 efx_ef10_tso_versions(struct efx_nic *efx) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + u32 tso_versions = 0; + + if (nic_data->datapath_caps & + (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN)) + tso_versions |= BIT(1); + if (nic_data->datapath_caps2 & + (1 << MC_CMD_GET_CAPABILITIES_V2_OUT_TX_TSO_V2_LBN)) + tso_versions |= BIT(2); + return tso_versions; +} + static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) { MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_TXQ_IN_LEN(EFX_MAX_DMAQ_SIZE * 8 / @@ -5759,6 +5773,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { #endif .get_mac_address = efx_ef10_get_mac_address_pf, .set_mac_address = efx_ef10_set_mac_address, + .tso_versions = efx_ef10_tso_versions, .revision = EFX_REV_HUNT_A0, .max_dma_mask = DMA_BIT_MASK(ESF_DZ_TX_KER_BUF_ADDR_WIDTH), diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 52ca040..756a0964 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -3200,23 +3200,6 @@ static int efx_pci_probe(struct pci_dev *pci_dev, efx = netdev_priv(net_dev); efx->type = (const struct efx_nic_type *) entry->driver_data; efx->fixed_features |= NETIF_F_HIGHDMA; - net_dev->features |= (efx->type->offload_features | NETIF_F_SG | - NETIF_F_TSO | NETIF_F_RXCSUM); - if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM)) - net_dev->features |= NETIF_F_TSO6; - /* Mask for features that also apply to VLAN devices */ - net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG | - NETIF_F_HIGHDMA | NETIF_F_ALL_TSO | - NETIF_F_RXCSUM); - - net_dev->hw_features = net_dev->features & ~efx->fixed_features; - - /* Disable VLAN filtering by default. It may be enforced if - * the feature is fixed (i.e. VLAN filters are required to - * receive VLAN tagged packets due to vPort restrictions). - */ - net_dev->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; - net_dev->features |= efx->fixed_features; pci_set_drvdata(pci_dev, efx); SET_NETDEV_DEV(net_dev, &pci_dev->dev); @@ -3239,6 +3222,27 @@ static int efx_pci_probe(struct pci_dev *pci_dev, if (rc) goto fail3; + net_dev->features |= (efx->type->offload_features | NETIF_F_SG | + NETIF_F_TSO | NETIF_F_RXCSUM); + if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM)) + net_dev->features |= NETIF_F_TSO6; + /* Check whether device supports TSO */ + if (!efx->type->tso_versions || !efx->type->tso_versions(efx)) + net_dev->features &= ~NETIF_F_ALL_TSO; + /* Mask for features that also apply to VLAN devices */ + net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG | + NETIF_F_HIGHDMA | NETIF_F_ALL_TSO | + NETIF_F_RXCSUM); + + net_dev->hw_features = net_dev->features & ~efx->fixed_features; + + /* Disable VLAN filtering by default. It may be enforced if + * the feature is fixed (i.e. VLAN filters are required to + * receive VLAN tagged packets due to vPort restrictions). + */ + net_dev->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; + net_dev->features |= efx->fixed_features; + rc = efx_register_netdev(efx); if (rc) goto fail4; diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index bd5edd6..740cdf0 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -69,6 +69,7 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = { EFX_ETHTOOL_UINT_TXQ_STAT(tso_bursts), EFX_ETHTOOL_UINT_TXQ_STAT(tso_long_headers), EFX_ETHTOOL_UINT_TXQ_STAT(tso_packets), + EFX_ETHTOOL_UINT_TXQ_STAT(tso_fallbacks), EFX_ETHTOOL_UINT_TXQ_STAT(pushes), EFX_ETHTOOL_UINT_TXQ_STAT(pio_packets), EFX_ETHTOOL_UINT_TXQ_STAT(cb_packets), diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 2da3e8f..f97f828 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -225,6 +225,7 @@ struct efx_tx_buffer { * @tso_long_headers: Number of packets with headers too long for standard * blocks * @tso_packets: Number of packets via the TSO xmit path + * @tso_fallbacks: Number of times TSO fallback used * @pushes: Number of times the TX push feature has been used * @pio_packets: Number of times the TX PIO feature has been used * @xmit_more_available: Are any packets waiting to be pushed to the NIC @@ -266,6 +267,7 @@ struct efx_tx_queue { unsigned int tso_bursts; unsigned int tso_long_headers; unsigned int tso_packets; + unsigned int tso_fallbacks; unsigned int pushes; unsigned int pio_packets; bool xmit_more_available; @@ -1225,6 +1227,8 @@ struct efx_mtd_partition { * and tx_type will already have been validated but this operation * must validate and update rx_filter. * @set_mac_address: Set the MAC address of the device + * @tso_versions: Returns mask of firmware-assisted TSO versions supported. + * If %NULL, then device does not support any TSO version. * @revision: Hardware architecture revision * @txd_ptr_tbl_base: TX descriptor ring base address * @rxd_ptr_tbl_base: RX descriptor ring base address @@ -1381,6 +1385,7 @@ struct efx_nic_type { void (*vswitching_remove)(struct efx_nic *efx); int (*get_mac_address)(struct efx_nic *efx, unsigned char *perm_addr); int (*set_mac_address)(struct efx_nic *efx); + u32 (*tso_versions)(struct efx_nic *efx); int revision; unsigned int txd_ptr_tbl_base; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 3089e88..1aa728c 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -446,10 +446,38 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) } } -static int efx_tx_tso_sw(struct efx_tx_queue *tx_queue, struct sk_buff *skb, - bool *data_mapped) +/* + * Fallback to software TSO. + * + * This is used if we are unable to send a GSO packet through hardware TSO. + * This should only ever happen due to per-queue restrictions - unsupported + * packets should first be filtered by the feature flags. + * + * Returns 0 on success, error code otherwise. + */ +static int efx_tx_tso_fallback(struct efx_tx_queue *tx_queue, + struct sk_buff *skb) { - return efx_enqueue_skb_tso(tx_queue, skb, data_mapped); + struct sk_buff *segments, *next; + + segments = skb_gso_segment(skb, 0); + if (IS_ERR(segments)) + return PTR_ERR(segments); + + dev_kfree_skb_any(skb); + skb = segments; + + while (skb) { + next = skb->next; + skb->next = NULL; + + if (next) + skb->xmit_more = true; + efx_enqueue_skb(tx_queue, skb); + skb = next; + } + + return 0; } /* @@ -473,6 +501,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) bool data_mapped = false; unsigned int segments; unsigned int skb_len; + int rc; skb_len = skb->len; segments = skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 0; @@ -485,7 +514,14 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) */ if (segments) { EFX_BUG_ON_PARANOID(!tx_queue->handle_tso); - if (tx_queue->handle_tso(tx_queue, skb, &data_mapped)) + rc = tx_queue->handle_tso(tx_queue, skb, &data_mapped); + if (rc == -EINVAL) { + rc = efx_tx_tso_fallback(tx_queue, skb); + tx_queue->tso_fallbacks++; + if (rc == 0) + return 0; + } + if (rc) goto err; #ifdef EFX_USE_PIO } else if (skb_len <= efx_piobuf_size && !skb->xmit_more && @@ -801,7 +837,7 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue) /* Set up default function pointers. These may get replaced by * efx_nic_init_tx() based off NIC/queue capabilities. */ - tx_queue->handle_tso = efx_tx_tso_sw; + tx_queue->handle_tso = efx_enqueue_skb_tso; /* Some older hardware requires Tx writes larger than 32. */ tx_queue->tx_min_size = EFX_WORKAROUND_15592(efx) ? 33 : 0; diff --git a/drivers/net/ethernet/sfc/tx_tso.c b/drivers/net/ethernet/sfc/tx_tso.c index 99936d7..6032887 100644 --- a/drivers/net/ethernet/sfc/tx_tso.c +++ b/drivers/net/ethernet/sfc/tx_tso.c @@ -29,8 +29,7 @@ /* Efx legacy TCP segmentation acceleration. * - * Why? Because by doing it here in the driver we can go significantly - * faster than the GSO. + * Utilises firmware support to go faster than GSO (but not as fast as TSOv2). * * Requires TX checksum offload support. */ @@ -47,15 +46,13 @@ * @in_len: Remaining length in current SKB fragment * @unmap_len: Length of SKB fragment * @unmap_addr: DMA address of SKB fragment - * @dma_flags: TX buffer flags for DMA mapping - %EFX_TX_BUF_MAP_SINGLE or 0 * @protocol: Network protocol (after any VLAN header) * @ip_off: Offset of IP header * @tcp_off: Offset of TCP header * @header_len: Number of bytes of header * @ip_base_len: IPv4 tot_len or IPv6 payload_len, before TCP payload - * @header_dma_addr: Header DMA address, when using option descriptors - * @header_unmap_len: Header DMA mapped length, or 0 if not using option - * descriptors + * @header_dma_addr: Header DMA address + * @header_unmap_len: Header DMA mapped length * * The state used during segmentation. It is put into this data structure * just to make it easy to pass into inline functions. @@ -72,7 +69,6 @@ struct tso_state { unsigned int in_len; unsigned int unmap_len; dma_addr_t unmap_addr; - unsigned short dma_flags; __be16 protocol; unsigned int ip_off; @@ -172,63 +168,6 @@ static __be16 efx_tso_check_protocol(struct sk_buff *skb) return protocol; } -static u8 *efx_tsoh_get_buffer(struct efx_tx_queue *tx_queue, - struct efx_tx_buffer *buffer, unsigned int len) -{ - u8 *result; - - EFX_BUG_ON_PARANOID(buffer->len); - EFX_BUG_ON_PARANOID(buffer->flags); - EFX_BUG_ON_PARANOID(buffer->unmap_len); - - result = efx_tx_get_copy_buffer_limited(tx_queue, buffer, len); - - if (result) { - buffer->flags = EFX_TX_BUF_CONT; - } else { - buffer->heap_buf = kmalloc(NET_IP_ALIGN + len, GFP_ATOMIC); - if (unlikely(!buffer->heap_buf)) - return NULL; - tx_queue->tso_long_headers++; - result = (u8 *)buffer->heap_buf + NET_IP_ALIGN; - buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_HEAP; - } - - buffer->len = len; - - return result; -} - -/* - * Put a TSO header into the TX queue. - * - * This is special-cased because we know that it is small enough to fit in - * a single fragment, and we know it doesn't cross a page boundary. It - * also allows us to not worry about end-of-packet etc. - */ -static int efx_tso_put_header(struct efx_tx_queue *tx_queue, - struct efx_tx_buffer *buffer, u8 *header) -{ - if (unlikely(buffer->flags & EFX_TX_BUF_HEAP)) { - buffer->dma_addr = dma_map_single(&tx_queue->efx->pci_dev->dev, - header, buffer->len, - DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(&tx_queue->efx->pci_dev->dev, - buffer->dma_addr))) { - kfree(buffer->heap_buf); - buffer->len = 0; - buffer->flags = 0; - return -ENOMEM; - } - buffer->unmap_len = buffer->len; - buffer->dma_offset = 0; - buffer->flags |= EFX_TX_BUF_MAP_SINGLE; - } - - ++tx_queue->insert_count; - return 0; -} - /* Parse the SKB header and initialise state. */ static int tso_start(struct tso_state *st, struct efx_nic *efx, @@ -237,12 +176,8 @@ static int tso_start(struct tso_state *st, struct efx_nic *efx, { struct device *dma_dev = &efx->pci_dev->dev; unsigned int header_len, in_len; - bool use_opt_desc = false; dma_addr_t dma_addr; - if (tx_queue->tso_version == 1) - use_opt_desc = true; - st->ip_off = skb_network_header(skb) - skb->data; st->tcp_off = skb_transport_header(skb) - skb->data; header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u); @@ -264,30 +199,12 @@ static int tso_start(struct tso_state *st, struct efx_nic *efx, st->out_len = skb->len - header_len; - if (!use_opt_desc) { - st->header_unmap_len = 0; - - if (likely(in_len == 0)) { - st->dma_flags = 0; - st->unmap_len = 0; - return 0; - } - - dma_addr = dma_map_single(dma_dev, skb->data + header_len, - in_len, DMA_TO_DEVICE); - st->dma_flags = EFX_TX_BUF_MAP_SINGLE; - st->dma_addr = dma_addr; - st->unmap_addr = dma_addr; - st->unmap_len = in_len; - } else { - dma_addr = dma_map_single(dma_dev, skb->data, - skb_headlen(skb), DMA_TO_DEVICE); - st->header_dma_addr = dma_addr; - st->header_unmap_len = skb_headlen(skb); - st->dma_flags = 0; - st->dma_addr = dma_addr + header_len; - st->unmap_len = 0; - } + dma_addr = dma_map_single(dma_dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + st->header_dma_addr = dma_addr; + st->header_unmap_len = skb_headlen(skb); + st->dma_addr = dma_addr + header_len; + st->unmap_len = 0; return unlikely(dma_mapping_error(dma_dev, dma_addr)) ? -ENOMEM : 0; } @@ -298,7 +215,6 @@ static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, st->unmap_addr = skb_frag_dma_map(&efx->pci_dev->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); if (likely(!dma_mapping_error(&efx->pci_dev->dev, st->unmap_addr))) { - st->dma_flags = 0; st->unmap_len = skb_frag_size(frag); st->in_len = skb_frag_size(frag); st->dma_addr = st->unmap_addr; @@ -352,7 +268,6 @@ static void tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue, /* Transfer ownership of the DMA mapping */ buffer->unmap_len = st->unmap_len; buffer->dma_offset = buffer->unmap_len - buffer->len; - buffer->flags |= st->dma_flags; st->unmap_len = 0; } @@ -369,7 +284,7 @@ static void tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue, * @st: TSO state * * Generate a new header and prepare for the new packet. Return 0 on - * success, or -%ENOMEM if failed to alloc header. + * success, or -%ENOMEM if failed to alloc header, or other negative error. */ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, const struct sk_buff *skb, @@ -378,7 +293,7 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, struct efx_tx_buffer *buffer = efx_tx_queue_get_insert_buffer(tx_queue); bool is_last = st->out_len <= skb_shinfo(skb)->gso_size; - u8 tcp_flags_mask; + u8 tcp_flags_mask, tcp_flags; if (!is_last) { st->packet_space = skb_shinfo(skb)->gso_size; @@ -388,82 +303,44 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, tcp_flags_mask = 0x00; } - if (!st->header_unmap_len) { - /* Allocate and insert a DMA-mapped header buffer. */ - struct tcphdr *tsoh_th; - unsigned int ip_length; - u8 *header; - int rc; - - header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len); - if (!header) - return -ENOMEM; - - tsoh_th = (struct tcphdr *)(header + st->tcp_off); - - /* Copy and update the headers. */ - memcpy(header, skb->data, st->header_len); - - tsoh_th->seq = htonl(st->seqnum); - ((u8 *)tsoh_th)[TCP_FLAGS_OFFSET] &= ~tcp_flags_mask; - - ip_length = st->ip_base_len + st->packet_space; - - if (st->protocol == htons(ETH_P_IP)) { - struct iphdr *tsoh_iph = - (struct iphdr *)(header + st->ip_off); - - tsoh_iph->tot_len = htons(ip_length); - tsoh_iph->id = htons(st->ipv4_id); - } else { - struct ipv6hdr *tsoh_iph = - (struct ipv6hdr *)(header + st->ip_off); - - tsoh_iph->payload_len = htons(ip_length); - } + if (WARN_ON(!st->header_unmap_len)) + return -EINVAL; + /* Send the original headers with a TSO option descriptor + * in front + */ + tcp_flags = ((u8 *)tcp_hdr(skb))[TCP_FLAGS_OFFSET] & ~tcp_flags_mask; + + buffer->flags = EFX_TX_BUF_OPTION; + buffer->len = 0; + buffer->unmap_len = 0; + EFX_POPULATE_QWORD_5(buffer->option, + ESF_DZ_TX_DESC_IS_OPT, 1, + ESF_DZ_TX_OPTION_TYPE, + ESE_DZ_TX_OPTION_DESC_TSO, + ESF_DZ_TX_TSO_TCP_FLAGS, tcp_flags, + ESF_DZ_TX_TSO_IP_ID, st->ipv4_id, + ESF_DZ_TX_TSO_TCP_SEQNO, st->seqnum); + ++tx_queue->insert_count; - rc = efx_tso_put_header(tx_queue, buffer, header); - if (unlikely(rc)) - return rc; - } else { - /* Send the original headers with a TSO option descriptor - * in front + /* We mapped the headers in tso_start(). Unmap them + * when the last segment is completed. + */ + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + buffer->dma_addr = st->header_dma_addr; + buffer->len = st->header_len; + if (is_last) { + buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_MAP_SINGLE; + buffer->unmap_len = st->header_unmap_len; + buffer->dma_offset = 0; + /* Ensure we only unmap them once in case of a + * later DMA mapping error and rollback */ - u8 tcp_flags = ((u8 *)tcp_hdr(skb))[TCP_FLAGS_OFFSET] & - ~tcp_flags_mask; - - buffer->flags = EFX_TX_BUF_OPTION; - buffer->len = 0; + st->header_unmap_len = 0; + } else { + buffer->flags = EFX_TX_BUF_CONT; buffer->unmap_len = 0; - EFX_POPULATE_QWORD_5(buffer->option, - ESF_DZ_TX_DESC_IS_OPT, 1, - ESF_DZ_TX_OPTION_TYPE, - ESE_DZ_TX_OPTION_DESC_TSO, - ESF_DZ_TX_TSO_TCP_FLAGS, tcp_flags, - ESF_DZ_TX_TSO_IP_ID, st->ipv4_id, - ESF_DZ_TX_TSO_TCP_SEQNO, st->seqnum); - ++tx_queue->insert_count; - - /* We mapped the headers in tso_start(). Unmap them - * when the last segment is completed. - */ - buffer = efx_tx_queue_get_insert_buffer(tx_queue); - buffer->dma_addr = st->header_dma_addr; - buffer->len = st->header_len; - if (is_last) { - buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_MAP_SINGLE; - buffer->unmap_len = st->header_unmap_len; - buffer->dma_offset = 0; - /* Ensure we only unmap them once in case of a - * later DMA mapping error and rollback - */ - st->header_unmap_len = 0; - } else { - buffer->flags = EFX_TX_BUF_CONT; - buffer->unmap_len = 0; - } - ++tx_queue->insert_count; } + ++tx_queue->insert_count; st->seqnum += skb_shinfo(skb)->gso_size; @@ -483,8 +360,8 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, * Context: You must hold netif_tx_lock() to call this function. * * Add socket buffer @skb to @tx_queue, doing TSO or return != 0 if - * @skb was not enqueued. In all cases @skb is consumed. Return - * %NETDEV_TX_OK. + * @skb was not enqueued. @skb is consumed unless return value is + * %EINVAL. */ int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, struct sk_buff *skb, @@ -494,6 +371,9 @@ int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, int frag_i, rc; struct tso_state state; + if (tx_queue->tso_version != 1) + return -EINVAL; + prefetch(skb->data); /* Find the packet protocol and sanity-check it */ @@ -503,7 +383,7 @@ int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, rc = tso_start(&state, efx, tx_queue, skb); if (rc) - goto mem_err; + goto fail; if (likely(state.in_len == 0)) { /* Grab the first payload fragment. */ @@ -512,14 +392,15 @@ int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, rc = tso_get_fragment(&state, efx, skb_shinfo(skb)->frags + frag_i); if (rc) - goto mem_err; + goto fail; } else { /* Payload starts in the header area. */ frag_i = -1; } - if (tso_start_new_packet(tx_queue, skb, &state) < 0) - goto mem_err; + rc = tso_start_new_packet(tx_queue, skb, &state); + if (rc) + goto fail; prefetch_ptr(tx_queue); @@ -534,37 +415,38 @@ int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, rc = tso_get_fragment(&state, efx, skb_shinfo(skb)->frags + frag_i); if (rc) - goto mem_err; + goto fail; } /* Start at new packet? */ - if (state.packet_space == 0 && - tso_start_new_packet(tx_queue, skb, &state) < 0) - goto mem_err; + if (state.packet_space == 0) { + rc = tso_start_new_packet(tx_queue, skb, &state); + if (rc) + goto fail; + } } *data_mapped = true; return 0; - mem_err: - netif_err(efx, tx_err, efx->net_dev, - "Out of memory for TSO headers, or DMA mapping error\n"); +fail: + if (rc == -ENOMEM) + netif_err(efx, tx_err, efx->net_dev, + "Out of memory for TSO headers, or DMA mapping error\n"); + else + netif_err(efx, tx_err, efx->net_dev, "TSO failed, rc = %d\n", rc); /* Free the DMA mapping we were in the process of writing out */ if (state.unmap_len) { - if (state.dma_flags & EFX_TX_BUF_MAP_SINGLE) - dma_unmap_single(&efx->pci_dev->dev, state.unmap_addr, - state.unmap_len, DMA_TO_DEVICE); - else - dma_unmap_page(&efx->pci_dev->dev, state.unmap_addr, - state.unmap_len, DMA_TO_DEVICE); + dma_unmap_page(&efx->pci_dev->dev, state.unmap_addr, + state.unmap_len, DMA_TO_DEVICE); } - /* Free the header DMA mapping, if using option descriptors */ + /* Free the header DMA mapping */ if (state.header_unmap_len) dma_unmap_single(&efx->pci_dev->dev, state.header_dma_addr, state.header_unmap_len, DMA_TO_DEVICE); - return -ENOMEM; + return rc; } -- cgit v1.1 From 0ac3ea70897fb9f84b620aeda074ecccf481629d Mon Sep 17 00:00:00 2001 From: Mohamad Haj Yahia Date: Thu, 17 Nov 2016 13:45:55 +0200 Subject: net/mlx5: Make the command interface cache more flexible Add more cache command size sets and more entries for each set based on the current commands set different sizes and commands frequency. Fixes: e126ba97dba9 ('mlx5: Add driver for Mellanox Connect-IB adapters') Signed-off-by: Mohamad Haj Yahia Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 145 ++++++++++++-------------- include/linux/mlx5/driver.h | 14 +-- 2 files changed, 76 insertions(+), 83 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 8561102..0fe7a60 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -54,14 +54,6 @@ enum { }; enum { - NUM_LONG_LISTS = 2, - NUM_MED_LISTS = 64, - LONG_LIST_SIZE = (2ULL * 1024 * 1024 * 1024 / PAGE_SIZE) * 8 + 16 + - MLX5_CMD_DATA_BLOCK_SIZE, - MED_LIST_SIZE = 16 + MLX5_CMD_DATA_BLOCK_SIZE, -}; - -enum { MLX5_CMD_DELIVERY_STAT_OK = 0x0, MLX5_CMD_DELIVERY_STAT_SIGNAT_ERR = 0x1, MLX5_CMD_DELIVERY_STAT_TOK_ERR = 0x2, @@ -1372,10 +1364,10 @@ static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg) { unsigned long flags; - if (msg->cache) { - spin_lock_irqsave(&msg->cache->lock, flags); - list_add_tail(&msg->list, &msg->cache->head); - spin_unlock_irqrestore(&msg->cache->lock, flags); + if (msg->parent) { + spin_lock_irqsave(&msg->parent->lock, flags); + list_add_tail(&msg->list, &msg->parent->head); + spin_unlock_irqrestore(&msg->parent->lock, flags); } else { mlx5_free_cmd_msg(dev, msg); } @@ -1472,30 +1464,37 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size, gfp_t gfp) { struct mlx5_cmd_msg *msg = ERR_PTR(-ENOMEM); + struct cmd_msg_cache *ch = NULL; struct mlx5_cmd *cmd = &dev->cmd; - struct cache_ent *ent = NULL; - - if (in_size > MED_LIST_SIZE && in_size <= LONG_LIST_SIZE) - ent = &cmd->cache.large; - else if (in_size > 16 && in_size <= MED_LIST_SIZE) - ent = &cmd->cache.med; - - if (ent) { - spin_lock_irq(&ent->lock); - if (!list_empty(&ent->head)) { - msg = list_entry(ent->head.next, typeof(*msg), list); - /* For cached lists, we must explicitly state what is - * the real size - */ - msg->len = in_size; - list_del(&msg->list); + int i; + + if (in_size <= 16) + goto cache_miss; + + for (i = 0; i < MLX5_NUM_COMMAND_CACHES; i++) { + ch = &cmd->cache[i]; + if (in_size > ch->max_inbox_size) + continue; + spin_lock_irq(&ch->lock); + if (list_empty(&ch->head)) { + spin_unlock_irq(&ch->lock); + continue; } - spin_unlock_irq(&ent->lock); + msg = list_entry(ch->head.next, typeof(*msg), list); + /* For cached lists, we must explicitly state what is + * the real size + */ + msg->len = in_size; + list_del(&msg->list); + spin_unlock_irq(&ch->lock); + break; } - if (IS_ERR(msg)) - msg = mlx5_alloc_cmd_msg(dev, gfp, in_size, 0); + if (!IS_ERR(msg)) + return msg; +cache_miss: + msg = mlx5_alloc_cmd_msg(dev, gfp, in_size, 0); return msg; } @@ -1593,58 +1592,56 @@ EXPORT_SYMBOL(mlx5_cmd_exec_cb); static void destroy_msg_cache(struct mlx5_core_dev *dev) { - struct mlx5_cmd *cmd = &dev->cmd; + struct cmd_msg_cache *ch; struct mlx5_cmd_msg *msg; struct mlx5_cmd_msg *n; + int i; - list_for_each_entry_safe(msg, n, &cmd->cache.large.head, list) { - list_del(&msg->list); - mlx5_free_cmd_msg(dev, msg); - } - - list_for_each_entry_safe(msg, n, &cmd->cache.med.head, list) { - list_del(&msg->list); - mlx5_free_cmd_msg(dev, msg); + for (i = 0; i < MLX5_NUM_COMMAND_CACHES; i++) { + ch = &dev->cmd.cache[i]; + list_for_each_entry_safe(msg, n, &ch->head, list) { + list_del(&msg->list); + mlx5_free_cmd_msg(dev, msg); + } } } -static int create_msg_cache(struct mlx5_core_dev *dev) +static unsigned cmd_cache_num_ent[MLX5_NUM_COMMAND_CACHES] = { + 512, 32, 16, 8, 2 +}; + +static unsigned cmd_cache_ent_size[MLX5_NUM_COMMAND_CACHES] = { + 16 + MLX5_CMD_DATA_BLOCK_SIZE, + 16 + MLX5_CMD_DATA_BLOCK_SIZE * 2, + 16 + MLX5_CMD_DATA_BLOCK_SIZE * 16, + 16 + MLX5_CMD_DATA_BLOCK_SIZE * 256, + 16 + MLX5_CMD_DATA_BLOCK_SIZE * 512, +}; + +static void create_msg_cache(struct mlx5_core_dev *dev) { struct mlx5_cmd *cmd = &dev->cmd; + struct cmd_msg_cache *ch; struct mlx5_cmd_msg *msg; - int err; int i; - - spin_lock_init(&cmd->cache.large.lock); - INIT_LIST_HEAD(&cmd->cache.large.head); - spin_lock_init(&cmd->cache.med.lock); - INIT_LIST_HEAD(&cmd->cache.med.head); - - for (i = 0; i < NUM_LONG_LISTS; i++) { - msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE, 0); - if (IS_ERR(msg)) { - err = PTR_ERR(msg); - goto ex_err; - } - msg->cache = &cmd->cache.large; - list_add_tail(&msg->list, &cmd->cache.large.head); - } - - for (i = 0; i < NUM_MED_LISTS; i++) { - msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE, 0); - if (IS_ERR(msg)) { - err = PTR_ERR(msg); - goto ex_err; + int k; + + /* Initialize and fill the caches with initial entries */ + for (k = 0; k < MLX5_NUM_COMMAND_CACHES; k++) { + ch = &cmd->cache[k]; + spin_lock_init(&ch->lock); + INIT_LIST_HEAD(&ch->head); + ch->num_ent = cmd_cache_num_ent[k]; + ch->max_inbox_size = cmd_cache_ent_size[k]; + for (i = 0; i < ch->num_ent; i++) { + msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL | __GFP_NOWARN, + ch->max_inbox_size, 0); + if (IS_ERR(msg)) + break; + msg->parent = ch; + list_add_tail(&msg->list, &ch->head); } - msg->cache = &cmd->cache.med; - list_add_tail(&msg->list, &cmd->cache.med.head); } - - return 0; - -ex_err: - destroy_msg_cache(dev); - return err; } static int alloc_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd) @@ -1767,11 +1764,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) cmd->mode = CMD_MODE_POLLING; - err = create_msg_cache(dev); - if (err) { - dev_err(&dev->pdev->dev, "failed to create command cache\n"); - goto err_free_page; - } + create_msg_cache(dev); set_wqname(dev); cmd->wq = create_singlethread_workqueue(cmd->wq_name); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index ecc451d..5e7dbbc 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -208,7 +208,7 @@ struct mlx5_cmd_first { struct mlx5_cmd_msg { struct list_head list; - struct cache_ent *cache; + struct cmd_msg_cache *parent; u32 len; struct mlx5_cmd_first first; struct mlx5_cmd_mailbox *next; @@ -228,17 +228,17 @@ struct mlx5_cmd_debug { u16 outlen; }; -struct cache_ent { +struct cmd_msg_cache { /* protect block chain allocations */ spinlock_t lock; struct list_head head; + unsigned int max_inbox_size; + unsigned int num_ent; }; -struct cmd_msg_cache { - struct cache_ent large; - struct cache_ent med; - +enum { + MLX5_NUM_COMMAND_CACHES = 5, }; struct mlx5_cmd_stats { @@ -281,7 +281,7 @@ struct mlx5_cmd { struct mlx5_cmd_work_ent *ent_arr[MLX5_MAX_COMMANDS]; struct pci_pool *pool; struct mlx5_cmd_debug dbg; - struct cmd_msg_cache cache; + struct cmd_msg_cache cache[MLX5_NUM_COMMAND_CACHES]; int checksum_disabled; struct mlx5_cmd_stats stats[MLX5_CMD_OP_MAX]; }; -- cgit v1.1 From 4ce3bf2fa8ba309b5ca19539fcc8671a0fc084f9 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 17 Nov 2016 13:45:56 +0200 Subject: net/mlx5: Port module event hardware structures Add hardware structures and constants definitions needed for module events support. Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky Signed-off-by: David S. Miller --- include/linux/mlx5/device.h | 11 +++++++++++ include/linux/mlx5/mlx5_ifc.h | 3 ++- include/linux/mlx5/port.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 5827614..52b4374 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -277,6 +277,7 @@ enum mlx5_event { MLX5_EVENT_TYPE_INTERNAL_ERROR = 0x08, MLX5_EVENT_TYPE_PORT_CHANGE = 0x09, MLX5_EVENT_TYPE_GPIO_EVENT = 0x15, + MLX5_EVENT_TYPE_PORT_MODULE_EVENT = 0x16, MLX5_EVENT_TYPE_REMOTE_CONFIG = 0x19, MLX5_EVENT_TYPE_DB_BF_CONGESTION = 0x1a, @@ -552,6 +553,15 @@ struct mlx5_eqe_vport_change { __be32 rsvd1[6]; } __packed; +struct mlx5_eqe_port_module { + u8 reserved_at_0[1]; + u8 module; + u8 reserved_at_2[1]; + u8 module_status; + u8 reserved_at_4[2]; + u8 error_type; +} __packed; + union ev_data { __be32 raw[7]; struct mlx5_eqe_cmd cmd; @@ -565,6 +575,7 @@ union ev_data { struct mlx5_eqe_page_req req_pages; struct mlx5_eqe_page_fault page_fault; struct mlx5_eqe_vport_change vport_change; + struct mlx5_eqe_port_module port_module; } __packed; struct mlx5_eqe { diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 2632cb2..cd1d530 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -824,7 +824,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 early_vf_enable[0x1]; u8 reserved_at_1a9[0x2]; u8 local_ca_ack_delay[0x5]; - u8 reserved_at_1af[0x2]; + u8 port_module_event[0x1]; + u8 reserved_at_1b0[0x1]; u8 ports_check[0x1]; u8 reserved_at_1b2[0x1]; u8 disable_link_up[0x1]; diff --git a/include/linux/mlx5/port.h b/include/linux/mlx5/port.h index b3065ac..dde8c7e 100644 --- a/include/linux/mlx5/port.h +++ b/include/linux/mlx5/port.h @@ -94,6 +94,9 @@ enum mlx5e_link_mode { #define MLX5E_PROT_MASK(link_mode) (1 << link_mode) +#define PORT_MODULE_EVENT_MODULE_STATUS_MASK 0xF +#define PORT_MODULE_EVENT_ERROR_TYPE_MASK 0xF + int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps); int mlx5_query_port_ptys(struct mlx5_core_dev *dev, u32 *ptys, int ptys_size, int proto_mask, u8 local_port); -- cgit v1.1 From d4eb4cd78b0774c7061db56844ed2ea7790cc77c Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 17 Nov 2016 13:45:57 +0200 Subject: net/mlx5: Add handling for port module event For each asynchronous port module event: 1. print with ratelimit to the dmesg log 2. increment the corresponding event counter Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 12 +++++ .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 1 + drivers/net/ethernet/mellanox/mlx5/core/port.c | 57 ++++++++++++++++++++++ include/linux/mlx5/driver.h | 27 ++++++++++ 4 files changed, 97 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index e74a73b..8ffcc88 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -139,6 +139,8 @@ static const char *eqe_type_str(u8 type) return "MLX5_EVENT_TYPE_PORT_CHANGE"; case MLX5_EVENT_TYPE_GPIO_EVENT: return "MLX5_EVENT_TYPE_GPIO_EVENT"; + case MLX5_EVENT_TYPE_PORT_MODULE_EVENT: + return "MLX5_EVENT_TYPE_PORT_MODULE_EVENT"; case MLX5_EVENT_TYPE_REMOTE_CONFIG: return "MLX5_EVENT_TYPE_REMOTE_CONFIG"; case MLX5_EVENT_TYPE_DB_BF_CONGESTION: @@ -285,6 +287,11 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq) mlx5_eswitch_vport_event(dev->priv.eswitch, eqe); break; #endif + + case MLX5_EVENT_TYPE_PORT_MODULE_EVENT: + mlx5_port_module_event(dev, eqe); + break; + default: mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n", eqe->type, eq->eqn); @@ -480,6 +487,11 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev) mlx5_core_is_pf(dev)) async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE); + if (MLX5_CAP_GEN(dev, port_module_event)) + async_event_mask |= (1ull << MLX5_EVENT_TYPE_PORT_MODULE_EVENT); + else + mlx5_core_dbg(dev, "port_module_event is not set\n"); + err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD, MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD, "mlx5_cmd_eq", &dev->priv.uuari.uars[0]); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 4762bb9..7e635eb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -81,6 +81,7 @@ int mlx5_cmd_init_hca(struct mlx5_core_dev *dev); int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev); void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param); +void mlx5_port_module_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe); void mlx5_enter_error_state(struct mlx5_core_dev *dev); void mlx5_disable_device(struct mlx5_core_dev *dev); void mlx5_recover_device(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 34e7184..b77928f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -746,3 +746,60 @@ void mlx5_query_port_fcs(struct mlx5_core_dev *mdev, bool *supported, *supported = !!(MLX5_GET(pcmr_reg, out, fcs_cap)); *enabled = !!(MLX5_GET(pcmr_reg, out, fcs_chk)); } + +static const char *mlx5_pme_status[MLX5_MODULE_STATUS_NUM] = { + "Cable plugged", /* MLX5_MODULE_STATUS_PLUGGED = 0x1 */ + "Cable unplugged", /* MLX5_MODULE_STATUS_UNPLUGGED = 0x2 */ + "Cable error", /* MLX5_MODULE_STATUS_ERROR = 0x3 */ +}; + +static const char *mlx5_pme_error[MLX5_MODULE_EVENT_ERROR_NUM] = { + "Power budget exceeded", + "Long Range for non MLNX cable", + "Bus stuck(I2C or data shorted)", + "No EEPROM/retry timeout", + "Enforce part number list", + "Unknown identifier", + "High Temperature", + "Bad or shorted cable/module", + "Unknown status", +}; + +void mlx5_port_module_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe) +{ + enum port_module_event_status_type module_status; + enum port_module_event_error_type error_type; + struct mlx5_eqe_port_module *module_event_eqe; + struct mlx5_priv *priv = &dev->priv; + u8 module_num; + + module_event_eqe = &eqe->data.port_module; + module_num = module_event_eqe->module; + module_status = module_event_eqe->module_status & + PORT_MODULE_EVENT_MODULE_STATUS_MASK; + error_type = module_event_eqe->error_type & + PORT_MODULE_EVENT_ERROR_TYPE_MASK; + + if (module_status < MLX5_MODULE_STATUS_ERROR) { + priv->pme_stats.status_counters[module_status - 1]++; + } else if (module_status == MLX5_MODULE_STATUS_ERROR) { + if (error_type >= MLX5_MODULE_EVENT_ERROR_UNKNOWN) + /* Unknown error type */ + error_type = MLX5_MODULE_EVENT_ERROR_UNKNOWN; + priv->pme_stats.error_counters[error_type]++; + } + + if (!printk_ratelimit()) + return; + + if (module_status < MLX5_MODULE_STATUS_ERROR) + mlx5_core_info(dev, + "Port module event: module %u, %s\n", + module_num, mlx5_pme_status[module_status - 1]); + + else if (module_status == MLX5_MODULE_STATUS_ERROR) + mlx5_core_info(dev, + "Port module event[error]: module %u, %s, %s\n", + module_num, mlx5_pme_status[module_status - 1], + mlx5_pme_error[error_type]); +} diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 5e7dbbc..7336c8e 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -498,6 +498,31 @@ struct mlx5_rl_table { struct mlx5_rl_entry *rl_entry; }; +enum port_module_event_status_type { + MLX5_MODULE_STATUS_PLUGGED = 0x1, + MLX5_MODULE_STATUS_UNPLUGGED = 0x2, + MLX5_MODULE_STATUS_ERROR = 0x3, + MLX5_MODULE_STATUS_NUM = 0x3, +}; + +enum port_module_event_error_type { + MLX5_MODULE_EVENT_ERROR_POWER_BUDGET_EXCEEDED, + MLX5_MODULE_EVENT_ERROR_LONG_RANGE_FOR_NON_MLNX_CABLE_MODULE, + MLX5_MODULE_EVENT_ERROR_BUS_STUCK, + MLX5_MODULE_EVENT_ERROR_NO_EEPROM_RETRY_TIMEOUT, + MLX5_MODULE_EVENT_ERROR_ENFORCE_PART_NUMBER_LIST, + MLX5_MODULE_EVENT_ERROR_UNKNOWN_IDENTIFIER, + MLX5_MODULE_EVENT_ERROR_HIGH_TEMPERATURE, + MLX5_MODULE_EVENT_ERROR_BAD_CABLE, + MLX5_MODULE_EVENT_ERROR_UNKNOWN, + MLX5_MODULE_EVENT_ERROR_NUM, +}; + +struct mlx5_port_module_event_stats { + u64 status_counters[MLX5_MODULE_STATUS_NUM]; + u64 error_counters[MLX5_MODULE_EVENT_ERROR_NUM]; +}; + struct mlx5_priv { char name[MLX5_MAX_NAME_LEN]; struct mlx5_eq_table eq_table; @@ -559,6 +584,8 @@ struct mlx5_priv { unsigned long pci_dev_data; struct mlx5_fc_stats fc_stats; struct mlx5_rl_table rl_table; + + struct mlx5_port_module_event_stats pme_stats; }; enum mlx5_device_state { -- cgit v1.1 From bedb7c909c1911270fcb084230245df4a00bd881 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 17 Nov 2016 13:45:58 +0200 Subject: net/mlx5e: Add port module event counters to ethtool stats Add port module event counters to ethtool -S command Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 23 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 17 ++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 27ff401..b154621 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -173,7 +173,10 @@ static int mlx5e_get_sset_count(struct net_device *dev, int sset) NUM_VPORT_COUNTERS + NUM_PPORT_COUNTERS + MLX5E_NUM_RQ_STATS(priv) + MLX5E_NUM_SQ_STATS(priv) + - MLX5E_NUM_PFC_COUNTERS(priv); + MLX5E_NUM_PFC_COUNTERS(priv) + + ARRAY_SIZE(mlx5e_pme_status_desc) + + ARRAY_SIZE(mlx5e_pme_error_desc); + case ETH_SS_PRIV_FLAGS: return ARRAY_SIZE(mlx5e_priv_flags); /* fallthrough */ @@ -237,6 +240,13 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) } } + /* port module event counters */ + for (i = 0; i < ARRAY_SIZE(mlx5e_pme_status_desc); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, mlx5e_pme_status_desc[i].format); + + for (i = 0; i < ARRAY_SIZE(mlx5e_pme_error_desc); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, mlx5e_pme_error_desc[i].format); + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) return; @@ -279,6 +289,7 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_priv *mlx5_priv; int i, j, tc, prio, idx = 0; unsigned long pfc_combined; @@ -335,6 +346,16 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev, } } + /* port module event counters */ + mlx5_priv = &priv->mdev->priv; + for (i = 0; i < ARRAY_SIZE(mlx5e_pme_status_desc); i++) + data[idx++] = MLX5E_READ_CTR64_CPU(mlx5_priv->pme_stats.status_counters, + mlx5e_pme_status_desc, i); + + for (i = 0; i < ARRAY_SIZE(mlx5e_pme_error_desc); i++) + data[idx++] = MLX5E_READ_CTR64_CPU(mlx5_priv->pme_stats.error_counters, + mlx5e_pme_error_desc, i); + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) return; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 57452fd..b817e51 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -379,4 +379,21 @@ struct mlx5e_stats { struct mlx5e_pport_stats pport; }; +static const struct counter_desc mlx5e_pme_status_desc[] = { + { "module_plug", 0 }, + { "module_unplug", 8 }, +}; + +static const struct counter_desc mlx5e_pme_error_desc[] = { + { "module_pwr_budget_exd", 0 }, /* power budget exceed */ + { "module_long_range", 8 }, /* long range for non MLNX cable */ + { "module_bus_stuck", 16 }, /* bus stuck (I2C or data shorted) */ + { "module_no_eeprom", 24 }, /* no eeprom/retry time out */ + { "module_enforce_part", 32 }, /* enforce part number list */ + { "module_unknown_id", 40 }, /* unknown identifier */ + { "module_high_temp", 48 }, /* high temperature */ + { "module_bad_shorted", 56 }, /* bad or shorted cable/module */ + { "module_unknown_status", 64 }, +}; + #endif /* __MLX5_EN_STATS_H__ */ -- cgit v1.1 From 0dbc6fe09fbe5f5191bcc606f3bdc9a829f97066 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 17 Nov 2016 13:45:59 +0200 Subject: net/mlx5: Set driver version infrastructure Add driver_version capability bit is enabled, and set driver version command in mlx5_ifc firmware header. The only purpose of this command is to store a driver version/OS string in FW to be reported and displayed in various management systems, such as IPMI/BMC. Signed-off-by: Saeed Mahameed Signed-off-by: Huy Nguyen Signed-off-by: Leon Romanovsky Signed-off-by: David S. Miller --- include/linux/mlx5/mlx5_ifc.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index cd1d530..f08a0624 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -83,6 +83,7 @@ enum { MLX5_CMD_OP_SET_HCA_CAP = 0x109, MLX5_CMD_OP_QUERY_ISSI = 0x10a, MLX5_CMD_OP_SET_ISSI = 0x10b, + MLX5_CMD_OP_SET_DRIVER_VERSION = 0x10d, MLX5_CMD_OP_CREATE_MKEY = 0x200, MLX5_CMD_OP_QUERY_MKEY = 0x201, MLX5_CMD_OP_DESTROY_MKEY = 0x202, @@ -909,7 +910,7 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 log_pg_sz[0x8]; u8 bf[0x1]; - u8 reserved_at_261[0x1]; + u8 driver_version[0x1]; u8 pad_tx_eth_packet[0x1]; u8 reserved_at_263[0x8]; u8 log_bf_reg_size[0x5]; @@ -4005,6 +4006,25 @@ struct mlx5_ifc_query_issi_in_bits { u8 reserved_at_40[0x40]; }; +struct mlx5_ifc_set_driver_version_out_bits { + u8 status[0x8]; + u8 reserved_0[0x18]; + + u8 syndrome[0x20]; + u8 reserved_1[0x40]; +}; + +struct mlx5_ifc_set_driver_version_in_bits { + u8 opcode[0x10]; + u8 reserved_0[0x10]; + + u8 reserved_1[0x10]; + u8 op_mod[0x10]; + + u8 reserved_2[0x40]; + u8 driver_version[64][0x8]; +}; + struct mlx5_ifc_query_hca_vport_pkey_out_bits { u8 status[0x8]; u8 reserved_at_8[0x18]; -- cgit v1.1 From 012e50e109fd27ff989492ad74c50ca7ab21e6a1 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 17 Nov 2016 13:46:00 +0200 Subject: net/mlx5: Set driver version into firmware If driver_version capability bit is enabled, set driver version to firmware after the init HCA command, for display purposes. Example of driver version: "Linux,mlx5_core,3.0-1" Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index c074933..f28df33 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -175,6 +175,41 @@ static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili) return err; } +static void mlx5_set_driver_version(struct mlx5_core_dev *dev) +{ + int driver_ver_sz = MLX5_FLD_SZ_BYTES(set_driver_version_in, + driver_version); + u8 in[MLX5_ST_SZ_BYTES(set_driver_version_in)] = {0}; + u8 out[MLX5_ST_SZ_BYTES(set_driver_version_out)] = {0}; + int remaining_size = driver_ver_sz; + char *string; + + if (!MLX5_CAP_GEN(dev, driver_version)) + return; + + string = MLX5_ADDR_OF(set_driver_version_in, in, driver_version); + + strncpy(string, "Linux", remaining_size); + + remaining_size = max_t(int, 0, driver_ver_sz - strlen(string)); + strncat(string, ",", remaining_size); + + remaining_size = max_t(int, 0, driver_ver_sz - strlen(string)); + strncat(string, DRIVER_NAME, remaining_size); + + remaining_size = max_t(int, 0, driver_ver_sz - strlen(string)); + strncat(string, ",", remaining_size); + + remaining_size = max_t(int, 0, driver_ver_sz - strlen(string)); + strncat(string, DRIVER_VERSION, remaining_size); + + /*Send the command*/ + MLX5_SET(set_driver_version_in, in, opcode, + MLX5_CMD_OP_SET_DRIVER_VERSION); + + mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + static int set_dma_caps(struct pci_dev *pdev) { int err; @@ -1015,6 +1050,8 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv, goto err_pagealloc_stop; } + mlx5_set_driver_version(dev); + mlx5_start_health_poll(dev); err = mlx5_query_hca_caps(dev); -- cgit v1.1 From 7f503169cabd70c1f13b9279c50eca7dfb9a7d51 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Thu, 17 Nov 2016 13:46:01 +0200 Subject: net/mlx5: Add MPCNT register infrastructure Add the needed infrastructure for future use of MPCNT register. Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/linux/mlx5/device.h | 5 +++ include/linux/mlx5/driver.h | 1 + include/linux/mlx5/mlx5_ifc.h | 93 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 52b4374..9f48936 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1071,6 +1071,11 @@ enum { MLX5_INFINIBAND_PORT_COUNTERS_GROUP = 0x20, }; +enum { + MLX5_PCIE_PERFORMANCE_COUNTERS_GROUP = 0x0, + MLX5_PCIE_TIMERS_AND_STATES_COUNTERS_GROUP = 0x2, +}; + static inline u16 mlx5_to_sw_pkey_sz(int pkey_sz) { if (pkey_sz > MLX5_MAX_LOG_PKEY_TABLE) diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 7336c8e..ae1f451 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -121,6 +121,7 @@ enum { MLX5_REG_HOST_ENDIANNESS = 0x7004, MLX5_REG_MCIA = 0x9014, MLX5_REG_MLCR = 0x902b, + MLX5_REG_MPCNT = 0x9051, }; enum { diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index f08a0624..a5f0fbe 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -1757,6 +1757,80 @@ struct mlx5_ifc_eth_802_3_cntrs_grp_data_layout_bits { u8 reserved_at_4c0[0x300]; }; +struct mlx5_ifc_pcie_perf_cntrs_grp_data_layout_bits { + u8 life_time_counter_high[0x20]; + + u8 life_time_counter_low[0x20]; + + u8 rx_errors[0x20]; + + u8 tx_errors[0x20]; + + u8 l0_to_recovery_eieos[0x20]; + + u8 l0_to_recovery_ts[0x20]; + + u8 l0_to_recovery_framing[0x20]; + + u8 l0_to_recovery_retrain[0x20]; + + u8 crc_error_dllp[0x20]; + + u8 crc_error_tlp[0x20]; + + u8 reserved_at_140[0x680]; +}; + +struct mlx5_ifc_pcie_tas_cntrs_grp_data_layout_bits { + u8 life_time_counter_high[0x20]; + + u8 life_time_counter_low[0x20]; + + u8 time_to_boot_image_start[0x20]; + + u8 time_to_link_image[0x20]; + + u8 calibration_time[0x20]; + + u8 time_to_first_perst[0x20]; + + u8 time_to_detect_state[0x20]; + + u8 time_to_l0[0x20]; + + u8 time_to_crs_en[0x20]; + + u8 time_to_plastic_image_start[0x20]; + + u8 time_to_iron_image_start[0x20]; + + u8 perst_handler[0x20]; + + u8 times_in_l1[0x20]; + + u8 times_in_l23[0x20]; + + u8 dl_down[0x20]; + + u8 config_cycle1usec[0x20]; + + u8 config_cycle2to7usec[0x20]; + + u8 config_cycle_8to15usec[0x20]; + + u8 config_cycle_16_to_63usec[0x20]; + + u8 config_cycle_64usec[0x20]; + + u8 correctable_err_msg_sent[0x20]; + + u8 non_fatal_err_msg_sent[0x20]; + + u8 fatal_err_msg_sent[0x20]; + + u8 reserved_at_2e0[0x4e0]; +}; + struct mlx5_ifc_cmd_inter_comp_event_bits { u8 command_completion_vector[0x20]; @@ -2921,6 +2995,12 @@ union mlx5_ifc_eth_cntrs_grp_data_layout_auto_bits { u8 reserved_at_0[0x7c0]; }; +union mlx5_ifc_pcie_cntrs_grp_data_layout_auto_bits { + struct mlx5_ifc_pcie_perf_cntrs_grp_data_layout_bits pcie_perf_cntrs_grp_data_layout; + struct mlx5_ifc_pcie_tas_cntrs_grp_data_layout_bits pcie_tas_cntrs_grp_data_layout; + u8 reserved_at_0[0x7c0]; +}; + union mlx5_ifc_event_auto_bits { struct mlx5_ifc_comp_event_bits comp_event; struct mlx5_ifc_dct_events_bits dct_events; @@ -7240,6 +7320,18 @@ struct mlx5_ifc_ppcnt_reg_bits { union mlx5_ifc_eth_cntrs_grp_data_layout_auto_bits counter_set; }; +struct mlx5_ifc_mpcnt_reg_bits { + u8 reserved_at_0[0x8]; + u8 pcie_index[0x8]; + u8 reserved_at_10[0xa]; + u8 grp[0x6]; + + u8 clr[0x1]; + u8 reserved_at_21[0x1f]; + + union mlx5_ifc_pcie_cntrs_grp_data_layout_auto_bits counter_set; +}; + struct mlx5_ifc_ppad_reg_bits { u8 reserved_at_0[0x3]; u8 single_mac[0x1]; @@ -7845,6 +7937,7 @@ union mlx5_ifc_ports_control_registers_document_bits { struct mlx5_ifc_pmtu_reg_bits pmtu_reg; struct mlx5_ifc_ppad_reg_bits ppad_reg; struct mlx5_ifc_ppcnt_reg_bits ppcnt_reg; + struct mlx5_ifc_mpcnt_reg_bits mpcnt_reg; struct mlx5_ifc_pplm_reg_bits pplm_reg; struct mlx5_ifc_pplr_reg_bits pplr_reg; struct mlx5_ifc_ppsc_reg_bits ppsc_reg; -- cgit v1.1 From 9c7262399ba12825f3ca4b00a76d8d5e77c720f5 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Thu, 17 Nov 2016 13:46:02 +0200 Subject: net/mlx5e: Expose PCIe statistics to ethtool This patch exposes two groups of PCIe counters: - Performance counters. - Timers and states counters. Queried with ethtool -S . Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 17 ++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 24 ++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 32 +++++++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index b154621..9ea7b37 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -171,6 +171,7 @@ static int mlx5e_get_sset_count(struct net_device *dev, int sset) return NUM_SW_COUNTERS + MLX5E_NUM_Q_CNTRS(priv) + NUM_VPORT_COUNTERS + NUM_PPORT_COUNTERS + + NUM_PCIE_COUNTERS + MLX5E_NUM_RQ_STATS(priv) + MLX5E_NUM_SQ_STATS(priv) + MLX5E_NUM_PFC_COUNTERS(priv) + @@ -216,6 +217,14 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) strcpy(data + (idx++) * ETH_GSTRING_LEN, pport_2819_stats_desc[i].format); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS; i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pcie_perf_stats_desc[i].format); + + for (i = 0; i < NUM_PCIE_TAS_COUNTERS; i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pcie_tas_stats_desc[i].format); + for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) sprintf(data + (idx++) * ETH_GSTRING_LEN, @@ -325,6 +334,14 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev, data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.RFC_2819_counters, pport_2819_stats_desc, i); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS; i++) + data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, + pcie_perf_stats_desc, i); + + for (i = 0; i < NUM_PCIE_TAS_COUNTERS; i++) + data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_tas_counters, + pcie_tas_stats_desc, i); + for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.per_prio_counters[prio], diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 00579ce..bd0732d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -290,12 +290,36 @@ static void mlx5e_update_q_counter(struct mlx5e_priv *priv) &qcnt->rx_out_of_buffer); } +static void mlx5e_update_pcie_counters(struct mlx5e_priv *priv) +{ + struct mlx5e_pcie_stats *pcie_stats = &priv->stats.pcie; + struct mlx5_core_dev *mdev = priv->mdev; + int sz = MLX5_ST_SZ_BYTES(mpcnt_reg); + void *out; + u32 *in; + + in = mlx5_vzalloc(sz); + if (!in) + return; + + out = pcie_stats->pcie_perf_counters; + MLX5_SET(mpcnt_reg, in, grp, MLX5_PCIE_PERFORMANCE_COUNTERS_GROUP); + mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_MPCNT, 0, 0); + + out = pcie_stats->pcie_tas_counters; + MLX5_SET(mpcnt_reg, in, grp, MLX5_PCIE_TIMERS_AND_STATES_COUNTERS_GROUP); + mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_MPCNT, 0, 0); + + kvfree(in); +} + void mlx5e_update_stats(struct mlx5e_priv *priv) { mlx5e_update_q_counter(priv); mlx5e_update_vport_counters(priv); mlx5e_update_pport_counters(priv); mlx5e_update_sw_counters(priv); + mlx5e_update_pcie_counters(priv); } void mlx5e_update_stats_work(struct work_struct *work) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index b817e51..5da6a1c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -39,7 +39,7 @@ #define MLX5E_READ_CTR32_CPU(ptr, dsc, i) \ (*(u32 *)((char *)ptr + dsc[i].offset)) #define MLX5E_READ_CTR32_BE(ptr, dsc, i) \ - be64_to_cpu(*(__be32 *)((char *)ptr + dsc[i].offset)) + be32_to_cpu(*(__be32 *)((char *)ptr + dsc[i].offset)) #define MLX5E_DECLARE_STAT(type, fld) #fld, offsetof(type, fld) #define MLX5E_DECLARE_RX_STAT(type, fld) "rx%d_"#fld, offsetof(type, fld) @@ -276,6 +276,32 @@ static const struct counter_desc pport_per_prio_pfc_stats_desc[] = { { "rx_%s_pause_transition", PPORT_PER_PRIO_OFF(rx_pause_transition) }, }; +#define PCIE_PERF_OFF(c) \ + MLX5_BYTE_OFF(mpcnt_reg, counter_set.pcie_perf_cntrs_grp_data_layout.c) +#define PCIE_PERF_GET(pcie_stats, c) \ + MLX5_GET(mpcnt_reg, pcie_stats->pcie_perf_counters, \ + counter_set.pcie_perf_cntrs_grp_data_layout.c) +#define PCIE_TAS_OFF(c) \ + MLX5_BYTE_OFF(mpcnt_reg, counter_set.pcie_tas_cntrs_grp_data_layout.c) +#define PCIE_TAS_GET(pcie_stats, c) \ + MLX5_GET(mpcnt_reg, pcie_stats->pcie_tas_counters, \ + counter_set.pcie_tas_cntrs_grp_data_layout.c) + +struct mlx5e_pcie_stats { + __be64 pcie_perf_counters[MLX5_ST_SZ_QW(mpcnt_reg)]; + __be64 pcie_tas_counters[MLX5_ST_SZ_QW(mpcnt_reg)]; +}; + +static const struct counter_desc pcie_perf_stats_desc[] = { + { "rx_pci_signal_integrity", PCIE_PERF_OFF(rx_errors) }, + { "tx_pci_signal_integrity", PCIE_PERF_OFF(tx_errors) }, +}; + +static const struct counter_desc pcie_tas_stats_desc[] = { + { "tx_pci_transport_nonfatal_msg", PCIE_TAS_OFF(non_fatal_err_msg_sent) }, + { "tx_pci_transport_fatal_msg", PCIE_TAS_OFF(fatal_err_msg_sent) }, +}; + struct mlx5e_rq_stats { u64 packets; u64 bytes; @@ -360,6 +386,8 @@ static const struct counter_desc sq_stats_desc[] = { #define NUM_PPORT_802_3_COUNTERS ARRAY_SIZE(pport_802_3_stats_desc) #define NUM_PPORT_2863_COUNTERS ARRAY_SIZE(pport_2863_stats_desc) #define NUM_PPORT_2819_COUNTERS ARRAY_SIZE(pport_2819_stats_desc) +#define NUM_PCIE_PERF_COUNTERS ARRAY_SIZE(pcie_perf_stats_desc) +#define NUM_PCIE_TAS_COUNTERS ARRAY_SIZE(pcie_tas_stats_desc) #define NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS \ ARRAY_SIZE(pport_per_prio_traffic_stats_desc) #define NUM_PPORT_PER_PRIO_PFC_COUNTERS \ @@ -369,6 +397,7 @@ static const struct counter_desc sq_stats_desc[] = { NUM_PPORT_2819_COUNTERS + \ NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS * \ NUM_PPORT_PRIO) +#define NUM_PCIE_COUNTERS (NUM_PCIE_PERF_COUNTERS + NUM_PCIE_TAS_COUNTERS) #define NUM_RQ_STATS ARRAY_SIZE(rq_stats_desc) #define NUM_SQ_STATS ARRAY_SIZE(sq_stats_desc) @@ -377,6 +406,7 @@ struct mlx5e_stats { struct mlx5e_qcounter_stats qcnt; struct mlx5e_vport_stats vport; struct mlx5e_pport_stats pport; + struct mlx5e_pcie_stats pcie; }; static const struct counter_desc mlx5e_pme_status_desc[] = { -- cgit v1.1 From 0d27f4e437e448c4ff440a31567b9729d1634d66 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Thu, 17 Nov 2016 13:07:20 +0100 Subject: ethtool: (uapi) Add ETHTOOL_PHY_GTUNABLE and ETHTOOL_PHY_STUNABLE Defines a generic API to get/set phy tunables. The API is using the existing ethtool_tunable/tunable_type_id types which is already being used for mac level tunables. Signed-off-by: Raju Lakkaraju Reviewed-by: Andrew Lunn Signed-off-by: Allan W. Nielsen Signed-off-by: David S. Miller --- include/uapi/linux/ethtool.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 8e54723..42f696f 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -248,6 +248,16 @@ struct ethtool_tunable { void *data[0]; }; +enum phy_tunable_id { + ETHTOOL_PHY_ID_UNSPEC, + + /* + * Add your fresh new phy tunable attribute above and remember to update + * phy_tunable_strings[] in net/core/ethtool.c + */ + __ETHTOOL_PHY_TUNABLE_COUNT, +}; + /** * struct ethtool_regs - hardware register dump * @cmd: Command number = %ETHTOOL_GREGS @@ -548,6 +558,7 @@ struct ethtool_pauseparam { * @ETH_SS_FEATURES: Device feature names * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS + * @ETH_SS_PHY_TUNABLES: PHY tunable names */ enum ethtool_stringset { ETH_SS_TEST = 0, @@ -558,6 +569,7 @@ enum ethtool_stringset { ETH_SS_RSS_HASH_FUNCS, ETH_SS_TUNABLES, ETH_SS_PHY_STATS, + ETH_SS_PHY_TUNABLES, }; /** @@ -1313,7 +1325,8 @@ struct ethtool_per_queue_op { #define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */ #define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */ - +#define ETHTOOL_PHY_GTUNABLE 0x0000004e /* Get PHY tunable configuration */ +#define ETHTOOL_PHY_STUNABLE 0x0000004f /* Set PHY tunable configuration */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET -- cgit v1.1 From 968ad9da7e0e333e25442950e10a1b631981ce84 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Thu, 17 Nov 2016 13:07:21 +0100 Subject: ethtool: Implements ETHTOOL_PHY_GTUNABLE/ETHTOOL_PHY_STUNABLE Adding get_tunable/set_tunable function pointer to the phy_driver structure, and uses these function pointers to implement the ETHTOOL_PHY_GTUNABLE/ETHTOOL_PHY_STUNABLE ioctls. Signed-off-by: Raju Lakkaraju Reviewed-by: Andrew Lunn Signed-off-by: Allan W. Nielsen Signed-off-by: David S. Miller --- include/linux/phy.h | 7 +++++ net/core/ethtool.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index b9bd3b4..edde28c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -611,6 +611,13 @@ struct phy_driver { void (*get_strings)(struct phy_device *dev, u8 *data); void (*get_stats)(struct phy_device *dev, struct ethtool_stats *stats, u64 *data); + + /* Get and Set PHY tunables */ + int (*get_tunable)(struct phy_device *dev, + struct ethtool_tunable *tuna, void *data); + int (*set_tunable)(struct phy_device *dev, + struct ethtool_tunable *tuna, + const void *data); }; #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 9774898..61aebdf 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -119,6 +119,11 @@ tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = { [ETHTOOL_TX_COPYBREAK] = "tx-copybreak", }; +static const char +phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = { + [ETHTOOL_ID_UNSPEC] = "Unspec", +}; + static int ethtool_get_features(struct net_device *dev, void __user *useraddr) { struct ethtool_gfeatures cmd = { @@ -227,6 +232,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset) if (sset == ETH_SS_TUNABLES) return ARRAY_SIZE(tunable_strings); + if (sset == ETH_SS_PHY_TUNABLES) + return ARRAY_SIZE(phy_tunable_strings); + if (sset == ETH_SS_PHY_STATS) { if (dev->phydev) return phy_get_sset_count(dev->phydev); @@ -253,6 +261,8 @@ static void __ethtool_get_strings(struct net_device *dev, sizeof(rss_hash_func_strings)); else if (stringset == ETH_SS_TUNABLES) memcpy(data, tunable_strings, sizeof(tunable_strings)); + else if (stringset == ETH_SS_PHY_TUNABLES) + memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings)); else if (stringset == ETH_SS_PHY_STATS) { struct phy_device *phydev = dev->phydev; @@ -2422,6 +2432,76 @@ static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr) }; } +static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna) +{ + switch (tuna->id) { + default: + return -EINVAL; + } + + return 0; +} + +static int get_phy_tunable(struct net_device *dev, void __user *useraddr) +{ + int ret; + struct ethtool_tunable tuna; + struct phy_device *phydev = dev->phydev; + void *data; + + if (!(phydev && phydev->drv && phydev->drv->get_tunable)) + return -EOPNOTSUPP; + + if (copy_from_user(&tuna, useraddr, sizeof(tuna))) + return -EFAULT; + ret = ethtool_phy_tunable_valid(&tuna); + if (ret) + return ret; + data = kmalloc(tuna.len, GFP_USER); + if (!data) + return -ENOMEM; + ret = phydev->drv->get_tunable(phydev, &tuna, data); + if (ret) + goto out; + useraddr += sizeof(tuna); + ret = -EFAULT; + if (copy_to_user(useraddr, data, tuna.len)) + goto out; + ret = 0; + +out: + kfree(data); + return ret; +} + +static int set_phy_tunable(struct net_device *dev, void __user *useraddr) +{ + int ret; + struct ethtool_tunable tuna; + struct phy_device *phydev = dev->phydev; + void *data; + + if (!(phydev && phydev->drv && phydev->drv->set_tunable)) + return -EOPNOTSUPP; + if (copy_from_user(&tuna, useraddr, sizeof(tuna))) + return -EFAULT; + ret = ethtool_phy_tunable_valid(&tuna); + if (ret) + return ret; + data = kmalloc(tuna.len, GFP_USER); + if (!data) + return -ENOMEM; + useraddr += sizeof(tuna); + ret = -EFAULT; + if (copy_from_user(data, useraddr, tuna.len)) + goto out; + ret = phydev->drv->set_tunable(phydev, &tuna, data); + +out: + kfree(data); + return ret; +} + /* The main entry point in this file. Called from net/core/dev_ioctl.c */ int dev_ethtool(struct net *net, struct ifreq *ifr) @@ -2479,6 +2559,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GET_TS_INFO: case ETHTOOL_GEEE: case ETHTOOL_GTUNABLE: + case ETHTOOL_PHY_GTUNABLE: break; default: if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) @@ -2684,6 +2765,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SLINKSETTINGS: rc = ethtool_set_link_ksettings(dev, useraddr); break; + case ETHTOOL_PHY_GTUNABLE: + rc = get_phy_tunable(dev, useraddr); + break; + case ETHTOOL_PHY_STUNABLE: + rc = set_phy_tunable(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } -- cgit v1.1 From 607c7029146790201e90b58c4235ddff0304d6e0 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Thu, 17 Nov 2016 13:07:22 +0100 Subject: ethtool: (uapi) Add ETHTOOL_PHY_DOWNSHIFT to PHY tunables For operation in cabling environments that are incompatible with 1000BASE-T, PHY device may provide an automatic link speed downshift operation. When enabled, the device automatically changes its 1000BASE-T auto-negotiation to the next slower speed after a configured number of failed attempts at 1000BASE-T. This feature is useful in setting up in networks using older cable installations that include only pairs A and B, and not pairs C and D. Signed-off-by: Raju Lakkaraju Signed-off-by: Allan W. Nielsen Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- include/uapi/linux/ethtool.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 42f696f..f0db778 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -248,9 +248,12 @@ struct ethtool_tunable { void *data[0]; }; +#define DOWNSHIFT_DEV_DEFAULT_COUNT 0xff +#define DOWNSHIFT_DEV_DISABLE 0 + enum phy_tunable_id { ETHTOOL_PHY_ID_UNSPEC, - + ETHTOOL_PHY_DOWNSHIFT, /* * Add your fresh new phy tunable attribute above and remember to update * phy_tunable_strings[] in net/core/ethtool.c -- cgit v1.1 From 65feddd5b91ba093e47eef026358e0823beedb9c Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Thu, 17 Nov 2016 13:07:23 +0100 Subject: ethtool: Core impl for ETHTOOL_PHY_DOWNSHIFT tunable Adding validation support for the ETHTOOL_PHY_DOWNSHIFT. Functional implementation needs to be done in the individual PHY drivers. Signed-off-by: Raju Lakkaraju Reviewed-by: Andrew Lunn Signed-off-by: Allan W. Nielsen Signed-off-by: David S. Miller --- net/core/ethtool.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 61aebdf..e9b45567 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -122,6 +122,7 @@ tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = { static const char phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = { [ETHTOOL_ID_UNSPEC] = "Unspec", + [ETHTOOL_PHY_DOWNSHIFT] = "phy-downshift", }; static int ethtool_get_features(struct net_device *dev, void __user *useraddr) @@ -2435,6 +2436,11 @@ static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr) static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna) { switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + if (tuna->len != sizeof(u8) || + tuna->type_id != ETHTOOL_TUNABLE_U8) + return -EINVAL; + break; default: return -EINVAL; } -- cgit v1.1 From 310d9ad57ae0a439b4f604e76854c8462303fa79 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Thu, 17 Nov 2016 13:07:24 +0100 Subject: net: phy: Add downshift get/set support in Microsemi PHYs driver Implements the phy tunable function pointers and implement downshift functionality for MSCC PHYs. Signed-off-by: Raju Lakkaraju Reviewed-by: Andrew Lunn Signed-off-by: Allan W. Nielsen Signed-off-by: David S. Miller --- drivers/net/phy/mscc.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index d0026ab..92018ba 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -46,8 +46,15 @@ enum rgmii_rx_clock_delay { #define MSCC_EXT_PAGE_ACCESS 31 #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */ +#define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */ #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */ +/* Extended Page 1 Registers */ +#define MSCC_PHY_ACTIPHY_CNTL 20 +#define DOWNSHIFT_CNTL_MASK 0x001C +#define DOWNSHIFT_EN 0x0010 +#define DOWNSHIFT_CNTL_POS 2 + /* Extended Page 2 Registers */ #define MSCC_PHY_RGMII_CNTL 20 #define RGMII_RX_CLK_DELAY_MASK 0x0070 @@ -75,6 +82,8 @@ enum rgmii_rx_clock_delay { #define MSCC_VDDMAC_2500 2500 #define MSCC_VDDMAC_3300 3300 +#define DOWNSHIFT_COUNT_MAX 5 + struct vsc8531_private { int rate_magic; }; @@ -101,6 +110,66 @@ static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page) return rc; } +static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count) +{ + int rc; + u16 reg_val; + + mutex_lock(&phydev->lock); + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED); + if (rc != 0) + goto out_unlock; + + reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); + reg_val &= DOWNSHIFT_CNTL_MASK; + if (!(reg_val & DOWNSHIFT_EN)) + *count = DOWNSHIFT_DEV_DISABLE; + else + *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2; + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD); + +out_unlock: + mutex_unlock(&phydev->lock); + + return rc; +} + +static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count) +{ + int rc; + u16 reg_val; + + if (count == DOWNSHIFT_DEV_DEFAULT_COUNT) { + /* Default downshift count 3 (i.e. Bit3:2 = 0b01) */ + count = ((1 << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN); + } else if (count > DOWNSHIFT_COUNT_MAX || count == 1) { + phydev_err(phydev, "Downshift count should be 2,3,4 or 5\n"); + return -ERANGE; + } else if (count) { + /* Downshift count is either 2,3,4 or 5 */ + count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN); + } + + mutex_lock(&phydev->lock); + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED); + if (rc != 0) + goto out_unlock; + + reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); + reg_val &= ~(DOWNSHIFT_CNTL_MASK); + reg_val |= count; + rc = phy_write(phydev, MSCC_PHY_ACTIPHY_CNTL, reg_val); + if (rc != 0) + goto out_unlock; + + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD); + +out_unlock: + mutex_unlock(&phydev->lock); + + return rc; +} + static int vsc85xx_wol_set(struct phy_device *phydev, struct ethtool_wolinfo *wol) { @@ -329,6 +398,29 @@ out_unlock: return rc; } +static int vsc85xx_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return vsc85xx_downshift_get(phydev, (u8 *)data); + default: + return -EINVAL; + } +} + +static int vsc85xx_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, + const void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return vsc85xx_downshift_set(phydev, *(u8 *)data); + default: + return -EINVAL; + } +} + static int vsc85xx_config_init(struct phy_device *phydev) { int rc; @@ -418,6 +510,8 @@ static struct phy_driver vsc85xx_driver[] = { .probe = &vsc85xx_probe, .set_wol = &vsc85xx_wol_set, .get_wol = &vsc85xx_wol_get, + .get_tunable = &vsc85xx_get_tunable, + .set_tunable = &vsc85xx_set_tunable, }, { .phy_id = PHY_ID_VSC8531, @@ -437,6 +531,8 @@ static struct phy_driver vsc85xx_driver[] = { .probe = &vsc85xx_probe, .set_wol = &vsc85xx_wol_set, .get_wol = &vsc85xx_wol_get, + .get_tunable = &vsc85xx_get_tunable, + .set_tunable = &vsc85xx_set_tunable, }, { .phy_id = PHY_ID_VSC8540, @@ -456,6 +552,8 @@ static struct phy_driver vsc85xx_driver[] = { .probe = &vsc85xx_probe, .set_wol = &vsc85xx_wol_set, .get_wol = &vsc85xx_wol_get, + .get_tunable = &vsc85xx_get_tunable, + .set_tunable = &vsc85xx_set_tunable, }, { .phy_id = PHY_ID_VSC8541, @@ -475,6 +573,8 @@ static struct phy_driver vsc85xx_driver[] = { .probe = &vsc85xx_probe, .set_wol = &vsc85xx_wol_set, .get_wol = &vsc85xx_wol_get, + .get_tunable = &vsc85xx_get_tunable, + .set_tunable = &vsc85xx_set_tunable, } }; -- cgit v1.1 From 5a4e4c8f0236bc4cbb88aef5a706116e60e7ccc7 Mon Sep 17 00:00:00 2001 From: "Lendacky, Thomas" Date: Thu, 17 Nov 2016 08:43:37 -0600 Subject: amd-xgbe: Update connection validation for backplane mode Update the connection type enumeration for backplane mode and return an error when there is a mismatch between the mode and the connection type. Signed-off-by: Tom Lendacky Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 348cc8c..9d8c9530 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -164,6 +164,7 @@ enum xgbe_conn_type { XGBE_CONN_TYPE_NONE = 0, XGBE_CONN_TYPE_SFP, XGBE_CONN_TYPE_MDIO, + XGBE_CONN_TYPE_RSVD1, XGBE_CONN_TYPE_BACKPLANE, XGBE_CONN_TYPE_MAX, }; @@ -2831,6 +2832,7 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) if (xgbe_phy_conn_type_mismatch(pdata)) { dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n", phy_data->port_mode, phy_data->conn_type); + return -EINVAL; } /* Validate the mode requested */ -- cgit v1.1 From 00a19e55ca180a95b1652705cbe8460701b06878 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 17 Nov 2016 11:19:10 -0800 Subject: net: gianfar_ptp: Rename FS bit to FIPERST FS is a global symbol used by the x86 32-bit architecture, fixes builds re-definitions: >> drivers/net/ethernet/freescale/gianfar_ptp.c:75:0: warning: "FS" >> redefined #define FS (1<<28) /* FIPER start indication */ In file included from arch/x86/include/uapi/asm/ptrace.h:5:0, from arch/x86/include/asm/ptrace.h:6, from arch/x86/include/asm/math_emu.h:4, from arch/x86/include/asm/processor.h:11, from include/linux/mutex.h:19, from include/linux/kernfs.h:13, from include/linux/sysfs.h:15, from include/linux/kobject.h:21, from include/linux/device.h:17, from drivers/net/ethernet/freescale/gianfar_ptp.c:23: arch/x86/include/uapi/asm/ptrace-abi.h:15:0: note: this is the location of the previous definition #define FS 9 Reported-by: kbuild test robot Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ptp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c index 5779881..3e8d1ff 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -72,7 +72,7 @@ struct gianfar_ptp_registers { /* Bit definitions for the TMR_CTRL register */ #define ALM1P (1<<31) /* Alarm1 output polarity */ #define ALM2P (1<<30) /* Alarm2 output polarity */ -#define FS (1<<28) /* FIPER start indication */ +#define FIPERST (1<<28) /* FIPER start indication */ #define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */ #define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */ #define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */ @@ -502,7 +502,7 @@ static int gianfar_ptp_probe(struct platform_device *dev) gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1); gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2); set_alarm(etsects); - gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE|FRD); + gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FIPERST|RTPE|TE|FRD); spin_unlock_irqrestore(&etsects->lock, flags); -- cgit v1.1 From 0827be21a0c1ffc903cc1f2641ea68b32601e595 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 17 Nov 2016 11:19:11 -0800 Subject: net: fsl: Allow most drivers to be built with COMPILE_TEST There are only a handful of Freescale Ethernet drivers that don't actually build with COMPILE_TEST: * FEC, for which we would need to define a default register layout if no supported architecture is defined * UCC_GETH which depends on PowerPC cpm.h header (which could be moved to a generic location) * GIANFAR needs to depend on HAS_DMA to fix linking failures on some architectures (like m32r) We need to fix an unmet dependency to get there though: warning: (FSL_XGMAC_MDIO) selects OF_MDIO which has unmet direct dependencies (OF && PHYLIB) which would result in CONFIG_OF_MDIO=[ym] without CONFIG_OF to be set. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index aa3f615..0d41551 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -8,7 +8,7 @@ config NET_VENDOR_FREESCALE depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \ M523x || M527x || M5272 || M528x || M520x || M532x || \ ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) || \ - ARCH_LAYERSCAPE + ARCH_LAYERSCAPE || COMPILE_TEST ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -65,6 +65,7 @@ config FSL_PQ_MDIO config FSL_XGMAC_MDIO tristate "Freescale XGMAC MDIO" select PHYLIB + depends on OF select OF_MDIO ---help--- This driver supports the MDIO bus on the Fman 10G Ethernet MACs, and @@ -85,6 +86,7 @@ config UGETH_TX_ON_DEMAND config GIANFAR tristate "Gianfar Ethernet" + depends on HAS_DMA select FSL_PQ_MDIO select PHYLIB select CRC32 -- cgit v1.1 From 603ab57363a0ba66c77ca4b3027bc0b4505df504 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 17 Nov 2016 11:19:12 -0800 Subject: bus: mvebu-bus: Provide inline stub for mvebu_mbus_get_dram_win_info In preparation for allowing CONFIG_MVNETA_BM to build with COMPILE_TEST, provide an inline stub for mvebu_mbus_get_dram_win_info(). Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/mbus.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/mbus.h b/include/linux/mbus.h index 2931aa4..0d3f14f 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -82,6 +82,7 @@ static inline int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, } #endif +#ifdef CONFIG_MVEBU_MBUS int mvebu_mbus_save_cpu_target(u32 __iomem *store_addr); void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); void mvebu_mbus_get_pcie_io_aperture(struct resource *res); @@ -97,5 +98,12 @@ int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, size_t mbus_size, phys_addr_t sdram_phys_base, size_t sdram_size); int mvebu_mbus_dt_init(bool is_coherent); +#else +static inline int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, + u8 *attr) +{ + return -EINVAL; +} +#endif /* CONFIG_MVEBU_MBUS */ #endif /* __LINUX_MBUS_H */ -- cgit v1.1 From a0627f776a4547039ded2f0a07eaf4f0c704be54 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 17 Nov 2016 11:19:13 -0800 Subject: net: marvell: Allow drivers to be built with COMPILE_TEST All Marvell Ethernet drivers actually build fine with COMPILE_TEST with a few warnings. We need to add a few HAS_DMA dependencies to fix linking failures on problematic architectures like m32r. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/Kconfig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 2664827..d74d4e6 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_MARVELL bool "Marvell devices" default y - depends on PCI || CPU_PXA168 || MV64X60 || PPC32 || PLAT_ORION || INET + depends on PCI || CPU_PXA168 || MV64X60 || PPC32 || PLAT_ORION || INET || COMPILE_TEST ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -18,7 +18,8 @@ if NET_VENDOR_MARVELL config MV643XX_ETH tristate "Marvell Discovery (643XX) and Orion ethernet support" - depends on (MV64X60 || PPC32 || PLAT_ORION) && INET + depends on (MV64X60 || PPC32 || PLAT_ORION || COMPILE_TEST) && INET + depends on HAS_DMA select PHYLIB select MVMDIO ---help--- @@ -55,7 +56,8 @@ config MVNETA_BM_ENABLE config MVNETA tristate "Marvell Armada 370/38x/XP network interface support" - depends on PLAT_ORION + depends on PLAT_ORION || COMPILE_TEST + depends on HAS_DMA select MVMDIO select FIXED_PHY ---help--- @@ -77,7 +79,8 @@ config MVNETA_BM config MVPP2 tristate "Marvell Armada 375 network interface support" - depends on MACH_ARMADA_375 + depends on MACH_ARMADA_375 || COMPILE_TEST + depends on HAS_DMA select MVMDIO ---help--- This driver supports the network interface units in the -- cgit v1.1 From 0717b8769bce4c40d1086a53648d2be306bc2a72 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 17 Nov 2016 11:19:14 -0800 Subject: net: dsa: mv88e6xxx: Select IRQ_DOMAIN Some architectures may not define IRQ_DOMAIN (like m32r), fixes undefined references to IRQ_DOMAIN functions. Fixes: dc30c35be720 ("net: dsa: mv88e6xxx: Implement interrupt support.") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig index 4866688..1aaa7a9 100644 --- a/drivers/net/dsa/mv88e6xxx/Kconfig +++ b/drivers/net/dsa/mv88e6xxx/Kconfig @@ -1,6 +1,7 @@ config NET_DSA_MV88E6XXX tristate "Marvell 88E6xxx Ethernet switch fabric support" depends on NET_DSA + select IRQ_DOMAIN select NET_DSA_TAG_EDSA select NET_DSA_TAG_DSA help -- cgit v1.1 From f6e3ef3e4d3546b17e9901925276b70a9ea02d94 Mon Sep 17 00:00:00 2001 From: Woojung Huh Date: Thu, 17 Nov 2016 22:10:02 +0000 Subject: lan78xx: relocate mdix setting to phy driver Relocate mdix code to phy driver to be called at config_init(). Signed-off-by: Woojung Huh Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/microchip.c | 36 +++++++++++++++++++++- drivers/net/usb/lan78xx.c | 73 ++------------------------------------------- 2 files changed, 38 insertions(+), 71 deletions(-) diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index 7c00e50..eb4db22 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -106,6 +106,40 @@ static int lan88xx_set_wol(struct phy_device *phydev, return 0; } +static void lan88xx_set_mdix(struct phy_device *phydev) +{ + int buf; + int val; + + switch (phydev->mdix) { + case ETH_TP_MDI: + val = LAN88XX_EXT_MODE_CTRL_MDI_; + break; + case ETH_TP_MDI_X: + val = LAN88XX_EXT_MODE_CTRL_MDI_X_; + break; + case ETH_TP_MDI_AUTO: + val = LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_; + break; + default: + return; + } + + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1); + buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); + buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; + buf |= val; + phy_write(phydev, LAN88XX_EXT_MODE_CTRL, buf); + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0); +} + +static int lan88xx_config_aneg(struct phy_device *phydev) +{ + lan88xx_set_mdix(phydev); + + return genphy_config_aneg(phydev); +} + static struct phy_driver microchip_phy_driver[] = { { .phy_id = 0x0007c130, @@ -120,7 +154,7 @@ static struct phy_driver microchip_phy_driver[] = { .remove = lan88xx_remove, .config_init = genphy_config_init, - .config_aneg = genphy_config_aneg, + .config_aneg = lan88xx_config_aneg, .read_status = genphy_read_status, .ack_interrupt = lan88xx_phy_ack_interrupt, diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index cf2857f..0c459e9 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1471,62 +1471,12 @@ static void lan78xx_set_msglevel(struct net_device *net, u32 level) dev->msg_enable = level; } -static int lan78xx_get_mdix_status(struct net_device *net) -{ - struct phy_device *phydev = net->phydev; - int buf; - - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1); - buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0); - - return buf; -} - -static void lan78xx_set_mdix_status(struct net_device *net, __u8 mdix_ctrl) -{ - struct lan78xx_net *dev = netdev_priv(net); - struct phy_device *phydev = net->phydev; - int buf; - - if (mdix_ctrl == ETH_TP_MDI) { - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, - LAN88XX_EXT_PAGE_SPACE_1); - buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); - buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; - phy_write(phydev, LAN88XX_EXT_MODE_CTRL, - buf | LAN88XX_EXT_MODE_CTRL_MDI_); - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, - LAN88XX_EXT_PAGE_SPACE_0); - } else if (mdix_ctrl == ETH_TP_MDI_X) { - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, - LAN88XX_EXT_PAGE_SPACE_1); - buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); - buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; - phy_write(phydev, LAN88XX_EXT_MODE_CTRL, - buf | LAN88XX_EXT_MODE_CTRL_MDI_X_); - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, - LAN88XX_EXT_PAGE_SPACE_0); - } else if (mdix_ctrl == ETH_TP_MDI_AUTO) { - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, - LAN88XX_EXT_PAGE_SPACE_1); - buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); - buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; - phy_write(phydev, LAN88XX_EXT_MODE_CTRL, - buf | LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_); - phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, - LAN88XX_EXT_PAGE_SPACE_0); - } - dev->mdix_ctrl = mdix_ctrl; -} - static int lan78xx_get_link_ksettings(struct net_device *net, struct ethtool_link_ksettings *cmd) { struct lan78xx_net *dev = netdev_priv(net); struct phy_device *phydev = net->phydev; int ret; - int buf; ret = usb_autopm_get_interface(dev->intf); if (ret < 0) @@ -1534,20 +1484,6 @@ static int lan78xx_get_link_ksettings(struct net_device *net, ret = phy_ethtool_ksettings_get(phydev, cmd); - buf = lan78xx_get_mdix_status(net); - - buf &= LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; - if (buf == LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_) { - cmd->base.eth_tp_mdix = ETH_TP_MDI_AUTO; - cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; - } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_) { - cmd->base.eth_tp_mdix = ETH_TP_MDI; - cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI; - } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_X_) { - cmd->base.eth_tp_mdix = ETH_TP_MDI_X; - cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_X; - } - usb_autopm_put_interface(dev->intf); return ret; @@ -1565,9 +1501,6 @@ static int lan78xx_set_link_ksettings(struct net_device *net, if (ret < 0) return ret; - if (dev->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl) - lan78xx_set_mdix_status(net, cmd->base.eth_tp_mdix_ctrl); - /* change speed & duplex */ ret = phy_ethtool_ksettings_set(phydev, cmd); @@ -2019,6 +1952,9 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) phydev->irq = 0; netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq); + /* set to AUTOMDIX */ + phydev->mdix = ETH_TP_MDI_AUTO; + ret = phy_connect_direct(dev->net, phydev, lan78xx_link_status_change, PHY_INTERFACE_MODE_GMII); @@ -2028,9 +1964,6 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) return -EIO; } - /* set to AUTOMDIX */ - lan78xx_set_mdix_status(dev->net, ETH_TP_MDI_AUTO); - /* MAC doesn't support 1000T Half */ phydev->supported &= ~SUPPORTED_1000baseT_Half; -- cgit v1.1 From c816061d27659cb666f8fd9a67669757f2c04a55 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Nov 2016 14:47:35 +0300 Subject: liquidio CN23XX: bitwise vs logical AND typo We obviously intended a bitwise AND here, not a logical one. Fixes: 8c978d059224 ("liquidio CN23XX: Mailbox support") Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c index 5309384..73696b42 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c @@ -301,7 +301,7 @@ int octeon_mbox_process_message(struct octeon_mbox *mbox) sizeof(struct octeon_mbox_cmd)); if (!mbox_cmd.msg.s.resp_needed) { mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVED; - if (!(mbox->state && + if (!(mbox->state & OCTEON_MBOX_STATE_RESPONSE_PENDING)) mbox->state = OCTEON_MBOX_STATE_IDLE; writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); -- cgit v1.1 From ab677ff4ad15bc26c359490ee201557f3a6d20df Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Fri, 18 Nov 2016 16:37:40 +0530 Subject: cxgb4: Allocate Tx queues dynamically Allocate resources dynamically for Upper layer driver's (ULD) like cxgbit, iw_cxgb4, cxgb4i and chcr. The resources allocated include Tx queues which are allocated when ULD register with cxgb4 driver and freed while un-registering. The Tx queues which are shared by ULD shall be allocated by first registering driver and un-allocated by last unregistering driver. Signed-off-by: Atul Gupta Signed-off-by: David S. Miller --- drivers/crypto/chelsio/chcr_algo.c | 16 +-- drivers/crypto/chelsio/chcr_core.c | 3 +- drivers/infiniband/hw/cxgb4/device.c | 1 + drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 19 +++- drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 12 -- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 64 +++++++---- drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c | 114 +++++++++++++++++++ drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 17 +++ drivers/net/ethernet/chelsio/cxgb4/sge.c | 121 +++++++++++++++------ drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 1 + drivers/target/iscsi/cxgbit/cxgbit_main.c | 1 + 11 files changed, 287 insertions(+), 82 deletions(-) diff --git a/drivers/crypto/chelsio/chcr_algo.c b/drivers/crypto/chelsio/chcr_algo.c index e4ddb92..56b1538 100644 --- a/drivers/crypto/chelsio/chcr_algo.c +++ b/drivers/crypto/chelsio/chcr_algo.c @@ -592,16 +592,18 @@ badkey_err: static int cxgb4_is_crypto_q_full(struct net_device *dev, unsigned int idx) { - int ret = 0; - struct sge_ofld_txq *q; struct adapter *adap = netdev2adap(dev); + struct sge_uld_txq_info *txq_info = + adap->sge.uld_txq_info[CXGB4_TX_CRYPTO]; + struct sge_uld_txq *txq; + int ret = 0; local_bh_disable(); - q = &adap->sge.ofldtxq[idx]; - spin_lock(&q->sendq.lock); - if (q->full) + txq = &txq_info->uldtxq[idx]; + spin_lock(&txq->sendq.lock); + if (txq->full) ret = -1; - spin_unlock(&q->sendq.lock); + spin_unlock(&txq->sendq.lock); local_bh_enable(); return ret; } @@ -674,11 +676,11 @@ static int chcr_device_init(struct chcr_context *ctx) } u_ctx = ULD_CTX(ctx); rxq_perchan = u_ctx->lldi.nrxq / u_ctx->lldi.nchan; - ctx->dev->tx_channel_id = 0; rxq_idx = ctx->dev->tx_channel_id * rxq_perchan; rxq_idx += id % rxq_perchan; spin_lock(&ctx->dev->lock_chcr_dev); ctx->tx_channel_id = rxq_idx; + ctx->dev->tx_channel_id = !ctx->dev->tx_channel_id; spin_unlock(&ctx->dev->lock_chcr_dev); } out: diff --git a/drivers/crypto/chelsio/chcr_core.c b/drivers/crypto/chelsio/chcr_core.c index fb5f9bb..4d7f670 100644 --- a/drivers/crypto/chelsio/chcr_core.c +++ b/drivers/crypto/chelsio/chcr_core.c @@ -42,6 +42,7 @@ static chcr_handler_func work_handlers[NUM_CPL_CMDS] = { static struct cxgb4_uld_info chcr_uld_info = { .name = DRV_MODULE_NAME, .nrxq = MAX_ULD_QSETS, + .ntxq = MAX_ULD_QSETS, .rxq_size = 1024, .add = chcr_uld_add, .state_change = chcr_uld_state_change, @@ -126,7 +127,7 @@ static int cpl_fw6_pld_handler(struct chcr_dev *dev, int chcr_send_wr(struct sk_buff *skb) { - return cxgb4_ofld_send(skb->dev, skb); + return cxgb4_crypto_send(skb->dev, skb); } static void *chcr_uld_add(const struct cxgb4_lld_info *lld) diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index 93e3d27..4e5baf4 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -1481,6 +1481,7 @@ static int c4iw_uld_control(void *handle, enum cxgb4_control control, ...) static struct cxgb4_uld_info c4iw_uld_info = { .name = DRV_NAME, .nrxq = MAX_ULD_QSETS, + .ntxq = MAX_ULD_QSETS, .rxq_size = 511, .ciq = true, .lro = false, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 2125903..0bce1bf 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -635,6 +635,7 @@ struct tx_sw_desc; struct sge_txq { unsigned int in_use; /* # of in-use Tx descriptors */ + unsigned int q_type; /* Q type Eth/Ctrl/Ofld */ unsigned int size; /* # of descriptors */ unsigned int cidx; /* SW consumer index */ unsigned int pidx; /* producer index */ @@ -665,7 +666,7 @@ struct sge_eth_txq { /* state for an SGE Ethernet Tx queue */ unsigned long mapping_err; /* # of I/O MMU packet mapping errors */ } ____cacheline_aligned_in_smp; -struct sge_ofld_txq { /* state for an SGE offload Tx queue */ +struct sge_uld_txq { /* state for an SGE offload Tx queue */ struct sge_txq q; struct adapter *adap; struct sk_buff_head sendq; /* list of backpressured packets */ @@ -693,14 +694,20 @@ struct sge_uld_rxq_info { u8 uld; /* uld type */ }; +struct sge_uld_txq_info { + struct sge_uld_txq *uldtxq; /* Txq's for ULD */ + atomic_t users; /* num users */ + u16 ntxq; /* # of egress uld queues */ +}; + struct sge { struct sge_eth_txq ethtxq[MAX_ETH_QSETS]; - struct sge_ofld_txq ofldtxq[MAX_OFLD_QSETS]; struct sge_ctrl_txq ctrlq[MAX_CTRL_QUEUES]; struct sge_eth_rxq ethrxq[MAX_ETH_QSETS]; struct sge_rspq fw_evtq ____cacheline_aligned_in_smp; struct sge_uld_rxq_info **uld_rxq_info; + struct sge_uld_txq_info **uld_txq_info; struct sge_rspq intrq ____cacheline_aligned_in_smp; spinlock_t intrq_lock; @@ -1298,8 +1305,9 @@ int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, unsigned int cmplqid); int t4_sge_mod_ctrl_txq(struct adapter *adap, unsigned int eqid, unsigned int cmplqid); -int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, - struct net_device *dev, unsigned int iqid); +int t4_sge_alloc_uld_txq(struct adapter *adap, struct sge_uld_txq *txq, + struct net_device *dev, unsigned int iqid, + unsigned int uld_type); irqreturn_t t4_sge_intr_msix(int irq, void *cookie); int t4_sge_init(struct adapter *adap); void t4_sge_start(struct adapter *adap); @@ -1661,4 +1669,7 @@ int t4_uld_mem_alloc(struct adapter *adap); void t4_uld_clean_up(struct adapter *adap); void t4_register_netevent_notifier(void); void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, struct sge_fl *fl); +void free_tx_desc(struct adapter *adap, struct sge_txq *q, + unsigned int n, bool unmap); +void free_txq(struct adapter *adap, struct sge_txq *q); #endif /* __CXGB4_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 20455d0..acc2312 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -2512,18 +2512,6 @@ do { \ RL("FLLow:", fl.low); RL("FLStarving:", fl.starving); - } else if (ofld_idx < ofld_entries) { - const struct sge_ofld_txq *tx = - &adap->sge.ofldtxq[ofld_idx * 4]; - int n = min(4, adap->sge.ofldqsets - 4 * ofld_idx); - - S("QType:", "OFLD-Txq"); - T("TxQ ID:", q.cntxt_id); - T("TxQ size:", q.size); - T("TxQ inuse:", q.in_use); - T("TxQ CIDX:", q.cidx); - T("TxQ PIDX:", q.pidx); - } else if (ctrl_idx < ctrl_entries) { const struct sge_ctrl_txq *tx = &adap->sge.ctrlq[ctrl_idx * 4]; int n = min(4, adap->params.nports - 4 * ctrl_idx); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index c0cc2ee..449884f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -530,15 +530,15 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, txq = q->adap->sge.egr_map[qid - q->adap->sge.egr_start]; txq->restarts++; - if ((u8 *)txq < (u8 *)q->adap->sge.ofldtxq) { + if (txq->q_type == CXGB4_TXQ_ETH) { struct sge_eth_txq *eq; eq = container_of(txq, struct sge_eth_txq, q); netif_tx_wake_queue(eq->txq); } else { - struct sge_ofld_txq *oq; + struct sge_uld_txq *oq; - oq = container_of(txq, struct sge_ofld_txq, q); + oq = container_of(txq, struct sge_uld_txq, q); tasklet_schedule(&oq->qresume_tsk); } } else if (opcode == CPL_FW6_MSG || opcode == CPL_FW4_MSG) { @@ -885,15 +885,6 @@ static int setup_sge_queues(struct adapter *adap) } } - j = s->ofldqsets / adap->params.nports; /* iscsi queues per channel */ - for_each_ofldtxq(s, i) { - err = t4_sge_alloc_ofld_txq(adap, &s->ofldtxq[i], - adap->port[i / j], - s->fw_evtq.cntxt_id); - if (err) - goto freeout; - } - for_each_port(adap, i) { /* Note that cmplqid below is 0 if we don't * have RDMA queues, and that's the right value. @@ -1922,8 +1913,18 @@ static void disable_dbs(struct adapter *adap) for_each_ethrxq(&adap->sge, i) disable_txq_db(&adap->sge.ethtxq[i].q); - for_each_ofldtxq(&adap->sge, i) - disable_txq_db(&adap->sge.ofldtxq[i].q); + if (is_offload(adap)) { + struct sge_uld_txq_info *txq_info = + adap->sge.uld_txq_info[CXGB4_TX_OFLD]; + + if (txq_info) { + for_each_ofldtxq(&adap->sge, i) { + struct sge_uld_txq *txq = &txq_info->uldtxq[i]; + + disable_txq_db(&txq->q); + } + } + } for_each_port(adap, i) disable_txq_db(&adap->sge.ctrlq[i].q); } @@ -1934,8 +1935,18 @@ static void enable_dbs(struct adapter *adap) for_each_ethrxq(&adap->sge, i) enable_txq_db(adap, &adap->sge.ethtxq[i].q); - for_each_ofldtxq(&adap->sge, i) - enable_txq_db(adap, &adap->sge.ofldtxq[i].q); + if (is_offload(adap)) { + struct sge_uld_txq_info *txq_info = + adap->sge.uld_txq_info[CXGB4_TX_OFLD]; + + if (txq_info) { + for_each_ofldtxq(&adap->sge, i) { + struct sge_uld_txq *txq = &txq_info->uldtxq[i]; + + enable_txq_db(adap, &txq->q); + } + } + } for_each_port(adap, i) enable_txq_db(adap, &adap->sge.ctrlq[i].q); } @@ -2006,8 +2017,17 @@ static void recover_all_queues(struct adapter *adap) for_each_ethrxq(&adap->sge, i) sync_txq_pidx(adap, &adap->sge.ethtxq[i].q); - for_each_ofldtxq(&adap->sge, i) - sync_txq_pidx(adap, &adap->sge.ofldtxq[i].q); + if (is_offload(adap)) { + struct sge_uld_txq_info *txq_info = + adap->sge.uld_txq_info[CXGB4_TX_OFLD]; + if (txq_info) { + for_each_ofldtxq(&adap->sge, i) { + struct sge_uld_txq *txq = &txq_info->uldtxq[i]; + + sync_txq_pidx(adap, &txq->q); + } + } + } for_each_port(adap, i) sync_txq_pidx(adap, &adap->sge.ctrlq[i].q); } @@ -3991,7 +4011,7 @@ static inline bool is_x_10g_port(const struct link_config *lc) static void cfg_queues(struct adapter *adap) { struct sge *s = &adap->sge; - int i, n10g = 0, qidx = 0; + int i = 0, n10g = 0, qidx = 0; #ifndef CONFIG_CHELSIO_T4_DCB int q10g = 0; #endif @@ -4006,8 +4026,7 @@ static void cfg_queues(struct adapter *adap) adap->params.crypto = 0; } - for_each_port(adap, i) - n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg); + n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg); #ifdef CONFIG_CHELSIO_T4_DCB /* For Data Center Bridging support we need to be able to support up * to 8 Traffic Priorities; each of which will be assigned to its @@ -4075,9 +4094,6 @@ static void cfg_queues(struct adapter *adap) for (i = 0; i < ARRAY_SIZE(s->ctrlq); i++) s->ctrlq[i].q.size = 512; - for (i = 0; i < ARRAY_SIZE(s->ofldtxq); i++) - s->ofldtxq[i].q.size = 1024; - init_rspq(adap, &s->fw_evtq, 0, 1, 1024, 64); init_rspq(adap, &s->intrq, 0, 1, 512, 64); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c index 2471ff4..565a6c6 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c @@ -447,6 +447,106 @@ static void quiesce_rx_uld(struct adapter *adap, unsigned int uld_type) quiesce_rx(adap, &rxq_info->uldrxq[idx].rspq); } +static void +free_sge_txq_uld(struct adapter *adap, struct sge_uld_txq_info *txq_info) +{ + int nq = txq_info->ntxq; + int i; + + for (i = 0; i < nq; i++) { + struct sge_uld_txq *txq = &txq_info->uldtxq[i]; + + if (txq && txq->q.desc) { + tasklet_kill(&txq->qresume_tsk); + t4_ofld_eq_free(adap, adap->mbox, adap->pf, 0, + txq->q.cntxt_id); + free_tx_desc(adap, &txq->q, txq->q.in_use, false); + kfree(txq->q.sdesc); + __skb_queue_purge(&txq->sendq); + free_txq(adap, &txq->q); + } + } +} + +static int +alloc_sge_txq_uld(struct adapter *adap, struct sge_uld_txq_info *txq_info, + unsigned int uld_type) +{ + struct sge *s = &adap->sge; + int nq = txq_info->ntxq; + int i, j, err; + + j = nq / adap->params.nports; + for (i = 0; i < nq; i++) { + struct sge_uld_txq *txq = &txq_info->uldtxq[i]; + + txq->q.size = 1024; + err = t4_sge_alloc_uld_txq(adap, txq, adap->port[i / j], + s->fw_evtq.cntxt_id, uld_type); + if (err) + goto freeout; + } + return 0; +freeout: + free_sge_txq_uld(adap, txq_info); + return err; +} + +static void +release_sge_txq_uld(struct adapter *adap, unsigned int uld_type) +{ + struct sge_uld_txq_info *txq_info = NULL; + int tx_uld_type = TX_ULD(uld_type); + + txq_info = adap->sge.uld_txq_info[tx_uld_type]; + + if (txq_info && atomic_dec_and_test(&txq_info->users)) { + free_sge_txq_uld(adap, txq_info); + kfree(txq_info->uldtxq); + kfree(txq_info); + adap->sge.uld_txq_info[tx_uld_type] = NULL; + } +} + +static int +setup_sge_txq_uld(struct adapter *adap, unsigned int uld_type, + const struct cxgb4_uld_info *uld_info) +{ + struct sge_uld_txq_info *txq_info = NULL; + int tx_uld_type, i; + + tx_uld_type = TX_ULD(uld_type); + txq_info = adap->sge.uld_txq_info[tx_uld_type]; + + if ((tx_uld_type == CXGB4_TX_OFLD) && txq_info && + (atomic_inc_return(&txq_info->users) > 1)) + return 0; + + txq_info = kzalloc(sizeof(*txq_info), GFP_KERNEL); + if (!txq_info) + return -ENOMEM; + + i = min_t(int, uld_info->ntxq, num_online_cpus()); + txq_info->ntxq = roundup(i, adap->params.nports); + + txq_info->uldtxq = kcalloc(txq_info->ntxq, sizeof(struct sge_uld_txq), + GFP_KERNEL); + if (!txq_info->uldtxq) { + kfree(txq_info->uldtxq); + return -ENOMEM; + } + + if (alloc_sge_txq_uld(adap, txq_info, tx_uld_type)) { + kfree(txq_info->uldtxq); + kfree(txq_info); + return -ENOMEM; + } + + atomic_inc(&txq_info->users); + adap->sge.uld_txq_info[tx_uld_type] = txq_info; + return 0; +} + static void uld_queue_init(struct adapter *adap, unsigned int uld_type, struct cxgb4_lld_info *lli) { @@ -472,7 +572,15 @@ int t4_uld_mem_alloc(struct adapter *adap) if (!s->uld_rxq_info) goto err_uld; + s->uld_txq_info = kzalloc(CXGB4_TX_MAX * + sizeof(struct sge_uld_txq_info *), + GFP_KERNEL); + if (!s->uld_txq_info) + goto err_uld_rx; return 0; + +err_uld_rx: + kfree(s->uld_rxq_info); err_uld: kfree(adap->uld); return -ENOMEM; @@ -482,6 +590,7 @@ void t4_uld_mem_free(struct adapter *adap) { struct sge *s = &adap->sge; + kfree(s->uld_txq_info); kfree(s->uld_rxq_info); kfree(adap->uld); } @@ -616,6 +725,9 @@ int cxgb4_register_uld(enum cxgb4_uld type, ret = -EBUSY; goto free_irq; } + ret = setup_sge_txq_uld(adap, type, p); + if (ret) + goto free_irq; adap->uld[type] = *p; uld_attach(adap, type); adap_idx++; @@ -644,6 +756,7 @@ out: break; adap->uld[type].handle = NULL; adap->uld[type].add = NULL; + release_sge_txq_uld(adap, type); if (adap->flags & FULL_INIT_DONE) quiesce_rx_uld(adap, type); if (adap->flags & USING_MSIX) @@ -679,6 +792,7 @@ int cxgb4_unregister_uld(enum cxgb4_uld type) continue; adap->uld[type].handle = NULL; adap->uld[type].add = NULL; + release_sge_txq_uld(adap, type); if (adap->flags & FULL_INIT_DONE) quiesce_rx_uld(adap, type); if (adap->flags & USING_MSIX) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h index 2996793..4c85660 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h @@ -77,6 +77,8 @@ enum { /* Special asynchronous notification message */ #define CXGB4_MSG_AN ((void *)1) +#define TX_ULD(uld)(((uld) != CXGB4_ULD_CRYPTO) ? CXGB4_TX_OFLD :\ + CXGB4_TX_CRYPTO) struct serv_entry { void *data; @@ -223,6 +225,19 @@ enum cxgb4_uld { CXGB4_ULD_MAX }; +enum cxgb4_tx_uld { + CXGB4_TX_OFLD, + CXGB4_TX_CRYPTO, + CXGB4_TX_MAX +}; + +enum cxgb4_txq_type { + CXGB4_TXQ_ETH, + CXGB4_TXQ_ULD, + CXGB4_TXQ_CTRL, + CXGB4_TXQ_MAX +}; + enum cxgb4_state { CXGB4_STATE_UP, CXGB4_STATE_START_RECOVERY, @@ -316,6 +331,7 @@ struct cxgb4_uld_info { void *handle; unsigned int nrxq; unsigned int rxq_size; + unsigned int ntxq; bool ciq; bool lro; void *(*add)(const struct cxgb4_lld_info *p); @@ -333,6 +349,7 @@ struct cxgb4_uld_info { int cxgb4_register_uld(enum cxgb4_uld type, const struct cxgb4_uld_info *p); int cxgb4_unregister_uld(enum cxgb4_uld type); int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb); +int cxgb4_crypto_send(struct net_device *dev, struct sk_buff *skb); unsigned int cxgb4_dbfifo_count(const struct net_device *dev, int lpfifo); unsigned int cxgb4_port_chan(const struct net_device *dev); unsigned int cxgb4_port_viid(const struct net_device *dev); diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 1e74fd6..b7d0753 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -377,8 +377,8 @@ unmap: dma_unmap_page(dev, be64_to_cpu(p->addr[0]), * Reclaims Tx descriptors from an SGE Tx queue and frees the associated * Tx buffers. Called with the Tx queue lock held. */ -static void free_tx_desc(struct adapter *adap, struct sge_txq *q, - unsigned int n, bool unmap) +void free_tx_desc(struct adapter *adap, struct sge_txq *q, + unsigned int n, bool unmap) { struct tx_sw_desc *d; unsigned int cidx = q->cidx; @@ -1543,7 +1543,7 @@ static inline unsigned int calc_tx_flits_ofld(const struct sk_buff *skb) * inability to map packets. A periodic timer attempts to restart * queues so marked. */ -static void txq_stop_maperr(struct sge_ofld_txq *q) +static void txq_stop_maperr(struct sge_uld_txq *q) { q->mapping_err++; q->q.stops++; @@ -1559,7 +1559,7 @@ static void txq_stop_maperr(struct sge_ofld_txq *q) * Stops an offload Tx queue that has become full and modifies the packet * being written to request a wakeup. */ -static void ofldtxq_stop(struct sge_ofld_txq *q, struct sk_buff *skb) +static void ofldtxq_stop(struct sge_uld_txq *q, struct sk_buff *skb) { struct fw_wr_hdr *wr = (struct fw_wr_hdr *)skb->data; @@ -1586,7 +1586,7 @@ static void ofldtxq_stop(struct sge_ofld_txq *q, struct sk_buff *skb) * boolean "service_ofldq_running" to make sure that only one instance * is ever running at a time ... */ -static void service_ofldq(struct sge_ofld_txq *q) +static void service_ofldq(struct sge_uld_txq *q) { u64 *pos, *before, *end; int credits; @@ -1706,7 +1706,7 @@ static void service_ofldq(struct sge_ofld_txq *q) * * Send an offload packet through an SGE offload queue. */ -static int ofld_xmit(struct sge_ofld_txq *q, struct sk_buff *skb) +static int ofld_xmit(struct sge_uld_txq *q, struct sk_buff *skb) { skb->priority = calc_tx_flits_ofld(skb); /* save for restart */ spin_lock(&q->sendq.lock); @@ -1735,7 +1735,7 @@ static int ofld_xmit(struct sge_ofld_txq *q, struct sk_buff *skb) */ static void restart_ofldq(unsigned long data) { - struct sge_ofld_txq *q = (struct sge_ofld_txq *)data; + struct sge_uld_txq *q = (struct sge_uld_txq *)data; spin_lock(&q->sendq.lock); q->full = 0; /* the queue actually is completely empty now */ @@ -1767,17 +1767,23 @@ static inline unsigned int is_ctrl_pkt(const struct sk_buff *skb) return skb->queue_mapping & 1; } -static inline int ofld_send(struct adapter *adap, struct sk_buff *skb) +static inline int uld_send(struct adapter *adap, struct sk_buff *skb, + unsigned int tx_uld_type) { + struct sge_uld_txq_info *txq_info; + struct sge_uld_txq *txq; unsigned int idx = skb_txq(skb); + txq_info = adap->sge.uld_txq_info[tx_uld_type]; + txq = &txq_info->uldtxq[idx]; + if (unlikely(is_ctrl_pkt(skb))) { /* Single ctrl queue is a requirement for LE workaround path */ if (adap->tids.nsftids) idx = 0; return ctrl_xmit(&adap->sge.ctrlq[idx], skb); } - return ofld_xmit(&adap->sge.ofldtxq[idx], skb); + return ofld_xmit(txq, skb); } /** @@ -1794,7 +1800,7 @@ int t4_ofld_send(struct adapter *adap, struct sk_buff *skb) int ret; local_bh_disable(); - ret = ofld_send(adap, skb); + ret = uld_send(adap, skb, CXGB4_TX_OFLD); local_bh_enable(); return ret; } @@ -1813,6 +1819,39 @@ int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb) } EXPORT_SYMBOL(cxgb4_ofld_send); +/** + * t4_crypto_send - send crypto packet + * @adap: the adapter + * @skb: the packet + * + * Sends crypto packet. We use the packet queue_mapping to select the + * appropriate Tx queue as follows: bit 0 indicates whether the packet + * should be sent as regular or control, bits 1-15 select the queue. + */ +static int t4_crypto_send(struct adapter *adap, struct sk_buff *skb) +{ + int ret; + + local_bh_disable(); + ret = uld_send(adap, skb, CXGB4_TX_CRYPTO); + local_bh_enable(); + return ret; +} + +/** + * cxgb4_crypto_send - send crypto packet + * @dev: the net device + * @skb: the packet + * + * Sends crypto packet. This is an exported version of @t4_crypto_send, + * intended for ULDs. + */ +int cxgb4_crypto_send(struct net_device *dev, struct sk_buff *skb) +{ + return t4_crypto_send(netdev2adap(dev), skb); +} +EXPORT_SYMBOL(cxgb4_crypto_send); + static inline void copy_frags(struct sk_buff *skb, const struct pkt_gl *gl, unsigned int offset) { @@ -2479,7 +2518,7 @@ static void sge_tx_timer_cb(unsigned long data) for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++) for (m = s->txq_maperr[i]; m; m &= m - 1) { unsigned long id = __ffs(m) + i * BITS_PER_LONG; - struct sge_ofld_txq *txq = s->egr_map[id]; + struct sge_uld_txq *txq = s->egr_map[id]; clear_bit(id, s->txq_maperr); tasklet_schedule(&txq->qresume_tsk); @@ -2799,6 +2838,7 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, return ret; } + txq->q.q_type = CXGB4_TXQ_ETH; init_txq(adap, &txq->q, FW_EQ_ETH_CMD_EQID_G(ntohl(c.eqid_pkd))); txq->txq = netdevq; txq->tso = txq->tx_cso = txq->vlan_ins = 0; @@ -2852,6 +2892,7 @@ int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, return ret; } + txq->q.q_type = CXGB4_TXQ_CTRL; init_txq(adap, &txq->q, FW_EQ_CTRL_CMD_EQID_G(ntohl(c.cmpliqid_eqid))); txq->adap = adap; skb_queue_head_init(&txq->sendq); @@ -2872,13 +2913,15 @@ int t4_sge_mod_ctrl_txq(struct adapter *adap, unsigned int eqid, return t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val); } -int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, - struct net_device *dev, unsigned int iqid) +int t4_sge_alloc_uld_txq(struct adapter *adap, struct sge_uld_txq *txq, + struct net_device *dev, unsigned int iqid, + unsigned int uld_type) { int ret, nentries; struct fw_eq_ofld_cmd c; struct sge *s = &adap->sge; struct port_info *pi = netdev_priv(dev); + int cmd = FW_EQ_OFLD_CMD; /* Add status entries */ nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc); @@ -2891,7 +2934,9 @@ int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, return -ENOMEM; memset(&c, 0, sizeof(c)); - c.op_to_vfn = htonl(FW_CMD_OP_V(FW_EQ_OFLD_CMD) | FW_CMD_REQUEST_F | + if (unlikely(uld_type == CXGB4_TX_CRYPTO)) + cmd = FW_EQ_CTRL_CMD; + c.op_to_vfn = htonl(FW_CMD_OP_V(cmd) | FW_CMD_REQUEST_F | FW_CMD_WRITE_F | FW_CMD_EXEC_F | FW_EQ_OFLD_CMD_PFN_V(adap->pf) | FW_EQ_OFLD_CMD_VFN_V(0)); @@ -2919,6 +2964,7 @@ int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, return ret; } + txq->q.q_type = CXGB4_TXQ_ULD; init_txq(adap, &txq->q, FW_EQ_OFLD_CMD_EQID_G(ntohl(c.eqid_pkd))); txq->adap = adap; skb_queue_head_init(&txq->sendq); @@ -2928,7 +2974,7 @@ int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, return 0; } -static void free_txq(struct adapter *adap, struct sge_txq *q) +void free_txq(struct adapter *adap, struct sge_txq *q) { struct sge *s = &adap->sge; @@ -3026,21 +3072,6 @@ void t4_free_sge_resources(struct adapter *adap) } } - /* clean up offload Tx queues */ - for (i = 0; i < ARRAY_SIZE(adap->sge.ofldtxq); i++) { - struct sge_ofld_txq *q = &adap->sge.ofldtxq[i]; - - if (q->q.desc) { - tasklet_kill(&q->qresume_tsk); - t4_ofld_eq_free(adap, adap->mbox, adap->pf, 0, - q->q.cntxt_id); - free_tx_desc(adap, &q->q, q->q.in_use, false); - kfree(q->q.sdesc); - __skb_queue_purge(&q->sendq); - free_txq(adap, &q->q); - } - } - /* clean up control Tx queues */ for (i = 0; i < ARRAY_SIZE(adap->sge.ctrlq); i++) { struct sge_ctrl_txq *cq = &adap->sge.ctrlq[i]; @@ -3093,12 +3124,34 @@ void t4_sge_stop(struct adapter *adap) if (s->tx_timer.function) del_timer_sync(&s->tx_timer); - for (i = 0; i < ARRAY_SIZE(s->ofldtxq); i++) { - struct sge_ofld_txq *q = &s->ofldtxq[i]; + if (is_offload(adap)) { + struct sge_uld_txq_info *txq_info; + + txq_info = adap->sge.uld_txq_info[CXGB4_TX_OFLD]; + if (txq_info) { + struct sge_uld_txq *txq = txq_info->uldtxq; - if (q->q.desc) - tasklet_kill(&q->qresume_tsk); + for_each_ofldtxq(&adap->sge, i) { + if (txq->q.desc) + tasklet_kill(&txq->qresume_tsk); + } + } } + + if (is_pci_uld(adap)) { + struct sge_uld_txq_info *txq_info; + + txq_info = adap->sge.uld_txq_info[CXGB4_TX_CRYPTO]; + if (txq_info) { + struct sge_uld_txq *txq = txq_info->uldtxq; + + for_each_ofldtxq(&adap->sge, i) { + if (txq->q.desc) + tasklet_kill(&txq->qresume_tsk); + } + } + } + for (i = 0; i < ARRAY_SIZE(s->ctrlq); i++) { struct sge_ctrl_txq *cq = &s->ctrlq[i]; diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 0039beb..4655a9f 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -85,6 +85,7 @@ static inline int send_tx_flowc_wr(struct cxgbi_sock *); static const struct cxgb4_uld_info cxgb4i_uld_info = { .name = DRV_MODULE_NAME, .nrxq = MAX_ULD_QSETS, + .ntxq = MAX_ULD_QSETS, .rxq_size = 1024, .lro = false, .add = t4_uld_add, diff --git a/drivers/target/iscsi/cxgbit/cxgbit_main.c b/drivers/target/iscsi/cxgbit/cxgbit_main.c index ad26b93..96eedfc 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_main.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_main.c @@ -653,6 +653,7 @@ static struct iscsit_transport cxgbit_transport = { static struct cxgb4_uld_info cxgbit_uld_info = { .name = DRV_NAME, .nrxq = MAX_ULD_QSETS, + .ntxq = MAX_ULD_QSETS, .rxq_size = 1024, .lro = true, .add = cxgbit_uld_add, -- cgit v1.1 From 6b4480d109b0ae186a2b1e1d8c5e83de51a0497e Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 18 Nov 2016 18:07:04 +0530 Subject: mwifiex: parse device tree node for PCIe This patch derives device tree node from pcie bus layer framework. Device tree bindings file has been renamed(marvell-sd8xxx.txt -> marvell-8xxx.txt) to accommodate PCIe changes. Signed-off-by: Xinming Hu Signed-off-by: Rajat Jain Reviewed-by: Brian Norris Signed-off-by: Amitkumar Karwar Acked-by: Rob Herring Signed-off-by: Kalle Valo --- .../bindings/net/wireless/marvell-8xxx.txt | 65 ++++++++++++++++++++++ .../bindings/net/wireless/marvell-sd8xxx.txt | 63 --------------------- drivers/net/wireless/marvell/mwifiex/pcie.c | 24 ++++++++ drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 3 +- 4 files changed, 91 insertions(+), 64 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt delete mode 100644 Documentation/devicetree/bindings/net/wireless/marvell-sd8xxx.txt diff --git a/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt b/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt new file mode 100644 index 0000000..980b16df --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt @@ -0,0 +1,65 @@ +Marvell 8897/8997 (sd8897/sd8997/pcie8997) SDIO/PCIE devices +------ + +This node provides properties for controlling the Marvell SDIO/PCIE wireless device. +The node is expected to be specified as a child node to the SDIO/PCIE controller that +connects the device to the system. + +Required properties: + + - compatible : should be one of the following: + * "marvell,sd8897" + * "marvell,sd8997" + * "pci11ab,2b42" + * "pci1b4b,2b42" + +Optional properties: + + - marvell,caldata* : A series of properties with marvell,caldata prefix, + represent calibration data downloaded to the device during + initialization. This is an array of unsigned 8-bit values. + the properties should follow below property name and + corresponding array length: + "marvell,caldata-txpwrlimit-2g" (length = 566). + "marvell,caldata-txpwrlimit-5g-sub0" (length = 502). + "marvell,caldata-txpwrlimit-5g-sub1" (length = 688). + "marvell,caldata-txpwrlimit-5g-sub2" (length = 750). + "marvell,caldata-txpwrlimit-5g-sub3" (length = 502). + - marvell,wakeup-pin : a wakeup pin number of wifi chip which will be configured + to firmware. Firmware will wakeup the host using this pin + during suspend/resume. + - interrupt-parent: phandle of the parent interrupt controller + - interrupts : interrupt pin number to the cpu. driver will request an irq based on + this interrupt number. during system suspend, the irq will be enabled + so that the wifi chip can wakeup host platform under certain condition. + during system resume, the irq will be disabled to make sure + unnecessary interrupt is not received. + +Example: + +Tx power limit calibration data is configured in below example. +The calibration data is an array of unsigned values, the length +can vary between hw versions. +IRQ pin 38 is used as system wakeup source interrupt. wakeup pin 3 is configured +so that firmware can wakeup host using this device side pin. + +&mmc3 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + cap-power-off-card; + keep-power-in-suspend; + + #address-cells = <1>; + #size-cells = <0>; + mwifiex: wifi@1 { + compatible = "marvell,sd8897"; + reg = <1>; + interrupt-parent = <&pio>; + interrupts = <38 IRQ_TYPE_LEVEL_LOW>; + + marvell,caldata_00_txpwrlimit_2g_cfg_set = /bits/ 8 < + 0x01 0x00 0x06 0x00 0x08 0x02 0x89 0x01>; + marvell,wakeup-pin = <3>; + }; +}; diff --git a/Documentation/devicetree/bindings/net/wireless/marvell-sd8xxx.txt b/Documentation/devicetree/bindings/net/wireless/marvell-sd8xxx.txt deleted file mode 100644 index c421aba..0000000 --- a/Documentation/devicetree/bindings/net/wireless/marvell-sd8xxx.txt +++ /dev/null @@ -1,63 +0,0 @@ -Marvell 8897/8997 (sd8897/sd8997) SDIO devices ------- - -This node provides properties for controlling the marvell sdio wireless device. -The node is expected to be specified as a child node to the SDIO controller that -connects the device to the system. - -Required properties: - - - compatible : should be one of the following: - * "marvell,sd8897" - * "marvell,sd8997" - -Optional properties: - - - marvell,caldata* : A series of properties with marvell,caldata prefix, - represent calibration data downloaded to the device during - initialization. This is an array of unsigned 8-bit values. - the properties should follow below property name and - corresponding array length: - "marvell,caldata-txpwrlimit-2g" (length = 566). - "marvell,caldata-txpwrlimit-5g-sub0" (length = 502). - "marvell,caldata-txpwrlimit-5g-sub1" (length = 688). - "marvell,caldata-txpwrlimit-5g-sub2" (length = 750). - "marvell,caldata-txpwrlimit-5g-sub3" (length = 502). - - marvell,wakeup-pin : a wakeup pin number of wifi chip which will be configured - to firmware. Firmware will wakeup the host using this pin - during suspend/resume. - - interrupt-parent: phandle of the parent interrupt controller - - interrupts : interrupt pin number to the cpu. driver will request an irq based on - this interrupt number. during system suspend, the irq will be enabled - so that the wifi chip can wakeup host platform under certain condition. - during system resume, the irq will be disabled to make sure - unnecessary interrupt is not received. - -Example: - -Tx power limit calibration data is configured in below example. -The calibration data is an array of unsigned values, the length -can vary between hw versions. -IRQ pin 38 is used as system wakeup source interrupt. wakeup pin 3 is configured -so that firmware can wakeup host using this device side pin. - -&mmc3 { - status = "okay"; - vmmc-supply = <&wlan_en_reg>; - bus-width = <4>; - cap-power-off-card; - keep-power-in-suspend; - - #address-cells = <1>; - #size-cells = <0>; - mwifiex: wifi@1 { - compatible = "marvell,sd8897"; - reg = <1>; - interrupt-parent = <&pio>; - interrupts = <38 IRQ_TYPE_LEVEL_LOW>; - - marvell,caldata_00_txpwrlimit_2g_cfg_set = /bits/ 8 < - 0x01 0x00 0x06 0x00 0x08 0x02 0x89 0x01>; - marvell,wakeup-pin = <3>; - }; -}; diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 3047c1a..5737cc6 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -37,6 +37,22 @@ static struct mwifiex_if_ops pcie_ops; static struct semaphore add_remove_card_sem; +static const struct of_device_id mwifiex_pcie_of_match_table[] = { + { .compatible = "pci11ab,2b42" }, + { .compatible = "pci1b4b,2b42" }, + { } +}; + +static int mwifiex_pcie_probe_of(struct device *dev) +{ + if (!of_match_node(mwifiex_pcie_of_match_table, dev->of_node)) { + dev_err(dev, "required compatible string missing\n"); + return -EINVAL; + } + + return 0; +} + static int mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, size_t size, int flags) @@ -185,6 +201,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pcie_service_card *card; + int ret; pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", pdev->vendor, pdev->device, pdev->revision); @@ -206,6 +223,13 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, card->pcie.can_ext_scan = data->can_ext_scan; } + /* device tree node parsing and platform specific configuration*/ + if (pdev->dev.of_node) { + ret = mwifiex_pcie_probe_of(&pdev->dev); + if (ret) + return ret; + } + if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, MWIFIEX_PCIE)) { pr_err("%s failed\n", __func__); diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 0a54e21..b697b61 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -2235,7 +2235,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) * The cal-data can be read from device tree and/or * a configuration file and downloaded to firmware. */ - if (priv->adapter->iface_type == MWIFIEX_SDIO && + if ((priv->adapter->iface_type == MWIFIEX_SDIO || + priv->adapter->iface_type == MWIFIEX_PCIE) && adapter->dev->of_node) { adapter->dt_node = adapter->dev->of_node; if (of_property_read_u32(adapter->dt_node, -- cgit v1.1 From 2e02b5814217b734fed1054aac4d1e256acbe6a9 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 15 Nov 2016 19:06:02 +0530 Subject: mwifiex: Allow mwifiex early access to device structure Today all the interface drivers (usb/pcie/sdio) assign the adapter->dev in the register_dev() callback, although they have this piece of info well before hand. This patch makes the device structure available for mwifiex right at the beginning, so that it can be used for early initialization if needed. This is needed for subsequent patches in this patchset that intend to unify and consolidate some of the code that would otherwise have to be duplicated among the interface drivers (sdio, pcie, usb). Signed-off-by: Rajat Jain Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 4 +++- drivers/net/wireless/marvell/mwifiex/main.h | 4 +++- drivers/net/wireless/marvell/mwifiex/pcie.c | 4 +--- drivers/net/wireless/marvell/mwifiex/sdio.c | 5 +---- drivers/net/wireless/marvell/mwifiex/usb.c | 3 +-- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 2478ccd..dcceab2 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1567,7 +1567,8 @@ EXPORT_SYMBOL_GPL(mwifiex_do_flr); */ int mwifiex_add_card(void *card, struct semaphore *sem, - struct mwifiex_if_ops *if_ops, u8 iface_type) + struct mwifiex_if_ops *if_ops, u8 iface_type, + struct device *dev) { struct mwifiex_adapter *adapter; @@ -1579,6 +1580,7 @@ mwifiex_add_card(void *card, struct semaphore *sem, goto err_init_sw; } + adapter->dev = dev; adapter->iface_type = iface_type; adapter->card_sem = sem; diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index d61fe3a..549e1ba 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1412,7 +1412,9 @@ static inline u8 mwifiex_is_tdls_link_setup(u8 status) int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); -int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); +int mwifiex_add_card(void *card, struct semaphore *sem, + struct mwifiex_if_ops *if_ops, u8 iface_type, + struct device *dev); int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 5737cc6..0b0f9c7 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -231,7 +231,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, } if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, - MWIFIEX_PCIE)) { + MWIFIEX_PCIE, &pdev->dev)) { pr_err("%s failed\n", __func__); return -1; } @@ -2992,11 +2992,9 @@ static void mwifiex_pcie_get_fw_name(struct mwifiex_adapter *adapter) static int mwifiex_register_dev(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; - struct pci_dev *pdev = card->dev; /* save adapter pointer in card */ card->adapter = adapter; - adapter->dev = &pdev->dev; if (mwifiex_pcie_request_irq(adapter)) return -1; diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index e25a7f0..037a0f9d 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -206,7 +206,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) } ret = mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, - MWIFIEX_SDIO); + MWIFIEX_SDIO, &func->dev); if (ret) { dev_err(&func->dev, "add card failed\n"); goto err_disable; @@ -2104,9 +2104,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) return ret; } - - adapter->dev = &func->dev; - strcpy(adapter->fw_name, card->firmware); if (card->fw_dump_enh) { adapter->mem_type_mapping_tbl = generic_mem_type_map; diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 57ed834..9cc2a0c0 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -476,7 +476,7 @@ static int mwifiex_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, card); ret = mwifiex_add_card(card, &add_remove_card_sem, &usb_ops, - MWIFIEX_USB); + MWIFIEX_USB, &card->udev->dev); if (ret) { pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); usb_reset_device(udev); @@ -927,7 +927,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; card->adapter = adapter; - adapter->dev = &card->udev->dev; switch (le16_to_cpu(card->udev->descriptor.idProduct)) { case USB8997_PID_1: -- cgit v1.1 From 5e28e5fbdcf03907abea24b1bdc387333cebee38 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 15 Nov 2016 19:06:03 +0530 Subject: mwifiex: Introduce mwifiex_probe_of() to parse common properties Introduce function mwifiex_probe_of() to parse common properties. Interface drivers get to decide whether or not the device tree node was a valid one (depending on the compatible property), Lets fill "adapter->dt_node" in mwifiex_add_card(). The function mwifiex_probe_of() is currently only a place holder with the next patch adding content to it. Signed-off-by: Rajat Jain Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 12 ++++++++++++ drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index dcceab2..835d330 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1552,6 +1552,16 @@ void mwifiex_do_flr(struct mwifiex_adapter *adapter, bool prepare) } EXPORT_SYMBOL_GPL(mwifiex_do_flr); +static void mwifiex_probe_of(struct mwifiex_adapter *adapter) +{ + struct device *dev = adapter->dev; + + if (!dev->of_node) + return; + + adapter->dt_node = dev->of_node; +} + /* * This function adds the card. * @@ -1581,6 +1591,8 @@ mwifiex_add_card(void *card, struct semaphore *sem, } adapter->dev = dev; + mwifiex_probe_of(adapter); + adapter->iface_type = iface_type; adapter->card_sem = sem; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index b697b61..bcd6408 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -2235,10 +2235,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) * The cal-data can be read from device tree and/or * a configuration file and downloaded to firmware. */ - if ((priv->adapter->iface_type == MWIFIEX_SDIO || - priv->adapter->iface_type == MWIFIEX_PCIE) && - adapter->dev->of_node) { - adapter->dt_node = adapter->dev->of_node; + if (adapter->dt_node) { if (of_property_read_u32(adapter->dt_node, "marvell,wakeup-pin", &data) == 0) { -- cgit v1.1 From 853402a0082315f6c4f38feeba2c6c81a393557c Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 15 Nov 2016 19:06:04 +0530 Subject: mwifiex: Enable WoWLAN for both sdio and pcie Commit ce4f6f0c353b ("mwifiex: add platform specific wakeup interrupt support") added WoWLAN feature only for sdio. This patch moves that code to the common module so that all the interface drivers can use it for free. It enables pcie and sdio for its use currently. Signed-off-by: Rajat Jain Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 41 ++++++++++++++++ drivers/net/wireless/marvell/mwifiex/main.h | 25 ++++++++++ drivers/net/wireless/marvell/mwifiex/pcie.c | 2 + drivers/net/wireless/marvell/mwifiex/sdio.c | 72 ++--------------------------- drivers/net/wireless/marvell/mwifiex/sdio.h | 8 ---- 5 files changed, 73 insertions(+), 75 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 835d330..948f5c2 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1552,14 +1552,55 @@ void mwifiex_do_flr(struct mwifiex_adapter *adapter, bool prepare) } EXPORT_SYMBOL_GPL(mwifiex_do_flr); +static irqreturn_t mwifiex_irq_wakeup_handler(int irq, void *priv) +{ + struct mwifiex_adapter *adapter = priv; + + if (adapter->irq_wakeup >= 0) { + dev_dbg(adapter->dev, "%s: wake by wifi", __func__); + adapter->wake_by_wifi = true; + disable_irq_nosync(irq); + } + + /* Notify PM core we are wakeup source */ + pm_wakeup_event(adapter->dev, 0); + + return IRQ_HANDLED; +} + static void mwifiex_probe_of(struct mwifiex_adapter *adapter) { + int ret; struct device *dev = adapter->dev; if (!dev->of_node) return; adapter->dt_node = dev->of_node; + adapter->irq_wakeup = irq_of_parse_and_map(adapter->dt_node, 0); + if (!adapter->irq_wakeup) { + dev_info(dev, "fail to parse irq_wakeup from device tree\n"); + return; + } + + ret = devm_request_irq(dev, adapter->irq_wakeup, + mwifiex_irq_wakeup_handler, IRQF_TRIGGER_LOW, + "wifi_wake", adapter); + if (ret) { + dev_err(dev, "Failed to request irq_wakeup %d (%d)\n", + adapter->irq_wakeup, ret); + goto err_exit; + } + + disable_irq(adapter->irq_wakeup); + if (device_init_wakeup(dev, true)) { + dev_err(dev, "fail to init wakeup for mwifiex\n"); + goto err_exit; + } + return; + +err_exit: + adapter->irq_wakeup = 0; } /* diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 549e1ba..ae5afe5 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1011,6 +1011,10 @@ struct mwifiex_adapter { bool usb_mc_setup; struct cfg80211_wowlan_nd_info *nd_info; struct ieee80211_regdomain *regd; + + /* Wake-on-WLAN (WoWLAN) */ + int irq_wakeup; + bool wake_by_wifi; }; void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); @@ -1410,6 +1414,27 @@ static inline u8 mwifiex_is_tdls_link_setup(u8 status) return false; } +/* Disable platform specific wakeup interrupt */ +static inline void mwifiex_disable_wake(struct mwifiex_adapter *adapter) +{ + if (adapter->irq_wakeup >= 0) { + disable_irq_wake(adapter->irq_wakeup); + if (!adapter->wake_by_wifi) + disable_irq(adapter->irq_wakeup); + } +} + +/* Enable platform specific wakeup interrupt */ +static inline void mwifiex_enable_wake(struct mwifiex_adapter *adapter) +{ + /* Enable platform specific wakeup interrupt */ + if (adapter->irq_wakeup >= 0) { + adapter->wake_by_wifi = false; + enable_irq(adapter->irq_wakeup); + enable_irq_wake(adapter->irq_wakeup); + } +} + int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); int mwifiex_add_card(void *card, struct semaphore *sem, diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 0b0f9c7..2ab1d17 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -131,6 +131,7 @@ static int mwifiex_pcie_suspend(struct device *dev) } adapter = card->adapter; + mwifiex_enable_wake(adapter); /* Enable the Host Sleep */ if (!mwifiex_enable_hs(adapter)) { @@ -186,6 +187,7 @@ static int mwifiex_pcie_resume(struct device *dev) mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), MWIFIEX_ASYNC_CMD); + mwifiex_disable_wake(adapter); return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 037a0f9d..f410cf5 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -79,67 +79,18 @@ static const struct of_device_id mwifiex_sdio_of_match_table[] = { { } }; -static irqreturn_t mwifiex_wake_irq_wifi(int irq, void *priv) -{ - struct mwifiex_plt_wake_cfg *cfg = priv; - - if (cfg->irq_wifi >= 0) { - pr_info("%s: wake by wifi", __func__); - cfg->wake_by_wifi = true; - disable_irq_nosync(irq); - } - - /* Notify PM core we are wakeup source */ - pm_wakeup_event(cfg->dev, 0); - - return IRQ_HANDLED; -} - /* This function parse device tree node using mmc subnode devicetree API. * The device node is saved in card->plt_of_node. * if the device tree node exist and include interrupts attributes, this * function will also request platform specific wakeup interrupt. */ -static int mwifiex_sdio_probe_of(struct device *dev, struct sdio_mmc_card *card) +static int mwifiex_sdio_probe_of(struct device *dev) { - struct mwifiex_plt_wake_cfg *cfg; - int ret; - if (!of_match_node(mwifiex_sdio_of_match_table, dev->of_node)) { dev_err(dev, "required compatible string missing\n"); return -EINVAL; } - card->plt_of_node = dev->of_node; - card->plt_wake_cfg = devm_kzalloc(dev, sizeof(*card->plt_wake_cfg), - GFP_KERNEL); - cfg = card->plt_wake_cfg; - if (cfg && card->plt_of_node) { - cfg->dev = dev; - cfg->irq_wifi = irq_of_parse_and_map(card->plt_of_node, 0); - if (!cfg->irq_wifi) { - dev_dbg(dev, - "fail to parse irq_wifi from device tree\n"); - } else { - ret = devm_request_irq(dev, cfg->irq_wifi, - mwifiex_wake_irq_wifi, - IRQF_TRIGGER_LOW, - "wifi_wake", cfg); - if (ret) { - dev_dbg(dev, - "Failed to request irq_wifi %d (%d)\n", - cfg->irq_wifi, ret); - card->plt_wake_cfg = NULL; - return 0; - } - disable_irq(cfg->irq_wifi); - } - } - - ret = device_init_wakeup(dev, true); - if (ret) - dev_err(dev, "fail to init wakeup for mwifiex"); - return 0; } @@ -198,11 +149,9 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) /* device tree node parsing and platform specific configuration*/ if (func->dev.of_node) { - ret = mwifiex_sdio_probe_of(&func->dev, card); - if (ret) { - dev_err(&func->dev, "SDIO dt node parse failed\n"); + ret = mwifiex_sdio_probe_of(&func->dev); + if (ret) goto err_disable; - } } ret = mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, @@ -265,12 +214,7 @@ static int mwifiex_sdio_resume(struct device *dev) mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), MWIFIEX_SYNC_CMD); - /* Disable platform specific wakeup interrupt */ - if (card->plt_wake_cfg && card->plt_wake_cfg->irq_wifi >= 0) { - disable_irq_wake(card->plt_wake_cfg->irq_wifi); - if (!card->plt_wake_cfg->wake_by_wifi) - disable_irq(card->plt_wake_cfg->irq_wifi); - } + mwifiex_disable_wake(adapter); return 0; } @@ -350,13 +294,7 @@ static int mwifiex_sdio_suspend(struct device *dev) } adapter = card->adapter; - - /* Enable platform specific wakeup interrupt */ - if (card->plt_wake_cfg && card->plt_wake_cfg->irq_wifi >= 0) { - card->plt_wake_cfg->wake_by_wifi = false; - enable_irq(card->plt_wake_cfg->irq_wifi); - enable_irq_wake(card->plt_wake_cfg->irq_wifi); - } + mwifiex_enable_wake(adapter); /* Enable the Host Sleep */ if (!mwifiex_enable_hs(adapter)) { diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h index 07cdd23..b9fbc5c 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.h +++ b/drivers/net/wireless/marvell/mwifiex/sdio.h @@ -154,12 +154,6 @@ a->mpa_rx.start_port = 0; \ } while (0) -struct mwifiex_plt_wake_cfg { - struct device *dev; - int irq_wifi; - bool wake_by_wifi; -}; - /* data structure for SDIO MPA TX */ struct mwifiex_sdio_mpa_tx { /* multiport tx aggregation buffer pointer */ @@ -243,8 +237,6 @@ struct mwifiex_sdio_card_reg { struct sdio_mmc_card { struct sdio_func *func; struct mwifiex_adapter *adapter; - struct device_node *plt_of_node; - struct mwifiex_plt_wake_cfg *plt_wake_cfg; const char *firmware; const struct mwifiex_sdio_card_reg *reg; -- cgit v1.1 From eb2428fb1a21e7c813987118b0296135ddec0f24 Mon Sep 17 00:00:00 2001 From: Shengzhen Li Date: Fri, 18 Nov 2016 19:30:24 +0530 Subject: mwifiex: check tx_hw_pending before downloading sleep confirm We may get SLEEP event from firmware even if TXDone interrupt for last Tx packet is still pending. In this case, we may end up accessing PCIe memory for handling TXDone after power save handshake is completed. This causes kernel crash with external abort. This patch will only allow downloading sleep confirm when no tx done interrupt is pending in the hardware. Signed-off-by: Cathy Luo Signed-off-by: Shengzhen Li Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Reviewed-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cmdevt.c | 5 +++-- drivers/net/wireless/marvell/mwifiex/init.c | 1 + drivers/net/wireless/marvell/mwifiex/main.h | 1 + drivers/net/wireless/marvell/mwifiex/pcie.c | 5 +++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 5347728..25a7475 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -1118,13 +1118,14 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) { - if (!adapter->cmd_sent && + if (!adapter->cmd_sent && !atomic_read(&adapter->tx_hw_pending) && !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) mwifiex_dnld_sleep_confirm_cmd(adapter); else mwifiex_dbg(adapter, CMD, - "cmd: Delay Sleep Confirm (%s%s%s)\n", + "cmd: Delay Sleep Confirm (%s%s%s%s)\n", (adapter->cmd_sent) ? "D" : "", + atomic_read(&adapter->tx_hw_pending) ? "T" : "", (adapter->curr_cmd) ? "C" : "", (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); } diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index 82839d9..b36cb3f 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -270,6 +270,7 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) adapter->adhoc_11n_enabled = false; mwifiex_wmm_init(adapter); + atomic_set(&adapter->tx_hw_pending, 0); sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) adapter->sleep_cfm->data; diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index ae5afe5..904a2ed 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -857,6 +857,7 @@ struct mwifiex_adapter { atomic_t rx_pending; atomic_t tx_pending; atomic_t cmd_pending; + atomic_t tx_hw_pending; struct workqueue_struct *workqueue; struct work_struct main_work; struct workqueue_struct *rx_workqueue; diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 2ab1d17..cfb45ef 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -516,6 +516,7 @@ static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) } } + atomic_set(&adapter->tx_hw_pending, 0); return 0; } @@ -715,6 +716,7 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) card->tx_buf_list[i] = NULL; } + atomic_set(&adapter->tx_hw_pending, 0); return; } @@ -1152,6 +1154,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) -1); else mwifiex_write_data_complete(adapter, skb, 0, 0); + atomic_dec(&adapter->tx_hw_pending); } card->tx_buf_list[wrdoneidx] = NULL; @@ -1244,6 +1247,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); card->tx_buf_list[wrindx] = skb; + atomic_inc(&adapter->tx_hw_pending); if (reg->pfu_enabled) { desc2 = card->txbd_ring[wrindx]; @@ -1321,6 +1325,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, done_unmap: mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); card->tx_buf_list[wrindx] = NULL; + atomic_dec(&adapter->tx_hw_pending); if (reg->pfu_enabled) memset(desc2, 0, sizeof(*desc2)); else -- cgit v1.1 From 6712076883ca49532b4e15216fb69cde69a5a919 Mon Sep 17 00:00:00 2001 From: Shengzhen Li Date: Fri, 18 Nov 2016 19:30:25 +0530 Subject: mwifiex: complete blocked power save handshake in main process Power save handshake with firmware might be blocked by on-going data transfer. this patch check the PS status in main process and complete previous blocked PS handshake. this patch also remove redudant check before call mwifiex_check_ps_cond function. Signed-off-by: Cathy Luo Signed-off-by: Shengzhen Li Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 948f5c2..9e7acb4 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -308,6 +308,9 @@ process_start: /* We have tried to wakeup the card already */ if (adapter->pm_wakeup_fw_try) break; + if (adapter->ps_state == PS_STATE_PRE_SLEEP) + mwifiex_check_ps_cond(adapter); + if (adapter->ps_state != PS_STATE_AWAKE) break; if (adapter->tx_lock_flag) { @@ -355,10 +358,8 @@ process_start: /* Check if we need to confirm Sleep Request received previously */ - if (adapter->ps_state == PS_STATE_PRE_SLEEP) { - if (!adapter->cmd_sent && !adapter->curr_cmd) - mwifiex_check_ps_cond(adapter); - } + if (adapter->ps_state == PS_STATE_PRE_SLEEP) + mwifiex_check_ps_cond(adapter); /* * The ps_state may have been changed during processing of * Sleep Request event. -- cgit v1.1 From 4a79aa17d53ea8d5fa4acdaed487a786a185936a Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 19:30:26 +0530 Subject: mwifiex: resolve races between async FW init (failure) and device removal It's possible for the FW init sequence to fail, which will trigger a device cleanup sequence in mwifiex_fw_dpc(). This sequence can race with device suspend() or remove() (e.g., reboot or unbind), and can trigger use-after-free issues. Currently, this driver attempts (poorly) to synchronize remove() using a semaphore, but it doesn't protect some of the critical sections properly. Particularly, we grab a pointer to the adapter struct (card->adapter) without checking if it's being freed or not. We later do a NULL check on the adapter, but that doesn't work if the adapter was freed. Also note that the PCIe interface driver doesn't ever set card->adapter to NULL, so even if we get the synchronization right, we still might try to redo the cleanup in ->remove(), even if the FW init failure sequence already did it. This patch replaces the static semaphore with a per-device completion struct, and uses that completion to synchronize the remove() thread with the mwifiex_fw_dpc(). A future patch will utilize this completion to synchronize the suspend() thread as well. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 47 +++++++++++------------------ drivers/net/wireless/marvell/mwifiex/main.h | 11 +++++-- drivers/net/wireless/marvell/mwifiex/pcie.c | 18 +++++------ drivers/net/wireless/marvell/mwifiex/pcie.h | 2 ++ drivers/net/wireless/marvell/mwifiex/sdio.c | 18 +++++------ drivers/net/wireless/marvell/mwifiex/sdio.h | 2 ++ drivers/net/wireless/marvell/mwifiex/usb.c | 23 ++++++-------- drivers/net/wireless/marvell/mwifiex/usb.h | 2 ++ 8 files changed, 55 insertions(+), 68 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 9e7acb4..eac44fe 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -521,9 +521,9 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) struct mwifiex_private *priv; struct mwifiex_adapter *adapter = context; struct mwifiex_fw_image fw; - struct semaphore *sem = adapter->card_sem; bool init_failed = false; struct wireless_dev *wdev; + struct completion *fw_done = adapter->fw_done; if (!firmware) { mwifiex_dbg(adapter, ERROR, @@ -670,7 +670,8 @@ done: } if (init_failed) mwifiex_free_adapter(adapter); - up(sem); + /* Tell all current and future waiters we're finished */ + complete_all(fw_done); return; } @@ -1365,7 +1366,7 @@ static void mwifiex_main_work_queue(struct work_struct *work) * code is extracted from mwifiex_remove_card() */ static int -mwifiex_shutdown_sw(struct mwifiex_adapter *adapter, struct semaphore *sem) +mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; int i; @@ -1373,8 +1374,9 @@ mwifiex_shutdown_sw(struct mwifiex_adapter *adapter, struct semaphore *sem) if (!adapter) goto exit_return; - if (down_interruptible(sem)) - goto exit_sem_err; + wait_for_completion(adapter->fw_done); + /* Caller should ensure we aren't suspending while this happens */ + reinit_completion(adapter->fw_done); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); mwifiex_deauthenticate(priv, NULL); @@ -1431,8 +1433,6 @@ mwifiex_shutdown_sw(struct mwifiex_adapter *adapter, struct semaphore *sem) rtnl_unlock(); } - up(sem); -exit_sem_err: mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); exit_return: return 0; @@ -1442,21 +1442,18 @@ exit_return: * code is extracted from mwifiex_add_card() */ static int -mwifiex_reinit_sw(struct mwifiex_adapter *adapter, struct semaphore *sem, +mwifiex_reinit_sw(struct mwifiex_adapter *adapter, struct completion *fw_done, struct mwifiex_if_ops *if_ops, u8 iface_type) { char fw_name[32]; struct pcie_service_card *card = adapter->card; - if (down_interruptible(sem)) - goto exit_sem_err; - mwifiex_init_lock_list(adapter); if (adapter->if_ops.up_dev) adapter->if_ops.up_dev(adapter); adapter->iface_type = iface_type; - adapter->card_sem = sem; + adapter->fw_done = fw_done; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; adapter->surprise_removed = false; @@ -1507,7 +1504,8 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter, struct semaphore *sem, } strcpy(adapter->fw_name, fw_name); mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); - up(sem); + + complete_all(adapter->fw_done); return 0; err_init_fw: @@ -1527,8 +1525,7 @@ err_init_fw: err_kmalloc: mwifiex_terminate_workqueue(adapter); adapter->surprise_removed = true; - up(sem); -exit_sem_err: + complete_all(adapter->fw_done); mwifiex_dbg(adapter, INFO, "%s, error\n", __func__); return -1; @@ -1543,12 +1540,12 @@ void mwifiex_do_flr(struct mwifiex_adapter *adapter, bool prepare) struct mwifiex_if_ops if_ops; if (!prepare) { - mwifiex_reinit_sw(adapter, adapter->card_sem, &if_ops, + mwifiex_reinit_sw(adapter, adapter->fw_done, &if_ops, adapter->iface_type); } else { memcpy(&if_ops, &adapter->if_ops, sizeof(struct mwifiex_if_ops)); - mwifiex_shutdown_sw(adapter, adapter->card_sem); + mwifiex_shutdown_sw(adapter); } } EXPORT_SYMBOL_GPL(mwifiex_do_flr); @@ -1618,15 +1615,12 @@ err_exit: * - Add logical interfaces */ int -mwifiex_add_card(void *card, struct semaphore *sem, +mwifiex_add_card(void *card, struct completion *fw_done, struct mwifiex_if_ops *if_ops, u8 iface_type, struct device *dev) { struct mwifiex_adapter *adapter; - if (down_interruptible(sem)) - goto exit_sem_err; - if (mwifiex_register(card, if_ops, (void **)&adapter)) { pr_err("%s: software init failed\n", __func__); goto err_init_sw; @@ -1636,7 +1630,7 @@ mwifiex_add_card(void *card, struct semaphore *sem, mwifiex_probe_of(adapter); adapter->iface_type = iface_type; - adapter->card_sem = sem; + adapter->fw_done = fw_done; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; adapter->surprise_removed = false; @@ -1705,9 +1699,7 @@ err_kmalloc: mwifiex_free_adapter(adapter); err_init_sw: - up(sem); -exit_sem_err: return -1; } EXPORT_SYMBOL_GPL(mwifiex_add_card); @@ -1723,14 +1715,11 @@ EXPORT_SYMBOL_GPL(mwifiex_add_card); * - Unregister the device * - Free the adapter structure */ -int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) +int mwifiex_remove_card(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv = NULL; int i; - if (down_trylock(sem)) - goto exit_sem_err; - if (!adapter) goto exit_remove; @@ -1800,8 +1789,6 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) mwifiex_free_adapter(adapter); exit_remove: - up(sem); -exit_sem_err: return 0; } EXPORT_SYMBOL_GPL(mwifiex_remove_card); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 904a2ed..cf4c780 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -20,6 +20,7 @@ #ifndef _MWIFIEX_MAIN_H_ #define _MWIFIEX_MAIN_H_ +#include #include #include #include @@ -985,7 +986,10 @@ struct mwifiex_adapter { u32 usr_dot_11ac_mcs_support; atomic_t pending_bridged_pkts; - struct semaphore *card_sem; + + /* For synchronizing FW initialization with device lifecycle. */ + struct completion *fw_done; + bool ext_scan; u8 fw_api_ver; u8 key_api_major_ver, key_api_minor_ver; @@ -1438,10 +1442,11 @@ static inline void mwifiex_enable_wake(struct mwifiex_adapter *adapter) int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); -int mwifiex_add_card(void *card, struct semaphore *sem, + +int mwifiex_add_card(void *card, struct completion *fw_done, struct mwifiex_if_ops *if_ops, u8 iface_type, struct device *dev); -int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); +int mwifiex_remove_card(struct mwifiex_adapter *adapter); void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, int maxlen); diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index cfb45ef..547806f 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -35,8 +35,6 @@ static u8 user_rmmod; static struct mwifiex_if_ops pcie_ops; -static struct semaphore add_remove_card_sem; - static const struct of_device_id mwifiex_pcie_of_match_table[] = { { .compatible = "pci11ab,2b42" }, { .compatible = "pci1b4b,2b42" }, @@ -212,6 +210,8 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, if (!card) return -ENOMEM; + init_completion(&card->fw_done); + card->dev = pdev; if (ent->driver_data) { @@ -232,7 +232,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, return ret; } - if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, + if (mwifiex_add_card(card, &card->fw_done, &pcie_ops, MWIFIEX_PCIE, &pdev->dev)) { pr_err("%s failed\n", __func__); return -1; @@ -254,6 +254,8 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) if (!card) return; + wait_for_completion(&card->fw_done); + adapter = card->adapter; if (!adapter || !adapter->priv_num) return; @@ -273,7 +275,7 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); } - mwifiex_remove_card(card->adapter, &add_remove_card_sem); + mwifiex_remove_card(adapter); } static void mwifiex_pcie_shutdown(struct pci_dev *pdev) @@ -3175,8 +3177,7 @@ static struct mwifiex_if_ops pcie_ops = { /* * This function initializes the PCIE driver module. * - * This initiates the semaphore and registers the device with - * PCIE bus. + * This registers the device with PCIE bus. */ static int mwifiex_pcie_init_module(void) { @@ -3184,8 +3185,6 @@ static int mwifiex_pcie_init_module(void) pr_debug("Marvell PCIe Driver\n"); - sema_init(&add_remove_card_sem, 1); - /* Clear the flag in case user removes the card. */ user_rmmod = 0; @@ -3209,9 +3208,6 @@ static int mwifiex_pcie_init_module(void) */ static void mwifiex_pcie_cleanup_module(void) { - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - /* Set the flag as user is removing this module. */ user_rmmod = 1; diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index 46f99ca..ae3365d 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -22,6 +22,7 @@ #ifndef _MWIFIEX_PCIE_H #define _MWIFIEX_PCIE_H +#include #include #include @@ -345,6 +346,7 @@ struct pcie_service_card { struct pci_dev *dev; struct mwifiex_adapter *adapter; struct mwifiex_pcie_device pcie; + struct completion fw_done; u8 txbd_flush; u32 txbd_wrptr; diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index f410cf5..5312ffb 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -49,8 +49,6 @@ static u8 user_rmmod; static struct mwifiex_if_ops sdio_ops; static unsigned long iface_work_flags; -static struct semaphore add_remove_card_sem; - static struct memory_type_mapping generic_mem_type_map[] = { {"DUMP", NULL, 0, 0xDD}, }; @@ -115,6 +113,8 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) if (!card) return -ENOMEM; + init_completion(&card->fw_done); + card->func = func; card->device_id = id; @@ -154,7 +154,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) goto err_disable; } - ret = mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, + ret = mwifiex_add_card(card, &card->fw_done, &sdio_ops, MWIFIEX_SDIO, &func->dev); if (ret) { dev_err(&func->dev, "add card failed\n"); @@ -235,6 +235,8 @@ mwifiex_sdio_remove(struct sdio_func *func) if (!card) return; + wait_for_completion(&card->fw_done); + adapter = card->adapter; if (!adapter || !adapter->priv_num) return; @@ -252,7 +254,7 @@ mwifiex_sdio_remove(struct sdio_func *func) mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); } - mwifiex_remove_card(card->adapter, &add_remove_card_sem); + mwifiex_remove_card(adapter); } /* @@ -2714,14 +2716,11 @@ static struct mwifiex_if_ops sdio_ops = { /* * This function initializes the SDIO driver. * - * This initiates the semaphore and registers the device with - * SDIO bus. + * This registers the device with SDIO bus. */ static int mwifiex_sdio_init_module(void) { - sema_init(&add_remove_card_sem, 1); - /* Clear the flag in case user removes the card. */ user_rmmod = 0; @@ -2740,9 +2739,6 @@ mwifiex_sdio_init_module(void) static void mwifiex_sdio_cleanup_module(void) { - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - /* Set the flag as user is removing this module. */ user_rmmod = 1; cancel_work_sync(&sdio_work); diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h index b9fbc5c..cdbf3a3a 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.h +++ b/drivers/net/wireless/marvell/mwifiex/sdio.h @@ -21,6 +21,7 @@ #define _MWIFIEX_SDIO_H +#include #include #include #include @@ -238,6 +239,7 @@ struct sdio_mmc_card { struct sdio_func *func; struct mwifiex_adapter *adapter; + struct completion fw_done; const char *firmware; const struct mwifiex_sdio_card_reg *reg; u8 max_ports; diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 9cc2a0c0..63a755c 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -24,7 +24,6 @@ static u8 user_rmmod; static struct mwifiex_if_ops usb_ops; -static struct semaphore add_remove_card_sem; static struct usb_device_id mwifiex_usb_table[] = { /* 8766 */ @@ -386,6 +385,8 @@ static int mwifiex_usb_probe(struct usb_interface *intf, if (!card) return -ENOMEM; + init_completion(&card->fw_done); + id_vendor = le16_to_cpu(udev->descriptor.idVendor); id_product = le16_to_cpu(udev->descriptor.idProduct); bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); @@ -475,7 +476,7 @@ static int mwifiex_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, card); - ret = mwifiex_add_card(card, &add_remove_card_sem, &usb_ops, + ret = mwifiex_add_card(card, &card->fw_done, &usb_ops, MWIFIEX_USB, &card->udev->dev); if (ret) { pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); @@ -601,13 +602,15 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); + if (!card) { + dev_err(&intf->dev, "%s: card is NULL\n", __func__); return; } + wait_for_completion(&card->fw_done); + adapter = card->adapter; - if (!adapter->priv_num) + if (!adapter || !adapter->priv_num) return; if (user_rmmod && !adapter->mfg_mode) { @@ -627,7 +630,7 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) mwifiex_dbg(adapter, FATAL, "%s: removing card\n", __func__); - mwifiex_remove_card(adapter, &add_remove_card_sem); + mwifiex_remove_card(adapter); usb_put_dev(interface_to_usbdev(intf)); } @@ -1200,8 +1203,7 @@ static struct mwifiex_if_ops usb_ops = { /* This function initializes the USB driver module. * - * This initiates the semaphore and registers the device with - * USB bus. + * This registers the device with USB bus. */ static int mwifiex_usb_init_module(void) { @@ -1209,8 +1211,6 @@ static int mwifiex_usb_init_module(void) pr_debug("Marvell USB8797 Driver\n"); - sema_init(&add_remove_card_sem, 1); - ret = usb_register(&mwifiex_usb_driver); if (ret) pr_err("Driver register failed!\n"); @@ -1230,9 +1230,6 @@ static int mwifiex_usb_init_module(void) */ static void mwifiex_usb_cleanup_module(void) { - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - /* set the flag as user is removing this module */ user_rmmod = 1; diff --git a/drivers/net/wireless/marvell/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h index 30e8eb8c..e5f204e 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.h +++ b/drivers/net/wireless/marvell/mwifiex/usb.h @@ -20,6 +20,7 @@ #ifndef _MWIFIEX_USB_H #define _MWIFIEX_USB_H +#include #include #define USB8XXX_VID 0x1286 @@ -75,6 +76,7 @@ struct usb_card_rec { struct mwifiex_adapter *adapter; struct usb_device *udev; struct usb_interface *intf; + struct completion fw_done; u8 rx_cmd_ep; struct urb_context rx_cmd; atomic_t rx_cmd_urb_pending; -- cgit v1.1 From a1beec4b2c6f74365e469df08270a3b847006bec Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 18 Nov 2016 19:30:27 +0530 Subject: mwifiex: remove redundant pdev check in suspend/resume handlers to_pci_dev() would just do struct offset arithmetic on struct device to get 'pdev' pointer. We never get NULL pdev pointer. Signed-off-by: Amitkumar Karwar Tested-by: Xinming Hu Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 547806f..86ae084 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -117,14 +117,9 @@ static int mwifiex_pcie_suspend(struct device *dev) struct pcie_service_card *card; struct pci_dev *pdev = to_pci_dev(dev); - if (pdev) { - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("Card or adapter structure is not valid\n"); - return 0; - } - } else { - pr_err("PCIE device is not specified\n"); + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_err("Card or adapter structure is not valid\n"); return 0; } @@ -162,14 +157,9 @@ static int mwifiex_pcie_resume(struct device *dev) struct pcie_service_card *card; struct pci_dev *pdev = to_pci_dev(dev); - if (pdev) { - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("Card or adapter structure is not valid\n"); - return 0; - } - } else { - pr_err("PCIE device is not specified\n"); + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + dev_err(dev, "Card or adapter structure is not valid\n"); return 0; } -- cgit v1.1 From 7ccdf72f9128104fb1dd1cf46856562380ef8f44 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 19:30:28 +0530 Subject: mwifiex: don't pretend to resume while remove()'ing The device core will not allow suspend() to race with remove(). Signed-off-by: Brian Norris Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 5 ----- drivers/net/wireless/marvell/mwifiex/sdio.c | 3 --- drivers/net/wireless/marvell/mwifiex/usb.c | 5 ----- 3 files changed, 13 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 86ae084..cecf1a0 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -251,11 +251,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) return; if (user_rmmod && !adapter->mfg_mode) { -#ifdef CONFIG_PM_SLEEP - if (adapter->is_suspended) - mwifiex_pcie_resume(&pdev->dev); -#endif - mwifiex_deauthenticate_all(adapter); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 5312ffb..2f5b01f 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -244,9 +244,6 @@ mwifiex_sdio_remove(struct sdio_func *func) mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); if (user_rmmod && !adapter->mfg_mode) { - if (adapter->is_suspended) - mwifiex_sdio_resume(adapter->dev); - mwifiex_deauthenticate_all(adapter); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 63a755c..45c9430a 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -614,11 +614,6 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) return; if (user_rmmod && !adapter->mfg_mode) { -#ifdef CONFIG_PM - if (adapter->is_suspended) - mwifiex_usb_resume(intf); -#endif - mwifiex_deauthenticate_all(adapter); mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, -- cgit v1.1 From b42dbb27e3260badaf540351889a1ebd6797575b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 19:30:29 +0530 Subject: mwifiex: resolve suspend() race with async FW init failure Signed-off-by: Brian Norris Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 12 ++++++++++-- drivers/net/wireless/marvell/mwifiex/sdio.c | 12 ++++++++++-- drivers/net/wireless/marvell/mwifiex/usb.c | 12 ++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index cecf1a0..6176cb78 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -118,12 +118,20 @@ static int mwifiex_pcie_suspend(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("Card or adapter structure is not valid\n"); + if (!card) { + dev_err(dev, "card structure is not valid\n"); return 0; } + /* Might still be loading firmware */ + wait_for_completion(&card->fw_done); + adapter = card->adapter; + if (!adapter) { + dev_err(dev, "adapter is not valid\n"); + return 0; + } + mwifiex_enable_wake(adapter); /* Enable the Host Sleep */ diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 2f5b01f..5077d56 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -283,8 +283,8 @@ static int mwifiex_sdio_suspend(struct device *dev) } card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_err("suspend: invalid card or adapter\n"); + if (!card) { + dev_err(dev, "suspend: invalid card\n"); return 0; } } else { @@ -292,7 +292,15 @@ static int mwifiex_sdio_suspend(struct device *dev) return 0; } + /* Might still be loading firmware */ + wait_for_completion(&card->fw_done); + adapter = card->adapter; + if (!adapter) { + dev_err(dev, "adapter is not valid\n"); + return 0; + } + mwifiex_enable_wake(adapter); /* Enable the Host Sleep */ diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 45c9430a..64a4020 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -503,11 +503,19 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) struct usb_tx_data_port *port; int i, j; - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); + if (!card) { + dev_err(&intf->dev, "%s: card is NULL\n", __func__); return 0; } + + /* Might still be loading firmware */ + wait_for_completion(&card->fw_done); + adapter = card->adapter; + if (!adapter) { + dev_err(&intf->dev, "card is not valid\n"); + return 0; + } if (unlikely(adapter->is_suspended)) mwifiex_dbg(adapter, WARN, -- cgit v1.1 From bcf28a2f284643060cdee550290ca2c2a559aa8e Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 18 Nov 2016 19:30:30 +0530 Subject: mwifiex: reset card->adapter during device unregister card->adapter gets initialized in mwifiex_register_dev(). As it's not cleared in mwifiex_unregister_dev(), we may end up accessing the memory which is already free in below scenario. Scenario: Driver initialization is failed due to incorrect firmware or some other reason. Meanwhile device reboot/unload occurs. This is safe, now that we've properly synchronized suspend() and remove() with the FW initialization thread; now that code can simply check for 'card->adapter == NULL' and exit safely. Signed-off-by: Xinming Hu Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 1 + drivers/net/wireless/marvell/mwifiex/sdio.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 6176cb78..f6d28d9 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -3042,6 +3042,7 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) if (card->msi_enable) pci_disable_msi(pdev); } + card->adapter = NULL; } } diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 5077d56..69542f8 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -2017,6 +2017,7 @@ mwifiex_unregister_dev(struct mwifiex_adapter *adapter) struct sdio_mmc_card *card = adapter->card; if (adapter->card) { + card->adapter = NULL; sdio_claim_host(card->func); sdio_disable_func(card->func); sdio_release_host(card->func); -- cgit v1.1 From 58b7033551cd0137f879c5828f84f54ecb0f9090 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 19:30:31 +0530 Subject: mwifiex: usb: handle HS failures SDIO and PCIe drivers handle this. Let's imitate it. Signed-off-by: Brian Norris Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/usb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 64a4020..55e3a01 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -521,7 +521,14 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) mwifiex_dbg(adapter, WARN, "Device already suspended\n"); - mwifiex_enable_hs(adapter); + /* Enable the Host Sleep */ + if (!mwifiex_enable_hs(adapter)) { + mwifiex_dbg(adapter, ERROR, + "cmd: failed to suspend\n"); + adapter->hs_enabling = false; + return -EFAULT; + } + /* 'is_suspended' flag indicates device is suspended. * It must be set here before the usb_kill_urb() calls. Reason -- cgit v1.1 From 6caf34cb3a92bbd5df6844cb3248c3858797452f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 19:30:32 +0530 Subject: mwifiex: sdio: don't check for NULL sdio_func sdio_func is retrieved via container_of() and should never be NULL. Checking for NULL just makes the logic more confusing than necessary. Stop doing that. Signed-off-by: Brian Norris Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 40 +++++++++++------------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 69542f8..0d00db5 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -188,15 +188,10 @@ static int mwifiex_sdio_resume(struct device *dev) struct mwifiex_adapter *adapter; mmc_pm_flag_t pm_flag = 0; - if (func) { - pm_flag = sdio_get_host_pm_caps(func); - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_err("resume: invalid card or adapter\n"); - return 0; - } - } else { - pr_err("resume: sdio_func is not specified\n"); + pm_flag = sdio_get_host_pm_caps(func); + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + dev_err(dev, "resume: invalid card or adapter\n"); return 0; } @@ -272,23 +267,18 @@ static int mwifiex_sdio_suspend(struct device *dev) mmc_pm_flag_t pm_flag = 0; int ret = 0; - if (func) { - pm_flag = sdio_get_host_pm_caps(func); - pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", - sdio_func_id(func), pm_flag); - if (!(pm_flag & MMC_PM_KEEP_POWER)) { - pr_err("%s: cannot remain alive while host is" - " suspended\n", sdio_func_id(func)); - return -ENOSYS; - } + pm_flag = sdio_get_host_pm_caps(func); + pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", + sdio_func_id(func), pm_flag); + if (!(pm_flag & MMC_PM_KEEP_POWER)) { + dev_err(dev, "%s: cannot remain alive while host is" + " suspended\n", sdio_func_id(func)); + return -ENOSYS; + } - card = sdio_get_drvdata(func); - if (!card) { - dev_err(dev, "suspend: invalid card\n"); - return 0; - } - } else { - pr_err("suspend: sdio_func is not specified\n"); + card = sdio_get_drvdata(func); + if (!card) { + dev_err(dev, "suspend: invalid card\n"); return 0; } -- cgit v1.1 From e98fb11ffa0ab8fd3e99a0d8ff7c2bbb7418a834 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 19:30:33 +0530 Subject: mwifiex: stop checking for NULL drvata/intfdata These are never NULL, so stop making people think they might be. I don't change this for SDIO because SDIO has a racy card-reset handler that reallocates this struct. I'd rather not touch that mess right now. Signed-off-by: Brian Norris Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 14 +++++--------- drivers/net/wireless/marvell/mwifiex/usb.c | 15 +++------------ 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index f6d28d9..98299a1 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -118,10 +118,6 @@ static int mwifiex_pcie_suspend(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); card = pci_get_drvdata(pdev); - if (!card) { - dev_err(dev, "card structure is not valid\n"); - return 0; - } /* Might still be loading firmware */ wait_for_completion(&card->fw_done); @@ -166,8 +162,9 @@ static int mwifiex_pcie_resume(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - dev_err(dev, "Card or adapter structure is not valid\n"); + + if (!card->adapter) { + dev_err(dev, "adapter structure is not valid\n"); return 0; } @@ -249,8 +246,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) struct mwifiex_private *priv; card = pci_get_drvdata(pdev); - if (!card) - return; wait_for_completion(&card->fw_done); @@ -2243,7 +2238,8 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) } card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { + + if (!card->adapter) { pr_err("info: %s: card=%p adapter=%p\n", __func__, card, card ? card->adapter : NULL); goto exit; diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 55e3a01..c3f696a 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -503,11 +503,6 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) struct usb_tx_data_port *port; int i, j; - if (!card) { - dev_err(&intf->dev, "%s: card is NULL\n", __func__); - return 0; - } - /* Might still be loading firmware */ wait_for_completion(&card->fw_done); @@ -574,8 +569,9 @@ static int mwifiex_usb_resume(struct usb_interface *intf) struct mwifiex_adapter *adapter; int i; - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); + if (!card->adapter) { + dev_err(&intf->dev, "%s: card->adapter is NULL\n", + __func__); return 0; } adapter = card->adapter; @@ -617,11 +613,6 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; - if (!card) { - dev_err(&intf->dev, "%s: card is NULL\n", __func__); - return; - } - wait_for_completion(&card->fw_done); adapter = card->adapter; -- cgit v1.1 From 97489c284d9fba528a7e0a19ed11d3d3c0e88e83 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 19:30:34 +0530 Subject: mwifiex: pcie: stop checking for NULL adapter->card It should never be NULL here, and to think otherwise makes things confusing. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 53 +++++++++++++---------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 98299a1..4b89f55 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -3015,31 +3015,28 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; - struct pci_dev *pdev; + struct pci_dev *pdev = card->dev; int i; - if (card) { - pdev = card->dev; - if (card->msix_enable) { - for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) - synchronize_irq(card->msix_entries[i].vector); + if (card->msix_enable) { + for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) + synchronize_irq(card->msix_entries[i].vector); - for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) - free_irq(card->msix_entries[i].vector, - &card->msix_ctx[i]); + for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) + free_irq(card->msix_entries[i].vector, + &card->msix_ctx[i]); - card->msix_enable = 0; - pci_disable_msix(pdev); - } else { - mwifiex_dbg(adapter, INFO, - "%s(): calling free_irq()\n", __func__); - free_irq(card->dev->irq, &card->share_irq_ctx); + card->msix_enable = 0; + pci_disable_msix(pdev); + } else { + mwifiex_dbg(adapter, INFO, + "%s(): calling free_irq()\n", __func__); + free_irq(card->dev->irq, &card->share_irq_ctx); - if (card->msi_enable) - pci_disable_msi(pdev); - } - card->adapter = NULL; + if (card->msi_enable) + pci_disable_msi(pdev); } + card->adapter = NULL; } /* This function initializes the PCI-E host memory space, WCB rings, etc. @@ -3122,18 +3119,14 @@ static void mwifiex_pcie_down_dev(struct mwifiex_adapter *adapter) adapter->seq_num = 0; adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; - if (card) { - if (reg->sleep_cookie) - mwifiex_pcie_delete_sleep_cookie_buf(adapter); - - mwifiex_pcie_delete_cmdrsp_buf(adapter); - mwifiex_pcie_delete_evtbd_ring(adapter); - mwifiex_pcie_delete_rxbd_ring(adapter); - mwifiex_pcie_delete_txbd_ring(adapter); - card->cmdrsp_buf = NULL; - } + if (reg->sleep_cookie) + mwifiex_pcie_delete_sleep_cookie_buf(adapter); - return; + mwifiex_pcie_delete_cmdrsp_buf(adapter); + mwifiex_pcie_delete_evtbd_ring(adapter); + mwifiex_pcie_delete_rxbd_ring(adapter); + mwifiex_pcie_delete_txbd_ring(adapter); + card->cmdrsp_buf = NULL; } static struct mwifiex_if_ops pcie_ops = { -- cgit v1.1 From 3e9e40e74753346218e8285cf1ecff9ef3a624c6 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 18 Nov 2016 15:40:38 -0800 Subject: virtio_net: Simplify call sites for virtio_net_hdr_{from, to}_skb(). No point storing the return value of virtio_net_hdr_to_skb() or virtio_net_hdr_from_skb() to a variable when the value is used only once as a boolean in an immediately following if statement. Signed-off-by: Jarno Rajahalme Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 5 ++--- drivers/net/tun.c | 8 +++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 070e329..5da9861 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -821,9 +821,8 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, if (iov_iter_count(iter) < vnet_hdr_len) return -EINVAL; - ret = virtio_net_hdr_from_skb(skb, &vnet_hdr, - macvtap_is_little_endian(q)); - if (ret) + if (virtio_net_hdr_from_skb(skb, &vnet_hdr, + macvtap_is_little_endian(q))) BUG(); if (copy_to_iter(&vnet_hdr, sizeof(vnet_hdr), iter) != diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 1588469..3b8d8cc 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1252,8 +1252,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, return -EFAULT; } - err = virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun)); - if (err) { + if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) { this_cpu_inc(tun->pcpu_stats->rx_frame_errors); kfree_skb(skb); return -EINVAL; @@ -1367,9 +1366,8 @@ static ssize_t tun_put_user(struct tun_struct *tun, if (iov_iter_count(iter) < vnet_hdr_sz) return -EINVAL; - ret = virtio_net_hdr_from_skb(skb, &gso, - tun_is_little_endian(tun)); - if (ret) { + if (virtio_net_hdr_from_skb(skb, &gso, + tun_is_little_endian(tun))) { struct skb_shared_info *sinfo = skb_shinfo(skb); pr_err("unexpected GSO type: " "0x%x, gso_size %d, hdr_len %d\n", -- cgit v1.1 From d66016a77757b004b8637f44d87bedfc4a47b89c Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 18 Nov 2016 15:40:39 -0800 Subject: virtio_net.h: Fix comment. Fix incorrent comment after the final #endif. Signed-off-by: Jarno Rajahalme Signed-off-by: David S. Miller --- include/linux/virtio_net.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 1c912f8..74f1e33 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -98,4 +98,4 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, return 0; } -#endif /* _LINUX_VIRTIO_BYTEORDER */ +#endif /* _LINUX_VIRTIO_NET_H */ -- cgit v1.1 From 9403cd7cbb08aa3709c632decafa2014c8ed96e6 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 18 Nov 2016 15:40:40 -0800 Subject: virtio_net: Do not clear memory for struct virtio_net_hdr twice. virtio_net_hdr_from_skb() clears the memory for the header, so there is no point for the callers to do the same. Signed-off-by: Jarno Rajahalme Signed-off-by: David S. Miller --- drivers/net/tun.c | 3 +-- include/linux/virtio_net.h | 2 +- net/packet/af_packet.c | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3b8d8cc..64e694c 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1360,8 +1360,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, } if (vnet_hdr_sz) { - struct virtio_net_hdr gso = { 0 }; /* no info leak */ - int ret; + struct virtio_net_hdr gso; if (iov_iter_count(iter) < vnet_hdr_sz) return -EINVAL; diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 74f1e33..6620400 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -58,7 +58,7 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, struct virtio_net_hdr *hdr, bool little_endian) { - memset(hdr, 0, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); /* no info leak */ if (skb_is_gso(skb)) { struct skb_shared_info *sinfo = skb_shinfo(skb); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d2238b2..abe6c0b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1970,8 +1970,6 @@ static unsigned int run_filter(struct sk_buff *skb, static int __packet_rcv_vnet(const struct sk_buff *skb, struct virtio_net_hdr *vnet_hdr) { - *vnet_hdr = (const struct virtio_net_hdr) { 0 }; - if (virtio_net_hdr_from_skb(skb, vnet_hdr, vio_le())) BUG(); -- cgit v1.1 From db60eb5f6506cbfb437cf51be050368e6577a848 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 18 Nov 2016 15:40:41 -0800 Subject: af_packet: Use virtio_net_hdr_to_skb(). Use the common virtio_net_hdr_to_skb() instead of open coding it. Other call sites were changed by commit fd2a0437dc, but this one was missed, maybe because it is split in two parts of the source code. Interim comparisons of 'vnet_hdr->gso_type' still work as both the vnet_hdr and skb notion of gso_type is zero when there is no gso. Fixes: fd2a0437dc ("virtio_net: introduce virtio_net_hdr_{from,to}_skb") Signed-off-by: Jarno Rajahalme Signed-off-by: David S. Miller --- net/packet/af_packet.c | 51 +++----------------------------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index abe6c0b..1816b77 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2388,8 +2388,6 @@ static void tpacket_set_protocol(const struct net_device *dev, static int __packet_snd_vnet_parse(struct virtio_net_hdr *vnet_hdr, size_t len) { - unsigned short gso_type = 0; - if ((vnet_hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && (__virtio16_to_cpu(vio_le(), vnet_hdr->csum_start) + __virtio16_to_cpu(vio_le(), vnet_hdr->csum_offset) + 2 > @@ -2401,29 +2399,6 @@ static int __packet_snd_vnet_parse(struct virtio_net_hdr *vnet_hdr, size_t len) if (__virtio16_to_cpu(vio_le(), vnet_hdr->hdr_len) > len) return -EINVAL; - if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { - switch (vnet_hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { - case VIRTIO_NET_HDR_GSO_TCPV4: - gso_type = SKB_GSO_TCPV4; - break; - case VIRTIO_NET_HDR_GSO_TCPV6: - gso_type = SKB_GSO_TCPV6; - break; - case VIRTIO_NET_HDR_GSO_UDP: - gso_type = SKB_GSO_UDP; - break; - default: - return -EINVAL; - } - - if (vnet_hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN) - gso_type |= SKB_GSO_TCP_ECN; - - if (vnet_hdr->gso_size == 0) - return -EINVAL; - } - - vnet_hdr->gso_type = gso_type; /* changes type, temporary storage */ return 0; } @@ -2443,27 +2418,6 @@ static int packet_snd_vnet_parse(struct msghdr *msg, size_t *len, return __packet_snd_vnet_parse(vnet_hdr, *len); } -static int packet_snd_vnet_gso(struct sk_buff *skb, - struct virtio_net_hdr *vnet_hdr) -{ - if (vnet_hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { - u16 s = __virtio16_to_cpu(vio_le(), vnet_hdr->csum_start); - u16 o = __virtio16_to_cpu(vio_le(), vnet_hdr->csum_offset); - - if (!skb_partial_csum_set(skb, s, o)) - return -EINVAL; - } - - skb_shinfo(skb)->gso_size = - __virtio16_to_cpu(vio_le(), vnet_hdr->gso_size); - skb_shinfo(skb)->gso_type = vnet_hdr->gso_type; - - /* Header must be checked, and gso_segs computed. */ - skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; - skb_shinfo(skb)->gso_segs = 0; - return 0; -} - static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, void *frame, struct net_device *dev, void *data, int tp_len, __be16 proto, unsigned char *addr, int hlen, int copylen, @@ -2723,7 +2677,8 @@ tpacket_error: } } - if (po->has_vnet_hdr && packet_snd_vnet_gso(skb, vnet_hdr)) { + if (po->has_vnet_hdr && virtio_net_hdr_to_skb(skb, vnet_hdr, + vio_le())) { tp_len = -EINVAL; goto tpacket_error; } @@ -2914,7 +2869,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) packet_pick_tx_queue(dev, skb); if (po->has_vnet_hdr) { - err = packet_snd_vnet_gso(skb, &vnet_hdr); + err = virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le()); if (err) goto out_free; len += sizeof(vnet_hdr); -- cgit v1.1 From 5a2138812604c32b7617b3b2e53e336617121d3b Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 18 Nov 2016 15:40:42 -0800 Subject: af_packet: Use virtio_net_hdr_from_skb() directly. Remove static function __packet_rcv_vnet(), which only called virtio_net_hdr_from_skb() and BUG()ged out if an error code was returned. Instead, call virtio_net_hdr_from_skb() from the former call sites of __packet_rcv_vnet() and actually use the error handling code that is already there. Signed-off-by: Jarno Rajahalme Signed-off-by: David S. Miller --- net/packet/af_packet.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 1816b77..fab9bbf 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1967,15 +1967,6 @@ static unsigned int run_filter(struct sk_buff *skb, return res; } -static int __packet_rcv_vnet(const struct sk_buff *skb, - struct virtio_net_hdr *vnet_hdr) -{ - if (virtio_net_hdr_from_skb(skb, vnet_hdr, vio_le())) - BUG(); - - return 0; -} - static int packet_rcv_vnet(struct msghdr *msg, const struct sk_buff *skb, size_t *len) { @@ -1985,7 +1976,7 @@ static int packet_rcv_vnet(struct msghdr *msg, const struct sk_buff *skb, return -EINVAL; *len -= sizeof(vnet_hdr); - if (__packet_rcv_vnet(skb, &vnet_hdr)) + if (virtio_net_hdr_from_skb(skb, &vnet_hdr, vio_le())) return -EINVAL; return memcpy_to_msg(msg, (void *)&vnet_hdr, sizeof(vnet_hdr)); @@ -2244,8 +2235,9 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, spin_unlock(&sk->sk_receive_queue.lock); if (po->has_vnet_hdr) { - if (__packet_rcv_vnet(skb, h.raw + macoff - - sizeof(struct virtio_net_hdr))) { + if (virtio_net_hdr_from_skb(skb, h.raw + macoff - + sizeof(struct virtio_net_hdr), + vio_le())) { spin_lock(&sk->sk_receive_queue.lock); goto drop_n_account; } -- cgit v1.1 From e0d7924a4aaadfb779178f00ff67e96c895eb5eb Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 19 Nov 2016 03:47:56 +0300 Subject: net: make struct napi_alloc_cache::skb_count unsigned int size_t is way too much for an integer not exceeding 64. Space savings: 10 bytes! add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-10 (-10) function old new delta napi_consume_skb 165 163 -2 __kfree_skb_flush 56 53 -3 __kfree_skb_defer 97 92 -5 Total: Before=154865639, After=154865629, chg -0.00% Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/core/skbuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 0b2a6e9..4c96cb1 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -354,7 +354,7 @@ EXPORT_SYMBOL(build_skb); struct napi_alloc_cache { struct page_frag_cache page; - size_t skb_count; + unsigned int skb_count; void *skb_cache[NAPI_SKB_CACHE_SIZE]; }; -- cgit v1.1 From 3b2c75d371740fb0dcd0c9eac545ab1dd28b4706 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 19 Nov 2016 03:54:35 +0300 Subject: netlink: use "unsigned int" in nla_next() ->nla_len is unsigned entity (it's length after all) and u16, thus it can't overflow when being aligned into int/unsigned int. (nlmsg_next has the same code, but I didn't yet convince myself it is correct to do so). There is pointer arithmetic in this function and offset being unsigned is better: add/remove: 0/0 grow/shrink: 1/64 up/down: 5/-309 (-304) function old new delta nl80211_set_wiphy 1444 1449 +5 team_nl_cmd_options_set 997 995 -2 tcf_em_tree_validate 872 870 -2 switchdev_port_bridge_setlink 352 350 -2 switchdev_port_br_afspec 312 310 -2 rtm_to_fib_config 428 426 -2 qla4xxx_sysfs_ddb_set_param 2193 2191 -2 qla4xxx_iface_set_param 4470 4468 -2 ovs_nla_free_flow_actions 152 150 -2 output_userspace 518 516 -2 ... nl80211_set_reg 654 649 -5 validate_scan_freqs 148 142 -6 validate_linkmsg 288 282 -6 nl80211_parse_connkeys 489 483 -6 nlattr_set 231 224 -7 nf_tables_delsetelem 267 260 -7 do_setlink 3416 3408 -8 netlbl_cipsov4_add_std 1672 1659 -13 nl80211_parse_sched_scan 2902 2888 -14 nl80211_trigger_scan 1738 1720 -18 do_execute_actions 2821 2738 -83 Total: Before=154865355, After=154865051, chg -0.00% Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/net/netlink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/netlink.h b/include/net/netlink.h index a34f53a..d3938f1 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -713,7 +713,7 @@ static inline int nla_ok(const struct nlattr *nla, int remaining) */ static inline struct nlattr *nla_next(const struct nlattr *nla, int *remaining) { - int totlen = NLA_ALIGN(nla->nla_len); + unsigned int totlen = NLA_ALIGN(nla->nla_len); *remaining -= totlen; return (struct nlattr *) ((char *) nla + totlen); -- cgit v1.1 From 32d84cdcfde00f28f6133f0e2b015c86f2466fb1 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 19 Nov 2016 03:59:07 +0300 Subject: netlink: smaller nla_attr_minlen table Length of a netlink attribute may be u16 but lengths of basic attributes are much smaller, so small we can save 16 bytes of .rodata and pocket change inside .text. 16-bit is worse on x86-64 than 8-bit because of operand size override prefix. add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-19 (-19) function old new delta validate_nla 418 417 -1 nla_policy_len 66 64 -2 nla_attr_minlen 32 16 -16 Total: Before=154865051, After=154865032, chg -0.00% Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- lib/nlattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nlattr.c b/lib/nlattr.c index fce1e9a..b42b857 100644 --- a/lib/nlattr.c +++ b/lib/nlattr.c @@ -14,7 +14,7 @@ #include #include -static const u16 nla_attr_minlen[NLA_TYPE_MAX+1] = { +static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = { [NLA_U8] = sizeof(u8), [NLA_U16] = sizeof(u16), [NLA_U32] = sizeof(u32), -- cgit v1.1 From c72d8cdaa5dbd3baf918046ee5149ab69330923e Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 19 Nov 2016 04:08:08 +0300 Subject: net: fix bogus cast in skb_pagelen() and use unsigned variables 1) cast to "int" is unnecessary: u8 will be promoted to int before decrementing, small positive numbers fit into "int", so their values won't be changed during promotion. Once everything is int including loop counters, signedness doesn't matter: 32-bit operations will stay 32-bit operations. But! Someone tried to make this loop smart by making everything of the same type apparently in an attempt to optimise it. Do the optimization, just differently. Do the cast where it matters. :^) 2) frag size is unsigned entity and sum of fragments sizes is also unsigned. Make everything unsigned, leave no MOVSX instruction behind. add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-4 (-4) function old new delta skb_cow_data 835 834 -1 ip_do_fragment 2549 2548 -1 ip6_fragment 3130 3128 -2 Total: Before=154865032, After=154865028, chg -0.00% Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 +++--- net/ipv4/ip_output.c | 2 +- net/ipv6/ip6_output.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a4aeeca..9c535fb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1799,11 +1799,11 @@ static inline unsigned int skb_headlen(const struct sk_buff *skb) return skb->len - skb->data_len; } -static inline int skb_pagelen(const struct sk_buff *skb) +static inline unsigned int skb_pagelen(const struct sk_buff *skb) { - int i, len = 0; + unsigned int i, len = 0; - for (i = (int)skb_shinfo(skb)->nr_frags - 1; i >= 0; i--) + for (i = skb_shinfo(skb)->nr_frags - 1; (int)i >= 0; i--) len += skb_frag_size(&skb_shinfo(skb)->frags[i]); return len + skb_headlen(skb); } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index eaf720b..358f2c8 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -581,7 +581,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, */ if (skb_has_frag_list(skb)) { struct sk_buff *frag, *frag2; - int first_len = skb_pagelen(skb); + unsigned int first_len = skb_pagelen(skb); if (first_len - hlen > mtu || ((first_len - hlen) & 7) || diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index b37054b..312cbd0 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -625,7 +625,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, hroom = LL_RESERVED_SPACE(rt->dst.dev); if (skb_has_frag_list(skb)) { - int first_len = skb_pagelen(skb); + unsigned int first_len = skb_pagelen(skb); struct sk_buff *frag2; if (first_len - hlen > mtu || -- cgit v1.1 From a3db3d3a52f0f159e0aa996792987acd8c14d2ad Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 20 Nov 2016 20:14:14 +0100 Subject: net: dsa: mv88e6xxx: Fix typos when removing g1 interrupts Simple typos, s/g2/g1/ Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index d6d9d66..6aa81d2 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -415,11 +415,11 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip) int irq, virq; for (irq = 0; irq < 16; irq++) { - virq = irq_find_mapping(chip->g2_irq.domain, irq); + virq = irq_find_mapping(chip->g1_irq.domain, irq); irq_dispose_mapping(virq); } - irq_domain_remove(chip->g2_irq.domain); + irq_domain_remove(chip->g1_irq.domain); } static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) -- cgit v1.1 From 46712644d6e72d8a66f20f51e260e1c3d1eddfeb Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 20 Nov 2016 20:14:15 +0100 Subject: net: dsa: mv88e6xxx: Fix unconditional irq freeing Trying to remove an IRQ domain that was not created results in an Opps. Add the necessary checks that the irqs were created before freeing them. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 6aa81d2..b843052 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3897,10 +3897,11 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) out_mdio: mv88e6xxx_mdio_unregister(chip); out_g2_irq: - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) + if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0) mv88e6xxx_g2_irq_free(chip); out_g1_irq: - mv88e6xxx_g1_irq_free(chip); + if (chip->irq > 0) + mv88e6xxx_g1_irq_free(chip); out: return err; } @@ -3914,9 +3915,11 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) mv88e6xxx_unregister_switch(chip); mv88e6xxx_mdio_unregister(chip); - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) - mv88e6xxx_g2_irq_free(chip); - mv88e6xxx_g1_irq_free(chip); + if (chip->irq > 0) { + if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) + mv88e6xxx_g2_irq_free(chip); + mv88e6xxx_g1_irq_free(chip); + } } static const struct of_device_id mv88e6xxx_of_match[] = { -- cgit v1.1 From 3460a5770ce97d7caeb4b11766c7bb2d74d0d29c Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 20 Nov 2016 20:14:16 +0100 Subject: net: dsa: mv88e6xxx: Mask g1 interrupts and free interrupt Fix the g1 interrupt free code such that is masks any further interrupts, and then releases the interrupt. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b843052..8fcef7e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -413,6 +413,13 @@ static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = { static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip) { int irq, virq; + u16 mask; + + mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask); + mask |= GENMASK(chip->g1_irq.nirqs, 0); + mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); + + free_irq(chip->irq, chip); for (irq = 0; irq < 16; irq++) { virq = irq_find_mapping(chip->g1_irq.domain, irq); -- cgit v1.1 From 3dd0ef05f74693e55549ed790e350af5a392234f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 20 Nov 2016 20:14:17 +0100 Subject: net: dsa: mv88e6xxx: Fix cleanup on error for g1 interrupt setup On error, remask the interrupts, release all maps, and remove the domain. This cannot be done using the mv88e6xxx_g1_irq_free() because some of these actions are not idempotent. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 8fcef7e..614b2f6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -431,8 +431,8 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip) static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) { - int err, irq; - u16 reg; + int err, irq, virq; + u16 reg, mask; chip->g1_irq.nirqs = chip->info->g1_irqs; chip->g1_irq.domain = irq_domain_add_simple( @@ -447,32 +447,41 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) chip->g1_irq.chip = mv88e6xxx_g1_irq_chip; chip->g1_irq.masked = ~0; - err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask); if (err) - goto out; + goto out_mapping; - reg &= ~GENMASK(chip->g1_irq.nirqs, 0); + mask &= ~GENMASK(chip->g1_irq.nirqs, 0); - err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); if (err) - goto out; + goto out_disable; /* Reading the interrupt status clears (most of) them */ err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); if (err) - goto out; + goto out_disable; err = request_threaded_irq(chip->irq, NULL, mv88e6xxx_g1_irq_thread_fn, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, dev_name(chip->dev), chip); if (err) - goto out; + goto out_disable; return 0; -out: - mv88e6xxx_g1_irq_free(chip); +out_disable: + mask |= GENMASK(chip->g1_irq.nirqs, 0); + mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); + +out_mapping: + for (irq = 0; irq < 16; irq++) { + virq = irq_find_mapping(chip->g1_irq.domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(chip->g1_irq.domain); return err; } -- cgit v1.1 From 8e757eba074fff8460dc91717695e009b5f93f76 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 20 Nov 2016 20:14:18 +0100 Subject: net: dsa: mv88e6xxx: Fix releasing for the global2 interrupts It is not possible to use devm_request_threaded_irq() because we have two stacked interrupt controllers in one device. The lower interrupt controller cannot be removed until the upper is fully removed. This happens too late with the devm API, resulting in error messages about removing a domain while there is still an active interrupt. Swap to using request_threaded_irq() and manage the release of the interrupt manually. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/global2.c | 28 ++++++++++++++++++---------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 1a0b135..536a27c 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -507,6 +507,9 @@ void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) { int irq, virq; + free_irq(chip->device_irq, chip); + irq_dispose_mapping(chip->device_irq); + for (irq = 0; irq < 16; irq++) { virq = irq_find_mapping(chip->g2_irq.domain, irq); irq_dispose_mapping(virq); @@ -517,8 +520,7 @@ void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) { - int device_irq; - int err, irq; + int err, irq, virq; if (!chip->dev->of_node) return -EINVAL; @@ -534,22 +536,28 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) chip->g2_irq.chip = mv88e6xxx_g2_irq_chip; chip->g2_irq.masked = ~0; - device_irq = irq_find_mapping(chip->g1_irq.domain, - GLOBAL_STATUS_IRQ_DEVICE); - if (device_irq < 0) { - err = device_irq; + chip->device_irq = irq_find_mapping(chip->g1_irq.domain, + GLOBAL_STATUS_IRQ_DEVICE); + if (chip->device_irq < 0) { + err = chip->device_irq; goto out; } - err = devm_request_threaded_irq(chip->dev, device_irq, NULL, - mv88e6xxx_g2_irq_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-g1", chip); + err = request_threaded_irq(chip->device_irq, NULL, + mv88e6xxx_g2_irq_thread_fn, + IRQF_ONESHOT, "mv88e6xxx-g1", chip); if (err) goto out; return 0; + out: - mv88e6xxx_g2_irq_free(chip); + for (irq = 0; irq < 16; irq++) { + virq = irq_find_mapping(chip->g2_irq.domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(chip->g2_irq.domain); return err; } diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 9296130..a386950 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -714,6 +714,7 @@ struct mv88e6xxx_chip { struct mv88e6xxx_irq g1_irq; struct mv88e6xxx_irq g2_irq; int irq; + int device_irq; }; struct mv88e6xxx_bus_ops { -- cgit v1.1 From 61f7c3f8032c840da4fb6b8a8714cb31e0b8ace8 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 20 Nov 2016 20:14:19 +0100 Subject: net: dsa: mv88e6xxx: Hold the mutex while freeing g1 interrupts Freeing interrupts requires switch register access to mask the interrupts. Hence we must hold the register mutex. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 614b2f6..e30d0ea 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3916,8 +3916,11 @@ out_g2_irq: if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0) mv88e6xxx_g2_irq_free(chip); out_g1_irq: - if (chip->irq > 0) + if (chip->irq > 0) { + mutex_lock(&chip->reg_lock); mv88e6xxx_g1_irq_free(chip); + mutex_unlock(&chip->reg_lock); + } out: return err; } -- cgit v1.1 From 97bc402db7821259f6a722cb38e060aa9b35b6e8 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 19 Nov 2016 01:45:00 +0100 Subject: bpf, mlx5: fix mlx5e_create_rq taking reference on prog In mlx5e_create_rq(), when creating a new queue, we call bpf_prog_add() but without checking the return value. bpf_prog_add() can fail since 92117d8443bc ("bpf: fix refcnt overflow"), so we really must check it. Take the reference right when we assign it to the rq from priv->xdp_prog, and just drop the reference on error path. Destruction in mlx5e_destroy_rq() looks good, though. Fixes: 86994156c736 ("net/mlx5e: XDP fast RX drop bpf programs support") Signed-off-by: Daniel Borkmann Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 13 +++++++++---- kernel/bpf/syscall.c | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index bd0732d..54bae79 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -513,7 +513,13 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, rq->channel = c; rq->ix = c->ix; rq->priv = c->priv; - rq->xdp_prog = priv->xdp_prog; + + rq->xdp_prog = priv->xdp_prog ? bpf_prog_inc(priv->xdp_prog) : NULL; + if (IS_ERR(rq->xdp_prog)) { + err = PTR_ERR(rq->xdp_prog); + rq->xdp_prog = NULL; + goto err_rq_wq_destroy; + } rq->buff.map_dir = DMA_FROM_DEVICE; if (rq->xdp_prog) @@ -590,12 +596,11 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, rq->page_cache.head = 0; rq->page_cache.tail = 0; - if (rq->xdp_prog) - bpf_prog_add(rq->xdp_prog, 1); - return 0; err_rq_wq_destroy: + if (rq->xdp_prog) + bpf_prog_put(rq->xdp_prog); mlx5_wq_destroy(&rq->wq_ctrl); return err; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ce1b7de..eb15498 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -696,6 +696,7 @@ struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog) { return bpf_prog_add(prog, 1); } +EXPORT_SYMBOL_GPL(bpf_prog_inc); static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type) { -- cgit v1.1 From c54c06290428554bc0e26d58f21a7865cbe995af Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 19 Nov 2016 01:45:01 +0100 Subject: bpf, mlx5: fix various refcount issues in mlx5e_xdp_set There are multiple issues in mlx5e_xdp_set(): 1) The batched bpf_prog_add() is currently not checked for errors. When doing so, it should be done at an earlier point in time to makes sure that we cannot fail anymore at the time we want to set the program for each channel. The batched refs short-cut can only be performed when we don't need to perform a reset for changing the rq type and the device was in opened state. In case the device was not in opened state, then the next mlx5e_open_locked() will aquire the refs from the control prog via mlx5e_create_rq(), same when we need to perform a reset. 2) When swapping the priv->xdp_prog, then no extra reference count must be taken since we got that from call path via dev_change_xdp_fd() already. Otherwise, we'd never be able to release the program. Also, bpf_prog_add() without checking the return code could fail. Fixes: 86994156c736 ("net/mlx5e: XDP fast RX drop bpf programs support") Signed-off-by: Daniel Borkmann Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 54bae79..491cff9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3144,11 +3144,21 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) if (was_opened && reset) mlx5e_close_locked(netdev); + if (was_opened && !reset) { + /* num_channels is invariant here, so we can take the + * batched reference right upfront. + */ + prog = bpf_prog_add(prog, priv->params.num_channels); + if (IS_ERR(prog)) { + err = PTR_ERR(prog); + goto unlock; + } + } - /* exchange programs */ + /* exchange programs, extra prog reference we got from caller + * as long as we don't fail from this point onwards. + */ old_prog = xchg(&priv->xdp_prog, prog); - if (prog) - bpf_prog_add(prog, 1); if (old_prog) bpf_prog_put(old_prog); @@ -3164,7 +3174,6 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) /* exchanging programs w/o reset, we update ref counts on behalf * of the channels RQs here. */ - bpf_prog_add(prog, priv->params.num_channels); for (i = 0; i < priv->params.num_channels; i++) { struct mlx5e_channel *c = priv->channel[i]; -- cgit v1.1 From a055c19be98bc065a4478663ba7f6833693b8958 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 19 Nov 2016 01:45:02 +0100 Subject: bpf, mlx5: drop priv->xdp_prog reference on netdev cleanup mlx5e_xdp_set() is currently the only place where we drop reference on the prog sitting in priv->xdp_prog when it's exchanged by a new one. We also need to make sure that we eventually release that reference, for example, in case the netdev is dismantled, otherwise we leak the program. Fixes: 86994156c736 ("net/mlx5e: XDP fast RX drop bpf programs support") Signed-off-by: Daniel Borkmann Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 491cff9..6957608 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3705,6 +3705,9 @@ static void mlx5e_nic_cleanup(struct mlx5e_priv *priv) if (MLX5_CAP_GEN(mdev, vport_group_manager)) mlx5_eswitch_unregister_vport_rep(esw, 0); + + if (priv->xdp_prog) + bpf_prog_put(priv->xdp_prog); } static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) -- cgit v1.1 From 6d67942dd0ebc3dddc86edf9208169d064a9b3d7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 19 Nov 2016 01:45:03 +0100 Subject: bpf: add __must_check attributes to refcount manipulating helpers Helpers like bpf_prog_add(), bpf_prog_inc(), bpf_map_inc() can fail with an error, so make sure the caller properly checks their return value and not just ignores it, which could worst-case lead to use after free. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 01c1487..69d0a7f 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -233,14 +233,14 @@ void bpf_register_map_type(struct bpf_map_type_list *tl); struct bpf_prog *bpf_prog_get(u32 ufd); struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type); -struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i); +struct bpf_prog * __must_check bpf_prog_add(struct bpf_prog *prog, int i); void bpf_prog_sub(struct bpf_prog *prog, int i); -struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog); +struct bpf_prog * __must_check bpf_prog_inc(struct bpf_prog *prog); void bpf_prog_put(struct bpf_prog *prog); struct bpf_map *bpf_map_get_with_uref(u32 ufd); struct bpf_map *__bpf_map_get(struct fd f); -struct bpf_map *bpf_map_inc(struct bpf_map *map, bool uref); +struct bpf_map * __must_check bpf_map_inc(struct bpf_map *map, bool uref); void bpf_map_put_with_uref(struct bpf_map *map); void bpf_map_put(struct bpf_map *map); int bpf_map_precharge_memlock(u32 pages); @@ -299,7 +299,8 @@ static inline struct bpf_prog *bpf_prog_get_type(u32 ufd, { return ERR_PTR(-EOPNOTSUPP); } -static inline struct bpf_prog *bpf_prog_add(struct bpf_prog *prog, int i) +static inline struct bpf_prog * __must_check bpf_prog_add(struct bpf_prog *prog, + int i) { return ERR_PTR(-EOPNOTSUPP); } @@ -311,7 +312,8 @@ static inline void bpf_prog_sub(struct bpf_prog *prog, int i) static inline void bpf_prog_put(struct bpf_prog *prog) { } -static inline struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog) + +static inline struct bpf_prog * __must_check bpf_prog_inc(struct bpf_prog *prog) { return ERR_PTR(-EOPNOTSUPP); } -- cgit v1.1 From d21dbdfe0afb5195607d207f894fe86d5b322d46 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 18 Nov 2016 17:18:03 -0800 Subject: udp: avoid one cache line miss in recvmsg() UDP_SKB_CB(skb)->partial_cov is located at offset 66 in skb, requesting a cold cache line being read in cpu cache. We can avoid this cache line miss for UDP sockets, as partial_cov has a meaning only for UDPLite. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/udp.c | 3 ++- net/ipv6/udp.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index e1fc011..b949770 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1389,7 +1389,8 @@ try_again: * coverage checksum (UDP-Lite), do it before the copy. */ - if (copied < ulen || UDP_SKB_CB(skb)->partial_cov || peeking) { + if (copied < ulen || peeking || + (is_udplite && UDP_SKB_CB(skb)->partial_cov)) { checksum_valid = !udp_lib_checksum_complete(skb); if (!checksum_valid) goto csum_copy_err; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4f99417..8fd4d89 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -363,7 +363,8 @@ try_again: * coverage checksum (UDP-Lite), do it before the copy. */ - if (copied < ulen || UDP_SKB_CB(skb)->partial_cov || peeking) { + if (copied < ulen || peeking || + (is_udplite && UDP_SKB_CB(skb)->partial_cov)) { checksum_valid = !udp_lib_checksum_complete(skb); if (!checksum_valid) goto csum_copy_err; -- cgit v1.1 From b668534c1d9b80f4cda4d761eb11d3a6c9f4ced8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 19 Nov 2016 14:57:11 -0800 Subject: bnx2: use READ_ONCE() instead of barrier() barrier() is a big hammer compared to READ_ONCE(), and requires comments explaining what is protected. READ_ONCE() is more precise and compiler should generate better overall code. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 9960a92..d5d1026 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -254,13 +254,10 @@ static inline u32 bnx2_tx_avail(struct bnx2 *bp, struct bnx2_tx_ring_info *txr) { u32 diff; - /* Tell compiler to fetch tx_prod and tx_cons from memory. */ - barrier(); - /* The ring uses 256 indices for 255 entries, one of them * needs to be skipped. */ - diff = txr->tx_prod - txr->tx_cons; + diff = READ_ONCE(txr->tx_prod) - READ_ONCE(txr->tx_cons); if (unlikely(diff >= BNX2_TX_DESC_CNT)) { diff &= 0xffff; if (diff == BNX2_TX_DESC_CNT) @@ -2839,10 +2836,8 @@ bnx2_get_hw_tx_cons(struct bnx2_napi *bnapi) { u16 cons; - /* Tell compiler that status block fields can change. */ - barrier(); - cons = *bnapi->hw_tx_cons_ptr; - barrier(); + cons = READ_ONCE(*bnapi->hw_tx_cons_ptr); + if (unlikely((cons & BNX2_MAX_TX_DESC_CNT) == BNX2_MAX_TX_DESC_CNT)) cons++; return cons; @@ -3141,10 +3136,8 @@ bnx2_get_hw_rx_cons(struct bnx2_napi *bnapi) { u16 cons; - /* Tell compiler that status block fields can change. */ - barrier(); - cons = *bnapi->hw_rx_cons_ptr; - barrier(); + cons = READ_ONCE(*bnapi->hw_rx_cons_ptr); + if (unlikely((cons & BNX2_MAX_RX_DESC_CNT) == BNX2_MAX_RX_DESC_CNT)) cons++; return cons; -- cgit v1.1 From dad42c3038a59d27fced28ee4ec1d4a891b28155 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 20 Nov 2016 09:24:36 -0800 Subject: mlx4: avoid unnecessary dirtying of critical fields While stressing a 40Gbit mlx4 NIC with busy polling, I found false sharing in mlx4 driver that can be easily avoided. This patch brings an additional 7 % performance improvement in UDP_RR workload. 1) If we received no frame during one mlx4_en_process_rx_cq() invocation, no need to call mlx4_cq_set_ci() and/or dirty ring->cons 2) Do not refill rx buffers if we have plenty of them. This avoids false sharing and allows some bulk/batch optimizations. Page allocator and its locks will thank us. Finally, mlx4_en_poll_rx_cq() should not return 0 if it determined cpu handling NIC IRQ should be changed. We should return budget-1 instead, to not fool net_rx_action() and its netdev_budget. v2: keep AVG_PERF_COUNTER(... polled) even if polled is 0 Signed-off-by: Eric Dumazet Cc: Tariq Toukan Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 47 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 22f08f9..6562f78 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -688,18 +688,23 @@ out_loopback: dev_kfree_skb_any(skb); } -static void mlx4_en_refill_rx_buffers(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring) +static bool mlx4_en_refill_rx_buffers(struct mlx4_en_priv *priv, + struct mlx4_en_rx_ring *ring) { - int index = ring->prod & ring->size_mask; + u32 missing = ring->actual_size - (ring->prod - ring->cons); - while ((u32) (ring->prod - ring->cons) < ring->actual_size) { - if (mlx4_en_prepare_rx_desc(priv, ring, index, + /* Try to batch allocations, but not too much. */ + if (missing < 8) + return false; + do { + if (mlx4_en_prepare_rx_desc(priv, ring, + ring->prod & ring->size_mask, GFP_ATOMIC | __GFP_COLD)) break; ring->prod++; - index = ring->prod & ring->size_mask; - } + } while (--missing); + + return true; } /* When hardware doesn't strip the vlan, we need to calculate the checksum @@ -1081,15 +1086,20 @@ consumed: out: rcu_read_unlock(); - if (doorbell_pending) - mlx4_en_xmit_doorbell(priv->tx_ring[TX_XDP][cq->ring]); + if (polled) { + if (doorbell_pending) + mlx4_en_xmit_doorbell(priv->tx_ring[TX_XDP][cq->ring]); + + mlx4_cq_set_ci(&cq->mcq); + wmb(); /* ensure HW sees CQ consumer before we post new buffers */ + ring->cons = cq->mcq.cons_index; + } AVG_PERF_COUNTER(priv->pstats.rx_coal_avg, polled); - mlx4_cq_set_ci(&cq->mcq); - wmb(); /* ensure HW sees CQ consumer before we post new buffers */ - ring->cons = cq->mcq.cons_index; - mlx4_en_refill_rx_buffers(priv, ring); - mlx4_en_update_rx_prod_db(ring); + + if (mlx4_en_refill_rx_buffers(priv, ring)) + mlx4_en_update_rx_prod_db(ring); + return polled; } @@ -1131,10 +1141,13 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) return budget; /* Current cpu is not according to smp_irq_affinity - - * probably affinity changed. need to stop this NAPI - * poll, and restart it on the right CPU + * probably affinity changed. Need to stop this NAPI + * poll, and restart it on the right CPU. + * Try to avoid returning a too small value (like 0), + * to not fool net_rx_action() and its netdev_budget */ - done = 0; + if (done) + done--; } /* Done for now */ if (napi_complete_done(napi, done)) -- cgit v1.1 From fc51f2b7e5f6cc0bb1958efda18926b0c600e9d9 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Mon, 21 Nov 2016 08:26:38 +0800 Subject: driver: macvlan: Remove duplicated IFF_UP condition check in macvlan_forward_source The function macvlan_forward_source_one has already checked the flag IFF_UP, so needn't check it outside in macvlan_forward_source too. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 13b7e0b..7ddfd2c 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -400,8 +400,7 @@ static void macvlan_forward_source(struct sk_buff *skb, hlist_for_each_entry_rcu(entry, h, hlist) { if (ether_addr_equal_64bits(entry->addr, addr)) - if (entry->vlan->dev->flags & IFF_UP) - macvlan_forward_source_one(skb, entry->vlan); + macvlan_forward_source_one(skb, entry->vlan); } } -- cgit v1.1 From 5e9235853d652a295d5f56cb8652950b6b5bf56b Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 21 Nov 2016 13:03:24 +0100 Subject: bridge: mcast: add IGMPv3 query support This patch adds basic support for IGMPv3 queries, the default is IGMPv2 as before. A new multicast option - multicast_igmp_version, adds the ability to change it between 2 and 3 via netlink and sysfs. The option struct member is in a 4 byte hole in net_bridge. There also a few minor style adjustments in br_multicast_new_group and br_multicast_add_group. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/bridge/br_multicast.c | 79 ++++++++++++++++++++++++++++++++++---------- net/bridge/br_netlink.c | 15 ++++++++- net/bridge/br_private.h | 3 ++ net/bridge/br_sysfs_br.c | 18 ++++++++++ 5 files changed, 98 insertions(+), 18 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b4fba66..325d2601 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -275,6 +275,7 @@ enum { IFLA_BR_PAD, IFLA_BR_VLAN_STATS_ENABLED, IFLA_BR_MCAST_STATS_ENABLED, + IFLA_BR_MCAST_IGMP_VERSION, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 073d54a..66192c1 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -365,13 +365,18 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, __be32 group, u8 *igmp_type) { + struct igmpv3_query *ihv3; + size_t igmp_hdr_size; struct sk_buff *skb; struct igmphdr *ih; struct ethhdr *eth; struct iphdr *iph; + igmp_hdr_size = sizeof(*ih); + if (br->multicast_igmp_version == 3) + igmp_hdr_size = sizeof(*ihv3); skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*eth) + sizeof(*iph) + - sizeof(*ih) + 4); + igmp_hdr_size + 4); if (!skb) goto out; @@ -396,7 +401,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, iph->version = 4; iph->ihl = 6; iph->tos = 0xc0; - iph->tot_len = htons(sizeof(*iph) + sizeof(*ih) + 4); + iph->tot_len = htons(sizeof(*iph) + igmp_hdr_size + 4); iph->id = 0; iph->frag_off = htons(IP_DF); iph->ttl = 1; @@ -412,17 +417,37 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, skb_put(skb, 24); skb_set_transport_header(skb, skb->len); - ih = igmp_hdr(skb); *igmp_type = IGMP_HOST_MEMBERSHIP_QUERY; - ih->type = IGMP_HOST_MEMBERSHIP_QUERY; - ih->code = (group ? br->multicast_last_member_interval : - br->multicast_query_response_interval) / - (HZ / IGMP_TIMER_SCALE); - ih->group = group; - ih->csum = 0; - ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr)); - skb_put(skb, sizeof(*ih)); + switch (br->multicast_igmp_version) { + case 2: + ih = igmp_hdr(skb); + ih->type = IGMP_HOST_MEMBERSHIP_QUERY; + ih->code = (group ? br->multicast_last_member_interval : + br->multicast_query_response_interval) / + (HZ / IGMP_TIMER_SCALE); + ih->group = group; + ih->csum = 0; + ih->csum = ip_compute_csum((void *)ih, sizeof(*ih)); + break; + case 3: + ihv3 = igmpv3_query_hdr(skb); + ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY; + ihv3->code = (group ? br->multicast_last_member_interval : + br->multicast_query_response_interval) / + (HZ / IGMP_TIMER_SCALE); + ihv3->group = group; + ihv3->qqic = br->multicast_query_interval / HZ; + ihv3->nsrcs = 0; + ihv3->resv = 0; + ihv3->suppress = 0; + ihv3->qrv = 2; + ihv3->csum = 0; + ihv3->csum = ip_compute_csum((void *)ihv3, sizeof(*ihv3)); + break; + } + + skb_put(skb, igmp_hdr_size); __skb_pull(skb, sizeof(*eth)); out: @@ -608,7 +633,8 @@ err: } struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, - struct net_bridge_port *port, struct br_ip *group) + struct net_bridge_port *p, + struct br_ip *group) { struct net_bridge_mdb_htable *mdb; struct net_bridge_mdb_entry *mp; @@ -624,7 +650,7 @@ struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, } hash = br_ip_hash(mdb, group); - mp = br_multicast_get_group(br, port, group, hash); + mp = br_multicast_get_group(br, p, group, hash); switch (PTR_ERR(mp)) { case 0: break; @@ -681,9 +707,9 @@ static int br_multicast_add_group(struct net_bridge *br, struct net_bridge_port *port, struct br_ip *group) { - struct net_bridge_mdb_entry *mp; - struct net_bridge_port_group *p; struct net_bridge_port_group __rcu **pp; + struct net_bridge_port_group *p; + struct net_bridge_mdb_entry *mp; unsigned long now = jiffies; int err; @@ -861,9 +887,9 @@ static void br_multicast_send_query(struct net_bridge *br, struct net_bridge_port *port, struct bridge_mcast_own_query *own_query) { - unsigned long time; - struct br_ip br_group; struct bridge_mcast_other_query *other_query = NULL; + struct br_ip br_group; + unsigned long time; if (!netif_running(br->dev) || br->multicast_disabled || !br->multicast_querier) @@ -1816,6 +1842,7 @@ void br_multicast_init(struct net_bridge *br) br->hash_elasticity = 4; br->hash_max = 512; + br->multicast_igmp_version = 2; br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; br->multicast_querier = 0; br->multicast_query_use_ifaddr = 0; @@ -2132,6 +2159,24 @@ unlock: return err; } +int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val) +{ + /* Currently we support only version 2 and 3 */ + switch (val) { + case 2: + case 3: + break; + default: + return -EINVAL; + } + + spin_lock_bh(&br->multicast_lock); + br->multicast_igmp_version = val; + spin_unlock_bh(&br->multicast_lock); + + return 0; +} + /** * br_multicast_list_adjacent - Returns snooped multicast addresses * @dev: The bridge port adjacent to which to retrieve addresses diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e99037c..10b9b80 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -858,6 +858,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 }, [IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 }, [IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 }, + [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -1069,6 +1070,15 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], mcast_stats = nla_get_u8(data[IFLA_BR_MCAST_STATS_ENABLED]); br->multicast_stats_enabled = !!mcast_stats; } + + if (data[IFLA_BR_MCAST_IGMP_VERSION]) { + __u8 igmp_version; + + igmp_version = nla_get_u8(data[IFLA_BR_MCAST_IGMP_VERSION]); + err = br_multicast_set_igmp_version(br, igmp_version); + if (err) + return err; + } #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (data[IFLA_BR_NF_CALL_IPTABLES]) { @@ -1135,6 +1145,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_INTVL */ nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */ nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_IGMP_VERSION */ #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_IPTABLES */ @@ -1210,7 +1221,9 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT, br->multicast_last_member_count) || nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT, - br->multicast_startup_query_count)) + br->multicast_startup_query_count) || + nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION, + br->multicast_igmp_version)) return -EMSGSIZE; clockval = jiffies_to_clock_t(br->multicast_last_member_interval); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1b63177..3d207d9 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -333,6 +333,8 @@ struct net_bridge u32 multicast_last_member_count; u32 multicast_startup_query_count; + u8 multicast_igmp_version; + unsigned long multicast_last_member_interval; unsigned long multicast_membership_interval; unsigned long multicast_querier_interval; @@ -582,6 +584,7 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val); int br_multicast_toggle(struct net_bridge *br, unsigned long val); int br_multicast_set_querier(struct net_bridge *br, unsigned long val); int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); +int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val); struct net_bridge_mdb_entry * br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst); struct net_bridge_mdb_entry * diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index e120307..f00d169 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -440,6 +440,23 @@ static ssize_t hash_max_store(struct device *d, struct device_attribute *attr, } static DEVICE_ATTR_RW(hash_max); +static ssize_t multicast_igmp_version_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct net_bridge *br = to_bridge(d); + + return sprintf(buf, "%u\n", br->multicast_igmp_version); +} + +static ssize_t multicast_igmp_version_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, br_multicast_set_igmp_version); +} +static DEVICE_ATTR_RW(multicast_igmp_version); + static ssize_t multicast_last_member_count_show(struct device *d, struct device_attribute *attr, char *buf) @@ -809,6 +826,7 @@ static struct attribute *bridge_attrs[] = { &dev_attr_multicast_query_response_interval.attr, &dev_attr_multicast_startup_query_interval.attr, &dev_attr_multicast_stats_enabled.attr, + &dev_attr_multicast_igmp_version.attr, #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) &dev_attr_nf_call_iptables.attr, -- cgit v1.1 From aa2ae3e71c74cc00ec22f133dc900b3817415785 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Mon, 21 Nov 2016 13:03:25 +0100 Subject: bridge: mcast: add MLDv2 querier support This patch adds basic support for MLDv2 queries, the default is MLDv1 as before. A new multicast option - multicast_mld_version, adds the ability to change it between 1 and 2 via netlink and sysfs. The MLD option is disabled if CONFIG_IPV6 is disabled. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/bridge/br_multicast.c | 89 +++++++++++++++++++++++++++++++++----------- net/bridge/br_netlink.c | 19 +++++++++- net/bridge/br_private.h | 4 ++ net/bridge/br_sysfs_br.c | 22 +++++++++++ 5 files changed, 113 insertions(+), 22 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 325d2601..92b2d49 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -276,6 +276,7 @@ enum { IFLA_BR_VLAN_STATS_ENABLED, IFLA_BR_MCAST_STATS_ENABLED, IFLA_BR_MCAST_IGMP_VERSION, + IFLA_BR_MCAST_MLD_VERSION, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 66192c1..b30e77e 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -459,15 +459,20 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, const struct in6_addr *grp, u8 *igmp_type) { - struct sk_buff *skb; + struct mld2_query *mld2q; + unsigned long interval; struct ipv6hdr *ip6h; struct mld_msg *mldq; + size_t mld_hdr_size; + struct sk_buff *skb; struct ethhdr *eth; u8 *hopopt; - unsigned long interval; + mld_hdr_size = sizeof(*mldq); + if (br->multicast_mld_version == 2) + mld_hdr_size = sizeof(*mld2q); skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*eth) + sizeof(*ip6h) + - 8 + sizeof(*mldq)); + 8 + mld_hdr_size); if (!skb) goto out; @@ -486,7 +491,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ip6h = ipv6_hdr(skb); *(__force __be32 *)ip6h = htonl(0x60000000); - ip6h->payload_len = htons(8 + sizeof(*mldq)); + ip6h->payload_len = htons(8 + mld_hdr_size); ip6h->nexthdr = IPPROTO_HOPOPTS; ip6h->hop_limit = 1; ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1)); @@ -514,26 +519,47 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, /* ICMPv6 */ skb_set_transport_header(skb, skb->len); - mldq = (struct mld_msg *) icmp6_hdr(skb); - interval = ipv6_addr_any(grp) ? br->multicast_query_response_interval : br->multicast_last_member_interval; - *igmp_type = ICMPV6_MGM_QUERY; - mldq->mld_type = ICMPV6_MGM_QUERY; - mldq->mld_code = 0; - mldq->mld_cksum = 0; - mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval)); - mldq->mld_reserved = 0; - mldq->mld_mca = *grp; - - /* checksum */ - mldq->mld_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, - sizeof(*mldq), IPPROTO_ICMPV6, - csum_partial(mldq, - sizeof(*mldq), 0)); - skb_put(skb, sizeof(*mldq)); + switch (br->multicast_mld_version) { + case 1: + mldq = (struct mld_msg *)icmp6_hdr(skb); + mldq->mld_type = ICMPV6_MGM_QUERY; + mldq->mld_code = 0; + mldq->mld_cksum = 0; + mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval)); + mldq->mld_reserved = 0; + mldq->mld_mca = *grp; + mldq->mld_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + sizeof(*mldq), IPPROTO_ICMPV6, + csum_partial(mldq, + sizeof(*mldq), + 0)); + break; + case 2: + mld2q = (struct mld2_query *)icmp6_hdr(skb); + mld2q->mld2q_mrc = ntohs((u16)jiffies_to_msecs(interval)); + mld2q->mld2q_type = ICMPV6_MGM_QUERY; + mld2q->mld2q_code = 0; + mld2q->mld2q_cksum = 0; + mld2q->mld2q_resv1 = 0; + mld2q->mld2q_resv2 = 0; + mld2q->mld2q_suppress = 0; + mld2q->mld2q_qrv = 2; + mld2q->mld2q_nsrcs = 0; + mld2q->mld2q_qqic = br->multicast_query_interval / HZ; + mld2q->mld2q_mca = *grp; + mld2q->mld2q_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + sizeof(*mld2q), + IPPROTO_ICMPV6, + csum_partial(mld2q, + sizeof(*mld2q), + 0)); + break; + } + skb_put(skb, mld_hdr_size); __skb_pull(skb, sizeof(*eth)); @@ -1842,7 +1868,6 @@ void br_multicast_init(struct net_bridge *br) br->hash_elasticity = 4; br->hash_max = 512; - br->multicast_igmp_version = 2; br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; br->multicast_querier = 0; br->multicast_query_use_ifaddr = 0; @@ -1858,7 +1883,9 @@ void br_multicast_init(struct net_bridge *br) br->ip4_other_query.delay_time = 0; br->ip4_querier.port = NULL; + br->multicast_igmp_version = 2; #if IS_ENABLED(CONFIG_IPV6) + br->multicast_mld_version = 1; br->ip6_other_query.delay_time = 0; br->ip6_querier.port = NULL; #endif @@ -2177,6 +2204,26 @@ int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val) return 0; } +#if IS_ENABLED(CONFIG_IPV6) +int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val) +{ + /* Currently we support version 1 and 2 */ + switch (val) { + case 1: + case 2: + break; + default: + return -EINVAL; + } + + spin_lock_bh(&br->multicast_lock); + br->multicast_mld_version = val; + spin_unlock_bh(&br->multicast_lock); + + return 0; +} +#endif + /** * br_multicast_list_adjacent - Returns snooped multicast addresses * @dev: The bridge port adjacent to which to retrieve addresses diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 10b9b80..71c7453 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -859,6 +859,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 }, [IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 }, [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 }, + [IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -1079,6 +1080,17 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], if (err) return err; } + +#if IS_ENABLED(CONFIG_IPV6) + if (data[IFLA_BR_MCAST_MLD_VERSION]) { + __u8 mld_version; + + mld_version = nla_get_u8(data[IFLA_BR_MCAST_MLD_VERSION]); + err = br_multicast_set_mld_version(br, mld_version); + if (err) + return err; + } +#endif #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (data[IFLA_BR_NF_CALL_IPTABLES]) { @@ -1146,6 +1158,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */ nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_IGMP_VERSION */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_MLD_VERSION */ #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_IPTABLES */ @@ -1225,7 +1238,11 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION, br->multicast_igmp_version)) return -EMSGSIZE; - +#if IS_ENABLED(CONFIG_IPV6) + if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION, + br->multicast_mld_version)) + return -EMSGSIZE; +#endif clockval = jiffies_to_clock_t(br->multicast_last_member_interval); if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_LAST_MEMBER_INTVL, clockval, IFLA_BR_PAD)) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 3d207d9..26aec23 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -355,6 +355,7 @@ struct net_bridge struct bridge_mcast_other_query ip6_other_query; struct bridge_mcast_own_query ip6_own_query; struct bridge_mcast_querier ip6_querier; + u8 multicast_mld_version; #endif /* IS_ENABLED(CONFIG_IPV6) */ #endif @@ -585,6 +586,9 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val); int br_multicast_set_querier(struct net_bridge *br, unsigned long val); int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val); +#if IS_ENABLED(CONFIG_IPV6) +int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val); +#endif struct net_bridge_mdb_entry * br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst); struct net_bridge_mdb_entry * diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index f00d169..c9d2e0a 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -659,6 +659,25 @@ static ssize_t multicast_stats_enabled_store(struct device *d, return store_bridge_parm(d, buf, len, set_stats_enabled); } static DEVICE_ATTR_RW(multicast_stats_enabled); + +#if IS_ENABLED(CONFIG_IPV6) +static ssize_t multicast_mld_version_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct net_bridge *br = to_bridge(d); + + return sprintf(buf, "%u\n", br->multicast_mld_version); +} + +static ssize_t multicast_mld_version_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, br_multicast_set_mld_version); +} +static DEVICE_ATTR_RW(multicast_mld_version); +#endif #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) static ssize_t nf_call_iptables_show( @@ -827,6 +846,9 @@ static struct attribute *bridge_attrs[] = { &dev_attr_multicast_startup_query_interval.attr, &dev_attr_multicast_stats_enabled.attr, &dev_attr_multicast_igmp_version.attr, +#if IS_ENABLED(CONFIG_IPV6) + &dev_attr_multicast_mld_version.attr, +#endif #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) &dev_attr_nf_call_iptables.attr, -- cgit v1.1 From 85f7e7508a1d288b513493196ef406c6c06134e1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 21 Nov 2016 14:18:37 +0100 Subject: tcp: add cwnd_undo functions to various tcp cc algorithms congestion control algorithms that do not halve cwnd in their .ssthresh should provide a .cwnd_undo rather than rely on current fallback which assumes reno halving (and thus doubles the cwnd). All of these do 'something else' in their .ssthresh implementation, thus store the cwnd on loss and provide .undo_cwnd to restore it again. A followup patch will remove the fallback and all algorithms will need to provide a .cwnd_undo function. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv4/tcp_highspeed.c | 11 ++++++++++- net/ipv4/tcp_illinois.c | 10 ++++++++++ net/ipv4/tcp_scalable.c | 15 +++++++++++++++ net/ipv4/tcp_veno.c | 10 ++++++++++ net/ipv4/tcp_yeah.c | 10 ++++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index db78424..6d9879e 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -94,6 +94,7 @@ static const struct hstcp_aimd_val { struct hstcp { u32 ai; + u32 loss_cwnd; }; static void hstcp_init(struct sock *sk) @@ -150,16 +151,24 @@ static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) static u32 hstcp_ssthresh(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); - const struct hstcp *ca = inet_csk_ca(sk); + struct hstcp *ca = inet_csk_ca(sk); + ca->loss_cwnd = tp->snd_cwnd; /* Do multiplicative decrease */ return max(tp->snd_cwnd - ((tp->snd_cwnd * hstcp_aimd_vals[ca->ai].md) >> 8), 2U); } +static u32 hstcp_cwnd_undo(struct sock *sk) +{ + const struct hstcp *ca = inet_csk_ca(sk); + + return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); +} static struct tcp_congestion_ops tcp_highspeed __read_mostly = { .init = hstcp_init, .ssthresh = hstcp_ssthresh, + .undo_cwnd = hstcp_cwnd_undo, .cong_avoid = hstcp_cong_avoid, .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index c8e6d86..60352ff 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -48,6 +48,7 @@ struct illinois { u32 end_seq; /* right edge of current RTT */ u32 alpha; /* Additive increase */ u32 beta; /* Muliplicative decrease */ + u32 loss_cwnd; /* cwnd on loss */ u16 acked; /* # packets acked by current ACK */ u8 rtt_above; /* average rtt has gone above threshold */ u8 rtt_low; /* # of rtts measurements below threshold */ @@ -296,10 +297,18 @@ static u32 tcp_illinois_ssthresh(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct illinois *ca = inet_csk_ca(sk); + ca->loss_cwnd = tp->snd_cwnd; /* Multiplicative decrease */ return max(tp->snd_cwnd - ((tp->snd_cwnd * ca->beta) >> BETA_SHIFT), 2U); } +static u32 tcp_illinois_cwnd_undo(struct sock *sk) +{ + const struct illinois *ca = inet_csk_ca(sk); + + return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); +} + /* Extract info for Tcp socket info provided via netlink. */ static size_t tcp_illinois_info(struct sock *sk, u32 ext, int *attr, union tcp_cc_info *info) @@ -327,6 +336,7 @@ static size_t tcp_illinois_info(struct sock *sk, u32 ext, int *attr, static struct tcp_congestion_ops tcp_illinois __read_mostly = { .init = tcp_illinois_init, .ssthresh = tcp_illinois_ssthresh, + .undo_cwnd = tcp_illinois_cwnd_undo, .cong_avoid = tcp_illinois_cong_avoid, .set_state = tcp_illinois_state, .get_info = tcp_illinois_info, diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index bf5ea9e..f212307 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -15,6 +15,10 @@ #define TCP_SCALABLE_AI_CNT 50U #define TCP_SCALABLE_MD_SCALE 3 +struct scalable { + u32 loss_cwnd; +}; + static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); @@ -32,12 +36,23 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked) static u32 tcp_scalable_ssthresh(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); + struct scalable *ca = inet_csk_ca(sk); + + ca->loss_cwnd = tp->snd_cwnd; return max(tp->snd_cwnd - (tp->snd_cwnd>>TCP_SCALABLE_MD_SCALE), 2U); } +static u32 tcp_scalable_cwnd_undo(struct sock *sk) +{ + const struct scalable *ca = inet_csk_ca(sk); + + return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); +} + static struct tcp_congestion_ops tcp_scalable __read_mostly = { .ssthresh = tcp_scalable_ssthresh, + .undo_cwnd = tcp_scalable_cwnd_undo, .cong_avoid = tcp_scalable_cong_avoid, .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index 40171e1..76005d4 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -30,6 +30,7 @@ struct veno { u32 basertt; /* the min of all Veno rtt measurements seen (in usec) */ u32 inc; /* decide whether to increase cwnd */ u32 diff; /* calculate the diff rate */ + u32 loss_cwnd; /* cwnd when loss occured */ }; /* There are several situations when we must "re-start" Veno: @@ -193,6 +194,7 @@ static u32 tcp_veno_ssthresh(struct sock *sk) const struct tcp_sock *tp = tcp_sk(sk); struct veno *veno = inet_csk_ca(sk); + veno->loss_cwnd = tp->snd_cwnd; if (veno->diff < beta) /* in "non-congestive state", cut cwnd by 1/5 */ return max(tp->snd_cwnd * 4 / 5, 2U); @@ -201,9 +203,17 @@ static u32 tcp_veno_ssthresh(struct sock *sk) return max(tp->snd_cwnd >> 1U, 2U); } +static u32 tcp_veno_cwnd_undo(struct sock *sk) +{ + const struct veno *veno = inet_csk_ca(sk); + + return max(tcp_sk(sk)->snd_cwnd, veno->loss_cwnd); +} + static struct tcp_congestion_ops tcp_veno __read_mostly = { .init = tcp_veno_init, .ssthresh = tcp_veno_ssthresh, + .undo_cwnd = tcp_veno_cwnd_undo, .cong_avoid = tcp_veno_cong_avoid, .pkts_acked = tcp_veno_pkts_acked, .set_state = tcp_veno_state, diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index 9c5fc97..e6ff99c 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -37,6 +37,7 @@ struct yeah { u32 fast_count; u32 pkts_acked; + u32 loss_cwnd; }; static void tcp_yeah_init(struct sock *sk) @@ -219,13 +220,22 @@ static u32 tcp_yeah_ssthresh(struct sock *sk) yeah->fast_count = 0; yeah->reno_count = max(yeah->reno_count>>1, 2U); + yeah->loss_cwnd = tp->snd_cwnd; return max_t(int, tp->snd_cwnd - reduction, 2); } +static u32 tcp_yeah_cwnd_undo(struct sock *sk) +{ + const struct yeah *yeah = inet_csk_ca(sk); + + return max(tcp_sk(sk)->snd_cwnd, yeah->loss_cwnd); +} + static struct tcp_congestion_ops tcp_yeah __read_mostly = { .init = tcp_yeah_init, .ssthresh = tcp_yeah_ssthresh, + .undo_cwnd = tcp_yeah_cwnd_undo, .cong_avoid = tcp_yeah_cong_avoid, .set_state = tcp_vegas_state, .cwnd_event = tcp_vegas_cwnd_event, -- cgit v1.1 From e97991832a4ea4a5f47d65f068a4c966a2eb5730 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 21 Nov 2016 14:18:38 +0100 Subject: tcp: make undo_cwnd mandatory for congestion modules The undo_cwnd fallback in the stack doubles cwnd based on ssthresh, which un-does reno halving behaviour. It seems more appropriate to let congctl algorithms pair .ssthresh and .undo_cwnd properly. Add a 'tcp_reno_undo_cwnd' function and wire it up for all congestion algorithms that used to rely on the fallback. Cc: Eric Dumazet Cc: Yuchung Cheng Cc: Neal Cardwell Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_cong.c | 14 ++++++++++++-- net/ipv4/tcp_dctcp.c | 1 + net/ipv4/tcp_hybla.c | 1 + net/ipv4/tcp_input.c | 5 +---- net/ipv4/tcp_lp.c | 1 + net/ipv4/tcp_vegas.c | 1 + net/ipv4/tcp_westwood.c | 1 + 8 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 123979f..7de8073 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -958,6 +958,7 @@ u32 tcp_slow_start(struct tcp_sock *tp, u32 acked); void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked); u32 tcp_reno_ssthresh(struct sock *sk); +u32 tcp_reno_undo_cwnd(struct sock *sk); void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked); extern struct tcp_congestion_ops tcp_reno; diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 1294af4..38905ec 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -68,8 +68,9 @@ int tcp_register_congestion_control(struct tcp_congestion_ops *ca) { int ret = 0; - /* all algorithms must implement ssthresh and cong_avoid ops */ - if (!ca->ssthresh || !(ca->cong_avoid || ca->cong_control)) { + /* all algorithms must implement these */ + if (!ca->ssthresh || !ca->undo_cwnd || + !(ca->cong_avoid || ca->cong_control)) { pr_err("%s does not implement required ops\n", ca->name); return -EINVAL; } @@ -441,10 +442,19 @@ u32 tcp_reno_ssthresh(struct sock *sk) } EXPORT_SYMBOL_GPL(tcp_reno_ssthresh); +u32 tcp_reno_undo_cwnd(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + + return max(tp->snd_cwnd, tp->snd_ssthresh << 1); +} +EXPORT_SYMBOL_GPL(tcp_reno_undo_cwnd); + struct tcp_congestion_ops tcp_reno = { .flags = TCP_CONG_NON_RESTRICTED, .name = "reno", .owner = THIS_MODULE, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, + .undo_cwnd = tcp_reno_undo_cwnd, }; diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c index 5113917..bde22eb 100644 --- a/net/ipv4/tcp_dctcp.c +++ b/net/ipv4/tcp_dctcp.c @@ -342,6 +342,7 @@ static struct tcp_congestion_ops dctcp __read_mostly = { static struct tcp_congestion_ops dctcp_reno __read_mostly = { .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, + .undo_cwnd = tcp_reno_undo_cwnd, .get_info = dctcp_get_info, .owner = THIS_MODULE, .name = "dctcp-reno", diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index 083831e..0f7175c 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -166,6 +166,7 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked) static struct tcp_congestion_ops tcp_hybla __read_mostly = { .init = hybla_init, .ssthresh = tcp_reno_ssthresh, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = hybla_cong_avoid, .set_state = hybla_state, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a70046f..22e6a20 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2394,10 +2394,7 @@ static void tcp_undo_cwnd_reduction(struct sock *sk, bool unmark_loss) if (tp->prior_ssthresh) { const struct inet_connection_sock *icsk = inet_csk(sk); - if (icsk->icsk_ca_ops->undo_cwnd) - tp->snd_cwnd = icsk->icsk_ca_ops->undo_cwnd(sk); - else - tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh << 1); + tp->snd_cwnd = icsk->icsk_ca_ops->undo_cwnd(sk); if (tp->prior_ssthresh > tp->snd_ssthresh) { tp->snd_ssthresh = tp->prior_ssthresh; diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index c67ece1..046fd39 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -316,6 +316,7 @@ static void tcp_lp_pkts_acked(struct sock *sk, const struct ack_sample *sample) static struct tcp_congestion_ops tcp_lp __read_mostly = { .init = tcp_lp_init, .ssthresh = tcp_reno_ssthresh, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = tcp_lp_cong_avoid, .pkts_acked = tcp_lp_pkts_acked, diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index 4c4bac1..218cfcc 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -307,6 +307,7 @@ EXPORT_SYMBOL_GPL(tcp_vegas_get_info); static struct tcp_congestion_ops tcp_vegas __read_mostly = { .init = tcp_vegas_init, .ssthresh = tcp_reno_ssthresh, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = tcp_vegas_cong_avoid, .pkts_acked = tcp_vegas_pkts_acked, .set_state = tcp_vegas_state, diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index 4b03a2e..fed66dc 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -278,6 +278,7 @@ static struct tcp_congestion_ops tcp_westwood __read_mostly = { .init = tcp_westwood_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, + .undo_cwnd = tcp_reno_undo_cwnd, .cwnd_event = tcp_westwood_event, .in_ack_event = tcp_westwood_ack, .get_info = tcp_westwood_info, -- cgit v1.1 From 9b4437a5b870cb3875a63beceae5d1984ee19a22 Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Mon, 21 Nov 2016 11:02:58 -0800 Subject: geneve: Unify LWT and netdev handling. Current geneve implementation has two separate cases to handle. 1. netdev xmit 2. LWT xmit. In case of netdev, geneve configuration is stored in various struct geneve_dev members. For example geneve_addr, ttl, tos, label, flags, dst_cache, etc. For LWT ip_tunnel_info is passed to the device in ip_tunnel_info. Following patch uses ip_tunnel_info struct to store almost all of configuration of a geneve netdevice. This allows us to unify most of geneve driver code around ip_tunnel_info struct. This dramatically simplify geneve code, since it does not need to handle two different configuration cases. Removes duplicate code, single code path can handle either type of geneve devices. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/geneve.c | 612 ++++++++++++++++++++++----------------------------- 1 file changed, 263 insertions(+), 349 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 90dc6b1..70f2a32 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -45,41 +45,22 @@ struct geneve_net { static unsigned int geneve_net_id; -union geneve_addr { - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - struct sockaddr sa; -}; - -static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, }; - /* Pseudo network device */ struct geneve_dev { struct hlist_node hlist; /* vni hash table */ struct net *net; /* netns for packet i/o */ struct net_device *dev; /* netdev for geneve tunnel */ + struct ip_tunnel_info info; struct geneve_sock __rcu *sock4; /* IPv4 socket used for geneve tunnel */ #if IS_ENABLED(CONFIG_IPV6) struct geneve_sock __rcu *sock6; /* IPv6 socket used for geneve tunnel */ #endif - u8 vni[3]; /* virtual network ID for tunnel */ - u8 ttl; /* TTL override */ - u8 tos; /* TOS override */ - union geneve_addr remote; /* IP address for link partner */ struct list_head next; /* geneve's per namespace list */ - __be32 label; /* IPv6 flowlabel override */ - __be16 dst_port; - bool collect_md; struct gro_cells gro_cells; - u32 flags; - struct dst_cache dst_cache; + bool collect_md; + bool use_udp6_rx_checksums; }; -/* Geneve device flags */ -#define GENEVE_F_UDP_ZERO_CSUM_TX BIT(0) -#define GENEVE_F_UDP_ZERO_CSUM6_TX BIT(1) -#define GENEVE_F_UDP_ZERO_CSUM6_RX BIT(2) - struct geneve_sock { bool collect_md; struct list_head list; @@ -87,7 +68,6 @@ struct geneve_sock { struct rcu_head rcu; int refcnt; struct hlist_head vni_list[VNI_HASH_SIZE]; - u32 flags; }; static inline __u32 geneve_net_vni_hash(u8 vni[3]) @@ -109,6 +89,20 @@ static __be64 vni_to_tunnel_id(const __u8 *vni) #endif } +/* Convert 64 bit tunnel ID to 24 bit VNI. */ +static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni) +{ +#ifdef __BIG_ENDIAN + vni[0] = (__force __u8)(tun_id >> 16); + vni[1] = (__force __u8)(tun_id >> 8); + vni[2] = (__force __u8)tun_id; +#else + vni[0] = (__force __u8)((__force u64)tun_id >> 40); + vni[1] = (__force __u8)((__force u64)tun_id >> 48); + vni[2] = (__force __u8)((__force u64)tun_id >> 56); +#endif +} + static sa_family_t geneve_get_sk_family(struct geneve_sock *gs) { return gs->sock->sk->sk_family; @@ -117,6 +111,7 @@ static sa_family_t geneve_get_sk_family(struct geneve_sock *gs) static struct geneve_dev *geneve_lookup(struct geneve_sock *gs, __be32 addr, u8 vni[]) { + __be64 id = vni_to_tunnel_id(vni); struct hlist_head *vni_list_head; struct geneve_dev *geneve; __u32 hash; @@ -125,8 +120,8 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs, hash = geneve_net_vni_hash(vni); vni_list_head = &gs->vni_list[hash]; hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) { - if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) && - addr == geneve->remote.sin.sin_addr.s_addr) + if (!memcmp(&id, &geneve->info.key.tun_id, sizeof(id)) && + addr == geneve->info.key.u.ipv4.dst) return geneve; } return NULL; @@ -136,6 +131,7 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs, static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs, struct in6_addr addr6, u8 vni[]) { + __be64 id = vni_to_tunnel_id(vni); struct hlist_head *vni_list_head; struct geneve_dev *geneve; __u32 hash; @@ -144,8 +140,8 @@ static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs, hash = geneve_net_vni_hash(vni); vni_list_head = &gs->vni_list[hash]; hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) { - if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) && - ipv6_addr_equal(&addr6, &geneve->remote.sin6.sin6_addr)) + if (!memcmp(&id, &geneve->info.key.tun_id, sizeof(id)) && + ipv6_addr_equal(&addr6, &geneve->info.key.u.ipv6.dst)) return geneve; } return NULL; @@ -160,15 +156,12 @@ static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb) static struct geneve_dev *geneve_lookup_skb(struct geneve_sock *gs, struct sk_buff *skb) { - u8 *vni; - __be32 addr; static u8 zero_vni[3]; -#if IS_ENABLED(CONFIG_IPV6) - static struct in6_addr zero_addr6; -#endif + u8 *vni; if (geneve_get_sk_family(gs) == AF_INET) { struct iphdr *iph; + __be32 addr; iph = ip_hdr(skb); /* outer IP header... */ @@ -183,6 +176,7 @@ static struct geneve_dev *geneve_lookup_skb(struct geneve_sock *gs, return geneve_lookup(gs, addr, vni); #if IS_ENABLED(CONFIG_IPV6) } else if (geneve_get_sk_family(gs) == AF_INET6) { + static struct in6_addr zero_addr6; struct ipv6hdr *ip6h; struct in6_addr addr6; @@ -305,13 +299,12 @@ static int geneve_init(struct net_device *dev) return err; } - err = dst_cache_init(&geneve->dst_cache, GFP_KERNEL); + err = dst_cache_init(&geneve->info.dst_cache, GFP_KERNEL); if (err) { free_percpu(dev->tstats); gro_cells_destroy(&geneve->gro_cells); return err; } - return 0; } @@ -319,7 +312,7 @@ static void geneve_uninit(struct net_device *dev) { struct geneve_dev *geneve = netdev_priv(dev); - dst_cache_destroy(&geneve->dst_cache); + dst_cache_destroy(&geneve->info.dst_cache); gro_cells_destroy(&geneve->gro_cells); free_percpu(dev->tstats); } @@ -368,7 +361,7 @@ drop: } static struct socket *geneve_create_sock(struct net *net, bool ipv6, - __be16 port, u32 flags) + __be16 port, bool ipv6_rx_csum) { struct socket *sock; struct udp_port_cfg udp_conf; @@ -379,8 +372,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6, if (ipv6) { udp_conf.family = AF_INET6; udp_conf.ipv6_v6only = 1; - udp_conf.use_udp6_rx_checksums = - !(flags & GENEVE_F_UDP_ZERO_CSUM6_RX); + udp_conf.use_udp6_rx_checksums = ipv6_rx_csum; } else { udp_conf.family = AF_INET; udp_conf.local_ip.s_addr = htonl(INADDR_ANY); @@ -491,7 +483,7 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb, /* Create new listen socket if needed */ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, - bool ipv6, u32 flags) + bool ipv6, bool ipv6_rx_csum) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; @@ -503,7 +495,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, if (!gs) return ERR_PTR(-ENOMEM); - sock = geneve_create_sock(net, ipv6, port, flags); + sock = geneve_create_sock(net, ipv6, port, ipv6_rx_csum); if (IS_ERR(sock)) { kfree(gs); return ERR_CAST(sock); @@ -579,21 +571,22 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6) struct net *net = geneve->net; struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; + __u8 vni[3]; __u32 hash; - gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port); + gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->info.key.tp_dst); if (gs) { gs->refcnt++; goto out; } - gs = geneve_socket_create(net, geneve->dst_port, ipv6, geneve->flags); + gs = geneve_socket_create(net, geneve->info.key.tp_dst, ipv6, + geneve->use_udp6_rx_checksums); if (IS_ERR(gs)) return PTR_ERR(gs); out: gs->collect_md = geneve->collect_md; - gs->flags = geneve->flags; #if IS_ENABLED(CONFIG_IPV6) if (ipv6) rcu_assign_pointer(geneve->sock6, gs); @@ -601,7 +594,8 @@ out: #endif rcu_assign_pointer(geneve->sock4, gs); - hash = geneve_net_vni_hash(geneve->vni); + tunnel_id_to_vni(geneve->info.key.tun_id, vni); + hash = geneve_net_vni_hash(vni); hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]); return 0; } @@ -609,7 +603,7 @@ out: static int geneve_open(struct net_device *dev) { struct geneve_dev *geneve = netdev_priv(dev); - bool ipv6 = geneve->remote.sa.sa_family == AF_INET6; + bool ipv6 = !!(geneve->info.mode & IP_TUNNEL_INFO_IPV6); bool metadata = geneve->collect_md; int ret = 0; @@ -653,12 +647,12 @@ static void geneve_build_header(struct genevehdr *geneveh, static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb, __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt, - u32 flags, bool xnet) + bool xnet) { + bool udp_sum = !!(tun_flags & TUNNEL_CSUM); struct genevehdr *gnvh; int min_headroom; int err; - bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM_TX); skb_scrub_packet(skb, xnet); @@ -686,12 +680,12 @@ free_rt: #if IS_ENABLED(CONFIG_IPV6) static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb, __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt, - u32 flags, bool xnet) + bool xnet) { + bool udp_sum = !!(tun_flags & TUNNEL_CSUM); struct genevehdr *gnvh; int min_headroom; int err; - bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM6_TX); skb_scrub_packet(skb, xnet); @@ -734,32 +728,22 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_mark = skb->mark; fl4->flowi4_proto = IPPROTO_UDP; + fl4->daddr = info->key.u.ipv4.dst; + fl4->saddr = info->key.u.ipv4.src; - if (info) { - fl4->daddr = info->key.u.ipv4.dst; - fl4->saddr = info->key.u.ipv4.src; - fl4->flowi4_tos = RT_TOS(info->key.tos); - dst_cache = &info->dst_cache; - } else { - tos = geneve->tos; - if (tos == 1) { - const struct iphdr *iip = ip_hdr(skb); - - tos = ip_tunnel_get_dsfield(iip, skb); - use_cache = false; - } - - fl4->flowi4_tos = RT_TOS(tos); - fl4->daddr = geneve->remote.sin.sin_addr.s_addr; - dst_cache = &geneve->dst_cache; + tos = info->key.tos; + if ((tos == 1) && !geneve->collect_md) { + tos = ip_tunnel_get_dsfield(ip_hdr(skb), skb); + use_cache = false; } + fl4->flowi4_tos = RT_TOS(tos); + dst_cache = &info->dst_cache; if (use_cache) { rt = dst_cache_get_ip4(dst_cache, &fl4->saddr); if (rt) return rt; } - rt = ip_route_output_key(geneve->net, fl4); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr); @@ -795,34 +779,22 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, memset(fl6, 0, sizeof(*fl6)); fl6->flowi6_mark = skb->mark; fl6->flowi6_proto = IPPROTO_UDP; - - if (info) { - fl6->daddr = info->key.u.ipv6.dst; - fl6->saddr = info->key.u.ipv6.src; - fl6->flowlabel = ip6_make_flowinfo(RT_TOS(info->key.tos), - info->key.label); - dst_cache = &info->dst_cache; - } else { - prio = geneve->tos; - if (prio == 1) { - const struct iphdr *iip = ip_hdr(skb); - - prio = ip_tunnel_get_dsfield(iip, skb); - use_cache = false; - } - - fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio), - geneve->label); - fl6->daddr = geneve->remote.sin6.sin6_addr; - dst_cache = &geneve->dst_cache; + fl6->daddr = info->key.u.ipv6.dst; + fl6->saddr = info->key.u.ipv6.src; + prio = info->key.tos; + if ((prio == 1) && !geneve->collect_md) { + prio = ip_tunnel_get_dsfield(ip_hdr(skb), skb); + use_cache = false; } + fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio), + info->key.label); + dst_cache = &info->dst_cache; if (use_cache) { dst = dst_cache_get_ip6(dst_cache, &fl6->saddr); if (dst) return dst; } - if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6)) { netdev_dbg(dev, "no route to %pI6\n", &fl6->daddr); return ERR_PTR(-ENETUNREACH); @@ -839,195 +811,130 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, } #endif -/* Convert 64 bit tunnel ID to 24 bit VNI. */ -static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni) -{ -#ifdef __BIG_ENDIAN - vni[0] = (__force __u8)(tun_id >> 16); - vni[1] = (__force __u8)(tun_id >> 8); - vni[2] = (__force __u8)tun_id; -#else - vni[0] = (__force __u8)((__force u64)tun_id >> 40); - vni[1] = (__force __u8)((__force u64)tun_id >> 48); - vni[2] = (__force __u8)((__force u64)tun_id >> 56); -#endif -} - -static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, - struct ip_tunnel_info *info) +static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, + struct geneve_dev *geneve, struct ip_tunnel_info *info) { - struct geneve_dev *geneve = netdev_priv(dev); - struct geneve_sock *gs4; - struct rtable *rt = NULL; - const struct iphdr *iip; /* interior IP header */ + bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); + struct geneve_sock *gs4 = rcu_dereference(geneve->sock4); + const struct ip_tunnel_key *key = &info->key; + struct rtable *rt; int err = -EINVAL; struct flowi4 fl4; + u8 *opts = NULL; __u8 tos, ttl; __be16 sport; __be16 df; - bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); - u32 flags = geneve->flags; + u8 vni[3]; - gs4 = rcu_dereference(geneve->sock4); if (!gs4) - goto tx_error; - - if (geneve->collect_md) { - if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) { - netdev_dbg(dev, "no tunnel metadata\n"); - goto tx_error; - } - if (info && ip_tunnel_info_af(info) != AF_INET) - goto tx_error; - } + return err; rt = geneve_get_v4_rt(skb, dev, &fl4, info); - if (IS_ERR(rt)) { - err = PTR_ERR(rt); - goto tx_error; - } + if (IS_ERR(rt)) + return PTR_ERR(rt); sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); - skb_reset_mac_header(skb); - - iip = ip_hdr(skb); - - if (info) { - const struct ip_tunnel_key *key = &info->key; - u8 *opts = NULL; - u8 vni[3]; - - tunnel_id_to_vni(key->tun_id, vni); - if (info->options_len) - opts = ip_tunnel_info_opts(info); - - if (key->tun_flags & TUNNEL_CSUM) - flags &= ~GENEVE_F_UDP_ZERO_CSUM_TX; - else - flags |= GENEVE_F_UDP_ZERO_CSUM_TX; - - err = geneve_build_skb(rt, skb, key->tun_flags, vni, - info->options_len, opts, flags, xnet); - if (unlikely(err)) - goto tx_error; - - tos = ip_tunnel_ecn_encap(key->tos, iip, skb); + if (geneve->collect_md) { + tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); ttl = key->ttl; - df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { - err = geneve_build_skb(rt, skb, 0, geneve->vni, - 0, NULL, flags, xnet); - if (unlikely(err)) - goto tx_error; - - tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb); - ttl = geneve->ttl; - if (!ttl && IN_MULTICAST(ntohl(fl4.daddr))) - ttl = 1; - ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); - df = 0; + tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb); + ttl = key->ttl ? : ip4_dst_hoplimit(&rt->dst); } - udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr, - tos, ttl, df, sport, geneve->dst_port, - !net_eq(geneve->net, dev_net(geneve->dev)), - !!(flags & GENEVE_F_UDP_ZERO_CSUM_TX)); + df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; - return NETDEV_TX_OK; - -tx_error: - dev_kfree_skb(skb); + tunnel_id_to_vni(key->tun_id, vni); + if (info->options_len) + opts = ip_tunnel_info_opts(info); - if (err == -ELOOP) - dev->stats.collisions++; - else if (err == -ENETUNREACH) - dev->stats.tx_carrier_errors++; + skb_reset_mac_header(skb); + err = geneve_build_skb(rt, skb, key->tun_flags, vni, + info->options_len, opts, xnet); + if (unlikely(err)) + return err; - dev->stats.tx_errors++; - return NETDEV_TX_OK; + udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr, + tos, ttl, df, sport, geneve->info.key.tp_dst, + !net_eq(geneve->net, dev_net(geneve->dev)), + !(info->key.tun_flags & TUNNEL_CSUM)); + return 0; } #if IS_ENABLED(CONFIG_IPV6) -static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, - struct ip_tunnel_info *info) +static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, + struct geneve_dev *geneve, struct ip_tunnel_info *info) { - struct geneve_dev *geneve = netdev_priv(dev); + bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); + struct geneve_sock *gs6 = rcu_dereference(geneve->sock6); + const struct ip_tunnel_key *key = &info->key; struct dst_entry *dst = NULL; - const struct iphdr *iip; /* interior IP header */ - struct geneve_sock *gs6; int err = -EINVAL; struct flowi6 fl6; + u8 *opts = NULL; __u8 prio, ttl; __be16 sport; - __be32 label; - bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); - u32 flags = geneve->flags; + u8 vni[3]; - gs6 = rcu_dereference(geneve->sock6); if (!gs6) - goto tx_error; - - if (geneve->collect_md) { - if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) { - netdev_dbg(dev, "no tunnel metadata\n"); - goto tx_error; - } - } + return err; dst = geneve_get_v6_dst(skb, dev, &fl6, info); - if (IS_ERR(dst)) { - err = PTR_ERR(dst); - goto tx_error; - } + if (IS_ERR(dst)) + return PTR_ERR(dst); sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); - skb_reset_mac_header(skb); - - iip = ip_hdr(skb); + if (geneve->collect_md) { + prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); + ttl = key->ttl; + } else { + prio = ip_tunnel_ecn_encap(ip6_tclass(fl6.flowlabel), + ip_hdr(skb), skb); + ttl = key->ttl ? : ip6_dst_hoplimit(dst); + } + tunnel_id_to_vni(key->tun_id, vni); + if (info->options_len) + opts = ip_tunnel_info_opts(info); - if (info) { - const struct ip_tunnel_key *key = &info->key; - u8 *opts = NULL; - u8 vni[3]; + skb_reset_mac_header(skb); + err = geneve6_build_skb(dst, skb, key->tun_flags, vni, + info->options_len, opts, xnet); + if (unlikely(err)) + return err; - tunnel_id_to_vni(key->tun_id, vni); - if (info->options_len) - opts = ip_tunnel_info_opts(info); + udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev, + &fl6.saddr, &fl6.daddr, prio, ttl, + info->key.label, sport, geneve->info.key.tp_dst, + !(info->key.tun_flags & TUNNEL_CSUM)); + return 0; +} +#endif - if (key->tun_flags & TUNNEL_CSUM) - flags &= ~GENEVE_F_UDP_ZERO_CSUM6_TX; - else - flags |= GENEVE_F_UDP_ZERO_CSUM6_TX; +static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct geneve_dev *geneve = netdev_priv(dev); + struct ip_tunnel_info *info = NULL; + int err; - err = geneve6_build_skb(dst, skb, key->tun_flags, vni, - info->options_len, opts, - flags, xnet); - if (unlikely(err)) + if (geneve->collect_md) { + info = skb_tunnel_info(skb); + if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) { + err = -EINVAL; + netdev_dbg(dev, "no tunnel metadata\n"); goto tx_error; - - prio = ip_tunnel_ecn_encap(key->tos, iip, skb); - ttl = key->ttl; - label = info->key.label; + } } else { - err = geneve6_build_skb(dst, skb, 0, geneve->vni, - 0, NULL, flags, xnet); - if (unlikely(err)) - goto tx_error; - - prio = ip_tunnel_ecn_encap(ip6_tclass(fl6.flowlabel), - iip, skb); - ttl = geneve->ttl; - if (!ttl && ipv6_addr_is_multicast(&fl6.daddr)) - ttl = 1; - ttl = ttl ? : ip6_dst_hoplimit(dst); - label = geneve->label; + info = &geneve->info; } - udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev, - &fl6.saddr, &fl6.daddr, prio, ttl, label, - sport, geneve->dst_port, - !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX)); - return NETDEV_TX_OK; +#if IS_ENABLED(CONFIG_IPV6) + if (info->mode & IP_TUNNEL_INFO_IPV6) + err = geneve6_xmit_skb(skb, dev, geneve, info); + else +#endif + err = geneve_xmit_skb(skb, dev, geneve, info); + if (likely(!err)) + return NETDEV_TX_OK; tx_error: dev_kfree_skb(skb); @@ -1039,23 +946,6 @@ tx_error: dev->stats.tx_errors++; return NETDEV_TX_OK; } -#endif - -static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct geneve_dev *geneve = netdev_priv(dev); - struct ip_tunnel_info *info = NULL; - - if (geneve->collect_md) - info = skb_tunnel_info(skb); - -#if IS_ENABLED(CONFIG_IPV6) - if ((info && ip_tunnel_info_af(info) == AF_INET6) || - (!info && geneve->remote.sa.sa_family == AF_INET6)) - return geneve6_xmit_skb(skb, dev, info); -#endif - return geneve_xmit_skb(skb, dev, info); -} static int geneve_change_mtu(struct net_device *dev, int new_mtu) { @@ -1073,14 +963,11 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) { struct ip_tunnel_info *info = skb_tunnel_info(skb); struct geneve_dev *geneve = netdev_priv(dev); - struct rtable *rt; - struct flowi4 fl4; -#if IS_ENABLED(CONFIG_IPV6) - struct dst_entry *dst; - struct flowi6 fl6; -#endif if (ip_tunnel_info_af(info) == AF_INET) { + struct rtable *rt; + struct flowi4 fl4; + rt = geneve_get_v4_rt(skb, dev, &fl4, info); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -1089,6 +976,9 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) info->key.u.ipv4.src = fl4.saddr; #if IS_ENABLED(CONFIG_IPV6) } else if (ip_tunnel_info_af(info) == AF_INET6) { + struct dst_entry *dst; + struct flowi6 fl6; + dst = geneve_get_v6_dst(skb, dev, &fl6, info); if (IS_ERR(dst)) return PTR_ERR(dst); @@ -1102,7 +992,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) info->key.tp_src = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); - info->key.tp_dst = geneve->dst_port; + info->key.tp_dst = geneve->info.key.tp_dst; return 0; } @@ -1224,78 +1114,69 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[]) } static struct geneve_dev *geneve_find_dev(struct geneve_net *gn, - __be16 dst_port, - union geneve_addr *remote, - u8 vni[], + const struct ip_tunnel_info *info, bool *tun_on_same_port, bool *tun_collect_md) { - struct geneve_dev *geneve, *t; + struct geneve_dev *geneve, *t = NULL; *tun_on_same_port = false; *tun_collect_md = false; - t = NULL; list_for_each_entry(geneve, &gn->geneve_list, next) { - if (geneve->dst_port == dst_port) { + if (info->key.tp_dst == geneve->info.key.tp_dst) { *tun_collect_md = geneve->collect_md; *tun_on_same_port = true; } - if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) && - !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) && - dst_port == geneve->dst_port) + if (info->key.tun_id == geneve->info.key.tun_id && + info->key.tp_dst == geneve->info.key.tp_dst && + !memcmp(&info->key.u, &geneve->info.key.u, sizeof(info->key.u))) t = geneve; } return t; } +static bool is_all_zero(const u8 *fp, size_t size) +{ + int i; + + for (i = 0; i < size; i++) + if (fp[i]) + return false; + return true; +} + +static bool is_tnl_info_zero(const struct ip_tunnel_info *info) +{ + if (info->key.tun_id || info->key.tun_flags || info->key.tos || + info->key.ttl || info->key.label || info->key.tp_src || + !is_all_zero((const u8 *)&info->key.u, sizeof(info->key.u))) + return false; + else + return true; +} + static int geneve_configure(struct net *net, struct net_device *dev, - union geneve_addr *remote, - __u32 vni, __u8 ttl, __u8 tos, __be32 label, - __be16 dst_port, bool metadata, u32 flags) + const struct ip_tunnel_info *info, + bool metadata, bool ipv6_rx_csum) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_dev *t, *geneve = netdev_priv(dev); bool tun_collect_md, tun_on_same_port; int err, encap_len; - if (!remote) - return -EINVAL; - if (metadata && - (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl || label)) + if (metadata && !is_tnl_info_zero(info)) return -EINVAL; geneve->net = net; geneve->dev = dev; - geneve->vni[0] = (vni & 0x00ff0000) >> 16; - geneve->vni[1] = (vni & 0x0000ff00) >> 8; - geneve->vni[2] = vni & 0x000000ff; - - if ((remote->sa.sa_family == AF_INET && - IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) || - (remote->sa.sa_family == AF_INET6 && - ipv6_addr_is_multicast(&remote->sin6.sin6_addr))) - return -EINVAL; - if (label && remote->sa.sa_family != AF_INET6) - return -EINVAL; - - geneve->remote = *remote; - - geneve->ttl = ttl; - geneve->tos = tos; - geneve->label = label; - geneve->dst_port = dst_port; - geneve->collect_md = metadata; - geneve->flags = flags; - - t = geneve_find_dev(gn, dst_port, remote, geneve->vni, - &tun_on_same_port, &tun_collect_md); + t = geneve_find_dev(gn, info, &tun_on_same_port, &tun_collect_md); if (t) return -EBUSY; /* make enough headroom for basic scenario */ encap_len = GENEVE_BASE_HLEN + ETH_HLEN; - if (remote->sa.sa_family == AF_INET) { + if (ip_tunnel_info_af(info) == AF_INET) { encap_len += sizeof(struct iphdr); dev->max_mtu -= sizeof(struct iphdr); } else { @@ -1312,7 +1193,10 @@ static int geneve_configure(struct net *net, struct net_device *dev, return -EPERM; } - dst_cache_reset(&geneve->dst_cache); + dst_cache_reset(&geneve->info.dst_cache); + geneve->info = *info; + geneve->collect_md = metadata; + geneve->use_udp6_rx_checksums = ipv6_rx_csum; err = register_netdevice(dev); if (err) @@ -1322,74 +1206,99 @@ static int geneve_configure(struct net *net, struct net_device *dev, return 0; } +static void init_tnl_info(struct ip_tunnel_info *info, __u16 dst_port) +{ + memset(info, 0, sizeof(*info)); + info->key.tp_dst = htons(dst_port); +} + static int geneve_newlink(struct net *net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - __be16 dst_port = htons(GENEVE_UDP_PORT); - __u8 ttl = 0, tos = 0; + bool use_udp6_rx_checksums = false; + struct ip_tunnel_info info; bool metadata = false; - union geneve_addr remote = geneve_remote_unspec; - __be32 label = 0; - __u32 vni = 0; - u32 flags = 0; + + init_tnl_info(&info, GENEVE_UDP_PORT); if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) return -EINVAL; if (data[IFLA_GENEVE_REMOTE]) { - remote.sa.sa_family = AF_INET; - remote.sin.sin_addr.s_addr = + info.key.u.ipv4.dst = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]); + + if (IN_MULTICAST(ntohl(info.key.u.ipv4.dst))) { + netdev_dbg(dev, "multicast remote is unsupported\n"); + return -EINVAL; + } } if (data[IFLA_GENEVE_REMOTE6]) { - if (!IS_ENABLED(CONFIG_IPV6)) - return -EPFNOSUPPORT; - - remote.sa.sa_family = AF_INET6; - remote.sin6.sin6_addr = + #if IS_ENABLED(CONFIG_IPV6) + info.mode = IP_TUNNEL_INFO_IPV6; + info.key.u.ipv6.dst = nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]); - if (ipv6_addr_type(&remote.sin6.sin6_addr) & + if (ipv6_addr_type(&info.key.u.ipv6.dst) & IPV6_ADDR_LINKLOCAL) { netdev_dbg(dev, "link-local remote is unsupported\n"); return -EINVAL; } + if (ipv6_addr_is_multicast(&info.key.u.ipv6.dst)) { + netdev_dbg(dev, "multicast remote is unsupported\n"); + return -EINVAL; + } + info.key.tun_flags |= TUNNEL_CSUM; + use_udp6_rx_checksums = true; +#else + return -EPFNOSUPPORT; +#endif } - if (data[IFLA_GENEVE_ID]) + if (data[IFLA_GENEVE_ID]) { + __u32 vni; + __u8 tvni[3]; + vni = nla_get_u32(data[IFLA_GENEVE_ID]); + tvni[0] = (vni & 0x00ff0000) >> 16; + tvni[1] = (vni & 0x0000ff00) >> 8; + tvni[2] = vni & 0x000000ff; + info.key.tun_id = vni_to_tunnel_id(tvni); + } if (data[IFLA_GENEVE_TTL]) - ttl = nla_get_u8(data[IFLA_GENEVE_TTL]); + info.key.ttl = nla_get_u8(data[IFLA_GENEVE_TTL]); if (data[IFLA_GENEVE_TOS]) - tos = nla_get_u8(data[IFLA_GENEVE_TOS]); + info.key.tos = nla_get_u8(data[IFLA_GENEVE_TOS]); - if (data[IFLA_GENEVE_LABEL]) - label = nla_get_be32(data[IFLA_GENEVE_LABEL]) & - IPV6_FLOWLABEL_MASK; + if (data[IFLA_GENEVE_LABEL]) { + info.key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) & + IPV6_FLOWLABEL_MASK; + if (info.key.label && (!(info.mode & IP_TUNNEL_INFO_IPV6))) + return -EINVAL; + } if (data[IFLA_GENEVE_PORT]) - dst_port = nla_get_be16(data[IFLA_GENEVE_PORT]); + info.key.tp_dst = nla_get_be16(data[IFLA_GENEVE_PORT]); if (data[IFLA_GENEVE_COLLECT_METADATA]) metadata = true; if (data[IFLA_GENEVE_UDP_CSUM] && !nla_get_u8(data[IFLA_GENEVE_UDP_CSUM])) - flags |= GENEVE_F_UDP_ZERO_CSUM_TX; + info.key.tun_flags |= TUNNEL_CSUM; if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] && nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) - flags |= GENEVE_F_UDP_ZERO_CSUM6_TX; + info.key.tun_flags &= ~TUNNEL_CSUM; if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] && nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) - flags |= GENEVE_F_UDP_ZERO_CSUM6_RX; + use_udp6_rx_checksums = false; - return geneve_configure(net, dev, &remote, vni, ttl, tos, label, - dst_port, metadata, flags); + return geneve_configure(net, dev, &info, metadata, use_udp6_rx_checksums); } static void geneve_dellink(struct net_device *dev, struct list_head *head) @@ -1418,45 +1327,52 @@ static size_t geneve_get_size(const struct net_device *dev) static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct geneve_dev *geneve = netdev_priv(dev); + struct ip_tunnel_info *info = &geneve->info; + __u8 tmp_vni[3]; __u32 vni; - vni = (geneve->vni[0] << 16) | (geneve->vni[1] << 8) | geneve->vni[2]; + tunnel_id_to_vni(info->key.tun_id, tmp_vni); + vni = (tmp_vni[0] << 16) | (tmp_vni[1] << 8) | tmp_vni[2]; if (nla_put_u32(skb, IFLA_GENEVE_ID, vni)) goto nla_put_failure; - if (geneve->remote.sa.sa_family == AF_INET) { + if (ip_tunnel_info_af(info) == AF_INET) { if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE, - geneve->remote.sin.sin_addr.s_addr)) + info->key.u.ipv4.dst)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM, + !!(info->key.tun_flags & TUNNEL_CSUM))) goto nla_put_failure; + #if IS_ENABLED(CONFIG_IPV6) } else { if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6, - &geneve->remote.sin6.sin6_addr)) + &info->key.u.ipv6.dst)) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + !(info->key.tun_flags & TUNNEL_CSUM))) + goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + !geneve->use_udp6_rx_checksums)) goto nla_put_failure; #endif } - if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) || - nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos) || - nla_put_be32(skb, IFLA_GENEVE_LABEL, geneve->label)) + if (nla_put_u8(skb, IFLA_GENEVE_TTL, info->key.ttl) || + nla_put_u8(skb, IFLA_GENEVE_TOS, info->key.tos) || + nla_put_be32(skb, IFLA_GENEVE_LABEL, info->key.label)) goto nla_put_failure; - if (nla_put_be16(skb, IFLA_GENEVE_PORT, geneve->dst_port)) + if (nla_put_be16(skb, IFLA_GENEVE_PORT, info->key.tp_dst)) goto nla_put_failure; if (geneve->collect_md) { if (nla_put_flag(skb, IFLA_GENEVE_COLLECT_METADATA)) goto nla_put_failure; } - - if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM, - !(geneve->flags & GENEVE_F_UDP_ZERO_CSUM_TX)) || - nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, - !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_TX)) || - nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, - !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_RX))) - goto nla_put_failure; - return 0; nla_put_failure: @@ -1480,6 +1396,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port) { struct nlattr *tb[IFLA_MAX + 1]; + struct ip_tunnel_info info; struct net_device *dev; LIST_HEAD(list_kill); int err; @@ -1490,9 +1407,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, if (IS_ERR(dev)) return dev; - err = geneve_configure(net, dev, &geneve_remote_unspec, - 0, 0, 0, 0, htons(dst_port), true, - GENEVE_F_UDP_ZERO_CSUM6_RX); + init_tnl_info(&info, dst_port); + err = geneve_configure(net, dev, &info, true, true); if (err) { free_netdev(dev); return ERR_PTR(err); @@ -1510,8 +1426,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, goto err; return dev; - - err: +err: geneve_dellink(dev, &list_kill); unregister_netdevice_many(&list_kill); return ERR_PTR(err); @@ -1594,7 +1509,6 @@ static int __init geneve_init_module(void) goto out3; return 0; - out3: unregister_netdevice_notifier(&geneve_notifier_block); out2: -- cgit v1.1 From c3ef5aa5e5f853168fe9728f390bfa2141eef4ec Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Mon, 21 Nov 2016 11:02:59 -0800 Subject: geneve: Merge ipv4 and ipv6 geneve_build_skb() There are minimal difference in building Geneve header between ipv4 and ipv6 geneve tunnels. Following patch refactors code to unify it. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/geneve.c | 100 ++++++++++++++------------------------------------- 1 file changed, 26 insertions(+), 74 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 70f2a32..95dd19a 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -630,67 +630,34 @@ static int geneve_stop(struct net_device *dev) } static void geneve_build_header(struct genevehdr *geneveh, - __be16 tun_flags, u8 vni[3], - u8 options_len, u8 *options) + const struct ip_tunnel_info *info) { geneveh->ver = GENEVE_VER; - geneveh->opt_len = options_len / 4; - geneveh->oam = !!(tun_flags & TUNNEL_OAM); - geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT); + geneveh->opt_len = info->options_len / 4; + geneveh->oam = !!(info->key.tun_flags & TUNNEL_OAM); + geneveh->critical = !!(info->key.tun_flags & TUNNEL_CRIT_OPT); geneveh->rsvd1 = 0; - memcpy(geneveh->vni, vni, 3); + tunnel_id_to_vni(info->key.tun_id, geneveh->vni); geneveh->proto_type = htons(ETH_P_TEB); geneveh->rsvd2 = 0; - memcpy(geneveh->options, options, options_len); + ip_tunnel_info_opts_get(geneveh->options, info); } -static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb, - __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt, - bool xnet) -{ - bool udp_sum = !!(tun_flags & TUNNEL_CSUM); - struct genevehdr *gnvh; - int min_headroom; - int err; - - skb_scrub_packet(skb, xnet); - - min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len - + GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr); - err = skb_cow_head(skb, min_headroom); - if (unlikely(err)) - goto free_rt; - - err = udp_tunnel_handle_offloads(skb, udp_sum); - if (err) - goto free_rt; - - gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len); - geneve_build_header(gnvh, tun_flags, vni, opt_len, opt); - - skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - return 0; - -free_rt: - ip_rt_put(rt); - return err; -} - -#if IS_ENABLED(CONFIG_IPV6) -static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb, - __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt, - bool xnet) +static int geneve_build_skb(struct dst_entry *dst, struct sk_buff *skb, + const struct ip_tunnel_info *info, + bool xnet, int ip_hdr_len) { - bool udp_sum = !!(tun_flags & TUNNEL_CSUM); + bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); struct genevehdr *gnvh; int min_headroom; int err; + skb_reset_mac_header(skb); skb_scrub_packet(skb, xnet); - min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len - + GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr); + min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len + + GENEVE_BASE_HLEN + info->options_len + ip_hdr_len; err = skb_cow_head(skb, min_headroom); if (unlikely(err)) goto free_dst; @@ -699,9 +666,9 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb, if (err) goto free_dst; - gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len); - geneve_build_header(gnvh, tun_flags, vni, opt_len, opt); - + gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + + info->options_len); + geneve_build_header(gnvh, info); skb_set_inner_protocol(skb, htons(ETH_P_TEB)); return 0; @@ -709,12 +676,11 @@ free_dst: dst_release(dst); return err; } -#endif static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, struct net_device *dev, struct flowi4 *fl4, - struct ip_tunnel_info *info) + const struct ip_tunnel_info *info) { bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct geneve_dev *geneve = netdev_priv(dev); @@ -738,7 +704,7 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, } fl4->flowi4_tos = RT_TOS(tos); - dst_cache = &info->dst_cache; + dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { rt = dst_cache_get_ip4(dst_cache, &fl4->saddr); if (rt) @@ -763,7 +729,7 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, struct net_device *dev, struct flowi6 *fl6, - struct ip_tunnel_info *info) + const struct ip_tunnel_info *info) { bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct geneve_dev *geneve = netdev_priv(dev); @@ -789,7 +755,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio), info->key.label); - dst_cache = &info->dst_cache; + dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { dst = dst_cache_get_ip6(dst_cache, &fl6->saddr); if (dst) @@ -812,7 +778,8 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, #endif static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, - struct geneve_dev *geneve, struct ip_tunnel_info *info) + struct geneve_dev *geneve, + const struct ip_tunnel_info *info) { bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); struct geneve_sock *gs4 = rcu_dereference(geneve->sock4); @@ -820,11 +787,9 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct rtable *rt; int err = -EINVAL; struct flowi4 fl4; - u8 *opts = NULL; __u8 tos, ttl; __be16 sport; __be16 df; - u8 vni[3]; if (!gs4) return err; @@ -843,13 +808,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, } df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; - tunnel_id_to_vni(key->tun_id, vni); - if (info->options_len) - opts = ip_tunnel_info_opts(info); - - skb_reset_mac_header(skb); - err = geneve_build_skb(rt, skb, key->tun_flags, vni, - info->options_len, opts, xnet); + err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr)); if (unlikely(err)) return err; @@ -862,7 +821,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, #if IS_ENABLED(CONFIG_IPV6) static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, - struct geneve_dev *geneve, struct ip_tunnel_info *info) + struct geneve_dev *geneve, + const struct ip_tunnel_info *info) { bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); struct geneve_sock *gs6 = rcu_dereference(geneve->sock6); @@ -870,10 +830,8 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct dst_entry *dst = NULL; int err = -EINVAL; struct flowi6 fl6; - u8 *opts = NULL; __u8 prio, ttl; __be16 sport; - u8 vni[3]; if (!gs6) return err; @@ -891,13 +849,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, ip_hdr(skb), skb); ttl = key->ttl ? : ip6_dst_hoplimit(dst); } - tunnel_id_to_vni(key->tun_id, vni); - if (info->options_len) - opts = ip_tunnel_info_opts(info); - - skb_reset_mac_header(skb); - err = geneve6_build_skb(dst, skb, key->tun_flags, vni, - info->options_len, opts, xnet); + err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct iphdr)); if (unlikely(err)) return err; -- cgit v1.1 From bcceeec3ccc413b11b41bd0559c7e8537ca9916e Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Mon, 21 Nov 2016 11:03:00 -0800 Subject: geneve: Remove redundant socket checks. Geneve already has check for device socket in route lookup function. So no need to check it in xmit function. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/geneve.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 95dd19a..ab9fea6 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -785,14 +785,11 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct geneve_sock *gs4 = rcu_dereference(geneve->sock4); const struct ip_tunnel_key *key = &info->key; struct rtable *rt; - int err = -EINVAL; struct flowi4 fl4; __u8 tos, ttl; __be16 sport; __be16 df; - - if (!gs4) - return err; + int err; rt = geneve_get_v4_rt(skb, dev, &fl4, info); if (IS_ERR(rt)) @@ -828,13 +825,10 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct geneve_sock *gs6 = rcu_dereference(geneve->sock6); const struct ip_tunnel_key *key = &info->key; struct dst_entry *dst = NULL; - int err = -EINVAL; struct flowi6 fl6; __u8 prio, ttl; __be16 sport; - - if (!gs6) - return err; + int err; dst = geneve_get_v6_dst(skb, dev, &fl6, info); if (IS_ERR(dst)) -- cgit v1.1 From 2e0b26e1035253bda7587f705f346385352e942d Mon Sep 17 00:00:00 2001 From: pravin shelar Date: Mon, 21 Nov 2016 11:03:01 -0800 Subject: geneve: Optimize geneve device lookup. Rather than comparing 64-bit tunnel-id, compare tunnel vni which is 24-bit id. This also save conversion from vni to tunnel id on each tunnel packet receive. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/geneve.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index ab9fea6..7b80e28 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -103,6 +103,17 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni) #endif } +static bool eq_tun_id_and_vni(u8 *tun_id, u8 *vni) +{ +#ifdef __BIG_ENDIAN + return (vni[0] == tun_id[2]) && + (vni[1] == tun_id[1]) && + (vni[2] == tun_id[0]); +#else + return !memcmp(vni, &tun_id[5], 3); +#endif +} + static sa_family_t geneve_get_sk_family(struct geneve_sock *gs) { return gs->sock->sk->sk_family; @@ -111,7 +122,6 @@ static sa_family_t geneve_get_sk_family(struct geneve_sock *gs) static struct geneve_dev *geneve_lookup(struct geneve_sock *gs, __be32 addr, u8 vni[]) { - __be64 id = vni_to_tunnel_id(vni); struct hlist_head *vni_list_head; struct geneve_dev *geneve; __u32 hash; @@ -120,7 +130,7 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs, hash = geneve_net_vni_hash(vni); vni_list_head = &gs->vni_list[hash]; hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) { - if (!memcmp(&id, &geneve->info.key.tun_id, sizeof(id)) && + if (eq_tun_id_and_vni((u8 *)&geneve->info.key.tun_id, vni) && addr == geneve->info.key.u.ipv4.dst) return geneve; } @@ -131,7 +141,6 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs, static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs, struct in6_addr addr6, u8 vni[]) { - __be64 id = vni_to_tunnel_id(vni); struct hlist_head *vni_list_head; struct geneve_dev *geneve; __u32 hash; @@ -140,7 +149,7 @@ static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs, hash = geneve_net_vni_hash(vni); vni_list_head = &gs->vni_list[hash]; hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) { - if (!memcmp(&id, &geneve->info.key.tun_id, sizeof(id)) && + if (eq_tun_id_and_vni((u8 *)&geneve->info.key.tun_id, vni) && ipv6_addr_equal(&addr6, &geneve->info.key.u.ipv6.dst)) return geneve; } -- cgit v1.1 From e796f49d826aadb856981c751efc086c6ce11a77 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Tue, 22 Nov 2016 02:00:14 +0530 Subject: net: ieee802154: constify ieee802154_ops structures Declare the structure ieee802154_ops as const as it is only passed as an argument to the function ieee802154_alloc_hw. This argument is of type const struct ieee802154_ops *, so ieee80254_ops structures having this property can be declared as const. Done using Coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct ieee802154_ops i@p = {...}; @ok1@ identifier r1.i; position p; expression e1; @@ ieee802154_alloc_hw(e1,&i@p) @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct ieee802154_ops i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct ieee802154_ops i; The before and after size details of the affected files are: text data bss dec hex filename 8669 1176 16 9861 2685 drivers/net/ieee802154/adf7242.o 8805 1048 16 9869 268d drivers/net/ieee802154/adf7242.o text data bss dec hex filename 7211 2296 32 9539 2543 drivers/net/ieee802154/atusb.o 7339 2160 32 9531 253b drivers/net/ieee802154/atusb.o Signed-off-by: Bhumika Goyal Acked-by: Stefan Schmidt Signed-off-by: David S. Miller --- drivers/net/ieee802154/adf7242.c | 2 +- drivers/net/ieee802154/atusb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c index 9fa7ac9..4ff4c7d 100644 --- a/drivers/net/ieee802154/adf7242.c +++ b/drivers/net/ieee802154/adf7242.c @@ -874,7 +874,7 @@ static int adf7242_rx(struct adf7242_local *lp) return 0; } -static struct ieee802154_ops adf7242_ops = { +static const struct ieee802154_ops adf7242_ops = { .owner = THIS_MODULE, .xmit_sync = adf7242_xmit, .ed = adf7242_ed, diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 1056ed1..322864a 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -567,7 +567,7 @@ atusb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) return 0; } -static struct ieee802154_ops atusb_ops = { +static const struct ieee802154_ops atusb_ops = { .owner = THIS_MODULE, .xmit_async = atusb_xmit, .ed = atusb_ed, -- cgit v1.1 From b4308f046a38dffc270b8865256320cbe2a2b1f8 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:26:55 +0100 Subject: net: dsa: mv88e6xxx: Take switch out of reset before probe The switch needs to be taken out of reset before we can read its ID register on the MDIO bus. Signed-off-by: Andrew Lunn Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index e30d0ea..062b912 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3855,16 +3855,16 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) if (err) return err; + chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(chip->reset)) + return PTR_ERR(chip->reset); + err = mv88e6xxx_detect(chip); if (err) return err; mv88e6xxx_phy_init(chip); - chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); - if (IS_ERR(chip->reset)) - return PTR_ERR(chip->reset); - if (chip->info->ops->get_eeprom && !of_property_read_u32(np, "eeprom-length", &eeprom_len)) chip->eeprom_len = eeprom_len; -- cgit v1.1 From 096eea0ff83bcdd14d77fb4efc0ffc2b548d4a5d Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:26:56 +0100 Subject: net: dsa: mv88e6xxx: Fix unused variable warning by using variable _mv88e6xxx_stats_wait() did not check the return value from mv88e6xxx_g1_read(), so the compiler complained about set but unused err. Signed-off-by: Andrew Lunn Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 062b912..c015212 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -787,6 +787,9 @@ static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) for (i = 0; i < 10; i++) { err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val); + if (err) + return err; + if ((val & GLOBAL_STATS_OP_BUSY) == 0) return 0; } -- cgit v1.1 From 1a3b39ecfe32bf9a13df44b4fadf91d4e4d3d5ff Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:26:57 +0100 Subject: net: dsa: mv88e6xxx: Add the mv88e6390 family With the devices added to the tables, the probe will recognize the switch. This however is not sufficient to make it work properly, other changes are needed because of incompatibilities. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- .../devicetree/bindings/net/dsa/marvell.txt | 3 +- drivers/net/dsa/mv88e6xxx/chip.c | 155 +++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 25 ++++ 3 files changed, 182 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/dsa/marvell.txt b/Documentation/devicetree/bindings/net/dsa/marvell.txt index 32025eb..b3dd6b4 100644 --- a/Documentation/devicetree/bindings/net/dsa/marvell.txt +++ b/Documentation/devicetree/bindings/net/dsa/marvell.txt @@ -14,7 +14,8 @@ The properties described here are those specific to Marvell devices. Additional required and optional properties can be found in dsa.txt. Required properties: -- compatible : Should be one of "marvell,mv88e6085", +- compatible : Should be one of "marvell,mv88e6085" or + "marvell,mv88e6190" - reg : Address on the MII bus for the switch. Optional properties: diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c015212..02d1b35 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3262,6 +3262,36 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_speed = mv88e6185_port_set_speed, }; +static const struct mv88e6xxx_ops mv88e6190_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, +}; + +static const struct mv88e6xxx_ops mv88e6190x_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390x_port_set_speed, +}; + +static const struct mv88e6xxx_ops mv88e6191_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, +}; + static const struct mv88e6xxx_ops mv88e6240_ops = { .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, @@ -3274,6 +3304,16 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_speed = mv88e6352_port_set_speed, }; +static const struct mv88e6xxx_ops mv88e6290_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, +}; + static const struct mv88e6xxx_ops mv88e6320_ops = { .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, @@ -3328,6 +3368,36 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_speed = mv88e6352_port_set_speed, }; +static const struct mv88e6xxx_ops mv88e6390_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, +}; + +static const struct mv88e6xxx_ops mv88e6390x_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390x_port_set_speed, +}; + +static const struct mv88e6xxx_ops mv88e6391_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, +}; + static const struct mv88e6xxx_info mv88e6xxx_table[] = { [MV88E6085] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, @@ -3483,6 +3553,47 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6185_ops, }, + [MV88E6190] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6190, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6190", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global1_addr = 0x1b, + .age_time_coeff = 15000, + .g1_irqs = 9, + .flags = MV88E6XXX_FLAGS_FAMILY_6390, + .ops = &mv88e6190_ops, + }, + + [MV88E6190X] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6190X, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6190X", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global1_addr = 0x1b, + .age_time_coeff = 15000, + .g1_irqs = 9, + .flags = MV88E6XXX_FLAGS_FAMILY_6390, + .ops = &mv88e6190x_ops, + }, + + [MV88E6191] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6191, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6191", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global1_addr = 0x1b, + .age_time_coeff = 15000, + .flags = MV88E6XXX_FLAGS_FAMILY_6390, + .ops = &mv88e6391_ops, + }, + [MV88E6240] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6240, .family = MV88E6XXX_FAMILY_6352, @@ -3497,6 +3608,20 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6240_ops, }, + [MV88E6290] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6290, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6290", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global1_addr = 0x1b, + .age_time_coeff = 15000, + .g1_irqs = 9, + .flags = MV88E6XXX_FLAGS_FAMILY_6390, + .ops = &mv88e6290_ops, + }, + [MV88E6320] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6320, .family = MV88E6XXX_FAMILY_6320, @@ -3566,6 +3691,32 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6352_ops, }, + [MV88E6390] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6390, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6390", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global1_addr = 0x1b, + .age_time_coeff = 15000, + .g1_irqs = 9, + .flags = MV88E6XXX_FLAGS_FAMILY_6390, + .ops = &mv88e6390_ops, + }, + [MV88E6390X] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6390X, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6390X", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global1_addr = 0x1b, + .age_time_coeff = 15000, + .g1_irqs = 9, + .flags = MV88E6XXX_FLAGS_FAMILY_6390, + .ops = &mv88e6390x_ops, + }, }; static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num) @@ -3949,6 +4100,10 @@ static const struct of_device_id mv88e6xxx_of_match[] = { .compatible = "marvell,mv88e6085", .data = &mv88e6xxx_table[MV88E6085], }, + { + .compatible = "marvell,mv88e6190", + .data = &mv88e6xxx_table[MV88E6190], + }, { /* sentinel */ }, }; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index a386950..de179c5 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -91,11 +91,17 @@ #define PORT_SWITCH_ID_PROD_NUM_6175 0x175 #define PORT_SWITCH_ID_PROD_NUM_6176 0x176 #define PORT_SWITCH_ID_PROD_NUM_6185 0x1a7 +#define PORT_SWITCH_ID_PROD_NUM_6190 0x190 +#define PORT_SWITCH_ID_PROD_NUM_6190X 0x0a0 +#define PORT_SWITCH_ID_PROD_NUM_6191 0x191 #define PORT_SWITCH_ID_PROD_NUM_6240 0x240 +#define PORT_SWITCH_ID_PROD_NUM_6290 0x290 #define PORT_SWITCH_ID_PROD_NUM_6321 0x310 #define PORT_SWITCH_ID_PROD_NUM_6352 0x352 #define PORT_SWITCH_ID_PROD_NUM_6350 0x371 #define PORT_SWITCH_ID_PROD_NUM_6351 0x375 +#define PORT_SWITCH_ID_PROD_NUM_6390 0x390 +#define PORT_SWITCH_ID_PROD_NUM_6390X 0x0a1 #define PORT_CONTROL 0x04 #define PORT_CONTROL_USE_CORE_TAG BIT(15) #define PORT_CONTROL_DROP_ON_LOCK BIT(14) @@ -378,12 +384,18 @@ enum mv88e6xxx_model { MV88E6175, MV88E6176, MV88E6185, + MV88E6190, + MV88E6190X, + MV88E6191, MV88E6240, + MV88E6290, MV88E6320, MV88E6321, MV88E6350, MV88E6351, MV88E6352, + MV88E6390, + MV88E6390X, }; enum mv88e6xxx_family { @@ -396,6 +408,7 @@ enum mv88e6xxx_family { MV88E6XXX_FAMILY_6320, /* 6320 6321 */ MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ + MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */ }; enum mv88e6xxx_cap { @@ -615,6 +628,18 @@ enum mv88e6xxx_cap { struct mv88e6xxx_ops; +#define MV88E6XXX_FLAGS_FAMILY_6390 \ + (MV88E6XXX_FLAG_EEE | \ + MV88E6XXX_FLAG_GLOBAL2 | \ + MV88E6XXX_FLAG_PPU_ACTIVE | \ + MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_TEMP_LIMIT | \ + MV88E6XXX_FLAG_VTU | \ + MV88E6XXX_FLAGS_IRL | \ + MV88E6XXX_FLAGS_MULTI_CHIP | \ + MV88E6XXX_FLAGS_PVT) + struct mv88e6xxx_info { enum mv88e6xxx_family family; u16 prod_num; -- cgit v1.1 From a605a0fe7115ead62f9a2e93819e21776261efec Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:26:58 +0100 Subject: net: dsa: mv88e6xxx: Abstract stats_snapshot into ops structure Taking a stats snapshot differs between same families. Abstract this into an ops member. At the same time, move the code into global1.[ch], since the registers are in the global1 range. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 37 +++++++++++++++++++++-------------- drivers/net/dsa/mv88e6xxx/global1.c | 27 +++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 2 ++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 5 +++++ 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 02d1b35..6e72877 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -797,22 +797,12 @@ static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) return -ETIMEDOUT; } -static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) { - int err; - - if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip)) - port = (port + 1) << 5; - - /* Snapshot the hardware statistics counters for this port. */ - err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, - GLOBAL_STATS_OP_CAPTURE_PORT | - GLOBAL_STATS_OP_HIST_RX_TX | port); - if (err) - return err; + if (!chip->info->ops->stats_snapshot) + return -EOPNOTSUPP; - /* Wait for the snapshotting to complete. */ - return _mv88e6xxx_stats_wait(chip); + return chip->info->ops->stats_snapshot(chip, port); } static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip, @@ -1003,7 +993,7 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); - ret = _mv88e6xxx_stats_snapshot(chip, port); + ret = mv88e6xxx_stats_snapshot(chip, port); if (ret < 0) { mutex_unlock(&chip->reg_lock); return; @@ -3162,6 +3152,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3171,6 +3162,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3180,6 +3172,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3189,6 +3182,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3198,6 +3192,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3207,6 +3202,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3217,6 +3213,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3229,6 +3226,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3239,6 +3237,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3251,6 +3250,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3260,6 +3260,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3302,6 +3303,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3323,6 +3325,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3334,6 +3337,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3344,6 +3348,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3354,6 +3359,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3366,6 +3372,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .stats_snapshot = mv88e6320_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6390_ops = { diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index d358720..47b507d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -32,3 +32,30 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) { return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask); } + +static int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g1_wait(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_BUSY); +} + +int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + /* Snapshot the hardware statistics counters for this port. */ + err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_CAPTURE_PORT | + GLOBAL_STATS_OP_HIST_RX_TX | port); + if (err) + return err; + + /* Wait for the snapshotting to complete. */ + return mv88e6xxx_g1_stats_wait(chip); +} + +int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +{ + port = (port + 1) << 5; + + return mv88e6xxx_g1_stats_snapshot(chip, port); +} diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 62291e6..0080a30 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -19,5 +19,7 @@ int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val); int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); +int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); +int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index de179c5..7c61cf6 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -794,6 +794,11 @@ struct mv88e6xxx_ops { * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value. */ int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); + + /* Snapshot the statistics for a port. The statistics can then + * be read back a leisure but still with a consistent view. + */ + int (*stats_snapshot)(struct mv88e6xxx_chip *chip, int port); }; enum stat_type { -- cgit v1.1 From 4b325d8c84a8dda1e3e59a6ddb2304e10b5d8c74 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:26:59 +0100 Subject: net: dsa: mv88e6xxx: Add comment about family a device belongs to Knowing the family of device belongs to helps with picking the ops implementation which is appropriate to the device. So add a comment to each structure of ops. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 6e72877..2ed7fc9 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3146,6 +3146,7 @@ static int mv88e6xxx_set_eeprom(struct dsa_switch *ds, } static const struct mv88e6xxx_ops mv88e6085_ops = { + /* MV88E6XXX_FAMILY_6097 */ .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, @@ -3156,6 +3157,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { }; static const struct mv88e6xxx_ops mv88e6095_ops = { + /* MV88E6XXX_FAMILY_6095 */ .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, @@ -3166,6 +3168,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { }; static const struct mv88e6xxx_ops mv88e6123_ops = { + /* MV88E6XXX_FAMILY_6165 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, @@ -3176,6 +3179,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { }; static const struct mv88e6xxx_ops mv88e6131_ops = { + /* MV88E6XXX_FAMILY_6185 */ .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, @@ -3186,6 +3190,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { }; static const struct mv88e6xxx_ops mv88e6161_ops = { + /* MV88E6XXX_FAMILY_6165 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, @@ -3196,6 +3201,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { }; static const struct mv88e6xxx_ops mv88e6165_ops = { + /* MV88E6XXX_FAMILY_6165 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_read, .phy_write = mv88e6xxx_write, @@ -3206,6 +3212,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { }; static const struct mv88e6xxx_ops mv88e6171_ops = { + /* MV88E6XXX_FAMILY_6351 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3217,6 +3224,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { }; static const struct mv88e6xxx_ops mv88e6172_ops = { + /* MV88E6XXX_FAMILY_6352 */ .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3230,6 +3238,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { }; static const struct mv88e6xxx_ops mv88e6175_ops = { + /* MV88E6XXX_FAMILY_6351 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3241,6 +3250,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { }; static const struct mv88e6xxx_ops mv88e6176_ops = { + /* MV88E6XXX_FAMILY_6352 */ .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3254,6 +3264,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { }; static const struct mv88e6xxx_ops mv88e6185_ops = { + /* MV88E6XXX_FAMILY_6185 */ .set_switch_mac = mv88e6xxx_g1_set_switch_mac, .phy_read = mv88e6xxx_phy_ppu_read, .phy_write = mv88e6xxx_phy_ppu_write, @@ -3264,6 +3275,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { }; static const struct mv88e6xxx_ops mv88e6190_ops = { + /* MV88E6XXX_FAMILY_6390 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3274,6 +3286,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { }; static const struct mv88e6xxx_ops mv88e6190x_ops = { + /* MV88E6XXX_FAMILY_6390 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3284,6 +3297,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { }; static const struct mv88e6xxx_ops mv88e6191_ops = { + /* MV88E6XXX_FAMILY_6390 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3294,6 +3308,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { }; static const struct mv88e6xxx_ops mv88e6240_ops = { + /* MV88E6XXX_FAMILY_6352 */ .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3307,6 +3322,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { }; static const struct mv88e6xxx_ops mv88e6290_ops = { + /* MV88E6XXX_FAMILY_6390 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3317,6 +3333,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { }; static const struct mv88e6xxx_ops mv88e6320_ops = { + /* MV88E6XXX_FAMILY_6320 */ .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3329,6 +3346,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { }; static const struct mv88e6xxx_ops mv88e6321_ops = { + /* MV88E6XXX_FAMILY_6321 */ .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3341,6 +3359,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { }; static const struct mv88e6xxx_ops mv88e6350_ops = { + /* MV88E6XXX_FAMILY_6351 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3352,6 +3371,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { }; static const struct mv88e6xxx_ops mv88e6351_ops = { + /* MV88E6XXX_FAMILY_6351 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3363,6 +3383,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { }; static const struct mv88e6xxx_ops mv88e6352_ops = { + /* MV88E6XXX_FAMILY_6352 */ .get_eeprom = mv88e6xxx_g2_get_eeprom16, .set_eeprom = mv88e6xxx_g2_set_eeprom16, .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3376,6 +3397,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { }; static const struct mv88e6xxx_ops mv88e6390_ops = { + /* MV88E6XXX_FAMILY_6390 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3386,6 +3408,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { }; static const struct mv88e6xxx_ops mv88e6390x_ops = { + /* MV88E6XXX_FAMILY_6390 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, @@ -3396,6 +3419,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { }; static const struct mv88e6xxx_ops mv88e6391_ops = { + /* MV88E6XXX_FAMILY_6390 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, -- cgit v1.1 From 795234739105381afd813ca7a0298b9c293f2765 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:27:00 +0100 Subject: net: dsa: mv88e6xxx: Add mv88e6390 stats snapshot operation The MV88E6390 has a control register for what the histogram statistics actually contain. This means the stat_snapshot method should not set this information. So implement the 6390 stats_snapshot function without these bits. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 7 +++++++ drivers/net/dsa/mv88e6xxx/global1.c | 16 ++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 1 + drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 4 +++- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2ed7fc9..c228cb1 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3283,6 +3283,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .stats_snapshot = mv88e6390_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3294,6 +3295,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, + .stats_snapshot = mv88e6390_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3305,6 +3307,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .stats_snapshot = mv88e6390_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3330,6 +3333,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .stats_snapshot = mv88e6390_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3405,6 +3409,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .stats_snapshot = mv88e6390_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3416,6 +3421,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, + .stats_snapshot = mv88e6390_g1_stats_snapshot, }; static const struct mv88e6xxx_ops mv88e6391_ops = { @@ -3427,6 +3433,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .stats_snapshot = mv88e6390_g1_stats_snapshot, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 47b507d..a3a441f 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -59,3 +59,19 @@ int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) return mv88e6xxx_g1_stats_snapshot(chip, port); } + +int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + port = (port + 1) << 5; + + /* Snapshot the hardware statistics counters for this port. */ + err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_CAPTURE_PORT | port); + if (err) + return err; + + /* Wait for the snapshotting to complete. */ + return mv88e6xxx_g1_stats_wait(chip); +} diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 0080a30..a9c8943 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -21,5 +21,6 @@ int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val); int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 7c61cf6..a647e03 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -283,7 +283,9 @@ #define GLOBAL_CONTROL_2 0x1c #define GLOBAL_CONTROL_2_NO_CASCADE 0xe000 #define GLOBAL_CONTROL_2_MULTIPLE_CASCADE 0xf000 - +#define GLOBAL_CONTROL_2_HIST_RX (0x1 << 6) +#define GLOBAL_CONTROL_2_HIST_TX (0x2 << 6) +#define GLOBAL_CONTROL_2_HIST_RX_TX (0x3 << 6) #define GLOBAL_STATS_OP 0x1d #define GLOBAL_STATS_OP_BUSY BIT(15) #define GLOBAL_STATS_OP_NOP (0 << 12) -- cgit v1.1 From de2273876e3fb5a5a62cc538936d06b014b06eda Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:27:01 +0100 Subject: net: dsa: mv88e6xxx: Add mv88e6390 statistics unit init The statistics unit on the mv88e6390 needs the histogram mode to be configured in a different register compared to other devices. Add an ops to do this. Signed-off-by: Andrew Lunn v2: Rename to mv88e6390_g1_stats_set_histogram Move into global1.c Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.c | 20 ++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 1 + drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 5 +++++ 4 files changed, 46 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c228cb1..dc23d04 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1009,6 +1009,14 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } +static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip) +{ + if (chip->info->ops->stats_set_histogram) + return chip->info->ops->stats_set_histogram(chip); + + return 0; +} + static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) { return 32 * sizeof(u16); @@ -2797,6 +2805,11 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err) return err; + /* Initialize the statistics unit */ + err = mv88e6xxx_stats_set_histogram(chip); + if (err) + return err; + /* Clear the statistics counters for all ports */ err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_FLUSH_ALL); @@ -3284,6 +3297,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3296,6 +3310,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3308,6 +3323,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3334,6 +3350,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3410,6 +3427,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3422,6 +3440,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, }; static const struct mv88e6xxx_ops mv88e6391_ops = { @@ -3434,6 +3453,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index a3a441f..fda8b3c 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -33,6 +33,26 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask); } +/* Offset 0x1c: Global Control 2 */ + +int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL_2, &val); + if (err) + return err; + + val |= GLOBAL_CONTROL_2_HIST_RX_TX; + + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, val); + + return err; +} + +/* Offset 0x1d: Statistics Operation 2 */ + static int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) { return mv88e6xxx_g1_wait(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_BUSY); diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index a9c8943..ffc9f45 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -22,5 +22,6 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index a647e03..d947a0c 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -801,6 +801,11 @@ struct mv88e6xxx_ops { * be read back a leisure but still with a consistent view. */ int (*stats_snapshot)(struct mv88e6xxx_chip *chip, int port); + + /* Set the histogram mode for statistics, when the control registers + * are separated out of the STATS_OP register. + */ + int (*stats_set_histogram)(struct mv88e6xxx_chip *chip); }; enum stat_type { -- cgit v1.1 From dfafe449bbc91dfeab9b722f4396d3846db4aa50 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:27:02 +0100 Subject: net: dsa: mv88e6xxx: Add stats_get_sset_count|string to ops structure Different families have different sets of statistics. Abstract this using a stats_get_sset_count and stats_get_strings op. Each stat has a bitmap, and the ops implementer uses a bit map mask to count the statistics which apply for the family, or return the list of strings. Signed-off-by: Andrew Lunn v2: Rename functions to avoid _ prefix. Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 243 +++++++++++++++++++++++----------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 14 +- 2 files changed, 177 insertions(+), 80 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index dc23d04..ef6d357 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -838,76 +838,76 @@ static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip, } static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { - { "in_good_octets", 8, 0x00, BANK0, }, - { "in_bad_octets", 4, 0x02, BANK0, }, - { "in_unicast", 4, 0x04, BANK0, }, - { "in_broadcasts", 4, 0x06, BANK0, }, - { "in_multicasts", 4, 0x07, BANK0, }, - { "in_pause", 4, 0x16, BANK0, }, - { "in_undersize", 4, 0x18, BANK0, }, - { "in_fragments", 4, 0x19, BANK0, }, - { "in_oversize", 4, 0x1a, BANK0, }, - { "in_jabber", 4, 0x1b, BANK0, }, - { "in_rx_error", 4, 0x1c, BANK0, }, - { "in_fcs_error", 4, 0x1d, BANK0, }, - { "out_octets", 8, 0x0e, BANK0, }, - { "out_unicast", 4, 0x10, BANK0, }, - { "out_broadcasts", 4, 0x13, BANK0, }, - { "out_multicasts", 4, 0x12, BANK0, }, - { "out_pause", 4, 0x15, BANK0, }, - { "excessive", 4, 0x11, BANK0, }, - { "collisions", 4, 0x1e, BANK0, }, - { "deferred", 4, 0x05, BANK0, }, - { "single", 4, 0x14, BANK0, }, - { "multiple", 4, 0x17, BANK0, }, - { "out_fcs_error", 4, 0x03, BANK0, }, - { "late", 4, 0x1f, BANK0, }, - { "hist_64bytes", 4, 0x08, BANK0, }, - { "hist_65_127bytes", 4, 0x09, BANK0, }, - { "hist_128_255bytes", 4, 0x0a, BANK0, }, - { "hist_256_511bytes", 4, 0x0b, BANK0, }, - { "hist_512_1023bytes", 4, 0x0c, BANK0, }, - { "hist_1024_max_bytes", 4, 0x0d, BANK0, }, - { "sw_in_discards", 4, 0x10, PORT, }, - { "sw_in_filtered", 2, 0x12, PORT, }, - { "sw_out_filtered", 2, 0x13, PORT, }, - { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, }, - { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_good_octets", 8, 0x00, STATS_TYPE_BANK0, }, + { "in_bad_octets", 4, 0x02, STATS_TYPE_BANK0, }, + { "in_unicast", 4, 0x04, STATS_TYPE_BANK0, }, + { "in_broadcasts", 4, 0x06, STATS_TYPE_BANK0, }, + { "in_multicasts", 4, 0x07, STATS_TYPE_BANK0, }, + { "in_pause", 4, 0x16, STATS_TYPE_BANK0, }, + { "in_undersize", 4, 0x18, STATS_TYPE_BANK0, }, + { "in_fragments", 4, 0x19, STATS_TYPE_BANK0, }, + { "in_oversize", 4, 0x1a, STATS_TYPE_BANK0, }, + { "in_jabber", 4, 0x1b, STATS_TYPE_BANK0, }, + { "in_rx_error", 4, 0x1c, STATS_TYPE_BANK0, }, + { "in_fcs_error", 4, 0x1d, STATS_TYPE_BANK0, }, + { "out_octets", 8, 0x0e, STATS_TYPE_BANK0, }, + { "out_unicast", 4, 0x10, STATS_TYPE_BANK0, }, + { "out_broadcasts", 4, 0x13, STATS_TYPE_BANK0, }, + { "out_multicasts", 4, 0x12, STATS_TYPE_BANK0, }, + { "out_pause", 4, 0x15, STATS_TYPE_BANK0, }, + { "excessive", 4, 0x11, STATS_TYPE_BANK0, }, + { "collisions", 4, 0x1e, STATS_TYPE_BANK0, }, + { "deferred", 4, 0x05, STATS_TYPE_BANK0, }, + { "single", 4, 0x14, STATS_TYPE_BANK0, }, + { "multiple", 4, 0x17, STATS_TYPE_BANK0, }, + { "out_fcs_error", 4, 0x03, STATS_TYPE_BANK0, }, + { "late", 4, 0x1f, STATS_TYPE_BANK0, }, + { "hist_64bytes", 4, 0x08, STATS_TYPE_BANK0, }, + { "hist_65_127bytes", 4, 0x09, STATS_TYPE_BANK0, }, + { "hist_128_255bytes", 4, 0x0a, STATS_TYPE_BANK0, }, + { "hist_256_511bytes", 4, 0x0b, STATS_TYPE_BANK0, }, + { "hist_512_1023bytes", 4, 0x0c, STATS_TYPE_BANK0, }, + { "hist_1024_max_bytes", 4, 0x0d, STATS_TYPE_BANK0, }, + { "sw_in_discards", 4, 0x10, STATS_TYPE_PORT, }, + { "sw_in_filtered", 2, 0x12, STATS_TYPE_PORT, }, + { "sw_out_filtered", 2, 0x13, STATS_TYPE_PORT, }, + { "in_discards", 4, 0x00, STATS_TYPE_BANK1, }, + { "in_filtered", 4, 0x01, STATS_TYPE_BANK1, }, + { "in_accepted", 4, 0x02, STATS_TYPE_BANK1, }, + { "in_bad_accepted", 4, 0x03, STATS_TYPE_BANK1, }, + { "in_good_avb_class_a", 4, 0x04, STATS_TYPE_BANK1, }, + { "in_good_avb_class_b", 4, 0x05, STATS_TYPE_BANK1, }, + { "in_bad_avb_class_a", 4, 0x06, STATS_TYPE_BANK1, }, + { "in_bad_avb_class_b", 4, 0x07, STATS_TYPE_BANK1, }, + { "tcam_counter_0", 4, 0x08, STATS_TYPE_BANK1, }, + { "tcam_counter_1", 4, 0x09, STATS_TYPE_BANK1, }, + { "tcam_counter_2", 4, 0x0a, STATS_TYPE_BANK1, }, + { "tcam_counter_3", 4, 0x0b, STATS_TYPE_BANK1, }, + { "in_da_unknown", 4, 0x0e, STATS_TYPE_BANK1, }, + { "in_management", 4, 0x0f, STATS_TYPE_BANK1, }, + { "out_queue_0", 4, 0x10, STATS_TYPE_BANK1, }, + { "out_queue_1", 4, 0x11, STATS_TYPE_BANK1, }, + { "out_queue_2", 4, 0x12, STATS_TYPE_BANK1, }, + { "out_queue_3", 4, 0x13, STATS_TYPE_BANK1, }, + { "out_queue_4", 4, 0x14, STATS_TYPE_BANK1, }, + { "out_queue_5", 4, 0x15, STATS_TYPE_BANK1, }, + { "out_queue_6", 4, 0x16, STATS_TYPE_BANK1, }, + { "out_queue_7", 4, 0x17, STATS_TYPE_BANK1, }, + { "out_cut_through", 4, 0x18, STATS_TYPE_BANK1, }, + { "out_octets_a", 4, 0x1a, STATS_TYPE_BANK1, }, + { "out_octets_b", 4, 0x1b, STATS_TYPE_BANK1, }, + { "out_management", 4, 0x1f, STATS_TYPE_BANK1, }, }; static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip, struct mv88e6xxx_hw_stat *stat) { switch (stat->type) { - case BANK0: + case STATS_TYPE_BANK0: return true; - case BANK1: + case STATS_TYPE_BANK1: return mv88e6xxx_6320_family(chip); - case PORT: + case STATS_TYPE_PORT: return mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip) || mv88e6xxx_6097_family(chip) || @@ -924,12 +924,12 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, { u32 low; u32 high = 0; + u16 reg = 0; int err; - u16 reg; u64 value; switch (s->type) { - case PORT: + case STATS_TYPE_PORT: err = mv88e6xxx_port_read(chip, port, s->reg, ®); if (err) return UINT64_MAX; @@ -942,26 +942,28 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, high = reg; } break; - case BANK0: - case BANK1: - _mv88e6xxx_stats_read(chip, s->reg, &low); + case STATS_TYPE_BANK1: + reg = GLOBAL_STATS_OP_BANK_1; + /* fall through */ + case STATS_TYPE_BANK0: + reg |= s->reg; + _mv88e6xxx_stats_read(chip, reg, &low); if (s->sizeof_stat == 8) - _mv88e6xxx_stats_read(chip, s->reg + 1, &high); + _mv88e6xxx_stats_read(chip, reg + 1, &high); } value = (((u64)high) << 16) | low; return value; } -static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, - uint8_t *data) +static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data, int types) { - struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_hw_stat *stat; int i, j; for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { stat = &mv88e6xxx_hw_stats[i]; - if (mv88e6xxx_has_stat(chip, stat)) { + if (stat->type & types) { memcpy(data + j * ETH_GSTRING_LEN, stat->string, ETH_GSTRING_LEN); j++; @@ -969,20 +971,65 @@ static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, } } -static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data) +{ + mv88e6xxx_stats_get_strings(chip, data, + STATS_TYPE_BANK0 | STATS_TYPE_PORT); +} + +static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data) +{ + mv88e6xxx_stats_get_strings(chip, data, + STATS_TYPE_BANK0 | STATS_TYPE_BANK1); +} + +static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, + uint8_t *data) { struct mv88e6xxx_chip *chip = ds->priv; + + if (chip->info->ops->stats_get_strings) + chip->info->ops->stats_get_strings(chip, data); +} + +static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip, + int types) +{ struct mv88e6xxx_hw_stat *stat; int i, j; for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { stat = &mv88e6xxx_hw_stats[i]; - if (mv88e6xxx_has_stat(chip, stat)) + if (stat->type & types) j++; } return j; } +static int mv88e6095_stats_get_sset_count(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 | + STATS_TYPE_PORT); +} + +static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 | + STATS_TYPE_BANK1); +} + +static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +{ + struct mv88e6xxx_chip *chip = ds->priv; + + if (chip->info->ops->stats_get_sset_count) + return chip->info->ops->stats_get_sset_count(chip); + + return 0; +} + static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { @@ -3167,6 +3214,8 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3178,6 +3227,8 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3189,6 +3240,8 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3200,6 +3253,8 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3211,6 +3266,8 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3222,6 +3279,8 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3234,6 +3293,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3248,6 +3309,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3260,6 +3323,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3274,6 +3339,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3285,6 +3352,8 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3298,6 +3367,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3311,6 +3382,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_speed = mv88e6390x_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3324,6 +3397,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3338,6 +3413,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3351,6 +3428,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3364,6 +3443,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3377,6 +3458,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3389,6 +3472,8 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3401,6 +3486,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3415,6 +3502,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -3428,6 +3517,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3441,6 +3532,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_speed = mv88e6390x_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_ops mv88e6391_ops = { @@ -3454,6 +3547,8 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_speed = mv88e6390_port_set_speed, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index d947a0c..09cce4c 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -806,19 +806,21 @@ struct mv88e6xxx_ops { * are separated out of the STATS_OP register. */ int (*stats_set_histogram)(struct mv88e6xxx_chip *chip); -}; -enum stat_type { - BANK0, - BANK1, - PORT, + /* Return the number of strings describing statistics */ + int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip); + void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data); }; +#define STATS_TYPE_PORT BIT(0) +#define STATS_TYPE_BANK0 BIT(1) +#define STATS_TYPE_BANK1 BIT(2) + struct mv88e6xxx_hw_stat { char string[ETH_GSTRING_LEN]; int sizeof_stat; int reg; - enum stat_type type; + int type; }; static inline bool mv88e6xxx_has(struct mv88e6xxx_chip *chip, -- cgit v1.1 From 052f947fe12353d36cf0d5430c2ddf87400a33ad Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:27:03 +0100 Subject: net: dsa: mv88e6xxx: Add stats_get_stats to ops structure Different families have different sets of statistics. Abstract this using a stats_get_stats op. The mv88e6390 needs a different implementation, which will be added later. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 83 +++++++++++++++++++++++------------ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 2 + 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ef6d357..ac99e5b 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -899,25 +899,6 @@ static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { { "out_management", 4, 0x1f, STATS_TYPE_BANK1, }, }; -static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_hw_stat *stat) -{ - switch (stat->type) { - case STATS_TYPE_BANK0: - return true; - case STATS_TYPE_BANK1: - return mv88e6xxx_6320_family(chip); - case STATS_TYPE_PORT: - return mv88e6xxx_6095_family(chip) || - mv88e6xxx_6185_family(chip) || - mv88e6xxx_6097_family(chip) || - mv88e6xxx_6165_family(chip) || - mv88e6xxx_6351_family(chip) || - mv88e6xxx_6352_family(chip); - } - return false; -} - static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, struct mv88e6xxx_hw_stat *s, int port) @@ -1030,13 +1011,47 @@ static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) return 0; } +static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data, int types) +{ + struct mv88e6xxx_hw_stat *stat; + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { + stat = &mv88e6xxx_hw_stats[i]; + if (stat->type & types) { + data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port); + j++; + } + } +} + +static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + return mv88e6xxx_stats_get_stats(chip, port, data, + STATS_TYPE_BANK0 | STATS_TYPE_PORT); +} + +static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + return mv88e6xxx_stats_get_stats(chip, port, data, + STATS_TYPE_BANK0 | STATS_TYPE_BANK1); +} + +static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + if (chip->info->ops->stats_get_stats) + chip->info->ops->stats_get_stats(chip, port, data); +} + static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { struct mv88e6xxx_chip *chip = ds->priv; - struct mv88e6xxx_hw_stat *stat; int ret; - int i, j; mutex_lock(&chip->reg_lock); @@ -1045,13 +1060,8 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); return; } - for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { - stat = &mv88e6xxx_hw_stats[i]; - if (mv88e6xxx_has_stat(chip, stat)) { - data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port); - j++; - } - } + + mv88e6xxx_get_stats(chip, port, data); mutex_unlock(&chip->reg_lock); } @@ -3216,6 +3226,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3229,6 +3240,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3242,6 +3254,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3255,6 +3268,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3268,6 +3282,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3281,6 +3296,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3295,6 +3311,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3311,6 +3328,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3325,6 +3343,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3341,6 +3360,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3354,6 +3374,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3415,6 +3436,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3445,6 +3467,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6320_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3460,6 +3483,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6320_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3474,6 +3498,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3488,6 +3513,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3504,6 +3530,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6390_ops = { diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 09cce4c..a6a66eb 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -810,6 +810,8 @@ struct mv88e6xxx_ops { /* Return the number of strings describing statistics */ int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip); void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data); + void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port, + uint64_t *data); }; #define STATS_TYPE_PORT BIT(0) -- cgit v1.1 From e0d8b61556b672e2b0f6f5f4e7517811b9905488 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:27:04 +0100 Subject: net: dsa: mv88e6xxx: Implement mv88e6390 get_stats The mv88e6390 uses a different bit to select between bank0 and bank1 of the statistics. So implement an ops function for this, and pass the selector bit to the generic stats read function. Also, the histogram selection has moved for the mv88e6390, so abstract its selection as well. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 39 +++++++++++++++++++++++++++-------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 3 ++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ac99e5b..5f21939 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -815,8 +815,7 @@ static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip, *val = 0; err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, - GLOBAL_STATS_OP_READ_CAPTURED | - GLOBAL_STATS_OP_HIST_RX_TX | stat); + GLOBAL_STATS_OP_READ_CAPTURED | stat); if (err) return; @@ -901,7 +900,8 @@ static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, struct mv88e6xxx_hw_stat *s, - int port) + int port, u16 bank1_select, + u16 histogram) { u32 low; u32 high = 0; @@ -924,10 +924,10 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, } break; case STATS_TYPE_BANK1: - reg = GLOBAL_STATS_OP_BANK_1; + reg = bank1_select; /* fall through */ case STATS_TYPE_BANK0: - reg |= s->reg; + reg |= s->reg | histogram; _mv88e6xxx_stats_read(chip, reg, &low); if (s->sizeof_stat == 8) _mv88e6xxx_stats_read(chip, reg + 1, &high); @@ -1012,7 +1012,8 @@ static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) } static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, - uint64_t *data, int types) + uint64_t *data, int types, + u16 bank1_select, u16 histogram) { struct mv88e6xxx_hw_stat *stat; int i, j; @@ -1020,7 +1021,9 @@ static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { stat = &mv88e6xxx_hw_stats[i]; if (stat->type & types) { - data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port); + data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port, + bank1_select, + histogram); j++; } } @@ -1030,14 +1033,25 @@ static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data) { return mv88e6xxx_stats_get_stats(chip, port, data, - STATS_TYPE_BANK0 | STATS_TYPE_PORT); + STATS_TYPE_BANK0 | STATS_TYPE_PORT, + 0, GLOBAL_STATS_OP_HIST_RX_TX); } static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data) { return mv88e6xxx_stats_get_stats(chip, port, data, - STATS_TYPE_BANK0 | STATS_TYPE_BANK1); + STATS_TYPE_BANK0 | STATS_TYPE_BANK1, + GLOBAL_STATS_OP_BANK_1_BIT_9, + GLOBAL_STATS_OP_HIST_RX_TX); +} + +static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + return mv88e6xxx_stats_get_stats(chip, port, data, + STATS_TYPE_BANK0 | STATS_TYPE_BANK1, + GLOBAL_STATS_OP_BANK_1_BIT_10, 0); } static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port, @@ -3390,6 +3404,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3405,6 +3420,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3420,6 +3436,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3452,6 +3469,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3546,6 +3564,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3561,6 +3580,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, }; static const struct mv88e6xxx_ops mv88e6391_ops = { @@ -3576,6 +3596,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index a6a66eb..9298faa 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -296,7 +296,8 @@ #define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY) #define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY) #define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY) -#define GLOBAL_STATS_OP_BANK_1 BIT(9) +#define GLOBAL_STATS_OP_BANK_1_BIT_9 BIT(9) +#define GLOBAL_STATS_OP_BANK_1_BIT_10 BIT(10) #define GLOBAL_STATS_COUNTER_32 0x1e #define GLOBAL_STATS_COUNTER_01 0x1f -- cgit v1.1 From 7f9ef3af397b2a10730e11fac8475f6509cbf603 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 21 Nov 2016 23:27:05 +0100 Subject: net: dsa: mv88e6xxx: Move g1 stats code in global1.[ch] Move the stats functions which access global 1 registers into global1.c. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 54 +++---------------------------------- drivers/net/dsa/mv88e6xxx/global1.c | 32 +++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/global1.h | 2 ++ 3 files changed, 36 insertions(+), 52 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5f21939..bada646 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -780,23 +780,6 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, netdev_err(ds->ports[port].netdev, "failed to configure MAC\n"); } -static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) -{ - u16 val; - int i, err; - - for (i = 0; i < 10; i++) { - err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val); - if (err) - return err; - - if ((val & GLOBAL_STATS_OP_BUSY) == 0) - return 0; - } - - return -ETIMEDOUT; -} - static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) { if (!chip->info->ops->stats_snapshot) @@ -805,37 +788,6 @@ static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) return chip->info->ops->stats_snapshot(chip, port); } -static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip, - int stat, u32 *val) -{ - u32 value; - u16 reg; - int err; - - *val = 0; - - err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, - GLOBAL_STATS_OP_READ_CAPTURED | stat); - if (err) - return; - - err = _mv88e6xxx_stats_wait(chip); - if (err) - return; - - err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, ®); - if (err) - return; - - value = reg << 16; - - err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, ®); - if (err) - return; - - *val = value | reg; -} - static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { { "in_good_octets", 8, 0x00, STATS_TYPE_BANK0, }, { "in_bad_octets", 4, 0x02, STATS_TYPE_BANK0, }, @@ -928,9 +880,9 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, /* fall through */ case STATS_TYPE_BANK0: reg |= s->reg | histogram; - _mv88e6xxx_stats_read(chip, reg, &low); + mv88e6xxx_g1_stats_read(chip, reg, &low); if (s->sizeof_stat == 8) - _mv88e6xxx_stats_read(chip, reg + 1, &high); + mv88e6xxx_g1_stats_read(chip, reg + 1, &high); } value = (((u64)high) << 16) | low; return value; @@ -2888,7 +2840,7 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) return err; /* Wait for the flush to complete. */ - err = _mv88e6xxx_stats_wait(chip); + err = mv88e6xxx_g1_stats_wait(chip); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index fda8b3c..5fcf23d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -53,7 +53,7 @@ int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) /* Offset 0x1d: Statistics Operation 2 */ -static int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) +int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) { return mv88e6xxx_g1_wait(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_BUSY); } @@ -95,3 +95,33 @@ int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) /* Wait for the snapshotting to complete. */ return mv88e6xxx_g1_stats_wait(chip); } + +void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val) +{ + u32 value; + u16 reg; + int err; + + *val = 0; + + err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_READ_CAPTURED | stat); + if (err) + return; + + err = mv88e6xxx_g1_stats_wait(chip); + if (err) + return; + + err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, ®); + if (err) + return; + + value = reg << 16; + + err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, ®); + if (err) + return; + + *val = value | reg; +} diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index ffc9f45..df3794c 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -19,9 +19,11 @@ int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val); int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); +int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); +void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val); #endif /* _MV88E6XXX_GLOBAL1_H */ -- cgit v1.1 From 55c63aaa6952c819e772e9c2d4a7602c062f012c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 22 Nov 2016 11:24:12 +0100 Subject: mlxsw: reg: Add Management Fan Speed Limit register The MFSL register is used to configure the fan speed event / interrupt notification mechanism. Fan speed threshold are defined for both under-speed and over-speed. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index edad7cb..2618e9c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4518,6 +4518,54 @@ static inline void mlxsw_reg_mfsm_pack(char *payload, u8 tacho) mlxsw_reg_mfsm_tacho_set(payload, tacho); } +/* MFSL - Management Fan Speed Limit Register + * ------------------------------------------ + * The Fan Speed Limit register is used to configure the fan speed + * event / interrupt notification mechanism. Fan speed threshold are + * defined for both under-speed and over-speed. + */ +#define MLXSW_REG_MFSL_ID 0x9004 +#define MLXSW_REG_MFSL_LEN 0x0C + +MLXSW_REG_DEFINE(mfsl, MLXSW_REG_MFSL_ID, MLXSW_REG_MFSL_LEN); + +/* reg_mfsl_tacho + * Fan tachometer index. + * Access: Index + */ +MLXSW_ITEM32(reg, mfsl, tacho, 0x00, 24, 4); + +/* reg_mfsl_tach_min + * Tachometer minimum value (minimum RPM). + * Access: RW + */ +MLXSW_ITEM32(reg, mfsl, tach_min, 0x04, 0, 16); + +/* reg_mfsl_tach_max + * Tachometer maximum value (maximum RPM). + * Access: RW + */ +MLXSW_ITEM32(reg, mfsl, tach_max, 0x08, 0, 16); + +static inline void mlxsw_reg_mfsl_pack(char *payload, u8 tacho, + u16 tach_min, u16 tach_max) +{ + MLXSW_REG_ZERO(mfsl, payload); + mlxsw_reg_mfsl_tacho_set(payload, tacho); + mlxsw_reg_mfsl_tach_min_set(payload, tach_min); + mlxsw_reg_mfsl_tach_max_set(payload, tach_max); +} + +static inline void mlxsw_reg_mfsl_unpack(char *payload, u8 tacho, + u16 *p_tach_min, u16 *p_tach_max) +{ + if (p_tach_min) + *p_tach_min = mlxsw_reg_mfsl_tach_min_get(payload); + + if (p_tach_max) + *p_tach_max = mlxsw_reg_mfsl_tach_max_get(payload); +} + /* MTCAP - Management Temperature Capabilities * ------------------------------------------- * This register exposes the capabilities of the device and @@ -5228,6 +5276,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(mfcr), MLXSW_REG(mfsc), MLXSW_REG(mfsm), + MLXSW_REG(mfsl), MLXSW_REG(mtcap), MLXSW_REG(mtmp), MLXSW_REG(mpat), -- cgit v1.1 From a50c1e35650b929500bd89be61c89d95a267ce56 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Tue, 22 Nov 2016 11:24:13 +0100 Subject: mlxsw: core: Implement thermal zone Implement thermal zone for mlxsw based HW. It uses temperature sensor provided by ASIC (the same as mlxsw hwmon interface) to report current temp to thermal core. The ASIC's PWM is then used to control speed of system fans registered as cooling devices. Signed-off-by: Ivan Vecera Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 9 + drivers/net/ethernet/mellanox/mlxsw/Makefile | 1 + drivers/net/ethernet/mellanox/mlxsw/core.c | 8 + drivers/net/ethernet/mellanox/mlxsw/core.h | 24 ++ drivers/net/ethernet/mellanox/mlxsw/core_thermal.c | 442 +++++++++++++++++++++ 5 files changed, 484 insertions(+) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/core_thermal.c diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index c9822e6..95ae4c0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -19,6 +19,15 @@ config MLXSW_CORE_HWMON ---help--- Say Y here if you want to expose HWMON interface on mlxsw devices. +config MLXSW_CORE_THERMAL + bool "Thermal zone support for Mellanox Technologies Switch ASICs" + depends on MLXSW_CORE && THERMAL + depends on !(MLXSW_CORE=y && THERMAL=m) + default y + ---help--- + Say Y here if you want to automatically control fans speed according + ambient temperature reported by ASIC. + config MLXSW_PCI tristate "PCI bus implementation for Mellanox Technologies Switch ASICs" depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 2722942..fe8dadb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o mlxsw_core-objs := core.o mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o +mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o mlxsw_pci-objs := pci.o obj-$(CONFIG_MLXSW_I2C) += mlxsw_i2c.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 763752f..bcd7251 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -131,6 +131,7 @@ struct mlxsw_core { } lag; struct mlxsw_res res; struct mlxsw_hwmon *hwmon; + struct mlxsw_thermal *thermal; struct mlxsw_core_port ports[MLXSW_PORT_MAX_PORTS]; unsigned long driver_priv[0]; /* driver_priv has to be always the last item */ @@ -1162,6 +1163,11 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_hwmon_init; + err = mlxsw_thermal_init(mlxsw_core, mlxsw_bus_info, + &mlxsw_core->thermal); + if (err) + goto err_thermal_init; + if (mlxsw_driver->init) { err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info); if (err) @@ -1178,6 +1184,7 @@ err_debugfs_init: if (mlxsw_core->driver->fini) mlxsw_core->driver->fini(mlxsw_core); err_driver_init: +err_thermal_init: err_hwmon_init: devlink_unregister(devlink); err_devlink_register: @@ -1204,6 +1211,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) mlxsw_core_debugfs_fini(mlxsw_core); if (mlxsw_core->driver->fini) mlxsw_core->driver->fini(mlxsw_core); + mlxsw_thermal_fini(mlxsw_core->thermal); devlink_unregister(devlink); mlxsw_emad_fini(mlxsw_core); mlxsw_core->bus->fini(mlxsw_core->bus_priv); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index f7a4d838..3de8955 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -321,4 +321,28 @@ static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, #endif +struct mlxsw_thermal; + +#ifdef CONFIG_MLXSW_CORE_THERMAL + +int mlxsw_thermal_init(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info, + struct mlxsw_thermal **p_thermal); +void mlxsw_thermal_fini(struct mlxsw_thermal *thermal); + +#else + +static inline int mlxsw_thermal_init(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info, + struct mlxsw_thermal **p_thermal) +{ + return 0; +} + +static inline void mlxsw_thermal_fini(struct mlxsw_thermal *thermal) +{ +} + +#endif + #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c new file mode 100644 index 0000000..d866c98 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c @@ -0,0 +1,442 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/core_thermal.c + * Copyright (c) 2016 Ivan Vecera + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#define MLXSW_THERMAL_POLL_INT 1000 /* ms */ +#define MLXSW_THERMAL_MAX_TEMP 110000 /* 110C */ +#define MLXSW_THERMAL_MAX_STATE 10 +#define MLXSW_THERMAL_MAX_DUTY 255 + +struct mlxsw_thermal_trip { + int type; + int temp; + int min_state; + int max_state; +}; + +static const struct mlxsw_thermal_trip default_thermal_trips[] = { + { /* In range - 0-40% PWM */ + .type = THERMAL_TRIP_ACTIVE, + .temp = 75000, + .min_state = 0, + .max_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10, + }, + { /* High - 40-100% PWM */ + .type = THERMAL_TRIP_ACTIVE, + .temp = 80000, + .min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10, + .max_state = MLXSW_THERMAL_MAX_STATE, + }, + { + /* Very high - 100% PWM */ + .type = THERMAL_TRIP_ACTIVE, + .temp = 85000, + .min_state = MLXSW_THERMAL_MAX_STATE, + .max_state = MLXSW_THERMAL_MAX_STATE, + }, + { /* Warning */ + .type = THERMAL_TRIP_HOT, + .temp = 105000, + .min_state = MLXSW_THERMAL_MAX_STATE, + .max_state = MLXSW_THERMAL_MAX_STATE, + }, + { /* Critical - soft poweroff */ + .type = THERMAL_TRIP_CRITICAL, + .temp = MLXSW_THERMAL_MAX_TEMP, + .min_state = MLXSW_THERMAL_MAX_STATE, + .max_state = MLXSW_THERMAL_MAX_STATE, + } +}; + +#define MLXSW_THERMAL_NUM_TRIPS ARRAY_SIZE(default_thermal_trips) + +/* Make sure all trips are writable */ +#define MLXSW_THERMAL_TRIP_MASK (BIT(MLXSW_THERMAL_NUM_TRIPS) - 1) + +struct mlxsw_thermal { + struct mlxsw_core *core; + const struct mlxsw_bus_info *bus_info; + struct thermal_zone_device *tzdev; + struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX]; + struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS]; + enum thermal_device_mode mode; +}; + +static inline u8 mlxsw_state_to_duty(int state) +{ + return DIV_ROUND_CLOSEST(state * MLXSW_THERMAL_MAX_DUTY, + MLXSW_THERMAL_MAX_STATE); +} + +static inline int mlxsw_duty_to_state(u8 duty) +{ + return DIV_ROUND_CLOSEST(duty * MLXSW_THERMAL_MAX_STATE, + MLXSW_THERMAL_MAX_DUTY); +} + +static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal, + struct thermal_cooling_device *cdev) +{ + int i; + + for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) + if (thermal->cdevs[i] == cdev) + return i; + + return -ENODEV; +} + +static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev, + struct thermal_cooling_device *cdev) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + struct device *dev = thermal->bus_info->dev; + int i, err; + + /* If the cooling device is one of ours bind it */ + if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) + return 0; + + for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { + const struct mlxsw_thermal_trip *trip = &thermal->trips[i]; + + err = thermal_zone_bind_cooling_device(tzdev, i, cdev, + trip->max_state, + trip->min_state, + THERMAL_WEIGHT_DEFAULT); + if (err < 0) { + dev_err(dev, "Failed to bind cooling device to trip %d\n", i); + return err; + } + } + return 0; +} + +static int mlxsw_thermal_unbind(struct thermal_zone_device *tzdev, + struct thermal_cooling_device *cdev) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + struct device *dev = thermal->bus_info->dev; + int i; + int err; + + /* If the cooling device is our one unbind it */ + if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0) + return 0; + + for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) { + err = thermal_zone_unbind_cooling_device(tzdev, i, cdev); + if (err < 0) { + dev_err(dev, "Failed to unbind cooling device\n"); + return err; + } + } + return 0; +} + +static int mlxsw_thermal_get_mode(struct thermal_zone_device *tzdev, + enum thermal_device_mode *mode) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + *mode = thermal->mode; + + return 0; +} + +static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev, + enum thermal_device_mode mode) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + mutex_lock(&tzdev->lock); + + if (mode == THERMAL_DEVICE_ENABLED) + tzdev->polling_delay = MLXSW_THERMAL_POLL_INT; + else + tzdev->polling_delay = 0; + + mutex_unlock(&tzdev->lock); + + thermal->mode = mode; + thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED); + + return 0; +} + +static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev, + int *p_temp) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + struct device *dev = thermal->bus_info->dev; + char mtmp_pl[MLXSW_REG_MTMP_LEN]; + unsigned int temp; + int err; + + mlxsw_reg_mtmp_pack(mtmp_pl, 0, false, false); + + err = mlxsw_reg_query(thermal->core, MLXSW_REG(mtmp), mtmp_pl); + if (err) { + dev_err(dev, "Failed to query temp sensor\n"); + return err; + } + mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL); + + *p_temp = (int) temp; + return 0; +} + +static int mlxsw_thermal_get_trip_type(struct thermal_zone_device *tzdev, + int trip, + enum thermal_trip_type *p_type) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + *p_type = thermal->trips[trip].type; + return 0; +} + +static int mlxsw_thermal_get_trip_temp(struct thermal_zone_device *tzdev, + int trip, int *p_temp) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS) + return -EINVAL; + + *p_temp = thermal->trips[trip].temp; + return 0; +} + +static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev, + int trip, int temp) +{ + struct mlxsw_thermal *thermal = tzdev->devdata; + + if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS || + temp > MLXSW_THERMAL_MAX_TEMP) + return -EINVAL; + + thermal->trips[trip].temp = temp; + return 0; +} + +static struct thermal_zone_device_ops mlxsw_thermal_ops = { + .bind = mlxsw_thermal_bind, + .unbind = mlxsw_thermal_unbind, + .get_mode = mlxsw_thermal_get_mode, + .set_mode = mlxsw_thermal_set_mode, + .get_temp = mlxsw_thermal_get_temp, + .get_trip_type = mlxsw_thermal_get_trip_type, + .get_trip_temp = mlxsw_thermal_get_trip_temp, + .set_trip_temp = mlxsw_thermal_set_trip_temp, +}; + +static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *p_state) +{ + *p_state = MLXSW_THERMAL_MAX_STATE; + return 0; +} + +static int mlxsw_thermal_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *p_state) + +{ + struct mlxsw_thermal *thermal = cdev->devdata; + struct device *dev = thermal->bus_info->dev; + char mfsc_pl[MLXSW_REG_MFSC_LEN]; + int err, idx; + u8 duty; + + idx = mlxsw_get_cooling_device_idx(thermal, cdev); + if (idx < 0) + return idx; + + mlxsw_reg_mfsc_pack(mfsc_pl, idx, 0); + err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsc), mfsc_pl); + if (err) { + dev_err(dev, "Failed to query PWM duty\n"); + return err; + } + + duty = mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl); + *p_state = mlxsw_duty_to_state(duty); + return 0; +} + +static int mlxsw_thermal_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) + +{ + struct mlxsw_thermal *thermal = cdev->devdata; + struct device *dev = thermal->bus_info->dev; + char mfsc_pl[MLXSW_REG_MFSC_LEN]; + int err, idx; + + idx = mlxsw_get_cooling_device_idx(thermal, cdev); + if (idx < 0) + return idx; + + mlxsw_reg_mfsc_pack(mfsc_pl, idx, mlxsw_state_to_duty(state)); + err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsc), mfsc_pl); + if (err) { + dev_err(dev, "Failed to write PWM duty\n"); + return err; + } + return 0; +} + +static const struct thermal_cooling_device_ops mlxsw_cooling_ops = { + .get_max_state = mlxsw_thermal_get_max_state, + .get_cur_state = mlxsw_thermal_get_cur_state, + .set_cur_state = mlxsw_thermal_set_cur_state, +}; + +int mlxsw_thermal_init(struct mlxsw_core *core, + const struct mlxsw_bus_info *bus_info, + struct mlxsw_thermal **p_thermal) +{ + char mfcr_pl[MLXSW_REG_MFCR_LEN] = { 0 }; + enum mlxsw_reg_mfcr_pwm_frequency freq; + struct device *dev = bus_info->dev; + struct mlxsw_thermal *thermal; + u16 tacho_active; + u8 pwm_active; + int err, i; + + thermal = devm_kzalloc(dev, sizeof(*thermal), + GFP_KERNEL); + if (!thermal) + return -ENOMEM; + + thermal->core = core; + thermal->bus_info = bus_info; + memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips)); + + err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl); + if (err) { + dev_err(dev, "Failed to probe PWMs\n"); + goto err_free_thermal; + } + mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active); + + for (i = 0; i < MLXSW_MFCR_TACHOS_MAX; i++) { + if (tacho_active & BIT(i)) { + char mfsl_pl[MLXSW_REG_MFSL_LEN]; + + mlxsw_reg_mfsl_pack(mfsl_pl, i, 0, 0); + + /* We need to query the register to preserve maximum */ + err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsl), + mfsl_pl); + if (err) + goto err_free_thermal; + + /* set the minimal RPMs to 0 */ + mlxsw_reg_mfsl_tach_min_set(mfsl_pl, 0); + err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsl), + mfsl_pl); + if (err) + goto err_free_thermal; + } + } + for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) { + if (pwm_active & BIT(i)) { + struct thermal_cooling_device *cdev; + + cdev = thermal_cooling_device_register("Fan", thermal, + &mlxsw_cooling_ops); + if (IS_ERR(cdev)) { + err = PTR_ERR(cdev); + dev_err(dev, "Failed to register cooling device\n"); + goto err_unreg_cdevs; + } + thermal->cdevs[i] = cdev; + } + } + + thermal->tzdev = thermal_zone_device_register("mlxsw", + MLXSW_THERMAL_NUM_TRIPS, + MLXSW_THERMAL_TRIP_MASK, + thermal, + &mlxsw_thermal_ops, + NULL, 0, + MLXSW_THERMAL_POLL_INT); + if (IS_ERR(thermal->tzdev)) { + err = PTR_ERR(thermal->tzdev); + dev_err(dev, "Failed to register thermal zone\n"); + goto err_unreg_cdevs; + } + + thermal->mode = THERMAL_DEVICE_ENABLED; + *p_thermal = thermal; + return 0; +err_unreg_cdevs: + for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) + if (thermal->cdevs[i]) + thermal_cooling_device_unregister(thermal->cdevs[i]); +err_free_thermal: + devm_kfree(dev, thermal); + return err; +} + +void mlxsw_thermal_fini(struct mlxsw_thermal *thermal) +{ + int i; + + if (thermal->tzdev) { + thermal_zone_device_unregister(thermal->tzdev); + thermal->tzdev = NULL; + } + + for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) { + if (thermal->cdevs[i]) { + thermal_cooling_device_unregister(thermal->cdevs[i]); + thermal->cdevs[i] = NULL; + } + } + + devm_kfree(thermal->bus_info->dev, thermal); +} -- cgit v1.1 From 06b37b650cf826349677564cb0ff1560ed8e51fc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 22 Nov 2016 15:21:22 +0100 Subject: marvell: mark mvneta and mvpp2 32-bit only Both of these drivers won't work on 64-bit architectures unless they are redesigned, since they store a virtual address pointer in a 32-bit field of the descriptors: drivers/net/ethernet/marvell/mvneta_bm.c: In function 'mvneta_bm_construct': drivers/net/ethernet/marvell/mvneta_bm.c:103:16: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] drivers/net/ethernet/marvell/mvpp2.c: In function 'mvpp2_prs_vlan_init': drivers/net/ethernet/marvell/mvpp2.c:2563:32: error: large integer implicitly truncated to unsigned type [-Werror=overflow] This limits the COMPILE_TEST option for the two drivers again to only build them on 32-bit. This seems nicer than shutting up the warnings, in case we ever actually want to use them on 64-bit, as the warnings indicate which parts of the driver are currently broken there. Fixes: a0627f776a45 ("net: marvell: Allow drivers to be built with COMPILE_TEST") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index d74d4e6..66fd9db 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -58,6 +58,7 @@ config MVNETA tristate "Marvell Armada 370/38x/XP network interface support" depends on PLAT_ORION || COMPILE_TEST depends on HAS_DMA + depends on !64BIT select MVMDIO select FIXED_PHY ---help--- @@ -81,6 +82,7 @@ config MVPP2 tristate "Marvell Armada 375 network interface support" depends on MACH_ARMADA_375 || COMPILE_TEST depends on HAS_DMA + depends on !64BIT select MVMDIO ---help--- This driver supports the network interface units in the -- cgit v1.1 From ff32eeb86aa18a3d11e32b279faaae4b2e4fef38 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 6 Sep 2016 12:38:41 +0530 Subject: ath10k: advertize hardware packet loss mechanism Indicate hardware (or firmware) supports that CQM packet-loss report will be generated based on station kickout algorithm. As of now mac80211 tracks connection loss by missing msdu counts (50) whereas ath10k firmware tracks them by missing ppdus (+ BAR tries). While firmware is trying to adapt its rate table, mac80211 might send out low_ack event to hostapd. This is causing frequent connect and disconnect iteration under noisy environment or when station is roaming around. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 144e0ef..12602af 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8005,6 +8005,7 @@ int ath10k_mac_register(struct ath10k *ar) ieee80211_hw_set(ar->hw, WANT_MONITOR_VIF); ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA); ieee80211_hw_set(ar->hw, QUEUE_CONTROL); + ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK); if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL); -- cgit v1.1 From 973324fff154beb51711136d5d242df7f328f708 Mon Sep 17 00:00:00 2001 From: "Pedersen, Thomas" Date: Wed, 28 Sep 2016 16:56:29 -0700 Subject: ath10k: implement offset_tsf ieee80211_op Current set_tsf is implemented in terms of TSF_INCREMENT only. Instead support new WMI command TSF_DECREMENT and export these through offset_tsf. Advantage is we get more accurate TSF adjustments, and don't calculate wrong offset in case absolute TSF was calculated from rx_mactime (actual TSF). The new WMI command is available in firmware 10.4-3.2.1-00033 for QCA4019 chips. Old drivers on new firmware or vice versa shouldn't be a problem since get/set tsf logic was already broken. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 25 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.c | 2 ++ drivers/net/wireless/ath/ath10k/wmi.h | 7 +++++++ 3 files changed, 34 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 12602af..7ab74a1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -7012,6 +7012,30 @@ static void ath10k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ath10k_warn(ar, "failed to set tsf offset: %d\n", ret); } +static void ath10k_offset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, s64 tsf_offset) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + u32 offset, vdev_param; + int ret; + + if (tsf_offset < 0) { + vdev_param = ar->wmi.vdev_param->dec_tsf; + offset = -tsf_offset; + } else { + vdev_param = ar->wmi.vdev_param->inc_tsf; + offset = tsf_offset; + } + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + vdev_param, offset); + + if (ret && ret != -EOPNOTSUPP) + ath10k_warn(ar, "failed to set tsf offset %d cmd %d: %d\n", + offset, vdev_param, ret); +} + static int ath10k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) @@ -7476,6 +7500,7 @@ static const struct ieee80211_ops ath10k_ops = { .sta_rc_update = ath10k_sta_rc_update, .get_tsf = ath10k_get_tsf, .set_tsf = ath10k_set_tsf, + .offset_tsf = ath10k_offset_tsf, .ampdu_action = ath10k_ampdu_action, .get_et_sset_count = ath10k_debug_get_et_sset_count, .get_et_stats = ath10k_debug_get_et_stats, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 387c4ee..bc89613 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1013,6 +1013,8 @@ static struct wmi_vdev_param_map wmi_10_4_vdev_param_map = { .rx_decap_type = WMI_10_4_VDEV_PARAM_RX_DECAP_TYPE, .bw_nss_ratemask = WMI_10_4_VDEV_PARAM_BW_NSS_RATEMASK, .set_tsf = WMI_10_4_VDEV_PARAM_TSF_INCREMENT, + .inc_tsf = WMI_10_4_VDEV_PARAM_TSF_INCREMENT, + .dec_tsf = WMI_10_4_VDEV_PARAM_TSF_DECREMENT, }; static struct wmi_pdev_param_map wmi_pdev_param_map = { diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 1b243c8..9d13c6f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4677,6 +4677,8 @@ struct wmi_vdev_param_map { u32 rx_decap_type; u32 bw_nss_ratemask; u32 set_tsf; + u32 inc_tsf; + u32 dec_tsf; }; #define WMI_VDEV_PARAM_UNSUPPORTED 0 @@ -5009,6 +5011,11 @@ enum wmi_10_4_vdev_param { WMI_10_4_VDEV_PARAM_STA_KICKOUT, WMI_10_4_VDEV_PARAM_CAPABILITIES, WMI_10_4_VDEV_PARAM_TSF_INCREMENT, + WMI_10_4_VDEV_PARAM_RX_FILTER, + WMI_10_4_VDEV_PARAM_MGMT_TX_POWER, + WMI_10_4_VDEV_PARAM_ATF_SSID_SCHED_POLICY, + WMI_10_4_VDEV_PARAM_DISABLE_DYN_BW_RTS, + WMI_10_4_VDEV_PARAM_TSF_DECREMENT, }; #define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0) -- cgit v1.1 From f6f64cfb966d95a5d1e68d6bd3b74b2938ee18cd Mon Sep 17 00:00:00 2001 From: "Pedersen, Thomas" Date: Wed, 28 Sep 2016 16:56:30 -0700 Subject: ath10k: remove set/get_tsf ieee80211_ops Neither of these did the right thing: - get_tsf just returned 0 - set_tsf assumed a simple offset was applied against get_tsf(), which works, except in the case of calculating TSF from rx_mactime (actual TSF). Just remove them for now. We can reimplement set_tsf in terms of TSF increment/decrement in the future if get_tsf is ever supported by FW. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 38 ------------------------------- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 1 - drivers/net/wireless/ath/ath10k/wmi.c | 4 ---- drivers/net/wireless/ath/ath10k/wmi.h | 1 - 4 files changed, 44 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 7ab74a1..3918097 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6976,42 +6976,6 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, ieee80211_queue_work(hw, &arsta->update_wk); } -static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - /* - * FIXME: Return 0 for time being. Need to figure out whether FW - * has the API to fetch 64-bit local TSF - */ - - return 0; -} - -static void ath10k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u64 tsf) -{ - struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - u32 tsf_offset, vdev_param = ar->wmi.vdev_param->set_tsf; - int ret; - - /* Workaround: - * - * Given tsf argument is entire TSF value, but firmware accepts - * only TSF offset to current TSF. - * - * get_tsf function is used to get offset value, however since - * ath10k_get_tsf is not implemented properly, it will return 0 always. - * Luckily all the caller functions to set_tsf, as of now, also rely on - * get_tsf function to get entire tsf value such get_tsf() + tsf_delta, - * final tsf offset value to firmware will be arithmetically correct. - */ - tsf_offset = tsf - ath10k_get_tsf(hw, vif); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - vdev_param, tsf_offset); - if (ret && ret != -EOPNOTSUPP) - ath10k_warn(ar, "failed to set tsf offset: %d\n", ret); -} - static void ath10k_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, s64 tsf_offset) { @@ -7498,8 +7462,6 @@ static const struct ieee80211_ops ath10k_ops = { .get_survey = ath10k_get_survey, .set_bitrate_mask = ath10k_mac_op_set_bitrate_mask, .sta_rc_update = ath10k_sta_rc_update, - .get_tsf = ath10k_get_tsf, - .set_tsf = ath10k_set_tsf, .offset_tsf = ath10k_offset_tsf, .ampdu_action = ath10k_ampdu_action, .get_et_sset_count = ath10k_debug_get_et_sset_count, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index e64f593..5e399c6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3464,7 +3464,6 @@ static struct wmi_vdev_param_map wmi_tlv_vdev_param_map = { .meru_vc = WMI_VDEV_PARAM_UNSUPPORTED, .rx_decap_type = WMI_VDEV_PARAM_UNSUPPORTED, .bw_nss_ratemask = WMI_VDEV_PARAM_UNSUPPORTED, - .set_tsf = WMI_VDEV_PARAM_UNSUPPORTED, }; static const struct wmi_ops wmi_tlv_ops = { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index bc89613..95be2ba 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -785,7 +785,6 @@ static struct wmi_vdev_param_map wmi_vdev_param_map = { .meru_vc = WMI_VDEV_PARAM_UNSUPPORTED, .rx_decap_type = WMI_VDEV_PARAM_UNSUPPORTED, .bw_nss_ratemask = WMI_VDEV_PARAM_UNSUPPORTED, - .set_tsf = WMI_VDEV_PARAM_UNSUPPORTED, }; /* 10.X WMI VDEV param map */ @@ -861,7 +860,6 @@ static struct wmi_vdev_param_map wmi_10x_vdev_param_map = { .meru_vc = WMI_VDEV_PARAM_UNSUPPORTED, .rx_decap_type = WMI_VDEV_PARAM_UNSUPPORTED, .bw_nss_ratemask = WMI_VDEV_PARAM_UNSUPPORTED, - .set_tsf = WMI_VDEV_PARAM_UNSUPPORTED, }; static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = { @@ -936,7 +934,6 @@ static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = { .meru_vc = WMI_VDEV_PARAM_UNSUPPORTED, .rx_decap_type = WMI_VDEV_PARAM_UNSUPPORTED, .bw_nss_ratemask = WMI_VDEV_PARAM_UNSUPPORTED, - .set_tsf = WMI_10X_VDEV_PARAM_TSF_INCREMENT, }; static struct wmi_vdev_param_map wmi_10_4_vdev_param_map = { @@ -1012,7 +1009,6 @@ static struct wmi_vdev_param_map wmi_10_4_vdev_param_map = { .meru_vc = WMI_10_4_VDEV_PARAM_MERU_VC, .rx_decap_type = WMI_10_4_VDEV_PARAM_RX_DECAP_TYPE, .bw_nss_ratemask = WMI_10_4_VDEV_PARAM_BW_NSS_RATEMASK, - .set_tsf = WMI_10_4_VDEV_PARAM_TSF_INCREMENT, .inc_tsf = WMI_10_4_VDEV_PARAM_TSF_INCREMENT, .dec_tsf = WMI_10_4_VDEV_PARAM_TSF_DECREMENT, }; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 9d13c6f..3a73b5c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4676,7 +4676,6 @@ struct wmi_vdev_param_map { u32 meru_vc; u32 rx_decap_type; u32 bw_nss_ratemask; - u32 set_tsf; u32 inc_tsf; u32 dec_tsf; }; -- cgit v1.1 From 18ae68fff392e445af3c2d8be9bef8a16e1c72a7 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 14 Nov 2016 14:25:23 +0100 Subject: ath10k: fix null deref on wmi-tlv when trying spectral scan WMI ops wrappers did not properly check for null function pointers for spectral scan. This caused null dereference crash with WMI-TLV based firmware which doesn't implement spectral scan. The crash could be triggered with: ip link set dev wlan0 up echo background > /sys/kernel/debug/ieee80211/phy0/ath10k/spectral_scan_ctl The crash looked like this: [ 168.031989] BUG: unable to handle kernel NULL pointer dereference at (null) [ 168.037406] IP: [< (null)>] (null) [ 168.040395] PGD cdd4067 PUD fa0f067 PMD 0 [ 168.043303] Oops: 0010 [#1] SMP [ 168.045377] Modules linked in: ath10k_pci(O) ath10k_core(O) ath mac80211 cfg80211 [last unloaded: cfg80211] [ 168.051560] CPU: 1 PID: 1380 Comm: bash Tainted: G W O 4.8.0 #78 [ 168.054336] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014 [ 168.059183] task: ffff88000c460c00 task.stack: ffff88000d4bc000 [ 168.061736] RIP: 0010:[<0000000000000000>] [< (null)>] (null) ... [ 168.100620] Call Trace: [ 168.101910] [] ? ath10k_spectral_scan_config+0x96/0x200 [ath10k_core] [ 168.104871] [] ? filemap_fault+0xb2/0x4a0 [ 168.106696] [] write_file_spec_scan_ctl+0x116/0x280 [ath10k_core] [ 168.109618] [] full_proxy_write+0x51/0x80 [ 168.111443] [] __vfs_write+0x28/0x120 [ 168.113090] [] ? security_file_permission+0x3d/0xc0 [ 168.114932] [] ? percpu_down_read+0x12/0x60 [ 168.116680] [] vfs_write+0xb8/0x1a0 [ 168.118293] [] SyS_write+0x46/0xa0 [ 168.119912] [] entry_SYSCALL_64_fastpath+0x1a/0xa4 [ 168.121737] Code: Bad RIP value. [ 168.123318] RIP [< (null)>] (null) Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-ops.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index c9a8bb1..c7956e1 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -660,6 +660,9 @@ ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, struct sk_buff *skb; u32 cmd_id; + if (!ar->wmi.ops->gen_vdev_spectral_conf) + return -EOPNOTSUPP; + skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -675,6 +678,9 @@ ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, struct sk_buff *skb; u32 cmd_id; + if (!ar->wmi.ops->gen_vdev_spectral_enable) + return -EOPNOTSUPP; + skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger, enable); if (IS_ERR(skb)) -- cgit v1.1 From 5a401f36ba3080cded5a65fc671648e7cf285ee4 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 14 Nov 2016 14:26:40 +0100 Subject: ath10k: add spectral scan support to wmi-tlv Command structure and event flow doesn't seem to be any different compared to existing implementation for other firmware branches. This patch effectively adds in-driver support for spectral scanning on QCA61x4 and QCA9377. Tested QCA9377 w/ WLAN.TF.1.0-00267-1. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 72 +++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 5e399c6..97b536e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3136,6 +3136,76 @@ ath10k_wmi_tlv_op_gen_echo(struct ath10k *ar, u32 value) return skb; } +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_spectral_conf(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg) +{ + struct wmi_vdev_spectral_conf_cmd *cmd; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_CONFIGURE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->scan_count = __cpu_to_le32(arg->scan_count); + cmd->scan_period = __cpu_to_le32(arg->scan_period); + cmd->scan_priority = __cpu_to_le32(arg->scan_priority); + cmd->scan_fft_size = __cpu_to_le32(arg->scan_fft_size); + cmd->scan_gc_ena = __cpu_to_le32(arg->scan_gc_ena); + cmd->scan_restart_ena = __cpu_to_le32(arg->scan_restart_ena); + cmd->scan_noise_floor_ref = __cpu_to_le32(arg->scan_noise_floor_ref); + cmd->scan_init_delay = __cpu_to_le32(arg->scan_init_delay); + cmd->scan_nb_tone_thr = __cpu_to_le32(arg->scan_nb_tone_thr); + cmd->scan_str_bin_thr = __cpu_to_le32(arg->scan_str_bin_thr); + cmd->scan_wb_rpt_mode = __cpu_to_le32(arg->scan_wb_rpt_mode); + cmd->scan_rssi_rpt_mode = __cpu_to_le32(arg->scan_rssi_rpt_mode); + cmd->scan_rssi_thr = __cpu_to_le32(arg->scan_rssi_thr); + cmd->scan_pwr_format = __cpu_to_le32(arg->scan_pwr_format); + cmd->scan_rpt_mode = __cpu_to_le32(arg->scan_rpt_mode); + cmd->scan_bin_scale = __cpu_to_le32(arg->scan_bin_scale); + cmd->scan_dbm_adj = __cpu_to_le32(arg->scan_dbm_adj); + cmd->scan_chn_mask = __cpu_to_le32(arg->scan_chn_mask); + + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, + u32 trigger, u32 enable) +{ + struct wmi_vdev_spectral_enable_cmd *cmd; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_ENABLE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->trigger_cmd = __cpu_to_le32(trigger); + cmd->enable_cmd = __cpu_to_le32(enable); + + return skb; +} + /****************/ /* TLV mappings */ /****************/ @@ -3541,6 +3611,8 @@ static const struct wmi_ops wmi_tlv_ops = { .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, .gen_echo = ath10k_wmi_tlv_op_gen_echo, + .gen_vdev_spectral_conf = ath10k_wmi_tlv_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_tlv_op_gen_vdev_spectral_enable, }; static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = { -- cgit v1.1 From 91851cc7a939039bd401adb6ca3da4402bec1d0c Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 15 Nov 2016 18:47:21 +0100 Subject: ath9k: fix ath9k_hw_gpio_get() to return 0 or 1 on success Commit b2d70d4944c1 ("ath9k: make GPIO API to support both of WMAC and SOC") refactored ath9k_hw_gpio_get() to support both WMAC and SOC GPIOs, changing the return on success from 1 to BIT(gpio). This broke some callers like ath_is_rfkill_set(). This doesn't fix any known bug in mainline at the moment, but should be fixed anyway. Instead of fixing all callers, change ath9k_hw_gpio_get() back to only return 0 or 1. Fixes: b2d70d4944c1 ("ath9k: make GPIO API to support both of WMAC and SOC") Cc: # v4.7+ Signed-off-by: Matthias Schiffer [kvalo@qca.qualcomm.com: mention that doesn't fix any known bug] Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 14b13f0..a35f78b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2792,7 +2792,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) WARN_ON(1); } - return val; + return !!val; } EXPORT_SYMBOL(ath9k_hw_gpio_get); -- cgit v1.1 From 40bea976c72b9ee60f8d097852deb53ccbeaffbe Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Wed, 16 Nov 2016 17:23:08 +0800 Subject: ath9k: fix NULL pointer dereference relay_open() may return NULL, check the return value to avoid the crash. BUG: unable to handle kernel NULL pointer dereference at 0000000000000040 IP: [] ath_cmn_process_fft+0xd5/0x700 [ath9k_common] PGD 41cf28067 PUD 41be92067 PMD 0 Oops: 0000 [#1] SMP CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.8.6+ #35 Hardware name: Hewlett-Packard h8-1080t/2A86, BIOS 6.15 07/04/2011 task: ffffffff81e0c4c0 task.stack: ffffffff81e00000 RIP: 0010:[] [] ath_cmn_process_fft+0xd5/0x700 [ath9k_common] RSP: 0018:ffff88041f203ca0 EFLAGS: 00010293 RAX: 0000000000000000 RBX: 000000000000059f RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000040 RDI: ffffffff81f0ca98 RBP: ffff88041f203dc8 R08: ffffffffffffffff R09: 00000000000000ff R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffffffff81f0ca98 R14: 0000000000000000 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff88041f200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000040 CR3: 000000041b6ec000 CR4: 00000000000006f0 Stack: 0000000000000363 00000000000003f3 00000000000003f3 00000000000001f9 000000000000049a 0000000001252c04 ffff88041f203e44 ffff880417b4bfd0 0000000000000008 ffff88041785b9c0 0000000000000002 ffff88041613dc60 Call Trace: [] ath9k_tasklet+0x1b1/0x220 [ath9k] [] tasklet_action+0x4d/0xf0 [] __do_softirq+0x92/0x2a0 Reported-by: Devin Tuchsen Tested-by: Devin Tuchsen Signed-off-by: Miaoqing Pan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/common-spectral.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index e2512d5..eedf86b 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -528,6 +528,9 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) return 0; + if (!spec_priv->rfs_chan_spec_scan) + return 1; + /* Output buffers are full, no need to process anything * since there is no space to put the result anyway */ @@ -1072,7 +1075,7 @@ static struct rchan_callbacks rfs_spec_scan_cb = { void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv) { - if (IS_ENABLED(CONFIG_ATH9K_DEBUGFS)) { + if (IS_ENABLED(CONFIG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) { relay_close(spec_priv->rfs_chan_spec_scan); spec_priv->rfs_chan_spec_scan = NULL; } @@ -1086,6 +1089,9 @@ void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, debugfs_phy, 1024, 256, &rfs_spec_scan_cb, NULL); + if (!spec_priv->rfs_chan_spec_scan) + return; + debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR, debugfs_phy, spec_priv, -- cgit v1.1 From f9e3033ff7eb9a0018856f5295312f78828a34f2 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 23 Nov 2016 16:06:40 +0200 Subject: wil6210: fix net queue stop/wake Driver calls to netif_tx_stop_all_queues/netif_tx_wake_all_queues are inconsistent. In several cases, driver can get to a situation where net queues are stopped forever and data cannot be sent. The fix is to stop net queues if there is at least one vring which is "full" and to wake net queues if all vrings are not "full". Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 12 +++- drivers/net/wireless/ath/wil6210/netdev.c | 2 +- drivers/net/wireless/ath/wil6210/txrx.c | 110 ++++++++++++++++++++++++++--- drivers/net/wireless/ath/wil6210/wil6210.h | 6 ++ drivers/net/wireless/ath/wil6210/wmi.c | 3 +- 5 files changed, 117 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index e7130b5..b04ff87 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -213,7 +213,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) memset(&sta->stats, 0, sizeof(sta->stats)); } -static bool wil_ap_is_connected(struct wil6210_priv *wil) +static bool wil_is_connected(struct wil6210_priv *wil) { int i; @@ -267,7 +267,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: wil_bcast_fini(wil); - netif_tx_stop_all_queues(ndev); + wil_update_net_queues_bh(wil, NULL, true); netif_carrier_off(ndev); if (test_bit(wil_status_fwconnected, wil->status)) { @@ -283,8 +283,12 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - if (!wil_ap_is_connected(wil)) + if (!wil_is_connected(wil)) { + wil_update_net_queues_bh(wil, NULL, true); clear_bit(wil_status_fwconnected, wil->status); + } else { + wil_update_net_queues_bh(wil, NULL, false); + } break; default: break; @@ -516,6 +520,8 @@ int wil_priv_init(struct wil6210_priv *wil) INIT_LIST_HEAD(&wil->pending_wmi_ev); INIT_LIST_HEAD(&wil->probe_client_pending); spin_lock_init(&wil->wmi_ev_lock); + spin_lock_init(&wil->net_queue_lock); + wil->net_queue_stopped = 1; init_waitqueue_head(&wil->wq); wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index d18372c..6676001 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -214,7 +214,7 @@ int wil_if_add(struct wil6210_priv *wil) netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, WIL6210_NAPI_BUDGET); - netif_tx_stop_all_queues(ndev); + wil_update_net_queues_bh(wil, NULL, true); rc = register_netdev(ndev); if (rc < 0) { diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 4c38520..4ac9ba0 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -88,6 +88,18 @@ static inline int wil_vring_wmark_high(struct vring *vring) return vring->size/4; } +/* returns true if num avail descriptors is lower than wmark_low */ +static inline int wil_vring_avail_low(struct vring *vring) +{ + return wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring); +} + +/* returns true if num avail descriptors is higher than wmark_high */ +static inline int wil_vring_avail_high(struct vring *vring) +{ + return wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring); +} + /* wil_val_in_range - check if value in [min,max) */ static inline bool wil_val_in_range(int val, int min, int max) { @@ -1780,6 +1792,89 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, return rc; } +/** + * Check status of tx vrings and stop/wake net queues if needed + * + * This function does one of two checks: + * In case check_stop is true, will check if net queues need to be stopped. If + * the conditions for stopping are met, netif_tx_stop_all_queues() is called. + * In case check_stop is false, will check if net queues need to be waked. If + * the conditions for waking are met, netif_tx_wake_all_queues() is called. + * vring is the vring which is currently being modified by either adding + * descriptors (tx) into it or removing descriptors (tx complete) from it. Can + * be null when irrelevant (e.g. connect/disconnect events). + * + * The implementation is to stop net queues if modified vring has low + * descriptor availability. Wake if all vrings are not in low descriptor + * availability and modified vring has high descriptor availability. + */ +static inline void __wil_update_net_queues(struct wil6210_priv *wil, + struct vring *vring, + bool check_stop) +{ + int i; + + if (vring) + wil_dbg_txrx(wil, "vring %d, check_stop=%d, stopped=%d", + (int)(vring - wil->vring_tx), check_stop, + wil->net_queue_stopped); + else + wil_dbg_txrx(wil, "check_stop=%d, stopped=%d", + check_stop, wil->net_queue_stopped); + + if (check_stop == wil->net_queue_stopped) + /* net queues already in desired state */ + return; + + if (check_stop) { + if (!vring || unlikely(wil_vring_avail_low(vring))) { + /* not enough room in the vring */ + netif_tx_stop_all_queues(wil_to_ndev(wil)); + wil->net_queue_stopped = true; + wil_dbg_txrx(wil, "netif_tx_stop called\n"); + } + return; + } + + /* check wake */ + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + struct vring *cur_vring = &wil->vring_tx[i]; + struct vring_tx_data *txdata = &wil->vring_tx_data[i]; + + if (!cur_vring->va || !txdata->enabled || cur_vring == vring) + continue; + + if (wil_vring_avail_low(cur_vring)) { + wil_dbg_txrx(wil, "vring %d full, can't wake\n", + (int)(cur_vring - wil->vring_tx)); + return; + } + } + + if (!vring || wil_vring_avail_high(vring)) { + /* enough room in the vring */ + wil_dbg_txrx(wil, "calling netif_tx_wake\n"); + netif_tx_wake_all_queues(wil_to_ndev(wil)); + wil->net_queue_stopped = false; + } +} + +void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring, + bool check_stop) +{ + spin_lock(&wil->net_queue_lock); + __wil_update_net_queues(wil, vring, check_stop); + spin_unlock(&wil->net_queue_lock); +} + +void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring, + bool check_stop) +{ + spin_lock_bh(&wil->net_queue_lock); + __wil_update_net_queues(wil, vring, check_stop); + spin_unlock_bh(&wil->net_queue_lock); +} + netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct wil6210_priv *wil = ndev_to_wil(ndev); @@ -1822,14 +1917,10 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* set up vring entry */ rc = wil_tx_vring(wil, vring, skb); - /* do we still have enough room in the vring? */ - if (unlikely(wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))) { - netif_tx_stop_all_queues(wil_to_ndev(wil)); - wil_dbg_txrx(wil, "netif_tx_stop : ring full\n"); - } - switch (rc) { case 0: + /* shall we stop net queues? */ + wil_update_net_queues_bh(wil, vring, true); /* statistics will be updated on the tx_complete */ dev_kfree_skb_any(skb); return NETDEV_TX_OK; @@ -1978,10 +2069,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) txdata->last_idle = get_cycles(); } - if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) { - wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n"); - netif_tx_wake_all_queues(wil_to_ndev(wil)); - } + /* shall we wake net queues? */ + if (done) + wil_update_net_queues(wil, vring, false); return done; } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index a949cd6..12cd81b 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -624,6 +624,8 @@ struct wil6210_priv { * - consumed in thread by wmi_event_worker */ spinlock_t wmi_ev_lock; + spinlock_t net_queue_lock; /* guarding stop/wake netif queue */ + int net_queue_stopped; /* netif_tx_stop_all_queues invoked */ struct napi_struct napi_rx; struct napi_struct napi_tx; /* keep alive */ @@ -886,6 +888,10 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size); int wil_bcast_init(struct wil6210_priv *wil); void wil_bcast_fini(struct wil6210_priv *wil); +void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring, + bool should_stop); +void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring, + bool check_stop); netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); int wil_tx_complete(struct wil6210_priv *wil, int ringid); void wil6210_unmask_irq_tx(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index fae4f12..890960e 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -548,7 +548,6 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { if (rc) { - netif_tx_stop_all_queues(ndev); netif_carrier_off(ndev); wil_err(wil, "%s: cfg80211_connect_result with failure\n", @@ -588,7 +587,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) wil->sta[evt->cid].status = wil_sta_connected; set_bit(wil_status_fwconnected, wil->status); - netif_tx_wake_all_queues(ndev); + wil_update_net_queues_bh(wil, NULL, false); out: if (rc) -- cgit v1.1 From 2c207eb8e6ab3f46d6c6a0daab16dc756562802b Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Wed, 23 Nov 2016 16:06:40 +0200 Subject: wil6210: add support for power save enable / disable New power management wmi commands provide the ability to change the device power save profile (enable / disable power save). Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 32 ++++++++++++++++++++++++++- drivers/net/wireless/ath/wil6210/wil6210.h | 2 ++ drivers/net/wireless/ath/wil6210/wmi.c | 34 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index d117240..c6dd7a3 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1424,6 +1424,34 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, mutex_unlock(&wil->mutex); } +static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool enabled, int timeout) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + enum wmi_ps_profile_type ps_profile; + int rc; + + if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) { + wil_err(wil, "set_power_mgmt not supported\n"); + return -EOPNOTSUPP; + } + + wil_dbg_misc(wil, "enabled=%d, timeout=%d\n", + enabled, timeout); + + if (enabled) + ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT; + else + ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED; + + rc = wmi_ps_dev_profile_cfg(wil, ps_profile); + if (rc) + wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc); + + return rc; +} + static struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, @@ -1450,6 +1478,7 @@ static struct cfg80211_ops wil_cfg80211_ops = { /* P2P device */ .start_p2p_device = wil_cfg80211_start_p2p_device, .stop_p2p_device = wil_cfg80211_stop_p2p_device, + .set_power_mgmt = wil_cfg80211_set_power_mgmt, }; static void wil_wiphy_init(struct wiphy *wiphy) @@ -1466,7 +1495,8 @@ static void wil_wiphy_init(struct wiphy *wiphy) BIT(NL80211_IFTYPE_MONITOR); wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_PS_ON_BY_DEFAULT; dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", __func__, wiphy->flags); wiphy->probe_resp_offload = diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 12cd81b..4b22ea4 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -819,6 +819,8 @@ int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason); int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason); int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, u16 status, bool amsdu, u16 agg_wsize, u16 timeout); +int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, + enum wmi_ps_profile_type ps_profile); int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, u8 dialog_token, __le16 ba_param_set, __le16 ba_timeout, __le16 ba_seq_ctrl); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 890960e..8c60437 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1563,6 +1563,40 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, return rc; } +int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, + enum wmi_ps_profile_type ps_profile) +{ + int rc; + struct wmi_ps_dev_profile_cfg_cmd cmd = { + .ps_profile = ps_profile, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_ps_dev_profile_cfg_event evt; + } __packed reply; + u32 status; + + wil_dbg_wmi(wil, "Setting ps dev profile %d\n", ps_profile); + + reply.evt.status = cpu_to_le32(WMI_PS_CFG_CMD_STATUS_ERROR); + + rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, &cmd, sizeof(cmd), + WMI_PS_DEV_PROFILE_CFG_EVENTID, &reply, sizeof(reply), + 100); + if (rc) + return rc; + + status = le32_to_cpu(reply.evt.status); + + if (status != WMI_PS_CFG_CMD_STATUS_SUCCESS) { + wil_err(wil, "ps dev profile cfg failed with status %d\n", + status); + rc = -EINVAL; + } + + return rc; +} + void wmi_event_flush(struct wil6210_priv *wil) { struct pending_wmi_event *evt, *t; -- cgit v1.1 From dfb5b098e0f40b68aa07f2ec55f4dd762efefbfa Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 23 Nov 2016 16:06:41 +0200 Subject: wil6210: fix deadlock when using fw_no_recovery option When FW crashes with no_fw_recovery option, driver waits for manual recovery with wil->mutex held, this can easily create deadlocks. Fix the problem by moving the wait outside the lock. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index b04ff87..a9bbd0b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -388,18 +388,19 @@ static void wil_fw_error_worker(struct work_struct *work) wil->last_fw_recovery = jiffies; + wil_info(wil, "fw error recovery requested (try %d)...\n", + wil->recovery_count); + if (!no_fw_recovery) + wil->recovery_state = fw_recovery_running; + if (wil_wait_for_recovery(wil) != 0) + return; + mutex_lock(&wil->mutex); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: - wil_info(wil, "fw error recovery requested (try %d)...\n", - wil->recovery_count); - if (!no_fw_recovery) - wil->recovery_state = fw_recovery_running; - if (0 != wil_wait_for_recovery(wil)) - break; - + /* silent recovery, upper layers will see disconnect */ __wil_down(wil); __wil_up(wil); break; -- cgit v1.1 From 035859a5117bc609132c3586b6c6bf4aba72425c Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Wed, 23 Nov 2016 16:06:42 +0200 Subject: wil6210: add support for abort scan Implement cfg80211 abort_scan op to allow the upper layer to abort an ongoing scan request. In addition, notify wil6210 device on scan abort request instead of just ignoring the scan response. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 31 ++++++++++++++++ drivers/net/wireless/ath/wil6210/main.c | 56 ++++++++++++++++------------- drivers/net/wireless/ath/wil6210/p2p.c | 10 ++---- drivers/net/wireless/ath/wil6210/wil6210.h | 3 ++ drivers/net/wireless/ath/wil6210/wmi.c | 21 +++++++++-- 5 files changed, 87 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index c6dd7a3..533872a 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -469,6 +469,34 @@ out: return rc; } +static void wil_cfg80211_abort_scan(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype); + + mutex_lock(&wil->mutex); + mutex_lock(&wil->p2p_wdev_mutex); + + if (!wil->scan_request) + goto out; + + if (wdev != wil->scan_request->wdev) { + wil_dbg_misc(wil, "abort scan was called on the wrong iface\n"); + goto out; + } + + if (wil->radio_wdev == wil->p2p_wdev) + wil_p2p_stop_radio_operations(wil); + else + wil_abort_scan(wil, true); + +out: + mutex_unlock(&wil->p2p_wdev_mutex); + mutex_unlock(&wil->mutex); +} + static void wil_print_crypto(struct wil6210_priv *wil, struct cfg80211_crypto_settings *c) { @@ -1419,8 +1447,10 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, wil_dbg_misc(wil, "%s: entered\n", __func__); mutex_lock(&wil->mutex); + mutex_lock(&wil->p2p_wdev_mutex); wil_p2p_stop_radio_operations(wil); p2p->p2p_dev_started = 0; + mutex_unlock(&wil->p2p_wdev_mutex); mutex_unlock(&wil->mutex); } @@ -1456,6 +1486,7 @@ static struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, .scan = wil_cfg80211_scan, + .abort_scan = wil_cfg80211_abort_scan, .connect = wil_cfg80211_connect, .disconnect = wil_cfg80211_disconnect, .change_virtual_intf = wil_cfg80211_change_iface, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index a9bbd0b..65a487c 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -24,6 +24,7 @@ #include "boot_loader.h" #define WAIT_FOR_HALP_VOTE_MS 100 +#define WAIT_FOR_SCAN_ABORT_MS 1000 bool debug_fw; /* = false; */ module_param(debug_fw, bool, S_IRUGO); @@ -808,6 +809,34 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil) return 0; } +void wil_abort_scan(struct wil6210_priv *wil, bool sync) +{ + int rc; + struct cfg80211_scan_info info = { + .aborted = true, + }; + + lockdep_assert_held(&wil->p2p_wdev_mutex); + + if (!wil->scan_request) + return; + + wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request); + del_timer_sync(&wil->scan_timer); + mutex_unlock(&wil->p2p_wdev_mutex); + rc = wmi_abort_scan(wil); + if (!rc && sync) + wait_event_interruptible_timeout(wil->wq, !wil->scan_request, + msecs_to_jiffies( + WAIT_FOR_SCAN_ABORT_MS)); + + mutex_lock(&wil->p2p_wdev_mutex); + if (wil->scan_request) { + cfg80211_scan_done(wil->scan_request, &info); + wil->scan_request = NULL; + } +} + /* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload @@ -860,17 +889,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) mutex_unlock(&wil->wmi_mutex); mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { - struct cfg80211_scan_info info = { - .aborted = true, - }; - - wil_dbg_misc(wil, "Abort scan_request 0x%p\n", - wil->scan_request); - del_timer_sync(&wil->scan_timer); - cfg80211_scan_done(wil->scan_request, &info); - wil->scan_request = NULL; - } + wil_abort_scan(wil, false); mutex_unlock(&wil->p2p_wdev_mutex); wil_mask_irq(wil); @@ -1063,20 +1082,9 @@ int __wil_down(struct wil6210_priv *wil) } wil_enable_irq(wil); - wil_p2p_stop_radio_operations(wil); - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { - struct cfg80211_scan_info info = { - .aborted = true, - }; - - wil_dbg_misc(wil, "Abort scan_request 0x%p\n", - wil->scan_request); - del_timer_sync(&wil->scan_timer); - cfg80211_scan_done(wil->scan_request, &info); - wil->scan_request = NULL; - } + wil_p2p_stop_radio_operations(wil); + wil_abort_scan(wil, false); mutex_unlock(&wil->p2p_wdev_mutex); wil_reset(wil, false); diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index 4087785..4f0eab0 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -272,8 +272,7 @@ void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) }; lockdep_assert_held(&wil->mutex); - - mutex_lock(&wil->p2p_wdev_mutex); + lockdep_assert_held(&wil->p2p_wdev_mutex); if (wil->radio_wdev != wil->p2p_wdev) goto out; @@ -281,10 +280,8 @@ void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) if (!p2p->discovery_started) { /* Regular scan on the p2p device */ if (wil->scan_request && - wil->scan_request->wdev == wil->p2p_wdev) { - cfg80211_scan_done(wil->scan_request, &info); - wil->scan_request = NULL; - } + wil->scan_request->wdev == wil->p2p_wdev) + wil_abort_scan(wil, true); goto out; } @@ -307,5 +304,4 @@ void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) out: wil->radio_wdev = wil->wdev; - mutex_unlock(&wil->p2p_wdev_mutex); } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 4b22ea4..d65a5e6 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -873,6 +873,9 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan, u8 hidden_ssid, u8 is_go); int wmi_pcp_stop(struct wil6210_priv *wil); int wmi_led_cfg(struct wil6210_priv *wil, bool enable); +int wmi_abort_scan(struct wil6210_priv *wil); +void wil_abort_scan(struct wil6210_priv *wil, bool sync); + void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, u16 reason_code, bool from_event); void wil_probe_client_flush(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 8c60437..e726548 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -427,18 +427,20 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, mutex_lock(&wil->p2p_wdev_mutex); if (wil->scan_request) { struct wmi_scan_complete_event *data = d; + int status = le32_to_cpu(data->status); struct cfg80211_scan_info info = { - .aborted = (data->status != WMI_SCAN_SUCCESS), + .aborted = ((status != WMI_SCAN_SUCCESS) && + (status != WMI_SCAN_ABORT_REJECTED)), }; - wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status); wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n", wil->scan_request, info.aborted); - del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, &info); wil->radio_wdev = wil->wdev; wil->scan_request = NULL; + wake_up_interruptible(&wil->wq); } else { wil_err(wil, "SCAN_COMPLETE while not scanning\n"); } @@ -1597,6 +1599,19 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, return rc; } +int wmi_abort_scan(struct wil6210_priv *wil) +{ + int rc; + + wil_dbg_wmi(wil, "sending WMI_ABORT_SCAN_CMDID\n"); + + rc = wmi_send(wil, WMI_ABORT_SCAN_CMDID, NULL, 0); + if (rc) + wil_err(wil, "Failed to abort scan (%d)\n", rc); + + return rc; +} + void wmi_event_flush(struct wil6210_priv *wil) { struct pending_wmi_event *evt, *t; -- cgit v1.1 From cbf795c195fb48dafd5f160570af9086fe04d4d0 Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 23 Nov 2016 16:06:42 +0200 Subject: wil6210: align to latest auto generated wmi.h Align to latest version of the auto generated wmi file describing the interface with FW. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.h | 208 ++++++++++++++++++++++++++++++++- 1 file changed, 206 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index f430e8a..2cc7775 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -51,8 +51,10 @@ enum wmi_mid { * the host */ enum wmi_fw_capability { - WMI_FW_CAPABILITY_FTM = 0, - WMI_FW_CAPABILITY_PS_CONFIG = 1, + WMI_FW_CAPABILITY_FTM = 0, + WMI_FW_CAPABILITY_PS_CONFIG = 1, + WMI_FW_CAPABILITY_RF_SECTORS = 2, + WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3, WMI_FW_CAPABILITY_MAX, }; @@ -182,11 +184,18 @@ enum wmi_command_id { WMI_RS_CFG_CMDID = 0x921, WMI_GET_DETAILED_RS_RES_CMDID = 0x922, WMI_AOA_MEAS_CMDID = 0x923, + WMI_SET_MGMT_RETRY_LIMIT_CMDID = 0x930, + WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931, WMI_TOF_SESSION_START_CMDID = 0x991, WMI_TOF_GET_CAPABILITIES_CMDID = 0x992, WMI_TOF_SET_LCR_CMDID = 0x993, WMI_TOF_SET_LCI_CMDID = 0x994, WMI_TOF_CHANNEL_INFO_CMDID = 0x995, + WMI_GET_RF_SECTOR_PARAMS_CMDID = 0x9A0, + WMI_SET_RF_SECTOR_PARAMS_CMDID = 0x9A1, + WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A2, + WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A3, + WMI_SET_RF_SECTOR_ON_CMDID = 0x9A4, WMI_SET_MAC_ADDRESS_CMDID = 0xF003, WMI_ABORT_SCAN_CMDID = 0xF007, WMI_SET_PROMISCUOUS_MODE_CMDID = 0xF041, @@ -879,6 +888,14 @@ struct wmi_aoa_meas_cmd { __le32 meas_rf_mask; } __packed; +/* WMI_SET_MGMT_RETRY_LIMIT_CMDID */ +struct wmi_set_mgmt_retry_limit_cmd { + /* MAC retransmit limit for mgmt frames */ + u8 mgmt_retry_limit; + /* alignment to 32b */ + u8 reserved[3]; +} __packed; + enum wmi_tof_burst_duration { WMI_TOF_BURST_DURATION_250_USEC = 2, WMI_TOF_BURST_DURATION_500_USEC = 3, @@ -1035,12 +1052,19 @@ enum wmi_event_id { WMI_RS_CFG_DONE_EVENTID = 0x1921, WMI_GET_DETAILED_RS_RES_EVENTID = 0x1922, WMI_AOA_MEAS_EVENTID = 0x1923, + WMI_SET_MGMT_RETRY_LIMIT_EVENTID = 0x1930, + WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931, WMI_TOF_SESSION_END_EVENTID = 0x1991, WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992, WMI_TOF_SET_LCR_EVENTID = 0x1993, WMI_TOF_SET_LCI_EVENTID = 0x1994, WMI_TOF_FTM_PER_DEST_RES_EVENTID = 0x1995, WMI_TOF_CHANNEL_INFO_EVENTID = 0x1996, + WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A0, + WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A1, + WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID = 0x19A2, + WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID = 0x19A3, + WMI_SET_RF_SECTOR_ON_DONE_EVENTID = 0x19A4, WMI_SET_CHANNEL_EVENTID = 0x9000, WMI_ASSOC_REQ_EVENTID = 0x9001, WMI_EAPOL_RX_EVENTID = 0x9002, @@ -2070,6 +2094,22 @@ struct wmi_aoa_meas_event { u8 meas_data[WMI_AOA_MAX_DATA_SIZE]; } __packed; +/* WMI_SET_MGMT_RETRY_LIMIT_EVENTID */ +struct wmi_set_mgmt_retry_limit_event { + /* enum wmi_fw_status */ + u8 status; + /* alignment to 32b */ + u8 reserved[3]; +} __packed; + +/* WMI_GET_MGMT_RETRY_LIMIT_EVENTID */ +struct wmi_get_mgmt_retry_limit_event { + /* MAC retransmit limit for mgmt frames */ + u8 mgmt_retry_limit; + /* alignment to 32b */ + u8 reserved[3]; +} __packed; + /* WMI_TOF_GET_CAPABILITIES_EVENTID */ struct wmi_tof_get_capabilities_event { u8 ftm_capability; @@ -2184,4 +2224,168 @@ struct wmi_tof_channel_info_event { u8 report[0]; } __packed; +/* Result status codes for WMI commands */ +enum wmi_rf_sector_status { + WMI_RF_SECTOR_STATUS_SUCCESS = 0x00, + WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR = 0x01, + WMI_RF_SECTOR_STATUS_BUSY_ERROR = 0x02, + WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR = 0x03, +}; + +/* Types of the RF sector (TX,RX) */ +enum wmi_rf_sector_type { + WMI_RF_SECTOR_TYPE_RX = 0x00, + WMI_RF_SECTOR_TYPE_TX = 0x01, +}; + +/* Content of RF Sector (six 32-bits registers) */ +struct wmi_rf_sector_info { + /* Phase values for RF Chains[15-0] (2bits per RF chain) */ + __le32 psh_hi; + /* Phase values for RF Chains[31-16] (2bits per RF chain) */ + __le32 psh_lo; + /* ETYPE Bit0 for all RF chains[31-0] - bit0 of Edge amplifier gain + * index + */ + __le32 etype0; + /* ETYPE Bit1 for all RF chains[31-0] - bit1 of Edge amplifier gain + * index + */ + __le32 etype1; + /* ETYPE Bit2 for all RF chains[31-0] - bit2 of Edge amplifier gain + * index + */ + __le32 etype2; + /* D-Type values (3bits each) for 8 Distribution amplifiers + X16 + * switch bits + */ + __le32 dtype_swch_off; +} __packed; + +#define WMI_INVALID_RF_SECTOR_INDEX (0xFFFF) +#define WMI_MAX_RF_MODULES_NUM (8) + +/* WMI_GET_RF_SECTOR_PARAMS_CMD */ +struct wmi_get_rf_sector_params_cmd { + /* Sector number to be retrieved */ + __le16 sector_idx; + /* enum wmi_rf_sector_type - type of requested RF sector */ + u8 sector_type; + /* bitmask vector specifying destination RF modules */ + u8 rf_modules_vec; +} __packed; + +/* \WMI_GET_RF_SECTOR_PARAMS_DONE_EVENT */ +struct wmi_get_rf_sector_params_done_event { + /* result status of WMI_GET_RF_SECTOR_PARAMS_CMD (enum + * wmi_rf_sector_status) + */ + u8 status; + /* align next field to U64 boundary */ + u8 reserved[7]; + /* TSF timestamp when RF sectors where retrieved */ + __le64 tsf; + /* Content of RF sector retrieved from each RF module */ + struct wmi_rf_sector_info sectors_info[WMI_MAX_RF_MODULES_NUM]; +} __packed; + +/* WMI_SET_RF_SECTOR_PARAMS_CMD */ +struct wmi_set_rf_sector_params_cmd { + /* Sector number to be retrieved */ + __le16 sector_idx; + /* enum wmi_rf_sector_type - type of requested RF sector */ + u8 sector_type; + /* bitmask vector specifying destination RF modules */ + u8 rf_modules_vec; + /* Content of RF sector to be written to each RF module */ + struct wmi_rf_sector_info sectors_info[WMI_MAX_RF_MODULES_NUM]; +} __packed; + +/* \WMI_SET_RF_SECTOR_PARAMS_DONE_EVENT */ +struct wmi_set_rf_sector_params_done_event { + /* result status of WMI_SET_RF_SECTOR_PARAMS_CMD (enum + * wmi_rf_sector_status) + */ + u8 status; +} __packed; + +/* WMI_GET_SELECTED_RF_SECTOR_INDEX_CMD - Get RF sector index selected by + * TXSS/BRP for communication with specified CID + */ +struct wmi_get_selected_rf_sector_index_cmd { + /* Connection/Station ID in [0:7] range */ + u8 cid; + /* type of requested RF sector (enum wmi_rf_sector_type) */ + u8 sector_type; + /* align to U32 boundary */ + u8 reserved[2]; +} __packed; + +/* \WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT - Returns retrieved RF sector + * index selected by TXSS/BRP for communication with specified CID + */ +struct wmi_get_selected_rf_sector_index_done_event { + /* Retrieved sector index selected in TXSS (for TX sector request) or + * BRP (for RX sector request) + */ + __le16 sector_idx; + /* result status of WMI_GET_SELECTED_RF_SECTOR_INDEX_CMD (enum + * wmi_rf_sector_status) + */ + u8 status; + /* align next field to U64 boundary */ + u8 reserved[5]; + /* TSF timestamp when result was retrieved */ + __le64 tsf; +} __packed; + +/* WMI_SET_SELECTED_RF_SECTOR_INDEX_CMD - Force RF sector index for + * communication with specified CID. Assumes that TXSS/BRP is disabled by + * other command + */ +struct wmi_set_selected_rf_sector_index_cmd { + /* Connection/Station ID in [0:7] range */ + u8 cid; + /* type of requested RF sector (enum wmi_rf_sector_type) */ + u8 sector_type; + /* Forced sector index */ + __le16 sector_idx; +} __packed; + +/* \WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT - Success/Fail status for + * WMI_SET_SELECTED_RF_SECTOR_INDEX_CMD + */ +struct wmi_set_selected_rf_sector_index_done_event { + /* result status of WMI_SET_SELECTED_RF_SECTOR_INDEX_CMD (enum + * wmi_rf_sector_status) + */ + u8 status; + /* align to U32 boundary */ + u8 reserved[3]; +} __packed; + +/* WMI_SET_RF_SECTOR_ON_CMD - Activates specified sector for specified rf + * modules + */ +struct wmi_set_rf_sector_on_cmd { + /* Sector index to be activated */ + __le16 sector_idx; + /* type of requested RF sector (enum wmi_rf_sector_type) */ + u8 sector_type; + /* bitmask vector specifying destination RF modules */ + u8 rf_modules_vec; +} __packed; + +/* \WMI_SET_RF_SECTOR_ON_DONE_EVENT - Success/Fail status for + * WMI_SET_RF_SECTOR_ON_CMD + */ +struct wmi_set_rf_sector_on_done_event { + /* result status of WMI_SET_RF_SECTOR_ON_CMD (enum + * wmi_rf_sector_status) + */ + u8 status; + /* align to U32 boundary */ + u8 reserved[3]; +} __packed; + #endif /* __WILOCITY_WMI_H__ */ -- cgit v1.1 From 3fea18d079e2f1f50bc2a37db826c9e6e8a4003e Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 23 Nov 2016 16:06:43 +0200 Subject: wil6210: support NL80211_ATTR_WIPHY_RETRY_SHORT Add support for setting retry limit for short frames, using NL80211_CMD_SET_WIPHY with the attribute NL80211_ATTR_WIPHY_RETRY_SHORT. Update wiphy->retry_short from the FW default when interface is brought up. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 21 ++++++++++ drivers/net/wireless/ath/wil6210/main.c | 15 ++++++++ drivers/net/wireless/ath/wil6210/wil6210.h | 2 + drivers/net/wireless/ath/wil6210/wmi.c | 59 +++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 533872a..22078b0 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -702,6 +702,26 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, return rc; } +static int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + + /* these parameters are explicitly not supported */ + if (changed & (WIPHY_PARAM_RETRY_LONG | + WIPHY_PARAM_FRAG_THRESHOLD | + WIPHY_PARAM_RTS_THRESHOLD)) + return -ENOTSUPP; + + if (changed & WIPHY_PARAM_RETRY_SHORT) { + rc = wmi_set_mgmt_retry(wil, wiphy->retry_short); + if (rc) + return rc; + } + + return 0; +} + int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie) @@ -1489,6 +1509,7 @@ static struct cfg80211_ops wil_cfg80211_ops = { .abort_scan = wil_cfg80211_abort_scan, .connect = wil_cfg80211_connect, .disconnect = wil_cfg80211_disconnect, + .set_wiphy_params = wil_cfg80211_set_wiphy_params, .change_virtual_intf = wil_cfg80211_change_iface, .get_station = wil_cfg80211_get_station, .dump_station = wil_cfg80211_dump_station, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 65a487c..70f9c07 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -693,6 +693,19 @@ static int wil_target_reset(struct wil6210_priv *wil) return 0; } +static void wil_collect_fw_info(struct wil6210_priv *wil) +{ + struct wiphy *wiphy = wil_to_wiphy(wil); + u8 retry_short; + int rc; + + rc = wmi_get_mgmt_retry(wil, &retry_short); + if (!rc) { + wiphy->retry_short = retry_short; + wil_dbg_misc(wil, "FW retry_short: %d\n", retry_short); + } +} + void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) { le32_to_cpus(&r->base); @@ -966,6 +979,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + wil_collect_fw_info(wil); + if (wil->platform_ops.notify) { rc = wil->platform_ops.notify(wil->platform_handle, WIL_PLATFORM_EVT_FW_RDY); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index d65a5e6..b449f84 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -821,6 +821,8 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, u16 status, bool amsdu, u16 agg_wsize, u16 timeout); int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile); +int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short); +int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short); int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, u8 dialog_token, __le16 ba_param_set, __le16 ba_timeout, __le16 ba_seq_ctrl); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index e726548..2971ddf 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1599,6 +1599,65 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, return rc; } +int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short) +{ + int rc; + struct wmi_set_mgmt_retry_limit_cmd cmd = { + .mgmt_retry_limit = retry_short, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_set_mgmt_retry_limit_event evt; + } __packed reply; + + wil_dbg_wmi(wil, "Setting mgmt retry short %d\n", retry_short); + + if (!test_bit(WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT, wil->fw_capabilities)) + return -ENOTSUPP; + + reply.evt.status = WMI_FW_STATUS_FAILURE; + + rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, &cmd, sizeof(cmd), + WMI_SET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), + 100); + if (rc) + return rc; + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "set mgmt retry limit failed with status %d\n", + reply.evt.status); + rc = -EINVAL; + } + + return rc; +} + +int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short) +{ + int rc; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_get_mgmt_retry_limit_event evt; + } __packed reply; + + wil_dbg_wmi(wil, "getting mgmt retry short\n"); + + if (!test_bit(WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT, wil->fw_capabilities)) + return -ENOTSUPP; + + reply.evt.mgmt_retry_limit = 0; + rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, NULL, 0, + WMI_GET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), + 100); + if (rc) + return rc; + + if (retry_short) + *retry_short = reply.evt.mgmt_retry_limit; + + return 0; +} + int wmi_abort_scan(struct wil6210_priv *wil) { int rc; -- cgit v1.1 From a083c8fd277b4122c804f18ec8c84165f345c71c Mon Sep 17 00:00:00 2001 From: Vishal Thanki Date: Wed, 16 Nov 2016 17:01:54 +0100 Subject: rt2x00: Fix incorrect usage of CONFIG_RT2X00_LIB_USB In device removal routine, usage of "#ifdef CONFIG_RT2X00_LIB_USB" will not cover the case when it is configured as module. This will omit the entire if-block which does cleanup of URBs and cancellation of pending work. Changing the #ifdef to #if IS_ENABLED() to fix it. Signed-off-by: Vishal Thanki Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index d659250..5071cf0 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1439,7 +1439,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) cancel_work_sync(&rt2x00dev->intf_work); cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); cancel_work_sync(&rt2x00dev->sleep_work); -#ifdef CONFIG_RT2X00_LIB_USB +#if IS_ENABLED(CONFIG_RT2X00_LIB_USB) if (rt2x00_is_usb(rt2x00dev)) { usb_kill_anchored_urbs(rt2x00dev->anchor); hrtimer_cancel(&rt2x00dev->txstatus_timer); -- cgit v1.1 From f25ba69c638b24097840a96bd3caf5599f9a3616 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Fri, 18 Nov 2016 09:59:52 +0800 Subject: brcmfmac: update beacon IE after bss up and clear when AP stopped Firmware doesn't update beacon/Probe Response vendor IEs correctly when bss is down, so we move brcmf_config_ap_mgmt_ie after BSS up. And host driver should clear IEs when AP stopped so that the IEs in host side will be synced with in firmware side. Signed-off-by: Wright Feng Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 1e7c6f0..b3cdc522 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -4578,8 +4578,6 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_configure_opensecurity(ifp); } - brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - /* Parameters shared by all radio interfaces */ if (!mbss) { if ((supports_11d) && (is_11d != ifp->vif->is_11d)) { @@ -4708,6 +4706,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, WARN_ON(1); } + brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); brcmf_net_setcarrier(ifp, true); @@ -4764,6 +4763,8 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); if (err < 0) brcmf_err("BRCMF_C_UP error %d\n", err); + + brcmf_vif_clear_mgmt_ies(ifp->vif); } else { bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); bss_enable.enable = cpu_to_le32(0); -- cgit v1.1 From cea5b03d8a10f718823692a617f738d9cbae193f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:43:53 +0100 Subject: rt2800: correctly report MCS TX parameters We should only set IEEE80211_HT_MCS_TX_RX_DIF when TX and RX MCS sets are not equal, i.e. when number of tx streams is different than number of RX streams. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 59c49af..9c01622 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -7461,7 +7461,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) char *default_power1; char *default_power2; char *default_power3; - unsigned int i; + unsigned int i, tx_chains, rx_chains; u32 reg; /* @@ -7586,21 +7586,24 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40; - if (rt2x00dev->default_ant.tx_chain_num >= 2) + tx_chains = rt2x00dev->default_ant.tx_chain_num; + rx_chains = rt2x00dev->default_ant.rx_chain_num; + + if (tx_chains >= 2) spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC; - spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num << - IEEE80211_HT_CAP_RX_STBC_SHIFT; + spec->ht.cap |= rx_chains << IEEE80211_HT_CAP_RX_STBC_SHIFT; spec->ht.ampdu_factor = 3; spec->ht.ampdu_density = 4; - spec->ht.mcs.tx_params = - IEEE80211_HT_MCS_TX_DEFINED | - IEEE80211_HT_MCS_TX_RX_DIFF | - ((rt2x00dev->default_ant.tx_chain_num - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + spec->ht.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (tx_chains != rx_chains) { + spec->ht.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + spec->ht.mcs.tx_params |= + (tx_chains - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + } - switch (rt2x00dev->default_ant.rx_chain_num) { + switch (rx_chains) { case 3: spec->ht.mcs.rx_mask[2] = 0xff; case 2: -- cgit v1.1 From b29a1c1f08f30e0022a5c2a9f812f35745d93650 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:43:54 +0100 Subject: rt2800usb: do not wipe out USB_DMA_CFG settings We should not reset USB_DMA_CFG on rt2800usb_init_registers() as this function is called indirectly from rt2800_enable_radio(). If we do so, we wipe out USB_DMA_CFG settings from rt2800usb_enable_radio(). Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800usb.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index 4b0bb6b..9f61293 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -341,8 +341,6 @@ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); - rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, USB_MODE_RESET, REGISTER_TIMEOUT); @@ -353,12 +351,11 @@ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) { - u32 reg; + u32 reg = 0; if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev))) return -EIO; - rt2x00usb_register_read(rt2x00dev, USB_DMA_CFG, ®); rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128); -- cgit v1.1 From 770e4b730a73a85d05e0f0aa59f412e62774f1c9 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:43:55 +0100 Subject: rt2800: OFDM rates are mandatory Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 9c01622..0051aef 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -1707,7 +1707,7 @@ void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, if (changed & BSS_CHANGED_BASIC_RATES) { rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, - erp->basic_rates); + 0xff0 | erp->basic_rates); rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); } -- cgit v1.1 From b4c449b1b8030fbc1635b683186c7454018dcb63 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:43:56 +0100 Subject: rt2800: do not overwrite WPDMA_GLO_CFG_WP_DMA_BURST_SIZE We already initlized WPDMA_GLO_CFG_WP_DMA_BURST_SIZE to 3 on rt2800_init_registers() for USB devices. For PCI devices we will use HW default setting, which is 2, so patch does not change behaviour on PCI devices. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 0051aef..fcee95d 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -6756,7 +6756,6 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1); - rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2); rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); -- cgit v1.1 From be82de9d64e281e802ab12b74f61c14e55f2a4fc Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:43:57 +0100 Subject: rt2800: correct AUTO_RSP_CFG Initialize AUTO_RSP_CFG register to similar value as vendor driver does. Do not set BAC_ACK_POLICY based on short preamble setting, those are unrelated. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index fcee95d..e81319d 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -1691,8 +1691,6 @@ void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, if (changed & BSS_CHANGED_ERP_PREAMBLE) { rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); - rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, - !!erp->short_preamble); rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, !!erp->short_preamble); rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); @@ -4735,9 +4733,9 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); rt2x00_set_field32(®, AUTO_RSP_CFG_AUTORESPONDER, 1); rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, 1); - rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 1); rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MREF, 0); - rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, 0); rt2x00_set_field32(®, AUTO_RSP_CFG_DUAL_CTS_EN, 0); rt2x00_set_field32(®, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0); rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); -- cgit v1.1 From 231aeca1e19e507af37d800de8d39fa6a72fbd92 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:43:58 +0100 Subject: rt2800: correct TX_SW_CFG1 for 5592 Those TX_SW_CFG1 values are used in vendor driver. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index e81319d..438f715 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -4670,11 +4670,14 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) 0x00000000); } } else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2x00_rt(rt2x00dev, RT5592)) { + rt2x00_rt(rt2x00dev, RT5392)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } else if (rt2x00_rt(rt2x00dev, RT5592)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); } else { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); -- cgit v1.1 From 6c40063d5a61cd245e0828088024fc1eb02a2083 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:43:59 +0100 Subject: rt2800: use RTS/CTS protection instead of CTS-to-self Change default to RTS/CTS protection. This has a cost of transmitting one more control frame (RTS) however protect us from traffic from hidden node. On station mode will use CTS-to-self if AP will configure that for the network. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 438f715..11cc079 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -1621,7 +1621,7 @@ static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev, * => Protect all HT40 transmissions. */ mm20_mode = gf20_mode = 0; - mm40_mode = gf40_mode = 2; + mm40_mode = gf40_mode = 1; break; case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: @@ -1644,7 +1644,7 @@ static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev, * Legacy STAs are present * => Protect all HT transmissions. */ - mm20_mode = mm40_mode = gf20_mode = gf40_mode = 2; + mm20_mode = mm40_mode = gf20_mode = gf40_mode = 1; /* * If erp protection is needed we have to protect HT @@ -1660,7 +1660,7 @@ static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev, /* check for STAs not supporting greenfield mode */ if (any_sta_nongf) - gf20_mode = gf40_mode = 2; + gf20_mode = gf40_mode = 1; /* Update HT protection config */ rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); -- cgit v1.1 From 8d79b007827824765df4cbb946b1a322ebd5e971 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:44:00 +0100 Subject: rt2800: tune *_PROT_CFG parameters Use RTS/CTS protection for TXOP on all rates modes as default and disable CCK rates (this cause performance problems). Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 11cc079..01aebec 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -4771,9 +4771,9 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, 0x4004); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 1); rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 0); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0); @@ -4784,9 +4784,9 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, 0x4084); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 1); rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 0); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1); @@ -4797,9 +4797,9 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, 0x4004); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 1); rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 0); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0); @@ -4810,9 +4810,9 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, 0x4084); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 1); rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 0); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1); rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1); -- cgit v1.1 From 159a55a64d44acbbd6f0d8f3c082e628d6d75670 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 18 Nov 2016 10:44:01 +0100 Subject: rt2800: disable CCK rates on HT Sending frames in CCK rates on HT can cause performance problems. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 01aebec..4fb79e0 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -7472,7 +7472,6 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) /* * Initialize all hw fields. */ - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_HT_CCK_RATES); ieee80211_hw_set(rt2x00dev->hw, REPORTS_TX_ACK_STATUS); ieee80211_hw_set(rt2x00dev->hw, AMPDU_AGGREGATION); ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); -- cgit v1.1 From cec17c382140a17ad54853a4b57a84588f090806 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 23 Nov 2016 16:58:10 +0200 Subject: ath10k: add per peer htt tx stats support for 10.4 Per peer tx stats are part of 'HTT_10_4_T2H_MSG_TYPE_PEER_STATS' event, Firmware sends one HTT event for every four PPDUs. HTT payload has success pkts/bytes, failed pkts/bytes, retry pkts/bytes and rate info per ppdu. Peer stats are enabled through 'WMI_SERVICE_PEER_STATS', which are nowadays enabled by default. Parse peer stats and update the tx rate information per STA. tx rate, Peer stats are tested on QCA4019 with Firmware version 10.4-3.2.1-00028. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 17 +++++ drivers/net/wireless/ath/ath10k/htt.c | 2 + drivers/net/wireless/ath/ath10k/htt.h | 25 +++++++ drivers/net/wireless/ath/ath10k/htt_rx.c | 125 +++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 10 ++- 5 files changed, 178 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 018fc22..6f2608a 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -337,6 +337,7 @@ struct ath10k_sta { u32 nss; u32 smps; u16 peer_id; + struct rate_info txrate; struct work_struct update_wk; @@ -692,6 +693,21 @@ struct ath10k_fw_components { struct ath10k_fw_file fw_file; }; +struct ath10k_per_peer_tx_stats { + u32 succ_bytes; + u32 retry_bytes; + u32 failed_bytes; + u8 ratecode; + u8 flags; + u16 peer_id; + u16 succ_pkts; + u16 retry_pkts; + u16 failed_pkts; + u16 duration; + u32 reserved1; + u32 reserved2; +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -905,6 +921,7 @@ struct ath10k { struct ath10k_thermal thermal; struct ath10k_wow wow; + struct ath10k_per_peer_tx_stats peer_tx_stats; /* NAPI */ struct net_device napi_dev; diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 130cd95..cd160b1 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -137,6 +137,8 @@ static const enum htt_t2h_msg_type htt_10_4_t2h_msg_types[] = { HTT_T2H_MSG_TYPE_STATS_NOUPLOAD, [HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND] = HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, + [HTT_10_4_T2H_MSG_TYPE_PEER_STATS] = + HTT_T2H_MSG_TYPE_PEER_STATS, }; int ath10k_htt_connect(struct ath10k_htt *htt) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 0d2ed09..164eb3a 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -419,6 +419,7 @@ enum htt_10_4_t2h_msg_type { HTT_10_4_T2H_MSG_TYPE_STATS_NOUPLOAD = 0x18, /* 0x19 to 0x2f are reserved */ HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND = 0x30, + HTT_10_4_T2H_MSG_TYPE_PEER_STATS = 0x31, /* keep this last */ HTT_10_4_T2H_NUM_MSGS }; @@ -453,6 +454,7 @@ enum htt_t2h_msg_type { HTT_T2H_MSG_TYPE_TX_FETCH_IND, HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM, HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, + HTT_T2H_MSG_TYPE_PEER_STATS, /* keep this last */ HTT_T2H_NUM_MSGS }; @@ -1470,6 +1472,28 @@ struct htt_channel_change { __le32 phymode; } __packed; +struct htt_per_peer_tx_stats_ind { + __le32 succ_bytes; + __le32 retry_bytes; + __le32 failed_bytes; + u8 ratecode; + u8 flags; + __le16 peer_id; + __le16 succ_pkts; + __le16 retry_pkts; + __le16 failed_pkts; + __le16 tx_duration; + __le32 reserved1; + __le32 reserved2; +} __packed; + +struct htt_peer_tx_stats { + u8 num_ppdu; + u8 ppdu_len; + u8 version; + u8 payload[0]; +} __packed; + union htt_rx_pn_t { /* WEP: 24-bit PN */ u32 pn24; @@ -1521,6 +1545,7 @@ struct htt_resp { struct htt_tx_fetch_confirm tx_fetch_confirm; struct htt_tx_mode_switch_ind tx_mode_switch_ind; struct htt_channel_change chan_change; + struct htt_peer_tx_stats peer_tx_stats; }; } __packed; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 285b235..86d082c 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2194,6 +2194,128 @@ void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb_any(skb); } +static inline bool is_valid_legacy_rate(u8 rate) +{ + static const u8 legacy_rates[] = {1, 2, 5, 11, 6, 9, 12, + 18, 24, 36, 48, 54}; + int i; + + for (i = 0; i < ARRAY_SIZE(legacy_rates); i++) { + if (rate == legacy_rates[i]) + return true; + } + + return false; +} + +static void +ath10k_update_per_peer_tx_stats(struct ath10k *ar, + struct ieee80211_sta *sta, + struct ath10k_per_peer_tx_stats *peer_stats) +{ + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + u8 rate = 0, sgi; + struct rate_info txrate; + + lockdep_assert_held(&ar->data_lock); + + txrate.flags = ATH10K_HW_PREAMBLE(peer_stats->ratecode); + txrate.bw = ATH10K_HW_BW(peer_stats->flags); + txrate.nss = ATH10K_HW_NSS(peer_stats->ratecode); + txrate.mcs = ATH10K_HW_MCS_RATE(peer_stats->ratecode); + sgi = ATH10K_HW_GI(peer_stats->flags); + + if (((txrate.flags == WMI_RATE_PREAMBLE_HT) || + (txrate.flags == WMI_RATE_PREAMBLE_VHT)) && txrate.mcs > 9) { + ath10k_warn(ar, "Invalid mcs %hhd peer stats", txrate.mcs); + return; + } + + if (txrate.flags == WMI_RATE_PREAMBLE_CCK || + txrate.flags == WMI_RATE_PREAMBLE_OFDM) { + rate = ATH10K_HW_LEGACY_RATE(peer_stats->ratecode); + + if (!is_valid_legacy_rate(rate)) { + ath10k_warn(ar, "Invalid legacy rate %hhd peer stats", + rate); + return; + } + + /* This is hacky, FW sends CCK rate 5.5Mbps as 6 */ + rate *= 10; + if (rate == 60 && txrate.flags == WMI_RATE_PREAMBLE_CCK) + rate = rate - 5; + arsta->txrate.legacy = rate * 10; + } else if (txrate.flags == WMI_RATE_PREAMBLE_HT) { + arsta->txrate.flags = RATE_INFO_FLAGS_MCS; + arsta->txrate.mcs = txrate.mcs; + } else { + arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + arsta->txrate.mcs = txrate.mcs; + } + + if (sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + + arsta->txrate.nss = txrate.nss; + arsta->txrate.bw = txrate.bw + RATE_INFO_BW_20; +} + +static void ath10k_htt_fetch_peer_stats(struct ath10k *ar, + struct sk_buff *skb) +{ + struct htt_resp *resp = (struct htt_resp *)skb->data; + struct ath10k_per_peer_tx_stats *p_tx_stats = &ar->peer_tx_stats; + struct htt_per_peer_tx_stats_ind *tx_stats; + struct ieee80211_sta *sta; + struct ath10k_peer *peer; + int peer_id, i; + u8 ppdu_len, num_ppdu; + + num_ppdu = resp->peer_tx_stats.num_ppdu; + ppdu_len = resp->peer_tx_stats.ppdu_len * sizeof(__le32); + + if (skb->len < sizeof(struct htt_resp_hdr) + num_ppdu * ppdu_len) { + ath10k_warn(ar, "Invalid peer stats buf length %d\n", skb->len); + return; + } + + tx_stats = (struct htt_per_peer_tx_stats_ind *) + (resp->peer_tx_stats.payload); + peer_id = __le16_to_cpu(tx_stats->peer_id); + + rcu_read_lock(); + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, peer_id); + if (!peer) { + ath10k_warn(ar, "Invalid peer id %d peer stats buffer\n", + peer_id); + goto out; + } + + sta = peer->sta; + for (i = 0; i < num_ppdu; i++) { + tx_stats = (struct htt_per_peer_tx_stats_ind *) + (resp->peer_tx_stats.payload + i * ppdu_len); + + p_tx_stats->succ_bytes = __le32_to_cpu(tx_stats->succ_bytes); + p_tx_stats->retry_bytes = __le32_to_cpu(tx_stats->retry_bytes); + p_tx_stats->failed_bytes = + __le32_to_cpu(tx_stats->failed_bytes); + p_tx_stats->ratecode = tx_stats->ratecode; + p_tx_stats->flags = tx_stats->flags; + p_tx_stats->succ_pkts = __le16_to_cpu(tx_stats->succ_pkts); + p_tx_stats->retry_pkts = __le16_to_cpu(tx_stats->retry_pkts); + p_tx_stats->failed_pkts = __le16_to_cpu(tx_stats->failed_pkts); + + ath10k_update_per_peer_tx_stats(ar, sta, p_tx_stats); + } + +out: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} + bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_htt *htt = &ar->htt; @@ -2354,6 +2476,9 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND: ath10k_htt_rx_tx_mode_switch_ind(ar, skb); break; + case HTT_T2H_MSG_TYPE_PEER_STATS: + ath10k_htt_fetch_peer_stats(ar, skb); + break; case HTT_T2H_MSG_TYPE_EN_STATS: default: ath10k_warn(ar, "htt event (%d) not handled\n", diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 3a73b5c..5d3dff9 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4603,9 +4603,17 @@ enum wmi_rate_preamble { #define ATH10K_HW_NSS(rate) (1 + (((rate) >> 4) & 0x3)) #define ATH10K_HW_PREAMBLE(rate) (((rate) >> 6) & 0x3) -#define ATH10K_HW_RATECODE(rate, nss, preamble) \ +#define ATH10K_HW_MCS_RATE(rate) ((rate) & 0xf) +#define ATH10K_HW_LEGACY_RATE(rate) ((rate) & 0x3f) +#define ATH10K_HW_BW(flags) (((flags) >> 3) & 0x3) +#define ATH10K_HW_GI(flags) (((flags) >> 5) & 0x1) +#define ATH10K_HW_RATECODE(rate, nss, preamble) \ (((preamble) << 6) | ((nss) << 4) | (rate)) +#define VHT_MCS_NUM 10 +#define VHT_BW_NUM 4 +#define VHT_NSS_NUM 4 + /* Value to disable fixed rate setting */ #define WMI_FIXED_RATE_NONE (0xff) -- cgit v1.1 From 5608eaf7b0862b32f1c22d89ea97ade564d9469c Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 23 Nov 2016 16:58:11 +0200 Subject: ath10k: add support for per sta tx bitrate Per STA tx bitrate info is filled from peer stats. Export per sta txrate info to cfg80211/nl80211 Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debugfs_sta.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index 9955fea..fce6f81 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -77,6 +77,19 @@ void ath10k_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sinfo->rx_duration = arsta->rx_duration; sinfo->filled |= 1ULL << NL80211_STA_INFO_RX_DURATION; + + if (!arsta->txrate.legacy && !arsta->txrate.nss) + return; + + if (arsta->txrate.legacy) { + sinfo->txrate.legacy = arsta->txrate.legacy; + } else { + sinfo->txrate.mcs = arsta->txrate.mcs; + sinfo->txrate.nss = arsta->txrate.nss; + sinfo->txrate.bw = arsta->txrate.bw; + } + sinfo->txrate.flags = arsta->txrate.flags; + sinfo->filled |= 1ULL << NL80211_STA_INFO_TX_BITRATE; } static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file, -- cgit v1.1 From 705d7aa062203226c8df73f18622664e30bd8a61 Mon Sep 17 00:00:00 2001 From: "Manoharan, Rajkumar" Date: Wed, 23 Nov 2016 16:58:11 +0200 Subject: ath10k: fix monitor vdev for receiving other bss frames In order to receive other BSS entries in mesh mode, Monitor vdev is created whenever filter flag is set with OTHER_BSS. Recently it is root caused that setting promisc filter for Mesh interface is causing performance and stability issues. To fix this issue, firmware will configure appropriate rxfilters by default for mesh vdev during vdev creation. This change fixes monitor vdev creation based on firmware IE Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 1 + drivers/net/wireless/ath/ath10k/core.h | 7 +++++++ drivers/net/wireless/ath/ath10k/mac.c | 4 +++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7005e2a..b796be1 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -326,6 +326,7 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_PEER_FLOW_CONTROL] = "peer-flow-ctrl", [ATH10K_FW_FEATURE_BTCOEX_PARAM] = "btcoex-param", [ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR] = "skip-null-func-war", + [ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST] = "allows-mesh-bcast", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 6f2608a..6d06ec5 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -562,6 +562,13 @@ enum ath10k_fw_features { */ ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR = 15, + /* Firmware allow other BSS mesh broadcast/multicast frames without + * creating monitor interface. Appropriate rxfilters are programmed for + * mesh vdev by firmware itself. This feature flags will be used for + * not creating monitor vdev while configuring mesh node. + */ + ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST = 16, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3918097..d06a12d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1167,7 +1167,9 @@ static bool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar) return false; return ar->monitor || - ar->filter_flags & FIF_OTHER_BSS || + (!test_bit(ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST, + ar->running_fw->fw_file.fw_features) && + (ar->filter_flags & FIF_OTHER_BSS)) || test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); } -- cgit v1.1 From b9116823189e85ccf3843c7d3ee3a361af0c3eb6 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 21 Nov 2016 13:56:31 +0000 Subject: VSOCK: add loopback to virtio_transport The VMware VMCI transport supports loopback inside virtual machines. This patch implements loopback for virtio-vsock. Flow control is handled by the virtio-vsock protocol as usual. The sending process stops transmitting on a connection when the peer's receive buffer space is exhausted. Cathy Avery noticed this difference between VMCI and virtio-vsock when a test case using loopback failed. Although loopback isn't the main point of AF_VSOCK, it is useful for testing and virtio-vsock must match VMCI semantics so that userspace programs run regardless of the underlying transport. My understanding is that loopback is not supported on the host side with VMCI. Follow that by implementing it only in the guest driver, not the vhost host driver. Cc: Jorgen Hansen Reported-by: Cathy Avery Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- net/vmw_vsock/virtio_transport.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 936d7ee..2e47f9f0 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -44,6 +44,10 @@ struct virtio_vsock { spinlock_t send_pkt_list_lock; struct list_head send_pkt_list; + struct work_struct loopback_work; + spinlock_t loopback_list_lock; /* protects loopback_list */ + struct list_head loopback_list; + atomic_t queued_replies; /* The following fields are protected by rx_lock. vqs[VSOCK_VQ_RX] @@ -74,6 +78,42 @@ static u32 virtio_transport_get_local_cid(void) return vsock->guest_cid; } +static void virtio_transport_loopback_work(struct work_struct *work) +{ + struct virtio_vsock *vsock = + container_of(work, struct virtio_vsock, loopback_work); + LIST_HEAD(pkts); + + spin_lock_bh(&vsock->loopback_list_lock); + list_splice_init(&vsock->loopback_list, &pkts); + spin_unlock_bh(&vsock->loopback_list_lock); + + mutex_lock(&vsock->rx_lock); + while (!list_empty(&pkts)) { + struct virtio_vsock_pkt *pkt; + + pkt = list_first_entry(&pkts, struct virtio_vsock_pkt, list); + list_del_init(&pkt->list); + + virtio_transport_recv_pkt(pkt); + } + mutex_unlock(&vsock->rx_lock); +} + +static int virtio_transport_send_pkt_loopback(struct virtio_vsock *vsock, + struct virtio_vsock_pkt *pkt) +{ + int len = pkt->len; + + spin_lock_bh(&vsock->loopback_list_lock); + list_add_tail(&pkt->list, &vsock->loopback_list); + spin_unlock_bh(&vsock->loopback_list_lock); + + queue_work(virtio_vsock_workqueue, &vsock->loopback_work); + + return len; +} + static void virtio_transport_send_pkt_work(struct work_struct *work) { @@ -159,6 +199,9 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt) return -ENODEV; } + if (le32_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) + return virtio_transport_send_pkt_loopback(vsock, pkt); + if (pkt->reply) atomic_inc(&vsock->queued_replies); @@ -510,10 +553,13 @@ static int virtio_vsock_probe(struct virtio_device *vdev) mutex_init(&vsock->event_lock); spin_lock_init(&vsock->send_pkt_list_lock); INIT_LIST_HEAD(&vsock->send_pkt_list); + spin_lock_init(&vsock->loopback_list_lock); + INIT_LIST_HEAD(&vsock->loopback_list); INIT_WORK(&vsock->rx_work, virtio_transport_rx_work); INIT_WORK(&vsock->tx_work, virtio_transport_tx_work); INIT_WORK(&vsock->event_work, virtio_transport_event_work); INIT_WORK(&vsock->send_pkt_work, virtio_transport_send_pkt_work); + INIT_WORK(&vsock->loopback_work, virtio_transport_loopback_work); mutex_lock(&vsock->rx_lock); virtio_vsock_rx_fill(vsock); @@ -539,6 +585,7 @@ static void virtio_vsock_remove(struct virtio_device *vdev) struct virtio_vsock *vsock = vdev->priv; struct virtio_vsock_pkt *pkt; + flush_work(&vsock->loopback_work); flush_work(&vsock->rx_work); flush_work(&vsock->tx_work); flush_work(&vsock->event_work); @@ -565,6 +612,15 @@ static void virtio_vsock_remove(struct virtio_device *vdev) } spin_unlock_bh(&vsock->send_pkt_list_lock); + spin_lock_bh(&vsock->loopback_list_lock); + while (!list_empty(&vsock->loopback_list)) { + pkt = list_first_entry(&vsock->loopback_list, + struct virtio_vsock_pkt, list); + list_del(&pkt->list); + virtio_transport_free_pkt(pkt); + } + spin_unlock_bh(&vsock->loopback_list_lock); + mutex_lock(&the_virtio_vsock_mutex); the_virtio_vsock = NULL; vsock_core_exit(); -- cgit v1.1 From e22e996b72d47c7bf5bb6b17071b2e9d3db462d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 22 Nov 2016 16:47:11 +0100 Subject: net/phy: add trace events for mdio accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it possible to generate trace events for mdio read and write accesses. Signed-off-by: Uwe Kleine-König Acked-by: Steven Rostedt Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 11 +++++++++++ include/trace/events/mdio.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 include/trace/events/mdio.h diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 09deef4..653d076 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -38,6 +38,9 @@ #include +#define CREATE_TRACE_POINTS +#include + int mdiobus_register_device(struct mdio_device *mdiodev) { if (mdiodev->bus->mdio_map[mdiodev->addr]) @@ -461,6 +464,8 @@ int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum) retval = bus->read(bus, addr, regnum); mutex_unlock(&bus->mdio_lock); + trace_mdio_access(bus, 1, addr, regnum, retval, retval); + return retval; } EXPORT_SYMBOL(mdiobus_read_nested); @@ -485,6 +490,8 @@ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) retval = bus->read(bus, addr, regnum); mutex_unlock(&bus->mdio_lock); + trace_mdio_access(bus, 1, addr, regnum, retval, retval); + return retval; } EXPORT_SYMBOL(mdiobus_read); @@ -513,6 +520,8 @@ int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val) err = bus->write(bus, addr, regnum, val); mutex_unlock(&bus->mdio_lock); + trace_mdio_access(bus, 0, addr, regnum, val, err); + return err; } EXPORT_SYMBOL(mdiobus_write_nested); @@ -538,6 +547,8 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) err = bus->write(bus, addr, regnum, val); mutex_unlock(&bus->mdio_lock); + trace_mdio_access(bus, 0, addr, regnum, val, err); + return err; } EXPORT_SYMBOL(mdiobus_write); diff --git a/include/trace/events/mdio.h b/include/trace/events/mdio.h new file mode 100644 index 0000000..00d85f5 --- /dev/null +++ b/include/trace/events/mdio.h @@ -0,0 +1,42 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mdio + +#if !defined(_TRACE_MDIO_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_MDIO_H + +#include + +TRACE_EVENT_CONDITION(mdio_access, + + TP_PROTO(struct mii_bus *bus, char read, + u8 addr, unsigned regnum, u16 val, int err), + + TP_ARGS(bus, read, addr, regnum, val, err), + + TP_CONDITION(err >= 0), + + TP_STRUCT__entry( + __array(char, busid, MII_BUS_ID_SIZE) + __field(char, read) + __field(u8, addr) + __field(u16, val) + __field(unsigned, regnum) + ), + + TP_fast_assign( + strncpy(__entry->busid, bus->id, MII_BUS_ID_SIZE); + __entry->read = read; + __entry->addr = addr; + __entry->regnum = regnum; + __entry->val = val; + ), + + TP_printk("%s %-5s phy:0x%02hhx reg:0x%02x val:0x%04hx", + __entry->busid, __entry->read ? "read" : "write", + __entry->addr, __entry->regnum, __entry->val) +); + +#endif /* if !defined(_TRACE_MDIO_H) || defined(TRACE_HEADER_MULTI_READ) */ + +/* This part must be outside protection */ +#include -- cgit v1.1 From 7d381a025f01550350a1890ab782afc832399eb6 Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Tue, 22 Nov 2016 17:47:21 +0100 Subject: net: dsa: mv88e6xxx: add MV88E6097 switch Add support for the MV88E6097 switch. The change was tested on an Armada based platform with a MV88E6097 switch. Signed-off-by: Stefan Eichenberger Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 26 ++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index bada646..b14b3d5 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3209,6 +3209,19 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_get_stats = mv88e6095_stats_get_stats, }; +static const struct mv88e6xxx_ops mv88e6097_ops = { + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, + .stats_get_sset_count = mv88e6095_stats_get_sset_count, + .stats_get_strings = mv88e6095_stats_get_strings, + .stats_get_stats = mv88e6095_stats_get_stats, +}; + static const struct mv88e6xxx_ops mv88e6123_ops = { /* MV88E6XXX_FAMILY_6165 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3580,6 +3593,19 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6095_ops, }, + [MV88E6097] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6097, + .family = MV88E6XXX_FAMILY_6097, + .name = "Marvell 88E6097/88E6097F", + .num_databases = 4096, + .num_ports = 11, + .port_base_addr = 0x10, + .global1_addr = 0x1b, + .age_time_coeff = 15000, + .flags = MV88E6XXX_FLAGS_FAMILY_6097, + .ops = &mv88e6097_ops, + }, + [MV88E6123] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6123, .family = MV88E6XXX_FAMILY_6165, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 9298faa..ab52c37 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -81,6 +81,7 @@ #define PORT_SWITCH_ID 0x03 #define PORT_SWITCH_ID_PROD_NUM_6085 0x04a #define PORT_SWITCH_ID_PROD_NUM_6095 0x095 +#define PORT_SWITCH_ID_PROD_NUM_6097 0x099 #define PORT_SWITCH_ID_PROD_NUM_6131 0x106 #define PORT_SWITCH_ID_PROD_NUM_6320 0x115 #define PORT_SWITCH_ID_PROD_NUM_6123 0x121 @@ -378,6 +379,7 @@ enum mv88e6xxx_model { MV88E6085, MV88E6095, + MV88E6097, MV88E6123, MV88E6131, MV88E6161, -- cgit v1.1 From f8071cde7890db198201afa748efd658a8ba0fb2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Nov 2016 12:51:50 -0800 Subject: tcp: enhance tcp_collapse_retrans() with skb_shift() In commit 2331ccc5b323 ("tcp: enhance tcp collapsing"), we made a first step allowing copying right skb to left skb head. Since all skbs in socket write queue are headless (but possibly the very first one), this strategy often does not work. This patch extends tcp_collapse_retrans() to perform frag shifting, thanks to skb_shift() helper. This helper needs to not BUG on non headless skbs, as callers are ok with that. Tested: Following packetdrill test now passes : 0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0 bind(3, ..., ...) = 0 +0 listen(3, 1) = 0 +0 < S 0:0(0) win 32792 +0 > S. 0:0(0) ack 1 +.100 < . 1:1(0) ack 1 win 257 +0 accept(3, ..., ...) = 4 +0 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 +0 write(4, ..., 200) = 200 +0 > P. 1:201(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 201:401(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 401:601(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 601:801(200) ack 1 +.001 write(4, ..., 200) = 200 +0 > P. 801:1001(200) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1001:1101(100) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1101:1201(100) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1201:1301(100) ack 1 +.001 write(4, ..., 100) = 100 +0 > P. 1301:1401(100) ack 1 +.099 < . 1:1(0) ack 201 win 257 +.001 < . 1:1(0) ack 201 win 257 +0 > P. 201:1001(800) ack 1 Signed-off-by: Eric Dumazet Cc: Neal Cardwell Cc: Yuchung Cheng Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- net/core/skbuff.c | 4 +++- net/ipv4/tcp_output.c | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4c96cb1..d1d1a5a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2656,7 +2656,9 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) struct skb_frag_struct *fragfrom, *fragto; BUG_ON(shiftlen > skb->len); - BUG_ON(skb_headlen(skb)); /* Would corrupt stream */ + + if (skb_headlen(skb)) + return 0; todo = shiftlen; from = 0; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f57b5aa..19105b4 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2514,7 +2514,7 @@ void tcp_skb_collapse_tstamp(struct sk_buff *skb, } /* Collapses two adjacent SKB's during retransmission. */ -static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) +static bool tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *next_skb = tcp_write_queue_next(sk, skb); @@ -2525,14 +2525,17 @@ static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) BUG_ON(tcp_skb_pcount(skb) != 1 || tcp_skb_pcount(next_skb) != 1); + if (next_skb_size) { + if (next_skb_size <= skb_availroom(skb)) + skb_copy_bits(next_skb, 0, skb_put(skb, next_skb_size), + next_skb_size); + else if (!skb_shift(skb, next_skb, next_skb_size)) + return false; + } tcp_highest_sack_combine(sk, next_skb, skb); tcp_unlink_write_queue(next_skb, sk); - if (next_skb_size) - skb_copy_bits(next_skb, 0, skb_put(skb, next_skb_size), - next_skb_size); - if (next_skb->ip_summed == CHECKSUM_PARTIAL) skb->ip_summed = CHECKSUM_PARTIAL; @@ -2561,6 +2564,7 @@ static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) tcp_skb_collapse_tstamp(skb, next_skb); sk_wmem_free_skb(sk, next_skb); + return true; } /* Check if coalescing SKBs is legal. */ @@ -2610,16 +2614,12 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to, if (space < 0) break; - /* Punt if not enough space exists in the first SKB for - * the data in the second - */ - if (skb->len > skb_availroom(to)) - break; if (after(TCP_SKB_CB(skb)->end_seq, tcp_wnd_end(tp))) break; - tcp_collapse_retrans(sk, to); + if (!tcp_collapse_retrans(sk, to)) + break; } } -- cgit v1.1 From 5519da874ad0f382b725281e443cf7fe58995ed8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 22 Nov 2016 11:40:54 -0800 Subject: net: phy: broadcom: Move bcm54xx_auxctl_{read, write} to common library We are going to need these functions to implement support for Broadcom Wirespeed, aka downshift. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm-phy-lib.c | 17 +++++++++++++++++ drivers/net/phy/bcm-phy-lib.h | 3 +++ drivers/net/phy/broadcom.c | 15 --------------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index df0416d..18e11b3 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -50,6 +50,23 @@ int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) } EXPORT_SYMBOL_GPL(bcm_phy_read_exp); +int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) +{ + /* The register must be written to both the Shadow Register Select and + * the Shadow Read Register Selector + */ + phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | + regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); + return phy_read(phydev, MII_BCM54XX_AUX_CTL); +} +EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); + +int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) +{ + return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); +} +EXPORT_SYMBOL(bcm54xx_auxctl_write); + int bcm_phy_write_misc(struct phy_device *phydev, u16 reg, u16 chl, u16 val) { diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index b2091c8..31cb4fd 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -19,6 +19,9 @@ int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val); int bcm_phy_read_exp(struct phy_device *phydev, u16 reg); +int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val); +int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum); + int bcm_phy_write_misc(struct phy_device *phydev, u16 reg, u16 chl, u16 value); int bcm_phy_read_misc(struct phy_device *phydev, diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index b1e32e9..409b365 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -30,21 +30,6 @@ MODULE_DESCRIPTION("Broadcom PHY driver"); MODULE_AUTHOR("Maciej W. Rozycki"); MODULE_LICENSE("GPL"); -static int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) -{ - /* The register must be written to both the Shadow Register Select and - * the Shadow Read Register Selector - */ - phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | - regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); - return phy_read(phydev, MII_BCM54XX_AUX_CTL); -} - -static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) -{ - return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); -} - static int bcm54810_config(struct phy_device *phydev) { int rc, val; -- cgit v1.1 From d06f78c4232d6a84b50839f61d9d7fbb222d8118 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 22 Nov 2016 11:40:55 -0800 Subject: net: phy: broadcom: Add support code for downshift/Wirespeed Broadcom's Wirespeed feature allows us to configure how auto-negotiation should behave with fewer working pairs of wires on a cable. Add support code for retrieving and setting such downshift counters using the recently added ethtool downshift tunables. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm-phy-lib.c | 86 +++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/bcm-phy-lib.h | 5 +++ include/linux/brcmphy.h | 10 +++++ 3 files changed, 101 insertions(+) diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index 18e11b3..d742894 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -225,6 +225,92 @@ int bcm_phy_enable_eee(struct phy_device *phydev) } EXPORT_SYMBOL_GPL(bcm_phy_enable_eee); +int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) +{ + int val; + + val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); + if (val < 0) + return val; + + /* Check if wirespeed is enabled or not */ + if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { + *count = DOWNSHIFT_DEV_DISABLE; + return 0; + } + + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); + if (val < 0) + return val; + + /* Downgrade after one link attempt */ + if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { + *count = 1; + } else { + /* Downgrade after configured retry count */ + val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; + val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; + *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; + } + + return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); + +int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) +{ + int val = 0, ret = 0; + + /* Range check the number given */ + if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && + count != DOWNSHIFT_DEV_DEFAULT_COUNT) { + return -ERANGE; + } + + val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); + if (val < 0) + return val; + + /* Se the write enable bit */ + val |= MII_BCM54XX_AUXCTL_MISC_WREN; + + if (count == DOWNSHIFT_DEV_DISABLE) { + val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; + return bcm54xx_auxctl_write(phydev, + MII_BCM54XX_AUXCTL_SHDWSEL_MISC, + val); + } else { + val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; + ret = bcm54xx_auxctl_write(phydev, + MII_BCM54XX_AUXCTL_SHDWSEL_MISC, + val); + if (ret < 0) + return ret; + } + + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); + val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | + BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); + + switch (count) { + case 1: + val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; + break; + case DOWNSHIFT_DEV_DEFAULT_COUNT: + val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; + break; + default: + val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; + break; + } + + return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); +} +EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); + MODULE_DESCRIPTION("Broadcom PHY Library"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index 31cb4fd..3f492e6 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -37,4 +37,9 @@ int bcm_phy_config_intr(struct phy_device *phydev); int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down); int bcm_phy_enable_eee(struct phy_device *phydev); + +int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count); + +int bcm_phy_downshift_set(struct phy_device *phydev, u8 count); + #endif /* _LINUX_BCM_PHY_LIB_H */ diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 848dc50..f9f8aaf 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -114,6 +114,7 @@ #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007 #define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12 #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN (1 << 8) +#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN (1 << 4) #define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007 @@ -130,6 +131,7 @@ #define BCM_LED_SRC_INTR 0x6 #define BCM_LED_SRC_QUALITY 0x7 #define BCM_LED_SRC_RCVLED 0x8 +#define BCM_LED_SRC_WIRESPEED 0x9 #define BCM_LED_SRC_MULTICOLOR1 0xa #define BCM_LED_SRC_OPENSHORT 0xb #define BCM_LED_SRC_OFF 0xe /* Tied high */ @@ -141,6 +143,14 @@ * Shadow values go into bits [14:10] of register 0x1c to select a shadow * register to access. */ + +/* 00100: Reserved control register 2 */ +#define BCM54XX_SHD_SCR2 0x04 +#define BCM54XX_SHD_SCR2_WSPD_RTRY_DIS 0x100 +#define BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT 2 +#define BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET 2 +#define BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK 0x7 + /* 00101: Spare Control Register 3 */ #define BCM54XX_SHD_SCR3 0x05 #define BCM54XX_SHD_SCR3_DEF_CLK125 0x0001 -- cgit v1.1 From 99cec8a4dda28972067c274a4215422262dc5522 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 22 Nov 2016 11:40:56 -0800 Subject: net: phy: broadcom: Allow enabling or disabling of EEE In preparation for adding support for Wirespeed/downshift, we need to change bcm_phy_eee_enable() to allow enabling or disabling EEE, so make the function take an extra enable/disable boolean parameter and rename it to illustrate it sets EEE, not necessarily just enables it. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm-cygnus.c | 2 +- drivers/net/phy/bcm-phy-lib.c | 14 ++++++++++---- drivers/net/phy/bcm-phy-lib.h | 2 +- drivers/net/phy/bcm7xxx.c | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c index 49bbc68..196400c 100644 --- a/drivers/net/phy/bcm-cygnus.c +++ b/drivers/net/phy/bcm-cygnus.c @@ -104,7 +104,7 @@ static int bcm_cygnus_config_init(struct phy_device *phydev) return rc; /* Advertise EEE */ - rc = bcm_phy_enable_eee(phydev); + rc = bcm_phy_set_eee(phydev, true); if (rc) return rc; diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index d742894..3156ce6 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -195,7 +195,7 @@ int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) } EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); -int bcm_phy_enable_eee(struct phy_device *phydev) +int bcm_phy_set_eee(struct phy_device *phydev, bool enable) { int val; @@ -205,7 +205,10 @@ int bcm_phy_enable_eee(struct phy_device *phydev) if (val < 0) return val; - val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; + if (enable) + val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; + else + val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, MDIO_MMD_AN, (u32)val); @@ -216,14 +219,17 @@ int bcm_phy_enable_eee(struct phy_device *phydev) if (val < 0) return val; - val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); + if (enable) + val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); + else + val &= ~(MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, MDIO_MMD_AN, (u32)val); return 0; } -EXPORT_SYMBOL_GPL(bcm_phy_enable_eee); +EXPORT_SYMBOL_GPL(bcm_phy_set_eee); int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) { diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index 3f492e6..a117f65 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -36,7 +36,7 @@ int bcm_phy_config_intr(struct phy_device *phydev); int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down); -int bcm_phy_enable_eee(struct phy_device *phydev); +int bcm_phy_set_eee(struct phy_device *phydev, bool enable); int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count); diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 9636da0..b7789e8 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -199,7 +199,7 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) if (ret) return ret; - ret = bcm_phy_enable_eee(phydev); + ret = bcm_phy_set_eee(phydev, true); if (ret) return ret; -- cgit v1.1 From db88816ba22284cef98ce290adfac03bb1d5221c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 22 Nov 2016 11:40:57 -0800 Subject: net: phy: bcm7xxx: Add support for downshift/Wirespeed Add support for configuring the downshift/Wirespeed enable/disable toggles and specify a link retry value ranging from 1 to 9. Since the integrated BCM7xxx have issues when wirespeed is enabled and EEE is also enabled, we do disable EEE if wirespeed is enabled. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index b7789e8..5b3be4c 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -167,6 +167,7 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) { u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags); + u8 count; int ret = 0; pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", @@ -199,7 +200,12 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) if (ret) return ret; - ret = bcm_phy_set_eee(phydev, true); + ret = bcm_phy_downshift_get(phydev, &count); + if (ret) + return ret; + + /* Only enable EEE if Wirespeed/downshift is disabled */ + ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); if (ret) return ret; @@ -303,6 +309,47 @@ static int bcm7xxx_suspend(struct phy_device *phydev) return 0; } +static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, + void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + return bcm_phy_downshift_get(phydev, (u8 *)data); + default: + return -EOPNOTSUPP; + } +} + +static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, + const void *data) +{ + u8 count = *(u8 *)data; + int ret; + + switch (tuna->id) { + case ETHTOOL_PHY_DOWNSHIFT: + ret = bcm_phy_downshift_set(phydev, count); + break; + default: + return -EOPNOTSUPP; + } + + if (ret) + return ret; + + /* Disable EEE advertisment since this prevents the PHY + * from successfully linking up, trigger auto-negotiation restart + * to let the MAC decide what to do. + */ + ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); + if (ret) + return ret; + + return genphy_restart_aneg(phydev); +} + #define BCM7XXX_28NM_GPHY(_oui, _name) \ { \ .phy_id = (_oui), \ @@ -315,6 +362,8 @@ static int bcm7xxx_suspend(struct phy_device *phydev) .config_aneg = genphy_config_aneg, \ .read_status = genphy_read_status, \ .resume = bcm7xxx_28nm_resume, \ + .get_tunable = bcm7xxx_28nm_get_tunable, \ + .set_tunable = bcm7xxx_28nm_set_tunable, \ } #define BCM7XXX_40NM_EPHY(_oui, _name) \ -- cgit v1.1 From 30ce0de435603da84412f07fa7f097b4c1c7b15f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 22 Nov 2016 11:40:58 -0800 Subject: net: dsa: bcm_sf2: Ensure we re-negotiate EEE during after link change In case the link change and EEE is enabled or disabled, always try to re-negotiate this with the link partner. Fixes: 450b05c15f9c ("net: dsa: bcm_sf2: add support for controlling EEE") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index e3ee27c..9ec33b5 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -588,6 +588,7 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); + struct ethtool_eee *p = &priv->port_sts[port].eee; u32 id_mode_dis = 0, port_mode; const char *str = NULL; u32 reg; @@ -662,6 +663,9 @@ force_link: reg |= DUPLX_MODE; core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); + + if (!phydev->is_pseudo_fixed_link) + p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev); } static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, -- cgit v1.1 From 3df5b3c67546fb05266766b6abaf71563f82efe4 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 22 Nov 2016 23:09:54 +0200 Subject: net: Add net-device param to the get offloaded stats ndo Some drivers would need to check few internal matters for that. To be used in downstream mlx5 commit. Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- include/linux/netdevice.h | 4 ++-- net/core/rtnetlink.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 4a1f9d5..e0d7d5a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -857,7 +857,7 @@ mlxsw_sp_port_get_sw_stats64(const struct net_device *dev, return 0; } -static bool mlxsw_sp_port_has_offload_stats(int attr_id) +static bool mlxsw_sp_port_has_offload_stats(const struct net_device *dev, int attr_id) { switch (attr_id) { case IFLA_OFFLOAD_XSTATS_CPU_HIT: diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e84800e..ae32a27 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -925,7 +925,7 @@ struct netdev_xdp { * 3. Update dev->stats asynchronously and atomically, and define * neither operation. * - * bool (*ndo_has_offload_stats)(int attr_id) + * bool (*ndo_has_offload_stats)(const struct net_device *dev, int attr_id) * Return true if this device supports offload stats of this attr_id. * * int (*ndo_get_offload_stats)(int attr_id, const struct net_device *dev, @@ -1165,7 +1165,7 @@ struct net_device_ops { struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev, struct rtnl_link_stats64 *storage); - bool (*ndo_has_offload_stats)(int attr_id); + bool (*ndo_has_offload_stats)(const struct net_device *dev, int attr_id); int (*ndo_get_offload_stats)(int attr_id, const struct net_device *dev, void *attr_data); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a99917b..ef8a960 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3671,7 +3671,7 @@ static int rtnl_get_offload_stats(struct sk_buff *skb, struct net_device *dev, if (!size) continue; - if (!dev->netdev_ops->ndo_has_offload_stats(attr_id)) + if (!dev->netdev_ops->ndo_has_offload_stats(dev, attr_id)) continue; attr = nla_reserve_64bit(skb, attr_id, size, @@ -3712,7 +3712,7 @@ static int rtnl_get_offload_stats_size(const struct net_device *dev) for (attr_id = IFLA_OFFLOAD_XSTATS_FIRST; attr_id <= IFLA_OFFLOAD_XSTATS_MAX; attr_id++) { - if (!dev->netdev_ops->ndo_has_offload_stats(attr_id)) + if (!dev->netdev_ops->ndo_has_offload_stats(dev, attr_id)) continue; size = rtnl_get_offload_stats_attr_size(attr_id); nla_size += nla_total_size_64bit(size); -- cgit v1.1 From 370bad0f9a5261da0ef0bc76705f5b0b8af148ab Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 22 Nov 2016 23:09:55 +0200 Subject: net/mlx5e: Support HW (offloaded) and SW counters for SRIOV switchdev mode Switchdev driver net-device port statistics should follow the model introduced in commit a5ea31f57309 'Merge branch net-offloaded-stats'. For VF reps we return the SRIOV eswitch vport stats as the usual ones and SW stats if asked. For the PF, if we're in the switchdev mode, we return the uplink stats and SW stats if asked, otherwise as before. The uplink stats are implemented using the PPCNT 802_3 counters which are already being read/cached by the driver. Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 9 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 31 +++--- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 111 +++++++++++++++++++-- drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 1 + 4 files changed, 128 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index ac09767..ebf5dbc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -874,6 +874,7 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv); void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv); int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr); void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); +void mlx5e_update_hw_rep_counters(struct mlx5e_priv *priv); int mlx5e_create_direct_rqts(struct mlx5e_priv *priv); void mlx5e_destroy_rqt(struct mlx5e_priv *priv, struct mlx5e_rqt *rqt); @@ -890,12 +891,16 @@ struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev, void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, struct mlx5e_priv *priv); int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev); void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev); -struct rtnl_link_stats64 * -mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats); u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout); void mlx5e_add_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti); void mlx5e_del_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti); +int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev, + void *sp); +bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id); + +bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); +bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv); #endif /* __MLX5_EN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 6957608..8e8d809 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -470,16 +470,6 @@ static void mlx5e_rq_free_mpwqe_info(struct mlx5e_rq *rq) kfree(rq->mpwqe.info); } -static bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv) -{ - struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv; - - if (rep && rep->vport != FDB_UPLINK_VPORT) - return true; - - return false; -} - static int mlx5e_create_rq(struct mlx5e_channel *c, struct mlx5e_rq_param *param, struct mlx5e_rq *rq) @@ -2664,7 +2654,7 @@ mqprio: return mlx5e_setup_tc(dev, tc->tc); } -struct rtnl_link_stats64 * +static struct rtnl_link_stats64 * mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -2672,13 +2662,20 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats) struct mlx5e_vport_stats *vstats = &priv->stats.vport; struct mlx5e_pport_stats *pstats = &priv->stats.pport; - stats->rx_packets = sstats->rx_packets; - stats->rx_bytes = sstats->rx_bytes; - stats->tx_packets = sstats->tx_packets; - stats->tx_bytes = sstats->tx_bytes; + if (mlx5e_is_uplink_rep(priv)) { + stats->rx_packets = PPORT_802_3_GET(pstats, a_frames_received_ok); + stats->rx_bytes = PPORT_802_3_GET(pstats, a_octets_received_ok); + stats->tx_packets = PPORT_802_3_GET(pstats, a_frames_transmitted_ok); + stats->tx_bytes = PPORT_802_3_GET(pstats, a_octets_transmitted_ok); + } else { + stats->rx_packets = sstats->rx_packets; + stats->rx_bytes = sstats->rx_bytes; + stats->tx_packets = sstats->tx_packets; + stats->tx_bytes = sstats->tx_bytes; + stats->tx_dropped = sstats->tx_queue_dropped; + } stats->rx_dropped = priv->stats.qcnt.rx_out_of_buffer; - stats->tx_dropped = sstats->tx_queue_dropped; stats->rx_length_errors = PPORT_802_3_GET(pstats, a_in_range_length_errors) + @@ -3290,6 +3287,8 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = mlx5e_netpoll, #endif + .ndo_has_offload_stats = mlx5e_has_offload_stats, + .ndo_get_offload_stats = mlx5e_get_offload_stats, }; static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index a84825d..e0d1a56 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -72,7 +72,29 @@ static void mlx5e_rep_get_strings(struct net_device *dev, } } -static void mlx5e_update_sw_rep_counters(struct mlx5e_priv *priv) +static void mlx5e_rep_update_hw_counters(struct mlx5e_priv *priv) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5_eswitch_rep *rep = priv->ppriv; + struct rtnl_link_stats64 *vport_stats; + struct ifla_vf_stats vf_stats; + int err; + + err = mlx5_eswitch_get_vport_stats(esw, rep->vport, &vf_stats); + if (err) { + pr_warn("vport %d error %d reading stats\n", rep->vport, err); + return; + } + + vport_stats = &priv->stats.vf_vport; + /* flip tx/rx as we are reporting the counters for the switch vport */ + vport_stats->rx_packets = vf_stats.tx_packets; + vport_stats->rx_bytes = vf_stats.tx_bytes; + vport_stats->tx_packets = vf_stats.rx_packets; + vport_stats->tx_bytes = vf_stats.rx_bytes; +} + +static void mlx5e_rep_update_sw_counters(struct mlx5e_priv *priv) { struct mlx5e_sw_stats *s = &priv->stats.sw; struct mlx5e_rq_stats *rq_stats; @@ -95,6 +117,12 @@ static void mlx5e_update_sw_rep_counters(struct mlx5e_priv *priv) } } +static void mlx5e_rep_update_stats(struct mlx5e_priv *priv) +{ + mlx5e_rep_update_sw_counters(priv); + mlx5e_rep_update_hw_counters(priv); +} + static void mlx5e_rep_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { @@ -106,7 +134,7 @@ static void mlx5e_rep_get_ethtool_stats(struct net_device *dev, mutex_lock(&priv->state_lock); if (test_bit(MLX5E_STATE_OPENED, &priv->state)) - mlx5e_update_sw_rep_counters(priv); + mlx5e_rep_update_sw_counters(priv); mutex_unlock(&priv->state_lock); for (i = 0; i < NUM_VPORT_REP_COUNTERS; i++) @@ -245,6 +273,77 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, } } +bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) +{ + struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv; + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + + if (rep && rep->vport == FDB_UPLINK_VPORT && esw->mode == SRIOV_OFFLOADS) + return true; + + return false; +} + +bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv) +{ + struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv; + + if (rep && rep->vport != FDB_UPLINK_VPORT) + return true; + + return false; +} + +bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + + switch (attr_id) { + case IFLA_OFFLOAD_XSTATS_CPU_HIT: + if (mlx5e_is_vf_vport_rep(priv) || mlx5e_is_uplink_rep(priv)) + return true; + } + + return false; +} + +static int +mlx5e_get_sw_stats64(const struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5e_sw_stats *sstats = &priv->stats.sw; + + stats->rx_packets = sstats->rx_packets; + stats->rx_bytes = sstats->rx_bytes; + stats->tx_packets = sstats->tx_packets; + stats->tx_bytes = sstats->tx_bytes; + + stats->tx_dropped = sstats->tx_queue_dropped; + + return 0; +} + +int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev, + void *sp) +{ + switch (attr_id) { + case IFLA_OFFLOAD_XSTATS_CPU_HIT: + return mlx5e_get_sw_stats64(dev, sp); + } + + return -EINVAL; +} + +static struct rtnl_link_stats64 * +mlx5e_rep_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + + memcpy(stats, &priv->stats.vf_vport, sizeof(*stats)); + return stats; +} + static const struct switchdev_ops mlx5e_rep_switchdev_ops = { .switchdev_port_attr_get = mlx5e_attr_get, }; @@ -255,9 +354,9 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = { .ndo_start_xmit = mlx5e_xmit, .ndo_get_phys_port_name = mlx5e_rep_get_phys_port_name, .ndo_setup_tc = mlx5e_rep_ndo_setup_tc, - .ndo_get_stats64 = mlx5e_get_stats, - .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, - .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, + .ndo_get_stats64 = mlx5e_rep_get_stats, + .ndo_has_offload_stats = mlx5e_has_offload_stats, + .ndo_get_offload_stats = mlx5e_get_offload_stats, }; static void mlx5e_build_rep_netdev_priv(struct mlx5_core_dev *mdev, @@ -407,7 +506,7 @@ static struct mlx5e_profile mlx5e_rep_profile = { .cleanup_rx = mlx5e_cleanup_rep_rx, .init_tx = mlx5e_init_rep_tx, .cleanup_tx = mlx5e_cleanup_nic_tx, - .update_stats = mlx5e_update_sw_rep_counters, + .update_stats = mlx5e_rep_update_stats, .max_nch = mlx5e_get_rep_max_num_channels, .max_tc = 1, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 5da6a1c..f202f87 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -407,6 +407,7 @@ struct mlx5e_stats { struct mlx5e_vport_stats vport; struct mlx5e_pport_stats pport; struct mlx5e_pcie_stats pcie; + struct rtnl_link_stats64 vf_vport; }; static const struct counter_desc mlx5e_pme_status_desc[] = { -- cgit v1.1 From 20a1ea6747836e841feb1941892b9baa1dbcd0fb Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 22 Nov 2016 23:09:56 +0200 Subject: net/mlx5e: Support VF vport link state control for SRIOV switchdev mode Reflect the administative link changes done on the VF representor to the VF e-switch vport. This means that doing ip link set down/up commands on the VF rep will modify the e-switch vport state which in turn will make proper VF drivers to set their carrier accordingly. Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 33 ++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index e0d1a56..5e33f6b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -236,6 +236,35 @@ void mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, mlx5e_tc_init(priv); } +static int mlx5e_rep_open(struct net_device *dev) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + int err; + + err = mlx5e_open(dev); + if (err) + return err; + + err = mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_UP); + if (!err) + netif_carrier_on(dev); + + return 0; +} + +static int mlx5e_rep_close(struct net_device *dev) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + + (void)mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN); + + return mlx5e_close(dev); +} + static int mlx5e_rep_get_phys_port_name(struct net_device *dev, char *buf, size_t len) { @@ -349,8 +378,8 @@ static const struct switchdev_ops mlx5e_rep_switchdev_ops = { }; static const struct net_device_ops mlx5e_netdev_ops_rep = { - .ndo_open = mlx5e_open, - .ndo_stop = mlx5e_close, + .ndo_open = mlx5e_rep_open, + .ndo_stop = mlx5e_rep_close, .ndo_start_xmit = mlx5e_xmit, .ndo_get_phys_port_name = mlx5e_rep_get_phys_port_name, .ndo_setup_tc = mlx5e_rep_ndo_setup_tc, -- cgit v1.1 From 59bfde01fab0c4550778cd53e8d266f1dfddf7b7 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Tue, 22 Nov 2016 23:09:57 +0200 Subject: devlink: Add E-Switch inline mode control Some HWs need the VF driver to put part of the packet headers on the TX descriptor so the e-switch can do proper matching and steering. The supported modes: none, link, network, transport. Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/net/devlink.h | 2 ++ include/uapi/linux/devlink.h | 8 +++++ net/core/devlink.c | 70 ++++++++++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/include/net/devlink.h b/include/net/devlink.h index 211bd3c..d29e5fc 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -92,6 +92,8 @@ struct devlink_ops { int (*eswitch_mode_get)(struct devlink *devlink, u16 *p_mode); int (*eswitch_mode_set)(struct devlink *devlink, u16 mode); + int (*eswitch_inline_mode_get)(struct devlink *devlink, u8 *p_inline_mode); + int (*eswitch_inline_mode_set)(struct devlink *devlink, u8 inline_mode); }; static inline void *devlink_priv(struct devlink *devlink) diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 915bfa7..9014c33 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -102,6 +102,13 @@ enum devlink_eswitch_mode { DEVLINK_ESWITCH_MODE_SWITCHDEV, }; +enum devlink_eswitch_inline_mode { + DEVLINK_ESWITCH_INLINE_MODE_NONE, + DEVLINK_ESWITCH_INLINE_MODE_LINK, + DEVLINK_ESWITCH_INLINE_MODE_NETWORK, + DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT, +}; + enum devlink_attr { /* don't change the order or add anything between, this is ABI! */ DEVLINK_ATTR_UNSPEC, @@ -133,6 +140,7 @@ enum devlink_attr { DEVLINK_ATTR_SB_OCC_CUR, /* u32 */ DEVLINK_ATTR_SB_OCC_MAX, /* u32 */ DEVLINK_ATTR_ESWITCH_MODE, /* u16 */ + DEVLINK_ATTR_ESWITCH_INLINE_MODE, /* u8 */ /* add new attributes above here, update the policy in devlink.c */ diff --git a/net/core/devlink.c b/net/core/devlink.c index c14f8b6..2b5bf9e 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1394,26 +1394,45 @@ static int devlink_nl_cmd_sb_occ_max_clear_doit(struct sk_buff *skb, static int devlink_eswitch_fill(struct sk_buff *msg, struct devlink *devlink, enum devlink_command cmd, u32 portid, - u32 seq, int flags, u16 mode) + u32 seq, int flags) { + const struct devlink_ops *ops = devlink->ops; void *hdr; + int err = 0; + u16 mode; + u8 inline_mode; hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); if (!hdr) return -EMSGSIZE; - if (devlink_nl_put_handle(msg, devlink)) - goto nla_put_failure; + err = devlink_nl_put_handle(msg, devlink); + if (err) + goto out; - if (nla_put_u16(msg, DEVLINK_ATTR_ESWITCH_MODE, mode)) - goto nla_put_failure; + err = ops->eswitch_mode_get(devlink, &mode); + if (err) + goto out; + err = nla_put_u16(msg, DEVLINK_ATTR_ESWITCH_MODE, mode); + if (err) + goto out; + + if (ops->eswitch_inline_mode_get) { + err = ops->eswitch_inline_mode_get(devlink, &inline_mode); + if (err) + goto out; + err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_INLINE_MODE, + inline_mode); + if (err) + goto out; + } genlmsg_end(msg, hdr); return 0; -nla_put_failure: +out: genlmsg_cancel(msg, hdr); - return -EMSGSIZE; + return err; } static int devlink_nl_cmd_eswitch_mode_get_doit(struct sk_buff *skb, @@ -1422,22 +1441,17 @@ static int devlink_nl_cmd_eswitch_mode_get_doit(struct sk_buff *skb, struct devlink *devlink = info->user_ptr[0]; const struct devlink_ops *ops = devlink->ops; struct sk_buff *msg; - u16 mode; int err; if (!ops || !ops->eswitch_mode_get) return -EOPNOTSUPP; - err = ops->eswitch_mode_get(devlink, &mode); - if (err) - return err; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; err = devlink_eswitch_fill(msg, devlink, DEVLINK_CMD_ESWITCH_MODE_GET, - info->snd_portid, info->snd_seq, 0, mode); + info->snd_portid, info->snd_seq, 0); if (err) { nlmsg_free(msg); @@ -1453,15 +1467,32 @@ static int devlink_nl_cmd_eswitch_mode_set_doit(struct sk_buff *skb, struct devlink *devlink = info->user_ptr[0]; const struct devlink_ops *ops = devlink->ops; u16 mode; + u8 inline_mode; + int err = 0; - if (!info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) - return -EINVAL; + if (!ops) + return -EOPNOTSUPP; - mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]); + if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) { + if (!ops->eswitch_mode_set) + return -EOPNOTSUPP; + mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]); + err = ops->eswitch_mode_set(devlink, mode); + if (err) + return err; + } - if (ops && ops->eswitch_mode_set) - return ops->eswitch_mode_set(devlink, mode); - return -EOPNOTSUPP; + if (info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) { + if (!ops->eswitch_inline_mode_set) + return -EOPNOTSUPP; + inline_mode = nla_get_u8( + info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]); + err = ops->eswitch_inline_mode_set(devlink, inline_mode); + if (err) + return err; + } + + return 0; } static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { @@ -1478,6 +1509,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 }, [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 }, [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 }, + [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 }, }; static const struct genl_ops devlink_nl_ops[] = { -- cgit v1.1 From 34e4e99078667d30f71a50c1e5181e4270e9d8bb Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Tue, 22 Nov 2016 23:09:58 +0200 Subject: net/mlx5: Enable to query min inline for a specific vport Also move the inline capablities enum to a shared header vport.h Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 6 ------ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 11 +++++------ drivers/net/ethernet/mellanox/mlx5/core/vport.c | 14 ++++++++------ include/linux/mlx5/vport.h | 10 ++++++++-- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index ebf5dbc..a2b32ed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -150,12 +150,6 @@ static inline int mlx5_max_log_rq_size(int wq_type) } } -enum { - MLX5E_INLINE_MODE_L2, - MLX5E_INLINE_MODE_VPORT_CONTEXT, - MLX5_INLINE_MODE_NOT_REQUIRED, -}; - struct mlx5e_tx_wqe { struct mlx5_wqe_ctrl_seg ctrl; struct mlx5_wqe_eth_seg eth; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 8e8d809..19403d6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -957,7 +957,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, sq->bf_buf_size = (1 << MLX5_CAP_GEN(mdev, log_bf_reg_size)) / 2; sq->max_inline = param->max_inline; sq->min_inline_mode = - MLX5_CAP_ETH(mdev, wqe_inline_mode) == MLX5E_INLINE_MODE_VPORT_CONTEXT ? + MLX5_CAP_ETH(mdev, wqe_inline_mode) == MLX5_CAP_INLINE_MODE_VPORT_CONTEXT ? param->min_inline_mode : 0; err = mlx5e_alloc_sq_db(sq, cpu_to_node(c->cpu)); @@ -3417,14 +3417,13 @@ static void mlx5e_query_min_inline(struct mlx5_core_dev *mdev, u8 *min_inline_mode) { switch (MLX5_CAP_ETH(mdev, wqe_inline_mode)) { - case MLX5E_INLINE_MODE_L2: + case MLX5_CAP_INLINE_MODE_L2: *min_inline_mode = MLX5_INLINE_MODE_L2; break; - case MLX5E_INLINE_MODE_VPORT_CONTEXT: - mlx5_query_nic_vport_min_inline(mdev, - min_inline_mode); + case MLX5_CAP_INLINE_MODE_VPORT_CONTEXT: + mlx5_query_nic_vport_min_inline(mdev, 0, min_inline_mode); break; - case MLX5_INLINE_MODE_NOT_REQUIRED: + case MLX5_CAP_INLINE_MODE_NOT_REQUIRED: *min_inline_mode = MLX5_INLINE_MODE_NONE; break; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 525f17a..269e440 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -113,15 +113,17 @@ static int mlx5_modify_nic_vport_context(struct mlx5_core_dev *mdev, void *in, return mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out)); } -void mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev, - u8 *min_inline_mode) +int mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev, + u16 vport, u8 *min_inline) { u32 out[MLX5_ST_SZ_DW(query_nic_vport_context_out)] = {0}; + int err; - mlx5_query_nic_vport_context(mdev, 0, out, sizeof(out)); - - *min_inline_mode = MLX5_GET(query_nic_vport_context_out, out, - nic_vport_context.min_wqe_inline_mode); + err = mlx5_query_nic_vport_context(mdev, vport, out, sizeof(out)); + if (!err) + *min_inline = MLX5_GET(query_nic_vport_context_out, out, + nic_vport_context.min_wqe_inline_mode); + return err; } EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_min_inline); diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index 451b0bd..ec35157 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -36,6 +36,12 @@ #include #include +enum { + MLX5_CAP_INLINE_MODE_L2, + MLX5_CAP_INLINE_MODE_VPORT_CONTEXT, + MLX5_CAP_INLINE_MODE_NOT_REQUIRED, +}; + u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport); u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport); @@ -43,8 +49,8 @@ int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport, u8 state); int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u16 vport, u8 *addr); -void mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev, - u8 *min_inline); +int mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev, + u16 vport, u8 *min_inline); int mlx5_modify_nic_vport_min_inline(struct mlx5_core_dev *mdev, u16 vport, u8 min_inline); int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *dev, -- cgit v1.1 From bffaa916588ebb065cfa0287195d9ad35612eecf Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Tue, 22 Nov 2016 23:09:59 +0200 Subject: net/mlx5: E-Switch, Add control for inline mode Implement devlink show and set of HW inline-mode. The supported modes: none, link, network, transport. We currently support one mode for all vports so set is done on all vports. When eswitch is first initialized the inline-mode is queried from the FW. Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 4 + .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 141 +++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 + 4 files changed, 148 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 9734ac8..d6807c3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1798,6 +1798,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) esw->total_vports = total_vports; esw->enabled_vports = 0; esw->mode = SRIOV_NONE; + esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE; dev->priv.eswitch = esw; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 40482e8..cf1aa56 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -200,6 +200,7 @@ struct mlx5_esw_offload { struct mlx5_flow_group *vport_rx_group; struct mlx5_eswitch_rep *vport_reps; DECLARE_HASHTABLE(encap_tbl, 8); + u8 inline_mode; }; struct mlx5_eswitch { @@ -309,6 +310,9 @@ void mlx5_eswitch_sqs2vport_stop(struct mlx5_eswitch *esw, int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode); int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode); +int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode); +int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode); +int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode); void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw, int vport_index, struct mlx5_eswitch_rep *rep); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 731f286..5c01550 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -657,6 +657,14 @@ static int esw_offloads_start(struct mlx5_eswitch *esw) if (err1) esw_warn(esw->dev, "Failed setting eswitch back to legacy, err %d\n", err); } + if (esw->offloads.inline_mode == MLX5_INLINE_MODE_NONE) { + if (mlx5_eswitch_inline_mode_get(esw, + num_vfs, + &esw->offloads.inline_mode)) { + esw->offloads.inline_mode = MLX5_INLINE_MODE_L2; + esw_warn(esw->dev, "Inline mode is different between vports\n"); + } + } return err; } @@ -771,6 +779,50 @@ static int esw_mode_to_devlink(u16 mlx5_mode, u16 *mode) return 0; } +static int esw_inline_mode_from_devlink(u8 mode, u8 *mlx5_mode) +{ + switch (mode) { + case DEVLINK_ESWITCH_INLINE_MODE_NONE: + *mlx5_mode = MLX5_INLINE_MODE_NONE; + break; + case DEVLINK_ESWITCH_INLINE_MODE_LINK: + *mlx5_mode = MLX5_INLINE_MODE_L2; + break; + case DEVLINK_ESWITCH_INLINE_MODE_NETWORK: + *mlx5_mode = MLX5_INLINE_MODE_IP; + break; + case DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: + *mlx5_mode = MLX5_INLINE_MODE_TCP_UDP; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int esw_inline_mode_to_devlink(u8 mlx5_mode, u8 *mode) +{ + switch (mlx5_mode) { + case MLX5_INLINE_MODE_NONE: + *mode = DEVLINK_ESWITCH_INLINE_MODE_NONE; + break; + case MLX5_INLINE_MODE_L2: + *mode = DEVLINK_ESWITCH_INLINE_MODE_LINK; + break; + case MLX5_INLINE_MODE_IP: + *mode = DEVLINK_ESWITCH_INLINE_MODE_NETWORK; + break; + case MLX5_INLINE_MODE_TCP_UDP: + *mode = DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT; + break; + default: + return -EINVAL; + } + + return 0; +} + int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode) { struct mlx5_core_dev *dev; @@ -815,6 +867,95 @@ int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) return esw_mode_to_devlink(dev->priv.eswitch->mode, mode); } +int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_eswitch *esw = dev->priv.eswitch; + int num_vports = esw->enabled_vports; + int err; + int vport; + u8 mlx5_mode; + + if (!MLX5_CAP_GEN(dev, vport_group_manager)) + return -EOPNOTSUPP; + + if (esw->mode == SRIOV_NONE) + return -EOPNOTSUPP; + + if (MLX5_CAP_ETH(dev, wqe_inline_mode) != + MLX5_CAP_INLINE_MODE_VPORT_CONTEXT) + return -EOPNOTSUPP; + + err = esw_inline_mode_from_devlink(mode, &mlx5_mode); + if (err) + goto out; + + for (vport = 1; vport < num_vports; vport++) { + err = mlx5_modify_nic_vport_min_inline(dev, vport, mlx5_mode); + if (err) { + esw_warn(dev, "Failed to set min inline on vport %d\n", + vport); + goto revert_inline_mode; + } + } + + esw->offloads.inline_mode = mlx5_mode; + return 0; + +revert_inline_mode: + while (--vport > 0) + mlx5_modify_nic_vport_min_inline(dev, + vport, + esw->offloads.inline_mode); +out: + return err; +} + +int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_eswitch *esw = dev->priv.eswitch; + + if (!MLX5_CAP_GEN(dev, vport_group_manager)) + return -EOPNOTSUPP; + + if (esw->mode == SRIOV_NONE) + return -EOPNOTSUPP; + + if (MLX5_CAP_ETH(dev, wqe_inline_mode) != + MLX5_CAP_INLINE_MODE_VPORT_CONTEXT) + return -EOPNOTSUPP; + + return esw_inline_mode_to_devlink(esw->offloads.inline_mode, mode); +} + +int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode) +{ + struct mlx5_core_dev *dev = esw->dev; + int vport; + u8 prev_mlx5_mode, mlx5_mode = MLX5_INLINE_MODE_L2; + + if (!MLX5_CAP_GEN(dev, vport_group_manager)) + return -EOPNOTSUPP; + + if (esw->mode == SRIOV_NONE) + return -EOPNOTSUPP; + + if (MLX5_CAP_ETH(dev, wqe_inline_mode) != + MLX5_CAP_INLINE_MODE_VPORT_CONTEXT) + return -EOPNOTSUPP; + + for (vport = 1; vport <= nvfs; vport++) { + mlx5_query_nic_vport_min_inline(dev, vport, &mlx5_mode); + if (vport > 1 && prev_mlx5_mode != mlx5_mode) + return -EINVAL; + prev_mlx5_mode = mlx5_mode; + } + + *mode = mlx5_mode; + return 0; +} + void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw, int vport_index, struct mlx5_eswitch_rep *__rep) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index f28df33..b440a16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1239,6 +1239,8 @@ static const struct devlink_ops mlx5_devlink_ops = { #ifdef CONFIG_MLX5_CORE_EN .eswitch_mode_set = mlx5_devlink_eswitch_mode_set, .eswitch_mode_get = mlx5_devlink_eswitch_mode_get, + .eswitch_inline_mode_set = mlx5_devlink_eswitch_inline_mode_set, + .eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get, #endif }; -- cgit v1.1 From de0af0bf64fc11fab253f63424bd3aba12e5f614 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Tue, 22 Nov 2016 23:10:00 +0200 Subject: net/mlx5e: Enforce min inline mode when offloading flows A flow should be offloaded only if the matches are allowed according to min inline mode. Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 46 +++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 4b99112..4d06fab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -279,8 +279,10 @@ static int parse_tunnel_attr(struct mlx5e_priv *priv, return 0; } -static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec, - struct tc_cls_flower_offload *f) +static int __parse_cls_flower(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + struct tc_cls_flower_offload *f, + u8 *min_inline) { void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, outer_headers); @@ -289,6 +291,8 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec u16 addr_type = 0; u8 ip_proto = 0; + *min_inline = MLX5_INLINE_MODE_L2; + if (f->dissector->used_keys & ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | BIT(FLOW_DISSECTOR_KEY_BASIC) | @@ -362,6 +366,9 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec mask->ip_proto); MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, key->ip_proto); + + if (mask->ip_proto) + *min_inline = MLX5_INLINE_MODE_IP; } if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { @@ -432,6 +439,9 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, dst_ipv4_dst_ipv6.ipv4_layout.ipv4), &key->dst, sizeof(key->dst)); + + if (mask->src || mask->dst) + *min_inline = MLX5_INLINE_MODE_IP; } if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { @@ -457,6 +467,10 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, dst_ipv4_dst_ipv6.ipv6_layout.ipv6), &key->dst, sizeof(key->dst)); + + if (ipv6_addr_type(&mask->src) != IPV6_ADDR_ANY || + ipv6_addr_type(&mask->dst) != IPV6_ADDR_ANY) + *min_inline = MLX5_INLINE_MODE_IP; } if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) { @@ -497,11 +511,39 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec "Only UDP and TCP transport are supported\n"); return -EINVAL; } + + if (mask->src || mask->dst) + *min_inline = MLX5_INLINE_MODE_TCP_UDP; } return 0; } +static int parse_cls_flower(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + struct tc_cls_flower_offload *f) +{ + struct mlx5_core_dev *dev = priv->mdev; + struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_eswitch_rep *rep = priv->ppriv; + u8 min_inline; + int err; + + err = __parse_cls_flower(priv, spec, f, &min_inline); + + if (!err && esw->mode == SRIOV_OFFLOADS && + rep->vport != FDB_UPLINK_VPORT) { + if (min_inline > esw->offloads.inline_mode) { + netdev_warn(priv->netdev, + "Flow is not offloaded due to min inline setting, required %d actual %d\n", + min_inline, esw->offloads.inline_mode); + return -EOPNOTSUPP; + } + } + + return err; +} + static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, u32 *action, u32 *flow_tag) { -- cgit v1.1 From 4b65246b427152fc7e8e1f673d3f0dc7582a89af Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 22 Nov 2016 13:55:31 -0800 Subject: ethtool: Protect {get, set}_phy_tunable with PHY device mutex PHY drivers should be able to rely on the caller of {get,set}_tunable to have acquired the PHY device mutex, in order to both serialize against concurrent calls of these functions, but also against PHY state machine changes. All ethtool PHY-level functions do this, except {get,set}_tunable, so we make them consistent here as well. We need to update the Microsemi PHY driver in the same commit to avoid introducing either deadlocks, or lack of proper locking. Fixes: 968ad9da7e0e ("ethtool: Implements ETHTOOL_PHY_GTUNABLE/ETHTOOL_PHY_STUNABLE") Fixes: 310d9ad57ae0 ("net: phy: Add downshift get/set support in Microsemi PHYs driver") Signed-off-by: Florian Fainelli Reviewed-by: Andrew Lunn Reviewed-by: Allan W. Nielsen Signed-off-by: David S. Miller --- drivers/net/phy/mscc.c | 16 +++++----------- net/core/ethtool.c | 4 ++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index 92018ba..7a3740c 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -115,10 +115,9 @@ static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count) int rc; u16 reg_val; - mutex_lock(&phydev->lock); rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED); if (rc != 0) - goto out_unlock; + goto out; reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); reg_val &= DOWNSHIFT_CNTL_MASK; @@ -128,9 +127,7 @@ static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count) *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2; rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD); -out_unlock: - mutex_unlock(&phydev->lock); - +out: return rc; } @@ -150,23 +147,20 @@ static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count) count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN); } - mutex_lock(&phydev->lock); rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED); if (rc != 0) - goto out_unlock; + goto out; reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); reg_val &= ~(DOWNSHIFT_CNTL_MASK); reg_val |= count; rc = phy_write(phydev, MSCC_PHY_ACTIPHY_CNTL, reg_val); if (rc != 0) - goto out_unlock; + goto out; rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD); -out_unlock: - mutex_unlock(&phydev->lock); - +out: return rc; } diff --git a/net/core/ethtool.c b/net/core/ethtool.c index e9b45567..0adb3be 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -2466,7 +2466,9 @@ static int get_phy_tunable(struct net_device *dev, void __user *useraddr) data = kmalloc(tuna.len, GFP_USER); if (!data) return -ENOMEM; + mutex_lock(&phydev->lock); ret = phydev->drv->get_tunable(phydev, &tuna, data); + mutex_unlock(&phydev->lock); if (ret) goto out; useraddr += sizeof(tuna); @@ -2501,7 +2503,9 @@ static int set_phy_tunable(struct net_device *dev, void __user *useraddr) ret = -EFAULT; if (copy_from_user(data, useraddr, tuna.len)) goto out; + mutex_lock(&phydev->lock); ret = phydev->drv->set_tunable(phydev, &tuna, data); + mutex_unlock(&phydev->lock); out: kfree(data); -- cgit v1.1 From e3f42f8453902d1520dcdc9909731f274c6d52c1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Nov 2016 15:56:10 -0800 Subject: mlx4: reorganize struct mlx4_en_tx_ring Goal is to reorganize this critical structure to increase performance. ndo_start_xmit() should only dirty one cache line, and access as few cache lines as possible. Add sp_ (Slow Path) prefix to fields that are not used in fast path, to make clear what is going on. After this patch pahole reports something much better, as all ndo_start_xmit() needed fields are packed into two cache lines instead of seven or eight struct mlx4_en_tx_ring { u32 last_nr_txbb; /* 0 0x4 */ u32 cons; /* 0x4 0x4 */ long unsigned int wake_queue; /* 0x8 0x8 */ struct netdev_queue * tx_queue; /* 0x10 0x8 */ u32 (*free_tx_desc)(struct mlx4_en_priv *, struct mlx4_en_tx_ring *, int, u8, u64, int); /* 0x18 0x8 */ struct mlx4_en_rx_ring * recycle_ring; /* 0x20 0x8 */ /* XXX 24 bytes hole, try to pack */ /* --- cacheline 1 boundary (64 bytes) --- */ u32 prod; /* 0x40 0x4 */ unsigned int tx_dropped; /* 0x44 0x4 */ long unsigned int bytes; /* 0x48 0x8 */ long unsigned int packets; /* 0x50 0x8 */ long unsigned int tx_csum; /* 0x58 0x8 */ long unsigned int tso_packets; /* 0x60 0x8 */ long unsigned int xmit_more; /* 0x68 0x8 */ struct mlx4_bf bf; /* 0x70 0x18 */ /* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */ __be32 doorbell_qpn; /* 0x88 0x4 */ __be32 mr_key; /* 0x8c 0x4 */ u32 size; /* 0x90 0x4 */ u32 size_mask; /* 0x94 0x4 */ u32 full_size; /* 0x98 0x4 */ u32 buf_size; /* 0x9c 0x4 */ void * buf; /* 0xa0 0x8 */ struct mlx4_en_tx_info * tx_info; /* 0xa8 0x8 */ int qpn; /* 0xb0 0x4 */ u8 queue_index; /* 0xb4 0x1 */ bool bf_enabled; /* 0xb5 0x1 */ bool bf_alloced; /* 0xb6 0x1 */ u8 hwtstamp_tx_type; /* 0xb7 0x1 */ u8 * bounce_buf; /* 0xb8 0x8 */ /* --- cacheline 3 boundary (192 bytes) --- */ long unsigned int queue_stopped; /* 0xc0 0x8 */ struct mlx4_hwq_resources sp_wqres; /* 0xc8 0x58 */ /* --- cacheline 4 boundary (256 bytes) was 32 bytes ago --- */ struct mlx4_qp sp_qp; /* 0x120 0x30 */ /* --- cacheline 5 boundary (320 bytes) was 16 bytes ago --- */ struct mlx4_qp_context sp_context; /* 0x150 0xf8 */ /* --- cacheline 9 boundary (576 bytes) was 8 bytes ago --- */ cpumask_t sp_affinity_mask; /* 0x248 0x20 */ enum mlx4_qp_state sp_qp_state; /* 0x268 0x4 */ u16 sp_stride; /* 0x26c 0x2 */ u16 sp_cqn; /* 0x26e 0x2 */ /* size: 640, cachelines: 10, members: 36 */ /* sum members: 600, holes: 1, sum holes: 24 */ /* padding: 16 */ }; Instead of this silly placement : struct mlx4_en_tx_ring { u32 last_nr_txbb; /* 0 0x4 */ u32 cons; /* 0x4 0x4 */ long unsigned int wake_queue; /* 0x8 0x8 */ /* XXX 48 bytes hole, try to pack */ /* --- cacheline 1 boundary (64 bytes) --- */ u32 prod; /* 0x40 0x4 */ /* XXX 4 bytes hole, try to pack */ long unsigned int bytes; /* 0x48 0x8 */ long unsigned int packets; /* 0x50 0x8 */ long unsigned int tx_csum; /* 0x58 0x8 */ long unsigned int tso_packets; /* 0x60 0x8 */ long unsigned int xmit_more; /* 0x68 0x8 */ unsigned int tx_dropped; /* 0x70 0x4 */ /* XXX 4 bytes hole, try to pack */ struct mlx4_bf bf; /* 0x78 0x18 */ /* --- cacheline 2 boundary (128 bytes) was 16 bytes ago --- */ long unsigned int queue_stopped; /* 0x90 0x8 */ cpumask_t affinity_mask; /* 0x98 0x10 */ struct mlx4_qp qp; /* 0xa8 0x30 */ /* --- cacheline 3 boundary (192 bytes) was 24 bytes ago --- */ struct mlx4_hwq_resources wqres; /* 0xd8 0x58 */ /* --- cacheline 4 boundary (256 bytes) was 48 bytes ago --- */ u32 size; /* 0x130 0x4 */ u32 size_mask; /* 0x134 0x4 */ u16 stride; /* 0x138 0x2 */ /* XXX 2 bytes hole, try to pack */ u32 full_size; /* 0x13c 0x4 */ /* --- cacheline 5 boundary (320 bytes) --- */ u16 cqn; /* 0x140 0x2 */ /* XXX 2 bytes hole, try to pack */ u32 buf_size; /* 0x144 0x4 */ __be32 doorbell_qpn; /* 0x148 0x4 */ __be32 mr_key; /* 0x14c 0x4 */ void * buf; /* 0x150 0x8 */ struct mlx4_en_tx_info * tx_info; /* 0x158 0x8 */ struct mlx4_en_rx_ring * recycle_ring; /* 0x160 0x8 */ u32 (*free_tx_desc)(struct mlx4_en_priv *, struct mlx4_en_tx_ring *, int, u8, u64, int); /* 0x168 0x8 */ u8 * bounce_buf; /* 0x170 0x8 */ struct mlx4_qp_context context; /* 0x178 0xf8 */ /* --- cacheline 9 boundary (576 bytes) was 48 bytes ago --- */ int qpn; /* 0x270 0x4 */ enum mlx4_qp_state qp_state; /* 0x274 0x4 */ u8 queue_index; /* 0x278 0x1 */ bool bf_enabled; /* 0x279 0x1 */ bool bf_alloced; /* 0x27a 0x1 */ /* XXX 5 bytes hole, try to pack */ /* --- cacheline 10 boundary (640 bytes) --- */ struct netdev_queue * tx_queue; /* 0x280 0x8 */ int hwtstamp_tx_type; /* 0x288 0x4 */ /* size: 704, cachelines: 11, members: 36 */ /* sum members: 587, holes: 6, sum holes: 65 */ /* padding: 52 */ }; Signed-off-by: Eric Dumazet Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 48 +++++++++++++------------- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 42 ++++++++++++---------- 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 9a807e9..9018bb1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1305,7 +1305,7 @@ static void mlx4_en_tx_timeout(struct net_device *dev) if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i))) continue; en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n", - i, tx_ring->qpn, tx_ring->cqn, + i, tx_ring->qpn, tx_ring->sp_cqn, tx_ring->cons, tx_ring->prod); } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 5de3cbe..4b597dc 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -66,7 +66,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, ring->size = size; ring->size_mask = size - 1; - ring->stride = stride; + ring->sp_stride = stride; ring->full_size = ring->size - HEADROOM - MAX_DESC_TXBBS; tmp = size * sizeof(struct mlx4_en_tx_info); @@ -90,22 +90,22 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, goto err_info; } } - ring->buf_size = ALIGN(size * ring->stride, MLX4_EN_PAGE_SIZE); + ring->buf_size = ALIGN(size * ring->sp_stride, MLX4_EN_PAGE_SIZE); /* Allocate HW buffers on provided NUMA node */ set_dev_node(&mdev->dev->persist->pdev->dev, node); - err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); + err = mlx4_alloc_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size); set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); if (err) { en_err(priv, "Failed allocating hwq resources\n"); goto err_bounce; } - ring->buf = ring->wqres.buf.direct.buf; + ring->buf = ring->sp_wqres.buf.direct.buf; en_dbg(DRV, priv, "Allocated TX ring (addr:%p) - buf:%p size:%d buf_size:%d dma:%llx\n", ring, ring->buf, ring->size, ring->buf_size, - (unsigned long long) ring->wqres.buf.direct.map); + (unsigned long long) ring->sp_wqres.buf.direct.map); err = mlx4_qp_reserve_range(mdev->dev, 1, 1, &ring->qpn, MLX4_RESERVE_ETH_BF_QP); @@ -114,12 +114,12 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, goto err_hwq_res; } - err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->qp, GFP_KERNEL); + err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->sp_qp, GFP_KERNEL); if (err) { en_err(priv, "Failed allocating qp %d\n", ring->qpn); goto err_reserve; } - ring->qp.event = mlx4_en_sqp_event; + ring->sp_qp.event = mlx4_en_sqp_event; err = mlx4_bf_alloc(mdev->dev, &ring->bf, node); if (err) { @@ -141,7 +141,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, if (queue_index < priv->num_tx_rings_p_up) cpumask_set_cpu(cpumask_local_spread(queue_index, priv->mdev->dev->numa_node), - &ring->affinity_mask); + &ring->sp_affinity_mask); *pring = ring; return 0; @@ -149,7 +149,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, err_reserve: mlx4_qp_release_range(mdev->dev, ring->qpn, 1); err_hwq_res: - mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); + mlx4_free_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size); err_bounce: kfree(ring->bounce_buf); ring->bounce_buf = NULL; @@ -171,10 +171,10 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, if (ring->bf_alloced) mlx4_bf_free(mdev->dev, &ring->bf); - mlx4_qp_remove(mdev->dev, &ring->qp); - mlx4_qp_free(mdev->dev, &ring->qp); + mlx4_qp_remove(mdev->dev, &ring->sp_qp); + mlx4_qp_free(mdev->dev, &ring->sp_qp); mlx4_qp_release_range(priv->mdev->dev, ring->qpn, 1); - mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); + mlx4_free_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size); kfree(ring->bounce_buf); ring->bounce_buf = NULL; kvfree(ring->tx_info); @@ -190,7 +190,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_dev *mdev = priv->mdev; int err; - ring->cqn = cq; + ring->sp_cqn = cq; ring->prod = 0; ring->cons = 0xffffffff; ring->last_nr_txbb = 1; @@ -198,21 +198,21 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, memset(ring->buf, 0, ring->buf_size); ring->free_tx_desc = mlx4_en_free_tx_desc; - ring->qp_state = MLX4_QP_STATE_RST; - ring->doorbell_qpn = cpu_to_be32(ring->qp.qpn << 8); + ring->sp_qp_state = MLX4_QP_STATE_RST; + ring->doorbell_qpn = cpu_to_be32(ring->sp_qp.qpn << 8); ring->mr_key = cpu_to_be32(mdev->mr.key); - mlx4_en_fill_qp_context(priv, ring->size, ring->stride, 1, 0, ring->qpn, - ring->cqn, user_prio, &ring->context); + mlx4_en_fill_qp_context(priv, ring->size, ring->sp_stride, 1, 0, ring->qpn, + ring->sp_cqn, user_prio, &ring->sp_context); if (ring->bf_alloced) - ring->context.usr_page = + ring->sp_context.usr_page = cpu_to_be32(mlx4_to_hw_uar_index(mdev->dev, ring->bf.uar->index)); - err = mlx4_qp_to_ready(mdev->dev, &ring->wqres.mtt, &ring->context, - &ring->qp, &ring->qp_state); - if (!cpumask_empty(&ring->affinity_mask)) - netif_set_xps_queue(priv->dev, &ring->affinity_mask, + err = mlx4_qp_to_ready(mdev->dev, &ring->sp_wqres.mtt, &ring->sp_context, + &ring->sp_qp, &ring->sp_qp_state); + if (!cpumask_empty(&ring->sp_affinity_mask)) + netif_set_xps_queue(priv->dev, &ring->sp_affinity_mask, ring->queue_index); return err; @@ -223,8 +223,8 @@ void mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv, { struct mlx4_en_dev *mdev = priv->mdev; - mlx4_qp_modify(mdev->dev, NULL, ring->qp_state, - MLX4_QP_STATE_RST, NULL, 0, 0, &ring->qp); + mlx4_qp_modify(mdev->dev, NULL, ring->sp_qp_state, + MLX4_QP_STATE_RST, NULL, 0, 0, &ring->sp_qp); } static inline bool mlx4_en_is_tx_ring_full(struct mlx4_en_tx_ring *ring) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index eff2165..574bcbb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -281,46 +281,50 @@ struct mlx4_en_tx_ring { u32 last_nr_txbb; u32 cons; unsigned long wake_queue; + struct netdev_queue *tx_queue; + u32 (*free_tx_desc)(struct mlx4_en_priv *priv, + struct mlx4_en_tx_ring *ring, + int index, u8 owner, + u64 timestamp, int napi_mode); + struct mlx4_en_rx_ring *recycle_ring; /* cache line used and dirtied in mlx4_en_xmit() */ u32 prod ____cacheline_aligned_in_smp; + unsigned int tx_dropped; unsigned long bytes; unsigned long packets; unsigned long tx_csum; unsigned long tso_packets; unsigned long xmit_more; - unsigned int tx_dropped; struct mlx4_bf bf; - unsigned long queue_stopped; /* Following part should be mostly read */ - cpumask_t affinity_mask; - struct mlx4_qp qp; - struct mlx4_hwq_resources wqres; + __be32 doorbell_qpn; + __be32 mr_key; u32 size; /* number of TXBBs */ u32 size_mask; - u16 stride; u32 full_size; - u16 cqn; /* index of port CQ associated with this ring */ u32 buf_size; - __be32 doorbell_qpn; - __be32 mr_key; void *buf; struct mlx4_en_tx_info *tx_info; - struct mlx4_en_rx_ring *recycle_ring; - u32 (*free_tx_desc)(struct mlx4_en_priv *priv, - struct mlx4_en_tx_ring *ring, - int index, u8 owner, - u64 timestamp, int napi_mode); - u8 *bounce_buf; - struct mlx4_qp_context context; int qpn; - enum mlx4_qp_state qp_state; u8 queue_index; bool bf_enabled; bool bf_alloced; - struct netdev_queue *tx_queue; - int hwtstamp_tx_type; + u8 hwtstamp_tx_type; + u8 *bounce_buf; + + /* Not used in fast path + * Only queue_stopped might be used if BQL is not properly working. + */ + unsigned long queue_stopped; + struct mlx4_hwq_resources sp_wqres; + struct mlx4_qp sp_qp; + struct mlx4_qp_context sp_context; + cpumask_t sp_affinity_mask; + enum mlx4_qp_state sp_qp_state; + u16 sp_stride; + u16 sp_cqn; /* index of port CQ associated with this ring */ } ____cacheline_aligned_in_smp; struct mlx4_en_rx_desc { -- cgit v1.1 From d2b024d32d6e6f704633a8a474bd883cf9e25254 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 22 Nov 2016 16:52:08 -0800 Subject: samples/bpf: fix sockex2 example since llvm commit "Do not expand UNDEF SDNode during insn selection lowering" llvm will generate code that uses uninitialized registers for cases where C code is actually uses uninitialized data. So this sockex2 example is technically broken. Fix it by initializing on the stack variable fully. Also increase verifier buffer limit, since verifier output may not fit in 64k for this sockex2 code depending on llvm version. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/libbpf.h | 2 +- samples/bpf/sockex2_kern.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index ac6edb6..de96a93 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h @@ -18,7 +18,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type, int bpf_obj_pin(int fd, const char *pathname); int bpf_obj_get(const char *pathname); -#define LOG_BUF_SIZE 65536 +#define LOG_BUF_SIZE (256 * 1024) extern char bpf_log_buf[LOG_BUF_SIZE]; /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ diff --git a/samples/bpf/sockex2_kern.c b/samples/bpf/sockex2_kern.c index 44e5846..f58acfc 100644 --- a/samples/bpf/sockex2_kern.c +++ b/samples/bpf/sockex2_kern.c @@ -198,7 +198,7 @@ struct bpf_map_def SEC("maps") hash_map = { SEC("socket2") int bpf_prog2(struct __sk_buff *skb) { - struct bpf_flow_keys flow; + struct bpf_flow_keys flow = {}; struct pair *value; u32 key; -- cgit v1.1 From db6a71dd9a75fb07f6758582299acfe1ab5827dc Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 22 Nov 2016 16:52:09 -0800 Subject: samples/bpf: fix bpf loader llvm can emit relocations into sections other than program code (like debug info sections). Ignore them during parsing of elf file Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/bpf_load.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index 97913e1..62f54d6 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -317,6 +317,10 @@ int load_bpf_file(char *path) &shdr_prog, &data_prog)) continue; + if (shdr_prog.sh_type != SHT_PROGBITS || + !(shdr_prog.sh_flags & SHF_EXECINSTR)) + continue; + insns = (struct bpf_insn *) data_prog->d_buf; processed_sec[shdr.sh_info] = true; -- cgit v1.1 From 436accebb53021ef7c63535f60bda410aa87c136 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 23 Nov 2016 10:26:49 +0800 Subject: tuntap: remove unnecessary sk_receive_queue length check during xmit After commit 1576d9860599 ("tun: switch to use skb array for tx"), sk_receive_queue was not used any more. So remove the uncessary sk_receive_queue length check during xmit. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 64e694c..e2af2dd 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -878,13 +878,6 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) sk_filter(tfile->socket.sk, skb)) goto drop; - /* Limit the number of packets queued by dividing txq length with the - * number of queues. - */ - if (skb_queue_len(&tfile->socket.sk->sk_receive_queue) * numqueues - >= dev->tx_queue_len) - goto drop; - if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) goto drop; -- cgit v1.1 From e6d6428449c5d2aa077e69630d7abf041cf9ba99 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Fri, 18 Nov 2016 16:07:09 +0530 Subject: rsi: Add support to filter rx frames Filtering rx frames after connection in station mode avoids the overhead of processing un-necessary frames. Hence rx filter frame is added which can be configured to device at suitable times. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 16 +++++++++++++- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 33 +++++++++++++++++++++++++++++ drivers/net/wireless/rsi/rsi_mgmt.h | 11 ++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index aaca258..6f065d3 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -275,6 +275,8 @@ static int rsi_mac80211_start(struct ieee80211_hw *hw) common->iface_down = false; mutex_unlock(&common->mutex); + rsi_send_rx_filter_frame(common, 0); + return 0; } @@ -471,11 +473,19 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, { struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; + u16 rx_filter_word = 0; mutex_lock(&common->mutex); if (changed & BSS_CHANGED_ASSOC) { rsi_dbg(INFO_ZONE, "%s: Changed Association status: %d\n", __func__, bss_conf->assoc); + if (bss_conf->assoc) { + /* Send the RX filter frame */ + rx_filter_word = (ALLOW_DATA_ASSOC_PEER | + ALLOW_CTRL_ASSOC_PEER | + ALLOW_MGMT_ASSOC_PEER); + rsi_send_rx_filter_frame(common, rx_filter_word); + } rsi_inform_bss_status(common, bss_conf->assoc, bss_conf->bssid, @@ -1013,6 +1023,7 @@ static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw, struct rsi_common *common = adapter->priv; mutex_lock(&common->mutex); + /* Resetting all the fields to default values */ common->bitrate_mask[NL80211_BAND_2GHZ] = 0; common->bitrate_mask[NL80211_BAND_5GHZ] = 0; @@ -1022,8 +1033,11 @@ static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw, common->vif_info[0].seq_start = 0; common->secinfo.ptk_cipher = 0; common->secinfo.gtk_cipher = 0; - mutex_unlock(&common->mutex); + rsi_send_rx_filter_frame(common, 0); + + mutex_unlock(&common->mutex); + return 0; } diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 30b9d44..631cdb1 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1243,6 +1243,39 @@ int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event) } +/** + * rsi_send_rx_filter_frame() - Sends a frame to filter the RX packets + * + * @common: Pointer to the driver private structure. + * @rx_filter_word: Flags of filter packets + * + * @Return: 0 on success, -1 on failure. + */ +int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word) +{ + struct rsi_mac_frame *cmd_frame; + struct sk_buff *skb; + + rsi_dbg(MGMT_TX_ZONE, "Sending RX filter frame\n"); + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + cmd_frame = (struct rsi_mac_frame *)skb->data; + + cmd_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + cmd_frame->desc_word[1] = cpu_to_le16(SET_RX_FILTER); + cmd_frame->desc_word[4] = cpu_to_le16(rx_filter_word); + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} /** * rsi_handle_ta_confirm_type() - This function handles the confirm frames. diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index d155358..2cf6542 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -140,6 +140,16 @@ #define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\ FIF_BCN_PRBRESP_PROMISC) + +/* Rx filter word definitions */ +#define PROMISCOUS_MODE BIT(0) +#define ALLOW_DATA_ASSOC_PEER BIT(1) +#define ALLOW_MGMT_ASSOC_PEER BIT(2) +#define ALLOW_CTRL_ASSOC_PEER BIT(3) +#define DISALLOW_BEACONS BIT(4) +#define ALLOW_CONN_PEER_MGMT_WHILE_BUF_FULL BIT(5) +#define DISALLOW_BROADCAST_DATA BIT(6) + enum opmode { STA_OPMODE = 1, AP_OPMODE = 2 @@ -313,4 +323,5 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb); int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb); int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb); int rsi_band_check(struct rsi_common *common); +int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word); #endif -- cgit v1.1 From 8b36de8cf5abc114594cf3bbd3a1b6701bf1e1a9 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Fri, 18 Nov 2016 16:08:04 +0530 Subject: rsi: Add support for configuring tx power TX power can be configured from iwconfig, iw or from mac80211 when regulatory changes are done. Hence support for configuring tx power to device is added using the RADIO_PARAMS_UPDATE command frame. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 34 ++++++++++++++++++++++++++ drivers/net/wireless/rsi/rsi_91x_mgmt.c | 37 +++++++++++++++++++++++++++++ drivers/net/wireless/rsi/rsi_main.h | 2 ++ drivers/net/wireless/rsi/rsi_mgmt.h | 4 +++- 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 6f065d3..f63d6bb 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -411,6 +411,34 @@ static int rsi_channel_change(struct ieee80211_hw *hw) } /** + * rsi_config_power() - This function configures tx power to device + * @hw: Pointer to the ieee80211_hw structure. + * + * Return: 0 on success, negative error code on failure. + */ +static int rsi_config_power(struct ieee80211_hw *hw) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + struct ieee80211_conf *conf = &hw->conf; + + if (adapter->sc_nvifs <= 0) { + rsi_dbg(ERR_ZONE, "%s: No virtual interface found\n", __func__); + return -EINVAL; + } + + rsi_dbg(INFO_ZONE, + "%s: Set tx power: %d dBM\n", __func__, conf->power_level); + + if (conf->power_level == common->tx_power) + return 0; + + common->tx_power = conf->power_level; + + return rsi_send_radio_params_update(common); +} + +/** * rsi_mac80211_config() - This function is a handler for configuration * requests. The stack calls this function to * change hardware configuration, e.g., channel. @@ -431,6 +459,12 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, if (changed & IEEE80211_CONF_CHANGE_CHANNEL) status = rsi_channel_change(hw); + /* tx power */ + if (changed & IEEE80211_CONF_CHANGE_POWER) { + rsi_dbg(INFO_ZONE, "%s: Configuring Power\n", __func__); + status = rsi_config_power(hw); + } + mutex_unlock(&common->mutex); return status; diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 631cdb1..99c2560 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -953,6 +953,43 @@ int rsi_set_channel(struct rsi_common *common, u16 channel) } /** + * rsi_send_radio_params_update() - This function sends the radio + * parameters update to device + * @common: Pointer to the driver private structure. + * @channel: Channel value to be set. + * + * Return: 0 on success, corresponding error code on failure. + */ +int rsi_send_radio_params_update(struct rsi_common *common) +{ + struct rsi_mac_frame *cmd_frame; + struct sk_buff *skb = NULL; + + rsi_dbg(MGMT_TX_ZONE, + "%s: Sending Radio Params update frame\n", __func__); + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + cmd_frame = (struct rsi_mac_frame *)skb->data; + + cmd_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + cmd_frame->desc_word[1] = cpu_to_le16(RADIO_PARAMS_UPDATE); + cmd_frame->desc_word[3] = cpu_to_le16(BIT(0)); + + cmd_frame->desc_word[3] |= cpu_to_le16(common->tx_power << 8); + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** * rsi_compare() - This function is used to compare two integers * @a: pointer to the first integer * @b: pointer to the second integer diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index dcd0957..3938f13 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -204,6 +204,8 @@ struct rsi_common { struct cqm_info cqm_info; bool hw_data_qs_blocked; + + int tx_power; }; struct rsi_hw { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 2cf6542..72f7cfb 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -200,7 +200,8 @@ enum cmd_frame_type { BG_SCAN_PARAMS, BG_SCAN_PROBE_REQ, CW_MODE_REQ, - PER_CMD_PKT + PER_CMD_PKT, + RADIO_PARAMS_UPDATE = 0x29 }; struct rsi_mac_frame { @@ -324,4 +325,5 @@ int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb); int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb); int rsi_band_check(struct rsi_common *common); int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word); +int rsi_send_radio_params_update(struct rsi_common *common); #endif -- cgit v1.1 From 4edbcd1aa7833e2ccc4933adab3767e3cd2e805e Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Fri, 18 Nov 2016 16:08:22 +0530 Subject: rsi: Add support for antenna selection RSI 9113 device supports single antenna for tx and rx. Support for using external is added. This can be configured from user space using iw. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 78 +++++++++++++++++++++++++++++ drivers/net/wireless/rsi/rsi_91x_mgmt.c | 33 ++++++++++++ drivers/net/wireless/rsi/rsi_main.h | 1 + drivers/net/wireless/rsi/rsi_mgmt.h | 5 ++ 4 files changed, 117 insertions(+) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index f63d6bb..b7ceab0 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1075,6 +1075,82 @@ static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw, return 0; } +/** + * rsi_mac80211_set_antenna() - This function is used to configure + * tx and rx antennas. + * @hw: Pointer to the ieee80211_hw structure. + * @tx_ant: Bitmap for tx antenna + * @rx_ant: Bitmap for rx antenna + * + * Return: 0 on success, Negative error code on failure. + */ +static int rsi_mac80211_set_antenna(struct ieee80211_hw *hw, + u32 tx_ant, u32 rx_ant) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + u8 antenna = 0; + + if (tx_ant > 1 || rx_ant > 1) { + rsi_dbg(ERR_ZONE, + "Invalid antenna selection (tx: %d, rx:%d)\n", + tx_ant, rx_ant); + rsi_dbg(ERR_ZONE, + "Use 0 for int_ant, 1 for ext_ant\n"); + return -EINVAL; + } + + rsi_dbg(INFO_ZONE, "%s: Antenna map Tx %x Rx %d\n", + __func__, tx_ant, rx_ant); + + mutex_lock(&common->mutex); + + antenna = tx_ant ? ANTENNA_SEL_UFL : ANTENNA_SEL_INT; + if (common->ant_in_use != antenna) + if (rsi_set_antenna(common, antenna)) + goto fail_set_antenna; + + rsi_dbg(INFO_ZONE, "(%s) Antenna path configured successfully\n", + tx_ant ? "UFL" : "INT"); + + common->ant_in_use = antenna; + + mutex_unlock(&common->mutex); + + return 0; + +fail_set_antenna: + rsi_dbg(ERR_ZONE, "%s: Failed.\n", __func__); + mutex_unlock(&common->mutex); + return -EINVAL; +} + +/** + * rsi_mac80211_get_antenna() - This function is used to configure + * tx and rx antennas. + * + * @hw: Pointer to the ieee80211_hw structure. + * @tx_ant: Bitmap for tx antenna + * @rx_ant: Bitmap for rx antenna + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_mac80211_get_antenna(struct ieee80211_hw *hw, + u32 *tx_ant, u32 *rx_ant) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + + *tx_ant = (common->ant_in_use == ANTENNA_SEL_UFL) ? 1 : 0; + *rx_ant = 0; + + mutex_unlock(&common->mutex); + + return 0; +} + static struct ieee80211_ops mac80211_ops = { .tx = rsi_mac80211_tx, .start = rsi_mac80211_start, @@ -1091,6 +1167,8 @@ static struct ieee80211_ops mac80211_ops = { .ampdu_action = rsi_mac80211_ampdu_action, .sta_add = rsi_mac80211_sta_add, .sta_remove = rsi_mac80211_sta_remove, + .set_antenna = rsi_mac80211_set_antenna, + .get_antenna = rsi_mac80211_get_antenna, }; /** diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 99c2560..985ef2a 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1315,6 +1315,39 @@ int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word) } /** + * rsi_set_antenna() - This fuction send antenna configuration request + * to device + * + * @common: Pointer to the driver private structure. + * @antenna: bitmap for tx antenna selection + * + * Return: 0 on Success, negative error code on failure + */ +int rsi_set_antenna(struct rsi_common *common, u8 antenna) +{ + struct rsi_mac_frame *cmd_frame; + struct sk_buff *skb; + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + cmd_frame = (struct rsi_mac_frame *)skb->data; + + cmd_frame->desc_word[1] = cpu_to_le16(ANT_SEL_FRAME); + cmd_frame->desc_word[3] = cpu_to_le16(antenna & 0x00ff); + cmd_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** * rsi_handle_ta_confirm_type() - This function handles the confirm frames. * @common: Pointer to the driver private structure. * @msg: Pointer to received packet. diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 3938f13..2405b30 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -206,6 +206,7 @@ struct rsi_common { bool hw_data_qs_blocked; int tx_power; + u8 ant_in_use; }; struct rsi_hw { diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 72f7cfb..73758ac 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -141,6 +141,9 @@ #define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\ FIF_BCN_PRBRESP_PROMISC) +#define ANTENNA_SEL_INT 0x02 /* RF_OUT_2 / Integerated */ +#define ANTENNA_SEL_UFL 0x03 /* RF_OUT_1 / U.FL */ + /* Rx filter word definitions */ #define PROMISCOUS_MODE BIT(0) #define ALLOW_DATA_ASSOC_PEER BIT(1) @@ -201,6 +204,7 @@ enum cmd_frame_type { BG_SCAN_PROBE_REQ, CW_MODE_REQ, PER_CMD_PKT, + ANT_SEL_FRAME = 0x20, RADIO_PARAMS_UPDATE = 0x29 }; @@ -326,4 +330,5 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb); int rsi_band_check(struct rsi_common *common); int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word); int rsi_send_radio_params_update(struct rsi_common *common); +int rsi_set_antenna(struct rsi_common *common, u8 antenna); #endif -- cgit v1.1 From 61d1084214228bf6691930f677cfde33af48ca29 Mon Sep 17 00:00:00 2001 From: Prameela Rani Garnepudi Date: Fri, 18 Nov 2016 16:08:43 +0530 Subject: rsi: Add support for 802.11d Transmit power level in a channel is determined based on the dfs region. To support regulatory rules dfs region should be configured to device during set channel request. Also antenna gain values are taken from the mac80211 channel parameters instead of fixed values. Signed-off-by: Prameela Rani Garnepudi Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 30 ++++++++++++++++++++++++++++- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 26 ++++++++++++++++++++----- drivers/net/wireless/rsi/rsi_main.h | 1 + drivers/net/wireless/rsi/rsi_mgmt.h | 3 ++- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index b7ceab0..dadaa73 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -390,7 +390,7 @@ static int rsi_channel_change(struct ieee80211_hw *hw) status = rsi_band_check(common); if (!status) - status = rsi_set_channel(adapter->priv, channel); + status = rsi_set_channel(adapter->priv, curchan); if (bss->assoc) { if (common->hw_data_qs_blocked && @@ -1151,6 +1151,32 @@ static int rsi_mac80211_get_antenna(struct ieee80211_hw *hw, return 0; } +static void rsi_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rsi_hw * adapter = hw->priv; + int i; + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (ch->flags & IEEE80211_CHAN_RADAR) + ch->flags |= IEEE80211_CHAN_NO_IR; + } + + rsi_dbg(INFO_ZONE, + "country = %s dfs_region = %d\n", + request->alpha2, request->dfs_region); + adapter->dfs_region = request->dfs_region; +} + static struct ieee80211_ops mac80211_ops = { .tx = rsi_mac80211_tx, .start = rsi_mac80211_start, @@ -1233,6 +1259,8 @@ int rsi_mac80211_attach(struct rsi_common *common) wiphy->bands[NL80211_BAND_5GHZ] = &adapter->sbands[NL80211_BAND_5GHZ]; + wiphy->reg_notifier = rsi_reg_notify; + status = ieee80211_register_hw(hw); if (status) return status; diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 985ef2a..fac87c0 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -913,7 +913,8 @@ int rsi_band_check(struct rsi_common *common) * * Return: 0 on success, corresponding error code on failure. */ -int rsi_set_channel(struct rsi_common *common, u16 channel) +int rsi_set_channel(struct rsi_common *common, + struct ieee80211_channel *channel) { struct sk_buff *skb = NULL; struct rsi_mac_frame *mgmt_frame; @@ -928,24 +929,39 @@ int rsi_set_channel(struct rsi_common *common, u16 channel) return -ENOMEM; } + if (!channel) { + dev_kfree_skb(skb); + return 0; + } memset(skb->data, 0, FRAME_DESC_SZ); mgmt_frame = (struct rsi_mac_frame *)skb->data; mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); mgmt_frame->desc_word[1] = cpu_to_le16(SCAN_REQUEST); - mgmt_frame->desc_word[4] = cpu_to_le16(channel); + mgmt_frame->desc_word[4] = cpu_to_le16(channel->hw_value); + + mgmt_frame->desc_word[4] |= + cpu_to_le16(((char)(channel->max_antenna_gain)) << 8); + mgmt_frame->desc_word[5] = + cpu_to_le16((char)(channel->max_antenna_gain)); mgmt_frame->desc_word[7] = cpu_to_le16(PUT_BBP_RESET | BBP_REG_WRITE | (RSI_RF_TYPE << 4)); - mgmt_frame->desc_word[5] = cpu_to_le16(0x01); - mgmt_frame->desc_word[6] = cpu_to_le16(0x12); + if (!(channel->flags & IEEE80211_CHAN_NO_IR) && + !(channel->flags & IEEE80211_CHAN_RADAR)) { + if (common->tx_power < channel->max_power) + mgmt_frame->desc_word[6] = cpu_to_le16(common->tx_power); + else + mgmt_frame->desc_word[6] = cpu_to_le16(channel->max_power); + } + mgmt_frame->desc_word[7] = cpu_to_le16(common->priv->dfs_region); if (common->channel_width == BW_40MHZ) mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8); - common->channel = channel; + common->channel = channel->hw_value; skb_put(skb, FRAME_DESC_SZ); diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h index 2405b30..1d5904b 100644 --- a/drivers/net/wireless/rsi/rsi_main.h +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -223,6 +223,7 @@ struct rsi_hw { struct rsi_debugfs *dfsentry; u8 num_debugfs_entries; #endif + u8 dfs_region; void *rsi_dev; int (*host_intf_read_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len); int (*host_intf_write_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len); diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h index 73758ac..dfbf7a5 100644 --- a/drivers/net/wireless/rsi/rsi_mgmt.h +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -314,7 +314,8 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, u16 ssn, u8 buf_size, u8 event); int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, u8 key_type, u8 key_id, u32 cipher); -int rsi_set_channel(struct rsi_common *common, u16 chno); +int rsi_set_channel(struct rsi_common *common, + struct ieee80211_channel *channel); int rsi_send_block_unblock_frame(struct rsi_common *common, bool event); void rsi_inform_bss_status(struct rsi_common *common, u8 status, const u8 *bssid, u8 qos_enable, u16 aid); -- cgit v1.1 From d96e39270ba5b81dee63f7a1bdaad01484c94e08 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 22:43:24 +0530 Subject: mwifiex: cleanup wake-IRQ handling if suspend fails We don't want to leave the wake IRQ enabled. Signed-off-by: Brian Norris Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 1 + drivers/net/wireless/marvell/mwifiex/sdio.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 4b89f55..e4f1f55 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -135,6 +135,7 @@ static int mwifiex_pcie_suspend(struct device *dev) mwifiex_dbg(adapter, ERROR, "cmd: failed to suspend\n"); adapter->hs_enabling = false; + mwifiex_disable_wake(adapter); return -EFAULT; } diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 0d00db5..1235f04 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -298,6 +298,7 @@ static int mwifiex_sdio_suspend(struct device *dev) mwifiex_dbg(adapter, ERROR, "cmd: failed to suspend\n"); adapter->hs_enabling = false; + mwifiex_disable_wake(adapter); return -EFAULT; } -- cgit v1.1 From b9da4d223bdade1f6f3401387761f61b0da1224e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Nov 2016 22:43:25 +0530 Subject: mwifiex: avoid double-disable_irq() race We have a race where the wakeup IRQ might be in flight while we're calling mwifiex_disable_wake() from resume(). This can leave us disabling the IRQ twice. Let's disable the IRQ and enable it in case if we have double-disabled it. Signed-off-by: Brian Norris Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index cf4c780..5c9bd94 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1424,8 +1424,13 @@ static inline void mwifiex_disable_wake(struct mwifiex_adapter *adapter) { if (adapter->irq_wakeup >= 0) { disable_irq_wake(adapter->irq_wakeup); - if (!adapter->wake_by_wifi) - disable_irq(adapter->irq_wakeup); + disable_irq(adapter->irq_wakeup); + if (adapter->wake_by_wifi) + /* Undo our disable, since interrupt handler already + * did this. + */ + enable_irq(adapter->irq_wakeup); + } } -- cgit v1.1 From a0aba89763f863261609e3e6edff138e4114384f Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 18 Nov 2016 16:44:22 -0500 Subject: rtl8xxxu: Fix memory leak in handling rxdesc16 packets A device running without RX package aggregation could return more data in the USB packet than the actual network packet. In this case the could would clone the skb but then determine that that there was no packet to handle and exit without freeing the cloned skb first. This has so far only been observed with 8188eu devices, but could affect others. Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index b2d7f6e..a96ff17 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5197,7 +5197,12 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) pkt_offset = roundup(pkt_len + drvinfo_sz + desc_shift + sizeof(struct rtl8xxxu_rxdesc16), 128); - if (pkt_cnt > 1) + /* + * Only clone the skb if there's enough data at the end to + * at least cover the rx descriptor + */ + if (pkt_cnt > 1 && + urb_len > (pkt_offset + sizeof(struct rtl8xxxu_rxdesc16))) next_skb = skb_clone(skb, GFP_ATOMIC); rx_status = IEEE80211_SKB_RXCB(skb); -- cgit v1.1 From cf7cfef0646274fdfafb2387f38d7acaa0dba8cf Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 18 Nov 2016 16:44:23 -0500 Subject: rtl8xxxu: Fix big-endian problem reporting mactime The full RX descriptor is converted so converting tsfl again would return it to it's original endian value. Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 4 ++-- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 10166289..08d587a 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -238,7 +238,7 @@ struct rtl8xxxu_rxdesc16 { u32 pattern1match:1; u32 pattern0match:1; #endif - __le32 tsfl; + u32 tsfl; #if 0 u32 bassn:12; u32 bavld:1; @@ -368,7 +368,7 @@ struct rtl8xxxu_rxdesc24 { u32 ldcp:1; u32 splcp:1; #endif - __le32 tsfl; + u32 tsfl; }; struct rtl8xxxu_txdesc32 { diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index a96ff17..a5e6ec2 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5220,7 +5220,7 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats, rx_desc->rxmcs); - rx_status->mactime = le32_to_cpu(rx_desc->tsfl); + rx_status->mactime = rx_desc->tsfl; rx_status->flag |= RX_FLAG_MACTIME_START; if (!rx_desc->swdec) @@ -5290,7 +5290,7 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb) rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats, rx_desc->rxmcs); - rx_status->mactime = le32_to_cpu(rx_desc->tsfl); + rx_status->mactime = rx_desc->tsfl; rx_status->flag |= RX_FLAG_MACTIME_START; if (!rx_desc->swdec) -- cgit v1.1 From b9af9355112773a1777fc7ba876882293f0fa58f Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 18 Nov 2016 16:44:24 -0500 Subject: rtl8xxxu: Fix rtl8723bu driver reload issue The generic disable_rf() function clears bits 22 and 23 in REG_RX_WAIT_CCA, however we did not re-enable them again in rtl8723b_enable_rf() This resolves the problem for me with 8723bu devices not working again after reloading the driver. Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c index 6c086b5..02b8ddd 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c @@ -1498,6 +1498,10 @@ static void rtl8723b_enable_rf(struct rtl8xxxu_priv *priv) u32 val32; u8 val8; + val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA); + val32 |= (BIT(22) | BIT(23)); + rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32); + /* * No indication anywhere as to what 0x0790 does. The 2 antenna * vendor code preserves bits 6-7 here. -- cgit v1.1 From 5d03f882c2fc0bb588e8a91ba3955f56170fc891 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 18 Nov 2016 16:44:25 -0500 Subject: rtl8xxxu: Fix rtl8192eu driver reload issue The 8192eu suffered from two issues when reloading the driver. The same problems as with the 8723bu where REG_RX_WAIT_CCA bits 22 and 23 didn't get set in rtl8192e_enable_rf(). In addition it also seems prone to issues when setting REG_RF_CTRL to 0 intead of just disabling the RF_ENABLE bit. Similar to what was causing issues with the 8188eu. With this patch I can successfully reload the driver and reassociate to an APi with an 8192eu dongle. Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index df54d27..a793fed 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -1461,7 +1461,9 @@ static int rtl8192eu_active_to_emu(struct rtl8xxxu_priv *priv) int count, ret = 0; /* Turn off RF */ - rtl8xxxu_write8(priv, REG_RF_CTRL, 0); + val8 = rtl8xxxu_read8(priv, REG_RF_CTRL); + val8 &= ~RF_ENABLE; + rtl8xxxu_write8(priv, REG_RF_CTRL, val8); /* Switch DPDT_SEL_P output from register 0x65[2] */ val8 = rtl8xxxu_read8(priv, REG_LEDCFG2); @@ -1593,6 +1595,10 @@ static void rtl8192e_enable_rf(struct rtl8xxxu_priv *priv) u32 val32; u8 val8; + val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA); + val32 |= (BIT(22) | BIT(23)); + rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32); + val8 = rtl8xxxu_read8(priv, REG_GPIO_MUXCFG); val8 |= BIT(5); rtl8xxxu_write8(priv, REG_GPIO_MUXCFG, val8); -- cgit v1.1 From a748a11038f86b1247e92fb131c663064397b706 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 18 Nov 2016 16:44:26 -0500 Subject: rtl8xxxu: Obtain RTS rates from mac80211 Use the mac80211 provided rate for RTS rather than the hard coded 24Mbps as suggested by the vendor drivers. Reported-by: Andrea Merello Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 6 +-- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 46 ++++++++++++++-------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 08d587a..bc3c990 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1340,7 +1340,7 @@ struct rtl8xxxu_fileops { void (*fill_txdesc) (struct ieee80211_hdr *hdr, struct rtl8xxxu_txdesc32 *tx_desc, u32 rate, u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable); + bool ampdu_enable, u32 rts_rate); int writeN_block_size; int rx_agg_buf_size; char tx_desc_size; @@ -1437,11 +1437,11 @@ bool rtl8xxxu_gen2_simularity_compare(struct rtl8xxxu_priv *priv, void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr, struct rtl8xxxu_txdesc32 *tx_desc, u32 rate, u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable); + bool ampdu_enable, u32 rts_rate); void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr, struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate, u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable); + bool ampdu_enable, u32 rts_rate); extern struct rtl8xxxu_fileops rtl8192cu_fops; extern struct rtl8xxxu_fileops rtl8192eu_fops; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index a5e6ec2..a4ac557 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4762,7 +4762,7 @@ void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr, struct rtl8xxxu_txdesc32 *tx_desc, u32 rate, u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable) + bool ampdu_enable, u32 rts_rate) { u16 seq_number; @@ -4796,15 +4796,16 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr, if (sgi) tx_desc->txdw5 |= cpu_to_le32(TXDESC32_SHORT_GI); + /* + * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled + */ + tx_desc->txdw4 |= cpu_to_le32(rts_rate << TXDESC32_RTS_RATE_SHIFT); if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { - /* - * Use RTS rate 24M - does the mac80211 tell - * us which to use? - */ - tx_desc->txdw4 |= cpu_to_le32(DESC_RATE_24M << - TXDESC32_RTS_RATE_SHIFT); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_RTS_CTS_ENABLE); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); + } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) { + tx_desc->txdw4 |= cpu_to_le32(TXDESC32_CTS_SELF_ENABLE); + tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); } } @@ -4816,7 +4817,7 @@ void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr, struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate, u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable) + bool ampdu_enable, u32 rts_rate) { struct rtl8xxxu_txdesc40 *tx_desc40; u16 seq_number; @@ -4849,15 +4850,19 @@ rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr, if (short_preamble) tx_desc40->txdw5 |= cpu_to_le32(TXDESC40_SHORT_PREAMBLE); + tx_desc40->txdw4 |= cpu_to_le32(rts_rate << TXDESC40_RTS_RATE_SHIFT); + /* + * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled + */ if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { - /* - * Use RTS rate 24M - does the mac80211 tell - * us which to use? - */ - tx_desc40->txdw4 |= cpu_to_le32(DESC_RATE_24M << - TXDESC40_RTS_RATE_SHIFT); tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_RTS_CTS_ENABLE); tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_HW_RTS_ENABLE); + } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) { + /* + * For some reason the vendor driver doesn't set + * TXDESC40_HW_RTS_ENABLE for CTS to SELF + */ + tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_CTS_SELF_ENABLE); } } @@ -4874,7 +4879,7 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, struct ieee80211_sta *sta = NULL; struct ieee80211_vif *vif = tx_info->control.vif; struct device *dev = &priv->udev->dev; - u32 queue, rate; + u32 queue, rate, rts_rate; u16 pktlen = skb->len; u16 seq_number; u16 rate_flag = tx_info->control.rates[0].flags; @@ -4974,10 +4979,17 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, (sta && vif && vif->bss_conf.use_short_preamble)) short_preamble = true; + if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) + rts_rate = ieee80211_get_rts_cts_rate(hw, tx_info)->hw_value; + else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) + rts_rate = ieee80211_get_rts_cts_rate(hw, tx_info)->hw_value; + else + rts_rate = 0; + seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - priv->fops->fill_txdesc(hdr, tx_desc, rate, rate_flag, - sgi, short_preamble, ampdu_enable); + priv->fops->fill_txdesc(hdr, tx_desc, rate, rate_flag, sgi, + short_preamble, ampdu_enable, rts_rate); rtl8xxxu_calc_tx_desc_csum(tx_desc); -- cgit v1.1 From b4c3d9cfb6075ded389485dfc8cb9e30aa5a4272 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Fri, 18 Nov 2016 16:44:27 -0500 Subject: rtl8xxxu: Pass tx_info to fill_txdesc in order to have access to retry count In order to obtain retry count for a given rate we need to pass the full struct ieee80211_tx_info to the function setting the rate in he TX descriptor. This uncovered a huge bug where the old code would use struct ieee80211_rate.flags to test for rate parameters, which is always zero, instead of the flags value from struct ieee80211_tx_rate. Time to find a brown paper bag :( Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 27 ++++---- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 71 ++++++++++++++-------- 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index bc3c990..df551b2 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1337,10 +1337,11 @@ struct rtl8xxxu_fileops { u32 ramask, int sgi); void (*report_connect) (struct rtl8xxxu_priv *priv, u8 macid, bool connect); - void (*fill_txdesc) (struct ieee80211_hdr *hdr, - struct rtl8xxxu_txdesc32 *tx_desc, u32 rate, - u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable, u32 rts_rate); + void (*fill_txdesc) (struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *tx_info, + struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, + bool short_preamble, bool ampdu_enable, + u32 rts_rate); int writeN_block_size; int rx_agg_buf_size; char tx_desc_size; @@ -1434,14 +1435,16 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb); int rtl8xxxu_gen2_channel_to_group(int channel); bool rtl8xxxu_gen2_simularity_compare(struct rtl8xxxu_priv *priv, int result[][8], int c1, int c2); -void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr, - struct rtl8xxxu_txdesc32 *tx_desc, u32 rate, - u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable, u32 rts_rate); -void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr, - struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate, - u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable, u32 rts_rate); +void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *tx_info, + struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, + bool short_preamble, bool ampdu_enable, + u32 rts_rate); +void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *tx_info, + struct rtl8xxxu_txdesc32 *tx_desc32, bool sgi, + bool short_preamble, bool ampdu_enable, + u32 rts_rate); extern struct rtl8xxxu_fileops rtl8192cu_fops; extern struct rtl8xxxu_fileops rtl8192eu_fops; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index a4ac557..04141e5 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4759,13 +4759,28 @@ static void rtl8xxxu_dump_action(struct device *dev, * This format is used on 8188cu/8192cu/8723au */ void -rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr, - struct rtl8xxxu_txdesc32 *tx_desc, u32 rate, - u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable, u32 rts_rate) +rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *tx_info, + struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, + bool short_preamble, bool ampdu_enable, u32 rts_rate) { + struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info); + struct rtl8xxxu_priv *priv = hw->priv; + struct device *dev = &priv->udev->dev; + u32 rate; + u16 rate_flags = tx_info->control.rates[0].flags; u16 seq_number; + if (rate_flags & IEEE80211_TX_RC_MCS && + !ieee80211_is_mgmt(hdr->frame_control)) + rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; + else + rate = tx_rate->hw_value; + + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX) + dev_info(dev, "%s: TX rate: %d, pkt size %d\n", + __func__, rate, cpu_to_le16(tx_desc->pkt_size)); + seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); tx_desc->txdw5 = cpu_to_le32(rate); @@ -4800,10 +4815,10 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr, * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled */ tx_desc->txdw4 |= cpu_to_le32(rts_rate << TXDESC32_RTS_RATE_SHIFT); - if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { + if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { tx_desc->txdw4 |= cpu_to_le32(TXDESC32_RTS_CTS_ENABLE); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); - } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) { + } else if (rate_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { tx_desc->txdw4 |= cpu_to_le32(TXDESC32_CTS_SELF_ENABLE); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); } @@ -4814,16 +4829,31 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr, * This format is used on 8192eu/8723bu */ void -rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr, - struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate, - u16 rate_flag, bool sgi, bool short_preamble, - bool ampdu_enable, u32 rts_rate) +rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *tx_info, + struct rtl8xxxu_txdesc32 *tx_desc32, bool sgi, + bool short_preamble, bool ampdu_enable, u32 rts_rate) { + struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info); + struct rtl8xxxu_priv *priv = hw->priv; + struct device *dev = &priv->udev->dev; struct rtl8xxxu_txdesc40 *tx_desc40; + u32 rate; + u16 rate_flags = tx_info->control.rates[0].flags; u16 seq_number; tx_desc40 = (struct rtl8xxxu_txdesc40 *)tx_desc32; + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX) + dev_info(dev, "%s: TX rate: %d, pkt size %d\n", + __func__, rate, cpu_to_le16(tx_desc40->pkt_size)); + + if (rate_flags & IEEE80211_TX_RC_MCS && + !ieee80211_is_mgmt(hdr->frame_control)) + rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; + else + rate = tx_rate->hw_value; + seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); tx_desc40->txdw4 = cpu_to_le32(rate); @@ -4854,10 +4884,10 @@ rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr, /* * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled */ - if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { + if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_RTS_CTS_ENABLE); tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_HW_RTS_ENABLE); - } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) { + } else if (rate_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { /* * For some reason the vendor driver doesn't set * TXDESC40_HW_RTS_ENABLE for CTS to SELF @@ -4872,14 +4902,13 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info); struct rtl8xxxu_priv *priv = hw->priv; struct rtl8xxxu_txdesc32 *tx_desc; struct rtl8xxxu_tx_urb *tx_urb; struct ieee80211_sta *sta = NULL; struct ieee80211_vif *vif = tx_info->control.vif; struct device *dev = &priv->udev->dev; - u32 queue, rate, rts_rate; + u32 queue, rts_rate; u16 pktlen = skb->len; u16 seq_number; u16 rate_flag = tx_info->control.rates[0].flags; @@ -4906,10 +4935,6 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, goto error; } - if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX) - dev_info(dev, "%s: TX rate: %d (%d), pkt size %d\n", - __func__, tx_rate->bitrate, tx_rate->hw_value, pktlen); - if (ieee80211_is_action(hdr->frame_control)) rtl8xxxu_dump_action(dev, hdr); @@ -4963,12 +4988,6 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, } } - if (rate_flag & IEEE80211_TX_RC_MCS && - !ieee80211_is_mgmt(hdr->frame_control)) - rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; - else - rate = tx_rate->hw_value; - if (rate_flag & IEEE80211_TX_RC_SHORT_GI || (ieee80211_is_data_qos(hdr->frame_control) && sta && sta->ht_cap.cap & @@ -4988,8 +5007,8 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - priv->fops->fill_txdesc(hdr, tx_desc, rate, rate_flag, sgi, - short_preamble, ampdu_enable, rts_rate); + priv->fops->fill_txdesc(hw, hdr, tx_info, tx_desc, sgi, short_preamble, + ampdu_enable, rts_rate); rtl8xxxu_calc_tx_desc_csum(tx_desc); -- cgit v1.1 From 13e1349aff8290d328a3caf34d42b909dbab8491 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 18 Nov 2016 16:44:28 -0500 Subject: rtl8xxxu: Fix non static symbol warning Fixes the following sparse warning: drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c:1559:6: warning: symbol 'rtl8192eu_power_off' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c index a793fed..a1178c5 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c @@ -1556,7 +1556,7 @@ exit: return ret; } -void rtl8192eu_power_off(struct rtl8xxxu_priv *priv) +static void rtl8192eu_power_off(struct rtl8xxxu_priv *priv) { u8 val8; u16 val16; -- cgit v1.1 From e267e71e68aeeb51cbb9893ef9d61a59205826c4 Mon Sep 17 00:00:00 2001 From: Karthik D A Date: Mon, 21 Nov 2016 17:49:04 +0530 Subject: mwifiex: Disable adhoc feature based on firmware capability We will read fw_cap_info filled by firmware to check whether to skip ADHOC related commands or not. Also, IBSS_COALESCING_STATUS command has been moved from init path to adhoc network creation path. Signed-off-by: Karthik D A Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 10 +++++++ drivers/net/wireless/marvell/mwifiex/fw.h | 1 + drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 36 +++++++++++++++++-------- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index c26469a..2dc2757 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -2143,6 +2143,16 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); if (mode == NL80211_IFTYPE_ADHOC) { + u16 enable = true; + + /* set ibss coalescing_status */ + ret = mwifiex_send_cmd( + priv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_SET, 0, &enable, true); + if (ret) + return ret; + /* "privacy" is set only for ad-hoc mode */ if (privacy) { /* diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 9b3ccea..ea45594 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -219,6 +219,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) #define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15)) #define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16)) +#define ISSUPP_ADHOC_ENABLED(FwCapInfo) (FwCapInfo & BIT(25)) #define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index bcd6408..f5dffdf 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1902,6 +1902,24 @@ static int mwifiex_cmd_get_wakeup_reason(struct mwifiex_private *priv, return 0; } +/* This function check if the command is supported by firmware */ +static int mwifiex_is_cmd_supported(struct mwifiex_private *priv, u16 cmd_no) +{ + if (!ISSUPP_ADHOC_ENABLED(priv->adapter->fw_cap_info)) { + switch (cmd_no) { + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + case HostCmd_CMD_802_11_AD_HOC_STOP: + return -EOPNOTSUPP; + default: + break; + } + } + + return 0; +} + /* * This function prepares the commands before sending them to the firmware. * @@ -1915,6 +1933,13 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, struct host_cmd_ds_command *cmd_ptr = cmd_buf; int ret = 0; + if (mwifiex_is_cmd_supported(priv, cmd_no)) { + mwifiex_dbg(priv->adapter, ERROR, + "0x%x command not supported by firmware\n", + cmd_no); + return -EOPNOTSUPP; + } + /* Prepare command */ switch (cmd_no) { case HostCmd_CMD_GET_HW_SPEC: @@ -2208,7 +2233,6 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) { struct mwifiex_adapter *adapter = priv->adapter; int ret; - u16 enable = true; struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; struct mwifiex_ds_auto_ds auto_ds; enum state_11d_t state_11d; @@ -2321,16 +2345,6 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) if (ret) return -1; - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { - /* set ibss coalescing_status */ - ret = mwifiex_send_cmd( - priv, - HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, - HostCmd_ACT_GEN_SET, 0, &enable, true); - if (ret) - return -1; - } - memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); amsdu_aggr_ctrl.enable = true; /* Send request to firmware */ -- cgit v1.1 From 4c0bfeaae9f98b87f23f12bad7842d781a94360b Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Mon, 21 Nov 2016 14:29:09 +0100 Subject: brcmsmac: fix array out-of-bounds access in qm_log10 I get the following UBSAN warning during boot on my laptop: ================================================================================ UBSAN: Undefined behaviour in drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c:280:21 index 32 is out of range for type 's16 [32]' CPU: 0 PID: 879 Comm: NetworkManager Not tainted 4.9.0-rc4 #28 Hardware name: LENOVO Lenovo IdeaPad N581/INVALID, BIOS 5ECN96WW(V9.01) 03/14/2013 ffff8800b74a6478 ffffffff828e59d2 0000000041b58ab3 ffffffff8398330c ffffffff828e5920 ffff8800b74a64a0 ffff8800b74a6450 0000000000000020 1ffffffff845848c ffffed0016e94bf1 ffffffffc22c2460 000000006b9c0514 Call Trace: [] dump_stack+0xb2/0x110 [] ? _atomic_dec_and_lock+0x150/0x150 [] ubsan_epilogue+0xd/0x4e [] __ubsan_handle_out_of_bounds+0xfa/0x13e [] ? __ubsan_handle_shift_out_of_bounds+0x241/0x241 [] ? bcma_host_pci_read16+0x59/0xa0 [bcma] [] ? bcma_host_pci_read16+0x68/0xa0 [bcma] [] ? read_phy_reg+0xe8/0x180 [brcmsmac] [] qm_log10+0x2e4/0x350 [brcmsmac] [] wlc_phy_init_lcnphy+0x538/0x1f20 [brcmsmac] [] ? wlc_lcnphy_periodic_cal+0x5c0/0x5c0 [brcmsmac] [] ? ieee80211_open+0xb3/0x110 [mac80211] [] ? sk_busy_loop+0x1e2/0x840 [] ? __dev_change_flags+0xae/0x220 ... The report is valid: doing the math in this function, with an input value N=63 the variable s16tableIndex gets a value of 31. This value is used as an index in the array log_table with 32 entries. But the next line is: s16errorApproximation = (s16) qm_mulu16(u16offset, (u16) (log_table[s16tableIndex + 1] - log_table[s16tableIndex])); With s16tableIndex + 1 we are trying an out-of-bounds access to the array. The log_table array provides log2 values in q.15 format and the above statement tries an error approximation with the next value. To fix this issue add the next value to the array and update the comment accordingly. Signed-off-by: Tobias Regnery Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c index faf1ebe..b9672da 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c @@ -179,7 +179,7 @@ s16 qm_norm32(s32 op) return u16extraSignBits; } -/* This table is log2(1+(i/32)) where i=[0:1:31], in q.15 format */ +/* This table is log2(1+(i/32)) where i=[0:1:32], in q.15 format */ static const s16 log_table[] = { 0, 1455, @@ -212,7 +212,8 @@ static const s16 log_table[] = { 29717, 30498, 31267, - 32024 + 32024, + 32768 }; #define LOG_TABLE_SIZE 32 /* log_table size */ -- cgit v1.1 From 619228d86b0e32e00758dcf07ca5d06903d9a9d7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 23 Nov 2016 11:02:44 +0000 Subject: cxgb4: fix memory leak on txq_info Currently if txq_info->uldtxq cannot be allocated then txq_info->txq is being kfree'd (which is redundant because it is NULL) instead of txq_info. Fix this by instead kfree'ing txq_info. Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c index 565a6c6..8098902 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c @@ -532,7 +532,7 @@ setup_sge_txq_uld(struct adapter *adap, unsigned int uld_type, txq_info->uldtxq = kcalloc(txq_info->ntxq, sizeof(struct sge_uld_txq), GFP_KERNEL); if (!txq_info->uldtxq) { - kfree(txq_info->uldtxq); + kfree(txq_info); return -ENOMEM; } -- cgit v1.1 From 0e33661de493db325435d565a4a722120ae4cbf3 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 23 Nov 2016 16:52:25 +0100 Subject: bpf: add new prog type for cgroup socket filtering This program type is similar to BPF_PROG_TYPE_SOCKET_FILTER, except that it does not allow BPF_LD_[ABS|IND] instructions and hooks up the bpf_skb_load_bytes() helper. Programs of this type will be attached to cgroups for network filtering and accounting. Signed-off-by: Daniel Mack Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 9 +++++++++ net/core/filter.c | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 7d9b283..5ae679f 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -98,8 +98,17 @@ enum bpf_prog_type { BPF_PROG_TYPE_TRACEPOINT, BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, + BPF_PROG_TYPE_CGROUP_SKB, }; +enum bpf_attach_type { + BPF_CGROUP_INET_INGRESS, + BPF_CGROUP_INET_EGRESS, + __MAX_BPF_ATTACH_TYPE +}; + +#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE + #define BPF_PSEUDO_MAP_FD 1 /* flags for BPF_MAP_UPDATE_ELEM command */ diff --git a/net/core/filter.c b/net/core/filter.c index dece94f..2de302d 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2630,6 +2630,17 @@ xdp_func_proto(enum bpf_func_id func_id) } } +static const struct bpf_func_proto * +cg_skb_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_skb_load_bytes: + return &bpf_skb_load_bytes_proto; + default: + return sk_filter_func_proto(func_id); + } +} + static bool __is_valid_access(int off, int size, enum bpf_access_type type) { if (off < 0 || off >= sizeof(struct __sk_buff)) @@ -2992,6 +3003,12 @@ static const struct bpf_verifier_ops xdp_ops = { .convert_ctx_access = xdp_convert_ctx_access, }; +static const struct bpf_verifier_ops cg_skb_ops = { + .get_func_proto = cg_skb_func_proto, + .is_valid_access = sk_filter_is_valid_access, + .convert_ctx_access = sk_filter_convert_ctx_access, +}; + static struct bpf_prog_type_list sk_filter_type __read_mostly = { .ops = &sk_filter_ops, .type = BPF_PROG_TYPE_SOCKET_FILTER, @@ -3012,12 +3029,18 @@ static struct bpf_prog_type_list xdp_type __read_mostly = { .type = BPF_PROG_TYPE_XDP, }; +static struct bpf_prog_type_list cg_skb_type __read_mostly = { + .ops = &cg_skb_ops, + .type = BPF_PROG_TYPE_CGROUP_SKB, +}; + static int __init register_sk_filter_ops(void) { bpf_register_prog_type(&sk_filter_type); bpf_register_prog_type(&sched_cls_type); bpf_register_prog_type(&sched_act_type); bpf_register_prog_type(&xdp_type); + bpf_register_prog_type(&cg_skb_type); return 0; } -- cgit v1.1 From 3007098494bec614fb55dee7bc0410bb7db5ad18 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 23 Nov 2016 16:52:26 +0100 Subject: cgroup: add support for eBPF programs This patch adds two sets of eBPF program pointers to struct cgroup. One for such that are directly pinned to a cgroup, and one for such that are effective for it. To illustrate the logic behind that, assume the following example cgroup hierarchy. A - B - C \ D - E If only B has a program attached, it will be effective for B, C, D and E. If D then attaches a program itself, that will be effective for both D and E, and the program in B will only affect B and C. Only one program of a given type is effective for a cgroup. Attaching and detaching programs will be done through the bpf(2) syscall. For now, ingress and egress inet socket filtering are the only supported use-cases. Signed-off-by: Daniel Mack Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf-cgroup.h | 79 +++++++++++++++++++++ include/linux/cgroup-defs.h | 4 ++ init/Kconfig | 12 ++++ kernel/bpf/Makefile | 1 + kernel/bpf/cgroup.c | 167 ++++++++++++++++++++++++++++++++++++++++++++ kernel/cgroup.c | 18 +++++ 6 files changed, 281 insertions(+) create mode 100644 include/linux/bpf-cgroup.h create mode 100644 kernel/bpf/cgroup.c diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h new file mode 100644 index 0000000..ec80d0c --- /dev/null +++ b/include/linux/bpf-cgroup.h @@ -0,0 +1,79 @@ +#ifndef _BPF_CGROUP_H +#define _BPF_CGROUP_H + +#include +#include +#include + +struct sock; +struct cgroup; +struct sk_buff; + +#ifdef CONFIG_CGROUP_BPF + +extern struct static_key_false cgroup_bpf_enabled_key; +#define cgroup_bpf_enabled static_branch_unlikely(&cgroup_bpf_enabled_key) + +struct cgroup_bpf { + /* + * Store two sets of bpf_prog pointers, one for programs that are + * pinned directly to this cgroup, and one for those that are effective + * when this cgroup is accessed. + */ + struct bpf_prog *prog[MAX_BPF_ATTACH_TYPE]; + struct bpf_prog *effective[MAX_BPF_ATTACH_TYPE]; +}; + +void cgroup_bpf_put(struct cgroup *cgrp); +void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent); + +void __cgroup_bpf_update(struct cgroup *cgrp, + struct cgroup *parent, + struct bpf_prog *prog, + enum bpf_attach_type type); + +/* Wrapper for __cgroup_bpf_update() protected by cgroup_mutex */ +void cgroup_bpf_update(struct cgroup *cgrp, + struct bpf_prog *prog, + enum bpf_attach_type type); + +int __cgroup_bpf_run_filter(struct sock *sk, + struct sk_buff *skb, + enum bpf_attach_type type); + +/* Wrappers for __cgroup_bpf_run_filter() guarded by cgroup_bpf_enabled. */ +#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled) \ + __ret = __cgroup_bpf_run_filter(sk, skb, \ + BPF_CGROUP_INET_INGRESS); \ + \ + __ret; \ +}) + +#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled && sk && sk == skb->sk) { \ + typeof(sk) __sk = sk_to_full_sk(sk); \ + if (sk_fullsock(__sk)) \ + __ret = __cgroup_bpf_run_filter(__sk, skb, \ + BPF_CGROUP_INET_EGRESS); \ + } \ + __ret; \ +}) + +#else + +struct cgroup_bpf {}; +static inline void cgroup_bpf_put(struct cgroup *cgrp) {} +static inline void cgroup_bpf_inherit(struct cgroup *cgrp, + struct cgroup *parent) {} + +#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; }) + +#endif /* CONFIG_CGROUP_BPF */ + +#endif /* _BPF_CGROUP_H */ diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 5b17de6..861b467 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef CONFIG_CGROUPS @@ -300,6 +301,9 @@ struct cgroup { /* used to schedule release agent */ struct work_struct release_agent_work; + /* used to store eBPF programs */ + struct cgroup_bpf bpf; + /* ids of the ancestors at each level including self */ int ancestor_ids[]; }; diff --git a/init/Kconfig b/init/Kconfig index 34407f1..405120b 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1154,6 +1154,18 @@ config CGROUP_PERF Say N if unsure. +config CGROUP_BPF + bool "Support for eBPF programs attached to cgroups" + depends on BPF_SYSCALL && SOCK_CGROUP_DATA + help + Allow attaching eBPF programs to a cgroup using the bpf(2) + syscall command BPF_PROG_ATTACH. + + In which context these programs are accessed depends on the type + of attachment. For instance, programs that are attached using + BPF_CGROUP_INET_INGRESS will be executed on the ingress path of + inet sockets. + config CGROUP_DEBUG bool "Example controller" default n diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index c4d89d6..1276474 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list ifeq ($(CONFIG_PERF_EVENTS),y) obj-$(CONFIG_BPF_SYSCALL) += stackmap.o endif +obj-$(CONFIG_CGROUP_BPF) += cgroup.o diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c new file mode 100644 index 0000000..a0ab43f --- /dev/null +++ b/kernel/bpf/cgroup.c @@ -0,0 +1,167 @@ +/* + * Functions to manage eBPF programs attached to cgroups + * + * Copyright (c) 2016 Daniel Mack + * + * This file is subject to the terms and conditions of version 2 of the GNU + * General Public License. See the file COPYING in the main directory of the + * Linux distribution for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +DEFINE_STATIC_KEY_FALSE(cgroup_bpf_enabled_key); +EXPORT_SYMBOL(cgroup_bpf_enabled_key); + +/** + * cgroup_bpf_put() - put references of all bpf programs + * @cgrp: the cgroup to modify + */ +void cgroup_bpf_put(struct cgroup *cgrp) +{ + unsigned int type; + + for (type = 0; type < ARRAY_SIZE(cgrp->bpf.prog); type++) { + struct bpf_prog *prog = cgrp->bpf.prog[type]; + + if (prog) { + bpf_prog_put(prog); + static_branch_dec(&cgroup_bpf_enabled_key); + } + } +} + +/** + * cgroup_bpf_inherit() - inherit effective programs from parent + * @cgrp: the cgroup to modify + * @parent: the parent to inherit from + */ +void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent) +{ + unsigned int type; + + for (type = 0; type < ARRAY_SIZE(cgrp->bpf.effective); type++) { + struct bpf_prog *e; + + e = rcu_dereference_protected(parent->bpf.effective[type], + lockdep_is_held(&cgroup_mutex)); + rcu_assign_pointer(cgrp->bpf.effective[type], e); + } +} + +/** + * __cgroup_bpf_update() - Update the pinned program of a cgroup, and + * propagate the change to descendants + * @cgrp: The cgroup which descendants to traverse + * @parent: The parent of @cgrp, or %NULL if @cgrp is the root + * @prog: A new program to pin + * @type: Type of pinning operation (ingress/egress) + * + * Each cgroup has a set of two pointers for bpf programs; one for eBPF + * programs it owns, and which is effective for execution. + * + * If @prog is %NULL, this function attaches a new program to the cgroup and + * releases the one that is currently attached, if any. @prog is then made + * the effective program of type @type in that cgroup. + * + * If @prog is %NULL, the currently attached program of type @type is released, + * and the effective program of the parent cgroup (if any) is inherited to + * @cgrp. + * + * Then, the descendants of @cgrp are walked and the effective program for + * each of them is set to the effective program of @cgrp unless the + * descendant has its own program attached, in which case the subbranch is + * skipped. This ensures that delegated subcgroups with own programs are left + * untouched. + * + * Must be called with cgroup_mutex held. + */ +void __cgroup_bpf_update(struct cgroup *cgrp, + struct cgroup *parent, + struct bpf_prog *prog, + enum bpf_attach_type type) +{ + struct bpf_prog *old_prog, *effective; + struct cgroup_subsys_state *pos; + + old_prog = xchg(cgrp->bpf.prog + type, prog); + + effective = (!prog && parent) ? + rcu_dereference_protected(parent->bpf.effective[type], + lockdep_is_held(&cgroup_mutex)) : + prog; + + css_for_each_descendant_pre(pos, &cgrp->self) { + struct cgroup *desc = container_of(pos, struct cgroup, self); + + /* skip the subtree if the descendant has its own program */ + if (desc->bpf.prog[type] && desc != cgrp) + pos = css_rightmost_descendant(pos); + else + rcu_assign_pointer(desc->bpf.effective[type], + effective); + } + + if (prog) + static_branch_inc(&cgroup_bpf_enabled_key); + + if (old_prog) { + bpf_prog_put(old_prog); + static_branch_dec(&cgroup_bpf_enabled_key); + } +} + +/** + * __cgroup_bpf_run_filter() - Run a program for packet filtering + * @sk: The socken sending or receiving traffic + * @skb: The skb that is being sent or received + * @type: The type of program to be exectuted + * + * If no socket is passed, or the socket is not of type INET or INET6, + * this function does nothing and returns 0. + * + * The program type passed in via @type must be suitable for network + * filtering. No further check is performed to assert that. + * + * This function will return %-EPERM if any if an attached program was found + * and if it returned != 1 during execution. In all other cases, 0 is returned. + */ +int __cgroup_bpf_run_filter(struct sock *sk, + struct sk_buff *skb, + enum bpf_attach_type type) +{ + struct bpf_prog *prog; + struct cgroup *cgrp; + int ret = 0; + + if (!sk || !sk_fullsock(sk)) + return 0; + + if (sk->sk_family != AF_INET && + sk->sk_family != AF_INET6) + return 0; + + cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); + + rcu_read_lock(); + + prog = rcu_dereference(cgrp->bpf.effective[type]); + if (prog) { + unsigned int offset = skb->data - skb_network_header(skb); + + __skb_push(skb, offset); + ret = bpf_prog_run_save_cb(prog, skb) == 1 ? 0 : -EPERM; + __skb_pull(skb, offset); + } + + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL(__cgroup_bpf_run_filter); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 85bc9be..2ee9ec3 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -5074,6 +5074,8 @@ static void css_release_work_fn(struct work_struct *work) if (cgrp->kn) RCU_INIT_POINTER(*(void __rcu __force **)&cgrp->kn->priv, NULL); + + cgroup_bpf_put(cgrp); } mutex_unlock(&cgroup_mutex); @@ -5281,6 +5283,9 @@ static struct cgroup *cgroup_create(struct cgroup *parent) if (!cgroup_on_dfl(cgrp)) cgrp->subtree_control = cgroup_control(cgrp); + if (parent) + cgroup_bpf_inherit(cgrp, parent); + cgroup_propagate_control(cgrp); /* @cgrp doesn't have dir yet so the following will only create csses */ @@ -6495,6 +6500,19 @@ static __init int cgroup_namespaces_init(void) } subsys_initcall(cgroup_namespaces_init); +#ifdef CONFIG_CGROUP_BPF +void cgroup_bpf_update(struct cgroup *cgrp, + struct bpf_prog *prog, + enum bpf_attach_type type) +{ + struct cgroup *parent = cgroup_parent(cgrp); + + mutex_lock(&cgroup_mutex); + __cgroup_bpf_update(cgrp, parent, prog, type); + mutex_unlock(&cgroup_mutex); +} +#endif /* CONFIG_CGROUP_BPF */ + #ifdef CONFIG_CGROUP_DEBUG static struct cgroup_subsys_state * debug_css_alloc(struct cgroup_subsys_state *parent_css) -- cgit v1.1 From f4324551489e8781d838f941b7aee4208e52e8bf Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 23 Nov 2016 16:52:27 +0100 Subject: bpf: add BPF_PROG_ATTACH and BPF_PROG_DETACH commands Extend the bpf(2) syscall by two new commands, BPF_PROG_ATTACH and BPF_PROG_DETACH which allow attaching and detaching eBPF programs to a target. On the API level, the target could be anything that has an fd in userspace, hence the name of the field in union bpf_attr is called 'target_fd'. When called with BPF_ATTACH_TYPE_CGROUP_INET_{E,IN}GRESS, the target is expected to be a valid file descriptor of a cgroup v2 directory which has the bpf controller enabled. These are the only use-cases implemented by this patch at this point, but more can be added. If a program of the given type already exists in the given cgroup, the program is swapped automically, so userspace does not have to drop an existing program first before installing a new one, which would otherwise leave a gap in which no program is attached. For more information on the propagation logic to subcgroups, please refer to the bpf cgroup controller implementation. The API is guarded by CAP_NET_ADMIN. Signed-off-by: Daniel Mack Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 8 +++++ kernel/bpf/syscall.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 5ae679f..1370a9d 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -73,6 +73,8 @@ enum bpf_cmd { BPF_PROG_LOAD, BPF_OBJ_PIN, BPF_OBJ_GET, + BPF_PROG_ATTACH, + BPF_PROG_DETACH, }; enum bpf_map_type { @@ -159,6 +161,12 @@ union bpf_attr { __aligned_u64 pathname; __u32 bpf_fd; }; + + struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ + __u32 target_fd; /* container object to attach to */ + __u32 attach_bpf_fd; /* eBPF program to attach */ + __u32 attach_type; + }; } __attribute__((aligned(8))); /* BPF helper function descriptions: diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index eb15498..1090d16 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -835,6 +835,77 @@ static int bpf_obj_get(const union bpf_attr *attr) return bpf_obj_get_user(u64_to_user_ptr(attr->pathname)); } +#ifdef CONFIG_CGROUP_BPF + +#define BPF_PROG_ATTACH_LAST_FIELD attach_type + +static int bpf_prog_attach(const union bpf_attr *attr) +{ + struct bpf_prog *prog; + struct cgroup *cgrp; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (CHECK_ATTR(BPF_PROG_ATTACH)) + return -EINVAL; + + switch (attr->attach_type) { + case BPF_CGROUP_INET_INGRESS: + case BPF_CGROUP_INET_EGRESS: + prog = bpf_prog_get_type(attr->attach_bpf_fd, + BPF_PROG_TYPE_CGROUP_SKB); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + cgrp = cgroup_get_from_fd(attr->target_fd); + if (IS_ERR(cgrp)) { + bpf_prog_put(prog); + return PTR_ERR(cgrp); + } + + cgroup_bpf_update(cgrp, prog, attr->attach_type); + cgroup_put(cgrp); + break; + + default: + return -EINVAL; + } + + return 0; +} + +#define BPF_PROG_DETACH_LAST_FIELD attach_type + +static int bpf_prog_detach(const union bpf_attr *attr) +{ + struct cgroup *cgrp; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (CHECK_ATTR(BPF_PROG_DETACH)) + return -EINVAL; + + switch (attr->attach_type) { + case BPF_CGROUP_INET_INGRESS: + case BPF_CGROUP_INET_EGRESS: + cgrp = cgroup_get_from_fd(attr->target_fd); + if (IS_ERR(cgrp)) + return PTR_ERR(cgrp); + + cgroup_bpf_update(cgrp, NULL, attr->attach_type); + cgroup_put(cgrp); + break; + + default: + return -EINVAL; + } + + return 0; +} +#endif /* CONFIG_CGROUP_BPF */ + SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) { union bpf_attr attr = {}; @@ -901,6 +972,16 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz case BPF_OBJ_GET: err = bpf_obj_get(&attr); break; + +#ifdef CONFIG_CGROUP_BPF + case BPF_PROG_ATTACH: + err = bpf_prog_attach(&attr); + break; + case BPF_PROG_DETACH: + err = bpf_prog_detach(&attr); + break; +#endif + default: err = -EINVAL; break; -- cgit v1.1 From c11cd3a6ec3a817c6b71b00c559e25d855f7e5b4 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 23 Nov 2016 16:52:28 +0100 Subject: net: filter: run cgroup eBPF ingress programs If the cgroup associated with the receiving socket has an eBPF programs installed, run them from sk_filter_trim_cap(). eBPF programs used in this context are expected to either return 1 to let the packet pass, or != 1 to drop them. The programs have access to the skb through bpf_skb_load_bytes(), and the payload starts at the network headers (L3). Note that cgroup_bpf_run_filter() is stubbed out as static inline nop for !CONFIG_CGROUP_BPF, and is otherwise guarded by a static key if the feature is unused. Signed-off-by: Daniel Mack Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/core/filter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index 2de302d..ea315af 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -78,6 +78,10 @@ int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap) if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC)) return -ENOMEM; + err = BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb); + if (err) + return err; + err = security_sock_rcv_skb(sk, skb); if (err) return err; -- cgit v1.1 From 33b486793cb31311f3a91ae4fe4be5926e7677b0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 23 Nov 2016 16:52:29 +0100 Subject: net: ipv4, ipv6: run cgroup eBPF egress programs If the cgroup associated with the receiving socket has an eBPF programs installed, run them from ip_output(), ip6_output() and ip_mc_output(). From mentioned functions we have two socket contexts as per 7026b1ddb6b8 ("netfilter: Pass socket pointer down through okfn()."). We explicitly need to use sk instead of skb->sk here, since otherwise the same program would run multiple times on egress when encap devices are involved, which is not desired in our case. eBPF programs used in this context are expected to either return 1 to let the packet pass, or != 1 to drop them. The programs have access to the skb through bpf_skb_load_bytes(), and the payload starts at the network headers (L3). Note that cgroup_bpf_run_filter() is stubbed out as static inline nop for !CONFIG_CGROUP_BPF, and is otherwise guarded by a static key if the feature is unused. Signed-off-by: Daniel Mack Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/ipv4/ip_output.c | 26 ++++++++++++++++++++++++-- net/ipv6/ip6_output.c | 9 +++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 358f2c8..9af2b78 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -74,6 +74,7 @@ #include #include #include +#include #include #include #include @@ -285,6 +286,13 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk, static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb) { unsigned int mtu; + int ret; + + ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb); + if (ret) { + kfree_skb(skb); + return ret; + } #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM) /* Policy lookup after SNAT yielded a new policy */ @@ -303,6 +311,20 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk return ip_finish_output2(net, sk, skb); } +static int ip_mc_finish_output(struct net *net, struct sock *sk, + struct sk_buff *skb) +{ + int ret; + + ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb); + if (ret) { + kfree_skb(skb); + return ret; + } + + return dev_loopback_xmit(net, sk, skb); +} + int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct rtable *rt = skb_rtable(skb); @@ -340,7 +362,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (newskb) NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, newskb, NULL, newskb->dev, - dev_loopback_xmit); + ip_mc_finish_output); } /* Multicasts with ttl 0 must not go beyond the host */ @@ -356,7 +378,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (newskb) NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, newskb, NULL, newskb->dev, - dev_loopback_xmit); + ip_mc_finish_output); } return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 312cbd0..70d0de40 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -131,6 +132,14 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb) { + int ret; + + ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb); + if (ret) { + kfree_skb(skb); + return ret; + } + if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || dst_allfrag(skb_dst(skb)) || (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)) -- cgit v1.1 From d8c5b17f2bc0de09fbbfa14d90e8168163a579e7 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 23 Nov 2016 16:52:30 +0100 Subject: samples: bpf: add userspace example for attaching eBPF programs to cgroups Add a simple userpace program to demonstrate the new API to attach eBPF programs to cgroups. This is what it does: * Create arraymap in kernel with 4 byte keys and 8 byte values * Load eBPF program The eBPF program accesses the map passed in to store two pieces of information. The number of invocations of the program, which maps to the number of packets received, is stored to key 0. Key 1 is incremented on each iteration by the number of bytes stored in the skb. * Detach any eBPF program previously attached to the cgroup * Attach the new program to the cgroup using BPF_PROG_ATTACH * Once a second, read map[0] and map[1] to see how many bytes and packets were seen on any socket of tasks in the given cgroup. The program takes a cgroup path as 1st argument, and either "ingress" or "egress" as 2nd. Optionally, "drop" can be passed as 3rd argument, which will make the generated eBPF program return 0 instead of 1, so the kernel will drop the packet. libbpf gained two new wrappers for the new syscall commands. Signed-off-by: Daniel Mack Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 2 + samples/bpf/libbpf.c | 21 ++++++ samples/bpf/libbpf.h | 3 + samples/bpf/test_cgrp2_attach.c | 147 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 samples/bpf/test_cgrp2_attach.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index f394ac6..fb17206 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -22,6 +22,7 @@ hostprogs-y += spintest hostprogs-y += map_perf_test hostprogs-y += test_overhead hostprogs-y += test_cgrp2_array_pin +hostprogs-y += test_cgrp2_attach hostprogs-y += xdp1 hostprogs-y += xdp2 hostprogs-y += test_current_task_under_cgroup @@ -49,6 +50,7 @@ spintest-objs := bpf_load.o libbpf.o spintest_user.o map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o +test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o xdp1-objs := bpf_load.o libbpf.o xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := bpf_load.o libbpf.o xdp1_user.o diff --git a/samples/bpf/libbpf.c b/samples/bpf/libbpf.c index 9969e35..9ce707b 100644 --- a/samples/bpf/libbpf.c +++ b/samples/bpf/libbpf.c @@ -104,6 +104,27 @@ int bpf_prog_load(enum bpf_prog_type prog_type, return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); } +int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type) +{ + union bpf_attr attr = { + .target_fd = target_fd, + .attach_bpf_fd = prog_fd, + .attach_type = type, + }; + + return syscall(__NR_bpf, BPF_PROG_ATTACH, &attr, sizeof(attr)); +} + +int bpf_prog_detach(int target_fd, enum bpf_attach_type type) +{ + union bpf_attr attr = { + .target_fd = target_fd, + .attach_type = type, + }; + + return syscall(__NR_bpf, BPF_PROG_DETACH, &attr, sizeof(attr)); +} + int bpf_obj_pin(int fd, const char *pathname) { union bpf_attr attr = { diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index de96a93..94a901d 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h @@ -15,6 +15,9 @@ int bpf_prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, int insn_len, const char *license, int kern_version); +int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type); +int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); + int bpf_obj_pin(int fd, const char *pathname); int bpf_obj_get(const char *pathname); diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c new file mode 100644 index 0000000..63ef208 --- /dev/null +++ b/samples/bpf/test_cgrp2_attach.c @@ -0,0 +1,147 @@ +/* eBPF example program: + * + * - Creates arraymap in kernel with 4 bytes keys and 8 byte values + * + * - Loads eBPF program + * + * The eBPF program accesses the map passed in to store two pieces of + * information. The number of invocations of the program, which maps + * to the number of packets received, is stored to key 0. Key 1 is + * incremented on each iteration by the number of bytes stored in + * the skb. + * + * - Detaches any eBPF program previously attached to the cgroup + * + * - Attaches the new program to a cgroup using BPF_PROG_ATTACH + * + * - Every second, reads map[0] and map[1] to see how many bytes and + * packets were seen on any socket of tasks in the given cgroup. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libbpf.h" + +enum { + MAP_KEY_PACKETS, + MAP_KEY_BYTES, +}; + +static int prog_load(int map_fd, int verdict) +{ + struct bpf_insn prog[] = { + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* save r6 so it's not clobbered by BPF_CALL */ + + /* Count packets */ + BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_PACKETS), /* r0 = 0 */ + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* load map fd to r1 */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */ + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */ + + /* Count bytes */ + BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_BYTES), /* r0 = 1 */ + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, offsetof(struct __sk_buff, len)), /* r1 = skb->len */ + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */ + + BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ + BPF_EXIT_INSN(), + }; + + return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SKB, + prog, sizeof(prog), "GPL", 0); +} + +static int usage(const char *argv0) +{ + printf("Usage: %s [drop]\n", argv0); + return EXIT_FAILURE; +} + +int main(int argc, char **argv) +{ + int cg_fd, map_fd, prog_fd, key, ret; + long long pkt_cnt, byte_cnt; + enum bpf_attach_type type; + int verdict = 1; + + if (argc < 3) + return usage(argv[0]); + + if (strcmp(argv[2], "ingress") == 0) + type = BPF_CGROUP_INET_INGRESS; + else if (strcmp(argv[2], "egress") == 0) + type = BPF_CGROUP_INET_EGRESS; + else + return usage(argv[0]); + + if (argc > 3 && strcmp(argv[3], "drop") == 0) + verdict = 0; + + cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY); + if (cg_fd < 0) { + printf("Failed to open cgroup path: '%s'\n", strerror(errno)); + return EXIT_FAILURE; + } + + map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, + sizeof(key), sizeof(byte_cnt), + 256, 0); + if (map_fd < 0) { + printf("Failed to create map: '%s'\n", strerror(errno)); + return EXIT_FAILURE; + } + + prog_fd = prog_load(map_fd, verdict); + printf("Output from kernel verifier:\n%s\n-------\n", bpf_log_buf); + + if (prog_fd < 0) { + printf("Failed to load prog: '%s'\n", strerror(errno)); + return EXIT_FAILURE; + } + + ret = bpf_prog_detach(cg_fd, type); + printf("bpf_prog_detach() returned '%s' (%d)\n", strerror(errno), errno); + + ret = bpf_prog_attach(prog_fd, cg_fd, type); + if (ret < 0) { + printf("Failed to attach prog to cgroup: '%s'\n", + strerror(errno)); + return EXIT_FAILURE; + } + + while (1) { + key = MAP_KEY_PACKETS; + assert(bpf_lookup_elem(map_fd, &key, &pkt_cnt) == 0); + + key = MAP_KEY_BYTES; + assert(bpf_lookup_elem(map_fd, &key, &byte_cnt) == 0); + + printf("cgroup received %lld packets, %lld bytes\n", + pkt_cnt, byte_cnt); + sleep(1); + } + + return EXIT_SUCCESS; +} -- cgit v1.1 From f52dffe049ee11ecc02588a118fbe4092672fbaa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 23 Nov 2016 08:44:56 -0800 Subject: net: properly flush delay-freed skbs Typical NAPI drivers use napi_consume_skb(skb) at TX completion time. This put skb in a percpu special queue, napi_alloc_cache, to get bulk frees. It turns out the queue is not flushed and hits the NAPI_SKB_CACHE_SIZE limit quite often, with skbs that were queued hundreds of usec earlier. I measured this can take ~6000 nsec to perform one flush. __kfree_skb_flush() can be called from two points right now : 1) From net_tx_action(), but only for skbs that were queued to sd->completion_queue. -> Irrelevant for NAPI drivers in normal operation. 2) From net_rx_action(), but only under high stress or if RPS/RFS has a pending action. This patch changes net_rx_action() to perform the flush in all cases and after more urgent operations happened (like kicking remote CPUS for RPS/RFS). Signed-off-by: Eric Dumazet Cc: Jesper Dangaard Brouer Cc: Alexander Duyck Acked-by: Alexander Duyck Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- net/core/dev.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index f71b34a..048b46b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5260,7 +5260,7 @@ static __latent_entropy void net_rx_action(struct softirq_action *h) if (list_empty(&list)) { if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll)) - return; + goto out; break; } @@ -5278,7 +5278,6 @@ static __latent_entropy void net_rx_action(struct softirq_action *h) } } - __kfree_skb_flush(); local_irq_disable(); list_splice_tail_init(&sd->poll_list, &list); @@ -5288,6 +5287,8 @@ static __latent_entropy void net_rx_action(struct softirq_action *h) __raise_softirq_irqoff(NET_RX_SOFTIRQ); net_rps_action_and_irq_enable(sd); +out: + __kfree_skb_flush(); } struct netdev_adjacent { -- cgit v1.1 From a7dac9f9c1695d7470b2f4e7447513e4a71b7c1b Mon Sep 17 00:00:00 2001 From: Woojung Huh Date: Wed, 23 Nov 2016 23:10:33 +0000 Subject: phy: fix error case of phy_led_triggers_(un)register When phy_init_hw() fails at phy_attach_direct(); - phy_detach() calls phy_led_triggers_unregister() without previous call of phy_led_triggers_register(). - still call phy_led_triggers_register() and cause memory leak. Fixes: 2e0bc452f472 ("net: phy: leds: add support for led triggers on phy link state change") Signed-off-by: Woojung Huh Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 6 +++--- drivers/net/phy/phy_led_triggers.c | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 9e8f048..ba86c19 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -914,15 +914,15 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, */ err = phy_init_hw(phydev); if (err) - phy_detach(phydev); - else - phy_resume(phydev); + goto error; + phy_resume(phydev); phy_led_triggers_register(phydev); return err; error: + phy_detach(phydev); put_device(d); module_put(bus->owner); return err; diff --git a/drivers/net/phy/phy_led_triggers.c b/drivers/net/phy/phy_led_triggers.c index cda600a..fa62bdf 100644 --- a/drivers/net/phy/phy_led_triggers.c +++ b/drivers/net/phy/phy_led_triggers.c @@ -130,7 +130,5 @@ void phy_led_triggers_unregister(struct phy_device *phy) for (i = 0; i < phy->phy_num_led_triggers; i++) phy_led_trigger_unregister(&phy->phy_led_triggers[i]); - - devm_kfree(&phy->mdio.dev, phy->phy_led_triggers); } EXPORT_SYMBOL_GPL(phy_led_triggers_unregister); -- cgit v1.1 From 5271156b1a93eeb9a1a48d8a4be5074b3c31a5cd Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Thu, 24 Nov 2016 14:48:00 +0530 Subject: net: thunderx: 80xx BGX0 configuration changes On 80xx only one lane of DLM0 and DLM1 (of BGX0) can be used , so even though lmac count may be 2 but LMAC1 should use serdes lane of DLM1. Since it's not possible to distinguish 80xx from 81xx as PCI devid are same, this patch adds this config support by replying on what firmware configures the lmacs with. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index 050e21f..1d6214b 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -970,11 +970,25 @@ static void bgx_set_lmac_config(struct bgx *bgx, u8 idx) lmac_set_training(bgx, lmac, lmac->lmacid); lmac_set_lane2sds(bgx, lmac); - /* Set LMAC type of other lmac on same DLM i.e LMAC 1/3 */ olmac = &bgx->lmac[idx + 1]; - olmac->lmac_type = lmac->lmac_type; + /* Check if other LMAC on the same DLM is already configured by + * firmware, if so use the same config or else set as same, as + * that of LMAC 0/2. + * This check is needed as on 80xx only one lane of each of the + * DLM of BGX0 is used, so have to rely on firmware for + * distingushing 80xx from 81xx. + */ + cmr_cfg = bgx_reg_read(bgx, idx + 1, BGX_CMRX_CFG); + lmac_type = (u8)((cmr_cfg >> 8) & 0x07); + lane_to_sds = (u8)(cmr_cfg & 0xFF); + if ((lmac_type == 0) && (lane_to_sds == 0xE4)) { + olmac->lmac_type = lmac->lmac_type; + lmac_set_lane2sds(bgx, olmac); + } else { + olmac->lmac_type = lmac_type; + olmac->lane_to_sds = lane_to_sds; + } lmac_set_training(bgx, olmac, olmac->lmacid); - lmac_set_lane2sds(bgx, olmac); } } -- cgit v1.1 From 1cc702591bae9f70b1aa2f9cb60241961bc1858a Mon Sep 17 00:00:00 2001 From: Thanneeru Srinivasulu Date: Thu, 24 Nov 2016 14:48:01 +0530 Subject: net: thunderx: Add ethtool support for supported ports and link modes. Signed-off-by: Thanneeru Srinivasulu Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nic.h | 2 ++ drivers/net/ethernet/cavium/thunder/nic_main.c | 1 + .../net/ethernet/cavium/thunder/nicvf_ethtool.c | 36 ++++++++++++++++++++-- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 1 + drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 1 + 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h index 86bd93c..be8404a 100644 --- a/drivers/net/ethernet/cavium/thunder/nic.h +++ b/drivers/net/ethernet/cavium/thunder/nic.h @@ -292,6 +292,7 @@ struct nicvf { u8 node; u8 cpi_alg; bool link_up; + u8 mac_type; u8 duplex; u32 speed; bool tns_mode; @@ -446,6 +447,7 @@ struct bgx_stats_msg { /* Physical interface link status */ struct bgx_link_status { u8 msg; + u8 mac_type; u8 link_up; u8 duplex; u32 speed; diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c index 6677b96..b87d416 100644 --- a/drivers/net/ethernet/cavium/thunder/nic_main.c +++ b/drivers/net/ethernet/cavium/thunder/nic_main.c @@ -1258,6 +1258,7 @@ static void nic_poll_for_link(struct work_struct *work) mbx.link_status.link_up = link.link_up; mbx.link_status.duplex = link.duplex; mbx.link_status.speed = link.speed; + mbx.link_status.mac_type = link.mac_type; nic_send_msg_to_vf(nic, vf, &mbx); } } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index 432bf6b..d4d76a7 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -130,12 +130,42 @@ static int nicvf_get_settings(struct net_device *netdev, return 0; } - if (nic->speed <= 1000) { - cmd->port = PORT_MII; + switch (nic->speed) { + case SPEED_1000: + cmd->port = PORT_MII | PORT_TP; cmd->autoneg = AUTONEG_ENABLE; - } else { + cmd->supported |= SUPPORTED_MII | SUPPORTED_TP; + cmd->supported |= SUPPORTED_1000baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_10baseT_Half; + cmd->supported |= SUPPORTED_Autoneg; + cmd->advertising |= ADVERTISED_1000baseT_Full | + ADVERTISED_1000baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_10baseT_Half; + break; + case SPEED_10000: + if (nic->mac_type == BGX_MODE_RXAUI) { + cmd->port = PORT_TP; + cmd->supported |= SUPPORTED_TP; + } else { + cmd->port = PORT_FIBRE; + cmd->supported |= SUPPORTED_FIBRE; + } + cmd->autoneg = AUTONEG_DISABLE; + cmd->supported |= SUPPORTED_10000baseT_Full; + break; + case SPEED_40000: cmd->port = PORT_FIBRE; cmd->autoneg = AUTONEG_DISABLE; + cmd->supported |= SUPPORTED_FIBRE; + cmd->supported |= SUPPORTED_40000baseCR4_Full; + break; } cmd->duplex = nic->duplex; ethtool_cmd_speed_set(cmd, nic->speed); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 7c2c373..c6c2303 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -221,6 +221,7 @@ static void nicvf_handle_mbx_intr(struct nicvf *nic) nic->link_up = mbx.link_status.link_up; nic->duplex = mbx.link_status.duplex; nic->speed = mbx.link_status.speed; + nic->mac_type = mbx.link_status.mac_type; if (nic->link_up) { netdev_info(nic->netdev, "%s: Link is Up %d Mbps %s\n", nic->netdev->name, nic->speed, diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index 1d6214b..29c727f 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -161,6 +161,7 @@ void bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status) return; lmac = &bgx->lmac[lmacid]; + link->mac_type = lmac->lmac_type; link->link_up = lmac->link_up; link->duplex = lmac->last_duplex; link->speed = lmac->last_speed; -- cgit v1.1 From d5b2d7a7184062c436f9a2b237e77bdb8e06a936 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Thu, 24 Nov 2016 14:48:02 +0530 Subject: net: thunderx: Configure RED and backpressure levels This patch enables moving average calculation of Rx pkt's resources and configures RED and backpressure levels for both CQ and RBDR. Also initialize SQ's CQ_LIMIT properly. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nic_main.c | 9 ++++++++ drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 9 ++++++-- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 24 +++++++++++++++++----- drivers/net/ethernet/cavium/thunder/q_struct.h | 8 ++++++-- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c index b87d416..17490ec 100644 --- a/drivers/net/ethernet/cavium/thunder/nic_main.c +++ b/drivers/net/ethernet/cavium/thunder/nic_main.c @@ -809,6 +809,15 @@ static int nic_config_loopback(struct nicpf *nic, struct set_loopback *lbk) bgx_lmac_internal_loopback(nic->node, bgx_idx, lmac_idx, lbk->enable); + /* Enable moving average calculation. + * Keep the LVL/AVG delay to HW enforced minimum so that, not too many + * packets sneek in between average calculations. + */ + nic_reg_write(nic, NIC_PF_CQ_AVG_CFG, + (BIT_ULL(20) | 0x2ull << 14 | 0x1)); + nic_reg_write(nic, NIC_PF_RRM_AVG_CFG, + (BIT_ULL(20) | 0x3ull << 14 | 0x1)); + return 0; } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 747ef08..7b336cd 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -544,14 +544,18 @@ static void nicvf_rcv_queue_config(struct nicvf *nic, struct queue_set *qs, nicvf_send_msg_to_pf(nic, &mbx); mbx.rq.msg = NIC_MBOX_MSG_RQ_BP_CFG; - mbx.rq.cfg = (1ULL << 63) | (1ULL << 62) | (qs->vnic_id << 0); + mbx.rq.cfg = BIT_ULL(63) | BIT_ULL(62) | + (RQ_PASS_RBDR_LVL << 16) | (RQ_PASS_CQ_LVL << 8) | + (qs->vnic_id << 0); nicvf_send_msg_to_pf(nic, &mbx); /* RQ drop config * Enable CQ drop to reserve sufficient CQEs for all tx packets */ mbx.rq.msg = NIC_MBOX_MSG_RQ_DROP_CFG; - mbx.rq.cfg = (1ULL << 62) | (RQ_CQ_DROP << 8); + mbx.rq.cfg = BIT_ULL(63) | BIT_ULL(62) | + (RQ_PASS_RBDR_LVL << 40) | (RQ_DROP_RBDR_LVL << 32) | + (RQ_PASS_CQ_LVL << 16) | (RQ_DROP_CQ_LVL << 8); nicvf_send_msg_to_pf(nic, &mbx); if (!nic->sqs_mode && (qidx == 0)) { @@ -650,6 +654,7 @@ static void nicvf_snd_queue_config(struct nicvf *nic, struct queue_set *qs, sq_cfg.ldwb = 0; sq_cfg.qsize = SND_QSIZE; sq_cfg.tstmp_bgx_intf = 0; + sq_cfg.cq_limit = 0; nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, *(u64 *)&sq_cfg); /* Set threshold value for interrupt generation */ diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 2e3c940..20511f2 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -85,12 +85,26 @@ #define MAX_CQES_FOR_TX ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) * \ MAX_CQE_PER_PKT_XMIT) -/* Calculate number of CQEs to reserve for all SQEs. - * Its 1/256th level of CQ size. - * '+ 1' to account for pipelining + +/* RED and Backpressure levels of CQ for pkt reception + * For CQ, level is a measure of emptiness i.e 0x0 means full + * eg: For CQ of size 4K, and for pass/drop levels of 128/96 + * HW accepts pkt if unused CQE >= 2048 + * RED accepts pkt if unused CQE < 2048 & >= 1536 + * DROPs pkts if unused CQE < 1536 + */ +#define RQ_PASS_CQ_LVL 128ULL +#define RQ_DROP_CQ_LVL 96ULL + +/* RED and Backpressure levels of RBDR for pkt reception + * For RBDR, level is a measure of fullness i.e 0x0 means empty + * eg: For RBDR of size 8K, and for pass/drop levels of 4/0 + * HW accepts pkt if unused RBs >= 256 + * RED accepts pkt if unused RBs < 256 & >= 0 + * DROPs pkts if unused RBs < 0 */ -#define RQ_CQ_DROP ((256 / (CMP_QUEUE_LEN / \ - (CMP_QUEUE_LEN - MAX_CQES_FOR_TX))) + 1) +#define RQ_PASS_RBDR_LVL 8ULL +#define RQ_DROP_RBDR_LVL 0ULL /* Descriptor size in bytes */ #define SND_QUEUE_DESC_SIZE 16 diff --git a/drivers/net/ethernet/cavium/thunder/q_struct.h b/drivers/net/ethernet/cavium/thunder/q_struct.h index 9e6d987..f363472 100644 --- a/drivers/net/ethernet/cavium/thunder/q_struct.h +++ b/drivers/net/ethernet/cavium/thunder/q_struct.h @@ -624,7 +624,9 @@ struct cq_cfg { struct sq_cfg { #if defined(__BIG_ENDIAN_BITFIELD) - u64 reserved_20_63:44; + u64 reserved_32_63:32; + u64 cq_limit:8; + u64 reserved_20_23:4; u64 ena:1; u64 reserved_18_18:1; u64 reset:1; @@ -642,7 +644,9 @@ struct sq_cfg { u64 reset:1; u64 reserved_18_18:1; u64 ena:1; - u64 reserved_20_63:44; + u64 reserved_20_23:4; + u64 cq_limit:8; + u64 reserved_32_63:32; #endif }; -- cgit v1.1 From 430da208089ba74ff3d2992d80387c8ea5cabd0e Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Thu, 24 Nov 2016 14:48:03 +0530 Subject: net: thunderx: Pause frame support Enable pause frames on both Rx and Tx side, configure pause interval e.t.c. Also support for enable/disable pause frames on Rx/Tx via ethtool has been added. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nic.h | 17 +++++++ drivers/net/ethernet/cavium/thunder/nic_main.c | 27 +++++++++++ .../net/ethernet/cavium/thunder/nicvf_ethtool.c | 51 +++++++++++++++++++++ drivers/net/ethernet/cavium/thunder/nicvf_main.c | 6 +++ drivers/net/ethernet/cavium/thunder/thunder_bgx.c | 53 ++++++++++++++++++++++ drivers/net/ethernet/cavium/thunder/thunder_bgx.h | 12 +++++ 6 files changed, 166 insertions(+) diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h index be8404a..e739c71 100644 --- a/drivers/net/ethernet/cavium/thunder/nic.h +++ b/drivers/net/ethernet/cavium/thunder/nic.h @@ -149,6 +149,12 @@ struct nicvf_rss_info { u64 key[RSS_HASH_KEY_SIZE]; } ____cacheline_aligned_in_smp; +struct nicvf_pfc { + u8 autoneg; + u8 fc_rx; + u8 fc_tx; +}; + enum rx_stats_reg_offset { RX_OCTS = 0x0, RX_UCAST = 0x1, @@ -298,6 +304,7 @@ struct nicvf { bool tns_mode; bool loopback_supported; struct nicvf_rss_info rss_info; + struct nicvf_pfc pfc; struct tasklet_struct qs_err_task; struct work_struct reset_task; @@ -358,6 +365,7 @@ struct nicvf { #define NIC_MBOX_MSG_SNICVF_PTR 0x15 /* Send sqet nicvf ptr to PVF */ #define NIC_MBOX_MSG_LOOPBACK 0x16 /* Set interface in loopback */ #define NIC_MBOX_MSG_RESET_STAT_COUNTER 0x17 /* Reset statistics counters */ +#define NIC_MBOX_MSG_PFC 0x18 /* Pause frame control */ #define NIC_MBOX_MSG_CFG_DONE 0xF0 /* VF configuration done */ #define NIC_MBOX_MSG_SHUTDOWN 0xF1 /* VF is being shutdown */ @@ -500,6 +508,14 @@ struct reset_stat_cfg { u16 sq_stat_mask; }; +struct pfc { + u8 msg; + u8 get; /* Get or set PFC settings */ + u8 autoneg; + u8 fc_rx; + u8 fc_tx; +}; + /* 128 bit shared memory between PF and each VF */ union nic_mbx { struct { u8 msg; } msg; @@ -518,6 +534,7 @@ union nic_mbx { struct nicvf_ptr nicvf; struct set_loopback lbk; struct reset_stat_cfg reset_stat; + struct pfc pfc; }; #define NIC_NODE_ID_MASK 0x03 diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c index 17490ec..767234e 100644 --- a/drivers/net/ethernet/cavium/thunder/nic_main.c +++ b/drivers/net/ethernet/cavium/thunder/nic_main.c @@ -898,6 +898,30 @@ static void nic_enable_vf(struct nicpf *nic, int vf, bool enable) bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, enable); } +static void nic_pause_frame(struct nicpf *nic, int vf, struct pfc *cfg) +{ + int bgx, lmac; + struct pfc pfc; + union nic_mbx mbx = {}; + + if (vf >= nic->num_vf_en) + return; + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + if (cfg->get) { + bgx_lmac_get_pfc(nic->node, bgx, lmac, &pfc); + mbx.pfc.msg = NIC_MBOX_MSG_PFC; + mbx.pfc.autoneg = pfc.autoneg; + mbx.pfc.fc_rx = pfc.fc_rx; + mbx.pfc.fc_tx = pfc.fc_tx; + nic_send_msg_to_vf(nic, vf, &mbx); + } else { + bgx_lmac_set_pfc(nic->node, bgx, lmac, cfg); + nic_mbx_send_ack(nic, vf); + } +} + /* Interrupt handler to handle mailbox messages from VFs */ static void nic_handle_mbx_intr(struct nicpf *nic, int vf) { @@ -1037,6 +1061,9 @@ static void nic_handle_mbx_intr(struct nicpf *nic, int vf) case NIC_MBOX_MSG_RESET_STAT_COUNTER: ret = nic_reset_stat_counters(nic, vf, &mbx.reset_stat); break; + case NIC_MBOX_MSG_PFC: + nic_pause_frame(nic, vf, &mbx.pfc); + goto unlock; default: dev_err(&nic->pdev->dev, "Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index d4d76a7..b048241 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -720,6 +720,55 @@ static int nicvf_set_channels(struct net_device *dev, return err; } +static void nicvf_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + struct nicvf *nic = netdev_priv(dev); + union nic_mbx mbx = {}; + + /* Supported only for 10G/40G interfaces */ + if ((nic->mac_type == BGX_MODE_SGMII) || + (nic->mac_type == BGX_MODE_QSGMII) || + (nic->mac_type == BGX_MODE_RGMII)) + return; + + mbx.pfc.msg = NIC_MBOX_MSG_PFC; + mbx.pfc.get = 1; + if (!nicvf_send_msg_to_pf(nic, &mbx)) { + pause->autoneg = nic->pfc.autoneg; + pause->rx_pause = nic->pfc.fc_rx; + pause->tx_pause = nic->pfc.fc_tx; + } +} + +static int nicvf_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + struct nicvf *nic = netdev_priv(dev); + union nic_mbx mbx = {}; + + /* Supported only for 10G/40G interfaces */ + if ((nic->mac_type == BGX_MODE_SGMII) || + (nic->mac_type == BGX_MODE_QSGMII) || + (nic->mac_type == BGX_MODE_RGMII)) + return -EOPNOTSUPP; + + if (pause->autoneg) + return -EOPNOTSUPP; + + mbx.pfc.msg = NIC_MBOX_MSG_PFC; + mbx.pfc.get = 0; + mbx.pfc.fc_rx = pause->rx_pause; + mbx.pfc.fc_tx = pause->tx_pause; + if (nicvf_send_msg_to_pf(nic, &mbx)) + return -EAGAIN; + + nic->pfc.fc_rx = pause->rx_pause; + nic->pfc.fc_tx = pause->tx_pause; + + return 0; +} + static const struct ethtool_ops nicvf_ethtool_ops = { .get_settings = nicvf_get_settings, .get_link = nicvf_get_link, @@ -741,6 +790,8 @@ static const struct ethtool_ops nicvf_ethtool_ops = { .set_rxfh = nicvf_set_rxfh, .get_channels = nicvf_get_channels, .set_channels = nicvf_set_channels, + .get_pauseparam = nicvf_get_pauseparam, + .set_pauseparam = nicvf_set_pauseparam, .get_ts_info = ethtool_op_get_ts_info, }; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index c6c2303..1eacec8 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -256,6 +256,12 @@ static void nicvf_handle_mbx_intr(struct nicvf *nic) nic->pnicvf = (struct nicvf *)mbx.nicvf.nicvf; nic->pf_acked = true; break; + case NIC_MBOX_MSG_PFC: + nic->pfc.autoneg = mbx.pfc.autoneg; + nic->pfc.fc_rx = mbx.pfc.fc_rx; + nic->pfc.fc_tx = mbx.pfc.fc_tx; + nic->pf_acked = true; + break; default: netdev_err(nic->netdev, "Invalid message from PF, msg 0x%x\n", mbx.msg.msg); diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index 29c727f..9211c75 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -212,6 +212,47 @@ void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable) } EXPORT_SYMBOL(bgx_lmac_rx_tx_enable); +void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause) +{ + struct pfc *pfc = (struct pfc *)pause; + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + struct lmac *lmac; + u64 cfg; + + if (!bgx) + return; + lmac = &bgx->lmac[lmacid]; + if (lmac->is_sgmii) + return; + + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL); + pfc->fc_rx = cfg & RX_EN; + pfc->fc_tx = cfg & TX_EN; + pfc->autoneg = 0; +} +EXPORT_SYMBOL(bgx_lmac_get_pfc); + +void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause) +{ + struct pfc *pfc = (struct pfc *)pause; + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + struct lmac *lmac; + u64 cfg; + + if (!bgx) + return; + lmac = &bgx->lmac[lmacid]; + if (lmac->is_sgmii) + return; + + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL); + cfg &= ~(RX_EN | TX_EN); + cfg |= (pfc->fc_rx ? RX_EN : 0x00); + cfg |= (pfc->fc_tx ? TX_EN : 0x00); + bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, cfg); +} +EXPORT_SYMBOL(bgx_lmac_set_pfc); + static void bgx_sgmii_change_link_state(struct lmac *lmac) { struct bgx *bgx = lmac->bgx; @@ -525,6 +566,18 @@ static int bgx_lmac_xaui_init(struct bgx *bgx, struct lmac *lmac) cfg |= SMU_TX_CTL_DIC_EN; bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg); + /* Enable receive and transmission of pause frames */ + bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, ((0xffffULL << 32) | + BCK_EN | DRP_EN | TX_EN | RX_EN)); + /* Configure pause time and interval */ + bgx_reg_write(bgx, lmacid, + BGX_SMUX_TX_PAUSE_PKT_TIME, DEFAULT_PAUSE_TIME); + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL); + cfg &= ~0xFFFFull; + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL, + cfg | (DEFAULT_PAUSE_TIME - 0x1000)); + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_ZERO, 0x01); + /* take lmac_count into account */ bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1)); /* max packet size */ diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h index 01cc7c8..c18ebfe 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h @@ -27,6 +27,7 @@ #define MAX_BGX_CHANS_PER_LMAC 16 #define MAX_DMAC_PER_LMAC 8 #define MAX_FRAME_SIZE 9216 +#define DEFAULT_PAUSE_TIME 0xFFFF #define BGX_ID_MASK 0x3 @@ -126,7 +127,10 @@ #define SMU_RX_CTL_STATUS (3ull << 0) #define BGX_SMUX_TX_APPEND 0x20100 #define SMU_TX_APPEND_FCS_D BIT_ULL(2) +#define BGX_SMUX_TX_PAUSE_PKT_TIME 0x20110 #define BGX_SMUX_TX_MIN_PKT 0x20118 +#define BGX_SMUX_TX_PAUSE_PKT_INTERVAL 0x20120 +#define BGX_SMUX_TX_PAUSE_ZERO 0x20138 #define BGX_SMUX_TX_INT 0x20140 #define BGX_SMUX_TX_CTL 0x20178 #define SMU_TX_CTL_DIC_EN BIT_ULL(0) @@ -136,6 +140,11 @@ #define BGX_SMUX_CTL 0x20200 #define SMU_CTL_RX_IDLE BIT_ULL(0) #define SMU_CTL_TX_IDLE BIT_ULL(1) +#define BGX_SMUX_CBFC_CTL 0x20218 +#define RX_EN BIT_ULL(0) +#define TX_EN BIT_ULL(1) +#define BCK_EN BIT_ULL(2) +#define DRP_EN BIT_ULL(3) #define BGX_GMP_PCS_MRX_CTL 0x30000 #define PCS_MRX_CTL_RST_AN BIT_ULL(9) @@ -207,6 +216,9 @@ void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac); void bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status); void bgx_lmac_internal_loopback(int node, int bgx_idx, int lmac_idx, bool enable); +void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause); +void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause); + void xcv_init_hw(void); void xcv_setup_link(bool link_up, int link_speed); -- cgit v1.1 From eafa6abd99eadb8971c01a4c591295ac4d8d0a9b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 24 Nov 2016 14:03:45 +0300 Subject: net/mlx5: remove a duplicate condition We verified that MLX5_FLOW_CONTEXT_ACTION_COUNT was set on the first line of the function so we don't need to check again here. Signed-off-by: Dan Carpenter Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 68ec4ea..a263d890 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1307,8 +1307,7 @@ static bool counter_is_valid(struct mlx5_fc *counter, u32 action) return false; return (action & (MLX5_FLOW_CONTEXT_ACTION_DROP | - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST)) && - (action & MLX5_FLOW_CONTEXT_ACTION_COUNT); + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST)); } static bool dest_is_valid(struct mlx5_flow_destination *dest, -- cgit v1.1 From ee4a60d898826d34b804a357847c7910aba80b96 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:29 +0100 Subject: mlxsw: spectrum: Remove unused traps Since commit 99724c18fc66 ("mlxsw: spectrum: Introduce support for router interfaces") we no longer rely on flooding traffic to the CPU in order to trap packets intended for the host itself. Therefore, the FDB MC trap can be removed. Remove traps for protocols that are not supported yet. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index e0d7d5a..2f06de3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2785,15 +2785,10 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port, } static const struct mlxsw_rx_listener mlxsw_sp_rx_listener[] = { - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, FDB_MC, TRAP_TO_CPU), - /* Traps for specific L2 packet types, not trapped as FDB MC */ + /* L2 traps */ MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, STP, TRAP_TO_CPU), MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, LACP, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, EAPOL, TRAP_TO_CPU), MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, LLDP, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, MMRP, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, MVRP, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, RPVST, TRAP_TO_CPU), MLXSW_SP_RXL(mlxsw_sp_rx_listener_mark_func, DHCP, MIRROR_TO_CPU), MLXSW_SP_RXL(mlxsw_sp_rx_listener_mark_func, IGMP_QUERY, MIRROR_TO_CPU), MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, IGMP_V1_REPORT, TRAP_TO_CPU), -- cgit v1.1 From 0791051c43ef994149c6d8b3ee7dc8d7e81a16a6 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:30 +0100 Subject: mlxsw: core: Create a generic function to register / unregister traps We have 2 types of HW traps to handle, rx traps and events. The registration workflow for both is very similar. So it only make sense to create one function to handle both. This patch creates a struct to hold the data for both cases. It also creates a registration and an un-registration functions that get this generic struct as input. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 67 ++++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/core.h | 18 ++++++++ 2 files changed, 85 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index bcd7251..a8cff78 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1392,6 +1392,73 @@ void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_event_listener_unregister); +static int mlxsw_core_listener_register(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listener, + void *priv) +{ + if (listener->is_event) + return mlxsw_core_event_listener_register(mlxsw_core, + &listener->u.event_listener, + priv); + else + return mlxsw_core_rx_listener_register(mlxsw_core, + &listener->u.rx_listener, + priv); +} + +static void mlxsw_core_listener_unregister(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listener, + void *priv) +{ + if (listener->is_event) + mlxsw_core_event_listener_unregister(mlxsw_core, + &listener->u.event_listener, + priv); + else + mlxsw_core_rx_listener_unregister(mlxsw_core, + &listener->u.rx_listener, + priv); +} + +int mlxsw_core_trap_register(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listener, void *priv) +{ + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + int err; + + err = mlxsw_core_listener_register(mlxsw_core, listener, priv); + if (err) + return err; + + mlxsw_reg_hpkt_pack(hpkt_pl, listener->action, listener->trap_id); + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); + if (err) + goto err_trap_set; + + return 0; + +err_trap_set: + mlxsw_core_listener_unregister(mlxsw_core, listener, priv); + return err; +} +EXPORT_SYMBOL(mlxsw_core_trap_register); + +void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listener, + void *priv) +{ + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + + if (!listener->is_event) { + mlxsw_reg_hpkt_pack(hpkt_pl, listener->unreg_action, + listener->trap_id); + mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); + } + + mlxsw_core_listener_unregister(mlxsw_core, listener, priv); +} +EXPORT_SYMBOL(mlxsw_core_trap_unregister); + static u64 mlxsw_core_tid_get(struct mlxsw_core *mlxsw_core) { return atomic64_inc_return(&mlxsw_core->emad.tid); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 3de8955..66d97c4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -90,6 +90,17 @@ struct mlxsw_event_listener { enum mlxsw_event_trap_id trap_id; }; +struct mlxsw_listener { + u16 trap_id; + union { + struct mlxsw_rx_listener rx_listener; + struct mlxsw_event_listener event_listener; + } u; + enum mlxsw_reg_hpkt_action action; + enum mlxsw_reg_hpkt_action unreg_action; + bool is_event; +}; + int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, const struct mlxsw_rx_listener *rxl, void *priv); @@ -104,6 +115,13 @@ void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core, const struct mlxsw_event_listener *el, void *priv); +int mlxsw_core_trap_register(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listener, + void *priv); +void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listener, + void *priv); + typedef void mlxsw_reg_trans_cb_t(struct mlxsw_core *mlxsw_core, char *payload, size_t payload_len, unsigned long cb_priv); -- cgit v1.1 From b63da93de89bcb009de89d15d4d01e75a2fc37f9 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:31 +0100 Subject: mlxsw: core: Expose generic macros for rx trap In Spectrum, there is a macro to arrange the traps list. This macro is useful for everyone who is using rx traps. Create a similar macro in core.h for creating the generic listener struct for rx traps. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 66d97c4..5f6fed1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -101,6 +101,20 @@ struct mlxsw_listener { bool is_event; }; +#define MLXSW_RXL(_func, _trap_id, _action, _unreg_action) \ + { \ + .trap_id = MLXSW_TRAP_ID_##_trap_id, \ + .u.rx_listener = \ + { \ + .func = _func, \ + .local_port = MLXSW_PORT_DONT_CARE, \ + .trap_id = MLXSW_TRAP_ID_##_trap_id, \ + }, \ + .action = MLXSW_REG_HPKT_ACTION_##_action, \ + .unreg_action = MLXSW_REG_HPKT_ACTION_##_unreg_action, \ + .is_event = false, \ + } + int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, const struct mlxsw_rx_listener *rxl, void *priv); -- cgit v1.1 From 14eeda99c4a6b6bf92261f980bcd95f345c76b64 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:32 +0100 Subject: mlxsw: spectrum: Use generic listener struct for rx traps Replace the old rx listener struct definitions by the generic ones. Use the new generic registering / unregistering functions for them. Add some macros to organize the trap list. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 95 +++++++++++--------------- 1 file changed, 38 insertions(+), 57 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 2f06de3..efa99af 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2744,8 +2744,8 @@ static void mlxsw_sp_event_unregister(struct mlxsw_sp *mlxsw_sp, mlxsw_core_event_listener_unregister(mlxsw_sp->core, el, mlxsw_sp); } -static void mlxsw_sp_rx_listener_func(struct sk_buff *skb, u8 local_port, - void *priv) +static void mlxsw_sp_rx_listener_no_mark_func(struct sk_buff *skb, + u8 local_port, void *priv) { struct mlxsw_sp *mlxsw_sp = priv; struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; @@ -2773,44 +2773,43 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port, void *priv) { skb->offload_fwd_mark = 1; - return mlxsw_sp_rx_listener_func(skb, local_port, priv); + return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv); } -#define MLXSW_SP_RXL(_func, _trap_id, _action) \ - { \ - .func = _func, \ - .local_port = MLXSW_PORT_DONT_CARE, \ - .trap_id = MLXSW_TRAP_ID_##_trap_id, \ - .action = MLXSW_REG_HPKT_ACTION_##_action, \ - } +#define MLXSW_SP_RXL_NO_MARK(_trap_id, _action) \ + MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action, \ + DISCARD) + +#define MLXSW_SP_RXL_MARK(_trap_id, _action) \ + MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action, \ + DISCARD) -static const struct mlxsw_rx_listener mlxsw_sp_rx_listener[] = { +static const struct mlxsw_listener mlxsw_sp_rx_listener[] = { /* L2 traps */ - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, STP, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, LACP, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, LLDP, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_mark_func, DHCP, MIRROR_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_mark_func, IGMP_QUERY, MIRROR_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, IGMP_V1_REPORT, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, IGMP_V2_REPORT, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, IGMP_V2_LEAVE, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, IGMP_V3_REPORT, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_mark_func, ARPBC, MIRROR_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_mark_func, ARPUC, MIRROR_TO_CPU), + MLXSW_SP_RXL_NO_MARK(STP, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(LACP, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(LLDP, TRAP_TO_CPU), + MLXSW_SP_RXL_MARK(DHCP, MIRROR_TO_CPU), + MLXSW_SP_RXL_MARK(IGMP_QUERY, MIRROR_TO_CPU), + MLXSW_SP_RXL_NO_MARK(IGMP_V1_REPORT, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(IGMP_V2_REPORT, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(IGMP_V2_LEAVE, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(IGMP_V3_REPORT, TRAP_TO_CPU), + MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU), + MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU), /* L3 traps */ - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, MTUERROR, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, TTLERROR, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, LBERROR, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_mark_func, OSPF, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, IP2ME, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, RTR_INGRESS0, TRAP_TO_CPU), - MLXSW_SP_RXL(mlxsw_sp_rx_listener_func, HOST_MISS_IPV4, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(LBERROR, TRAP_TO_CPU), + MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU), }; static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) { char htgt_pl[MLXSW_REG_HTGT_LEN]; - char hpkt_pl[MLXSW_REG_HPKT_LEN]; int i; int err; @@ -2825,50 +2824,32 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) return err; for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { - err = mlxsw_core_rx_listener_register(mlxsw_sp->core, - &mlxsw_sp_rx_listener[i], - mlxsw_sp); + err = mlxsw_core_trap_register(mlxsw_sp->core, + &mlxsw_sp_rx_listener[i], + mlxsw_sp); if (err) goto err_rx_listener_register; - mlxsw_reg_hpkt_pack(hpkt_pl, mlxsw_sp_rx_listener[i].action, - mlxsw_sp_rx_listener[i].trap_id); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); - if (err) - goto err_rx_trap_set; } return 0; -err_rx_trap_set: - mlxsw_core_rx_listener_unregister(mlxsw_sp->core, - &mlxsw_sp_rx_listener[i], - mlxsw_sp); err_rx_listener_register: for (i--; i >= 0; i--) { - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, - mlxsw_sp_rx_listener[i].trap_id); - mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); - - mlxsw_core_rx_listener_unregister(mlxsw_sp->core, - &mlxsw_sp_rx_listener[i], - mlxsw_sp); + mlxsw_core_trap_unregister(mlxsw_sp->core, + &mlxsw_sp_rx_listener[i], + mlxsw_sp); } return err; } static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp) { - char hpkt_pl[MLXSW_REG_HPKT_LEN]; int i; for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, - mlxsw_sp_rx_listener[i].trap_id); - mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); - - mlxsw_core_rx_listener_unregister(mlxsw_sp->core, - &mlxsw_sp_rx_listener[i], - mlxsw_sp); + mlxsw_core_trap_unregister(mlxsw_sp->core, + &mlxsw_sp_rx_listener[i], + mlxsw_sp); } } -- cgit v1.1 From 2332d8c7df9531a0b73ff3a7d2d65b0c99122c21 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:33 +0100 Subject: mlxsw: switchx2: Use generic listener struct for rx traps Reorganize the traps to use the new generic listener struct and functions. Use macros to shorten the traps list. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 127 ++++++------------------- 1 file changed, 27 insertions(+), 100 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 60f19fb..df6afdd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1494,84 +1494,29 @@ err_port_module_info_get: return err; } -static const struct mlxsw_rx_listener mlxsw_sx_rx_listener[] = { - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_FDB_MC, - }, - /* Traps for specific L2 packet types, not trapped as FDB MC */ - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_STP, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_LACP, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_EAPOL, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_LLDP, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_MMRP, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_MVRP, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_RPVST, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_DHCP, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_IGMP_QUERY, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_IGMP_V1_REPORT, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_IGMP_V2_REPORT, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_IGMP_V2_LEAVE, - }, - { - .func = mlxsw_sx_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_IGMP_V3_REPORT, - }, +#define MLXSW_SX_RXL(_trap_id) \ + MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU, FORWARD) + +static const struct mlxsw_listener mlxsw_sx_rx_listener[] = { + MLXSW_SX_RXL(FDB_MC), + MLXSW_SX_RXL(STP), + MLXSW_SX_RXL(LACP), + MLXSW_SX_RXL(EAPOL), + MLXSW_SX_RXL(LLDP), + MLXSW_SX_RXL(MMRP), + MLXSW_SX_RXL(MVRP), + MLXSW_SX_RXL(RPVST), + MLXSW_SX_RXL(DHCP), + MLXSW_SX_RXL(IGMP_QUERY), + MLXSW_SX_RXL(IGMP_V1_REPORT), + MLXSW_SX_RXL(IGMP_V2_REPORT), + MLXSW_SX_RXL(IGMP_V2_LEAVE), + MLXSW_SX_RXL(IGMP_V3_REPORT), }; static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) { char htgt_pl[MLXSW_REG_HTGT_LEN]; - char hpkt_pl[MLXSW_REG_HPKT_LEN]; int i; int err; @@ -1586,50 +1531,32 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) return err; for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) { - err = mlxsw_core_rx_listener_register(mlxsw_sx->core, - &mlxsw_sx_rx_listener[i], - mlxsw_sx); + err = mlxsw_core_trap_register(mlxsw_sx->core, + &mlxsw_sx_rx_listener[i], + mlxsw_sx); if (err) goto err_rx_listener_register; - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, - mlxsw_sx_rx_listener[i].trap_id); - err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); - if (err) - goto err_rx_trap_set; } return 0; -err_rx_trap_set: - mlxsw_core_rx_listener_unregister(mlxsw_sx->core, - &mlxsw_sx_rx_listener[i], - mlxsw_sx); err_rx_listener_register: for (i--; i >= 0; i--) { - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - mlxsw_sx_rx_listener[i].trap_id); - mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); - - mlxsw_core_rx_listener_unregister(mlxsw_sx->core, - &mlxsw_sx_rx_listener[i], - mlxsw_sx); + mlxsw_core_trap_unregister(mlxsw_sx->core, + &mlxsw_sx_rx_listener[i], + mlxsw_sx); } return err; } static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx) { - char hpkt_pl[MLXSW_REG_HPKT_LEN]; int i; for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) { - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - mlxsw_sx_rx_listener[i].trap_id); - mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); - - mlxsw_core_rx_listener_unregister(mlxsw_sx->core, - &mlxsw_sx_rx_listener[i], - mlxsw_sx); + mlxsw_core_trap_unregister(mlxsw_sx->core, + &mlxsw_sx_rx_listener[i], + mlxsw_sx); } } -- cgit v1.1 From fb9012d93f3d8abc316ec06105a7e781c37e2696 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:34 +0100 Subject: mlxsw: core: Introduce generic macro for event Create a macro for creating the generic listener struct for events, similar to the one for rx traps. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 5f6fed1..852218e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -115,6 +115,18 @@ struct mlxsw_listener { .is_event = false, \ } +#define MLXSW_EVENTL(_func, _trap_id) \ + { \ + .trap_id = MLXSW_TRAP_ID_##_trap_id, \ + .u.event_listener = \ + { \ + .func = _func, \ + .trap_id = MLXSW_TRAP_ID_##_trap_id, \ + }, \ + .action = MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, \ + .is_event = true, \ + } + int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, const struct mlxsw_rx_listener *rxl, void *priv); -- cgit v1.1 From 4544913ed79275ce6a1cd98baa7edbf018c5242a Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:35 +0100 Subject: mlxsw: spectrum: Use generic listener struct for events Change the events to use the generic listener struct. Merge the event list into the trap list, so the same functions will handle both. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 77 ++++---------------------- 1 file changed, 12 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index efa99af..d758ce6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2698,52 +2698,6 @@ static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, } } -static struct mlxsw_event_listener mlxsw_sp_pude_event = { - .func = mlxsw_sp_pude_event_func, - .trap_id = MLXSW_TRAP_ID_PUDE, -}; - -static int mlxsw_sp_event_register(struct mlxsw_sp *mlxsw_sp, - enum mlxsw_event_trap_id trap_id) -{ - struct mlxsw_event_listener *el; - char hpkt_pl[MLXSW_REG_HPKT_LEN]; - int err; - - switch (trap_id) { - case MLXSW_TRAP_ID_PUDE: - el = &mlxsw_sp_pude_event; - break; - } - err = mlxsw_core_event_listener_register(mlxsw_sp->core, el, mlxsw_sp); - if (err) - return err; - - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); - if (err) - goto err_event_trap_set; - - return 0; - -err_event_trap_set: - mlxsw_core_event_listener_unregister(mlxsw_sp->core, el, mlxsw_sp); - return err; -} - -static void mlxsw_sp_event_unregister(struct mlxsw_sp *mlxsw_sp, - enum mlxsw_event_trap_id trap_id) -{ - struct mlxsw_event_listener *el; - - switch (trap_id) { - case MLXSW_TRAP_ID_PUDE: - el = &mlxsw_sp_pude_event; - break; - } - mlxsw_core_event_listener_unregister(mlxsw_sp->core, el, mlxsw_sp); -} - static void mlxsw_sp_rx_listener_no_mark_func(struct sk_buff *skb, u8 local_port, void *priv) { @@ -2784,7 +2738,9 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port, MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action, \ DISCARD) -static const struct mlxsw_listener mlxsw_sp_rx_listener[] = { +static const struct mlxsw_listener mlxsw_sp_listener[] = { + /* Events */ + MLXSW_EVENTL(mlxsw_sp_pude_event_func, PUDE), /* L2 traps */ MLXSW_SP_RXL_NO_MARK(STP, TRAP_TO_CPU), MLXSW_SP_RXL_NO_MARK(LACP, TRAP_TO_CPU), @@ -2823,20 +2779,20 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) if (err) return err; - for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener); i++) { err = mlxsw_core_trap_register(mlxsw_sp->core, - &mlxsw_sp_rx_listener[i], + &mlxsw_sp_listener[i], mlxsw_sp); if (err) - goto err_rx_listener_register; + goto err_listener_register; } return 0; -err_rx_listener_register: +err_listener_register: for (i--; i >= 0; i--) { mlxsw_core_trap_unregister(mlxsw_sp->core, - &mlxsw_sp_rx_listener[i], + &mlxsw_sp_listener[i], mlxsw_sp); } return err; @@ -2846,9 +2802,9 @@ static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp) { int i; - for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_listener); i++) { mlxsw_core_trap_unregister(mlxsw_sp->core, - &mlxsw_sp_rx_listener[i], + &mlxsw_sp_listener[i], mlxsw_sp); } } @@ -2952,16 +2908,10 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, return err; } - err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE); - if (err) { - dev_err(mlxsw_sp->bus_info->dev, "Failed to register for PUDE events\n"); - return err; - } - err = mlxsw_sp_traps_init(mlxsw_sp); if (err) { - dev_err(mlxsw_sp->bus_info->dev, "Failed to set traps for RX\n"); - goto err_rx_listener_register; + dev_err(mlxsw_sp->bus_info->dev, "Failed to set traps\n"); + return err; } err = mlxsw_sp_flood_init(mlxsw_sp); @@ -3021,8 +2971,6 @@ err_lag_init: err_buffers_init: err_flood_init: mlxsw_sp_traps_fini(mlxsw_sp); -err_rx_listener_register: - mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); return err; } @@ -3037,7 +2985,6 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) mlxsw_sp_lag_fini(mlxsw_sp); mlxsw_sp_buffers_fini(mlxsw_sp); mlxsw_sp_traps_fini(mlxsw_sp); - mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); WARN_ON(!list_empty(&mlxsw_sp->vfids.list)); WARN_ON(!list_empty(&mlxsw_sp->fids)); } -- cgit v1.1 From 6bf08b53ed84a256f5e789d278e4852006f8e387 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:36 +0100 Subject: mlxsw: switchx2: Use generic listener struct for events Change the events to use the generic listener struct. Merge the event list into the trap list, so the same functions will handle both. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 78 ++++---------------------- 1 file changed, 12 insertions(+), 66 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index df6afdd..675ce12 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1396,52 +1396,6 @@ static void mlxsw_sx_pude_event_func(const struct mlxsw_reg_info *reg, mlxsw_sx_pude_ib_event_func(mlxsw_sx_port, status); } -static struct mlxsw_event_listener mlxsw_sx_pude_event = { - .func = mlxsw_sx_pude_event_func, - .trap_id = MLXSW_TRAP_ID_PUDE, -}; - -static int mlxsw_sx_event_register(struct mlxsw_sx *mlxsw_sx, - enum mlxsw_event_trap_id trap_id) -{ - struct mlxsw_event_listener *el; - char hpkt_pl[MLXSW_REG_HPKT_LEN]; - int err; - - switch (trap_id) { - case MLXSW_TRAP_ID_PUDE: - el = &mlxsw_sx_pude_event; - break; - } - err = mlxsw_core_event_listener_register(mlxsw_sx->core, el, mlxsw_sx); - if (err) - return err; - - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id); - err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); - if (err) - goto err_event_trap_set; - - return 0; - -err_event_trap_set: - mlxsw_core_event_listener_unregister(mlxsw_sx->core, el, mlxsw_sx); - return err; -} - -static void mlxsw_sx_event_unregister(struct mlxsw_sx *mlxsw_sx, - enum mlxsw_event_trap_id trap_id) -{ - struct mlxsw_event_listener *el; - - switch (trap_id) { - case MLXSW_TRAP_ID_PUDE: - el = &mlxsw_sx_pude_event; - break; - } - mlxsw_core_event_listener_unregister(mlxsw_sx->core, el, mlxsw_sx); -} - static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port, void *priv) { @@ -1497,7 +1451,8 @@ err_port_module_info_get: #define MLXSW_SX_RXL(_trap_id) \ MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU, FORWARD) -static const struct mlxsw_listener mlxsw_sx_rx_listener[] = { +static const struct mlxsw_listener mlxsw_sx_listener[] = { + MLXSW_EVENTL(mlxsw_sx_pude_event_func, PUDE), MLXSW_SX_RXL(FDB_MC), MLXSW_SX_RXL(STP), MLXSW_SX_RXL(LACP), @@ -1530,20 +1485,20 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) if (err) return err; - for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) { + for (i = 0; i < ARRAY_SIZE(mlxsw_sx_listener); i++) { err = mlxsw_core_trap_register(mlxsw_sx->core, - &mlxsw_sx_rx_listener[i], + &mlxsw_sx_listener[i], mlxsw_sx); if (err) - goto err_rx_listener_register; + goto err_listener_register; } return 0; -err_rx_listener_register: +err_listener_register: for (i--; i >= 0; i--) { mlxsw_core_trap_unregister(mlxsw_sx->core, - &mlxsw_sx_rx_listener[i], + &mlxsw_sx_listener[i], mlxsw_sx); } return err; @@ -1553,9 +1508,9 @@ static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx) { int i; - for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) { + for (i = 0; i < ARRAY_SIZE(mlxsw_sx_listener); i++) { mlxsw_core_trap_unregister(mlxsw_sx->core, - &mlxsw_sx_rx_listener[i], + &mlxsw_sx_listener[i], mlxsw_sx); } } @@ -1649,16 +1604,10 @@ static int mlxsw_sx_init(struct mlxsw_core *mlxsw_core, return err; } - err = mlxsw_sx_event_register(mlxsw_sx, MLXSW_TRAP_ID_PUDE); - if (err) { - dev_err(mlxsw_sx->bus_info->dev, "Failed to register for PUDE events\n"); - goto err_event_register; - } - err = mlxsw_sx_traps_init(mlxsw_sx); if (err) { - dev_err(mlxsw_sx->bus_info->dev, "Failed to set traps for RX\n"); - goto err_rx_listener_register; + dev_err(mlxsw_sx->bus_info->dev, "Failed to set traps\n"); + goto err_listener_register; } err = mlxsw_sx_flood_init(mlxsw_sx); @@ -1671,9 +1620,7 @@ static int mlxsw_sx_init(struct mlxsw_core *mlxsw_core, err_flood_init: mlxsw_sx_traps_fini(mlxsw_sx); -err_rx_listener_register: - mlxsw_sx_event_unregister(mlxsw_sx, MLXSW_TRAP_ID_PUDE); -err_event_register: +err_listener_register: mlxsw_sx_ports_remove(mlxsw_sx); return err; } @@ -1683,7 +1630,6 @@ static void mlxsw_sx_fini(struct mlxsw_core *mlxsw_core) struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core); mlxsw_sx_traps_fini(mlxsw_sx); - mlxsw_sx_event_unregister(mlxsw_sx, MLXSW_TRAP_ID_PUDE); mlxsw_sx_ports_remove(mlxsw_sx); } -- cgit v1.1 From 85d5c9cd90d2b271d8498ee6362ad57e00a2af16 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:37 +0100 Subject: mlxsw: switchib: Use generic listener struct for events Change the event handling in Switchib to be comptible with Spectrum and Switchx2. Use the generic listener struct for the events. Init and fini them by loop (and not by calling each event by its name). Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/switchib.c | 62 +++++++++++--------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c index 1552594..847cbbf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -408,51 +408,43 @@ static void mlxsw_sib_pude_event_func(const struct mlxsw_reg_info *reg, mlxsw_sib_pude_ib_event_func(mlxsw_sib_port, status); } -static struct mlxsw_event_listener mlxsw_sib_pude_event = { - .func = mlxsw_sib_pude_event_func, - .trap_id = MLXSW_TRAP_ID_PUDE, +static const struct mlxsw_listener mlxsw_sib_listener[] = { + MLXSW_EVENTL(mlxsw_sib_pude_event_func, PUDE), }; -static int mlxsw_sib_event_register(struct mlxsw_sib *mlxsw_sib, - enum mlxsw_event_trap_id trap_id) +static int mlxsw_sib_taps_init(struct mlxsw_sib *mlxsw_sib) { - struct mlxsw_event_listener *el; - char hpkt_pl[MLXSW_REG_HPKT_LEN]; + int i; int err; - switch (trap_id) { - case MLXSW_TRAP_ID_PUDE: - el = &mlxsw_sib_pude_event; - break; + for (i = 0; i < ARRAY_SIZE(mlxsw_sib_listener); i++) { + err = mlxsw_core_trap_register(mlxsw_sib->core, + &mlxsw_sib_listener[i], + mlxsw_sib); + if (err) + goto err_rx_listener_register; } - err = mlxsw_core_event_listener_register(mlxsw_sib->core, el, - mlxsw_sib); - if (err) - return err; - - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id); - err = mlxsw_reg_write(mlxsw_sib->core, MLXSW_REG(hpkt), hpkt_pl); - if (err) - goto err_event_trap_set; return 0; -err_event_trap_set: - mlxsw_core_event_listener_unregister(mlxsw_sib->core, el, mlxsw_sib); +err_rx_listener_register: + for (i--; i >= 0; i--) { + mlxsw_core_trap_unregister(mlxsw_sib->core, + &mlxsw_sib_listener[i], + mlxsw_sib); + } + return err; } -static void mlxsw_sib_event_unregister(struct mlxsw_sib *mlxsw_sib, - enum mlxsw_event_trap_id trap_id) +static void mlxsw_sib_traps_fini(struct mlxsw_sib *mlxsw_sib) { - struct mlxsw_event_listener *el; + int i; - switch (trap_id) { - case MLXSW_TRAP_ID_PUDE: - el = &mlxsw_sib_pude_event; - break; + for (i = 0; i < ARRAY_SIZE(mlxsw_sib_listener); i++) { + mlxsw_core_trap_unregister(mlxsw_sib->core, + &mlxsw_sib_listener[i], mlxsw_sib); } - mlxsw_core_event_listener_unregister(mlxsw_sib->core, el, mlxsw_sib); } static int mlxsw_sib_init(struct mlxsw_core *mlxsw_core, @@ -470,15 +462,15 @@ static int mlxsw_sib_init(struct mlxsw_core *mlxsw_core, return err; } - err = mlxsw_sib_event_register(mlxsw_sib, MLXSW_TRAP_ID_PUDE); + err = mlxsw_sib_taps_init(mlxsw_sib); if (err) { - dev_err(mlxsw_sib->bus_info->dev, "Failed to register for PUDE events\n"); - goto err_event_register; + dev_err(mlxsw_sib->bus_info->dev, "Failed to set traps\n"); + goto err_traps_init_err; } return 0; -err_event_register: +err_traps_init_err: mlxsw_sib_ports_remove(mlxsw_sib); return err; } @@ -487,7 +479,7 @@ static void mlxsw_sib_fini(struct mlxsw_core *mlxsw_core) { struct mlxsw_sib *mlxsw_sib = mlxsw_core_driver_priv(mlxsw_core); - mlxsw_sib_event_unregister(mlxsw_sib, MLXSW_TRAP_ID_PUDE); + mlxsw_sib_traps_fini(mlxsw_sib); mlxsw_sib_ports_remove(mlxsw_sib); } -- cgit v1.1 From d570b7ee4e47721099fc6309fd3ff9b637261357 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:38 +0100 Subject: mlxsw: Change trap set function Change trap setting function so instead of determining the trap group by trap id, it gets it as a parameter (so later we can have different trap groups for Spectrum and Switchx2). Add "is_ctrl" parameter to the trap setting function. It control whether the trapped packets wait in a designated control buffer or in their default one. This parameter is ignored by Switchx2 and Switchib. Add these parameters to the traps array in Spectrum, Switchx2 and Switchib. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 14 +++++--- drivers/net/ethernet/mellanox/mlxsw/core.h | 8 ++++- drivers/net/ethernet/mellanox/mlxsw/reg.h | 20 ++++------- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 46 +++++++++++++------------- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 5 +-- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index a8cff78..d3b82d6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -590,7 +590,8 @@ static int mlxsw_emad_traps_set(struct mlxsw_core *mlxsw_core) return err; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, - MLXSW_TRAP_ID_ETHEMAD); + MLXSW_TRAP_ID_ETHEMAD, + MLXSW_REG_HTGT_TRAP_GROUP_EMAD, false); return mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); } @@ -643,7 +644,8 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core) mlxsw_core->emad.use_emad = false; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, - MLXSW_TRAP_ID_ETHEMAD); + MLXSW_TRAP_ID_ETHEMAD, + MLXSW_REG_HTGT_TRAP_GROUP_EMAD, false); mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); mlxsw_core_rx_listener_unregister(mlxsw_core, @@ -1430,8 +1432,9 @@ int mlxsw_core_trap_register(struct mlxsw_core *mlxsw_core, if (err) return err; - mlxsw_reg_hpkt_pack(hpkt_pl, listener->action, listener->trap_id); - err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); + mlxsw_reg_hpkt_pack(hpkt_pl, listener->action, listener->trap_id, + listener->trap_group, listener->is_ctrl); + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); if (err) goto err_trap_set; @@ -1451,7 +1454,8 @@ void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, if (!listener->is_event) { mlxsw_reg_hpkt_pack(hpkt_pl, listener->unreg_action, - listener->trap_id); + listener->trap_id, listener->trap_group, + listener->is_ctrl); mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 852218e..b5b5b36 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -98,10 +98,12 @@ struct mlxsw_listener { } u; enum mlxsw_reg_hpkt_action action; enum mlxsw_reg_hpkt_action unreg_action; + u8 trap_group; + bool is_ctrl; /* should go via control buffer or not */ bool is_event; }; -#define MLXSW_RXL(_func, _trap_id, _action, _unreg_action) \ +#define MLXSW_RXL(_func, _trap_id, _action, _is_ctrl, _unreg_action) \ { \ .trap_id = MLXSW_TRAP_ID_##_trap_id, \ .u.rx_listener = \ @@ -112,6 +114,8 @@ struct mlxsw_listener { }, \ .action = MLXSW_REG_HPKT_ACTION_##_action, \ .unreg_action = MLXSW_REG_HPKT_ACTION_##_unreg_action, \ + .trap_group = MLXSW_REG_HTGT_TRAP_GROUP_RX, \ + .is_ctrl = _is_ctrl, \ .is_event = false, \ } @@ -124,6 +128,8 @@ struct mlxsw_listener { .trap_id = MLXSW_TRAP_ID_##_trap_id, \ }, \ .action = MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, \ + .trap_group = MLXSW_REG_HTGT_TRAP_GROUP_EMAD, \ + .is_ctrl = false, \ .is_event = true, \ } diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 2618e9c..68a0e63 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3214,6 +3214,7 @@ enum { /* reg_hpkt_ctrl * Configure dedicated buffer resources for control packets. + * Ignored by SwitchX-2. * 0 - Keep factory defaults. * 1 - Do not use control buffer for this trap ID. * 2 - Use control buffer for this trap ID. @@ -3221,25 +3222,18 @@ enum { */ MLXSW_ITEM32(reg, hpkt, ctrl, 0x04, 16, 2); -static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) +static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id, + enum mlxsw_reg_htgt_trap_group trap_group, + bool is_ctrl) { - enum mlxsw_reg_htgt_trap_group trap_group; - MLXSW_REG_ZERO(hpkt, payload); mlxsw_reg_hpkt_ack_set(payload, MLXSW_REG_HPKT_ACK_NOT_REQUIRED); mlxsw_reg_hpkt_action_set(payload, action); - switch (trap_id) { - case MLXSW_TRAP_ID_ETHEMAD: - case MLXSW_TRAP_ID_PUDE: - trap_group = MLXSW_REG_HTGT_TRAP_GROUP_EMAD; - break; - default: - trap_group = MLXSW_REG_HTGT_TRAP_GROUP_RX; - break; - } mlxsw_reg_hpkt_trap_group_set(payload, trap_group); mlxsw_reg_hpkt_trap_id_set(payload, trap_id); - mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT); + mlxsw_reg_hpkt_ctrl_set(payload, is_ctrl ? + MLXSW_REG_HPKT_CTRL_PACKET_USE_BUFFER : + MLXSW_REG_HPKT_CTRL_PACKET_NO_BUFFER); } /* RGCR - Router General Configuration Register diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index d758ce6..3582e3f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2730,37 +2730,37 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port, return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv); } -#define MLXSW_SP_RXL_NO_MARK(_trap_id, _action) \ - MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action, \ - DISCARD) +#define MLXSW_SP_RXL_NO_MARK(_trap_id, _action, _is_ctrl) \ + MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action, \ + _is_ctrl, DISCARD) -#define MLXSW_SP_RXL_MARK(_trap_id, _action) \ +#define MLXSW_SP_RXL_MARK(_trap_id, _action, _is_ctrl) \ MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action, \ - DISCARD) + _is_ctrl, DISCARD) static const struct mlxsw_listener mlxsw_sp_listener[] = { /* Events */ MLXSW_EVENTL(mlxsw_sp_pude_event_func, PUDE), /* L2 traps */ - MLXSW_SP_RXL_NO_MARK(STP, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(LACP, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(LLDP, TRAP_TO_CPU), - MLXSW_SP_RXL_MARK(DHCP, MIRROR_TO_CPU), - MLXSW_SP_RXL_MARK(IGMP_QUERY, MIRROR_TO_CPU), - MLXSW_SP_RXL_NO_MARK(IGMP_V1_REPORT, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(IGMP_V2_REPORT, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(IGMP_V2_LEAVE, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(IGMP_V3_REPORT, TRAP_TO_CPU), - MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU), - MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU), + MLXSW_SP_RXL_NO_MARK(STP, TRAP_TO_CPU, true), + MLXSW_SP_RXL_NO_MARK(LACP, TRAP_TO_CPU, true), + MLXSW_SP_RXL_NO_MARK(LLDP, TRAP_TO_CPU, true), + MLXSW_SP_RXL_MARK(DHCP, MIRROR_TO_CPU, false), + MLXSW_SP_RXL_MARK(IGMP_QUERY, MIRROR_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V1_REPORT, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V2_REPORT, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V2_LEAVE, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V3_REPORT, TRAP_TO_CPU, false), + MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU, false), + MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU, false), /* L3 traps */ - MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(LBERROR, TRAP_TO_CPU), - MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU), - MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU), + MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(LBERROR, TRAP_TO_CPU, false), + MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, false), }; static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 675ce12..a158756 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1448,8 +1448,9 @@ err_port_module_info_get: return err; } -#define MLXSW_SX_RXL(_trap_id) \ - MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU, FORWARD) +#define MLXSW_SX_RXL(_trap_id) \ + MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU, \ + false, FORWARD) static const struct mlxsw_listener mlxsw_sx_listener[] = { MLXSW_EVENTL(mlxsw_sx_pude_event_func, PUDE), -- cgit v1.1 From 0fb78a4e9c8563e1f95358794bd168c5c1e72009 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:39 +0100 Subject: mlxsw: Add option to choose trap group Currently, we set the trap group to pre-determined option, based on whether it is an rx or event trap. This commit adds a possibility to chose the trap group, so it can be set to different values in the following patches. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.h | 29 +++++++++++++------------- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 8 +++---- drivers/net/ethernet/mellanox/mlxsw/switchib.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 4 ++-- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index b5b5b36..d663b8e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -103,7 +103,8 @@ struct mlxsw_listener { bool is_event; }; -#define MLXSW_RXL(_func, _trap_id, _action, _is_ctrl, _unreg_action) \ +#define MLXSW_RXL(_func, _trap_id, _action, _is_ctrl, _trap_group, \ + _unreg_action) \ { \ .trap_id = MLXSW_TRAP_ID_##_trap_id, \ .u.rx_listener = \ @@ -114,23 +115,23 @@ struct mlxsw_listener { }, \ .action = MLXSW_REG_HPKT_ACTION_##_action, \ .unreg_action = MLXSW_REG_HPKT_ACTION_##_unreg_action, \ - .trap_group = MLXSW_REG_HTGT_TRAP_GROUP_RX, \ + .trap_group = MLXSW_REG_HTGT_TRAP_GROUP_##_trap_group, \ .is_ctrl = _is_ctrl, \ .is_event = false, \ } -#define MLXSW_EVENTL(_func, _trap_id) \ - { \ - .trap_id = MLXSW_TRAP_ID_##_trap_id, \ - .u.event_listener = \ - { \ - .func = _func, \ - .trap_id = MLXSW_TRAP_ID_##_trap_id, \ - }, \ - .action = MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, \ - .trap_group = MLXSW_REG_HTGT_TRAP_GROUP_EMAD, \ - .is_ctrl = false, \ - .is_event = true, \ +#define MLXSW_EVENTL(_func, _trap_id, _trap_group) \ + { \ + .trap_id = MLXSW_TRAP_ID_##_trap_id, \ + .u.event_listener = \ + { \ + .func = _func, \ + .trap_id = MLXSW_TRAP_ID_##_trap_id, \ + }, \ + .action = MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, \ + .trap_group = MLXSW_REG_HTGT_TRAP_GROUP_##_trap_group, \ + .is_ctrl = false, \ + .is_event = true, \ } int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3582e3f..44243a4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2731,16 +2731,16 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port, } #define MLXSW_SP_RXL_NO_MARK(_trap_id, _action, _is_ctrl) \ - MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action, \ - _is_ctrl, DISCARD) + MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action, \ + _is_ctrl, RX, DISCARD) #define MLXSW_SP_RXL_MARK(_trap_id, _action, _is_ctrl) \ MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action, \ - _is_ctrl, DISCARD) + _is_ctrl, RX, DISCARD) static const struct mlxsw_listener mlxsw_sp_listener[] = { /* Events */ - MLXSW_EVENTL(mlxsw_sp_pude_event_func, PUDE), + MLXSW_EVENTL(mlxsw_sp_pude_event_func, PUDE, EMAD), /* L2 traps */ MLXSW_SP_RXL_NO_MARK(STP, TRAP_TO_CPU, true), MLXSW_SP_RXL_NO_MARK(LACP, TRAP_TO_CPU, true), diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c index 847cbbf..b798711 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -409,7 +409,7 @@ static void mlxsw_sib_pude_event_func(const struct mlxsw_reg_info *reg, } static const struct mlxsw_listener mlxsw_sib_listener[] = { - MLXSW_EVENTL(mlxsw_sib_pude_event_func, PUDE), + MLXSW_EVENTL(mlxsw_sib_pude_event_func, PUDE, EMAD), }; static int mlxsw_sib_taps_init(struct mlxsw_sib *mlxsw_sib) diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index a158756..1bcb391 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1450,10 +1450,10 @@ err_port_module_info_get: #define MLXSW_SX_RXL(_trap_id) \ MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU, \ - false, FORWARD) + false, RX, FORWARD) static const struct mlxsw_listener mlxsw_sx_listener[] = { - MLXSW_EVENTL(mlxsw_sx_pude_event_func, PUDE), + MLXSW_EVENTL(mlxsw_sx_pude_event_func, PUDE, EMAD), MLXSW_SX_RXL(FDB_MC), MLXSW_SX_RXL(STP), MLXSW_SX_RXL(LACP), -- cgit v1.1 From 9d87fceac6e1a7885e3f7766de3ec2ab7d152aa2 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:40 +0100 Subject: mlxsw: core: Change emad trap group settings Currently, the emad trap init was done in the core. In the future we will want to add some changes to the traps groups, according to device type. This commit create a driver function to create the trap group for the emad, so later it can be changed by devices. It also changes the emad registration to use the new generic functions. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 49 ++++++-------------------- drivers/net/ethernet/mellanox/mlxsw/core.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 9 +++++ drivers/net/ethernet/mellanox/mlxsw/switchib.c | 10 ++++++ drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 9 +++++ 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index d3b82d6..b21f88c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -572,28 +572,9 @@ free_skb: dev_kfree_skb(skb); } -static const struct mlxsw_rx_listener mlxsw_emad_rx_listener = { - .func = mlxsw_emad_rx_listener_func, - .local_port = MLXSW_PORT_DONT_CARE, - .trap_id = MLXSW_TRAP_ID_ETHEMAD, -}; - -static int mlxsw_emad_traps_set(struct mlxsw_core *mlxsw_core) -{ - char htgt_pl[MLXSW_REG_HTGT_LEN]; - char hpkt_pl[MLXSW_REG_HPKT_LEN]; - int err; - - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD); - err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); - if (err) - return err; - - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, - MLXSW_TRAP_ID_ETHEMAD, - MLXSW_REG_HTGT_TRAP_GROUP_EMAD, false); - return mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); -} +static const struct mlxsw_listener mlxsw_emad_rx_listener = + MLXSW_RXL(mlxsw_emad_rx_listener_func, ETHEMAD, TRAP_TO_CPU, false, + EMAD, DISCARD); static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) { @@ -614,43 +595,33 @@ static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) INIT_LIST_HEAD(&mlxsw_core->emad.trans_list); spin_lock_init(&mlxsw_core->emad.trans_list_lock); - err = mlxsw_core_rx_listener_register(mlxsw_core, - &mlxsw_emad_rx_listener, - mlxsw_core); + err = mlxsw_core_trap_register(mlxsw_core, &mlxsw_emad_rx_listener, + mlxsw_core); if (err) return err; - err = mlxsw_emad_traps_set(mlxsw_core); + err = mlxsw_core->driver->basic_trap_groups_set(mlxsw_core); if (err) goto err_emad_trap_set; - mlxsw_core->emad.use_emad = true; return 0; err_emad_trap_set: - mlxsw_core_rx_listener_unregister(mlxsw_core, - &mlxsw_emad_rx_listener, - mlxsw_core); + mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener, + mlxsw_core); return err; } static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core) { - char hpkt_pl[MLXSW_REG_HPKT_LEN]; if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) return; mlxsw_core->emad.use_emad = false; - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, - MLXSW_TRAP_ID_ETHEMAD, - MLXSW_REG_HTGT_TRAP_GROUP_EMAD, false); - mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); - - mlxsw_core_rx_listener_unregister(mlxsw_core, - &mlxsw_emad_rx_listener, - mlxsw_core); + mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener, + mlxsw_core); } static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index d663b8e..e856b49 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -265,6 +265,7 @@ struct mlxsw_driver { int (*init)(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info); void (*fini)(struct mlxsw_core *mlxsw_core); + int (*basic_trap_groups_set)(struct mlxsw_core *mlxsw_core); int (*port_type_set)(struct mlxsw_core *mlxsw_core, u8 local_port, enum devlink_port_type new_type); int (*port_split)(struct mlxsw_core *mlxsw_core, u8 local_port, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 44243a4..79c0a01 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2890,6 +2890,14 @@ static void mlxsw_sp_lag_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->lags); } +static int mlxsw_sp_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) +{ + char htgt_pl[MLXSW_REG_HTGT_LEN]; + + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); +} + static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info) { @@ -3026,6 +3034,7 @@ static struct mlxsw_driver mlxsw_sp_driver = { .priv_size = sizeof(struct mlxsw_sp), .init = mlxsw_sp_init, .fini = mlxsw_sp_fini, + .basic_trap_groups_set = mlxsw_sp_basic_trap_groups_set, .port_split = mlxsw_sp_port_split, .port_unsplit = mlxsw_sp_port_unsplit, .sb_pool_get = mlxsw_sp_sb_pool_get, diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c index b798711..9a5f829 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -447,6 +447,14 @@ static void mlxsw_sib_traps_fini(struct mlxsw_sib *mlxsw_sib) } } +static int mlxsw_sib_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) +{ + char htgt_pl[MLXSW_REG_HTGT_LEN]; + + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); +} + static int mlxsw_sib_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info) { @@ -504,6 +512,7 @@ static struct mlxsw_driver mlxsw_sib_driver = { .priv_size = sizeof(struct mlxsw_sib), .init = mlxsw_sib_init, .fini = mlxsw_sib_fini, + .basic_trap_groups_set = mlxsw_sib_basic_trap_groups_set, .txhdr_construct = mlxsw_sib_tx_v1_hdr_construct, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sib_config_profile, @@ -514,6 +523,7 @@ static struct mlxsw_driver mlxsw_sib2_driver = { .priv_size = sizeof(struct mlxsw_sib), .init = mlxsw_sib_init, .fini = mlxsw_sib_fini, + .basic_trap_groups_set = mlxsw_sib_basic_trap_groups_set, .txhdr_construct = mlxsw_sib_tx_v1_hdr_construct, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sib_config_profile, diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 1bcb391..8caa962 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1584,6 +1584,14 @@ static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx) return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sgcr), sgcr_pl); } +static int mlxsw_sx_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) +{ + char htgt_pl[MLXSW_REG_HTGT_LEN]; + + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); +} + static int mlxsw_sx_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info) { @@ -1674,6 +1682,7 @@ static struct mlxsw_driver mlxsw_sx_driver = { .priv_size = sizeof(struct mlxsw_sx), .init = mlxsw_sx_init, .fini = mlxsw_sx_fini, + .basic_trap_groups_set = mlxsw_sx_basic_trap_groups_set, .txhdr_construct = mlxsw_sx_txhdr_construct, .txhdr_len = MLXSW_TXHDR_LEN, .profile = &mlxsw_sx_config_profile, -- cgit v1.1 From 23432cb86a53b03da6dc827ee373d113ee1cc2f8 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:41 +0100 Subject: mlxsw: resources: Add max trap groups resource Add the max number of trap groups to resource query. Signed-off-by: Nogah Frankel Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index a031e45..07b09f8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -42,6 +42,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_KVD_SIZE, MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE, MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE, + MLXSW_RES_ID_MAX_TRAP_GROUPS, MLXSW_RES_ID_MAX_SPAN, MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, @@ -63,6 +64,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_KVD_SIZE] = 0x1001, [MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE] = 0x1002, [MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE] = 0x1003, + [MLXSW_RES_ID_MAX_TRAP_GROUPS] = 0x2201, [MLXSW_RES_ID_MAX_SPAN] = 0x2420, [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, -- cgit v1.1 From 579c82e4c5a8d6653533b2ffd8a2b519928050cb Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:42 +0100 Subject: mlxsw: Change trap groups setting Trap groups have many options which we currently set to default values. In the next patches we will use many of them with non-default values. Some of these options have no default value, so this patch sets them as params for the trap group set function. Others almost always use the same values, so the set function will use this default values. In the rare cases when they will need to be with other values, these values can be set directly (using the macros for fields in registers). Parameters without default value: TC - the traffic class for packets that hit this trap group. (old default is the max tc) priority - if one packet hits multiple trap groups, the group with the higher priority will "catch" it. (old default is 0) policer - limit rate policer (old default is disabled) Default parameters: swid - switch id, relevant for the emad trap only, ignored on Spectrum. (new default is 0) rdq - CPU receive descriptor queue (new default is identical to trap group id) Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 53 +++++++++++++------------- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 47 ++++++++++++++++++----- drivers/net/ethernet/mellanox/mlxsw/switchib.c | 8 +++- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 24 ++++++++++-- 4 files changed, 92 insertions(+), 40 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 68a0e63..2116f8f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3057,6 +3057,8 @@ enum { */ MLXSW_ITEM32(reg, htgt, pide, 0x04, 15, 1); +#define MLXSW_REG_HTGT_INVALID_POLICER 0xff + /* reg_htgt_pid * Policer ID for the trap group. * Access: RW @@ -3082,6 +3084,8 @@ MLXSW_ITEM32(reg, htgt, mirror_action, 0x08, 8, 2); */ MLXSW_ITEM32(reg, htgt, mirroring_agent, 0x08, 0, 3); +#define MLXSW_REG_HTGT_DEFAULT_PRIORITY 0 + /* reg_htgt_priority * Trap group priority. * In case a packet matches multiple classification rules, the packet will @@ -3095,52 +3099,47 @@ MLXSW_ITEM32(reg, htgt, mirroring_agent, 0x08, 0, 3); */ MLXSW_ITEM32(reg, htgt, priority, 0x0C, 0, 4); +#define MLXSW_REG_HTGT_DEFAULT_TC 7 + /* reg_htgt_local_path_cpu_tclass * CPU ingress traffic class for the trap group. * Access: RW */ MLXSW_ITEM32(reg, htgt, local_path_cpu_tclass, 0x10, 16, 6); -#define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD 0x15 -#define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX 0x14 -#define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_CTRL 0x13 - +enum mlxsw_reg_htgt_local_path_rdq { + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_CTRL = 0x13, + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_RX = 0x14, + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_EMAD = 0x15, + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SIB_EMAD = 0x15, +}; /* reg_htgt_local_path_rdq * Receive descriptor queue (RDQ) to use for the trap group. * Access: RW */ MLXSW_ITEM32(reg, htgt, local_path_rdq, 0x10, 0, 6); -static inline void mlxsw_reg_htgt_pack(char *payload, - enum mlxsw_reg_htgt_trap_group group) +static inline void mlxsw_reg_htgt_pack(char *payload, u8 group, u8 policer_id, + u8 priority, u8 tc) { - u8 swid, rdq; - MLXSW_REG_ZERO(htgt, payload); - switch (group) { - case MLXSW_REG_HTGT_TRAP_GROUP_EMAD: - swid = MLXSW_PORT_SWID_ALL_SWIDS; - rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD; - break; - case MLXSW_REG_HTGT_TRAP_GROUP_RX: - swid = 0; - rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX; - break; - case MLXSW_REG_HTGT_TRAP_GROUP_CTRL: - swid = 0; - rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_CTRL; - break; + + if (policer_id == MLXSW_REG_HTGT_INVALID_POLICER) { + mlxsw_reg_htgt_pide_set(payload, + MLXSW_REG_HTGT_POLICER_DISABLE); + } else { + mlxsw_reg_htgt_pide_set(payload, + MLXSW_REG_HTGT_POLICER_ENABLE); + mlxsw_reg_htgt_pid_set(payload, policer_id); } - mlxsw_reg_htgt_swid_set(payload, swid); + mlxsw_reg_htgt_type_set(payload, MLXSW_REG_HTGT_PATH_TYPE_LOCAL); mlxsw_reg_htgt_trap_group_set(payload, group); - mlxsw_reg_htgt_pide_set(payload, MLXSW_REG_HTGT_POLICER_DISABLE); - mlxsw_reg_htgt_pid_set(payload, 0); mlxsw_reg_htgt_mirror_action_set(payload, MLXSW_REG_HTGT_TRAP_TO_CPU); mlxsw_reg_htgt_mirroring_agent_set(payload, 0); - mlxsw_reg_htgt_priority_set(payload, 0); - mlxsw_reg_htgt_local_path_cpu_tclass_set(payload, 7); - mlxsw_reg_htgt_local_path_rdq_set(payload, rdq); + mlxsw_reg_htgt_priority_set(payload, priority); + mlxsw_reg_htgt_local_path_cpu_tclass_set(payload, tc); + mlxsw_reg_htgt_local_path_rdq_set(payload, group); } /* HPKT - Host Packet Trap diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 79c0a01..75ba22a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2763,19 +2763,45 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, false), }; -static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) +static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) { char htgt_pl[MLXSW_REG_HTGT_LEN]; + int max_trap_groups; + u8 priority, tc; + int i, err; + + if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_TRAP_GROUPS)) + return -EIO; + + max_trap_groups = MLXSW_CORE_RES_GET(mlxsw_core, MAX_TRAP_GROUPS); + + for (i = 0; i < max_trap_groups; i++) { + switch (i) { + case MLXSW_REG_HTGT_TRAP_GROUP_EMAD: + case MLXSW_REG_HTGT_TRAP_GROUP_RX: + case MLXSW_REG_HTGT_TRAP_GROUP_CTRL: + priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY; + tc = MLXSW_REG_HTGT_DEFAULT_TC; + break; + default: + continue; + } + mlxsw_reg_htgt_pack(htgt_pl, i, MLXSW_REG_HTGT_INVALID_POLICER, + priority, tc); + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); + if (err) + return err; + } + + return 0; +} + +static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) +{ int i; int err; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_RX); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl); - if (err) - return err; - - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl); + err = mlxsw_sp_trap_groups_set(mlxsw_sp->core); if (err) return err; @@ -2894,7 +2920,10 @@ static int mlxsw_sp_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) { char htgt_pl[MLXSW_REG_HTGT_LEN]; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD); + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD, + MLXSW_REG_HTGT_INVALID_POLICER, + MLXSW_REG_HTGT_DEFAULT_PRIORITY, + MLXSW_REG_HTGT_DEFAULT_TC); return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c index 9a5f829..74341fe 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -451,7 +451,13 @@ static int mlxsw_sib_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) { char htgt_pl[MLXSW_REG_HTGT_LEN]; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD); + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD, + MLXSW_REG_HTGT_INVALID_POLICER, + MLXSW_REG_HTGT_DEFAULT_PRIORITY, + MLXSW_REG_HTGT_DEFAULT_TC); + mlxsw_reg_htgt_swid_set(htgt_pl, MLXSW_PORT_SWID_ALL_SWIDS); + mlxsw_reg_htgt_local_path_rdq_set(htgt_pl, + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SIB_EMAD); return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 8caa962..4cdc02c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1476,12 +1476,24 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) int i; int err; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_RX); + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_RX, + MLXSW_REG_HTGT_INVALID_POLICER, + MLXSW_REG_HTGT_DEFAULT_PRIORITY, + MLXSW_REG_HTGT_DEFAULT_TC); + mlxsw_reg_htgt_local_path_rdq_set(htgt_pl, + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_RX); + err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl); if (err) return err; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL); + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL, + MLXSW_REG_HTGT_INVALID_POLICER, + MLXSW_REG_HTGT_DEFAULT_PRIORITY, + MLXSW_REG_HTGT_DEFAULT_TC); + mlxsw_reg_htgt_local_path_rdq_set(htgt_pl, + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_CTRL); + err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl); if (err) return err; @@ -1588,7 +1600,13 @@ static int mlxsw_sx_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) { char htgt_pl[MLXSW_REG_HTGT_LEN]; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD); + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD, + MLXSW_REG_HTGT_INVALID_POLICER, + MLXSW_REG_HTGT_DEFAULT_PRIORITY, + MLXSW_REG_HTGT_DEFAULT_TC); + mlxsw_reg_htgt_swid_set(htgt_pl, MLXSW_PORT_SWID_ALL_SWIDS); + mlxsw_reg_htgt_local_path_rdq_set(htgt_pl, + MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_EMAD); return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); } -- cgit v1.1 From 616d8040e56fa5cd276c988e351cc928002c3502 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:43 +0100 Subject: mlxsw: spectrum: Add BGP trap Add a trap for BGP protocol that was previously trapped by the generic trap for IP2ME. This trap will allow us to have better control (over priority and rate) of the traffic. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 + drivers/net/ethernet/mellanox/mlxsw/trap.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 75ba22a..42ac968 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2761,6 +2761,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU, false), MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU, false), MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, false), + MLXSW_SP_RXL_MARK(BGP_IPV4, TRAP_TO_CPU, false), }; static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index ed8e301..7ab275d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -62,6 +62,7 @@ enum { MLXSW_TRAP_ID_OSPF = 0x55, MLXSW_TRAP_ID_IP2ME = 0x5F, MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70, + MLXSW_TRAP_ID_BGP_IPV4 = 0x88, MLXSW_TRAP_ID_HOST_MISS_IPV4 = 0x90, MLXSW_TRAP_ID_MAX = 0x1FF -- cgit v1.1 From 117b0dad2d54a4265f24eff1328ba594d9153f2a Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:44 +0100 Subject: mlxsw: Create a different trap group list for each device Trap groups can be used to control traps priority, both in terms of which trap "wins" if a packet matches two traps (priority) and in terms of packets from which trap group will be scheduled to the cpu first (tc). They can also be used to set rate limiters (policers) on them (will be added in the next patches). Currently, we support two trap groups. In Spectrum we want a better resolution, so every protocol / flow will have a different trap group, so we can control its parameters separately. Once the policers will be implemented, it will also allow us limit the rate of each protocol by itself. This patch change the trap group list to include: * the emad trap group, which is shared for all the devices. * Switchx2's trap groups, which are a copy of the current trap groups. * Spectrum's new trap groups, in order to match the above guidelines. (Switchib is using only the emad trap group, so it require no changes). This patch also includes new configuration for Spectrum's trap groups, with primary priority order within them. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 17 ++++- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 86 +++++++++++++++++--------- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 8 +-- 3 files changed, 77 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 2116f8f..2d92e41 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3034,8 +3034,21 @@ MLXSW_ITEM32(reg, htgt, type, 0x00, 8, 4); enum mlxsw_reg_htgt_trap_group { MLXSW_REG_HTGT_TRAP_GROUP_EMAD, - MLXSW_REG_HTGT_TRAP_GROUP_RX, - MLXSW_REG_HTGT_TRAP_GROUP_CTRL, + MLXSW_REG_HTGT_TRAP_GROUP_SX2_RX, + MLXSW_REG_HTGT_TRAP_GROUP_SX2_CTRL, + MLXSW_REG_HTGT_TRAP_GROUP_SP_STP, + MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP, + MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP, + MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP, + MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4, + MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF, + MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP, + MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS, + MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP, + MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE, + MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME, + MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP, + MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT, }; /* reg_htgt_trap_group diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 42ac968..2207001 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2730,46 +2730,50 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port, return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv); } -#define MLXSW_SP_RXL_NO_MARK(_trap_id, _action, _is_ctrl) \ +#define MLXSW_SP_RXL_NO_MARK(_trap_id, _action, _trap_group, _is_ctrl) \ MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action, \ - _is_ctrl, RX, DISCARD) + _is_ctrl, SP_##_trap_group, DISCARD) -#define MLXSW_SP_RXL_MARK(_trap_id, _action, _is_ctrl) \ +#define MLXSW_SP_RXL_MARK(_trap_id, _action, _trap_group, _is_ctrl) \ MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action, \ - _is_ctrl, RX, DISCARD) + _is_ctrl, SP_##_trap_group, DISCARD) + +#define MLXSW_SP_EVENTL(_func, _trap_id) \ + MLXSW_EVENTL(_func, _trap_id, SP_EVENT) static const struct mlxsw_listener mlxsw_sp_listener[] = { /* Events */ - MLXSW_EVENTL(mlxsw_sp_pude_event_func, PUDE, EMAD), + MLXSW_SP_EVENTL(mlxsw_sp_pude_event_func, PUDE), /* L2 traps */ - MLXSW_SP_RXL_NO_MARK(STP, TRAP_TO_CPU, true), - MLXSW_SP_RXL_NO_MARK(LACP, TRAP_TO_CPU, true), - MLXSW_SP_RXL_NO_MARK(LLDP, TRAP_TO_CPU, true), - MLXSW_SP_RXL_MARK(DHCP, MIRROR_TO_CPU, false), - MLXSW_SP_RXL_MARK(IGMP_QUERY, MIRROR_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(IGMP_V1_REPORT, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(IGMP_V2_REPORT, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(IGMP_V2_LEAVE, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(IGMP_V3_REPORT, TRAP_TO_CPU, false), - MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU, false), - MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(STP, TRAP_TO_CPU, STP, true), + MLXSW_SP_RXL_NO_MARK(LACP, TRAP_TO_CPU, LACP, true), + MLXSW_SP_RXL_NO_MARK(LLDP, TRAP_TO_CPU, LLDP, true), + MLXSW_SP_RXL_MARK(DHCP, MIRROR_TO_CPU, DHCP, false), + MLXSW_SP_RXL_MARK(IGMP_QUERY, MIRROR_TO_CPU, IGMP, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V1_REPORT, TRAP_TO_CPU, IGMP, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V2_REPORT, TRAP_TO_CPU, IGMP, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V2_LEAVE, TRAP_TO_CPU, IGMP, false), + MLXSW_SP_RXL_NO_MARK(IGMP_V3_REPORT, TRAP_TO_CPU, IGMP, false), + MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU, ARP, false), + MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU, ARP, false), /* L3 traps */ - MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(LBERROR, TRAP_TO_CPU, false), - MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU, false), - MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, false), - MLXSW_SP_RXL_MARK(BGP_IPV4, TRAP_TO_CPU, false), + MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_NO_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU, OSPF, false), + MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false), + MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false), + MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, ARP_MISS, false), + MLXSW_SP_RXL_NO_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false), }; static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) { char htgt_pl[MLXSW_REG_HTGT_LEN]; + enum mlxsw_reg_htgt_trap_group i; int max_trap_groups; u8 priority, tc; - int i, err; + int err; if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_TRAP_GROUPS)) return -EIO; @@ -2778,15 +2782,41 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) for (i = 0; i < max_trap_groups; i++) { switch (i) { - case MLXSW_REG_HTGT_TRAP_GROUP_EMAD: - case MLXSW_REG_HTGT_TRAP_GROUP_RX: - case MLXSW_REG_HTGT_TRAP_GROUP_CTRL: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_STP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF: + priority = 5; + tc = 5; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP: + priority = 4; + tc = 4; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME: + priority = 3; + tc = 3; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: + priority = 2; + tc = 2; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: + priority = 1; + tc = 1; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT: priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY; tc = MLXSW_REG_HTGT_DEFAULT_TC; break; default: continue; } + mlxsw_reg_htgt_pack(htgt_pl, i, MLXSW_REG_HTGT_INVALID_POLICER, priority, tc); err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 4cdc02c..150ccf5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1448,9 +1448,9 @@ err_port_module_info_get: return err; } -#define MLXSW_SX_RXL(_trap_id) \ +#define MLXSW_SX_RXL(_trap_id) \ MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU, \ - false, RX, FORWARD) + false, SX2_RX, FORWARD) static const struct mlxsw_listener mlxsw_sx_listener[] = { MLXSW_EVENTL(mlxsw_sx_pude_event_func, PUDE, EMAD), @@ -1476,7 +1476,7 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) int i; int err; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_RX, + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SX2_RX, MLXSW_REG_HTGT_INVALID_POLICER, MLXSW_REG_HTGT_DEFAULT_PRIORITY, MLXSW_REG_HTGT_DEFAULT_TC); @@ -1487,7 +1487,7 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) if (err) return err; - mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL, + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SX2_CTRL, MLXSW_REG_HTGT_INVALID_POLICER, MLXSW_REG_HTGT_DEFAULT_PRIORITY, MLXSW_REG_HTGT_DEFAULT_TC); -- cgit v1.1 From 2b77958bf4c80cf40fe669c7cf6b65861f9c2e09 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:45 +0100 Subject: mlxsw: resources: Add max cpu policers resource Add a new resource to resources query: max cpu policers which tells us how many policers can be used to limit the data rate to the cpu port. Signed-off-by: Nogah Frankel Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index 07b09f8..1c2119b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -47,6 +47,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, MLXSW_RES_ID_MAX_LAG_MEMBERS, + MLXSW_RES_ID_MAX_CPU_POLICERS, MLXSW_RES_ID_MAX_VRS, MLXSW_RES_ID_MAX_RIFS, @@ -69,6 +70,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, + [MLXSW_RES_ID_MAX_CPU_POLICERS] = 0x2A13, [MLXSW_RES_ID_MAX_VRS] = 0x2C01, [MLXSW_RES_ID_MAX_RIFS] = 0x2C02, }; -- cgit v1.1 From 76a4c7d32a0b0a5a4a072a56a3dc4a2850afd8d8 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:46 +0100 Subject: mlxsw: reg: Add QoS Policer Configuration Register The QPCR register is used to create and control policers. A policer can discard or change the color of packets that are trapped by a specific trap. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 141 ++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 2d92e41..1357fe0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -1757,6 +1757,146 @@ static inline void mlxsw_reg_spvmlr_pack(char *payload, u8 local_port, } } +/* QPCR - QoS Policer Configuration Register + * ----------------------------------------- + * The QPCR register is used to create policers - that limit + * the rate of bytes or packets via some trap group. + */ +#define MLXSW_REG_QPCR_ID 0x4004 +#define MLXSW_REG_QPCR_LEN 0x28 + +MLXSW_REG_DEFINE(qpcr, MLXSW_REG_QPCR_ID, MLXSW_REG_QPCR_LEN); + +enum mlxsw_reg_qpcr_g { + MLXSW_REG_QPCR_G_GLOBAL = 2, + MLXSW_REG_QPCR_G_STORM_CONTROL = 3, +}; + +/* reg_qpcr_g + * The policer type. + * Access: Index + */ +MLXSW_ITEM32(reg, qpcr, g, 0x00, 14, 2); + +/* reg_qpcr_pid + * Policer ID. + * Access: Index + */ +MLXSW_ITEM32(reg, qpcr, pid, 0x00, 0, 14); + +/* reg_qpcr_color_aware + * Is the policer aware of colors. + * Must be 0 (unaware) for cpu port. + * Access: RW for unbounded policer. RO for bounded policer. + */ +MLXSW_ITEM32(reg, qpcr, color_aware, 0x04, 15, 1); + +/* reg_qpcr_bytes + * Is policer limit is for bytes per sec or packets per sec. + * 0 - packets + * 1 - bytes + * Access: RW for unbounded policer. RO for bounded policer. + */ +MLXSW_ITEM32(reg, qpcr, bytes, 0x04, 14, 1); + +enum mlxsw_reg_qpcr_ir_units { + MLXSW_REG_QPCR_IR_UNITS_M, + MLXSW_REG_QPCR_IR_UNITS_K, +}; + +/* reg_qpcr_ir_units + * Policer's units for cir and eir fields (for bytes limits only) + * 1 - 10^3 + * 0 - 10^6 + * Access: OP + */ +MLXSW_ITEM32(reg, qpcr, ir_units, 0x04, 12, 1); + +enum mlxsw_reg_qpcr_rate_type { + MLXSW_REG_QPCR_RATE_TYPE_SINGLE = 1, + MLXSW_REG_QPCR_RATE_TYPE_DOUBLE = 2, +}; + +/* reg_qpcr_rate_type + * Policer can have one limit (single rate) or 2 limits with specific operation + * for packets that exceed the lower rate but not the upper one. + * (For cpu port must be single rate) + * Access: RW for unbounded policer. RO for bounded policer. + */ +MLXSW_ITEM32(reg, qpcr, rate_type, 0x04, 8, 2); + +/* reg_qpc_cbs + * Policer's committed burst size. + * The policer is working with time slices of 50 nano sec. By default every + * slice is granted the proportionate share of the committed rate. If we want to + * allow a slice to exceed that share (while still keeping the rate per sec) we + * can allow burst. The burst size is between the default proportionate share + * (and no lower than 8) to 32Gb. (Even though giving a number higher than the + * committed rate will result in exceeding the rate). The burst size must be a + * log of 2 and will be determined by 2^cbs. + * Access: RW + */ +MLXSW_ITEM32(reg, qpcr, cbs, 0x08, 24, 6); + +/* reg_qpcr_cir + * Policer's committed rate. + * The rate used for sungle rate, the lower rate for double rate. + * For bytes limits, the rate will be this value * the unit from ir_units. + * (Resolution error is up to 1%). + * Access: RW + */ +MLXSW_ITEM32(reg, qpcr, cir, 0x0C, 0, 32); + +/* reg_qpcr_eir + * Policer's exceed rate. + * The higher rate for double rate, reserved for single rate. + * Lower rate for double rate policer. + * For bytes limits, the rate will be this value * the unit from ir_units. + * (Resolution error is up to 1%). + * Access: RW + */ +MLXSW_ITEM32(reg, qpcr, eir, 0x10, 0, 32); + +#define MLXSW_REG_QPCR_DOUBLE_RATE_ACTION 2 + +/* reg_qpcr_exceed_action. + * What to do with packets between the 2 limits for double rate. + * Access: RW for unbounded policer. RO for bounded policer. + */ +MLXSW_ITEM32(reg, qpcr, exceed_action, 0x14, 0, 4); + +enum mlxsw_reg_qpcr_action { + /* Discard */ + MLXSW_REG_QPCR_ACTION_DISCARD = 1, + /* Forward and set color to red. + * If the packet is intended to cpu port, it will be dropped. + */ + MLXSW_REG_QPCR_ACTION_FORWARD = 2, +}; + +/* reg_qpcr_violate_action + * What to do with packets that cross the cir limit (for single rate) or the eir + * limit (for double rate). + * Access: RW for unbounded policer. RO for bounded policer. + */ +MLXSW_ITEM32(reg, qpcr, violate_action, 0x18, 0, 4); + +static inline void mlxsw_reg_qpcr_pack(char *payload, u16 pid, + enum mlxsw_reg_qpcr_ir_units ir_units, + bool bytes, u32 cir, u16 cbs) +{ + MLXSW_REG_ZERO(qpcr, payload); + mlxsw_reg_qpcr_pid_set(payload, pid); + mlxsw_reg_qpcr_g_set(payload, MLXSW_REG_QPCR_G_GLOBAL); + mlxsw_reg_qpcr_rate_type_set(payload, MLXSW_REG_QPCR_RATE_TYPE_SINGLE); + mlxsw_reg_qpcr_violate_action_set(payload, + MLXSW_REG_QPCR_ACTION_DISCARD); + mlxsw_reg_qpcr_cir_set(payload, cir); + mlxsw_reg_qpcr_ir_units_set(payload, ir_units); + mlxsw_reg_qpcr_bytes_set(payload, bytes); + mlxsw_reg_qpcr_cbs_set(payload, cbs); +} + /* QTCT - QoS Switch Traffic Class Table * ------------------------------------- * Configures the mapping between the packet switch priority and the @@ -5254,6 +5394,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(svpe), MLXSW_REG(sfmr), MLXSW_REG(spvmlr), + MLXSW_REG(qpcr), MLXSW_REG(qtct), MLXSW_REG(qeec), MLXSW_REG(pmlp), -- cgit v1.1 From 9148e7cf73ced9bf9d2a1104f6ab3b69303662a2 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Fri, 25 Nov 2016 10:33:47 +0100 Subject: mlxsw: spectrum: Add policers for trap groups Configure policers and connect them to trap groups. While many trap groups share policer's configuration they don't share the actual policer because each trap group represents a different flow / protocol and we don't want one of them to be able to exceed its rate on behalf of another. For example, if STP and LLDP gets to send 128 packets/sec each, if we put them in one 256 packets/sec policer, one can send 200 packets while the other only 50. Note that IP2ME covers lots of flows, so it's limit is set to match the cpu ability to handle data. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 74 +++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 2207001..fece974 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2767,20 +2767,82 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_NO_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false), }; +static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core) +{ + char qpcr_pl[MLXSW_REG_QPCR_LEN]; + enum mlxsw_reg_qpcr_ir_units ir_units; + int max_cpu_policers; + bool is_bytes; + u8 burst_size; + u32 rate; + int i, err; + + if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_CPU_POLICERS)) + return -EIO; + + max_cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS); + + ir_units = MLXSW_REG_QPCR_IR_UNITS_M; + for (i = 0; i < max_cpu_policers; i++) { + is_bytes = false; + switch (i) { + case MLXSW_REG_HTGT_TRAP_GROUP_SP_STP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF: + rate = 128; + burst_size = 7; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP: + rate = 16 * 1024; + burst_size = 10; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: + rate = 1024; + burst_size = 7; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME: + is_bytes = true; + rate = 4 * 1024; + burst_size = 4; + break; + default: + continue; + } + + mlxsw_reg_qpcr_pack(qpcr_pl, i, ir_units, is_bytes, rate, + burst_size); + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(qpcr), qpcr_pl); + if (err) + return err; + } + + return 0; +} + static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) { char htgt_pl[MLXSW_REG_HTGT_LEN]; enum mlxsw_reg_htgt_trap_group i; + int max_cpu_policers; int max_trap_groups; u8 priority, tc; + u16 policer_id; int err; if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_TRAP_GROUPS)) return -EIO; max_trap_groups = MLXSW_CORE_RES_GET(mlxsw_core, MAX_TRAP_GROUPS); + max_cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS); for (i = 0; i < max_trap_groups; i++) { + policer_id = i; switch (i) { case MLXSW_REG_HTGT_TRAP_GROUP_SP_STP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP: @@ -2812,13 +2874,17 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) case MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT: priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY; tc = MLXSW_REG_HTGT_DEFAULT_TC; + policer_id = MLXSW_REG_HTGT_INVALID_POLICER; break; default: continue; } - mlxsw_reg_htgt_pack(htgt_pl, i, MLXSW_REG_HTGT_INVALID_POLICER, - priority, tc); + if (max_cpu_policers <= policer_id && + policer_id != MLXSW_REG_HTGT_INVALID_POLICER) + return -EIO; + + mlxsw_reg_htgt_pack(htgt_pl, i, policer_id, priority, tc); err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); if (err) return err; @@ -2832,6 +2898,10 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) int i; int err; + err = mlxsw_sp_cpu_policers_set(mlxsw_sp->core); + if (err) + return err; + err = mlxsw_sp_trap_groups_set(mlxsw_sp->core); if (err) return err; -- cgit v1.1 From 21fcf572716f61926f5d23a358e79d06a9d4d54f Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Wed, 5 Oct 2016 22:51:42 +0200 Subject: Bluetooth: __ variants of u8 and friends are not neccessary inside kernel bluetooth.h is not part of user API, so __ variants are not neccessary here. Signed-off-by: Pavel Machek Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 0a1e21d..0148719 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -197,7 +197,7 @@ typedef struct { #define BDADDR_LE_PUBLIC 0x01 #define BDADDR_LE_RANDOM 0x02 -static inline bool bdaddr_type_is_valid(__u8 type) +static inline bool bdaddr_type_is_valid(u8 type) { switch (type) { case BDADDR_BREDR: @@ -209,7 +209,7 @@ static inline bool bdaddr_type_is_valid(__u8 type) return false; } -static inline bool bdaddr_type_is_le(__u8 type) +static inline bool bdaddr_type_is_le(u8 type) { switch (type) { case BDADDR_LE_PUBLIC: @@ -279,15 +279,16 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); /* Skb helpers */ struct l2cap_ctrl { - __u8 sframe:1, + u8 sframe:1, poll:1, final:1, fcs:1, sar:2, super:2; - __u16 reqseq; - __u16 txseq; - __u8 retries; + + u16 reqseq; + u16 txseq; + u8 retries; __le16 psm; bdaddr_t bdaddr; struct l2cap_chan *chan; @@ -303,7 +304,7 @@ typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status, #define HCI_REQ_SKB BIT(1) struct hci_ctrl { - __u16 opcode; + u16 opcode; u8 req_flags; u8 req_event; union { @@ -313,10 +314,10 @@ struct hci_ctrl { }; struct bt_skb_cb { - __u8 pkt_type; - __u8 force_active; - __u16 expect; - __u8 incoming:1; + u8 pkt_type; + u8 force_active; + u16 expect; + u8 incoming:1; union { struct l2cap_ctrl l2cap; struct hci_ctrl hci; @@ -366,7 +367,7 @@ out: return NULL; } -int bt_to_errno(__u16 code); +int bt_to_errno(u16 code); void hci_sock_set_flag(struct sock *sk, int nr); void hci_sock_clear_flag(struct sock *sk, int nr); -- cgit v1.1 From 6e5ffd0f4509f7331ecebf12463f4eb5a213cf4a Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Mon, 17 Oct 2016 18:31:08 +0000 Subject: Bluetooth: hci_bcsp: Use setup_timer Kernel API instead of init_timer Replace init_timer function with setup_timer reported by coccinelle Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcsp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index a2c921f..910ec96 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -733,9 +733,7 @@ static int bcsp_open(struct hci_uart *hu) skb_queue_head_init(&bcsp->rel); skb_queue_head_init(&bcsp->unrel); - init_timer(&bcsp->tbcsp); - bcsp->tbcsp.function = bcsp_timed_event; - bcsp->tbcsp.data = (u_long)hu; + setup_timer(&bcsp->tbcsp, bcsp_timed_event, (u_long)hu); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -- cgit v1.1 From a1e0d04396684a38fd4cdaf084d38bf2cd5a2253 Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Tue, 18 Oct 2016 17:30:10 +0000 Subject: Bluetooth: hci_h5: Use setup_timer Kernel API instead of init_timer Replace init_timer function with setup_timer reported by coccinelle Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 0879d64..90d0456 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -204,9 +204,7 @@ static int h5_open(struct hci_uart *hu) h5_reset_rx(h5); - init_timer(&h5->timer); - h5->timer.function = h5_timed_event; - h5->timer.data = (unsigned long)hu; + setup_timer(&h5->timer, h5_timed_event, (unsigned long)hu); h5->tx_win = H5_TX_WIN_MAX; -- cgit v1.1 From 1ae6d00ad05730bca283dc09e7a9c162c2f22b01 Mon Sep 17 00:00:00 2001 From: Prasanna Karthik Date: Tue, 18 Oct 2016 23:55:12 +0000 Subject: Bluetooth: hci_qca: Use setup_timer Kernel API instead of init_timer Replace init_timer function with setup_timer reported by coccinelle Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 6c867fb..05c2307 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -438,14 +438,11 @@ static int qca_open(struct hci_uart *hu) hu->priv = qca; - init_timer(&qca->wake_retrans_timer); - qca->wake_retrans_timer.function = hci_ibs_wake_retrans_timeout; - qca->wake_retrans_timer.data = (u_long)hu; + setup_timer(&qca->wake_retrans_timer, hci_ibs_wake_retrans_timeout, + (u_long)hu); qca->wake_retrans = IBS_WAKE_RETRANS_TIMEOUT_MS; - init_timer(&qca->tx_idle_timer); - qca->tx_idle_timer.function = hci_ibs_tx_idle_timeout; - qca->tx_idle_timer.data = (u_long)hu; + setup_timer(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, (u_long)hu); qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS; BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u", -- cgit v1.1 From b9972d2205f696fdb53ee0133aebd2d0a99e1801 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 23 Nov 2016 09:46:52 -0800 Subject: mlx4: do not use priv->stats_lock in mlx4_en_auto_moderation() Per RX ring packets/bytes counters are not protected by global priv->stats_lock. Better not confuse the reader, and use READ_ONCE() to show we read these counters without surrounding synchronization. Interrupt moderation is best effort, and we do not really care of ultra precise counters. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index c48ce3f..ea76b28 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1394,10 +1394,8 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv) return; for (ring = 0; ring < priv->rx_ring_num; ring++) { - spin_lock_bh(&priv->stats_lock); - rx_packets = priv->rx_ring[ring]->packets; - rx_bytes = priv->rx_ring[ring]->bytes; - spin_unlock_bh(&priv->stats_lock); + rx_packets = READ_ONCE(priv->rx_ring[ring]->packets); + rx_bytes = READ_ONCE(priv->rx_ring[ring]->bytes); rx_pkt_diff = ((unsigned long) (rx_packets - priv->last_moder_packets[ring])); -- cgit v1.1 From 428951161b8ab73d547c8915dcfc5fa6f10012a8 Mon Sep 17 00:00:00 2001 From: Ulrik De Bie Date: Wed, 23 Nov 2016 21:11:04 +0100 Subject: ptp: gianfar: Use high resolution frequency method. This patch depends on commit d8d263541913 ("ptp: Introduce a high resolution frequency adjustment method.") The gianfar devices offer a frequency resolution of about 0.46 ppb (depends on actual value of tmr_add, for the calculation assumed 0x80000000). This patch lets users of the device benefit from the increased frequency resolution when tuning the clock. Thanks to the rounding the maximum error between the requested frequency and the applied frequency will then be about 0.23 ppb. Tested on a v3.3.8 kernel on a real gianfar device. Verified compilation on net-next (currently at v4.9-rc5). Signed-off-by: Ulrik De Bie Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ptp.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c index 3e8d1ff..721be13 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -280,21 +280,26 @@ static irqreturn_t isr(int irq, void *priv) * PTP clock operations */ -static int ptp_gianfar_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ptp_gianfar_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { - u64 adj; - u32 diff, tmr_add; + u64 adj, diff; + u32 tmr_add; int neg_adj = 0; struct etsects *etsects = container_of(ptp, struct etsects, caps); - if (ppb < 0) { + if (scaled_ppm < 0) { neg_adj = 1; - ppb = -ppb; + scaled_ppm = -scaled_ppm; } tmr_add = etsects->tmr_add; adj = tmr_add; - adj *= ppb; - diff = div_u64(adj, 1000000000ULL); + + /* calculate diff as adj*(scaled_ppm/65536)/1000000 + * and round() to the nearest integer + */ + adj *= scaled_ppm; + diff = div_u64(adj, 8000000); + diff = (diff >> 13) + ((diff >> 12) & 1); tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; @@ -415,7 +420,7 @@ static struct ptp_clock_info ptp_gianfar_caps = { .n_per_out = 0, .n_pins = 0, .pps = 1, - .adjfreq = ptp_gianfar_adjfreq, + .adjfine = ptp_gianfar_adjfine, .adjtime = ptp_gianfar_adjtime, .gettime64 = ptp_gianfar_gettime, .settime64 = ptp_gianfar_settime, -- cgit v1.1 From 5a717f4f8f2830f297b5511022481bdc27b9d576 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 24 Nov 2016 07:04:08 +0200 Subject: netdevice: fix sparse warning for HARD_TX_LOCK sparse warns about context imbalance in any code that uses HARD_TX_LOCK/UNLOCK - this is because it's unable to determine that flags don't change so lock and unlock are paired. Seems easy enough to fix by adding __acquire/__release calls. With this patch af_packet.c is now sparse-clean, Signed-off-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- include/linux/netdevice.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ff57cd2..4ffcd87 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3462,6 +3462,17 @@ static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) txq->xmit_lock_owner = cpu; } +static inline bool __netif_tx_acquire(struct netdev_queue *txq) +{ + __acquire(&txq->_xmit_lock); + return true; +} + +static inline void __netif_tx_release(struct netdev_queue *txq) +{ + __release(&txq->_xmit_lock); +} + static inline void __netif_tx_lock_bh(struct netdev_queue *txq) { spin_lock_bh(&txq->_xmit_lock); @@ -3563,17 +3574,21 @@ static inline void netif_tx_unlock_bh(struct net_device *dev) #define HARD_TX_LOCK(dev, txq, cpu) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ __netif_tx_lock(txq, cpu); \ + } else { \ + __netif_tx_acquire(txq); \ } \ } #define HARD_TX_TRYLOCK(dev, txq) \ (((dev->features & NETIF_F_LLTX) == 0) ? \ __netif_tx_trylock(txq) : \ - true ) + __netif_tx_acquire(txq)) #define HARD_TX_UNLOCK(dev, txq) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ __netif_tx_unlock(txq); \ + } else { \ + __netif_tx_release(txq); \ } \ } -- cgit v1.1 From e3739099270b238630368b1d8f547c1bb31ba6ad Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 25 Nov 2016 13:43:04 +0300 Subject: sfc: remove unneeded variable We don't use ->heap_buf after commit 46d1efd852cc ("sfc: remove Software TSO") so let's remove the last traces. Signed-off-by: Dan Carpenter Acked-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/net_driver.h | 8 +------- drivers/net/ethernet/sfc/tx.c | 2 -- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index f97f828..18aceb2 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -139,8 +139,6 @@ struct efx_special_buffer { * struct efx_tx_buffer - buffer state for a TX descriptor * @skb: When @flags & %EFX_TX_BUF_SKB, the associated socket buffer to be * freed when descriptor completes - * @heap_buf: When @flags & %EFX_TX_BUF_HEAP, the associated heap buffer to be - * freed when descriptor completes. * @option: When @flags & %EFX_TX_BUF_OPTION, a NIC-specific option descriptor. * @dma_addr: DMA address of the fragment. * @flags: Flags for allocation and DMA mapping type @@ -151,10 +149,7 @@ struct efx_special_buffer { * Only valid if @unmap_len != 0. */ struct efx_tx_buffer { - union { - const struct sk_buff *skb; - void *heap_buf; - }; + const struct sk_buff *skb; union { efx_qword_t option; dma_addr_t dma_addr; @@ -166,7 +161,6 @@ struct efx_tx_buffer { }; #define EFX_TX_BUF_CONT 1 /* not last descriptor of packet */ #define EFX_TX_BUF_SKB 2 /* buffer is last part of skb */ -#define EFX_TX_BUF_HEAP 4 /* buffer was allocated with kmalloc() */ #define EFX_TX_BUF_MAP_SINGLE 8 /* buffer was mapped with dma_map_single() */ #define EFX_TX_BUF_OPTION 0x10 /* empty buffer for option descriptor */ diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 1aa728c..bb07034 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -84,8 +84,6 @@ static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, netif_vdbg(tx_queue->efx, tx_done, tx_queue->efx->net_dev, "TX queue %d transmission id %x complete\n", tx_queue->queue, tx_queue->read_count); - } else if (buffer->flags & EFX_TX_BUF_HEAP) { - kfree(buffer->heap_buf); } buffer->len = 0; -- cgit v1.1 From 88575199cc65de99a156888629a68180c830eff2 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 26 Nov 2016 01:28:04 +0100 Subject: bpf: drop unnecessary context cast from BPF_PROG_RUN Since long already bpf_func is not only about struct sk_buff * as input anymore. Make it generic as void *, so that callers don't need to cast for it each time they call BPF_PROG_RUN(). Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 2 +- include/linux/filter.h | 6 +++--- kernel/events/core.c | 2 +- kernel/seccomp.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index eb37157..876ab3a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1518,7 +1518,7 @@ static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, unsigned int len) xdp.data = data; xdp.data_end = data + len; - return BPF_PROG_RUN(prog, (void *)&xdp); + return BPF_PROG_RUN(prog, &xdp); } /** diff --git a/include/linux/filter.h b/include/linux/filter.h index 1f09c52..7f246a2 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -408,8 +408,8 @@ struct bpf_prog { enum bpf_prog_type type; /* Type of BPF program */ struct bpf_prog_aux *aux; /* Auxiliary fields */ struct sock_fprog_kern *orig_prog; /* Original BPF program */ - unsigned int (*bpf_func)(const struct sk_buff *skb, - const struct bpf_insn *filter); + unsigned int (*bpf_func)(const void *ctx, + const struct bpf_insn *insn); /* Instructions for interpreter */ union { struct sock_filter insns[0]; @@ -504,7 +504,7 @@ static inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog, u32 ret; rcu_read_lock(); - ret = BPF_PROG_RUN(prog, (void *)xdp); + ret = BPF_PROG_RUN(prog, xdp); rcu_read_unlock(); return ret; diff --git a/kernel/events/core.c b/kernel/events/core.c index 6ee1feb..22cc734 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7726,7 +7726,7 @@ static void bpf_overflow_handler(struct perf_event *event, if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) goto out; rcu_read_lock(); - ret = BPF_PROG_RUN(event->prog, (void *)&ctx); + ret = BPF_PROG_RUN(event->prog, &ctx); rcu_read_unlock(); out: __this_cpu_dec(bpf_prog_active); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 0db7c8a..bff9c77 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -195,7 +195,7 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd) * value always takes priority (ignoring the DATA). */ for (; f; f = f->prev) { - u32 cur_ret = BPF_PROG_RUN(f->prog, (void *)sd); + u32 cur_ret = BPF_PROG_RUN(f->prog, sd); if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) ret = cur_ret; -- cgit v1.1 From 55556dd59dfc49e9429b058cf7138a6726c949c7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 26 Nov 2016 01:28:05 +0100 Subject: bpf: drop useless bpf_fd member from cls/act After setup we don't need to keep user space fd number around anymore, as it also has no useful meaning for anyone, just remove it. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/sched/act_bpf.c | 7 ------- net/sched/cls_bpf.c | 9 +-------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 1aa4ecf..84c1d2d 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -28,7 +28,6 @@ struct tcf_bpf_cfg { struct bpf_prog *filter; struct sock_filter *bpf_ops; const char *bpf_name; - u32 bpf_fd; u16 bpf_num_ops; bool is_ebpf; }; @@ -118,9 +117,6 @@ static int tcf_bpf_dump_bpf_info(const struct tcf_bpf *prog, static int tcf_bpf_dump_ebpf_info(const struct tcf_bpf *prog, struct sk_buff *skb) { - if (nla_put_u32(skb, TCA_ACT_BPF_FD, prog->bpf_fd)) - return -EMSGSIZE; - if (prog->bpf_name && nla_put_string(skb, TCA_ACT_BPF_NAME, prog->bpf_name)) return -EMSGSIZE; @@ -233,7 +229,6 @@ static int tcf_bpf_init_from_efd(struct nlattr **tb, struct tcf_bpf_cfg *cfg) } } - cfg->bpf_fd = bpf_fd; cfg->bpf_name = name; cfg->filter = fp; cfg->is_ebpf = true; @@ -332,8 +327,6 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, if (cfg.bpf_num_ops) prog->bpf_num_ops = cfg.bpf_num_ops; - if (cfg.bpf_fd) - prog->bpf_fd = cfg.bpf_fd; prog->tcf_action = parm->action; rcu_assign_pointer(prog->filter, cfg.filter); diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 52dc85a..28cb5fa 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -45,10 +45,7 @@ struct cls_bpf_prog { u32 gen_flags; struct tcf_exts exts; u32 handle; - union { - u32 bpf_fd; - u16 bpf_num_ops; - }; + u16 bpf_num_ops; struct sock_filter *bpf_ops; const char *bpf_name; struct tcf_proto *tp; @@ -377,7 +374,6 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, } prog->bpf_ops = NULL; - prog->bpf_fd = bpf_fd; prog->bpf_name = name; prog->filter = fp; @@ -561,9 +557,6 @@ static int cls_bpf_dump_bpf_info(const struct cls_bpf_prog *prog, static int cls_bpf_dump_ebpf_info(const struct cls_bpf_prog *prog, struct sk_buff *skb) { - if (nla_put_u32(skb, TCA_BPF_FD, prog->bpf_fd)) - return -EMSGSIZE; - if (prog->bpf_name && nla_put_string(skb, TCA_BPF_NAME, prog->bpf_name)) return -EMSGSIZE; -- cgit v1.1 From c491680f8f489926eebfdf2cd006767fc8bdaa49 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 26 Nov 2016 01:28:06 +0100 Subject: bpf: reuse dev_is_mac_header_xmit for redirect Commit dcf800344a91 ("net/sched: act_mirred: Refactor detection whether dev needs xmit at mac header") added dev_is_mac_header_xmit(); since it's also useful elsewhere, move it to if_arp.h and reuse it for BPF. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/if_arp.h | 16 ++++++++++++++++ net/core/filter.c | 14 ++++---------- net/sched/act_mirred.c | 15 +-------------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index f563907..3355efc 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -44,4 +44,20 @@ static inline int arp_hdr_len(struct net_device *dev) return sizeof(struct arphdr) + (dev->addr_len + sizeof(u32)) * 2; } } + +static inline bool dev_is_mac_header_xmit(const struct net_device *dev) +{ + switch (dev->type) { + case ARPHRD_TUNNEL: + case ARPHRD_TUNNEL6: + case ARPHRD_SIT: + case ARPHRD_IPGRE: + case ARPHRD_VOID: + case ARPHRD_NONE: + return false; + default: + return true; + } +} + #endif /* _LINUX_IF_ARP_H */ diff --git a/net/core/filter.c b/net/core/filter.c index ea315af..698a262 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1696,17 +1697,10 @@ static int __bpf_redirect_common(struct sk_buff *skb, struct net_device *dev, static int __bpf_redirect(struct sk_buff *skb, struct net_device *dev, u32 flags) { - switch (dev->type) { - case ARPHRD_TUNNEL: - case ARPHRD_TUNNEL6: - case ARPHRD_SIT: - case ARPHRD_IPGRE: - case ARPHRD_VOID: - case ARPHRD_NONE: - return __bpf_redirect_no_mac(skb, dev, flags); - default: + if (dev_is_mac_header_xmit(dev)) return __bpf_redirect_common(skb, dev, flags); - } + else + return __bpf_redirect_no_mac(skb, dev, flags); } BPF_CALL_3(bpf_clone_redirect, struct sk_buff *, skb, u32, ifindex, u64, flags) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index b2d417b..1af7baa 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -73,20 +74,6 @@ static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { static unsigned int mirred_net_id; static struct tc_action_ops act_mirred_ops; -static bool dev_is_mac_header_xmit(const struct net_device *dev) -{ - switch (dev->type) { - case ARPHRD_TUNNEL: - case ARPHRD_TUNNEL6: - case ARPHRD_SIT: - case ARPHRD_IPGRE: - case ARPHRD_VOID: - case ARPHRD_NONE: - return false; - } - return true; -} - static int tcf_mirred_init(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_action **a, int ovr, int bind) -- cgit v1.1 From 21116b7068b9b66ac16b2fe3675469f459968c3f Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 26 Nov 2016 01:28:07 +0100 Subject: bpf: add owner_prog_type and accounted mem to array map's fdinfo Allow for checking the owner_prog_type of a program array map. In some cases bpf(2) can return -EINVAL /after/ the verifier passed and did all the rewrites of the bpf program. The reason that lets us fail at this late stage is that program array maps are incompatible. Allow users to inspect this earlier after they got the map fd through BPF_OBJ_GET command. tc will get support for this. Also, display how much we charged the map with regards to RLIMIT_MEMLOCK. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/syscall.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 1090d16..4caa18e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -138,18 +138,31 @@ static int bpf_map_release(struct inode *inode, struct file *filp) static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp) { const struct bpf_map *map = filp->private_data; + const struct bpf_array *array; + u32 owner_prog_type = 0; + + if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) { + array = container_of(map, struct bpf_array, map); + owner_prog_type = array->owner_prog_type; + } seq_printf(m, "map_type:\t%u\n" "key_size:\t%u\n" "value_size:\t%u\n" "max_entries:\t%u\n" - "map_flags:\t%#x\n", + "map_flags:\t%#x\n" + "memlock:\t%llu\n", map->map_type, map->key_size, map->value_size, map->max_entries, - map->map_flags); + map->map_flags, + map->pages * 1ULL << PAGE_SHIFT); + + if (owner_prog_type) + seq_printf(m, "owner_prog_type:\t%u\n", + owner_prog_type); } #endif -- cgit v1.1 From a3af5f80010625a9ffbe8edd4bae615a7516b6bc Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 26 Nov 2016 01:28:08 +0100 Subject: bpf: allow for mount options to specify permissions Since we recently converted the BPF filesystem over to use mount_nodev(), we now have the possibility to also hold mount options in sb's s_fs_info. This work implements mount options support for specifying permissions on the sb's inode, which will be used by tc when it manually needs to mount the fs. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/inode.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 2565809..0b030c9 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -364,15 +365,66 @@ static void bpf_evict_inode(struct inode *inode) static const struct super_operations bpf_super_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, + .show_options = generic_show_options, .evict_inode = bpf_evict_inode, }; +enum { + OPT_MODE, + OPT_ERR, +}; + +static const match_table_t bpf_mount_tokens = { + { OPT_MODE, "mode=%o" }, + { OPT_ERR, NULL }, +}; + +struct bpf_mount_opts { + umode_t mode; +}; + +static int bpf_parse_options(char *data, struct bpf_mount_opts *opts) +{ + substring_t args[MAX_OPT_ARGS]; + int option, token; + char *ptr; + + opts->mode = S_IRWXUGO; + + while ((ptr = strsep(&data, ",")) != NULL) { + if (!*ptr) + continue; + + token = match_token(ptr, bpf_mount_tokens, args); + switch (token) { + case OPT_MODE: + if (match_octal(&args[0], &option)) + return -EINVAL; + opts->mode = option & S_IALLUGO; + break; + /* We might like to report bad mount options here, but + * traditionally we've ignored all mount options, so we'd + * better continue to ignore non-existing options for bpf. + */ + } + } + + return 0; +} + static int bpf_fill_super(struct super_block *sb, void *data, int silent) { static struct tree_descr bpf_rfiles[] = { { "" } }; + struct bpf_mount_opts opts; struct inode *inode; int ret; + save_mount_options(sb, data); + + ret = bpf_parse_options(data, &opts); + if (ret) + return ret; + ret = simple_fill_super(sb, BPF_FS_MAGIC, bpf_rfiles); if (ret) return ret; @@ -382,7 +434,7 @@ static int bpf_fill_super(struct super_block *sb, void *data, int silent) inode = sb->s_root->d_inode; inode->i_op = &bpf_dir_iops; inode->i_mode &= ~S_IALLUGO; - inode->i_mode |= S_ISVTX | S_IRWXUGO; + inode->i_mode |= S_ISVTX | opts.mode; return 0; } -- cgit v1.1 From e00c7b216f34444252f3771f7d4ed48d4f032636 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 26 Nov 2016 01:28:09 +0100 Subject: bpf: fix multiple issues in selftest suite and samples 1) The test_lru_map and test_lru_dist fails building on my machine since the sys/resource.h header is not included. 2) test_verifier fails in one test case where we try to call an invalid function, since the verifier log output changed wrt printing function names. 3) Current selftest suite code relies on sysconf(_SC_NPROCESSORS_CONF) for retrieving the number of possible CPUs. This is broken at least in our scenario and really just doesn't work. glibc tries a number of things for retrieving _SC_NPROCESSORS_CONF. First it tries equivalent of /sys/devices/system/cpu/cpu[0-9]* | wc -l, if that fails, depending on the config, it either tries to count CPUs in /proc/cpuinfo, or returns the _SC_NPROCESSORS_ONLN value instead. If /proc/cpuinfo has some issue, it returns just 1 worst case. This oddity is nothing new [1], but semantics/behaviour seems to be settled. _SC_NPROCESSORS_ONLN will parse /sys/devices/system/cpu/online, if that fails it looks into /proc/stat for cpuX entries, and if also that fails for some reason, /proc/cpuinfo is consulted (and returning 1 if unlikely all breaks down). While that might match num_possible_cpus() from the kernel in some cases, it's really not guaranteed with CPU hotplugging, and can result in a buffer overflow since the array in user space could have too few number of slots, and on perpcu map lookup, the kernel will write beyond that memory of the value buffer. William Tu reported such mismatches: [...] The fact that sysconf(_SC_NPROCESSORS_CONF) != num_possible_cpu() happens when CPU hotadd is enabled. For example, in Fusion when setting vcpu.hotadd = "TRUE" or in KVM, setting ./qemu-system-x86_64 -smp 2, maxcpus=4 ... the num_possible_cpu() will be 4 and sysconf() will be 2 [2]. [...] Documentation/cputopology.txt says /sys/devices/system/cpu/possible outputs cpu_possible_mask. That is the same as in num_possible_cpus(), so first step would be to fix the _SC_NPROCESSORS_CONF calls with our own implementation. Later, we could add support to bpf(2) for passing a mask via CPU_SET(3), for example, to just select a subset of CPUs. BPF samples code needs this fix as well (at least so that people stop copying this). Thus, define bpf_num_possible_cpus() once in selftests and import it from there for the sample code to avoid duplicating it. The remaining sysconf(_SC_NPROCESSORS_CONF) in samples are unrelated. After all three issues are fixed, the test suite runs fine again: # make run_tests | grep self selftests: test_verifier [PASS] selftests: test_maps [PASS] selftests: test_lru_map [PASS] selftests: test_kmod.sh [PASS] [1] https://www.sourceware.org/ml/libc-alpha/2011-06/msg00079.html [2] https://www.mail-archive.com/netdev@vger.kernel.org/msg121183.html Fixes: 3059303f59cf ("samples/bpf: update tracex[23] examples to use per-cpu maps") Fixes: 86af8b4191d2 ("Add sample for adding simple drop program to link") Fixes: df570f577231 ("samples/bpf: unit test for BPF_MAP_TYPE_PERCPU_ARRAY") Fixes: e15596717948 ("samples/bpf: unit test for BPF_MAP_TYPE_PERCPU_HASH") Fixes: ebb676daa1a3 ("bpf: Print function name in addition to function id") Fixes: 5db58faf989f ("bpf: Add tests for the LRU bpf_htab") Signed-off-by: Daniel Borkmann Cc: William Tu Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 1 + samples/bpf/test_lru_dist.c | 5 +++- samples/bpf/tracex2_user.c | 4 ++- samples/bpf/tracex3_user.c | 6 +++-- samples/bpf/xdp1_user.c | 4 ++- tools/testing/selftests/bpf/bpf_util.h | 38 +++++++++++++++++++++++++++++ tools/testing/selftests/bpf/test_lru_map.c | 8 ++++-- tools/testing/selftests/bpf/test_maps.c | 7 +++--- tools/testing/selftests/bpf/test_verifier.c | 2 +- 9 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 tools/testing/selftests/bpf/bpf_util.h diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index fb17206..22b6407e 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -91,6 +91,7 @@ always += trace_event_kern.o always += sampleip_kern.o HOSTCFLAGS += -I$(objtree)/usr/include +HOSTCFLAGS += -I$(objtree)/tools/testing/selftests/bpf/ HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable HOSTLOADLIBES_fds_example += -lelf diff --git a/samples/bpf/test_lru_dist.c b/samples/bpf/test_lru_dist.c index 2859977..316230a 100644 --- a/samples/bpf/test_lru_dist.c +++ b/samples/bpf/test_lru_dist.c @@ -16,10 +16,13 @@ #include #include #include +#include #include #include #include + #include "libbpf.h" +#include "bpf_util.h" #define min(a, b) ((a) < (b) ? (a) : (b)) #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) @@ -510,7 +513,7 @@ int main(int argc, char **argv) srand(time(NULL)); - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + nr_cpus = bpf_num_possible_cpus(); assert(nr_cpus != -1); printf("nr_cpus:%d\n\n", nr_cpus); diff --git a/samples/bpf/tracex2_user.c b/samples/bpf/tracex2_user.c index ab5b19e..3e225e3 100644 --- a/samples/bpf/tracex2_user.c +++ b/samples/bpf/tracex2_user.c @@ -4,8 +4,10 @@ #include #include #include + #include "libbpf.h" #include "bpf_load.h" +#include "bpf_util.h" #define MAX_INDEX 64 #define MAX_STARS 38 @@ -36,8 +38,8 @@ struct hist_key { static void print_hist_for_pid(int fd, void *task) { + unsigned int nr_cpus = bpf_num_possible_cpus(); struct hist_key key = {}, next_key; - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); long values[nr_cpus]; char starstr[MAX_STARS]; long value; diff --git a/samples/bpf/tracex3_user.c b/samples/bpf/tracex3_user.c index 48716f7..d0851cb 100644 --- a/samples/bpf/tracex3_user.c +++ b/samples/bpf/tracex3_user.c @@ -11,8 +11,10 @@ #include #include #include + #include "libbpf.h" #include "bpf_load.h" +#include "bpf_util.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) @@ -20,7 +22,7 @@ static void clear_stats(int fd) { - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + unsigned int nr_cpus = bpf_num_possible_cpus(); __u64 values[nr_cpus]; __u32 key; @@ -77,7 +79,7 @@ static void print_banner(void) static void print_hist(int fd) { - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + unsigned int nr_cpus = bpf_num_possible_cpus(); __u64 total_events = 0; long values[nr_cpus]; __u64 max_cnt = 0; diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index a5e109e..2b2150d 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -15,7 +15,9 @@ #include #include #include + #include "bpf_load.h" +#include "bpf_util.h" #include "libbpf.h" static int set_link_xdp_fd(int ifindex, int fd) @@ -120,7 +122,7 @@ static void int_exit(int sig) */ static void poll_stats(int interval) { - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + unsigned int nr_cpus = bpf_num_possible_cpus(); const unsigned int nr_keys = 256; __u64 values[nr_cpus], prev[nr_keys][nr_cpus]; __u32 key; diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h new file mode 100644 index 0000000..84a5d18 --- /dev/null +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -0,0 +1,38 @@ +#ifndef __BPF_UTIL__ +#define __BPF_UTIL__ + +#include +#include +#include +#include + +static inline unsigned int bpf_num_possible_cpus(void) +{ + static const char *fcpu = "/sys/devices/system/cpu/possible"; + unsigned int start, end, possible_cpus = 0; + char buff[128]; + FILE *fp; + + fp = fopen(fcpu, "r"); + if (!fp) { + printf("Failed to open %s: '%s'!\n", fcpu, strerror(errno)); + exit(1); + } + + while (fgets(buff, sizeof(buff), fp)) { + if (sscanf(buff, "%u-%u", &start, &end) == 2) { + possible_cpus = start == 0 ? end + 1 : 0; + break; + } + } + + fclose(fp); + if (!possible_cpus) { + printf("Failed to retrieve # possible CPUs!\n"); + exit(1); + } + + return possible_cpus; +} + +#endif /* __BPF_UTIL__ */ diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index 627757e..b13fed5 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -12,10 +12,14 @@ #include #include #include -#include #include #include + +#include +#include + #include "bpf_sys.h" +#include "bpf_util.h" #define LOCAL_FREE_TARGET (128) #define PERCPU_FREE_TARGET (16) @@ -559,7 +563,7 @@ int main(int argc, char **argv) assert(!setrlimit(RLIMIT_MEMLOCK, &r)); - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + nr_cpus = bpf_num_possible_cpus(); assert(nr_cpus != -1); printf("nr_cpus:%d\n\n", nr_cpus); diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index ee384f0..eedfef8 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -22,6 +22,7 @@ #include #include "bpf_sys.h" +#include "bpf_util.h" static int map_flags; @@ -110,7 +111,7 @@ static void test_hashmap(int task, void *data) static void test_hashmap_percpu(int task, void *data) { - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + unsigned int nr_cpus = bpf_num_possible_cpus(); long long value[nr_cpus]; long long key, next_key; int expected_key_mask = 0; @@ -258,7 +259,7 @@ static void test_arraymap(int task, void *data) static void test_arraymap_percpu(int task, void *data) { - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + unsigned int nr_cpus = bpf_num_possible_cpus(); int key, next_key, fd, i; long values[nr_cpus]; @@ -313,7 +314,7 @@ static void test_arraymap_percpu(int task, void *data) static void test_arraymap_percpu_many_keys(void) { - unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + unsigned int nr_cpus = bpf_num_possible_cpus(); unsigned int nr_keys = 20000; long values[nr_cpus]; int key, fd, i; diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 0ef8eaf..3c4a1fb 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -285,7 +285,7 @@ static struct bpf_test tests[] = { BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 1234567), BPF_EXIT_INSN(), }, - .errstr = "invalid func 1234567", + .errstr = "invalid func unknown#1234567", .result = REJECT, }, { -- cgit v1.1 From c06696a9582098e4e12b027effa405135f79aaa5 Mon Sep 17 00:00:00 2001 From: Barry Day Date: Sat, 26 Nov 2016 12:12:03 +1000 Subject: rtl8xxxu: tx rate reported before set Move the dev_info call that attempts to show the rate used before it is set. Signed-off-by: Barry Day Reported-by: Stephen Rothwell Signed-off-by: Barry Day Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 04141e5..a9137ab 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4844,16 +4844,16 @@ rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, tx_desc40 = (struct rtl8xxxu_txdesc40 *)tx_desc32; - if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX) - dev_info(dev, "%s: TX rate: %d, pkt size %d\n", - __func__, rate, cpu_to_le16(tx_desc40->pkt_size)); - if (rate_flags & IEEE80211_TX_RC_MCS && !ieee80211_is_mgmt(hdr->frame_control)) rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; else rate = tx_rate->hw_value; + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX) + dev_info(dev, "%s: TX rate: %d, pkt size %d\n", + __func__, rate, cpu_to_le16(tx_desc40->pkt_size)); + seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); tx_desc40->txdw4 = cpu_to_le32(rate); -- cgit v1.1 From c534178bdd74a2f2fb56d8050743624b0b08403a Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Fri, 25 Nov 2016 09:41:29 +0100 Subject: net: dsa: mv88e6xxx: add g1_irqs definition for MV88E6097 Add the missing definition of g1_irqs for MV88E6097. Signed-off-by: Stefan Eichenberger Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b14b3d5..7002d0f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3602,6 +3602,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 8, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6097_ops, }, -- cgit v1.1 From 15da3cc890f8dc8451f2a92c9bc0dcfe5386daef Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Fri, 25 Nov 2016 09:41:30 +0100 Subject: net: dsa: mv88e6xxx: add missing comment for MV88E6097 Add a missing comment for the MV88E6097 because of unification. Signed-off-by: Stefan Eichenberger Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 7002d0f..68eb8fc 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3210,6 +3210,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { }; static const struct mv88e6xxx_ops mv88e6097_ops = { + /* MV88E6XXX_FAMILY_6097 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, -- cgit v1.1 From 4490001029012539937ff02778fe6180613fa949 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 25 Nov 2016 12:37:26 +0800 Subject: virtio-net: enable multiqueue by default We use single queue even if multiqueue is enabled and let admin to enable it through ethtool later. This is used to avoid possible regression (small packet TCP stream transmission). But looks like an overkill since: - single queue user can disable multiqueue when launching qemu - brings extra troubles for the management since it needs extra admin tool in guest to enable multiqueue - multiqueue performs much better than single queue in most of the cases So this patch enables multiqueue by default: if #queues is less than or equal to #vcpu, enable as much as queue pairs; if #queues is greater than #vcpu, enable #vcpu queue pairs. Cc: Hannes Frederic Sowa Cc: Michael S. Tsirkin Cc: Neil Horman Cc: Jeremy Eder Cc: Marko Myllynen Cc: Maxime Coquelin Signed-off-by: Jason Wang Acked-by: Neil Horman Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index d4ac7a6..a21d93a 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1886,8 +1886,11 @@ static int virtnet_probe(struct virtio_device *vdev) if (vi->any_header_sg) dev->needed_headroom = vi->hdr_len; - /* Use single tx/rx queue pair as default */ - vi->curr_queue_pairs = 1; + /* Enable multiqueue by default */ + if (num_online_cpus() >= max_queue_pairs) + vi->curr_queue_pairs = max_queue_pairs; + else + vi->curr_queue_pairs = num_online_cpus(); vi->max_queue_pairs = max_queue_pairs; /* Allocate/initialize the rx/tx queues, and invoke find_vqs */ @@ -1918,6 +1921,8 @@ static int virtnet_probe(struct virtio_device *vdev) goto free_unregister_netdev; } + virtnet_set_affinity(vi); + /* Assume link up if device can't report link status, otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -- cgit v1.1 From 80653f73c53de8f5874e03d0d3bdd1943c2a33cf Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 27 Nov 2016 17:02:03 +0200 Subject: net/mlx5e: Add qos capability check Make sure firmware supports qos before exposing the DCB API. Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 19403d6..2b42112 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3549,7 +3549,8 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) if (MLX5_CAP_GEN(mdev, vport_group_manager)) { netdev->netdev_ops = &mlx5e_netdev_ops_sriov; #ifdef CONFIG_MLX5_CORE_EN_DCB - netdev->dcbnl_ops = &mlx5e_dcbnl_ops; + if (MLX5_CAP_GEN(mdev, qos)) + netdev->dcbnl_ops = &mlx5e_dcbnl_ops; #endif } else { netdev->netdev_ops = &mlx5e_netdev_ops_basic; -- cgit v1.1 From 3a6a931dfb8e49a7377825b465d84e110fe89f68 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 27 Nov 2016 17:02:04 +0200 Subject: net/mlx5e: Support DCBX CEE API Add DCBX CEE API interface for ConnectX-4. Configurations are stored in a temporary structure and are applied to the card's firmware when the CEE's setall callback function is called. Note: priority group in CEE is equivalent to traffic class in ConnectX-4 hardware spec. bw allocation per priority in CEE is not supported because ConnectX-4 only supports bw allocation per traffic class. user priority in CEE does not have an equivalent term in ConnectX-4. Therefore, user priority to priority mapping in CEE is not supported. Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 24 ++ drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 301 ++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/port.c | 43 +++ include/linux/mlx5/port.h | 4 + 4 files changed, 370 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index a2b32ed..31387ed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -221,6 +221,26 @@ struct mlx5e_params { u32 lro_timeout; }; +#ifdef CONFIG_MLX5_CORE_EN_DCB +struct mlx5e_cee_config { + /* bw pct for priority group */ + u8 pg_bw_pct[CEE_DCBX_MAX_PGS]; + u8 prio_to_pg_map[CEE_DCBX_MAX_PRIO]; + bool pfc_setting[CEE_DCBX_MAX_PRIO]; + bool pfc_enable; +}; + +enum { + MLX5_DCB_CHG_RESET, + MLX5_DCB_NO_CHG, + MLX5_DCB_CHG_NO_RESET, +}; + +struct mlx5e_dcbx { + struct mlx5e_cee_config cee_cfg; /* pending configuration */ +}; +#endif + struct mlx5e_tstamp { rwlock_t lock; struct cyclecounter cycles; @@ -688,6 +708,10 @@ struct mlx5e_priv { struct mlx5e_stats stats; struct mlx5e_tstamp tstamp; u16 q_counter; +#ifdef CONFIG_MLX5_CORE_EN_DCB + struct mlx5e_dcbx dcbx; +#endif + const struct mlx5e_profile *profile; void *ppriv; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 762af16..0595243 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -38,6 +38,9 @@ #define MLX5E_100MB (100000) #define MLX5E_1GB (1000000) +#define MLX5E_CEE_STATE_UP 1 +#define MLX5E_CEE_STATE_DOWN 0 + static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, struct ieee_ets *ets) { @@ -222,13 +225,15 @@ static int mlx5e_dcbnl_ieee_setpfc(struct net_device *dev, static u8 mlx5e_dcbnl_getdcbx(struct net_device *dev) { - return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; + return DCB_CAP_DCBX_HOST | + DCB_CAP_DCBX_VER_IEEE | + DCB_CAP_DCBX_VER_CEE; } static u8 mlx5e_dcbnl_setdcbx(struct net_device *dev, u8 mode) { if ((mode & DCB_CAP_DCBX_LLD_MANAGED) || - (mode & DCB_CAP_DCBX_VER_CEE) || + !(mode & DCB_CAP_DCBX_VER_CEE) || !(mode & DCB_CAP_DCBX_VER_IEEE) || !(mode & DCB_CAP_DCBX_HOST)) return 1; @@ -304,6 +309,281 @@ static int mlx5e_dcbnl_ieee_setmaxrate(struct net_device *netdev, return mlx5_modify_port_ets_rate_limit(mdev, max_bw_value, max_bw_unit); } +static u8 mlx5e_dcbnl_setall(struct net_device *netdev) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; + struct mlx5_core_dev *mdev = priv->mdev; + struct ieee_ets ets; + struct ieee_pfc pfc; + int err; + int i; + + memset(&ets, 0, sizeof(ets)); + memset(&pfc, 0, sizeof(pfc)); + + ets.ets_cap = IEEE_8021QAZ_MAX_TCS; + for (i = 0; i < CEE_DCBX_MAX_PGS; i++) { + ets.tc_tx_bw[i] = cee_cfg->pg_bw_pct[i]; + ets.tc_rx_bw[i] = cee_cfg->pg_bw_pct[i]; + ets.tc_tsa[i] = IEEE_8021QAZ_TSA_ETS; + ets.prio_tc[i] = cee_cfg->prio_to_pg_map[i]; + } + + err = mlx5e_dbcnl_validate_ets(netdev, &ets); + if (err) { + netdev_err(netdev, + "%s, Failed to validate ETS: %d\n", __func__, err); + goto out; + } + + err = mlx5e_dcbnl_ieee_setets_core(priv, &ets); + if (err) { + netdev_err(netdev, + "%s, Failed to set ETS: %d\n", __func__, err); + goto out; + } + + /* Set PFC */ + pfc.pfc_cap = mlx5_max_tc(mdev) + 1; + if (!cee_cfg->pfc_enable) + pfc.pfc_en = 0; + else + for (i = 0; i < CEE_DCBX_MAX_PRIO; i++) + pfc.pfc_en |= cee_cfg->pfc_setting[i] << i; + + err = mlx5e_dcbnl_ieee_setpfc(netdev, &pfc); + if (err) { + netdev_err(netdev, + "%s, Failed to set PFC: %d\n", __func__, err); + goto out; + } +out: + return err ? MLX5_DCB_NO_CHG : MLX5_DCB_CHG_RESET; +} + +static u8 mlx5e_dcbnl_getstate(struct net_device *netdev) +{ + return MLX5E_CEE_STATE_UP; +} + +static void mlx5e_dcbnl_getpermhwaddr(struct net_device *netdev, + u8 *perm_addr) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + + if (!perm_addr) + return; + + mlx5_query_nic_vport_mac_address(priv->mdev, 0, perm_addr); +} + +static void mlx5e_dcbnl_setpgtccfgtx(struct net_device *netdev, + int priority, u8 prio_type, + u8 pgid, u8 bw_pct, u8 up_map) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; + + if (priority >= CEE_DCBX_MAX_PRIO) { + netdev_err(netdev, + "%s, priority is out of range\n", __func__); + return; + } + + if (pgid >= CEE_DCBX_MAX_PGS) { + netdev_err(netdev, + "%s, priority group is out of range\n", __func__); + return; + } + + cee_cfg->prio_to_pg_map[priority] = pgid; +} + +static void mlx5e_dcbnl_setpgbwgcfgtx(struct net_device *netdev, + int pgid, u8 bw_pct) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; + + if (pgid >= CEE_DCBX_MAX_PGS) { + netdev_err(netdev, + "%s, priority group is out of range\n", __func__); + return; + } + + cee_cfg->pg_bw_pct[pgid] = bw_pct; +} + +static void mlx5e_dcbnl_getpgtccfgtx(struct net_device *netdev, + int priority, u8 *prio_type, + u8 *pgid, u8 *bw_pct, u8 *up_map) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + + if (priority >= CEE_DCBX_MAX_PRIO) { + netdev_err(netdev, + "%s, priority is out of range\n", __func__); + return; + } + + *prio_type = 0; + *bw_pct = 0; + *up_map = 0; + + if (mlx5_query_port_prio_tc(mdev, priority, pgid)) + *pgid = 0; +} + +static void mlx5e_dcbnl_getpgbwgcfgtx(struct net_device *netdev, + int pgid, u8 *bw_pct) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + + if (pgid >= CEE_DCBX_MAX_PGS) { + netdev_err(netdev, + "%s, priority group is out of range\n", __func__); + return; + } + + if (mlx5_query_port_tc_bw_alloc(mdev, pgid, bw_pct)) + *bw_pct = 0; +} + +static void mlx5e_dcbnl_setpfccfg(struct net_device *netdev, + int priority, u8 setting) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; + + if (priority >= CEE_DCBX_MAX_PRIO) { + netdev_err(netdev, + "%s, priority is out of range\n", __func__); + return; + } + + if (setting > 1) + return; + + cee_cfg->pfc_setting[priority] = setting; +} + +static int +mlx5e_dcbnl_get_priority_pfc(struct net_device *netdev, + int priority, u8 *setting) +{ + struct ieee_pfc pfc; + int err; + + err = mlx5e_dcbnl_ieee_getpfc(netdev, &pfc); + + if (err) + *setting = 0; + else + *setting = (pfc.pfc_en >> priority) & 0x01; + + return err; +} + +static void mlx5e_dcbnl_getpfccfg(struct net_device *netdev, + int priority, u8 *setting) +{ + if (priority >= CEE_DCBX_MAX_PRIO) { + netdev_err(netdev, + "%s, priority is out of range\n", __func__); + return; + } + + if (!setting) + return; + + mlx5e_dcbnl_get_priority_pfc(netdev, priority, setting); +} + +static u8 mlx5e_dcbnl_getcap(struct net_device *netdev, + int capid, u8 *cap) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + u8 rval = 0; + + switch (capid) { + case DCB_CAP_ATTR_PG: + *cap = true; + break; + case DCB_CAP_ATTR_PFC: + *cap = true; + break; + case DCB_CAP_ATTR_UP2TC: + *cap = false; + break; + case DCB_CAP_ATTR_PG_TCS: + *cap = 1 << mlx5_max_tc(mdev); + break; + case DCB_CAP_ATTR_PFC_TCS: + *cap = 1 << mlx5_max_tc(mdev); + break; + case DCB_CAP_ATTR_GSP: + *cap = false; + break; + case DCB_CAP_ATTR_BCN: + *cap = false; + break; + case DCB_CAP_ATTR_DCBX: + *cap = (DCB_CAP_DCBX_LLD_MANAGED | + DCB_CAP_DCBX_VER_CEE | + DCB_CAP_DCBX_STATIC); + break; + default: + *cap = 0; + rval = 1; + break; + } + + return rval; +} + +static int mlx5e_dcbnl_getnumtcs(struct net_device *netdev, + int tcs_id, u8 *num) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + + switch (tcs_id) { + case DCB_NUMTCS_ATTR_PG: + case DCB_NUMTCS_ATTR_PFC: + *num = mlx5_max_tc(mdev) + 1; + break; + default: + return -EINVAL; + } + + return 0; +} + +static u8 mlx5e_dcbnl_getpfcstate(struct net_device *netdev) +{ + struct ieee_pfc pfc; + + if (mlx5e_dcbnl_ieee_getpfc(netdev, &pfc)) + return MLX5E_CEE_STATE_DOWN; + + return pfc.pfc_en ? MLX5E_CEE_STATE_UP : MLX5E_CEE_STATE_DOWN; +} + +static void mlx5e_dcbnl_setpfcstate(struct net_device *netdev, u8 state) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; + + if ((state != MLX5E_CEE_STATE_UP) && (state != MLX5E_CEE_STATE_DOWN)) + return; + + cee_cfg->pfc_enable = state; +} + const struct dcbnl_rtnl_ops mlx5e_dcbnl_ops = { .ieee_getets = mlx5e_dcbnl_ieee_getets, .ieee_setets = mlx5e_dcbnl_ieee_setets, @@ -313,4 +593,21 @@ const struct dcbnl_rtnl_ops mlx5e_dcbnl_ops = { .ieee_setpfc = mlx5e_dcbnl_ieee_setpfc, .getdcbx = mlx5e_dcbnl_getdcbx, .setdcbx = mlx5e_dcbnl_setdcbx, + +/* CEE interfaces */ + .setall = mlx5e_dcbnl_setall, + .getstate = mlx5e_dcbnl_getstate, + .getpermhwaddr = mlx5e_dcbnl_getpermhwaddr, + + .setpgtccfgtx = mlx5e_dcbnl_setpgtccfgtx, + .setpgbwgcfgtx = mlx5e_dcbnl_setpgbwgcfgtx, + .getpgtccfgtx = mlx5e_dcbnl_getpgtccfgtx, + .getpgbwgcfgtx = mlx5e_dcbnl_getpgbwgcfgtx, + + .setpfccfg = mlx5e_dcbnl_setpfccfg, + .getpfccfg = mlx5e_dcbnl_getpfccfg, + .getcap = mlx5e_dcbnl_getcap, + .getnumtcs = mlx5e_dcbnl_getnumtcs, + .getpfcstate = mlx5e_dcbnl_getpfcstate, + .setpfcstate = mlx5e_dcbnl_setpfcstate, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index b77928f..ed4898f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -572,6 +572,28 @@ int mlx5_set_port_prio_tc(struct mlx5_core_dev *mdev, u8 *prio_tc) } EXPORT_SYMBOL_GPL(mlx5_set_port_prio_tc); +int mlx5_query_port_prio_tc(struct mlx5_core_dev *mdev, + u8 prio, u8 *tc) +{ + u32 in[MLX5_ST_SZ_DW(qtct_reg)]; + u32 out[MLX5_ST_SZ_DW(qtct_reg)]; + int err; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(qtct_reg, in, port_number, 1); + MLX5_SET(qtct_reg, in, prio, prio); + + err = mlx5_core_access_reg(mdev, in, sizeof(in), out, + sizeof(out), MLX5_REG_QTCT, 0, 0); + if (!err) + *tc = MLX5_GET(qtct_reg, out, tclass); + + return err; +} +EXPORT_SYMBOL_GPL(mlx5_query_port_prio_tc); + static int mlx5_set_port_qetcr_reg(struct mlx5_core_dev *mdev, u32 *in, int inlen) { @@ -625,6 +647,27 @@ int mlx5_set_port_tc_bw_alloc(struct mlx5_core_dev *mdev, u8 *tc_bw) } EXPORT_SYMBOL_GPL(mlx5_set_port_tc_bw_alloc); +int mlx5_query_port_tc_bw_alloc(struct mlx5_core_dev *mdev, + u8 tc, u8 *bw_pct) +{ + u32 out[MLX5_ST_SZ_DW(qetc_reg)]; + void *ets_tcn_conf; + int err; + + err = mlx5_query_port_qetcr_reg(mdev, out, sizeof(out)); + if (err) + return err; + + ets_tcn_conf = MLX5_ADDR_OF(qetc_reg, out, + tc_configuration[tc]); + + *bw_pct = MLX5_GET(ets_tcn_config_reg, ets_tcn_conf, + bw_allocation); + + return 0; +} +EXPORT_SYMBOL_GPL(mlx5_query_port_tc_bw_alloc); + int mlx5_modify_port_ets_rate_limit(struct mlx5_core_dev *mdev, u8 *max_bw_value, u8 *max_bw_units) diff --git a/include/linux/mlx5/port.h b/include/linux/mlx5/port.h index dde8c7e..bdee439 100644 --- a/include/linux/mlx5/port.h +++ b/include/linux/mlx5/port.h @@ -141,8 +141,12 @@ int mlx5_query_port_pfc(struct mlx5_core_dev *dev, u8 *pfc_en_tx, int mlx5_max_tc(struct mlx5_core_dev *mdev); int mlx5_set_port_prio_tc(struct mlx5_core_dev *mdev, u8 *prio_tc); +int mlx5_query_port_prio_tc(struct mlx5_core_dev *mdev, + u8 prio, u8 *tc); int mlx5_set_port_tc_group(struct mlx5_core_dev *mdev, u8 *tc_group); int mlx5_set_port_tc_bw_alloc(struct mlx5_core_dev *mdev, u8 *tc_bw); +int mlx5_query_port_tc_bw_alloc(struct mlx5_core_dev *mdev, + u8 tc, u8 *bw_pct); int mlx5_modify_port_ets_rate_limit(struct mlx5_core_dev *mdev, u8 *max_bw_value, u8 *max_bw_unit); -- cgit v1.1 From 820c2c5e773d283ab102f1b64618e2ddfbb975ef Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 27 Nov 2016 17:02:05 +0200 Subject: net/mlx5e: Read ETS settings directly from firmware Issue description: Current implementation saves the ETS settings from user in a temporal soft copy and returns this settings when user queries the ETS settings. With the new DCBX firmware, the ETS settings can be changed by firmware when the DCBX is in firmware controlled mode. Therefore, user will obtain wrong values from the temporal soft copy. Solution: 1. Read the ETS settings directly from firmware. 2. For tc_tsa: a. Initialize tc_tsa to vendor IEEE_8021QAZ_TSA_VENDOR at netdev creation. b. When reading ETS setting from FW, if the traffic class bandwidth is less than 100, set tc_tsa to IEEE_8021QAZ_TSA_ETS. This implementation solves the scenarios when the DCBX is in FW control and willing bit is on which means the ETS setting is dictated by remote switch. Also check ETS capability where needed. Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 6 +-- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 46 +++++++++++++++++----- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 29 ++++++++------ 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 31387ed..60aa13b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -214,9 +214,6 @@ struct mlx5e_params { u8 toeplitz_hash_key[40]; u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE]; bool vlan_strip_disable; -#ifdef CONFIG_MLX5_CORE_EN_DCB - struct ieee_ets ets; -#endif bool rx_am_enabled; u32 lro_timeout; }; @@ -238,6 +235,9 @@ enum { struct mlx5e_dcbx { struct mlx5e_cee_config cee_cfg; /* pending configuration */ + + /* The only setting that cannot be read from FW */ + u8 tc_tsa[IEEE_8021QAZ_MAX_TCS]; }; #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 0595243..35e90d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -45,12 +45,31 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, struct ieee_ets *ets) { struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + int err = 0; + int i; if (!MLX5_CAP_GEN(priv->mdev, ets)) return -ENOTSUPP; - memcpy(ets, &priv->params.ets, sizeof(*ets)); - return 0; + ets->ets_cap = mlx5_max_tc(priv->mdev) + 1; + for (i = 0; i < ets->ets_cap; i++) { + err = mlx5_query_port_prio_tc(mdev, i, &ets->prio_tc[i]); + if (err) + return err; + } + + for (i = 0; i < ets->ets_cap; i++) { + err = mlx5_query_port_tc_bw_alloc(mdev, i, &ets->tc_tx_bw[i]); + if (err) + return err; + if (ets->tc_tx_bw[i] < MLX5E_MAX_BW_ALLOC) + priv->dcbx.tc_tsa[i] = IEEE_8021QAZ_TSA_ETS; + } + + memcpy(ets->tc_tsa, priv->dcbx.tc_tsa, sizeof(ets->tc_tsa)); + + return err; } enum { @@ -113,9 +132,6 @@ int mlx5e_dcbnl_ieee_setets_core(struct mlx5e_priv *priv, struct ieee_ets *ets) int max_tc = mlx5_max_tc(mdev); int err; - if (!MLX5_CAP_GEN(mdev, ets)) - return -ENOTSUPP; - mlx5e_build_tc_group(ets, tc_group, max_tc); mlx5e_build_tc_tx_bw(ets, tc_tx_bw, tc_group, max_tc); @@ -127,7 +143,14 @@ int mlx5e_dcbnl_ieee_setets_core(struct mlx5e_priv *priv, struct ieee_ets *ets) if (err) return err; - return mlx5_set_port_tc_bw_alloc(mdev, tc_tx_bw); + err = mlx5_set_port_tc_bw_alloc(mdev, tc_tx_bw); + + if (err) + return err; + + memcpy(priv->dcbx.tc_tsa, ets->tc_tsa, sizeof(ets->tc_tsa)); + + return err; } static int mlx5e_dbcnl_validate_ets(struct net_device *netdev, @@ -173,6 +196,9 @@ static int mlx5e_dcbnl_ieee_setets(struct net_device *netdev, struct mlx5e_priv *priv = netdev_priv(netdev); int err; + if (!MLX5_CAP_GEN(priv->mdev, ets)) + return -ENOTSUPP; + err = mlx5e_dbcnl_validate_ets(netdev, ets); if (err) return err; @@ -181,9 +207,6 @@ static int mlx5e_dcbnl_ieee_setets(struct net_device *netdev, if (err) return err; - memcpy(&priv->params.ets, ets, sizeof(*ets)); - priv->params.ets.ets_cap = mlx5_max_tc(priv->mdev) + 1; - return 0; } @@ -316,9 +339,12 @@ static u8 mlx5e_dcbnl_setall(struct net_device *netdev) struct mlx5_core_dev *mdev = priv->mdev; struct ieee_ets ets; struct ieee_pfc pfc; - int err; + int err = -ENOTSUPP; int i; + if (!MLX5_CAP_GEN(mdev, ets)) + goto out; + memset(&ets, 0, sizeof(ets)); memset(&pfc, 0, sizeof(pfc)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 2b42112..b1e8ec5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3328,18 +3328,27 @@ u16 mlx5e_get_max_inline_cap(struct mlx5_core_dev *mdev) #ifdef CONFIG_MLX5_CORE_EN_DCB static void mlx5e_ets_init(struct mlx5e_priv *priv) { + struct ieee_ets ets; int i; - priv->params.ets.ets_cap = mlx5_max_tc(priv->mdev) + 1; - for (i = 0; i < priv->params.ets.ets_cap; i++) { - priv->params.ets.tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC; - priv->params.ets.tc_tsa[i] = IEEE_8021QAZ_TSA_VENDOR; - priv->params.ets.prio_tc[i] = i; + if (!MLX5_CAP_GEN(priv->mdev, ets)) + return; + + memset(&ets, 0, sizeof(ets)); + ets.ets_cap = mlx5_max_tc(priv->mdev) + 1; + for (i = 0; i < ets.ets_cap; i++) { + ets.tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC; + ets.tc_tsa[i] = IEEE_8021QAZ_TSA_VENDOR; + ets.prio_tc[i] = i; } + memcpy(priv->dcbx.tc_tsa, ets.tc_tsa, sizeof(ets.tc_tsa)); + /* tclass[prio=0]=1, tclass[prio=1]=0, tclass[prio=i]=i (for i>1) */ - priv->params.ets.prio_tc[0] = 1; - priv->params.ets.prio_tc[1] = 0; + ets.prio_tc[0] = 1; + ets.prio_tc[1] = 0; + + mlx5e_dcbnl_ieee_setets_core(priv, &ets); } #endif @@ -3509,10 +3518,6 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, MLX5E_SET_PRIV_FLAG(priv, MLX5E_PFLAG_RX_CQE_BASED_MODER, priv->params.rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); -#ifdef CONFIG_MLX5_CORE_EN_DCB - mlx5e_ets_init(priv); -#endif - mutex_init(&priv->state_lock); INIT_WORK(&priv->update_carrier_work, mlx5e_update_carrier_work); @@ -3789,7 +3794,7 @@ static int mlx5e_init_nic_tx(struct mlx5e_priv *priv) } #ifdef CONFIG_MLX5_CORE_EN_DCB - mlx5e_dcbnl_ieee_setets_core(priv, &priv->params.ets); + mlx5e_ets_init(priv); #endif return 0; } -- cgit v1.1 From 341c5ee2fb78420ffc441df36f93226be8069b0a Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 27 Nov 2016 17:02:06 +0200 Subject: net/mlx5: Add DCBX firmware commands support Add set/query commands for DCBX_PARAM register Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/port.c | 20 ++++++++++++++++++++ include/linux/mlx5/driver.h | 7 +++++++ include/linux/mlx5/port.h | 2 ++ 3 files changed, 29 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index ed4898f..d2ec9d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -548,6 +548,26 @@ int mlx5_max_tc(struct mlx5_core_dev *mdev) return num_tc - 1; } +int mlx5_query_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *out) +{ + u32 in[MLX5_ST_SZ_DW(dcbx_param)] = {0}; + + MLX5_SET(dcbx_param, in, port_number, 1); + + return mlx5_core_access_reg(mdev, in, sizeof(in), out, + sizeof(in), MLX5_REG_DCBX_PARAM, 0, 0); +} + +int mlx5_set_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *in) +{ + u32 out[MLX5_ST_SZ_DW(dcbx_param)]; + + MLX5_SET(dcbx_param, in, port_number, 1); + + return mlx5_core_access_reg(mdev, in, sizeof(out), out, + sizeof(out), MLX5_REG_DCBX_PARAM, 0, 1); +} + int mlx5_set_port_prio_tc(struct mlx5_core_dev *mdev, u8 *prio_tc) { u32 in[MLX5_ST_SZ_DW(qtct_reg)] = {0}; diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index ae1f451..68b85ef 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -104,6 +104,8 @@ enum { enum { MLX5_REG_QETCR = 0x4005, MLX5_REG_QTCT = 0x400a, + MLX5_REG_DCBX_PARAM = 0x4020, + MLX5_REG_DCBX_APP = 0x4021, MLX5_REG_PCAP = 0x5001, MLX5_REG_PMTU = 0x5003, MLX5_REG_PTYS = 0x5004, @@ -124,6 +126,11 @@ enum { MLX5_REG_MPCNT = 0x9051, }; +enum mlx5_dcbx_oper_mode { + MLX5E_DCBX_PARAM_VER_OPER_HOST = 0x0, + MLX5E_DCBX_PARAM_VER_OPER_AUTO = 0x3, +}; + enum { MLX5_ATOMIC_OPS_CMP_SWAP = 1 << 0, MLX5_ATOMIC_OPS_FETCH_ADD = 1 << 1, diff --git a/include/linux/mlx5/port.h b/include/linux/mlx5/port.h index bdee439..e527732 100644 --- a/include/linux/mlx5/port.h +++ b/include/linux/mlx5/port.h @@ -162,4 +162,6 @@ void mlx5_query_port_fcs(struct mlx5_core_dev *mdev, bool *supported, int mlx5_query_module_eeprom(struct mlx5_core_dev *dev, u16 offset, u16 size, u8 *data); +int mlx5_query_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *out); +int mlx5_set_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *in); #endif /* __MLX5_PORT_H__ */ -- cgit v1.1 From e207b7e991768b724f6d216de49c9b800e203eed Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 27 Nov 2016 17:02:07 +0200 Subject: net/mlx5e: ConnectX-4 firmware support for DCBX DBCX by default is controlled by firmware where dcbx capability bit is set. In this mode, firmware is responsible for reading/sending the TLV packets from/to the remote partner. This patch sets up the infrastructure to move between HOST/FW DCBX control mode. Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 + drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 91 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 29 +------ 3 files changed, 94 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 60aa13b..6c954cd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -234,6 +234,7 @@ enum { }; struct mlx5e_dcbx { + enum mlx5_dcbx_oper_mode mode; struct mlx5e_cee_config cee_cfg; /* pending configuration */ /* The only setting that cannot be read from FW */ @@ -843,6 +844,7 @@ extern const struct ethtool_ops mlx5e_ethtool_ops; #ifdef CONFIG_MLX5_CORE_EN_DCB extern const struct dcbnl_rtnl_ops mlx5e_dcbnl_ops; int mlx5e_dcbnl_ieee_setets_core(struct mlx5e_priv *priv, struct ieee_ets *ets); +void mlx5e_dcbnl_initialize(struct mlx5e_priv *priv); #endif #ifndef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 35e90d1..1753182 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -41,6 +41,45 @@ #define MLX5E_CEE_STATE_UP 1 #define MLX5E_CEE_STATE_DOWN 0 +/* If dcbx mode is non-host set the dcbx mode to host. + */ +static int mlx5e_dcbnl_set_dcbx_mode(struct mlx5e_priv *priv, + enum mlx5_dcbx_oper_mode mode) +{ + struct mlx5_core_dev *mdev = priv->mdev; + u32 param[MLX5_ST_SZ_DW(dcbx_param)]; + int err; + + err = mlx5_query_port_dcbx_param(mdev, param); + if (err) + return err; + + MLX5_SET(dcbx_param, param, version_admin, mode); + if (mode != MLX5E_DCBX_PARAM_VER_OPER_HOST) + MLX5_SET(dcbx_param, param, willing_admin, 1); + + return mlx5_set_port_dcbx_param(mdev, param); +} + +static int mlx5e_dcbnl_switch_to_host_mode(struct mlx5e_priv *priv) +{ + struct mlx5e_dcbx *dcbx = &priv->dcbx; + int err; + + if (!MLX5_CAP_GEN(priv->mdev, dcbx)) + return 0; + + if (dcbx->mode == MLX5E_DCBX_PARAM_VER_OPER_HOST) + return 0; + + err = mlx5e_dcbnl_set_dcbx_mode(priv, MLX5E_DCBX_PARAM_VER_OPER_HOST); + if (err) + return err; + + dcbx->mode = MLX5E_DCBX_PARAM_VER_OPER_HOST; + return 0; +} + static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, struct ieee_ets *ets) { @@ -255,6 +294,9 @@ static u8 mlx5e_dcbnl_getdcbx(struct net_device *dev) static u8 mlx5e_dcbnl_setdcbx(struct net_device *dev, u8 mode) { + if (mlx5e_dcbnl_switch_to_host_mode(netdev_priv(dev))) + return 1; + if ((mode & DCB_CAP_DCBX_LLD_MANAGED) || !(mode & DCB_CAP_DCBX_VER_CEE) || !(mode & DCB_CAP_DCBX_VER_IEEE) || @@ -637,3 +679,52 @@ const struct dcbnl_rtnl_ops mlx5e_dcbnl_ops = { .getpfcstate = mlx5e_dcbnl_getpfcstate, .setpfcstate = mlx5e_dcbnl_setpfcstate, }; + +static void mlx5e_dcbnl_query_dcbx_mode(struct mlx5e_priv *priv, + enum mlx5_dcbx_oper_mode *mode) +{ + u32 out[MLX5_ST_SZ_DW(dcbx_param)]; + + *mode = MLX5E_DCBX_PARAM_VER_OPER_HOST; + + if (!mlx5_query_port_dcbx_param(priv->mdev, out)) + *mode = MLX5_GET(dcbx_param, out, version_oper); + + /* From driver's point of view, we only care if the mode + * is host (HOST) or non-host (AUTO) + */ + if (*mode != MLX5E_DCBX_PARAM_VER_OPER_HOST) + *mode = MLX5E_DCBX_PARAM_VER_OPER_AUTO; +} + +static void mlx5e_ets_init(struct mlx5e_priv *priv) +{ + int i; + struct ieee_ets ets; + + memset(&ets, 0, sizeof(ets)); + ets.ets_cap = mlx5_max_tc(priv->mdev) + 1; + for (i = 0; i < ets.ets_cap; i++) { + ets.tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC; + ets.tc_tsa[i] = IEEE_8021QAZ_TSA_VENDOR; + ets.prio_tc[i] = i; + } + + memcpy(priv->dcbx.tc_tsa, ets.tc_tsa, sizeof(ets.tc_tsa)); + + /* tclass[prio=0]=1, tclass[prio=1]=0, tclass[prio=i]=i (for i>1) */ + ets.prio_tc[0] = 1; + ets.prio_tc[1] = 0; + + mlx5e_dcbnl_ieee_setets_core(priv, &ets); +} + +void mlx5e_dcbnl_initialize(struct mlx5e_priv *priv) +{ + struct mlx5e_dcbx *dcbx = &priv->dcbx; + + if (MLX5_CAP_GEN(priv->mdev, dcbx)) + mlx5e_dcbnl_query_dcbx_mode(priv, &dcbx->mode); + + mlx5e_ets_init(priv); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index b1e8ec5..f5b93c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3325,33 +3325,6 @@ u16 mlx5e_get_max_inline_cap(struct mlx5_core_dev *mdev) 2 /*sizeof(mlx5e_tx_wqe.inline_hdr_start)*/; } -#ifdef CONFIG_MLX5_CORE_EN_DCB -static void mlx5e_ets_init(struct mlx5e_priv *priv) -{ - struct ieee_ets ets; - int i; - - if (!MLX5_CAP_GEN(priv->mdev, ets)) - return; - - memset(&ets, 0, sizeof(ets)); - ets.ets_cap = mlx5_max_tc(priv->mdev) + 1; - for (i = 0; i < ets.ets_cap; i++) { - ets.tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC; - ets.tc_tsa[i] = IEEE_8021QAZ_TSA_VENDOR; - ets.prio_tc[i] = i; - } - - memcpy(priv->dcbx.tc_tsa, ets.tc_tsa, sizeof(ets.tc_tsa)); - - /* tclass[prio=0]=1, tclass[prio=1]=0, tclass[prio=i]=i (for i>1) */ - ets.prio_tc[0] = 1; - ets.prio_tc[1] = 0; - - mlx5e_dcbnl_ieee_setets_core(priv, &ets); -} -#endif - void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, u32 *indirection_rqt, int len, int num_channels) @@ -3794,7 +3767,7 @@ static int mlx5e_init_nic_tx(struct mlx5e_priv *priv) } #ifdef CONFIG_MLX5_CORE_EN_DCB - mlx5e_ets_init(priv); + mlx5e_dcbnl_initialize(priv); #endif return 0; } -- cgit v1.1 From 0eca995f3ede3a4fc77e0ea29a7a6c0a4c4fde0f Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 27 Nov 2016 17:02:08 +0200 Subject: net/mlx5e: Add DCBX control interface Use setdcbx interface to set the DCBX mode to firmware or os. If setdcbx is called with mode value of zero, the DCBX mode is set to firmware. Signed-off-by: Huy Nguyen Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 27 +++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 1753182..7f6c225 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -287,13 +287,34 @@ static int mlx5e_dcbnl_ieee_setpfc(struct net_device *dev, static u8 mlx5e_dcbnl_getdcbx(struct net_device *dev) { - return DCB_CAP_DCBX_HOST | - DCB_CAP_DCBX_VER_IEEE | - DCB_CAP_DCBX_VER_CEE; + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5e_dcbx *dcbx = &priv->dcbx; + u8 mode = DCB_CAP_DCBX_VER_IEEE | DCB_CAP_DCBX_VER_CEE; + + if (dcbx->mode == MLX5E_DCBX_PARAM_VER_OPER_HOST) + mode |= DCB_CAP_DCBX_HOST; + + return mode; } static u8 mlx5e_dcbnl_setdcbx(struct net_device *dev, u8 mode) { + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5e_dcbx *dcbx = &priv->dcbx; + + if ((!mode) && MLX5_CAP_GEN(priv->mdev, dcbx)) { + if (dcbx->mode == MLX5E_DCBX_PARAM_VER_OPER_AUTO) + return 0; + + /* set dcbx to fw controlled */ + if (!mlx5e_dcbnl_set_dcbx_mode(priv, MLX5E_DCBX_PARAM_VER_OPER_AUTO)) { + dcbx->mode = MLX5E_DCBX_PARAM_VER_OPER_AUTO; + return 0; + } + + return 1; + } + if (mlx5e_dcbnl_switch_to_host_mode(netdev_priv(dev))) return 1; -- cgit v1.1 From d605d6686dc7e7c767cd2dbf89a4ebd195891212 Mon Sep 17 00:00:00 2001 From: Kamal Heib Date: Sun, 27 Nov 2016 17:02:09 +0200 Subject: net/mlx5e: Add support for ethtool self diagnostics test The self diagnostics test implementaion include the following features: 1. Link Test: Check that link is in up state. 2. Speed Test: Check that link was negotiated correctly. 3. Health Test: Check the device health. Signed-off-by: Kamal Heib Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en.h | 5 + .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 8 +- .../net/ethernet/mellanox/mlx5/core/en_selftest.c | 126 +++++++++++++++++++++ 4 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 0343725..9f43beb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -8,6 +8,6 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o eswitch_offloads.o \ en_main.o en_common.o en_fs.o en_ethtool.o en_tx.o \ en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ - en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o + en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o en_selftest.o mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 6c954cd..f7bb4a7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -167,6 +167,8 @@ struct mlx5e_umr_wqe { struct mlx5_wqe_data_seg data; }; +extern const char mlx5e_self_tests[][ETH_GSTRING_LEN]; + static const char mlx5e_priv_flags[][ETH_GSTRING_LEN] = { "rx_cqe_moder", }; @@ -754,6 +756,9 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv); void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv); void mlx5e_init_l2_addr(struct mlx5e_priv *priv); void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft); +int mlx5e_self_test_num(struct mlx5e_priv *priv); +void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest, + u64 *buf); int mlx5e_ethtool_get_flow(struct mlx5e_priv *priv, struct ethtool_rxnfc *info, int location); int mlx5e_ethtool_get_all_flows(struct mlx5e_priv *priv, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 9ea7b37..839c4e9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -180,6 +180,8 @@ static int mlx5e_get_sset_count(struct net_device *dev, int sset) case ETH_SS_PRIV_FLAGS: return ARRAY_SIZE(mlx5e_priv_flags); + case ETH_SS_TEST: + return mlx5e_self_test_num(priv); /* fallthrough */ default: return -EOPNOTSUPP; @@ -286,6 +288,9 @@ static void mlx5e_get_strings(struct net_device *dev, break; case ETH_SS_TEST: + for (i = 0; i < mlx5e_self_test_num(priv); i++) + strcpy(data + i * ETH_GSTRING_LEN, + mlx5e_self_tests[i]); break; case ETH_SS_STATS: @@ -1573,5 +1578,6 @@ const struct ethtool_ops mlx5e_ethtool_ops = { .get_module_info = mlx5e_get_module_info, .get_module_eeprom = mlx5e_get_module_eeprom, .get_priv_flags = mlx5e_get_priv_flags, - .set_priv_flags = mlx5e_set_priv_flags + .set_priv_flags = mlx5e_set_priv_flags, + .self_test = mlx5e_self_test, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c new file mode 100644 index 0000000..49504a7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016, Mellanox Technologies, Ltd. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "en.h" + +enum { + MLX5E_ST_LINK_STATE, + MLX5E_ST_LINK_SPEED, + MLX5E_ST_HEALTH_INFO, + MLX5E_ST_NUM, +}; + +const char mlx5e_self_tests[MLX5E_ST_NUM][ETH_GSTRING_LEN] = { + "Link Test", + "Speed Test", + "Health Test", +}; + +int mlx5e_self_test_num(struct mlx5e_priv *priv) +{ + return ARRAY_SIZE(mlx5e_self_tests); +} + +static int mlx5e_test_health_info(struct mlx5e_priv *priv) +{ + struct mlx5_core_health *health = &priv->mdev->priv.health; + + return health->sick ? 1 : 0; +} + +static int mlx5e_test_link_state(struct mlx5e_priv *priv) +{ + u8 port_state; + + if (!netif_carrier_ok(priv->netdev)) + return 1; + + port_state = mlx5_query_vport_state(priv->mdev, MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT, 0); + return port_state == VPORT_STATE_UP ? 0 : 1; +} + +static int mlx5e_test_link_speed(struct mlx5e_priv *priv) +{ + u32 out[MLX5_ST_SZ_DW(ptys_reg)]; + u32 eth_proto_oper; + int i; + + if (!netif_carrier_ok(priv->netdev)) + return 1; + + if (mlx5_query_port_ptys(priv->mdev, out, sizeof(out), MLX5_PTYS_EN, 1)) + return 1; + + eth_proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper); + for (i = 0; i < MLX5E_LINK_MODES_NUMBER; i++) { + if (eth_proto_oper & MLX5E_PROT_MASK(i)) + return 0; + } + return 1; +} + +static int (*mlx5e_st_func[MLX5E_ST_NUM])(struct mlx5e_priv *) = { + mlx5e_test_link_state, + mlx5e_test_link_speed, + mlx5e_test_health_info, +}; + +void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest, + u64 *buf) +{ + struct mlx5e_priv *priv = netdev_priv(ndev); + int i; + + memset(buf, 0, sizeof(u64) * MLX5E_ST_NUM); + + mutex_lock(&priv->state_lock); + netdev_info(ndev, "Self test begin..\n"); + + for (i = 0; i < MLX5E_ST_NUM; i++) { + netdev_info(ndev, "\t[%d] %s start..\n", + i, mlx5e_self_tests[i]); + buf[i] = mlx5e_st_func[i](priv); + netdev_info(ndev, "\t[%d] %s end: result(%lld)\n", + i, mlx5e_self_tests[i], buf[i]); + } + + mutex_unlock(&priv->state_lock); + + for (i = 0; i < MLX5E_ST_NUM; i++) { + if (buf[i]) { + etest->flags |= ETH_TEST_FL_FAILED; + break; + } + } + netdev_info(ndev, "Self test out: status flags(0x%x)\n", + etest->flags); +} -- cgit v1.1 From 0952da791c97477da52fbdb2242477d781b9f37e Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sun, 27 Nov 2016 17:02:10 +0200 Subject: net/mlx5e: Add support for loopback selftest Extend the self diagnostic tests to support loopback test. The loopback test doesn't require the offline flag, it will use the generic dev_queue_xmit and a dedicated packet_type to capture and verify mlx5e selftest loopback packets. Signed-off-by: Saeed Mahameed Signed-off-by: Kamal Heib Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 3 +- .../net/ethernet/mellanox/mlx5/core/en_common.c | 7 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- .../net/ethernet/mellanox/mlx5/core/en_selftest.c | 218 +++++++++++++++++++++ 4 files changed, 227 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index f7bb4a7..9cf32d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -885,7 +885,8 @@ void mlx5e_destroy_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir); int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev); void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev); -int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5_core_dev *mdev); +int mlx5e_refresh_tirs_self_loopback(struct mlx5_core_dev *mdev, + bool enable_uc_lb); struct mlx5_eswitch_rep; int mlx5e_vport_rep_load(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c index 029e856..f175518 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c @@ -137,7 +137,8 @@ void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev) mlx5_unmap_free_uar(mdev, &res->cq_uar); } -int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5_core_dev *mdev) +int mlx5e_refresh_tirs_self_loopback(struct mlx5_core_dev *mdev, + bool enable_uc_lb) { struct mlx5e_tir *tir; void *in; @@ -149,6 +150,10 @@ int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5_core_dev *mdev) if (!in) return -ENOMEM; + if (enable_uc_lb) + MLX5_SET(modify_tir_in, in, ctx.self_lb_block, + MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST_); + MLX5_SET(modify_tir_in, in, bitmask.self_lb_en, 1); list_for_each_entry(tir, &mdev->mlx5e_res.td.tirs_list, list) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index f5b93c2..89d5c65 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2136,7 +2136,7 @@ int mlx5e_open_locked(struct net_device *netdev) goto err_clear_state_opened_flag; } - err = mlx5e_refresh_tirs_self_loopback_enable(priv->mdev); + err = mlx5e_refresh_tirs_self_loopback(priv->mdev, false); if (err) { netdev_err(netdev, "%s: mlx5e_refresh_tirs_self_loopback_enable failed, %d\n", __func__, err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c index 49504a7..c32af7d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c @@ -30,12 +30,16 @@ * SOFTWARE. */ +#include +#include +#include #include "en.h" enum { MLX5E_ST_LINK_STATE, MLX5E_ST_LINK_SPEED, MLX5E_ST_HEALTH_INFO, + MLX5E_ST_LOOPBACK, MLX5E_ST_NUM, }; @@ -43,6 +47,7 @@ const char mlx5e_self_tests[MLX5E_ST_NUM][ETH_GSTRING_LEN] = { "Link Test", "Speed Test", "Health Test", + "Loopback Test", }; int mlx5e_self_test_num(struct mlx5e_priv *priv) @@ -88,10 +93,223 @@ static int mlx5e_test_link_speed(struct mlx5e_priv *priv) return 1; } +/* loopback test */ +#define MLX5E_TEST_PKT_SIZE (MLX5_MPWRQ_SMALL_PACKET_THRESHOLD - NET_IP_ALIGN) +static const char mlx5e_test_text[ETH_GSTRING_LEN] = "MLX5E SELF TEST"; +#define MLX5E_TEST_MAGIC 0x5AEED15C001ULL + +struct mlx5ehdr { + __be32 version; + __be64 magic; + char text[ETH_GSTRING_LEN]; +}; + +static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) +{ + struct sk_buff *skb = NULL; + struct mlx5ehdr *mlxh; + struct ethhdr *ethh; + struct udphdr *udph; + struct iphdr *iph; + int datalen, iplen; + + datalen = MLX5E_TEST_PKT_SIZE - + (sizeof(*ethh) + sizeof(*iph) + sizeof(*udph)); + + skb = netdev_alloc_skb(priv->netdev, MLX5E_TEST_PKT_SIZE); + if (!skb) { + netdev_err(priv->netdev, "\tFailed to alloc loopback skb\n"); + return NULL; + } + + prefetchw(skb->data); + skb_reserve(skb, NET_IP_ALIGN); + + /* Reserve for ethernet and IP header */ + ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + + skb_set_network_header(skb, skb->len); + iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)); + + skb_set_transport_header(skb, skb->len); + udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr)); + + /* Fill ETH header */ + ether_addr_copy(ethh->h_dest, priv->netdev->dev_addr); + eth_zero_addr(ethh->h_source); + ethh->h_proto = htons(ETH_P_IP); + + /* Fill UDP header */ + udph->source = htons(9); + udph->dest = htons(9); /* Discard Protocol */ + udph->len = htons(datalen + sizeof(struct udphdr)); + udph->check = 0; + + /* Fill IP header */ + iph->ihl = 5; + iph->ttl = 32; + iph->version = 4; + iph->protocol = IPPROTO_UDP; + iplen = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen; + iph->tot_len = htons(iplen); + iph->frag_off = 0; + iph->saddr = 0; + iph->daddr = 0; + iph->tos = 0; + iph->id = 0; + ip_send_check(iph); + + /* Fill test header and data */ + mlxh = (struct mlx5ehdr *)skb_put(skb, sizeof(*mlxh)); + mlxh->version = 0; + mlxh->magic = cpu_to_be64(MLX5E_TEST_MAGIC); + strlcpy(mlxh->text, mlx5e_test_text, sizeof(mlxh->text)); + datalen -= sizeof(*mlxh); + memset(skb_put(skb, datalen), 0, datalen); + + skb->csum = 0; + skb->ip_summed = CHECKSUM_PARTIAL; + udp4_hwcsum(skb, iph->saddr, iph->daddr); + + skb->protocol = htons(ETH_P_IP); + skb->pkt_type = PACKET_HOST; + skb->dev = priv->netdev; + + return skb; +} + +struct mlx5e_lbt_priv { + struct packet_type pt; + struct completion comp; + bool loopback_ok; +}; + +static int +mlx5e_test_loopback_validate(struct sk_buff *skb, + struct net_device *ndev, + struct packet_type *pt, + struct net_device *orig_ndev) +{ + struct mlx5e_lbt_priv *lbtp = pt->af_packet_priv; + struct mlx5ehdr *mlxh; + struct ethhdr *ethh; + struct udphdr *udph; + struct iphdr *iph; + + /* We are only going to peek, no need to clone the SKB */ + if (skb->protocol != htons(ETH_P_IP)) + goto out; + + if (MLX5E_TEST_PKT_SIZE - ETH_HLEN > skb_headlen(skb)) + goto out; + + ethh = (struct ethhdr *)skb_mac_header(skb); + if (!ether_addr_equal(ethh->h_dest, orig_ndev->dev_addr)) + goto out; + + iph = ip_hdr(skb); + if (iph->protocol != IPPROTO_UDP) + goto out; + + udph = udp_hdr(skb); + if (udph->dest != htons(9)) + goto out; + + mlxh = (struct mlx5ehdr *)((char *)udph + sizeof(*udph)); + if (mlxh->magic != cpu_to_be64(MLX5E_TEST_MAGIC)) + goto out; /* so close ! */ + + /* bingo */ + lbtp->loopback_ok = true; + complete(&lbtp->comp); +out: + kfree_skb(skb); + return 0; +} + +static int mlx5e_test_loopback_setup(struct mlx5e_priv *priv, + struct mlx5e_lbt_priv *lbtp) +{ + int err = 0; + + err = mlx5e_refresh_tirs_self_loopback(priv->mdev, true); + if (err) { + netdev_err(priv->netdev, + "\tFailed to enable UC loopback err(%d)\n", err); + return err; + } + + lbtp->loopback_ok = false; + init_completion(&lbtp->comp); + + lbtp->pt.type = htons(ETH_P_ALL); + lbtp->pt.func = mlx5e_test_loopback_validate; + lbtp->pt.dev = priv->netdev; + lbtp->pt.af_packet_priv = lbtp; + dev_add_pack(&lbtp->pt); + return err; +} + +static void mlx5e_test_loopback_cleanup(struct mlx5e_priv *priv, + struct mlx5e_lbt_priv *lbtp) +{ + dev_remove_pack(&lbtp->pt); + mlx5e_refresh_tirs_self_loopback(priv->mdev, false); +} + +#define MLX5E_LB_VERIFY_TIMEOUT (msecs_to_jiffies(200)) +static int mlx5e_test_loopback(struct mlx5e_priv *priv) +{ + struct mlx5e_lbt_priv *lbtp; + struct sk_buff *skb = NULL; + int err; + + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { + netdev_err(priv->netdev, + "\tCan't perform loobpack test while device is down\n"); + return -ENODEV; + } + + lbtp = kzalloc(sizeof(*lbtp), GFP_KERNEL); + if (!lbtp) + return -ENOMEM; + lbtp->loopback_ok = false; + + err = mlx5e_test_loopback_setup(priv, lbtp); + if (err) + goto out; + + skb = mlx5e_test_get_udp_skb(priv); + if (!skb) { + err = -ENOMEM; + goto cleanup; + } + + skb_set_queue_mapping(skb, 0); + err = dev_queue_xmit(skb); + if (err) { + netdev_err(priv->netdev, + "\tFailed to xmit loopback packet err(%d)\n", + err); + goto cleanup; + } + + wait_for_completion_timeout(&lbtp->comp, MLX5E_LB_VERIFY_TIMEOUT); + err = !lbtp->loopback_ok; + +cleanup: + mlx5e_test_loopback_cleanup(priv, lbtp); +out: + kfree(lbtp); + return err; +} + static int (*mlx5e_st_func[MLX5E_ST_NUM])(struct mlx5e_priv *) = { mlx5e_test_link_state, mlx5e_test_link_speed, mlx5e_test_health_info, + mlx5e_test_loopback }; void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest, -- cgit v1.1 From 59ece1c9693914f969eb896906889591c4d2f172 Mon Sep 17 00:00:00 2001 From: Shaker Daibes Date: Sun, 27 Nov 2016 17:02:11 +0200 Subject: net/mlx5e: Moves pflags to priv->params pflags is a configuration parameter for the netdev, naturally it belongs to priv->params. Also introduce MLX5E_GET_PFLAG Signed-off-by: Shaker Daibes Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 16 +++++++++------- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 6 +++--- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 9cf32d3..84ac78f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -177,14 +177,16 @@ enum mlx5e_priv_flag { MLX5E_PFLAG_RX_CQE_BASED_MODER = (1 << 0), }; -#define MLX5E_SET_PRIV_FLAG(priv, pflag, enable) \ - do { \ - if (enable) \ - priv->pflags |= pflag; \ - else \ - priv->pflags &= ~pflag; \ +#define MLX5E_SET_PFLAG(priv, pflag, enable) \ + do { \ + if (enable) \ + (priv)->params.pflags |= (pflag); \ + else \ + (priv)->params.pflags &= ~(pflag); \ } while (0) +#define MLX5E_GET_PFLAG(priv, pflag) (!!((priv)->params.pflags & (pflag))) + #ifdef CONFIG_MLX5_CORE_EN_DCB #define MLX5E_MAX_BW_ALLOC 100 /* Max percentage of BW allocation */ #endif @@ -218,6 +220,7 @@ struct mlx5e_params { bool vlan_strip_disable; bool rx_am_enabled; u32 lro_timeout; + u32 pflags; }; #ifdef CONFIG_MLX5_CORE_EN_DCB @@ -705,7 +708,6 @@ struct mlx5e_priv { struct work_struct tx_timeout_work; struct delayed_work update_stats_work; - u32 pflags; struct mlx5_core_dev *mdev; struct net_device *netdev; struct mlx5e_stats stats; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 839c4e9..d2bdccb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1488,7 +1488,7 @@ static int mlx5e_handle_pflag(struct net_device *netdev, { struct mlx5e_priv *priv = netdev_priv(netdev); bool enable = !!(wanted_flags & flag); - u32 changes = wanted_flags ^ priv->pflags; + u32 changes = wanted_flags ^ priv->params.pflags; int err; if (!(changes & flag)) @@ -1501,7 +1501,7 @@ static int mlx5e_handle_pflag(struct net_device *netdev, return err; } - MLX5E_SET_PRIV_FLAG(priv, flag, enable); + MLX5E_SET_PFLAG(priv, flag, enable); return 0; } @@ -1524,7 +1524,7 @@ static u32 mlx5e_get_priv_flags(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - return priv->pflags; + return priv->params.pflags; } static int mlx5e_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 89d5c65..004940a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3488,8 +3488,8 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); /* Initialize pflags */ - MLX5E_SET_PRIV_FLAG(priv, MLX5E_PFLAG_RX_CQE_BASED_MODER, - priv->params.rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); + MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_BASED_MODER, + priv->params.rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); mutex_init(&priv->state_lock); -- cgit v1.1 From 9bcc86064bb5006257e3367fc4439f4072d82442 Mon Sep 17 00:00:00 2001 From: Shaker Daibes Date: Sun, 27 Nov 2016 17:02:12 +0200 Subject: net/mlx5e: Add CQE compression user control The user can now override the automatic driver decision using the rx_cqe_compress flag, which is the preference for CQE compression. The flag is initialized with the automatic driver decision. Signed-off-by: Shaker Daibes Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 5 +-- drivers/net/ethernet/mellanox/mlx5/core/en_clock.c | 3 +- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 39 ++++++++++++++++++++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 13 ++++---- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 4 +-- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 84ac78f..442dbc3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -171,10 +171,12 @@ extern const char mlx5e_self_tests[][ETH_GSTRING_LEN]; static const char mlx5e_priv_flags[][ETH_GSTRING_LEN] = { "rx_cqe_moder", + "rx_cqe_compress", }; enum mlx5e_priv_flag { MLX5E_PFLAG_RX_CQE_BASED_MODER = (1 << 0), + MLX5E_PFLAG_RX_CQE_COMPRESS = (1 << 1), }; #define MLX5E_SET_PFLAG(priv, pflag, enable) \ @@ -205,8 +207,7 @@ struct mlx5e_params { u16 num_channels; u8 num_tc; u8 rx_cq_period_mode; - bool rx_cqe_compress_admin; - bool rx_cqe_compress; + bool rx_cqe_compress_def; struct mlx5e_cq_moder rx_cq_moderation; struct mlx5e_cq_moder tx_cq_moderation; u16 min_rx_wqes; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c index 13dc388..2cd8e56 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -94,7 +94,7 @@ int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: /* Reset CQE compression to Admin default */ - mlx5e_modify_rx_cqe_compression(priv, priv->params.rx_cqe_compress_admin); + mlx5e_modify_rx_cqe_compression(priv, priv->params.rx_cqe_compress_def); break; case HWTSTAMP_FILTER_ALL: case HWTSTAMP_FILTER_SOME: @@ -111,6 +111,7 @@ int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: /* Disable CQE compression */ + netdev_warn(dev, "Disabling cqe compression"); mlx5e_modify_rx_cqe_compression(priv, false); config.rx_filter = HWTSTAMP_FILTER_ALL; break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index d2bdccb..aa963d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1481,6 +1481,35 @@ static int set_pflag_rx_cqe_based_moder(struct net_device *netdev, bool enable) return err; } +static int set_pflag_rx_cqe_compress(struct net_device *netdev, + bool enable) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + int err = 0; + bool reset; + + if (!MLX5_CAP_GEN(mdev, cqe_compression)) + return -ENOTSUPP; + + if (enable && priv->tstamp.hwtstamp_config.rx_filter != HWTSTAMP_FILTER_NONE) { + netdev_err(netdev, "Can't enable cqe compression while timestamping is enabled.\n"); + return -EINVAL; + } + + reset = test_bit(MLX5E_STATE_OPENED, &priv->state); + + if (reset) + mlx5e_close_locked(netdev); + + MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, enable); + priv->params.rx_cqe_compress_def = enable; + + if (reset) + err = mlx5e_open_locked(netdev); + return err; +} + static int mlx5e_handle_pflag(struct net_device *netdev, u32 wanted_flags, enum mlx5e_priv_flag flag, @@ -1511,13 +1540,19 @@ static int mlx5e_set_priv_flags(struct net_device *netdev, u32 pflags) int err; mutex_lock(&priv->state_lock); - err = mlx5e_handle_pflag(netdev, pflags, MLX5E_PFLAG_RX_CQE_BASED_MODER, set_pflag_rx_cqe_based_moder); + if (err) + goto out; + err = mlx5e_handle_pflag(netdev, pflags, + MLX5E_PFLAG_RX_CQE_COMPRESS, + set_pflag_rx_cqe_compress); + +out: mutex_unlock(&priv->state_lock); - return err ? -EINVAL : 0; + return err; } static u32 mlx5e_get_priv_flags(struct net_device *netdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 004940a..6b492ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -84,7 +84,8 @@ static void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type) switch (priv->params.rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW; - priv->params.mpwqe_log_stride_sz = priv->params.rx_cqe_compress ? + priv->params.mpwqe_log_stride_sz = + MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS) ? MLX5_MPWRQ_LOG_STRIDE_SIZE_CQE_COMPRESS : MLX5_MPWRQ_LOG_STRIDE_SIZE; priv->params.mpwqe_log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ - @@ -101,7 +102,7 @@ static void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type) priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, BIT(priv->params.log_rq_size), BIT(priv->params.mpwqe_log_stride_sz), - priv->params.rx_cqe_compress_admin); + MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS)); } static void mlx5e_set_rq_priv_params(struct mlx5e_priv *priv) @@ -1664,7 +1665,7 @@ static void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv, } MLX5_SET(cqc, cqc, log_cq_size, log_cq_size); - if (priv->params.rx_cqe_compress) { + if (MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS)) { MLX5_SET(cqc, cqc, mini_cqe_res_format, MLX5_CQE_FORMAT_CSUM); MLX5_SET(cqc, cqc, cqe_comp_en, 1); } @@ -3447,17 +3448,16 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, priv->params.log_sq_size = MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE; /* set CQE compression */ - priv->params.rx_cqe_compress_admin = false; + priv->params.rx_cqe_compress_def = false; if (MLX5_CAP_GEN(mdev, cqe_compression) && MLX5_CAP_GEN(mdev, vport_group_manager)) { mlx5e_get_max_linkspeed(mdev, &link_speed); mlx5e_get_pci_bw(mdev, &pci_bw); mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n", link_speed, pci_bw); - priv->params.rx_cqe_compress_admin = + priv->params.rx_cqe_compress_def = cqe_compress_heuristic(link_speed, pci_bw); } - priv->params.rx_cqe_compress = priv->params.rx_cqe_compress_admin; mlx5e_set_rq_priv_params(priv); if (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) @@ -3490,6 +3490,7 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, /* Initialize pflags */ MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_BASED_MODER, priv->params.rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); + MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, priv->params.rx_cqe_compress_def); mutex_init(&priv->state_lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index c6de6fb..b036710 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -164,14 +164,14 @@ void mlx5e_modify_rx_cqe_compression(struct mlx5e_priv *priv, bool val) mutex_lock(&priv->state_lock); - if (priv->params.rx_cqe_compress == val) + if (MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS) == val) goto unlock; was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); if (was_opened) mlx5e_close_locked(priv->netdev); - priv->params.rx_cqe_compress = val; + MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, val); if (was_opened) mlx5e_open_locked(priv->netdev); -- cgit v1.1 From 5edef2f288bcfb8c4d72105ea5b09a760341a946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Sun, 27 Nov 2016 23:26:28 +0100 Subject: net: dsa: mv88e6xxx: Fix mv88e6xxx_g1_irq_free() interrupt count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mv88e6xxx_g1_irq_setup() sets up chip->g1_irq.nirqs interrupt mappings, so free the same amount. This will be 8 or 9 in practice, less than 16. Fixes: dc30c35be720 ("net: dsa: mv88e6xxx: Implement interrupt support.") Cc: Andrew Lunn Signed-off-by: Andreas Färber Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 68eb8fc..ce2f7ff 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -421,7 +421,7 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip) free_irq(chip->irq, chip); - for (irq = 0; irq < 16; irq++) { + for (irq = 0; irq < chip->g1_irq.nirqs; irq++) { virq = irq_find_mapping(chip->g1_irq.domain, irq); irq_dispose_mapping(virq); } -- cgit v1.1 From 527fd70e26e68d65a47e17e71f637dc42dbd26cc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 27 Nov 2016 18:45:12 -0800 Subject: Documentation: net: phy: remove description of function pointers Remove the function pointers documentation which duplicates information found in include/linux/phy.h. Maintaining documentation about two different locations just does not work, but the code is less likely to be outdated. Reviewed-by: Martin Blumenstingl Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index 7ab9404..4b25c0f 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -251,39 +251,8 @@ Writing a PHY driver PHY_BASIC_FEATURES, but you can look in include/mii.h for other features. - Each driver consists of a number of function pointers: - - soft_reset: perform a PHY software reset - config_init: configures PHY into a sane state after a reset. - For instance, a Davicom PHY requires descrambling disabled. - probe: Allocate phy->priv, optionally refuse to bind. - PHY may not have been reset or had fixups run yet. - suspend/resume: power management - config_aneg: Changes the speed/duplex/negotiation settings - aneg_done: Determines the auto-negotiation result - read_status: Reads the current speed/duplex/negotiation settings - ack_interrupt: Clear a pending interrupt - did_interrupt: Checks if the PHY generated an interrupt - config_intr: Enable or disable interrupts - remove: Does any driver take-down - ts_info: Queries about the HW timestamping status - match_phy_device: used for Clause 45 capable PHYs to match devices - in package and ensure they are compatible - hwtstamp: Set the PHY HW timestamping configuration - rxtstamp: Requests a receive timestamp at the PHY level for a 'skb' - txtsamp: Requests a transmit timestamp at the PHY level for a 'skb' - set_wol: Enable Wake-on-LAN at the PHY level - get_wol: Get the Wake-on-LAN status at the PHY level - link_change_notify: called to inform the core is about to change the - link state, can be used to work around bogus PHY between state changes - read_mmd_indirect: Read PHY MMD indirect register - write_mmd_indirect: Write PHY MMD indirect register - module_info: Get the size and type of an EEPROM contained in an plug-in - module - module_eeprom: Get EEPROM information of a plug-in module - get_sset_count: Get number of strings sets that get_strings will count - get_strings: Get strings from requested objects (statistics) - get_stats: Get the extended statistics from the PHY device + Each driver consists of a number of function pointers, documented + in include/linux/phy.h under the phy_driver structure. Of these, only config_aneg and read_status are required to be assigned by the driver code. The rest are optional. Also, it is -- cgit v1.1 From 2fa3e25b454e267d8f55ee19c27be540495463e7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 27 Nov 2016 18:45:13 -0800 Subject: Documentation: net: phy: Add a paragraph about pause frames/flow control Describe that the Ethernet MAC controller is ultimately responsible for dealing with proper pause frames/flow control advertisement and enabling, and that it is therefore allowed to have it change phydev->supported/advertising with SUPPORTED_Pause and SUPPORTED_AsymPause. Reviewed-by: Martin Blumenstingl Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index 4b25c0f..9a42a94 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -127,8 +127,9 @@ Letting the PHY Abstraction Layer do Everything values pruned from them which don't make sense for your controller (a 10/100 controller may be connected to a gigabit capable PHY, so you would need to mask off SUPPORTED_1000baseT*). See include/linux/ethtool.h for definitions - for these bitfields. Note that you should not SET any bits, or the PHY may - get put into an unsupported state. + for these bitfields. Note that you should not SET any bits, except the + SUPPORTED_Pause and SUPPORTED_AsymPause bits (see below), or the PHY may get + put into an unsupported state. Lastly, once the controller is ready to handle network traffic, you call phy_start(phydev). This tells the PAL that you are ready, and configures the @@ -139,6 +140,19 @@ Letting the PHY Abstraction Layer do Everything When you want to disconnect from the network (even if just briefly), you call phy_stop(phydev). +Pause frames / flow control + + The PHY does not participate directly in flow control/pause frames except by + making sure that the SUPPORTED_Pause and SUPPORTED_AsymPause bits are set in + MII_ADVERTISE to indicate towards the link partner that the Ethernet MAC + controller supports such a thing. Since flow control/pause frames generation + involves the Ethernet MAC driver, it is recommended that this driver takes care + of properly indicating advertisement and support for such features by setting + the SUPPORTED_Pause and SUPPORTED_AsymPause bits accordingly. This can be done + either before or after phy_connect() and/or as a result of implementing the + ethtool::set_pauseparam feature. + + Keeping Close Tabs on the PAL It is possible that the PAL's built-in state machine needs a little help to -- cgit v1.1 From bf8f6952a233f5084431b06f49dc0e1d8907969e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 27 Nov 2016 18:45:14 -0800 Subject: Documentation: net: phy: Add blurb about RGMII RGMII is a recurring source of pain for people with Gigabit Ethernet hardware since it may require PHY driver and MAC driver level configuration hints. Document what are the expectations from PHYLIB and what options exist. Reviewed-by: Martin Blumenstingl Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 77 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index 9a42a94..c7ba84b 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -65,6 +65,83 @@ The MDIO bus drivers/net/ethernet/freescale/fsl_pq_mdio.c and an associated DTS file for one of the users. (e.g. "git grep fsl,.*-mdio arch/powerpc/boot/dts/") +(RG)MII/electrical interface considerations + + The Reduced Gigabit Medium Independent Interface (RGMII) is a 12-pin + electrical signal interface using a synchronous 125Mhz clock signal and several + data lines. Due to this design decision, a 1.5ns to 2ns delay must be added + between the clock line (RXC or TXC) and the data lines to let the PHY (clock + sink) have enough setup and hold times to sample the data lines correctly. The + PHY library offers different types of PHY_INTERFACE_MODE_RGMII* values to let + the PHY driver and optionally the MAC driver, implement the required delay. The + values of phy_interface_t must be understood from the perspective of the PHY + device itself, leading to the following: + + * PHY_INTERFACE_MODE_RGMII: the PHY is not responsible for inserting any + internal delay by itself, it assumes that either the Ethernet MAC (if capable + or the PCB traces) insert the correct 1.5-2ns delay + + * PHY_INTERFACE_MODE_RGMII_TXID: the PHY should insert an internal delay + for the transmit data lines (TXD[3:0]) processed by the PHY device + + * PHY_INTERFACE_MODE_RGMII_RXID: the PHY should insert an internal delay + for the receive data lines (RXD[3:0]) processed by the PHY device + + * PHY_INTERFACE_MODE_RGMII_ID: the PHY should insert internal delays for + both transmit AND receive data lines from/to the PHY device + + Whenever possible, use the PHY side RGMII delay for these reasons: + + * PHY devices may offer sub-nanosecond granularity in how they allow a + receiver/transmitter side delay (e.g: 0.5, 1.0, 1.5ns) to be specified. Such + precision may be required to account for differences in PCB trace lengths + + * PHY devices are typically qualified for a large range of applications + (industrial, medical, automotive...), and they provide a constant and + reliable delay across temperature/pressure/voltage ranges + + * PHY device drivers in PHYLIB being reusable by nature, being able to + configure correctly a specified delay enables more designs with similar delay + requirements to be operate correctly + + For cases where the PHY is not capable of providing this delay, but the + Ethernet MAC driver is capable of doing so, the correct phy_interface_t value + should be PHY_INTERFACE_MODE_RGMII, and the Ethernet MAC driver should be + configured correctly in order to provide the required transmit and/or receive + side delay from the perspective of the PHY device. Conversely, if the Ethernet + MAC driver looks at the phy_interface_t value, for any other mode but + PHY_INTERFACE_MODE_RGMII, it should make sure that the MAC-level delays are + disabled. + + In case neither the Ethernet MAC, nor the PHY are capable of providing the + required delays, as defined per the RGMII standard, several options may be + available: + + * Some SoCs may offer a pin pad/mux/controller capable of configuring a given + set of pins'strength, delays, and voltage; and it may be a suitable + option to insert the expected 2ns RGMII delay. + + * Modifying the PCB design to include a fixed delay (e.g: using a specifically + designed serpentine), which may not require software configuration at all. + +Common problems with RGMII delay mismatch + + When there is a RGMII delay mismatch between the Ethernet MAC and the PHY, this + will most likely result in the clock and data line signals to be unstable when + the PHY or MAC take a snapshot of these signals to translate them into logical + 1 or 0 states and reconstruct the data being transmitted/received. Typical + symptoms include: + + * Transmission/reception partially works, and there is frequent or occasional + packet loss observed + + * Ethernet MAC may report some or all packets ingressing with a FCS/CRC error, + or just discard them all + + * Switching to lower speeds such as 10/100Mbits/sec makes the problem go away + (since there is enough setup/hold time in that case) + + Connecting to a PHY Sometime during startup, the network driver needs to establish a connection -- cgit v1.1 From cc9111162b4c707797ca573139c943bf75ed326e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 27 Nov 2016 18:45:15 -0800 Subject: Documentation: net: phy: Add links to several standards documents Add links to the IEEE 802.3-2008 document, and the RGMII v1.3 and v2.0 revisions of the standard. Reviewed-by: Martin Blumenstingl Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index c7ba84b..e017d93 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -407,3 +407,13 @@ Board Fixups The stubs set one of the two matching criteria, and set the other one to match anything. +Standards + + IEEE Standard 802.3: CSMA/CD Access Method and Physical Layer Specifications, Section Two: + http://standards.ieee.org/getieee802/download/802.3-2008_section2.pdf + + RGMII v1.3: + http://web.archive.org/web/20160303212629/http://www.hp.com/rnd/pdfs/RGMIIv1_3.pdf + + RGMII v2.0: + http://web.archive.org/web/20160303171328/http://www.hp.com/rnd/pdfs/RGMIIv2_0_final_hp.pdf -- cgit v1.1 From 31ac1c19455f8aee1872512d62be1f41a497cde0 Mon Sep 17 00:00:00 2001 From: Haishuang Yan Date: Mon, 28 Nov 2016 13:26:58 +0800 Subject: geneve: fix ip_hdr_len reserved for geneve6 tunnel. It shold reserved sizeof(ipv6hdr) for geneve in ipv6 tunnel. Fixes: c3ef5aa5e5 ('geneve: Merge ipv4 and ipv6 geneve_build_skb()') Signed-off-by: Haishuang Yan Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/geneve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 7b80e28..45301cb 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -852,7 +852,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, ip_hdr(skb), skb); ttl = key->ttl ? : ip6_dst_hoplimit(dst); } - err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct iphdr)); + err = geneve_build_skb(dst, skb, info, xnet, sizeof(struct ipv6hdr)); if (unlikely(err)) return err; -- cgit v1.1 From 70d740598713cb6164e9b6b4ccd03ea3101377cd Mon Sep 17 00:00:00 2001 From: Kirtika Ruchandani Date: Wed, 23 Nov 2016 17:24:40 -0800 Subject: mwifiex: Removed unused mwifiex_private* 'priv' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit bec568ff5107 removed the last remaining usage of struct mwifiex_private* priv in mwifiex_fw_dpc(), by removing the call to mwifiex_del_virtual_intf(). Compiling mwifiex/ with W=1 gives the following warning, fix it. mwifiex/main.c: In function ‘mwifiex_fw_dpc’: mwifiex/main.c:520:26: warning: variable ‘priv’ set but not used [-Wunused-but-set-variable] Fixes: bec568ff5107 ("mwifiex: failure path handling in mwifiex_add_virtual_intf()") Cc: Amitkumar Karwar Signed-off-by: Kirtika Ruchandani Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index eac44fe..e5c3a8a 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -518,7 +518,6 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) { int ret; char fmt[64]; - struct mwifiex_private *priv; struct mwifiex_adapter *adapter = context; struct mwifiex_fw_image fw; bool init_failed = false; @@ -576,8 +575,6 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) goto err_init_fw; } - priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; - if (!adapter->wiphy) { if (mwifiex_register_cfg80211(adapter)) { mwifiex_dbg(adapter, ERROR, -- cgit v1.1 From 8ac9134161d9d09ac5cc9df844e35156c54285cb Mon Sep 17 00:00:00 2001 From: Kirtika Ruchandani Date: Wed, 23 Nov 2016 17:24:54 -0800 Subject: mwifiex: Remove unused 'chan_num' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b5413e6b2228 removed all uses of chan_num in mwifiex_config_scan(). Compiling mwifiex with W=1 gives the following warning, fix it. mwifiex/scan.c: In function ‘mwifiex_config_scan’: mwifiex/scan.c:830:6: warning: variable ‘chan_num’ set but not used [-Wunused-but-set-variable] Fixes: b5413e6b2228 ("mwifiex: increase the number of nodes in command pool") Cc: Amitkumar Karwar Signed-off-by: Kirtika Ruchandani Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/scan.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 98ce072..1816916 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -827,7 +827,6 @@ mwifiex_config_scan(struct mwifiex_private *priv, u32 num_probes; u32 ssid_len; u32 chan_idx; - u32 chan_num; u32 scan_type; u16 scan_dur; u8 channel; @@ -1105,13 +1104,12 @@ mwifiex_config_scan(struct mwifiex_private *priv, mwifiex_dbg(adapter, INFO, "info: Scan: Scanning current channel only\n"); } - chan_num = chan_idx; } else { mwifiex_dbg(adapter, INFO, "info: Scan: Creating full region channel list\n"); - chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in, - scan_chan_list, - *filtered_scan); + mwifiex_scan_create_channel_list(priv, user_scan_in, + scan_chan_list, + *filtered_scan); } } -- cgit v1.1 From 2c2bcabf02e1453c1c8aceb6ee4c0ac62066a3a7 Mon Sep 17 00:00:00 2001 From: Kirtika Ruchandani Date: Wed, 23 Nov 2016 17:25:29 -0800 Subject: mwifiex: Remove unused 'sta_ptr' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 429d90d2212b introduced mwifiex_cmd_tdls_oper() which initializes struct mwifiex_sta_node* sta_ptr, but does not use it. Compiling with W=1 gives the following warning, fix it. mwifiex/sta_cmd.c: In function ‘mwifiex_cmd_tdls_oper’: mwifiex/sta_cmd.c:1732:27: warning: variable ‘sta_ptr’ set but not used [-Wunused-but-set-variable] Fixes: 429d90d2212b ("mwifiex: add cfg80211 tdls_oper handler support") Cc: Avinash Patil Signed-off-by: Kirtika Ruchandani Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index f5dffdf..125e448 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1746,7 +1746,6 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, { struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; struct mwifiex_ds_tdls_oper *oper = data_buf; - struct mwifiex_sta_node *sta_ptr; struct host_cmd_tlv_rates *tlv_rates; struct mwifiex_ie_types_htcap *ht_capab; struct mwifiex_ie_types_qos_info *wmm_qos_info; @@ -1764,7 +1763,6 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, tdls_oper->reason = 0; memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); - sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper); -- cgit v1.1 From 60261b266a84efe035aaf4d0a49d826ceb8fb1ab Mon Sep 17 00:00:00 2001 From: Kirtika Ruchandani Date: Wed, 23 Nov 2016 17:25:50 -0800 Subject: mwifiex: Remove unused 'adapter'variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 3935ccc14d2c introduced mwifiex_tm_cmd() which initializes struct mwifiex_adapter* adapter, but doesn't use it. Compiling with W=1 gives the following warning, fix it. mwifiex/cfg80211.c: In function ‘mwifiex_tm_cmd’: mwifiex/cfg80211.c:3973:26: warning: variable ‘adapter’ set but not used [-Wunused-but-set-variable] Fixes: 3935ccc14d2c ("mwifiex: add cfg80211 testmode support") Cc: Xinming Hu Cc: Amitkumar Karwar Signed-off-by: Kirtika Ruchandani Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 2dc2757..a7d359e 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3990,13 +3990,11 @@ static int mwifiex_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); struct mwifiex_ds_misc_cmd *hostcmd; struct nlattr *tb[MWIFIEX_TM_ATTR_MAX + 1]; - struct mwifiex_adapter *adapter; struct sk_buff *skb; int err; if (!priv) return -EINVAL; - adapter = priv->adapter; err = nla_parse(tb, MWIFIEX_TM_ATTR_MAX, data, len, mwifiex_tm_policy); -- cgit v1.1 From e9f1db8b6859ceb75f2517cd4b54782d1024360d Mon Sep 17 00:00:00 2001 From: Kirtika Ruchandani Date: Wed, 23 Nov 2016 17:26:06 -0800 Subject: mwifiex: Remove unused 'pm_flag' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mwifiex_sdio_resume() intializes pm_flag, just like mwifiex_sdio_suspend(), but does not use it. Compiling with W=1 gives the following warning, fix it. mwifiex/sdio.c: In function ‘mwifiex_sdio_resume’: mwifiex/sdio.c:234:16: warning: variable ‘pm_flag’ set but not used [-Wunused-but-set-variable] sdio_get_host_pm_caps() is just an acessor, so the call to it is safe to remove. The unused variable seems to be present since 5e6e3a92b9a4 which introduced mwifiex_sdio_resume(). Fixes: 5e6e3a92b9a4 ("wireless: mwifiex: initial commit for Marvell mwifiex driver") Cc: Bing Zhao Cc: Amitkumar Karwar Signed-off-by: Kirtika Ruchandani Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 1235f04..8b33a57 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -186,9 +186,7 @@ static int mwifiex_sdio_resume(struct device *dev) struct sdio_func *func = dev_to_sdio_func(dev); struct sdio_mmc_card *card; struct mwifiex_adapter *adapter; - mmc_pm_flag_t pm_flag = 0; - pm_flag = sdio_get_host_pm_caps(func); card = sdio_get_drvdata(func); if (!card || !card->adapter) { dev_err(dev, "resume: invalid card or adapter\n"); -- cgit v1.1 From 67dd2a754906863b221b855677edd020820dd1fc Mon Sep 17 00:00:00 2001 From: Kirtika Ruchandani Date: Wed, 23 Nov 2016 17:26:57 -0800 Subject: mwifiex: Removed unused 'pkt_type' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 92263a841b15 introduced mwifiex_deaggr_sdio_pkt which initializes variable pkt_type but does not use it. Compiling with W=1 gives the following warning, fix it. mwifiex/sdio.c: In function ‘mwifiex_deaggr_sdio_pkt’: mwifiex/sdio.c:1198:6: warning: variable ‘pkt_type’ set but not used [-Wunused-but-set-variable] Fixes: 92263a841b15 ("mwifiex: add SDIO rx single port aggregation") Cc: Zhaoyang Liu Cc: Amitkumar Karwar Signed-off-by: Kirtika Ruchandani Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 8b33a57..740d79c 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -1135,7 +1135,6 @@ static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, { u32 total_pkt_len, pkt_len; struct sk_buff *skb_deaggr; - u32 pkt_type; u16 blk_size; u8 blk_num; u8 *data; @@ -1156,8 +1155,6 @@ static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, break; } pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET)); - pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET + - 2)); if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { mwifiex_dbg(adapter, ERROR, "%s: error in pkt_len,\t" -- cgit v1.1 From 4133828c76c3f358f05818ddfec1506274159256 Mon Sep 17 00:00:00 2001 From: Kirtika Ruchandani Date: Wed, 23 Nov 2016 17:27:10 -0800 Subject: mwifiex: Remove unused 'bcd_usb' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mwifiex_usb_probe() defines and sets bcd_usb but does not use it, Compiling with W=1 gives the following warning, fix it. mwifiex/usb.c: In function ‘mwifiex_usb_probe’: mwifiex/usb.c:383:41: warning: variable ‘bcd_usb’ set but not used [-Wunused-but-set-variable] The unused variable seems to be present since 4daffe354366 which introduced mwifiex_usb_probe(). Fixes: 4daffe354366 ("mwifiex: add support for Marvell USB8797 chipset") Cc: Amitkumar Karwar Signed-off-by: Kirtika Ruchandani Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index c3f696a..c563160 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -379,7 +379,7 @@ static int mwifiex_usb_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *epd; int ret, i; struct usb_card_rec *card; - u16 id_vendor, id_product, bcd_device, bcd_usb; + u16 id_vendor, id_product, bcd_device; card = devm_kzalloc(&intf->dev, sizeof(*card), GFP_KERNEL); if (!card) @@ -390,7 +390,6 @@ static int mwifiex_usb_probe(struct usb_interface *intf, id_vendor = le16_to_cpu(udev->descriptor.idVendor); id_product = le16_to_cpu(udev->descriptor.idProduct); bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); - bcd_usb = le16_to_cpu(udev->descriptor.bcdUSB); pr_debug("info: VID/PID = %X/%X, Boot2 version = %X\n", id_vendor, id_product, bcd_device); -- cgit v1.1 From 22dde1ed5a48a570aaed5ead4c7efee174f75e2e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 22 Nov 2016 18:39:45 -0800 Subject: mwifiex: pcie: implement timeout loop for FW programming doorbell Marvell Wifi PCIe modules don't always behave nicely for PCIe power management when their firmware hasn't been loaded, particularly after suspending the PCIe link one or more times. When this happens, we might end up spinning forever in this status-polling tight loop. Let's make this less tight by adding a timeout and by sleeping a bit in between reads, as we do with the other similar loops. This prevents us from hogging a CPU even in such pathological cases, and allows the FW initialization to just fail gracefully instead. I chose the same polling parameters as the earlier loop in this function, and empirically, I found that this loop never makes it more than about 12 cycles in a sane FW init sequence. I had no official information on the actual intended latency for this portion of the download. Signed-off-by: Brian Norris Acked-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index e4f1f55..4db07da 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2051,7 +2051,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, } /* Wait for the command done interrupt */ - do { + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, &ireg_intr)) { mwifiex_dbg(adapter, ERROR, @@ -2063,8 +2063,18 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, ret = -1; goto done; } - } while ((ireg_intr & CPU_INTR_DOOR_BELL) == - CPU_INTR_DOOR_BELL); + if (!(ireg_intr & CPU_INTR_DOOR_BELL)) + break; + usleep_range(10, 20); + } + if (ireg_intr & CPU_INTR_DOOR_BELL) { + mwifiex_dbg(adapter, ERROR, "%s: Card failed to ACK download\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + ret = -1; + goto done; + } mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); -- cgit v1.1 From be4b092cab84b2ecc01ee7f4da6a044279430b6f Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 23 Nov 2016 10:25:20 +0000 Subject: brcmfmac: add pcie host dongle interface rev6 support In rev6 of pcie host dongle interface protocol, host needs to maximum supported ring number from dongle shared memory and set up ring buffer and ring indices offset accordingly. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 10 +- .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 38 +++-- .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.h | 4 + .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 171 ++++++++++++--------- 4 files changed, 132 insertions(+), 91 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 2b24654..e21f760 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -22,10 +22,12 @@ /* IDs of the 6 default common rings of msgbuf protocol */ #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 #define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1 +#define BRCMF_H2D_MSGRING_FLOWRING_IDSTART 2 #define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2 #define BRCMF_D2H_MSGRING_TX_COMPLETE 3 #define BRCMF_D2H_MSGRING_RX_COMPLETE 4 + #define BRCMF_NROF_H2D_COMMON_MSGRINGS 2 #define BRCMF_NROF_D2H_COMMON_MSGRINGS 3 #define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ @@ -95,14 +97,18 @@ struct brcmf_bus_ops { * @flowrings: commonrings which are dynamically created and destroyed for data. * @rx_dataoffset: if set then all rx data has this this offset. * @max_rxbufpost: maximum number of buffers to post for rx. - * @nrof_flowrings: number of flowrings. + * @max_flowrings: maximum number of tx flow rings supported. + * @max_submissionrings: maximum number of submission rings(h2d) supported. + * @max_completionrings: maximum number of completion rings(d2h) supported. */ struct brcmf_bus_msgbuf { struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; struct brcmf_commonring **flowrings; u32 rx_dataoffset; u32 max_rxbufpost; - u32 nrof_flowrings; + u16 max_flowrings; + u16 max_submissionrings; + u16 max_completionrings; }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 7cc8851..d2c834c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -87,11 +87,6 @@ struct msgbuf_common_hdr { __le32 request_id; }; -struct msgbuf_buf_addr { - __le32 low_addr; - __le32 high_addr; -}; - struct msgbuf_ioctl_req_hdr { struct msgbuf_common_hdr msg; __le32 cmd; @@ -227,7 +222,10 @@ struct brcmf_msgbuf { struct brcmf_commonring **commonrings; struct brcmf_commonring **flowrings; dma_addr_t *flowring_dma_handle; - u16 nrof_flowrings; + + u16 max_flowrings; + u16 max_submissionrings; + u16 max_completionrings; u16 rx_dataoffset; u32 max_rxbufpost; @@ -610,7 +608,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, create->msg.request_id = 0; create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); create->flow_ring_id = cpu_to_le16(flowid + - BRCMF_NROF_H2D_COMMON_MSGRINGS); + BRCMF_H2D_MSGRING_FLOWRING_IDSTART); memcpy(create->sa, work->sa, ETH_ALEN); memcpy(create->da, work->da, ETH_ALEN); address = (u64)msgbuf->flowring_dma_handle[flowid]; @@ -760,7 +758,7 @@ static void brcmf_msgbuf_txflow_worker(struct work_struct *worker) u32 flowid; msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work); - for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) { + for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->max_flowrings) { clear_bit(flowid, msgbuf->flow_map); brcmf_msgbuf_txflow(msgbuf, flowid); } @@ -866,7 +864,7 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) tx_status = (struct msgbuf_tx_status *)buf; idx = le32_to_cpu(tx_status->msg.request_id); flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); - flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, msgbuf->tx_pktids, idx); if (!skb) @@ -1174,7 +1172,7 @@ brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf; flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); - flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; status = le16_to_cpu(flowring_create_resp->compl_hdr.status); if (status) { @@ -1202,7 +1200,7 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf; flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); - flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; status = le16_to_cpu(flowring_delete_resp->compl_hdr.status); if (status) { @@ -1307,7 +1305,7 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev) brcmf_msgbuf_process_rx(msgbuf, buf); for_each_set_bit(flowid, msgbuf->txstatus_done_map, - msgbuf->nrof_flowrings) { + msgbuf->max_flowrings) { clear_bit(flowid, msgbuf->txstatus_done_map); commonring = msgbuf->flowrings[flowid]; qlen = brcmf_flowring_qlen(msgbuf->flow, flowid); @@ -1349,7 +1347,7 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid) delete->msg.request_id = 0; delete->flow_ring_id = cpu_to_le16(flowid + - BRCMF_NROF_H2D_COMMON_MSGRINGS); + BRCMF_H2D_MSGRING_FLOWRING_IDSTART); delete->reason = 0; brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n", @@ -1427,10 +1425,10 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) if_msgbuf = drvr->bus_if->msgbuf; - if (if_msgbuf->nrof_flowrings >= BRCMF_FLOWRING_HASHSIZE) { + if (if_msgbuf->max_flowrings >= BRCMF_FLOWRING_HASHSIZE) { brcmf_err("driver not configured for this many flowrings %d\n", - if_msgbuf->nrof_flowrings); - if_msgbuf->nrof_flowrings = BRCMF_FLOWRING_HASHSIZE - 1; + if_msgbuf->max_flowrings); + if_msgbuf->max_flowrings = BRCMF_FLOWRING_HASHSIZE - 1; } msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL); @@ -1443,7 +1441,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) goto fail; } INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); - count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); + count = BITS_TO_LONGS(if_msgbuf->max_flowrings); count = count * sizeof(unsigned long); msgbuf->flow_map = kzalloc(count, GFP_KERNEL); if (!msgbuf->flow_map) @@ -1479,8 +1477,8 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) msgbuf->commonrings = (struct brcmf_commonring **)if_msgbuf->commonrings; msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; - msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; - msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * + msgbuf->max_flowrings = if_msgbuf->max_flowrings; + msgbuf->flowring_dma_handle = kzalloc(msgbuf->max_flowrings * sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); if (!msgbuf->flowring_dma_handle) goto fail; @@ -1501,7 +1499,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) goto fail; msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev, - if_msgbuf->nrof_flowrings); + if_msgbuf->max_flowrings); if (!msgbuf->flow) goto fail; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h index ee6906a..f93ba6b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h @@ -31,6 +31,10 @@ #define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE 32 #define BRCMF_H2D_TXFLOWRING_ITEMSIZE 48 +struct msgbuf_buf_addr { + __le32 low_addr; + __le32 high_addr; +}; int brcmf_proto_msgbuf_rx_trigger(struct device *dev); void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 3deba90..048027f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -135,7 +135,7 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_PCIE_MB_INT_D2H3_DB1) #define BRCMF_PCIE_MIN_SHARED_VERSION 5 -#define BRCMF_PCIE_MAX_SHARED_VERSION 5 +#define BRCMF_PCIE_MAX_SHARED_VERSION 6 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF #define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 #define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 @@ -166,17 +166,6 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_RING_MEM_SZ 16 #define BRCMF_RING_STATE_SZ 8 -#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET 4 -#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 -#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 -#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 -#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20 -#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28 -#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36 -#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44 -#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 -#define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 - #define BRCMF_DEF_MAX_RXBUFPOST 255 #define BRCMF_CONSOLE_BUFADDR_OFFSET 8 @@ -231,7 +220,9 @@ struct brcmf_pcie_shared_info { struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; struct brcmf_pcie_ringbuf *flowrings; u16 max_rxbufpost; - u32 nrof_flowrings; + u16 max_flowrings; + u16 max_submissionrings; + u16 max_completionrings; u32 rx_dataoffset; u32 htod_mb_data_addr; u32 dtoh_mb_data_addr; @@ -241,6 +232,7 @@ struct brcmf_pcie_shared_info { dma_addr_t scratch_dmahandle; void *ringupd; dma_addr_t ringupd_dmahandle; + u8 version; }; struct brcmf_pcie_core_info { @@ -284,6 +276,36 @@ struct brcmf_pcie_ringbuf { u8 id; }; +/** + * struct brcmf_pcie_dhi_ringinfo - dongle/host interface shared ring info + * + * @ringmem: dongle memory pointer to ring memory location + * @h2d_w_idx_ptr: h2d ring write indices dongle memory pointers + * @h2d_r_idx_ptr: h2d ring read indices dongle memory pointers + * @d2h_w_idx_ptr: d2h ring write indices dongle memory pointers + * @d2h_r_idx_ptr: d2h ring read indices dongle memory pointers + * @h2d_w_idx_hostaddr: h2d ring write indices host memory pointers + * @h2d_r_idx_hostaddr: h2d ring read indices host memory pointers + * @d2h_w_idx_hostaddr: d2h ring write indices host memory pointers + * @d2h_r_idx_hostaddr: d2h ring reaD indices host memory pointers + * @max_flowrings: maximum number of tx flow rings supported. + * @max_submissionrings: maximum number of submission rings(h2d) supported. + * @max_completionrings: maximum number of completion rings(d2h) supported. + */ +struct brcmf_pcie_dhi_ringinfo { + __le32 ringmem; + __le32 h2d_w_idx_ptr; + __le32 h2d_r_idx_ptr; + __le32 d2h_w_idx_ptr; + __le32 d2h_r_idx_ptr; + struct msgbuf_buf_addr h2d_w_idx_hostaddr; + struct msgbuf_buf_addr h2d_r_idx_hostaddr; + struct msgbuf_buf_addr d2h_w_idx_hostaddr; + struct msgbuf_buf_addr d2h_r_idx_hostaddr; + __le16 max_flowrings; + __le16 max_submissionrings; + __le16 max_completionrings; +}; static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = { BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM, @@ -1054,26 +1076,35 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) { struct brcmf_pcie_ringbuf *ring; struct brcmf_pcie_ringbuf *rings; - u32 ring_addr; u32 d2h_w_idx_ptr; u32 d2h_r_idx_ptr; u32 h2d_w_idx_ptr; u32 h2d_r_idx_ptr; - u32 addr; u32 ring_mem_ptr; u32 i; u64 address; u32 bufsz; - u16 max_sub_queues; u8 idx_offset; - - ring_addr = devinfo->shared.ring_info_addr; - brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); - addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; - max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); + struct brcmf_pcie_dhi_ringinfo ringinfo; + u16 max_flowrings; + u16 max_submissionrings; + u16 max_completionrings; + + memcpy_fromio(&ringinfo, devinfo->tcm + devinfo->shared.ring_info_addr, + sizeof(ringinfo)); + if (devinfo->shared.version >= 6) { + max_submissionrings = le16_to_cpu(ringinfo.max_submissionrings); + max_flowrings = le16_to_cpu(ringinfo.max_flowrings); + max_completionrings = le16_to_cpu(ringinfo.max_completionrings); + } else { + max_submissionrings = le16_to_cpu(ringinfo.max_flowrings); + max_flowrings = max_submissionrings - + BRCMF_NROF_H2D_COMMON_MSGRINGS; + max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS; + } if (devinfo->dma_idx_sz != 0) { - bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * + bufsz = (max_submissionrings + max_completionrings) * devinfo->dma_idx_sz * 2; devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, &devinfo->idxbuf_dmahandle, @@ -1083,14 +1114,10 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) } if (devinfo->dma_idx_sz == 0) { - addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; - d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; - d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; - h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; - h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + d2h_w_idx_ptr = le32_to_cpu(ringinfo.d2h_w_idx_ptr); + d2h_r_idx_ptr = le32_to_cpu(ringinfo.d2h_r_idx_ptr); + h2d_w_idx_ptr = le32_to_cpu(ringinfo.h2d_w_idx_ptr); + h2d_r_idx_ptr = le32_to_cpu(ringinfo.h2d_r_idx_ptr); idx_offset = sizeof(u32); devinfo->write_ptr = brcmf_pcie_write_tcm16; devinfo->read_ptr = brcmf_pcie_read_tcm16; @@ -1103,34 +1130,42 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) devinfo->read_ptr = brcmf_pcie_read_idx; h2d_w_idx_ptr = 0; - addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; address = (u64)devinfo->idxbuf_dmahandle; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - - h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; - addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; - address += max_sub_queues * idx_offset; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - - d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; - addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; - address += max_sub_queues * idx_offset; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + ringinfo.h2d_w_idx_hostaddr.low_addr = + cpu_to_le32(address & 0xffffffff); + ringinfo.h2d_w_idx_hostaddr.high_addr = + cpu_to_le32(address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + + max_submissionrings * idx_offset; + address += max_submissionrings * idx_offset; + ringinfo.h2d_r_idx_hostaddr.low_addr = + cpu_to_le32(address & 0xffffffff); + ringinfo.h2d_r_idx_hostaddr.high_addr = + cpu_to_le32(address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + + max_submissionrings * idx_offset; + address += max_submissionrings * idx_offset; + ringinfo.d2h_w_idx_hostaddr.low_addr = + cpu_to_le32(address & 0xffffffff); + ringinfo.d2h_w_idx_hostaddr.high_addr = + cpu_to_le32(address >> 32); d2h_r_idx_ptr = d2h_w_idx_ptr + - BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; - addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; - address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + max_completionrings * idx_offset; + address += max_completionrings * idx_offset; + ringinfo.d2h_r_idx_hostaddr.low_addr = + cpu_to_le32(address & 0xffffffff); + ringinfo.d2h_r_idx_hostaddr.high_addr = + cpu_to_le32(address >> 32); + + memcpy_toio(devinfo->tcm + devinfo->shared.ring_info_addr, + &ringinfo, sizeof(ringinfo)); brcmf_dbg(PCIE, "Using host memory indices\n"); } - addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; - ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + ring_mem_ptr = le32_to_cpu(ringinfo.ringmem); for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) { ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); @@ -1161,20 +1196,19 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring_mem_ptr += BRCMF_RING_MEM_SZ; } - devinfo->shared.nrof_flowrings = - max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; - rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), - GFP_KERNEL); + devinfo->shared.max_flowrings = max_flowrings; + devinfo->shared.max_submissionrings = max_submissionrings; + devinfo->shared.max_completionrings = max_completionrings; + rings = kcalloc(max_flowrings, sizeof(*ring), GFP_KERNEL); if (!rings) goto fail; - brcmf_dbg(PCIE, "Nr of flowrings is %d\n", - devinfo->shared.nrof_flowrings); + brcmf_dbg(PCIE, "Nr of flowrings is %d\n", max_flowrings); - for (i = 0; i < devinfo->shared.nrof_flowrings; i++) { + for (i = 0; i < max_flowrings; i++) { ring = &rings[i]; ring->devinfo = devinfo; - ring->id = i + BRCMF_NROF_COMMON_MSGRINGS; + ring->id = i + BRCMF_H2D_MSGRING_FLOWRING_IDSTART; brcmf_commonring_register_cb(&ring->commonring, brcmf_pcie_ring_mb_ring_bell, brcmf_pcie_ring_mb_update_rptr, @@ -1357,17 +1391,16 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, { struct brcmf_pcie_shared_info *shared; u32 addr; - u32 version; shared = &devinfo->shared; shared->tcm_base_address = sharedram_addr; shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr); - version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK; - brcmf_dbg(PCIE, "PCIe protocol version %d\n", version); - if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) || - (version < BRCMF_PCIE_MIN_SHARED_VERSION)) { - brcmf_err("Unsupported PCIE version %d\n", version); + shared->version = (u8)(shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK); + brcmf_dbg(PCIE, "PCIe protocol version %d\n", shared->version); + if ((shared->version > BRCMF_PCIE_MAX_SHARED_VERSION) || + (shared->version < BRCMF_PCIE_MIN_SHARED_VERSION)) { + brcmf_err("Unsupported PCIE version %d\n", shared->version); return -EINVAL; } @@ -1661,18 +1694,18 @@ static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, bus->msgbuf->commonrings[i] = &devinfo->shared.commonrings[i]->commonring; - flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), + flowrings = kcalloc(devinfo->shared.max_flowrings, sizeof(*flowrings), GFP_KERNEL); if (!flowrings) goto fail; - for (i = 0; i < devinfo->shared.nrof_flowrings; i++) + for (i = 0; i < devinfo->shared.max_flowrings; i++) flowrings[i] = &devinfo->shared.flowrings[i].commonring; bus->msgbuf->flowrings = flowrings; bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset; bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost; - bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings; + bus->msgbuf->max_flowrings = devinfo->shared.max_flowrings; init_waitqueue_head(&devinfo->mbdata_resp_wait); -- cgit v1.1 From dc630dc5c753ccba97ce174f9c2894f802f9bd93 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:21 +0000 Subject: brcmfmac: add support for 43341 chip This chip was already supported, but seems a device came up giving a different chip identifier. So adding that effectively mapping to the same firmware file as for 43340 chip. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 1 + drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index b892dac..dfb0658 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -621,6 +621,7 @@ static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339), diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index d0407d9..f1fb8a3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -36,6 +36,7 @@ #define BRCM_CC_4330_CHIP_ID 0x4330 #define BRCM_CC_4334_CHIP_ID 0x4334 #define BRCM_CC_43340_CHIP_ID 43340 +#define BRCM_CC_43341_CHIP_ID 43341 #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 -- cgit v1.1 From ac55136f43d3336c7b40238b779c404008229929 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:22 +0000 Subject: brcmfmac: move pno helper functions in separate source file Introducing new source file for pno related functionality. Moving existing pno functions. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/Makefile | 3 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 93 ++----------------- .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 100 +++++++++++++++++++++ .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 40 +++++++++ 4 files changed, 150 insertions(+), 86 deletions(-) create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index 9e4b505..d1568be 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -35,7 +35,8 @@ brcmfmac-objs += \ firmware.o \ feature.o \ btcoex.o \ - vendor.o + vendor.o \ + pno.o brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \ bcdc.o brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index b3cdc522..e030ac4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -32,6 +32,7 @@ #include "fwil_types.h" #include "p2p.h" #include "btcoex.h" +#include "pno.h" #include "cfg80211.h" #include "feature.h" #include "fwil.h" @@ -41,16 +42,6 @@ #include "common.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 -#define BRCMF_PNO_VERSION 2 -#define BRCMF_PNO_TIME 30 -#define BRCMF_PNO_REPEAT 4 -#define BRCMF_PNO_FREQ_EXPO_MAX 3 -#define BRCMF_PNO_MAX_PFN_COUNT 16 -#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 -#define BRCMF_PNO_HIDDEN_BIT 2 -#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF -#define BRCMF_PNO_SCAN_COMPLETE 1 -#define BRCMF_PNO_SCAN_INCOMPLETE 0 #define WPA_OUI "\x00\x50\xF2" /* WPA OUI */ #define WPA_OUI_TYPE 1 @@ -3323,76 +3314,6 @@ out_err: return err; } -static int brcmf_dev_pno_clean(struct net_device *ndev) -{ - int ret; - - /* Disable pfn */ - ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); - if (ret == 0) { - /* clear pfn */ - ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", - NULL, 0); - } - if (ret < 0) - brcmf_err("failed code %d\n", ret); - - return ret; -} - -static int brcmf_dev_pno_config(struct brcmf_if *ifp, - struct cfg80211_sched_scan_request *request) -{ - struct brcmf_pno_param_le pfn_param; - struct brcmf_pno_macaddr_le pfn_mac; - s32 err; - u8 *mac_mask; - int i; - - memset(&pfn_param, 0, sizeof(pfn_param)); - pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); - - /* set extra pno params */ - pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); - pfn_param.repeat = BRCMF_PNO_REPEAT; - pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; - - /* set up pno scan fr */ - pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); - - err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, - sizeof(pfn_param)); - if (err) { - brcmf_err("pfn_set failed, err=%d\n", err); - return err; - } - - /* Find out if mac randomization should be turned on */ - if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) - return 0; - - pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; - pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; - - memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); - mac_mask = request->mac_addr_mask; - for (i = 0; i < ETH_ALEN; i++) { - pfn_mac.mac[i] &= mac_mask[i]; - pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); - } - /* Clear multi bit */ - pfn_mac.mac[0] &= 0xFE; - /* Set locally administered */ - pfn_mac.mac[0] |= 0x02; - - err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, - sizeof(pfn_mac)); - if (err) - brcmf_err("pfn_macaddr failed, err=%d\n", err); - - return err; -} - static int brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *ndev, @@ -3436,15 +3357,16 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, if (request->n_match_sets > 0) { /* clean up everything */ - ret = brcmf_dev_pno_clean(ndev); + ret = brcmf_pno_clean(ifp); if (ret < 0) { brcmf_err("failed error=%d\n", ret); return ret; } /* configure pno */ - if (brcmf_dev_pno_config(ifp, request)) - return -EINVAL; + ret = brcmf_pno_config(ifp, request); + if (ret < 0) + return ret; /* configure each match set */ for (i = 0; i < request->n_match_sets; i++) { @@ -3486,11 +3408,12 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); brcmf_dbg(SCAN, "enter\n"); - brcmf_dev_pno_clean(ndev); + brcmf_pno_clean(ifp); if (cfg->sched_escan) - brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); + brcmf_notify_escan_complete(cfg, ifp, true, true); return 0; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c new file mode 100644 index 0000000..2f6a4e0 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016 Broadcom + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include "core.h" +#include "debug.h" +#include "pno.h" +#include "fwil.h" +#include "fwil_types.h" + +#define BRCMF_PNO_VERSION 2 +#define BRCMF_PNO_TIME 30 +#define BRCMF_PNO_REPEAT 4 +#define BRCMF_PNO_FREQ_EXPO_MAX 3 +#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 +#define BRCMF_PNO_SCAN_INCOMPLETE 0 + +int brcmf_pno_clean(struct brcmf_if *ifp) +{ + int ret; + + /* Disable pfn */ + ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0); + if (ret == 0) { + /* clear pfn */ + ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0); + } + if (ret < 0) + brcmf_err("failed code %d\n", ret); + + return ret; +} + +int brcmf_pno_config(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *request) +{ + struct brcmf_pno_param_le pfn_param; + struct brcmf_pno_macaddr_le pfn_mac; + s32 err; + u8 *mac_mask; + int i; + + memset(&pfn_param, 0, sizeof(pfn_param)); + pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); + + /* set extra pno params */ + pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = BRCMF_PNO_REPEAT; + pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; + + /* set up pno scan fr */ + pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); + + err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, + sizeof(pfn_param)); + if (err) { + brcmf_err("pfn_set failed, err=%d\n", err); + return err; + } + + /* Find out if mac randomization should be turned on */ + if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) + return 0; + + pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; + pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; + + memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); + mac_mask = request->mac_addr_mask; + for (i = 0; i < ETH_ALEN; i++) { + pfn_mac.mac[i] &= mac_mask[i]; + pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); + } + /* Clear multi bit */ + pfn_mac.mac[0] &= 0xFE; + /* Set locally administered */ + pfn_mac.mac[0] |= 0x02; + + err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, + sizeof(pfn_mac)); + if (err) + brcmf_err("pfn_macaddr failed, err=%d\n", err); + + return err; +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h new file mode 100644 index 0000000..08d701e --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Broadcom + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _BRCMF_PNO_H +#define _BRCMF_PNO_H + +#define BRCMF_PNO_SCAN_COMPLETE 1 +#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF +#define BRCMF_PNO_HIDDEN_BIT 2 +#define BRCMF_PNO_MAX_PFN_COUNT 16 + +/** + * brcmf_pno_clean - disable and clear pno in firmware. + * + * @ifp: interface object used. + */ +int brcmf_pno_clean(struct brcmf_if *ifp); + +/** + * brcmf_pno_config - configure pno parameters. + * + * @ifp: interface object used. + * @request: scheduled scan parameters. + */ +int brcmf_pno_config(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *request); + +#endif /* _BRCMF_PNO_H */ -- cgit v1.1 From 3e2e86ab19c2a43953de30089c5411c580ddb5f7 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:23 +0000 Subject: brcmfmac: fix handling ssids in .sched_scan_start() callback The ssids list in the scheduled scan request were not properly taken into account when configuring in firmware. The hidden bit was set for any ssid resulting in active scanning for all. Only set it for ssids that are in the ssids list. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 103 ++++++++++----------- .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 18 ++++ .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 12 ++- 3 files changed, 76 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index e030ac4..3d51e84 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3314,19 +3314,37 @@ out_err: return err; } +static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, + struct cfg80211_sched_scan_request *req) +{ + int i; + + if (!ssid || !req->ssids || !req->n_ssids) + return false; + + for (i = 0; i < req->n_ssids; i++) { + if (ssid->ssid_len == req->ssids[i].ssid_len) { + if (!strncmp(ssid->ssid, req->ssids[i].ssid, + ssid->ssid_len)) + return true; + } + } + return false; +} + static int brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_sched_scan_request *request) + struct cfg80211_sched_scan_request *req) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_pno_net_param_le pfn; + struct cfg80211_ssid *ssid; int i; int ret = 0; brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", - request->n_match_sets, request->n_ssids); + req->n_match_sets, req->n_ssids); if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); return -EAGAIN; @@ -3337,71 +3355,46 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, return -EAGAIN; } - if (!request->n_ssids || !request->n_match_sets) { - brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", - request->n_ssids); + if (req->n_match_sets <= 0) { + brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n", + req->n_match_sets); return -EINVAL; } - if (request->n_ssids > 0) { - for (i = 0; i < request->n_ssids; i++) { - /* Active scan req for ssids */ - brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", - request->ssids[i].ssid); - - /* match_set ssids is a supert set of n_ssid list, - * so we need not add these set separately. - */ - } + /* clean up everything */ + ret = brcmf_pno_clean(ifp); + if (ret < 0) { + brcmf_err("failed error=%d\n", ret); + return ret; } - if (request->n_match_sets > 0) { - /* clean up everything */ - ret = brcmf_pno_clean(ifp); - if (ret < 0) { - brcmf_err("failed error=%d\n", ret); - return ret; - } + /* configure pno */ + ret = brcmf_pno_config(ifp, req); + if (ret < 0) + return ret; - /* configure pno */ - ret = brcmf_pno_config(ifp, request); - if (ret < 0) - return ret; + /* configure each match set */ + for (i = 0; i < req->n_match_sets; i++) { - /* configure each match set */ - for (i = 0; i < request->n_match_sets; i++) { - struct cfg80211_ssid *ssid; - u32 ssid_len; + ssid = &req->match_sets[i].ssid; - ssid = &request->match_sets[i].ssid; - ssid_len = ssid->ssid_len; + if (!ssid->ssid_len) { + brcmf_err("skip broadcast ssid\n"); + continue; + } - if (!ssid_len) { - brcmf_err("skip broadcast ssid\n"); - continue; - } - pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); - pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); - pfn.wsec = cpu_to_le32(0); - pfn.infra = cpu_to_le32(1); - pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); - pfn.ssid.SSID_len = cpu_to_le32(ssid_len); - memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); - ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, - sizeof(pfn)); + ret = brcmf_pno_add_ssid(ifp, ssid, + brcmf_is_ssid_active(ssid, req)); + if (ret < 0) brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", ret == 0 ? "set" : "failed", ssid->ssid); - } - /* Enable the PNO */ - if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { - brcmf_err("PNO enable failed!! ret=%d\n", ret); - return -EINVAL; - } - } else { - return -EINVAL; } + /* Enable the PNO */ + ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); + if (ret < 0) + brcmf_err("PNO enable failed!! ret=%d\n", ret); - return 0; + return ret; } static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index 2f6a4e0..c7967d9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -28,6 +28,8 @@ #define BRCMF_PNO_FREQ_EXPO_MAX 3 #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 #define BRCMF_PNO_SCAN_INCOMPLETE 0 +#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF +#define BRCMF_PNO_HIDDEN_BIT 2 int brcmf_pno_clean(struct brcmf_if *ifp) { @@ -98,3 +100,19 @@ int brcmf_pno_config(struct brcmf_if *ifp, return err; } +int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, + bool active) +{ + struct brcmf_pno_net_param_le pfn; + + pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); + pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); + pfn.wsec = cpu_to_le32(0); + pfn.infra = cpu_to_le32(1); + if (active) + pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); + pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len); + memcpy(pfn.ssid.SSID, ssid->ssid, ssid->ssid_len); + return brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn)); +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h index 08d701e..a4a23fc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h @@ -17,8 +17,6 @@ #define _BRCMF_PNO_H #define BRCMF_PNO_SCAN_COMPLETE 1 -#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF -#define BRCMF_PNO_HIDDEN_BIT 2 #define BRCMF_PNO_MAX_PFN_COUNT 16 /** @@ -37,4 +35,14 @@ int brcmf_pno_clean(struct brcmf_if *ifp); int brcmf_pno_config(struct brcmf_if *ifp, struct cfg80211_sched_scan_request *request); +/** + * brcmf_pno_add_ssid - add ssid for pno in firmware. + * + * @ifp: interface object used. + * @ssid: ssid information. + * @active: indicate this ssid needs to be actively probed. + */ +int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, + bool active); + #endif /* _BRCMF_PNO_H */ -- cgit v1.1 From ab5981c830339b945ddbedc314567a9e5f506d72 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:24 +0000 Subject: brcmfmac: change prototype for brcmf_do_escan() Reduce the number of parameters as the removed ones can be obtained through struct brcmf_if parameter. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 3d51e84..35867c8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1080,9 +1080,9 @@ exit: } static s32 -brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, - struct brcmf_if *ifp, struct cfg80211_scan_request *request) +brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request) { + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; s32 err; u32 passive_scan; struct brcmf_scan_results *results; @@ -1090,7 +1090,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, brcmf_dbg(SCAN, "Enter\n"); escan->ifp = ifp; - escan->wiphy = wiphy; + escan->wiphy = cfg->wiphy; escan->escan_state = WL_ESCAN_STATE_SCANNING; passive_scan = cfg->active_scan ? 0 : 1; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, @@ -1170,7 +1170,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, if (err) goto scan_out; - err = brcmf_do_escan(cfg, wiphy, vif->ifp, request); + err = brcmf_do_escan(vif->ifp, request); if (err) goto scan_out; } else { @@ -3289,7 +3289,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); cfg->escan_info.run = brcmf_run_escan; - err = brcmf_do_escan(cfg, wiphy, ifp, request); + err = brcmf_do_escan(ifp, request); if (err) { clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); goto out_err; -- cgit v1.1 From fa85b30a908455ff25def3a5f319aad272ef4862 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:25 +0000 Subject: brcmfmac: make internal escan more generic For scheduled scan we initiate an escan in firmware to obtain more info missing from the scheduled scan notification we get from firmware. For upcoming functionality this is also required so make it a bit more generic. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 187 ++++++++++++--------- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 4 +- 2 files changed, 109 insertions(+), 82 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 35867c8..f9b10bb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -757,12 +757,12 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, brcmf_scan_config_mpc(ifp, 1); /* - * e-scan can be initiated by scheduled scan + * e-scan can be initiated internally * which takes precedence. */ - if (cfg->sched_escan) { + if (cfg->internal_escan) { brcmf_dbg(SCAN, "scheduled scan completed\n"); - cfg->sched_escan = false; + cfg->internal_escan = false; if (!aborted) cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); } else if (scan_request) { @@ -3013,7 +3013,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) struct escan_info *escan = &cfg->escan_info; set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); - if (cfg->scan_request) { + if (cfg->internal_escan || cfg->scan_request) { escan->escan_state = WL_ESCAN_STATE_IDLE; brcmf_notify_escan_complete(cfg, escan->ifp, true, true); } @@ -3036,7 +3036,7 @@ static void brcmf_escan_timeout(unsigned long data) struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)data; - if (cfg->scan_request) { + if (cfg->internal_escan || cfg->scan_request) { brcmf_err("timer expired\n"); schedule_work(&cfg->escan_timeout_work); } @@ -3119,7 +3119,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) goto exit; - if (!cfg->scan_request) { + if (!cfg->internal_escan && !cfg->scan_request) { brcmf_dbg(SCAN, "result without cfg80211 request\n"); goto exit; } @@ -3165,7 +3165,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) goto exit; - if (cfg->scan_request) { + if (cfg->internal_escan || cfg->scan_request) { brcmf_inform_bss(cfg); aborted = status != BRCMF_E_STATUS_SUCCESS; brcmf_notify_escan_complete(cfg, ifp, aborted, false); @@ -3190,6 +3190,73 @@ static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) brcmf_cfg80211_escan_timeout_worker); } +static struct cfg80211_scan_request * +brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) { + struct cfg80211_scan_request *req; + size_t req_size; + + req_size = sizeof(*req) + + n_netinfo * sizeof(req->channels[0]) + + n_netinfo * sizeof(*req->ssids); + + req = kzalloc(req_size, GFP_KERNEL); + if (req) { + req->wiphy = wiphy; + req->ssids = (void *)(&req->channels[0]) + + n_netinfo * sizeof(req->channels[0]); + } + return req; +} + +static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req, + u8 *ssid, u8 ssid_len, u8 channel) +{ + struct ieee80211_channel *chan; + enum nl80211_band band; + int freq; + + if (channel <= CH_MAX_2G_CHANNEL) + band = NL80211_BAND_2GHZ; + else + band = NL80211_BAND_5GHZ; + + freq = ieee80211_channel_to_frequency(channel, band); + if (!freq) + return -EINVAL; + + chan = ieee80211_get_channel(req->wiphy, freq); + if (!chan) + return -EINVAL; + + req->channels[req->n_channels++] = chan; + memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len); + req->ssids[req->n_ssids++].ssid_len = ssid_len; + + return 0; +} + +static int brcmf_start_internal_escan(struct brcmf_if *ifp, + struct cfg80211_scan_request *request) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + int err; + + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + /* Abort any on-going scan */ + brcmf_abort_scanning(cfg); + } + + set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + cfg->escan_info.run = brcmf_run_escan; + err = brcmf_do_escan(ifp, request); + if (err) { + clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + return err; + } + cfg->internal_escan = true; + return 0; +} + /* PFN result doesn't have all the info which are required by the supplicant * (For e.g IEs) Do a target Escan so that sched scan results are reported * via wl_inform_single_bss in the required format. Escan does require the @@ -3203,12 +3270,8 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct brcmf_pno_net_info_le *netinfo, *netinfo_start; struct cfg80211_scan_request *request = NULL; - struct cfg80211_ssid *ssid = NULL; - struct ieee80211_channel *channel = NULL; struct wiphy *wiphy = cfg_to_wiphy(cfg); - int err = 0; - int channel_req = 0; - int band = 0; + int i, err = 0; struct brcmf_pno_scanresults_le *pfn_result; u32 result_count; u32 status; @@ -3234,83 +3297,47 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, */ WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); - if (result_count > 0) { - int i; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); - channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); - if (!request || !ssid || !channel) { - err = -ENOMEM; - goto out_err; - } - - request->wiphy = wiphy; - data += sizeof(struct brcmf_pno_scanresults_le); - netinfo_start = (struct brcmf_pno_net_info_le *)data; - - for (i = 0; i < result_count; i++) { - netinfo = &netinfo_start[i]; - if (!netinfo) { - brcmf_err("Invalid netinfo ptr. index: %d\n", - i); - err = -EINVAL; - goto out_err; - } - - brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", - netinfo->SSID, netinfo->channel); - memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); - ssid[i].ssid_len = netinfo->SSID_len; - request->n_ssids++; - - channel_req = netinfo->channel; - if (channel_req <= CH_MAX_2G_CHANNEL) - band = NL80211_BAND_2GHZ; - else - band = NL80211_BAND_5GHZ; - channel[i].center_freq = - ieee80211_channel_to_frequency(channel_req, - band); - channel[i].band = band; - channel[i].flags |= IEEE80211_CHAN_NO_HT40; - request->channels[i] = &channel[i]; - request->n_channels++; - } + if (!result_count) { + brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); + goto out_err; + } + request = brcmf_alloc_internal_escan_request(wiphy, + result_count); + if (!request) { + err = -ENOMEM; + goto out_err; + } - /* assign parsed ssid array */ - if (request->n_ssids) - request->ssids = &ssid[0]; + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo_start = (struct brcmf_pno_net_info_le *)data; - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - /* Abort any on-going scan */ - brcmf_abort_scanning(cfg); + for (i = 0; i < result_count; i++) { + netinfo = &netinfo_start[i]; + if (!netinfo) { + brcmf_err("Invalid netinfo ptr. index: %d\n", + i); + err = -EINVAL; + goto out_err; } - set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - cfg->escan_info.run = brcmf_run_escan; - err = brcmf_do_escan(ifp, request); - if (err) { - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n", + netinfo->SSID, netinfo->channel); + err = brcmf_internal_escan_add_info(request, + netinfo->SSID, + netinfo->SSID_len, + netinfo->channel); + if (err) goto out_err; - } - cfg->sched_escan = true; - cfg->scan_request = request; - } else { - brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); - goto out_err; } - kfree(ssid); - kfree(channel); - kfree(request); - return 0; + err = brcmf_start_internal_escan(ifp, request); + if (!err) + goto free_req; out_err: - kfree(ssid); - kfree(channel); - kfree(request); cfg80211_sched_scan_stopped(wiphy); +free_req: + kfree(request); return err; } @@ -3405,7 +3432,7 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, brcmf_dbg(SCAN, "enter\n"); brcmf_pno_clean(ifp); - if (cfg->sched_escan) + if (cfg->internal_escan) brcmf_notify_escan_complete(cfg, ifp, true, true); return 0; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 8889832..0c9a708 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -271,7 +271,7 @@ struct brcmf_cfg80211_wowl { * @pub: common driver information. * @channel: current channel. * @active_scan: current scan mode. - * @sched_escan: e-scan for scheduled scan support running. + * @internal_escan: indicates internally initiated e-scan is running. * @ibss_starter: indicates this sta is ibss starter. * @pwr_save: indicate whether dongle to support power save mode. * @dongle_up: indicate whether dongle up or not. @@ -303,7 +303,7 @@ struct brcmf_cfg80211_info { struct brcmf_pub *pub; u32 channel; bool active_scan; - bool sched_escan; + bool internal_escan; bool ibss_starter; bool pwr_save; bool dongle_up; -- cgit v1.1 From fca6cb2f059e51dec3fcf3589a5abbbcce5b4043 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:26 +0000 Subject: brcmfmac: split up brcmf_pno_config() function The brcmf_pno_config() function handles two configurations in firmware. Split it and have caller sort out what is needed. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 11 +++- .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 60 ++++++++++++++++------ .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 17 ++++-- 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index f9b10bb..e77f904 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -42,6 +42,7 @@ #include "common.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 +#define BRCMF_SCHED_SCAN_PERIOD 30 #define WPA_OUI "\x00\x50\xF2" /* WPA OUI */ #define WPA_OUI_TYPE 1 @@ -3396,10 +3397,18 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } /* configure pno */ - ret = brcmf_pno_config(ifp, req); + ret = brcmf_pno_config(ifp, BRCMF_SCHED_SCAN_PERIOD, 0, 0); if (ret < 0) return ret; + /* configure random mac */ + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + ret = brcmf_pno_set_random(ifp, req->mac_addr, + req->mac_addr_mask); + if (ret < 0) + return ret; + } + /* configure each match set */ for (i = 0; i < req->n_match_sets; i++) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index c7967d9..72d9eef 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -23,10 +23,12 @@ #include "fwil_types.h" #define BRCMF_PNO_VERSION 2 -#define BRCMF_PNO_TIME 30 #define BRCMF_PNO_REPEAT 4 #define BRCMF_PNO_FREQ_EXPO_MAX 3 +#define BRCMF_PNO_IMMEDIATE_SCAN_BIT 3 +#define BRCMF_PNO_ENABLE_BD_SCAN_BIT 5 #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 +#define BRCMF_PNO_REPORT_SEPARATELY_BIT 11 #define BRCMF_PNO_SCAN_INCOMPLETE 0 #define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF #define BRCMF_PNO_HIDDEN_BIT 2 @@ -47,42 +49,68 @@ int brcmf_pno_clean(struct brcmf_if *ifp) return ret; } -int brcmf_pno_config(struct brcmf_if *ifp, - struct cfg80211_sched_scan_request *request) +int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, + u32 mscan, u32 bestn) { struct brcmf_pno_param_le pfn_param; - struct brcmf_pno_macaddr_le pfn_mac; + u16 flags; + u32 pfnmem; s32 err; - u8 *mac_mask; - int i; memset(&pfn_param, 0, sizeof(pfn_param)); pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); /* set extra pno params */ - pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); + flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) | + BIT(BRCMF_PNO_REPORT_SEPARATELY_BIT) | + BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); pfn_param.repeat = BRCMF_PNO_REPEAT; pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; /* set up pno scan fr */ - pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); + pfn_param.scan_freq = cpu_to_le32(scan_freq); + + if (mscan) { + pfnmem = bestn; + + /* set bestn in firmware */ + err = brcmf_fil_iovar_int_set(ifp, "pfnmem", pfnmem); + if (err < 0) { + brcmf_err("failed to set pfnmem\n"); + goto exit; + } + /* get max mscan which the firmware supports */ + err = brcmf_fil_iovar_int_get(ifp, "pfnmem", &pfnmem); + if (err < 0) { + brcmf_err("failed to get pfnmem\n"); + goto exit; + } + mscan = min_t(u32, mscan, pfnmem); + pfn_param.mscan = mscan; + pfn_param.bestn = bestn; + flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT); + brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn); + } + pfn_param.flags = cpu_to_le16(flags); err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, sizeof(pfn_param)); - if (err) { + if (err) brcmf_err("pfn_set failed, err=%d\n", err); - return err; - } - /* Find out if mac randomization should be turned on */ - if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) - return 0; +exit: + return err; +} + +int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask) +{ + struct brcmf_pno_macaddr_le pfn_mac; + int err, i; pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; - memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); - mac_mask = request->mac_addr_mask; + memcpy(pfn_mac.mac, mac_addr, ETH_ALEN); for (i = 0; i < ETH_ALEN; i++) { pfn_mac.mac[i] &= mac_mask[i]; pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h index a4a23fc..f2c5a53 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h @@ -30,10 +30,21 @@ int brcmf_pno_clean(struct brcmf_if *ifp); * brcmf_pno_config - configure pno parameters. * * @ifp: interface object used. - * @request: scheduled scan parameters. + * @scan_freq: scan frequency period in seconds. + * @mscan: maximum number of scans stored in firmware. + * @bestn: maximum number of APs per scan stored in firmware. */ -int brcmf_pno_config(struct brcmf_if *ifp, - struct cfg80211_sched_scan_request *request); +int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, + u32 mscan, u32 bestn); + +/** + * brcmf_pno_set_random - setup randomisation mac address for pno. + * + * @ifp: interface object used. + * @mac_addr: MAC address used with randomisation. + * @mac_mask: MAC address mask used for randomisation. + */ +int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask); /** * brcmf_pno_add_ssid - add ssid for pno in firmware. -- cgit v1.1 From 3e48611d31dd333be01576902f2dc11adefc9a06 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:27 +0000 Subject: brcmfmac: move scheduled scan activation to pno source file Rework .sched_scan_start() callback moving actual configuration of the device in pno source file. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 65 +----------- .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 110 +++++++++++++++++---- .../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 29 +----- 3 files changed, 94 insertions(+), 110 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index e77f904..4fbc473 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -42,7 +42,6 @@ #include "common.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 -#define BRCMF_SCHED_SCAN_PERIOD 30 #define WPA_OUI "\x00\x50\xF2" /* WPA OUI */ #define WPA_OUI_TYPE 1 @@ -3342,24 +3341,6 @@ free_req: return err; } -static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, - struct cfg80211_sched_scan_request *req) -{ - int i; - - if (!ssid || !req->ssids || !req->n_ssids) - return false; - - for (i = 0; i < req->n_ssids; i++) { - if (ssid->ssid_len == req->ssids[i].ssid_len) { - if (!strncmp(ssid->ssid, req->ssids[i].ssid, - ssid->ssid_len)) - return true; - } - } - return false; -} - static int brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *ndev, @@ -3367,9 +3348,6 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct cfg80211_ssid *ssid; - int i; - int ret = 0; brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", req->n_match_sets, req->n_ssids); @@ -3389,48 +3367,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, return -EINVAL; } - /* clean up everything */ - ret = brcmf_pno_clean(ifp); - if (ret < 0) { - brcmf_err("failed error=%d\n", ret); - return ret; - } - - /* configure pno */ - ret = brcmf_pno_config(ifp, BRCMF_SCHED_SCAN_PERIOD, 0, 0); - if (ret < 0) - return ret; - - /* configure random mac */ - if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - ret = brcmf_pno_set_random(ifp, req->mac_addr, - req->mac_addr_mask); - if (ret < 0) - return ret; - } - - /* configure each match set */ - for (i = 0; i < req->n_match_sets; i++) { - - ssid = &req->match_sets[i].ssid; - - if (!ssid->ssid_len) { - brcmf_err("skip broadcast ssid\n"); - continue; - } - - ret = brcmf_pno_add_ssid(ifp, ssid, - brcmf_is_ssid_active(ssid, req)); - if (ret < 0) - brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", - ret == 0 ? "set" : "failed", ssid->ssid); - } - /* Enable the PNO */ - ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); - if (ret < 0) - brcmf_err("PNO enable failed!! ret=%d\n", ret); - - return ret; + return brcmf_pno_start_sched_scan(ifp, req); } static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index 72d9eef..d16e89e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -32,25 +32,10 @@ #define BRCMF_PNO_SCAN_INCOMPLETE 0 #define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF #define BRCMF_PNO_HIDDEN_BIT 2 +#define BRCMF_PNO_SCHED_SCAN_PERIOD 30 -int brcmf_pno_clean(struct brcmf_if *ifp) -{ - int ret; - - /* Disable pfn */ - ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0); - if (ret == 0) { - /* clear pfn */ - ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0); - } - if (ret < 0) - brcmf_err("failed code %d\n", ret); - - return ret; -} - -int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, - u32 mscan, u32 bestn) +static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, + u32 mscan, u32 bestn) { struct brcmf_pno_param_le pfn_param; u16 flags; @@ -102,7 +87,8 @@ exit: return err; } -int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask) +static int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, + u8 *mac_mask) { struct brcmf_pno_macaddr_le pfn_mac; int err, i; @@ -128,8 +114,8 @@ int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask) return err; } -int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, - bool active) +static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, + bool active) { struct brcmf_pno_net_param_le pfn; @@ -144,3 +130,85 @@ int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, return brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn)); } +static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, + struct cfg80211_sched_scan_request *req) +{ + int i; + + if (!ssid || !req->ssids || !req->n_ssids) + return false; + + for (i = 0; i < req->n_ssids; i++) { + if (ssid->ssid_len == req->ssids[i].ssid_len) { + if (!strncmp(ssid->ssid, req->ssids[i].ssid, + ssid->ssid_len)) + return true; + } + } + return false; +} + +int brcmf_pno_clean(struct brcmf_if *ifp) +{ + int ret; + + /* Disable pfn */ + ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0); + if (ret == 0) { + /* clear pfn */ + ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0); + } + if (ret < 0) + brcmf_err("failed code %d\n", ret); + + return ret; +} + +int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *req) +{ + struct cfg80211_ssid *ssid; + int i, ret; + + /* clean up everything */ + ret = brcmf_pno_clean(ifp); + if (ret < 0) { + brcmf_err("failed error=%d\n", ret); + return ret; + } + + /* configure pno */ + ret = brcmf_pno_config(ifp, BRCMF_PNO_SCHED_SCAN_PERIOD, 0, 0); + if (ret < 0) + return ret; + + /* configure random mac */ + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + ret = brcmf_pno_set_random(ifp, req->mac_addr, + req->mac_addr_mask); + if (ret < 0) + return ret; + } + + /* configure each match set */ + for (i = 0; i < req->n_match_sets; i++) { + ssid = &req->match_sets[i].ssid; + if (!ssid->ssid_len) { + brcmf_err("skip broadcast ssid\n"); + continue; + } + + ret = brcmf_pno_add_ssid(ifp, ssid, + brcmf_is_ssid_active(ssid, req)); + if (ret < 0) + brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", + ret == 0 ? "set" : "failed", ssid->ssid); + } + /* Enable the PNO */ + ret = brcmf_fil_iovar_int_set(ifp, "pfn", 1); + if (ret < 0) + brcmf_err("PNO enable failed!! ret=%d\n", ret); + + return ret; +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h index f2c5a53..5803a4c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h @@ -27,33 +27,12 @@ int brcmf_pno_clean(struct brcmf_if *ifp); /** - * brcmf_pno_config - configure pno parameters. + * brcmf_pno_start_sched_scan - initiate scheduled scan on device. * * @ifp: interface object used. - * @scan_freq: scan frequency period in seconds. - * @mscan: maximum number of scans stored in firmware. - * @bestn: maximum number of APs per scan stored in firmware. + * @req: configuration parameters for scheduled scan. */ -int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, - u32 mscan, u32 bestn); - -/** - * brcmf_pno_set_random - setup randomisation mac address for pno. - * - * @ifp: interface object used. - * @mac_addr: MAC address used with randomisation. - * @mac_mask: MAC address mask used for randomisation. - */ -int brcmf_pno_set_random(struct brcmf_if *ifp, u8 *mac_addr, u8 *mac_mask); - -/** - * brcmf_pno_add_ssid - add ssid for pno in firmware. - * - * @ifp: interface object used. - * @ssid: ssid information. - * @active: indicate this ssid needs to be actively probed. - */ -int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, - bool active); +int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *req); #endif /* _BRCMF_PNO_H */ -- cgit v1.1 From 331e789443618ca9cc3ed48ada4e670225cca036 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:28 +0000 Subject: brcmfmac: use provided channels for scheduled scan User-space can provide list of channels in the schedule scan request. This was ignored so all channels supported and allowed by the device were used. This patch configures the device to use the channels as listed in the request. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 16 +++++++++++++ .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 26 +++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index a4118c0..64aed2d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -131,6 +131,7 @@ #define BRCMF_TXBF_MU_BFR_CAP BIT(1) #define BRCMF_MAXPMKID 16 /* max # PMKID cache entries */ +#define BRCMF_NUMCHANNELS 64 #define BRCMF_PFN_MACADDR_CFG_VER 1 #define BRCMF_PFN_MAC_OUI_ONLY BIT(0) @@ -719,6 +720,21 @@ struct brcmf_pno_param_le { }; /** + * struct brcmf_pno_config_le - PNO channel configuration. + * + * @reporttype: determines what is reported. + * @channel_num: number of channels specified in @channel_list. + * @channel_list: channels to use in PNO scan. + * @flags: reserved. + */ +struct brcmf_pno_config_le { + __le32 reporttype; + __le32 channel_num; + __le16 channel_list[BRCMF_NUMCHANNELS]; + __le32 flags; +}; + +/** * struct brcmf_pno_net_param_le - scan parameters per preferred network. * * @ssid: ssid name and its length. diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index d16e89e..bc33bdc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -18,9 +18,10 @@ #include "core.h" #include "debug.h" -#include "pno.h" #include "fwil.h" #include "fwil_types.h" +#include "cfg80211.h" +#include "pno.h" #define BRCMF_PNO_VERSION 2 #define BRCMF_PNO_REPEAT 4 @@ -34,6 +35,15 @@ #define BRCMF_PNO_HIDDEN_BIT 2 #define BRCMF_PNO_SCHED_SCAN_PERIOD 30 +static int brcmf_pno_channel_config(struct brcmf_if *ifp, + struct brcmf_pno_config_le *cfg) +{ + cfg->reporttype = 0; + cfg->flags = 0; + + return brcmf_fil_iovar_data_set(ifp, "pfn_cfg", cfg, sizeof(*cfg)); +} + static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, u32 mscan, u32 bestn) { @@ -167,7 +177,10 @@ int brcmf_pno_clean(struct brcmf_if *ifp) int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, struct cfg80211_sched_scan_request *req) { + struct brcmu_d11inf *d11inf; + struct brcmf_pno_config_le pno_cfg; struct cfg80211_ssid *ssid; + u16 chan; int i, ret; /* clean up everything */ @@ -190,6 +203,17 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, return ret; } + /* configure channels to use */ + d11inf = &ifp->drvr->config->d11inf; + for (i = 0; i < req->n_channels; i++) { + chan = req->channels[i]->hw_value; + pno_cfg.channel_list[i] = cpu_to_le16(chan); + } + if (req->n_channels) { + pno_cfg.channel_num = cpu_to_le32(req->n_channels); + brcmf_pno_channel_config(ifp, &pno_cfg); + } + /* configure each match set */ for (i = 0; i < req->n_match_sets; i++) { ssid = &req->match_sets[i].ssid; -- cgit v1.1 From dfe5b0d52d5880bd9d4b427e1a53c9e9e4c3c820 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:29 +0000 Subject: brcmfmac: remove restriction from .sched_scan_start() callback In the .sched_scan_start() callback a condition was checked whether a normal scan was ongoing. However, there is no need for this check as it is ok to start the scheduled scan irrespective whether or not a normal scan is ongoing. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 4fbc473..75ad8b3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3351,10 +3351,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", req->n_match_sets, req->n_ssids); - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } + if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { brcmf_err("Scanning suppressed: status (%lu)\n", cfg->scan_status); -- cgit v1.1 From c6989fd55ceb633d2f18c12ffae01b9123125c89 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:30 +0000 Subject: brcmfmac: use requested scan interval in scheduled scan User-space can specify the interval for the scheduled scan. This interval is found in scheduled scan plan. The driver supports only one plan, which is legacy behaviour. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 6 +++++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 6 ++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 75ad8b3..cc12b14 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6312,6 +6312,7 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD; wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index bc33bdc..f273cab 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -63,6 +63,10 @@ static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq, pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; /* set up pno scan fr */ + if (scan_freq < BRCMF_PNO_SCHED_SCAN_MIN_PERIOD) { + brcmf_dbg(SCAN, "scan period too small, using minimum\n"); + scan_freq = BRCMF_PNO_SCHED_SCAN_MIN_PERIOD; + } pfn_param.scan_freq = cpu_to_le32(scan_freq); if (mscan) { @@ -191,7 +195,7 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, } /* configure pno */ - ret = brcmf_pno_config(ifp, BRCMF_PNO_SCHED_SCAN_PERIOD, 0, 0); + ret = brcmf_pno_config(ifp, req->scan_plans[0].interval, 0, 0); if (ret < 0) return ret; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h index 5803a4c..bae55b2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h @@ -16,8 +16,10 @@ #ifndef _BRCMF_PNO_H #define _BRCMF_PNO_H -#define BRCMF_PNO_SCAN_COMPLETE 1 -#define BRCMF_PNO_MAX_PFN_COUNT 16 +#define BRCMF_PNO_SCAN_COMPLETE 1 +#define BRCMF_PNO_MAX_PFN_COUNT 16 +#define BRCMF_PNO_SCHED_SCAN_MIN_PERIOD 10 +#define BRCMF_PNO_SCHED_SCAN_MAX_PERIOD 508 /** * brcmf_pno_clean - disable and clear pno in firmware. -- cgit v1.1 From 53e3a80d80c80bf50ab64cf6c44fb0fa41aa22d8 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Wed, 23 Nov 2016 10:25:31 +0000 Subject: brcmfmac: fix scheduled scan result handling for newer chips The scan results for scheduled scan as retrieved from the device have changed. A field has been added which is not needed. However, the appended info is. Luckily they are versioned so check that to find out the location of the appended data. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 24 +++++++++++++++++++++- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 7 +++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index cc12b14..f28f03a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3257,6 +3257,28 @@ static int brcmf_start_internal_escan(struct brcmf_if *ifp, return 0; } +static struct brcmf_pno_net_info_le * +brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1) +{ + struct brcmf_pno_scanresults_v2_le *pfn_v2; + struct brcmf_pno_net_info_le *netinfo; + + switch (pfn_v1->version) { + default: + WARN_ON(1); + /* fall-thru */ + case cpu_to_le32(1): + netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1); + break; + case cpu_to_le32(2): + pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1; + netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1); + break; + } + + return netinfo; +} + /* PFN result doesn't have all the info which are required by the supplicant * (For e.g IEs) Do a target Escan so that sched scan results are reported * via wl_inform_single_bss in the required format. Escan does require the @@ -3309,7 +3331,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, } data += sizeof(struct brcmf_pno_scanresults_le); - netinfo_start = (struct brcmf_pno_net_info_le *)data; + netinfo_start = brcmf_get_netinfo_array(pfn_result); for (i = 0; i < result_count; i++) { netinfo = &netinfo_start[i]; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 64aed2d..9a1eb5a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -785,6 +785,13 @@ struct brcmf_pno_scanresults_le { __le32 count; }; +struct brcmf_pno_scanresults_v2_le { + __le32 version; + __le32 status; + __le32 count; + __le32 scan_ch_bucket; +}; + /** * struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization. * -- cgit v1.1 From 05db221e30a924f5f80cbef34eb9f210522ceb84 Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Fri, 25 Nov 2016 03:13:34 -0800 Subject: mt7601u: wait for clear rxq when stopping mac mt7601u_mac_stop_hw should stop polling the rxq once it remains empty but instead continues polling after the rxq status stays clear; bringing down the interface takes about six seconds from this alone. Speed up path by exiting rxq loop once status repeatedly polls empty. Signed-off-by: Anthony Romano Reviewed-by: Jakub Kicinski Signed-off-by: Kalle Valo --- drivers/net/wireless/mediatek/mt7601u/init.c | 14 +++++++------- drivers/net/wireless/mediatek/mt7601u/regs.h | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c index 44d46e2..a6e9017 100644 --- a/drivers/net/wireless/mediatek/mt7601u/init.c +++ b/drivers/net/wireless/mediatek/mt7601u/init.c @@ -293,13 +293,13 @@ static void mt7601u_mac_stop_hw(struct mt7601u_dev *dev) ok = 0; i = 200; while (i--) { - if ((mt76_rr(dev, 0x0430) & 0x00ff0000) || - (mt76_rr(dev, 0x0a30) & 0xffffffff) || - (mt76_rr(dev, 0x0a34) & 0xffffffff)) - ok++; - if (ok > 6) - break; - + if (!(mt76_rr(dev, MT_RXQ_STA) & 0x00ff0000) && + !mt76_rr(dev, 0x0a30) && + !mt76_rr(dev, 0x0a34)) { + if (ok++ > 5) + break; + continue; + } msleep(1); } diff --git a/drivers/net/wireless/mediatek/mt7601u/regs.h b/drivers/net/wireless/mediatek/mt7601u/regs.h index 27a429d..2a88370 100644 --- a/drivers/net/wireless/mediatek/mt7601u/regs.h +++ b/drivers/net/wireless/mediatek/mt7601u/regs.h @@ -192,6 +192,9 @@ #define MT_BCN_OFFSET_BASE 0x041c #define MT_BCN_OFFSET(_n) (MT_BCN_OFFSET_BASE + ((_n) << 2)) +#define MT_RXQ_STA 0x0430 +#define MT_TXQ_STA 0x0434 + #define MT_RF_CSR_CFG 0x0500 #define MT_RF_CSR_CFG_DATA GENMASK(7, 0) #define MT_RF_CSR_CFG_REG_ID GENMASK(13, 8) -- cgit v1.1 From ba9f93f82abafe2552eac942ebb11c2df4f8dd7f Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 26 Nov 2016 14:43:35 -0600 Subject: rtlwifi: Fix enter/exit power_save In commit a5ffbe0a1993 ("rtlwifi: Fix scheduling while atomic bug") and commit a269913c52ad ("rtlwifi: Rework rtl_lps_leave() and rtl_lps_enter() to use work queue"), an error was introduced in the power-save routines due to the fact that leaving PS was delayed by the use of a work queue. This problem is fixed by detecting if the enter or leave routines are in interrupt mode. If so, the workqueue is used to place the request. If in normal mode, the enter or leave routines are called directly. Fixes: a269913c52ad ("rtlwifi: Rework rtl_lps_leave() and rtl_lps_enter() to use work queue") Reported-by: Ping-Ke Shih Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/base.c | 8 +++---- drivers/net/wireless/realtek/rtlwifi/core.c | 9 +++----- drivers/net/wireless/realtek/rtlwifi/pci.c | 14 ++++------- drivers/net/wireless/realtek/rtlwifi/ps.c | 36 ++++++++++++++++++++++------- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 264466f..4ac928b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc); static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc) { + struct ieee80211_hw *hw = rtlpriv->hw; + rtlpriv->ra.is_special_data = true; if (rtlpriv->cfg->ops->get_btc_status()) rtlpriv->btcoexist.btc_ops->btc_special_packet_notify( rtlpriv, 1); - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } @@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx, if (is_tx) { rtlpriv->ra.is_special_data = true; - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index a11e8f5..f90ff0a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1150,10 +1150,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, } else { mstatus = RT_MEDIA_DISCONNECT; - if (mac->link_state == MAC80211_LINKED) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + if (mac->link_state == MAC80211_LINKED) + rtl_lps_leave(hw); if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE) rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); mac->link_state = MAC80211_NOLINK; @@ -1431,8 +1429,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw, } if (mac->link_state == MAC80211_LINKED) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); mac->link_state = MAC80211_LINKED_SCANNING; } else { rtl_ips_nic_on(hw); diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index d044b23..8bfe020 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -659,11 +659,9 @@ tx_status_ok: } if (((rtlpriv->link_info.num_rx_inperiod + - rtlpriv->link_info.num_tx_inperiod) > 8) || - (rtlpriv->link_info.num_rx_inperiod > 2)) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + rtlpriv->link_info.num_tx_inperiod) > 8) || + (rtlpriv->link_info.num_rx_inperiod > 2)) + rtl_lps_leave(hw); } static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, @@ -914,10 +912,8 @@ new_trx_end: } if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || - (rtlpriv->link_info.num_rx_inperiod > 2)) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + (rtlpriv->link_info.num_rx_inperiod > 2)) + rtl_lps_leave(hw); skb = new_skb; no_new: if (rtlpriv->use_new_trx_flow) { diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c index 18d979a..d0ffc4d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/ps.c +++ b/drivers/net/wireless/realtek/rtlwifi/ps.c @@ -407,8 +407,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) } } -/*Enter the leisure power save mode.*/ -void rtl_lps_enter(struct ieee80211_hw *hw) +/* Interrupt safe routine to enter the leisure power save mode.*/ +static void rtl_lps_enter_core(struct ieee80211_hw *hw) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); @@ -444,10 +444,9 @@ void rtl_lps_enter(struct ieee80211_hw *hw) spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); } -EXPORT_SYMBOL(rtl_lps_enter); -/*Leave the leisure power save mode.*/ -void rtl_lps_leave(struct ieee80211_hw *hw) +/* Interrupt safe routine to leave the leisure power save mode.*/ +static void rtl_lps_leave_core(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); @@ -477,7 +476,6 @@ void rtl_lps_leave(struct ieee80211_hw *hw) } spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); } -EXPORT_SYMBOL(rtl_lps_leave); /* For sw LPS*/ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) @@ -670,12 +668,34 @@ void rtl_lps_change_work_callback(struct work_struct *work) struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->enter_ps) - rtl_lps_enter(hw); + rtl_lps_enter_core(hw); else - rtl_lps_leave(hw); + rtl_lps_leave_core(hw); } EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback); +void rtl_lps_enter(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (!in_interrupt()) + return rtl_lps_enter_core(hw); + rtlpriv->enter_ps = true; + schedule_work(&rtlpriv->works.lps_change_work); +} +EXPORT_SYMBOL_GPL(rtl_lps_enter); + +void rtl_lps_leave(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (!in_interrupt()) + return rtl_lps_leave_core(hw); + rtlpriv->enter_ps = false; + schedule_work(&rtlpriv->works.lps_change_work); +} +EXPORT_SYMBOL_GPL(rtl_lps_leave); + void rtl_swlps_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, -- cgit v1.1 From 59391a96dc731ff87f824d03c81bb2a9668ef66a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Nov 2016 08:57:12 +0100 Subject: bcma: add Dell Inspiron 3148 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is what is in the laptop: 01:00.0 Network controller [0280]: Broadcom Limited BCM43142 802.11b/g/n [14e4:4365] (rev 01) Subsystem: Dell Device [1028:0018] Flags: bus master, fast devsel, latency 0, IRQ 18 Memory at b0400000 (64-bit, non-prefetchable) [size=32K] Capabilities: [40] Power Management version 3 Capabilities: [58] Vendor Specific Information: Len=78 Capabilities: [48] MSI: Enable- Count=1/1 Maskable- 64bit+ Capabilities: [d0] Express Endpoint, MSI 00 Capabilities: [100] Advanced Error Reporting Capabilities: [13c] Virtual Channel Capabilities: [160] Device Serial Number 00-00-9a-ff-ff-f3-40-b8 Capabilities: [16c] Power Budgeting With the patch, I can see: bcma: bus0: Found chip with id 43142, rev 0x01 and package 0x08 bcma: bus0: Core 0 found: ChipCommon (manuf 0x4BF, id 0x800, rev 0x28, class 0x0) bcma: bus0: Core 1 found: IEEE 802.11 (manuf 0x4BF, id 0x812, rev 0x21, class 0x0) bcma: bus0: Core 2 found: PCIe (manuf 0x4BF, id 0x820, rev 0x16, class 0x0) bcma: bus0: Core 3 found: UNKNOWN (manuf 0x43B, id 0x368, rev 0x00, class 0x0) bcma: bus0: Bus registered The wifi is not currently supported by brcmsmac yet: brcmsmac bcma1:1: mfg 4bf core 812 rev 33 class 0 irq 18 brcmsmac: unknown device id 4365 So don't expect a working wifi from this patch :). Signed-off-by: Jiri Slaby Cc: RafaÅ‚ MiÅ‚ecki Cc: Signed-off-by: Kalle Valo --- drivers/bcma/host_pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index bd46569..9258429 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -295,6 +295,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4360) }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0016) }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0018) }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_FOXCONN, 0xe092) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) }, -- cgit v1.1 From 40931b85113dad7881d49e8759e5ad41d30a5e6c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 25 Nov 2016 07:46:20 -0800 Subject: mlx4: give precise rx/tx bytes/packets counters mlx4 stats are chaotic because a deferred work queue is responsible to update them every 250 ms. Even sampling stats every one second with "sar -n DEV 1" gives variations like the following : lpaa23:~# sar -n DEV 1 10 | grep eth0 | cut -c1-65 07:39:22 eth0 146877.00 3265554.00 9467.15 4828168.50 07:39:23 eth0 146587.00 3260329.00 9448.15 4820445.98 07:39:24 eth0 146894.00 3259989.00 9468.55 4819943.26 07:39:25 eth0 110368.00 2454497.00 7113.95 3629012.17 <<>> 07:39:26 eth0 146563.00 3257502.00 9447.25 4816266.23 07:39:27 eth0 145678.00 3258292.00 9389.79 4817414.39 07:39:28 eth0 145268.00 3253171.00 9363.85 4809852.46 07:39:29 eth0 146439.00 3262185.00 9438.97 4823172.48 07:39:30 eth0 146758.00 3264175.00 9459.94 4826124.13 07:39:31 eth0 146843.00 3256903.00 9465.44 4815381.97 Average: eth0 142827.50 3179259.70 9206.30 4700578.16 This patch allows rx/tx bytes/packets counters being folded at the time we need stats. We now can fetch stats every 1 ms if we want to check NIC behavior on a small time window. It is also easier to detect anomalies. lpaa23:~# sar -n DEV 1 10 | grep eth0 | cut -c1-65 07:42:50 eth0 142915.00 3177696.00 9212.06 4698270.42 07:42:51 eth0 143741.00 3200232.00 9265.15 4731593.02 07:42:52 eth0 142781.00 3171600.00 9202.92 4689260.16 07:42:53 eth0 143835.00 3192932.00 9271.80 4720761.39 07:42:54 eth0 141922.00 3165174.00 9147.64 4679759.21 07:42:55 eth0 142993.00 3207038.00 9216.78 4741653.05 07:42:56 eth0 141394.06 3154335.64 9113.85 4663731.73 07:42:57 eth0 141850.00 3161202.00 9144.48 4673866.07 07:42:58 eth0 143439.00 3180736.00 9246.05 4702755.35 07:42:59 eth0 143501.00 3210992.00 9249.99 4747501.84 Average: eth0 142835.66 3182165.93 9206.98 4704874.08 Signed-off-by: Eric Dumazet Cc: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 2 + drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 1 + drivers/net/ethernet/mellanox/mlx4/en_port.c | 77 +++++++++++++++++-------- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 1 + 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 487a58f..d9c9f86 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -367,6 +367,8 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, spin_lock_bh(&priv->stats_lock); + mlx4_en_fold_software_stats(dev); + for (i = 0; i < NUM_MAIN_STATS; i++, bitmap_iterator_inc(&it)) if (bitmap_iterator_test(&it)) data[index++] = ((unsigned long *)&dev->stats)[i]; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index ea76b28..12ea3405 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1324,6 +1324,7 @@ mlx4_en_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) struct mlx4_en_priv *priv = netdev_priv(dev); spin_lock_bh(&priv->stats_lock); + mlx4_en_fold_software_stats(dev); netdev_stats_to_stats64(stats, &dev->stats); spin_unlock_bh(&priv->stats_lock); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c index 1eb4c1e..c6c4f12 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c @@ -147,6 +147,39 @@ static unsigned long en_stats_adder(__be64 *start, __be64 *next, int num) return ret; } +void mlx4_en_fold_software_stats(struct net_device *dev) +{ + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = priv->mdev; + unsigned long packets, bytes; + int i; + + if (mlx4_is_master(mdev->dev)) + return; + + packets = 0; + bytes = 0; + for (i = 0; i < priv->rx_ring_num; i++) { + const struct mlx4_en_rx_ring *ring = priv->rx_ring[i]; + + packets += READ_ONCE(ring->packets); + bytes += READ_ONCE(ring->bytes); + } + dev->stats.rx_packets = packets; + dev->stats.rx_bytes = bytes; + + packets = 0; + bytes = 0; + for (i = 0; i < priv->tx_ring_num[TX]; i++) { + const struct mlx4_en_tx_ring *ring = priv->tx_ring[TX][i]; + + packets += READ_ONCE(ring->packets); + bytes += READ_ONCE(ring->bytes); + } + dev->stats.tx_packets = packets; + dev->stats.tx_bytes = bytes; +} + int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) { struct mlx4_counter tmp_counter_stats; @@ -159,6 +192,7 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) u64 in_mod = reset << 8 | port; int err; int i, counter_index; + unsigned long sw_tx_dropped = 0; unsigned long sw_rx_dropped = 0; mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); @@ -174,8 +208,8 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) spin_lock_bh(&priv->stats_lock); - stats->rx_packets = 0; - stats->rx_bytes = 0; + mlx4_en_fold_software_stats(dev); + priv->port_stats.rx_chksum_good = 0; priv->port_stats.rx_chksum_none = 0; priv->port_stats.rx_chksum_complete = 0; @@ -183,19 +217,16 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->xdp_stats.rx_xdp_tx = 0; priv->xdp_stats.rx_xdp_tx_full = 0; for (i = 0; i < priv->rx_ring_num; i++) { - stats->rx_packets += priv->rx_ring[i]->packets; - stats->rx_bytes += priv->rx_ring[i]->bytes; - sw_rx_dropped += priv->rx_ring[i]->dropped; - priv->port_stats.rx_chksum_good += priv->rx_ring[i]->csum_ok; - priv->port_stats.rx_chksum_none += priv->rx_ring[i]->csum_none; - priv->port_stats.rx_chksum_complete += priv->rx_ring[i]->csum_complete; - priv->xdp_stats.rx_xdp_drop += priv->rx_ring[i]->xdp_drop; - priv->xdp_stats.rx_xdp_tx += priv->rx_ring[i]->xdp_tx; - priv->xdp_stats.rx_xdp_tx_full += priv->rx_ring[i]->xdp_tx_full; + const struct mlx4_en_rx_ring *ring = priv->rx_ring[i]; + + sw_rx_dropped += READ_ONCE(ring->dropped); + priv->port_stats.rx_chksum_good += READ_ONCE(ring->csum_ok); + priv->port_stats.rx_chksum_none += READ_ONCE(ring->csum_none); + priv->port_stats.rx_chksum_complete += READ_ONCE(ring->csum_complete); + priv->xdp_stats.rx_xdp_drop += READ_ONCE(ring->xdp_drop); + priv->xdp_stats.rx_xdp_tx += READ_ONCE(ring->xdp_tx); + priv->xdp_stats.rx_xdp_tx_full += READ_ONCE(ring->xdp_tx_full); } - stats->tx_packets = 0; - stats->tx_bytes = 0; - stats->tx_dropped = 0; priv->port_stats.tx_chksum_offload = 0; priv->port_stats.queue_stopped = 0; priv->port_stats.wake_queue = 0; @@ -205,15 +236,14 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) for (i = 0; i < priv->tx_ring_num[TX]; i++) { const struct mlx4_en_tx_ring *ring = priv->tx_ring[TX][i]; - stats->tx_packets += ring->packets; - stats->tx_bytes += ring->bytes; - stats->tx_dropped += ring->tx_dropped; - priv->port_stats.tx_chksum_offload += ring->tx_csum; - priv->port_stats.queue_stopped += ring->queue_stopped; - priv->port_stats.wake_queue += ring->wake_queue; - priv->port_stats.tso_packets += ring->tso_packets; - priv->port_stats.xmit_more += ring->xmit_more; + sw_tx_dropped += READ_ONCE(ring->tx_dropped); + priv->port_stats.tx_chksum_offload += READ_ONCE(ring->tx_csum); + priv->port_stats.queue_stopped += READ_ONCE(ring->queue_stopped); + priv->port_stats.wake_queue += READ_ONCE(ring->wake_queue); + priv->port_stats.tso_packets += READ_ONCE(ring->tso_packets); + priv->port_stats.xmit_more += READ_ONCE(ring->xmit_more); } + if (mlx4_is_master(mdev->dev)) { stats->rx_packets = en_stats_adder(&mlx4_en_stats->RTOT_prio_0, &mlx4_en_stats->RTOT_prio_1, @@ -251,7 +281,8 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) stats->rx_length_errors = be32_to_cpu(mlx4_en_stats->RdropLength); stats->rx_crc_errors = be32_to_cpu(mlx4_en_stats->RCRC); stats->rx_fifo_errors = be32_to_cpu(mlx4_en_stats->RdropOvflw); - stats->tx_dropped += be32_to_cpu(mlx4_en_stats->TDROP); + stats->tx_dropped = be32_to_cpu(mlx4_en_stats->TDROP) + + sw_tx_dropped; /* RX stats */ priv->pkstats.rx_multicast_packets = stats->multicast; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 574bcbb..20a9364 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -755,6 +755,7 @@ void mlx4_en_rx_irq(struct mlx4_cq *mcq); int mlx4_SET_MCAST_FLTR(struct mlx4_dev *dev, u8 port, u64 mac, u64 clear, u8 mode); int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv); +void mlx4_en_fold_software_stats(struct net_device *dev); int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset); int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port); -- cgit v1.1 From b26bff6e52d8b66045b94f2d06f8a349224a55d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Sun, 27 Nov 2016 23:59:51 +0100 Subject: MAINTAINERS: Add device tree bindings to mv88e6xx section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also include the netdev list for convenience, as done elsewhere. Reviewed-by: Andrew Lunn Cc: Vivien Didelot Signed-off-by: Andreas Färber Signed-off-by: David S. Miller --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5196538..3348d0e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7567,8 +7567,10 @@ S: Maintained MARVELL 88E6XXX ETHERNET SWITCH FABRIC DRIVER M: Andrew Lunn M: Vivien Didelot +L: netdev@vger.kernel.org S: Maintained F: drivers/net/dsa/mv88e6xxx/ +F: Documentation/devicetree/bindings/net/dsa/marvell.txt MARVELL ARMADA DRM SUPPORT M: Russell King -- cgit v1.1 From 530742e707190b585f0874876ad1fcac9a0abbcb Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Mon, 28 Nov 2016 07:12:37 +0000 Subject: net: arc_emac: add dependencies on associated arches and compile test Add dependencies on the architectures that support these devices and add compile test to ensure ongoing code build coverage. Signed-off-by: Peter Robinson Signed-off-by: David S. Miller --- drivers/net/ethernet/arc/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig index 6890451..e743ddf 100644 --- a/drivers/net/ethernet/arc/Kconfig +++ b/drivers/net/ethernet/arc/Kconfig @@ -17,13 +17,14 @@ if NET_VENDOR_ARC config ARC_EMAC_CORE tristate + depends on ARC || ARCH_ROCKCHIP || COMPILE_TEST select MII select PHYLIB config ARC_EMAC tristate "ARC EMAC support" select ARC_EMAC_CORE - depends on OF_IRQ && OF_NET && HAS_DMA + depends on OF_IRQ && OF_NET && HAS_DMA && (ARC || COMPILE_TEST) ---help--- On some legacy ARC (Synopsys) FPGA boards such as ARCAngel4/ML50x non-standard on-chip ethernet device ARC EMAC 10/100 is used. @@ -32,7 +33,7 @@ config ARC_EMAC config EMAC_ROCKCHIP tristate "Rockchip EMAC support" select ARC_EMAC_CORE - depends on OF_IRQ && OF_NET && REGULATOR && HAS_DMA + depends on OF_IRQ && OF_NET && REGULATOR && HAS_DMA && (ARCH_ROCKCHIP || COMPILE_TEST) ---help--- Support for Rockchip RK3036/RK3066/RK3188 EMAC ethernet controllers. This selects Rockchip SoC glue layer support for the -- cgit v1.1 From 436feafe95cc2de3e1a56a0679c80291e1776894 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 24 Nov 2016 15:36:33 +0100 Subject: net: stmmac: enable tx queue 0 for gmac4 IPs synthesized with multiple TX queues The dwmac4 IP can synthesized with 1-8 number of tx queues. On an IP synthesized with DWC_EQOS_NUM_TXQ > 1, all txqueues are disabled by default. For these IPs, the bitfield TXQEN is R/W. Always enable tx queue 0. The write will have no effect on IPs synthesized with DWC_EQOS_NUM_TXQ == 1. The driver does still not utilize more than one tx queue in the IP. Signed-off-by: Niklas Cassel Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 3 +++ drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 6f4f5ce..3e8d4fe 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -155,8 +155,11 @@ enum power_event { #define MTL_CHAN_RX_DEBUG(x) (MTL_CHANX_BASE_ADDR(x) + 0x38) #define MTL_OP_MODE_RSF BIT(5) +#define MTL_OP_MODE_TXQEN BIT(3) #define MTL_OP_MODE_TSF BIT(1) +#define MTL_OP_MODE_TQS_MASK GENMASK(24, 16) + #define MTL_OP_MODE_TTC_MASK 0x70 #define MTL_OP_MODE_TTC_SHIFT 4 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 116151c..577316d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -213,7 +213,17 @@ static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode, else mtl_tx_op |= MTL_OP_MODE_TTC_512; } - + /* For an IP with DWC_EQOS_NUM_TXQ == 1, the fields TXQEN and TQS are RO + * with reset values: TXQEN on, TQS == DWC_EQOS_TXFIFO_SIZE. + * For an IP with DWC_EQOS_NUM_TXQ > 1, the fields TXQEN and TQS are R/W + * with reset values: TXQEN off, TQS 256 bytes. + * + * Write the bits in both cases, since it will have no effect when RO. + * For DWC_EQOS_NUM_TXQ > 1, the top bits in MTL_OP_MODE_TQS_MASK might + * be RO, however, writing the whole TQS field will result in a value + * equal to DWC_EQOS_TXFIFO_SIZE, just like for DWC_EQOS_NUM_TXQ == 1. + */ + mtl_tx_op |= MTL_OP_MODE_TXQEN | MTL_OP_MODE_TQS_MASK; writel(mtl_tx_op, ioaddr + MTL_CHAN_TX_OP_MODE(channel)); mtl_rx_op = readl(ioaddr + MTL_CHAN_RX_OP_MODE(channel)); -- cgit v1.1 From d853d145ea3e63387a2ac759aa41d5e43876e561 Mon Sep 17 00:00:00 2001 From: jbrunet Date: Mon, 28 Nov 2016 10:46:46 +0100 Subject: net: phy: add an option to disable EEE advertisement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds an option to disable EEE advertisement in the generic PHY by providing a mask of prohibited modes corresponding to the value found in the MDIO_AN_EEE_ADV register. On some platforms, PHY Low power idle seems to be causing issues, even breaking the link some cases. The patch provides a convenient way for these platforms to disable EEE advertisement and work around the issue. Signed-off-by: Jerome Brunet Tested-by: Yegor Yefremov Tested-by: Andreas Färber Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 3 ++ drivers/net/phy/phy_device.c | 80 +++++++++++++++++++++++++++++++++++++++----- include/linux/phy.h | 3 ++ 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 73adbaa..a3981cc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1396,6 +1396,9 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); + /* Mask prohibited EEE modes */ + val &= ~phydev->eee_broken_modes; + phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val); return 0; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index ba86c19..83e52f1 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1121,6 +1121,43 @@ static int genphy_config_advert(struct phy_device *phydev) } /** + * genphy_config_eee_advert - disable unwanted eee mode advertisement + * @phydev: target phy_device struct + * + * Description: Writes MDIO_AN_EEE_ADV after disabling unsupported energy + * efficent ethernet modes. Returns 0 if the PHY's advertisement hasn't + * changed, and 1 if it has changed. + */ +static int genphy_config_eee_advert(struct phy_device *phydev) +{ + u32 broken = phydev->eee_broken_modes; + u32 old_adv, adv; + + /* Nothing to disable */ + if (!broken) + return 0; + + /* If the following call fails, we assume that EEE is not + * supported by the phy. If we read 0, EEE is not advertised + * In both case, we don't need to continue + */ + adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); + if (adv <= 0) + return 0; + + old_adv = adv; + adv &= ~broken; + + /* Advertising remains unchanged with the broken mask */ + if (old_adv == adv) + return 0; + + phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, adv); + + return 1; +} + +/** * genphy_setup_forced - configures/forces speed/duplex from @phydev * @phydev: target phy_device struct * @@ -1178,15 +1215,20 @@ EXPORT_SYMBOL(genphy_restart_aneg); */ int genphy_config_aneg(struct phy_device *phydev) { - int result; + int err, changed; + + changed = genphy_config_eee_advert(phydev); if (AUTONEG_ENABLE != phydev->autoneg) return genphy_setup_forced(phydev); - result = genphy_config_advert(phydev); - if (result < 0) /* error */ - return result; - if (result == 0) { + err = genphy_config_advert(phydev); + if (err < 0) /* error */ + return err; + + changed |= err; + + if (changed == 0) { /* Advertisement hasn't changed, but maybe aneg was never on to * begin with? Or maybe phy was isolated? */ @@ -1196,16 +1238,16 @@ int genphy_config_aneg(struct phy_device *phydev) return ctl; if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) - result = 1; /* do restart aneg */ + changed = 1; /* do restart aneg */ } /* Only restart aneg if we are advertising something different * than we were before. */ - if (result > 0) - result = genphy_restart_aneg(phydev); + if (changed > 0) + return genphy_restart_aneg(phydev); - return result; + return 0; } EXPORT_SYMBOL(genphy_config_aneg); @@ -1563,6 +1605,21 @@ static void of_set_phy_supported(struct phy_device *phydev) __set_phy_supported(phydev, max_speed); } +static void of_set_phy_eee_broken(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + u32 broken; + + if (!IS_ENABLED(CONFIG_OF_MDIO)) + return; + + if (!node) + return; + + if (!of_property_read_u32(node, "eee-broken-modes", &broken)) + phydev->eee_broken_modes = broken; +} + /** * phy_probe - probe and init a PHY device * @dev: device to probe and init @@ -1600,6 +1657,11 @@ static int phy_probe(struct device *dev) of_set_phy_supported(phydev); phydev->advertising = phydev->supported; + /* Get the EEE modes we want to prohibit. We will ask + * the PHY stop advertising these mode later on + */ + of_set_phy_eee_broken(phydev); + /* Set the state to READY by default */ phydev->state = PHY_READY; diff --git a/include/linux/phy.h b/include/linux/phy.h index edde28c..b53177f 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -417,6 +417,9 @@ struct phy_device { u32 advertising; u32 lp_advertising; + /* Energy efficient ethernet modes which should be prohibited */ + u32 eee_broken_modes; + int autoneg; int link_timeout; -- cgit v1.1 From 1fc31357ad194fb98691f3d122bcd47e59239e83 Mon Sep 17 00:00:00 2001 From: jbrunet Date: Mon, 28 Nov 2016 10:46:47 +0100 Subject: dt-bindings: net: add EEE capability constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jerome Brunet Tested-by: Yegor Yefremov Tested-by: Andreas Färber Signed-off-by: David S. Miller --- include/dt-bindings/net/mdio.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 include/dt-bindings/net/mdio.h diff --git a/include/dt-bindings/net/mdio.h b/include/dt-bindings/net/mdio.h new file mode 100644 index 0000000..99c6d90 --- /dev/null +++ b/include/dt-bindings/net/mdio.h @@ -0,0 +1,19 @@ +/* + * This header provides generic constants for ethernet MDIO bindings + */ + +#ifndef _DT_BINDINGS_NET_MDIO_H +#define _DT_BINDINGS_NET_MDIO_H + +/* + * EEE capability Advertisement + */ + +#define MDIO_EEE_100TX 0x0002 /* 100TX EEE cap */ +#define MDIO_EEE_1000T 0x0004 /* 1000T EEE cap */ +#define MDIO_EEE_10GT 0x0008 /* 10GT EEE cap */ +#define MDIO_EEE_1000KX 0x0010 /* 1000KX EEE cap */ +#define MDIO_EEE_10GKX4 0x0020 /* 10G KX4 EEE cap */ +#define MDIO_EEE_10GKR 0x0040 /* 10G KR EEE cap */ + +#endif -- cgit v1.1 From c44a3bc142f35a5857ec9a1367789a5f3b60aa6d Mon Sep 17 00:00:00 2001 From: jbrunet Date: Mon, 28 Nov 2016 10:46:48 +0100 Subject: dt: bindings: add ethernet phy eee-broken-modes option documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jerome Brunet Reviewed-by: Andreas Färber Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/phy.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt index 4627da3..54749b6 100644 --- a/Documentation/devicetree/bindings/net/phy.txt +++ b/Documentation/devicetree/bindings/net/phy.txt @@ -38,6 +38,8 @@ Optional Properties: - enet-phy-lane-swap: If set, indicates the PHY will swap the TX/RX lanes to compensate for the board being designed with the lanes swapped. +- eee-broken-modes: Bits to clear in the MDIO_AN_EEE_ADV register to + disable EEE broken modes. Example: -- cgit v1.1 From 01ae87eab53675cbdabd5c4d727c4a35e397cce0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 28 Nov 2016 14:11:04 +0100 Subject: bpf: cgroup: fix documentation of __cgroup_bpf_update() There's a 'not' missing in one paragraph. Add it. Fixes: 3007098494be ("cgroup: add support for eBPF programs") Signed-off-by: Daniel Mack Reported-by: Rami Rosen Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/cgroup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index a0ab43f..8c784f8 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -66,8 +66,8 @@ void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent) * Each cgroup has a set of two pointers for bpf programs; one for eBPF * programs it owns, and which is effective for execution. * - * If @prog is %NULL, this function attaches a new program to the cgroup and - * releases the one that is currently attached, if any. @prog is then made + * If @prog is not %NULL, this function attaches a new program to the cgroup + * and releases the one that is currently attached, if any. @prog is then made * the effective program of type @type in that cgroup. * * If @prog is %NULL, the currently attached program of type @type is released, -- cgit v1.1 From 22d3efe5f62e048eb2c85b9ef24e3b7af545a458 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 28 Nov 2016 12:55:59 +0100 Subject: stmmac: fix comments, make debug output consistent Fix comments, add some new, and make debugfs output consistent. Signed-off-by: Pavel Machek Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 1 + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 6d2de4e..5bd4c05 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -44,6 +44,7 @@ #define DWMAC_CORE_4_00 0x40 #define STMMAC_CHAN0 0 /* Always supported and default for all chips */ +/* These need to be power of two, and >= 4 */ #define DMA_TX_SIZE 512 #define DMA_RX_SIZE 512 #define STMMAC_GET_ENTRY(x, size) ((x + 1) & (size - 1)) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 29557d2..48a4e84 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -105,8 +105,8 @@ module_param(eee_timer, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec"); #define STMMAC_LPI_T(x) (jiffies + msecs_to_jiffies(x)) -/* By default the driver will use the ring mode to manage tx and rx descriptors - * but passing this value so user can force to use the chain instead of the ring +/* By default the driver will use the ring mode to manage tx and rx descriptors, + * but allow user to force to use the chain instead of the ring */ static unsigned int chain_mode; module_param(chain_mode, int, S_IRUGO); @@ -2960,6 +2960,8 @@ static int stmmac_sysfs_ring_open(struct inode *inode, struct file *file) return single_open(file, stmmac_sysfs_ring_read, inode->i_private); } +/* Debugfs files, should appear in /sys/kernel/debug/stmmaceth/eth0 */ + static const struct file_operations stmmac_rings_status_fops = { .owner = THIS_MODULE, .open = stmmac_sysfs_ring_open, @@ -2982,11 +2984,11 @@ static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v) seq_printf(seq, "\tDMA HW features\n"); seq_printf(seq, "==============================\n"); - seq_printf(seq, "\t10/100 Mbps %s\n", + seq_printf(seq, "\t10/100 Mbps: %s\n", (priv->dma_cap.mbps_10_100) ? "Y" : "N"); - seq_printf(seq, "\t1000 Mbps %s\n", + seq_printf(seq, "\t1000 Mbps: %s\n", (priv->dma_cap.mbps_1000) ? "Y" : "N"); - seq_printf(seq, "\tHalf duple %s\n", + seq_printf(seq, "\tHalf duplex: %s\n", (priv->dma_cap.half_duplex) ? "Y" : "N"); seq_printf(seq, "\tHash Filter: %s\n", (priv->dma_cap.hash_filter) ? "Y" : "N"); @@ -3004,9 +3006,9 @@ static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v) (priv->dma_cap.rmon) ? "Y" : "N"); seq_printf(seq, "\tIEEE 1588-2002 Time Stamp: %s\n", (priv->dma_cap.time_stamp) ? "Y" : "N"); - seq_printf(seq, "\tIEEE 1588-2008 Advanced Time Stamp:%s\n", + seq_printf(seq, "\tIEEE 1588-2008 Advanced Time Stamp: %s\n", (priv->dma_cap.atime_stamp) ? "Y" : "N"); - seq_printf(seq, "\t802.3az - Energy-Efficient Ethernet (EEE) %s\n", + seq_printf(seq, "\t802.3az - Energy-Efficient Ethernet (EEE): %s\n", (priv->dma_cap.eee) ? "Y" : "N"); seq_printf(seq, "\tAV features: %s\n", (priv->dma_cap.av) ? "Y" : "N"); seq_printf(seq, "\tChecksum Offload in TX: %s\n", -- cgit v1.1 From 0075bd692dca037108fd8b5d90a68d34bad4c2f8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 28 Nov 2016 15:18:31 +0100 Subject: net: phy: Fix use after free in phy_detach() If device_release_driver(&phydev->mdio.dev) is called, it releases all resources belonging to the PHY device. Hence the subsequent call to phy_led_triggers_unregister() will access already freed memory when unregistering the LEDs. Move the call to phy_led_triggers_unregister() before the possible call to device_release_driver() to fix this. Fixes: 2e0bc452f4721520 ("net: phy: leds: add support for led triggers on phy link state change") Signed-off-by: Geert Uytterhoeven Tested-by: Zach Brown Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 83e52f1..aeaf1bc 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -981,6 +981,8 @@ void phy_detach(struct phy_device *phydev) phydev->attached_dev = NULL; phy_suspend(phydev); + phy_led_triggers_unregister(phydev); + /* If the device had no specific driver before (i.e. - it * was using the generic driver), we unbind the device * from the generic driver so that there's a chance a @@ -994,8 +996,6 @@ void phy_detach(struct phy_device *phydev) } } - phy_led_triggers_unregister(phydev); - /* * The phydev might go away on the put_device() below, so avoid * a use-after-free bug by reading the underlying bus first. -- cgit v1.1 From 67ea7ef19bddaf75c842f56181deba89e4ea62a2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 28 Nov 2016 15:26:04 +0100 Subject: mlxsw: switchib: add MLXSW_PCI dependency The newly added switchib driver fails to link if MLXSW_PCI=m: drivers/net/ethernet/mellanox/mlxsw/mlxsw_switchib.o: In function^Cmlxsw_sib_module_exit': switchib.c:(.exit.text+0x8): undefined reference to `mlxsw_pci_driver_unregister' switchib.c:(.exit.text+0x10): undefined reference to `mlxsw_pci_driver_unregister' drivers/net/ethernet/mellanox/mlxsw/mlxsw_switchib.o: In function `mlxsw_sib_module_init': switchib.c:(.init.text+0x28): undefined reference to `mlxsw_pci_driver_register' switchib.c:(.init.text+0x38): undefined reference to `mlxsw_pci_driver_register' switchib.c:(.init.text+0x48): undefined reference to `mlxsw_pci_driver_unregister' The other two such sub-drivers have a dependency, so add the same one here. In theory we could allow this driver if MLXSW_PCI is disabled, but it's probably not worth it. Fixes: d1ba52638456 ("mlxsw: switchib: Introduce SwitchIB and SwitchIB silicon driver") Reviewed-by: Jiri Pirko Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 95ae4c0..16f44b9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -50,7 +50,7 @@ config MLXSW_I2C config MLXSW_SWITCHIB tristate "Mellanox Technologies SwitchIB and SwitchIB-2 support" - depends on MLXSW_CORE && NET_SWITCHDEV + depends on MLXSW_CORE && MLXSW_PCI && NET_SWITCHDEV default m ---help--- This driver supports Mellanox Technologies SwitchIB and SwitchIB-2 -- cgit v1.1 From f414b48e92d2b91654e31b96c9cf387eed1ed54f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 28 Nov 2016 18:01:23 +0100 Subject: mlxsw: resources: Add maximum buffer size We need to be able to limit the size of shared buffer pools, so query the maximum size from the device during init. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index 1c2119b..3c2171d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -47,6 +47,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, MLXSW_RES_ID_MAX_LAG_MEMBERS, + MLXSW_RES_ID_MAX_BUFFER_SIZE, MLXSW_RES_ID_MAX_CPU_POLICERS, MLXSW_RES_ID_MAX_VRS, MLXSW_RES_ID_MAX_RIFS, @@ -70,6 +71,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, + [MLXSW_RES_ID_MAX_BUFFER_SIZE] = 0x2802, /* Bytes */ [MLXSW_RES_ID_MAX_CPU_POLICERS] = 0x2A13, [MLXSW_RES_ID_MAX_VRS] = 0x2C01, [MLXSW_RES_ID_MAX_RIFS] = 0x2C02, -- cgit v1.1 From 87259f18778cce3fe3b141c5f56a3e64e5ffa5a9 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 28 Nov 2016 18:01:24 +0100 Subject: mlxsw: spectrum_buffers: Limit size of pools The shared buffer pools are containers whose size is used to calculate the maximum usage for packets from / to a specific port / {port, PG/TC}, when dynamic threshold is employed. While it's perfectly fine for the sum of the pools to exceed the maximum size of the shared buffer, a single pool cannot. Add a check when the pool size is set and forbid sizes larger than the maximum size of the shared buffer. Without the patch: $ devlink sb pool set pci/0000:03:00.0 pool 0 size 999999999 thtype dynamic // No error is returned With the patch: $ devlink sb pool set pci/0000:03:00.0 pool 0 size 999999999 thtype dynamic devlink answers: Invalid argument Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c index bcaed8a..a746826 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -611,6 +611,9 @@ int mlxsw_sp_sb_pool_set(struct mlxsw_core *mlxsw_core, u32 pool_size = MLXSW_SP_BYTES_TO_CELLS(size); enum mlxsw_reg_sbpr_mode mode; + if (size > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) + return -EINVAL; + mode = (enum mlxsw_reg_sbpr_mode) threshold_type; return mlxsw_sp_sb_pr_write(mlxsw_sp, pool, dir, mode, pool_size); } -- cgit v1.1 From 81d4d7289a6b8120d118f46c5182dbb86dca8436 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 28 Nov 2016 18:01:25 +0100 Subject: mlxsw: core: Add missing rollback in error path Without this rollback, the thermal zone is still registered during the error path, whereas its private data is freed upon the destruction of the underlying bus device due to the use of devm_kzalloc(). This results in use after free. Fix this by calling mlxsw_thermal_fini() from the appropriate place in the error path. Fixes: a50c1e35650b ("mlxsw: core: Implement thermal zone") Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index b21f88c..7a0ad39 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1157,6 +1157,7 @@ err_debugfs_init: if (mlxsw_core->driver->fini) mlxsw_core->driver->fini(mlxsw_core); err_driver_init: + mlxsw_thermal_fini(mlxsw_core->thermal); err_thermal_init: err_hwmon_init: devlink_unregister(devlink); -- cgit v1.1 From 523779c734d91e6908b2d6c1fd607c5b3a753e2d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 28 Nov 2016 18:01:26 +0100 Subject: mlxsw: core: Change order of operations in removal path We call bus->init() before allocating 'lag.mapping'. Change the order of operations in removal path to reflect that. This makes the error path of mlxsw_core_bus_device_register() symmetric with mlxsw_core_bus_device_unregister(). Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 7a0ad39..4dc028b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1188,8 +1188,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) mlxsw_thermal_fini(mlxsw_core->thermal); devlink_unregister(devlink); mlxsw_emad_fini(mlxsw_core); - mlxsw_core->bus->fini(mlxsw_core->bus_priv); kfree(mlxsw_core->lag.mapping); + mlxsw_core->bus->fini(mlxsw_core->bus_priv); free_percpu(mlxsw_core->pcpu_stats); devlink_free(devlink); mlxsw_core_driver_put(device_kind); -- cgit v1.1 From 93ba2222550415e61f773a8e2ecd1f2a665f5ef5 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Mon, 28 Nov 2016 18:25:44 +0100 Subject: hv_netvsc: remove excessive logging on MTU change When we change MTU or the number of channels on a netvsc device we get the following logged: hv_netvsc bf5edba8...: net device safe to remove hv_netvsc: hv_netvsc channel opened successfully hv_netvsc bf5edba8...: Send section size: 6144, Section count:2560 hv_netvsc bf5edba8...: Device MAC 00:15:5d:1e:91:12 link state up This information is useful as debug at most. Signed-off-by: Vitaly Kuznetsov Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 8 ++++---- drivers/net/hyperv/rndis_filter.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index e2bfaac..5a1cc08 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -410,8 +410,8 @@ static int netvsc_init_buf(struct hv_device *device) net_device->send_section_cnt = net_device->send_buf_size / net_device->send_section_size; - dev_info(&device->device, "Send section size: %d, Section count:%d\n", - net_device->send_section_size, net_device->send_section_cnt); + netdev_dbg(ndev, "Send section size: %d, Section count:%d\n", + net_device->send_section_size, net_device->send_section_cnt); /* Setup state for managing the send buffer. */ net_device->map_words = DIV_ROUND_UP(net_device->send_section_cnt, @@ -578,7 +578,7 @@ void netvsc_device_remove(struct hv_device *device) * At this point, no one should be accessing net_device * except in here */ - dev_notice(&device->device, "net device safe to remove\n"); + netdev_dbg(ndev, "net device safe to remove\n"); /* Now, we can close the channel safely */ vmbus_close(device->channel); @@ -1387,7 +1387,7 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) } /* Channel is opened */ - pr_info("hv_netvsc channel opened successfully\n"); + netdev_dbg(ndev, "hv_netvsc channel opened successfully\n"); /* If we're reopening the device we may have multiple queues, fill the * chn_table with the default channel to use it before subchannels are diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 9195d5d..8d90904 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1059,9 +1059,9 @@ int rndis_filter_device_add(struct hv_device *dev, device_info->link_state = rndis_device->link_state; - dev_info(&dev->device, "Device MAC %pM link state %s\n", - rndis_device->hw_mac_adr, - device_info->link_state ? "down" : "up"); + netdev_dbg(net, "Device MAC %pM link state %s\n", + rndis_device->hw_mac_adr, + device_info->link_state ? "down" : "up"); if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5) return 0; -- cgit v1.1 From 5b3211dcd43582c48291e7fc22b2e6d5c7faded5 Mon Sep 17 00:00:00 2001 From: "vegard.nossum@oracle.com" Date: Wed, 20 Jul 2016 10:43:11 +0200 Subject: ieee802154: check device type I've observed a NULL pointer dereference in ieee802154_del_iface() during netlink fuzzing. It's the ->wpan_phy dereference here: phy = dev->ieee802154_ptr->wpan_phy; My bet is that we're not checking that this is an IEEE802154 interface, so let's do what ieee802154_nl_get_dev() is doing. (Maybe we should even be calling this directly?) Cc: Lennert Buytenhek Cc: Alexander Aring Cc: Marcel Holtmann Cc: Dmitry Eremin-Solenikov Cc: Sergey Lapin Signed-off-by: Vegard Nossum Acked-by: Alexander Aring Signed-off-by: Stefan Schmidt --- net/ieee802154/nl-phy.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 77d7301..dc2960b 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -286,9 +286,12 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info) if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0') return -EINVAL; /* name should be null-terminated */ + rc = -ENODEV; dev = dev_get_by_name(genl_info_net(info), name); if (!dev) - return -ENODEV; + return rc; + if (dev->type != ARPHRD_IEEE802154) + goto out; phy = dev->ieee802154_ptr->wpan_phy; BUG_ON(!phy); @@ -342,6 +345,7 @@ nla_put_failure: nlmsg_free(msg); out_dev: wpan_phy_put(phy); +out: if (dev) dev_put(dev); -- cgit v1.1 From 4d7101d76572257dbbdca0e17f132a04e4b2b77d Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Tue, 6 Sep 2016 09:50:04 +0200 Subject: at86rf230: fix cca ed values for rf233 This patch changes the rssi base value to -94 for at86rf33 transceivers. The code before assumes a rssi base value of -91 which is for the at86rf231 transceiver only. This change need to update the cca ed threshold mapping table. Signed-off-by: Alexander Aring Acked-by: Stefan Schmidt Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/at86rf230.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 9f10da6..0570257 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -990,7 +990,12 @@ at86rf23x_set_channel(struct at86rf230_local *lp, u8 page, u8 channel) } #define AT86RF2XX_MAX_ED_LEVELS 0xF -static const s32 at86rf23x_ed_levels[AT86RF2XX_MAX_ED_LEVELS + 1] = { +static const s32 at86rf233_ed_levels[AT86RF2XX_MAX_ED_LEVELS + 1] = { + -9400, -9200, -9000, -8800, -8600, -8400, -8200, -8000, -7800, -7600, + -7400, -7200, -7000, -6800, -6600, -6400, +}; + +static const s32 at86rf231_ed_levels[AT86RF2XX_MAX_ED_LEVELS + 1] = { -9100, -8900, -8700, -8500, -8300, -8100, -7900, -7700, -7500, -7300, -7100, -6900, -6700, -6500, -6300, -6100, }; @@ -1343,7 +1348,7 @@ static struct at86rf2xx_chip_data at86rf233_data = { .t_sleep_to_off = 1000, .t_frame = 4096, .t_p_ack = 545, - .rssi_base_val = -91, + .rssi_base_val = -94, .set_channel = at86rf23x_set_channel, .set_txpower = at86rf23x_set_txpower, }; @@ -1557,9 +1562,6 @@ at86rf230_detect_device(struct at86rf230_local *lp) lp->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND) | BIT(NL802154_CCA_OPT_ENERGY_CARRIER_OR); - lp->hw->phy->supported.cca_ed_levels = at86rf23x_ed_levels; - lp->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(at86rf23x_ed_levels); - lp->hw->phy->cca.mode = NL802154_CCA_ENERGY; switch (part) { @@ -1575,6 +1577,8 @@ at86rf230_detect_device(struct at86rf230_local *lp) lp->hw->phy->symbol_duration = 16; lp->hw->phy->supported.tx_powers = at86rf231_powers; lp->hw->phy->supported.tx_powers_size = ARRAY_SIZE(at86rf231_powers); + lp->hw->phy->supported.cca_ed_levels = at86rf231_ed_levels; + lp->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(at86rf231_ed_levels); break; case 7: chip = "at86rf212"; @@ -1598,6 +1602,8 @@ at86rf230_detect_device(struct at86rf230_local *lp) lp->hw->phy->symbol_duration = 16; lp->hw->phy->supported.tx_powers = at86rf233_powers; lp->hw->phy->supported.tx_powers_size = ARRAY_SIZE(at86rf233_powers); + lp->hw->phy->supported.cca_ed_levels = at86rf233_ed_levels; + lp->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(at86rf233_ed_levels); break; default: chip = "unknown"; -- cgit v1.1 From 5cc92049e298b0e2d2d7a27867f05b48e7f7fe3a Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Tue, 25 Oct 2016 16:34:35 +0200 Subject: ieee802154: add myself as co-maintainer to MAINTAINERS file Going to help Alexander, spread the review and development load. Signed-off-by: Stefan Schmidt Acked-by: Alexander Aring --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5196538..0e6002a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6105,6 +6105,7 @@ F: drivers/idle/i7300_idle.c IEEE 802.15.4 SUBSYSTEM M: Alexander Aring +M: Stefan Schmidt L: linux-wpan@vger.kernel.org W: http://wpan.cakelab.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git -- cgit v1.1 From e03551d7b99c35d7d35f6862c37e3ce9f7c6787c Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Tue, 22 Nov 2016 14:02:07 +0100 Subject: ieee802154: fakelb: print number of created fake devices during probe When using fakelb with different network sizes it becomes handy to have the number of created fake devices printed in the log as well. Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/fakelb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c index ec387ef..0d673f7 100644 --- a/drivers/net/ieee802154/fakelb.c +++ b/drivers/net/ieee802154/fakelb.c @@ -218,7 +218,7 @@ static int fakelb_probe(struct platform_device *pdev) goto err_slave; } - dev_info(&pdev->dev, "added ieee802154 hardware\n"); + dev_info(&pdev->dev, "added %i fake ieee802154 hardware devices\n", numlbs); return 0; err_slave: -- cgit v1.1 From 6bf0d84d13e968b4f8bf0710e0cae785e228dbba Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Mon, 28 Nov 2016 18:22:33 +0100 Subject: docs: ieee802154: update main documentation file This updates some out of date documentation and fixes some wrong assumptions as well as pure grammar fixes. This file needs to move towards the new kernel doc system and getting an overhaul during this work. Signed-off-by: Stefan Schmidt --- Documentation/networking/ieee802154.txt | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/Documentation/networking/ieee802154.txt b/Documentation/networking/ieee802154.txt index aa69ccc..c411434 100644 --- a/Documentation/networking/ieee802154.txt +++ b/Documentation/networking/ieee802154.txt @@ -4,20 +4,20 @@ Introduction ============ -The IEEE 802.15.4 working group focuses on standardization of bottom -two layers: Medium Access Control (MAC) and Physical (PHY). And there +The IEEE 802.15.4 working group focuses on standardization of the bottom +two layers: Medium Access Control (MAC) and Physical access (PHY). And there are mainly two options available for upper layers: - ZigBee - proprietary protocol from the ZigBee Alliance - 6LoWPAN - IPv6 networking over low rate personal area networks -The linux-wpan project goal is to provide a complete implementation +The goal of the Linux-wpan is to provide a complete implementation of the IEEE 802.15.4 and 6LoWPAN protocols. IEEE 802.15.4 is a stack of protocols for organizing Low-Rate Wireless Personal Area Networks. The stack is composed of three main parts: - IEEE 802.15.4 layer; We have chosen to use plain Berkeley socket API, - the generic Linux networking stack to transfer IEEE 802.15.4 messages - and a special protocol over genetlink for configuration/management + the generic Linux networking stack to transfer IEEE 802.15.4 data + messages and a special protocol over netlink for configuration/management - MAC - provides access to shared channel and reliable data delivery - PHY - represents device drivers @@ -33,15 +33,13 @@ include/net/af_ieee802154.h header or in the special header in the userspace package (see either http://wpan.cakelab.org/ or the git tree at https://github.com/linux-wpan/wpan-tools). -One can use SOCK_RAW for passing raw data towards device xmit function. YMMV. - Kernel side ============= Like with WiFi, there are several types of devices implementing IEEE 802.15.4. 1) 'HardMAC'. The MAC layer is implemented in the device itself, the device - exports MLME and data API. + exports a management (e.g. MLME) and data API. 2) 'SoftMAC' or just radio. These types of devices are just radio transceivers possibly with some kinds of acceleration like automatic CRC computation and comparation, automagic ACK handling, address matching, etc. @@ -106,7 +104,7 @@ Fake drivers In addition there is a driver available which simulates a real device with SoftMAC (fakelb - IEEE 802.15.4 loopback driver) interface. This option -provides possibility to test and debug stack without usage of real hardware. +provides a possibility to test and debug the stack without usage of real hardware. See sources in drivers/net/ieee802154 folder for more details. @@ -125,17 +123,15 @@ to support the IPv6 minimum MTU requirement [RFC2460], and stateless header compression for IPv6 datagrams (LOWPAN_HC1 and LOWPAN_HC2) to reduce the relatively large IPv6 and UDP headers down to (in the best case) several bytes. -In Semptember 2011 the standard update was published - [RFC6282]. +In September 2011 the standard update was published - [RFC6282]. It deprecates HC1 and HC2 compression and defines IPHC encoding format which is used in this Linux implementation. All the code related to 6lowpan you may find in files: net/6lowpan/* and net/ieee802154/6lowpan/* -To setup 6lowpan interface you need (busybox release > 1.17.0): -1. Add IEEE802.15.4 interface and initialize PANid; +To setup a 6LoWPAN interface you need: +1. Add IEEE802.15.4 interface and set channel and PAN ID; 2. Add 6lowpan interface by command like: # ip link add link wpan0 name lowpan0 type lowpan -3. Set MAC (if needs): - # ip link set lowpan0 address de:ad:be:ef:ca:fe:ba:be -4. Bring up 'lowpan0' interface +3. Bring up 'lowpan0' interface -- cgit v1.1 From a090994980a15f8cc14fc188b5929bd61d2ae9c3 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Mon, 28 Nov 2016 09:41:33 +0100 Subject: cpsw: ethtool: add support for getting/setting EEE registers Add the ability to query and set Energy Efficient Ethernet parameters via ethtool for applicable devices. This patch doesn't activate full EEE support in cpsw driver, but it enables reading and writing EEE advertising settings. This way one can disable advertising EEE for certain speeds. Signed-off-by: Yegor Yefremov Acked-by: Rami Rosen Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index da40ea5..df87bff 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2237,6 +2237,30 @@ err: return ret; } +static int cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + int slave_no = cpsw_slave_index(cpsw, priv); + + if (cpsw->slaves[slave_no].phy) + return phy_ethtool_get_eee(cpsw->slaves[slave_no].phy, edata); + else + return -EOPNOTSUPP; +} + +static int cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + int slave_no = cpsw_slave_index(cpsw, priv); + + if (cpsw->slaves[slave_no].phy) + return phy_ethtool_set_eee(cpsw->slaves[slave_no].phy, edata); + else + return -EOPNOTSUPP; +} + static const struct ethtool_ops cpsw_ethtool_ops = { .get_drvinfo = cpsw_get_drvinfo, .get_msglevel = cpsw_get_msglevel, @@ -2260,6 +2284,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = { .set_channels = cpsw_set_channels, .get_link_ksettings = cpsw_get_link_ksettings, .set_link_ksettings = cpsw_set_link_ksettings, + .get_eee = cpsw_get_eee, + .set_eee = cpsw_set_eee, }; static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_common *cpsw, -- cgit v1.1 From 05b055e89121394058c75dc354e9a46e1e765579 Mon Sep 17 00:00:00 2001 From: Francis Yan Date: Sun, 27 Nov 2016 23:07:13 -0800 Subject: tcp: instrument tcp sender limits chronographs This patch implements the skeleton of the TCP chronograph instrumentation on sender side limits: 1) idle (unspec) 2) busy sending data other than 3-4 below 3) rwnd-limited 4) sndbuf-limited The limits are enumerated 'tcp_chrono'. Since a connection in theory can idle forever, we do not track the actual length of this uninteresting idle period. For the rest we track how long the sender spends in each limit. At any point during the life time of a connection, the sender must be in one of the four states. If there are multiple conditions worthy of tracking in a chronograph then the highest priority enum takes precedence over the other conditions. So that if something "more interesting" starts happening, stop the previous chrono and start a new one. The time unit is jiffy(u32) in order to save space in tcp_sock. This implies application must sample the stats no longer than every 49 days of 1ms jiffy. Signed-off-by: Francis Yan Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 7 +++++-- include/net/tcp.h | 14 ++++++++++++++ net/ipv4/tcp_output.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 32a7c7e..d5d3bd8 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -211,8 +211,11 @@ struct tcp_sock { u8 reord; /* reordering detected */ } rack; u16 advmss; /* Advertised MSS */ - u8 rate_app_limited:1, /* rate_{delivered,interval_us} limited? */ - unused:7; + u32 chrono_start; /* Start time in jiffies of a TCP chrono */ + u32 chrono_stat[3]; /* Time in jiffies for chrono_stat stats */ + u8 chrono_type:2, /* current chronograph type */ + rate_app_limited:1, /* rate_{delivered,interval_us} limited? */ + unused:5; u8 nonagle : 4,/* Disable Nagle algorithm? */ thin_lto : 1,/* Use linear timeouts for thin streams */ thin_dupack : 1,/* Fast retransmit on first dupack */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 7de8073..e5ff408 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1516,6 +1516,20 @@ struct tcp_fastopen_context { struct rcu_head rcu; }; +/* Latencies incurred by various limits for a sender. They are + * chronograph-like stats that are mutually exclusive. + */ +enum tcp_chrono { + TCP_CHRONO_UNSPEC, + TCP_CHRONO_BUSY, /* Actively sending data (non-empty write queue) */ + TCP_CHRONO_RWND_LIMITED, /* Stalled by insufficient receive window */ + TCP_CHRONO_SNDBUF_LIMITED, /* Stalled by insufficient send buffer */ + __TCP_CHRONO_MAX, +}; + +void tcp_chrono_start(struct sock *sk, const enum tcp_chrono type); +void tcp_chrono_stop(struct sock *sk, const enum tcp_chrono type); + /* write queue abstraction */ static inline void tcp_write_queue_purge(struct sock *sk) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 19105b4..34f7517 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2081,6 +2081,36 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, return false; } +static void tcp_chrono_set(struct tcp_sock *tp, const enum tcp_chrono new) +{ + const u32 now = tcp_time_stamp; + + if (tp->chrono_type > TCP_CHRONO_UNSPEC) + tp->chrono_stat[tp->chrono_type - 1] += now - tp->chrono_start; + tp->chrono_start = now; + tp->chrono_type = new; +} + +void tcp_chrono_start(struct sock *sk, const enum tcp_chrono type) +{ + struct tcp_sock *tp = tcp_sk(sk); + + /* If there are multiple conditions worthy of tracking in a + * chronograph then the highest priority enum takes precedence over + * the other conditions. So that if something "more interesting" + * starts happening, stop the previous chrono and start a new one. + */ + if (type > tp->chrono_type) + tcp_chrono_set(tp, type); +} + +void tcp_chrono_stop(struct sock *sk, const enum tcp_chrono type) +{ + struct tcp_sock *tp = tcp_sk(sk); + + tcp_chrono_set(tp, TCP_CHRONO_UNSPEC); +} + /* This routine writes packets to the network. It advances the * send_head. This happens as incoming acks open up the remote * window for us. -- cgit v1.1 From 0f87230d1a6c253681550c6064715d06a32be73d Mon Sep 17 00:00:00 2001 From: Francis Yan Date: Sun, 27 Nov 2016 23:07:14 -0800 Subject: tcp: instrument how long TCP is busy sending This patch measures TCP busy time, which is defined as the period of time when sender has data (or FIN) to send. The time starts when data is buffered and stops when the write queue is flushed by ACKs or error events. Note the busy time does not include SYN time, unless data is included in SYN (i.e. Fast Open). It does include FIN time even if the FIN carries no payload. Excluding pure FIN is possible but would incur one additional test in the fast path, which may not be worth it. Signed-off-by: Francis Yan Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 6 +++++- net/ipv4/tcp_input.c | 3 +++ net/ipv4/tcp_output.c | 19 ++++++++++++++++--- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index e5ff408..3e097e3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1535,6 +1535,7 @@ static inline void tcp_write_queue_purge(struct sock *sk) { struct sk_buff *skb; + tcp_chrono_stop(sk, TCP_CHRONO_BUSY); while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) sk_wmem_free_skb(sk, skb); sk_mem_reclaim(sk); @@ -1593,8 +1594,10 @@ static inline void tcp_advance_send_head(struct sock *sk, const struct sk_buff * static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unlinked) { - if (sk->sk_send_head == skb_unlinked) + if (sk->sk_send_head == skb_unlinked) { sk->sk_send_head = NULL; + tcp_chrono_stop(sk, TCP_CHRONO_BUSY); + } if (tcp_sk(sk)->highest_sack == skb_unlinked) tcp_sk(sk)->highest_sack = NULL; } @@ -1616,6 +1619,7 @@ static inline void tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb /* Queue it, remembering where we must start sending. */ if (sk->sk_send_head == NULL) { sk->sk_send_head = skb; + tcp_chrono_start(sk, TCP_CHRONO_BUSY); if (tcp_sk(sk)->highest_sack == NULL) tcp_sk(sk)->highest_sack = skb; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 22e6a20..a5d1727 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3178,6 +3178,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, tp->lost_skb_hint = NULL; } + if (!skb) + tcp_chrono_stop(sk, TCP_CHRONO_BUSY); + if (likely(between(tp->snd_up, prior_snd_una, tp->snd_una))) tp->snd_up = tp->snd_una; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 34f7517..e8ea584 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2096,8 +2096,8 @@ void tcp_chrono_start(struct sock *sk, const enum tcp_chrono type) struct tcp_sock *tp = tcp_sk(sk); /* If there are multiple conditions worthy of tracking in a - * chronograph then the highest priority enum takes precedence over - * the other conditions. So that if something "more interesting" + * chronograph then the highest priority enum takes precedence + * over the other conditions. So that if something "more interesting" * starts happening, stop the previous chrono and start a new one. */ if (type > tp->chrono_type) @@ -2108,7 +2108,18 @@ void tcp_chrono_stop(struct sock *sk, const enum tcp_chrono type) { struct tcp_sock *tp = tcp_sk(sk); - tcp_chrono_set(tp, TCP_CHRONO_UNSPEC); + + /* There are multiple conditions worthy of tracking in a + * chronograph, so that the highest priority enum takes + * precedence over the other conditions (see tcp_chrono_start). + * If a condition stops, we only stop chrono tracking if + * it's the "most interesting" or current chrono we are + * tracking and starts busy chrono if we have pending data. + */ + if (tcp_write_queue_empty(sk)) + tcp_chrono_set(tp, TCP_CHRONO_UNSPEC); + else if (type == tp->chrono_type) + tcp_chrono_set(tp, TCP_CHRONO_BUSY); } /* This routine writes packets to the network. It advances the @@ -3328,6 +3339,8 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) fo->copied = space; tcp_connect_queue_skb(sk, syn_data); + if (syn_data->len) + tcp_chrono_start(sk, TCP_CHRONO_BUSY); err = tcp_transmit_skb(sk, syn_data, 1, sk->sk_allocation); -- cgit v1.1 From 5615f88614a47d2b802e1d14d31b623696109276 Mon Sep 17 00:00:00 2001 From: Francis Yan Date: Sun, 27 Nov 2016 23:07:15 -0800 Subject: tcp: instrument how long TCP is limited by receive window This patch measures the total time when the TCP stops sending because the receiver's advertised window is not large enough. Note that once the limit is lifted we are likely in the busy status if we have data pending. Signed-off-by: Francis Yan Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index e8ea584..b74444c 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2144,7 +2144,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, unsigned int tso_segs, sent_pkts; int cwnd_quota; int result; - bool is_cwnd_limited = false; + bool is_cwnd_limited = false, is_rwnd_limited = false; u32 max_segs; sent_pkts = 0; @@ -2181,8 +2181,10 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, break; } - if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) + if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) { + is_rwnd_limited = true; break; + } if (tso_segs == 1) { if (unlikely(!tcp_nagle_test(tp, skb, mss_now, @@ -2227,6 +2229,11 @@ repair: break; } + if (is_rwnd_limited) + tcp_chrono_start(sk, TCP_CHRONO_RWND_LIMITED); + else + tcp_chrono_stop(sk, TCP_CHRONO_RWND_LIMITED); + if (likely(sent_pkts)) { if (tcp_in_cwnd_reduction(sk)) tp->prr_out += sent_pkts; -- cgit v1.1 From b0f71bd3e190df827d25d7f19bf09037567f14b7 Mon Sep 17 00:00:00 2001 From: Francis Yan Date: Sun, 27 Nov 2016 23:07:16 -0800 Subject: tcp: instrument how long TCP is limited by insufficient send buffer This patch measures the amount of time when TCP runs out of new data to send to the network due to insufficient send buffer, while TCP is still busy delivering (i.e. write queue is not empty). The goal is to indicate either the send buffer autotuning or user SO_SNDBUF setting has resulted network under-utilization. The measurement starts conservatively by checking various conditions to minimize false claims (i.e. under-estimation is more likely). The measurement stops when the SOCK_NOSPACE flag is cleared. But it does not account the time elapsed till the next application write. Also the measurement only starts if the sender is still busy sending data, s.t. the limit accounted is part of the total busy time. Signed-off-by: Francis Yan Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 10 ++++++++-- net/ipv4/tcp_input.c | 5 ++++- net/ipv4/tcp_output.c | 12 ++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 913f9bb..259ffb5 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -996,8 +996,11 @@ do_error: goto out; out_err: /* make sure we wake any epoll edge trigger waiter */ - if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && err == -EAGAIN)) + if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && + err == -EAGAIN)) { sk->sk_write_space(sk); + tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); + } return sk_stream_error(sk, flags, err); } @@ -1331,8 +1334,11 @@ do_error: out_err: err = sk_stream_error(sk, flags, err); /* make sure we wake any epoll edge trigger waiter */ - if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && err == -EAGAIN)) + if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && + err == -EAGAIN)) { sk->sk_write_space(sk); + tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); + } release_sock(sk); return err; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a5d1727..56fe736 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5059,8 +5059,11 @@ static void tcp_check_space(struct sock *sk) /* pairs with tcp_poll() */ smp_mb__after_atomic(); if (sk->sk_socket && - test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) + test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { tcp_new_space(sk); + if (!test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) + tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); + } } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index b74444c..d3545d0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1514,6 +1514,18 @@ static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited) if (sysctl_tcp_slow_start_after_idle && (s32)(tcp_time_stamp - tp->snd_cwnd_stamp) >= inet_csk(sk)->icsk_rto) tcp_cwnd_application_limited(sk); + + /* The following conditions together indicate the starvation + * is caused by insufficient sender buffer: + * 1) just sent some data (see tcp_write_xmit) + * 2) not cwnd limited (this else condition) + * 3) no more data to send (null tcp_send_head ) + * 4) application is hitting buffer limit (SOCK_NOSPACE) + */ + if (!tcp_send_head(sk) && sk->sk_socket && + test_bit(SOCK_NOSPACE, &sk->sk_socket->flags) && + (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) + tcp_chrono_start(sk, TCP_CHRONO_SNDBUF_LIMITED); } } -- cgit v1.1 From efd90174167530c67a54273fd5d8369c87f9bd32 Mon Sep 17 00:00:00 2001 From: Francis Yan Date: Sun, 27 Nov 2016 23:07:17 -0800 Subject: tcp: export sender limits chronographs to TCP_INFO This patch exports all the sender chronograph measurements collected in the previous patches to TCP_INFO interface. Note that busy time exported includes all the other sending limits (rwnd-limited, sndbuf-limited). Internally the time unit is jiffy but externally the measurements are in microseconds for future extensions. Signed-off-by: Francis Yan Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 4 ++++ net/ipv4/tcp.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 73ac0db..2863b66 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -214,6 +214,10 @@ struct tcp_info { __u32 tcpi_data_segs_out; /* RFC4898 tcpEStatsDataSegsOut */ __u64 tcpi_delivery_rate; + + __u64 tcpi_busy_time; /* Time (usec) busy sending data */ + __u64 tcpi_rwnd_limited; /* Time (usec) limited by receive window */ + __u64 tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */ }; /* for TCP_MD5SIG socket option */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 259ffb5..cdde20f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2708,6 +2708,25 @@ int compat_tcp_setsockopt(struct sock *sk, int level, int optname, EXPORT_SYMBOL(compat_tcp_setsockopt); #endif +static void tcp_get_info_chrono_stats(const struct tcp_sock *tp, + struct tcp_info *info) +{ + u64 stats[__TCP_CHRONO_MAX], total = 0; + enum tcp_chrono i; + + for (i = TCP_CHRONO_BUSY; i < __TCP_CHRONO_MAX; ++i) { + stats[i] = tp->chrono_stat[i - 1]; + if (i == tp->chrono_type) + stats[i] += tcp_time_stamp - tp->chrono_start; + stats[i] *= USEC_PER_SEC / HZ; + total += stats[i]; + } + + info->tcpi_busy_time = total; + info->tcpi_rwnd_limited = stats[TCP_CHRONO_RWND_LIMITED]; + info->tcpi_sndbuf_limited = stats[TCP_CHRONO_SNDBUF_LIMITED]; +} + /* Return information about state of tcp endpoint in API format. */ void tcp_get_info(struct sock *sk, struct tcp_info *info) { @@ -2800,6 +2819,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_bytes_acked = tp->bytes_acked; info->tcpi_bytes_received = tp->bytes_received; info->tcpi_notsent_bytes = max_t(int, 0, tp->write_seq - tp->snd_nxt); + tcp_get_info_chrono_stats(tp, info); unlock_sock_fast(sk, slow); -- cgit v1.1 From 1c885808e45601b2b6f68b30ac1d999e10b6f606 Mon Sep 17 00:00:00 2001 From: Francis Yan Date: Sun, 27 Nov 2016 23:07:18 -0800 Subject: tcp: SOF_TIMESTAMPING_OPT_STATS option for SO_TIMESTAMPING This patch exports the sender chronograph stats via the socket SO_TIMESTAMPING channel. Currently we can instrument how long a particular application unit of data was queued in TCP by tracking SOF_TIMESTAMPING_TX_SOFTWARE and SOF_TIMESTAMPING_TX_SCHED. Having these sender chronograph stats exported simultaneously along with these timestamps allow further breaking down the various sender limitation. For example, a video server can tell if a particular chunk of video on a connection takes a long time to deliver because TCP was experiencing small receive window. It is not possible to tell before this patch without packet traces. To prepare these stats, the user needs to set SOF_TIMESTAMPING_OPT_STATS and SOF_TIMESTAMPING_OPT_TSONLY flags while requesting other SOF_TIMESTAMPING TX timestamps. When the timestamps are available in the error queue, the stats are returned in a separate control message of type SCM_TIMESTAMPING_OPT_STATS, in a list of TLVs (struct nlattr) of types: TCP_NLA_BUSY_TIME, TCP_NLA_RWND_LIMITED, TCP_NLA_SNDBUF_LIMITED. Unit is microsecond. Signed-off-by: Francis Yan Signed-off-by: Yuchung Cheng Signed-off-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 10 ++++++++++ arch/alpha/include/uapi/asm/socket.h | 2 ++ arch/frv/include/uapi/asm/socket.h | 2 ++ arch/ia64/include/uapi/asm/socket.h | 2 ++ arch/m32r/include/uapi/asm/socket.h | 2 ++ arch/mips/include/uapi/asm/socket.h | 2 ++ arch/mn10300/include/uapi/asm/socket.h | 2 ++ arch/parisc/include/uapi/asm/socket.h | 2 ++ arch/powerpc/include/uapi/asm/socket.h | 2 ++ arch/s390/include/uapi/asm/socket.h | 2 ++ arch/sparc/include/uapi/asm/socket.h | 2 ++ arch/xtensa/include/uapi/asm/socket.h | 2 ++ include/linux/tcp.h | 2 ++ include/uapi/asm-generic/socket.h | 2 ++ include/uapi/linux/net_tstamp.h | 3 ++- include/uapi/linux/tcp.h | 8 ++++++++ net/core/skbuff.c | 14 +++++++++++--- net/core/sock.c | 7 +++++++ net/ipv4/tcp.c | 20 ++++++++++++++++++++ net/socket.c | 7 ++++++- 20 files changed, 90 insertions(+), 5 deletions(-) diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt index 671cccf..96f5069 100644 --- a/Documentation/networking/timestamping.txt +++ b/Documentation/networking/timestamping.txt @@ -182,6 +182,16 @@ SOF_TIMESTAMPING_OPT_TSONLY: the timestamp even if sysctl net.core.tstamp_allow_data is 0. This option disables SOF_TIMESTAMPING_OPT_CMSG. +SOF_TIMESTAMPING_OPT_STATS: + + Optional stats that are obtained along with the transmit timestamps. + It must be used together with SOF_TIMESTAMPING_OPT_TSONLY. When the + transmit timestamp is available, the stats are available in a + separate control message of type SCM_TIMESTAMPING_OPT_STATS, as a + list of TLVs (struct nlattr) of types. These stats allow the + application to associate various transport layer stats with + the transmit timestamps, such as how long a certain block of + data was limited by peer's receiver window. New applications are encouraged to pass SOF_TIMESTAMPING_OPT_ID to disambiguate timestamps and SOF_TIMESTAMPING_OPT_TSONLY to operate diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 9e46d6e..afc901b 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -97,4 +97,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index afbc98f0..81e0353 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -90,5 +90,7 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index 0018fad..57feb0c 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -99,4 +99,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index 5fe42fc..5853f8e9 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -90,4 +90,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 2027240a..566ecdc 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -108,4 +108,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index 5129f23..0e12527 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -90,4 +90,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index 9c935d7..7a109b7 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -89,4 +89,6 @@ #define SO_CNX_ADVICE 0x402E +#define SCM_TIMESTAMPING_OPT_STATS 0x402F + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index 1672e33..44583a5 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -97,4 +97,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 41b51c2..b24a64c 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -96,4 +96,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index 31aede3..a25dc32 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -86,6 +86,8 @@ #define SO_CNX_ADVICE 0x0037 +#define SCM_TIMESTAMPING_OPT_STATS 0x0038 + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index 81435d9..9fdbe1f 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -101,4 +101,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/linux/tcp.h b/include/linux/tcp.h index d5d3bd8..00e0ee8 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -428,4 +428,6 @@ static inline void tcp_saved_syn_free(struct tcp_sock *tp) tp->saved_syn = NULL; } +struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk); + #endif /* _LINUX_TCP_H */ diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 67d632f..2c748dd 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -92,4 +92,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h index 264e515..464dcca 100644 --- a/include/uapi/linux/net_tstamp.h +++ b/include/uapi/linux/net_tstamp.h @@ -25,8 +25,9 @@ enum { SOF_TIMESTAMPING_TX_ACK = (1<<9), SOF_TIMESTAMPING_OPT_CMSG = (1<<10), SOF_TIMESTAMPING_OPT_TSONLY = (1<<11), + SOF_TIMESTAMPING_OPT_STATS = (1<<12), - SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_TSONLY, + SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_STATS, SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) | SOF_TIMESTAMPING_LAST }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 2863b66..c53de26 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -220,6 +220,14 @@ struct tcp_info { __u64 tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */ }; +/* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */ +enum { + TCP_NLA_PAD, + TCP_NLA_BUSY, /* Time (usec) busy sending data */ + TCP_NLA_RWND_LIMITED, /* Time (usec) limited by receive window */ + TCP_NLA_SNDBUF_LIMITED, /* Time (usec) limited by send buffer */ +}; + /* for TCP_MD5SIG socket option */ #define TCP_MD5SIG_MAXKEYLEN 80 diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d1d1a5a..ea6fa95 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3839,10 +3839,18 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb, if (!skb_may_tx_timestamp(sk, tsonly)) return; - if (tsonly) - skb = alloc_skb(0, GFP_ATOMIC); - else + if (tsonly) { +#ifdef CONFIG_INET + if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS) && + sk->sk_protocol == IPPROTO_TCP && + sk->sk_type == SOCK_STREAM) + skb = tcp_get_timestamping_opt_stats(sk); + else +#endif + skb = alloc_skb(0, GFP_ATOMIC); + } else { skb = skb_clone(orig_skb, GFP_ATOMIC); + } if (!skb) return; diff --git a/net/core/sock.c b/net/core/sock.c index 14e6145..d8c7f8c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -854,6 +854,13 @@ set_rcvbuf: sk->sk_tskey = 0; } } + + if (val & SOF_TIMESTAMPING_OPT_STATS && + !(val & SOF_TIMESTAMPING_OPT_TSONLY)) { + ret = -EINVAL; + break; + } + sk->sk_tsflags = val; if (val & SOF_TIMESTAMPING_RX_SOFTWARE) sock_enable_timestamp(sk, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index cdde20f..1149b48 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2841,6 +2841,26 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) } EXPORT_SYMBOL_GPL(tcp_get_info); +struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *stats; + struct tcp_info info; + + stats = alloc_skb(3 * nla_total_size_64bit(sizeof(u64)), GFP_ATOMIC); + if (!stats) + return NULL; + + tcp_get_info_chrono_stats(tp, &info); + nla_put_u64_64bit(stats, TCP_NLA_BUSY, + info.tcpi_busy_time, TCP_NLA_PAD); + nla_put_u64_64bit(stats, TCP_NLA_RWND_LIMITED, + info.tcpi_rwnd_limited, TCP_NLA_PAD); + nla_put_u64_64bit(stats, TCP_NLA_SNDBUF_LIMITED, + info.tcpi_sndbuf_limited, TCP_NLA_PAD); + return stats; +} + static int do_tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { diff --git a/net/socket.c b/net/socket.c index e2584c5..e631894 100644 --- a/net/socket.c +++ b/net/socket.c @@ -693,9 +693,14 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) && ktime_to_timespec_cond(shhwtstamps->hwtstamp, tss.ts + 2)) empty = 0; - if (!empty) + if (!empty) { put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPING, sizeof(tss), &tss); + + if (skb->len && (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS)) + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPING_OPT_STATS, + skb->len, skb->data); + } } EXPORT_SYMBOL_GPL(__sock_recv_timestamp); -- cgit v1.1 From 6bb10c2bc6576d80d2e933a58b4210101e56c30c Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Mon, 28 Nov 2016 10:47:52 +0100 Subject: cpsw: ethtool: add support for nway reset This patch adds support for ethtool's '-r' command. Restarting N-WAY negotiation can be useful to activate newly changed EEE settings etc. Signed-off-by: Yegor Yefremov Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index df87bff..caec6ac 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2261,6 +2261,18 @@ static int cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata) return -EOPNOTSUPP; } +static int cpsw_nway_reset(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + int slave_no = cpsw_slave_index(cpsw, priv); + + if (cpsw->slaves[slave_no].phy) + return genphy_restart_aneg(cpsw->slaves[slave_no].phy); + else + return -EOPNOTSUPP; +} + static const struct ethtool_ops cpsw_ethtool_ops = { .get_drvinfo = cpsw_get_drvinfo, .get_msglevel = cpsw_get_msglevel, @@ -2286,6 +2298,7 @@ static const struct ethtool_ops cpsw_ethtool_ops = { .set_link_ksettings = cpsw_set_link_ksettings, .get_eee = cpsw_get_eee, .set_eee = cpsw_set_eee, + .nway_reset = cpsw_nway_reset, }; static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_common *cpsw, -- cgit v1.1 From 5a6681e22c1409089132085811857d6da828761b Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 28 Nov 2016 18:55:34 +0000 Subject: sfc: separate out SFC4000 ("Falcon") support into new sfc-falcon driver Rationale: The differences between Falcon and Siena are in many ways larger than those between Siena and EF10 (despite Siena being nominally "Falcon- architecture"); for instance, Falcon has no MCPU, so there is no MCDI. Removing Falcon support from the sfc driver should simplify the latter, and avoid the possibility of Falcon support being broken by changes to sfc (which are rarely if ever tested on Falcon, it being end-of-lifed hardware). The sfc-falcon driver created in this changeset is essentially a copy of the sfc driver, but with Siena- and EF10-specific code, including MCDI, removed and with the "efx_" identifier prefix changed to "ef4_" (for "EFX 4000- series") to avoid collisions when both drivers are built-in. This changeset removes Falcon from the sfc driver's PCI ID table; then in sfc I've removed obvious Falcon-related code: I removed the Falcon NIC functions, Falcon PHY code, and EFX_REV_FALCON_*, then fixed up everything that referenced them. Also, increment minor version of both drivers (to 4.1). For now, CONFIG_SFC selects CONFIG_SFC_FALCON, so that updating old configs doesn't cause Falcon support to disappear; but that should be undone at some point in the future. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/sfc/Kconfig | 8 +- drivers/net/ethernet/sfc/Makefile | 7 +- drivers/net/ethernet/sfc/efx.c | 29 +- drivers/net/ethernet/sfc/ethtool.c | 15 +- drivers/net/ethernet/sfc/falcon.c | 2907 -------------------- drivers/net/ethernet/sfc/falcon/Kconfig | 21 + drivers/net/ethernet/sfc/falcon/Makefile | 6 + drivers/net/ethernet/sfc/falcon/bitfield.h | 542 ++++ drivers/net/ethernet/sfc/falcon/efx.c | 3350 +++++++++++++++++++++++ drivers/net/ethernet/sfc/falcon/efx.h | 277 ++ drivers/net/ethernet/sfc/falcon/enum.h | 171 ++ drivers/net/ethernet/sfc/falcon/ethtool.c | 1343 +++++++++ drivers/net/ethernet/sfc/falcon/falcon.c | 2903 ++++++++++++++++++++ drivers/net/ethernet/sfc/falcon/falcon_boards.c | 764 ++++++ drivers/net/ethernet/sfc/falcon/farch.c | 2892 +++++++++++++++++++ drivers/net/ethernet/sfc/falcon/farch_regs.h | 2932 ++++++++++++++++++++ drivers/net/ethernet/sfc/falcon/filter.h | 272 ++ drivers/net/ethernet/sfc/falcon/io.h | 290 ++ drivers/net/ethernet/sfc/falcon/mdio_10g.c | 323 +++ drivers/net/ethernet/sfc/falcon/mdio_10g.h | 110 + drivers/net/ethernet/sfc/falcon/mtd.c | 133 + drivers/net/ethernet/sfc/falcon/net_driver.h | 1464 ++++++++++ drivers/net/ethernet/sfc/falcon/nic.c | 527 ++++ drivers/net/ethernet/sfc/falcon/nic.h | 513 ++++ drivers/net/ethernet/sfc/falcon/phy.h | 50 + drivers/net/ethernet/sfc/falcon/qt202x_phy.c | 495 ++++ drivers/net/ethernet/sfc/falcon/rx.c | 974 +++++++ drivers/net/ethernet/sfc/falcon/selftest.c | 808 ++++++ drivers/net/ethernet/sfc/falcon/selftest.h | 55 + drivers/net/ethernet/sfc/falcon/tenxpress.c | 494 ++++ drivers/net/ethernet/sfc/falcon/tx.c | 649 +++++ drivers/net/ethernet/sfc/falcon/tx.h | 27 + drivers/net/ethernet/sfc/falcon/txc43128_phy.c | 560 ++++ drivers/net/ethernet/sfc/falcon/workarounds.h | 44 + drivers/net/ethernet/sfc/falcon_boards.c | 764 ------ drivers/net/ethernet/sfc/farch.c | 165 +- drivers/net/ethernet/sfc/mcdi.c | 1 - drivers/net/ethernet/sfc/mcdi_port.c | 1 - drivers/net/ethernet/sfc/mdio_10g.c | 323 --- drivers/net/ethernet/sfc/mdio_10g.h | 110 - drivers/net/ethernet/sfc/net_driver.h | 4 +- drivers/net/ethernet/sfc/nic.h | 172 +- drivers/net/ethernet/sfc/phy.h | 50 - drivers/net/ethernet/sfc/qt202x_phy.c | 495 ---- drivers/net/ethernet/sfc/rx.c | 19 +- drivers/net/ethernet/sfc/siena.c | 1 - drivers/net/ethernet/sfc/tenxpress.c | 494 ---- drivers/net/ethernet/sfc/tx.c | 22 +- drivers/net/ethernet/sfc/txc43128_phy.c | 560 ---- drivers/net/ethernet/sfc/workarounds.h | 21 - 52 files changed, 23076 insertions(+), 6083 deletions(-) delete mode 100644 drivers/net/ethernet/sfc/falcon.c create mode 100644 drivers/net/ethernet/sfc/falcon/Kconfig create mode 100644 drivers/net/ethernet/sfc/falcon/Makefile create mode 100644 drivers/net/ethernet/sfc/falcon/bitfield.h create mode 100644 drivers/net/ethernet/sfc/falcon/efx.c create mode 100644 drivers/net/ethernet/sfc/falcon/efx.h create mode 100644 drivers/net/ethernet/sfc/falcon/enum.h create mode 100644 drivers/net/ethernet/sfc/falcon/ethtool.c create mode 100644 drivers/net/ethernet/sfc/falcon/falcon.c create mode 100644 drivers/net/ethernet/sfc/falcon/falcon_boards.c create mode 100644 drivers/net/ethernet/sfc/falcon/farch.c create mode 100644 drivers/net/ethernet/sfc/falcon/farch_regs.h create mode 100644 drivers/net/ethernet/sfc/falcon/filter.h create mode 100644 drivers/net/ethernet/sfc/falcon/io.h create mode 100644 drivers/net/ethernet/sfc/falcon/mdio_10g.c create mode 100644 drivers/net/ethernet/sfc/falcon/mdio_10g.h create mode 100644 drivers/net/ethernet/sfc/falcon/mtd.c create mode 100644 drivers/net/ethernet/sfc/falcon/net_driver.h create mode 100644 drivers/net/ethernet/sfc/falcon/nic.c create mode 100644 drivers/net/ethernet/sfc/falcon/nic.h create mode 100644 drivers/net/ethernet/sfc/falcon/phy.h create mode 100644 drivers/net/ethernet/sfc/falcon/qt202x_phy.c create mode 100644 drivers/net/ethernet/sfc/falcon/rx.c create mode 100644 drivers/net/ethernet/sfc/falcon/selftest.c create mode 100644 drivers/net/ethernet/sfc/falcon/selftest.h create mode 100644 drivers/net/ethernet/sfc/falcon/tenxpress.c create mode 100644 drivers/net/ethernet/sfc/falcon/tx.c create mode 100644 drivers/net/ethernet/sfc/falcon/tx.h create mode 100644 drivers/net/ethernet/sfc/falcon/txc43128_phy.c create mode 100644 drivers/net/ethernet/sfc/falcon/workarounds.h delete mode 100644 drivers/net/ethernet/sfc/falcon_boards.c delete mode 100644 drivers/net/ethernet/sfc/mdio_10g.c delete mode 100644 drivers/net/ethernet/sfc/mdio_10g.h delete mode 100644 drivers/net/ethernet/sfc/phy.h delete mode 100644 drivers/net/ethernet/sfc/qt202x_phy.c delete mode 100644 drivers/net/ethernet/sfc/tenxpress.c delete mode 100644 drivers/net/ethernet/sfc/txc43128_phy.c diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 8cc7467..6738fbb 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -165,6 +165,7 @@ source "drivers/net/ethernet/seeq/Kconfig" source "drivers/net/ethernet/silan/Kconfig" source "drivers/net/ethernet/sis/Kconfig" source "drivers/net/ethernet/sfc/Kconfig" +source "drivers/net/ethernet/sfc/falcon/Kconfig" source "drivers/net/ethernet/sgi/Kconfig" source "drivers/net/ethernet/smsc/Kconfig" source "drivers/net/ethernet/stmicro/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index a09423d..e762445 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/ obj-$(CONFIG_NET_VENDOR_SILAN) += silan/ obj-$(CONFIG_NET_VENDOR_SIS) += sis/ obj-$(CONFIG_SFC) += sfc/ +obj-$(CONFIG_SFC_FALCON) += sfc/falcon/ obj-$(CONFIG_NET_VENDOR_SGI) += sgi/ obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/ obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/ diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig index 4dd92b7..0e16197 100644 --- a/drivers/net/ethernet/sfc/Kconfig +++ b/drivers/net/ethernet/sfc/Kconfig @@ -1,20 +1,20 @@ config SFC - tristate "Solarflare SFC4000/SFC9000/SFC9100-family support" + tristate "Solarflare SFC9000/SFC9100-family support" depends on PCI select MDIO select CRC32 select I2C select I2C_ALGOBIT select PTP_1588_CLOCK + select SFC_FALCON ---help--- This driver supports 10/40-gigabit Ethernet cards based on - the Solarflare SFC4000, SFC9000-family and SFC9100-family - controllers. + the Solarflare SFC9000-family and SFC9100-family controllers. To compile this driver as a module, choose M here. The module will be called sfc. config SFC_MTD - bool "Solarflare SFC4000/SFC9000/SFC9100-family MTD support" + bool "Solarflare SFC9000/SFC9100-family MTD support" depends on SFC && MTD && !(SFC=y && MTD=m) default y ---help--- diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index b3b620f..520cfcc 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -1,7 +1,6 @@ -sfc-y += efx.o nic.o farch.o falcon.o siena.o ef10.o tx.o \ - rx.o selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ - tenxpress.o txc43128_phy.o falcon_boards.o \ - mcdi.o mcdi_port.o mcdi_mon.o ptp.o tx_tso.o +sfc-y += efx.o nic.o farch.o siena.o ef10.o tx.o rx.o \ + selftest.o ethtool.o ptp.o tx_tso.o \ + mcdi.o mcdi_port.o mcdi_mon.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o siena_sriov.o ef10_sriov.o diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 756a0964..4a234a8 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -733,16 +733,7 @@ static void efx_stop_datapath(struct efx_nic *efx) } rc = efx->type->fini_dmaq(efx); - if (rc && EFX_WORKAROUND_7803(efx)) { - /* Schedule a reset to recover from the flush failure. The - * descriptor caches reference memory we're about to free, - * but falcon_reconfigure_mac_wrapper() won't reconnect - * the MACs because of the pending reset. - */ - netif_err(efx, drv, efx->net_dev, - "Resetting to recover from flush failure\n"); - efx_schedule_reset(efx, RESET_TYPE_ALL); - } else if (rc) { + if (rc) { netif_err(efx, drv, efx->net_dev, "failed to flush queues\n"); } else { netif_dbg(efx, drv, efx->net_dev, @@ -1892,15 +1883,13 @@ static void efx_start_all(struct efx_nic *efx) queue_delayed_work(efx->workqueue, &efx->monitor_work, efx_monitor_interval); - /* If link state detection is normally event-driven, we have + /* Link state detection is normally event-driven; we have * to poll now because we could have missed a change */ - if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { - mutex_lock(&efx->mac_lock); - if (efx->phy_op->poll(efx)) - efx_link_status_changed(efx); - mutex_unlock(&efx->mac_lock); - } + mutex_lock(&efx->mac_lock); + if (efx->phy_op->poll(efx)) + efx_link_status_changed(efx); + mutex_unlock(&efx->mac_lock); efx->type->start_stats(efx); efx->type->pull_stats(efx); @@ -2842,12 +2831,6 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) /* PCI device ID table */ static const struct pci_device_id efx_pci_table[] = { - {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, - PCI_DEVICE_ID_SOLARFLARE_SFC4000A_0), - .driver_data = (unsigned long) &falcon_a1_nic_type}, - {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, - PCI_DEVICE_ID_SOLARFLARE_SFC4000B), - .driver_data = (unsigned long) &falcon_b0_nic_type}, {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0803), /* SFC9020 */ .driver_data = (unsigned long) &siena_a0_nic_type}, {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0813), /* SFL9021 */ diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 740cdf0..ca29d3d 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -169,9 +169,8 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev, strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); - if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) - efx_mcdi_print_fwver(efx, info->fw_version, - sizeof(info->fw_version)); + efx_mcdi_print_fwver(efx, info->fw_version, + sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); } @@ -966,8 +965,6 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, return 0; case ETHTOOL_GRXFH: { - unsigned min_revision = 0; - info->data = 0; switch (info->flow_type) { case UDP_V4_FLOW: @@ -980,7 +977,6 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, case AH_ESP_V4_FLOW: case IPV4_FLOW: info->data |= RXH_IP_SRC | RXH_IP_DST; - min_revision = EFX_REV_FALCON_B0; break; case UDP_V6_FLOW: if (efx->rx_hash_udp_4tuple) @@ -992,13 +988,10 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, case AH_ESP_V6_FLOW: case IPV6_FLOW: info->data |= RXH_IP_SRC | RXH_IP_DST; - min_revision = EFX_REV_SIENA_A0; break; default: break; } - if (efx_nic_rev(efx) < min_revision) - info->data = 0; return 0; } @@ -1271,9 +1264,7 @@ static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev) { struct efx_nic *efx = netdev_priv(net_dev); - return ((efx_nic_rev(efx) < EFX_REV_FALCON_B0 || - efx->n_rx_channels == 1) ? - 0 : ARRAY_SIZE(efx->rx_indir_table)); + return (efx->n_rx_channels == 1) ? 0 : ARRAY_SIZE(efx->rx_indir_table); } static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key, diff --git a/drivers/net/ethernet/sfc/falcon.c b/drivers/net/ethernet/sfc/falcon.c deleted file mode 100644 index 6a1e74b..0000000 --- a/drivers/net/ethernet/sfc/falcon.c +++ /dev/null @@ -1,2907 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2013 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "net_driver.h" -#include "bitfield.h" -#include "efx.h" -#include "nic.h" -#include "farch_regs.h" -#include "io.h" -#include "phy.h" -#include "workarounds.h" -#include "selftest.h" -#include "mdio_10g.h" - -/* Hardware control for SFC4000 (aka Falcon). */ - -/************************************************************************** - * - * NIC stats - * - ************************************************************************** - */ - -#define FALCON_MAC_STATS_SIZE 0x100 - -#define XgRxOctets_offset 0x0 -#define XgRxOctets_WIDTH 48 -#define XgRxOctetsOK_offset 0x8 -#define XgRxOctetsOK_WIDTH 48 -#define XgRxPkts_offset 0x10 -#define XgRxPkts_WIDTH 32 -#define XgRxPktsOK_offset 0x14 -#define XgRxPktsOK_WIDTH 32 -#define XgRxBroadcastPkts_offset 0x18 -#define XgRxBroadcastPkts_WIDTH 32 -#define XgRxMulticastPkts_offset 0x1C -#define XgRxMulticastPkts_WIDTH 32 -#define XgRxUnicastPkts_offset 0x20 -#define XgRxUnicastPkts_WIDTH 32 -#define XgRxUndersizePkts_offset 0x24 -#define XgRxUndersizePkts_WIDTH 32 -#define XgRxOversizePkts_offset 0x28 -#define XgRxOversizePkts_WIDTH 32 -#define XgRxJabberPkts_offset 0x2C -#define XgRxJabberPkts_WIDTH 32 -#define XgRxUndersizeFCSerrorPkts_offset 0x30 -#define XgRxUndersizeFCSerrorPkts_WIDTH 32 -#define XgRxDropEvents_offset 0x34 -#define XgRxDropEvents_WIDTH 32 -#define XgRxFCSerrorPkts_offset 0x38 -#define XgRxFCSerrorPkts_WIDTH 32 -#define XgRxAlignError_offset 0x3C -#define XgRxAlignError_WIDTH 32 -#define XgRxSymbolError_offset 0x40 -#define XgRxSymbolError_WIDTH 32 -#define XgRxInternalMACError_offset 0x44 -#define XgRxInternalMACError_WIDTH 32 -#define XgRxControlPkts_offset 0x48 -#define XgRxControlPkts_WIDTH 32 -#define XgRxPausePkts_offset 0x4C -#define XgRxPausePkts_WIDTH 32 -#define XgRxPkts64Octets_offset 0x50 -#define XgRxPkts64Octets_WIDTH 32 -#define XgRxPkts65to127Octets_offset 0x54 -#define XgRxPkts65to127Octets_WIDTH 32 -#define XgRxPkts128to255Octets_offset 0x58 -#define XgRxPkts128to255Octets_WIDTH 32 -#define XgRxPkts256to511Octets_offset 0x5C -#define XgRxPkts256to511Octets_WIDTH 32 -#define XgRxPkts512to1023Octets_offset 0x60 -#define XgRxPkts512to1023Octets_WIDTH 32 -#define XgRxPkts1024to15xxOctets_offset 0x64 -#define XgRxPkts1024to15xxOctets_WIDTH 32 -#define XgRxPkts15xxtoMaxOctets_offset 0x68 -#define XgRxPkts15xxtoMaxOctets_WIDTH 32 -#define XgRxLengthError_offset 0x6C -#define XgRxLengthError_WIDTH 32 -#define XgTxPkts_offset 0x80 -#define XgTxPkts_WIDTH 32 -#define XgTxOctets_offset 0x88 -#define XgTxOctets_WIDTH 48 -#define XgTxMulticastPkts_offset 0x90 -#define XgTxMulticastPkts_WIDTH 32 -#define XgTxBroadcastPkts_offset 0x94 -#define XgTxBroadcastPkts_WIDTH 32 -#define XgTxUnicastPkts_offset 0x98 -#define XgTxUnicastPkts_WIDTH 32 -#define XgTxControlPkts_offset 0x9C -#define XgTxControlPkts_WIDTH 32 -#define XgTxPausePkts_offset 0xA0 -#define XgTxPausePkts_WIDTH 32 -#define XgTxPkts64Octets_offset 0xA4 -#define XgTxPkts64Octets_WIDTH 32 -#define XgTxPkts65to127Octets_offset 0xA8 -#define XgTxPkts65to127Octets_WIDTH 32 -#define XgTxPkts128to255Octets_offset 0xAC -#define XgTxPkts128to255Octets_WIDTH 32 -#define XgTxPkts256to511Octets_offset 0xB0 -#define XgTxPkts256to511Octets_WIDTH 32 -#define XgTxPkts512to1023Octets_offset 0xB4 -#define XgTxPkts512to1023Octets_WIDTH 32 -#define XgTxPkts1024to15xxOctets_offset 0xB8 -#define XgTxPkts1024to15xxOctets_WIDTH 32 -#define XgTxPkts1519toMaxOctets_offset 0xBC -#define XgTxPkts1519toMaxOctets_WIDTH 32 -#define XgTxUndersizePkts_offset 0xC0 -#define XgTxUndersizePkts_WIDTH 32 -#define XgTxOversizePkts_offset 0xC4 -#define XgTxOversizePkts_WIDTH 32 -#define XgTxNonTcpUdpPkt_offset 0xC8 -#define XgTxNonTcpUdpPkt_WIDTH 16 -#define XgTxMacSrcErrPkt_offset 0xCC -#define XgTxMacSrcErrPkt_WIDTH 16 -#define XgTxIpSrcErrPkt_offset 0xD0 -#define XgTxIpSrcErrPkt_WIDTH 16 -#define XgDmaDone_offset 0xD4 -#define XgDmaDone_WIDTH 32 - -#define FALCON_XMAC_STATS_DMA_FLAG(efx) \ - (*(u32 *)((efx)->stats_buffer.addr + XgDmaDone_offset)) - -#define FALCON_DMA_STAT(ext_name, hw_name) \ - [FALCON_STAT_ ## ext_name] = \ - { #ext_name, \ - /* 48-bit stats are zero-padded to 64 on DMA */ \ - hw_name ## _ ## WIDTH == 48 ? 64 : hw_name ## _ ## WIDTH, \ - hw_name ## _ ## offset } -#define FALCON_OTHER_STAT(ext_name) \ - [FALCON_STAT_ ## ext_name] = { #ext_name, 0, 0 } -#define GENERIC_SW_STAT(ext_name) \ - [GENERIC_STAT_ ## ext_name] = { #ext_name, 0, 0 } - -static const struct efx_hw_stat_desc falcon_stat_desc[FALCON_STAT_COUNT] = { - FALCON_DMA_STAT(tx_bytes, XgTxOctets), - FALCON_DMA_STAT(tx_packets, XgTxPkts), - FALCON_DMA_STAT(tx_pause, XgTxPausePkts), - FALCON_DMA_STAT(tx_control, XgTxControlPkts), - FALCON_DMA_STAT(tx_unicast, XgTxUnicastPkts), - FALCON_DMA_STAT(tx_multicast, XgTxMulticastPkts), - FALCON_DMA_STAT(tx_broadcast, XgTxBroadcastPkts), - FALCON_DMA_STAT(tx_lt64, XgTxUndersizePkts), - FALCON_DMA_STAT(tx_64, XgTxPkts64Octets), - FALCON_DMA_STAT(tx_65_to_127, XgTxPkts65to127Octets), - FALCON_DMA_STAT(tx_128_to_255, XgTxPkts128to255Octets), - FALCON_DMA_STAT(tx_256_to_511, XgTxPkts256to511Octets), - FALCON_DMA_STAT(tx_512_to_1023, XgTxPkts512to1023Octets), - FALCON_DMA_STAT(tx_1024_to_15xx, XgTxPkts1024to15xxOctets), - FALCON_DMA_STAT(tx_15xx_to_jumbo, XgTxPkts1519toMaxOctets), - FALCON_DMA_STAT(tx_gtjumbo, XgTxOversizePkts), - FALCON_DMA_STAT(tx_non_tcpudp, XgTxNonTcpUdpPkt), - FALCON_DMA_STAT(tx_mac_src_error, XgTxMacSrcErrPkt), - FALCON_DMA_STAT(tx_ip_src_error, XgTxIpSrcErrPkt), - FALCON_DMA_STAT(rx_bytes, XgRxOctets), - FALCON_DMA_STAT(rx_good_bytes, XgRxOctetsOK), - FALCON_OTHER_STAT(rx_bad_bytes), - FALCON_DMA_STAT(rx_packets, XgRxPkts), - FALCON_DMA_STAT(rx_good, XgRxPktsOK), - FALCON_DMA_STAT(rx_bad, XgRxFCSerrorPkts), - FALCON_DMA_STAT(rx_pause, XgRxPausePkts), - FALCON_DMA_STAT(rx_control, XgRxControlPkts), - FALCON_DMA_STAT(rx_unicast, XgRxUnicastPkts), - FALCON_DMA_STAT(rx_multicast, XgRxMulticastPkts), - FALCON_DMA_STAT(rx_broadcast, XgRxBroadcastPkts), - FALCON_DMA_STAT(rx_lt64, XgRxUndersizePkts), - FALCON_DMA_STAT(rx_64, XgRxPkts64Octets), - FALCON_DMA_STAT(rx_65_to_127, XgRxPkts65to127Octets), - FALCON_DMA_STAT(rx_128_to_255, XgRxPkts128to255Octets), - FALCON_DMA_STAT(rx_256_to_511, XgRxPkts256to511Octets), - FALCON_DMA_STAT(rx_512_to_1023, XgRxPkts512to1023Octets), - FALCON_DMA_STAT(rx_1024_to_15xx, XgRxPkts1024to15xxOctets), - FALCON_DMA_STAT(rx_15xx_to_jumbo, XgRxPkts15xxtoMaxOctets), - FALCON_DMA_STAT(rx_gtjumbo, XgRxOversizePkts), - FALCON_DMA_STAT(rx_bad_lt64, XgRxUndersizeFCSerrorPkts), - FALCON_DMA_STAT(rx_bad_gtjumbo, XgRxJabberPkts), - FALCON_DMA_STAT(rx_overflow, XgRxDropEvents), - FALCON_DMA_STAT(rx_symbol_error, XgRxSymbolError), - FALCON_DMA_STAT(rx_align_error, XgRxAlignError), - FALCON_DMA_STAT(rx_length_error, XgRxLengthError), - FALCON_DMA_STAT(rx_internal_error, XgRxInternalMACError), - FALCON_OTHER_STAT(rx_nodesc_drop_cnt), - GENERIC_SW_STAT(rx_nodesc_trunc), - GENERIC_SW_STAT(rx_noskb_drops), -}; -static const unsigned long falcon_stat_mask[] = { - [0 ... BITS_TO_LONGS(FALCON_STAT_COUNT) - 1] = ~0UL, -}; - -/************************************************************************** - * - * Basic SPI command set and bit definitions - * - *************************************************************************/ - -#define SPI_WRSR 0x01 /* Write status register */ -#define SPI_WRITE 0x02 /* Write data to memory array */ -#define SPI_READ 0x03 /* Read data from memory array */ -#define SPI_WRDI 0x04 /* Reset write enable latch */ -#define SPI_RDSR 0x05 /* Read status register */ -#define SPI_WREN 0x06 /* Set write enable latch */ -#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */ - -#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */ -#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */ -#define SPI_STATUS_BP1 0x08 /* Block protection bit 1 */ -#define SPI_STATUS_BP0 0x04 /* Block protection bit 0 */ -#define SPI_STATUS_WEN 0x02 /* State of the write enable latch */ -#define SPI_STATUS_NRDY 0x01 /* Device busy flag */ - -/************************************************************************** - * - * Non-volatile memory layout - * - ************************************************************************** - */ - -/* SFC4000 flash is partitioned into: - * 0-0x400 chip and board config (see struct falcon_nvconfig) - * 0x400-0x8000 unused (or may contain VPD if EEPROM not present) - * 0x8000-end boot code (mapped to PCI expansion ROM) - * SFC4000 small EEPROM (size < 0x400) is used for VPD only. - * SFC4000 large EEPROM (size >= 0x400) is partitioned into: - * 0-0x400 chip and board config - * configurable VPD - * 0x800-0x1800 boot config - * Aside from the chip and board config, all of these are optional and may - * be absent or truncated depending on the devices used. - */ -#define FALCON_NVCONFIG_END 0x400U -#define FALCON_FLASH_BOOTCODE_START 0x8000U -#define FALCON_EEPROM_BOOTCONFIG_START 0x800U -#define FALCON_EEPROM_BOOTCONFIG_END 0x1800U - -/* Board configuration v2 (v1 is obsolete; later versions are compatible) */ -struct falcon_nvconfig_board_v2 { - __le16 nports; - u8 port0_phy_addr; - u8 port0_phy_type; - u8 port1_phy_addr; - u8 port1_phy_type; - __le16 asic_sub_revision; - __le16 board_revision; -} __packed; - -/* Board configuration v3 extra information */ -struct falcon_nvconfig_board_v3 { - __le32 spi_device_type[2]; -} __packed; - -/* Bit numbers for spi_device_type */ -#define SPI_DEV_TYPE_SIZE_LBN 0 -#define SPI_DEV_TYPE_SIZE_WIDTH 5 -#define SPI_DEV_TYPE_ADDR_LEN_LBN 6 -#define SPI_DEV_TYPE_ADDR_LEN_WIDTH 2 -#define SPI_DEV_TYPE_ERASE_CMD_LBN 8 -#define SPI_DEV_TYPE_ERASE_CMD_WIDTH 8 -#define SPI_DEV_TYPE_ERASE_SIZE_LBN 16 -#define SPI_DEV_TYPE_ERASE_SIZE_WIDTH 5 -#define SPI_DEV_TYPE_BLOCK_SIZE_LBN 24 -#define SPI_DEV_TYPE_BLOCK_SIZE_WIDTH 5 -#define SPI_DEV_TYPE_FIELD(type, field) \ - (((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field))) - -#define FALCON_NVCONFIG_OFFSET 0x300 - -#define FALCON_NVCONFIG_BOARD_MAGIC_NUM 0xFA1C -struct falcon_nvconfig { - efx_oword_t ee_vpd_cfg_reg; /* 0x300 */ - u8 mac_address[2][8]; /* 0x310 */ - efx_oword_t pcie_sd_ctl0123_reg; /* 0x320 */ - efx_oword_t pcie_sd_ctl45_reg; /* 0x330 */ - efx_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */ - efx_oword_t hw_init_reg; /* 0x350 */ - efx_oword_t nic_stat_reg; /* 0x360 */ - efx_oword_t glb_ctl_reg; /* 0x370 */ - efx_oword_t srm_cfg_reg; /* 0x380 */ - efx_oword_t spare_reg; /* 0x390 */ - __le16 board_magic_num; /* 0x3A0 */ - __le16 board_struct_ver; - __le16 board_checksum; - struct falcon_nvconfig_board_v2 board_v2; - efx_oword_t ee_base_page_reg; /* 0x3B0 */ - struct falcon_nvconfig_board_v3 board_v3; /* 0x3C0 */ -} __packed; - -/*************************************************************************/ - -static int falcon_reset_hw(struct efx_nic *efx, enum reset_type method); -static void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); - -static const unsigned int -/* "Large" EEPROM device: Atmel AT25640 or similar - * 8 KB, 16-bit address, 32 B write block */ -large_eeprom_type = ((13 << SPI_DEV_TYPE_SIZE_LBN) - | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN) - | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)), -/* Default flash device: Atmel AT25F1024 - * 128 KB, 24-bit address, 32 KB erase block, 256 B write block */ -default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN) - | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN) - | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN) - | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN) - | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)); - -/************************************************************************** - * - * I2C bus - this is a bit-bashing interface using GPIO pins - * Note that it uses the output enables to tristate the outputs - * SDA is the data pin and SCL is the clock - * - ************************************************************************** - */ -static void falcon_setsda(void *data, int state) -{ - struct efx_nic *efx = (struct efx_nic *)data; - efx_oword_t reg; - - efx_reado(efx, ®, FR_AB_GPIO_CTL); - EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, !state); - efx_writeo(efx, ®, FR_AB_GPIO_CTL); -} - -static void falcon_setscl(void *data, int state) -{ - struct efx_nic *efx = (struct efx_nic *)data; - efx_oword_t reg; - - efx_reado(efx, ®, FR_AB_GPIO_CTL); - EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO0_OEN, !state); - efx_writeo(efx, ®, FR_AB_GPIO_CTL); -} - -static int falcon_getsda(void *data) -{ - struct efx_nic *efx = (struct efx_nic *)data; - efx_oword_t reg; - - efx_reado(efx, ®, FR_AB_GPIO_CTL); - return EFX_OWORD_FIELD(reg, FRF_AB_GPIO3_IN); -} - -static int falcon_getscl(void *data) -{ - struct efx_nic *efx = (struct efx_nic *)data; - efx_oword_t reg; - - efx_reado(efx, ®, FR_AB_GPIO_CTL); - return EFX_OWORD_FIELD(reg, FRF_AB_GPIO0_IN); -} - -static const struct i2c_algo_bit_data falcon_i2c_bit_operations = { - .setsda = falcon_setsda, - .setscl = falcon_setscl, - .getsda = falcon_getsda, - .getscl = falcon_getscl, - .udelay = 5, - /* Wait up to 50 ms for slave to let us pull SCL high */ - .timeout = DIV_ROUND_UP(HZ, 20), -}; - -static void falcon_push_irq_moderation(struct efx_channel *channel) -{ - efx_dword_t timer_cmd; - struct efx_nic *efx = channel->efx; - - /* Set timer register */ - if (channel->irq_moderation_us) { - unsigned int ticks; - - ticks = efx_usecs_to_ticks(efx, channel->irq_moderation_us); - EFX_POPULATE_DWORD_2(timer_cmd, - FRF_AB_TC_TIMER_MODE, - FFE_BB_TIMER_MODE_INT_HLDOFF, - FRF_AB_TC_TIMER_VAL, - ticks - 1); - } else { - EFX_POPULATE_DWORD_2(timer_cmd, - FRF_AB_TC_TIMER_MODE, - FFE_BB_TIMER_MODE_DIS, - FRF_AB_TC_TIMER_VAL, 0); - } - BUILD_BUG_ON(FR_AA_TIMER_COMMAND_KER != FR_BZ_TIMER_COMMAND_P0); - efx_writed_page_locked(efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, - channel->channel); -} - -static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); - -static void falcon_prepare_flush(struct efx_nic *efx) -{ - falcon_deconfigure_mac_wrapper(efx); - - /* Wait for the tx and rx fifo's to get to the next packet boundary - * (~1ms without back-pressure), then to drain the remainder of the - * fifo's at data path speeds (negligible), with a healthy margin. */ - msleep(10); -} - -/* Acknowledge a legacy interrupt from Falcon - * - * This acknowledges a legacy (not MSI) interrupt via INT_ACK_KER_REG. - * - * Due to SFC bug 3706 (silicon revision <=A1) reads can be duplicated in the - * BIU. Interrupt acknowledge is read sensitive so must write instead - * (then read to ensure the BIU collector is flushed) - * - * NB most hardware supports MSI interrupts - */ -static inline void falcon_irq_ack_a1(struct efx_nic *efx) -{ - efx_dword_t reg; - - EFX_POPULATE_DWORD_1(reg, FRF_AA_INT_ACK_KER_FIELD, 0xb7eb7e); - efx_writed(efx, ®, FR_AA_INT_ACK_KER); - efx_readd(efx, ®, FR_AA_WORK_AROUND_BROKEN_PCI_READS); -} - -static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) -{ - struct efx_nic *efx = dev_id; - efx_oword_t *int_ker = efx->irq_status.addr; - int syserr; - int queues; - - /* Check to see if this is our interrupt. If it isn't, we - * exit without having touched the hardware. - */ - if (unlikely(EFX_OWORD_IS_ZERO(*int_ker))) { - netif_vdbg(efx, intr, efx->net_dev, - "IRQ %d on CPU %d not for me\n", irq, - raw_smp_processor_id()); - return IRQ_NONE; - } - efx->last_irq_cpu = raw_smp_processor_id(); - netif_vdbg(efx, intr, efx->net_dev, - "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", - irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); - - if (!likely(ACCESS_ONCE(efx->irq_soft_enabled))) - return IRQ_HANDLED; - - /* Check to see if we have a serious error condition */ - syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); - if (unlikely(syserr)) - return efx_farch_fatal_interrupt(efx); - - /* Determine interrupting queues, clear interrupt status - * register and acknowledge the device interrupt. - */ - BUILD_BUG_ON(FSF_AZ_NET_IVEC_INT_Q_WIDTH > EFX_MAX_CHANNELS); - queues = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_INT_Q); - EFX_ZERO_OWORD(*int_ker); - wmb(); /* Ensure the vector is cleared before interrupt ack */ - falcon_irq_ack_a1(efx); - - if (queues & 1) - efx_schedule_channel_irq(efx_get_channel(efx, 0)); - if (queues & 2) - efx_schedule_channel_irq(efx_get_channel(efx, 1)); - return IRQ_HANDLED; -} - -/************************************************************************** - * - * RSS - * - ************************************************************************** - */ -static int dummy_rx_push_rss_config(struct efx_nic *efx, bool user, - const u32 *rx_indir_table) -{ - (void) efx; - (void) user; - (void) rx_indir_table; - return -ENOSYS; -} - -static int falcon_b0_rx_push_rss_config(struct efx_nic *efx, bool user, - const u32 *rx_indir_table) -{ - efx_oword_t temp; - - (void) user; - /* Set hash key for IPv4 */ - memcpy(&temp, efx->rx_hash_key, sizeof(temp)); - efx_writeo(efx, &temp, FR_BZ_RX_RSS_TKEY); - - memcpy(efx->rx_indir_table, rx_indir_table, - sizeof(efx->rx_indir_table)); - efx_farch_rx_push_indir_table(efx); - return 0; -} - -/************************************************************************** - * - * EEPROM/flash - * - ************************************************************************** - */ - -#define FALCON_SPI_MAX_LEN sizeof(efx_oword_t) - -static int falcon_spi_poll(struct efx_nic *efx) -{ - efx_oword_t reg; - efx_reado(efx, ®, FR_AB_EE_SPI_HCMD); - return EFX_OWORD_FIELD(reg, FRF_AB_EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0; -} - -/* Wait for SPI command completion */ -static int falcon_spi_wait(struct efx_nic *efx) -{ - /* Most commands will finish quickly, so we start polling at - * very short intervals. Sometimes the command may have to - * wait for VPD or expansion ROM access outside of our - * control, so we allow up to 100 ms. */ - unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 10); - int i; - - for (i = 0; i < 10; i++) { - if (!falcon_spi_poll(efx)) - return 0; - udelay(10); - } - - for (;;) { - if (!falcon_spi_poll(efx)) - return 0; - if (time_after_eq(jiffies, timeout)) { - netif_err(efx, hw, efx->net_dev, - "timed out waiting for SPI\n"); - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } -} - -static int -falcon_spi_cmd(struct efx_nic *efx, const struct falcon_spi_device *spi, - unsigned int command, int address, - const void *in, void *out, size_t len) -{ - bool addressed = (address >= 0); - bool reading = (out != NULL); - efx_oword_t reg; - int rc; - - /* Input validation */ - if (len > FALCON_SPI_MAX_LEN) - return -EINVAL; - - /* Check that previous command is not still running */ - rc = falcon_spi_poll(efx); - if (rc) - return rc; - - /* Program address register, if we have an address */ - if (addressed) { - EFX_POPULATE_OWORD_1(reg, FRF_AB_EE_SPI_HADR_ADR, address); - efx_writeo(efx, ®, FR_AB_EE_SPI_HADR); - } - - /* Program data register, if we have data */ - if (in != NULL) { - memcpy(®, in, len); - efx_writeo(efx, ®, FR_AB_EE_SPI_HDATA); - } - - /* Issue read/write command */ - EFX_POPULATE_OWORD_7(reg, - FRF_AB_EE_SPI_HCMD_CMD_EN, 1, - FRF_AB_EE_SPI_HCMD_SF_SEL, spi->device_id, - FRF_AB_EE_SPI_HCMD_DABCNT, len, - FRF_AB_EE_SPI_HCMD_READ, reading, - FRF_AB_EE_SPI_HCMD_DUBCNT, 0, - FRF_AB_EE_SPI_HCMD_ADBCNT, - (addressed ? spi->addr_len : 0), - FRF_AB_EE_SPI_HCMD_ENC, command); - efx_writeo(efx, ®, FR_AB_EE_SPI_HCMD); - - /* Wait for read/write to complete */ - rc = falcon_spi_wait(efx); - if (rc) - return rc; - - /* Read data */ - if (out != NULL) { - efx_reado(efx, ®, FR_AB_EE_SPI_HDATA); - memcpy(out, ®, len); - } - - return 0; -} - -static inline u8 -falcon_spi_munge_command(const struct falcon_spi_device *spi, - const u8 command, const unsigned int address) -{ - return command | (((address >> 8) & spi->munge_address) << 3); -} - -static int -falcon_spi_read(struct efx_nic *efx, const struct falcon_spi_device *spi, - loff_t start, size_t len, size_t *retlen, u8 *buffer) -{ - size_t block_len, pos = 0; - unsigned int command; - int rc = 0; - - while (pos < len) { - block_len = min(len - pos, FALCON_SPI_MAX_LEN); - - command = falcon_spi_munge_command(spi, SPI_READ, start + pos); - rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, - buffer + pos, block_len); - if (rc) - break; - pos += block_len; - - /* Avoid locking up the system */ - cond_resched(); - if (signal_pending(current)) { - rc = -EINTR; - break; - } - } - - if (retlen) - *retlen = pos; - return rc; -} - -#ifdef CONFIG_SFC_MTD - -struct falcon_mtd_partition { - struct efx_mtd_partition common; - const struct falcon_spi_device *spi; - size_t offset; -}; - -#define to_falcon_mtd_partition(mtd) \ - container_of(mtd, struct falcon_mtd_partition, common.mtd) - -static size_t -falcon_spi_write_limit(const struct falcon_spi_device *spi, size_t start) -{ - return min(FALCON_SPI_MAX_LEN, - (spi->block_size - (start & (spi->block_size - 1)))); -} - -/* Wait up to 10 ms for buffered write completion */ -static int -falcon_spi_wait_write(struct efx_nic *efx, const struct falcon_spi_device *spi) -{ - unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100); - u8 status; - int rc; - - for (;;) { - rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, - &status, sizeof(status)); - if (rc) - return rc; - if (!(status & SPI_STATUS_NRDY)) - return 0; - if (time_after_eq(jiffies, timeout)) { - netif_err(efx, hw, efx->net_dev, - "SPI write timeout on device %d" - " last status=0x%02x\n", - spi->device_id, status); - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } -} - -static int -falcon_spi_write(struct efx_nic *efx, const struct falcon_spi_device *spi, - loff_t start, size_t len, size_t *retlen, const u8 *buffer) -{ - u8 verify_buffer[FALCON_SPI_MAX_LEN]; - size_t block_len, pos = 0; - unsigned int command; - int rc = 0; - - while (pos < len) { - rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); - if (rc) - break; - - block_len = min(len - pos, - falcon_spi_write_limit(spi, start + pos)); - command = falcon_spi_munge_command(spi, SPI_WRITE, start + pos); - rc = falcon_spi_cmd(efx, spi, command, start + pos, - buffer + pos, NULL, block_len); - if (rc) - break; - - rc = falcon_spi_wait_write(efx, spi); - if (rc) - break; - - command = falcon_spi_munge_command(spi, SPI_READ, start + pos); - rc = falcon_spi_cmd(efx, spi, command, start + pos, - NULL, verify_buffer, block_len); - if (memcmp(verify_buffer, buffer + pos, block_len)) { - rc = -EIO; - break; - } - - pos += block_len; - - /* Avoid locking up the system */ - cond_resched(); - if (signal_pending(current)) { - rc = -EINTR; - break; - } - } - - if (retlen) - *retlen = pos; - return rc; -} - -static int -falcon_spi_slow_wait(struct falcon_mtd_partition *part, bool uninterruptible) -{ - const struct falcon_spi_device *spi = part->spi; - struct efx_nic *efx = part->common.mtd.priv; - u8 status; - int rc, i; - - /* Wait up to 4s for flash/EEPROM to finish a slow operation. */ - for (i = 0; i < 40; i++) { - __set_current_state(uninterruptible ? - TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 10); - rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, - &status, sizeof(status)); - if (rc) - return rc; - if (!(status & SPI_STATUS_NRDY)) - return 0; - if (signal_pending(current)) - return -EINTR; - } - pr_err("%s: timed out waiting for %s\n", - part->common.name, part->common.dev_type_name); - return -ETIMEDOUT; -} - -static int -falcon_spi_unlock(struct efx_nic *efx, const struct falcon_spi_device *spi) -{ - const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | - SPI_STATUS_BP0); - u8 status; - int rc; - - rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, - &status, sizeof(status)); - if (rc) - return rc; - - if (!(status & unlock_mask)) - return 0; /* already unlocked */ - - rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); - if (rc) - return rc; - rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0); - if (rc) - return rc; - - status &= ~unlock_mask; - rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status, - NULL, sizeof(status)); - if (rc) - return rc; - rc = falcon_spi_wait_write(efx, spi); - if (rc) - return rc; - - return 0; -} - -#define FALCON_SPI_VERIFY_BUF_LEN 16 - -static int -falcon_spi_erase(struct falcon_mtd_partition *part, loff_t start, size_t len) -{ - const struct falcon_spi_device *spi = part->spi; - struct efx_nic *efx = part->common.mtd.priv; - unsigned pos, block_len; - u8 empty[FALCON_SPI_VERIFY_BUF_LEN]; - u8 buffer[FALCON_SPI_VERIFY_BUF_LEN]; - int rc; - - if (len != spi->erase_size) - return -EINVAL; - - if (spi->erase_command == 0) - return -EOPNOTSUPP; - - rc = falcon_spi_unlock(efx, spi); - if (rc) - return rc; - rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); - if (rc) - return rc; - rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL, - NULL, 0); - if (rc) - return rc; - rc = falcon_spi_slow_wait(part, false); - - /* Verify the entire region has been wiped */ - memset(empty, 0xff, sizeof(empty)); - for (pos = 0; pos < len; pos += block_len) { - block_len = min(len - pos, sizeof(buffer)); - rc = falcon_spi_read(efx, spi, start + pos, block_len, - NULL, buffer); - if (rc) - return rc; - if (memcmp(empty, buffer, block_len)) - return -EIO; - - /* Avoid locking up the system */ - cond_resched(); - if (signal_pending(current)) - return -EINTR; - } - - return rc; -} - -static void falcon_mtd_rename(struct efx_mtd_partition *part) -{ - struct efx_nic *efx = part->mtd.priv; - - snprintf(part->name, sizeof(part->name), "%s %s", - efx->name, part->type_name); -} - -static int falcon_mtd_read(struct mtd_info *mtd, loff_t start, - size_t len, size_t *retlen, u8 *buffer) -{ - struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); - struct efx_nic *efx = mtd->priv; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - rc = mutex_lock_interruptible(&nic_data->spi_lock); - if (rc) - return rc; - rc = falcon_spi_read(efx, part->spi, part->offset + start, - len, retlen, buffer); - mutex_unlock(&nic_data->spi_lock); - return rc; -} - -static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) -{ - struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); - struct efx_nic *efx = mtd->priv; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - rc = mutex_lock_interruptible(&nic_data->spi_lock); - if (rc) - return rc; - rc = falcon_spi_erase(part, part->offset + start, len); - mutex_unlock(&nic_data->spi_lock); - return rc; -} - -static int falcon_mtd_write(struct mtd_info *mtd, loff_t start, - size_t len, size_t *retlen, const u8 *buffer) -{ - struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); - struct efx_nic *efx = mtd->priv; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - rc = mutex_lock_interruptible(&nic_data->spi_lock); - if (rc) - return rc; - rc = falcon_spi_write(efx, part->spi, part->offset + start, - len, retlen, buffer); - mutex_unlock(&nic_data->spi_lock); - return rc; -} - -static int falcon_mtd_sync(struct mtd_info *mtd) -{ - struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); - struct efx_nic *efx = mtd->priv; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - mutex_lock(&nic_data->spi_lock); - rc = falcon_spi_slow_wait(part, true); - mutex_unlock(&nic_data->spi_lock); - return rc; -} - -static int falcon_mtd_probe(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - struct falcon_mtd_partition *parts; - struct falcon_spi_device *spi; - size_t n_parts; - int rc = -ENODEV; - - ASSERT_RTNL(); - - /* Allocate space for maximum number of partitions */ - parts = kcalloc(2, sizeof(*parts), GFP_KERNEL); - if (!parts) - return -ENOMEM; - n_parts = 0; - - spi = &nic_data->spi_flash; - if (falcon_spi_present(spi) && spi->size > FALCON_FLASH_BOOTCODE_START) { - parts[n_parts].spi = spi; - parts[n_parts].offset = FALCON_FLASH_BOOTCODE_START; - parts[n_parts].common.dev_type_name = "flash"; - parts[n_parts].common.type_name = "sfc_flash_bootrom"; - parts[n_parts].common.mtd.type = MTD_NORFLASH; - parts[n_parts].common.mtd.flags = MTD_CAP_NORFLASH; - parts[n_parts].common.mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; - parts[n_parts].common.mtd.erasesize = spi->erase_size; - n_parts++; - } - - spi = &nic_data->spi_eeprom; - if (falcon_spi_present(spi) && spi->size > FALCON_EEPROM_BOOTCONFIG_START) { - parts[n_parts].spi = spi; - parts[n_parts].offset = FALCON_EEPROM_BOOTCONFIG_START; - parts[n_parts].common.dev_type_name = "EEPROM"; - parts[n_parts].common.type_name = "sfc_bootconfig"; - parts[n_parts].common.mtd.type = MTD_RAM; - parts[n_parts].common.mtd.flags = MTD_CAP_RAM; - parts[n_parts].common.mtd.size = - min(spi->size, FALCON_EEPROM_BOOTCONFIG_END) - - FALCON_EEPROM_BOOTCONFIG_START; - parts[n_parts].common.mtd.erasesize = spi->erase_size; - n_parts++; - } - - rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts)); - if (rc) - kfree(parts); - return rc; -} - -#endif /* CONFIG_SFC_MTD */ - -/************************************************************************** - * - * XMAC operations - * - ************************************************************************** - */ - -/* Configure the XAUI driver that is an output from Falcon */ -static void falcon_setup_xaui(struct efx_nic *efx) -{ - efx_oword_t sdctl, txdrv; - - /* Move the XAUI into low power, unless there is no PHY, in - * which case the XAUI will have to drive a cable. */ - if (efx->phy_type == PHY_TYPE_NONE) - return; - - efx_reado(efx, &sdctl, FR_AB_XX_SD_CTL); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVD, FFE_AB_XX_SD_CTL_DRV_DEF); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVD, FFE_AB_XX_SD_CTL_DRV_DEF); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVC, FFE_AB_XX_SD_CTL_DRV_DEF); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVC, FFE_AB_XX_SD_CTL_DRV_DEF); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVB, FFE_AB_XX_SD_CTL_DRV_DEF); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVB, FFE_AB_XX_SD_CTL_DRV_DEF); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVA, FFE_AB_XX_SD_CTL_DRV_DEF); - EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVA, FFE_AB_XX_SD_CTL_DRV_DEF); - efx_writeo(efx, &sdctl, FR_AB_XX_SD_CTL); - - EFX_POPULATE_OWORD_8(txdrv, - FRF_AB_XX_DEQD, FFE_AB_XX_TXDRV_DEQ_DEF, - FRF_AB_XX_DEQC, FFE_AB_XX_TXDRV_DEQ_DEF, - FRF_AB_XX_DEQB, FFE_AB_XX_TXDRV_DEQ_DEF, - FRF_AB_XX_DEQA, FFE_AB_XX_TXDRV_DEQ_DEF, - FRF_AB_XX_DTXD, FFE_AB_XX_TXDRV_DTX_DEF, - FRF_AB_XX_DTXC, FFE_AB_XX_TXDRV_DTX_DEF, - FRF_AB_XX_DTXB, FFE_AB_XX_TXDRV_DTX_DEF, - FRF_AB_XX_DTXA, FFE_AB_XX_TXDRV_DTX_DEF); - efx_writeo(efx, &txdrv, FR_AB_XX_TXDRV_CTL); -} - -int falcon_reset_xaui(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t reg; - int count; - - /* Don't fetch MAC statistics over an XMAC reset */ - WARN_ON(nic_data->stats_disable_count == 0); - - /* Start reset sequence */ - EFX_POPULATE_OWORD_1(reg, FRF_AB_XX_RST_XX_EN, 1); - efx_writeo(efx, ®, FR_AB_XX_PWR_RST); - - /* Wait up to 10 ms for completion, then reinitialise */ - for (count = 0; count < 1000; count++) { - efx_reado(efx, ®, FR_AB_XX_PWR_RST); - if (EFX_OWORD_FIELD(reg, FRF_AB_XX_RST_XX_EN) == 0 && - EFX_OWORD_FIELD(reg, FRF_AB_XX_SD_RST_ACT) == 0) { - falcon_setup_xaui(efx); - return 0; - } - udelay(10); - } - netif_err(efx, hw, efx->net_dev, - "timed out waiting for XAUI/XGXS reset\n"); - return -ETIMEDOUT; -} - -static void falcon_ack_status_intr(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t reg; - - if ((efx_nic_rev(efx) != EFX_REV_FALCON_B0) || LOOPBACK_INTERNAL(efx)) - return; - - /* We expect xgmii faults if the wireside link is down */ - if (!efx->link_state.up) - return; - - /* We can only use this interrupt to signal the negative edge of - * xaui_align [we have to poll the positive edge]. */ - if (nic_data->xmac_poll_required) - return; - - efx_reado(efx, ®, FR_AB_XM_MGT_INT_MSK); -} - -static bool falcon_xgxs_link_ok(struct efx_nic *efx) -{ - efx_oword_t reg; - bool align_done, link_ok = false; - int sync_status; - - /* Read link status */ - efx_reado(efx, ®, FR_AB_XX_CORE_STAT); - - align_done = EFX_OWORD_FIELD(reg, FRF_AB_XX_ALIGN_DONE); - sync_status = EFX_OWORD_FIELD(reg, FRF_AB_XX_SYNC_STAT); - if (align_done && (sync_status == FFE_AB_XX_STAT_ALL_LANES)) - link_ok = true; - - /* Clear link status ready for next read */ - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_COMMA_DET, FFE_AB_XX_STAT_ALL_LANES); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_CHAR_ERR, FFE_AB_XX_STAT_ALL_LANES); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_DISPERR, FFE_AB_XX_STAT_ALL_LANES); - efx_writeo(efx, ®, FR_AB_XX_CORE_STAT); - - return link_ok; -} - -static bool falcon_xmac_link_ok(struct efx_nic *efx) -{ - /* - * Check MAC's XGXS link status except when using XGMII loopback - * which bypasses the XGXS block. - * If possible, check PHY's XGXS link status except when using - * MAC loopback. - */ - return (efx->loopback_mode == LOOPBACK_XGMII || - falcon_xgxs_link_ok(efx)) && - (!(efx->mdio.mmds & (1 << MDIO_MMD_PHYXS)) || - LOOPBACK_INTERNAL(efx) || - efx_mdio_phyxgxs_lane_sync(efx)); -} - -static void falcon_reconfigure_xmac_core(struct efx_nic *efx) -{ - unsigned int max_frame_len; - efx_oword_t reg; - bool rx_fc = !!(efx->link_state.fc & EFX_FC_RX); - bool tx_fc = !!(efx->link_state.fc & EFX_FC_TX); - - /* Configure MAC - cut-thru mode is hard wired on */ - EFX_POPULATE_OWORD_3(reg, - FRF_AB_XM_RX_JUMBO_MODE, 1, - FRF_AB_XM_TX_STAT_EN, 1, - FRF_AB_XM_RX_STAT_EN, 1); - efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); - - /* Configure TX */ - EFX_POPULATE_OWORD_6(reg, - FRF_AB_XM_TXEN, 1, - FRF_AB_XM_TX_PRMBL, 1, - FRF_AB_XM_AUTO_PAD, 1, - FRF_AB_XM_TXCRC, 1, - FRF_AB_XM_FCNTL, tx_fc, - FRF_AB_XM_IPG, 0x3); - efx_writeo(efx, ®, FR_AB_XM_TX_CFG); - - /* Configure RX */ - EFX_POPULATE_OWORD_5(reg, - FRF_AB_XM_RXEN, 1, - FRF_AB_XM_AUTO_DEPAD, 0, - FRF_AB_XM_ACPT_ALL_MCAST, 1, - FRF_AB_XM_ACPT_ALL_UCAST, !efx->unicast_filter, - FRF_AB_XM_PASS_CRC_ERR, 1); - efx_writeo(efx, ®, FR_AB_XM_RX_CFG); - - /* Set frame length */ - max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); - EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_MAX_RX_FRM_SIZE, max_frame_len); - efx_writeo(efx, ®, FR_AB_XM_RX_PARAM); - EFX_POPULATE_OWORD_2(reg, - FRF_AB_XM_MAX_TX_FRM_SIZE, max_frame_len, - FRF_AB_XM_TX_JUMBO_MODE, 1); - efx_writeo(efx, ®, FR_AB_XM_TX_PARAM); - - EFX_POPULATE_OWORD_2(reg, - FRF_AB_XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ - FRF_AB_XM_DIS_FCNTL, !rx_fc); - efx_writeo(efx, ®, FR_AB_XM_FC); - - /* Set MAC address */ - memcpy(®, &efx->net_dev->dev_addr[0], 4); - efx_writeo(efx, ®, FR_AB_XM_ADR_LO); - memcpy(®, &efx->net_dev->dev_addr[4], 2); - efx_writeo(efx, ®, FR_AB_XM_ADR_HI); -} - -static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) -{ - efx_oword_t reg; - bool xgxs_loopback = (efx->loopback_mode == LOOPBACK_XGXS); - bool xaui_loopback = (efx->loopback_mode == LOOPBACK_XAUI); - bool xgmii_loopback = (efx->loopback_mode == LOOPBACK_XGMII); - bool old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback; - - /* XGXS block is flaky and will need to be reset if moving - * into our out of XGMII, XGXS or XAUI loopbacks. */ - efx_reado(efx, ®, FR_AB_XX_CORE_STAT); - old_xgxs_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN); - old_xgmii_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN); - - efx_reado(efx, ®, FR_AB_XX_SD_CTL); - old_xaui_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_LPBKA); - - /* The PHY driver may have turned XAUI off */ - if ((xgxs_loopback != old_xgxs_loopback) || - (xaui_loopback != old_xaui_loopback) || - (xgmii_loopback != old_xgmii_loopback)) - falcon_reset_xaui(efx); - - efx_reado(efx, ®, FR_AB_XX_CORE_STAT); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_FORCE_SIG, - (xgxs_loopback || xaui_loopback) ? - FFE_AB_XX_FORCE_SIG_ALL_LANES : 0); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN, xgxs_loopback); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN, xgmii_loopback); - efx_writeo(efx, ®, FR_AB_XX_CORE_STAT); - - efx_reado(efx, ®, FR_AB_XX_SD_CTL); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKD, xaui_loopback); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKC, xaui_loopback); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKB, xaui_loopback); - EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKA, xaui_loopback); - efx_writeo(efx, ®, FR_AB_XX_SD_CTL); -} - - -/* Try to bring up the Falcon side of the Falcon-Phy XAUI link */ -static bool falcon_xmac_link_ok_retry(struct efx_nic *efx, int tries) -{ - bool mac_up = falcon_xmac_link_ok(efx); - - if (LOOPBACK_MASK(efx) & LOOPBACKS_EXTERNAL(efx) & LOOPBACKS_WS || - efx_phy_mode_disabled(efx->phy_mode)) - /* XAUI link is expected to be down */ - return mac_up; - - falcon_stop_nic_stats(efx); - - while (!mac_up && tries) { - netif_dbg(efx, hw, efx->net_dev, "bashing xaui\n"); - falcon_reset_xaui(efx); - udelay(200); - - mac_up = falcon_xmac_link_ok(efx); - --tries; - } - - falcon_start_nic_stats(efx); - - return mac_up; -} - -static bool falcon_xmac_check_fault(struct efx_nic *efx) -{ - return !falcon_xmac_link_ok_retry(efx, 5); -} - -static int falcon_reconfigure_xmac(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - - efx_farch_filter_sync_rx_mode(efx); - - falcon_reconfigure_xgxs_core(efx); - falcon_reconfigure_xmac_core(efx); - - falcon_reconfigure_mac_wrapper(efx); - - nic_data->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 5); - falcon_ack_status_intr(efx); - - return 0; -} - -static void falcon_poll_xmac(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - - /* We expect xgmii faults if the wireside link is down */ - if (!efx->link_state.up || !nic_data->xmac_poll_required) - return; - - nic_data->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 1); - falcon_ack_status_intr(efx); -} - -/************************************************************************** - * - * MAC wrapper - * - ************************************************************************** - */ - -static void falcon_push_multicast_hash(struct efx_nic *efx) -{ - union efx_multicast_hash *mc_hash = &efx->multicast_hash; - - WARN_ON(!mutex_is_locked(&efx->mac_lock)); - - efx_writeo(efx, &mc_hash->oword[0], FR_AB_MAC_MC_HASH_REG0); - efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1); -} - -static void falcon_reset_macs(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t reg, mac_ctrl; - int count; - - if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { - /* It's not safe to use GLB_CTL_REG to reset the - * macs, so instead use the internal MAC resets - */ - EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1); - efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); - - for (count = 0; count < 10000; count++) { - efx_reado(efx, ®, FR_AB_XM_GLB_CFG); - if (EFX_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) == - 0) - return; - udelay(10); - } - - netif_err(efx, hw, efx->net_dev, - "timed out waiting for XMAC core reset\n"); - } - - /* Mac stats will fail whist the TX fifo is draining */ - WARN_ON(nic_data->stats_disable_count == 0); - - efx_reado(efx, &mac_ctrl, FR_AB_MAC_CTRL); - EFX_SET_OWORD_FIELD(mac_ctrl, FRF_BB_TXFIFO_DRAIN_EN, 1); - efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); - - efx_reado(efx, ®, FR_AB_GLB_CTL); - EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1); - EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGRX, 1); - EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_EM, 1); - efx_writeo(efx, ®, FR_AB_GLB_CTL); - - count = 0; - while (1) { - efx_reado(efx, ®, FR_AB_GLB_CTL); - if (!EFX_OWORD_FIELD(reg, FRF_AB_RST_XGTX) && - !EFX_OWORD_FIELD(reg, FRF_AB_RST_XGRX) && - !EFX_OWORD_FIELD(reg, FRF_AB_RST_EM)) { - netif_dbg(efx, hw, efx->net_dev, - "Completed MAC reset after %d loops\n", - count); - break; - } - if (count > 20) { - netif_err(efx, hw, efx->net_dev, "MAC reset failed\n"); - break; - } - count++; - udelay(10); - } - - /* Ensure the correct MAC is selected before statistics - * are re-enabled by the caller */ - efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); - - falcon_setup_xaui(efx); -} - -static void falcon_drain_tx_fifo(struct efx_nic *efx) -{ - efx_oword_t reg; - - if ((efx_nic_rev(efx) < EFX_REV_FALCON_B0) || - (efx->loopback_mode != LOOPBACK_NONE)) - return; - - efx_reado(efx, ®, FR_AB_MAC_CTRL); - /* There is no point in draining more than once */ - if (EFX_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN)) - return; - - falcon_reset_macs(efx); -} - -static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) -{ - efx_oword_t reg; - - if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) - return; - - /* Isolate the MAC -> RX */ - efx_reado(efx, ®, FR_AZ_RX_CFG); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0); - efx_writeo(efx, ®, FR_AZ_RX_CFG); - - /* Isolate TX -> MAC */ - falcon_drain_tx_fifo(efx); -} - -static void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) -{ - struct efx_link_state *link_state = &efx->link_state; - efx_oword_t reg; - int link_speed, isolate; - - isolate = !!ACCESS_ONCE(efx->reset_pending); - - switch (link_state->speed) { - case 10000: link_speed = 3; break; - case 1000: link_speed = 2; break; - case 100: link_speed = 1; break; - default: link_speed = 0; break; - } - - /* MAC_LINK_STATUS controls MAC backpressure but doesn't work - * as advertised. Disable to ensure packets are not - * indefinitely held and TX queue can be flushed at any point - * while the link is down. */ - EFX_POPULATE_OWORD_5(reg, - FRF_AB_MAC_XOFF_VAL, 0xffff /* max pause time */, - FRF_AB_MAC_BCAD_ACPT, 1, - FRF_AB_MAC_UC_PROM, !efx->unicast_filter, - FRF_AB_MAC_LINK_STATUS, 1, /* always set */ - FRF_AB_MAC_SPEED, link_speed); - /* On B0, MAC backpressure can be disabled and packets get - * discarded. */ - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { - EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, - !link_state->up || isolate); - } - - efx_writeo(efx, ®, FR_AB_MAC_CTRL); - - /* Restore the multicast hash registers. */ - falcon_push_multicast_hash(efx); - - efx_reado(efx, ®, FR_AZ_RX_CFG); - /* Enable XOFF signal from RX FIFO (we enabled it during NIC - * initialisation but it may read back as 0) */ - EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); - /* Unisolate the MAC -> RX */ - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, !isolate); - efx_writeo(efx, ®, FR_AZ_RX_CFG); -} - -static void falcon_stats_request(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t reg; - - WARN_ON(nic_data->stats_pending); - WARN_ON(nic_data->stats_disable_count); - - FALCON_XMAC_STATS_DMA_FLAG(efx) = 0; - nic_data->stats_pending = true; - wmb(); /* ensure done flag is clear */ - - /* Initiate DMA transfer of stats */ - EFX_POPULATE_OWORD_2(reg, - FRF_AB_MAC_STAT_DMA_CMD, 1, - FRF_AB_MAC_STAT_DMA_ADR, - efx->stats_buffer.dma_addr); - efx_writeo(efx, ®, FR_AB_MAC_STAT_DMA); - - mod_timer(&nic_data->stats_timer, round_jiffies_up(jiffies + HZ / 2)); -} - -static void falcon_stats_complete(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - - if (!nic_data->stats_pending) - return; - - nic_data->stats_pending = false; - if (FALCON_XMAC_STATS_DMA_FLAG(efx)) { - rmb(); /* read the done flag before the stats */ - efx_nic_update_stats(falcon_stat_desc, FALCON_STAT_COUNT, - falcon_stat_mask, nic_data->stats, - efx->stats_buffer.addr, true); - } else { - netif_err(efx, hw, efx->net_dev, - "timed out waiting for statistics\n"); - } -} - -static void falcon_stats_timer_func(unsigned long context) -{ - struct efx_nic *efx = (struct efx_nic *)context; - struct falcon_nic_data *nic_data = efx->nic_data; - - spin_lock(&efx->stats_lock); - - falcon_stats_complete(efx); - if (nic_data->stats_disable_count == 0) - falcon_stats_request(efx); - - spin_unlock(&efx->stats_lock); -} - -static bool falcon_loopback_link_poll(struct efx_nic *efx) -{ - struct efx_link_state old_state = efx->link_state; - - WARN_ON(!mutex_is_locked(&efx->mac_lock)); - WARN_ON(!LOOPBACK_INTERNAL(efx)); - - efx->link_state.fd = true; - efx->link_state.fc = efx->wanted_fc; - efx->link_state.up = true; - efx->link_state.speed = 10000; - - return !efx_link_state_equal(&efx->link_state, &old_state); -} - -static int falcon_reconfigure_port(struct efx_nic *efx) -{ - int rc; - - WARN_ON(efx_nic_rev(efx) > EFX_REV_FALCON_B0); - - /* Poll the PHY link state *before* reconfiguring it. This means we - * will pick up the correct speed (in loopback) to select the correct - * MAC. - */ - if (LOOPBACK_INTERNAL(efx)) - falcon_loopback_link_poll(efx); - else - efx->phy_op->poll(efx); - - falcon_stop_nic_stats(efx); - falcon_deconfigure_mac_wrapper(efx); - - falcon_reset_macs(efx); - - efx->phy_op->reconfigure(efx); - rc = falcon_reconfigure_xmac(efx); - BUG_ON(rc); - - falcon_start_nic_stats(efx); - - /* Synchronise efx->link_state with the kernel */ - efx_link_status_changed(efx); - - return 0; -} - -/* TX flow control may automatically turn itself off if the link - * partner (intermittently) stops responding to pause frames. There - * isn't any indication that this has happened, so the best we do is - * leave it up to the user to spot this and fix it by cycling transmit - * flow control on this end. - */ - -static void falcon_a1_prepare_enable_fc_tx(struct efx_nic *efx) -{ - /* Schedule a reset to recover */ - efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); -} - -static void falcon_b0_prepare_enable_fc_tx(struct efx_nic *efx) -{ - /* Recover by resetting the EM block */ - falcon_stop_nic_stats(efx); - falcon_drain_tx_fifo(efx); - falcon_reconfigure_xmac(efx); - falcon_start_nic_stats(efx); -} - -/************************************************************************** - * - * PHY access via GMII - * - ************************************************************************** - */ - -/* Wait for GMII access to complete */ -static int falcon_gmii_wait(struct efx_nic *efx) -{ - efx_oword_t md_stat; - int count; - - /* wait up to 50ms - taken max from datasheet */ - for (count = 0; count < 5000; count++) { - efx_reado(efx, &md_stat, FR_AB_MD_STAT); - if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSY) == 0) { - if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_LNFL) != 0 || - EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSERR) != 0) { - netif_err(efx, hw, efx->net_dev, - "error from GMII access " - EFX_OWORD_FMT"\n", - EFX_OWORD_VAL(md_stat)); - return -EIO; - } - return 0; - } - udelay(10); - } - netif_err(efx, hw, efx->net_dev, "timed out waiting for GMII\n"); - return -ETIMEDOUT; -} - -/* Write an MDIO register of a PHY connected to Falcon. */ -static int falcon_mdio_write(struct net_device *net_dev, - int prtad, int devad, u16 addr, u16 value) -{ - struct efx_nic *efx = netdev_priv(net_dev); - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t reg; - int rc; - - netif_vdbg(efx, hw, efx->net_dev, - "writing MDIO %d register %d.%d with 0x%04x\n", - prtad, devad, addr, value); - - mutex_lock(&nic_data->mdio_lock); - - /* Check MDIO not currently being accessed */ - rc = falcon_gmii_wait(efx); - if (rc) - goto out; - - /* Write the address/ID register */ - EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr); - efx_writeo(efx, ®, FR_AB_MD_PHY_ADR); - - EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, - FRF_AB_MD_DEV_ADR, devad); - efx_writeo(efx, ®, FR_AB_MD_ID); - - /* Write data */ - EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_TXD, value); - efx_writeo(efx, ®, FR_AB_MD_TXD); - - EFX_POPULATE_OWORD_2(reg, - FRF_AB_MD_WRC, 1, - FRF_AB_MD_GC, 0); - efx_writeo(efx, ®, FR_AB_MD_CS); - - /* Wait for data to be written */ - rc = falcon_gmii_wait(efx); - if (rc) { - /* Abort the write operation */ - EFX_POPULATE_OWORD_2(reg, - FRF_AB_MD_WRC, 0, - FRF_AB_MD_GC, 1); - efx_writeo(efx, ®, FR_AB_MD_CS); - udelay(10); - } - -out: - mutex_unlock(&nic_data->mdio_lock); - return rc; -} - -/* Read an MDIO register of a PHY connected to Falcon. */ -static int falcon_mdio_read(struct net_device *net_dev, - int prtad, int devad, u16 addr) -{ - struct efx_nic *efx = netdev_priv(net_dev); - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t reg; - int rc; - - mutex_lock(&nic_data->mdio_lock); - - /* Check MDIO not currently being accessed */ - rc = falcon_gmii_wait(efx); - if (rc) - goto out; - - EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr); - efx_writeo(efx, ®, FR_AB_MD_PHY_ADR); - - EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, - FRF_AB_MD_DEV_ADR, devad); - efx_writeo(efx, ®, FR_AB_MD_ID); - - /* Request data to be read */ - EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_RDC, 1, FRF_AB_MD_GC, 0); - efx_writeo(efx, ®, FR_AB_MD_CS); - - /* Wait for data to become available */ - rc = falcon_gmii_wait(efx); - if (rc == 0) { - efx_reado(efx, ®, FR_AB_MD_RXD); - rc = EFX_OWORD_FIELD(reg, FRF_AB_MD_RXD); - netif_vdbg(efx, hw, efx->net_dev, - "read from MDIO %d register %d.%d, got %04x\n", - prtad, devad, addr, rc); - } else { - /* Abort the read operation */ - EFX_POPULATE_OWORD_2(reg, - FRF_AB_MD_RIC, 0, - FRF_AB_MD_GC, 1); - efx_writeo(efx, ®, FR_AB_MD_CS); - - netif_dbg(efx, hw, efx->net_dev, - "read from MDIO %d register %d.%d, got error %d\n", - prtad, devad, addr, rc); - } - -out: - mutex_unlock(&nic_data->mdio_lock); - return rc; -} - -/* This call is responsible for hooking in the MAC and PHY operations */ -static int falcon_probe_port(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - switch (efx->phy_type) { - case PHY_TYPE_SFX7101: - efx->phy_op = &falcon_sfx7101_phy_ops; - break; - case PHY_TYPE_QT2022C2: - case PHY_TYPE_QT2025C: - efx->phy_op = &falcon_qt202x_phy_ops; - break; - case PHY_TYPE_TXC43128: - efx->phy_op = &falcon_txc_phy_ops; - break; - default: - netif_err(efx, probe, efx->net_dev, "Unknown PHY type %d\n", - efx->phy_type); - return -ENODEV; - } - - /* Fill out MDIO structure and loopback modes */ - mutex_init(&nic_data->mdio_lock); - efx->mdio.mdio_read = falcon_mdio_read; - efx->mdio.mdio_write = falcon_mdio_write; - rc = efx->phy_op->probe(efx); - if (rc != 0) - return rc; - - /* Initial assumption */ - efx->link_state.speed = 10000; - efx->link_state.fd = true; - - /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) - efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; - else - efx->wanted_fc = EFX_FC_RX; - if (efx->mdio.mmds & MDIO_DEVS_AN) - efx->wanted_fc |= EFX_FC_AUTO; - - /* Allocate buffer for stats */ - rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, - FALCON_MAC_STATS_SIZE, GFP_KERNEL); - if (rc) - return rc; - netif_dbg(efx, probe, efx->net_dev, - "stats buffer at %llx (virt %p phys %llx)\n", - (u64)efx->stats_buffer.dma_addr, - efx->stats_buffer.addr, - (u64)virt_to_phys(efx->stats_buffer.addr)); - - return 0; -} - -static void falcon_remove_port(struct efx_nic *efx) -{ - efx->phy_op->remove(efx); - efx_nic_free_buffer(efx, &efx->stats_buffer); -} - -/* Global events are basically PHY events */ -static bool -falcon_handle_global_event(struct efx_channel *channel, efx_qword_t *event) -{ - struct efx_nic *efx = channel->efx; - struct falcon_nic_data *nic_data = efx->nic_data; - - if (EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_G_PHY0_INTR) || - EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XG_PHY0_INTR) || - EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XFP_PHY0_INTR)) - /* Ignored */ - return true; - - if ((efx_nic_rev(efx) == EFX_REV_FALCON_B0) && - EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_XG_MGT_INTR)) { - nic_data->xmac_poll_required = true; - return true; - } - - if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1 ? - EFX_QWORD_FIELD(*event, FSF_AA_GLB_EV_RX_RECOVERY) : - EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_RX_RECOVERY)) { - netif_err(efx, rx_err, efx->net_dev, - "channel %d seen global RX_RESET event. Resetting.\n", - channel->channel); - - atomic_inc(&efx->rx_reset); - efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); - return true; - } - - return false; -} - -/************************************************************************** - * - * Falcon test code - * - **************************************************************************/ - -static int -falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - struct falcon_nvconfig *nvconfig; - struct falcon_spi_device *spi; - void *region; - int rc, magic_num, struct_ver; - __le16 *word, *limit; - u32 csum; - - if (falcon_spi_present(&nic_data->spi_flash)) - spi = &nic_data->spi_flash; - else if (falcon_spi_present(&nic_data->spi_eeprom)) - spi = &nic_data->spi_eeprom; - else - return -EINVAL; - - region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL); - if (!region) - return -ENOMEM; - nvconfig = region + FALCON_NVCONFIG_OFFSET; - - mutex_lock(&nic_data->spi_lock); - rc = falcon_spi_read(efx, spi, 0, FALCON_NVCONFIG_END, NULL, region); - mutex_unlock(&nic_data->spi_lock); - if (rc) { - netif_err(efx, hw, efx->net_dev, "Failed to read %s\n", - falcon_spi_present(&nic_data->spi_flash) ? - "flash" : "EEPROM"); - rc = -EIO; - goto out; - } - - magic_num = le16_to_cpu(nvconfig->board_magic_num); - struct_ver = le16_to_cpu(nvconfig->board_struct_ver); - - rc = -EINVAL; - if (magic_num != FALCON_NVCONFIG_BOARD_MAGIC_NUM) { - netif_err(efx, hw, efx->net_dev, - "NVRAM bad magic 0x%x\n", magic_num); - goto out; - } - if (struct_ver < 2) { - netif_err(efx, hw, efx->net_dev, - "NVRAM has ancient version 0x%x\n", struct_ver); - goto out; - } else if (struct_ver < 4) { - word = &nvconfig->board_magic_num; - limit = (__le16 *) (nvconfig + 1); - } else { - word = region; - limit = region + FALCON_NVCONFIG_END; - } - for (csum = 0; word < limit; ++word) - csum += le16_to_cpu(*word); - - if (~csum & 0xffff) { - netif_err(efx, hw, efx->net_dev, - "NVRAM has incorrect checksum\n"); - goto out; - } - - rc = 0; - if (nvconfig_out) - memcpy(nvconfig_out, nvconfig, sizeof(*nvconfig)); - - out: - kfree(region); - return rc; -} - -static int falcon_test_nvram(struct efx_nic *efx) -{ - return falcon_read_nvram(efx, NULL); -} - -static const struct efx_farch_register_test falcon_b0_register_tests[] = { - { FR_AZ_ADR_REGION, - EFX_OWORD32(0x0003FFFF, 0x0003FFFF, 0x0003FFFF, 0x0003FFFF) }, - { FR_AZ_RX_CFG, - EFX_OWORD32(0xFFFFFFFE, 0x00017FFF, 0x00000000, 0x00000000) }, - { FR_AZ_TX_CFG, - EFX_OWORD32(0x7FFF0037, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AZ_TX_RESERVED, - EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, - { FR_AB_MAC_CTRL, - EFX_OWORD32(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AZ_SRM_TX_DC_CFG, - EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AZ_RX_DC_CFG, - EFX_OWORD32(0x0000000F, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AZ_RX_DC_PF_WM, - EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, - { FR_BZ_DP_CTRL, - EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_GM_CFG2, - EFX_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_GMF_CFG0, - EFX_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_XM_GLB_CFG, - EFX_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_XM_TX_CFG, - EFX_OWORD32(0x00080164, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_XM_RX_CFG, - EFX_OWORD32(0x07100A0C, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_XM_RX_PARAM, - EFX_OWORD32(0x00001FF8, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_XM_FC, - EFX_OWORD32(0xFFFF0001, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_XM_ADR_LO, - EFX_OWORD32(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000) }, - { FR_AB_XX_SD_CTL, - EFX_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) }, -}; - -static int -falcon_b0_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) -{ - enum reset_type reset_method = RESET_TYPE_INVISIBLE; - int rc, rc2; - - mutex_lock(&efx->mac_lock); - if (efx->loopback_modes) { - /* We need the 312 clock from the PHY to test the XMAC - * registers, so move into XGMII loopback if available */ - if (efx->loopback_modes & (1 << LOOPBACK_XGMII)) - efx->loopback_mode = LOOPBACK_XGMII; - else - efx->loopback_mode = __ffs(efx->loopback_modes); - } - __efx_reconfigure_port(efx); - mutex_unlock(&efx->mac_lock); - - efx_reset_down(efx, reset_method); - - tests->registers = - efx_farch_test_registers(efx, falcon_b0_register_tests, - ARRAY_SIZE(falcon_b0_register_tests)) - ? -1 : 1; - - rc = falcon_reset_hw(efx, reset_method); - rc2 = efx_reset_up(efx, reset_method, rc == 0); - return rc ? rc : rc2; -} - -/************************************************************************** - * - * Device reset - * - ************************************************************************** - */ - -static enum reset_type falcon_map_reset_reason(enum reset_type reason) -{ - switch (reason) { - case RESET_TYPE_RX_RECOVERY: - case RESET_TYPE_DMA_ERROR: - case RESET_TYPE_TX_SKIP: - /* These can occasionally occur due to hardware bugs. - * We try to reset without disrupting the link. - */ - return RESET_TYPE_INVISIBLE; - default: - return RESET_TYPE_ALL; - } -} - -static int falcon_map_reset_flags(u32 *flags) -{ - enum { - FALCON_RESET_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER | - ETH_RESET_OFFLOAD | ETH_RESET_MAC), - FALCON_RESET_ALL = FALCON_RESET_INVISIBLE | ETH_RESET_PHY, - FALCON_RESET_WORLD = FALCON_RESET_ALL | ETH_RESET_IRQ, - }; - - if ((*flags & FALCON_RESET_WORLD) == FALCON_RESET_WORLD) { - *flags &= ~FALCON_RESET_WORLD; - return RESET_TYPE_WORLD; - } - - if ((*flags & FALCON_RESET_ALL) == FALCON_RESET_ALL) { - *flags &= ~FALCON_RESET_ALL; - return RESET_TYPE_ALL; - } - - if ((*flags & FALCON_RESET_INVISIBLE) == FALCON_RESET_INVISIBLE) { - *flags &= ~FALCON_RESET_INVISIBLE; - return RESET_TYPE_INVISIBLE; - } - - return -EINVAL; -} - -/* Resets NIC to known state. This routine must be called in process - * context and is allowed to sleep. */ -static int __falcon_reset_hw(struct efx_nic *efx, enum reset_type method) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t glb_ctl_reg_ker; - int rc; - - netif_dbg(efx, hw, efx->net_dev, "performing %s hardware reset\n", - RESET_TYPE(method)); - - /* Initiate device reset */ - if (method == RESET_TYPE_WORLD) { - rc = pci_save_state(efx->pci_dev); - if (rc) { - netif_err(efx, drv, efx->net_dev, - "failed to backup PCI state of primary " - "function prior to hardware reset\n"); - goto fail1; - } - if (efx_nic_is_dual_func(efx)) { - rc = pci_save_state(nic_data->pci_dev2); - if (rc) { - netif_err(efx, drv, efx->net_dev, - "failed to backup PCI state of " - "secondary function prior to " - "hardware reset\n"); - goto fail2; - } - } - - EFX_POPULATE_OWORD_2(glb_ctl_reg_ker, - FRF_AB_EXT_PHY_RST_DUR, - FFE_AB_EXT_PHY_RST_DUR_10240US, - FRF_AB_SWRST, 1); - } else { - EFX_POPULATE_OWORD_7(glb_ctl_reg_ker, - /* exclude PHY from "invisible" reset */ - FRF_AB_EXT_PHY_RST_CTL, - method == RESET_TYPE_INVISIBLE, - /* exclude EEPROM/flash and PCIe */ - FRF_AB_PCIE_CORE_RST_CTL, 1, - FRF_AB_PCIE_NSTKY_RST_CTL, 1, - FRF_AB_PCIE_SD_RST_CTL, 1, - FRF_AB_EE_RST_CTL, 1, - FRF_AB_EXT_PHY_RST_DUR, - FFE_AB_EXT_PHY_RST_DUR_10240US, - FRF_AB_SWRST, 1); - } - efx_writeo(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); - - netif_dbg(efx, hw, efx->net_dev, "waiting for hardware reset\n"); - schedule_timeout_uninterruptible(HZ / 20); - - /* Restore PCI configuration if needed */ - if (method == RESET_TYPE_WORLD) { - if (efx_nic_is_dual_func(efx)) - pci_restore_state(nic_data->pci_dev2); - pci_restore_state(efx->pci_dev); - netif_dbg(efx, drv, efx->net_dev, - "successfully restored PCI config\n"); - } - - /* Assert that reset complete */ - efx_reado(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); - if (EFX_OWORD_FIELD(glb_ctl_reg_ker, FRF_AB_SWRST) != 0) { - rc = -ETIMEDOUT; - netif_err(efx, hw, efx->net_dev, - "timed out waiting for hardware reset\n"); - goto fail3; - } - netif_dbg(efx, hw, efx->net_dev, "hardware reset complete\n"); - - return 0; - - /* pci_save_state() and pci_restore_state() MUST be called in pairs */ -fail2: - pci_restore_state(efx->pci_dev); -fail1: -fail3: - return rc; -} - -static int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - mutex_lock(&nic_data->spi_lock); - rc = __falcon_reset_hw(efx, method); - mutex_unlock(&nic_data->spi_lock); - - return rc; -} - -static void falcon_monitor(struct efx_nic *efx) -{ - bool link_changed; - int rc; - - BUG_ON(!mutex_is_locked(&efx->mac_lock)); - - rc = falcon_board(efx)->type->monitor(efx); - if (rc) { - netif_err(efx, hw, efx->net_dev, - "Board sensor %s; shutting down PHY\n", - (rc == -ERANGE) ? "reported fault" : "failed"); - efx->phy_mode |= PHY_MODE_LOW_POWER; - rc = __efx_reconfigure_port(efx); - WARN_ON(rc); - } - - if (LOOPBACK_INTERNAL(efx)) - link_changed = falcon_loopback_link_poll(efx); - else - link_changed = efx->phy_op->poll(efx); - - if (link_changed) { - falcon_stop_nic_stats(efx); - falcon_deconfigure_mac_wrapper(efx); - - falcon_reset_macs(efx); - rc = falcon_reconfigure_xmac(efx); - BUG_ON(rc); - - falcon_start_nic_stats(efx); - - efx_link_status_changed(efx); - } - - falcon_poll_xmac(efx); -} - -/* Zeroes out the SRAM contents. This routine must be called in - * process context and is allowed to sleep. - */ -static int falcon_reset_sram(struct efx_nic *efx) -{ - efx_oword_t srm_cfg_reg_ker, gpio_cfg_reg_ker; - int count; - - /* Set the SRAM wake/sleep GPIO appropriately. */ - efx_reado(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL); - EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OEN, 1); - EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OUT, 1); - efx_writeo(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL); - - /* Initiate SRAM reset */ - EFX_POPULATE_OWORD_2(srm_cfg_reg_ker, - FRF_AZ_SRM_INIT_EN, 1, - FRF_AZ_SRM_NB_SZ, 0); - efx_writeo(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); - - /* Wait for SRAM reset to complete */ - count = 0; - do { - netif_dbg(efx, hw, efx->net_dev, - "waiting for SRAM reset (attempt %d)...\n", count); - - /* SRAM reset is slow; expect around 16ms */ - schedule_timeout_uninterruptible(HZ / 50); - - /* Check for reset complete */ - efx_reado(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); - if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, FRF_AZ_SRM_INIT_EN)) { - netif_dbg(efx, hw, efx->net_dev, - "SRAM reset complete\n"); - - return 0; - } - } while (++count < 20); /* wait up to 0.4 sec */ - - netif_err(efx, hw, efx->net_dev, "timed out waiting for SRAM reset\n"); - return -ETIMEDOUT; -} - -static void falcon_spi_device_init(struct efx_nic *efx, - struct falcon_spi_device *spi_device, - unsigned int device_id, u32 device_type) -{ - if (device_type != 0) { - spi_device->device_id = device_id; - spi_device->size = - 1 << SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_SIZE); - spi_device->addr_len = - SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ADDR_LEN); - spi_device->munge_address = (spi_device->size == 1 << 9 && - spi_device->addr_len == 1); - spi_device->erase_command = - SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ERASE_CMD); - spi_device->erase_size = - 1 << SPI_DEV_TYPE_FIELD(device_type, - SPI_DEV_TYPE_ERASE_SIZE); - spi_device->block_size = - 1 << SPI_DEV_TYPE_FIELD(device_type, - SPI_DEV_TYPE_BLOCK_SIZE); - } else { - spi_device->size = 0; - } -} - -/* Extract non-volatile configuration */ -static int falcon_probe_nvconfig(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - struct falcon_nvconfig *nvconfig; - int rc; - - nvconfig = kmalloc(sizeof(*nvconfig), GFP_KERNEL); - if (!nvconfig) - return -ENOMEM; - - rc = falcon_read_nvram(efx, nvconfig); - if (rc) - goto out; - - efx->phy_type = nvconfig->board_v2.port0_phy_type; - efx->mdio.prtad = nvconfig->board_v2.port0_phy_addr; - - if (le16_to_cpu(nvconfig->board_struct_ver) >= 3) { - falcon_spi_device_init( - efx, &nic_data->spi_flash, FFE_AB_SPI_DEVICE_FLASH, - le32_to_cpu(nvconfig->board_v3 - .spi_device_type[FFE_AB_SPI_DEVICE_FLASH])); - falcon_spi_device_init( - efx, &nic_data->spi_eeprom, FFE_AB_SPI_DEVICE_EEPROM, - le32_to_cpu(nvconfig->board_v3 - .spi_device_type[FFE_AB_SPI_DEVICE_EEPROM])); - } - - /* Read the MAC addresses */ - ether_addr_copy(efx->net_dev->perm_addr, nvconfig->mac_address[0]); - - netif_dbg(efx, probe, efx->net_dev, "PHY is %d phy_id %d\n", - efx->phy_type, efx->mdio.prtad); - - rc = falcon_probe_board(efx, - le16_to_cpu(nvconfig->board_v2.board_revision)); -out: - kfree(nvconfig); - return rc; -} - -static int falcon_dimension_resources(struct efx_nic *efx) -{ - efx->rx_dc_base = 0x20000; - efx->tx_dc_base = 0x26000; - return 0; -} - -/* Probe all SPI devices on the NIC */ -static void falcon_probe_spi_devices(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t nic_stat, gpio_ctl, ee_vpd_cfg; - int boot_dev; - - efx_reado(efx, &gpio_ctl, FR_AB_GPIO_CTL); - efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); - efx_reado(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); - - if (EFX_OWORD_FIELD(gpio_ctl, FRF_AB_GPIO3_PWRUP_VALUE)) { - boot_dev = (EFX_OWORD_FIELD(nic_stat, FRF_AB_SF_PRST) ? - FFE_AB_SPI_DEVICE_FLASH : FFE_AB_SPI_DEVICE_EEPROM); - netif_dbg(efx, probe, efx->net_dev, "Booted from %s\n", - boot_dev == FFE_AB_SPI_DEVICE_FLASH ? - "flash" : "EEPROM"); - } else { - /* Disable VPD and set clock dividers to safe - * values for initial programming. */ - boot_dev = -1; - netif_dbg(efx, probe, efx->net_dev, - "Booted from internal ASIC settings;" - " setting SPI config\n"); - EFX_POPULATE_OWORD_3(ee_vpd_cfg, FRF_AB_EE_VPD_EN, 0, - /* 125 MHz / 7 ~= 20 MHz */ - FRF_AB_EE_SF_CLOCK_DIV, 7, - /* 125 MHz / 63 ~= 2 MHz */ - FRF_AB_EE_EE_CLOCK_DIV, 63); - efx_writeo(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); - } - - mutex_init(&nic_data->spi_lock); - - if (boot_dev == FFE_AB_SPI_DEVICE_FLASH) - falcon_spi_device_init(efx, &nic_data->spi_flash, - FFE_AB_SPI_DEVICE_FLASH, - default_flash_type); - if (boot_dev == FFE_AB_SPI_DEVICE_EEPROM) - falcon_spi_device_init(efx, &nic_data->spi_eeprom, - FFE_AB_SPI_DEVICE_EEPROM, - large_eeprom_type); -} - -static unsigned int falcon_a1_mem_map_size(struct efx_nic *efx) -{ - return 0x20000; -} - -static unsigned int falcon_b0_mem_map_size(struct efx_nic *efx) -{ - /* Map everything up to and including the RSS indirection table. - * The PCI core takes care of mapping the MSI-X tables. - */ - return FR_BZ_RX_INDIRECTION_TBL + - FR_BZ_RX_INDIRECTION_TBL_STEP * FR_BZ_RX_INDIRECTION_TBL_ROWS; -} - -static int falcon_probe_nic(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data; - struct falcon_board *board; - int rc; - - efx->primary = efx; /* only one usable function per controller */ - - /* Allocate storage for hardware specific data */ - nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL); - if (!nic_data) - return -ENOMEM; - efx->nic_data = nic_data; - - rc = -ENODEV; - - if (efx_farch_fpga_ver(efx) != 0) { - netif_err(efx, probe, efx->net_dev, - "Falcon FPGA not supported\n"); - goto fail1; - } - - if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { - efx_oword_t nic_stat; - struct pci_dev *dev; - u8 pci_rev = efx->pci_dev->revision; - - if ((pci_rev == 0xff) || (pci_rev == 0)) { - netif_err(efx, probe, efx->net_dev, - "Falcon rev A0 not supported\n"); - goto fail1; - } - efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); - if (EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_10G) == 0) { - netif_err(efx, probe, efx->net_dev, - "Falcon rev A1 1G not supported\n"); - goto fail1; - } - if (EFX_OWORD_FIELD(nic_stat, FRF_AA_STRAP_PCIE) == 0) { - netif_err(efx, probe, efx->net_dev, - "Falcon rev A1 PCI-X not supported\n"); - goto fail1; - } - - dev = pci_dev_get(efx->pci_dev); - while ((dev = pci_get_device(PCI_VENDOR_ID_SOLARFLARE, - PCI_DEVICE_ID_SOLARFLARE_SFC4000A_1, - dev))) { - if (dev->bus == efx->pci_dev->bus && - dev->devfn == efx->pci_dev->devfn + 1) { - nic_data->pci_dev2 = dev; - break; - } - } - if (!nic_data->pci_dev2) { - netif_err(efx, probe, efx->net_dev, - "failed to find secondary function\n"); - rc = -ENODEV; - goto fail2; - } - } - - /* Now we can reset the NIC */ - rc = __falcon_reset_hw(efx, RESET_TYPE_ALL); - if (rc) { - netif_err(efx, probe, efx->net_dev, "failed to reset NIC\n"); - goto fail3; - } - - /* Allocate memory for INT_KER */ - rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t), - GFP_KERNEL); - if (rc) - goto fail4; - BUG_ON(efx->irq_status.dma_addr & 0x0f); - - netif_dbg(efx, probe, efx->net_dev, - "INT_KER at %llx (virt %p phys %llx)\n", - (u64)efx->irq_status.dma_addr, - efx->irq_status.addr, - (u64)virt_to_phys(efx->irq_status.addr)); - - falcon_probe_spi_devices(efx); - - /* Read in the non-volatile configuration */ - rc = falcon_probe_nvconfig(efx); - if (rc) { - if (rc == -EINVAL) - netif_err(efx, probe, efx->net_dev, "NVRAM is invalid\n"); - goto fail5; - } - - efx->max_channels = (efx_nic_rev(efx) <= EFX_REV_FALCON_A1 ? 4 : - EFX_MAX_CHANNELS); - efx->max_tx_channels = efx->max_channels; - efx->timer_quantum_ns = 4968; /* 621 cycles */ - efx->timer_max_ns = efx->type->timer_period_max * - efx->timer_quantum_ns; - - /* Initialise I2C adapter */ - board = falcon_board(efx); - board->i2c_adap.owner = THIS_MODULE; - board->i2c_data = falcon_i2c_bit_operations; - board->i2c_data.data = efx; - board->i2c_adap.algo_data = &board->i2c_data; - board->i2c_adap.dev.parent = &efx->pci_dev->dev; - strlcpy(board->i2c_adap.name, "SFC4000 GPIO", - sizeof(board->i2c_adap.name)); - rc = i2c_bit_add_bus(&board->i2c_adap); - if (rc) - goto fail5; - - rc = falcon_board(efx)->type->init(efx); - if (rc) { - netif_err(efx, probe, efx->net_dev, - "failed to initialise board\n"); - goto fail6; - } - - nic_data->stats_disable_count = 1; - setup_timer(&nic_data->stats_timer, &falcon_stats_timer_func, - (unsigned long)efx); - - return 0; - - fail6: - i2c_del_adapter(&board->i2c_adap); - memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); - fail5: - efx_nic_free_buffer(efx, &efx->irq_status); - fail4: - fail3: - if (nic_data->pci_dev2) { - pci_dev_put(nic_data->pci_dev2); - nic_data->pci_dev2 = NULL; - } - fail2: - fail1: - kfree(efx->nic_data); - return rc; -} - -static void falcon_init_rx_cfg(struct efx_nic *efx) -{ - /* RX control FIFO thresholds (32 entries) */ - const unsigned ctrl_xon_thr = 20; - const unsigned ctrl_xoff_thr = 25; - efx_oword_t reg; - - efx_reado(efx, ®, FR_AZ_RX_CFG); - if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { - /* Data FIFO size is 5.5K. The RX DMA engine only - * supports scattering for user-mode queues, but will - * split DMA writes at intervals of RX_USR_BUF_SIZE - * (32-byte units) even for kernel-mode queues. We - * set it to be so large that that never happens. - */ - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_DESC_PUSH_EN, 0); - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_USR_BUF_SIZE, - (3 * 4096) >> 5); - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, 512 >> 8); - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, 2048 >> 8); - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_TX_TH, ctrl_xon_thr); - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_TX_TH, ctrl_xoff_thr); - } else { - /* Data FIFO size is 80K; register fields moved */ - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_DESC_PUSH_EN, 0); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_USR_BUF_SIZE, - EFX_RX_USR_BUF_SIZE >> 5); - /* Send XON and XOFF at ~3 * max MTU away from empty/full */ - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, 27648 >> 8); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, 54272 >> 8); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_TX_TH, ctrl_xon_thr); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_TX_TH, ctrl_xoff_thr); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); - - /* Enable hash insertion. This is broken for the - * 'Falcon' hash so also select Toeplitz TCP/IPv4 and - * IPv4 hashes. */ - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_HASH_INSRT_HDR, 1); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_HASH_ALG, 1); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_IP_HASH, 1); - } - /* Always enable XOFF signal from RX FIFO. We enable - * or disable transmission of pause frames at the MAC. */ - EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); - efx_writeo(efx, ®, FR_AZ_RX_CFG); -} - -/* This call performs hardware-specific global initialisation, such as - * defining the descriptor cache sizes and number of RSS channels. - * It does not set up any buffers, descriptor rings or event queues. - */ -static int falcon_init_nic(struct efx_nic *efx) -{ - efx_oword_t temp; - int rc; - - /* Use on-chip SRAM */ - efx_reado(efx, &temp, FR_AB_NIC_STAT); - EFX_SET_OWORD_FIELD(temp, FRF_AB_ONCHIP_SRAM, 1); - efx_writeo(efx, &temp, FR_AB_NIC_STAT); - - rc = falcon_reset_sram(efx); - if (rc) - return rc; - - /* Clear the parity enables on the TX data fifos as - * they produce false parity errors because of timing issues - */ - if (EFX_WORKAROUND_5129(efx)) { - efx_reado(efx, &temp, FR_AZ_CSR_SPARE); - EFX_SET_OWORD_FIELD(temp, FRF_AB_MEM_PERR_EN_TX_DATA, 0); - efx_writeo(efx, &temp, FR_AZ_CSR_SPARE); - } - - if (EFX_WORKAROUND_7244(efx)) { - efx_reado(efx, &temp, FR_BZ_RX_FILTER_CTL); - EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_FULL_SRCH_LIMIT, 8); - EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_WILD_SRCH_LIMIT, 8); - EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_FULL_SRCH_LIMIT, 8); - EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_WILD_SRCH_LIMIT, 8); - efx_writeo(efx, &temp, FR_BZ_RX_FILTER_CTL); - } - - /* XXX This is documented only for Falcon A0/A1 */ - /* Setup RX. Wait for descriptor is broken and must - * be disabled. RXDP recovery shouldn't be needed, but is. - */ - efx_reado(efx, &temp, FR_AA_RX_SELF_RST); - EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_NODESC_WAIT_DIS, 1); - EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_SELF_RST_EN, 1); - if (EFX_WORKAROUND_5583(efx)) - EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_ISCSI_DIS, 1); - efx_writeo(efx, &temp, FR_AA_RX_SELF_RST); - - /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 - * descriptors (which is bad). - */ - efx_reado(efx, &temp, FR_AZ_TX_CFG); - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); - efx_writeo(efx, &temp, FR_AZ_TX_CFG); - - falcon_init_rx_cfg(efx); - - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { - falcon_b0_rx_push_rss_config(efx, false, efx->rx_indir_table); - - /* Set destination of both TX and RX Flush events */ - EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); - efx_writeo(efx, &temp, FR_BZ_DP_CTRL); - } - - efx_farch_init_common(efx); - - return 0; -} - -static void falcon_remove_nic(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - struct falcon_board *board = falcon_board(efx); - - board->type->fini(efx); - - /* Remove I2C adapter and clear it in preparation for a retry */ - i2c_del_adapter(&board->i2c_adap); - memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); - - efx_nic_free_buffer(efx, &efx->irq_status); - - __falcon_reset_hw(efx, RESET_TYPE_ALL); - - /* Release the second function after the reset */ - if (nic_data->pci_dev2) { - pci_dev_put(nic_data->pci_dev2); - nic_data->pci_dev2 = NULL; - } - - /* Tear down the private nic state */ - kfree(efx->nic_data); - efx->nic_data = NULL; -} - -static size_t falcon_describe_nic_stats(struct efx_nic *efx, u8 *names) -{ - return efx_nic_describe_stats(falcon_stat_desc, FALCON_STAT_COUNT, - falcon_stat_mask, names); -} - -static size_t falcon_update_nic_stats(struct efx_nic *efx, u64 *full_stats, - struct rtnl_link_stats64 *core_stats) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - u64 *stats = nic_data->stats; - efx_oword_t cnt; - - if (!nic_data->stats_disable_count) { - efx_reado(efx, &cnt, FR_AZ_RX_NODESC_DROP); - stats[FALCON_STAT_rx_nodesc_drop_cnt] += - EFX_OWORD_FIELD(cnt, FRF_AB_RX_NODESC_DROP_CNT); - - if (nic_data->stats_pending && - FALCON_XMAC_STATS_DMA_FLAG(efx)) { - nic_data->stats_pending = false; - rmb(); /* read the done flag before the stats */ - efx_nic_update_stats( - falcon_stat_desc, FALCON_STAT_COUNT, - falcon_stat_mask, - stats, efx->stats_buffer.addr, true); - } - - /* Update derived statistic */ - efx_update_diff_stat(&stats[FALCON_STAT_rx_bad_bytes], - stats[FALCON_STAT_rx_bytes] - - stats[FALCON_STAT_rx_good_bytes] - - stats[FALCON_STAT_rx_control] * 64); - efx_update_sw_stats(efx, stats); - } - - if (full_stats) - memcpy(full_stats, stats, sizeof(u64) * FALCON_STAT_COUNT); - - if (core_stats) { - core_stats->rx_packets = stats[FALCON_STAT_rx_packets]; - core_stats->tx_packets = stats[FALCON_STAT_tx_packets]; - core_stats->rx_bytes = stats[FALCON_STAT_rx_bytes]; - core_stats->tx_bytes = stats[FALCON_STAT_tx_bytes]; - core_stats->rx_dropped = stats[FALCON_STAT_rx_nodesc_drop_cnt] + - stats[GENERIC_STAT_rx_nodesc_trunc] + - stats[GENERIC_STAT_rx_noskb_drops]; - core_stats->multicast = stats[FALCON_STAT_rx_multicast]; - core_stats->rx_length_errors = - stats[FALCON_STAT_rx_gtjumbo] + - stats[FALCON_STAT_rx_length_error]; - core_stats->rx_crc_errors = stats[FALCON_STAT_rx_bad]; - core_stats->rx_frame_errors = stats[FALCON_STAT_rx_align_error]; - core_stats->rx_fifo_errors = stats[FALCON_STAT_rx_overflow]; - - core_stats->rx_errors = (core_stats->rx_length_errors + - core_stats->rx_crc_errors + - core_stats->rx_frame_errors + - stats[FALCON_STAT_rx_symbol_error]); - } - - return FALCON_STAT_COUNT; -} - -void falcon_start_nic_stats(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - - spin_lock_bh(&efx->stats_lock); - if (--nic_data->stats_disable_count == 0) - falcon_stats_request(efx); - spin_unlock_bh(&efx->stats_lock); -} - -/* We don't acutally pull stats on falcon. Wait 10ms so that - * they arrive when we call this just after start_stats - */ -static void falcon_pull_nic_stats(struct efx_nic *efx) -{ - msleep(10); -} - -void falcon_stop_nic_stats(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - int i; - - might_sleep(); - - spin_lock_bh(&efx->stats_lock); - ++nic_data->stats_disable_count; - spin_unlock_bh(&efx->stats_lock); - - del_timer_sync(&nic_data->stats_timer); - - /* Wait enough time for the most recent transfer to - * complete. */ - for (i = 0; i < 4 && nic_data->stats_pending; i++) { - if (FALCON_XMAC_STATS_DMA_FLAG(efx)) - break; - msleep(1); - } - - spin_lock_bh(&efx->stats_lock); - falcon_stats_complete(efx); - spin_unlock_bh(&efx->stats_lock); -} - -static void falcon_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) -{ - falcon_board(efx)->type->set_id_led(efx, mode); -} - -/************************************************************************** - * - * Wake on LAN - * - ************************************************************************** - */ - -static void falcon_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) -{ - wol->supported = 0; - wol->wolopts = 0; - memset(&wol->sopass, 0, sizeof(wol->sopass)); -} - -static int falcon_set_wol(struct efx_nic *efx, u32 type) -{ - if (type != 0) - return -EINVAL; - return 0; -} - -/************************************************************************** - * - * Revision-dependent attributes used by efx.c and nic.c - * - ************************************************************************** - */ - -const struct efx_nic_type falcon_a1_nic_type = { - .is_vf = false, - .mem_bar = EFX_MEM_BAR, - .mem_map_size = falcon_a1_mem_map_size, - .probe = falcon_probe_nic, - .remove = falcon_remove_nic, - .init = falcon_init_nic, - .dimension_resources = falcon_dimension_resources, - .fini = falcon_irq_ack_a1, - .monitor = falcon_monitor, - .map_reset_reason = falcon_map_reset_reason, - .map_reset_flags = falcon_map_reset_flags, - .reset = falcon_reset_hw, - .probe_port = falcon_probe_port, - .remove_port = falcon_remove_port, - .handle_global_event = falcon_handle_global_event, - .fini_dmaq = efx_farch_fini_dmaq, - .prepare_flush = falcon_prepare_flush, - .finish_flush = efx_port_dummy_op_void, - .prepare_flr = efx_port_dummy_op_void, - .finish_flr = efx_farch_finish_flr, - .describe_stats = falcon_describe_nic_stats, - .update_stats = falcon_update_nic_stats, - .start_stats = falcon_start_nic_stats, - .pull_stats = falcon_pull_nic_stats, - .stop_stats = falcon_stop_nic_stats, - .set_id_led = falcon_set_id_led, - .push_irq_moderation = falcon_push_irq_moderation, - .reconfigure_port = falcon_reconfigure_port, - .prepare_enable_fc_tx = falcon_a1_prepare_enable_fc_tx, - .reconfigure_mac = falcon_reconfigure_xmac, - .check_mac_fault = falcon_xmac_check_fault, - .get_wol = falcon_get_wol, - .set_wol = falcon_set_wol, - .resume_wol = efx_port_dummy_op_void, - .test_nvram = falcon_test_nvram, - .irq_enable_master = efx_farch_irq_enable_master, - .irq_test_generate = efx_farch_irq_test_generate, - .irq_disable_non_ev = efx_farch_irq_disable_master, - .irq_handle_msi = efx_farch_msi_interrupt, - .irq_handle_legacy = falcon_legacy_interrupt_a1, - .tx_probe = efx_farch_tx_probe, - .tx_init = efx_farch_tx_init, - .tx_remove = efx_farch_tx_remove, - .tx_write = efx_farch_tx_write, - .tx_limit_len = efx_farch_tx_limit_len, - .rx_push_rss_config = dummy_rx_push_rss_config, - .rx_probe = efx_farch_rx_probe, - .rx_init = efx_farch_rx_init, - .rx_remove = efx_farch_rx_remove, - .rx_write = efx_farch_rx_write, - .rx_defer_refill = efx_farch_rx_defer_refill, - .ev_probe = efx_farch_ev_probe, - .ev_init = efx_farch_ev_init, - .ev_fini = efx_farch_ev_fini, - .ev_remove = efx_farch_ev_remove, - .ev_process = efx_farch_ev_process, - .ev_read_ack = efx_farch_ev_read_ack, - .ev_test_generate = efx_farch_ev_test_generate, - - /* We don't expose the filter table on Falcon A1 as it is not - * mapped into function 0, but these implementations still - * work with a degenerate case of all tables set to size 0. - */ - .filter_table_probe = efx_farch_filter_table_probe, - .filter_table_restore = efx_farch_filter_table_restore, - .filter_table_remove = efx_farch_filter_table_remove, - .filter_insert = efx_farch_filter_insert, - .filter_remove_safe = efx_farch_filter_remove_safe, - .filter_get_safe = efx_farch_filter_get_safe, - .filter_clear_rx = efx_farch_filter_clear_rx, - .filter_count_rx_used = efx_farch_filter_count_rx_used, - .filter_get_rx_id_limit = efx_farch_filter_get_rx_id_limit, - .filter_get_rx_ids = efx_farch_filter_get_rx_ids, - -#ifdef CONFIG_SFC_MTD - .mtd_probe = falcon_mtd_probe, - .mtd_rename = falcon_mtd_rename, - .mtd_read = falcon_mtd_read, - .mtd_erase = falcon_mtd_erase, - .mtd_write = falcon_mtd_write, - .mtd_sync = falcon_mtd_sync, -#endif - - .revision = EFX_REV_FALCON_A1, - .txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER, - .rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER, - .buf_tbl_base = FR_AA_BUF_FULL_TBL_KER, - .evq_ptr_tbl_base = FR_AA_EVQ_PTR_TBL_KER, - .evq_rptr_tbl_base = FR_AA_EVQ_RPTR_KER, - .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), - .rx_buffer_padding = 0x24, - .can_rx_scatter = false, - .max_interrupt_mode = EFX_INT_MODE_MSI, - .timer_period_max = 1 << FRF_AB_TC_TIMER_VAL_WIDTH, - .offload_features = NETIF_F_IP_CSUM, - .mcdi_max_ver = -1, -}; - -const struct efx_nic_type falcon_b0_nic_type = { - .is_vf = false, - .mem_bar = EFX_MEM_BAR, - .mem_map_size = falcon_b0_mem_map_size, - .probe = falcon_probe_nic, - .remove = falcon_remove_nic, - .init = falcon_init_nic, - .dimension_resources = falcon_dimension_resources, - .fini = efx_port_dummy_op_void, - .monitor = falcon_monitor, - .map_reset_reason = falcon_map_reset_reason, - .map_reset_flags = falcon_map_reset_flags, - .reset = falcon_reset_hw, - .probe_port = falcon_probe_port, - .remove_port = falcon_remove_port, - .handle_global_event = falcon_handle_global_event, - .fini_dmaq = efx_farch_fini_dmaq, - .prepare_flush = falcon_prepare_flush, - .finish_flush = efx_port_dummy_op_void, - .prepare_flr = efx_port_dummy_op_void, - .finish_flr = efx_farch_finish_flr, - .describe_stats = falcon_describe_nic_stats, - .update_stats = falcon_update_nic_stats, - .start_stats = falcon_start_nic_stats, - .pull_stats = falcon_pull_nic_stats, - .stop_stats = falcon_stop_nic_stats, - .set_id_led = falcon_set_id_led, - .push_irq_moderation = falcon_push_irq_moderation, - .reconfigure_port = falcon_reconfigure_port, - .prepare_enable_fc_tx = falcon_b0_prepare_enable_fc_tx, - .reconfigure_mac = falcon_reconfigure_xmac, - .check_mac_fault = falcon_xmac_check_fault, - .get_wol = falcon_get_wol, - .set_wol = falcon_set_wol, - .resume_wol = efx_port_dummy_op_void, - .test_chip = falcon_b0_test_chip, - .test_nvram = falcon_test_nvram, - .irq_enable_master = efx_farch_irq_enable_master, - .irq_test_generate = efx_farch_irq_test_generate, - .irq_disable_non_ev = efx_farch_irq_disable_master, - .irq_handle_msi = efx_farch_msi_interrupt, - .irq_handle_legacy = efx_farch_legacy_interrupt, - .tx_probe = efx_farch_tx_probe, - .tx_init = efx_farch_tx_init, - .tx_remove = efx_farch_tx_remove, - .tx_write = efx_farch_tx_write, - .tx_limit_len = efx_farch_tx_limit_len, - .rx_push_rss_config = falcon_b0_rx_push_rss_config, - .rx_probe = efx_farch_rx_probe, - .rx_init = efx_farch_rx_init, - .rx_remove = efx_farch_rx_remove, - .rx_write = efx_farch_rx_write, - .rx_defer_refill = efx_farch_rx_defer_refill, - .ev_probe = efx_farch_ev_probe, - .ev_init = efx_farch_ev_init, - .ev_fini = efx_farch_ev_fini, - .ev_remove = efx_farch_ev_remove, - .ev_process = efx_farch_ev_process, - .ev_read_ack = efx_farch_ev_read_ack, - .ev_test_generate = efx_farch_ev_test_generate, - .filter_table_probe = efx_farch_filter_table_probe, - .filter_table_restore = efx_farch_filter_table_restore, - .filter_table_remove = efx_farch_filter_table_remove, - .filter_update_rx_scatter = efx_farch_filter_update_rx_scatter, - .filter_insert = efx_farch_filter_insert, - .filter_remove_safe = efx_farch_filter_remove_safe, - .filter_get_safe = efx_farch_filter_get_safe, - .filter_clear_rx = efx_farch_filter_clear_rx, - .filter_count_rx_used = efx_farch_filter_count_rx_used, - .filter_get_rx_id_limit = efx_farch_filter_get_rx_id_limit, - .filter_get_rx_ids = efx_farch_filter_get_rx_ids, -#ifdef CONFIG_RFS_ACCEL - .filter_rfs_insert = efx_farch_filter_rfs_insert, - .filter_rfs_expire_one = efx_farch_filter_rfs_expire_one, -#endif -#ifdef CONFIG_SFC_MTD - .mtd_probe = falcon_mtd_probe, - .mtd_rename = falcon_mtd_rename, - .mtd_read = falcon_mtd_read, - .mtd_erase = falcon_mtd_erase, - .mtd_write = falcon_mtd_write, - .mtd_sync = falcon_mtd_sync, -#endif - - .revision = EFX_REV_FALCON_B0, - .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, - .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, - .buf_tbl_base = FR_BZ_BUF_FULL_TBL, - .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, - .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, - .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), - .rx_prefix_size = FS_BZ_RX_PREFIX_SIZE, - .rx_hash_offset = FS_BZ_RX_PREFIX_HASH_OFST, - .rx_buffer_padding = 0, - .can_rx_scatter = true, - .max_interrupt_mode = EFX_INT_MODE_MSIX, - .timer_period_max = 1 << FRF_AB_TC_TIMER_VAL_WIDTH, - .offload_features = NETIF_F_IP_CSUM | NETIF_F_RXHASH | NETIF_F_NTUPLE, - .mcdi_max_ver = -1, - .max_rx_ip_filters = FR_BZ_RX_FILTER_TBL0_ROWS, -}; diff --git a/drivers/net/ethernet/sfc/falcon/Kconfig b/drivers/net/ethernet/sfc/falcon/Kconfig new file mode 100644 index 0000000..6248e96 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/Kconfig @@ -0,0 +1,21 @@ +config SFC_FALCON + tristate "Solarflare SFC4000 support" + depends on PCI + select MDIO + select CRC32 + select I2C + select I2C_ALGOBIT + ---help--- + This driver supports 10-gigabit Ethernet cards based on + the Solarflare SFC4000 controller. + + To compile this driver as a module, choose M here. The module + will be called sfc-falcon. +config SFC_FALCON_MTD + bool "Solarflare SFC4000 MTD support" + depends on SFC_FALCON && MTD && !(SFC_FALCON=y && MTD=m) + default y + ---help--- + This exposes the on-board flash and/or EEPROM as MTD devices + (e.g. /dev/mtd1). This is required to update the boot + configuration under Linux. diff --git a/drivers/net/ethernet/sfc/falcon/Makefile b/drivers/net/ethernet/sfc/falcon/Makefile new file mode 100644 index 0000000..aa1b459 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/Makefile @@ -0,0 +1,6 @@ +sfc-falcon-y += efx.o nic.o farch.o falcon.o tx.o rx.o selftest.o \ + ethtool.o qt202x_phy.o mdio_10g.o tenxpress.o \ + txc43128_phy.o falcon_boards.o + +sfc-falcon-$(CONFIG_SFC_FALCON_MTD) += mtd.o +obj-$(CONFIG_SFC_FALCON) += sfc-falcon.o diff --git a/drivers/net/ethernet/sfc/falcon/bitfield.h b/drivers/net/ethernet/sfc/falcon/bitfield.h new file mode 100644 index 0000000..230fd77 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/bitfield.h @@ -0,0 +1,542 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_BITFIELD_H +#define EF4_BITFIELD_H + +/* + * Efx bitfield access + * + * Efx NICs make extensive use of bitfields up to 128 bits + * wide. Since there is no native 128-bit datatype on most systems, + * and since 64-bit datatypes are inefficient on 32-bit systems and + * vice versa, we wrap accesses in a way that uses the most efficient + * datatype. + * + * The NICs are PCI devices and therefore little-endian. Since most + * of the quantities that we deal with are DMAed to/from host memory, + * we define our datatypes (ef4_oword_t, ef4_qword_t and + * ef4_dword_t) to be little-endian. + */ + +/* Lowest bit numbers and widths */ +#define EF4_DUMMY_FIELD_LBN 0 +#define EF4_DUMMY_FIELD_WIDTH 0 +#define EF4_WORD_0_LBN 0 +#define EF4_WORD_0_WIDTH 16 +#define EF4_WORD_1_LBN 16 +#define EF4_WORD_1_WIDTH 16 +#define EF4_DWORD_0_LBN 0 +#define EF4_DWORD_0_WIDTH 32 +#define EF4_DWORD_1_LBN 32 +#define EF4_DWORD_1_WIDTH 32 +#define EF4_DWORD_2_LBN 64 +#define EF4_DWORD_2_WIDTH 32 +#define EF4_DWORD_3_LBN 96 +#define EF4_DWORD_3_WIDTH 32 +#define EF4_QWORD_0_LBN 0 +#define EF4_QWORD_0_WIDTH 64 + +/* Specified attribute (e.g. LBN) of the specified field */ +#define EF4_VAL(field, attribute) field ## _ ## attribute +/* Low bit number of the specified field */ +#define EF4_LOW_BIT(field) EF4_VAL(field, LBN) +/* Bit width of the specified field */ +#define EF4_WIDTH(field) EF4_VAL(field, WIDTH) +/* High bit number of the specified field */ +#define EF4_HIGH_BIT(field) (EF4_LOW_BIT(field) + EF4_WIDTH(field) - 1) +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 64 bits. + */ +#define EF4_MASK64(width) \ + ((width) == 64 ? ~((u64) 0) : \ + (((((u64) 1) << (width))) - 1)) + +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 32 bits. Use + * EF4_MASK64 for higher width fields. + */ +#define EF4_MASK32(width) \ + ((width) == 32 ? ~((u32) 0) : \ + (((((u32) 1) << (width))) - 1)) + +/* A doubleword (i.e. 4 byte) datatype - little-endian in HW */ +typedef union ef4_dword { + __le32 u32[1]; +} ef4_dword_t; + +/* A quadword (i.e. 8 byte) datatype - little-endian in HW */ +typedef union ef4_qword { + __le64 u64[1]; + __le32 u32[2]; + ef4_dword_t dword[2]; +} ef4_qword_t; + +/* An octword (eight-word, i.e. 16 byte) datatype - little-endian in HW */ +typedef union ef4_oword { + __le64 u64[2]; + ef4_qword_t qword[2]; + __le32 u32[4]; + ef4_dword_t dword[4]; +} ef4_oword_t; + +/* Format string and value expanders for printk */ +#define EF4_DWORD_FMT "%08x" +#define EF4_QWORD_FMT "%08x:%08x" +#define EF4_OWORD_FMT "%08x:%08x:%08x:%08x" +#define EF4_DWORD_VAL(dword) \ + ((unsigned int) le32_to_cpu((dword).u32[0])) +#define EF4_QWORD_VAL(qword) \ + ((unsigned int) le32_to_cpu((qword).u32[1])), \ + ((unsigned int) le32_to_cpu((qword).u32[0])) +#define EF4_OWORD_VAL(oword) \ + ((unsigned int) le32_to_cpu((oword).u32[3])), \ + ((unsigned int) le32_to_cpu((oword).u32[2])), \ + ((unsigned int) le32_to_cpu((oword).u32[1])), \ + ((unsigned int) le32_to_cpu((oword).u32[0])) + +/* + * Extract bit field portion [low,high) from the native-endian element + * which contains bits [min,max). + * + * For example, suppose "element" represents the high 32 bits of a + * 64-bit value, and we wish to extract the bits belonging to the bit + * field occupying bits 28-45 of this 64-bit value. + * + * Then EF4_EXTRACT ( element, 32, 63, 28, 45 ) would give + * + * ( element ) << 4 + * + * The result will contain the relevant bits filled in in the range + * [0,high-low), with garbage in bits [high-low+1,...). + */ +#define EF4_EXTRACT_NATIVE(native_element, min, max, low, high) \ + ((low) > (max) || (high) < (min) ? 0 : \ + (low) > (min) ? \ + (native_element) >> ((low) - (min)) : \ + (native_element) << ((min) - (low))) + +/* + * Extract bit field portion [low,high) from the 64-bit little-endian + * element which contains bits [min,max) + */ +#define EF4_EXTRACT64(element, min, max, low, high) \ + EF4_EXTRACT_NATIVE(le64_to_cpu(element), min, max, low, high) + +/* + * Extract bit field portion [low,high) from the 32-bit little-endian + * element which contains bits [min,max) + */ +#define EF4_EXTRACT32(element, min, max, low, high) \ + EF4_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high) + +#define EF4_EXTRACT_OWORD64(oword, low, high) \ + ((EF4_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ + EF4_EXTRACT64((oword).u64[1], 64, 127, low, high)) & \ + EF4_MASK64((high) + 1 - (low))) + +#define EF4_EXTRACT_QWORD64(qword, low, high) \ + (EF4_EXTRACT64((qword).u64[0], 0, 63, low, high) & \ + EF4_MASK64((high) + 1 - (low))) + +#define EF4_EXTRACT_OWORD32(oword, low, high) \ + ((EF4_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ + EF4_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ + EF4_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ + EF4_EXTRACT32((oword).u32[3], 96, 127, low, high)) & \ + EF4_MASK32((high) + 1 - (low))) + +#define EF4_EXTRACT_QWORD32(qword, low, high) \ + ((EF4_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ + EF4_EXTRACT32((qword).u32[1], 32, 63, low, high)) & \ + EF4_MASK32((high) + 1 - (low))) + +#define EF4_EXTRACT_DWORD(dword, low, high) \ + (EF4_EXTRACT32((dword).u32[0], 0, 31, low, high) & \ + EF4_MASK32((high) + 1 - (low))) + +#define EF4_OWORD_FIELD64(oword, field) \ + EF4_EXTRACT_OWORD64(oword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field)) + +#define EF4_QWORD_FIELD64(qword, field) \ + EF4_EXTRACT_QWORD64(qword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field)) + +#define EF4_OWORD_FIELD32(oword, field) \ + EF4_EXTRACT_OWORD32(oword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field)) + +#define EF4_QWORD_FIELD32(qword, field) \ + EF4_EXTRACT_QWORD32(qword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field)) + +#define EF4_DWORD_FIELD(dword, field) \ + EF4_EXTRACT_DWORD(dword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field)) + +#define EF4_OWORD_IS_ZERO64(oword) \ + (((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0) + +#define EF4_QWORD_IS_ZERO64(qword) \ + (((qword).u64[0]) == (__force __le64) 0) + +#define EF4_OWORD_IS_ZERO32(oword) \ + (((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3]) \ + == (__force __le32) 0) + +#define EF4_QWORD_IS_ZERO32(qword) \ + (((qword).u32[0] | (qword).u32[1]) == (__force __le32) 0) + +#define EF4_DWORD_IS_ZERO(dword) \ + (((dword).u32[0]) == (__force __le32) 0) + +#define EF4_OWORD_IS_ALL_ONES64(oword) \ + (((oword).u64[0] & (oword).u64[1]) == ~((__force __le64) 0)) + +#define EF4_QWORD_IS_ALL_ONES64(qword) \ + ((qword).u64[0] == ~((__force __le64) 0)) + +#define EF4_OWORD_IS_ALL_ONES32(oword) \ + (((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \ + == ~((__force __le32) 0)) + +#define EF4_QWORD_IS_ALL_ONES32(qword) \ + (((qword).u32[0] & (qword).u32[1]) == ~((__force __le32) 0)) + +#define EF4_DWORD_IS_ALL_ONES(dword) \ + ((dword).u32[0] == ~((__force __le32) 0)) + +#if BITS_PER_LONG == 64 +#define EF4_OWORD_FIELD EF4_OWORD_FIELD64 +#define EF4_QWORD_FIELD EF4_QWORD_FIELD64 +#define EF4_OWORD_IS_ZERO EF4_OWORD_IS_ZERO64 +#define EF4_QWORD_IS_ZERO EF4_QWORD_IS_ZERO64 +#define EF4_OWORD_IS_ALL_ONES EF4_OWORD_IS_ALL_ONES64 +#define EF4_QWORD_IS_ALL_ONES EF4_QWORD_IS_ALL_ONES64 +#else +#define EF4_OWORD_FIELD EF4_OWORD_FIELD32 +#define EF4_QWORD_FIELD EF4_QWORD_FIELD32 +#define EF4_OWORD_IS_ZERO EF4_OWORD_IS_ZERO32 +#define EF4_QWORD_IS_ZERO EF4_QWORD_IS_ZERO32 +#define EF4_OWORD_IS_ALL_ONES EF4_OWORD_IS_ALL_ONES32 +#define EF4_QWORD_IS_ALL_ONES EF4_QWORD_IS_ALL_ONES32 +#endif + +/* + * Construct bit field portion + * + * Creates the portion of the bit field [low,high) that lies within + * the range [min,max). + */ +#define EF4_INSERT_NATIVE64(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u64) (value)) << (low - min)) : \ + (((u64) (value)) >> (min - low)))) + +#define EF4_INSERT_NATIVE32(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u32) (value)) << (low - min)) : \ + (((u32) (value)) >> (min - low)))) + +#define EF4_INSERT_NATIVE(min, max, low, high, value) \ + ((((max - min) >= 32) || ((high - low) >= 32)) ? \ + EF4_INSERT_NATIVE64(min, max, low, high, value) : \ + EF4_INSERT_NATIVE32(min, max, low, high, value)) + +/* + * Construct bit field portion + * + * Creates the portion of the named bit field that lies within the + * range [min,max). + */ +#define EF4_INSERT_FIELD_NATIVE(min, max, field, value) \ + EF4_INSERT_NATIVE(min, max, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field), value) + +/* + * Construct bit field + * + * Creates the portion of the named bit fields that lie within the + * range [min,max). + */ +#define EF4_INSERT_FIELDS_NATIVE(min, max, \ + field1, value1, \ + field2, value2, \ + field3, value3, \ + field4, value4, \ + field5, value5, \ + field6, value6, \ + field7, value7, \ + field8, value8, \ + field9, value9, \ + field10, value10) \ + (EF4_INSERT_FIELD_NATIVE((min), (max), field1, (value1)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field2, (value2)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field3, (value3)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field4, (value4)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field5, (value5)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field6, (value6)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field7, (value7)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field8, (value8)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field9, (value9)) | \ + EF4_INSERT_FIELD_NATIVE((min), (max), field10, (value10))) + +#define EF4_INSERT_FIELDS64(...) \ + cpu_to_le64(EF4_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EF4_INSERT_FIELDS32(...) \ + cpu_to_le32(EF4_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EF4_POPULATE_OWORD64(oword, ...) do { \ + (oword).u64[0] = EF4_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + (oword).u64[1] = EF4_INSERT_FIELDS64(64, 127, __VA_ARGS__); \ + } while (0) + +#define EF4_POPULATE_QWORD64(qword, ...) do { \ + (qword).u64[0] = EF4_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + } while (0) + +#define EF4_POPULATE_OWORD32(oword, ...) do { \ + (oword).u32[0] = EF4_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (oword).u32[1] = EF4_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + (oword).u32[2] = EF4_INSERT_FIELDS32(64, 95, __VA_ARGS__); \ + (oword).u32[3] = EF4_INSERT_FIELDS32(96, 127, __VA_ARGS__); \ + } while (0) + +#define EF4_POPULATE_QWORD32(qword, ...) do { \ + (qword).u32[0] = EF4_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (qword).u32[1] = EF4_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + } while (0) + +#define EF4_POPULATE_DWORD(dword, ...) do { \ + (dword).u32[0] = EF4_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + } while (0) + +#if BITS_PER_LONG == 64 +#define EF4_POPULATE_OWORD EF4_POPULATE_OWORD64 +#define EF4_POPULATE_QWORD EF4_POPULATE_QWORD64 +#else +#define EF4_POPULATE_OWORD EF4_POPULATE_OWORD32 +#define EF4_POPULATE_QWORD EF4_POPULATE_QWORD32 +#endif + +/* Populate an octword field with various numbers of arguments */ +#define EF4_POPULATE_OWORD_10 EF4_POPULATE_OWORD +#define EF4_POPULATE_OWORD_9(oword, ...) \ + EF4_POPULATE_OWORD_10(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_8(oword, ...) \ + EF4_POPULATE_OWORD_9(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_7(oword, ...) \ + EF4_POPULATE_OWORD_8(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_6(oword, ...) \ + EF4_POPULATE_OWORD_7(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_5(oword, ...) \ + EF4_POPULATE_OWORD_6(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_4(oword, ...) \ + EF4_POPULATE_OWORD_5(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_3(oword, ...) \ + EF4_POPULATE_OWORD_4(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_2(oword, ...) \ + EF4_POPULATE_OWORD_3(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_OWORD_1(oword, ...) \ + EF4_POPULATE_OWORD_2(oword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_ZERO_OWORD(oword) \ + EF4_POPULATE_OWORD_1(oword, EF4_DUMMY_FIELD, 0) +#define EF4_SET_OWORD(oword) \ + EF4_POPULATE_OWORD_4(oword, \ + EF4_DWORD_0, 0xffffffff, \ + EF4_DWORD_1, 0xffffffff, \ + EF4_DWORD_2, 0xffffffff, \ + EF4_DWORD_3, 0xffffffff) + +/* Populate a quadword field with various numbers of arguments */ +#define EF4_POPULATE_QWORD_10 EF4_POPULATE_QWORD +#define EF4_POPULATE_QWORD_9(qword, ...) \ + EF4_POPULATE_QWORD_10(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_8(qword, ...) \ + EF4_POPULATE_QWORD_9(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_7(qword, ...) \ + EF4_POPULATE_QWORD_8(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_6(qword, ...) \ + EF4_POPULATE_QWORD_7(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_5(qword, ...) \ + EF4_POPULATE_QWORD_6(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_4(qword, ...) \ + EF4_POPULATE_QWORD_5(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_3(qword, ...) \ + EF4_POPULATE_QWORD_4(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_2(qword, ...) \ + EF4_POPULATE_QWORD_3(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_QWORD_1(qword, ...) \ + EF4_POPULATE_QWORD_2(qword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_ZERO_QWORD(qword) \ + EF4_POPULATE_QWORD_1(qword, EF4_DUMMY_FIELD, 0) +#define EF4_SET_QWORD(qword) \ + EF4_POPULATE_QWORD_2(qword, \ + EF4_DWORD_0, 0xffffffff, \ + EF4_DWORD_1, 0xffffffff) + +/* Populate a dword field with various numbers of arguments */ +#define EF4_POPULATE_DWORD_10 EF4_POPULATE_DWORD +#define EF4_POPULATE_DWORD_9(dword, ...) \ + EF4_POPULATE_DWORD_10(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_8(dword, ...) \ + EF4_POPULATE_DWORD_9(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_7(dword, ...) \ + EF4_POPULATE_DWORD_8(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_6(dword, ...) \ + EF4_POPULATE_DWORD_7(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_5(dword, ...) \ + EF4_POPULATE_DWORD_6(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_4(dword, ...) \ + EF4_POPULATE_DWORD_5(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_3(dword, ...) \ + EF4_POPULATE_DWORD_4(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_2(dword, ...) \ + EF4_POPULATE_DWORD_3(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_POPULATE_DWORD_1(dword, ...) \ + EF4_POPULATE_DWORD_2(dword, EF4_DUMMY_FIELD, 0, __VA_ARGS__) +#define EF4_ZERO_DWORD(dword) \ + EF4_POPULATE_DWORD_1(dword, EF4_DUMMY_FIELD, 0) +#define EF4_SET_DWORD(dword) \ + EF4_POPULATE_DWORD_1(dword, EF4_DWORD_0, 0xffffffff) + +/* + * Modify a named field within an already-populated structure. Used + * for read-modify-write operations. + * + */ +#define EF4_INVERT_OWORD(oword) do { \ + (oword).u64[0] = ~((oword).u64[0]); \ + (oword).u64[1] = ~((oword).u64[1]); \ + } while (0) + +#define EF4_AND_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] & (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] & (mask).u64[1]; \ + } while (0) + +#define EF4_OR_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] | (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] | (mask).u64[1]; \ + } while (0) + +#define EF4_INSERT64(min, max, low, high, value) \ + cpu_to_le64(EF4_INSERT_NATIVE(min, max, low, high, value)) + +#define EF4_INSERT32(min, max, low, high, value) \ + cpu_to_le32(EF4_INSERT_NATIVE(min, max, low, high, value)) + +#define EF4_INPLACE_MASK64(min, max, low, high) \ + EF4_INSERT64(min, max, low, high, EF4_MASK64((high) + 1 - (low))) + +#define EF4_INPLACE_MASK32(min, max, low, high) \ + EF4_INSERT32(min, max, low, high, EF4_MASK32((high) + 1 - (low))) + +#define EF4_SET_OWORD64(oword, low, high, value) do { \ + (oword).u64[0] = (((oword).u64[0] \ + & ~EF4_INPLACE_MASK64(0, 63, low, high)) \ + | EF4_INSERT64(0, 63, low, high, value)); \ + (oword).u64[1] = (((oword).u64[1] \ + & ~EF4_INPLACE_MASK64(64, 127, low, high)) \ + | EF4_INSERT64(64, 127, low, high, value)); \ + } while (0) + +#define EF4_SET_QWORD64(qword, low, high, value) do { \ + (qword).u64[0] = (((qword).u64[0] \ + & ~EF4_INPLACE_MASK64(0, 63, low, high)) \ + | EF4_INSERT64(0, 63, low, high, value)); \ + } while (0) + +#define EF4_SET_OWORD32(oword, low, high, value) do { \ + (oword).u32[0] = (((oword).u32[0] \ + & ~EF4_INPLACE_MASK32(0, 31, low, high)) \ + | EF4_INSERT32(0, 31, low, high, value)); \ + (oword).u32[1] = (((oword).u32[1] \ + & ~EF4_INPLACE_MASK32(32, 63, low, high)) \ + | EF4_INSERT32(32, 63, low, high, value)); \ + (oword).u32[2] = (((oword).u32[2] \ + & ~EF4_INPLACE_MASK32(64, 95, low, high)) \ + | EF4_INSERT32(64, 95, low, high, value)); \ + (oword).u32[3] = (((oword).u32[3] \ + & ~EF4_INPLACE_MASK32(96, 127, low, high)) \ + | EF4_INSERT32(96, 127, low, high, value)); \ + } while (0) + +#define EF4_SET_QWORD32(qword, low, high, value) do { \ + (qword).u32[0] = (((qword).u32[0] \ + & ~EF4_INPLACE_MASK32(0, 31, low, high)) \ + | EF4_INSERT32(0, 31, low, high, value)); \ + (qword).u32[1] = (((qword).u32[1] \ + & ~EF4_INPLACE_MASK32(32, 63, low, high)) \ + | EF4_INSERT32(32, 63, low, high, value)); \ + } while (0) + +#define EF4_SET_DWORD32(dword, low, high, value) do { \ + (dword).u32[0] = (((dword).u32[0] \ + & ~EF4_INPLACE_MASK32(0, 31, low, high)) \ + | EF4_INSERT32(0, 31, low, high, value)); \ + } while (0) + +#define EF4_SET_OWORD_FIELD64(oword, field, value) \ + EF4_SET_OWORD64(oword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field), value) + +#define EF4_SET_QWORD_FIELD64(qword, field, value) \ + EF4_SET_QWORD64(qword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field), value) + +#define EF4_SET_OWORD_FIELD32(oword, field, value) \ + EF4_SET_OWORD32(oword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field), value) + +#define EF4_SET_QWORD_FIELD32(qword, field, value) \ + EF4_SET_QWORD32(qword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field), value) + +#define EF4_SET_DWORD_FIELD(dword, field, value) \ + EF4_SET_DWORD32(dword, EF4_LOW_BIT(field), \ + EF4_HIGH_BIT(field), value) + + + +#if BITS_PER_LONG == 64 +#define EF4_SET_OWORD_FIELD EF4_SET_OWORD_FIELD64 +#define EF4_SET_QWORD_FIELD EF4_SET_QWORD_FIELD64 +#else +#define EF4_SET_OWORD_FIELD EF4_SET_OWORD_FIELD32 +#define EF4_SET_QWORD_FIELD EF4_SET_QWORD_FIELD32 +#endif + +/* Used to avoid compiler warnings about shift range exceeding width + * of the data types when dma_addr_t is only 32 bits wide. + */ +#define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t)) +#define EF4_DMA_TYPE_WIDTH(width) \ + (((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH) + + +/* Static initialiser */ +#define EF4_OWORD32(a, b, c, d) \ + { .u32 = { cpu_to_le32(a), cpu_to_le32(b), \ + cpu_to_le32(c), cpu_to_le32(d) } } + +#endif /* EF4_BITFIELD_H */ diff --git a/drivers/net/ethernet/sfc/falcon/efx.c b/drivers/net/ethernet/sfc/falcon/efx.c new file mode 100644 index 0000000..5c5cb3c --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/efx.c @@ -0,0 +1,3350 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "efx.h" +#include "nic.h" +#include "selftest.h" + +#include "workarounds.h" + +/************************************************************************** + * + * Type name strings + * + ************************************************************************** + */ + +/* Loopback mode names (see LOOPBACK_MODE()) */ +const unsigned int ef4_loopback_mode_max = LOOPBACK_MAX; +const char *const ef4_loopback_mode_names[] = { + [LOOPBACK_NONE] = "NONE", + [LOOPBACK_DATA] = "DATAPATH", + [LOOPBACK_GMAC] = "GMAC", + [LOOPBACK_XGMII] = "XGMII", + [LOOPBACK_XGXS] = "XGXS", + [LOOPBACK_XAUI] = "XAUI", + [LOOPBACK_GMII] = "GMII", + [LOOPBACK_SGMII] = "SGMII", + [LOOPBACK_XGBR] = "XGBR", + [LOOPBACK_XFI] = "XFI", + [LOOPBACK_XAUI_FAR] = "XAUI_FAR", + [LOOPBACK_GMII_FAR] = "GMII_FAR", + [LOOPBACK_SGMII_FAR] = "SGMII_FAR", + [LOOPBACK_XFI_FAR] = "XFI_FAR", + [LOOPBACK_GPHY] = "GPHY", + [LOOPBACK_PHYXS] = "PHYXS", + [LOOPBACK_PCS] = "PCS", + [LOOPBACK_PMAPMD] = "PMA/PMD", + [LOOPBACK_XPORT] = "XPORT", + [LOOPBACK_XGMII_WS] = "XGMII_WS", + [LOOPBACK_XAUI_WS] = "XAUI_WS", + [LOOPBACK_XAUI_WS_FAR] = "XAUI_WS_FAR", + [LOOPBACK_XAUI_WS_NEAR] = "XAUI_WS_NEAR", + [LOOPBACK_GMII_WS] = "GMII_WS", + [LOOPBACK_XFI_WS] = "XFI_WS", + [LOOPBACK_XFI_WS_FAR] = "XFI_WS_FAR", + [LOOPBACK_PHYXS_WS] = "PHYXS_WS", +}; + +const unsigned int ef4_reset_type_max = RESET_TYPE_MAX; +const char *const ef4_reset_type_names[] = { + [RESET_TYPE_INVISIBLE] = "INVISIBLE", + [RESET_TYPE_ALL] = "ALL", + [RESET_TYPE_RECOVER_OR_ALL] = "RECOVER_OR_ALL", + [RESET_TYPE_WORLD] = "WORLD", + [RESET_TYPE_RECOVER_OR_DISABLE] = "RECOVER_OR_DISABLE", + [RESET_TYPE_DATAPATH] = "DATAPATH", + [RESET_TYPE_DISABLE] = "DISABLE", + [RESET_TYPE_TX_WATCHDOG] = "TX_WATCHDOG", + [RESET_TYPE_INT_ERROR] = "INT_ERROR", + [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY", + [RESET_TYPE_DMA_ERROR] = "DMA_ERROR", + [RESET_TYPE_TX_SKIP] = "TX_SKIP", +}; + +/* Reset workqueue. If any NIC has a hardware failure then a reset will be + * queued onto this work queue. This is not a per-nic work queue, because + * ef4_reset_work() acquires the rtnl lock, so resets are naturally serialised. + */ +static struct workqueue_struct *reset_workqueue; + +/* How often and how many times to poll for a reset while waiting for a + * BIST that another function started to complete. + */ +#define BIST_WAIT_DELAY_MS 100 +#define BIST_WAIT_DELAY_COUNT 100 + +/************************************************************************** + * + * Configurable values + * + *************************************************************************/ + +/* + * Use separate channels for TX and RX events + * + * Set this to 1 to use separate channels for TX and RX. It allows us + * to control interrupt affinity separately for TX and RX. + * + * This is only used in MSI-X interrupt mode + */ +bool ef4_separate_tx_channels; +module_param(ef4_separate_tx_channels, bool, 0444); +MODULE_PARM_DESC(ef4_separate_tx_channels, + "Use separate channels for TX and RX"); + +/* This is the weight assigned to each of the (per-channel) virtual + * NAPI devices. + */ +static int napi_weight = 64; + +/* This is the time (in jiffies) between invocations of the hardware + * monitor. + * On Falcon-based NICs, this will: + * - Check the on-board hardware monitor; + * - Poll the link state and reconfigure the hardware as necessary. + * On Siena-based NICs for power systems with EEH support, this will give EEH a + * chance to start. + */ +static unsigned int ef4_monitor_interval = 1 * HZ; + +/* Initial interrupt moderation settings. They can be modified after + * module load with ethtool. + * + * The default for RX should strike a balance between increasing the + * round-trip latency and reducing overhead. + */ +static unsigned int rx_irq_mod_usec = 60; + +/* Initial interrupt moderation settings. They can be modified after + * module load with ethtool. + * + * This default is chosen to ensure that a 10G link does not go idle + * while a TX queue is stopped after it has become full. A queue is + * restarted when it drops below half full. The time this takes (assuming + * worst case 3 descriptors per packet and 1024 descriptors) is + * 512 / 3 * 1.2 = 205 usec. + */ +static unsigned int tx_irq_mod_usec = 150; + +/* This is the first interrupt mode to try out of: + * 0 => MSI-X + * 1 => MSI + * 2 => legacy + */ +static unsigned int interrupt_mode; + +/* This is the requested number of CPUs to use for Receive-Side Scaling (RSS), + * i.e. the number of CPUs among which we may distribute simultaneous + * interrupt handling. + * + * Cards without MSI-X will only target one CPU via legacy or MSI interrupt. + * The default (0) means to assign an interrupt to each core. + */ +static unsigned int rss_cpus; +module_param(rss_cpus, uint, 0444); +MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling"); + +static bool phy_flash_cfg; +module_param(phy_flash_cfg, bool, 0644); +MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); + +static unsigned irq_adapt_low_thresh = 8000; +module_param(irq_adapt_low_thresh, uint, 0644); +MODULE_PARM_DESC(irq_adapt_low_thresh, + "Threshold score for reducing IRQ moderation"); + +static unsigned irq_adapt_high_thresh = 16000; +module_param(irq_adapt_high_thresh, uint, 0644); +MODULE_PARM_DESC(irq_adapt_high_thresh, + "Threshold score for increasing IRQ moderation"); + +static unsigned debug = (NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_IFDOWN | + NETIF_MSG_IFUP | NETIF_MSG_RX_ERR | + NETIF_MSG_TX_ERR | NETIF_MSG_HW); +module_param(debug, uint, 0); +MODULE_PARM_DESC(debug, "Bitmapped debugging message enable value"); + +/************************************************************************** + * + * Utility functions and prototypes + * + *************************************************************************/ + +static int ef4_soft_enable_interrupts(struct ef4_nic *efx); +static void ef4_soft_disable_interrupts(struct ef4_nic *efx); +static void ef4_remove_channel(struct ef4_channel *channel); +static void ef4_remove_channels(struct ef4_nic *efx); +static const struct ef4_channel_type ef4_default_channel_type; +static void ef4_remove_port(struct ef4_nic *efx); +static void ef4_init_napi_channel(struct ef4_channel *channel); +static void ef4_fini_napi(struct ef4_nic *efx); +static void ef4_fini_napi_channel(struct ef4_channel *channel); +static void ef4_fini_struct(struct ef4_nic *efx); +static void ef4_start_all(struct ef4_nic *efx); +static void ef4_stop_all(struct ef4_nic *efx); + +#define EF4_ASSERT_RESET_SERIALISED(efx) \ + do { \ + if ((efx->state == STATE_READY) || \ + (efx->state == STATE_RECOVERY) || \ + (efx->state == STATE_DISABLED)) \ + ASSERT_RTNL(); \ + } while (0) + +static int ef4_check_disabled(struct ef4_nic *efx) +{ + if (efx->state == STATE_DISABLED || efx->state == STATE_RECOVERY) { + netif_err(efx, drv, efx->net_dev, + "device is disabled due to earlier errors\n"); + return -EIO; + } + return 0; +} + +/************************************************************************** + * + * Event queue processing + * + *************************************************************************/ + +/* Process channel's event queue + * + * This function is responsible for processing the event queue of a + * single channel. The caller must guarantee that this function will + * never be concurrently called more than once on the same channel, + * though different channels may be being processed concurrently. + */ +static int ef4_process_channel(struct ef4_channel *channel, int budget) +{ + struct ef4_tx_queue *tx_queue; + int spent; + + if (unlikely(!channel->enabled)) + return 0; + + ef4_for_each_channel_tx_queue(tx_queue, channel) { + tx_queue->pkts_compl = 0; + tx_queue->bytes_compl = 0; + } + + spent = ef4_nic_process_eventq(channel, budget); + if (spent && ef4_channel_has_rx_queue(channel)) { + struct ef4_rx_queue *rx_queue = + ef4_channel_get_rx_queue(channel); + + ef4_rx_flush_packet(channel); + ef4_fast_push_rx_descriptors(rx_queue, true); + } + + /* Update BQL */ + ef4_for_each_channel_tx_queue(tx_queue, channel) { + if (tx_queue->bytes_compl) { + netdev_tx_completed_queue(tx_queue->core_txq, + tx_queue->pkts_compl, tx_queue->bytes_compl); + } + } + + return spent; +} + +/* NAPI poll handler + * + * NAPI guarantees serialisation of polls of the same device, which + * provides the guarantee required by ef4_process_channel(). + */ +static void ef4_update_irq_mod(struct ef4_nic *efx, struct ef4_channel *channel) +{ + int step = efx->irq_mod_step_us; + + if (channel->irq_mod_score < irq_adapt_low_thresh) { + if (channel->irq_moderation_us > step) { + channel->irq_moderation_us -= step; + efx->type->push_irq_moderation(channel); + } + } else if (channel->irq_mod_score > irq_adapt_high_thresh) { + if (channel->irq_moderation_us < + efx->irq_rx_moderation_us) { + channel->irq_moderation_us += step; + efx->type->push_irq_moderation(channel); + } + } + + channel->irq_count = 0; + channel->irq_mod_score = 0; +} + +static int ef4_poll(struct napi_struct *napi, int budget) +{ + struct ef4_channel *channel = + container_of(napi, struct ef4_channel, napi_str); + struct ef4_nic *efx = channel->efx; + int spent; + + if (!ef4_channel_lock_napi(channel)) + return budget; + + netif_vdbg(efx, intr, efx->net_dev, + "channel %d NAPI poll executing on CPU %d\n", + channel->channel, raw_smp_processor_id()); + + spent = ef4_process_channel(channel, budget); + + if (spent < budget) { + if (ef4_channel_has_rx_queue(channel) && + efx->irq_rx_adaptive && + unlikely(++channel->irq_count == 1000)) { + ef4_update_irq_mod(efx, channel); + } + + ef4_filter_rfs_expire(channel); + + /* There is no race here; although napi_disable() will + * only wait for napi_complete(), this isn't a problem + * since ef4_nic_eventq_read_ack() will have no effect if + * interrupts have already been disabled. + */ + napi_complete(napi); + ef4_nic_eventq_read_ack(channel); + } + + ef4_channel_unlock_napi(channel); + return spent; +} + +/* Create event queue + * Event queue memory allocations are done only once. If the channel + * is reset, the memory buffer will be reused; this guards against + * errors during channel reset and also simplifies interrupt handling. + */ +static int ef4_probe_eventq(struct ef4_channel *channel) +{ + struct ef4_nic *efx = channel->efx; + unsigned long entries; + + netif_dbg(efx, probe, efx->net_dev, + "chan %d create event queue\n", channel->channel); + + /* Build an event queue with room for one event per tx and rx buffer, + * plus some extra for link state events and MCDI completions. */ + entries = roundup_pow_of_two(efx->rxq_entries + efx->txq_entries + 128); + EF4_BUG_ON_PARANOID(entries > EF4_MAX_EVQ_SIZE); + channel->eventq_mask = max(entries, EF4_MIN_EVQ_SIZE) - 1; + + return ef4_nic_probe_eventq(channel); +} + +/* Prepare channel's event queue */ +static int ef4_init_eventq(struct ef4_channel *channel) +{ + struct ef4_nic *efx = channel->efx; + int rc; + + EF4_WARN_ON_PARANOID(channel->eventq_init); + + netif_dbg(efx, drv, efx->net_dev, + "chan %d init event queue\n", channel->channel); + + rc = ef4_nic_init_eventq(channel); + if (rc == 0) { + efx->type->push_irq_moderation(channel); + channel->eventq_read_ptr = 0; + channel->eventq_init = true; + } + return rc; +} + +/* Enable event queue processing and NAPI */ +void ef4_start_eventq(struct ef4_channel *channel) +{ + netif_dbg(channel->efx, ifup, channel->efx->net_dev, + "chan %d start event queue\n", channel->channel); + + /* Make sure the NAPI handler sees the enabled flag set */ + channel->enabled = true; + smp_wmb(); + + ef4_channel_enable(channel); + napi_enable(&channel->napi_str); + ef4_nic_eventq_read_ack(channel); +} + +/* Disable event queue processing and NAPI */ +void ef4_stop_eventq(struct ef4_channel *channel) +{ + if (!channel->enabled) + return; + + napi_disable(&channel->napi_str); + while (!ef4_channel_disable(channel)) + usleep_range(1000, 20000); + channel->enabled = false; +} + +static void ef4_fini_eventq(struct ef4_channel *channel) +{ + if (!channel->eventq_init) + return; + + netif_dbg(channel->efx, drv, channel->efx->net_dev, + "chan %d fini event queue\n", channel->channel); + + ef4_nic_fini_eventq(channel); + channel->eventq_init = false; +} + +static void ef4_remove_eventq(struct ef4_channel *channel) +{ + netif_dbg(channel->efx, drv, channel->efx->net_dev, + "chan %d remove event queue\n", channel->channel); + + ef4_nic_remove_eventq(channel); +} + +/************************************************************************** + * + * Channel handling + * + *************************************************************************/ + +/* Allocate and initialise a channel structure. */ +static struct ef4_channel * +ef4_alloc_channel(struct ef4_nic *efx, int i, struct ef4_channel *old_channel) +{ + struct ef4_channel *channel; + struct ef4_rx_queue *rx_queue; + struct ef4_tx_queue *tx_queue; + int j; + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return NULL; + + channel->efx = efx; + channel->channel = i; + channel->type = &ef4_default_channel_type; + + for (j = 0; j < EF4_TXQ_TYPES; j++) { + tx_queue = &channel->tx_queue[j]; + tx_queue->efx = efx; + tx_queue->queue = i * EF4_TXQ_TYPES + j; + tx_queue->channel = channel; + } + + rx_queue = &channel->rx_queue; + rx_queue->efx = efx; + setup_timer(&rx_queue->slow_fill, ef4_rx_slow_fill, + (unsigned long)rx_queue); + + return channel; +} + +/* Allocate and initialise a channel structure, copying parameters + * (but not resources) from an old channel structure. + */ +static struct ef4_channel * +ef4_copy_channel(const struct ef4_channel *old_channel) +{ + struct ef4_channel *channel; + struct ef4_rx_queue *rx_queue; + struct ef4_tx_queue *tx_queue; + int j; + + channel = kmalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return NULL; + + *channel = *old_channel; + + channel->napi_dev = NULL; + INIT_HLIST_NODE(&channel->napi_str.napi_hash_node); + channel->napi_str.napi_id = 0; + channel->napi_str.state = 0; + memset(&channel->eventq, 0, sizeof(channel->eventq)); + + for (j = 0; j < EF4_TXQ_TYPES; j++) { + tx_queue = &channel->tx_queue[j]; + if (tx_queue->channel) + tx_queue->channel = channel; + tx_queue->buffer = NULL; + memset(&tx_queue->txd, 0, sizeof(tx_queue->txd)); + } + + rx_queue = &channel->rx_queue; + rx_queue->buffer = NULL; + memset(&rx_queue->rxd, 0, sizeof(rx_queue->rxd)); + setup_timer(&rx_queue->slow_fill, ef4_rx_slow_fill, + (unsigned long)rx_queue); + + return channel; +} + +static int ef4_probe_channel(struct ef4_channel *channel) +{ + struct ef4_tx_queue *tx_queue; + struct ef4_rx_queue *rx_queue; + int rc; + + netif_dbg(channel->efx, probe, channel->efx->net_dev, + "creating channel %d\n", channel->channel); + + rc = channel->type->pre_probe(channel); + if (rc) + goto fail; + + rc = ef4_probe_eventq(channel); + if (rc) + goto fail; + + ef4_for_each_channel_tx_queue(tx_queue, channel) { + rc = ef4_probe_tx_queue(tx_queue); + if (rc) + goto fail; + } + + ef4_for_each_channel_rx_queue(rx_queue, channel) { + rc = ef4_probe_rx_queue(rx_queue); + if (rc) + goto fail; + } + + return 0; + +fail: + ef4_remove_channel(channel); + return rc; +} + +static void +ef4_get_channel_name(struct ef4_channel *channel, char *buf, size_t len) +{ + struct ef4_nic *efx = channel->efx; + const char *type; + int number; + + number = channel->channel; + if (efx->tx_channel_offset == 0) { + type = ""; + } else if (channel->channel < efx->tx_channel_offset) { + type = "-rx"; + } else { + type = "-tx"; + number -= efx->tx_channel_offset; + } + snprintf(buf, len, "%s%s-%d", efx->name, type, number); +} + +static void ef4_set_channel_names(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + channel->type->get_name(channel, + efx->msi_context[channel->channel].name, + sizeof(efx->msi_context[0].name)); +} + +static int ef4_probe_channels(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + int rc; + + /* Restart special buffer allocation */ + efx->next_buffer_table = 0; + + /* Probe channels in reverse, so that any 'extra' channels + * use the start of the buffer table. This allows the traffic + * channels to be resized without moving them or wasting the + * entries before them. + */ + ef4_for_each_channel_rev(channel, efx) { + rc = ef4_probe_channel(channel); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "failed to create channel %d\n", + channel->channel); + goto fail; + } + } + ef4_set_channel_names(efx); + + return 0; + +fail: + ef4_remove_channels(efx); + return rc; +} + +/* Channels are shutdown and reinitialised whilst the NIC is running + * to propagate configuration changes (mtu, checksum offload), or + * to clear hardware error conditions + */ +static void ef4_start_datapath(struct ef4_nic *efx) +{ + netdev_features_t old_features = efx->net_dev->features; + bool old_rx_scatter = efx->rx_scatter; + struct ef4_tx_queue *tx_queue; + struct ef4_rx_queue *rx_queue; + struct ef4_channel *channel; + size_t rx_buf_len; + + /* Calculate the rx buffer allocation parameters required to + * support the current MTU, including padding for header + * alignment and overruns. + */ + efx->rx_dma_len = (efx->rx_prefix_size + + EF4_MAX_FRAME_LEN(efx->net_dev->mtu) + + efx->type->rx_buffer_padding); + rx_buf_len = (sizeof(struct ef4_rx_page_state) + + efx->rx_ip_align + efx->rx_dma_len); + if (rx_buf_len <= PAGE_SIZE) { + efx->rx_scatter = efx->type->always_rx_scatter; + efx->rx_buffer_order = 0; + } else if (efx->type->can_rx_scatter) { + BUILD_BUG_ON(EF4_RX_USR_BUF_SIZE % L1_CACHE_BYTES); + BUILD_BUG_ON(sizeof(struct ef4_rx_page_state) + + 2 * ALIGN(NET_IP_ALIGN + EF4_RX_USR_BUF_SIZE, + EF4_RX_BUF_ALIGNMENT) > + PAGE_SIZE); + efx->rx_scatter = true; + efx->rx_dma_len = EF4_RX_USR_BUF_SIZE; + efx->rx_buffer_order = 0; + } else { + efx->rx_scatter = false; + efx->rx_buffer_order = get_order(rx_buf_len); + } + + ef4_rx_config_page_split(efx); + if (efx->rx_buffer_order) + netif_dbg(efx, drv, efx->net_dev, + "RX buf len=%u; page order=%u batch=%u\n", + efx->rx_dma_len, efx->rx_buffer_order, + efx->rx_pages_per_batch); + else + netif_dbg(efx, drv, efx->net_dev, + "RX buf len=%u step=%u bpp=%u; page batch=%u\n", + efx->rx_dma_len, efx->rx_page_buf_step, + efx->rx_bufs_per_page, efx->rx_pages_per_batch); + + /* Restore previously fixed features in hw_features and remove + * features which are fixed now + */ + efx->net_dev->hw_features |= efx->net_dev->features; + efx->net_dev->hw_features &= ~efx->fixed_features; + efx->net_dev->features |= efx->fixed_features; + if (efx->net_dev->features != old_features) + netdev_features_change(efx->net_dev); + + /* RX filters may also have scatter-enabled flags */ + if (efx->rx_scatter != old_rx_scatter) + efx->type->filter_update_rx_scatter(efx); + + /* We must keep at least one descriptor in a TX ring empty. + * We could avoid this when the queue size does not exactly + * match the hardware ring size, but it's not that important. + * Therefore we stop the queue when one more skb might fill + * the ring completely. We wake it when half way back to + * empty. + */ + efx->txq_stop_thresh = efx->txq_entries - ef4_tx_max_skb_descs(efx); + efx->txq_wake_thresh = efx->txq_stop_thresh / 2; + + /* Initialise the channels */ + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_tx_queue(tx_queue, channel) { + ef4_init_tx_queue(tx_queue); + atomic_inc(&efx->active_queues); + } + + ef4_for_each_channel_rx_queue(rx_queue, channel) { + ef4_init_rx_queue(rx_queue); + atomic_inc(&efx->active_queues); + ef4_stop_eventq(channel); + ef4_fast_push_rx_descriptors(rx_queue, false); + ef4_start_eventq(channel); + } + + WARN_ON(channel->rx_pkt_n_frags); + } + + if (netif_device_present(efx->net_dev)) + netif_tx_wake_all_queues(efx->net_dev); +} + +static void ef4_stop_datapath(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + struct ef4_tx_queue *tx_queue; + struct ef4_rx_queue *rx_queue; + int rc; + + EF4_ASSERT_RESET_SERIALISED(efx); + BUG_ON(efx->port_enabled); + + /* Stop RX refill */ + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_rx_queue(rx_queue, channel) + rx_queue->refill_enabled = false; + } + + ef4_for_each_channel(channel, efx) { + /* RX packet processing is pipelined, so wait for the + * NAPI handler to complete. At least event queue 0 + * might be kept active by non-data events, so don't + * use napi_synchronize() but actually disable NAPI + * temporarily. + */ + if (ef4_channel_has_rx_queue(channel)) { + ef4_stop_eventq(channel); + ef4_start_eventq(channel); + } + } + + rc = efx->type->fini_dmaq(efx); + if (rc && EF4_WORKAROUND_7803(efx)) { + /* Schedule a reset to recover from the flush failure. The + * descriptor caches reference memory we're about to free, + * but falcon_reconfigure_mac_wrapper() won't reconnect + * the MACs because of the pending reset. + */ + netif_err(efx, drv, efx->net_dev, + "Resetting to recover from flush failure\n"); + ef4_schedule_reset(efx, RESET_TYPE_ALL); + } else if (rc) { + netif_err(efx, drv, efx->net_dev, "failed to flush queues\n"); + } else { + netif_dbg(efx, drv, efx->net_dev, + "successfully flushed all queues\n"); + } + + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_rx_queue(rx_queue, channel) + ef4_fini_rx_queue(rx_queue); + ef4_for_each_possible_channel_tx_queue(tx_queue, channel) + ef4_fini_tx_queue(tx_queue); + } +} + +static void ef4_remove_channel(struct ef4_channel *channel) +{ + struct ef4_tx_queue *tx_queue; + struct ef4_rx_queue *rx_queue; + + netif_dbg(channel->efx, drv, channel->efx->net_dev, + "destroy chan %d\n", channel->channel); + + ef4_for_each_channel_rx_queue(rx_queue, channel) + ef4_remove_rx_queue(rx_queue); + ef4_for_each_possible_channel_tx_queue(tx_queue, channel) + ef4_remove_tx_queue(tx_queue); + ef4_remove_eventq(channel); + channel->type->post_remove(channel); +} + +static void ef4_remove_channels(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + ef4_remove_channel(channel); +} + +int +ef4_realloc_channels(struct ef4_nic *efx, u32 rxq_entries, u32 txq_entries) +{ + struct ef4_channel *other_channel[EF4_MAX_CHANNELS], *channel; + u32 old_rxq_entries, old_txq_entries; + unsigned i, next_buffer_table = 0; + int rc, rc2; + + rc = ef4_check_disabled(efx); + if (rc) + return rc; + + /* Not all channels should be reallocated. We must avoid + * reallocating their buffer table entries. + */ + ef4_for_each_channel(channel, efx) { + struct ef4_rx_queue *rx_queue; + struct ef4_tx_queue *tx_queue; + + if (channel->type->copy) + continue; + next_buffer_table = max(next_buffer_table, + channel->eventq.index + + channel->eventq.entries); + ef4_for_each_channel_rx_queue(rx_queue, channel) + next_buffer_table = max(next_buffer_table, + rx_queue->rxd.index + + rx_queue->rxd.entries); + ef4_for_each_channel_tx_queue(tx_queue, channel) + next_buffer_table = max(next_buffer_table, + tx_queue->txd.index + + tx_queue->txd.entries); + } + + ef4_device_detach_sync(efx); + ef4_stop_all(efx); + ef4_soft_disable_interrupts(efx); + + /* Clone channels (where possible) */ + memset(other_channel, 0, sizeof(other_channel)); + for (i = 0; i < efx->n_channels; i++) { + channel = efx->channel[i]; + if (channel->type->copy) + channel = channel->type->copy(channel); + if (!channel) { + rc = -ENOMEM; + goto out; + } + other_channel[i] = channel; + } + + /* Swap entry counts and channel pointers */ + old_rxq_entries = efx->rxq_entries; + old_txq_entries = efx->txq_entries; + efx->rxq_entries = rxq_entries; + efx->txq_entries = txq_entries; + for (i = 0; i < efx->n_channels; i++) { + channel = efx->channel[i]; + efx->channel[i] = other_channel[i]; + other_channel[i] = channel; + } + + /* Restart buffer table allocation */ + efx->next_buffer_table = next_buffer_table; + + for (i = 0; i < efx->n_channels; i++) { + channel = efx->channel[i]; + if (!channel->type->copy) + continue; + rc = ef4_probe_channel(channel); + if (rc) + goto rollback; + ef4_init_napi_channel(efx->channel[i]); + } + +out: + /* Destroy unused channel structures */ + for (i = 0; i < efx->n_channels; i++) { + channel = other_channel[i]; + if (channel && channel->type->copy) { + ef4_fini_napi_channel(channel); + ef4_remove_channel(channel); + kfree(channel); + } + } + + rc2 = ef4_soft_enable_interrupts(efx); + if (rc2) { + rc = rc ? rc : rc2; + netif_err(efx, drv, efx->net_dev, + "unable to restart interrupts on channel reallocation\n"); + ef4_schedule_reset(efx, RESET_TYPE_DISABLE); + } else { + ef4_start_all(efx); + netif_device_attach(efx->net_dev); + } + return rc; + +rollback: + /* Swap back */ + efx->rxq_entries = old_rxq_entries; + efx->txq_entries = old_txq_entries; + for (i = 0; i < efx->n_channels; i++) { + channel = efx->channel[i]; + efx->channel[i] = other_channel[i]; + other_channel[i] = channel; + } + goto out; +} + +void ef4_schedule_slow_fill(struct ef4_rx_queue *rx_queue) +{ + mod_timer(&rx_queue->slow_fill, jiffies + msecs_to_jiffies(100)); +} + +static const struct ef4_channel_type ef4_default_channel_type = { + .pre_probe = ef4_channel_dummy_op_int, + .post_remove = ef4_channel_dummy_op_void, + .get_name = ef4_get_channel_name, + .copy = ef4_copy_channel, + .keep_eventq = false, +}; + +int ef4_channel_dummy_op_int(struct ef4_channel *channel) +{ + return 0; +} + +void ef4_channel_dummy_op_void(struct ef4_channel *channel) +{ +} + +/************************************************************************** + * + * Port handling + * + **************************************************************************/ + +/* This ensures that the kernel is kept informed (via + * netif_carrier_on/off) of the link status, and also maintains the + * link status's stop on the port's TX queue. + */ +void ef4_link_status_changed(struct ef4_nic *efx) +{ + struct ef4_link_state *link_state = &efx->link_state; + + /* SFC Bug 5356: A net_dev notifier is registered, so we must ensure + * that no events are triggered between unregister_netdev() and the + * driver unloading. A more general condition is that NETDEV_CHANGE + * can only be generated between NETDEV_UP and NETDEV_DOWN */ + if (!netif_running(efx->net_dev)) + return; + + if (link_state->up != netif_carrier_ok(efx->net_dev)) { + efx->n_link_state_changes++; + + if (link_state->up) + netif_carrier_on(efx->net_dev); + else + netif_carrier_off(efx->net_dev); + } + + /* Status message for kernel log */ + if (link_state->up) + netif_info(efx, link, efx->net_dev, + "link up at %uMbps %s-duplex (MTU %d)\n", + link_state->speed, link_state->fd ? "full" : "half", + efx->net_dev->mtu); + else + netif_info(efx, link, efx->net_dev, "link down\n"); +} + +void ef4_link_set_advertising(struct ef4_nic *efx, u32 advertising) +{ + efx->link_advertising = advertising; + if (advertising) { + if (advertising & ADVERTISED_Pause) + efx->wanted_fc |= (EF4_FC_TX | EF4_FC_RX); + else + efx->wanted_fc &= ~(EF4_FC_TX | EF4_FC_RX); + if (advertising & ADVERTISED_Asym_Pause) + efx->wanted_fc ^= EF4_FC_TX; + } +} + +void ef4_link_set_wanted_fc(struct ef4_nic *efx, u8 wanted_fc) +{ + efx->wanted_fc = wanted_fc; + if (efx->link_advertising) { + if (wanted_fc & EF4_FC_RX) + efx->link_advertising |= (ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + else + efx->link_advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + if (wanted_fc & EF4_FC_TX) + efx->link_advertising ^= ADVERTISED_Asym_Pause; + } +} + +static void ef4_fini_port(struct ef4_nic *efx); + +/* We assume that efx->type->reconfigure_mac will always try to sync RX + * filters and therefore needs to read-lock the filter table against freeing + */ +void ef4_mac_reconfigure(struct ef4_nic *efx) +{ + down_read(&efx->filter_sem); + efx->type->reconfigure_mac(efx); + up_read(&efx->filter_sem); +} + +/* Push loopback/power/transmit disable settings to the PHY, and reconfigure + * the MAC appropriately. All other PHY configuration changes are pushed + * through phy_op->set_settings(), and pushed asynchronously to the MAC + * through ef4_monitor(). + * + * Callers must hold the mac_lock + */ +int __ef4_reconfigure_port(struct ef4_nic *efx) +{ + enum ef4_phy_mode phy_mode; + int rc; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + /* Disable PHY transmit in mac level loopbacks */ + phy_mode = efx->phy_mode; + if (LOOPBACK_INTERNAL(efx)) + efx->phy_mode |= PHY_MODE_TX_DISABLED; + else + efx->phy_mode &= ~PHY_MODE_TX_DISABLED; + + rc = efx->type->reconfigure_port(efx); + + if (rc) + efx->phy_mode = phy_mode; + + return rc; +} + +/* Reinitialise the MAC to pick up new PHY settings, even if the port is + * disabled. */ +int ef4_reconfigure_port(struct ef4_nic *efx) +{ + int rc; + + EF4_ASSERT_RESET_SERIALISED(efx); + + mutex_lock(&efx->mac_lock); + rc = __ef4_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + + return rc; +} + +/* Asynchronous work item for changing MAC promiscuity and multicast + * hash. Avoid a drain/rx_ingress enable by reconfiguring the current + * MAC directly. */ +static void ef4_mac_work(struct work_struct *data) +{ + struct ef4_nic *efx = container_of(data, struct ef4_nic, mac_work); + + mutex_lock(&efx->mac_lock); + if (efx->port_enabled) + ef4_mac_reconfigure(efx); + mutex_unlock(&efx->mac_lock); +} + +static int ef4_probe_port(struct ef4_nic *efx) +{ + int rc; + + netif_dbg(efx, probe, efx->net_dev, "create port\n"); + + if (phy_flash_cfg) + efx->phy_mode = PHY_MODE_SPECIAL; + + /* Connect up MAC/PHY operations table */ + rc = efx->type->probe_port(efx); + if (rc) + return rc; + + /* Initialise MAC address to permanent address */ + ether_addr_copy(efx->net_dev->dev_addr, efx->net_dev->perm_addr); + + return 0; +} + +static int ef4_init_port(struct ef4_nic *efx) +{ + int rc; + + netif_dbg(efx, drv, efx->net_dev, "init port\n"); + + mutex_lock(&efx->mac_lock); + + rc = efx->phy_op->init(efx); + if (rc) + goto fail1; + + efx->port_initialized = true; + + /* Reconfigure the MAC before creating dma queues (required for + * Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */ + ef4_mac_reconfigure(efx); + + /* Ensure the PHY advertises the correct flow control settings */ + rc = efx->phy_op->reconfigure(efx); + if (rc && rc != -EPERM) + goto fail2; + + mutex_unlock(&efx->mac_lock); + return 0; + +fail2: + efx->phy_op->fini(efx); +fail1: + mutex_unlock(&efx->mac_lock); + return rc; +} + +static void ef4_start_port(struct ef4_nic *efx) +{ + netif_dbg(efx, ifup, efx->net_dev, "start port\n"); + BUG_ON(efx->port_enabled); + + mutex_lock(&efx->mac_lock); + efx->port_enabled = true; + + /* Ensure MAC ingress/egress is enabled */ + ef4_mac_reconfigure(efx); + + mutex_unlock(&efx->mac_lock); +} + +/* Cancel work for MAC reconfiguration, periodic hardware monitoring + * and the async self-test, wait for them to finish and prevent them + * being scheduled again. This doesn't cover online resets, which + * should only be cancelled when removing the device. + */ +static void ef4_stop_port(struct ef4_nic *efx) +{ + netif_dbg(efx, ifdown, efx->net_dev, "stop port\n"); + + EF4_ASSERT_RESET_SERIALISED(efx); + + mutex_lock(&efx->mac_lock); + efx->port_enabled = false; + mutex_unlock(&efx->mac_lock); + + /* Serialise against ef4_set_multicast_list() */ + netif_addr_lock_bh(efx->net_dev); + netif_addr_unlock_bh(efx->net_dev); + + cancel_delayed_work_sync(&efx->monitor_work); + ef4_selftest_async_cancel(efx); + cancel_work_sync(&efx->mac_work); +} + +static void ef4_fini_port(struct ef4_nic *efx) +{ + netif_dbg(efx, drv, efx->net_dev, "shut down port\n"); + + if (!efx->port_initialized) + return; + + efx->phy_op->fini(efx); + efx->port_initialized = false; + + efx->link_state.up = false; + ef4_link_status_changed(efx); +} + +static void ef4_remove_port(struct ef4_nic *efx) +{ + netif_dbg(efx, drv, efx->net_dev, "destroying port\n"); + + efx->type->remove_port(efx); +} + +/************************************************************************** + * + * NIC handling + * + **************************************************************************/ + +static LIST_HEAD(ef4_primary_list); +static LIST_HEAD(ef4_unassociated_list); + +static bool ef4_same_controller(struct ef4_nic *left, struct ef4_nic *right) +{ + return left->type == right->type && + left->vpd_sn && right->vpd_sn && + !strcmp(left->vpd_sn, right->vpd_sn); +} + +static void ef4_associate(struct ef4_nic *efx) +{ + struct ef4_nic *other, *next; + + if (efx->primary == efx) { + /* Adding primary function; look for secondaries */ + + netif_dbg(efx, probe, efx->net_dev, "adding to primary list\n"); + list_add_tail(&efx->node, &ef4_primary_list); + + list_for_each_entry_safe(other, next, &ef4_unassociated_list, + node) { + if (ef4_same_controller(efx, other)) { + list_del(&other->node); + netif_dbg(other, probe, other->net_dev, + "moving to secondary list of %s %s\n", + pci_name(efx->pci_dev), + efx->net_dev->name); + list_add_tail(&other->node, + &efx->secondary_list); + other->primary = efx; + } + } + } else { + /* Adding secondary function; look for primary */ + + list_for_each_entry(other, &ef4_primary_list, node) { + if (ef4_same_controller(efx, other)) { + netif_dbg(efx, probe, efx->net_dev, + "adding to secondary list of %s %s\n", + pci_name(other->pci_dev), + other->net_dev->name); + list_add_tail(&efx->node, + &other->secondary_list); + efx->primary = other; + return; + } + } + + netif_dbg(efx, probe, efx->net_dev, + "adding to unassociated list\n"); + list_add_tail(&efx->node, &ef4_unassociated_list); + } +} + +static void ef4_dissociate(struct ef4_nic *efx) +{ + struct ef4_nic *other, *next; + + list_del(&efx->node); + efx->primary = NULL; + + list_for_each_entry_safe(other, next, &efx->secondary_list, node) { + list_del(&other->node); + netif_dbg(other, probe, other->net_dev, + "moving to unassociated list\n"); + list_add_tail(&other->node, &ef4_unassociated_list); + other->primary = NULL; + } +} + +/* This configures the PCI device to enable I/O and DMA. */ +static int ef4_init_io(struct ef4_nic *efx) +{ + struct pci_dev *pci_dev = efx->pci_dev; + dma_addr_t dma_mask = efx->type->max_dma_mask; + unsigned int mem_map_size = efx->type->mem_map_size(efx); + int rc, bar; + + netif_dbg(efx, probe, efx->net_dev, "initialising I/O\n"); + + bar = efx->type->mem_bar; + + rc = pci_enable_device(pci_dev); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "failed to enable PCI device\n"); + goto fail1; + } + + pci_set_master(pci_dev); + + /* Set the PCI DMA mask. Try all possibilities from our + * genuine mask down to 32 bits, because some architectures + * (e.g. x86_64 with iommu_sac_force set) will allow 40 bit + * masks event though they reject 46 bit masks. + */ + while (dma_mask > 0x7fffffffUL) { + rc = dma_set_mask_and_coherent(&pci_dev->dev, dma_mask); + if (rc == 0) + break; + dma_mask >>= 1; + } + if (rc) { + netif_err(efx, probe, efx->net_dev, + "could not find a suitable DMA mask\n"); + goto fail2; + } + netif_dbg(efx, probe, efx->net_dev, + "using DMA mask %llx\n", (unsigned long long) dma_mask); + + efx->membase_phys = pci_resource_start(efx->pci_dev, bar); + rc = pci_request_region(pci_dev, bar, "sfc"); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "request for memory BAR failed\n"); + rc = -EIO; + goto fail3; + } + efx->membase = ioremap_nocache(efx->membase_phys, mem_map_size); + if (!efx->membase) { + netif_err(efx, probe, efx->net_dev, + "could not map memory BAR at %llx+%x\n", + (unsigned long long)efx->membase_phys, mem_map_size); + rc = -ENOMEM; + goto fail4; + } + netif_dbg(efx, probe, efx->net_dev, + "memory BAR at %llx+%x (virtual %p)\n", + (unsigned long long)efx->membase_phys, mem_map_size, + efx->membase); + + return 0; + + fail4: + pci_release_region(efx->pci_dev, bar); + fail3: + efx->membase_phys = 0; + fail2: + pci_disable_device(efx->pci_dev); + fail1: + return rc; +} + +static void ef4_fini_io(struct ef4_nic *efx) +{ + int bar; + + netif_dbg(efx, drv, efx->net_dev, "shutting down I/O\n"); + + if (efx->membase) { + iounmap(efx->membase); + efx->membase = NULL; + } + + if (efx->membase_phys) { + bar = efx->type->mem_bar; + pci_release_region(efx->pci_dev, bar); + efx->membase_phys = 0; + } + + /* Don't disable bus-mastering if VFs are assigned */ + if (!pci_vfs_assigned(efx->pci_dev)) + pci_disable_device(efx->pci_dev); +} + +void ef4_set_default_rx_indir_table(struct ef4_nic *efx) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++) + efx->rx_indir_table[i] = + ethtool_rxfh_indir_default(i, efx->rss_spread); +} + +static unsigned int ef4_wanted_parallelism(struct ef4_nic *efx) +{ + cpumask_var_t thread_mask; + unsigned int count; + int cpu; + + if (rss_cpus) { + count = rss_cpus; + } else { + if (unlikely(!zalloc_cpumask_var(&thread_mask, GFP_KERNEL))) { + netif_warn(efx, probe, efx->net_dev, + "RSS disabled due to allocation failure\n"); + return 1; + } + + count = 0; + for_each_online_cpu(cpu) { + if (!cpumask_test_cpu(cpu, thread_mask)) { + ++count; + cpumask_or(thread_mask, thread_mask, + topology_sibling_cpumask(cpu)); + } + } + + free_cpumask_var(thread_mask); + } + + return count; +} + +/* Probe the number and type of interrupts we are able to obtain, and + * the resulting numbers of channels and RX queues. + */ +static int ef4_probe_interrupts(struct ef4_nic *efx) +{ + unsigned int extra_channels = 0; + unsigned int i, j; + int rc; + + for (i = 0; i < EF4_MAX_EXTRA_CHANNELS; i++) + if (efx->extra_channel_type[i]) + ++extra_channels; + + if (efx->interrupt_mode == EF4_INT_MODE_MSIX) { + struct msix_entry xentries[EF4_MAX_CHANNELS]; + unsigned int n_channels; + + n_channels = ef4_wanted_parallelism(efx); + if (ef4_separate_tx_channels) + n_channels *= 2; + n_channels += extra_channels; + n_channels = min(n_channels, efx->max_channels); + + for (i = 0; i < n_channels; i++) + xentries[i].entry = i; + rc = pci_enable_msix_range(efx->pci_dev, + xentries, 1, n_channels); + if (rc < 0) { + /* Fall back to single channel MSI */ + efx->interrupt_mode = EF4_INT_MODE_MSI; + netif_err(efx, drv, efx->net_dev, + "could not enable MSI-X\n"); + } else if (rc < n_channels) { + netif_err(efx, drv, efx->net_dev, + "WARNING: Insufficient MSI-X vectors" + " available (%d < %u).\n", rc, n_channels); + netif_err(efx, drv, efx->net_dev, + "WARNING: Performance may be reduced.\n"); + n_channels = rc; + } + + if (rc > 0) { + efx->n_channels = n_channels; + if (n_channels > extra_channels) + n_channels -= extra_channels; + if (ef4_separate_tx_channels) { + efx->n_tx_channels = min(max(n_channels / 2, + 1U), + efx->max_tx_channels); + efx->n_rx_channels = max(n_channels - + efx->n_tx_channels, + 1U); + } else { + efx->n_tx_channels = min(n_channels, + efx->max_tx_channels); + efx->n_rx_channels = n_channels; + } + for (i = 0; i < efx->n_channels; i++) + ef4_get_channel(efx, i)->irq = + xentries[i].vector; + } + } + + /* Try single interrupt MSI */ + if (efx->interrupt_mode == EF4_INT_MODE_MSI) { + efx->n_channels = 1; + efx->n_rx_channels = 1; + efx->n_tx_channels = 1; + rc = pci_enable_msi(efx->pci_dev); + if (rc == 0) { + ef4_get_channel(efx, 0)->irq = efx->pci_dev->irq; + } else { + netif_err(efx, drv, efx->net_dev, + "could not enable MSI\n"); + efx->interrupt_mode = EF4_INT_MODE_LEGACY; + } + } + + /* Assume legacy interrupts */ + if (efx->interrupt_mode == EF4_INT_MODE_LEGACY) { + efx->n_channels = 1 + (ef4_separate_tx_channels ? 1 : 0); + efx->n_rx_channels = 1; + efx->n_tx_channels = 1; + efx->legacy_irq = efx->pci_dev->irq; + } + + /* Assign extra channels if possible */ + j = efx->n_channels; + for (i = 0; i < EF4_MAX_EXTRA_CHANNELS; i++) { + if (!efx->extra_channel_type[i]) + continue; + if (efx->interrupt_mode != EF4_INT_MODE_MSIX || + efx->n_channels <= extra_channels) { + efx->extra_channel_type[i]->handle_no_channel(efx); + } else { + --j; + ef4_get_channel(efx, j)->type = + efx->extra_channel_type[i]; + } + } + + efx->rss_spread = efx->n_rx_channels; + + return 0; +} + +static int ef4_soft_enable_interrupts(struct ef4_nic *efx) +{ + struct ef4_channel *channel, *end_channel; + int rc; + + BUG_ON(efx->state == STATE_DISABLED); + + efx->irq_soft_enabled = true; + smp_wmb(); + + ef4_for_each_channel(channel, efx) { + if (!channel->type->keep_eventq) { + rc = ef4_init_eventq(channel); + if (rc) + goto fail; + } + ef4_start_eventq(channel); + } + + return 0; +fail: + end_channel = channel; + ef4_for_each_channel(channel, efx) { + if (channel == end_channel) + break; + ef4_stop_eventq(channel); + if (!channel->type->keep_eventq) + ef4_fini_eventq(channel); + } + + return rc; +} + +static void ef4_soft_disable_interrupts(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + if (efx->state == STATE_DISABLED) + return; + + efx->irq_soft_enabled = false; + smp_wmb(); + + if (efx->legacy_irq) + synchronize_irq(efx->legacy_irq); + + ef4_for_each_channel(channel, efx) { + if (channel->irq) + synchronize_irq(channel->irq); + + ef4_stop_eventq(channel); + if (!channel->type->keep_eventq) + ef4_fini_eventq(channel); + } +} + +static int ef4_enable_interrupts(struct ef4_nic *efx) +{ + struct ef4_channel *channel, *end_channel; + int rc; + + BUG_ON(efx->state == STATE_DISABLED); + + if (efx->eeh_disabled_legacy_irq) { + enable_irq(efx->legacy_irq); + efx->eeh_disabled_legacy_irq = false; + } + + efx->type->irq_enable_master(efx); + + ef4_for_each_channel(channel, efx) { + if (channel->type->keep_eventq) { + rc = ef4_init_eventq(channel); + if (rc) + goto fail; + } + } + + rc = ef4_soft_enable_interrupts(efx); + if (rc) + goto fail; + + return 0; + +fail: + end_channel = channel; + ef4_for_each_channel(channel, efx) { + if (channel == end_channel) + break; + if (channel->type->keep_eventq) + ef4_fini_eventq(channel); + } + + efx->type->irq_disable_non_ev(efx); + + return rc; +} + +static void ef4_disable_interrupts(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + ef4_soft_disable_interrupts(efx); + + ef4_for_each_channel(channel, efx) { + if (channel->type->keep_eventq) + ef4_fini_eventq(channel); + } + + efx->type->irq_disable_non_ev(efx); +} + +static void ef4_remove_interrupts(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + /* Remove MSI/MSI-X interrupts */ + ef4_for_each_channel(channel, efx) + channel->irq = 0; + pci_disable_msi(efx->pci_dev); + pci_disable_msix(efx->pci_dev); + + /* Remove legacy interrupt */ + efx->legacy_irq = 0; +} + +static void ef4_set_channels(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + struct ef4_tx_queue *tx_queue; + + efx->tx_channel_offset = + ef4_separate_tx_channels ? + efx->n_channels - efx->n_tx_channels : 0; + + /* We need to mark which channels really have RX and TX + * queues, and adjust the TX queue numbers if we have separate + * RX-only and TX-only channels. + */ + ef4_for_each_channel(channel, efx) { + if (channel->channel < efx->n_rx_channels) + channel->rx_queue.core_index = channel->channel; + else + channel->rx_queue.core_index = -1; + + ef4_for_each_channel_tx_queue(tx_queue, channel) + tx_queue->queue -= (efx->tx_channel_offset * + EF4_TXQ_TYPES); + } +} + +static int ef4_probe_nic(struct ef4_nic *efx) +{ + int rc; + + netif_dbg(efx, probe, efx->net_dev, "creating NIC\n"); + + /* Carry out hardware-type specific initialisation */ + rc = efx->type->probe(efx); + if (rc) + return rc; + + do { + if (!efx->max_channels || !efx->max_tx_channels) { + netif_err(efx, drv, efx->net_dev, + "Insufficient resources to allocate" + " any channels\n"); + rc = -ENOSPC; + goto fail1; + } + + /* Determine the number of channels and queues by trying + * to hook in MSI-X interrupts. + */ + rc = ef4_probe_interrupts(efx); + if (rc) + goto fail1; + + ef4_set_channels(efx); + + /* dimension_resources can fail with EAGAIN */ + rc = efx->type->dimension_resources(efx); + if (rc != 0 && rc != -EAGAIN) + goto fail2; + + if (rc == -EAGAIN) + /* try again with new max_channels */ + ef4_remove_interrupts(efx); + + } while (rc == -EAGAIN); + + if (efx->n_channels > 1) + netdev_rss_key_fill(&efx->rx_hash_key, + sizeof(efx->rx_hash_key)); + ef4_set_default_rx_indir_table(efx); + + netif_set_real_num_tx_queues(efx->net_dev, efx->n_tx_channels); + netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels); + + /* Initialise the interrupt moderation settings */ + efx->irq_mod_step_us = DIV_ROUND_UP(efx->timer_quantum_ns, 1000); + ef4_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true, + true); + + return 0; + +fail2: + ef4_remove_interrupts(efx); +fail1: + efx->type->remove(efx); + return rc; +} + +static void ef4_remove_nic(struct ef4_nic *efx) +{ + netif_dbg(efx, drv, efx->net_dev, "destroying NIC\n"); + + ef4_remove_interrupts(efx); + efx->type->remove(efx); +} + +static int ef4_probe_filters(struct ef4_nic *efx) +{ + int rc; + + spin_lock_init(&efx->filter_lock); + init_rwsem(&efx->filter_sem); + mutex_lock(&efx->mac_lock); + down_write(&efx->filter_sem); + rc = efx->type->filter_table_probe(efx); + if (rc) + goto out_unlock; + +#ifdef CONFIG_RFS_ACCEL + if (efx->type->offload_features & NETIF_F_NTUPLE) { + struct ef4_channel *channel; + int i, success = 1; + + ef4_for_each_channel(channel, efx) { + channel->rps_flow_id = + kcalloc(efx->type->max_rx_ip_filters, + sizeof(*channel->rps_flow_id), + GFP_KERNEL); + if (!channel->rps_flow_id) + success = 0; + else + for (i = 0; + i < efx->type->max_rx_ip_filters; + ++i) + channel->rps_flow_id[i] = + RPS_FLOW_ID_INVALID; + } + + if (!success) { + ef4_for_each_channel(channel, efx) + kfree(channel->rps_flow_id); + efx->type->filter_table_remove(efx); + rc = -ENOMEM; + goto out_unlock; + } + + efx->rps_expire_index = efx->rps_expire_channel = 0; + } +#endif +out_unlock: + up_write(&efx->filter_sem); + mutex_unlock(&efx->mac_lock); + return rc; +} + +static void ef4_remove_filters(struct ef4_nic *efx) +{ +#ifdef CONFIG_RFS_ACCEL + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + kfree(channel->rps_flow_id); +#endif + down_write(&efx->filter_sem); + efx->type->filter_table_remove(efx); + up_write(&efx->filter_sem); +} + +static void ef4_restore_filters(struct ef4_nic *efx) +{ + down_read(&efx->filter_sem); + efx->type->filter_table_restore(efx); + up_read(&efx->filter_sem); +} + +/************************************************************************** + * + * NIC startup/shutdown + * + *************************************************************************/ + +static int ef4_probe_all(struct ef4_nic *efx) +{ + int rc; + + rc = ef4_probe_nic(efx); + if (rc) { + netif_err(efx, probe, efx->net_dev, "failed to create NIC\n"); + goto fail1; + } + + rc = ef4_probe_port(efx); + if (rc) { + netif_err(efx, probe, efx->net_dev, "failed to create port\n"); + goto fail2; + } + + BUILD_BUG_ON(EF4_DEFAULT_DMAQ_SIZE < EF4_RXQ_MIN_ENT); + if (WARN_ON(EF4_DEFAULT_DMAQ_SIZE < EF4_TXQ_MIN_ENT(efx))) { + rc = -EINVAL; + goto fail3; + } + efx->rxq_entries = efx->txq_entries = EF4_DEFAULT_DMAQ_SIZE; + + rc = ef4_probe_filters(efx); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "failed to create filter tables\n"); + goto fail4; + } + + rc = ef4_probe_channels(efx); + if (rc) + goto fail5; + + return 0; + + fail5: + ef4_remove_filters(efx); + fail4: + fail3: + ef4_remove_port(efx); + fail2: + ef4_remove_nic(efx); + fail1: + return rc; +} + +/* If the interface is supposed to be running but is not, start + * the hardware and software data path, regular activity for the port + * (MAC statistics, link polling, etc.) and schedule the port to be + * reconfigured. Interrupts must already be enabled. This function + * is safe to call multiple times, so long as the NIC is not disabled. + * Requires the RTNL lock. + */ +static void ef4_start_all(struct ef4_nic *efx) +{ + EF4_ASSERT_RESET_SERIALISED(efx); + BUG_ON(efx->state == STATE_DISABLED); + + /* Check that it is appropriate to restart the interface. All + * of these flags are safe to read under just the rtnl lock */ + if (efx->port_enabled || !netif_running(efx->net_dev) || + efx->reset_pending) + return; + + ef4_start_port(efx); + ef4_start_datapath(efx); + + /* Start the hardware monitor if there is one */ + if (efx->type->monitor != NULL) + queue_delayed_work(efx->workqueue, &efx->monitor_work, + ef4_monitor_interval); + + efx->type->start_stats(efx); + efx->type->pull_stats(efx); + spin_lock_bh(&efx->stats_lock); + efx->type->update_stats(efx, NULL, NULL); + spin_unlock_bh(&efx->stats_lock); +} + +/* Quiesce the hardware and software data path, and regular activity + * for the port without bringing the link down. Safe to call multiple + * times with the NIC in almost any state, but interrupts should be + * enabled. Requires the RTNL lock. + */ +static void ef4_stop_all(struct ef4_nic *efx) +{ + EF4_ASSERT_RESET_SERIALISED(efx); + + /* port_enabled can be read safely under the rtnl lock */ + if (!efx->port_enabled) + return; + + /* update stats before we go down so we can accurately count + * rx_nodesc_drops + */ + efx->type->pull_stats(efx); + spin_lock_bh(&efx->stats_lock); + efx->type->update_stats(efx, NULL, NULL); + spin_unlock_bh(&efx->stats_lock); + efx->type->stop_stats(efx); + ef4_stop_port(efx); + + /* Stop the kernel transmit interface. This is only valid if + * the device is stopped or detached; otherwise the watchdog + * may fire immediately. + */ + WARN_ON(netif_running(efx->net_dev) && + netif_device_present(efx->net_dev)); + netif_tx_disable(efx->net_dev); + + ef4_stop_datapath(efx); +} + +static void ef4_remove_all(struct ef4_nic *efx) +{ + ef4_remove_channels(efx); + ef4_remove_filters(efx); + ef4_remove_port(efx); + ef4_remove_nic(efx); +} + +/************************************************************************** + * + * Interrupt moderation + * + **************************************************************************/ +unsigned int ef4_usecs_to_ticks(struct ef4_nic *efx, unsigned int usecs) +{ + if (usecs == 0) + return 0; + if (usecs * 1000 < efx->timer_quantum_ns) + return 1; /* never round down to 0 */ + return usecs * 1000 / efx->timer_quantum_ns; +} + +unsigned int ef4_ticks_to_usecs(struct ef4_nic *efx, unsigned int ticks) +{ + /* We must round up when converting ticks to microseconds + * because we round down when converting the other way. + */ + return DIV_ROUND_UP(ticks * efx->timer_quantum_ns, 1000); +} + +/* Set interrupt moderation parameters */ +int ef4_init_irq_moderation(struct ef4_nic *efx, unsigned int tx_usecs, + unsigned int rx_usecs, bool rx_adaptive, + bool rx_may_override_tx) +{ + struct ef4_channel *channel; + unsigned int timer_max_us; + + EF4_ASSERT_RESET_SERIALISED(efx); + + timer_max_us = efx->timer_max_ns / 1000; + + if (tx_usecs > timer_max_us || rx_usecs > timer_max_us) + return -EINVAL; + + if (tx_usecs != rx_usecs && efx->tx_channel_offset == 0 && + !rx_may_override_tx) { + netif_err(efx, drv, efx->net_dev, "Channels are shared. " + "RX and TX IRQ moderation must be equal\n"); + return -EINVAL; + } + + efx->irq_rx_adaptive = rx_adaptive; + efx->irq_rx_moderation_us = rx_usecs; + ef4_for_each_channel(channel, efx) { + if (ef4_channel_has_rx_queue(channel)) + channel->irq_moderation_us = rx_usecs; + else if (ef4_channel_has_tx_queues(channel)) + channel->irq_moderation_us = tx_usecs; + } + + return 0; +} + +void ef4_get_irq_moderation(struct ef4_nic *efx, unsigned int *tx_usecs, + unsigned int *rx_usecs, bool *rx_adaptive) +{ + *rx_adaptive = efx->irq_rx_adaptive; + *rx_usecs = efx->irq_rx_moderation_us; + + /* If channels are shared between RX and TX, so is IRQ + * moderation. Otherwise, IRQ moderation is the same for all + * TX channels and is not adaptive. + */ + if (efx->tx_channel_offset == 0) { + *tx_usecs = *rx_usecs; + } else { + struct ef4_channel *tx_channel; + + tx_channel = efx->channel[efx->tx_channel_offset]; + *tx_usecs = tx_channel->irq_moderation_us; + } +} + +/************************************************************************** + * + * Hardware monitor + * + **************************************************************************/ + +/* Run periodically off the general workqueue */ +static void ef4_monitor(struct work_struct *data) +{ + struct ef4_nic *efx = container_of(data, struct ef4_nic, + monitor_work.work); + + netif_vdbg(efx, timer, efx->net_dev, + "hardware monitor executing on CPU %d\n", + raw_smp_processor_id()); + BUG_ON(efx->type->monitor == NULL); + + /* If the mac_lock is already held then it is likely a port + * reconfiguration is already in place, which will likely do + * most of the work of monitor() anyway. */ + if (mutex_trylock(&efx->mac_lock)) { + if (efx->port_enabled) + efx->type->monitor(efx); + mutex_unlock(&efx->mac_lock); + } + + queue_delayed_work(efx->workqueue, &efx->monitor_work, + ef4_monitor_interval); +} + +/************************************************************************** + * + * ioctls + * + *************************************************************************/ + +/* Net device ioctl + * Context: process, rtnl_lock() held. + */ +static int ef4_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct mii_ioctl_data *data = if_mii(ifr); + + /* Convert phy_id from older PRTAD/DEVAD format */ + if ((cmd == SIOCGMIIREG || cmd == SIOCSMIIREG) && + (data->phy_id & 0xfc00) == 0x0400) + data->phy_id ^= MDIO_PHY_ID_C45 | 0x0400; + + return mdio_mii_ioctl(&efx->mdio, data, cmd); +} + +/************************************************************************** + * + * NAPI interface + * + **************************************************************************/ + +static void ef4_init_napi_channel(struct ef4_channel *channel) +{ + struct ef4_nic *efx = channel->efx; + + channel->napi_dev = efx->net_dev; + netif_napi_add(channel->napi_dev, &channel->napi_str, + ef4_poll, napi_weight); + ef4_channel_busy_poll_init(channel); +} + +static void ef4_init_napi(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + ef4_init_napi_channel(channel); +} + +static void ef4_fini_napi_channel(struct ef4_channel *channel) +{ + if (channel->napi_dev) + netif_napi_del(&channel->napi_str); + + channel->napi_dev = NULL; +} + +static void ef4_fini_napi(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + ef4_fini_napi_channel(channel); +} + +/************************************************************************** + * + * Kernel netpoll interface + * + *************************************************************************/ + +#ifdef CONFIG_NET_POLL_CONTROLLER + +/* Although in the common case interrupts will be disabled, this is not + * guaranteed. However, all our work happens inside the NAPI callback, + * so no locking is required. + */ +static void ef4_netpoll(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + ef4_schedule_channel(channel); +} + +#endif + +#ifdef CONFIG_NET_RX_BUSY_POLL +static int ef4_busy_poll(struct napi_struct *napi) +{ + struct ef4_channel *channel = + container_of(napi, struct ef4_channel, napi_str); + struct ef4_nic *efx = channel->efx; + int budget = 4; + int old_rx_packets, rx_packets; + + if (!netif_running(efx->net_dev)) + return LL_FLUSH_FAILED; + + if (!ef4_channel_try_lock_poll(channel)) + return LL_FLUSH_BUSY; + + old_rx_packets = channel->rx_queue.rx_packets; + ef4_process_channel(channel, budget); + + rx_packets = channel->rx_queue.rx_packets - old_rx_packets; + + /* There is no race condition with NAPI here. + * NAPI will automatically be rescheduled if it yielded during busy + * polling, because it was not able to take the lock and thus returned + * the full budget. + */ + ef4_channel_unlock_poll(channel); + + return rx_packets; +} +#endif + +/************************************************************************** + * + * Kernel net device interface + * + *************************************************************************/ + +/* Context: process, rtnl_lock() held. */ +int ef4_net_open(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int rc; + + netif_dbg(efx, ifup, efx->net_dev, "opening device on CPU %d\n", + raw_smp_processor_id()); + + rc = ef4_check_disabled(efx); + if (rc) + return rc; + if (efx->phy_mode & PHY_MODE_SPECIAL) + return -EBUSY; + + /* Notify the kernel of the link state polled during driver load, + * before the monitor starts running */ + ef4_link_status_changed(efx); + + ef4_start_all(efx); + ef4_selftest_async_start(efx); + return 0; +} + +/* Context: process, rtnl_lock() held. + * Note that the kernel will ignore our return code; this method + * should really be a void. + */ +int ef4_net_stop(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + netif_dbg(efx, ifdown, efx->net_dev, "closing on CPU %d\n", + raw_smp_processor_id()); + + /* Stop the device and flush all the channels */ + ef4_stop_all(efx); + + return 0; +} + +/* Context: process, dev_base_lock or RTNL held, non-blocking. */ +static struct rtnl_link_stats64 *ef4_net_stats(struct net_device *net_dev, + struct rtnl_link_stats64 *stats) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + spin_lock_bh(&efx->stats_lock); + efx->type->update_stats(efx, NULL, stats); + spin_unlock_bh(&efx->stats_lock); + + return stats; +} + +/* Context: netif_tx_lock held, BHs disabled. */ +static void ef4_watchdog(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + netif_err(efx, tx_err, efx->net_dev, + "TX stuck with port_enabled=%d: resetting channels\n", + efx->port_enabled); + + ef4_schedule_reset(efx, RESET_TYPE_TX_WATCHDOG); +} + + +/* Context: process, rtnl_lock() held. */ +static int ef4_change_mtu(struct net_device *net_dev, int new_mtu) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int rc; + + rc = ef4_check_disabled(efx); + if (rc) + return rc; + + netif_dbg(efx, drv, efx->net_dev, "changing MTU to %d\n", new_mtu); + + ef4_device_detach_sync(efx); + ef4_stop_all(efx); + + mutex_lock(&efx->mac_lock); + net_dev->mtu = new_mtu; + ef4_mac_reconfigure(efx); + mutex_unlock(&efx->mac_lock); + + ef4_start_all(efx); + netif_device_attach(efx->net_dev); + return 0; +} + +static int ef4_set_mac_address(struct net_device *net_dev, void *data) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct sockaddr *addr = data; + u8 *new_addr = addr->sa_data; + u8 old_addr[6]; + int rc; + + if (!is_valid_ether_addr(new_addr)) { + netif_err(efx, drv, efx->net_dev, + "invalid ethernet MAC address requested: %pM\n", + new_addr); + return -EADDRNOTAVAIL; + } + + /* save old address */ + ether_addr_copy(old_addr, net_dev->dev_addr); + ether_addr_copy(net_dev->dev_addr, new_addr); + if (efx->type->set_mac_address) { + rc = efx->type->set_mac_address(efx); + if (rc) { + ether_addr_copy(net_dev->dev_addr, old_addr); + return rc; + } + } + + /* Reconfigure the MAC */ + mutex_lock(&efx->mac_lock); + ef4_mac_reconfigure(efx); + mutex_unlock(&efx->mac_lock); + + return 0; +} + +/* Context: netif_addr_lock held, BHs disabled. */ +static void ef4_set_rx_mode(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + if (efx->port_enabled) + queue_work(efx->workqueue, &efx->mac_work); + /* Otherwise ef4_start_port() will do this */ +} + +static int ef4_set_features(struct net_device *net_dev, netdev_features_t data) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int rc; + + /* If disabling RX n-tuple filtering, clear existing filters */ + if (net_dev->features & ~data & NETIF_F_NTUPLE) { + rc = efx->type->filter_clear_rx(efx, EF4_FILTER_PRI_MANUAL); + if (rc) + return rc; + } + + /* If Rx VLAN filter is changed, update filters via mac_reconfigure */ + if ((net_dev->features ^ data) & NETIF_F_HW_VLAN_CTAG_FILTER) { + /* ef4_set_rx_mode() will schedule MAC work to update filters + * when a new features are finally set in net_dev. + */ + ef4_set_rx_mode(net_dev); + } + + return 0; +} + +static const struct net_device_ops ef4_netdev_ops = { + .ndo_open = ef4_net_open, + .ndo_stop = ef4_net_stop, + .ndo_get_stats64 = ef4_net_stats, + .ndo_tx_timeout = ef4_watchdog, + .ndo_start_xmit = ef4_hard_start_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = ef4_ioctl, + .ndo_change_mtu = ef4_change_mtu, + .ndo_set_mac_address = ef4_set_mac_address, + .ndo_set_rx_mode = ef4_set_rx_mode, + .ndo_set_features = ef4_set_features, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ef4_netpoll, +#endif + .ndo_setup_tc = ef4_setup_tc, +#ifdef CONFIG_NET_RX_BUSY_POLL + .ndo_busy_poll = ef4_busy_poll, +#endif +#ifdef CONFIG_RFS_ACCEL + .ndo_rx_flow_steer = ef4_filter_rfs, +#endif +}; + +static void ef4_update_name(struct ef4_nic *efx) +{ + strcpy(efx->name, efx->net_dev->name); + ef4_mtd_rename(efx); + ef4_set_channel_names(efx); +} + +static int ef4_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); + + if ((net_dev->netdev_ops == &ef4_netdev_ops) && + event == NETDEV_CHANGENAME) + ef4_update_name(netdev_priv(net_dev)); + + return NOTIFY_DONE; +} + +static struct notifier_block ef4_netdev_notifier = { + .notifier_call = ef4_netdev_event, +}; + +static ssize_t +show_phy_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ef4_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + return sprintf(buf, "%d\n", efx->phy_type); +} +static DEVICE_ATTR(phy_type, 0444, show_phy_type, NULL); + +static int ef4_register_netdev(struct ef4_nic *efx) +{ + struct net_device *net_dev = efx->net_dev; + struct ef4_channel *channel; + int rc; + + net_dev->watchdog_timeo = 5 * HZ; + net_dev->irq = efx->pci_dev->irq; + net_dev->netdev_ops = &ef4_netdev_ops; + net_dev->ethtool_ops = &ef4_ethtool_ops; + net_dev->gso_max_segs = EF4_TSO_MAX_SEGS; + net_dev->min_mtu = EF4_MIN_MTU; + net_dev->max_mtu = EF4_MAX_MTU; + + rtnl_lock(); + + /* Enable resets to be scheduled and check whether any were + * already requested. If so, the NIC is probably hosed so we + * abort. + */ + efx->state = STATE_READY; + smp_mb(); /* ensure we change state before checking reset_pending */ + if (efx->reset_pending) { + netif_err(efx, probe, efx->net_dev, + "aborting probe due to scheduled reset\n"); + rc = -EIO; + goto fail_locked; + } + + rc = dev_alloc_name(net_dev, net_dev->name); + if (rc < 0) + goto fail_locked; + ef4_update_name(efx); + + /* Always start with carrier off; PHY events will detect the link */ + netif_carrier_off(net_dev); + + rc = register_netdevice(net_dev); + if (rc) + goto fail_locked; + + ef4_for_each_channel(channel, efx) { + struct ef4_tx_queue *tx_queue; + ef4_for_each_channel_tx_queue(tx_queue, channel) + ef4_init_tx_queue_core_txq(tx_queue); + } + + ef4_associate(efx); + + rtnl_unlock(); + + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_type); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "failed to init net dev attributes\n"); + goto fail_registered; + } + return 0; + +fail_registered: + rtnl_lock(); + ef4_dissociate(efx); + unregister_netdevice(net_dev); +fail_locked: + efx->state = STATE_UNINIT; + rtnl_unlock(); + netif_err(efx, drv, efx->net_dev, "could not register net dev\n"); + return rc; +} + +static void ef4_unregister_netdev(struct ef4_nic *efx) +{ + if (!efx->net_dev) + return; + + BUG_ON(netdev_priv(efx->net_dev) != efx); + + if (ef4_dev_registered(efx)) { + strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type); + unregister_netdev(efx->net_dev); + } +} + +/************************************************************************** + * + * Device reset and suspend + * + **************************************************************************/ + +/* Tears down the entire software state and most of the hardware state + * before reset. */ +void ef4_reset_down(struct ef4_nic *efx, enum reset_type method) +{ + EF4_ASSERT_RESET_SERIALISED(efx); + + ef4_stop_all(efx); + ef4_disable_interrupts(efx); + + mutex_lock(&efx->mac_lock); + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE && + method != RESET_TYPE_DATAPATH) + efx->phy_op->fini(efx); + efx->type->fini(efx); +} + +/* This function will always ensure that the locks acquired in + * ef4_reset_down() are released. A failure return code indicates + * that we were unable to reinitialise the hardware, and the + * driver should be disabled. If ok is false, then the rx and tx + * engines are not restarted, pending a RESET_DISABLE. */ +int ef4_reset_up(struct ef4_nic *efx, enum reset_type method, bool ok) +{ + int rc; + + EF4_ASSERT_RESET_SERIALISED(efx); + + /* Ensure that SRAM is initialised even if we're disabling the device */ + rc = efx->type->init(efx); + if (rc) { + netif_err(efx, drv, efx->net_dev, "failed to initialise NIC\n"); + goto fail; + } + + if (!ok) + goto fail; + + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE && + method != RESET_TYPE_DATAPATH) { + rc = efx->phy_op->init(efx); + if (rc) + goto fail; + rc = efx->phy_op->reconfigure(efx); + if (rc && rc != -EPERM) + netif_err(efx, drv, efx->net_dev, + "could not restore PHY settings\n"); + } + + rc = ef4_enable_interrupts(efx); + if (rc) + goto fail; + + down_read(&efx->filter_sem); + ef4_restore_filters(efx); + up_read(&efx->filter_sem); + + mutex_unlock(&efx->mac_lock); + + ef4_start_all(efx); + + return 0; + +fail: + efx->port_initialized = false; + + mutex_unlock(&efx->mac_lock); + + return rc; +} + +/* Reset the NIC using the specified method. Note that the reset may + * fail, in which case the card will be left in an unusable state. + * + * Caller must hold the rtnl_lock. + */ +int ef4_reset(struct ef4_nic *efx, enum reset_type method) +{ + int rc, rc2; + bool disabled; + + netif_info(efx, drv, efx->net_dev, "resetting (%s)\n", + RESET_TYPE(method)); + + ef4_device_detach_sync(efx); + ef4_reset_down(efx, method); + + rc = efx->type->reset(efx, method); + if (rc) { + netif_err(efx, drv, efx->net_dev, "failed to reset hardware\n"); + goto out; + } + + /* Clear flags for the scopes we covered. We assume the NIC and + * driver are now quiescent so that there is no race here. + */ + if (method < RESET_TYPE_MAX_METHOD) + efx->reset_pending &= -(1 << (method + 1)); + else /* it doesn't fit into the well-ordered scope hierarchy */ + __clear_bit(method, &efx->reset_pending); + + /* Reinitialise bus-mastering, which may have been turned off before + * the reset was scheduled. This is still appropriate, even in the + * RESET_TYPE_DISABLE since this driver generally assumes the hardware + * can respond to requests. */ + pci_set_master(efx->pci_dev); + +out: + /* Leave device stopped if necessary */ + disabled = rc || + method == RESET_TYPE_DISABLE || + method == RESET_TYPE_RECOVER_OR_DISABLE; + rc2 = ef4_reset_up(efx, method, !disabled); + if (rc2) { + disabled = true; + if (!rc) + rc = rc2; + } + + if (disabled) { + dev_close(efx->net_dev); + netif_err(efx, drv, efx->net_dev, "has been disabled\n"); + efx->state = STATE_DISABLED; + } else { + netif_dbg(efx, drv, efx->net_dev, "reset complete\n"); + netif_device_attach(efx->net_dev); + } + return rc; +} + +/* Try recovery mechanisms. + * For now only EEH is supported. + * Returns 0 if the recovery mechanisms are unsuccessful. + * Returns a non-zero value otherwise. + */ +int ef4_try_recovery(struct ef4_nic *efx) +{ +#ifdef CONFIG_EEH + /* A PCI error can occur and not be seen by EEH because nothing + * happens on the PCI bus. In this case the driver may fail and + * schedule a 'recover or reset', leading to this recovery handler. + * Manually call the eeh failure check function. + */ + struct eeh_dev *eehdev = pci_dev_to_eeh_dev(efx->pci_dev); + if (eeh_dev_check_failure(eehdev)) { + /* The EEH mechanisms will handle the error and reset the + * device if necessary. + */ + return 1; + } +#endif + return 0; +} + +/* The worker thread exists so that code that cannot sleep can + * schedule a reset for later. + */ +static void ef4_reset_work(struct work_struct *data) +{ + struct ef4_nic *efx = container_of(data, struct ef4_nic, reset_work); + unsigned long pending; + enum reset_type method; + + pending = ACCESS_ONCE(efx->reset_pending); + method = fls(pending) - 1; + + if ((method == RESET_TYPE_RECOVER_OR_DISABLE || + method == RESET_TYPE_RECOVER_OR_ALL) && + ef4_try_recovery(efx)) + return; + + if (!pending) + return; + + rtnl_lock(); + + /* We checked the state in ef4_schedule_reset() but it may + * have changed by now. Now that we have the RTNL lock, + * it cannot change again. + */ + if (efx->state == STATE_READY) + (void)ef4_reset(efx, method); + + rtnl_unlock(); +} + +void ef4_schedule_reset(struct ef4_nic *efx, enum reset_type type) +{ + enum reset_type method; + + if (efx->state == STATE_RECOVERY) { + netif_dbg(efx, drv, efx->net_dev, + "recovering: skip scheduling %s reset\n", + RESET_TYPE(type)); + return; + } + + switch (type) { + case RESET_TYPE_INVISIBLE: + case RESET_TYPE_ALL: + case RESET_TYPE_RECOVER_OR_ALL: + case RESET_TYPE_WORLD: + case RESET_TYPE_DISABLE: + case RESET_TYPE_RECOVER_OR_DISABLE: + case RESET_TYPE_DATAPATH: + method = type; + netif_dbg(efx, drv, efx->net_dev, "scheduling %s reset\n", + RESET_TYPE(method)); + break; + default: + method = efx->type->map_reset_reason(type); + netif_dbg(efx, drv, efx->net_dev, + "scheduling %s reset for %s\n", + RESET_TYPE(method), RESET_TYPE(type)); + break; + } + + set_bit(method, &efx->reset_pending); + smp_mb(); /* ensure we change reset_pending before checking state */ + + /* If we're not READY then just leave the flags set as the cue + * to abort probing or reschedule the reset later. + */ + if (ACCESS_ONCE(efx->state) != STATE_READY) + return; + + queue_work(reset_workqueue, &efx->reset_work); +} + +/************************************************************************** + * + * List of NICs we support + * + **************************************************************************/ + +/* PCI device ID table */ +static const struct pci_device_id ef4_pci_table[] = { + {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, + PCI_DEVICE_ID_SOLARFLARE_SFC4000A_0), + .driver_data = (unsigned long) &falcon_a1_nic_type}, + {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, + PCI_DEVICE_ID_SOLARFLARE_SFC4000B), + .driver_data = (unsigned long) &falcon_b0_nic_type}, + {0} /* end of list */ +}; + +/************************************************************************** + * + * Dummy PHY/MAC operations + * + * Can be used for some unimplemented operations + * Needed so all function pointers are valid and do not have to be tested + * before use + * + **************************************************************************/ +int ef4_port_dummy_op_int(struct ef4_nic *efx) +{ + return 0; +} +void ef4_port_dummy_op_void(struct ef4_nic *efx) {} + +static bool ef4_port_dummy_op_poll(struct ef4_nic *efx) +{ + return false; +} + +static const struct ef4_phy_operations ef4_dummy_phy_operations = { + .init = ef4_port_dummy_op_int, + .reconfigure = ef4_port_dummy_op_int, + .poll = ef4_port_dummy_op_poll, + .fini = ef4_port_dummy_op_void, +}; + +/************************************************************************** + * + * Data housekeeping + * + **************************************************************************/ + +/* This zeroes out and then fills in the invariants in a struct + * ef4_nic (including all sub-structures). + */ +static int ef4_init_struct(struct ef4_nic *efx, + struct pci_dev *pci_dev, struct net_device *net_dev) +{ + int i; + + /* Initialise common structures */ + INIT_LIST_HEAD(&efx->node); + INIT_LIST_HEAD(&efx->secondary_list); + spin_lock_init(&efx->biu_lock); +#ifdef CONFIG_SFC_FALCON_MTD + INIT_LIST_HEAD(&efx->mtd_list); +#endif + INIT_WORK(&efx->reset_work, ef4_reset_work); + INIT_DELAYED_WORK(&efx->monitor_work, ef4_monitor); + INIT_DELAYED_WORK(&efx->selftest_work, ef4_selftest_async_work); + efx->pci_dev = pci_dev; + efx->msg_enable = debug; + efx->state = STATE_UNINIT; + strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); + + efx->net_dev = net_dev; + efx->rx_prefix_size = efx->type->rx_prefix_size; + efx->rx_ip_align = + NET_IP_ALIGN ? (efx->rx_prefix_size + NET_IP_ALIGN) % 4 : 0; + efx->rx_packet_hash_offset = + efx->type->rx_hash_offset - efx->type->rx_prefix_size; + efx->rx_packet_ts_offset = + efx->type->rx_ts_offset - efx->type->rx_prefix_size; + spin_lock_init(&efx->stats_lock); + mutex_init(&efx->mac_lock); + efx->phy_op = &ef4_dummy_phy_operations; + efx->mdio.dev = net_dev; + INIT_WORK(&efx->mac_work, ef4_mac_work); + init_waitqueue_head(&efx->flush_wq); + + for (i = 0; i < EF4_MAX_CHANNELS; i++) { + efx->channel[i] = ef4_alloc_channel(efx, i, NULL); + if (!efx->channel[i]) + goto fail; + efx->msi_context[i].efx = efx; + efx->msi_context[i].index = i; + } + + /* Higher numbered interrupt modes are less capable! */ + efx->interrupt_mode = max(efx->type->max_interrupt_mode, + interrupt_mode); + + /* Would be good to use the net_dev name, but we're too early */ + snprintf(efx->workqueue_name, sizeof(efx->workqueue_name), "sfc%s", + pci_name(pci_dev)); + efx->workqueue = create_singlethread_workqueue(efx->workqueue_name); + if (!efx->workqueue) + goto fail; + + return 0; + +fail: + ef4_fini_struct(efx); + return -ENOMEM; +} + +static void ef4_fini_struct(struct ef4_nic *efx) +{ + int i; + + for (i = 0; i < EF4_MAX_CHANNELS; i++) + kfree(efx->channel[i]); + + kfree(efx->vpd_sn); + + if (efx->workqueue) { + destroy_workqueue(efx->workqueue); + efx->workqueue = NULL; + } +} + +void ef4_update_sw_stats(struct ef4_nic *efx, u64 *stats) +{ + u64 n_rx_nodesc_trunc = 0; + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + n_rx_nodesc_trunc += channel->n_rx_nodesc_trunc; + stats[GENERIC_STAT_rx_nodesc_trunc] = n_rx_nodesc_trunc; + stats[GENERIC_STAT_rx_noskb_drops] = atomic_read(&efx->n_rx_noskb_drops); +} + +/************************************************************************** + * + * PCI interface + * + **************************************************************************/ + +/* Main body of final NIC shutdown code + * This is called only at module unload (or hotplug removal). + */ +static void ef4_pci_remove_main(struct ef4_nic *efx) +{ + /* Flush reset_work. It can no longer be scheduled since we + * are not READY. + */ + BUG_ON(efx->state == STATE_READY); + cancel_work_sync(&efx->reset_work); + + ef4_disable_interrupts(efx); + ef4_nic_fini_interrupt(efx); + ef4_fini_port(efx); + efx->type->fini(efx); + ef4_fini_napi(efx); + ef4_remove_all(efx); +} + +/* Final NIC shutdown + * This is called only at module unload (or hotplug removal). A PF can call + * this on its VFs to ensure they are unbound first. + */ +static void ef4_pci_remove(struct pci_dev *pci_dev) +{ + struct ef4_nic *efx; + + efx = pci_get_drvdata(pci_dev); + if (!efx) + return; + + /* Mark the NIC as fini, then stop the interface */ + rtnl_lock(); + ef4_dissociate(efx); + dev_close(efx->net_dev); + ef4_disable_interrupts(efx); + efx->state = STATE_UNINIT; + rtnl_unlock(); + + ef4_unregister_netdev(efx); + + ef4_mtd_remove(efx); + + ef4_pci_remove_main(efx); + + ef4_fini_io(efx); + netif_dbg(efx, drv, efx->net_dev, "shutdown successful\n"); + + ef4_fini_struct(efx); + free_netdev(efx->net_dev); + + pci_disable_pcie_error_reporting(pci_dev); +}; + +/* NIC VPD information + * Called during probe to display the part number of the + * installed NIC. VPD is potentially very large but this should + * always appear within the first 512 bytes. + */ +#define SFC_VPD_LEN 512 +static void ef4_probe_vpd_strings(struct ef4_nic *efx) +{ + struct pci_dev *dev = efx->pci_dev; + char vpd_data[SFC_VPD_LEN]; + ssize_t vpd_size; + int ro_start, ro_size, i, j; + + /* Get the vpd data from the device */ + vpd_size = pci_read_vpd(dev, 0, sizeof(vpd_data), vpd_data); + if (vpd_size <= 0) { + netif_err(efx, drv, efx->net_dev, "Unable to read VPD\n"); + return; + } + + /* Get the Read only section */ + ro_start = pci_vpd_find_tag(vpd_data, 0, vpd_size, PCI_VPD_LRDT_RO_DATA); + if (ro_start < 0) { + netif_err(efx, drv, efx->net_dev, "VPD Read-only not found\n"); + return; + } + + ro_size = pci_vpd_lrdt_size(&vpd_data[ro_start]); + j = ro_size; + i = ro_start + PCI_VPD_LRDT_TAG_SIZE; + if (i + j > vpd_size) + j = vpd_size - i; + + /* Get the Part number */ + i = pci_vpd_find_info_keyword(vpd_data, i, j, "PN"); + if (i < 0) { + netif_err(efx, drv, efx->net_dev, "Part number not found\n"); + return; + } + + j = pci_vpd_info_field_size(&vpd_data[i]); + i += PCI_VPD_INFO_FLD_HDR_SIZE; + if (i + j > vpd_size) { + netif_err(efx, drv, efx->net_dev, "Incomplete part number\n"); + return; + } + + netif_info(efx, drv, efx->net_dev, + "Part Number : %.*s\n", j, &vpd_data[i]); + + i = ro_start + PCI_VPD_LRDT_TAG_SIZE; + j = ro_size; + i = pci_vpd_find_info_keyword(vpd_data, i, j, "SN"); + if (i < 0) { + netif_err(efx, drv, efx->net_dev, "Serial number not found\n"); + return; + } + + j = pci_vpd_info_field_size(&vpd_data[i]); + i += PCI_VPD_INFO_FLD_HDR_SIZE; + if (i + j > vpd_size) { + netif_err(efx, drv, efx->net_dev, "Incomplete serial number\n"); + return; + } + + efx->vpd_sn = kmalloc(j + 1, GFP_KERNEL); + if (!efx->vpd_sn) + return; + + snprintf(efx->vpd_sn, j + 1, "%s", &vpd_data[i]); +} + + +/* Main body of NIC initialisation + * This is called at module load (or hotplug insertion, theoretically). + */ +static int ef4_pci_probe_main(struct ef4_nic *efx) +{ + int rc; + + /* Do start-of-day initialisation */ + rc = ef4_probe_all(efx); + if (rc) + goto fail1; + + ef4_init_napi(efx); + + rc = efx->type->init(efx); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "failed to initialise NIC\n"); + goto fail3; + } + + rc = ef4_init_port(efx); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "failed to initialise port\n"); + goto fail4; + } + + rc = ef4_nic_init_interrupt(efx); + if (rc) + goto fail5; + rc = ef4_enable_interrupts(efx); + if (rc) + goto fail6; + + return 0; + + fail6: + ef4_nic_fini_interrupt(efx); + fail5: + ef4_fini_port(efx); + fail4: + efx->type->fini(efx); + fail3: + ef4_fini_napi(efx); + ef4_remove_all(efx); + fail1: + return rc; +} + +/* NIC initialisation + * + * This is called at module load (or hotplug insertion, + * theoretically). It sets up PCI mappings, resets the NIC, + * sets up and registers the network devices with the kernel and hooks + * the interrupt service routine. It does not prepare the device for + * transmission; this is left to the first time one of the network + * interfaces is brought up (i.e. ef4_net_open). + */ +static int ef4_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *entry) +{ + struct net_device *net_dev; + struct ef4_nic *efx; + int rc; + + /* Allocate and initialise a struct net_device and struct ef4_nic */ + net_dev = alloc_etherdev_mqs(sizeof(*efx), EF4_MAX_CORE_TX_QUEUES, + EF4_MAX_RX_QUEUES); + if (!net_dev) + return -ENOMEM; + efx = netdev_priv(net_dev); + efx->type = (const struct ef4_nic_type *) entry->driver_data; + efx->fixed_features |= NETIF_F_HIGHDMA; + + pci_set_drvdata(pci_dev, efx); + SET_NETDEV_DEV(net_dev, &pci_dev->dev); + rc = ef4_init_struct(efx, pci_dev, net_dev); + if (rc) + goto fail1; + + netif_info(efx, probe, efx->net_dev, + "Solarflare NIC detected\n"); + + ef4_probe_vpd_strings(efx); + + /* Set up basic I/O (BAR mappings etc) */ + rc = ef4_init_io(efx); + if (rc) + goto fail2; + + rc = ef4_pci_probe_main(efx); + if (rc) + goto fail3; + + net_dev->features |= (efx->type->offload_features | NETIF_F_SG | + NETIF_F_RXCSUM); + /* Mask for features that also apply to VLAN devices */ + net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG | + NETIF_F_HIGHDMA | NETIF_F_RXCSUM); + + net_dev->hw_features = net_dev->features & ~efx->fixed_features; + + /* Disable VLAN filtering by default. It may be enforced if + * the feature is fixed (i.e. VLAN filters are required to + * receive VLAN tagged packets due to vPort restrictions). + */ + net_dev->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; + net_dev->features |= efx->fixed_features; + + rc = ef4_register_netdev(efx); + if (rc) + goto fail4; + + netif_dbg(efx, probe, efx->net_dev, "initialisation successful\n"); + + /* Try to create MTDs, but allow this to fail */ + rtnl_lock(); + rc = ef4_mtd_probe(efx); + rtnl_unlock(); + if (rc && rc != -EPERM) + netif_warn(efx, probe, efx->net_dev, + "failed to create MTDs (%d)\n", rc); + + rc = pci_enable_pcie_error_reporting(pci_dev); + if (rc && rc != -EINVAL) + netif_notice(efx, probe, efx->net_dev, + "PCIE error reporting unavailable (%d).\n", + rc); + + return 0; + + fail4: + ef4_pci_remove_main(efx); + fail3: + ef4_fini_io(efx); + fail2: + ef4_fini_struct(efx); + fail1: + WARN_ON(rc > 0); + netif_dbg(efx, drv, efx->net_dev, "initialisation failed. rc=%d\n", rc); + free_netdev(net_dev); + return rc; +} + +static int ef4_pm_freeze(struct device *dev) +{ + struct ef4_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + + rtnl_lock(); + + if (efx->state != STATE_DISABLED) { + efx->state = STATE_UNINIT; + + ef4_device_detach_sync(efx); + + ef4_stop_all(efx); + ef4_disable_interrupts(efx); + } + + rtnl_unlock(); + + return 0; +} + +static int ef4_pm_thaw(struct device *dev) +{ + int rc; + struct ef4_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + + rtnl_lock(); + + if (efx->state != STATE_DISABLED) { + rc = ef4_enable_interrupts(efx); + if (rc) + goto fail; + + mutex_lock(&efx->mac_lock); + efx->phy_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); + + ef4_start_all(efx); + + netif_device_attach(efx->net_dev); + + efx->state = STATE_READY; + + efx->type->resume_wol(efx); + } + + rtnl_unlock(); + + /* Reschedule any quenched resets scheduled during ef4_pm_freeze() */ + queue_work(reset_workqueue, &efx->reset_work); + + return 0; + +fail: + rtnl_unlock(); + + return rc; +} + +static int ef4_pm_poweroff(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct ef4_nic *efx = pci_get_drvdata(pci_dev); + + efx->type->fini(efx); + + efx->reset_pending = 0; + + pci_save_state(pci_dev); + return pci_set_power_state(pci_dev, PCI_D3hot); +} + +/* Used for both resume and restore */ +static int ef4_pm_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct ef4_nic *efx = pci_get_drvdata(pci_dev); + int rc; + + rc = pci_set_power_state(pci_dev, PCI_D0); + if (rc) + return rc; + pci_restore_state(pci_dev); + rc = pci_enable_device(pci_dev); + if (rc) + return rc; + pci_set_master(efx->pci_dev); + rc = efx->type->reset(efx, RESET_TYPE_ALL); + if (rc) + return rc; + rc = efx->type->init(efx); + if (rc) + return rc; + rc = ef4_pm_thaw(dev); + return rc; +} + +static int ef4_pm_suspend(struct device *dev) +{ + int rc; + + ef4_pm_freeze(dev); + rc = ef4_pm_poweroff(dev); + if (rc) + ef4_pm_resume(dev); + return rc; +} + +static const struct dev_pm_ops ef4_pm_ops = { + .suspend = ef4_pm_suspend, + .resume = ef4_pm_resume, + .freeze = ef4_pm_freeze, + .thaw = ef4_pm_thaw, + .poweroff = ef4_pm_poweroff, + .restore = ef4_pm_resume, +}; + +/* A PCI error affecting this device was detected. + * At this point MMIO and DMA may be disabled. + * Stop the software path and request a slot reset. + */ +static pci_ers_result_t ef4_io_error_detected(struct pci_dev *pdev, + enum pci_channel_state state) +{ + pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; + struct ef4_nic *efx = pci_get_drvdata(pdev); + + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + rtnl_lock(); + + if (efx->state != STATE_DISABLED) { + efx->state = STATE_RECOVERY; + efx->reset_pending = 0; + + ef4_device_detach_sync(efx); + + ef4_stop_all(efx); + ef4_disable_interrupts(efx); + + status = PCI_ERS_RESULT_NEED_RESET; + } else { + /* If the interface is disabled we don't want to do anything + * with it. + */ + status = PCI_ERS_RESULT_RECOVERED; + } + + rtnl_unlock(); + + pci_disable_device(pdev); + + return status; +} + +/* Fake a successful reset, which will be performed later in ef4_io_resume. */ +static pci_ers_result_t ef4_io_slot_reset(struct pci_dev *pdev) +{ + struct ef4_nic *efx = pci_get_drvdata(pdev); + pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; + int rc; + + if (pci_enable_device(pdev)) { + netif_err(efx, hw, efx->net_dev, + "Cannot re-enable PCI device after reset.\n"); + status = PCI_ERS_RESULT_DISCONNECT; + } + + rc = pci_cleanup_aer_uncorrect_error_status(pdev); + if (rc) { + netif_err(efx, hw, efx->net_dev, + "pci_cleanup_aer_uncorrect_error_status failed (%d)\n", rc); + /* Non-fatal error. Continue. */ + } + + return status; +} + +/* Perform the actual reset and resume I/O operations. */ +static void ef4_io_resume(struct pci_dev *pdev) +{ + struct ef4_nic *efx = pci_get_drvdata(pdev); + int rc; + + rtnl_lock(); + + if (efx->state == STATE_DISABLED) + goto out; + + rc = ef4_reset(efx, RESET_TYPE_ALL); + if (rc) { + netif_err(efx, hw, efx->net_dev, + "ef4_reset failed after PCI error (%d)\n", rc); + } else { + efx->state = STATE_READY; + netif_dbg(efx, hw, efx->net_dev, + "Done resetting and resuming IO after PCI error.\n"); + } + +out: + rtnl_unlock(); +} + +/* For simplicity and reliability, we always require a slot reset and try to + * reset the hardware when a pci error affecting the device is detected. + * We leave both the link_reset and mmio_enabled callback unimplemented: + * with our request for slot reset the mmio_enabled callback will never be + * called, and the link_reset callback is not used by AER or EEH mechanisms. + */ +static const struct pci_error_handlers ef4_err_handlers = { + .error_detected = ef4_io_error_detected, + .slot_reset = ef4_io_slot_reset, + .resume = ef4_io_resume, +}; + +static struct pci_driver ef4_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = ef4_pci_table, + .probe = ef4_pci_probe, + .remove = ef4_pci_remove, + .driver.pm = &ef4_pm_ops, + .err_handler = &ef4_err_handlers, +}; + +/************************************************************************** + * + * Kernel module interface + * + *************************************************************************/ + +module_param(interrupt_mode, uint, 0444); +MODULE_PARM_DESC(interrupt_mode, + "Interrupt mode (0=>MSIX 1=>MSI 2=>legacy)"); + +static int __init ef4_init_module(void) +{ + int rc; + + printk(KERN_INFO "Solarflare Falcon driver v" EF4_DRIVER_VERSION "\n"); + + rc = register_netdevice_notifier(&ef4_netdev_notifier); + if (rc) + goto err_notifier; + + reset_workqueue = create_singlethread_workqueue("sfc_reset"); + if (!reset_workqueue) { + rc = -ENOMEM; + goto err_reset; + } + + rc = pci_register_driver(&ef4_pci_driver); + if (rc < 0) + goto err_pci; + + return 0; + + err_pci: + destroy_workqueue(reset_workqueue); + err_reset: + unregister_netdevice_notifier(&ef4_netdev_notifier); + err_notifier: + return rc; +} + +static void __exit ef4_exit_module(void) +{ + printk(KERN_INFO "Solarflare Falcon driver unloading\n"); + + pci_unregister_driver(&ef4_pci_driver); + destroy_workqueue(reset_workqueue); + unregister_netdevice_notifier(&ef4_netdev_notifier); + +} + +module_init(ef4_init_module); +module_exit(ef4_exit_module); + +MODULE_AUTHOR("Solarflare Communications and " + "Michael Brown "); +MODULE_DESCRIPTION("Solarflare Falcon network driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, ef4_pci_table); diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h new file mode 100644 index 0000000..c89456f --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -0,0 +1,277 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_EFX_H +#define EF4_EFX_H + +#include "net_driver.h" +#include "filter.h" + +/* All controllers use BAR 0 for I/O space and BAR 2(&3) for memory */ +/* All VFs use BAR 0/1 for memory */ +#define EF4_MEM_BAR 2 +#define EF4_MEM_VF_BAR 0 + +int ef4_net_open(struct net_device *net_dev); +int ef4_net_stop(struct net_device *net_dev); + +/* TX */ +int ef4_probe_tx_queue(struct ef4_tx_queue *tx_queue); +void ef4_remove_tx_queue(struct ef4_tx_queue *tx_queue); +void ef4_init_tx_queue(struct ef4_tx_queue *tx_queue); +void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue); +void ef4_fini_tx_queue(struct ef4_tx_queue *tx_queue); +netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev); +netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); +void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); +int ef4_setup_tc(struct net_device *net_dev, u32 handle, __be16 proto, + struct tc_to_netdev *tc); +unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); +extern bool ef4_separate_tx_channels; + +/* RX */ +void ef4_set_default_rx_indir_table(struct ef4_nic *efx); +void ef4_rx_config_page_split(struct ef4_nic *efx); +int ef4_probe_rx_queue(struct ef4_rx_queue *rx_queue); +void ef4_remove_rx_queue(struct ef4_rx_queue *rx_queue); +void ef4_init_rx_queue(struct ef4_rx_queue *rx_queue); +void ef4_fini_rx_queue(struct ef4_rx_queue *rx_queue); +void ef4_fast_push_rx_descriptors(struct ef4_rx_queue *rx_queue, bool atomic); +void ef4_rx_slow_fill(unsigned long context); +void __ef4_rx_packet(struct ef4_channel *channel); +void ef4_rx_packet(struct ef4_rx_queue *rx_queue, unsigned int index, + unsigned int n_frags, unsigned int len, u16 flags); +static inline void ef4_rx_flush_packet(struct ef4_channel *channel) +{ + if (channel->rx_pkt_n_frags) + __ef4_rx_packet(channel); +} +void ef4_schedule_slow_fill(struct ef4_rx_queue *rx_queue); + +#define EF4_MAX_DMAQ_SIZE 4096UL +#define EF4_DEFAULT_DMAQ_SIZE 1024UL +#define EF4_MIN_DMAQ_SIZE 512UL + +#define EF4_MAX_EVQ_SIZE 16384UL +#define EF4_MIN_EVQ_SIZE 512UL + +/* Maximum number of TCP segments we support for soft-TSO */ +#define EF4_TSO_MAX_SEGS 100 + +/* The smallest [rt]xq_entries that the driver supports. RX minimum + * is a bit arbitrary. For TX, we must have space for at least 2 + * TSO skbs. + */ +#define EF4_RXQ_MIN_ENT 128U +#define EF4_TXQ_MIN_ENT(efx) (2 * ef4_tx_max_skb_descs(efx)) + +static inline bool ef4_rss_enabled(struct ef4_nic *efx) +{ + return efx->rss_spread > 1; +} + +/* Filters */ + +void ef4_mac_reconfigure(struct ef4_nic *efx); + +/** + * ef4_filter_insert_filter - add or replace a filter + * @efx: NIC in which to insert the filter + * @spec: Specification for the filter + * @replace_equal: Flag for whether the specified filter may replace an + * existing filter with equal priority + * + * On success, return the filter ID. + * On failure, return a negative error code. + * + * If existing filters have equal match values to the new filter spec, + * then the new filter might replace them or the function might fail, + * as follows. + * + * 1. If the existing filters have lower priority, or @replace_equal + * is set and they have equal priority, replace them. + * + * 2. If the existing filters have higher priority, return -%EPERM. + * + * 3. If !ef4_filter_is_mc_recipient(@spec), or the NIC does not + * support delivery to multiple recipients, return -%EEXIST. + * + * This implies that filters for multiple multicast recipients must + * all be inserted with the same priority and @replace_equal = %false. + */ +static inline s32 ef4_filter_insert_filter(struct ef4_nic *efx, + struct ef4_filter_spec *spec, + bool replace_equal) +{ + return efx->type->filter_insert(efx, spec, replace_equal); +} + +/** + * ef4_filter_remove_id_safe - remove a filter by ID, carefully + * @efx: NIC from which to remove the filter + * @priority: Priority of filter, as passed to @ef4_filter_insert_filter + * @filter_id: ID of filter, as returned by @ef4_filter_insert_filter + * + * This function will range-check @filter_id, so it is safe to call + * with a value passed from userland. + */ +static inline int ef4_filter_remove_id_safe(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 filter_id) +{ + return efx->type->filter_remove_safe(efx, priority, filter_id); +} + +/** + * ef4_filter_get_filter_safe - retrieve a filter by ID, carefully + * @efx: NIC from which to remove the filter + * @priority: Priority of filter, as passed to @ef4_filter_insert_filter + * @filter_id: ID of filter, as returned by @ef4_filter_insert_filter + * @spec: Buffer in which to store filter specification + * + * This function will range-check @filter_id, so it is safe to call + * with a value passed from userland. + */ +static inline int +ef4_filter_get_filter_safe(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 filter_id, struct ef4_filter_spec *spec) +{ + return efx->type->filter_get_safe(efx, priority, filter_id, spec); +} + +static inline u32 ef4_filter_count_rx_used(struct ef4_nic *efx, + enum ef4_filter_priority priority) +{ + return efx->type->filter_count_rx_used(efx, priority); +} +static inline u32 ef4_filter_get_rx_id_limit(struct ef4_nic *efx) +{ + return efx->type->filter_get_rx_id_limit(efx); +} +static inline s32 ef4_filter_get_rx_ids(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 *buf, u32 size) +{ + return efx->type->filter_get_rx_ids(efx, priority, buf, size); +} +#ifdef CONFIG_RFS_ACCEL +int ef4_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, + u16 rxq_index, u32 flow_id); +bool __ef4_filter_rfs_expire(struct ef4_nic *efx, unsigned quota); +static inline void ef4_filter_rfs_expire(struct ef4_channel *channel) +{ + if (channel->rfs_filters_added >= 60 && + __ef4_filter_rfs_expire(channel->efx, 100)) + channel->rfs_filters_added -= 60; +} +#define ef4_filter_rfs_enabled() 1 +#else +static inline void ef4_filter_rfs_expire(struct ef4_channel *channel) {} +#define ef4_filter_rfs_enabled() 0 +#endif +bool ef4_filter_is_mc_recipient(const struct ef4_filter_spec *spec); + +/* Channels */ +int ef4_channel_dummy_op_int(struct ef4_channel *channel); +void ef4_channel_dummy_op_void(struct ef4_channel *channel); +int ef4_realloc_channels(struct ef4_nic *efx, u32 rxq_entries, u32 txq_entries); + +/* Ports */ +int ef4_reconfigure_port(struct ef4_nic *efx); +int __ef4_reconfigure_port(struct ef4_nic *efx); + +/* Ethtool support */ +extern const struct ethtool_ops ef4_ethtool_ops; + +/* Reset handling */ +int ef4_reset(struct ef4_nic *efx, enum reset_type method); +void ef4_reset_down(struct ef4_nic *efx, enum reset_type method); +int ef4_reset_up(struct ef4_nic *efx, enum reset_type method, bool ok); +int ef4_try_recovery(struct ef4_nic *efx); + +/* Global */ +void ef4_schedule_reset(struct ef4_nic *efx, enum reset_type type); +unsigned int ef4_usecs_to_ticks(struct ef4_nic *efx, unsigned int usecs); +unsigned int ef4_ticks_to_usecs(struct ef4_nic *efx, unsigned int ticks); +int ef4_init_irq_moderation(struct ef4_nic *efx, unsigned int tx_usecs, + unsigned int rx_usecs, bool rx_adaptive, + bool rx_may_override_tx); +void ef4_get_irq_moderation(struct ef4_nic *efx, unsigned int *tx_usecs, + unsigned int *rx_usecs, bool *rx_adaptive); +void ef4_stop_eventq(struct ef4_channel *channel); +void ef4_start_eventq(struct ef4_channel *channel); + +/* Dummy PHY ops for PHY drivers */ +int ef4_port_dummy_op_int(struct ef4_nic *efx); +void ef4_port_dummy_op_void(struct ef4_nic *efx); + +/* Update the generic software stats in the passed stats array */ +void ef4_update_sw_stats(struct ef4_nic *efx, u64 *stats); + +/* MTD */ +#ifdef CONFIG_SFC_FALCON_MTD +int ef4_mtd_add(struct ef4_nic *efx, struct ef4_mtd_partition *parts, + size_t n_parts, size_t sizeof_part); +static inline int ef4_mtd_probe(struct ef4_nic *efx) +{ + return efx->type->mtd_probe(efx); +} +void ef4_mtd_rename(struct ef4_nic *efx); +void ef4_mtd_remove(struct ef4_nic *efx); +#else +static inline int ef4_mtd_probe(struct ef4_nic *efx) { return 0; } +static inline void ef4_mtd_rename(struct ef4_nic *efx) {} +static inline void ef4_mtd_remove(struct ef4_nic *efx) {} +#endif + +static inline void ef4_schedule_channel(struct ef4_channel *channel) +{ + netif_vdbg(channel->efx, intr, channel->efx->net_dev, + "channel %d scheduling NAPI poll on CPU%d\n", + channel->channel, raw_smp_processor_id()); + + napi_schedule(&channel->napi_str); +} + +static inline void ef4_schedule_channel_irq(struct ef4_channel *channel) +{ + channel->event_test_cpu = raw_smp_processor_id(); + ef4_schedule_channel(channel); +} + +void ef4_link_status_changed(struct ef4_nic *efx); +void ef4_link_set_advertising(struct ef4_nic *efx, u32); +void ef4_link_set_wanted_fc(struct ef4_nic *efx, u8); + +static inline void ef4_device_detach_sync(struct ef4_nic *efx) +{ + struct net_device *dev = efx->net_dev; + + /* Lock/freeze all TX queues so that we can be sure the + * TX scheduler is stopped when we're done and before + * netif_device_present() becomes false. + */ + netif_tx_lock_bh(dev); + netif_device_detach(dev); + netif_tx_unlock_bh(dev); +} + +static inline bool ef4_rwsem_assert_write_locked(struct rw_semaphore *sem) +{ + if (WARN_ON(down_read_trylock(sem))) { + up_read(sem); + return false; + } + return true; +} + +#endif /* EF4_EFX_H */ diff --git a/drivers/net/ethernet/sfc/falcon/enum.h b/drivers/net/ethernet/sfc/falcon/enum.h new file mode 100644 index 0000000..30a1136 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/enum.h @@ -0,0 +1,171 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2007-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_ENUM_H +#define EF4_ENUM_H + +/** + * enum ef4_loopback_mode - loopback modes + * @LOOPBACK_NONE: no loopback + * @LOOPBACK_DATA: data path loopback + * @LOOPBACK_GMAC: loopback within GMAC + * @LOOPBACK_XGMII: loopback after XMAC + * @LOOPBACK_XGXS: loopback within BPX after XGXS + * @LOOPBACK_XAUI: loopback within BPX before XAUI serdes + * @LOOPBACK_GMII: loopback within BPX after GMAC + * @LOOPBACK_SGMII: loopback within BPX within SGMII + * @LOOPBACK_XGBR: loopback within BPX within XGBR + * @LOOPBACK_XFI: loopback within BPX before XFI serdes + * @LOOPBACK_XAUI_FAR: loopback within BPX after XAUI serdes + * @LOOPBACK_GMII_FAR: loopback within BPX before SGMII + * @LOOPBACK_SGMII_FAR: loopback within BPX after SGMII + * @LOOPBACK_XFI_FAR: loopback after XFI serdes + * @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level + * @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level + * @LOOPBACK_PCS: loopback within 10G PHY at PCS level + * @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level + * @LOOPBACK_XPORT: cross port loopback + * @LOOPBACK_XGMII_WS: wireside loopback excluding XMAC + * @LOOPBACK_XAUI_WS: wireside loopback within BPX within XAUI serdes + * @LOOPBACK_XAUI_WS_FAR: wireside loopback within BPX including XAUI serdes + * @LOOPBACK_XAUI_WS_NEAR: wireside loopback within BPX excluding XAUI serdes + * @LOOPBACK_GMII_WS: wireside loopback excluding GMAC + * @LOOPBACK_XFI_WS: wireside loopback excluding XFI serdes + * @LOOPBACK_XFI_WS_FAR: wireside loopback including XFI serdes + * @LOOPBACK_PHYXS_WS: wireside loopback within 10G PHY at PHYXS level + */ +/* Please keep up-to-date w.r.t the following two #defines */ +enum ef4_loopback_mode { + LOOPBACK_NONE = 0, + LOOPBACK_DATA = 1, + LOOPBACK_GMAC = 2, + LOOPBACK_XGMII = 3, + LOOPBACK_XGXS = 4, + LOOPBACK_XAUI = 5, + LOOPBACK_GMII = 6, + LOOPBACK_SGMII = 7, + LOOPBACK_XGBR = 8, + LOOPBACK_XFI = 9, + LOOPBACK_XAUI_FAR = 10, + LOOPBACK_GMII_FAR = 11, + LOOPBACK_SGMII_FAR = 12, + LOOPBACK_XFI_FAR = 13, + LOOPBACK_GPHY = 14, + LOOPBACK_PHYXS = 15, + LOOPBACK_PCS = 16, + LOOPBACK_PMAPMD = 17, + LOOPBACK_XPORT = 18, + LOOPBACK_XGMII_WS = 19, + LOOPBACK_XAUI_WS = 20, + LOOPBACK_XAUI_WS_FAR = 21, + LOOPBACK_XAUI_WS_NEAR = 22, + LOOPBACK_GMII_WS = 23, + LOOPBACK_XFI_WS = 24, + LOOPBACK_XFI_WS_FAR = 25, + LOOPBACK_PHYXS_WS = 26, + LOOPBACK_MAX +}; +#define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD + +/* These loopbacks occur within the controller */ +#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_DATA) | \ + (1 << LOOPBACK_GMAC) | \ + (1 << LOOPBACK_XGMII)| \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI) | \ + (1 << LOOPBACK_GMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_XGBR) | \ + (1 << LOOPBACK_XFI) | \ + (1 << LOOPBACK_XAUI_FAR) | \ + (1 << LOOPBACK_GMII_FAR) | \ + (1 << LOOPBACK_SGMII_FAR) | \ + (1 << LOOPBACK_XFI_FAR) | \ + (1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR)) + +#define LOOPBACKS_WS ((1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR) | \ + (1 << LOOPBACK_PHYXS_WS)) + +#define LOOPBACKS_EXTERNAL(_efx) \ + ((_efx)->loopback_modes & ~LOOPBACKS_INTERNAL & \ + ~(1 << LOOPBACK_NONE)) + +#define LOOPBACK_MASK(_efx) \ + (1 << (_efx)->loopback_mode) + +#define LOOPBACK_INTERNAL(_efx) \ + (!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx))) + +#define LOOPBACK_EXTERNAL(_efx) \ + (!!(LOOPBACK_MASK(_efx) & LOOPBACKS_EXTERNAL(_efx))) + +#define LOOPBACK_CHANGED(_from, _to, _mask) \ + (!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask))) + +#define LOOPBACK_OUT_OF(_from, _to, _mask) \ + ((LOOPBACK_MASK(_from) & (_mask)) && !(LOOPBACK_MASK(_to) & (_mask))) + +/*****************************************************************************/ + +/** + * enum reset_type - reset types + * + * %RESET_TYPE_INVSIBLE, %RESET_TYPE_ALL, %RESET_TYPE_WORLD and + * %RESET_TYPE_DISABLE specify the method/scope of the reset. The + * other valuesspecify reasons, which ef4_schedule_reset() will choose + * a method for. + * + * Reset methods are numbered in order of increasing scope. + * + * @RESET_TYPE_INVISIBLE: Reset datapath and MAC + * @RESET_TYPE_RECOVER_OR_ALL: Try to recover. Apply RESET_TYPE_ALL + * if unsuccessful. + * @RESET_TYPE_ALL: Reset datapath, MAC and PHY + * @RESET_TYPE_WORLD: Reset as much as possible + * @RESET_TYPE_RECOVER_OR_DISABLE: Try to recover. Apply RESET_TYPE_DISABLE if + * unsuccessful. + * @RESET_TYPE_DATAPATH: Reset datapath only. + * @RESET_TYPE_DISABLE: Reset datapath, MAC and PHY; leave NIC disabled + * @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog + * @RESET_TYPE_INT_ERROR: reset due to internal error + * @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors + * @RESET_TYPE_DMA_ERROR: DMA error + * @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors + */ +enum reset_type { + RESET_TYPE_INVISIBLE, + RESET_TYPE_RECOVER_OR_ALL, + RESET_TYPE_ALL, + RESET_TYPE_WORLD, + RESET_TYPE_RECOVER_OR_DISABLE, + RESET_TYPE_DATAPATH, + RESET_TYPE_DISABLE, + RESET_TYPE_MAX_METHOD, + RESET_TYPE_TX_WATCHDOG, + RESET_TYPE_INT_ERROR, + RESET_TYPE_RX_RECOVERY, + RESET_TYPE_DMA_ERROR, + RESET_TYPE_TX_SKIP, + RESET_TYPE_MAX, +}; + +#endif /* EF4_ENUM_H */ diff --git a/drivers/net/ethernet/sfc/falcon/ethtool.c b/drivers/net/ethernet/sfc/falcon/ethtool.c new file mode 100644 index 0000000..8e1929b --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/ethtool.c @@ -0,0 +1,1343 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include "net_driver.h" +#include "workarounds.h" +#include "selftest.h" +#include "efx.h" +#include "filter.h" +#include "nic.h" + +struct ef4_sw_stat_desc { + const char *name; + enum { + EF4_ETHTOOL_STAT_SOURCE_nic, + EF4_ETHTOOL_STAT_SOURCE_channel, + EF4_ETHTOOL_STAT_SOURCE_tx_queue + } source; + unsigned offset; + u64(*get_stat) (void *field); /* Reader function */ +}; + +/* Initialiser for a struct ef4_sw_stat_desc with type-checking */ +#define EF4_ETHTOOL_STAT(stat_name, source_name, field, field_type, \ + get_stat_function) { \ + .name = #stat_name, \ + .source = EF4_ETHTOOL_STAT_SOURCE_##source_name, \ + .offset = ((((field_type *) 0) == \ + &((struct ef4_##source_name *)0)->field) ? \ + offsetof(struct ef4_##source_name, field) : \ + offsetof(struct ef4_##source_name, field)), \ + .get_stat = get_stat_function, \ +} + +static u64 ef4_get_uint_stat(void *field) +{ + return *(unsigned int *)field; +} + +static u64 ef4_get_atomic_stat(void *field) +{ + return atomic_read((atomic_t *) field); +} + +#define EF4_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \ + EF4_ETHTOOL_STAT(field, nic, field, \ + atomic_t, ef4_get_atomic_stat) + +#define EF4_ETHTOOL_UINT_CHANNEL_STAT(field) \ + EF4_ETHTOOL_STAT(field, channel, n_##field, \ + unsigned int, ef4_get_uint_stat) + +#define EF4_ETHTOOL_UINT_TXQ_STAT(field) \ + EF4_ETHTOOL_STAT(tx_##field, tx_queue, field, \ + unsigned int, ef4_get_uint_stat) + +static const struct ef4_sw_stat_desc ef4_sw_stat_desc[] = { + EF4_ETHTOOL_UINT_TXQ_STAT(merge_events), + EF4_ETHTOOL_UINT_TXQ_STAT(pushes), + EF4_ETHTOOL_UINT_TXQ_STAT(cb_packets), + EF4_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), + EF4_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), + EF4_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), + EF4_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err), + EF4_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch), + EF4_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), + EF4_ETHTOOL_UINT_CHANNEL_STAT(rx_merge_events), + EF4_ETHTOOL_UINT_CHANNEL_STAT(rx_merge_packets), +}; + +#define EF4_ETHTOOL_SW_STAT_COUNT ARRAY_SIZE(ef4_sw_stat_desc) + +#define EF4_ETHTOOL_EEPROM_MAGIC 0xEFAB + +/************************************************************************** + * + * Ethtool operations + * + ************************************************************************** + */ + +/* Identify device by flashing LEDs */ +static int ef4_ethtool_phys_id(struct net_device *net_dev, + enum ethtool_phys_id_state state) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + enum ef4_led_mode mode = EF4_LED_DEFAULT; + + switch (state) { + case ETHTOOL_ID_ON: + mode = EF4_LED_ON; + break; + case ETHTOOL_ID_OFF: + mode = EF4_LED_OFF; + break; + case ETHTOOL_ID_INACTIVE: + mode = EF4_LED_DEFAULT; + break; + case ETHTOOL_ID_ACTIVE: + return 1; /* cycle on/off once per second */ + } + + efx->type->set_id_led(efx, mode); + return 0; +} + +/* This must be called with rtnl_lock held. */ +static int ef4_ethtool_get_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct ef4_link_state *link_state = &efx->link_state; + + mutex_lock(&efx->mac_lock); + efx->phy_op->get_settings(efx, ecmd); + mutex_unlock(&efx->mac_lock); + + /* Both MACs support pause frames (bidirectional and respond-only) */ + ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + + if (LOOPBACK_INTERNAL(efx)) { + ethtool_cmd_speed_set(ecmd, link_state->speed); + ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF; + } + + return 0; +} + +/* This must be called with rtnl_lock held. */ +static int ef4_ethtool_set_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int rc; + + /* GMAC does not support 1000Mbps HD */ + if ((ethtool_cmd_speed(ecmd) == SPEED_1000) && + (ecmd->duplex != DUPLEX_FULL)) { + netif_dbg(efx, drv, efx->net_dev, + "rejecting unsupported 1000Mbps HD setting\n"); + return -EINVAL; + } + + mutex_lock(&efx->mac_lock); + rc = efx->phy_op->set_settings(efx, ecmd); + mutex_unlock(&efx->mac_lock); + return rc; +} + +static void ef4_ethtool_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *info) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strlcpy(info->version, EF4_DRIVER_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); +} + +static int ef4_ethtool_get_regs_len(struct net_device *net_dev) +{ + return ef4_nic_get_regs_len(netdev_priv(net_dev)); +} + +static void ef4_ethtool_get_regs(struct net_device *net_dev, + struct ethtool_regs *regs, void *buf) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + regs->version = efx->type->revision; + ef4_nic_get_regs(efx, buf); +} + +static u32 ef4_ethtool_get_msglevel(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + return efx->msg_enable; +} + +static void ef4_ethtool_set_msglevel(struct net_device *net_dev, u32 msg_enable) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + efx->msg_enable = msg_enable; +} + +/** + * ef4_fill_test - fill in an individual self-test entry + * @test_index: Index of the test + * @strings: Ethtool strings, or %NULL + * @data: Ethtool test results, or %NULL + * @test: Pointer to test result (used only if data != %NULL) + * @unit_format: Unit name format (e.g. "chan\%d") + * @unit_id: Unit id (e.g. 0 for "chan0") + * @test_format: Test name format (e.g. "loopback.\%s.tx.sent") + * @test_id: Test id (e.g. "PHYXS" for "loopback.PHYXS.tx_sent") + * + * Fill in an individual self-test entry. + */ +static void ef4_fill_test(unsigned int test_index, u8 *strings, u64 *data, + int *test, const char *unit_format, int unit_id, + const char *test_format, const char *test_id) +{ + char unit_str[ETH_GSTRING_LEN], test_str[ETH_GSTRING_LEN]; + + /* Fill data value, if applicable */ + if (data) + data[test_index] = *test; + + /* Fill string, if applicable */ + if (strings) { + if (strchr(unit_format, '%')) + snprintf(unit_str, sizeof(unit_str), + unit_format, unit_id); + else + strcpy(unit_str, unit_format); + snprintf(test_str, sizeof(test_str), test_format, test_id); + snprintf(strings + test_index * ETH_GSTRING_LEN, + ETH_GSTRING_LEN, + "%-6s %-24s", unit_str, test_str); + } +} + +#define EF4_CHANNEL_NAME(_channel) "chan%d", _channel->channel +#define EF4_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue +#define EF4_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue +#define EF4_LOOPBACK_NAME(_mode, _counter) \ + "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, ef4_loopback_mode) + +/** + * ef4_fill_loopback_test - fill in a block of loopback self-test entries + * @efx: Efx NIC + * @lb_tests: Efx loopback self-test results structure + * @mode: Loopback test mode + * @test_index: Starting index of the test + * @strings: Ethtool strings, or %NULL + * @data: Ethtool test results, or %NULL + * + * Fill in a block of loopback self-test entries. Return new test + * index. + */ +static int ef4_fill_loopback_test(struct ef4_nic *efx, + struct ef4_loopback_self_tests *lb_tests, + enum ef4_loopback_mode mode, + unsigned int test_index, + u8 *strings, u64 *data) +{ + struct ef4_channel *channel = + ef4_get_channel(efx, efx->tx_channel_offset); + struct ef4_tx_queue *tx_queue; + + ef4_for_each_channel_tx_queue(tx_queue, channel) { + ef4_fill_test(test_index++, strings, data, + &lb_tests->tx_sent[tx_queue->queue], + EF4_TX_QUEUE_NAME(tx_queue), + EF4_LOOPBACK_NAME(mode, "tx_sent")); + ef4_fill_test(test_index++, strings, data, + &lb_tests->tx_done[tx_queue->queue], + EF4_TX_QUEUE_NAME(tx_queue), + EF4_LOOPBACK_NAME(mode, "tx_done")); + } + ef4_fill_test(test_index++, strings, data, + &lb_tests->rx_good, + "rx", 0, + EF4_LOOPBACK_NAME(mode, "rx_good")); + ef4_fill_test(test_index++, strings, data, + &lb_tests->rx_bad, + "rx", 0, + EF4_LOOPBACK_NAME(mode, "rx_bad")); + + return test_index; +} + +/** + * ef4_ethtool_fill_self_tests - get self-test details + * @efx: Efx NIC + * @tests: Efx self-test results structure, or %NULL + * @strings: Ethtool strings, or %NULL + * @data: Ethtool test results, or %NULL + * + * Get self-test number of strings, strings, and/or test results. + * Return number of strings (== number of test results). + * + * The reason for merging these three functions is to make sure that + * they can never be inconsistent. + */ +static int ef4_ethtool_fill_self_tests(struct ef4_nic *efx, + struct ef4_self_tests *tests, + u8 *strings, u64 *data) +{ + struct ef4_channel *channel; + unsigned int n = 0, i; + enum ef4_loopback_mode mode; + + ef4_fill_test(n++, strings, data, &tests->phy_alive, + "phy", 0, "alive", NULL); + ef4_fill_test(n++, strings, data, &tests->nvram, + "core", 0, "nvram", NULL); + ef4_fill_test(n++, strings, data, &tests->interrupt, + "core", 0, "interrupt", NULL); + + /* Event queues */ + ef4_for_each_channel(channel, efx) { + ef4_fill_test(n++, strings, data, + &tests->eventq_dma[channel->channel], + EF4_CHANNEL_NAME(channel), + "eventq.dma", NULL); + ef4_fill_test(n++, strings, data, + &tests->eventq_int[channel->channel], + EF4_CHANNEL_NAME(channel), + "eventq.int", NULL); + } + + ef4_fill_test(n++, strings, data, &tests->memory, + "core", 0, "memory", NULL); + ef4_fill_test(n++, strings, data, &tests->registers, + "core", 0, "registers", NULL); + + if (efx->phy_op->run_tests != NULL) { + EF4_BUG_ON_PARANOID(efx->phy_op->test_name == NULL); + + for (i = 0; true; ++i) { + const char *name; + + EF4_BUG_ON_PARANOID(i >= EF4_MAX_PHY_TESTS); + name = efx->phy_op->test_name(efx, i); + if (name == NULL) + break; + + ef4_fill_test(n++, strings, data, &tests->phy_ext[i], + "phy", 0, name, NULL); + } + } + + /* Loopback tests */ + for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { + if (!(efx->loopback_modes & (1 << mode))) + continue; + n = ef4_fill_loopback_test(efx, + &tests->loopback[mode], mode, n, + strings, data); + } + + return n; +} + +static size_t ef4_describe_per_queue_stats(struct ef4_nic *efx, u8 *strings) +{ + size_t n_stats = 0; + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) { + if (ef4_channel_has_tx_queues(channel)) { + n_stats++; + if (strings != NULL) { + snprintf(strings, ETH_GSTRING_LEN, + "tx-%u.tx_packets", + channel->tx_queue[0].queue / + EF4_TXQ_TYPES); + + strings += ETH_GSTRING_LEN; + } + } + } + ef4_for_each_channel(channel, efx) { + if (ef4_channel_has_rx_queue(channel)) { + n_stats++; + if (strings != NULL) { + snprintf(strings, ETH_GSTRING_LEN, + "rx-%d.rx_packets", channel->channel); + strings += ETH_GSTRING_LEN; + } + } + } + return n_stats; +} + +static int ef4_ethtool_get_sset_count(struct net_device *net_dev, + int string_set) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + switch (string_set) { + case ETH_SS_STATS: + return efx->type->describe_stats(efx, NULL) + + EF4_ETHTOOL_SW_STAT_COUNT + + ef4_describe_per_queue_stats(efx, NULL); + case ETH_SS_TEST: + return ef4_ethtool_fill_self_tests(efx, NULL, NULL, NULL); + default: + return -EINVAL; + } +} + +static void ef4_ethtool_get_strings(struct net_device *net_dev, + u32 string_set, u8 *strings) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int i; + + switch (string_set) { + case ETH_SS_STATS: + strings += (efx->type->describe_stats(efx, strings) * + ETH_GSTRING_LEN); + for (i = 0; i < EF4_ETHTOOL_SW_STAT_COUNT; i++) + strlcpy(strings + i * ETH_GSTRING_LEN, + ef4_sw_stat_desc[i].name, ETH_GSTRING_LEN); + strings += EF4_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN; + strings += (ef4_describe_per_queue_stats(efx, strings) * + ETH_GSTRING_LEN); + break; + case ETH_SS_TEST: + ef4_ethtool_fill_self_tests(efx, NULL, strings, NULL); + break; + default: + /* No other string sets */ + break; + } +} + +static void ef4_ethtool_get_stats(struct net_device *net_dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + const struct ef4_sw_stat_desc *stat; + struct ef4_channel *channel; + struct ef4_tx_queue *tx_queue; + struct ef4_rx_queue *rx_queue; + int i; + + spin_lock_bh(&efx->stats_lock); + + /* Get NIC statistics */ + data += efx->type->update_stats(efx, data, NULL); + + /* Get software statistics */ + for (i = 0; i < EF4_ETHTOOL_SW_STAT_COUNT; i++) { + stat = &ef4_sw_stat_desc[i]; + switch (stat->source) { + case EF4_ETHTOOL_STAT_SOURCE_nic: + data[i] = stat->get_stat((void *)efx + stat->offset); + break; + case EF4_ETHTOOL_STAT_SOURCE_channel: + data[i] = 0; + ef4_for_each_channel(channel, efx) + data[i] += stat->get_stat((void *)channel + + stat->offset); + break; + case EF4_ETHTOOL_STAT_SOURCE_tx_queue: + data[i] = 0; + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_tx_queue(tx_queue, channel) + data[i] += + stat->get_stat((void *)tx_queue + + stat->offset); + } + break; + } + } + data += EF4_ETHTOOL_SW_STAT_COUNT; + + spin_unlock_bh(&efx->stats_lock); + + ef4_for_each_channel(channel, efx) { + if (ef4_channel_has_tx_queues(channel)) { + *data = 0; + ef4_for_each_channel_tx_queue(tx_queue, channel) { + *data += tx_queue->tx_packets; + } + data++; + } + } + ef4_for_each_channel(channel, efx) { + if (ef4_channel_has_rx_queue(channel)) { + *data = 0; + ef4_for_each_channel_rx_queue(rx_queue, channel) { + *data += rx_queue->rx_packets; + } + data++; + } + } +} + +static void ef4_ethtool_self_test(struct net_device *net_dev, + struct ethtool_test *test, u64 *data) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct ef4_self_tests *ef4_tests; + bool already_up; + int rc = -ENOMEM; + + ef4_tests = kzalloc(sizeof(*ef4_tests), GFP_KERNEL); + if (!ef4_tests) + goto fail; + + if (efx->state != STATE_READY) { + rc = -EBUSY; + goto out; + } + + netif_info(efx, drv, efx->net_dev, "starting %sline testing\n", + (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); + + /* We need rx buffers and interrupts. */ + already_up = (efx->net_dev->flags & IFF_UP); + if (!already_up) { + rc = dev_open(efx->net_dev); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "failed opening device.\n"); + goto out; + } + } + + rc = ef4_selftest(efx, ef4_tests, test->flags); + + if (!already_up) + dev_close(efx->net_dev); + + netif_info(efx, drv, efx->net_dev, "%s %sline self-tests\n", + rc == 0 ? "passed" : "failed", + (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); + +out: + ef4_ethtool_fill_self_tests(efx, ef4_tests, NULL, data); + kfree(ef4_tests); +fail: + if (rc) + test->flags |= ETH_TEST_FL_FAILED; +} + +/* Restart autonegotiation */ +static int ef4_ethtool_nway_reset(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + return mdio45_nway_restart(&efx->mdio); +} + +/* + * Each channel has a single IRQ and moderation timer, started by any + * completion (or other event). Unless the module parameter + * separate_tx_channels is set, IRQs and moderation are therefore + * shared between RX and TX completions. In this case, when RX IRQ + * moderation is explicitly changed then TX IRQ moderation is + * automatically changed too, but otherwise we fail if the two values + * are requested to be different. + * + * The hardware does not support a limit on the number of completions + * before an IRQ, so we do not use the max_frames fields. We should + * report and require that max_frames == (usecs != 0), but this would + * invalidate existing user documentation. + * + * The hardware does not have distinct settings for interrupt + * moderation while the previous IRQ is being handled, so we should + * not use the 'irq' fields. However, an earlier developer + * misunderstood the meaning of the 'irq' fields and the driver did + * not support the standard fields. To avoid invalidating existing + * user documentation, we report and accept changes through either the + * standard or 'irq' fields. If both are changed at the same time, we + * prefer the standard field. + * + * We implement adaptive IRQ moderation, but use a different algorithm + * from that assumed in the definition of struct ethtool_coalesce. + * Therefore we do not use any of the adaptive moderation parameters + * in it. + */ + +static int ef4_ethtool_get_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *coalesce) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + unsigned int tx_usecs, rx_usecs; + bool rx_adaptive; + + ef4_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &rx_adaptive); + + coalesce->tx_coalesce_usecs = tx_usecs; + coalesce->tx_coalesce_usecs_irq = tx_usecs; + coalesce->rx_coalesce_usecs = rx_usecs; + coalesce->rx_coalesce_usecs_irq = rx_usecs; + coalesce->use_adaptive_rx_coalesce = rx_adaptive; + + return 0; +} + +static int ef4_ethtool_set_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *coalesce) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct ef4_channel *channel; + unsigned int tx_usecs, rx_usecs; + bool adaptive, rx_may_override_tx; + int rc; + + if (coalesce->use_adaptive_tx_coalesce) + return -EINVAL; + + ef4_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &adaptive); + + if (coalesce->rx_coalesce_usecs != rx_usecs) + rx_usecs = coalesce->rx_coalesce_usecs; + else + rx_usecs = coalesce->rx_coalesce_usecs_irq; + + adaptive = coalesce->use_adaptive_rx_coalesce; + + /* If channels are shared, TX IRQ moderation can be quietly + * overridden unless it is changed from its old value. + */ + rx_may_override_tx = (coalesce->tx_coalesce_usecs == tx_usecs && + coalesce->tx_coalesce_usecs_irq == tx_usecs); + if (coalesce->tx_coalesce_usecs != tx_usecs) + tx_usecs = coalesce->tx_coalesce_usecs; + else + tx_usecs = coalesce->tx_coalesce_usecs_irq; + + rc = ef4_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive, + rx_may_override_tx); + if (rc != 0) + return rc; + + ef4_for_each_channel(channel, efx) + efx->type->push_irq_moderation(channel); + + return 0; +} + +static void ef4_ethtool_get_ringparam(struct net_device *net_dev, + struct ethtool_ringparam *ring) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + ring->rx_max_pending = EF4_MAX_DMAQ_SIZE; + ring->tx_max_pending = EF4_MAX_DMAQ_SIZE; + ring->rx_pending = efx->rxq_entries; + ring->tx_pending = efx->txq_entries; +} + +static int ef4_ethtool_set_ringparam(struct net_device *net_dev, + struct ethtool_ringparam *ring) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + u32 txq_entries; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending || + ring->rx_pending > EF4_MAX_DMAQ_SIZE || + ring->tx_pending > EF4_MAX_DMAQ_SIZE) + return -EINVAL; + + if (ring->rx_pending < EF4_RXQ_MIN_ENT) { + netif_err(efx, drv, efx->net_dev, + "RX queues cannot be smaller than %u\n", + EF4_RXQ_MIN_ENT); + return -EINVAL; + } + + txq_entries = max(ring->tx_pending, EF4_TXQ_MIN_ENT(efx)); + if (txq_entries != ring->tx_pending) + netif_warn(efx, drv, efx->net_dev, + "increasing TX queue size to minimum of %u\n", + txq_entries); + + return ef4_realloc_channels(efx, ring->rx_pending, txq_entries); +} + +static int ef4_ethtool_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *pause) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + u8 wanted_fc, old_fc; + u32 old_adv; + int rc = 0; + + mutex_lock(&efx->mac_lock); + + wanted_fc = ((pause->rx_pause ? EF4_FC_RX : 0) | + (pause->tx_pause ? EF4_FC_TX : 0) | + (pause->autoneg ? EF4_FC_AUTO : 0)); + + if ((wanted_fc & EF4_FC_TX) && !(wanted_fc & EF4_FC_RX)) { + netif_dbg(efx, drv, efx->net_dev, + "Flow control unsupported: tx ON rx OFF\n"); + rc = -EINVAL; + goto out; + } + + if ((wanted_fc & EF4_FC_AUTO) && !efx->link_advertising) { + netif_dbg(efx, drv, efx->net_dev, + "Autonegotiation is disabled\n"); + rc = -EINVAL; + goto out; + } + + /* Hook for Falcon bug 11482 workaround */ + if (efx->type->prepare_enable_fc_tx && + (wanted_fc & EF4_FC_TX) && !(efx->wanted_fc & EF4_FC_TX)) + efx->type->prepare_enable_fc_tx(efx); + + old_adv = efx->link_advertising; + old_fc = efx->wanted_fc; + ef4_link_set_wanted_fc(efx, wanted_fc); + if (efx->link_advertising != old_adv || + (efx->wanted_fc ^ old_fc) & EF4_FC_AUTO) { + rc = efx->phy_op->reconfigure(efx); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "Unable to advertise requested flow " + "control setting\n"); + goto out; + } + } + + /* Reconfigure the MAC. The PHY *may* generate a link state change event + * if the user just changed the advertised capabilities, but there's no + * harm doing this twice */ + ef4_mac_reconfigure(efx); + +out: + mutex_unlock(&efx->mac_lock); + + return rc; +} + +static void ef4_ethtool_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *pause) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + pause->rx_pause = !!(efx->wanted_fc & EF4_FC_RX); + pause->tx_pause = !!(efx->wanted_fc & EF4_FC_TX); + pause->autoneg = !!(efx->wanted_fc & EF4_FC_AUTO); +} + +static void ef4_ethtool_get_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + return efx->type->get_wol(efx, wol); +} + + +static int ef4_ethtool_set_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + return efx->type->set_wol(efx, wol->wolopts); +} + +static int ef4_ethtool_reset(struct net_device *net_dev, u32 *flags) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int rc; + + rc = efx->type->map_reset_flags(flags); + if (rc < 0) + return rc; + + return ef4_reset(efx, rc); +} + +/* MAC address mask including only I/G bit */ +static const u8 mac_addr_ig_mask[ETH_ALEN] __aligned(2) = {0x01, 0, 0, 0, 0, 0}; + +#define IP4_ADDR_FULL_MASK ((__force __be32)~0) +#define IP_PROTO_FULL_MASK 0xFF +#define PORT_FULL_MASK ((__force __be16)~0) +#define ETHER_TYPE_FULL_MASK ((__force __be16)~0) + +static inline void ip6_fill_mask(__be32 *mask) +{ + mask[0] = mask[1] = mask[2] = mask[3] = ~(__be32)0; +} + +static int ef4_ethtool_get_class_rule(struct ef4_nic *efx, + struct ethtool_rx_flow_spec *rule) +{ + struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; + struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; + struct ethtool_usrip4_spec *uip_entry = &rule->h_u.usr_ip4_spec; + struct ethtool_usrip4_spec *uip_mask = &rule->m_u.usr_ip4_spec; + struct ethtool_tcpip6_spec *ip6_entry = &rule->h_u.tcp_ip6_spec; + struct ethtool_tcpip6_spec *ip6_mask = &rule->m_u.tcp_ip6_spec; + struct ethtool_usrip6_spec *uip6_entry = &rule->h_u.usr_ip6_spec; + struct ethtool_usrip6_spec *uip6_mask = &rule->m_u.usr_ip6_spec; + struct ethhdr *mac_entry = &rule->h_u.ether_spec; + struct ethhdr *mac_mask = &rule->m_u.ether_spec; + struct ef4_filter_spec spec; + int rc; + + rc = ef4_filter_get_filter_safe(efx, EF4_FILTER_PRI_MANUAL, + rule->location, &spec); + if (rc) + return rc; + + if (spec.dmaq_id == EF4_FILTER_RX_DMAQ_ID_DROP) + rule->ring_cookie = RX_CLS_FLOW_DISC; + else + rule->ring_cookie = spec.dmaq_id; + + if ((spec.match_flags & EF4_FILTER_MATCH_ETHER_TYPE) && + spec.ether_type == htons(ETH_P_IP) && + (spec.match_flags & EF4_FILTER_MATCH_IP_PROTO) && + (spec.ip_proto == IPPROTO_TCP || spec.ip_proto == IPPROTO_UDP) && + !(spec.match_flags & + ~(EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_OUTER_VID | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_REM_HOST | + EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_PORT | EF4_FILTER_MATCH_REM_PORT))) { + rule->flow_type = ((spec.ip_proto == IPPROTO_TCP) ? + TCP_V4_FLOW : UDP_V4_FLOW); + if (spec.match_flags & EF4_FILTER_MATCH_LOC_HOST) { + ip_entry->ip4dst = spec.loc_host[0]; + ip_mask->ip4dst = IP4_ADDR_FULL_MASK; + } + if (spec.match_flags & EF4_FILTER_MATCH_REM_HOST) { + ip_entry->ip4src = spec.rem_host[0]; + ip_mask->ip4src = IP4_ADDR_FULL_MASK; + } + if (spec.match_flags & EF4_FILTER_MATCH_LOC_PORT) { + ip_entry->pdst = spec.loc_port; + ip_mask->pdst = PORT_FULL_MASK; + } + if (spec.match_flags & EF4_FILTER_MATCH_REM_PORT) { + ip_entry->psrc = spec.rem_port; + ip_mask->psrc = PORT_FULL_MASK; + } + } else if ((spec.match_flags & EF4_FILTER_MATCH_ETHER_TYPE) && + spec.ether_type == htons(ETH_P_IPV6) && + (spec.match_flags & EF4_FILTER_MATCH_IP_PROTO) && + (spec.ip_proto == IPPROTO_TCP || spec.ip_proto == IPPROTO_UDP) && + !(spec.match_flags & + ~(EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_OUTER_VID | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_REM_HOST | + EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_PORT | EF4_FILTER_MATCH_REM_PORT))) { + rule->flow_type = ((spec.ip_proto == IPPROTO_TCP) ? + TCP_V6_FLOW : UDP_V6_FLOW); + if (spec.match_flags & EF4_FILTER_MATCH_LOC_HOST) { + memcpy(ip6_entry->ip6dst, spec.loc_host, + sizeof(ip6_entry->ip6dst)); + ip6_fill_mask(ip6_mask->ip6dst); + } + if (spec.match_flags & EF4_FILTER_MATCH_REM_HOST) { + memcpy(ip6_entry->ip6src, spec.rem_host, + sizeof(ip6_entry->ip6src)); + ip6_fill_mask(ip6_mask->ip6src); + } + if (spec.match_flags & EF4_FILTER_MATCH_LOC_PORT) { + ip6_entry->pdst = spec.loc_port; + ip6_mask->pdst = PORT_FULL_MASK; + } + if (spec.match_flags & EF4_FILTER_MATCH_REM_PORT) { + ip6_entry->psrc = spec.rem_port; + ip6_mask->psrc = PORT_FULL_MASK; + } + } else if (!(spec.match_flags & + ~(EF4_FILTER_MATCH_LOC_MAC | EF4_FILTER_MATCH_LOC_MAC_IG | + EF4_FILTER_MATCH_REM_MAC | EF4_FILTER_MATCH_ETHER_TYPE | + EF4_FILTER_MATCH_OUTER_VID))) { + rule->flow_type = ETHER_FLOW; + if (spec.match_flags & + (EF4_FILTER_MATCH_LOC_MAC | EF4_FILTER_MATCH_LOC_MAC_IG)) { + ether_addr_copy(mac_entry->h_dest, spec.loc_mac); + if (spec.match_flags & EF4_FILTER_MATCH_LOC_MAC) + eth_broadcast_addr(mac_mask->h_dest); + else + ether_addr_copy(mac_mask->h_dest, + mac_addr_ig_mask); + } + if (spec.match_flags & EF4_FILTER_MATCH_REM_MAC) { + ether_addr_copy(mac_entry->h_source, spec.rem_mac); + eth_broadcast_addr(mac_mask->h_source); + } + if (spec.match_flags & EF4_FILTER_MATCH_ETHER_TYPE) { + mac_entry->h_proto = spec.ether_type; + mac_mask->h_proto = ETHER_TYPE_FULL_MASK; + } + } else if (spec.match_flags & EF4_FILTER_MATCH_ETHER_TYPE && + spec.ether_type == htons(ETH_P_IP) && + !(spec.match_flags & + ~(EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_OUTER_VID | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_REM_HOST | + EF4_FILTER_MATCH_IP_PROTO))) { + rule->flow_type = IPV4_USER_FLOW; + uip_entry->ip_ver = ETH_RX_NFC_IP4; + if (spec.match_flags & EF4_FILTER_MATCH_IP_PROTO) { + uip_mask->proto = IP_PROTO_FULL_MASK; + uip_entry->proto = spec.ip_proto; + } + if (spec.match_flags & EF4_FILTER_MATCH_LOC_HOST) { + uip_entry->ip4dst = spec.loc_host[0]; + uip_mask->ip4dst = IP4_ADDR_FULL_MASK; + } + if (spec.match_flags & EF4_FILTER_MATCH_REM_HOST) { + uip_entry->ip4src = spec.rem_host[0]; + uip_mask->ip4src = IP4_ADDR_FULL_MASK; + } + } else if (spec.match_flags & EF4_FILTER_MATCH_ETHER_TYPE && + spec.ether_type == htons(ETH_P_IPV6) && + !(spec.match_flags & + ~(EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_OUTER_VID | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_REM_HOST | + EF4_FILTER_MATCH_IP_PROTO))) { + rule->flow_type = IPV6_USER_FLOW; + if (spec.match_flags & EF4_FILTER_MATCH_IP_PROTO) { + uip6_mask->l4_proto = IP_PROTO_FULL_MASK; + uip6_entry->l4_proto = spec.ip_proto; + } + if (spec.match_flags & EF4_FILTER_MATCH_LOC_HOST) { + memcpy(uip6_entry->ip6dst, spec.loc_host, + sizeof(uip6_entry->ip6dst)); + ip6_fill_mask(uip6_mask->ip6dst); + } + if (spec.match_flags & EF4_FILTER_MATCH_REM_HOST) { + memcpy(uip6_entry->ip6src, spec.rem_host, + sizeof(uip6_entry->ip6src)); + ip6_fill_mask(uip6_mask->ip6src); + } + } else { + /* The above should handle all filters that we insert */ + WARN_ON(1); + return -EINVAL; + } + + if (spec.match_flags & EF4_FILTER_MATCH_OUTER_VID) { + rule->flow_type |= FLOW_EXT; + rule->h_ext.vlan_tci = spec.outer_vid; + rule->m_ext.vlan_tci = htons(0xfff); + } + + return rc; +} + +static int +ef4_ethtool_get_rxnfc(struct net_device *net_dev, + struct ethtool_rxnfc *info, u32 *rule_locs) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + switch (info->cmd) { + case ETHTOOL_GRXRINGS: + info->data = efx->n_rx_channels; + return 0; + + case ETHTOOL_GRXFH: { + unsigned min_revision = 0; + + info->data = 0; + switch (info->flow_type) { + case TCP_V4_FLOW: + info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + case UDP_V4_FLOW: + case SCTP_V4_FLOW: + case AH_ESP_V4_FLOW: + case IPV4_FLOW: + info->data |= RXH_IP_SRC | RXH_IP_DST; + min_revision = EF4_REV_FALCON_B0; + break; + default: + break; + } + if (ef4_nic_rev(efx) < min_revision) + info->data = 0; + return 0; + } + + case ETHTOOL_GRXCLSRLCNT: + info->data = ef4_filter_get_rx_id_limit(efx); + if (info->data == 0) + return -EOPNOTSUPP; + info->data |= RX_CLS_LOC_SPECIAL; + info->rule_cnt = + ef4_filter_count_rx_used(efx, EF4_FILTER_PRI_MANUAL); + return 0; + + case ETHTOOL_GRXCLSRULE: + if (ef4_filter_get_rx_id_limit(efx) == 0) + return -EOPNOTSUPP; + return ef4_ethtool_get_class_rule(efx, &info->fs); + + case ETHTOOL_GRXCLSRLALL: { + s32 rc; + info->data = ef4_filter_get_rx_id_limit(efx); + if (info->data == 0) + return -EOPNOTSUPP; + rc = ef4_filter_get_rx_ids(efx, EF4_FILTER_PRI_MANUAL, + rule_locs, info->rule_cnt); + if (rc < 0) + return rc; + info->rule_cnt = rc; + return 0; + } + + default: + return -EOPNOTSUPP; + } +} + +static inline bool ip6_mask_is_full(__be32 mask[4]) +{ + return !~(mask[0] & mask[1] & mask[2] & mask[3]); +} + +static inline bool ip6_mask_is_empty(__be32 mask[4]) +{ + return !(mask[0] | mask[1] | mask[2] | mask[3]); +} + +static int ef4_ethtool_set_class_rule(struct ef4_nic *efx, + struct ethtool_rx_flow_spec *rule) +{ + struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; + struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; + struct ethtool_usrip4_spec *uip_entry = &rule->h_u.usr_ip4_spec; + struct ethtool_usrip4_spec *uip_mask = &rule->m_u.usr_ip4_spec; + struct ethtool_tcpip6_spec *ip6_entry = &rule->h_u.tcp_ip6_spec; + struct ethtool_tcpip6_spec *ip6_mask = &rule->m_u.tcp_ip6_spec; + struct ethtool_usrip6_spec *uip6_entry = &rule->h_u.usr_ip6_spec; + struct ethtool_usrip6_spec *uip6_mask = &rule->m_u.usr_ip6_spec; + struct ethhdr *mac_entry = &rule->h_u.ether_spec; + struct ethhdr *mac_mask = &rule->m_u.ether_spec; + struct ef4_filter_spec spec; + int rc; + + /* Check that user wants us to choose the location */ + if (rule->location != RX_CLS_LOC_ANY) + return -EINVAL; + + /* Range-check ring_cookie */ + if (rule->ring_cookie >= efx->n_rx_channels && + rule->ring_cookie != RX_CLS_FLOW_DISC) + return -EINVAL; + + /* Check for unsupported extensions */ + if ((rule->flow_type & FLOW_EXT) && + (rule->m_ext.vlan_etype || rule->m_ext.data[0] || + rule->m_ext.data[1])) + return -EINVAL; + + ef4_filter_init_rx(&spec, EF4_FILTER_PRI_MANUAL, + efx->rx_scatter ? EF4_FILTER_FLAG_RX_SCATTER : 0, + (rule->ring_cookie == RX_CLS_FLOW_DISC) ? + EF4_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie); + + switch (rule->flow_type & ~FLOW_EXT) { + case TCP_V4_FLOW: + case UDP_V4_FLOW: + spec.match_flags = (EF4_FILTER_MATCH_ETHER_TYPE | + EF4_FILTER_MATCH_IP_PROTO); + spec.ether_type = htons(ETH_P_IP); + spec.ip_proto = ((rule->flow_type & ~FLOW_EXT) == TCP_V4_FLOW ? + IPPROTO_TCP : IPPROTO_UDP); + if (ip_mask->ip4dst) { + if (ip_mask->ip4dst != IP4_ADDR_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_LOC_HOST; + spec.loc_host[0] = ip_entry->ip4dst; + } + if (ip_mask->ip4src) { + if (ip_mask->ip4src != IP4_ADDR_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_REM_HOST; + spec.rem_host[0] = ip_entry->ip4src; + } + if (ip_mask->pdst) { + if (ip_mask->pdst != PORT_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_LOC_PORT; + spec.loc_port = ip_entry->pdst; + } + if (ip_mask->psrc) { + if (ip_mask->psrc != PORT_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_REM_PORT; + spec.rem_port = ip_entry->psrc; + } + if (ip_mask->tos) + return -EINVAL; + break; + + case TCP_V6_FLOW: + case UDP_V6_FLOW: + spec.match_flags = (EF4_FILTER_MATCH_ETHER_TYPE | + EF4_FILTER_MATCH_IP_PROTO); + spec.ether_type = htons(ETH_P_IPV6); + spec.ip_proto = ((rule->flow_type & ~FLOW_EXT) == TCP_V6_FLOW ? + IPPROTO_TCP : IPPROTO_UDP); + if (!ip6_mask_is_empty(ip6_mask->ip6dst)) { + if (!ip6_mask_is_full(ip6_mask->ip6dst)) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_LOC_HOST; + memcpy(spec.loc_host, ip6_entry->ip6dst, sizeof(spec.loc_host)); + } + if (!ip6_mask_is_empty(ip6_mask->ip6src)) { + if (!ip6_mask_is_full(ip6_mask->ip6src)) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_REM_HOST; + memcpy(spec.rem_host, ip6_entry->ip6src, sizeof(spec.rem_host)); + } + if (ip6_mask->pdst) { + if (ip6_mask->pdst != PORT_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_LOC_PORT; + spec.loc_port = ip6_entry->pdst; + } + if (ip6_mask->psrc) { + if (ip6_mask->psrc != PORT_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_REM_PORT; + spec.rem_port = ip6_entry->psrc; + } + if (ip6_mask->tclass) + return -EINVAL; + break; + + case IPV4_USER_FLOW: + if (uip_mask->l4_4_bytes || uip_mask->tos || uip_mask->ip_ver || + uip_entry->ip_ver != ETH_RX_NFC_IP4) + return -EINVAL; + spec.match_flags = EF4_FILTER_MATCH_ETHER_TYPE; + spec.ether_type = htons(ETH_P_IP); + if (uip_mask->ip4dst) { + if (uip_mask->ip4dst != IP4_ADDR_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_LOC_HOST; + spec.loc_host[0] = uip_entry->ip4dst; + } + if (uip_mask->ip4src) { + if (uip_mask->ip4src != IP4_ADDR_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_REM_HOST; + spec.rem_host[0] = uip_entry->ip4src; + } + if (uip_mask->proto) { + if (uip_mask->proto != IP_PROTO_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_IP_PROTO; + spec.ip_proto = uip_entry->proto; + } + break; + + case IPV6_USER_FLOW: + if (uip6_mask->l4_4_bytes || uip6_mask->tclass) + return -EINVAL; + spec.match_flags = EF4_FILTER_MATCH_ETHER_TYPE; + spec.ether_type = htons(ETH_P_IPV6); + if (!ip6_mask_is_empty(uip6_mask->ip6dst)) { + if (!ip6_mask_is_full(uip6_mask->ip6dst)) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_LOC_HOST; + memcpy(spec.loc_host, uip6_entry->ip6dst, sizeof(spec.loc_host)); + } + if (!ip6_mask_is_empty(uip6_mask->ip6src)) { + if (!ip6_mask_is_full(uip6_mask->ip6src)) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_REM_HOST; + memcpy(spec.rem_host, uip6_entry->ip6src, sizeof(spec.rem_host)); + } + if (uip6_mask->l4_proto) { + if (uip6_mask->l4_proto != IP_PROTO_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_IP_PROTO; + spec.ip_proto = uip6_entry->l4_proto; + } + break; + + case ETHER_FLOW: + if (!is_zero_ether_addr(mac_mask->h_dest)) { + if (ether_addr_equal(mac_mask->h_dest, + mac_addr_ig_mask)) + spec.match_flags |= EF4_FILTER_MATCH_LOC_MAC_IG; + else if (is_broadcast_ether_addr(mac_mask->h_dest)) + spec.match_flags |= EF4_FILTER_MATCH_LOC_MAC; + else + return -EINVAL; + ether_addr_copy(spec.loc_mac, mac_entry->h_dest); + } + if (!is_zero_ether_addr(mac_mask->h_source)) { + if (!is_broadcast_ether_addr(mac_mask->h_source)) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_REM_MAC; + ether_addr_copy(spec.rem_mac, mac_entry->h_source); + } + if (mac_mask->h_proto) { + if (mac_mask->h_proto != ETHER_TYPE_FULL_MASK) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_ETHER_TYPE; + spec.ether_type = mac_entry->h_proto; + } + break; + + default: + return -EINVAL; + } + + if ((rule->flow_type & FLOW_EXT) && rule->m_ext.vlan_tci) { + if (rule->m_ext.vlan_tci != htons(0xfff)) + return -EINVAL; + spec.match_flags |= EF4_FILTER_MATCH_OUTER_VID; + spec.outer_vid = rule->h_ext.vlan_tci; + } + + rc = ef4_filter_insert_filter(efx, &spec, true); + if (rc < 0) + return rc; + + rule->location = rc; + return 0; +} + +static int ef4_ethtool_set_rxnfc(struct net_device *net_dev, + struct ethtool_rxnfc *info) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + if (ef4_filter_get_rx_id_limit(efx) == 0) + return -EOPNOTSUPP; + + switch (info->cmd) { + case ETHTOOL_SRXCLSRLINS: + return ef4_ethtool_set_class_rule(efx, &info->fs); + + case ETHTOOL_SRXCLSRLDEL: + return ef4_filter_remove_id_safe(efx, EF4_FILTER_PRI_MANUAL, + info->fs.location); + + default: + return -EOPNOTSUPP; + } +} + +static u32 ef4_ethtool_get_rxfh_indir_size(struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + return ((ef4_nic_rev(efx) < EF4_REV_FALCON_B0 || + efx->n_rx_channels == 1) ? + 0 : ARRAY_SIZE(efx->rx_indir_table)); +} + +static int ef4_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + if (indir) + memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table)); + return 0; +} + +static int ef4_ethtool_set_rxfh(struct net_device *net_dev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + + /* We do not allow change in unsupported parameters */ + if (key || + (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) + return -EOPNOTSUPP; + if (!indir) + return 0; + + return efx->type->rx_push_rss_config(efx, true, indir); +} + +static int ef4_ethtool_get_module_eeprom(struct net_device *net_dev, + struct ethtool_eeprom *ee, + u8 *data) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int ret; + + if (!efx->phy_op || !efx->phy_op->get_module_eeprom) + return -EOPNOTSUPP; + + mutex_lock(&efx->mac_lock); + ret = efx->phy_op->get_module_eeprom(efx, ee, data); + mutex_unlock(&efx->mac_lock); + + return ret; +} + +static int ef4_ethtool_get_module_info(struct net_device *net_dev, + struct ethtool_modinfo *modinfo) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + int ret; + + if (!efx->phy_op || !efx->phy_op->get_module_info) + return -EOPNOTSUPP; + + mutex_lock(&efx->mac_lock); + ret = efx->phy_op->get_module_info(efx, modinfo); + mutex_unlock(&efx->mac_lock); + + return ret; +} + +const struct ethtool_ops ef4_ethtool_ops = { + .get_settings = ef4_ethtool_get_settings, + .set_settings = ef4_ethtool_set_settings, + .get_drvinfo = ef4_ethtool_get_drvinfo, + .get_regs_len = ef4_ethtool_get_regs_len, + .get_regs = ef4_ethtool_get_regs, + .get_msglevel = ef4_ethtool_get_msglevel, + .set_msglevel = ef4_ethtool_set_msglevel, + .nway_reset = ef4_ethtool_nway_reset, + .get_link = ethtool_op_get_link, + .get_coalesce = ef4_ethtool_get_coalesce, + .set_coalesce = ef4_ethtool_set_coalesce, + .get_ringparam = ef4_ethtool_get_ringparam, + .set_ringparam = ef4_ethtool_set_ringparam, + .get_pauseparam = ef4_ethtool_get_pauseparam, + .set_pauseparam = ef4_ethtool_set_pauseparam, + .get_sset_count = ef4_ethtool_get_sset_count, + .self_test = ef4_ethtool_self_test, + .get_strings = ef4_ethtool_get_strings, + .set_phys_id = ef4_ethtool_phys_id, + .get_ethtool_stats = ef4_ethtool_get_stats, + .get_wol = ef4_ethtool_get_wol, + .set_wol = ef4_ethtool_set_wol, + .reset = ef4_ethtool_reset, + .get_rxnfc = ef4_ethtool_get_rxnfc, + .set_rxnfc = ef4_ethtool_set_rxnfc, + .get_rxfh_indir_size = ef4_ethtool_get_rxfh_indir_size, + .get_rxfh = ef4_ethtool_get_rxfh, + .set_rxfh = ef4_ethtool_set_rxfh, + .get_module_info = ef4_ethtool_get_module_info, + .get_module_eeprom = ef4_ethtool_get_module_eeprom, +}; diff --git a/drivers/net/ethernet/sfc/falcon/falcon.c b/drivers/net/ethernet/sfc/falcon/falcon.c new file mode 100644 index 0000000..c6ff0cc --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/falcon.c @@ -0,0 +1,2903 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "farch_regs.h" +#include "io.h" +#include "phy.h" +#include "workarounds.h" +#include "selftest.h" +#include "mdio_10g.h" + +/* Hardware control for SFC4000 (aka Falcon). */ + +/************************************************************************** + * + * NIC stats + * + ************************************************************************** + */ + +#define FALCON_MAC_STATS_SIZE 0x100 + +#define XgRxOctets_offset 0x0 +#define XgRxOctets_WIDTH 48 +#define XgRxOctetsOK_offset 0x8 +#define XgRxOctetsOK_WIDTH 48 +#define XgRxPkts_offset 0x10 +#define XgRxPkts_WIDTH 32 +#define XgRxPktsOK_offset 0x14 +#define XgRxPktsOK_WIDTH 32 +#define XgRxBroadcastPkts_offset 0x18 +#define XgRxBroadcastPkts_WIDTH 32 +#define XgRxMulticastPkts_offset 0x1C +#define XgRxMulticastPkts_WIDTH 32 +#define XgRxUnicastPkts_offset 0x20 +#define XgRxUnicastPkts_WIDTH 32 +#define XgRxUndersizePkts_offset 0x24 +#define XgRxUndersizePkts_WIDTH 32 +#define XgRxOversizePkts_offset 0x28 +#define XgRxOversizePkts_WIDTH 32 +#define XgRxJabberPkts_offset 0x2C +#define XgRxJabberPkts_WIDTH 32 +#define XgRxUndersizeFCSerrorPkts_offset 0x30 +#define XgRxUndersizeFCSerrorPkts_WIDTH 32 +#define XgRxDropEvents_offset 0x34 +#define XgRxDropEvents_WIDTH 32 +#define XgRxFCSerrorPkts_offset 0x38 +#define XgRxFCSerrorPkts_WIDTH 32 +#define XgRxAlignError_offset 0x3C +#define XgRxAlignError_WIDTH 32 +#define XgRxSymbolError_offset 0x40 +#define XgRxSymbolError_WIDTH 32 +#define XgRxInternalMACError_offset 0x44 +#define XgRxInternalMACError_WIDTH 32 +#define XgRxControlPkts_offset 0x48 +#define XgRxControlPkts_WIDTH 32 +#define XgRxPausePkts_offset 0x4C +#define XgRxPausePkts_WIDTH 32 +#define XgRxPkts64Octets_offset 0x50 +#define XgRxPkts64Octets_WIDTH 32 +#define XgRxPkts65to127Octets_offset 0x54 +#define XgRxPkts65to127Octets_WIDTH 32 +#define XgRxPkts128to255Octets_offset 0x58 +#define XgRxPkts128to255Octets_WIDTH 32 +#define XgRxPkts256to511Octets_offset 0x5C +#define XgRxPkts256to511Octets_WIDTH 32 +#define XgRxPkts512to1023Octets_offset 0x60 +#define XgRxPkts512to1023Octets_WIDTH 32 +#define XgRxPkts1024to15xxOctets_offset 0x64 +#define XgRxPkts1024to15xxOctets_WIDTH 32 +#define XgRxPkts15xxtoMaxOctets_offset 0x68 +#define XgRxPkts15xxtoMaxOctets_WIDTH 32 +#define XgRxLengthError_offset 0x6C +#define XgRxLengthError_WIDTH 32 +#define XgTxPkts_offset 0x80 +#define XgTxPkts_WIDTH 32 +#define XgTxOctets_offset 0x88 +#define XgTxOctets_WIDTH 48 +#define XgTxMulticastPkts_offset 0x90 +#define XgTxMulticastPkts_WIDTH 32 +#define XgTxBroadcastPkts_offset 0x94 +#define XgTxBroadcastPkts_WIDTH 32 +#define XgTxUnicastPkts_offset 0x98 +#define XgTxUnicastPkts_WIDTH 32 +#define XgTxControlPkts_offset 0x9C +#define XgTxControlPkts_WIDTH 32 +#define XgTxPausePkts_offset 0xA0 +#define XgTxPausePkts_WIDTH 32 +#define XgTxPkts64Octets_offset 0xA4 +#define XgTxPkts64Octets_WIDTH 32 +#define XgTxPkts65to127Octets_offset 0xA8 +#define XgTxPkts65to127Octets_WIDTH 32 +#define XgTxPkts128to255Octets_offset 0xAC +#define XgTxPkts128to255Octets_WIDTH 32 +#define XgTxPkts256to511Octets_offset 0xB0 +#define XgTxPkts256to511Octets_WIDTH 32 +#define XgTxPkts512to1023Octets_offset 0xB4 +#define XgTxPkts512to1023Octets_WIDTH 32 +#define XgTxPkts1024to15xxOctets_offset 0xB8 +#define XgTxPkts1024to15xxOctets_WIDTH 32 +#define XgTxPkts1519toMaxOctets_offset 0xBC +#define XgTxPkts1519toMaxOctets_WIDTH 32 +#define XgTxUndersizePkts_offset 0xC0 +#define XgTxUndersizePkts_WIDTH 32 +#define XgTxOversizePkts_offset 0xC4 +#define XgTxOversizePkts_WIDTH 32 +#define XgTxNonTcpUdpPkt_offset 0xC8 +#define XgTxNonTcpUdpPkt_WIDTH 16 +#define XgTxMacSrcErrPkt_offset 0xCC +#define XgTxMacSrcErrPkt_WIDTH 16 +#define XgTxIpSrcErrPkt_offset 0xD0 +#define XgTxIpSrcErrPkt_WIDTH 16 +#define XgDmaDone_offset 0xD4 +#define XgDmaDone_WIDTH 32 + +#define FALCON_XMAC_STATS_DMA_FLAG(efx) \ + (*(u32 *)((efx)->stats_buffer.addr + XgDmaDone_offset)) + +#define FALCON_DMA_STAT(ext_name, hw_name) \ + [FALCON_STAT_ ## ext_name] = \ + { #ext_name, \ + /* 48-bit stats are zero-padded to 64 on DMA */ \ + hw_name ## _ ## WIDTH == 48 ? 64 : hw_name ## _ ## WIDTH, \ + hw_name ## _ ## offset } +#define FALCON_OTHER_STAT(ext_name) \ + [FALCON_STAT_ ## ext_name] = { #ext_name, 0, 0 } +#define GENERIC_SW_STAT(ext_name) \ + [GENERIC_STAT_ ## ext_name] = { #ext_name, 0, 0 } + +static const struct ef4_hw_stat_desc falcon_stat_desc[FALCON_STAT_COUNT] = { + FALCON_DMA_STAT(tx_bytes, XgTxOctets), + FALCON_DMA_STAT(tx_packets, XgTxPkts), + FALCON_DMA_STAT(tx_pause, XgTxPausePkts), + FALCON_DMA_STAT(tx_control, XgTxControlPkts), + FALCON_DMA_STAT(tx_unicast, XgTxUnicastPkts), + FALCON_DMA_STAT(tx_multicast, XgTxMulticastPkts), + FALCON_DMA_STAT(tx_broadcast, XgTxBroadcastPkts), + FALCON_DMA_STAT(tx_lt64, XgTxUndersizePkts), + FALCON_DMA_STAT(tx_64, XgTxPkts64Octets), + FALCON_DMA_STAT(tx_65_to_127, XgTxPkts65to127Octets), + FALCON_DMA_STAT(tx_128_to_255, XgTxPkts128to255Octets), + FALCON_DMA_STAT(tx_256_to_511, XgTxPkts256to511Octets), + FALCON_DMA_STAT(tx_512_to_1023, XgTxPkts512to1023Octets), + FALCON_DMA_STAT(tx_1024_to_15xx, XgTxPkts1024to15xxOctets), + FALCON_DMA_STAT(tx_15xx_to_jumbo, XgTxPkts1519toMaxOctets), + FALCON_DMA_STAT(tx_gtjumbo, XgTxOversizePkts), + FALCON_DMA_STAT(tx_non_tcpudp, XgTxNonTcpUdpPkt), + FALCON_DMA_STAT(tx_mac_src_error, XgTxMacSrcErrPkt), + FALCON_DMA_STAT(tx_ip_src_error, XgTxIpSrcErrPkt), + FALCON_DMA_STAT(rx_bytes, XgRxOctets), + FALCON_DMA_STAT(rx_good_bytes, XgRxOctetsOK), + FALCON_OTHER_STAT(rx_bad_bytes), + FALCON_DMA_STAT(rx_packets, XgRxPkts), + FALCON_DMA_STAT(rx_good, XgRxPktsOK), + FALCON_DMA_STAT(rx_bad, XgRxFCSerrorPkts), + FALCON_DMA_STAT(rx_pause, XgRxPausePkts), + FALCON_DMA_STAT(rx_control, XgRxControlPkts), + FALCON_DMA_STAT(rx_unicast, XgRxUnicastPkts), + FALCON_DMA_STAT(rx_multicast, XgRxMulticastPkts), + FALCON_DMA_STAT(rx_broadcast, XgRxBroadcastPkts), + FALCON_DMA_STAT(rx_lt64, XgRxUndersizePkts), + FALCON_DMA_STAT(rx_64, XgRxPkts64Octets), + FALCON_DMA_STAT(rx_65_to_127, XgRxPkts65to127Octets), + FALCON_DMA_STAT(rx_128_to_255, XgRxPkts128to255Octets), + FALCON_DMA_STAT(rx_256_to_511, XgRxPkts256to511Octets), + FALCON_DMA_STAT(rx_512_to_1023, XgRxPkts512to1023Octets), + FALCON_DMA_STAT(rx_1024_to_15xx, XgRxPkts1024to15xxOctets), + FALCON_DMA_STAT(rx_15xx_to_jumbo, XgRxPkts15xxtoMaxOctets), + FALCON_DMA_STAT(rx_gtjumbo, XgRxOversizePkts), + FALCON_DMA_STAT(rx_bad_lt64, XgRxUndersizeFCSerrorPkts), + FALCON_DMA_STAT(rx_bad_gtjumbo, XgRxJabberPkts), + FALCON_DMA_STAT(rx_overflow, XgRxDropEvents), + FALCON_DMA_STAT(rx_symbol_error, XgRxSymbolError), + FALCON_DMA_STAT(rx_align_error, XgRxAlignError), + FALCON_DMA_STAT(rx_length_error, XgRxLengthError), + FALCON_DMA_STAT(rx_internal_error, XgRxInternalMACError), + FALCON_OTHER_STAT(rx_nodesc_drop_cnt), + GENERIC_SW_STAT(rx_nodesc_trunc), + GENERIC_SW_STAT(rx_noskb_drops), +}; +static const unsigned long falcon_stat_mask[] = { + [0 ... BITS_TO_LONGS(FALCON_STAT_COUNT) - 1] = ~0UL, +}; + +/************************************************************************** + * + * Basic SPI command set and bit definitions + * + *************************************************************************/ + +#define SPI_WRSR 0x01 /* Write status register */ +#define SPI_WRITE 0x02 /* Write data to memory array */ +#define SPI_READ 0x03 /* Read data from memory array */ +#define SPI_WRDI 0x04 /* Reset write enable latch */ +#define SPI_RDSR 0x05 /* Read status register */ +#define SPI_WREN 0x06 /* Set write enable latch */ +#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */ + +#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */ +#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */ +#define SPI_STATUS_BP1 0x08 /* Block protection bit 1 */ +#define SPI_STATUS_BP0 0x04 /* Block protection bit 0 */ +#define SPI_STATUS_WEN 0x02 /* State of the write enable latch */ +#define SPI_STATUS_NRDY 0x01 /* Device busy flag */ + +/************************************************************************** + * + * Non-volatile memory layout + * + ************************************************************************** + */ + +/* SFC4000 flash is partitioned into: + * 0-0x400 chip and board config (see struct falcon_nvconfig) + * 0x400-0x8000 unused (or may contain VPD if EEPROM not present) + * 0x8000-end boot code (mapped to PCI expansion ROM) + * SFC4000 small EEPROM (size < 0x400) is used for VPD only. + * SFC4000 large EEPROM (size >= 0x400) is partitioned into: + * 0-0x400 chip and board config + * configurable VPD + * 0x800-0x1800 boot config + * Aside from the chip and board config, all of these are optional and may + * be absent or truncated depending on the devices used. + */ +#define FALCON_NVCONFIG_END 0x400U +#define FALCON_FLASH_BOOTCODE_START 0x8000U +#define FALCON_EEPROM_BOOTCONFIG_START 0x800U +#define FALCON_EEPROM_BOOTCONFIG_END 0x1800U + +/* Board configuration v2 (v1 is obsolete; later versions are compatible) */ +struct falcon_nvconfig_board_v2 { + __le16 nports; + u8 port0_phy_addr; + u8 port0_phy_type; + u8 port1_phy_addr; + u8 port1_phy_type; + __le16 asic_sub_revision; + __le16 board_revision; +} __packed; + +/* Board configuration v3 extra information */ +struct falcon_nvconfig_board_v3 { + __le32 spi_device_type[2]; +} __packed; + +/* Bit numbers for spi_device_type */ +#define SPI_DEV_TYPE_SIZE_LBN 0 +#define SPI_DEV_TYPE_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_ADDR_LEN_LBN 6 +#define SPI_DEV_TYPE_ADDR_LEN_WIDTH 2 +#define SPI_DEV_TYPE_ERASE_CMD_LBN 8 +#define SPI_DEV_TYPE_ERASE_CMD_WIDTH 8 +#define SPI_DEV_TYPE_ERASE_SIZE_LBN 16 +#define SPI_DEV_TYPE_ERASE_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_BLOCK_SIZE_LBN 24 +#define SPI_DEV_TYPE_BLOCK_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_FIELD(type, field) \ + (((type) >> EF4_LOW_BIT(field)) & EF4_MASK32(EF4_WIDTH(field))) + +#define FALCON_NVCONFIG_OFFSET 0x300 + +#define FALCON_NVCONFIG_BOARD_MAGIC_NUM 0xFA1C +struct falcon_nvconfig { + ef4_oword_t ee_vpd_cfg_reg; /* 0x300 */ + u8 mac_address[2][8]; /* 0x310 */ + ef4_oword_t pcie_sd_ctl0123_reg; /* 0x320 */ + ef4_oword_t pcie_sd_ctl45_reg; /* 0x330 */ + ef4_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */ + ef4_oword_t hw_init_reg; /* 0x350 */ + ef4_oword_t nic_stat_reg; /* 0x360 */ + ef4_oword_t glb_ctl_reg; /* 0x370 */ + ef4_oword_t srm_cfg_reg; /* 0x380 */ + ef4_oword_t spare_reg; /* 0x390 */ + __le16 board_magic_num; /* 0x3A0 */ + __le16 board_struct_ver; + __le16 board_checksum; + struct falcon_nvconfig_board_v2 board_v2; + ef4_oword_t ee_base_page_reg; /* 0x3B0 */ + struct falcon_nvconfig_board_v3 board_v3; /* 0x3C0 */ +} __packed; + +/*************************************************************************/ + +static int falcon_reset_hw(struct ef4_nic *efx, enum reset_type method); +static void falcon_reconfigure_mac_wrapper(struct ef4_nic *efx); + +static const unsigned int +/* "Large" EEPROM device: Atmel AT25640 or similar + * 8 KB, 16-bit address, 32 B write block */ +large_eeprom_type = ((13 << SPI_DEV_TYPE_SIZE_LBN) + | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)), +/* Default flash device: Atmel AT25F1024 + * 128 KB, 24-bit address, 32 KB erase block, 256 B write block */ +default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN) + | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN) + | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN) + | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)); + +/************************************************************************** + * + * I2C bus - this is a bit-bashing interface using GPIO pins + * Note that it uses the output enables to tristate the outputs + * SDA is the data pin and SCL is the clock + * + ************************************************************************** + */ +static void falcon_setsda(void *data, int state) +{ + struct ef4_nic *efx = (struct ef4_nic *)data; + ef4_oword_t reg; + + ef4_reado(efx, ®, FR_AB_GPIO_CTL); + EF4_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, !state); + ef4_writeo(efx, ®, FR_AB_GPIO_CTL); +} + +static void falcon_setscl(void *data, int state) +{ + struct ef4_nic *efx = (struct ef4_nic *)data; + ef4_oword_t reg; + + ef4_reado(efx, ®, FR_AB_GPIO_CTL); + EF4_SET_OWORD_FIELD(reg, FRF_AB_GPIO0_OEN, !state); + ef4_writeo(efx, ®, FR_AB_GPIO_CTL); +} + +static int falcon_getsda(void *data) +{ + struct ef4_nic *efx = (struct ef4_nic *)data; + ef4_oword_t reg; + + ef4_reado(efx, ®, FR_AB_GPIO_CTL); + return EF4_OWORD_FIELD(reg, FRF_AB_GPIO3_IN); +} + +static int falcon_getscl(void *data) +{ + struct ef4_nic *efx = (struct ef4_nic *)data; + ef4_oword_t reg; + + ef4_reado(efx, ®, FR_AB_GPIO_CTL); + return EF4_OWORD_FIELD(reg, FRF_AB_GPIO0_IN); +} + +static const struct i2c_algo_bit_data falcon_i2c_bit_operations = { + .setsda = falcon_setsda, + .setscl = falcon_setscl, + .getsda = falcon_getsda, + .getscl = falcon_getscl, + .udelay = 5, + /* Wait up to 50 ms for slave to let us pull SCL high */ + .timeout = DIV_ROUND_UP(HZ, 20), +}; + +static void falcon_push_irq_moderation(struct ef4_channel *channel) +{ + ef4_dword_t timer_cmd; + struct ef4_nic *efx = channel->efx; + + /* Set timer register */ + if (channel->irq_moderation_us) { + unsigned int ticks; + + ticks = ef4_usecs_to_ticks(efx, channel->irq_moderation_us); + EF4_POPULATE_DWORD_2(timer_cmd, + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_INT_HLDOFF, + FRF_AB_TC_TIMER_VAL, + ticks - 1); + } else { + EF4_POPULATE_DWORD_2(timer_cmd, + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_DIS, + FRF_AB_TC_TIMER_VAL, 0); + } + BUILD_BUG_ON(FR_AA_TIMER_COMMAND_KER != FR_BZ_TIMER_COMMAND_P0); + ef4_writed_page_locked(efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, + channel->channel); +} + +static void falcon_deconfigure_mac_wrapper(struct ef4_nic *efx); + +static void falcon_prepare_flush(struct ef4_nic *efx) +{ + falcon_deconfigure_mac_wrapper(efx); + + /* Wait for the tx and rx fifo's to get to the next packet boundary + * (~1ms without back-pressure), then to drain the remainder of the + * fifo's at data path speeds (negligible), with a healthy margin. */ + msleep(10); +} + +/* Acknowledge a legacy interrupt from Falcon + * + * This acknowledges a legacy (not MSI) interrupt via INT_ACK_KER_REG. + * + * Due to SFC bug 3706 (silicon revision <=A1) reads can be duplicated in the + * BIU. Interrupt acknowledge is read sensitive so must write instead + * (then read to ensure the BIU collector is flushed) + * + * NB most hardware supports MSI interrupts + */ +static inline void falcon_irq_ack_a1(struct ef4_nic *efx) +{ + ef4_dword_t reg; + + EF4_POPULATE_DWORD_1(reg, FRF_AA_INT_ACK_KER_FIELD, 0xb7eb7e); + ef4_writed(efx, ®, FR_AA_INT_ACK_KER); + ef4_readd(efx, ®, FR_AA_WORK_AROUND_BROKEN_PCI_READS); +} + +static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +{ + struct ef4_nic *efx = dev_id; + ef4_oword_t *int_ker = efx->irq_status.addr; + int syserr; + int queues; + + /* Check to see if this is our interrupt. If it isn't, we + * exit without having touched the hardware. + */ + if (unlikely(EF4_OWORD_IS_ZERO(*int_ker))) { + netif_vdbg(efx, intr, efx->net_dev, + "IRQ %d on CPU %d not for me\n", irq, + raw_smp_processor_id()); + return IRQ_NONE; + } + efx->last_irq_cpu = raw_smp_processor_id(); + netif_vdbg(efx, intr, efx->net_dev, + "IRQ %d on CPU %d status " EF4_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EF4_OWORD_VAL(*int_ker)); + + if (!likely(ACCESS_ONCE(efx->irq_soft_enabled))) + return IRQ_HANDLED; + + /* Check to see if we have a serious error condition */ + syserr = EF4_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return ef4_farch_fatal_interrupt(efx); + + /* Determine interrupting queues, clear interrupt status + * register and acknowledge the device interrupt. + */ + BUILD_BUG_ON(FSF_AZ_NET_IVEC_INT_Q_WIDTH > EF4_MAX_CHANNELS); + queues = EF4_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_INT_Q); + EF4_ZERO_OWORD(*int_ker); + wmb(); /* Ensure the vector is cleared before interrupt ack */ + falcon_irq_ack_a1(efx); + + if (queues & 1) + ef4_schedule_channel_irq(ef4_get_channel(efx, 0)); + if (queues & 2) + ef4_schedule_channel_irq(ef4_get_channel(efx, 1)); + return IRQ_HANDLED; +} + +/************************************************************************** + * + * RSS + * + ************************************************************************** + */ +static int dummy_rx_push_rss_config(struct ef4_nic *efx, bool user, + const u32 *rx_indir_table) +{ + (void) efx; + (void) user; + (void) rx_indir_table; + return -ENOSYS; +} + +static int falcon_b0_rx_push_rss_config(struct ef4_nic *efx, bool user, + const u32 *rx_indir_table) +{ + ef4_oword_t temp; + + (void) user; + /* Set hash key for IPv4 */ + memcpy(&temp, efx->rx_hash_key, sizeof(temp)); + ef4_writeo(efx, &temp, FR_BZ_RX_RSS_TKEY); + + memcpy(efx->rx_indir_table, rx_indir_table, + sizeof(efx->rx_indir_table)); + ef4_farch_rx_push_indir_table(efx); + return 0; +} + +/************************************************************************** + * + * EEPROM/flash + * + ************************************************************************** + */ + +#define FALCON_SPI_MAX_LEN sizeof(ef4_oword_t) + +static int falcon_spi_poll(struct ef4_nic *efx) +{ + ef4_oword_t reg; + ef4_reado(efx, ®, FR_AB_EE_SPI_HCMD); + return EF4_OWORD_FIELD(reg, FRF_AB_EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0; +} + +/* Wait for SPI command completion */ +static int falcon_spi_wait(struct ef4_nic *efx) +{ + /* Most commands will finish quickly, so we start polling at + * very short intervals. Sometimes the command may have to + * wait for VPD or expansion ROM access outside of our + * control, so we allow up to 100 ms. */ + unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 10); + int i; + + for (i = 0; i < 10; i++) { + if (!falcon_spi_poll(efx)) + return 0; + udelay(10); + } + + for (;;) { + if (!falcon_spi_poll(efx)) + return 0; + if (time_after_eq(jiffies, timeout)) { + netif_err(efx, hw, efx->net_dev, + "timed out waiting for SPI\n"); + return -ETIMEDOUT; + } + schedule_timeout_uninterruptible(1); + } +} + +static int +falcon_spi_cmd(struct ef4_nic *efx, const struct falcon_spi_device *spi, + unsigned int command, int address, + const void *in, void *out, size_t len) +{ + bool addressed = (address >= 0); + bool reading = (out != NULL); + ef4_oword_t reg; + int rc; + + /* Input validation */ + if (len > FALCON_SPI_MAX_LEN) + return -EINVAL; + + /* Check that previous command is not still running */ + rc = falcon_spi_poll(efx); + if (rc) + return rc; + + /* Program address register, if we have an address */ + if (addressed) { + EF4_POPULATE_OWORD_1(reg, FRF_AB_EE_SPI_HADR_ADR, address); + ef4_writeo(efx, ®, FR_AB_EE_SPI_HADR); + } + + /* Program data register, if we have data */ + if (in != NULL) { + memcpy(®, in, len); + ef4_writeo(efx, ®, FR_AB_EE_SPI_HDATA); + } + + /* Issue read/write command */ + EF4_POPULATE_OWORD_7(reg, + FRF_AB_EE_SPI_HCMD_CMD_EN, 1, + FRF_AB_EE_SPI_HCMD_SF_SEL, spi->device_id, + FRF_AB_EE_SPI_HCMD_DABCNT, len, + FRF_AB_EE_SPI_HCMD_READ, reading, + FRF_AB_EE_SPI_HCMD_DUBCNT, 0, + FRF_AB_EE_SPI_HCMD_ADBCNT, + (addressed ? spi->addr_len : 0), + FRF_AB_EE_SPI_HCMD_ENC, command); + ef4_writeo(efx, ®, FR_AB_EE_SPI_HCMD); + + /* Wait for read/write to complete */ + rc = falcon_spi_wait(efx); + if (rc) + return rc; + + /* Read data */ + if (out != NULL) { + ef4_reado(efx, ®, FR_AB_EE_SPI_HDATA); + memcpy(out, ®, len); + } + + return 0; +} + +static inline u8 +falcon_spi_munge_command(const struct falcon_spi_device *spi, + const u8 command, const unsigned int address) +{ + return command | (((address >> 8) & spi->munge_address) << 3); +} + +static int +falcon_spi_read(struct ef4_nic *efx, const struct falcon_spi_device *spi, + loff_t start, size_t len, size_t *retlen, u8 *buffer) +{ + size_t block_len, pos = 0; + unsigned int command; + int rc = 0; + + while (pos < len) { + block_len = min(len - pos, FALCON_SPI_MAX_LEN); + + command = falcon_spi_munge_command(spi, SPI_READ, start + pos); + rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, + buffer + pos, block_len); + if (rc) + break; + pos += block_len; + + /* Avoid locking up the system */ + cond_resched(); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + } + + if (retlen) + *retlen = pos; + return rc; +} + +#ifdef CONFIG_SFC_FALCON_MTD + +struct falcon_mtd_partition { + struct ef4_mtd_partition common; + const struct falcon_spi_device *spi; + size_t offset; +}; + +#define to_falcon_mtd_partition(mtd) \ + container_of(mtd, struct falcon_mtd_partition, common.mtd) + +static size_t +falcon_spi_write_limit(const struct falcon_spi_device *spi, size_t start) +{ + return min(FALCON_SPI_MAX_LEN, + (spi->block_size - (start & (spi->block_size - 1)))); +} + +/* Wait up to 10 ms for buffered write completion */ +static int +falcon_spi_wait_write(struct ef4_nic *efx, const struct falcon_spi_device *spi) +{ + unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100); + u8 status; + int rc; + + for (;;) { + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); + if (rc) + return rc; + if (!(status & SPI_STATUS_NRDY)) + return 0; + if (time_after_eq(jiffies, timeout)) { + netif_err(efx, hw, efx->net_dev, + "SPI write timeout on device %d" + " last status=0x%02x\n", + spi->device_id, status); + return -ETIMEDOUT; + } + schedule_timeout_uninterruptible(1); + } +} + +static int +falcon_spi_write(struct ef4_nic *efx, const struct falcon_spi_device *spi, + loff_t start, size_t len, size_t *retlen, const u8 *buffer) +{ + u8 verify_buffer[FALCON_SPI_MAX_LEN]; + size_t block_len, pos = 0; + unsigned int command; + int rc = 0; + + while (pos < len) { + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); + if (rc) + break; + + block_len = min(len - pos, + falcon_spi_write_limit(spi, start + pos)); + command = falcon_spi_munge_command(spi, SPI_WRITE, start + pos); + rc = falcon_spi_cmd(efx, spi, command, start + pos, + buffer + pos, NULL, block_len); + if (rc) + break; + + rc = falcon_spi_wait_write(efx, spi); + if (rc) + break; + + command = falcon_spi_munge_command(spi, SPI_READ, start + pos); + rc = falcon_spi_cmd(efx, spi, command, start + pos, + NULL, verify_buffer, block_len); + if (memcmp(verify_buffer, buffer + pos, block_len)) { + rc = -EIO; + break; + } + + pos += block_len; + + /* Avoid locking up the system */ + cond_resched(); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + } + + if (retlen) + *retlen = pos; + return rc; +} + +static int +falcon_spi_slow_wait(struct falcon_mtd_partition *part, bool uninterruptible) +{ + const struct falcon_spi_device *spi = part->spi; + struct ef4_nic *efx = part->common.mtd.priv; + u8 status; + int rc, i; + + /* Wait up to 4s for flash/EEPROM to finish a slow operation. */ + for (i = 0; i < 40; i++) { + __set_current_state(uninterruptible ? + TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); + if (rc) + return rc; + if (!(status & SPI_STATUS_NRDY)) + return 0; + if (signal_pending(current)) + return -EINTR; + } + pr_err("%s: timed out waiting for %s\n", + part->common.name, part->common.dev_type_name); + return -ETIMEDOUT; +} + +static int +falcon_spi_unlock(struct ef4_nic *efx, const struct falcon_spi_device *spi) +{ + const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | + SPI_STATUS_BP0); + u8 status; + int rc; + + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); + if (rc) + return rc; + + if (!(status & unlock_mask)) + return 0; /* already unlocked */ + + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); + if (rc) + return rc; + rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0); + if (rc) + return rc; + + status &= ~unlock_mask; + rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status, + NULL, sizeof(status)); + if (rc) + return rc; + rc = falcon_spi_wait_write(efx, spi); + if (rc) + return rc; + + return 0; +} + +#define FALCON_SPI_VERIFY_BUF_LEN 16 + +static int +falcon_spi_erase(struct falcon_mtd_partition *part, loff_t start, size_t len) +{ + const struct falcon_spi_device *spi = part->spi; + struct ef4_nic *efx = part->common.mtd.priv; + unsigned pos, block_len; + u8 empty[FALCON_SPI_VERIFY_BUF_LEN]; + u8 buffer[FALCON_SPI_VERIFY_BUF_LEN]; + int rc; + + if (len != spi->erase_size) + return -EINVAL; + + if (spi->erase_command == 0) + return -EOPNOTSUPP; + + rc = falcon_spi_unlock(efx, spi); + if (rc) + return rc; + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); + if (rc) + return rc; + rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL, + NULL, 0); + if (rc) + return rc; + rc = falcon_spi_slow_wait(part, false); + + /* Verify the entire region has been wiped */ + memset(empty, 0xff, sizeof(empty)); + for (pos = 0; pos < len; pos += block_len) { + block_len = min(len - pos, sizeof(buffer)); + rc = falcon_spi_read(efx, spi, start + pos, block_len, + NULL, buffer); + if (rc) + return rc; + if (memcmp(empty, buffer, block_len)) + return -EIO; + + /* Avoid locking up the system */ + cond_resched(); + if (signal_pending(current)) + return -EINTR; + } + + return rc; +} + +static void falcon_mtd_rename(struct ef4_mtd_partition *part) +{ + struct ef4_nic *efx = part->mtd.priv; + + snprintf(part->name, sizeof(part->name), "%s %s", + efx->name, part->type_name); +} + +static int falcon_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) +{ + struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); + struct ef4_nic *efx = mtd->priv; + struct falcon_nic_data *nic_data = efx->nic_data; + int rc; + + rc = mutex_lock_interruptible(&nic_data->spi_lock); + if (rc) + return rc; + rc = falcon_spi_read(efx, part->spi, part->offset + start, + len, retlen, buffer); + mutex_unlock(&nic_data->spi_lock); + return rc; +} + +static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) +{ + struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); + struct ef4_nic *efx = mtd->priv; + struct falcon_nic_data *nic_data = efx->nic_data; + int rc; + + rc = mutex_lock_interruptible(&nic_data->spi_lock); + if (rc) + return rc; + rc = falcon_spi_erase(part, part->offset + start, len); + mutex_unlock(&nic_data->spi_lock); + return rc; +} + +static int falcon_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) +{ + struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); + struct ef4_nic *efx = mtd->priv; + struct falcon_nic_data *nic_data = efx->nic_data; + int rc; + + rc = mutex_lock_interruptible(&nic_data->spi_lock); + if (rc) + return rc; + rc = falcon_spi_write(efx, part->spi, part->offset + start, + len, retlen, buffer); + mutex_unlock(&nic_data->spi_lock); + return rc; +} + +static int falcon_mtd_sync(struct mtd_info *mtd) +{ + struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd); + struct ef4_nic *efx = mtd->priv; + struct falcon_nic_data *nic_data = efx->nic_data; + int rc; + + mutex_lock(&nic_data->spi_lock); + rc = falcon_spi_slow_wait(part, true); + mutex_unlock(&nic_data->spi_lock); + return rc; +} + +static int falcon_mtd_probe(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_mtd_partition *parts; + struct falcon_spi_device *spi; + size_t n_parts; + int rc = -ENODEV; + + ASSERT_RTNL(); + + /* Allocate space for maximum number of partitions */ + parts = kcalloc(2, sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + n_parts = 0; + + spi = &nic_data->spi_flash; + if (falcon_spi_present(spi) && spi->size > FALCON_FLASH_BOOTCODE_START) { + parts[n_parts].spi = spi; + parts[n_parts].offset = FALCON_FLASH_BOOTCODE_START; + parts[n_parts].common.dev_type_name = "flash"; + parts[n_parts].common.type_name = "sfc_flash_bootrom"; + parts[n_parts].common.mtd.type = MTD_NORFLASH; + parts[n_parts].common.mtd.flags = MTD_CAP_NORFLASH; + parts[n_parts].common.mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; + parts[n_parts].common.mtd.erasesize = spi->erase_size; + n_parts++; + } + + spi = &nic_data->spi_eeprom; + if (falcon_spi_present(spi) && spi->size > FALCON_EEPROM_BOOTCONFIG_START) { + parts[n_parts].spi = spi; + parts[n_parts].offset = FALCON_EEPROM_BOOTCONFIG_START; + parts[n_parts].common.dev_type_name = "EEPROM"; + parts[n_parts].common.type_name = "sfc_bootconfig"; + parts[n_parts].common.mtd.type = MTD_RAM; + parts[n_parts].common.mtd.flags = MTD_CAP_RAM; + parts[n_parts].common.mtd.size = + min(spi->size, FALCON_EEPROM_BOOTCONFIG_END) - + FALCON_EEPROM_BOOTCONFIG_START; + parts[n_parts].common.mtd.erasesize = spi->erase_size; + n_parts++; + } + + rc = ef4_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts)); + if (rc) + kfree(parts); + return rc; +} + +#endif /* CONFIG_SFC_FALCON_MTD */ + +/************************************************************************** + * + * XMAC operations + * + ************************************************************************** + */ + +/* Configure the XAUI driver that is an output from Falcon */ +static void falcon_setup_xaui(struct ef4_nic *efx) +{ + ef4_oword_t sdctl, txdrv; + + /* Move the XAUI into low power, unless there is no PHY, in + * which case the XAUI will have to drive a cable. */ + if (efx->phy_type == PHY_TYPE_NONE) + return; + + ef4_reado(efx, &sdctl, FR_AB_XX_SD_CTL); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVD, FFE_AB_XX_SD_CTL_DRV_DEF); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVD, FFE_AB_XX_SD_CTL_DRV_DEF); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVC, FFE_AB_XX_SD_CTL_DRV_DEF); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVC, FFE_AB_XX_SD_CTL_DRV_DEF); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVB, FFE_AB_XX_SD_CTL_DRV_DEF); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVB, FFE_AB_XX_SD_CTL_DRV_DEF); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVA, FFE_AB_XX_SD_CTL_DRV_DEF); + EF4_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVA, FFE_AB_XX_SD_CTL_DRV_DEF); + ef4_writeo(efx, &sdctl, FR_AB_XX_SD_CTL); + + EF4_POPULATE_OWORD_8(txdrv, + FRF_AB_XX_DEQD, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQC, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQB, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQA, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DTXD, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXC, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXB, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXA, FFE_AB_XX_TXDRV_DTX_DEF); + ef4_writeo(efx, &txdrv, FR_AB_XX_TXDRV_CTL); +} + +int falcon_reset_xaui(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t reg; + int count; + + /* Don't fetch MAC statistics over an XMAC reset */ + WARN_ON(nic_data->stats_disable_count == 0); + + /* Start reset sequence */ + EF4_POPULATE_OWORD_1(reg, FRF_AB_XX_RST_XX_EN, 1); + ef4_writeo(efx, ®, FR_AB_XX_PWR_RST); + + /* Wait up to 10 ms for completion, then reinitialise */ + for (count = 0; count < 1000; count++) { + ef4_reado(efx, ®, FR_AB_XX_PWR_RST); + if (EF4_OWORD_FIELD(reg, FRF_AB_XX_RST_XX_EN) == 0 && + EF4_OWORD_FIELD(reg, FRF_AB_XX_SD_RST_ACT) == 0) { + falcon_setup_xaui(efx); + return 0; + } + udelay(10); + } + netif_err(efx, hw, efx->net_dev, + "timed out waiting for XAUI/XGXS reset\n"); + return -ETIMEDOUT; +} + +static void falcon_ack_status_intr(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t reg; + + if ((ef4_nic_rev(efx) != EF4_REV_FALCON_B0) || LOOPBACK_INTERNAL(efx)) + return; + + /* We expect xgmii faults if the wireside link is down */ + if (!efx->link_state.up) + return; + + /* We can only use this interrupt to signal the negative edge of + * xaui_align [we have to poll the positive edge]. */ + if (nic_data->xmac_poll_required) + return; + + ef4_reado(efx, ®, FR_AB_XM_MGT_INT_MSK); +} + +static bool falcon_xgxs_link_ok(struct ef4_nic *efx) +{ + ef4_oword_t reg; + bool align_done, link_ok = false; + int sync_status; + + /* Read link status */ + ef4_reado(efx, ®, FR_AB_XX_CORE_STAT); + + align_done = EF4_OWORD_FIELD(reg, FRF_AB_XX_ALIGN_DONE); + sync_status = EF4_OWORD_FIELD(reg, FRF_AB_XX_SYNC_STAT); + if (align_done && (sync_status == FFE_AB_XX_STAT_ALL_LANES)) + link_ok = true; + + /* Clear link status ready for next read */ + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_COMMA_DET, FFE_AB_XX_STAT_ALL_LANES); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_CHAR_ERR, FFE_AB_XX_STAT_ALL_LANES); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_DISPERR, FFE_AB_XX_STAT_ALL_LANES); + ef4_writeo(efx, ®, FR_AB_XX_CORE_STAT); + + return link_ok; +} + +static bool falcon_xmac_link_ok(struct ef4_nic *efx) +{ + /* + * Check MAC's XGXS link status except when using XGMII loopback + * which bypasses the XGXS block. + * If possible, check PHY's XGXS link status except when using + * MAC loopback. + */ + return (efx->loopback_mode == LOOPBACK_XGMII || + falcon_xgxs_link_ok(efx)) && + (!(efx->mdio.mmds & (1 << MDIO_MMD_PHYXS)) || + LOOPBACK_INTERNAL(efx) || + ef4_mdio_phyxgxs_lane_sync(efx)); +} + +static void falcon_reconfigure_xmac_core(struct ef4_nic *efx) +{ + unsigned int max_frame_len; + ef4_oword_t reg; + bool rx_fc = !!(efx->link_state.fc & EF4_FC_RX); + bool tx_fc = !!(efx->link_state.fc & EF4_FC_TX); + + /* Configure MAC - cut-thru mode is hard wired on */ + EF4_POPULATE_OWORD_3(reg, + FRF_AB_XM_RX_JUMBO_MODE, 1, + FRF_AB_XM_TX_STAT_EN, 1, + FRF_AB_XM_RX_STAT_EN, 1); + ef4_writeo(efx, ®, FR_AB_XM_GLB_CFG); + + /* Configure TX */ + EF4_POPULATE_OWORD_6(reg, + FRF_AB_XM_TXEN, 1, + FRF_AB_XM_TX_PRMBL, 1, + FRF_AB_XM_AUTO_PAD, 1, + FRF_AB_XM_TXCRC, 1, + FRF_AB_XM_FCNTL, tx_fc, + FRF_AB_XM_IPG, 0x3); + ef4_writeo(efx, ®, FR_AB_XM_TX_CFG); + + /* Configure RX */ + EF4_POPULATE_OWORD_5(reg, + FRF_AB_XM_RXEN, 1, + FRF_AB_XM_AUTO_DEPAD, 0, + FRF_AB_XM_ACPT_ALL_MCAST, 1, + FRF_AB_XM_ACPT_ALL_UCAST, !efx->unicast_filter, + FRF_AB_XM_PASS_CRC_ERR, 1); + ef4_writeo(efx, ®, FR_AB_XM_RX_CFG); + + /* Set frame length */ + max_frame_len = EF4_MAX_FRAME_LEN(efx->net_dev->mtu); + EF4_POPULATE_OWORD_1(reg, FRF_AB_XM_MAX_RX_FRM_SIZE, max_frame_len); + ef4_writeo(efx, ®, FR_AB_XM_RX_PARAM); + EF4_POPULATE_OWORD_2(reg, + FRF_AB_XM_MAX_TX_FRM_SIZE, max_frame_len, + FRF_AB_XM_TX_JUMBO_MODE, 1); + ef4_writeo(efx, ®, FR_AB_XM_TX_PARAM); + + EF4_POPULATE_OWORD_2(reg, + FRF_AB_XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ + FRF_AB_XM_DIS_FCNTL, !rx_fc); + ef4_writeo(efx, ®, FR_AB_XM_FC); + + /* Set MAC address */ + memcpy(®, &efx->net_dev->dev_addr[0], 4); + ef4_writeo(efx, ®, FR_AB_XM_ADR_LO); + memcpy(®, &efx->net_dev->dev_addr[4], 2); + ef4_writeo(efx, ®, FR_AB_XM_ADR_HI); +} + +static void falcon_reconfigure_xgxs_core(struct ef4_nic *efx) +{ + ef4_oword_t reg; + bool xgxs_loopback = (efx->loopback_mode == LOOPBACK_XGXS); + bool xaui_loopback = (efx->loopback_mode == LOOPBACK_XAUI); + bool xgmii_loopback = (efx->loopback_mode == LOOPBACK_XGMII); + bool old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback; + + /* XGXS block is flaky and will need to be reset if moving + * into our out of XGMII, XGXS or XAUI loopbacks. */ + ef4_reado(efx, ®, FR_AB_XX_CORE_STAT); + old_xgxs_loopback = EF4_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN); + old_xgmii_loopback = EF4_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN); + + ef4_reado(efx, ®, FR_AB_XX_SD_CTL); + old_xaui_loopback = EF4_OWORD_FIELD(reg, FRF_AB_XX_LPBKA); + + /* The PHY driver may have turned XAUI off */ + if ((xgxs_loopback != old_xgxs_loopback) || + (xaui_loopback != old_xaui_loopback) || + (xgmii_loopback != old_xgmii_loopback)) + falcon_reset_xaui(efx); + + ef4_reado(efx, ®, FR_AB_XX_CORE_STAT); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_FORCE_SIG, + (xgxs_loopback || xaui_loopback) ? + FFE_AB_XX_FORCE_SIG_ALL_LANES : 0); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN, xgxs_loopback); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN, xgmii_loopback); + ef4_writeo(efx, ®, FR_AB_XX_CORE_STAT); + + ef4_reado(efx, ®, FR_AB_XX_SD_CTL); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKD, xaui_loopback); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKC, xaui_loopback); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKB, xaui_loopback); + EF4_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKA, xaui_loopback); + ef4_writeo(efx, ®, FR_AB_XX_SD_CTL); +} + + +/* Try to bring up the Falcon side of the Falcon-Phy XAUI link */ +static bool falcon_xmac_link_ok_retry(struct ef4_nic *efx, int tries) +{ + bool mac_up = falcon_xmac_link_ok(efx); + + if (LOOPBACK_MASK(efx) & LOOPBACKS_EXTERNAL(efx) & LOOPBACKS_WS || + ef4_phy_mode_disabled(efx->phy_mode)) + /* XAUI link is expected to be down */ + return mac_up; + + falcon_stop_nic_stats(efx); + + while (!mac_up && tries) { + netif_dbg(efx, hw, efx->net_dev, "bashing xaui\n"); + falcon_reset_xaui(efx); + udelay(200); + + mac_up = falcon_xmac_link_ok(efx); + --tries; + } + + falcon_start_nic_stats(efx); + + return mac_up; +} + +static bool falcon_xmac_check_fault(struct ef4_nic *efx) +{ + return !falcon_xmac_link_ok_retry(efx, 5); +} + +static int falcon_reconfigure_xmac(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + ef4_farch_filter_sync_rx_mode(efx); + + falcon_reconfigure_xgxs_core(efx); + falcon_reconfigure_xmac_core(efx); + + falcon_reconfigure_mac_wrapper(efx); + + nic_data->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 5); + falcon_ack_status_intr(efx); + + return 0; +} + +static void falcon_poll_xmac(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + /* We expect xgmii faults if the wireside link is down */ + if (!efx->link_state.up || !nic_data->xmac_poll_required) + return; + + nic_data->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 1); + falcon_ack_status_intr(efx); +} + +/************************************************************************** + * + * MAC wrapper + * + ************************************************************************** + */ + +static void falcon_push_multicast_hash(struct ef4_nic *efx) +{ + union ef4_multicast_hash *mc_hash = &efx->multicast_hash; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + ef4_writeo(efx, &mc_hash->oword[0], FR_AB_MAC_MC_HASH_REG0); + ef4_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1); +} + +static void falcon_reset_macs(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t reg, mac_ctrl; + int count; + + if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0) { + /* It's not safe to use GLB_CTL_REG to reset the + * macs, so instead use the internal MAC resets + */ + EF4_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1); + ef4_writeo(efx, ®, FR_AB_XM_GLB_CFG); + + for (count = 0; count < 10000; count++) { + ef4_reado(efx, ®, FR_AB_XM_GLB_CFG); + if (EF4_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) == + 0) + return; + udelay(10); + } + + netif_err(efx, hw, efx->net_dev, + "timed out waiting for XMAC core reset\n"); + } + + /* Mac stats will fail whist the TX fifo is draining */ + WARN_ON(nic_data->stats_disable_count == 0); + + ef4_reado(efx, &mac_ctrl, FR_AB_MAC_CTRL); + EF4_SET_OWORD_FIELD(mac_ctrl, FRF_BB_TXFIFO_DRAIN_EN, 1); + ef4_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); + + ef4_reado(efx, ®, FR_AB_GLB_CTL); + EF4_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1); + EF4_SET_OWORD_FIELD(reg, FRF_AB_RST_XGRX, 1); + EF4_SET_OWORD_FIELD(reg, FRF_AB_RST_EM, 1); + ef4_writeo(efx, ®, FR_AB_GLB_CTL); + + count = 0; + while (1) { + ef4_reado(efx, ®, FR_AB_GLB_CTL); + if (!EF4_OWORD_FIELD(reg, FRF_AB_RST_XGTX) && + !EF4_OWORD_FIELD(reg, FRF_AB_RST_XGRX) && + !EF4_OWORD_FIELD(reg, FRF_AB_RST_EM)) { + netif_dbg(efx, hw, efx->net_dev, + "Completed MAC reset after %d loops\n", + count); + break; + } + if (count > 20) { + netif_err(efx, hw, efx->net_dev, "MAC reset failed\n"); + break; + } + count++; + udelay(10); + } + + /* Ensure the correct MAC is selected before statistics + * are re-enabled by the caller */ + ef4_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); + + falcon_setup_xaui(efx); +} + +static void falcon_drain_tx_fifo(struct ef4_nic *efx) +{ + ef4_oword_t reg; + + if ((ef4_nic_rev(efx) < EF4_REV_FALCON_B0) || + (efx->loopback_mode != LOOPBACK_NONE)) + return; + + ef4_reado(efx, ®, FR_AB_MAC_CTRL); + /* There is no point in draining more than once */ + if (EF4_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN)) + return; + + falcon_reset_macs(efx); +} + +static void falcon_deconfigure_mac_wrapper(struct ef4_nic *efx) +{ + ef4_oword_t reg; + + if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0) + return; + + /* Isolate the MAC -> RX */ + ef4_reado(efx, ®, FR_AZ_RX_CFG); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0); + ef4_writeo(efx, ®, FR_AZ_RX_CFG); + + /* Isolate TX -> MAC */ + falcon_drain_tx_fifo(efx); +} + +static void falcon_reconfigure_mac_wrapper(struct ef4_nic *efx) +{ + struct ef4_link_state *link_state = &efx->link_state; + ef4_oword_t reg; + int link_speed, isolate; + + isolate = !!ACCESS_ONCE(efx->reset_pending); + + switch (link_state->speed) { + case 10000: link_speed = 3; break; + case 1000: link_speed = 2; break; + case 100: link_speed = 1; break; + default: link_speed = 0; break; + } + + /* MAC_LINK_STATUS controls MAC backpressure but doesn't work + * as advertised. Disable to ensure packets are not + * indefinitely held and TX queue can be flushed at any point + * while the link is down. */ + EF4_POPULATE_OWORD_5(reg, + FRF_AB_MAC_XOFF_VAL, 0xffff /* max pause time */, + FRF_AB_MAC_BCAD_ACPT, 1, + FRF_AB_MAC_UC_PROM, !efx->unicast_filter, + FRF_AB_MAC_LINK_STATUS, 1, /* always set */ + FRF_AB_MAC_SPEED, link_speed); + /* On B0, MAC backpressure can be disabled and packets get + * discarded. */ + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) { + EF4_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, + !link_state->up || isolate); + } + + ef4_writeo(efx, ®, FR_AB_MAC_CTRL); + + /* Restore the multicast hash registers. */ + falcon_push_multicast_hash(efx); + + ef4_reado(efx, ®, FR_AZ_RX_CFG); + /* Enable XOFF signal from RX FIFO (we enabled it during NIC + * initialisation but it may read back as 0) */ + EF4_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); + /* Unisolate the MAC -> RX */ + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, !isolate); + ef4_writeo(efx, ®, FR_AZ_RX_CFG); +} + +static void falcon_stats_request(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t reg; + + WARN_ON(nic_data->stats_pending); + WARN_ON(nic_data->stats_disable_count); + + FALCON_XMAC_STATS_DMA_FLAG(efx) = 0; + nic_data->stats_pending = true; + wmb(); /* ensure done flag is clear */ + + /* Initiate DMA transfer of stats */ + EF4_POPULATE_OWORD_2(reg, + FRF_AB_MAC_STAT_DMA_CMD, 1, + FRF_AB_MAC_STAT_DMA_ADR, + efx->stats_buffer.dma_addr); + ef4_writeo(efx, ®, FR_AB_MAC_STAT_DMA); + + mod_timer(&nic_data->stats_timer, round_jiffies_up(jiffies + HZ / 2)); +} + +static void falcon_stats_complete(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + if (!nic_data->stats_pending) + return; + + nic_data->stats_pending = false; + if (FALCON_XMAC_STATS_DMA_FLAG(efx)) { + rmb(); /* read the done flag before the stats */ + ef4_nic_update_stats(falcon_stat_desc, FALCON_STAT_COUNT, + falcon_stat_mask, nic_data->stats, + efx->stats_buffer.addr, true); + } else { + netif_err(efx, hw, efx->net_dev, + "timed out waiting for statistics\n"); + } +} + +static void falcon_stats_timer_func(unsigned long context) +{ + struct ef4_nic *efx = (struct ef4_nic *)context; + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock(&efx->stats_lock); + + falcon_stats_complete(efx); + if (nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + + spin_unlock(&efx->stats_lock); +} + +static bool falcon_loopback_link_poll(struct ef4_nic *efx) +{ + struct ef4_link_state old_state = efx->link_state; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + WARN_ON(!LOOPBACK_INTERNAL(efx)); + + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + efx->link_state.up = true; + efx->link_state.speed = 10000; + + return !ef4_link_state_equal(&efx->link_state, &old_state); +} + +static int falcon_reconfigure_port(struct ef4_nic *efx) +{ + int rc; + + WARN_ON(ef4_nic_rev(efx) > EF4_REV_FALCON_B0); + + /* Poll the PHY link state *before* reconfiguring it. This means we + * will pick up the correct speed (in loopback) to select the correct + * MAC. + */ + if (LOOPBACK_INTERNAL(efx)) + falcon_loopback_link_poll(efx); + else + efx->phy_op->poll(efx); + + falcon_stop_nic_stats(efx); + falcon_deconfigure_mac_wrapper(efx); + + falcon_reset_macs(efx); + + efx->phy_op->reconfigure(efx); + rc = falcon_reconfigure_xmac(efx); + BUG_ON(rc); + + falcon_start_nic_stats(efx); + + /* Synchronise efx->link_state with the kernel */ + ef4_link_status_changed(efx); + + return 0; +} + +/* TX flow control may automatically turn itself off if the link + * partner (intermittently) stops responding to pause frames. There + * isn't any indication that this has happened, so the best we do is + * leave it up to the user to spot this and fix it by cycling transmit + * flow control on this end. + */ + +static void falcon_a1_prepare_enable_fc_tx(struct ef4_nic *efx) +{ + /* Schedule a reset to recover */ + ef4_schedule_reset(efx, RESET_TYPE_INVISIBLE); +} + +static void falcon_b0_prepare_enable_fc_tx(struct ef4_nic *efx) +{ + /* Recover by resetting the EM block */ + falcon_stop_nic_stats(efx); + falcon_drain_tx_fifo(efx); + falcon_reconfigure_xmac(efx); + falcon_start_nic_stats(efx); +} + +/************************************************************************** + * + * PHY access via GMII + * + ************************************************************************** + */ + +/* Wait for GMII access to complete */ +static int falcon_gmii_wait(struct ef4_nic *efx) +{ + ef4_oword_t md_stat; + int count; + + /* wait up to 50ms - taken max from datasheet */ + for (count = 0; count < 5000; count++) { + ef4_reado(efx, &md_stat, FR_AB_MD_STAT); + if (EF4_OWORD_FIELD(md_stat, FRF_AB_MD_BSY) == 0) { + if (EF4_OWORD_FIELD(md_stat, FRF_AB_MD_LNFL) != 0 || + EF4_OWORD_FIELD(md_stat, FRF_AB_MD_BSERR) != 0) { + netif_err(efx, hw, efx->net_dev, + "error from GMII access " + EF4_OWORD_FMT"\n", + EF4_OWORD_VAL(md_stat)); + return -EIO; + } + return 0; + } + udelay(10); + } + netif_err(efx, hw, efx->net_dev, "timed out waiting for GMII\n"); + return -ETIMEDOUT; +} + +/* Write an MDIO register of a PHY connected to Falcon. */ +static int falcon_mdio_write(struct net_device *net_dev, + int prtad, int devad, u16 addr, u16 value) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t reg; + int rc; + + netif_vdbg(efx, hw, efx->net_dev, + "writing MDIO %d register %d.%d with 0x%04x\n", + prtad, devad, addr, value); + + mutex_lock(&nic_data->mdio_lock); + + /* Check MDIO not currently being accessed */ + rc = falcon_gmii_wait(efx); + if (rc) + goto out; + + /* Write the address/ID register */ + EF4_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr); + ef4_writeo(efx, ®, FR_AB_MD_PHY_ADR); + + EF4_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, + FRF_AB_MD_DEV_ADR, devad); + ef4_writeo(efx, ®, FR_AB_MD_ID); + + /* Write data */ + EF4_POPULATE_OWORD_1(reg, FRF_AB_MD_TXD, value); + ef4_writeo(efx, ®, FR_AB_MD_TXD); + + EF4_POPULATE_OWORD_2(reg, + FRF_AB_MD_WRC, 1, + FRF_AB_MD_GC, 0); + ef4_writeo(efx, ®, FR_AB_MD_CS); + + /* Wait for data to be written */ + rc = falcon_gmii_wait(efx); + if (rc) { + /* Abort the write operation */ + EF4_POPULATE_OWORD_2(reg, + FRF_AB_MD_WRC, 0, + FRF_AB_MD_GC, 1); + ef4_writeo(efx, ®, FR_AB_MD_CS); + udelay(10); + } + +out: + mutex_unlock(&nic_data->mdio_lock); + return rc; +} + +/* Read an MDIO register of a PHY connected to Falcon. */ +static int falcon_mdio_read(struct net_device *net_dev, + int prtad, int devad, u16 addr) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t reg; + int rc; + + mutex_lock(&nic_data->mdio_lock); + + /* Check MDIO not currently being accessed */ + rc = falcon_gmii_wait(efx); + if (rc) + goto out; + + EF4_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr); + ef4_writeo(efx, ®, FR_AB_MD_PHY_ADR); + + EF4_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, + FRF_AB_MD_DEV_ADR, devad); + ef4_writeo(efx, ®, FR_AB_MD_ID); + + /* Request data to be read */ + EF4_POPULATE_OWORD_2(reg, FRF_AB_MD_RDC, 1, FRF_AB_MD_GC, 0); + ef4_writeo(efx, ®, FR_AB_MD_CS); + + /* Wait for data to become available */ + rc = falcon_gmii_wait(efx); + if (rc == 0) { + ef4_reado(efx, ®, FR_AB_MD_RXD); + rc = EF4_OWORD_FIELD(reg, FRF_AB_MD_RXD); + netif_vdbg(efx, hw, efx->net_dev, + "read from MDIO %d register %d.%d, got %04x\n", + prtad, devad, addr, rc); + } else { + /* Abort the read operation */ + EF4_POPULATE_OWORD_2(reg, + FRF_AB_MD_RIC, 0, + FRF_AB_MD_GC, 1); + ef4_writeo(efx, ®, FR_AB_MD_CS); + + netif_dbg(efx, hw, efx->net_dev, + "read from MDIO %d register %d.%d, got error %d\n", + prtad, devad, addr, rc); + } + +out: + mutex_unlock(&nic_data->mdio_lock); + return rc; +} + +/* This call is responsible for hooking in the MAC and PHY operations */ +static int falcon_probe_port(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + int rc; + + switch (efx->phy_type) { + case PHY_TYPE_SFX7101: + efx->phy_op = &falcon_sfx7101_phy_ops; + break; + case PHY_TYPE_QT2022C2: + case PHY_TYPE_QT2025C: + efx->phy_op = &falcon_qt202x_phy_ops; + break; + case PHY_TYPE_TXC43128: + efx->phy_op = &falcon_txc_phy_ops; + break; + default: + netif_err(efx, probe, efx->net_dev, "Unknown PHY type %d\n", + efx->phy_type); + return -ENODEV; + } + + /* Fill out MDIO structure and loopback modes */ + mutex_init(&nic_data->mdio_lock); + efx->mdio.mdio_read = falcon_mdio_read; + efx->mdio.mdio_write = falcon_mdio_write; + rc = efx->phy_op->probe(efx); + if (rc != 0) + return rc; + + /* Initial assumption */ + efx->link_state.speed = 10000; + efx->link_state.fd = true; + + /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) + efx->wanted_fc = EF4_FC_RX | EF4_FC_TX; + else + efx->wanted_fc = EF4_FC_RX; + if (efx->mdio.mmds & MDIO_DEVS_AN) + efx->wanted_fc |= EF4_FC_AUTO; + + /* Allocate buffer for stats */ + rc = ef4_nic_alloc_buffer(efx, &efx->stats_buffer, + FALCON_MAC_STATS_SIZE, GFP_KERNEL); + if (rc) + return rc; + netif_dbg(efx, probe, efx->net_dev, + "stats buffer at %llx (virt %p phys %llx)\n", + (u64)efx->stats_buffer.dma_addr, + efx->stats_buffer.addr, + (u64)virt_to_phys(efx->stats_buffer.addr)); + + return 0; +} + +static void falcon_remove_port(struct ef4_nic *efx) +{ + efx->phy_op->remove(efx); + ef4_nic_free_buffer(efx, &efx->stats_buffer); +} + +/* Global events are basically PHY events */ +static bool +falcon_handle_global_event(struct ef4_channel *channel, ef4_qword_t *event) +{ + struct ef4_nic *efx = channel->efx; + struct falcon_nic_data *nic_data = efx->nic_data; + + if (EF4_QWORD_FIELD(*event, FSF_AB_GLB_EV_G_PHY0_INTR) || + EF4_QWORD_FIELD(*event, FSF_AB_GLB_EV_XG_PHY0_INTR) || + EF4_QWORD_FIELD(*event, FSF_AB_GLB_EV_XFP_PHY0_INTR)) + /* Ignored */ + return true; + + if ((ef4_nic_rev(efx) == EF4_REV_FALCON_B0) && + EF4_QWORD_FIELD(*event, FSF_BB_GLB_EV_XG_MGT_INTR)) { + nic_data->xmac_poll_required = true; + return true; + } + + if (ef4_nic_rev(efx) <= EF4_REV_FALCON_A1 ? + EF4_QWORD_FIELD(*event, FSF_AA_GLB_EV_RX_RECOVERY) : + EF4_QWORD_FIELD(*event, FSF_BB_GLB_EV_RX_RECOVERY)) { + netif_err(efx, rx_err, efx->net_dev, + "channel %d seen global RX_RESET event. Resetting.\n", + channel->channel); + + atomic_inc(&efx->rx_reset); + ef4_schedule_reset(efx, EF4_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); + return true; + } + + return false; +} + +/************************************************************************** + * + * Falcon test code + * + **************************************************************************/ + +static int +falcon_read_nvram(struct ef4_nic *efx, struct falcon_nvconfig *nvconfig_out) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_nvconfig *nvconfig; + struct falcon_spi_device *spi; + void *region; + int rc, magic_num, struct_ver; + __le16 *word, *limit; + u32 csum; + + if (falcon_spi_present(&nic_data->spi_flash)) + spi = &nic_data->spi_flash; + else if (falcon_spi_present(&nic_data->spi_eeprom)) + spi = &nic_data->spi_eeprom; + else + return -EINVAL; + + region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL); + if (!region) + return -ENOMEM; + nvconfig = region + FALCON_NVCONFIG_OFFSET; + + mutex_lock(&nic_data->spi_lock); + rc = falcon_spi_read(efx, spi, 0, FALCON_NVCONFIG_END, NULL, region); + mutex_unlock(&nic_data->spi_lock); + if (rc) { + netif_err(efx, hw, efx->net_dev, "Failed to read %s\n", + falcon_spi_present(&nic_data->spi_flash) ? + "flash" : "EEPROM"); + rc = -EIO; + goto out; + } + + magic_num = le16_to_cpu(nvconfig->board_magic_num); + struct_ver = le16_to_cpu(nvconfig->board_struct_ver); + + rc = -EINVAL; + if (magic_num != FALCON_NVCONFIG_BOARD_MAGIC_NUM) { + netif_err(efx, hw, efx->net_dev, + "NVRAM bad magic 0x%x\n", magic_num); + goto out; + } + if (struct_ver < 2) { + netif_err(efx, hw, efx->net_dev, + "NVRAM has ancient version 0x%x\n", struct_ver); + goto out; + } else if (struct_ver < 4) { + word = &nvconfig->board_magic_num; + limit = (__le16 *) (nvconfig + 1); + } else { + word = region; + limit = region + FALCON_NVCONFIG_END; + } + for (csum = 0; word < limit; ++word) + csum += le16_to_cpu(*word); + + if (~csum & 0xffff) { + netif_err(efx, hw, efx->net_dev, + "NVRAM has incorrect checksum\n"); + goto out; + } + + rc = 0; + if (nvconfig_out) + memcpy(nvconfig_out, nvconfig, sizeof(*nvconfig)); + + out: + kfree(region); + return rc; +} + +static int falcon_test_nvram(struct ef4_nic *efx) +{ + return falcon_read_nvram(efx, NULL); +} + +static const struct ef4_farch_register_test falcon_b0_register_tests[] = { + { FR_AZ_ADR_REGION, + EF4_OWORD32(0x0003FFFF, 0x0003FFFF, 0x0003FFFF, 0x0003FFFF) }, + { FR_AZ_RX_CFG, + EF4_OWORD32(0xFFFFFFFE, 0x00017FFF, 0x00000000, 0x00000000) }, + { FR_AZ_TX_CFG, + EF4_OWORD32(0x7FFF0037, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_TX_RESERVED, + EF4_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, + { FR_AB_MAC_CTRL, + EF4_OWORD32(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_SRM_TX_DC_CFG, + EF4_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_CFG, + EF4_OWORD32(0x0000000F, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_PF_WM, + EF4_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_DP_CTRL, + EF4_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_GM_CFG2, + EF4_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_GMF_CFG0, + EF4_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_GLB_CFG, + EF4_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_TX_CFG, + EF4_OWORD32(0x00080164, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_RX_CFG, + EF4_OWORD32(0x07100A0C, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_RX_PARAM, + EF4_OWORD32(0x00001FF8, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_FC, + EF4_OWORD32(0xFFFF0001, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_ADR_LO, + EF4_OWORD32(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XX_SD_CTL, + EF4_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) }, +}; + +static int +falcon_b0_test_chip(struct ef4_nic *efx, struct ef4_self_tests *tests) +{ + enum reset_type reset_method = RESET_TYPE_INVISIBLE; + int rc, rc2; + + mutex_lock(&efx->mac_lock); + if (efx->loopback_modes) { + /* We need the 312 clock from the PHY to test the XMAC + * registers, so move into XGMII loopback if available */ + if (efx->loopback_modes & (1 << LOOPBACK_XGMII)) + efx->loopback_mode = LOOPBACK_XGMII; + else + efx->loopback_mode = __ffs(efx->loopback_modes); + } + __ef4_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + + ef4_reset_down(efx, reset_method); + + tests->registers = + ef4_farch_test_registers(efx, falcon_b0_register_tests, + ARRAY_SIZE(falcon_b0_register_tests)) + ? -1 : 1; + + rc = falcon_reset_hw(efx, reset_method); + rc2 = ef4_reset_up(efx, reset_method, rc == 0); + return rc ? rc : rc2; +} + +/************************************************************************** + * + * Device reset + * + ************************************************************************** + */ + +static enum reset_type falcon_map_reset_reason(enum reset_type reason) +{ + switch (reason) { + case RESET_TYPE_RX_RECOVERY: + case RESET_TYPE_DMA_ERROR: + case RESET_TYPE_TX_SKIP: + /* These can occasionally occur due to hardware bugs. + * We try to reset without disrupting the link. + */ + return RESET_TYPE_INVISIBLE; + default: + return RESET_TYPE_ALL; + } +} + +static int falcon_map_reset_flags(u32 *flags) +{ + enum { + FALCON_RESET_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER | + ETH_RESET_OFFLOAD | ETH_RESET_MAC), + FALCON_RESET_ALL = FALCON_RESET_INVISIBLE | ETH_RESET_PHY, + FALCON_RESET_WORLD = FALCON_RESET_ALL | ETH_RESET_IRQ, + }; + + if ((*flags & FALCON_RESET_WORLD) == FALCON_RESET_WORLD) { + *flags &= ~FALCON_RESET_WORLD; + return RESET_TYPE_WORLD; + } + + if ((*flags & FALCON_RESET_ALL) == FALCON_RESET_ALL) { + *flags &= ~FALCON_RESET_ALL; + return RESET_TYPE_ALL; + } + + if ((*flags & FALCON_RESET_INVISIBLE) == FALCON_RESET_INVISIBLE) { + *flags &= ~FALCON_RESET_INVISIBLE; + return RESET_TYPE_INVISIBLE; + } + + return -EINVAL; +} + +/* Resets NIC to known state. This routine must be called in process + * context and is allowed to sleep. */ +static int __falcon_reset_hw(struct ef4_nic *efx, enum reset_type method) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t glb_ctl_reg_ker; + int rc; + + netif_dbg(efx, hw, efx->net_dev, "performing %s hardware reset\n", + RESET_TYPE(method)); + + /* Initiate device reset */ + if (method == RESET_TYPE_WORLD) { + rc = pci_save_state(efx->pci_dev); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "failed to backup PCI state of primary " + "function prior to hardware reset\n"); + goto fail1; + } + if (ef4_nic_is_dual_func(efx)) { + rc = pci_save_state(nic_data->pci_dev2); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "failed to backup PCI state of " + "secondary function prior to " + "hardware reset\n"); + goto fail2; + } + } + + EF4_POPULATE_OWORD_2(glb_ctl_reg_ker, + FRF_AB_EXT_PHY_RST_DUR, + FFE_AB_EXT_PHY_RST_DUR_10240US, + FRF_AB_SWRST, 1); + } else { + EF4_POPULATE_OWORD_7(glb_ctl_reg_ker, + /* exclude PHY from "invisible" reset */ + FRF_AB_EXT_PHY_RST_CTL, + method == RESET_TYPE_INVISIBLE, + /* exclude EEPROM/flash and PCIe */ + FRF_AB_PCIE_CORE_RST_CTL, 1, + FRF_AB_PCIE_NSTKY_RST_CTL, 1, + FRF_AB_PCIE_SD_RST_CTL, 1, + FRF_AB_EE_RST_CTL, 1, + FRF_AB_EXT_PHY_RST_DUR, + FFE_AB_EXT_PHY_RST_DUR_10240US, + FRF_AB_SWRST, 1); + } + ef4_writeo(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); + + netif_dbg(efx, hw, efx->net_dev, "waiting for hardware reset\n"); + schedule_timeout_uninterruptible(HZ / 20); + + /* Restore PCI configuration if needed */ + if (method == RESET_TYPE_WORLD) { + if (ef4_nic_is_dual_func(efx)) + pci_restore_state(nic_data->pci_dev2); + pci_restore_state(efx->pci_dev); + netif_dbg(efx, drv, efx->net_dev, + "successfully restored PCI config\n"); + } + + /* Assert that reset complete */ + ef4_reado(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); + if (EF4_OWORD_FIELD(glb_ctl_reg_ker, FRF_AB_SWRST) != 0) { + rc = -ETIMEDOUT; + netif_err(efx, hw, efx->net_dev, + "timed out waiting for hardware reset\n"); + goto fail3; + } + netif_dbg(efx, hw, efx->net_dev, "hardware reset complete\n"); + + return 0; + + /* pci_save_state() and pci_restore_state() MUST be called in pairs */ +fail2: + pci_restore_state(efx->pci_dev); +fail1: +fail3: + return rc; +} + +static int falcon_reset_hw(struct ef4_nic *efx, enum reset_type method) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + int rc; + + mutex_lock(&nic_data->spi_lock); + rc = __falcon_reset_hw(efx, method); + mutex_unlock(&nic_data->spi_lock); + + return rc; +} + +static void falcon_monitor(struct ef4_nic *efx) +{ + bool link_changed; + int rc; + + BUG_ON(!mutex_is_locked(&efx->mac_lock)); + + rc = falcon_board(efx)->type->monitor(efx); + if (rc) { + netif_err(efx, hw, efx->net_dev, + "Board sensor %s; shutting down PHY\n", + (rc == -ERANGE) ? "reported fault" : "failed"); + efx->phy_mode |= PHY_MODE_LOW_POWER; + rc = __ef4_reconfigure_port(efx); + WARN_ON(rc); + } + + if (LOOPBACK_INTERNAL(efx)) + link_changed = falcon_loopback_link_poll(efx); + else + link_changed = efx->phy_op->poll(efx); + + if (link_changed) { + falcon_stop_nic_stats(efx); + falcon_deconfigure_mac_wrapper(efx); + + falcon_reset_macs(efx); + rc = falcon_reconfigure_xmac(efx); + BUG_ON(rc); + + falcon_start_nic_stats(efx); + + ef4_link_status_changed(efx); + } + + falcon_poll_xmac(efx); +} + +/* Zeroes out the SRAM contents. This routine must be called in + * process context and is allowed to sleep. + */ +static int falcon_reset_sram(struct ef4_nic *efx) +{ + ef4_oword_t srm_cfg_reg_ker, gpio_cfg_reg_ker; + int count; + + /* Set the SRAM wake/sleep GPIO appropriately. */ + ef4_reado(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL); + EF4_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OEN, 1); + EF4_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OUT, 1); + ef4_writeo(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL); + + /* Initiate SRAM reset */ + EF4_POPULATE_OWORD_2(srm_cfg_reg_ker, + FRF_AZ_SRM_INIT_EN, 1, + FRF_AZ_SRM_NB_SZ, 0); + ef4_writeo(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); + + /* Wait for SRAM reset to complete */ + count = 0; + do { + netif_dbg(efx, hw, efx->net_dev, + "waiting for SRAM reset (attempt %d)...\n", count); + + /* SRAM reset is slow; expect around 16ms */ + schedule_timeout_uninterruptible(HZ / 50); + + /* Check for reset complete */ + ef4_reado(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); + if (!EF4_OWORD_FIELD(srm_cfg_reg_ker, FRF_AZ_SRM_INIT_EN)) { + netif_dbg(efx, hw, efx->net_dev, + "SRAM reset complete\n"); + + return 0; + } + } while (++count < 20); /* wait up to 0.4 sec */ + + netif_err(efx, hw, efx->net_dev, "timed out waiting for SRAM reset\n"); + return -ETIMEDOUT; +} + +static void falcon_spi_device_init(struct ef4_nic *efx, + struct falcon_spi_device *spi_device, + unsigned int device_id, u32 device_type) +{ + if (device_type != 0) { + spi_device->device_id = device_id; + spi_device->size = + 1 << SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_SIZE); + spi_device->addr_len = + SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ADDR_LEN); + spi_device->munge_address = (spi_device->size == 1 << 9 && + spi_device->addr_len == 1); + spi_device->erase_command = + SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ERASE_CMD); + spi_device->erase_size = + 1 << SPI_DEV_TYPE_FIELD(device_type, + SPI_DEV_TYPE_ERASE_SIZE); + spi_device->block_size = + 1 << SPI_DEV_TYPE_FIELD(device_type, + SPI_DEV_TYPE_BLOCK_SIZE); + } else { + spi_device->size = 0; + } +} + +/* Extract non-volatile configuration */ +static int falcon_probe_nvconfig(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_nvconfig *nvconfig; + int rc; + + nvconfig = kmalloc(sizeof(*nvconfig), GFP_KERNEL); + if (!nvconfig) + return -ENOMEM; + + rc = falcon_read_nvram(efx, nvconfig); + if (rc) + goto out; + + efx->phy_type = nvconfig->board_v2.port0_phy_type; + efx->mdio.prtad = nvconfig->board_v2.port0_phy_addr; + + if (le16_to_cpu(nvconfig->board_struct_ver) >= 3) { + falcon_spi_device_init( + efx, &nic_data->spi_flash, FFE_AB_SPI_DEVICE_FLASH, + le32_to_cpu(nvconfig->board_v3 + .spi_device_type[FFE_AB_SPI_DEVICE_FLASH])); + falcon_spi_device_init( + efx, &nic_data->spi_eeprom, FFE_AB_SPI_DEVICE_EEPROM, + le32_to_cpu(nvconfig->board_v3 + .spi_device_type[FFE_AB_SPI_DEVICE_EEPROM])); + } + + /* Read the MAC addresses */ + ether_addr_copy(efx->net_dev->perm_addr, nvconfig->mac_address[0]); + + netif_dbg(efx, probe, efx->net_dev, "PHY is %d phy_id %d\n", + efx->phy_type, efx->mdio.prtad); + + rc = falcon_probe_board(efx, + le16_to_cpu(nvconfig->board_v2.board_revision)); +out: + kfree(nvconfig); + return rc; +} + +static int falcon_dimension_resources(struct ef4_nic *efx) +{ + efx->rx_dc_base = 0x20000; + efx->tx_dc_base = 0x26000; + return 0; +} + +/* Probe all SPI devices on the NIC */ +static void falcon_probe_spi_devices(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t nic_stat, gpio_ctl, ee_vpd_cfg; + int boot_dev; + + ef4_reado(efx, &gpio_ctl, FR_AB_GPIO_CTL); + ef4_reado(efx, &nic_stat, FR_AB_NIC_STAT); + ef4_reado(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); + + if (EF4_OWORD_FIELD(gpio_ctl, FRF_AB_GPIO3_PWRUP_VALUE)) { + boot_dev = (EF4_OWORD_FIELD(nic_stat, FRF_AB_SF_PRST) ? + FFE_AB_SPI_DEVICE_FLASH : FFE_AB_SPI_DEVICE_EEPROM); + netif_dbg(efx, probe, efx->net_dev, "Booted from %s\n", + boot_dev == FFE_AB_SPI_DEVICE_FLASH ? + "flash" : "EEPROM"); + } else { + /* Disable VPD and set clock dividers to safe + * values for initial programming. */ + boot_dev = -1; + netif_dbg(efx, probe, efx->net_dev, + "Booted from internal ASIC settings;" + " setting SPI config\n"); + EF4_POPULATE_OWORD_3(ee_vpd_cfg, FRF_AB_EE_VPD_EN, 0, + /* 125 MHz / 7 ~= 20 MHz */ + FRF_AB_EE_SF_CLOCK_DIV, 7, + /* 125 MHz / 63 ~= 2 MHz */ + FRF_AB_EE_EE_CLOCK_DIV, 63); + ef4_writeo(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); + } + + mutex_init(&nic_data->spi_lock); + + if (boot_dev == FFE_AB_SPI_DEVICE_FLASH) + falcon_spi_device_init(efx, &nic_data->spi_flash, + FFE_AB_SPI_DEVICE_FLASH, + default_flash_type); + if (boot_dev == FFE_AB_SPI_DEVICE_EEPROM) + falcon_spi_device_init(efx, &nic_data->spi_eeprom, + FFE_AB_SPI_DEVICE_EEPROM, + large_eeprom_type); +} + +static unsigned int falcon_a1_mem_map_size(struct ef4_nic *efx) +{ + return 0x20000; +} + +static unsigned int falcon_b0_mem_map_size(struct ef4_nic *efx) +{ + /* Map everything up to and including the RSS indirection table. + * The PCI core takes care of mapping the MSI-X tables. + */ + return FR_BZ_RX_INDIRECTION_TBL + + FR_BZ_RX_INDIRECTION_TBL_STEP * FR_BZ_RX_INDIRECTION_TBL_ROWS; +} + +static int falcon_probe_nic(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data; + struct falcon_board *board; + int rc; + + efx->primary = efx; /* only one usable function per controller */ + + /* Allocate storage for hardware specific data */ + nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL); + if (!nic_data) + return -ENOMEM; + efx->nic_data = nic_data; + + rc = -ENODEV; + + if (ef4_farch_fpga_ver(efx) != 0) { + netif_err(efx, probe, efx->net_dev, + "Falcon FPGA not supported\n"); + goto fail1; + } + + if (ef4_nic_rev(efx) <= EF4_REV_FALCON_A1) { + ef4_oword_t nic_stat; + struct pci_dev *dev; + u8 pci_rev = efx->pci_dev->revision; + + if ((pci_rev == 0xff) || (pci_rev == 0)) { + netif_err(efx, probe, efx->net_dev, + "Falcon rev A0 not supported\n"); + goto fail1; + } + ef4_reado(efx, &nic_stat, FR_AB_NIC_STAT); + if (EF4_OWORD_FIELD(nic_stat, FRF_AB_STRAP_10G) == 0) { + netif_err(efx, probe, efx->net_dev, + "Falcon rev A1 1G not supported\n"); + goto fail1; + } + if (EF4_OWORD_FIELD(nic_stat, FRF_AA_STRAP_PCIE) == 0) { + netif_err(efx, probe, efx->net_dev, + "Falcon rev A1 PCI-X not supported\n"); + goto fail1; + } + + dev = pci_dev_get(efx->pci_dev); + while ((dev = pci_get_device(PCI_VENDOR_ID_SOLARFLARE, + PCI_DEVICE_ID_SOLARFLARE_SFC4000A_1, + dev))) { + if (dev->bus == efx->pci_dev->bus && + dev->devfn == efx->pci_dev->devfn + 1) { + nic_data->pci_dev2 = dev; + break; + } + } + if (!nic_data->pci_dev2) { + netif_err(efx, probe, efx->net_dev, + "failed to find secondary function\n"); + rc = -ENODEV; + goto fail2; + } + } + + /* Now we can reset the NIC */ + rc = __falcon_reset_hw(efx, RESET_TYPE_ALL); + if (rc) { + netif_err(efx, probe, efx->net_dev, "failed to reset NIC\n"); + goto fail3; + } + + /* Allocate memory for INT_KER */ + rc = ef4_nic_alloc_buffer(efx, &efx->irq_status, sizeof(ef4_oword_t), + GFP_KERNEL); + if (rc) + goto fail4; + BUG_ON(efx->irq_status.dma_addr & 0x0f); + + netif_dbg(efx, probe, efx->net_dev, + "INT_KER at %llx (virt %p phys %llx)\n", + (u64)efx->irq_status.dma_addr, + efx->irq_status.addr, + (u64)virt_to_phys(efx->irq_status.addr)); + + falcon_probe_spi_devices(efx); + + /* Read in the non-volatile configuration */ + rc = falcon_probe_nvconfig(efx); + if (rc) { + if (rc == -EINVAL) + netif_err(efx, probe, efx->net_dev, "NVRAM is invalid\n"); + goto fail5; + } + + efx->max_channels = (ef4_nic_rev(efx) <= EF4_REV_FALCON_A1 ? 4 : + EF4_MAX_CHANNELS); + efx->max_tx_channels = efx->max_channels; + efx->timer_quantum_ns = 4968; /* 621 cycles */ + efx->timer_max_ns = efx->type->timer_period_max * + efx->timer_quantum_ns; + + /* Initialise I2C adapter */ + board = falcon_board(efx); + board->i2c_adap.owner = THIS_MODULE; + board->i2c_data = falcon_i2c_bit_operations; + board->i2c_data.data = efx; + board->i2c_adap.algo_data = &board->i2c_data; + board->i2c_adap.dev.parent = &efx->pci_dev->dev; + strlcpy(board->i2c_adap.name, "SFC4000 GPIO", + sizeof(board->i2c_adap.name)); + rc = i2c_bit_add_bus(&board->i2c_adap); + if (rc) + goto fail5; + + rc = falcon_board(efx)->type->init(efx); + if (rc) { + netif_err(efx, probe, efx->net_dev, + "failed to initialise board\n"); + goto fail6; + } + + nic_data->stats_disable_count = 1; + setup_timer(&nic_data->stats_timer, &falcon_stats_timer_func, + (unsigned long)efx); + + return 0; + + fail6: + i2c_del_adapter(&board->i2c_adap); + memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); + fail5: + ef4_nic_free_buffer(efx, &efx->irq_status); + fail4: + fail3: + if (nic_data->pci_dev2) { + pci_dev_put(nic_data->pci_dev2); + nic_data->pci_dev2 = NULL; + } + fail2: + fail1: + kfree(efx->nic_data); + return rc; +} + +static void falcon_init_rx_cfg(struct ef4_nic *efx) +{ + /* RX control FIFO thresholds (32 entries) */ + const unsigned ctrl_xon_thr = 20; + const unsigned ctrl_xoff_thr = 25; + ef4_oword_t reg; + + ef4_reado(efx, ®, FR_AZ_RX_CFG); + if (ef4_nic_rev(efx) <= EF4_REV_FALCON_A1) { + /* Data FIFO size is 5.5K. The RX DMA engine only + * supports scattering for user-mode queues, but will + * split DMA writes at intervals of RX_USR_BUF_SIZE + * (32-byte units) even for kernel-mode queues. We + * set it to be so large that that never happens. + */ + EF4_SET_OWORD_FIELD(reg, FRF_AA_RX_DESC_PUSH_EN, 0); + EF4_SET_OWORD_FIELD(reg, FRF_AA_RX_USR_BUF_SIZE, + (3 * 4096) >> 5); + EF4_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, 512 >> 8); + EF4_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, 2048 >> 8); + EF4_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_TX_TH, ctrl_xon_thr); + EF4_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_TX_TH, ctrl_xoff_thr); + } else { + /* Data FIFO size is 80K; register fields moved */ + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_DESC_PUSH_EN, 0); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_USR_BUF_SIZE, + EF4_RX_USR_BUF_SIZE >> 5); + /* Send XON and XOFF at ~3 * max MTU away from empty/full */ + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, 27648 >> 8); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, 54272 >> 8); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_TX_TH, ctrl_xon_thr); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_TX_TH, ctrl_xoff_thr); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); + + /* Enable hash insertion. This is broken for the + * 'Falcon' hash so also select Toeplitz TCP/IPv4 and + * IPv4 hashes. */ + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_HASH_INSRT_HDR, 1); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_HASH_ALG, 1); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_RX_IP_HASH, 1); + } + /* Always enable XOFF signal from RX FIFO. We enable + * or disable transmission of pause frames at the MAC. */ + EF4_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); + ef4_writeo(efx, ®, FR_AZ_RX_CFG); +} + +/* This call performs hardware-specific global initialisation, such as + * defining the descriptor cache sizes and number of RSS channels. + * It does not set up any buffers, descriptor rings or event queues. + */ +static int falcon_init_nic(struct ef4_nic *efx) +{ + ef4_oword_t temp; + int rc; + + /* Use on-chip SRAM */ + ef4_reado(efx, &temp, FR_AB_NIC_STAT); + EF4_SET_OWORD_FIELD(temp, FRF_AB_ONCHIP_SRAM, 1); + ef4_writeo(efx, &temp, FR_AB_NIC_STAT); + + rc = falcon_reset_sram(efx); + if (rc) + return rc; + + /* Clear the parity enables on the TX data fifos as + * they produce false parity errors because of timing issues + */ + if (EF4_WORKAROUND_5129(efx)) { + ef4_reado(efx, &temp, FR_AZ_CSR_SPARE); + EF4_SET_OWORD_FIELD(temp, FRF_AB_MEM_PERR_EN_TX_DATA, 0); + ef4_writeo(efx, &temp, FR_AZ_CSR_SPARE); + } + + if (EF4_WORKAROUND_7244(efx)) { + ef4_reado(efx, &temp, FR_BZ_RX_FILTER_CTL); + EF4_SET_OWORD_FIELD(temp, FRF_BZ_UDP_FULL_SRCH_LIMIT, 8); + EF4_SET_OWORD_FIELD(temp, FRF_BZ_UDP_WILD_SRCH_LIMIT, 8); + EF4_SET_OWORD_FIELD(temp, FRF_BZ_TCP_FULL_SRCH_LIMIT, 8); + EF4_SET_OWORD_FIELD(temp, FRF_BZ_TCP_WILD_SRCH_LIMIT, 8); + ef4_writeo(efx, &temp, FR_BZ_RX_FILTER_CTL); + } + + /* XXX This is documented only for Falcon A0/A1 */ + /* Setup RX. Wait for descriptor is broken and must + * be disabled. RXDP recovery shouldn't be needed, but is. + */ + ef4_reado(efx, &temp, FR_AA_RX_SELF_RST); + EF4_SET_OWORD_FIELD(temp, FRF_AA_RX_NODESC_WAIT_DIS, 1); + EF4_SET_OWORD_FIELD(temp, FRF_AA_RX_SELF_RST_EN, 1); + if (EF4_WORKAROUND_5583(efx)) + EF4_SET_OWORD_FIELD(temp, FRF_AA_RX_ISCSI_DIS, 1); + ef4_writeo(efx, &temp, FR_AA_RX_SELF_RST); + + /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 + * descriptors (which is bad). + */ + ef4_reado(efx, &temp, FR_AZ_TX_CFG); + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); + ef4_writeo(efx, &temp, FR_AZ_TX_CFG); + + falcon_init_rx_cfg(efx); + + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) { + falcon_b0_rx_push_rss_config(efx, false, efx->rx_indir_table); + + /* Set destination of both TX and RX Flush events */ + EF4_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); + ef4_writeo(efx, &temp, FR_BZ_DP_CTRL); + } + + ef4_farch_init_common(efx); + + return 0; +} + +static void falcon_remove_nic(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_board *board = falcon_board(efx); + + board->type->fini(efx); + + /* Remove I2C adapter and clear it in preparation for a retry */ + i2c_del_adapter(&board->i2c_adap); + memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); + + ef4_nic_free_buffer(efx, &efx->irq_status); + + __falcon_reset_hw(efx, RESET_TYPE_ALL); + + /* Release the second function after the reset */ + if (nic_data->pci_dev2) { + pci_dev_put(nic_data->pci_dev2); + nic_data->pci_dev2 = NULL; + } + + /* Tear down the private nic state */ + kfree(efx->nic_data); + efx->nic_data = NULL; +} + +static size_t falcon_describe_nic_stats(struct ef4_nic *efx, u8 *names) +{ + return ef4_nic_describe_stats(falcon_stat_desc, FALCON_STAT_COUNT, + falcon_stat_mask, names); +} + +static size_t falcon_update_nic_stats(struct ef4_nic *efx, u64 *full_stats, + struct rtnl_link_stats64 *core_stats) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + u64 *stats = nic_data->stats; + ef4_oword_t cnt; + + if (!nic_data->stats_disable_count) { + ef4_reado(efx, &cnt, FR_AZ_RX_NODESC_DROP); + stats[FALCON_STAT_rx_nodesc_drop_cnt] += + EF4_OWORD_FIELD(cnt, FRF_AB_RX_NODESC_DROP_CNT); + + if (nic_data->stats_pending && + FALCON_XMAC_STATS_DMA_FLAG(efx)) { + nic_data->stats_pending = false; + rmb(); /* read the done flag before the stats */ + ef4_nic_update_stats( + falcon_stat_desc, FALCON_STAT_COUNT, + falcon_stat_mask, + stats, efx->stats_buffer.addr, true); + } + + /* Update derived statistic */ + ef4_update_diff_stat(&stats[FALCON_STAT_rx_bad_bytes], + stats[FALCON_STAT_rx_bytes] - + stats[FALCON_STAT_rx_good_bytes] - + stats[FALCON_STAT_rx_control] * 64); + ef4_update_sw_stats(efx, stats); + } + + if (full_stats) + memcpy(full_stats, stats, sizeof(u64) * FALCON_STAT_COUNT); + + if (core_stats) { + core_stats->rx_packets = stats[FALCON_STAT_rx_packets]; + core_stats->tx_packets = stats[FALCON_STAT_tx_packets]; + core_stats->rx_bytes = stats[FALCON_STAT_rx_bytes]; + core_stats->tx_bytes = stats[FALCON_STAT_tx_bytes]; + core_stats->rx_dropped = stats[FALCON_STAT_rx_nodesc_drop_cnt] + + stats[GENERIC_STAT_rx_nodesc_trunc] + + stats[GENERIC_STAT_rx_noskb_drops]; + core_stats->multicast = stats[FALCON_STAT_rx_multicast]; + core_stats->rx_length_errors = + stats[FALCON_STAT_rx_gtjumbo] + + stats[FALCON_STAT_rx_length_error]; + core_stats->rx_crc_errors = stats[FALCON_STAT_rx_bad]; + core_stats->rx_frame_errors = stats[FALCON_STAT_rx_align_error]; + core_stats->rx_fifo_errors = stats[FALCON_STAT_rx_overflow]; + + core_stats->rx_errors = (core_stats->rx_length_errors + + core_stats->rx_crc_errors + + core_stats->rx_frame_errors + + stats[FALCON_STAT_rx_symbol_error]); + } + + return FALCON_STAT_COUNT; +} + +void falcon_start_nic_stats(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock_bh(&efx->stats_lock); + if (--nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + spin_unlock_bh(&efx->stats_lock); +} + +/* We don't acutally pull stats on falcon. Wait 10ms so that + * they arrive when we call this just after start_stats + */ +static void falcon_pull_nic_stats(struct ef4_nic *efx) +{ + msleep(10); +} + +void falcon_stop_nic_stats(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + int i; + + might_sleep(); + + spin_lock_bh(&efx->stats_lock); + ++nic_data->stats_disable_count; + spin_unlock_bh(&efx->stats_lock); + + del_timer_sync(&nic_data->stats_timer); + + /* Wait enough time for the most recent transfer to + * complete. */ + for (i = 0; i < 4 && nic_data->stats_pending; i++) { + if (FALCON_XMAC_STATS_DMA_FLAG(efx)) + break; + msleep(1); + } + + spin_lock_bh(&efx->stats_lock); + falcon_stats_complete(efx); + spin_unlock_bh(&efx->stats_lock); +} + +static void falcon_set_id_led(struct ef4_nic *efx, enum ef4_led_mode mode) +{ + falcon_board(efx)->type->set_id_led(efx, mode); +} + +/************************************************************************** + * + * Wake on LAN + * + ************************************************************************** + */ + +static void falcon_get_wol(struct ef4_nic *efx, struct ethtool_wolinfo *wol) +{ + wol->supported = 0; + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static int falcon_set_wol(struct ef4_nic *efx, u32 type) +{ + if (type != 0) + return -EINVAL; + return 0; +} + +/************************************************************************** + * + * Revision-dependent attributes used by efx.c and nic.c + * + ************************************************************************** + */ + +const struct ef4_nic_type falcon_a1_nic_type = { + .mem_bar = EF4_MEM_BAR, + .mem_map_size = falcon_a1_mem_map_size, + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .dimension_resources = falcon_dimension_resources, + .fini = falcon_irq_ack_a1, + .monitor = falcon_monitor, + .map_reset_reason = falcon_map_reset_reason, + .map_reset_flags = falcon_map_reset_flags, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .handle_global_event = falcon_handle_global_event, + .fini_dmaq = ef4_farch_fini_dmaq, + .prepare_flush = falcon_prepare_flush, + .finish_flush = ef4_port_dummy_op_void, + .prepare_flr = ef4_port_dummy_op_void, + .finish_flr = ef4_farch_finish_flr, + .describe_stats = falcon_describe_nic_stats, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .pull_stats = falcon_pull_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .reconfigure_port = falcon_reconfigure_port, + .prepare_enable_fc_tx = falcon_a1_prepare_enable_fc_tx, + .reconfigure_mac = falcon_reconfigure_xmac, + .check_mac_fault = falcon_xmac_check_fault, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = ef4_port_dummy_op_void, + .test_nvram = falcon_test_nvram, + .irq_enable_master = ef4_farch_irq_enable_master, + .irq_test_generate = ef4_farch_irq_test_generate, + .irq_disable_non_ev = ef4_farch_irq_disable_master, + .irq_handle_msi = ef4_farch_msi_interrupt, + .irq_handle_legacy = falcon_legacy_interrupt_a1, + .tx_probe = ef4_farch_tx_probe, + .tx_init = ef4_farch_tx_init, + .tx_remove = ef4_farch_tx_remove, + .tx_write = ef4_farch_tx_write, + .tx_limit_len = ef4_farch_tx_limit_len, + .rx_push_rss_config = dummy_rx_push_rss_config, + .rx_probe = ef4_farch_rx_probe, + .rx_init = ef4_farch_rx_init, + .rx_remove = ef4_farch_rx_remove, + .rx_write = ef4_farch_rx_write, + .rx_defer_refill = ef4_farch_rx_defer_refill, + .ev_probe = ef4_farch_ev_probe, + .ev_init = ef4_farch_ev_init, + .ev_fini = ef4_farch_ev_fini, + .ev_remove = ef4_farch_ev_remove, + .ev_process = ef4_farch_ev_process, + .ev_read_ack = ef4_farch_ev_read_ack, + .ev_test_generate = ef4_farch_ev_test_generate, + + /* We don't expose the filter table on Falcon A1 as it is not + * mapped into function 0, but these implementations still + * work with a degenerate case of all tables set to size 0. + */ + .filter_table_probe = ef4_farch_filter_table_probe, + .filter_table_restore = ef4_farch_filter_table_restore, + .filter_table_remove = ef4_farch_filter_table_remove, + .filter_insert = ef4_farch_filter_insert, + .filter_remove_safe = ef4_farch_filter_remove_safe, + .filter_get_safe = ef4_farch_filter_get_safe, + .filter_clear_rx = ef4_farch_filter_clear_rx, + .filter_count_rx_used = ef4_farch_filter_count_rx_used, + .filter_get_rx_id_limit = ef4_farch_filter_get_rx_id_limit, + .filter_get_rx_ids = ef4_farch_filter_get_rx_ids, + +#ifdef CONFIG_SFC_FALCON_MTD + .mtd_probe = falcon_mtd_probe, + .mtd_rename = falcon_mtd_rename, + .mtd_read = falcon_mtd_read, + .mtd_erase = falcon_mtd_erase, + .mtd_write = falcon_mtd_write, + .mtd_sync = falcon_mtd_sync, +#endif + + .revision = EF4_REV_FALCON_A1, + .txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER, + .rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER, + .buf_tbl_base = FR_AA_BUF_FULL_TBL_KER, + .evq_ptr_tbl_base = FR_AA_EVQ_PTR_TBL_KER, + .evq_rptr_tbl_base = FR_AA_EVQ_RPTR_KER, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), + .rx_buffer_padding = 0x24, + .can_rx_scatter = false, + .max_interrupt_mode = EF4_INT_MODE_MSI, + .timer_period_max = 1 << FRF_AB_TC_TIMER_VAL_WIDTH, + .offload_features = NETIF_F_IP_CSUM, +}; + +const struct ef4_nic_type falcon_b0_nic_type = { + .mem_bar = EF4_MEM_BAR, + .mem_map_size = falcon_b0_mem_map_size, + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .dimension_resources = falcon_dimension_resources, + .fini = ef4_port_dummy_op_void, + .monitor = falcon_monitor, + .map_reset_reason = falcon_map_reset_reason, + .map_reset_flags = falcon_map_reset_flags, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .handle_global_event = falcon_handle_global_event, + .fini_dmaq = ef4_farch_fini_dmaq, + .prepare_flush = falcon_prepare_flush, + .finish_flush = ef4_port_dummy_op_void, + .prepare_flr = ef4_port_dummy_op_void, + .finish_flr = ef4_farch_finish_flr, + .describe_stats = falcon_describe_nic_stats, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .pull_stats = falcon_pull_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .reconfigure_port = falcon_reconfigure_port, + .prepare_enable_fc_tx = falcon_b0_prepare_enable_fc_tx, + .reconfigure_mac = falcon_reconfigure_xmac, + .check_mac_fault = falcon_xmac_check_fault, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = ef4_port_dummy_op_void, + .test_chip = falcon_b0_test_chip, + .test_nvram = falcon_test_nvram, + .irq_enable_master = ef4_farch_irq_enable_master, + .irq_test_generate = ef4_farch_irq_test_generate, + .irq_disable_non_ev = ef4_farch_irq_disable_master, + .irq_handle_msi = ef4_farch_msi_interrupt, + .irq_handle_legacy = ef4_farch_legacy_interrupt, + .tx_probe = ef4_farch_tx_probe, + .tx_init = ef4_farch_tx_init, + .tx_remove = ef4_farch_tx_remove, + .tx_write = ef4_farch_tx_write, + .tx_limit_len = ef4_farch_tx_limit_len, + .rx_push_rss_config = falcon_b0_rx_push_rss_config, + .rx_probe = ef4_farch_rx_probe, + .rx_init = ef4_farch_rx_init, + .rx_remove = ef4_farch_rx_remove, + .rx_write = ef4_farch_rx_write, + .rx_defer_refill = ef4_farch_rx_defer_refill, + .ev_probe = ef4_farch_ev_probe, + .ev_init = ef4_farch_ev_init, + .ev_fini = ef4_farch_ev_fini, + .ev_remove = ef4_farch_ev_remove, + .ev_process = ef4_farch_ev_process, + .ev_read_ack = ef4_farch_ev_read_ack, + .ev_test_generate = ef4_farch_ev_test_generate, + .filter_table_probe = ef4_farch_filter_table_probe, + .filter_table_restore = ef4_farch_filter_table_restore, + .filter_table_remove = ef4_farch_filter_table_remove, + .filter_update_rx_scatter = ef4_farch_filter_update_rx_scatter, + .filter_insert = ef4_farch_filter_insert, + .filter_remove_safe = ef4_farch_filter_remove_safe, + .filter_get_safe = ef4_farch_filter_get_safe, + .filter_clear_rx = ef4_farch_filter_clear_rx, + .filter_count_rx_used = ef4_farch_filter_count_rx_used, + .filter_get_rx_id_limit = ef4_farch_filter_get_rx_id_limit, + .filter_get_rx_ids = ef4_farch_filter_get_rx_ids, +#ifdef CONFIG_RFS_ACCEL + .filter_rfs_insert = ef4_farch_filter_rfs_insert, + .filter_rfs_expire_one = ef4_farch_filter_rfs_expire_one, +#endif +#ifdef CONFIG_SFC_FALCON_MTD + .mtd_probe = falcon_mtd_probe, + .mtd_rename = falcon_mtd_rename, + .mtd_read = falcon_mtd_read, + .mtd_erase = falcon_mtd_erase, + .mtd_write = falcon_mtd_write, + .mtd_sync = falcon_mtd_sync, +#endif + + .revision = EF4_REV_FALCON_B0, + .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, + .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, + .buf_tbl_base = FR_BZ_BUF_FULL_TBL, + .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, + .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), + .rx_prefix_size = FS_BZ_RX_PREFIX_SIZE, + .rx_hash_offset = FS_BZ_RX_PREFIX_HASH_OFST, + .rx_buffer_padding = 0, + .can_rx_scatter = true, + .max_interrupt_mode = EF4_INT_MODE_MSIX, + .timer_period_max = 1 << FRF_AB_TC_TIMER_VAL_WIDTH, + .offload_features = NETIF_F_IP_CSUM | NETIF_F_RXHASH | NETIF_F_NTUPLE, + .max_rx_ip_filters = FR_BZ_RX_FILTER_TBL0_ROWS, +}; diff --git a/drivers/net/ethernet/sfc/falcon/falcon_boards.c b/drivers/net/ethernet/sfc/falcon/falcon_boards.c new file mode 100644 index 0000000..dec83a2 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/falcon_boards.c @@ -0,0 +1,764 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2007-2012 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include + +#include "net_driver.h" +#include "phy.h" +#include "efx.h" +#include "nic.h" +#include "workarounds.h" + +/* Macros for unpacking the board revision */ +/* The revision info is in host byte order. */ +#define FALCON_BOARD_TYPE(_rev) (_rev >> 8) +#define FALCON_BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) +#define FALCON_BOARD_MINOR(_rev) (_rev & 0xf) + +/* Board types */ +#define FALCON_BOARD_SFE4001 0x01 +#define FALCON_BOARD_SFE4002 0x02 +#define FALCON_BOARD_SFE4003 0x03 +#define FALCON_BOARD_SFN4112F 0x52 + +/* Board temperature is about 15°C above ambient when air flow is + * limited. The maximum acceptable ambient temperature varies + * depending on the PHY specifications but the critical temperature + * above which we should shut down to avoid damage is 80°C. */ +#define FALCON_BOARD_TEMP_BIAS 15 +#define FALCON_BOARD_TEMP_CRIT (80 + FALCON_BOARD_TEMP_BIAS) + +/* SFC4000 datasheet says: 'The maximum permitted junction temperature + * is 125°C; the thermal design of the environment for the SFC4000 + * should aim to keep this well below 100°C.' */ +#define FALCON_JUNC_TEMP_MIN 0 +#define FALCON_JUNC_TEMP_MAX 90 +#define FALCON_JUNC_TEMP_CRIT 125 + +/***************************************************************************** + * Support for LM87 sensor chip used on several boards + */ +#define LM87_REG_TEMP_HW_INT_LOCK 0x13 +#define LM87_REG_TEMP_HW_EXT_LOCK 0x14 +#define LM87_REG_TEMP_HW_INT 0x17 +#define LM87_REG_TEMP_HW_EXT 0x18 +#define LM87_REG_TEMP_EXT1 0x26 +#define LM87_REG_TEMP_INT 0x27 +#define LM87_REG_ALARMS1 0x41 +#define LM87_REG_ALARMS2 0x42 +#define LM87_IN_LIMITS(nr, _min, _max) \ + 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min +#define LM87_AIN_LIMITS(nr, _min, _max) \ + 0x3B + (nr), _max, 0x1A + (nr), _min +#define LM87_TEMP_INT_LIMITS(_min, _max) \ + 0x39, _max, 0x3A, _min +#define LM87_TEMP_EXT1_LIMITS(_min, _max) \ + 0x37, _max, 0x38, _min + +#define LM87_ALARM_TEMP_INT 0x10 +#define LM87_ALARM_TEMP_EXT1 0x20 + +#if IS_ENABLED(CONFIG_SENSORS_LM87) + +static int ef4_poke_lm87(struct i2c_client *client, const u8 *reg_values) +{ + while (*reg_values) { + u8 reg = *reg_values++; + u8 value = *reg_values++; + int rc = i2c_smbus_write_byte_data(client, reg, value); + if (rc) + return rc; + } + return 0; +} + +static const u8 falcon_lm87_common_regs[] = { + LM87_REG_TEMP_HW_INT_LOCK, FALCON_BOARD_TEMP_CRIT, + LM87_REG_TEMP_HW_INT, FALCON_BOARD_TEMP_CRIT, + LM87_TEMP_EXT1_LIMITS(FALCON_JUNC_TEMP_MIN, FALCON_JUNC_TEMP_MAX), + LM87_REG_TEMP_HW_EXT_LOCK, FALCON_JUNC_TEMP_CRIT, + LM87_REG_TEMP_HW_EXT, FALCON_JUNC_TEMP_CRIT, + 0 +}; + +static int ef4_init_lm87(struct ef4_nic *efx, const struct i2c_board_info *info, + const u8 *reg_values) +{ + struct falcon_board *board = falcon_board(efx); + struct i2c_client *client = i2c_new_device(&board->i2c_adap, info); + int rc; + + if (!client) + return -EIO; + + /* Read-to-clear alarm/interrupt status */ + i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); + i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); + + rc = ef4_poke_lm87(client, reg_values); + if (rc) + goto err; + rc = ef4_poke_lm87(client, falcon_lm87_common_regs); + if (rc) + goto err; + + board->hwmon_client = client; + return 0; + +err: + i2c_unregister_device(client); + return rc; +} + +static void ef4_fini_lm87(struct ef4_nic *efx) +{ + i2c_unregister_device(falcon_board(efx)->hwmon_client); +} + +static int ef4_check_lm87(struct ef4_nic *efx, unsigned mask) +{ + struct i2c_client *client = falcon_board(efx)->hwmon_client; + bool temp_crit, elec_fault, is_failure; + u16 alarms; + s32 reg; + + /* If link is up then do not monitor temperature */ + if (EF4_WORKAROUND_7884(efx) && efx->link_state.up) + return 0; + + reg = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); + if (reg < 0) + return reg; + alarms = reg; + reg = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); + if (reg < 0) + return reg; + alarms |= reg << 8; + alarms &= mask; + + temp_crit = false; + if (alarms & LM87_ALARM_TEMP_INT) { + reg = i2c_smbus_read_byte_data(client, LM87_REG_TEMP_INT); + if (reg < 0) + return reg; + if (reg > FALCON_BOARD_TEMP_CRIT) + temp_crit = true; + } + if (alarms & LM87_ALARM_TEMP_EXT1) { + reg = i2c_smbus_read_byte_data(client, LM87_REG_TEMP_EXT1); + if (reg < 0) + return reg; + if (reg > FALCON_JUNC_TEMP_CRIT) + temp_crit = true; + } + elec_fault = alarms & ~(LM87_ALARM_TEMP_INT | LM87_ALARM_TEMP_EXT1); + is_failure = temp_crit || elec_fault; + + if (alarms) + netif_err(efx, hw, efx->net_dev, + "LM87 detected a hardware %s (status %02x:%02x)" + "%s%s%s%s\n", + is_failure ? "failure" : "problem", + alarms & 0xff, alarms >> 8, + (alarms & LM87_ALARM_TEMP_INT) ? + "; board is overheating" : "", + (alarms & LM87_ALARM_TEMP_EXT1) ? + "; controller is overheating" : "", + temp_crit ? "; reached critical temperature" : "", + elec_fault ? "; electrical fault" : ""); + + return is_failure ? -ERANGE : 0; +} + +#else /* !CONFIG_SENSORS_LM87 */ + +static inline int +ef4_init_lm87(struct ef4_nic *efx, const struct i2c_board_info *info, + const u8 *reg_values) +{ + return 0; +} +static inline void ef4_fini_lm87(struct ef4_nic *efx) +{ +} +static inline int ef4_check_lm87(struct ef4_nic *efx, unsigned mask) +{ + return 0; +} + +#endif /* CONFIG_SENSORS_LM87 */ + +/***************************************************************************** + * Support for the SFE4001 NIC. + * + * The SFE4001 does not power-up fully at reset due to its high power + * consumption. We control its power via a PCA9539 I/O expander. + * It also has a MAX6647 temperature monitor which we expose to + * the lm90 driver. + * + * This also provides minimal support for reflashing the PHY, which is + * initiated by resetting it with the FLASH_CFG_1 pin pulled down. + * On SFE4001 rev A2 and later this is connected to the 3V3X output of + * the IO-expander. + * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually + * exclusive with the network device being open. + */ + +/************************************************************************** + * Support for I2C IO Expander device on SFE4001 + */ +#define PCA9539 0x74 + +#define P0_IN 0x00 +#define P0_OUT 0x02 +#define P0_INVERT 0x04 +#define P0_CONFIG 0x06 + +#define P0_EN_1V0X_LBN 0 +#define P0_EN_1V0X_WIDTH 1 +#define P0_EN_1V2_LBN 1 +#define P0_EN_1V2_WIDTH 1 +#define P0_EN_2V5_LBN 2 +#define P0_EN_2V5_WIDTH 1 +#define P0_EN_3V3X_LBN 3 +#define P0_EN_3V3X_WIDTH 1 +#define P0_EN_5V_LBN 4 +#define P0_EN_5V_WIDTH 1 +#define P0_SHORTEN_JTAG_LBN 5 +#define P0_SHORTEN_JTAG_WIDTH 1 +#define P0_X_TRST_LBN 6 +#define P0_X_TRST_WIDTH 1 +#define P0_DSP_RESET_LBN 7 +#define P0_DSP_RESET_WIDTH 1 + +#define P1_IN 0x01 +#define P1_OUT 0x03 +#define P1_INVERT 0x05 +#define P1_CONFIG 0x07 + +#define P1_AFE_PWD_LBN 0 +#define P1_AFE_PWD_WIDTH 1 +#define P1_DSP_PWD25_LBN 1 +#define P1_DSP_PWD25_WIDTH 1 +#define P1_RESERVED_LBN 2 +#define P1_RESERVED_WIDTH 2 +#define P1_SPARE_LBN 4 +#define P1_SPARE_WIDTH 4 + +/* Temperature Sensor */ +#define MAX664X_REG_RSL 0x02 +#define MAX664X_REG_WLHO 0x0B + +static void sfe4001_poweroff(struct ef4_nic *efx) +{ + struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; + struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; + + /* Turn off all power rails and disable outputs */ + i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff); + i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff); + i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff); + + /* Clear any over-temperature alert */ + i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); +} + +static int sfe4001_poweron(struct ef4_nic *efx) +{ + struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; + struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; + unsigned int i, j; + int rc; + u8 out; + + /* Clear any previous over-temperature alert */ + rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); + if (rc < 0) + return rc; + + /* Enable port 0 and port 1 outputs on IO expander */ + rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00); + if (rc) + return rc; + rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, + 0xff & ~(1 << P1_SPARE_LBN)); + if (rc) + goto fail_on; + + /* If PHY power is on, turn it all off and wait 1 second to + * ensure a full reset. + */ + rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT); + if (rc < 0) + goto fail_on; + out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) | + (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) | + (0 << P0_EN_1V0X_LBN)); + if (rc != out) { + netif_info(efx, hw, efx->net_dev, "power-cycling PHY\n"); + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + schedule_timeout_uninterruptible(HZ); + } + + for (i = 0; i < 20; ++i) { + /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */ + out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | + (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | + (1 << P0_X_TRST_LBN)); + if (efx->phy_mode & PHY_MODE_SPECIAL) + out |= 1 << P0_EN_3V3X_LBN; + + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + msleep(10); + + /* Turn on 1V power rail */ + out &= ~(1 << P0_EN_1V0X_LBN); + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + + netif_info(efx, hw, efx->net_dev, + "waiting for DSP boot (attempt %d)...\n", i); + + /* In flash config mode, DSP does not turn on AFE, so + * just wait 1 second. + */ + if (efx->phy_mode & PHY_MODE_SPECIAL) { + schedule_timeout_uninterruptible(HZ); + return 0; + } + + for (j = 0; j < 10; ++j) { + msleep(100); + + /* Check DSP has asserted AFE power line */ + rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN); + if (rc < 0) + goto fail_on; + if (rc & (1 << P1_AFE_PWD_LBN)) + return 0; + } + } + + netif_info(efx, hw, efx->net_dev, "timed out waiting for DSP boot\n"); + rc = -ETIMEDOUT; +fail_on: + sfe4001_poweroff(efx); + return rc; +} + +static ssize_t show_phy_flash_cfg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ef4_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL)); +} + +static ssize_t set_phy_flash_cfg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ef4_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + enum ef4_phy_mode old_mode, new_mode; + int err; + + rtnl_lock(); + old_mode = efx->phy_mode; + if (count == 0 || *buf == '0') + new_mode = old_mode & ~PHY_MODE_SPECIAL; + else + new_mode = PHY_MODE_SPECIAL; + if (!((old_mode ^ new_mode) & PHY_MODE_SPECIAL)) { + err = 0; + } else if (efx->state != STATE_READY || netif_running(efx->net_dev)) { + err = -EBUSY; + } else { + /* Reset the PHY, reconfigure the MAC and enable/disable + * MAC stats accordingly. */ + efx->phy_mode = new_mode; + if (new_mode & PHY_MODE_SPECIAL) + falcon_stop_nic_stats(efx); + err = sfe4001_poweron(efx); + if (!err) + err = ef4_reconfigure_port(efx); + if (!(new_mode & PHY_MODE_SPECIAL)) + falcon_start_nic_stats(efx); + } + rtnl_unlock(); + + return err ? err : count; +} + +static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg); + +static void sfe4001_fini(struct ef4_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + netif_info(efx, drv, efx->net_dev, "%s\n", __func__); + + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + sfe4001_poweroff(efx); + i2c_unregister_device(board->ioexp_client); + i2c_unregister_device(board->hwmon_client); +} + +static int sfe4001_check_hw(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + s32 status; + + /* If XAUI link is up then do not monitor */ + if (EF4_WORKAROUND_7884(efx) && !nic_data->xmac_poll_required) + return 0; + + /* Check the powered status of the PHY. Lack of power implies that + * the MAX6647 has shut down power to it, probably due to a temp. + * alarm. Reading the power status rather than the MAX6647 status + * directly because the later is read-to-clear and would thus + * start to power up the PHY again when polled, causing us to blip + * the power undesirably. + * We know we can read from the IO expander because we did + * it during power-on. Assume failure now is bad news. */ + status = i2c_smbus_read_byte_data(falcon_board(efx)->ioexp_client, P1_IN); + if (status >= 0 && + (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0) + return 0; + + /* Use board power control, not PHY power control */ + sfe4001_poweroff(efx); + efx->phy_mode = PHY_MODE_OFF; + + return (status < 0) ? -EIO : -ERANGE; +} + +static const struct i2c_board_info sfe4001_hwmon_info = { + I2C_BOARD_INFO("max6647", 0x4e), +}; + +/* This board uses an I2C expander to provider power to the PHY, which needs to + * be turned on before the PHY can be used. + * Context: Process context, rtnl lock held + */ +static int sfe4001_init(struct ef4_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + int rc; + +#if IS_ENABLED(CONFIG_SENSORS_LM90) + board->hwmon_client = + i2c_new_device(&board->i2c_adap, &sfe4001_hwmon_info); +#else + board->hwmon_client = + i2c_new_dummy(&board->i2c_adap, sfe4001_hwmon_info.addr); +#endif + if (!board->hwmon_client) + return -EIO; + + /* Raise board/PHY high limit from 85 to 90 degrees Celsius */ + rc = i2c_smbus_write_byte_data(board->hwmon_client, + MAX664X_REG_WLHO, 90); + if (rc) + goto fail_hwmon; + + board->ioexp_client = i2c_new_dummy(&board->i2c_adap, PCA9539); + if (!board->ioexp_client) { + rc = -EIO; + goto fail_hwmon; + } + + if (efx->phy_mode & PHY_MODE_SPECIAL) { + /* PHY won't generate a 156.25 MHz clock and MAC stats fetch + * will fail. */ + falcon_stop_nic_stats(efx); + } + rc = sfe4001_poweron(efx); + if (rc) + goto fail_ioexp; + + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + if (rc) + goto fail_on; + + netif_info(efx, hw, efx->net_dev, "PHY is powered on\n"); + return 0; + +fail_on: + sfe4001_poweroff(efx); +fail_ioexp: + i2c_unregister_device(board->ioexp_client); +fail_hwmon: + i2c_unregister_device(board->hwmon_client); + return rc; +} + +/***************************************************************************** + * Support for the SFE4002 + * + */ +static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */ + +static const u8 sfe4002_lm87_regs[] = { + LM87_IN_LIMITS(0, 0x7c, 0x99), /* 2.5V: 1.8V +/- 10% */ + LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */ + LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */ + LM87_IN_LIMITS(3, 0xac, 0xd4), /* 5V: 5.0V +/- 10% */ + LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */ + LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */ + LM87_AIN_LIMITS(0, 0x98, 0xbb), /* AIN1: 1.66V +/- 10% */ + LM87_AIN_LIMITS(1, 0x8a, 0xa9), /* AIN2: 1.5V +/- 10% */ + LM87_TEMP_INT_LIMITS(0, 80 + FALCON_BOARD_TEMP_BIAS), + LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX), + 0 +}; + +static const struct i2c_board_info sfe4002_hwmon_info = { + I2C_BOARD_INFO("lm87", 0x2e), + .platform_data = &sfe4002_lm87_channel, +}; + +/****************************************************************************/ +/* LED allocations. Note that on rev A0 boards the schematic and the reality + * differ: red and green are swapped. Below is the fixed (A1) layout (there + * are only 3 A0 boards in existence, so no real reason to make this + * conditional). + */ +#define SFE4002_FAULT_LED (2) /* Red */ +#define SFE4002_RX_LED (0) /* Green */ +#define SFE4002_TX_LED (1) /* Amber */ + +static void sfe4002_init_phy(struct ef4_nic *efx) +{ + /* Set the TX and RX LEDs to reflect status and activity, and the + * fault LED off */ + falcon_qt202x_set_led(efx, SFE4002_TX_LED, + QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); + falcon_qt202x_set_led(efx, SFE4002_RX_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); + falcon_qt202x_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); +} + +static void sfe4002_set_id_led(struct ef4_nic *efx, enum ef4_led_mode mode) +{ + falcon_qt202x_set_led( + efx, SFE4002_FAULT_LED, + (mode == EF4_LED_ON) ? QUAKE_LED_ON : QUAKE_LED_OFF); +} + +static int sfe4002_check_hw(struct ef4_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + /* A0 board rev. 4002s report a temperature fault the whole time + * (bad sensor) so we mask it out. */ + unsigned alarm_mask = + (board->major == 0 && board->minor == 0) ? + ~LM87_ALARM_TEMP_EXT1 : ~0; + + return ef4_check_lm87(efx, alarm_mask); +} + +static int sfe4002_init(struct ef4_nic *efx) +{ + return ef4_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); +} + +/***************************************************************************** + * Support for the SFN4112F + * + */ +static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */ + +static const u8 sfn4112f_lm87_regs[] = { + LM87_IN_LIMITS(0, 0x7c, 0x99), /* 2.5V: 1.8V +/- 10% */ + LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */ + LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */ + LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */ + LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */ + LM87_AIN_LIMITS(1, 0x8a, 0xa9), /* AIN2: 1.5V +/- 10% */ + LM87_TEMP_INT_LIMITS(0, 60 + FALCON_BOARD_TEMP_BIAS), + LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX), + 0 +}; + +static const struct i2c_board_info sfn4112f_hwmon_info = { + I2C_BOARD_INFO("lm87", 0x2e), + .platform_data = &sfn4112f_lm87_channel, +}; + +#define SFN4112F_ACT_LED 0 +#define SFN4112F_LINK_LED 1 + +static void sfn4112f_init_phy(struct ef4_nic *efx) +{ + falcon_qt202x_set_led(efx, SFN4112F_ACT_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT); + falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT); +} + +static void sfn4112f_set_id_led(struct ef4_nic *efx, enum ef4_led_mode mode) +{ + int reg; + + switch (mode) { + case EF4_LED_OFF: + reg = QUAKE_LED_OFF; + break; + case EF4_LED_ON: + reg = QUAKE_LED_ON; + break; + default: + reg = QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT; + break; + } + + falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, reg); +} + +static int sfn4112f_check_hw(struct ef4_nic *efx) +{ + /* Mask out unused sensors */ + return ef4_check_lm87(efx, ~0x48); +} + +static int sfn4112f_init(struct ef4_nic *efx) +{ + return ef4_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs); +} + +/***************************************************************************** + * Support for the SFE4003 + * + */ +static u8 sfe4003_lm87_channel = 0x03; /* use AIN not FAN inputs */ + +static const u8 sfe4003_lm87_regs[] = { + LM87_IN_LIMITS(0, 0x67, 0x7f), /* 2.5V: 1.5V +/- 10% */ + LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */ + LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */ + LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */ + LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */ + LM87_TEMP_INT_LIMITS(0, 70 + FALCON_BOARD_TEMP_BIAS), + 0 +}; + +static const struct i2c_board_info sfe4003_hwmon_info = { + I2C_BOARD_INFO("lm87", 0x2e), + .platform_data = &sfe4003_lm87_channel, +}; + +/* Board-specific LED info. */ +#define SFE4003_RED_LED_GPIO 11 +#define SFE4003_LED_ON 1 +#define SFE4003_LED_OFF 0 + +static void sfe4003_set_id_led(struct ef4_nic *efx, enum ef4_led_mode mode) +{ + struct falcon_board *board = falcon_board(efx); + + /* The LEDs were not wired to GPIOs before A3 */ + if (board->minor < 3 && board->major == 0) + return; + + falcon_txc_set_gpio_val( + efx, SFE4003_RED_LED_GPIO, + (mode == EF4_LED_ON) ? SFE4003_LED_ON : SFE4003_LED_OFF); +} + +static void sfe4003_init_phy(struct ef4_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + /* The LEDs were not wired to GPIOs before A3 */ + if (board->minor < 3 && board->major == 0) + return; + + falcon_txc_set_gpio_dir(efx, SFE4003_RED_LED_GPIO, TXC_GPIO_DIR_OUTPUT); + falcon_txc_set_gpio_val(efx, SFE4003_RED_LED_GPIO, SFE4003_LED_OFF); +} + +static int sfe4003_check_hw(struct ef4_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + /* A0/A1/A2 board rev. 4003s report a temperature fault the whole time + * (bad sensor) so we mask it out. */ + unsigned alarm_mask = + (board->major == 0 && board->minor <= 2) ? + ~LM87_ALARM_TEMP_EXT1 : ~0; + + return ef4_check_lm87(efx, alarm_mask); +} + +static int sfe4003_init(struct ef4_nic *efx) +{ + return ef4_init_lm87(efx, &sfe4003_hwmon_info, sfe4003_lm87_regs); +} + +static const struct falcon_board_type board_types[] = { + { + .id = FALCON_BOARD_SFE4001, + .init = sfe4001_init, + .init_phy = ef4_port_dummy_op_void, + .fini = sfe4001_fini, + .set_id_led = tenxpress_set_id_led, + .monitor = sfe4001_check_hw, + }, + { + .id = FALCON_BOARD_SFE4002, + .init = sfe4002_init, + .init_phy = sfe4002_init_phy, + .fini = ef4_fini_lm87, + .set_id_led = sfe4002_set_id_led, + .monitor = sfe4002_check_hw, + }, + { + .id = FALCON_BOARD_SFE4003, + .init = sfe4003_init, + .init_phy = sfe4003_init_phy, + .fini = ef4_fini_lm87, + .set_id_led = sfe4003_set_id_led, + .monitor = sfe4003_check_hw, + }, + { + .id = FALCON_BOARD_SFN4112F, + .init = sfn4112f_init, + .init_phy = sfn4112f_init_phy, + .fini = ef4_fini_lm87, + .set_id_led = sfn4112f_set_id_led, + .monitor = sfn4112f_check_hw, + }, +}; + +int falcon_probe_board(struct ef4_nic *efx, u16 revision_info) +{ + struct falcon_board *board = falcon_board(efx); + u8 type_id = FALCON_BOARD_TYPE(revision_info); + int i; + + board->major = FALCON_BOARD_MAJOR(revision_info); + board->minor = FALCON_BOARD_MINOR(revision_info); + + for (i = 0; i < ARRAY_SIZE(board_types); i++) + if (board_types[i].id == type_id) + board->type = &board_types[i]; + + if (board->type) { + return 0; + } else { + netif_err(efx, probe, efx->net_dev, "unknown board type %d\n", + type_id); + return -ENODEV; + } +} diff --git a/drivers/net/ethernet/sfc/falcon/farch.c b/drivers/net/ethernet/sfc/falcon/farch.c new file mode 100644 index 0000000..05916c7 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/farch.c @@ -0,0 +1,2892 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "farch_regs.h" +#include "io.h" +#include "workarounds.h" + +/* Falcon-architecture (SFC4000) support */ + +/************************************************************************** + * + * Configurable values + * + ************************************************************************** + */ + +/* This is set to 16 for a good reason. In summary, if larger than + * 16, the descriptor cache holds more than a default socket + * buffer's worth of packets (for UDP we can only have at most one + * socket buffer's worth outstanding). This combined with the fact + * that we only get 1 TX event per descriptor cache means the NIC + * goes idle. + */ +#define TX_DC_ENTRIES 16 +#define TX_DC_ENTRIES_ORDER 1 + +#define RX_DC_ENTRIES 64 +#define RX_DC_ENTRIES_ORDER 3 + +/* If EF4_MAX_INT_ERRORS internal errors occur within + * EF4_INT_ERROR_EXPIRE seconds, we consider the NIC broken and + * disable it. + */ +#define EF4_INT_ERROR_EXPIRE 3600 +#define EF4_MAX_INT_ERRORS 5 + +/* Depth of RX flush request fifo */ +#define EF4_RX_FLUSH_COUNT 4 + +/* Driver generated events */ +#define _EF4_CHANNEL_MAGIC_TEST 0x000101 +#define _EF4_CHANNEL_MAGIC_FILL 0x000102 +#define _EF4_CHANNEL_MAGIC_RX_DRAIN 0x000103 +#define _EF4_CHANNEL_MAGIC_TX_DRAIN 0x000104 + +#define _EF4_CHANNEL_MAGIC(_code, _data) ((_code) << 8 | (_data)) +#define _EF4_CHANNEL_MAGIC_CODE(_magic) ((_magic) >> 8) + +#define EF4_CHANNEL_MAGIC_TEST(_channel) \ + _EF4_CHANNEL_MAGIC(_EF4_CHANNEL_MAGIC_TEST, (_channel)->channel) +#define EF4_CHANNEL_MAGIC_FILL(_rx_queue) \ + _EF4_CHANNEL_MAGIC(_EF4_CHANNEL_MAGIC_FILL, \ + ef4_rx_queue_index(_rx_queue)) +#define EF4_CHANNEL_MAGIC_RX_DRAIN(_rx_queue) \ + _EF4_CHANNEL_MAGIC(_EF4_CHANNEL_MAGIC_RX_DRAIN, \ + ef4_rx_queue_index(_rx_queue)) +#define EF4_CHANNEL_MAGIC_TX_DRAIN(_tx_queue) \ + _EF4_CHANNEL_MAGIC(_EF4_CHANNEL_MAGIC_TX_DRAIN, \ + (_tx_queue)->queue) + +static void ef4_farch_magic_event(struct ef4_channel *channel, u32 magic); + +/************************************************************************** + * + * Hardware access + * + **************************************************************************/ + +static inline void ef4_write_buf_tbl(struct ef4_nic *efx, ef4_qword_t *value, + unsigned int index) +{ + ef4_sram_writeq(efx, efx->membase + efx->type->buf_tbl_base, + value, index); +} + +static bool ef4_masked_compare_oword(const ef4_oword_t *a, const ef4_oword_t *b, + const ef4_oword_t *mask) +{ + return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) || + ((a->u64[1] ^ b->u64[1]) & mask->u64[1]); +} + +int ef4_farch_test_registers(struct ef4_nic *efx, + const struct ef4_farch_register_test *regs, + size_t n_regs) +{ + unsigned address = 0; + int i, j; + ef4_oword_t mask, imask, original, reg, buf; + + for (i = 0; i < n_regs; ++i) { + address = regs[i].address; + mask = imask = regs[i].mask; + EF4_INVERT_OWORD(imask); + + ef4_reado(efx, &original, address); + + /* bit sweep on and off */ + for (j = 0; j < 128; j++) { + if (!EF4_EXTRACT_OWORD32(mask, j, j)) + continue; + + /* Test this testable bit can be set in isolation */ + EF4_AND_OWORD(reg, original, mask); + EF4_SET_OWORD32(reg, j, j, 1); + + ef4_writeo(efx, ®, address); + ef4_reado(efx, &buf, address); + + if (ef4_masked_compare_oword(®, &buf, &mask)) + goto fail; + + /* Test this testable bit can be cleared in isolation */ + EF4_OR_OWORD(reg, original, mask); + EF4_SET_OWORD32(reg, j, j, 0); + + ef4_writeo(efx, ®, address); + ef4_reado(efx, &buf, address); + + if (ef4_masked_compare_oword(®, &buf, &mask)) + goto fail; + } + + ef4_writeo(efx, &original, address); + } + + return 0; + +fail: + netif_err(efx, hw, efx->net_dev, + "wrote "EF4_OWORD_FMT" read "EF4_OWORD_FMT + " at address 0x%x mask "EF4_OWORD_FMT"\n", EF4_OWORD_VAL(reg), + EF4_OWORD_VAL(buf), address, EF4_OWORD_VAL(mask)); + return -EIO; +} + +/************************************************************************** + * + * Special buffer handling + * Special buffers are used for event queues and the TX and RX + * descriptor rings. + * + *************************************************************************/ + +/* + * Initialise a special buffer + * + * This will define a buffer (previously allocated via + * ef4_alloc_special_buffer()) in the buffer table, allowing + * it to be used for event queues, descriptor rings etc. + */ +static void +ef4_init_special_buffer(struct ef4_nic *efx, struct ef4_special_buffer *buffer) +{ + ef4_qword_t buf_desc; + unsigned int index; + dma_addr_t dma_addr; + int i; + + EF4_BUG_ON_PARANOID(!buffer->buf.addr); + + /* Write buffer descriptors to NIC */ + for (i = 0; i < buffer->entries; i++) { + index = buffer->index + i; + dma_addr = buffer->buf.dma_addr + (i * EF4_BUF_SIZE); + netif_dbg(efx, probe, efx->net_dev, + "mapping special buffer %d at %llx\n", + index, (unsigned long long)dma_addr); + EF4_POPULATE_QWORD_3(buf_desc, + FRF_AZ_BUF_ADR_REGION, 0, + FRF_AZ_BUF_ADR_FBUF, dma_addr >> 12, + FRF_AZ_BUF_OWNER_ID_FBUF, 0); + ef4_write_buf_tbl(efx, &buf_desc, index); + } +} + +/* Unmaps a buffer and clears the buffer table entries */ +static void +ef4_fini_special_buffer(struct ef4_nic *efx, struct ef4_special_buffer *buffer) +{ + ef4_oword_t buf_tbl_upd; + unsigned int start = buffer->index; + unsigned int end = (buffer->index + buffer->entries - 1); + + if (!buffer->entries) + return; + + netif_dbg(efx, hw, efx->net_dev, "unmapping special buffers %d-%d\n", + buffer->index, buffer->index + buffer->entries - 1); + + EF4_POPULATE_OWORD_4(buf_tbl_upd, + FRF_AZ_BUF_UPD_CMD, 0, + FRF_AZ_BUF_CLR_CMD, 1, + FRF_AZ_BUF_CLR_END_ID, end, + FRF_AZ_BUF_CLR_START_ID, start); + ef4_writeo(efx, &buf_tbl_upd, FR_AZ_BUF_TBL_UPD); +} + +/* + * Allocate a new special buffer + * + * This allocates memory for a new buffer, clears it and allocates a + * new buffer ID range. It does not write into the buffer table. + * + * This call will allocate 4KB buffers, since 8KB buffers can't be + * used for event queues and descriptor rings. + */ +static int ef4_alloc_special_buffer(struct ef4_nic *efx, + struct ef4_special_buffer *buffer, + unsigned int len) +{ + len = ALIGN(len, EF4_BUF_SIZE); + + if (ef4_nic_alloc_buffer(efx, &buffer->buf, len, GFP_KERNEL)) + return -ENOMEM; + buffer->entries = len / EF4_BUF_SIZE; + BUG_ON(buffer->buf.dma_addr & (EF4_BUF_SIZE - 1)); + + /* Select new buffer ID */ + buffer->index = efx->next_buffer_table; + efx->next_buffer_table += buffer->entries; + + netif_dbg(efx, probe, efx->net_dev, + "allocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->buf.dma_addr, len, + buffer->buf.addr, (u64)virt_to_phys(buffer->buf.addr)); + + return 0; +} + +static void +ef4_free_special_buffer(struct ef4_nic *efx, struct ef4_special_buffer *buffer) +{ + if (!buffer->buf.addr) + return; + + netif_dbg(efx, hw, efx->net_dev, + "deallocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->buf.dma_addr, buffer->buf.len, + buffer->buf.addr, (u64)virt_to_phys(buffer->buf.addr)); + + ef4_nic_free_buffer(efx, &buffer->buf); + buffer->entries = 0; +} + +/************************************************************************** + * + * TX path + * + **************************************************************************/ + +/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */ +static inline void ef4_farch_notify_tx_desc(struct ef4_tx_queue *tx_queue) +{ + unsigned write_ptr; + ef4_dword_t reg; + + write_ptr = tx_queue->write_count & tx_queue->ptr_mask; + EF4_POPULATE_DWORD_1(reg, FRF_AZ_TX_DESC_WPTR_DWORD, write_ptr); + ef4_writed_page(tx_queue->efx, ®, + FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue); +} + +/* Write pointer and first descriptor for TX descriptor ring */ +static inline void ef4_farch_push_tx_desc(struct ef4_tx_queue *tx_queue, + const ef4_qword_t *txd) +{ + unsigned write_ptr; + ef4_oword_t reg; + + BUILD_BUG_ON(FRF_AZ_TX_DESC_LBN != 0); + BUILD_BUG_ON(FR_AA_TX_DESC_UPD_KER != FR_BZ_TX_DESC_UPD_P0); + + write_ptr = tx_queue->write_count & tx_queue->ptr_mask; + EF4_POPULATE_OWORD_2(reg, FRF_AZ_TX_DESC_PUSH_CMD, true, + FRF_AZ_TX_DESC_WPTR, write_ptr); + reg.qword[0] = *txd; + ef4_writeo_page(tx_queue->efx, ®, + FR_BZ_TX_DESC_UPD_P0, tx_queue->queue); +} + + +/* For each entry inserted into the software descriptor ring, create a + * descriptor in the hardware TX descriptor ring (in host memory), and + * write a doorbell. + */ +void ef4_farch_tx_write(struct ef4_tx_queue *tx_queue) +{ + struct ef4_tx_buffer *buffer; + ef4_qword_t *txd; + unsigned write_ptr; + unsigned old_write_count = tx_queue->write_count; + + tx_queue->xmit_more_available = false; + if (unlikely(tx_queue->write_count == tx_queue->insert_count)) + return; + + do { + write_ptr = tx_queue->write_count & tx_queue->ptr_mask; + buffer = &tx_queue->buffer[write_ptr]; + txd = ef4_tx_desc(tx_queue, write_ptr); + ++tx_queue->write_count; + + EF4_BUG_ON_PARANOID(buffer->flags & EF4_TX_BUF_OPTION); + + /* Create TX descriptor ring entry */ + BUILD_BUG_ON(EF4_TX_BUF_CONT != 1); + EF4_POPULATE_QWORD_4(*txd, + FSF_AZ_TX_KER_CONT, + buffer->flags & EF4_TX_BUF_CONT, + FSF_AZ_TX_KER_BYTE_COUNT, buffer->len, + FSF_AZ_TX_KER_BUF_REGION, 0, + FSF_AZ_TX_KER_BUF_ADDR, buffer->dma_addr); + } while (tx_queue->write_count != tx_queue->insert_count); + + wmb(); /* Ensure descriptors are written before they are fetched */ + + if (ef4_nic_may_push_tx_desc(tx_queue, old_write_count)) { + txd = ef4_tx_desc(tx_queue, + old_write_count & tx_queue->ptr_mask); + ef4_farch_push_tx_desc(tx_queue, txd); + ++tx_queue->pushes; + } else { + ef4_farch_notify_tx_desc(tx_queue); + } +} + +unsigned int ef4_farch_tx_limit_len(struct ef4_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len) +{ + /* Don't cross 4K boundaries with descriptors. */ + unsigned int limit = (~dma_addr & (EF4_PAGE_SIZE - 1)) + 1; + + len = min(limit, len); + + if (EF4_WORKAROUND_5391(tx_queue->efx) && (dma_addr & 0xf)) + len = min_t(unsigned int, len, 512 - (dma_addr & 0xf)); + + return len; +} + + +/* Allocate hardware resources for a TX queue */ +int ef4_farch_tx_probe(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + unsigned entries; + + entries = tx_queue->ptr_mask + 1; + return ef4_alloc_special_buffer(efx, &tx_queue->txd, + entries * sizeof(ef4_qword_t)); +} + +void ef4_farch_tx_init(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + ef4_oword_t reg; + + /* Pin TX descriptor ring */ + ef4_init_special_buffer(efx, &tx_queue->txd); + + /* Push TX descriptor ring to card */ + EF4_POPULATE_OWORD_10(reg, + FRF_AZ_TX_DESCQ_EN, 1, + FRF_AZ_TX_ISCSI_DDIG_EN, 0, + FRF_AZ_TX_ISCSI_HDIG_EN, 0, + FRF_AZ_TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, + FRF_AZ_TX_DESCQ_EVQ_ID, + tx_queue->channel->channel, + FRF_AZ_TX_DESCQ_OWNER_ID, 0, + FRF_AZ_TX_DESCQ_LABEL, tx_queue->queue, + FRF_AZ_TX_DESCQ_SIZE, + __ffs(tx_queue->txd.entries), + FRF_AZ_TX_DESCQ_TYPE, 0, + FRF_BZ_TX_NON_IP_DROP_DIS, 1); + + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) { + int csum = tx_queue->queue & EF4_TXQ_TYPE_OFFLOAD; + EF4_SET_OWORD_FIELD(reg, FRF_BZ_TX_IP_CHKSM_DIS, !csum); + EF4_SET_OWORD_FIELD(reg, FRF_BZ_TX_TCP_CHKSM_DIS, + !csum); + } + + ef4_writeo_table(efx, ®, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0) { + /* Only 128 bits in this register */ + BUILD_BUG_ON(EF4_MAX_TX_QUEUES > 128); + + ef4_reado(efx, ®, FR_AA_TX_CHKSM_CFG); + if (tx_queue->queue & EF4_TXQ_TYPE_OFFLOAD) + __clear_bit_le(tx_queue->queue, ®); + else + __set_bit_le(tx_queue->queue, ®); + ef4_writeo(efx, ®, FR_AA_TX_CHKSM_CFG); + } + + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) { + EF4_POPULATE_OWORD_1(reg, + FRF_BZ_TX_PACE, + (tx_queue->queue & EF4_TXQ_TYPE_HIGHPRI) ? + FFE_BZ_TX_PACE_OFF : + FFE_BZ_TX_PACE_RESERVED); + ef4_writeo_table(efx, ®, FR_BZ_TX_PACE_TBL, + tx_queue->queue); + } +} + +static void ef4_farch_flush_tx_queue(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + ef4_oword_t tx_flush_descq; + + WARN_ON(atomic_read(&tx_queue->flush_outstanding)); + atomic_set(&tx_queue->flush_outstanding, 1); + + EF4_POPULATE_OWORD_2(tx_flush_descq, + FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); + ef4_writeo(efx, &tx_flush_descq, FR_AZ_TX_FLUSH_DESCQ); +} + +void ef4_farch_tx_fini(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + ef4_oword_t tx_desc_ptr; + + /* Remove TX descriptor ring from card */ + EF4_ZERO_OWORD(tx_desc_ptr); + ef4_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + /* Unpin TX descriptor ring */ + ef4_fini_special_buffer(efx, &tx_queue->txd); +} + +/* Free buffers backing TX queue */ +void ef4_farch_tx_remove(struct ef4_tx_queue *tx_queue) +{ + ef4_free_special_buffer(tx_queue->efx, &tx_queue->txd); +} + +/************************************************************************** + * + * RX path + * + **************************************************************************/ + +/* This creates an entry in the RX descriptor queue */ +static inline void +ef4_farch_build_rx_desc(struct ef4_rx_queue *rx_queue, unsigned index) +{ + struct ef4_rx_buffer *rx_buf; + ef4_qword_t *rxd; + + rxd = ef4_rx_desc(rx_queue, index); + rx_buf = ef4_rx_buffer(rx_queue, index); + EF4_POPULATE_QWORD_3(*rxd, + FSF_AZ_RX_KER_BUF_SIZE, + rx_buf->len - + rx_queue->efx->type->rx_buffer_padding, + FSF_AZ_RX_KER_BUF_REGION, 0, + FSF_AZ_RX_KER_BUF_ADDR, rx_buf->dma_addr); +} + +/* This writes to the RX_DESC_WPTR register for the specified receive + * descriptor ring. + */ +void ef4_farch_rx_write(struct ef4_rx_queue *rx_queue) +{ + struct ef4_nic *efx = rx_queue->efx; + ef4_dword_t reg; + unsigned write_ptr; + + while (rx_queue->notified_count != rx_queue->added_count) { + ef4_farch_build_rx_desc( + rx_queue, + rx_queue->notified_count & rx_queue->ptr_mask); + ++rx_queue->notified_count; + } + + wmb(); + write_ptr = rx_queue->added_count & rx_queue->ptr_mask; + EF4_POPULATE_DWORD_1(reg, FRF_AZ_RX_DESC_WPTR_DWORD, write_ptr); + ef4_writed_page(efx, ®, FR_AZ_RX_DESC_UPD_DWORD_P0, + ef4_rx_queue_index(rx_queue)); +} + +int ef4_farch_rx_probe(struct ef4_rx_queue *rx_queue) +{ + struct ef4_nic *efx = rx_queue->efx; + unsigned entries; + + entries = rx_queue->ptr_mask + 1; + return ef4_alloc_special_buffer(efx, &rx_queue->rxd, + entries * sizeof(ef4_qword_t)); +} + +void ef4_farch_rx_init(struct ef4_rx_queue *rx_queue) +{ + ef4_oword_t rx_desc_ptr; + struct ef4_nic *efx = rx_queue->efx; + bool is_b0 = ef4_nic_rev(efx) >= EF4_REV_FALCON_B0; + bool iscsi_digest_en = is_b0; + bool jumbo_en; + + /* For kernel-mode queues in Falcon A1, the JUMBO flag enables + * DMA to continue after a PCIe page boundary (and scattering + * is not possible). In Falcon B0 and Siena, it enables + * scatter. + */ + jumbo_en = !is_b0 || efx->rx_scatter; + + netif_dbg(efx, hw, efx->net_dev, + "RX queue %d ring in special buffers %d-%d\n", + ef4_rx_queue_index(rx_queue), rx_queue->rxd.index, + rx_queue->rxd.index + rx_queue->rxd.entries - 1); + + rx_queue->scatter_n = 0; + + /* Pin RX descriptor ring */ + ef4_init_special_buffer(efx, &rx_queue->rxd); + + /* Push RX descriptor ring to card */ + EF4_POPULATE_OWORD_10(rx_desc_ptr, + FRF_AZ_RX_ISCSI_DDIG_EN, iscsi_digest_en, + FRF_AZ_RX_ISCSI_HDIG_EN, iscsi_digest_en, + FRF_AZ_RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, + FRF_AZ_RX_DESCQ_EVQ_ID, + ef4_rx_queue_channel(rx_queue)->channel, + FRF_AZ_RX_DESCQ_OWNER_ID, 0, + FRF_AZ_RX_DESCQ_LABEL, + ef4_rx_queue_index(rx_queue), + FRF_AZ_RX_DESCQ_SIZE, + __ffs(rx_queue->rxd.entries), + FRF_AZ_RX_DESCQ_TYPE, 0 /* kernel queue */ , + FRF_AZ_RX_DESCQ_JUMBO, jumbo_en, + FRF_AZ_RX_DESCQ_EN, 1); + ef4_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + ef4_rx_queue_index(rx_queue)); +} + +static void ef4_farch_flush_rx_queue(struct ef4_rx_queue *rx_queue) +{ + struct ef4_nic *efx = rx_queue->efx; + ef4_oword_t rx_flush_descq; + + EF4_POPULATE_OWORD_2(rx_flush_descq, + FRF_AZ_RX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_RX_FLUSH_DESCQ, + ef4_rx_queue_index(rx_queue)); + ef4_writeo(efx, &rx_flush_descq, FR_AZ_RX_FLUSH_DESCQ); +} + +void ef4_farch_rx_fini(struct ef4_rx_queue *rx_queue) +{ + ef4_oword_t rx_desc_ptr; + struct ef4_nic *efx = rx_queue->efx; + + /* Remove RX descriptor ring from card */ + EF4_ZERO_OWORD(rx_desc_ptr); + ef4_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + ef4_rx_queue_index(rx_queue)); + + /* Unpin RX descriptor ring */ + ef4_fini_special_buffer(efx, &rx_queue->rxd); +} + +/* Free buffers backing RX queue */ +void ef4_farch_rx_remove(struct ef4_rx_queue *rx_queue) +{ + ef4_free_special_buffer(rx_queue->efx, &rx_queue->rxd); +} + +/************************************************************************** + * + * Flush handling + * + **************************************************************************/ + +/* ef4_farch_flush_queues() must be woken up when all flushes are completed, + * or more RX flushes can be kicked off. + */ +static bool ef4_farch_flush_wake(struct ef4_nic *efx) +{ + /* Ensure that all updates are visible to ef4_farch_flush_queues() */ + smp_mb(); + + return (atomic_read(&efx->active_queues) == 0 || + (atomic_read(&efx->rxq_flush_outstanding) < EF4_RX_FLUSH_COUNT + && atomic_read(&efx->rxq_flush_pending) > 0)); +} + +static bool ef4_check_tx_flush_complete(struct ef4_nic *efx) +{ + bool i = true; + ef4_oword_t txd_ptr_tbl; + struct ef4_channel *channel; + struct ef4_tx_queue *tx_queue; + + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_tx_queue(tx_queue, channel) { + ef4_reado_table(efx, &txd_ptr_tbl, + FR_BZ_TX_DESC_PTR_TBL, tx_queue->queue); + if (EF4_OWORD_FIELD(txd_ptr_tbl, + FRF_AZ_TX_DESCQ_FLUSH) || + EF4_OWORD_FIELD(txd_ptr_tbl, + FRF_AZ_TX_DESCQ_EN)) { + netif_dbg(efx, hw, efx->net_dev, + "flush did not complete on TXQ %d\n", + tx_queue->queue); + i = false; + } else if (atomic_cmpxchg(&tx_queue->flush_outstanding, + 1, 0)) { + /* The flush is complete, but we didn't + * receive a flush completion event + */ + netif_dbg(efx, hw, efx->net_dev, + "flush complete on TXQ %d, so drain " + "the queue\n", tx_queue->queue); + /* Don't need to increment active_queues as it + * has already been incremented for the queues + * which did not drain + */ + ef4_farch_magic_event(channel, + EF4_CHANNEL_MAGIC_TX_DRAIN( + tx_queue)); + } + } + } + + return i; +} + +/* Flush all the transmit queues, and continue flushing receive queues until + * they're all flushed. Wait for the DRAIN events to be received so that there + * are no more RX and TX events left on any channel. */ +static int ef4_farch_do_flush(struct ef4_nic *efx) +{ + unsigned timeout = msecs_to_jiffies(5000); /* 5s for all flushes and drains */ + struct ef4_channel *channel; + struct ef4_rx_queue *rx_queue; + struct ef4_tx_queue *tx_queue; + int rc = 0; + + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_tx_queue(tx_queue, channel) { + ef4_farch_flush_tx_queue(tx_queue); + } + ef4_for_each_channel_rx_queue(rx_queue, channel) { + rx_queue->flush_pending = true; + atomic_inc(&efx->rxq_flush_pending); + } + } + + while (timeout && atomic_read(&efx->active_queues) > 0) { + /* The hardware supports four concurrent rx flushes, each of + * which may need to be retried if there is an outstanding + * descriptor fetch + */ + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_rx_queue(rx_queue, channel) { + if (atomic_read(&efx->rxq_flush_outstanding) >= + EF4_RX_FLUSH_COUNT) + break; + + if (rx_queue->flush_pending) { + rx_queue->flush_pending = false; + atomic_dec(&efx->rxq_flush_pending); + atomic_inc(&efx->rxq_flush_outstanding); + ef4_farch_flush_rx_queue(rx_queue); + } + } + } + + timeout = wait_event_timeout(efx->flush_wq, + ef4_farch_flush_wake(efx), + timeout); + } + + if (atomic_read(&efx->active_queues) && + !ef4_check_tx_flush_complete(efx)) { + netif_err(efx, hw, efx->net_dev, "failed to flush %d queues " + "(rx %d+%d)\n", atomic_read(&efx->active_queues), + atomic_read(&efx->rxq_flush_outstanding), + atomic_read(&efx->rxq_flush_pending)); + rc = -ETIMEDOUT; + + atomic_set(&efx->active_queues, 0); + atomic_set(&efx->rxq_flush_pending, 0); + atomic_set(&efx->rxq_flush_outstanding, 0); + } + + return rc; +} + +int ef4_farch_fini_dmaq(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + struct ef4_tx_queue *tx_queue; + struct ef4_rx_queue *rx_queue; + int rc = 0; + + /* Do not attempt to write to the NIC during EEH recovery */ + if (efx->state != STATE_RECOVERY) { + /* Only perform flush if DMA is enabled */ + if (efx->pci_dev->is_busmaster) { + efx->type->prepare_flush(efx); + rc = ef4_farch_do_flush(efx); + efx->type->finish_flush(efx); + } + + ef4_for_each_channel(channel, efx) { + ef4_for_each_channel_rx_queue(rx_queue, channel) + ef4_farch_rx_fini(rx_queue); + ef4_for_each_channel_tx_queue(tx_queue, channel) + ef4_farch_tx_fini(tx_queue); + } + } + + return rc; +} + +/* Reset queue and flush accounting after FLR + * + * One possible cause of FLR recovery is that DMA may be failing (eg. if bus + * mastering was disabled), in which case we don't receive (RXQ) flush + * completion events. This means that efx->rxq_flush_outstanding remained at 4 + * after the FLR; also, efx->active_queues was non-zero (as no flush completion + * events were received, and we didn't go through ef4_check_tx_flush_complete()) + * If we don't fix this up, on the next call to ef4_realloc_channels() we won't + * flush any RX queues because efx->rxq_flush_outstanding is at the limit of 4 + * for batched flush requests; and the efx->active_queues gets messed up because + * we keep incrementing for the newly initialised queues, but it never went to + * zero previously. Then we get a timeout every time we try to restart the + * queues, as it doesn't go back to zero when we should be flushing the queues. + */ +void ef4_farch_finish_flr(struct ef4_nic *efx) +{ + atomic_set(&efx->rxq_flush_pending, 0); + atomic_set(&efx->rxq_flush_outstanding, 0); + atomic_set(&efx->active_queues, 0); +} + + +/************************************************************************** + * + * Event queue processing + * Event queues are processed by per-channel tasklets. + * + **************************************************************************/ + +/* Update a channel's event queue's read pointer (RPTR) register + * + * This writes the EVQ_RPTR_REG register for the specified channel's + * event queue. + */ +void ef4_farch_ev_read_ack(struct ef4_channel *channel) +{ + ef4_dword_t reg; + struct ef4_nic *efx = channel->efx; + + EF4_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, + channel->eventq_read_ptr & channel->eventq_mask); + + /* For Falcon A1, EVQ_RPTR_KER is documented as having a step size + * of 4 bytes, but it is really 16 bytes just like later revisions. + */ + ef4_writed(efx, ®, + efx->type->evq_rptr_tbl_base + + FR_BZ_EVQ_RPTR_STEP * channel->channel); +} + +/* Use HW to insert a SW defined event */ +void ef4_farch_generate_event(struct ef4_nic *efx, unsigned int evq, + ef4_qword_t *event) +{ + ef4_oword_t drv_ev_reg; + + BUILD_BUG_ON(FRF_AZ_DRV_EV_DATA_LBN != 0 || + FRF_AZ_DRV_EV_DATA_WIDTH != 64); + drv_ev_reg.u32[0] = event->u32[0]; + drv_ev_reg.u32[1] = event->u32[1]; + drv_ev_reg.u32[2] = 0; + drv_ev_reg.u32[3] = 0; + EF4_SET_OWORD_FIELD(drv_ev_reg, FRF_AZ_DRV_EV_QID, evq); + ef4_writeo(efx, &drv_ev_reg, FR_AZ_DRV_EV); +} + +static void ef4_farch_magic_event(struct ef4_channel *channel, u32 magic) +{ + ef4_qword_t event; + + EF4_POPULATE_QWORD_2(event, FSF_AZ_EV_CODE, + FSE_AZ_EV_CODE_DRV_GEN_EV, + FSF_AZ_DRV_GEN_EV_MAGIC, magic); + ef4_farch_generate_event(channel->efx, channel->channel, &event); +} + +/* Handle a transmit completion event + * + * The NIC batches TX completion events; the message we receive is of + * the form "complete all TX events up to this index". + */ +static int +ef4_farch_handle_tx_event(struct ef4_channel *channel, ef4_qword_t *event) +{ + unsigned int tx_ev_desc_ptr; + unsigned int tx_ev_q_label; + struct ef4_tx_queue *tx_queue; + struct ef4_nic *efx = channel->efx; + int tx_packets = 0; + + if (unlikely(ACCESS_ONCE(efx->reset_pending))) + return 0; + + if (likely(EF4_QWORD_FIELD(*event, FSF_AZ_TX_EV_COMP))) { + /* Transmit completion */ + tx_ev_desc_ptr = EF4_QWORD_FIELD(*event, FSF_AZ_TX_EV_DESC_PTR); + tx_ev_q_label = EF4_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = ef4_channel_get_tx_queue( + channel, tx_ev_q_label % EF4_TXQ_TYPES); + tx_packets = ((tx_ev_desc_ptr - tx_queue->read_count) & + tx_queue->ptr_mask); + ef4_xmit_done(tx_queue, tx_ev_desc_ptr); + } else if (EF4_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) { + /* Rewrite the FIFO write pointer */ + tx_ev_q_label = EF4_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = ef4_channel_get_tx_queue( + channel, tx_ev_q_label % EF4_TXQ_TYPES); + + netif_tx_lock(efx->net_dev); + ef4_farch_notify_tx_desc(tx_queue); + netif_tx_unlock(efx->net_dev); + } else if (EF4_QWORD_FIELD(*event, FSF_AZ_TX_EV_PKT_ERR)) { + ef4_schedule_reset(efx, RESET_TYPE_DMA_ERROR); + } else { + netif_err(efx, tx_err, efx->net_dev, + "channel %d unexpected TX event " + EF4_QWORD_FMT"\n", channel->channel, + EF4_QWORD_VAL(*event)); + } + + return tx_packets; +} + +/* Detect errors included in the rx_evt_pkt_ok bit. */ +static u16 ef4_farch_handle_rx_not_ok(struct ef4_rx_queue *rx_queue, + const ef4_qword_t *event) +{ + struct ef4_channel *channel = ef4_rx_queue_channel(rx_queue); + struct ef4_nic *efx = rx_queue->efx; + bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; + bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; + bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; + bool rx_ev_other_err, rx_ev_pause_frm; + bool rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned rx_ev_pkt_type; + + rx_ev_hdr_type = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + rx_ev_mcast_pkt = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + rx_ev_tobe_disc = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_TOBE_DISC); + rx_ev_pkt_type = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_TYPE); + rx_ev_buf_owner_id_err = EF4_QWORD_FIELD(*event, + FSF_AZ_RX_EV_BUF_OWNER_ID_ERR); + rx_ev_ip_hdr_chksum_err = EF4_QWORD_FIELD(*event, + FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR); + rx_ev_tcp_udp_chksum_err = EF4_QWORD_FIELD(*event, + FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR); + rx_ev_eth_crc_err = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_ETH_CRC_ERR); + rx_ev_frm_trunc = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_FRM_TRUNC); + rx_ev_drib_nib = ((ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) ? + 0 : EF4_QWORD_FIELD(*event, FSF_AA_RX_EV_DRIB_NIB)); + rx_ev_pause_frm = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_PAUSE_FRM_ERR); + + /* Every error apart from tobe_disc and pause_frm */ + rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | + rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | + rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); + + /* Count errors that are not in MAC stats. Ignore expected + * checksum errors during self-test. */ + if (rx_ev_frm_trunc) + ++channel->n_rx_frm_trunc; + else if (rx_ev_tobe_disc) + ++channel->n_rx_tobe_disc; + else if (!efx->loopback_selftest) { + if (rx_ev_ip_hdr_chksum_err) + ++channel->n_rx_ip_hdr_chksum_err; + else if (rx_ev_tcp_udp_chksum_err) + ++channel->n_rx_tcp_udp_chksum_err; + } + + /* TOBE_DISC is expected on unicast mismatches; don't print out an + * error message. FRM_TRUNC indicates RXDP dropped the packet due + * to a FIFO overflow. + */ +#ifdef DEBUG + if (rx_ev_other_err && net_ratelimit()) { + netif_dbg(efx, rx_err, efx->net_dev, + " RX queue %d unexpected RX event " + EF4_QWORD_FMT "%s%s%s%s%s%s%s%s\n", + ef4_rx_queue_index(rx_queue), EF4_QWORD_VAL(*event), + rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", + rx_ev_ip_hdr_chksum_err ? + " [IP_HDR_CHKSUM_ERR]" : "", + rx_ev_tcp_udp_chksum_err ? + " [TCP_UDP_CHKSUM_ERR]" : "", + rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", + rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", + rx_ev_drib_nib ? " [DRIB_NIB]" : "", + rx_ev_tobe_disc ? " [TOBE_DISC]" : "", + rx_ev_pause_frm ? " [PAUSE]" : ""); + } +#endif + + /* The frame must be discarded if any of these are true. */ + return (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | + rx_ev_tobe_disc | rx_ev_pause_frm) ? + EF4_RX_PKT_DISCARD : 0; +} + +/* Handle receive events that are not in-order. Return true if this + * can be handled as a partial packet discard, false if it's more + * serious. + */ +static bool +ef4_farch_handle_rx_bad_index(struct ef4_rx_queue *rx_queue, unsigned index) +{ + struct ef4_channel *channel = ef4_rx_queue_channel(rx_queue); + struct ef4_nic *efx = rx_queue->efx; + unsigned expected, dropped; + + if (rx_queue->scatter_n && + index == ((rx_queue->removed_count + rx_queue->scatter_n - 1) & + rx_queue->ptr_mask)) { + ++channel->n_rx_nodesc_trunc; + return true; + } + + expected = rx_queue->removed_count & rx_queue->ptr_mask; + dropped = (index - expected) & rx_queue->ptr_mask; + netif_info(efx, rx_err, efx->net_dev, + "dropped %d events (index=%d expected=%d)\n", + dropped, index, expected); + + ef4_schedule_reset(efx, EF4_WORKAROUND_5676(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); + return false; +} + +/* Handle a packet received event + * + * The NIC gives a "discard" flag if it's a unicast packet with the + * wrong destination address + * Also "is multicast" and "matches multicast filter" flags can be used to + * discard non-matching multicast packets. + */ +static void +ef4_farch_handle_rx_event(struct ef4_channel *channel, const ef4_qword_t *event) +{ + unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt; + unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned expected_ptr; + bool rx_ev_pkt_ok, rx_ev_sop, rx_ev_cont; + u16 flags; + struct ef4_rx_queue *rx_queue; + struct ef4_nic *efx = channel->efx; + + if (unlikely(ACCESS_ONCE(efx->reset_pending))) + return; + + rx_ev_cont = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT); + rx_ev_sop = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP); + WARN_ON(EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_Q_LABEL) != + channel->channel); + + rx_queue = ef4_channel_get_rx_queue(channel); + + rx_ev_desc_ptr = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_DESC_PTR); + expected_ptr = ((rx_queue->removed_count + rx_queue->scatter_n) & + rx_queue->ptr_mask); + + /* Check for partial drops and other errors */ + if (unlikely(rx_ev_desc_ptr != expected_ptr) || + unlikely(rx_ev_sop != (rx_queue->scatter_n == 0))) { + if (rx_ev_desc_ptr != expected_ptr && + !ef4_farch_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr)) + return; + + /* Discard all pending fragments */ + if (rx_queue->scatter_n) { + ef4_rx_packet( + rx_queue, + rx_queue->removed_count & rx_queue->ptr_mask, + rx_queue->scatter_n, 0, EF4_RX_PKT_DISCARD); + rx_queue->removed_count += rx_queue->scatter_n; + rx_queue->scatter_n = 0; + } + + /* Return if there is no new fragment */ + if (rx_ev_desc_ptr != expected_ptr) + return; + + /* Discard new fragment if not SOP */ + if (!rx_ev_sop) { + ef4_rx_packet( + rx_queue, + rx_queue->removed_count & rx_queue->ptr_mask, + 1, 0, EF4_RX_PKT_DISCARD); + ++rx_queue->removed_count; + return; + } + } + + ++rx_queue->scatter_n; + if (rx_ev_cont) + return; + + rx_ev_byte_cnt = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT); + rx_ev_pkt_ok = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK); + rx_ev_hdr_type = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + + if (likely(rx_ev_pkt_ok)) { + /* If packet is marked as OK then we can rely on the + * hardware checksum and classification. + */ + flags = 0; + switch (rx_ev_hdr_type) { + case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP: + flags |= EF4_RX_PKT_TCP; + /* fall through */ + case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP: + flags |= EF4_RX_PKT_CSUMMED; + /* fall through */ + case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER: + case FSE_AZ_RX_EV_HDR_TYPE_OTHER: + break; + } + } else { + flags = ef4_farch_handle_rx_not_ok(rx_queue, event); + } + + /* Detect multicast packets that didn't match the filter */ + rx_ev_mcast_pkt = EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + if (rx_ev_mcast_pkt) { + unsigned int rx_ev_mcast_hash_match = + EF4_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_HASH_MATCH); + + if (unlikely(!rx_ev_mcast_hash_match)) { + ++channel->n_rx_mcast_mismatch; + flags |= EF4_RX_PKT_DISCARD; + } + } + + channel->irq_mod_score += 2; + + /* Handle received packet */ + ef4_rx_packet(rx_queue, + rx_queue->removed_count & rx_queue->ptr_mask, + rx_queue->scatter_n, rx_ev_byte_cnt, flags); + rx_queue->removed_count += rx_queue->scatter_n; + rx_queue->scatter_n = 0; +} + +/* If this flush done event corresponds to a &struct ef4_tx_queue, then + * send an %EF4_CHANNEL_MAGIC_TX_DRAIN event to drain the event queue + * of all transmit completions. + */ +static void +ef4_farch_handle_tx_flush_done(struct ef4_nic *efx, ef4_qword_t *event) +{ + struct ef4_tx_queue *tx_queue; + int qid; + + qid = EF4_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBDATA); + if (qid < EF4_TXQ_TYPES * efx->n_tx_channels) { + tx_queue = ef4_get_tx_queue(efx, qid / EF4_TXQ_TYPES, + qid % EF4_TXQ_TYPES); + if (atomic_cmpxchg(&tx_queue->flush_outstanding, 1, 0)) { + ef4_farch_magic_event(tx_queue->channel, + EF4_CHANNEL_MAGIC_TX_DRAIN(tx_queue)); + } + } +} + +/* If this flush done event corresponds to a &struct ef4_rx_queue: If the flush + * was successful then send an %EF4_CHANNEL_MAGIC_RX_DRAIN, otherwise add + * the RX queue back to the mask of RX queues in need of flushing. + */ +static void +ef4_farch_handle_rx_flush_done(struct ef4_nic *efx, ef4_qword_t *event) +{ + struct ef4_channel *channel; + struct ef4_rx_queue *rx_queue; + int qid; + bool failed; + + qid = EF4_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_RX_DESCQ_ID); + failed = EF4_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL); + if (qid >= efx->n_channels) + return; + channel = ef4_get_channel(efx, qid); + if (!ef4_channel_has_rx_queue(channel)) + return; + rx_queue = ef4_channel_get_rx_queue(channel); + + if (failed) { + netif_info(efx, hw, efx->net_dev, + "RXQ %d flush retry\n", qid); + rx_queue->flush_pending = true; + atomic_inc(&efx->rxq_flush_pending); + } else { + ef4_farch_magic_event(ef4_rx_queue_channel(rx_queue), + EF4_CHANNEL_MAGIC_RX_DRAIN(rx_queue)); + } + atomic_dec(&efx->rxq_flush_outstanding); + if (ef4_farch_flush_wake(efx)) + wake_up(&efx->flush_wq); +} + +static void +ef4_farch_handle_drain_event(struct ef4_channel *channel) +{ + struct ef4_nic *efx = channel->efx; + + WARN_ON(atomic_read(&efx->active_queues) == 0); + atomic_dec(&efx->active_queues); + if (ef4_farch_flush_wake(efx)) + wake_up(&efx->flush_wq); +} + +static void ef4_farch_handle_generated_event(struct ef4_channel *channel, + ef4_qword_t *event) +{ + struct ef4_nic *efx = channel->efx; + struct ef4_rx_queue *rx_queue = + ef4_channel_has_rx_queue(channel) ? + ef4_channel_get_rx_queue(channel) : NULL; + unsigned magic, code; + + magic = EF4_QWORD_FIELD(*event, FSF_AZ_DRV_GEN_EV_MAGIC); + code = _EF4_CHANNEL_MAGIC_CODE(magic); + + if (magic == EF4_CHANNEL_MAGIC_TEST(channel)) { + channel->event_test_cpu = raw_smp_processor_id(); + } else if (rx_queue && magic == EF4_CHANNEL_MAGIC_FILL(rx_queue)) { + /* The queue must be empty, so we won't receive any rx + * events, so ef4_process_channel() won't refill the + * queue. Refill it here */ + ef4_fast_push_rx_descriptors(rx_queue, true); + } else if (rx_queue && magic == EF4_CHANNEL_MAGIC_RX_DRAIN(rx_queue)) { + ef4_farch_handle_drain_event(channel); + } else if (code == _EF4_CHANNEL_MAGIC_TX_DRAIN) { + ef4_farch_handle_drain_event(channel); + } else { + netif_dbg(efx, hw, efx->net_dev, "channel %d received " + "generated event "EF4_QWORD_FMT"\n", + channel->channel, EF4_QWORD_VAL(*event)); + } +} + +static void +ef4_farch_handle_driver_event(struct ef4_channel *channel, ef4_qword_t *event) +{ + struct ef4_nic *efx = channel->efx; + unsigned int ev_sub_code; + unsigned int ev_sub_data; + + ev_sub_code = EF4_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBCODE); + ev_sub_data = EF4_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBDATA); + + switch (ev_sub_code) { + case FSE_AZ_TX_DESCQ_FLS_DONE_EV: + netif_vdbg(efx, hw, efx->net_dev, "channel %d TXQ %d flushed\n", + channel->channel, ev_sub_data); + ef4_farch_handle_tx_flush_done(efx, event); + break; + case FSE_AZ_RX_DESCQ_FLS_DONE_EV: + netif_vdbg(efx, hw, efx->net_dev, "channel %d RXQ %d flushed\n", + channel->channel, ev_sub_data); + ef4_farch_handle_rx_flush_done(efx, event); + break; + case FSE_AZ_EVQ_INIT_DONE_EV: + netif_dbg(efx, hw, efx->net_dev, + "channel %d EVQ %d initialised\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_SRM_UPD_DONE_EV: + netif_vdbg(efx, hw, efx->net_dev, + "channel %d SRAM update done\n", channel->channel); + break; + case FSE_AZ_WAKE_UP_EV: + netif_vdbg(efx, hw, efx->net_dev, + "channel %d RXQ %d wakeup event\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_TIMER_EV: + netif_vdbg(efx, hw, efx->net_dev, + "channel %d RX queue %d timer expired\n", + channel->channel, ev_sub_data); + break; + case FSE_AA_RX_RECOVER_EV: + netif_err(efx, rx_err, efx->net_dev, + "channel %d seen DRIVER RX_RESET event. " + "Resetting.\n", channel->channel); + atomic_inc(&efx->rx_reset); + ef4_schedule_reset(efx, + EF4_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : + RESET_TYPE_DISABLE); + break; + case FSE_BZ_RX_DSC_ERROR_EV: + netif_err(efx, rx_err, efx->net_dev, + "RX DMA Q %d reports descriptor fetch error." + " RX Q %d is disabled.\n", ev_sub_data, + ev_sub_data); + ef4_schedule_reset(efx, RESET_TYPE_DMA_ERROR); + break; + case FSE_BZ_TX_DSC_ERROR_EV: + netif_err(efx, tx_err, efx->net_dev, + "TX DMA Q %d reports descriptor fetch error." + " TX Q %d is disabled.\n", ev_sub_data, + ev_sub_data); + ef4_schedule_reset(efx, RESET_TYPE_DMA_ERROR); + break; + default: + netif_vdbg(efx, hw, efx->net_dev, + "channel %d unknown driver event code %d " + "data %04x\n", channel->channel, ev_sub_code, + ev_sub_data); + break; + } +} + +int ef4_farch_ev_process(struct ef4_channel *channel, int budget) +{ + struct ef4_nic *efx = channel->efx; + unsigned int read_ptr; + ef4_qword_t event, *p_event; + int ev_code; + int tx_packets = 0; + int spent = 0; + + if (budget <= 0) + return spent; + + read_ptr = channel->eventq_read_ptr; + + for (;;) { + p_event = ef4_event(channel, read_ptr); + event = *p_event; + + if (!ef4_event_present(&event)) + /* End of events */ + break; + + netif_vdbg(channel->efx, intr, channel->efx->net_dev, + "channel %d event is "EF4_QWORD_FMT"\n", + channel->channel, EF4_QWORD_VAL(event)); + + /* Clear this event by marking it all ones */ + EF4_SET_QWORD(*p_event); + + ++read_ptr; + + ev_code = EF4_QWORD_FIELD(event, FSF_AZ_EV_CODE); + + switch (ev_code) { + case FSE_AZ_EV_CODE_RX_EV: + ef4_farch_handle_rx_event(channel, &event); + if (++spent == budget) + goto out; + break; + case FSE_AZ_EV_CODE_TX_EV: + tx_packets += ef4_farch_handle_tx_event(channel, + &event); + if (tx_packets > efx->txq_entries) { + spent = budget; + goto out; + } + break; + case FSE_AZ_EV_CODE_DRV_GEN_EV: + ef4_farch_handle_generated_event(channel, &event); + break; + case FSE_AZ_EV_CODE_DRIVER_EV: + ef4_farch_handle_driver_event(channel, &event); + break; + case FSE_AZ_EV_CODE_GLOBAL_EV: + if (efx->type->handle_global_event && + efx->type->handle_global_event(channel, &event)) + break; + /* else fall through */ + default: + netif_err(channel->efx, hw, channel->efx->net_dev, + "channel %d unknown event type %d (data " + EF4_QWORD_FMT ")\n", channel->channel, + ev_code, EF4_QWORD_VAL(event)); + } + } + +out: + channel->eventq_read_ptr = read_ptr; + return spent; +} + +/* Allocate buffer table entries for event queue */ +int ef4_farch_ev_probe(struct ef4_channel *channel) +{ + struct ef4_nic *efx = channel->efx; + unsigned entries; + + entries = channel->eventq_mask + 1; + return ef4_alloc_special_buffer(efx, &channel->eventq, + entries * sizeof(ef4_qword_t)); +} + +int ef4_farch_ev_init(struct ef4_channel *channel) +{ + ef4_oword_t reg; + struct ef4_nic *efx = channel->efx; + + netif_dbg(efx, hw, efx->net_dev, + "channel %d event queue in special buffers %d-%d\n", + channel->channel, channel->eventq.index, + channel->eventq.index + channel->eventq.entries - 1); + + /* Pin event queue buffer */ + ef4_init_special_buffer(efx, &channel->eventq); + + /* Fill event queue with all ones (i.e. empty events) */ + memset(channel->eventq.buf.addr, 0xff, channel->eventq.buf.len); + + /* Push event queue to card */ + EF4_POPULATE_OWORD_3(reg, + FRF_AZ_EVQ_EN, 1, + FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries), + FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index); + ef4_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + + return 0; +} + +void ef4_farch_ev_fini(struct ef4_channel *channel) +{ + ef4_oword_t reg; + struct ef4_nic *efx = channel->efx; + + /* Remove event queue from card */ + EF4_ZERO_OWORD(reg); + ef4_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + + /* Unpin event queue */ + ef4_fini_special_buffer(efx, &channel->eventq); +} + +/* Free buffers backing event queue */ +void ef4_farch_ev_remove(struct ef4_channel *channel) +{ + ef4_free_special_buffer(channel->efx, &channel->eventq); +} + + +void ef4_farch_ev_test_generate(struct ef4_channel *channel) +{ + ef4_farch_magic_event(channel, EF4_CHANNEL_MAGIC_TEST(channel)); +} + +void ef4_farch_rx_defer_refill(struct ef4_rx_queue *rx_queue) +{ + ef4_farch_magic_event(ef4_rx_queue_channel(rx_queue), + EF4_CHANNEL_MAGIC_FILL(rx_queue)); +} + +/************************************************************************** + * + * Hardware interrupts + * The hardware interrupt handler does very little work; all the event + * queue processing is carried out by per-channel tasklets. + * + **************************************************************************/ + +/* Enable/disable/generate interrupts */ +static inline void ef4_farch_interrupts(struct ef4_nic *efx, + bool enabled, bool force) +{ + ef4_oword_t int_en_reg_ker; + + EF4_POPULATE_OWORD_3(int_en_reg_ker, + FRF_AZ_KER_INT_LEVE_SEL, efx->irq_level, + FRF_AZ_KER_INT_KER, force, + FRF_AZ_DRV_INT_EN_KER, enabled); + ef4_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER); +} + +void ef4_farch_irq_enable_master(struct ef4_nic *efx) +{ + EF4_ZERO_OWORD(*((ef4_oword_t *) efx->irq_status.addr)); + wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ + + ef4_farch_interrupts(efx, true, false); +} + +void ef4_farch_irq_disable_master(struct ef4_nic *efx) +{ + /* Disable interrupts */ + ef4_farch_interrupts(efx, false, false); +} + +/* Generate a test interrupt + * Interrupt must already have been enabled, otherwise nasty things + * may happen. + */ +int ef4_farch_irq_test_generate(struct ef4_nic *efx) +{ + ef4_farch_interrupts(efx, true, true); + return 0; +} + +/* Process a fatal interrupt + * Disable bus mastering ASAP and schedule a reset + */ +irqreturn_t ef4_farch_fatal_interrupt(struct ef4_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + ef4_oword_t *int_ker = efx->irq_status.addr; + ef4_oword_t fatal_intr; + int error, mem_perr; + + ef4_reado(efx, &fatal_intr, FR_AZ_FATAL_INTR_KER); + error = EF4_OWORD_FIELD(fatal_intr, FRF_AZ_FATAL_INTR); + + netif_err(efx, hw, efx->net_dev, "SYSTEM ERROR "EF4_OWORD_FMT" status " + EF4_OWORD_FMT ": %s\n", EF4_OWORD_VAL(*int_ker), + EF4_OWORD_VAL(fatal_intr), + error ? "disabling bus mastering" : "no recognised error"); + + /* If this is a memory parity error dump which blocks are offending */ + mem_perr = (EF4_OWORD_FIELD(fatal_intr, FRF_AZ_MEM_PERR_INT_KER) || + EF4_OWORD_FIELD(fatal_intr, FRF_AZ_SRM_PERR_INT_KER)); + if (mem_perr) { + ef4_oword_t reg; + ef4_reado(efx, ®, FR_AZ_MEM_STAT); + netif_err(efx, hw, efx->net_dev, + "SYSTEM ERROR: memory parity error "EF4_OWORD_FMT"\n", + EF4_OWORD_VAL(reg)); + } + + /* Disable both devices */ + pci_clear_master(efx->pci_dev); + if (ef4_nic_is_dual_func(efx)) + pci_clear_master(nic_data->pci_dev2); + ef4_farch_irq_disable_master(efx); + + /* Count errors and reset or disable the NIC accordingly */ + if (efx->int_error_count == 0 || + time_after(jiffies, efx->int_error_expire)) { + efx->int_error_count = 0; + efx->int_error_expire = + jiffies + EF4_INT_ERROR_EXPIRE * HZ; + } + if (++efx->int_error_count < EF4_MAX_INT_ERRORS) { + netif_err(efx, hw, efx->net_dev, + "SYSTEM ERROR - reset scheduled\n"); + ef4_schedule_reset(efx, RESET_TYPE_INT_ERROR); + } else { + netif_err(efx, hw, efx->net_dev, + "SYSTEM ERROR - max number of errors seen." + "NIC will be disabled\n"); + ef4_schedule_reset(efx, RESET_TYPE_DISABLE); + } + + return IRQ_HANDLED; +} + +/* Handle a legacy interrupt + * Acknowledges the interrupt and schedule event queue processing. + */ +irqreturn_t ef4_farch_legacy_interrupt(int irq, void *dev_id) +{ + struct ef4_nic *efx = dev_id; + bool soft_enabled = ACCESS_ONCE(efx->irq_soft_enabled); + ef4_oword_t *int_ker = efx->irq_status.addr; + irqreturn_t result = IRQ_NONE; + struct ef4_channel *channel; + ef4_dword_t reg; + u32 queues; + int syserr; + + /* Read the ISR which also ACKs the interrupts */ + ef4_readd(efx, ®, FR_BZ_INT_ISR0); + queues = EF4_EXTRACT_DWORD(reg, 0, 31); + + /* Legacy interrupts are disabled too late by the EEH kernel + * code. Disable them earlier. + * If an EEH error occurred, the read will have returned all ones. + */ + if (EF4_DWORD_IS_ALL_ONES(reg) && ef4_try_recovery(efx) && + !efx->eeh_disabled_legacy_irq) { + disable_irq_nosync(efx->legacy_irq); + efx->eeh_disabled_legacy_irq = true; + } + + /* Handle non-event-queue sources */ + if (queues & (1U << efx->irq_level) && soft_enabled) { + syserr = EF4_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return ef4_farch_fatal_interrupt(efx); + efx->last_irq_cpu = raw_smp_processor_id(); + } + + if (queues != 0) { + efx->irq_zero_count = 0; + + /* Schedule processing of any interrupting queues */ + if (likely(soft_enabled)) { + ef4_for_each_channel(channel, efx) { + if (queues & 1) + ef4_schedule_channel_irq(channel); + queues >>= 1; + } + } + result = IRQ_HANDLED; + + } else { + ef4_qword_t *event; + + /* Legacy ISR read can return zero once (SF bug 15783) */ + + /* We can't return IRQ_HANDLED more than once on seeing ISR=0 + * because this might be a shared interrupt. */ + if (efx->irq_zero_count++ == 0) + result = IRQ_HANDLED; + + /* Ensure we schedule or rearm all event queues */ + if (likely(soft_enabled)) { + ef4_for_each_channel(channel, efx) { + event = ef4_event(channel, + channel->eventq_read_ptr); + if (ef4_event_present(event)) + ef4_schedule_channel_irq(channel); + else + ef4_farch_ev_read_ack(channel); + } + } + } + + if (result == IRQ_HANDLED) + netif_vdbg(efx, intr, efx->net_dev, + "IRQ %d on CPU %d status " EF4_DWORD_FMT "\n", + irq, raw_smp_processor_id(), EF4_DWORD_VAL(reg)); + + return result; +} + +/* Handle an MSI interrupt + * + * Handle an MSI hardware interrupt. This routine schedules event + * queue processing. No interrupt acknowledgement cycle is necessary. + * Also, we never need to check that the interrupt is for us, since + * MSI interrupts cannot be shared. + */ +irqreturn_t ef4_farch_msi_interrupt(int irq, void *dev_id) +{ + struct ef4_msi_context *context = dev_id; + struct ef4_nic *efx = context->efx; + ef4_oword_t *int_ker = efx->irq_status.addr; + int syserr; + + netif_vdbg(efx, intr, efx->net_dev, + "IRQ %d on CPU %d status " EF4_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EF4_OWORD_VAL(*int_ker)); + + if (!likely(ACCESS_ONCE(efx->irq_soft_enabled))) + return IRQ_HANDLED; + + /* Handle non-event-queue sources */ + if (context->index == efx->irq_level) { + syserr = EF4_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return ef4_farch_fatal_interrupt(efx); + efx->last_irq_cpu = raw_smp_processor_id(); + } + + /* Schedule processing of the channel */ + ef4_schedule_channel_irq(efx->channel[context->index]); + + return IRQ_HANDLED; +} + +/* Setup RSS indirection table. + * This maps from the hash value of the packet to RXQ + */ +void ef4_farch_rx_push_indir_table(struct ef4_nic *efx) +{ + size_t i = 0; + ef4_dword_t dword; + + BUG_ON(ef4_nic_rev(efx) < EF4_REV_FALCON_B0); + + BUILD_BUG_ON(ARRAY_SIZE(efx->rx_indir_table) != + FR_BZ_RX_INDIRECTION_TBL_ROWS); + + for (i = 0; i < FR_BZ_RX_INDIRECTION_TBL_ROWS; i++) { + EF4_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE, + efx->rx_indir_table[i]); + ef4_writed(efx, &dword, + FR_BZ_RX_INDIRECTION_TBL + + FR_BZ_RX_INDIRECTION_TBL_STEP * i); + } +} + +/* Looks at available SRAM resources and works out how many queues we + * can support, and where things like descriptor caches should live. + * + * SRAM is split up as follows: + * 0 buftbl entries for channels + * efx->vf_buftbl_base buftbl entries for SR-IOV + * efx->rx_dc_base RX descriptor caches + * efx->tx_dc_base TX descriptor caches + */ +void ef4_farch_dimension_resources(struct ef4_nic *efx, unsigned sram_lim_qw) +{ + unsigned vi_count, buftbl_min; + + /* Account for the buffer table entries backing the datapath channels + * and the descriptor caches for those channels. + */ + buftbl_min = ((efx->n_rx_channels * EF4_MAX_DMAQ_SIZE + + efx->n_tx_channels * EF4_TXQ_TYPES * EF4_MAX_DMAQ_SIZE + + efx->n_channels * EF4_MAX_EVQ_SIZE) + * sizeof(ef4_qword_t) / EF4_BUF_SIZE); + vi_count = max(efx->n_channels, efx->n_tx_channels * EF4_TXQ_TYPES); + + efx->tx_dc_base = sram_lim_qw - vi_count * TX_DC_ENTRIES; + efx->rx_dc_base = efx->tx_dc_base - vi_count * RX_DC_ENTRIES; +} + +u32 ef4_farch_fpga_ver(struct ef4_nic *efx) +{ + ef4_oword_t altera_build; + ef4_reado(efx, &altera_build, FR_AZ_ALTERA_BUILD); + return EF4_OWORD_FIELD(altera_build, FRF_AZ_ALTERA_BUILD_VER); +} + +void ef4_farch_init_common(struct ef4_nic *efx) +{ + ef4_oword_t temp; + + /* Set positions of descriptor caches in SRAM. */ + EF4_POPULATE_OWORD_1(temp, FRF_AZ_SRM_TX_DC_BASE_ADR, efx->tx_dc_base); + ef4_writeo(efx, &temp, FR_AZ_SRM_TX_DC_CFG); + EF4_POPULATE_OWORD_1(temp, FRF_AZ_SRM_RX_DC_BASE_ADR, efx->rx_dc_base); + ef4_writeo(efx, &temp, FR_AZ_SRM_RX_DC_CFG); + + /* Set TX descriptor cache size. */ + BUILD_BUG_ON(TX_DC_ENTRIES != (8 << TX_DC_ENTRIES_ORDER)); + EF4_POPULATE_OWORD_1(temp, FRF_AZ_TX_DC_SIZE, TX_DC_ENTRIES_ORDER); + ef4_writeo(efx, &temp, FR_AZ_TX_DC_CFG); + + /* Set RX descriptor cache size. Set low watermark to size-8, as + * this allows most efficient prefetching. + */ + BUILD_BUG_ON(RX_DC_ENTRIES != (8 << RX_DC_ENTRIES_ORDER)); + EF4_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_SIZE, RX_DC_ENTRIES_ORDER); + ef4_writeo(efx, &temp, FR_AZ_RX_DC_CFG); + EF4_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_PF_LWM, RX_DC_ENTRIES - 8); + ef4_writeo(efx, &temp, FR_AZ_RX_DC_PF_WM); + + /* Program INT_KER address */ + EF4_POPULATE_OWORD_2(temp, + FRF_AZ_NORM_INT_VEC_DIS_KER, + EF4_INT_MODE_USE_MSI(efx), + FRF_AZ_INT_ADR_KER, efx->irq_status.dma_addr); + ef4_writeo(efx, &temp, FR_AZ_INT_ADR_KER); + + /* Use a valid MSI-X vector */ + efx->irq_level = 0; + + /* Enable all the genuinely fatal interrupts. (They are still + * masked by the overall interrupt mask, controlled by + * falcon_interrupts()). + * + * Note: All other fatal interrupts are enabled + */ + EF4_POPULATE_OWORD_3(temp, + FRF_AZ_ILL_ADR_INT_KER_EN, 1, + FRF_AZ_RBUF_OWN_INT_KER_EN, 1, + FRF_AZ_TBUF_OWN_INT_KER_EN, 1); + EF4_INVERT_OWORD(temp); + ef4_writeo(efx, &temp, FR_AZ_FATAL_INTR_KER); + + /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be + * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. + */ + ef4_reado(efx, &temp, FR_AZ_TX_RESERVED); + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe); + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1); + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1); + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 1); + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1); + /* Enable SW_EV to inherit in char driver - assume harmless here */ + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1); + /* Prefetch threshold 2 => fetch when descriptor cache half empty */ + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_THRESHOLD, 2); + /* Disable hardware watchdog which can misfire */ + EF4_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_WD_TMR, 0x3fffff); + /* Squash TX of packets of 16 bytes or less */ + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) + EF4_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); + ef4_writeo(efx, &temp, FR_AZ_TX_RESERVED); + + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) { + EF4_POPULATE_OWORD_4(temp, + /* Default values */ + FRF_BZ_TX_PACE_SB_NOT_AF, 0x15, + FRF_BZ_TX_PACE_SB_AF, 0xb, + FRF_BZ_TX_PACE_FB_BASE, 0, + /* Allow large pace values in the + * fast bin. */ + FRF_BZ_TX_PACE_BIN_TH, + FFE_BZ_TX_PACE_RESERVED); + ef4_writeo(efx, &temp, FR_BZ_TX_PACE); + } +} + +/************************************************************************** + * + * Filter tables + * + ************************************************************************** + */ + +/* "Fudge factors" - difference between programmed value and actual depth. + * Due to pipelined implementation we need to program H/W with a value that + * is larger than the hop limit we want. + */ +#define EF4_FARCH_FILTER_CTL_SRCH_FUDGE_WILD 3 +#define EF4_FARCH_FILTER_CTL_SRCH_FUDGE_FULL 1 + +/* Hard maximum search limit. Hardware will time-out beyond 200-something. + * We also need to avoid infinite loops in ef4_farch_filter_search() when the + * table is full. + */ +#define EF4_FARCH_FILTER_CTL_SRCH_MAX 200 + +/* Don't try very hard to find space for performance hints, as this is + * counter-productive. */ +#define EF4_FARCH_FILTER_CTL_SRCH_HINT_MAX 5 + +enum ef4_farch_filter_type { + EF4_FARCH_FILTER_TCP_FULL = 0, + EF4_FARCH_FILTER_TCP_WILD, + EF4_FARCH_FILTER_UDP_FULL, + EF4_FARCH_FILTER_UDP_WILD, + EF4_FARCH_FILTER_MAC_FULL = 4, + EF4_FARCH_FILTER_MAC_WILD, + EF4_FARCH_FILTER_UC_DEF = 8, + EF4_FARCH_FILTER_MC_DEF, + EF4_FARCH_FILTER_TYPE_COUNT, /* number of specific types */ +}; + +enum ef4_farch_filter_table_id { + EF4_FARCH_FILTER_TABLE_RX_IP = 0, + EF4_FARCH_FILTER_TABLE_RX_MAC, + EF4_FARCH_FILTER_TABLE_RX_DEF, + EF4_FARCH_FILTER_TABLE_TX_MAC, + EF4_FARCH_FILTER_TABLE_COUNT, +}; + +enum ef4_farch_filter_index { + EF4_FARCH_FILTER_INDEX_UC_DEF, + EF4_FARCH_FILTER_INDEX_MC_DEF, + EF4_FARCH_FILTER_SIZE_RX_DEF, +}; + +struct ef4_farch_filter_spec { + u8 type:4; + u8 priority:4; + u8 flags; + u16 dmaq_id; + u32 data[3]; +}; + +struct ef4_farch_filter_table { + enum ef4_farch_filter_table_id id; + u32 offset; /* address of table relative to BAR */ + unsigned size; /* number of entries */ + unsigned step; /* step between entries */ + unsigned used; /* number currently used */ + unsigned long *used_bitmap; + struct ef4_farch_filter_spec *spec; + unsigned search_limit[EF4_FARCH_FILTER_TYPE_COUNT]; +}; + +struct ef4_farch_filter_state { + struct ef4_farch_filter_table table[EF4_FARCH_FILTER_TABLE_COUNT]; +}; + +static void +ef4_farch_filter_table_clear_entry(struct ef4_nic *efx, + struct ef4_farch_filter_table *table, + unsigned int filter_idx); + +/* The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit + * key derived from the n-tuple. The initial LFSR state is 0xffff. */ +static u16 ef4_farch_filter_hash(u32 key) +{ + u16 tmp; + + /* First 16 rounds */ + tmp = 0x1fff ^ key >> 16; + tmp = tmp ^ tmp >> 3 ^ tmp >> 6; + tmp = tmp ^ tmp >> 9; + /* Last 16 rounds */ + tmp = tmp ^ tmp << 13 ^ key; + tmp = tmp ^ tmp >> 3 ^ tmp >> 6; + return tmp ^ tmp >> 9; +} + +/* To allow for hash collisions, filter search continues at these + * increments from the first possible entry selected by the hash. */ +static u16 ef4_farch_filter_increment(u32 key) +{ + return key * 2 - 1; +} + +static enum ef4_farch_filter_table_id +ef4_farch_filter_spec_table_id(const struct ef4_farch_filter_spec *spec) +{ + BUILD_BUG_ON(EF4_FARCH_FILTER_TABLE_RX_IP != + (EF4_FARCH_FILTER_TCP_FULL >> 2)); + BUILD_BUG_ON(EF4_FARCH_FILTER_TABLE_RX_IP != + (EF4_FARCH_FILTER_TCP_WILD >> 2)); + BUILD_BUG_ON(EF4_FARCH_FILTER_TABLE_RX_IP != + (EF4_FARCH_FILTER_UDP_FULL >> 2)); + BUILD_BUG_ON(EF4_FARCH_FILTER_TABLE_RX_IP != + (EF4_FARCH_FILTER_UDP_WILD >> 2)); + BUILD_BUG_ON(EF4_FARCH_FILTER_TABLE_RX_MAC != + (EF4_FARCH_FILTER_MAC_FULL >> 2)); + BUILD_BUG_ON(EF4_FARCH_FILTER_TABLE_RX_MAC != + (EF4_FARCH_FILTER_MAC_WILD >> 2)); + BUILD_BUG_ON(EF4_FARCH_FILTER_TABLE_TX_MAC != + EF4_FARCH_FILTER_TABLE_RX_MAC + 2); + return (spec->type >> 2) + ((spec->flags & EF4_FILTER_FLAG_TX) ? 2 : 0); +} + +static void ef4_farch_filter_push_rx_config(struct ef4_nic *efx) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + struct ef4_farch_filter_table *table; + ef4_oword_t filter_ctl; + + ef4_reado(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); + + table = &state->table[EF4_FARCH_FILTER_TABLE_RX_IP]; + EF4_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT, + table->search_limit[EF4_FARCH_FILTER_TCP_FULL] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); + EF4_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT, + table->search_limit[EF4_FARCH_FILTER_TCP_WILD] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); + EF4_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT, + table->search_limit[EF4_FARCH_FILTER_UDP_FULL] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); + EF4_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT, + table->search_limit[EF4_FARCH_FILTER_UDP_WILD] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); + + table = &state->table[EF4_FARCH_FILTER_TABLE_RX_MAC]; + if (table->size) { + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT, + table->search_limit[EF4_FARCH_FILTER_MAC_FULL] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT, + table->search_limit[EF4_FARCH_FILTER_MAC_WILD] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); + } + + table = &state->table[EF4_FARCH_FILTER_TABLE_RX_DEF]; + if (table->size) { + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_CZ_UNICAST_NOMATCH_Q_ID, + table->spec[EF4_FARCH_FILTER_INDEX_UC_DEF].dmaq_id); + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED, + !!(table->spec[EF4_FARCH_FILTER_INDEX_UC_DEF].flags & + EF4_FILTER_FLAG_RX_RSS)); + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_CZ_MULTICAST_NOMATCH_Q_ID, + table->spec[EF4_FARCH_FILTER_INDEX_MC_DEF].dmaq_id); + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED, + !!(table->spec[EF4_FARCH_FILTER_INDEX_MC_DEF].flags & + EF4_FILTER_FLAG_RX_RSS)); + + /* There is a single bit to enable RX scatter for all + * unmatched packets. Only set it if scatter is + * enabled in both filter specs. + */ + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_BZ_SCATTER_ENBL_NO_MATCH_Q, + !!(table->spec[EF4_FARCH_FILTER_INDEX_UC_DEF].flags & + table->spec[EF4_FARCH_FILTER_INDEX_MC_DEF].flags & + EF4_FILTER_FLAG_RX_SCATTER)); + } else if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) { + /* We don't expose 'default' filters because unmatched + * packets always go to the queue number found in the + * RSS table. But we still need to set the RX scatter + * bit here. + */ + EF4_SET_OWORD_FIELD( + filter_ctl, FRF_BZ_SCATTER_ENBL_NO_MATCH_Q, + efx->rx_scatter); + } + + ef4_writeo(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL); +} + +static void ef4_farch_filter_push_tx_limits(struct ef4_nic *efx) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + struct ef4_farch_filter_table *table; + ef4_oword_t tx_cfg; + + ef4_reado(efx, &tx_cfg, FR_AZ_TX_CFG); + + table = &state->table[EF4_FARCH_FILTER_TABLE_TX_MAC]; + if (table->size) { + EF4_SET_OWORD_FIELD( + tx_cfg, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE, + table->search_limit[EF4_FARCH_FILTER_MAC_FULL] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); + EF4_SET_OWORD_FIELD( + tx_cfg, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE, + table->search_limit[EF4_FARCH_FILTER_MAC_WILD] + + EF4_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); + } + + ef4_writeo(efx, &tx_cfg, FR_AZ_TX_CFG); +} + +static int +ef4_farch_filter_from_gen_spec(struct ef4_farch_filter_spec *spec, + const struct ef4_filter_spec *gen_spec) +{ + bool is_full = false; + + if ((gen_spec->flags & EF4_FILTER_FLAG_RX_RSS) && + gen_spec->rss_context != EF4_FILTER_RSS_CONTEXT_DEFAULT) + return -EINVAL; + + spec->priority = gen_spec->priority; + spec->flags = gen_spec->flags; + spec->dmaq_id = gen_spec->dmaq_id; + + switch (gen_spec->match_flags) { + case (EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_LOC_PORT | + EF4_FILTER_MATCH_REM_HOST | EF4_FILTER_MATCH_REM_PORT): + is_full = true; + /* fall through */ + case (EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_LOC_PORT): { + __be32 rhost, host1, host2; + __be16 rport, port1, port2; + + EF4_BUG_ON_PARANOID(!(gen_spec->flags & EF4_FILTER_FLAG_RX)); + + if (gen_spec->ether_type != htons(ETH_P_IP)) + return -EPROTONOSUPPORT; + if (gen_spec->loc_port == 0 || + (is_full && gen_spec->rem_port == 0)) + return -EADDRNOTAVAIL; + switch (gen_spec->ip_proto) { + case IPPROTO_TCP: + spec->type = (is_full ? EF4_FARCH_FILTER_TCP_FULL : + EF4_FARCH_FILTER_TCP_WILD); + break; + case IPPROTO_UDP: + spec->type = (is_full ? EF4_FARCH_FILTER_UDP_FULL : + EF4_FARCH_FILTER_UDP_WILD); + break; + default: + return -EPROTONOSUPPORT; + } + + /* Filter is constructed in terms of source and destination, + * with the odd wrinkle that the ports are swapped in a UDP + * wildcard filter. We need to convert from local and remote + * (= zero for wildcard) addresses. + */ + rhost = is_full ? gen_spec->rem_host[0] : 0; + rport = is_full ? gen_spec->rem_port : 0; + host1 = rhost; + host2 = gen_spec->loc_host[0]; + if (!is_full && gen_spec->ip_proto == IPPROTO_UDP) { + port1 = gen_spec->loc_port; + port2 = rport; + } else { + port1 = rport; + port2 = gen_spec->loc_port; + } + spec->data[0] = ntohl(host1) << 16 | ntohs(port1); + spec->data[1] = ntohs(port2) << 16 | ntohl(host1) >> 16; + spec->data[2] = ntohl(host2); + + break; + } + + case EF4_FILTER_MATCH_LOC_MAC | EF4_FILTER_MATCH_OUTER_VID: + is_full = true; + /* fall through */ + case EF4_FILTER_MATCH_LOC_MAC: + spec->type = (is_full ? EF4_FARCH_FILTER_MAC_FULL : + EF4_FARCH_FILTER_MAC_WILD); + spec->data[0] = is_full ? ntohs(gen_spec->outer_vid) : 0; + spec->data[1] = (gen_spec->loc_mac[2] << 24 | + gen_spec->loc_mac[3] << 16 | + gen_spec->loc_mac[4] << 8 | + gen_spec->loc_mac[5]); + spec->data[2] = (gen_spec->loc_mac[0] << 8 | + gen_spec->loc_mac[1]); + break; + + case EF4_FILTER_MATCH_LOC_MAC_IG: + spec->type = (is_multicast_ether_addr(gen_spec->loc_mac) ? + EF4_FARCH_FILTER_MC_DEF : + EF4_FARCH_FILTER_UC_DEF); + memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */ + break; + + default: + return -EPROTONOSUPPORT; + } + + return 0; +} + +static void +ef4_farch_filter_to_gen_spec(struct ef4_filter_spec *gen_spec, + const struct ef4_farch_filter_spec *spec) +{ + bool is_full = false; + + /* *gen_spec should be completely initialised, to be consistent + * with ef4_filter_init_{rx,tx}() and in case we want to copy + * it back to userland. + */ + memset(gen_spec, 0, sizeof(*gen_spec)); + + gen_spec->priority = spec->priority; + gen_spec->flags = spec->flags; + gen_spec->dmaq_id = spec->dmaq_id; + + switch (spec->type) { + case EF4_FARCH_FILTER_TCP_FULL: + case EF4_FARCH_FILTER_UDP_FULL: + is_full = true; + /* fall through */ + case EF4_FARCH_FILTER_TCP_WILD: + case EF4_FARCH_FILTER_UDP_WILD: { + __be32 host1, host2; + __be16 port1, port2; + + gen_spec->match_flags = + EF4_FILTER_MATCH_ETHER_TYPE | + EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_LOC_PORT; + if (is_full) + gen_spec->match_flags |= (EF4_FILTER_MATCH_REM_HOST | + EF4_FILTER_MATCH_REM_PORT); + gen_spec->ether_type = htons(ETH_P_IP); + gen_spec->ip_proto = + (spec->type == EF4_FARCH_FILTER_TCP_FULL || + spec->type == EF4_FARCH_FILTER_TCP_WILD) ? + IPPROTO_TCP : IPPROTO_UDP; + + host1 = htonl(spec->data[0] >> 16 | spec->data[1] << 16); + port1 = htons(spec->data[0]); + host2 = htonl(spec->data[2]); + port2 = htons(spec->data[1] >> 16); + if (spec->flags & EF4_FILTER_FLAG_TX) { + gen_spec->loc_host[0] = host1; + gen_spec->rem_host[0] = host2; + } else { + gen_spec->loc_host[0] = host2; + gen_spec->rem_host[0] = host1; + } + if (!!(gen_spec->flags & EF4_FILTER_FLAG_TX) ^ + (!is_full && gen_spec->ip_proto == IPPROTO_UDP)) { + gen_spec->loc_port = port1; + gen_spec->rem_port = port2; + } else { + gen_spec->loc_port = port2; + gen_spec->rem_port = port1; + } + + break; + } + + case EF4_FARCH_FILTER_MAC_FULL: + is_full = true; + /* fall through */ + case EF4_FARCH_FILTER_MAC_WILD: + gen_spec->match_flags = EF4_FILTER_MATCH_LOC_MAC; + if (is_full) + gen_spec->match_flags |= EF4_FILTER_MATCH_OUTER_VID; + gen_spec->loc_mac[0] = spec->data[2] >> 8; + gen_spec->loc_mac[1] = spec->data[2]; + gen_spec->loc_mac[2] = spec->data[1] >> 24; + gen_spec->loc_mac[3] = spec->data[1] >> 16; + gen_spec->loc_mac[4] = spec->data[1] >> 8; + gen_spec->loc_mac[5] = spec->data[1]; + gen_spec->outer_vid = htons(spec->data[0]); + break; + + case EF4_FARCH_FILTER_UC_DEF: + case EF4_FARCH_FILTER_MC_DEF: + gen_spec->match_flags = EF4_FILTER_MATCH_LOC_MAC_IG; + gen_spec->loc_mac[0] = spec->type == EF4_FARCH_FILTER_MC_DEF; + break; + + default: + WARN_ON(1); + break; + } +} + +static void +ef4_farch_filter_init_rx_auto(struct ef4_nic *efx, + struct ef4_farch_filter_spec *spec) +{ + /* If there's only one channel then disable RSS for non VF + * traffic, thereby allowing VFs to use RSS when the PF can't. + */ + spec->priority = EF4_FILTER_PRI_AUTO; + spec->flags = (EF4_FILTER_FLAG_RX | + (ef4_rss_enabled(efx) ? EF4_FILTER_FLAG_RX_RSS : 0) | + (efx->rx_scatter ? EF4_FILTER_FLAG_RX_SCATTER : 0)); + spec->dmaq_id = 0; +} + +/* Build a filter entry and return its n-tuple key. */ +static u32 ef4_farch_filter_build(ef4_oword_t *filter, + struct ef4_farch_filter_spec *spec) +{ + u32 data3; + + switch (ef4_farch_filter_spec_table_id(spec)) { + case EF4_FARCH_FILTER_TABLE_RX_IP: { + bool is_udp = (spec->type == EF4_FARCH_FILTER_UDP_FULL || + spec->type == EF4_FARCH_FILTER_UDP_WILD); + EF4_POPULATE_OWORD_7( + *filter, + FRF_BZ_RSS_EN, + !!(spec->flags & EF4_FILTER_FLAG_RX_RSS), + FRF_BZ_SCATTER_EN, + !!(spec->flags & EF4_FILTER_FLAG_RX_SCATTER), + FRF_BZ_TCP_UDP, is_udp, + FRF_BZ_RXQ_ID, spec->dmaq_id, + EF4_DWORD_2, spec->data[2], + EF4_DWORD_1, spec->data[1], + EF4_DWORD_0, spec->data[0]); + data3 = is_udp; + break; + } + + case EF4_FARCH_FILTER_TABLE_RX_MAC: { + bool is_wild = spec->type == EF4_FARCH_FILTER_MAC_WILD; + EF4_POPULATE_OWORD_7( + *filter, + FRF_CZ_RMFT_RSS_EN, + !!(spec->flags & EF4_FILTER_FLAG_RX_RSS), + FRF_CZ_RMFT_SCATTER_EN, + !!(spec->flags & EF4_FILTER_FLAG_RX_SCATTER), + FRF_CZ_RMFT_RXQ_ID, spec->dmaq_id, + FRF_CZ_RMFT_WILDCARD_MATCH, is_wild, + FRF_CZ_RMFT_DEST_MAC_HI, spec->data[2], + FRF_CZ_RMFT_DEST_MAC_LO, spec->data[1], + FRF_CZ_RMFT_VLAN_ID, spec->data[0]); + data3 = is_wild; + break; + } + + case EF4_FARCH_FILTER_TABLE_TX_MAC: { + bool is_wild = spec->type == EF4_FARCH_FILTER_MAC_WILD; + EF4_POPULATE_OWORD_5(*filter, + FRF_CZ_TMFT_TXQ_ID, spec->dmaq_id, + FRF_CZ_TMFT_WILDCARD_MATCH, is_wild, + FRF_CZ_TMFT_SRC_MAC_HI, spec->data[2], + FRF_CZ_TMFT_SRC_MAC_LO, spec->data[1], + FRF_CZ_TMFT_VLAN_ID, spec->data[0]); + data3 = is_wild | spec->dmaq_id << 1; + break; + } + + default: + BUG(); + } + + return spec->data[0] ^ spec->data[1] ^ spec->data[2] ^ data3; +} + +static bool ef4_farch_filter_equal(const struct ef4_farch_filter_spec *left, + const struct ef4_farch_filter_spec *right) +{ + if (left->type != right->type || + memcmp(left->data, right->data, sizeof(left->data))) + return false; + + if (left->flags & EF4_FILTER_FLAG_TX && + left->dmaq_id != right->dmaq_id) + return false; + + return true; +} + +/* + * Construct/deconstruct external filter IDs. At least the RX filter + * IDs must be ordered by matching priority, for RX NFC semantics. + * + * Deconstruction needs to be robust against invalid IDs so that + * ef4_filter_remove_id_safe() and ef4_filter_get_filter_safe() can + * accept user-provided IDs. + */ + +#define EF4_FARCH_FILTER_MATCH_PRI_COUNT 5 + +static const u8 ef4_farch_filter_type_match_pri[EF4_FARCH_FILTER_TYPE_COUNT] = { + [EF4_FARCH_FILTER_TCP_FULL] = 0, + [EF4_FARCH_FILTER_UDP_FULL] = 0, + [EF4_FARCH_FILTER_TCP_WILD] = 1, + [EF4_FARCH_FILTER_UDP_WILD] = 1, + [EF4_FARCH_FILTER_MAC_FULL] = 2, + [EF4_FARCH_FILTER_MAC_WILD] = 3, + [EF4_FARCH_FILTER_UC_DEF] = 4, + [EF4_FARCH_FILTER_MC_DEF] = 4, +}; + +static const enum ef4_farch_filter_table_id ef4_farch_filter_range_table[] = { + EF4_FARCH_FILTER_TABLE_RX_IP, /* RX match pri 0 */ + EF4_FARCH_FILTER_TABLE_RX_IP, + EF4_FARCH_FILTER_TABLE_RX_MAC, + EF4_FARCH_FILTER_TABLE_RX_MAC, + EF4_FARCH_FILTER_TABLE_RX_DEF, /* RX match pri 4 */ + EF4_FARCH_FILTER_TABLE_TX_MAC, /* TX match pri 0 */ + EF4_FARCH_FILTER_TABLE_TX_MAC, /* TX match pri 1 */ +}; + +#define EF4_FARCH_FILTER_INDEX_WIDTH 13 +#define EF4_FARCH_FILTER_INDEX_MASK ((1 << EF4_FARCH_FILTER_INDEX_WIDTH) - 1) + +static inline u32 +ef4_farch_filter_make_id(const struct ef4_farch_filter_spec *spec, + unsigned int index) +{ + unsigned int range; + + range = ef4_farch_filter_type_match_pri[spec->type]; + if (!(spec->flags & EF4_FILTER_FLAG_RX)) + range += EF4_FARCH_FILTER_MATCH_PRI_COUNT; + + return range << EF4_FARCH_FILTER_INDEX_WIDTH | index; +} + +static inline enum ef4_farch_filter_table_id +ef4_farch_filter_id_table_id(u32 id) +{ + unsigned int range = id >> EF4_FARCH_FILTER_INDEX_WIDTH; + + if (range < ARRAY_SIZE(ef4_farch_filter_range_table)) + return ef4_farch_filter_range_table[range]; + else + return EF4_FARCH_FILTER_TABLE_COUNT; /* invalid */ +} + +static inline unsigned int ef4_farch_filter_id_index(u32 id) +{ + return id & EF4_FARCH_FILTER_INDEX_MASK; +} + +u32 ef4_farch_filter_get_rx_id_limit(struct ef4_nic *efx) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + unsigned int range = EF4_FARCH_FILTER_MATCH_PRI_COUNT - 1; + enum ef4_farch_filter_table_id table_id; + + do { + table_id = ef4_farch_filter_range_table[range]; + if (state->table[table_id].size != 0) + return range << EF4_FARCH_FILTER_INDEX_WIDTH | + state->table[table_id].size; + } while (range--); + + return 0; +} + +s32 ef4_farch_filter_insert(struct ef4_nic *efx, + struct ef4_filter_spec *gen_spec, + bool replace_equal) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + struct ef4_farch_filter_table *table; + struct ef4_farch_filter_spec spec; + ef4_oword_t filter; + int rep_index, ins_index; + unsigned int depth = 0; + int rc; + + rc = ef4_farch_filter_from_gen_spec(&spec, gen_spec); + if (rc) + return rc; + + table = &state->table[ef4_farch_filter_spec_table_id(&spec)]; + if (table->size == 0) + return -EINVAL; + + netif_vdbg(efx, hw, efx->net_dev, + "%s: type %d search_limit=%d", __func__, spec.type, + table->search_limit[spec.type]); + + if (table->id == EF4_FARCH_FILTER_TABLE_RX_DEF) { + /* One filter spec per type */ + BUILD_BUG_ON(EF4_FARCH_FILTER_INDEX_UC_DEF != 0); + BUILD_BUG_ON(EF4_FARCH_FILTER_INDEX_MC_DEF != + EF4_FARCH_FILTER_MC_DEF - EF4_FARCH_FILTER_UC_DEF); + rep_index = spec.type - EF4_FARCH_FILTER_UC_DEF; + ins_index = rep_index; + + spin_lock_bh(&efx->filter_lock); + } else { + /* Search concurrently for + * (1) a filter to be replaced (rep_index): any filter + * with the same match values, up to the current + * search depth for this type, and + * (2) the insertion point (ins_index): (1) or any + * free slot before it or up to the maximum search + * depth for this priority + * We fail if we cannot find (2). + * + * We can stop once either + * (a) we find (1), in which case we have definitely + * found (2) as well; or + * (b) we have searched exhaustively for (1), and have + * either found (2) or searched exhaustively for it + */ + u32 key = ef4_farch_filter_build(&filter, &spec); + unsigned int hash = ef4_farch_filter_hash(key); + unsigned int incr = ef4_farch_filter_increment(key); + unsigned int max_rep_depth = table->search_limit[spec.type]; + unsigned int max_ins_depth = + spec.priority <= EF4_FILTER_PRI_HINT ? + EF4_FARCH_FILTER_CTL_SRCH_HINT_MAX : + EF4_FARCH_FILTER_CTL_SRCH_MAX; + unsigned int i = hash & (table->size - 1); + + ins_index = -1; + depth = 1; + + spin_lock_bh(&efx->filter_lock); + + for (;;) { + if (!test_bit(i, table->used_bitmap)) { + if (ins_index < 0) + ins_index = i; + } else if (ef4_farch_filter_equal(&spec, + &table->spec[i])) { + /* Case (a) */ + if (ins_index < 0) + ins_index = i; + rep_index = i; + break; + } + + if (depth >= max_rep_depth && + (ins_index >= 0 || depth >= max_ins_depth)) { + /* Case (b) */ + if (ins_index < 0) { + rc = -EBUSY; + goto out; + } + rep_index = -1; + break; + } + + i = (i + incr) & (table->size - 1); + ++depth; + } + } + + /* If we found a filter to be replaced, check whether we + * should do so + */ + if (rep_index >= 0) { + struct ef4_farch_filter_spec *saved_spec = + &table->spec[rep_index]; + + if (spec.priority == saved_spec->priority && !replace_equal) { + rc = -EEXIST; + goto out; + } + if (spec.priority < saved_spec->priority) { + rc = -EPERM; + goto out; + } + if (saved_spec->priority == EF4_FILTER_PRI_AUTO || + saved_spec->flags & EF4_FILTER_FLAG_RX_OVER_AUTO) + spec.flags |= EF4_FILTER_FLAG_RX_OVER_AUTO; + } + + /* Insert the filter */ + if (ins_index != rep_index) { + __set_bit(ins_index, table->used_bitmap); + ++table->used; + } + table->spec[ins_index] = spec; + + if (table->id == EF4_FARCH_FILTER_TABLE_RX_DEF) { + ef4_farch_filter_push_rx_config(efx); + } else { + if (table->search_limit[spec.type] < depth) { + table->search_limit[spec.type] = depth; + if (spec.flags & EF4_FILTER_FLAG_TX) + ef4_farch_filter_push_tx_limits(efx); + else + ef4_farch_filter_push_rx_config(efx); + } + + ef4_writeo(efx, &filter, + table->offset + table->step * ins_index); + + /* If we were able to replace a filter by inserting + * at a lower depth, clear the replaced filter + */ + if (ins_index != rep_index && rep_index >= 0) + ef4_farch_filter_table_clear_entry(efx, table, + rep_index); + } + + netif_vdbg(efx, hw, efx->net_dev, + "%s: filter type %d index %d rxq %u set", + __func__, spec.type, ins_index, spec.dmaq_id); + rc = ef4_farch_filter_make_id(&spec, ins_index); + +out: + spin_unlock_bh(&efx->filter_lock); + return rc; +} + +static void +ef4_farch_filter_table_clear_entry(struct ef4_nic *efx, + struct ef4_farch_filter_table *table, + unsigned int filter_idx) +{ + static ef4_oword_t filter; + + EF4_WARN_ON_PARANOID(!test_bit(filter_idx, table->used_bitmap)); + BUG_ON(table->offset == 0); /* can't clear MAC default filters */ + + __clear_bit(filter_idx, table->used_bitmap); + --table->used; + memset(&table->spec[filter_idx], 0, sizeof(table->spec[0])); + + ef4_writeo(efx, &filter, table->offset + table->step * filter_idx); + + /* If this filter required a greater search depth than + * any other, the search limit for its type can now be + * decreased. However, it is hard to determine that + * unless the table has become completely empty - in + * which case, all its search limits can be set to 0. + */ + if (unlikely(table->used == 0)) { + memset(table->search_limit, 0, sizeof(table->search_limit)); + if (table->id == EF4_FARCH_FILTER_TABLE_TX_MAC) + ef4_farch_filter_push_tx_limits(efx); + else + ef4_farch_filter_push_rx_config(efx); + } +} + +static int ef4_farch_filter_remove(struct ef4_nic *efx, + struct ef4_farch_filter_table *table, + unsigned int filter_idx, + enum ef4_filter_priority priority) +{ + struct ef4_farch_filter_spec *spec = &table->spec[filter_idx]; + + if (!test_bit(filter_idx, table->used_bitmap) || + spec->priority != priority) + return -ENOENT; + + if (spec->flags & EF4_FILTER_FLAG_RX_OVER_AUTO) { + ef4_farch_filter_init_rx_auto(efx, spec); + ef4_farch_filter_push_rx_config(efx); + } else { + ef4_farch_filter_table_clear_entry(efx, table, filter_idx); + } + + return 0; +} + +int ef4_farch_filter_remove_safe(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 filter_id) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + enum ef4_farch_filter_table_id table_id; + struct ef4_farch_filter_table *table; + unsigned int filter_idx; + struct ef4_farch_filter_spec *spec; + int rc; + + table_id = ef4_farch_filter_id_table_id(filter_id); + if ((unsigned int)table_id >= EF4_FARCH_FILTER_TABLE_COUNT) + return -ENOENT; + table = &state->table[table_id]; + + filter_idx = ef4_farch_filter_id_index(filter_id); + if (filter_idx >= table->size) + return -ENOENT; + spec = &table->spec[filter_idx]; + + spin_lock_bh(&efx->filter_lock); + rc = ef4_farch_filter_remove(efx, table, filter_idx, priority); + spin_unlock_bh(&efx->filter_lock); + + return rc; +} + +int ef4_farch_filter_get_safe(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 filter_id, struct ef4_filter_spec *spec_buf) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + enum ef4_farch_filter_table_id table_id; + struct ef4_farch_filter_table *table; + struct ef4_farch_filter_spec *spec; + unsigned int filter_idx; + int rc; + + table_id = ef4_farch_filter_id_table_id(filter_id); + if ((unsigned int)table_id >= EF4_FARCH_FILTER_TABLE_COUNT) + return -ENOENT; + table = &state->table[table_id]; + + filter_idx = ef4_farch_filter_id_index(filter_id); + if (filter_idx >= table->size) + return -ENOENT; + spec = &table->spec[filter_idx]; + + spin_lock_bh(&efx->filter_lock); + + if (test_bit(filter_idx, table->used_bitmap) && + spec->priority == priority) { + ef4_farch_filter_to_gen_spec(spec_buf, spec); + rc = 0; + } else { + rc = -ENOENT; + } + + spin_unlock_bh(&efx->filter_lock); + + return rc; +} + +static void +ef4_farch_filter_table_clear(struct ef4_nic *efx, + enum ef4_farch_filter_table_id table_id, + enum ef4_filter_priority priority) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + struct ef4_farch_filter_table *table = &state->table[table_id]; + unsigned int filter_idx; + + spin_lock_bh(&efx->filter_lock); + for (filter_idx = 0; filter_idx < table->size; ++filter_idx) { + if (table->spec[filter_idx].priority != EF4_FILTER_PRI_AUTO) + ef4_farch_filter_remove(efx, table, + filter_idx, priority); + } + spin_unlock_bh(&efx->filter_lock); +} + +int ef4_farch_filter_clear_rx(struct ef4_nic *efx, + enum ef4_filter_priority priority) +{ + ef4_farch_filter_table_clear(efx, EF4_FARCH_FILTER_TABLE_RX_IP, + priority); + ef4_farch_filter_table_clear(efx, EF4_FARCH_FILTER_TABLE_RX_MAC, + priority); + ef4_farch_filter_table_clear(efx, EF4_FARCH_FILTER_TABLE_RX_DEF, + priority); + return 0; +} + +u32 ef4_farch_filter_count_rx_used(struct ef4_nic *efx, + enum ef4_filter_priority priority) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + enum ef4_farch_filter_table_id table_id; + struct ef4_farch_filter_table *table; + unsigned int filter_idx; + u32 count = 0; + + spin_lock_bh(&efx->filter_lock); + + for (table_id = EF4_FARCH_FILTER_TABLE_RX_IP; + table_id <= EF4_FARCH_FILTER_TABLE_RX_DEF; + table_id++) { + table = &state->table[table_id]; + for (filter_idx = 0; filter_idx < table->size; filter_idx++) { + if (test_bit(filter_idx, table->used_bitmap) && + table->spec[filter_idx].priority == priority) + ++count; + } + } + + spin_unlock_bh(&efx->filter_lock); + + return count; +} + +s32 ef4_farch_filter_get_rx_ids(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 *buf, u32 size) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + enum ef4_farch_filter_table_id table_id; + struct ef4_farch_filter_table *table; + unsigned int filter_idx; + s32 count = 0; + + spin_lock_bh(&efx->filter_lock); + + for (table_id = EF4_FARCH_FILTER_TABLE_RX_IP; + table_id <= EF4_FARCH_FILTER_TABLE_RX_DEF; + table_id++) { + table = &state->table[table_id]; + for (filter_idx = 0; filter_idx < table->size; filter_idx++) { + if (test_bit(filter_idx, table->used_bitmap) && + table->spec[filter_idx].priority == priority) { + if (count == size) { + count = -EMSGSIZE; + goto out; + } + buf[count++] = ef4_farch_filter_make_id( + &table->spec[filter_idx], filter_idx); + } + } + } +out: + spin_unlock_bh(&efx->filter_lock); + + return count; +} + +/* Restore filter stater after reset */ +void ef4_farch_filter_table_restore(struct ef4_nic *efx) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + enum ef4_farch_filter_table_id table_id; + struct ef4_farch_filter_table *table; + ef4_oword_t filter; + unsigned int filter_idx; + + spin_lock_bh(&efx->filter_lock); + + for (table_id = 0; table_id < EF4_FARCH_FILTER_TABLE_COUNT; table_id++) { + table = &state->table[table_id]; + + /* Check whether this is a regular register table */ + if (table->step == 0) + continue; + + for (filter_idx = 0; filter_idx < table->size; filter_idx++) { + if (!test_bit(filter_idx, table->used_bitmap)) + continue; + ef4_farch_filter_build(&filter, &table->spec[filter_idx]); + ef4_writeo(efx, &filter, + table->offset + table->step * filter_idx); + } + } + + ef4_farch_filter_push_rx_config(efx); + ef4_farch_filter_push_tx_limits(efx); + + spin_unlock_bh(&efx->filter_lock); +} + +void ef4_farch_filter_table_remove(struct ef4_nic *efx) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + enum ef4_farch_filter_table_id table_id; + + for (table_id = 0; table_id < EF4_FARCH_FILTER_TABLE_COUNT; table_id++) { + kfree(state->table[table_id].used_bitmap); + vfree(state->table[table_id].spec); + } + kfree(state); +} + +int ef4_farch_filter_table_probe(struct ef4_nic *efx) +{ + struct ef4_farch_filter_state *state; + struct ef4_farch_filter_table *table; + unsigned table_id; + + state = kzalloc(sizeof(struct ef4_farch_filter_state), GFP_KERNEL); + if (!state) + return -ENOMEM; + efx->filter_state = state; + + if (ef4_nic_rev(efx) >= EF4_REV_FALCON_B0) { + table = &state->table[EF4_FARCH_FILTER_TABLE_RX_IP]; + table->id = EF4_FARCH_FILTER_TABLE_RX_IP; + table->offset = FR_BZ_RX_FILTER_TBL0; + table->size = FR_BZ_RX_FILTER_TBL0_ROWS; + table->step = FR_BZ_RX_FILTER_TBL0_STEP; + } + + for (table_id = 0; table_id < EF4_FARCH_FILTER_TABLE_COUNT; table_id++) { + table = &state->table[table_id]; + if (table->size == 0) + continue; + table->used_bitmap = kcalloc(BITS_TO_LONGS(table->size), + sizeof(unsigned long), + GFP_KERNEL); + if (!table->used_bitmap) + goto fail; + table->spec = vzalloc(table->size * sizeof(*table->spec)); + if (!table->spec) + goto fail; + } + + table = &state->table[EF4_FARCH_FILTER_TABLE_RX_DEF]; + if (table->size) { + /* RX default filters must always exist */ + struct ef4_farch_filter_spec *spec; + unsigned i; + + for (i = 0; i < EF4_FARCH_FILTER_SIZE_RX_DEF; i++) { + spec = &table->spec[i]; + spec->type = EF4_FARCH_FILTER_UC_DEF + i; + ef4_farch_filter_init_rx_auto(efx, spec); + __set_bit(i, table->used_bitmap); + } + } + + ef4_farch_filter_push_rx_config(efx); + + return 0; + +fail: + ef4_farch_filter_table_remove(efx); + return -ENOMEM; +} + +/* Update scatter enable flags for filters pointing to our own RX queues */ +void ef4_farch_filter_update_rx_scatter(struct ef4_nic *efx) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + enum ef4_farch_filter_table_id table_id; + struct ef4_farch_filter_table *table; + ef4_oword_t filter; + unsigned int filter_idx; + + spin_lock_bh(&efx->filter_lock); + + for (table_id = EF4_FARCH_FILTER_TABLE_RX_IP; + table_id <= EF4_FARCH_FILTER_TABLE_RX_DEF; + table_id++) { + table = &state->table[table_id]; + + for (filter_idx = 0; filter_idx < table->size; filter_idx++) { + if (!test_bit(filter_idx, table->used_bitmap) || + table->spec[filter_idx].dmaq_id >= + efx->n_rx_channels) + continue; + + if (efx->rx_scatter) + table->spec[filter_idx].flags |= + EF4_FILTER_FLAG_RX_SCATTER; + else + table->spec[filter_idx].flags &= + ~EF4_FILTER_FLAG_RX_SCATTER; + + if (table_id == EF4_FARCH_FILTER_TABLE_RX_DEF) + /* Pushed by ef4_farch_filter_push_rx_config() */ + continue; + + ef4_farch_filter_build(&filter, &table->spec[filter_idx]); + ef4_writeo(efx, &filter, + table->offset + table->step * filter_idx); + } + } + + ef4_farch_filter_push_rx_config(efx); + + spin_unlock_bh(&efx->filter_lock); +} + +#ifdef CONFIG_RFS_ACCEL + +s32 ef4_farch_filter_rfs_insert(struct ef4_nic *efx, + struct ef4_filter_spec *gen_spec) +{ + return ef4_farch_filter_insert(efx, gen_spec, true); +} + +bool ef4_farch_filter_rfs_expire_one(struct ef4_nic *efx, u32 flow_id, + unsigned int index) +{ + struct ef4_farch_filter_state *state = efx->filter_state; + struct ef4_farch_filter_table *table = + &state->table[EF4_FARCH_FILTER_TABLE_RX_IP]; + + if (test_bit(index, table->used_bitmap) && + table->spec[index].priority == EF4_FILTER_PRI_HINT && + rps_may_expire_flow(efx->net_dev, table->spec[index].dmaq_id, + flow_id, index)) { + ef4_farch_filter_table_clear_entry(efx, table, index); + return true; + } + + return false; +} + +#endif /* CONFIG_RFS_ACCEL */ + +void ef4_farch_filter_sync_rx_mode(struct ef4_nic *efx) +{ + struct net_device *net_dev = efx->net_dev; + struct netdev_hw_addr *ha; + union ef4_multicast_hash *mc_hash = &efx->multicast_hash; + u32 crc; + int bit; + + if (!ef4_dev_registered(efx)) + return; + + netif_addr_lock_bh(net_dev); + + efx->unicast_filter = !(net_dev->flags & IFF_PROMISC); + + /* Build multicast hash table */ + if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { + memset(mc_hash, 0xff, sizeof(*mc_hash)); + } else { + memset(mc_hash, 0x00, sizeof(*mc_hash)); + netdev_for_each_mc_addr(ha, net_dev) { + crc = ether_crc_le(ETH_ALEN, ha->addr); + bit = crc & (EF4_MCAST_HASH_ENTRIES - 1); + __set_bit_le(bit, mc_hash); + } + + /* Broadcast packets go through the multicast hash filter. + * ether_crc_le() of the broadcast address is 0xbe2612ff + * so we always add bit 0xff to the mask. + */ + __set_bit_le(0xff, mc_hash); + } + + netif_addr_unlock_bh(net_dev); +} diff --git a/drivers/net/ethernet/sfc/falcon/farch_regs.h b/drivers/net/ethernet/sfc/falcon/farch_regs.h new file mode 100644 index 0000000..8095f27 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/farch_regs.h @@ -0,0 +1,2932 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2012 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_FARCH_REGS_H +#define EF4_FARCH_REGS_H + +/* + * Falcon hardware architecture definitions have a name prefix following + * the format: + * + * F__ + * + * The following strings are used: + * + * MMIO register MC register Host memory structure + * ------------------------------------------------------------- + * Address R MCR + * Bitfield RF MCRF SF + * Enumerator FE MCFE SE + * + * is the first revision to which the definition applies: + * + * A: Falcon A1 (SFC4000AB) + * B: Falcon B0 (SFC4000BA) + * C: Siena A0 (SFL9021AA) + * + * If the definition has been changed or removed in later revisions + * then is the last revision to which the definition applies; + * otherwise it is "Z". + */ + +/************************************************************************** + * + * Falcon/Siena registers and descriptors + * + ************************************************************************** + */ + +/* ADR_REGION_REG: Address region register */ +#define FR_AZ_ADR_REGION 0x00000000 +#define FRF_AZ_ADR_REGION3_LBN 96 +#define FRF_AZ_ADR_REGION3_WIDTH 18 +#define FRF_AZ_ADR_REGION2_LBN 64 +#define FRF_AZ_ADR_REGION2_WIDTH 18 +#define FRF_AZ_ADR_REGION1_LBN 32 +#define FRF_AZ_ADR_REGION1_WIDTH 18 +#define FRF_AZ_ADR_REGION0_LBN 0 +#define FRF_AZ_ADR_REGION0_WIDTH 18 + +/* INT_EN_REG_KER: Kernel driver Interrupt enable register */ +#define FR_AZ_INT_EN_KER 0x00000010 +#define FRF_AZ_KER_INT_LEVE_SEL_LBN 8 +#define FRF_AZ_KER_INT_LEVE_SEL_WIDTH 6 +#define FRF_AZ_KER_INT_CHAR_LBN 4 +#define FRF_AZ_KER_INT_CHAR_WIDTH 1 +#define FRF_AZ_KER_INT_KER_LBN 3 +#define FRF_AZ_KER_INT_KER_WIDTH 1 +#define FRF_AZ_DRV_INT_EN_KER_LBN 0 +#define FRF_AZ_DRV_INT_EN_KER_WIDTH 1 + +/* INT_EN_REG_CHAR: Char Driver interrupt enable register */ +#define FR_BZ_INT_EN_CHAR 0x00000020 +#define FRF_BZ_CHAR_INT_LEVE_SEL_LBN 8 +#define FRF_BZ_CHAR_INT_LEVE_SEL_WIDTH 6 +#define FRF_BZ_CHAR_INT_CHAR_LBN 4 +#define FRF_BZ_CHAR_INT_CHAR_WIDTH 1 +#define FRF_BZ_CHAR_INT_KER_LBN 3 +#define FRF_BZ_CHAR_INT_KER_WIDTH 1 +#define FRF_BZ_DRV_INT_EN_CHAR_LBN 0 +#define FRF_BZ_DRV_INT_EN_CHAR_WIDTH 1 + +/* INT_ADR_REG_KER: Interrupt host address for Kernel driver */ +#define FR_AZ_INT_ADR_KER 0x00000030 +#define FRF_AZ_NORM_INT_VEC_DIS_KER_LBN 64 +#define FRF_AZ_NORM_INT_VEC_DIS_KER_WIDTH 1 +#define FRF_AZ_INT_ADR_KER_LBN 0 +#define FRF_AZ_INT_ADR_KER_WIDTH 64 + +/* INT_ADR_REG_CHAR: Interrupt host address for Char driver */ +#define FR_BZ_INT_ADR_CHAR 0x00000040 +#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_LBN 64 +#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_WIDTH 1 +#define FRF_BZ_INT_ADR_CHAR_LBN 0 +#define FRF_BZ_INT_ADR_CHAR_WIDTH 64 + +/* INT_ACK_KER: Kernel interrupt acknowledge register */ +#define FR_AA_INT_ACK_KER 0x00000050 +#define FRF_AA_INT_ACK_KER_FIELD_LBN 0 +#define FRF_AA_INT_ACK_KER_FIELD_WIDTH 32 + +/* INT_ISR0_REG: Function 0 Interrupt Acknowledge Status register */ +#define FR_BZ_INT_ISR0 0x00000090 +#define FRF_BZ_INT_ISR_REG_LBN 0 +#define FRF_BZ_INT_ISR_REG_WIDTH 64 + +/* HW_INIT_REG: Hardware initialization register */ +#define FR_AZ_HW_INIT 0x000000c0 +#define FRF_BB_BDMRD_CPLF_FULL_LBN 124 +#define FRF_BB_BDMRD_CPLF_FULL_WIDTH 1 +#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_LBN 121 +#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_WIDTH 3 +#define FRF_CZ_TX_MRG_TAGS_LBN 120 +#define FRF_CZ_TX_MRG_TAGS_WIDTH 1 +#define FRF_AB_TRGT_MASK_ALL_LBN 100 +#define FRF_AB_TRGT_MASK_ALL_WIDTH 1 +#define FRF_AZ_DOORBELL_DROP_LBN 92 +#define FRF_AZ_DOORBELL_DROP_WIDTH 8 +#define FRF_AB_TX_RREQ_MASK_EN_LBN 76 +#define FRF_AB_TX_RREQ_MASK_EN_WIDTH 1 +#define FRF_AB_PE_EIDLE_DIS_LBN 75 +#define FRF_AB_PE_EIDLE_DIS_WIDTH 1 +#define FRF_AA_FC_BLOCKING_EN_LBN 45 +#define FRF_AA_FC_BLOCKING_EN_WIDTH 1 +#define FRF_BZ_B2B_REQ_EN_LBN 45 +#define FRF_BZ_B2B_REQ_EN_WIDTH 1 +#define FRF_AA_B2B_REQ_EN_LBN 44 +#define FRF_AA_B2B_REQ_EN_WIDTH 1 +#define FRF_BB_FC_BLOCKING_EN_LBN 44 +#define FRF_BB_FC_BLOCKING_EN_WIDTH 1 +#define FRF_AZ_POST_WR_MASK_LBN 40 +#define FRF_AZ_POST_WR_MASK_WIDTH 4 +#define FRF_AZ_TLP_TC_LBN 34 +#define FRF_AZ_TLP_TC_WIDTH 3 +#define FRF_AZ_TLP_ATTR_LBN 32 +#define FRF_AZ_TLP_ATTR_WIDTH 2 +#define FRF_AB_INTB_VEC_LBN 24 +#define FRF_AB_INTB_VEC_WIDTH 5 +#define FRF_AB_INTA_VEC_LBN 16 +#define FRF_AB_INTA_VEC_WIDTH 5 +#define FRF_AZ_WD_TIMER_LBN 8 +#define FRF_AZ_WD_TIMER_WIDTH 8 +#define FRF_AZ_US_DISABLE_LBN 5 +#define FRF_AZ_US_DISABLE_WIDTH 1 +#define FRF_AZ_TLP_EP_LBN 4 +#define FRF_AZ_TLP_EP_WIDTH 1 +#define FRF_AZ_ATTR_SEL_LBN 3 +#define FRF_AZ_ATTR_SEL_WIDTH 1 +#define FRF_AZ_TD_SEL_LBN 1 +#define FRF_AZ_TD_SEL_WIDTH 1 +#define FRF_AZ_TLP_TD_LBN 0 +#define FRF_AZ_TLP_TD_WIDTH 1 + +/* EE_SPI_HCMD_REG: SPI host command register */ +#define FR_AB_EE_SPI_HCMD 0x00000100 +#define FRF_AB_EE_SPI_HCMD_CMD_EN_LBN 31 +#define FRF_AB_EE_SPI_HCMD_CMD_EN_WIDTH 1 +#define FRF_AB_EE_WR_TIMER_ACTIVE_LBN 28 +#define FRF_AB_EE_WR_TIMER_ACTIVE_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_SF_SEL_LBN 24 +#define FRF_AB_EE_SPI_HCMD_SF_SEL_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_DABCNT_LBN 16 +#define FRF_AB_EE_SPI_HCMD_DABCNT_WIDTH 5 +#define FRF_AB_EE_SPI_HCMD_READ_LBN 15 +#define FRF_AB_EE_SPI_HCMD_READ_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_DUBCNT_LBN 12 +#define FRF_AB_EE_SPI_HCMD_DUBCNT_WIDTH 2 +#define FRF_AB_EE_SPI_HCMD_ADBCNT_LBN 8 +#define FRF_AB_EE_SPI_HCMD_ADBCNT_WIDTH 2 +#define FRF_AB_EE_SPI_HCMD_ENC_LBN 0 +#define FRF_AB_EE_SPI_HCMD_ENC_WIDTH 8 + +/* USR_EV_CFG: User Level Event Configuration register */ +#define FR_CZ_USR_EV_CFG 0x00000100 +#define FRF_CZ_USREV_DIS_LBN 16 +#define FRF_CZ_USREV_DIS_WIDTH 1 +#define FRF_CZ_DFLT_EVQ_LBN 0 +#define FRF_CZ_DFLT_EVQ_WIDTH 10 + +/* EE_SPI_HADR_REG: SPI host address register */ +#define FR_AB_EE_SPI_HADR 0x00000110 +#define FRF_AB_EE_SPI_HADR_DUBYTE_LBN 24 +#define FRF_AB_EE_SPI_HADR_DUBYTE_WIDTH 8 +#define FRF_AB_EE_SPI_HADR_ADR_LBN 0 +#define FRF_AB_EE_SPI_HADR_ADR_WIDTH 24 + +/* EE_SPI_HDATA_REG: SPI host data register */ +#define FR_AB_EE_SPI_HDATA 0x00000120 +#define FRF_AB_EE_SPI_HDATA3_LBN 96 +#define FRF_AB_EE_SPI_HDATA3_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA2_LBN 64 +#define FRF_AB_EE_SPI_HDATA2_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA1_LBN 32 +#define FRF_AB_EE_SPI_HDATA1_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA0_LBN 0 +#define FRF_AB_EE_SPI_HDATA0_WIDTH 32 + +/* EE_BASE_PAGE_REG: Expansion ROM base mirror register */ +#define FR_AB_EE_BASE_PAGE 0x00000130 +#define FRF_AB_EE_EXPROM_MASK_LBN 16 +#define FRF_AB_EE_EXPROM_MASK_WIDTH 13 +#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_LBN 0 +#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_WIDTH 13 + +/* EE_VPD_CFG0_REG: SPI/VPD configuration register 0 */ +#define FR_AB_EE_VPD_CFG0 0x00000140 +#define FRF_AB_EE_SF_FASTRD_EN_LBN 127 +#define FRF_AB_EE_SF_FASTRD_EN_WIDTH 1 +#define FRF_AB_EE_SF_CLOCK_DIV_LBN 120 +#define FRF_AB_EE_SF_CLOCK_DIV_WIDTH 7 +#define FRF_AB_EE_VPD_WIP_POLL_LBN 119 +#define FRF_AB_EE_VPD_WIP_POLL_WIDTH 1 +#define FRF_AB_EE_EE_CLOCK_DIV_LBN 112 +#define FRF_AB_EE_EE_CLOCK_DIV_WIDTH 7 +#define FRF_AB_EE_EE_WR_TMR_VALUE_LBN 96 +#define FRF_AB_EE_EE_WR_TMR_VALUE_WIDTH 16 +#define FRF_AB_EE_VPDW_LENGTH_LBN 80 +#define FRF_AB_EE_VPDW_LENGTH_WIDTH 15 +#define FRF_AB_EE_VPDW_BASE_LBN 64 +#define FRF_AB_EE_VPDW_BASE_WIDTH 15 +#define FRF_AB_EE_VPD_WR_CMD_EN_LBN 56 +#define FRF_AB_EE_VPD_WR_CMD_EN_WIDTH 8 +#define FRF_AB_EE_VPD_BASE_LBN 32 +#define FRF_AB_EE_VPD_BASE_WIDTH 24 +#define FRF_AB_EE_VPD_LENGTH_LBN 16 +#define FRF_AB_EE_VPD_LENGTH_WIDTH 15 +#define FRF_AB_EE_VPD_AD_SIZE_LBN 8 +#define FRF_AB_EE_VPD_AD_SIZE_WIDTH 5 +#define FRF_AB_EE_VPD_ACCESS_ON_LBN 5 +#define FRF_AB_EE_VPD_ACCESS_ON_WIDTH 1 +#define FRF_AB_EE_VPD_ACCESS_BLOCK_LBN 4 +#define FRF_AB_EE_VPD_ACCESS_BLOCK_WIDTH 1 +#define FRF_AB_EE_VPD_DEV_SF_SEL_LBN 2 +#define FRF_AB_EE_VPD_DEV_SF_SEL_WIDTH 1 +#define FRF_AB_EE_VPD_EN_AD9_MODE_LBN 1 +#define FRF_AB_EE_VPD_EN_AD9_MODE_WIDTH 1 +#define FRF_AB_EE_VPD_EN_LBN 0 +#define FRF_AB_EE_VPD_EN_WIDTH 1 + +/* EE_VPD_SW_CNTL_REG: VPD access SW control register */ +#define FR_AB_EE_VPD_SW_CNTL 0x00000150 +#define FRF_AB_EE_VPD_CYCLE_PENDING_LBN 31 +#define FRF_AB_EE_VPD_CYCLE_PENDING_WIDTH 1 +#define FRF_AB_EE_VPD_CYC_WRITE_LBN 28 +#define FRF_AB_EE_VPD_CYC_WRITE_WIDTH 1 +#define FRF_AB_EE_VPD_CYC_ADR_LBN 0 +#define FRF_AB_EE_VPD_CYC_ADR_WIDTH 15 + +/* EE_VPD_SW_DATA_REG: VPD access SW data register */ +#define FR_AB_EE_VPD_SW_DATA 0x00000160 +#define FRF_AB_EE_VPD_CYC_DAT_LBN 0 +#define FRF_AB_EE_VPD_CYC_DAT_WIDTH 32 + +/* PBMX_DBG_IADDR_REG: Capture Module address register */ +#define FR_CZ_PBMX_DBG_IADDR 0x000001f0 +#define FRF_CZ_PBMX_DBG_IADDR_LBN 0 +#define FRF_CZ_PBMX_DBG_IADDR_WIDTH 32 + +/* PCIE_CORE_INDIRECT_REG: Indirect Access to PCIE Core registers */ +#define FR_BB_PCIE_CORE_INDIRECT 0x000001f0 +#define FRF_BB_PCIE_CORE_TARGET_DATA_LBN 32 +#define FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH 32 +#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_LBN 15 +#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_WIDTH 1 +#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_LBN 0 +#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_WIDTH 12 + +/* PBMX_DBG_IDATA_REG: Capture Module data register */ +#define FR_CZ_PBMX_DBG_IDATA 0x000001f8 +#define FRF_CZ_PBMX_DBG_IDATA_LBN 0 +#define FRF_CZ_PBMX_DBG_IDATA_WIDTH 64 + +/* NIC_STAT_REG: NIC status register */ +#define FR_AB_NIC_STAT 0x00000200 +#define FRF_BB_AER_DIS_LBN 34 +#define FRF_BB_AER_DIS_WIDTH 1 +#define FRF_BB_EE_STRAP_EN_LBN 31 +#define FRF_BB_EE_STRAP_EN_WIDTH 1 +#define FRF_BB_EE_STRAP_LBN 24 +#define FRF_BB_EE_STRAP_WIDTH 4 +#define FRF_BB_REVISION_ID_LBN 17 +#define FRF_BB_REVISION_ID_WIDTH 7 +#define FRF_AB_ONCHIP_SRAM_LBN 16 +#define FRF_AB_ONCHIP_SRAM_WIDTH 1 +#define FRF_AB_SF_PRST_LBN 9 +#define FRF_AB_SF_PRST_WIDTH 1 +#define FRF_AB_EE_PRST_LBN 8 +#define FRF_AB_EE_PRST_WIDTH 1 +#define FRF_AB_ATE_MODE_LBN 3 +#define FRF_AB_ATE_MODE_WIDTH 1 +#define FRF_AB_STRAP_PINS_LBN 0 +#define FRF_AB_STRAP_PINS_WIDTH 3 + +/* GPIO_CTL_REG: GPIO control register */ +#define FR_AB_GPIO_CTL 0x00000210 +#define FRF_AB_GPIO_OUT3_LBN 112 +#define FRF_AB_GPIO_OUT3_WIDTH 16 +#define FRF_AB_GPIO_IN3_LBN 104 +#define FRF_AB_GPIO_IN3_WIDTH 8 +#define FRF_AB_GPIO_PWRUP_VALUE3_LBN 96 +#define FRF_AB_GPIO_PWRUP_VALUE3_WIDTH 8 +#define FRF_AB_GPIO_OUT2_LBN 80 +#define FRF_AB_GPIO_OUT2_WIDTH 16 +#define FRF_AB_GPIO_IN2_LBN 72 +#define FRF_AB_GPIO_IN2_WIDTH 8 +#define FRF_AB_GPIO_PWRUP_VALUE2_LBN 64 +#define FRF_AB_GPIO_PWRUP_VALUE2_WIDTH 8 +#define FRF_AB_GPIO15_OEN_LBN 63 +#define FRF_AB_GPIO15_OEN_WIDTH 1 +#define FRF_AB_GPIO14_OEN_LBN 62 +#define FRF_AB_GPIO14_OEN_WIDTH 1 +#define FRF_AB_GPIO13_OEN_LBN 61 +#define FRF_AB_GPIO13_OEN_WIDTH 1 +#define FRF_AB_GPIO12_OEN_LBN 60 +#define FRF_AB_GPIO12_OEN_WIDTH 1 +#define FRF_AB_GPIO11_OEN_LBN 59 +#define FRF_AB_GPIO11_OEN_WIDTH 1 +#define FRF_AB_GPIO10_OEN_LBN 58 +#define FRF_AB_GPIO10_OEN_WIDTH 1 +#define FRF_AB_GPIO9_OEN_LBN 57 +#define FRF_AB_GPIO9_OEN_WIDTH 1 +#define FRF_AB_GPIO8_OEN_LBN 56 +#define FRF_AB_GPIO8_OEN_WIDTH 1 +#define FRF_AB_GPIO15_OUT_LBN 55 +#define FRF_AB_GPIO15_OUT_WIDTH 1 +#define FRF_AB_GPIO14_OUT_LBN 54 +#define FRF_AB_GPIO14_OUT_WIDTH 1 +#define FRF_AB_GPIO13_OUT_LBN 53 +#define FRF_AB_GPIO13_OUT_WIDTH 1 +#define FRF_AB_GPIO12_OUT_LBN 52 +#define FRF_AB_GPIO12_OUT_WIDTH 1 +#define FRF_AB_GPIO11_OUT_LBN 51 +#define FRF_AB_GPIO11_OUT_WIDTH 1 +#define FRF_AB_GPIO10_OUT_LBN 50 +#define FRF_AB_GPIO10_OUT_WIDTH 1 +#define FRF_AB_GPIO9_OUT_LBN 49 +#define FRF_AB_GPIO9_OUT_WIDTH 1 +#define FRF_AB_GPIO8_OUT_LBN 48 +#define FRF_AB_GPIO8_OUT_WIDTH 1 +#define FRF_AB_GPIO15_IN_LBN 47 +#define FRF_AB_GPIO15_IN_WIDTH 1 +#define FRF_AB_GPIO14_IN_LBN 46 +#define FRF_AB_GPIO14_IN_WIDTH 1 +#define FRF_AB_GPIO13_IN_LBN 45 +#define FRF_AB_GPIO13_IN_WIDTH 1 +#define FRF_AB_GPIO12_IN_LBN 44 +#define FRF_AB_GPIO12_IN_WIDTH 1 +#define FRF_AB_GPIO11_IN_LBN 43 +#define FRF_AB_GPIO11_IN_WIDTH 1 +#define FRF_AB_GPIO10_IN_LBN 42 +#define FRF_AB_GPIO10_IN_WIDTH 1 +#define FRF_AB_GPIO9_IN_LBN 41 +#define FRF_AB_GPIO9_IN_WIDTH 1 +#define FRF_AB_GPIO8_IN_LBN 40 +#define FRF_AB_GPIO8_IN_WIDTH 1 +#define FRF_AB_GPIO15_PWRUP_VALUE_LBN 39 +#define FRF_AB_GPIO15_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO14_PWRUP_VALUE_LBN 38 +#define FRF_AB_GPIO14_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO13_PWRUP_VALUE_LBN 37 +#define FRF_AB_GPIO13_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO12_PWRUP_VALUE_LBN 36 +#define FRF_AB_GPIO12_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO11_PWRUP_VALUE_LBN 35 +#define FRF_AB_GPIO11_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO10_PWRUP_VALUE_LBN 34 +#define FRF_AB_GPIO10_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO9_PWRUP_VALUE_LBN 33 +#define FRF_AB_GPIO9_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO8_PWRUP_VALUE_LBN 32 +#define FRF_AB_GPIO8_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_CLK156_OUT_EN_LBN 31 +#define FRF_AB_CLK156_OUT_EN_WIDTH 1 +#define FRF_AB_USE_NIC_CLK_LBN 30 +#define FRF_AB_USE_NIC_CLK_WIDTH 1 +#define FRF_AB_GPIO5_OEN_LBN 29 +#define FRF_AB_GPIO5_OEN_WIDTH 1 +#define FRF_AB_GPIO4_OEN_LBN 28 +#define FRF_AB_GPIO4_OEN_WIDTH 1 +#define FRF_AB_GPIO3_OEN_LBN 27 +#define FRF_AB_GPIO3_OEN_WIDTH 1 +#define FRF_AB_GPIO2_OEN_LBN 26 +#define FRF_AB_GPIO2_OEN_WIDTH 1 +#define FRF_AB_GPIO1_OEN_LBN 25 +#define FRF_AB_GPIO1_OEN_WIDTH 1 +#define FRF_AB_GPIO0_OEN_LBN 24 +#define FRF_AB_GPIO0_OEN_WIDTH 1 +#define FRF_AB_GPIO7_OUT_LBN 23 +#define FRF_AB_GPIO7_OUT_WIDTH 1 +#define FRF_AB_GPIO6_OUT_LBN 22 +#define FRF_AB_GPIO6_OUT_WIDTH 1 +#define FRF_AB_GPIO5_OUT_LBN 21 +#define FRF_AB_GPIO5_OUT_WIDTH 1 +#define FRF_AB_GPIO4_OUT_LBN 20 +#define FRF_AB_GPIO4_OUT_WIDTH 1 +#define FRF_AB_GPIO3_OUT_LBN 19 +#define FRF_AB_GPIO3_OUT_WIDTH 1 +#define FRF_AB_GPIO2_OUT_LBN 18 +#define FRF_AB_GPIO2_OUT_WIDTH 1 +#define FRF_AB_GPIO1_OUT_LBN 17 +#define FRF_AB_GPIO1_OUT_WIDTH 1 +#define FRF_AB_GPIO0_OUT_LBN 16 +#define FRF_AB_GPIO0_OUT_WIDTH 1 +#define FRF_AB_GPIO7_IN_LBN 15 +#define FRF_AB_GPIO7_IN_WIDTH 1 +#define FRF_AB_GPIO6_IN_LBN 14 +#define FRF_AB_GPIO6_IN_WIDTH 1 +#define FRF_AB_GPIO5_IN_LBN 13 +#define FRF_AB_GPIO5_IN_WIDTH 1 +#define FRF_AB_GPIO4_IN_LBN 12 +#define FRF_AB_GPIO4_IN_WIDTH 1 +#define FRF_AB_GPIO3_IN_LBN 11 +#define FRF_AB_GPIO3_IN_WIDTH 1 +#define FRF_AB_GPIO2_IN_LBN 10 +#define FRF_AB_GPIO2_IN_WIDTH 1 +#define FRF_AB_GPIO1_IN_LBN 9 +#define FRF_AB_GPIO1_IN_WIDTH 1 +#define FRF_AB_GPIO0_IN_LBN 8 +#define FRF_AB_GPIO0_IN_WIDTH 1 +#define FRF_AB_GPIO7_PWRUP_VALUE_LBN 7 +#define FRF_AB_GPIO7_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO6_PWRUP_VALUE_LBN 6 +#define FRF_AB_GPIO6_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO5_PWRUP_VALUE_LBN 5 +#define FRF_AB_GPIO5_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO4_PWRUP_VALUE_LBN 4 +#define FRF_AB_GPIO4_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO3_PWRUP_VALUE_LBN 3 +#define FRF_AB_GPIO3_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO2_PWRUP_VALUE_LBN 2 +#define FRF_AB_GPIO2_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO1_PWRUP_VALUE_LBN 1 +#define FRF_AB_GPIO1_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO0_PWRUP_VALUE_LBN 0 +#define FRF_AB_GPIO0_PWRUP_VALUE_WIDTH 1 + +/* GLB_CTL_REG: Global control register */ +#define FR_AB_GLB_CTL 0x00000220 +#define FRF_AB_EXT_PHY_RST_CTL_LBN 63 +#define FRF_AB_EXT_PHY_RST_CTL_WIDTH 1 +#define FRF_AB_XAUI_SD_RST_CTL_LBN 62 +#define FRF_AB_XAUI_SD_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_SD_RST_CTL_LBN 61 +#define FRF_AB_PCIE_SD_RST_CTL_WIDTH 1 +#define FRF_AA_PCIX_RST_CTL_LBN 60 +#define FRF_AA_PCIX_RST_CTL_WIDTH 1 +#define FRF_BB_BIU_RST_CTL_LBN 60 +#define FRF_BB_BIU_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_STKY_RST_CTL_LBN 59 +#define FRF_AB_PCIE_STKY_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_NSTKY_RST_CTL_LBN 58 +#define FRF_AB_PCIE_NSTKY_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_CORE_RST_CTL_LBN 57 +#define FRF_AB_PCIE_CORE_RST_CTL_WIDTH 1 +#define FRF_AB_XGRX_RST_CTL_LBN 56 +#define FRF_AB_XGRX_RST_CTL_WIDTH 1 +#define FRF_AB_XGTX_RST_CTL_LBN 55 +#define FRF_AB_XGTX_RST_CTL_WIDTH 1 +#define FRF_AB_EM_RST_CTL_LBN 54 +#define FRF_AB_EM_RST_CTL_WIDTH 1 +#define FRF_AB_EV_RST_CTL_LBN 53 +#define FRF_AB_EV_RST_CTL_WIDTH 1 +#define FRF_AB_SR_RST_CTL_LBN 52 +#define FRF_AB_SR_RST_CTL_WIDTH 1 +#define FRF_AB_RX_RST_CTL_LBN 51 +#define FRF_AB_RX_RST_CTL_WIDTH 1 +#define FRF_AB_TX_RST_CTL_LBN 50 +#define FRF_AB_TX_RST_CTL_WIDTH 1 +#define FRF_AB_EE_RST_CTL_LBN 49 +#define FRF_AB_EE_RST_CTL_WIDTH 1 +#define FRF_AB_CS_RST_CTL_LBN 48 +#define FRF_AB_CS_RST_CTL_WIDTH 1 +#define FRF_AB_HOT_RST_CTL_LBN 40 +#define FRF_AB_HOT_RST_CTL_WIDTH 2 +#define FRF_AB_RST_EXT_PHY_LBN 31 +#define FRF_AB_RST_EXT_PHY_WIDTH 1 +#define FRF_AB_RST_XAUI_SD_LBN 30 +#define FRF_AB_RST_XAUI_SD_WIDTH 1 +#define FRF_AB_RST_PCIE_SD_LBN 29 +#define FRF_AB_RST_PCIE_SD_WIDTH 1 +#define FRF_AA_RST_PCIX_LBN 28 +#define FRF_AA_RST_PCIX_WIDTH 1 +#define FRF_BB_RST_BIU_LBN 28 +#define FRF_BB_RST_BIU_WIDTH 1 +#define FRF_AB_RST_PCIE_STKY_LBN 27 +#define FRF_AB_RST_PCIE_STKY_WIDTH 1 +#define FRF_AB_RST_PCIE_NSTKY_LBN 26 +#define FRF_AB_RST_PCIE_NSTKY_WIDTH 1 +#define FRF_AB_RST_PCIE_CORE_LBN 25 +#define FRF_AB_RST_PCIE_CORE_WIDTH 1 +#define FRF_AB_RST_XGRX_LBN 24 +#define FRF_AB_RST_XGRX_WIDTH 1 +#define FRF_AB_RST_XGTX_LBN 23 +#define FRF_AB_RST_XGTX_WIDTH 1 +#define FRF_AB_RST_EM_LBN 22 +#define FRF_AB_RST_EM_WIDTH 1 +#define FRF_AB_RST_EV_LBN 21 +#define FRF_AB_RST_EV_WIDTH 1 +#define FRF_AB_RST_SR_LBN 20 +#define FRF_AB_RST_SR_WIDTH 1 +#define FRF_AB_RST_RX_LBN 19 +#define FRF_AB_RST_RX_WIDTH 1 +#define FRF_AB_RST_TX_LBN 18 +#define FRF_AB_RST_TX_WIDTH 1 +#define FRF_AB_RST_SF_LBN 17 +#define FRF_AB_RST_SF_WIDTH 1 +#define FRF_AB_RST_CS_LBN 16 +#define FRF_AB_RST_CS_WIDTH 1 +#define FRF_AB_INT_RST_DUR_LBN 4 +#define FRF_AB_INT_RST_DUR_WIDTH 3 +#define FRF_AB_EXT_PHY_RST_DUR_LBN 1 +#define FRF_AB_EXT_PHY_RST_DUR_WIDTH 3 +#define FFE_AB_EXT_PHY_RST_DUR_10240US 7 +#define FFE_AB_EXT_PHY_RST_DUR_5120US 6 +#define FFE_AB_EXT_PHY_RST_DUR_2560US 5 +#define FFE_AB_EXT_PHY_RST_DUR_1280US 4 +#define FFE_AB_EXT_PHY_RST_DUR_640US 3 +#define FFE_AB_EXT_PHY_RST_DUR_320US 2 +#define FFE_AB_EXT_PHY_RST_DUR_160US 1 +#define FFE_AB_EXT_PHY_RST_DUR_80US 0 +#define FRF_AB_SWRST_LBN 0 +#define FRF_AB_SWRST_WIDTH 1 + +/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */ +#define FR_AZ_FATAL_INTR_KER 0x00000230 +#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_LBN 44 +#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_WIDTH 1 +#define FRF_AB_PCI_BUSERR_INT_KER_EN_LBN 43 +#define FRF_AB_PCI_BUSERR_INT_KER_EN_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_KER_EN_LBN 43 +#define FRF_CZ_MBU_PERR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_SRAM_OOB_INT_KER_EN_LBN 42 +#define FRF_AZ_SRAM_OOB_INT_KER_EN_WIDTH 1 +#define FRF_AZ_BUFID_OOB_INT_KER_EN_LBN 41 +#define FRF_AZ_BUFID_OOB_INT_KER_EN_WIDTH 1 +#define FRF_AZ_MEM_PERR_INT_KER_EN_LBN 40 +#define FRF_AZ_MEM_PERR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_RBUF_OWN_INT_KER_EN_LBN 39 +#define FRF_AZ_RBUF_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_TBUF_OWN_INT_KER_EN_LBN 38 +#define FRF_AZ_TBUF_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_LBN 37 +#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_LBN 36 +#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_EVQ_OWN_INT_KER_EN_LBN 35 +#define FRF_AZ_EVQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_EVF_OFLO_INT_KER_EN_LBN 34 +#define FRF_AZ_EVF_OFLO_INT_KER_EN_WIDTH 1 +#define FRF_AZ_ILL_ADR_INT_KER_EN_LBN 33 +#define FRF_AZ_ILL_ADR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_SRM_PERR_INT_KER_EN_LBN 32 +#define FRF_AZ_SRM_PERR_INT_KER_EN_WIDTH 1 +#define FRF_CZ_SRAM_PERR_INT_P_KER_LBN 12 +#define FRF_CZ_SRAM_PERR_INT_P_KER_WIDTH 1 +#define FRF_AB_PCI_BUSERR_INT_KER_LBN 11 +#define FRF_AB_PCI_BUSERR_INT_KER_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_KER_LBN 11 +#define FRF_CZ_MBU_PERR_INT_KER_WIDTH 1 +#define FRF_AZ_SRAM_OOB_INT_KER_LBN 10 +#define FRF_AZ_SRAM_OOB_INT_KER_WIDTH 1 +#define FRF_AZ_BUFID_DC_OOB_INT_KER_LBN 9 +#define FRF_AZ_BUFID_DC_OOB_INT_KER_WIDTH 1 +#define FRF_AZ_MEM_PERR_INT_KER_LBN 8 +#define FRF_AZ_MEM_PERR_INT_KER_WIDTH 1 +#define FRF_AZ_RBUF_OWN_INT_KER_LBN 7 +#define FRF_AZ_RBUF_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_TBUF_OWN_INT_KER_LBN 6 +#define FRF_AZ_TBUF_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_RDESCQ_OWN_INT_KER_LBN 5 +#define FRF_AZ_RDESCQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_TDESCQ_OWN_INT_KER_LBN 4 +#define FRF_AZ_TDESCQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_EVQ_OWN_INT_KER_LBN 3 +#define FRF_AZ_EVQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_EVF_OFLO_INT_KER_LBN 2 +#define FRF_AZ_EVF_OFLO_INT_KER_WIDTH 1 +#define FRF_AZ_ILL_ADR_INT_KER_LBN 1 +#define FRF_AZ_ILL_ADR_INT_KER_WIDTH 1 +#define FRF_AZ_SRM_PERR_INT_KER_LBN 0 +#define FRF_AZ_SRM_PERR_INT_KER_WIDTH 1 + +/* FATAL_INTR_REG_CHAR: Fatal interrupt register for Char */ +#define FR_BZ_FATAL_INTR_CHAR 0x00000240 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_LBN 44 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_WIDTH 1 +#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_LBN 43 +#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_CHAR_EN_LBN 43 +#define FRF_CZ_MBU_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_LBN 42 +#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_LBN 41 +#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_MEM_PERR_INT_CHAR_EN_LBN 40 +#define FRF_BZ_MEM_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_LBN 39 +#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_LBN 38 +#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_LBN 37 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_LBN 36 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_LBN 35 +#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_LBN 34 +#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_EN_LBN 33 +#define FRF_BZ_ILL_ADR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_SRM_PERR_INT_CHAR_EN_LBN 32 +#define FRF_BZ_SRM_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_LBN 12 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_WIDTH 1 +#define FRF_BB_PCI_BUSERR_INT_CHAR_LBN 11 +#define FRF_BB_PCI_BUSERR_INT_CHAR_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_CHAR_LBN 11 +#define FRF_CZ_MBU_PERR_INT_CHAR_WIDTH 1 +#define FRF_BZ_SRAM_OOB_INT_CHAR_LBN 10 +#define FRF_BZ_SRAM_OOB_INT_CHAR_WIDTH 1 +#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_LBN 9 +#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_WIDTH 1 +#define FRF_BZ_MEM_PERR_INT_CHAR_LBN 8 +#define FRF_BZ_MEM_PERR_INT_CHAR_WIDTH 1 +#define FRF_BZ_RBUF_OWN_INT_CHAR_LBN 7 +#define FRF_BZ_RBUF_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_TBUF_OWN_INT_CHAR_LBN 6 +#define FRF_BZ_TBUF_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_LBN 5 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_LBN 4 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_EVQ_OWN_INT_CHAR_LBN 3 +#define FRF_BZ_EVQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_EVF_OFLO_INT_CHAR_LBN 2 +#define FRF_BZ_EVF_OFLO_INT_CHAR_WIDTH 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_LBN 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_WIDTH 1 +#define FRF_BZ_SRM_PERR_INT_CHAR_LBN 0 +#define FRF_BZ_SRM_PERR_INT_CHAR_WIDTH 1 + +/* DP_CTRL_REG: Datapath control register */ +#define FR_BZ_DP_CTRL 0x00000250 +#define FRF_BZ_FLS_EVQ_ID_LBN 0 +#define FRF_BZ_FLS_EVQ_ID_WIDTH 12 + +/* MEM_STAT_REG: Memory status register */ +#define FR_AZ_MEM_STAT 0x00000260 +#define FRF_AB_MEM_PERR_VEC_LBN 53 +#define FRF_AB_MEM_PERR_VEC_WIDTH 38 +#define FRF_AB_MBIST_CORR_LBN 38 +#define FRF_AB_MBIST_CORR_WIDTH 15 +#define FRF_AB_MBIST_ERR_LBN 0 +#define FRF_AB_MBIST_ERR_WIDTH 40 +#define FRF_CZ_MEM_PERR_VEC_LBN 0 +#define FRF_CZ_MEM_PERR_VEC_WIDTH 35 + +/* CS_DEBUG_REG: Debug register */ +#define FR_AZ_CS_DEBUG 0x00000270 +#define FRF_AB_GLB_DEBUG2_SEL_LBN 50 +#define FRF_AB_GLB_DEBUG2_SEL_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL2_LBN 47 +#define FRF_AB_DEBUG_BLK_SEL2_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL1_LBN 44 +#define FRF_AB_DEBUG_BLK_SEL1_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL0_LBN 41 +#define FRF_AB_DEBUG_BLK_SEL0_WIDTH 3 +#define FRF_CZ_CS_PORT_NUM_LBN 40 +#define FRF_CZ_CS_PORT_NUM_WIDTH 2 +#define FRF_AB_MISC_DEBUG_ADDR_LBN 36 +#define FRF_AB_MISC_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_SERDES_DEBUG_ADDR_LBN 31 +#define FRF_AB_SERDES_DEBUG_ADDR_WIDTH 5 +#define FRF_CZ_CS_PORT_FPE_LBN 1 +#define FRF_CZ_CS_PORT_FPE_WIDTH 35 +#define FRF_AB_EM_DEBUG_ADDR_LBN 26 +#define FRF_AB_EM_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_SR_DEBUG_ADDR_LBN 21 +#define FRF_AB_SR_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_EV_DEBUG_ADDR_LBN 16 +#define FRF_AB_EV_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_RX_DEBUG_ADDR_LBN 11 +#define FRF_AB_RX_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_TX_DEBUG_ADDR_LBN 6 +#define FRF_AB_TX_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_CS_BIU_DEBUG_ADDR_LBN 1 +#define FRF_AB_CS_BIU_DEBUG_ADDR_WIDTH 5 +#define FRF_AZ_CS_DEBUG_EN_LBN 0 +#define FRF_AZ_CS_DEBUG_EN_WIDTH 1 + +/* DRIVER_REG: Driver scratch register [0-7] */ +#define FR_AZ_DRIVER 0x00000280 +#define FR_AZ_DRIVER_STEP 16 +#define FR_AZ_DRIVER_ROWS 8 +#define FRF_AZ_DRIVER_DW0_LBN 0 +#define FRF_AZ_DRIVER_DW0_WIDTH 32 + +/* ALTERA_BUILD_REG: Altera build register */ +#define FR_AZ_ALTERA_BUILD 0x00000300 +#define FRF_AZ_ALTERA_BUILD_VER_LBN 0 +#define FRF_AZ_ALTERA_BUILD_VER_WIDTH 32 + +/* CSR_SPARE_REG: Spare register */ +#define FR_AZ_CSR_SPARE 0x00000310 +#define FRF_AB_MEM_PERR_EN_LBN 64 +#define FRF_AB_MEM_PERR_EN_WIDTH 38 +#define FRF_CZ_MEM_PERR_EN_LBN 64 +#define FRF_CZ_MEM_PERR_EN_WIDTH 35 +#define FRF_AB_MEM_PERR_EN_TX_DATA_LBN 72 +#define FRF_AB_MEM_PERR_EN_TX_DATA_WIDTH 2 +#define FRF_AZ_CSR_SPARE_BITS_LBN 0 +#define FRF_AZ_CSR_SPARE_BITS_WIDTH 32 + +/* PCIE_SD_CTL0123_REG: PCIE SerDes control register 0 to 3 */ +#define FR_AB_PCIE_SD_CTL0123 0x00000320 +#define FRF_AB_PCIE_TESTSIG_H_LBN 96 +#define FRF_AB_PCIE_TESTSIG_H_WIDTH 19 +#define FRF_AB_PCIE_TESTSIG_L_LBN 64 +#define FRF_AB_PCIE_TESTSIG_L_WIDTH 19 +#define FRF_AB_PCIE_OFFSET_LBN 56 +#define FRF_AB_PCIE_OFFSET_WIDTH 8 +#define FRF_AB_PCIE_OFFSETEN_H_LBN 55 +#define FRF_AB_PCIE_OFFSETEN_H_WIDTH 1 +#define FRF_AB_PCIE_OFFSETEN_L_LBN 54 +#define FRF_AB_PCIE_OFFSETEN_L_WIDTH 1 +#define FRF_AB_PCIE_HIVMODE_H_LBN 53 +#define FRF_AB_PCIE_HIVMODE_H_WIDTH 1 +#define FRF_AB_PCIE_HIVMODE_L_LBN 52 +#define FRF_AB_PCIE_HIVMODE_L_WIDTH 1 +#define FRF_AB_PCIE_PARRESET_H_LBN 51 +#define FRF_AB_PCIE_PARRESET_H_WIDTH 1 +#define FRF_AB_PCIE_PARRESET_L_LBN 50 +#define FRF_AB_PCIE_PARRESET_L_WIDTH 1 +#define FRF_AB_PCIE_LPBKWDRV_H_LBN 49 +#define FRF_AB_PCIE_LPBKWDRV_H_WIDTH 1 +#define FRF_AB_PCIE_LPBKWDRV_L_LBN 48 +#define FRF_AB_PCIE_LPBKWDRV_L_WIDTH 1 +#define FRF_AB_PCIE_LPBK_LBN 40 +#define FRF_AB_PCIE_LPBK_WIDTH 8 +#define FRF_AB_PCIE_PARLPBK_LBN 32 +#define FRF_AB_PCIE_PARLPBK_WIDTH 8 +#define FRF_AB_PCIE_RXTERMADJ_H_LBN 30 +#define FRF_AB_PCIE_RXTERMADJ_H_WIDTH 2 +#define FRF_AB_PCIE_RXTERMADJ_L_LBN 28 +#define FRF_AB_PCIE_RXTERMADJ_L_WIDTH 2 +#define FFE_AB_PCIE_RXTERMADJ_MIN15PCNT 3 +#define FFE_AB_PCIE_RXTERMADJ_PL10PCNT 2 +#define FFE_AB_PCIE_RXTERMADJ_MIN17PCNT 1 +#define FFE_AB_PCIE_RXTERMADJ_NOMNL 0 +#define FRF_AB_PCIE_TXTERMADJ_H_LBN 26 +#define FRF_AB_PCIE_TXTERMADJ_H_WIDTH 2 +#define FRF_AB_PCIE_TXTERMADJ_L_LBN 24 +#define FRF_AB_PCIE_TXTERMADJ_L_WIDTH 2 +#define FFE_AB_PCIE_TXTERMADJ_MIN15PCNT 3 +#define FFE_AB_PCIE_TXTERMADJ_PL10PCNT 2 +#define FFE_AB_PCIE_TXTERMADJ_MIN17PCNT 1 +#define FFE_AB_PCIE_TXTERMADJ_NOMNL 0 +#define FRF_AB_PCIE_RXEQCTL_H_LBN 18 +#define FRF_AB_PCIE_RXEQCTL_H_WIDTH 2 +#define FRF_AB_PCIE_RXEQCTL_L_LBN 16 +#define FRF_AB_PCIE_RXEQCTL_L_WIDTH 2 +#define FFE_AB_PCIE_RXEQCTL_OFF_ALT 3 +#define FFE_AB_PCIE_RXEQCTL_OFF 2 +#define FFE_AB_PCIE_RXEQCTL_MIN 1 +#define FFE_AB_PCIE_RXEQCTL_MAX 0 +#define FRF_AB_PCIE_HIDRV_LBN 8 +#define FRF_AB_PCIE_HIDRV_WIDTH 8 +#define FRF_AB_PCIE_LODRV_LBN 0 +#define FRF_AB_PCIE_LODRV_WIDTH 8 + +/* PCIE_SD_CTL45_REG: PCIE SerDes control register 4 and 5 */ +#define FR_AB_PCIE_SD_CTL45 0x00000330 +#define FRF_AB_PCIE_DTX7_LBN 60 +#define FRF_AB_PCIE_DTX7_WIDTH 4 +#define FRF_AB_PCIE_DTX6_LBN 56 +#define FRF_AB_PCIE_DTX6_WIDTH 4 +#define FRF_AB_PCIE_DTX5_LBN 52 +#define FRF_AB_PCIE_DTX5_WIDTH 4 +#define FRF_AB_PCIE_DTX4_LBN 48 +#define FRF_AB_PCIE_DTX4_WIDTH 4 +#define FRF_AB_PCIE_DTX3_LBN 44 +#define FRF_AB_PCIE_DTX3_WIDTH 4 +#define FRF_AB_PCIE_DTX2_LBN 40 +#define FRF_AB_PCIE_DTX2_WIDTH 4 +#define FRF_AB_PCIE_DTX1_LBN 36 +#define FRF_AB_PCIE_DTX1_WIDTH 4 +#define FRF_AB_PCIE_DTX0_LBN 32 +#define FRF_AB_PCIE_DTX0_WIDTH 4 +#define FRF_AB_PCIE_DEQ7_LBN 28 +#define FRF_AB_PCIE_DEQ7_WIDTH 4 +#define FRF_AB_PCIE_DEQ6_LBN 24 +#define FRF_AB_PCIE_DEQ6_WIDTH 4 +#define FRF_AB_PCIE_DEQ5_LBN 20 +#define FRF_AB_PCIE_DEQ5_WIDTH 4 +#define FRF_AB_PCIE_DEQ4_LBN 16 +#define FRF_AB_PCIE_DEQ4_WIDTH 4 +#define FRF_AB_PCIE_DEQ3_LBN 12 +#define FRF_AB_PCIE_DEQ3_WIDTH 4 +#define FRF_AB_PCIE_DEQ2_LBN 8 +#define FRF_AB_PCIE_DEQ2_WIDTH 4 +#define FRF_AB_PCIE_DEQ1_LBN 4 +#define FRF_AB_PCIE_DEQ1_WIDTH 4 +#define FRF_AB_PCIE_DEQ0_LBN 0 +#define FRF_AB_PCIE_DEQ0_WIDTH 4 + +/* PCIE_PCS_CTL_STAT_REG: PCIE PCS control and status register */ +#define FR_AB_PCIE_PCS_CTL_STAT 0x00000340 +#define FRF_AB_PCIE_PRBSERRCOUNT0_H_LBN 52 +#define FRF_AB_PCIE_PRBSERRCOUNT0_H_WIDTH 4 +#define FRF_AB_PCIE_PRBSERRCOUNT0_L_LBN 48 +#define FRF_AB_PCIE_PRBSERRCOUNT0_L_WIDTH 4 +#define FRF_AB_PCIE_PRBSERR_LBN 40 +#define FRF_AB_PCIE_PRBSERR_WIDTH 8 +#define FRF_AB_PCIE_PRBSERRH0_LBN 32 +#define FRF_AB_PCIE_PRBSERRH0_WIDTH 8 +#define FRF_AB_PCIE_FASTINIT_H_LBN 15 +#define FRF_AB_PCIE_FASTINIT_H_WIDTH 1 +#define FRF_AB_PCIE_FASTINIT_L_LBN 14 +#define FRF_AB_PCIE_FASTINIT_L_WIDTH 1 +#define FRF_AB_PCIE_CTCDISABLE_H_LBN 13 +#define FRF_AB_PCIE_CTCDISABLE_H_WIDTH 1 +#define FRF_AB_PCIE_CTCDISABLE_L_LBN 12 +#define FRF_AB_PCIE_CTCDISABLE_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSSYNC_H_LBN 11 +#define FRF_AB_PCIE_PRBSSYNC_H_WIDTH 1 +#define FRF_AB_PCIE_PRBSSYNC_L_LBN 10 +#define FRF_AB_PCIE_PRBSSYNC_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSERRACK_H_LBN 9 +#define FRF_AB_PCIE_PRBSERRACK_H_WIDTH 1 +#define FRF_AB_PCIE_PRBSERRACK_L_LBN 8 +#define FRF_AB_PCIE_PRBSERRACK_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSSEL_LBN 0 +#define FRF_AB_PCIE_PRBSSEL_WIDTH 8 + +/* DEBUG_DATA_OUT_REG: Live Debug and Debug 2 out ports */ +#define FR_BB_DEBUG_DATA_OUT 0x00000350 +#define FRF_BB_DEBUG2_PORT_LBN 25 +#define FRF_BB_DEBUG2_PORT_WIDTH 15 +#define FRF_BB_DEBUG1_PORT_LBN 0 +#define FRF_BB_DEBUG1_PORT_WIDTH 25 + +/* EVQ_RPTR_REGP0: Event queue read pointer register */ +#define FR_BZ_EVQ_RPTR_P0 0x00000400 +#define FR_BZ_EVQ_RPTR_P0_STEP 8192 +#define FR_BZ_EVQ_RPTR_P0_ROWS 1024 +/* EVQ_RPTR_REG_KER: Event queue read pointer register */ +#define FR_AA_EVQ_RPTR_KER 0x00011b00 +#define FR_AA_EVQ_RPTR_KER_STEP 4 +#define FR_AA_EVQ_RPTR_KER_ROWS 4 +/* EVQ_RPTR_REG: Event queue read pointer register */ +#define FR_BZ_EVQ_RPTR 0x00fa0000 +#define FR_BZ_EVQ_RPTR_STEP 16 +#define FR_BB_EVQ_RPTR_ROWS 4096 +#define FR_CZ_EVQ_RPTR_ROWS 1024 +/* EVQ_RPTR_REGP123: Event queue read pointer register */ +#define FR_BB_EVQ_RPTR_P123 0x01000400 +#define FR_BB_EVQ_RPTR_P123_STEP 8192 +#define FR_BB_EVQ_RPTR_P123_ROWS 3072 +#define FRF_AZ_EVQ_RPTR_VLD_LBN 15 +#define FRF_AZ_EVQ_RPTR_VLD_WIDTH 1 +#define FRF_AZ_EVQ_RPTR_LBN 0 +#define FRF_AZ_EVQ_RPTR_WIDTH 15 + +/* TIMER_COMMAND_REGP0: Timer Command Registers */ +#define FR_BZ_TIMER_COMMAND_P0 0x00000420 +#define FR_BZ_TIMER_COMMAND_P0_STEP 8192 +#define FR_BZ_TIMER_COMMAND_P0_ROWS 1024 +/* TIMER_COMMAND_REG_KER: Timer Command Registers */ +#define FR_AA_TIMER_COMMAND_KER 0x00000420 +#define FR_AA_TIMER_COMMAND_KER_STEP 8192 +#define FR_AA_TIMER_COMMAND_KER_ROWS 4 +/* TIMER_COMMAND_REGP123: Timer Command Registers */ +#define FR_BB_TIMER_COMMAND_P123 0x01000420 +#define FR_BB_TIMER_COMMAND_P123_STEP 8192 +#define FR_BB_TIMER_COMMAND_P123_ROWS 3072 +#define FRF_CZ_TC_TIMER_MODE_LBN 14 +#define FRF_CZ_TC_TIMER_MODE_WIDTH 2 +#define FRF_AB_TC_TIMER_MODE_LBN 12 +#define FRF_AB_TC_TIMER_MODE_WIDTH 2 +#define FRF_CZ_TC_TIMER_VAL_LBN 0 +#define FRF_CZ_TC_TIMER_VAL_WIDTH 14 +#define FRF_AB_TC_TIMER_VAL_LBN 0 +#define FRF_AB_TC_TIMER_VAL_WIDTH 12 + +/* DRV_EV_REG: Driver generated event register */ +#define FR_AZ_DRV_EV 0x00000440 +#define FRF_AZ_DRV_EV_QID_LBN 64 +#define FRF_AZ_DRV_EV_QID_WIDTH 12 +#define FRF_AZ_DRV_EV_DATA_LBN 0 +#define FRF_AZ_DRV_EV_DATA_WIDTH 64 + +/* EVQ_CTL_REG: Event queue control register */ +#define FR_AZ_EVQ_CTL 0x00000450 +#define FRF_CZ_RX_EVQ_WAKEUP_MASK_LBN 15 +#define FRF_CZ_RX_EVQ_WAKEUP_MASK_WIDTH 10 +#define FRF_BB_RX_EVQ_WAKEUP_MASK_LBN 15 +#define FRF_BB_RX_EVQ_WAKEUP_MASK_WIDTH 6 +#define FRF_AZ_EVQ_OWNERR_CTL_LBN 14 +#define FRF_AZ_EVQ_OWNERR_CTL_WIDTH 1 +#define FRF_AZ_EVQ_FIFO_AF_TH_LBN 7 +#define FRF_AZ_EVQ_FIFO_AF_TH_WIDTH 7 +#define FRF_AZ_EVQ_FIFO_NOTAF_TH_LBN 0 +#define FRF_AZ_EVQ_FIFO_NOTAF_TH_WIDTH 7 + +/* EVQ_CNT1_REG: Event counter 1 register */ +#define FR_AZ_EVQ_CNT1 0x00000460 +#define FRF_AZ_EVQ_CNT_PRE_FIFO_LBN 120 +#define FRF_AZ_EVQ_CNT_PRE_FIFO_WIDTH 7 +#define FRF_AZ_EVQ_CNT_TOBIU_LBN 100 +#define FRF_AZ_EVQ_CNT_TOBIU_WIDTH 20 +#define FRF_AZ_EVQ_TX_REQ_CNT_LBN 80 +#define FRF_AZ_EVQ_TX_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_RX_REQ_CNT_LBN 60 +#define FRF_AZ_EVQ_RX_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_EM_REQ_CNT_LBN 40 +#define FRF_AZ_EVQ_EM_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_CSR_REQ_CNT_LBN 20 +#define FRF_AZ_EVQ_CSR_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_ERR_REQ_CNT_LBN 0 +#define FRF_AZ_EVQ_ERR_REQ_CNT_WIDTH 20 + +/* EVQ_CNT2_REG: Event counter 2 register */ +#define FR_AZ_EVQ_CNT2 0x00000470 +#define FRF_AZ_EVQ_UPD_REQ_CNT_LBN 104 +#define FRF_AZ_EVQ_UPD_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_CLR_REQ_CNT_LBN 84 +#define FRF_AZ_EVQ_CLR_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_RDY_CNT_LBN 80 +#define FRF_AZ_EVQ_RDY_CNT_WIDTH 4 +#define FRF_AZ_EVQ_WU_REQ_CNT_LBN 60 +#define FRF_AZ_EVQ_WU_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_WET_REQ_CNT_LBN 40 +#define FRF_AZ_EVQ_WET_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_INIT_REQ_CNT_LBN 20 +#define FRF_AZ_EVQ_INIT_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_TM_REQ_CNT_LBN 0 +#define FRF_AZ_EVQ_TM_REQ_CNT_WIDTH 20 + +/* USR_EV_REG: Event mailbox register */ +#define FR_CZ_USR_EV 0x00000540 +#define FR_CZ_USR_EV_STEP 8192 +#define FR_CZ_USR_EV_ROWS 1024 +#define FRF_CZ_USR_EV_DATA_LBN 0 +#define FRF_CZ_USR_EV_DATA_WIDTH 32 + +/* BUF_TBL_CFG_REG: Buffer table configuration register */ +#define FR_AZ_BUF_TBL_CFG 0x00000600 +#define FRF_AZ_BUF_TBL_MODE_LBN 3 +#define FRF_AZ_BUF_TBL_MODE_WIDTH 1 + +/* SRM_RX_DC_CFG_REG: SRAM receive descriptor cache configuration register */ +#define FR_AZ_SRM_RX_DC_CFG 0x00000610 +#define FRF_AZ_SRM_CLK_TMP_EN_LBN 21 +#define FRF_AZ_SRM_CLK_TMP_EN_WIDTH 1 +#define FRF_AZ_SRM_RX_DC_BASE_ADR_LBN 0 +#define FRF_AZ_SRM_RX_DC_BASE_ADR_WIDTH 21 + +/* SRM_TX_DC_CFG_REG: SRAM transmit descriptor cache configuration register */ +#define FR_AZ_SRM_TX_DC_CFG 0x00000620 +#define FRF_AZ_SRM_TX_DC_BASE_ADR_LBN 0 +#define FRF_AZ_SRM_TX_DC_BASE_ADR_WIDTH 21 + +/* SRM_CFG_REG: SRAM configuration register */ +#define FR_AZ_SRM_CFG 0x00000630 +#define FRF_AZ_SRM_OOB_ADR_INTEN_LBN 5 +#define FRF_AZ_SRM_OOB_ADR_INTEN_WIDTH 1 +#define FRF_AZ_SRM_OOB_BUF_INTEN_LBN 4 +#define FRF_AZ_SRM_OOB_BUF_INTEN_WIDTH 1 +#define FRF_AZ_SRM_INIT_EN_LBN 3 +#define FRF_AZ_SRM_INIT_EN_WIDTH 1 +#define FRF_AZ_SRM_NUM_BANK_LBN 2 +#define FRF_AZ_SRM_NUM_BANK_WIDTH 1 +#define FRF_AZ_SRM_BANK_SIZE_LBN 0 +#define FRF_AZ_SRM_BANK_SIZE_WIDTH 2 + +/* BUF_TBL_UPD_REG: Buffer table update register */ +#define FR_AZ_BUF_TBL_UPD 0x00000650 +#define FRF_AZ_BUF_UPD_CMD_LBN 63 +#define FRF_AZ_BUF_UPD_CMD_WIDTH 1 +#define FRF_AZ_BUF_CLR_CMD_LBN 62 +#define FRF_AZ_BUF_CLR_CMD_WIDTH 1 +#define FRF_AZ_BUF_CLR_END_ID_LBN 32 +#define FRF_AZ_BUF_CLR_END_ID_WIDTH 20 +#define FRF_AZ_BUF_CLR_START_ID_LBN 0 +#define FRF_AZ_BUF_CLR_START_ID_WIDTH 20 + +/* SRM_UPD_EVQ_REG: Buffer table update register */ +#define FR_AZ_SRM_UPD_EVQ 0x00000660 +#define FRF_AZ_SRM_UPD_EVQ_ID_LBN 0 +#define FRF_AZ_SRM_UPD_EVQ_ID_WIDTH 12 + +/* SRAM_PARITY_REG: SRAM parity register. */ +#define FR_AZ_SRAM_PARITY 0x00000670 +#define FRF_CZ_BYPASS_ECC_LBN 3 +#define FRF_CZ_BYPASS_ECC_WIDTH 1 +#define FRF_CZ_SEC_INT_LBN 2 +#define FRF_CZ_SEC_INT_WIDTH 1 +#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_LBN 1 +#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_WIDTH 1 +#define FRF_AB_FORCE_SRAM_PERR_LBN 0 +#define FRF_AB_FORCE_SRAM_PERR_WIDTH 1 +#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_LBN 0 +#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_WIDTH 1 + +/* RX_CFG_REG: Receive configuration register */ +#define FR_AZ_RX_CFG 0x00000800 +#define FRF_CZ_RX_MIN_KBUF_SIZE_LBN 72 +#define FRF_CZ_RX_MIN_KBUF_SIZE_WIDTH 14 +#define FRF_CZ_RX_HDR_SPLIT_EN_LBN 71 +#define FRF_CZ_RX_HDR_SPLIT_EN_WIDTH 1 +#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_LBN 62 +#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_WIDTH 9 +#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_LBN 53 +#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_WIDTH 9 +#define FRF_CZ_RX_PRE_RFF_IPG_LBN 49 +#define FRF_CZ_RX_PRE_RFF_IPG_WIDTH 4 +#define FRF_BZ_RX_TCP_SUP_LBN 48 +#define FRF_BZ_RX_TCP_SUP_WIDTH 1 +#define FRF_BZ_RX_INGR_EN_LBN 47 +#define FRF_BZ_RX_INGR_EN_WIDTH 1 +#define FRF_BZ_RX_IP_HASH_LBN 46 +#define FRF_BZ_RX_IP_HASH_WIDTH 1 +#define FRF_BZ_RX_HASH_ALG_LBN 45 +#define FRF_BZ_RX_HASH_ALG_WIDTH 1 +#define FRF_BZ_RX_HASH_INSRT_HDR_LBN 44 +#define FRF_BZ_RX_HASH_INSRT_HDR_WIDTH 1 +#define FRF_BZ_RX_DESC_PUSH_EN_LBN 43 +#define FRF_BZ_RX_DESC_PUSH_EN_WIDTH 1 +#define FRF_BZ_RX_RDW_PATCH_EN_LBN 42 +#define FRF_BZ_RX_RDW_PATCH_EN_WIDTH 1 +#define FRF_BB_RX_PCI_BURST_SIZE_LBN 39 +#define FRF_BB_RX_PCI_BURST_SIZE_WIDTH 3 +#define FRF_BZ_RX_OWNERR_CTL_LBN 38 +#define FRF_BZ_RX_OWNERR_CTL_WIDTH 1 +#define FRF_BZ_RX_XON_TX_TH_LBN 33 +#define FRF_BZ_RX_XON_TX_TH_WIDTH 5 +#define FRF_AA_RX_DESC_PUSH_EN_LBN 35 +#define FRF_AA_RX_DESC_PUSH_EN_WIDTH 1 +#define FRF_AA_RX_RDW_PATCH_EN_LBN 34 +#define FRF_AA_RX_RDW_PATCH_EN_WIDTH 1 +#define FRF_AA_RX_PCI_BURST_SIZE_LBN 31 +#define FRF_AA_RX_PCI_BURST_SIZE_WIDTH 3 +#define FRF_BZ_RX_XOFF_TX_TH_LBN 28 +#define FRF_BZ_RX_XOFF_TX_TH_WIDTH 5 +#define FRF_AA_RX_OWNERR_CTL_LBN 30 +#define FRF_AA_RX_OWNERR_CTL_WIDTH 1 +#define FRF_AA_RX_XON_TX_TH_LBN 25 +#define FRF_AA_RX_XON_TX_TH_WIDTH 5 +#define FRF_BZ_RX_USR_BUF_SIZE_LBN 19 +#define FRF_BZ_RX_USR_BUF_SIZE_WIDTH 9 +#define FRF_AA_RX_XOFF_TX_TH_LBN 20 +#define FRF_AA_RX_XOFF_TX_TH_WIDTH 5 +#define FRF_AA_RX_USR_BUF_SIZE_LBN 11 +#define FRF_AA_RX_USR_BUF_SIZE_WIDTH 9 +#define FRF_BZ_RX_XON_MAC_TH_LBN 10 +#define FRF_BZ_RX_XON_MAC_TH_WIDTH 9 +#define FRF_AA_RX_XON_MAC_TH_LBN 6 +#define FRF_AA_RX_XON_MAC_TH_WIDTH 5 +#define FRF_BZ_RX_XOFF_MAC_TH_LBN 1 +#define FRF_BZ_RX_XOFF_MAC_TH_WIDTH 9 +#define FRF_AA_RX_XOFF_MAC_TH_LBN 1 +#define FRF_AA_RX_XOFF_MAC_TH_WIDTH 5 +#define FRF_AZ_RX_XOFF_MAC_EN_LBN 0 +#define FRF_AZ_RX_XOFF_MAC_EN_WIDTH 1 + +/* RX_FILTER_CTL_REG: Receive filter control registers */ +#define FR_BZ_RX_FILTER_CTL 0x00000810 +#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_LBN 94 +#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_WIDTH 8 +#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_LBN 86 +#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_WIDTH 8 +#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_LBN 85 +#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_WIDTH 1 +#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_LBN 69 +#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_WIDTH 16 +#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_LBN 57 +#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_WIDTH 12 +#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_LBN 56 +#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_WIDTH 1 +#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_LBN 55 +#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_WIDTH 1 +#define FRF_CZ_UNICAST_NOMATCH_Q_ID_LBN 43 +#define FRF_CZ_UNICAST_NOMATCH_Q_ID_WIDTH 12 +#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_LBN 42 +#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_WIDTH 1 +#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_LBN 41 +#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_WIDTH 1 +#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_LBN 40 +#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_WIDTH 1 +#define FRF_BZ_UDP_FULL_SRCH_LIMIT_LBN 32 +#define FRF_BZ_UDP_FULL_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_NUM_KER_LBN 24 +#define FRF_BZ_NUM_KER_WIDTH 2 +#define FRF_BZ_UDP_WILD_SRCH_LIMIT_LBN 16 +#define FRF_BZ_UDP_WILD_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_TCP_WILD_SRCH_LIMIT_LBN 8 +#define FRF_BZ_TCP_WILD_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_TCP_FULL_SRCH_LIMIT_LBN 0 +#define FRF_BZ_TCP_FULL_SRCH_LIMIT_WIDTH 8 + +/* RX_FLUSH_DESCQ_REG: Receive flush descriptor queue register */ +#define FR_AZ_RX_FLUSH_DESCQ 0x00000820 +#define FRF_AZ_RX_FLUSH_DESCQ_CMD_LBN 24 +#define FRF_AZ_RX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FRF_AZ_RX_FLUSH_DESCQ_LBN 0 +#define FRF_AZ_RX_FLUSH_DESCQ_WIDTH 12 + +/* RX_DESC_UPD_REGP0: Receive descriptor update register. */ +#define FR_BZ_RX_DESC_UPD_P0 0x00000830 +#define FR_BZ_RX_DESC_UPD_P0_STEP 8192 +#define FR_BZ_RX_DESC_UPD_P0_ROWS 1024 +/* RX_DESC_UPD_REG_KER: Receive descriptor update register. */ +#define FR_AA_RX_DESC_UPD_KER 0x00000830 +#define FR_AA_RX_DESC_UPD_KER_STEP 8192 +#define FR_AA_RX_DESC_UPD_KER_ROWS 4 +/* RX_DESC_UPD_REGP123: Receive descriptor update register. */ +#define FR_BB_RX_DESC_UPD_P123 0x01000830 +#define FR_BB_RX_DESC_UPD_P123_STEP 8192 +#define FR_BB_RX_DESC_UPD_P123_ROWS 3072 +#define FRF_AZ_RX_DESC_WPTR_LBN 96 +#define FRF_AZ_RX_DESC_WPTR_WIDTH 12 +#define FRF_AZ_RX_DESC_PUSH_CMD_LBN 95 +#define FRF_AZ_RX_DESC_PUSH_CMD_WIDTH 1 +#define FRF_AZ_RX_DESC_LBN 0 +#define FRF_AZ_RX_DESC_WIDTH 64 + +/* RX_DC_CFG_REG: Receive descriptor cache configuration register */ +#define FR_AZ_RX_DC_CFG 0x00000840 +#define FRF_AB_RX_MAX_PF_LBN 2 +#define FRF_AB_RX_MAX_PF_WIDTH 2 +#define FRF_AZ_RX_DC_SIZE_LBN 0 +#define FRF_AZ_RX_DC_SIZE_WIDTH 2 +#define FFE_AZ_RX_DC_SIZE_64 3 +#define FFE_AZ_RX_DC_SIZE_32 2 +#define FFE_AZ_RX_DC_SIZE_16 1 +#define FFE_AZ_RX_DC_SIZE_8 0 + +/* RX_DC_PF_WM_REG: Receive descriptor cache pre-fetch watermark register */ +#define FR_AZ_RX_DC_PF_WM 0x00000850 +#define FRF_AZ_RX_DC_PF_HWM_LBN 6 +#define FRF_AZ_RX_DC_PF_HWM_WIDTH 6 +#define FRF_AZ_RX_DC_PF_LWM_LBN 0 +#define FRF_AZ_RX_DC_PF_LWM_WIDTH 6 + +/* RX_RSS_TKEY_REG: RSS Toeplitz hash key */ +#define FR_BZ_RX_RSS_TKEY 0x00000860 +#define FRF_BZ_RX_RSS_TKEY_HI_LBN 64 +#define FRF_BZ_RX_RSS_TKEY_HI_WIDTH 64 +#define FRF_BZ_RX_RSS_TKEY_LO_LBN 0 +#define FRF_BZ_RX_RSS_TKEY_LO_WIDTH 64 + +/* RX_NODESC_DROP_REG: Receive dropped packet counter register */ +#define FR_AZ_RX_NODESC_DROP 0x00000880 +#define FRF_CZ_RX_NODESC_DROP_CNT_LBN 0 +#define FRF_CZ_RX_NODESC_DROP_CNT_WIDTH 32 +#define FRF_AB_RX_NODESC_DROP_CNT_LBN 0 +#define FRF_AB_RX_NODESC_DROP_CNT_WIDTH 16 + +/* RX_SELF_RST_REG: Receive self reset register */ +#define FR_AA_RX_SELF_RST 0x00000890 +#define FRF_AA_RX_ISCSI_DIS_LBN 17 +#define FRF_AA_RX_ISCSI_DIS_WIDTH 1 +#define FRF_AA_RX_SW_RST_REG_LBN 16 +#define FRF_AA_RX_SW_RST_REG_WIDTH 1 +#define FRF_AA_RX_NODESC_WAIT_DIS_LBN 9 +#define FRF_AA_RX_NODESC_WAIT_DIS_WIDTH 1 +#define FRF_AA_RX_SELF_RST_EN_LBN 8 +#define FRF_AA_RX_SELF_RST_EN_WIDTH 1 +#define FRF_AA_RX_MAX_PF_LAT_LBN 4 +#define FRF_AA_RX_MAX_PF_LAT_WIDTH 4 +#define FRF_AA_RX_MAX_LU_LAT_LBN 0 +#define FRF_AA_RX_MAX_LU_LAT_WIDTH 4 + +/* RX_DEBUG_REG: undocumented register */ +#define FR_AZ_RX_DEBUG 0x000008a0 +#define FRF_AZ_RX_DEBUG_LBN 0 +#define FRF_AZ_RX_DEBUG_WIDTH 64 + +/* RX_PUSH_DROP_REG: Receive descriptor push dropped counter register */ +#define FR_AZ_RX_PUSH_DROP 0x000008b0 +#define FRF_AZ_RX_PUSH_DROP_CNT_LBN 0 +#define FRF_AZ_RX_PUSH_DROP_CNT_WIDTH 32 + +/* RX_RSS_IPV6_REG1: IPv6 RSS Toeplitz hash key low bytes */ +#define FR_CZ_RX_RSS_IPV6_REG1 0x000008d0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_WIDTH 128 + +/* RX_RSS_IPV6_REG2: IPv6 RSS Toeplitz hash key middle bytes */ +#define FR_CZ_RX_RSS_IPV6_REG2 0x000008e0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_WIDTH 128 + +/* RX_RSS_IPV6_REG3: IPv6 RSS Toeplitz hash key upper bytes and IPv6 RSS settings */ +#define FR_CZ_RX_RSS_IPV6_REG3 0x000008f0 +#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_LBN 66 +#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_LBN 65 +#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_LBN 64 +#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH 64 + +/* TX_FLUSH_DESCQ_REG: Transmit flush descriptor queue register */ +#define FR_AZ_TX_FLUSH_DESCQ 0x00000a00 +#define FRF_AZ_TX_FLUSH_DESCQ_CMD_LBN 12 +#define FRF_AZ_TX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FRF_AZ_TX_FLUSH_DESCQ_LBN 0 +#define FRF_AZ_TX_FLUSH_DESCQ_WIDTH 12 + +/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */ +#define FR_BZ_TX_DESC_UPD_P0 0x00000a10 +#define FR_BZ_TX_DESC_UPD_P0_STEP 8192 +#define FR_BZ_TX_DESC_UPD_P0_ROWS 1024 +/* TX_DESC_UPD_REG_KER: Transmit descriptor update register. */ +#define FR_AA_TX_DESC_UPD_KER 0x00000a10 +#define FR_AA_TX_DESC_UPD_KER_STEP 8192 +#define FR_AA_TX_DESC_UPD_KER_ROWS 8 +/* TX_DESC_UPD_REGP123: Transmit descriptor update register. */ +#define FR_BB_TX_DESC_UPD_P123 0x01000a10 +#define FR_BB_TX_DESC_UPD_P123_STEP 8192 +#define FR_BB_TX_DESC_UPD_P123_ROWS 3072 +#define FRF_AZ_TX_DESC_WPTR_LBN 96 +#define FRF_AZ_TX_DESC_WPTR_WIDTH 12 +#define FRF_AZ_TX_DESC_PUSH_CMD_LBN 95 +#define FRF_AZ_TX_DESC_PUSH_CMD_WIDTH 1 +#define FRF_AZ_TX_DESC_LBN 0 +#define FRF_AZ_TX_DESC_WIDTH 95 + +/* TX_DC_CFG_REG: Transmit descriptor cache configuration register */ +#define FR_AZ_TX_DC_CFG 0x00000a20 +#define FRF_AZ_TX_DC_SIZE_LBN 0 +#define FRF_AZ_TX_DC_SIZE_WIDTH 2 +#define FFE_AZ_TX_DC_SIZE_32 2 +#define FFE_AZ_TX_DC_SIZE_16 1 +#define FFE_AZ_TX_DC_SIZE_8 0 + +/* TX_CHKSM_CFG_REG: Transmit checksum configuration register */ +#define FR_AA_TX_CHKSM_CFG 0x00000a30 +#define FRF_AA_TX_Q_CHKSM_DIS_96_127_LBN 96 +#define FRF_AA_TX_Q_CHKSM_DIS_96_127_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_64_95_LBN 64 +#define FRF_AA_TX_Q_CHKSM_DIS_64_95_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_32_63_LBN 32 +#define FRF_AA_TX_Q_CHKSM_DIS_32_63_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_0_31_LBN 0 +#define FRF_AA_TX_Q_CHKSM_DIS_0_31_WIDTH 32 + +/* TX_CFG_REG: Transmit configuration register */ +#define FR_AZ_TX_CFG 0x00000a50 +#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_LBN 114 +#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_WIDTH 8 +#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_LBN 113 +#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_WIDTH 1 +#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_LBN 105 +#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_LBN 97 +#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_LBN 89 +#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_LBN 81 +#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_LBN 73 +#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_LBN 65 +#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_LBN 64 +#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_WIDTH 1 +#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_LBN 48 +#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_WIDTH 16 +#define FRF_CZ_TX_FILTER_EN_BIT_LBN 47 +#define FRF_CZ_TX_FILTER_EN_BIT_WIDTH 1 +#define FRF_AZ_TX_IP_ID_P0_OFS_LBN 16 +#define FRF_AZ_TX_IP_ID_P0_OFS_WIDTH 15 +#define FRF_AZ_TX_NO_EOP_DISC_EN_LBN 5 +#define FRF_AZ_TX_NO_EOP_DISC_EN_WIDTH 1 +#define FRF_AZ_TX_P1_PRI_EN_LBN 4 +#define FRF_AZ_TX_P1_PRI_EN_WIDTH 1 +#define FRF_AZ_TX_OWNERR_CTL_LBN 2 +#define FRF_AZ_TX_OWNERR_CTL_WIDTH 1 +#define FRF_AA_TX_NON_IP_DROP_DIS_LBN 1 +#define FRF_AA_TX_NON_IP_DROP_DIS_WIDTH 1 +#define FRF_AZ_TX_IP_ID_REP_EN_LBN 0 +#define FRF_AZ_TX_IP_ID_REP_EN_WIDTH 1 + +/* TX_PUSH_DROP_REG: Transmit push dropped register */ +#define FR_AZ_TX_PUSH_DROP 0x00000a60 +#define FRF_AZ_TX_PUSH_DROP_CNT_LBN 0 +#define FRF_AZ_TX_PUSH_DROP_CNT_WIDTH 32 + +/* TX_RESERVED_REG: Transmit configuration register */ +#define FR_AZ_TX_RESERVED 0x00000a80 +#define FRF_AZ_TX_EVT_CNT_LBN 121 +#define FRF_AZ_TX_EVT_CNT_WIDTH 7 +#define FRF_AZ_TX_PREF_AGE_CNT_LBN 119 +#define FRF_AZ_TX_PREF_AGE_CNT_WIDTH 2 +#define FRF_AZ_TX_RD_COMP_TMR_LBN 96 +#define FRF_AZ_TX_RD_COMP_TMR_WIDTH 23 +#define FRF_AZ_TX_PUSH_EN_LBN 89 +#define FRF_AZ_TX_PUSH_EN_WIDTH 1 +#define FRF_AZ_TX_PUSH_CHK_DIS_LBN 88 +#define FRF_AZ_TX_PUSH_CHK_DIS_WIDTH 1 +#define FRF_AZ_TX_D_FF_FULL_P0_LBN 85 +#define FRF_AZ_TX_D_FF_FULL_P0_WIDTH 1 +#define FRF_AZ_TX_DMAR_ST_P0_LBN 81 +#define FRF_AZ_TX_DMAR_ST_P0_WIDTH 1 +#define FRF_AZ_TX_DMAQ_ST_LBN 78 +#define FRF_AZ_TX_DMAQ_ST_WIDTH 1 +#define FRF_AZ_TX_RX_SPACER_LBN 64 +#define FRF_AZ_TX_RX_SPACER_WIDTH 8 +#define FRF_AZ_TX_DROP_ABORT_EN_LBN 60 +#define FRF_AZ_TX_DROP_ABORT_EN_WIDTH 1 +#define FRF_AZ_TX_SOFT_EVT_EN_LBN 59 +#define FRF_AZ_TX_SOFT_EVT_EN_WIDTH 1 +#define FRF_AZ_TX_PS_EVT_DIS_LBN 58 +#define FRF_AZ_TX_PS_EVT_DIS_WIDTH 1 +#define FRF_AZ_TX_RX_SPACER_EN_LBN 57 +#define FRF_AZ_TX_RX_SPACER_EN_WIDTH 1 +#define FRF_AZ_TX_XP_TIMER_LBN 52 +#define FRF_AZ_TX_XP_TIMER_WIDTH 5 +#define FRF_AZ_TX_PREF_SPACER_LBN 44 +#define FRF_AZ_TX_PREF_SPACER_WIDTH 8 +#define FRF_AZ_TX_PREF_WD_TMR_LBN 22 +#define FRF_AZ_TX_PREF_WD_TMR_WIDTH 22 +#define FRF_AZ_TX_ONLY1TAG_LBN 21 +#define FRF_AZ_TX_ONLY1TAG_WIDTH 1 +#define FRF_AZ_TX_PREF_THRESHOLD_LBN 19 +#define FRF_AZ_TX_PREF_THRESHOLD_WIDTH 2 +#define FRF_AZ_TX_ONE_PKT_PER_Q_LBN 18 +#define FRF_AZ_TX_ONE_PKT_PER_Q_WIDTH 1 +#define FRF_AZ_TX_DIS_NON_IP_EV_LBN 17 +#define FRF_AZ_TX_DIS_NON_IP_EV_WIDTH 1 +#define FRF_AA_TX_DMA_FF_THR_LBN 16 +#define FRF_AA_TX_DMA_FF_THR_WIDTH 1 +#define FRF_AZ_TX_DMA_SPACER_LBN 8 +#define FRF_AZ_TX_DMA_SPACER_WIDTH 8 +#define FRF_AA_TX_TCP_DIS_LBN 7 +#define FRF_AA_TX_TCP_DIS_WIDTH 1 +#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_LBN 7 +#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_WIDTH 1 +#define FRF_AA_TX_IP_DIS_LBN 6 +#define FRF_AA_TX_IP_DIS_WIDTH 1 +#define FRF_AZ_TX_MAX_CPL_LBN 2 +#define FRF_AZ_TX_MAX_CPL_WIDTH 2 +#define FFE_AZ_TX_MAX_CPL_16 3 +#define FFE_AZ_TX_MAX_CPL_8 2 +#define FFE_AZ_TX_MAX_CPL_4 1 +#define FFE_AZ_TX_MAX_CPL_NOLIMIT 0 +#define FRF_AZ_TX_MAX_PREF_LBN 0 +#define FRF_AZ_TX_MAX_PREF_WIDTH 2 +#define FFE_AZ_TX_MAX_PREF_32 3 +#define FFE_AZ_TX_MAX_PREF_16 2 +#define FFE_AZ_TX_MAX_PREF_8 1 +#define FFE_AZ_TX_MAX_PREF_OFF 0 + +/* TX_PACE_REG: Transmit pace control register */ +#define FR_BZ_TX_PACE 0x00000a90 +#define FRF_BZ_TX_PACE_SB_NOT_AF_LBN 19 +#define FRF_BZ_TX_PACE_SB_NOT_AF_WIDTH 10 +#define FRF_BZ_TX_PACE_SB_AF_LBN 9 +#define FRF_BZ_TX_PACE_SB_AF_WIDTH 10 +#define FRF_BZ_TX_PACE_FB_BASE_LBN 5 +#define FRF_BZ_TX_PACE_FB_BASE_WIDTH 4 +#define FRF_BZ_TX_PACE_BIN_TH_LBN 0 +#define FRF_BZ_TX_PACE_BIN_TH_WIDTH 5 + +/* TX_PACE_DROP_QID_REG: PACE Drop QID Counter */ +#define FR_BZ_TX_PACE_DROP_QID 0x00000aa0 +#define FRF_BZ_TX_PACE_QID_DRP_CNT_LBN 0 +#define FRF_BZ_TX_PACE_QID_DRP_CNT_WIDTH 16 + +/* TX_VLAN_REG: Transmit VLAN tag register */ +#define FR_BB_TX_VLAN 0x00000ae0 +#define FRF_BB_TX_VLAN_EN_LBN 127 +#define FRF_BB_TX_VLAN_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_PORT1_EN_LBN 125 +#define FRF_BB_TX_VLAN7_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_PORT0_EN_LBN 124 +#define FRF_BB_TX_VLAN7_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_LBN 112 +#define FRF_BB_TX_VLAN7_WIDTH 12 +#define FRF_BB_TX_VLAN6_PORT1_EN_LBN 109 +#define FRF_BB_TX_VLAN6_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN6_PORT0_EN_LBN 108 +#define FRF_BB_TX_VLAN6_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN6_LBN 96 +#define FRF_BB_TX_VLAN6_WIDTH 12 +#define FRF_BB_TX_VLAN5_PORT1_EN_LBN 93 +#define FRF_BB_TX_VLAN5_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN5_PORT0_EN_LBN 92 +#define FRF_BB_TX_VLAN5_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN5_LBN 80 +#define FRF_BB_TX_VLAN5_WIDTH 12 +#define FRF_BB_TX_VLAN4_PORT1_EN_LBN 77 +#define FRF_BB_TX_VLAN4_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN4_PORT0_EN_LBN 76 +#define FRF_BB_TX_VLAN4_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN4_LBN 64 +#define FRF_BB_TX_VLAN4_WIDTH 12 +#define FRF_BB_TX_VLAN3_PORT1_EN_LBN 61 +#define FRF_BB_TX_VLAN3_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN3_PORT0_EN_LBN 60 +#define FRF_BB_TX_VLAN3_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN3_LBN 48 +#define FRF_BB_TX_VLAN3_WIDTH 12 +#define FRF_BB_TX_VLAN2_PORT1_EN_LBN 45 +#define FRF_BB_TX_VLAN2_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN2_PORT0_EN_LBN 44 +#define FRF_BB_TX_VLAN2_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN2_LBN 32 +#define FRF_BB_TX_VLAN2_WIDTH 12 +#define FRF_BB_TX_VLAN1_PORT1_EN_LBN 29 +#define FRF_BB_TX_VLAN1_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN1_PORT0_EN_LBN 28 +#define FRF_BB_TX_VLAN1_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN1_LBN 16 +#define FRF_BB_TX_VLAN1_WIDTH 12 +#define FRF_BB_TX_VLAN0_PORT1_EN_LBN 13 +#define FRF_BB_TX_VLAN0_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN0_PORT0_EN_LBN 12 +#define FRF_BB_TX_VLAN0_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN0_LBN 0 +#define FRF_BB_TX_VLAN0_WIDTH 12 + +/* TX_IPFIL_PORTEN_REG: Transmit filter control register */ +#define FR_BZ_TX_IPFIL_PORTEN 0x00000af0 +#define FRF_BZ_TX_MADR0_FIL_EN_LBN 64 +#define FRF_BZ_TX_MADR0_FIL_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL31_PORT_EN_LBN 62 +#define FRF_BB_TX_IPFIL31_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL30_PORT_EN_LBN 60 +#define FRF_BB_TX_IPFIL30_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL29_PORT_EN_LBN 58 +#define FRF_BB_TX_IPFIL29_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL28_PORT_EN_LBN 56 +#define FRF_BB_TX_IPFIL28_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL27_PORT_EN_LBN 54 +#define FRF_BB_TX_IPFIL27_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL26_PORT_EN_LBN 52 +#define FRF_BB_TX_IPFIL26_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL25_PORT_EN_LBN 50 +#define FRF_BB_TX_IPFIL25_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL24_PORT_EN_LBN 48 +#define FRF_BB_TX_IPFIL24_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL23_PORT_EN_LBN 46 +#define FRF_BB_TX_IPFIL23_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL22_PORT_EN_LBN 44 +#define FRF_BB_TX_IPFIL22_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL21_PORT_EN_LBN 42 +#define FRF_BB_TX_IPFIL21_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL20_PORT_EN_LBN 40 +#define FRF_BB_TX_IPFIL20_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL19_PORT_EN_LBN 38 +#define FRF_BB_TX_IPFIL19_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL18_PORT_EN_LBN 36 +#define FRF_BB_TX_IPFIL18_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL17_PORT_EN_LBN 34 +#define FRF_BB_TX_IPFIL17_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL16_PORT_EN_LBN 32 +#define FRF_BB_TX_IPFIL16_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL15_PORT_EN_LBN 30 +#define FRF_BB_TX_IPFIL15_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL14_PORT_EN_LBN 28 +#define FRF_BB_TX_IPFIL14_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL13_PORT_EN_LBN 26 +#define FRF_BB_TX_IPFIL13_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL12_PORT_EN_LBN 24 +#define FRF_BB_TX_IPFIL12_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL11_PORT_EN_LBN 22 +#define FRF_BB_TX_IPFIL11_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL10_PORT_EN_LBN 20 +#define FRF_BB_TX_IPFIL10_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL9_PORT_EN_LBN 18 +#define FRF_BB_TX_IPFIL9_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL8_PORT_EN_LBN 16 +#define FRF_BB_TX_IPFIL8_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL7_PORT_EN_LBN 14 +#define FRF_BB_TX_IPFIL7_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL6_PORT_EN_LBN 12 +#define FRF_BB_TX_IPFIL6_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL5_PORT_EN_LBN 10 +#define FRF_BB_TX_IPFIL5_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL4_PORT_EN_LBN 8 +#define FRF_BB_TX_IPFIL4_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL3_PORT_EN_LBN 6 +#define FRF_BB_TX_IPFIL3_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL2_PORT_EN_LBN 4 +#define FRF_BB_TX_IPFIL2_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL1_PORT_EN_LBN 2 +#define FRF_BB_TX_IPFIL1_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL0_PORT_EN_LBN 0 +#define FRF_BB_TX_IPFIL0_PORT_EN_WIDTH 1 + +/* TX_IPFIL_TBL: Transmit IP source address filter table */ +#define FR_BB_TX_IPFIL_TBL 0x00000b00 +#define FR_BB_TX_IPFIL_TBL_STEP 16 +#define FR_BB_TX_IPFIL_TBL_ROWS 16 +#define FRF_BB_TX_IPFIL_MASK_1_LBN 96 +#define FRF_BB_TX_IPFIL_MASK_1_WIDTH 32 +#define FRF_BB_TX_IP_SRC_ADR_1_LBN 64 +#define FRF_BB_TX_IP_SRC_ADR_1_WIDTH 32 +#define FRF_BB_TX_IPFIL_MASK_0_LBN 32 +#define FRF_BB_TX_IPFIL_MASK_0_WIDTH 32 +#define FRF_BB_TX_IP_SRC_ADR_0_LBN 0 +#define FRF_BB_TX_IP_SRC_ADR_0_WIDTH 32 + +/* MD_TXD_REG: PHY management transmit data register */ +#define FR_AB_MD_TXD 0x00000c00 +#define FRF_AB_MD_TXD_LBN 0 +#define FRF_AB_MD_TXD_WIDTH 16 + +/* MD_RXD_REG: PHY management receive data register */ +#define FR_AB_MD_RXD 0x00000c10 +#define FRF_AB_MD_RXD_LBN 0 +#define FRF_AB_MD_RXD_WIDTH 16 + +/* MD_CS_REG: PHY management configuration & status register */ +#define FR_AB_MD_CS 0x00000c20 +#define FRF_AB_MD_RD_EN_CMD_LBN 15 +#define FRF_AB_MD_RD_EN_CMD_WIDTH 1 +#define FRF_AB_MD_WR_EN_CMD_LBN 14 +#define FRF_AB_MD_WR_EN_CMD_WIDTH 1 +#define FRF_AB_MD_ADDR_CMD_LBN 13 +#define FRF_AB_MD_ADDR_CMD_WIDTH 1 +#define FRF_AB_MD_PT_LBN 7 +#define FRF_AB_MD_PT_WIDTH 3 +#define FRF_AB_MD_PL_LBN 6 +#define FRF_AB_MD_PL_WIDTH 1 +#define FRF_AB_MD_INT_CLR_LBN 5 +#define FRF_AB_MD_INT_CLR_WIDTH 1 +#define FRF_AB_MD_GC_LBN 4 +#define FRF_AB_MD_GC_WIDTH 1 +#define FRF_AB_MD_PRSP_LBN 3 +#define FRF_AB_MD_PRSP_WIDTH 1 +#define FRF_AB_MD_RIC_LBN 2 +#define FRF_AB_MD_RIC_WIDTH 1 +#define FRF_AB_MD_RDC_LBN 1 +#define FRF_AB_MD_RDC_WIDTH 1 +#define FRF_AB_MD_WRC_LBN 0 +#define FRF_AB_MD_WRC_WIDTH 1 + +/* MD_PHY_ADR_REG: PHY management PHY address register */ +#define FR_AB_MD_PHY_ADR 0x00000c30 +#define FRF_AB_MD_PHY_ADR_LBN 0 +#define FRF_AB_MD_PHY_ADR_WIDTH 16 + +/* MD_ID_REG: PHY management ID register */ +#define FR_AB_MD_ID 0x00000c40 +#define FRF_AB_MD_PRT_ADR_LBN 11 +#define FRF_AB_MD_PRT_ADR_WIDTH 5 +#define FRF_AB_MD_DEV_ADR_LBN 6 +#define FRF_AB_MD_DEV_ADR_WIDTH 5 + +/* MD_STAT_REG: PHY management status & mask register */ +#define FR_AB_MD_STAT 0x00000c50 +#define FRF_AB_MD_PINT_LBN 4 +#define FRF_AB_MD_PINT_WIDTH 1 +#define FRF_AB_MD_DONE_LBN 3 +#define FRF_AB_MD_DONE_WIDTH 1 +#define FRF_AB_MD_BSERR_LBN 2 +#define FRF_AB_MD_BSERR_WIDTH 1 +#define FRF_AB_MD_LNFL_LBN 1 +#define FRF_AB_MD_LNFL_WIDTH 1 +#define FRF_AB_MD_BSY_LBN 0 +#define FRF_AB_MD_BSY_WIDTH 1 + +/* MAC_STAT_DMA_REG: Port MAC statistical counter DMA register */ +#define FR_AB_MAC_STAT_DMA 0x00000c60 +#define FRF_AB_MAC_STAT_DMA_CMD_LBN 48 +#define FRF_AB_MAC_STAT_DMA_CMD_WIDTH 1 +#define FRF_AB_MAC_STAT_DMA_ADR_LBN 0 +#define FRF_AB_MAC_STAT_DMA_ADR_WIDTH 48 + +/* MAC_CTRL_REG: Port MAC control register */ +#define FR_AB_MAC_CTRL 0x00000c80 +#define FRF_AB_MAC_XOFF_VAL_LBN 16 +#define FRF_AB_MAC_XOFF_VAL_WIDTH 16 +#define FRF_BB_TXFIFO_DRAIN_EN_LBN 7 +#define FRF_BB_TXFIFO_DRAIN_EN_WIDTH 1 +#define FRF_AB_MAC_XG_DISTXCRC_LBN 5 +#define FRF_AB_MAC_XG_DISTXCRC_WIDTH 1 +#define FRF_AB_MAC_BCAD_ACPT_LBN 4 +#define FRF_AB_MAC_BCAD_ACPT_WIDTH 1 +#define FRF_AB_MAC_UC_PROM_LBN 3 +#define FRF_AB_MAC_UC_PROM_WIDTH 1 +#define FRF_AB_MAC_LINK_STATUS_LBN 2 +#define FRF_AB_MAC_LINK_STATUS_WIDTH 1 +#define FRF_AB_MAC_SPEED_LBN 0 +#define FRF_AB_MAC_SPEED_WIDTH 2 +#define FFE_AB_MAC_SPEED_10G 3 +#define FFE_AB_MAC_SPEED_1G 2 +#define FFE_AB_MAC_SPEED_100M 1 +#define FFE_AB_MAC_SPEED_10M 0 + +/* GEN_MODE_REG: General Purpose mode register (external interrupt mask) */ +#define FR_BB_GEN_MODE 0x00000c90 +#define FRF_BB_XFP_PHY_INT_POL_SEL_LBN 3 +#define FRF_BB_XFP_PHY_INT_POL_SEL_WIDTH 1 +#define FRF_BB_XG_PHY_INT_POL_SEL_LBN 2 +#define FRF_BB_XG_PHY_INT_POL_SEL_WIDTH 1 +#define FRF_BB_XFP_PHY_INT_MASK_LBN 1 +#define FRF_BB_XFP_PHY_INT_MASK_WIDTH 1 +#define FRF_BB_XG_PHY_INT_MASK_LBN 0 +#define FRF_BB_XG_PHY_INT_MASK_WIDTH 1 + +/* MAC_MC_HASH_REG0: Multicast address hash table */ +#define FR_AB_MAC_MC_HASH_REG0 0x00000ca0 +#define FRF_AB_MAC_MCAST_HASH0_LBN 0 +#define FRF_AB_MAC_MCAST_HASH0_WIDTH 128 + +/* MAC_MC_HASH_REG1: Multicast address hash table */ +#define FR_AB_MAC_MC_HASH_REG1 0x00000cb0 +#define FRF_AB_MAC_MCAST_HASH1_LBN 0 +#define FRF_AB_MAC_MCAST_HASH1_WIDTH 128 + +/* GM_CFG1_REG: GMAC configuration register 1 */ +#define FR_AB_GM_CFG1 0x00000e00 +#define FRF_AB_GM_SW_RST_LBN 31 +#define FRF_AB_GM_SW_RST_WIDTH 1 +#define FRF_AB_GM_SIM_RST_LBN 30 +#define FRF_AB_GM_SIM_RST_WIDTH 1 +#define FRF_AB_GM_RST_RX_MAC_CTL_LBN 19 +#define FRF_AB_GM_RST_RX_MAC_CTL_WIDTH 1 +#define FRF_AB_GM_RST_TX_MAC_CTL_LBN 18 +#define FRF_AB_GM_RST_TX_MAC_CTL_WIDTH 1 +#define FRF_AB_GM_RST_RX_FUNC_LBN 17 +#define FRF_AB_GM_RST_RX_FUNC_WIDTH 1 +#define FRF_AB_GM_RST_TX_FUNC_LBN 16 +#define FRF_AB_GM_RST_TX_FUNC_WIDTH 1 +#define FRF_AB_GM_LOOP_LBN 8 +#define FRF_AB_GM_LOOP_WIDTH 1 +#define FRF_AB_GM_RX_FC_EN_LBN 5 +#define FRF_AB_GM_RX_FC_EN_WIDTH 1 +#define FRF_AB_GM_TX_FC_EN_LBN 4 +#define FRF_AB_GM_TX_FC_EN_WIDTH 1 +#define FRF_AB_GM_SYNC_RXEN_LBN 3 +#define FRF_AB_GM_SYNC_RXEN_WIDTH 1 +#define FRF_AB_GM_RX_EN_LBN 2 +#define FRF_AB_GM_RX_EN_WIDTH 1 +#define FRF_AB_GM_SYNC_TXEN_LBN 1 +#define FRF_AB_GM_SYNC_TXEN_WIDTH 1 +#define FRF_AB_GM_TX_EN_LBN 0 +#define FRF_AB_GM_TX_EN_WIDTH 1 + +/* GM_CFG2_REG: GMAC configuration register 2 */ +#define FR_AB_GM_CFG2 0x00000e10 +#define FRF_AB_GM_PAMBL_LEN_LBN 12 +#define FRF_AB_GM_PAMBL_LEN_WIDTH 4 +#define FRF_AB_GM_IF_MODE_LBN 8 +#define FRF_AB_GM_IF_MODE_WIDTH 2 +#define FFE_AB_IF_MODE_BYTE_MODE 2 +#define FFE_AB_IF_MODE_NIBBLE_MODE 1 +#define FRF_AB_GM_HUGE_FRM_EN_LBN 5 +#define FRF_AB_GM_HUGE_FRM_EN_WIDTH 1 +#define FRF_AB_GM_LEN_CHK_LBN 4 +#define FRF_AB_GM_LEN_CHK_WIDTH 1 +#define FRF_AB_GM_PAD_CRC_EN_LBN 2 +#define FRF_AB_GM_PAD_CRC_EN_WIDTH 1 +#define FRF_AB_GM_CRC_EN_LBN 1 +#define FRF_AB_GM_CRC_EN_WIDTH 1 +#define FRF_AB_GM_FD_LBN 0 +#define FRF_AB_GM_FD_WIDTH 1 + +/* GM_IPG_REG: GMAC IPG register */ +#define FR_AB_GM_IPG 0x00000e20 +#define FRF_AB_GM_NONB2B_IPG1_LBN 24 +#define FRF_AB_GM_NONB2B_IPG1_WIDTH 7 +#define FRF_AB_GM_NONB2B_IPG2_LBN 16 +#define FRF_AB_GM_NONB2B_IPG2_WIDTH 7 +#define FRF_AB_GM_MIN_IPG_ENF_LBN 8 +#define FRF_AB_GM_MIN_IPG_ENF_WIDTH 8 +#define FRF_AB_GM_B2B_IPG_LBN 0 +#define FRF_AB_GM_B2B_IPG_WIDTH 7 + +/* GM_HD_REG: GMAC half duplex register */ +#define FR_AB_GM_HD 0x00000e30 +#define FRF_AB_GM_ALT_BOFF_VAL_LBN 20 +#define FRF_AB_GM_ALT_BOFF_VAL_WIDTH 4 +#define FRF_AB_GM_ALT_BOFF_EN_LBN 19 +#define FRF_AB_GM_ALT_BOFF_EN_WIDTH 1 +#define FRF_AB_GM_BP_NO_BOFF_LBN 18 +#define FRF_AB_GM_BP_NO_BOFF_WIDTH 1 +#define FRF_AB_GM_DIS_BOFF_LBN 17 +#define FRF_AB_GM_DIS_BOFF_WIDTH 1 +#define FRF_AB_GM_EXDEF_TX_EN_LBN 16 +#define FRF_AB_GM_EXDEF_TX_EN_WIDTH 1 +#define FRF_AB_GM_RTRY_LIMIT_LBN 12 +#define FRF_AB_GM_RTRY_LIMIT_WIDTH 4 +#define FRF_AB_GM_COL_WIN_LBN 0 +#define FRF_AB_GM_COL_WIN_WIDTH 10 + +/* GM_MAX_FLEN_REG: GMAC maximum frame length register */ +#define FR_AB_GM_MAX_FLEN 0x00000e40 +#define FRF_AB_GM_MAX_FLEN_LBN 0 +#define FRF_AB_GM_MAX_FLEN_WIDTH 16 + +/* GM_TEST_REG: GMAC test register */ +#define FR_AB_GM_TEST 0x00000e70 +#define FRF_AB_GM_MAX_BOFF_LBN 3 +#define FRF_AB_GM_MAX_BOFF_WIDTH 1 +#define FRF_AB_GM_REG_TX_FLOW_EN_LBN 2 +#define FRF_AB_GM_REG_TX_FLOW_EN_WIDTH 1 +#define FRF_AB_GM_TEST_PAUSE_LBN 1 +#define FRF_AB_GM_TEST_PAUSE_WIDTH 1 +#define FRF_AB_GM_SHORT_SLOT_LBN 0 +#define FRF_AB_GM_SHORT_SLOT_WIDTH 1 + +/* GM_ADR1_REG: GMAC station address register 1 */ +#define FR_AB_GM_ADR1 0x00000f00 +#define FRF_AB_GM_ADR_B0_LBN 24 +#define FRF_AB_GM_ADR_B0_WIDTH 8 +#define FRF_AB_GM_ADR_B1_LBN 16 +#define FRF_AB_GM_ADR_B1_WIDTH 8 +#define FRF_AB_GM_ADR_B2_LBN 8 +#define FRF_AB_GM_ADR_B2_WIDTH 8 +#define FRF_AB_GM_ADR_B3_LBN 0 +#define FRF_AB_GM_ADR_B3_WIDTH 8 + +/* GM_ADR2_REG: GMAC station address register 2 */ +#define FR_AB_GM_ADR2 0x00000f10 +#define FRF_AB_GM_ADR_B4_LBN 24 +#define FRF_AB_GM_ADR_B4_WIDTH 8 +#define FRF_AB_GM_ADR_B5_LBN 16 +#define FRF_AB_GM_ADR_B5_WIDTH 8 + +/* GMF_CFG0_REG: GMAC FIFO configuration register 0 */ +#define FR_AB_GMF_CFG0 0x00000f20 +#define FRF_AB_GMF_FTFENRPLY_LBN 20 +#define FRF_AB_GMF_FTFENRPLY_WIDTH 1 +#define FRF_AB_GMF_STFENRPLY_LBN 19 +#define FRF_AB_GMF_STFENRPLY_WIDTH 1 +#define FRF_AB_GMF_FRFENRPLY_LBN 18 +#define FRF_AB_GMF_FRFENRPLY_WIDTH 1 +#define FRF_AB_GMF_SRFENRPLY_LBN 17 +#define FRF_AB_GMF_SRFENRPLY_WIDTH 1 +#define FRF_AB_GMF_WTMENRPLY_LBN 16 +#define FRF_AB_GMF_WTMENRPLY_WIDTH 1 +#define FRF_AB_GMF_FTFENREQ_LBN 12 +#define FRF_AB_GMF_FTFENREQ_WIDTH 1 +#define FRF_AB_GMF_STFENREQ_LBN 11 +#define FRF_AB_GMF_STFENREQ_WIDTH 1 +#define FRF_AB_GMF_FRFENREQ_LBN 10 +#define FRF_AB_GMF_FRFENREQ_WIDTH 1 +#define FRF_AB_GMF_SRFENREQ_LBN 9 +#define FRF_AB_GMF_SRFENREQ_WIDTH 1 +#define FRF_AB_GMF_WTMENREQ_LBN 8 +#define FRF_AB_GMF_WTMENREQ_WIDTH 1 +#define FRF_AB_GMF_HSTRSTFT_LBN 4 +#define FRF_AB_GMF_HSTRSTFT_WIDTH 1 +#define FRF_AB_GMF_HSTRSTST_LBN 3 +#define FRF_AB_GMF_HSTRSTST_WIDTH 1 +#define FRF_AB_GMF_HSTRSTFR_LBN 2 +#define FRF_AB_GMF_HSTRSTFR_WIDTH 1 +#define FRF_AB_GMF_HSTRSTSR_LBN 1 +#define FRF_AB_GMF_HSTRSTSR_WIDTH 1 +#define FRF_AB_GMF_HSTRSTWT_LBN 0 +#define FRF_AB_GMF_HSTRSTWT_WIDTH 1 + +/* GMF_CFG1_REG: GMAC FIFO configuration register 1 */ +#define FR_AB_GMF_CFG1 0x00000f30 +#define FRF_AB_GMF_CFGFRTH_LBN 16 +#define FRF_AB_GMF_CFGFRTH_WIDTH 5 +#define FRF_AB_GMF_CFGXOFFRTX_LBN 0 +#define FRF_AB_GMF_CFGXOFFRTX_WIDTH 16 + +/* GMF_CFG2_REG: GMAC FIFO configuration register 2 */ +#define FR_AB_GMF_CFG2 0x00000f40 +#define FRF_AB_GMF_CFGHWM_LBN 16 +#define FRF_AB_GMF_CFGHWM_WIDTH 6 +#define FRF_AB_GMF_CFGLWM_LBN 0 +#define FRF_AB_GMF_CFGLWM_WIDTH 6 + +/* GMF_CFG3_REG: GMAC FIFO configuration register 3 */ +#define FR_AB_GMF_CFG3 0x00000f50 +#define FRF_AB_GMF_CFGHWMFT_LBN 16 +#define FRF_AB_GMF_CFGHWMFT_WIDTH 6 +#define FRF_AB_GMF_CFGFTTH_LBN 0 +#define FRF_AB_GMF_CFGFTTH_WIDTH 6 + +/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */ +#define FR_AB_GMF_CFG4 0x00000f60 +#define FRF_AB_GMF_HSTFLTRFRM_LBN 0 +#define FRF_AB_GMF_HSTFLTRFRM_WIDTH 18 + +/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */ +#define FR_AB_GMF_CFG5 0x00000f70 +#define FRF_AB_GMF_CFGHDPLX_LBN 22 +#define FRF_AB_GMF_CFGHDPLX_WIDTH 1 +#define FRF_AB_GMF_SRFULL_LBN 21 +#define FRF_AB_GMF_SRFULL_WIDTH 1 +#define FRF_AB_GMF_HSTSRFULLCLR_LBN 20 +#define FRF_AB_GMF_HSTSRFULLCLR_WIDTH 1 +#define FRF_AB_GMF_CFGBYTMODE_LBN 19 +#define FRF_AB_GMF_CFGBYTMODE_WIDTH 1 +#define FRF_AB_GMF_HSTDRPLT64_LBN 18 +#define FRF_AB_GMF_HSTDRPLT64_WIDTH 1 +#define FRF_AB_GMF_HSTFLTRFRMDC_LBN 0 +#define FRF_AB_GMF_HSTFLTRFRMDC_WIDTH 18 + +/* TX_SRC_MAC_TBL: Transmit IP source address filter table */ +#define FR_BB_TX_SRC_MAC_TBL 0x00001000 +#define FR_BB_TX_SRC_MAC_TBL_STEP 16 +#define FR_BB_TX_SRC_MAC_TBL_ROWS 16 +#define FRF_BB_TX_SRC_MAC_ADR_1_LBN 64 +#define FRF_BB_TX_SRC_MAC_ADR_1_WIDTH 48 +#define FRF_BB_TX_SRC_MAC_ADR_0_LBN 0 +#define FRF_BB_TX_SRC_MAC_ADR_0_WIDTH 48 + +/* TX_SRC_MAC_CTL_REG: Transmit MAC source address filter control */ +#define FR_BB_TX_SRC_MAC_CTL 0x00001100 +#define FRF_BB_TX_SRC_DROP_CTR_LBN 16 +#define FRF_BB_TX_SRC_DROP_CTR_WIDTH 16 +#define FRF_BB_TX_SRC_FLTR_EN_LBN 15 +#define FRF_BB_TX_SRC_FLTR_EN_WIDTH 1 +#define FRF_BB_TX_DROP_CTR_CLR_LBN 12 +#define FRF_BB_TX_DROP_CTR_CLR_WIDTH 1 +#define FRF_BB_TX_MAC_QID_SEL_LBN 0 +#define FRF_BB_TX_MAC_QID_SEL_WIDTH 3 + +/* XM_ADR_LO_REG: XGMAC address register low */ +#define FR_AB_XM_ADR_LO 0x00001200 +#define FRF_AB_XM_ADR_LO_LBN 0 +#define FRF_AB_XM_ADR_LO_WIDTH 32 + +/* XM_ADR_HI_REG: XGMAC address register high */ +#define FR_AB_XM_ADR_HI 0x00001210 +#define FRF_AB_XM_ADR_HI_LBN 0 +#define FRF_AB_XM_ADR_HI_WIDTH 16 + +/* XM_GLB_CFG_REG: XGMAC global configuration */ +#define FR_AB_XM_GLB_CFG 0x00001220 +#define FRF_AB_XM_RMTFLT_GEN_LBN 17 +#define FRF_AB_XM_RMTFLT_GEN_WIDTH 1 +#define FRF_AB_XM_DEBUG_MODE_LBN 16 +#define FRF_AB_XM_DEBUG_MODE_WIDTH 1 +#define FRF_AB_XM_RX_STAT_EN_LBN 11 +#define FRF_AB_XM_RX_STAT_EN_WIDTH 1 +#define FRF_AB_XM_TX_STAT_EN_LBN 10 +#define FRF_AB_XM_TX_STAT_EN_WIDTH 1 +#define FRF_AB_XM_RX_JUMBO_MODE_LBN 6 +#define FRF_AB_XM_RX_JUMBO_MODE_WIDTH 1 +#define FRF_AB_XM_WAN_MODE_LBN 5 +#define FRF_AB_XM_WAN_MODE_WIDTH 1 +#define FRF_AB_XM_INTCLR_MODE_LBN 3 +#define FRF_AB_XM_INTCLR_MODE_WIDTH 1 +#define FRF_AB_XM_CORE_RST_LBN 0 +#define FRF_AB_XM_CORE_RST_WIDTH 1 + +/* XM_TX_CFG_REG: XGMAC transmit configuration */ +#define FR_AB_XM_TX_CFG 0x00001230 +#define FRF_AB_XM_TX_PROG_LBN 24 +#define FRF_AB_XM_TX_PROG_WIDTH 1 +#define FRF_AB_XM_IPG_LBN 16 +#define FRF_AB_XM_IPG_WIDTH 4 +#define FRF_AB_XM_FCNTL_LBN 10 +#define FRF_AB_XM_FCNTL_WIDTH 1 +#define FRF_AB_XM_TXCRC_LBN 8 +#define FRF_AB_XM_TXCRC_WIDTH 1 +#define FRF_AB_XM_EDRC_LBN 6 +#define FRF_AB_XM_EDRC_WIDTH 1 +#define FRF_AB_XM_AUTO_PAD_LBN 5 +#define FRF_AB_XM_AUTO_PAD_WIDTH 1 +#define FRF_AB_XM_TX_PRMBL_LBN 2 +#define FRF_AB_XM_TX_PRMBL_WIDTH 1 +#define FRF_AB_XM_TXEN_LBN 1 +#define FRF_AB_XM_TXEN_WIDTH 1 +#define FRF_AB_XM_TX_RST_LBN 0 +#define FRF_AB_XM_TX_RST_WIDTH 1 + +/* XM_RX_CFG_REG: XGMAC receive configuration */ +#define FR_AB_XM_RX_CFG 0x00001240 +#define FRF_AB_XM_PASS_LENERR_LBN 26 +#define FRF_AB_XM_PASS_LENERR_WIDTH 1 +#define FRF_AB_XM_PASS_CRC_ERR_LBN 25 +#define FRF_AB_XM_PASS_CRC_ERR_WIDTH 1 +#define FRF_AB_XM_PASS_PRMBLE_ERR_LBN 24 +#define FRF_AB_XM_PASS_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_REJ_BCAST_LBN 20 +#define FRF_AB_XM_REJ_BCAST_WIDTH 1 +#define FRF_AB_XM_ACPT_ALL_MCAST_LBN 11 +#define FRF_AB_XM_ACPT_ALL_MCAST_WIDTH 1 +#define FRF_AB_XM_ACPT_ALL_UCAST_LBN 9 +#define FRF_AB_XM_ACPT_ALL_UCAST_WIDTH 1 +#define FRF_AB_XM_AUTO_DEPAD_LBN 8 +#define FRF_AB_XM_AUTO_DEPAD_WIDTH 1 +#define FRF_AB_XM_RXCRC_LBN 3 +#define FRF_AB_XM_RXCRC_WIDTH 1 +#define FRF_AB_XM_RX_PRMBL_LBN 2 +#define FRF_AB_XM_RX_PRMBL_WIDTH 1 +#define FRF_AB_XM_RXEN_LBN 1 +#define FRF_AB_XM_RXEN_WIDTH 1 +#define FRF_AB_XM_RX_RST_LBN 0 +#define FRF_AB_XM_RX_RST_WIDTH 1 + +/* XM_MGT_INT_MASK: documentation to be written for sum_XM_MGT_INT_MASK */ +#define FR_AB_XM_MGT_INT_MASK 0x00001250 +#define FRF_AB_XM_MSK_STA_INTR_LBN 16 +#define FRF_AB_XM_MSK_STA_INTR_WIDTH 1 +#define FRF_AB_XM_MSK_STAT_CNTR_HF_LBN 9 +#define FRF_AB_XM_MSK_STAT_CNTR_HF_WIDTH 1 +#define FRF_AB_XM_MSK_STAT_CNTR_OF_LBN 8 +#define FRF_AB_XM_MSK_STAT_CNTR_OF_WIDTH 1 +#define FRF_AB_XM_MSK_PRMBLE_ERR_LBN 2 +#define FRF_AB_XM_MSK_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_MSK_RMTFLT_LBN 1 +#define FRF_AB_XM_MSK_RMTFLT_WIDTH 1 +#define FRF_AB_XM_MSK_LCLFLT_LBN 0 +#define FRF_AB_XM_MSK_LCLFLT_WIDTH 1 + +/* XM_FC_REG: XGMAC flow control register */ +#define FR_AB_XM_FC 0x00001270 +#define FRF_AB_XM_PAUSE_TIME_LBN 16 +#define FRF_AB_XM_PAUSE_TIME_WIDTH 16 +#define FRF_AB_XM_RX_MAC_STAT_LBN 11 +#define FRF_AB_XM_RX_MAC_STAT_WIDTH 1 +#define FRF_AB_XM_TX_MAC_STAT_LBN 10 +#define FRF_AB_XM_TX_MAC_STAT_WIDTH 1 +#define FRF_AB_XM_MCNTL_PASS_LBN 8 +#define FRF_AB_XM_MCNTL_PASS_WIDTH 2 +#define FRF_AB_XM_REJ_CNTL_UCAST_LBN 6 +#define FRF_AB_XM_REJ_CNTL_UCAST_WIDTH 1 +#define FRF_AB_XM_REJ_CNTL_MCAST_LBN 5 +#define FRF_AB_XM_REJ_CNTL_MCAST_WIDTH 1 +#define FRF_AB_XM_ZPAUSE_LBN 2 +#define FRF_AB_XM_ZPAUSE_WIDTH 1 +#define FRF_AB_XM_XMIT_PAUSE_LBN 1 +#define FRF_AB_XM_XMIT_PAUSE_WIDTH 1 +#define FRF_AB_XM_DIS_FCNTL_LBN 0 +#define FRF_AB_XM_DIS_FCNTL_WIDTH 1 + +/* XM_PAUSE_TIME_REG: XGMAC pause time register */ +#define FR_AB_XM_PAUSE_TIME 0x00001290 +#define FRF_AB_XM_TX_PAUSE_CNT_LBN 16 +#define FRF_AB_XM_TX_PAUSE_CNT_WIDTH 16 +#define FRF_AB_XM_RX_PAUSE_CNT_LBN 0 +#define FRF_AB_XM_RX_PAUSE_CNT_WIDTH 16 + +/* XM_TX_PARAM_REG: XGMAC transmit parameter register */ +#define FR_AB_XM_TX_PARAM 0x000012d0 +#define FRF_AB_XM_TX_JUMBO_MODE_LBN 31 +#define FRF_AB_XM_TX_JUMBO_MODE_WIDTH 1 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_LBN 19 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH 11 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN 16 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH 3 +#define FRF_AB_XM_PAD_CHAR_LBN 0 +#define FRF_AB_XM_PAD_CHAR_WIDTH 8 + +/* XM_RX_PARAM_REG: XGMAC receive parameter register */ +#define FR_AB_XM_RX_PARAM 0x000012e0 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_LBN 3 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH 11 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN 0 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH 3 + +/* XM_MGT_INT_MSK_REG: XGMAC management interrupt mask register */ +#define FR_AB_XM_MGT_INT_MSK 0x000012f0 +#define FRF_AB_XM_STAT_CNTR_OF_LBN 9 +#define FRF_AB_XM_STAT_CNTR_OF_WIDTH 1 +#define FRF_AB_XM_STAT_CNTR_HF_LBN 8 +#define FRF_AB_XM_STAT_CNTR_HF_WIDTH 1 +#define FRF_AB_XM_PRMBLE_ERR_LBN 2 +#define FRF_AB_XM_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_RMTFLT_LBN 1 +#define FRF_AB_XM_RMTFLT_WIDTH 1 +#define FRF_AB_XM_LCLFLT_LBN 0 +#define FRF_AB_XM_LCLFLT_WIDTH 1 + +/* XX_PWR_RST_REG: XGXS/XAUI powerdown/reset register */ +#define FR_AB_XX_PWR_RST 0x00001300 +#define FRF_AB_XX_PWRDND_SIG_LBN 31 +#define FRF_AB_XX_PWRDND_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNC_SIG_LBN 30 +#define FRF_AB_XX_PWRDNC_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNB_SIG_LBN 29 +#define FRF_AB_XX_PWRDNB_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNA_SIG_LBN 28 +#define FRF_AB_XX_PWRDNA_SIG_WIDTH 1 +#define FRF_AB_XX_SIM_MODE_LBN 27 +#define FRF_AB_XX_SIM_MODE_WIDTH 1 +#define FRF_AB_XX_RSTPLLCD_SIG_LBN 25 +#define FRF_AB_XX_RSTPLLCD_SIG_WIDTH 1 +#define FRF_AB_XX_RSTPLLAB_SIG_LBN 24 +#define FRF_AB_XX_RSTPLLAB_SIG_WIDTH 1 +#define FRF_AB_XX_RESETD_SIG_LBN 23 +#define FRF_AB_XX_RESETD_SIG_WIDTH 1 +#define FRF_AB_XX_RESETC_SIG_LBN 22 +#define FRF_AB_XX_RESETC_SIG_WIDTH 1 +#define FRF_AB_XX_RESETB_SIG_LBN 21 +#define FRF_AB_XX_RESETB_SIG_WIDTH 1 +#define FRF_AB_XX_RESETA_SIG_LBN 20 +#define FRF_AB_XX_RESETA_SIG_WIDTH 1 +#define FRF_AB_XX_RSTXGXSRX_SIG_LBN 18 +#define FRF_AB_XX_RSTXGXSRX_SIG_WIDTH 1 +#define FRF_AB_XX_RSTXGXSTX_SIG_LBN 17 +#define FRF_AB_XX_RSTXGXSTX_SIG_WIDTH 1 +#define FRF_AB_XX_SD_RST_ACT_LBN 16 +#define FRF_AB_XX_SD_RST_ACT_WIDTH 1 +#define FRF_AB_XX_PWRDND_EN_LBN 15 +#define FRF_AB_XX_PWRDND_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNC_EN_LBN 14 +#define FRF_AB_XX_PWRDNC_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNB_EN_LBN 13 +#define FRF_AB_XX_PWRDNB_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNA_EN_LBN 12 +#define FRF_AB_XX_PWRDNA_EN_WIDTH 1 +#define FRF_AB_XX_RSTPLLCD_EN_LBN 9 +#define FRF_AB_XX_RSTPLLCD_EN_WIDTH 1 +#define FRF_AB_XX_RSTPLLAB_EN_LBN 8 +#define FRF_AB_XX_RSTPLLAB_EN_WIDTH 1 +#define FRF_AB_XX_RESETD_EN_LBN 7 +#define FRF_AB_XX_RESETD_EN_WIDTH 1 +#define FRF_AB_XX_RESETC_EN_LBN 6 +#define FRF_AB_XX_RESETC_EN_WIDTH 1 +#define FRF_AB_XX_RESETB_EN_LBN 5 +#define FRF_AB_XX_RESETB_EN_WIDTH 1 +#define FRF_AB_XX_RESETA_EN_LBN 4 +#define FRF_AB_XX_RESETA_EN_WIDTH 1 +#define FRF_AB_XX_RSTXGXSRX_EN_LBN 2 +#define FRF_AB_XX_RSTXGXSRX_EN_WIDTH 1 +#define FRF_AB_XX_RSTXGXSTX_EN_LBN 1 +#define FRF_AB_XX_RSTXGXSTX_EN_WIDTH 1 +#define FRF_AB_XX_RST_XX_EN_LBN 0 +#define FRF_AB_XX_RST_XX_EN_WIDTH 1 + +/* XX_SD_CTL_REG: XGXS/XAUI powerdown/reset control register */ +#define FR_AB_XX_SD_CTL 0x00001310 +#define FRF_AB_XX_TERMADJ1_LBN 17 +#define FRF_AB_XX_TERMADJ1_WIDTH 1 +#define FRF_AB_XX_TERMADJ0_LBN 16 +#define FRF_AB_XX_TERMADJ0_WIDTH 1 +#define FRF_AB_XX_HIDRVD_LBN 15 +#define FRF_AB_XX_HIDRVD_WIDTH 1 +#define FRF_AB_XX_LODRVD_LBN 14 +#define FRF_AB_XX_LODRVD_WIDTH 1 +#define FRF_AB_XX_HIDRVC_LBN 13 +#define FRF_AB_XX_HIDRVC_WIDTH 1 +#define FRF_AB_XX_LODRVC_LBN 12 +#define FRF_AB_XX_LODRVC_WIDTH 1 +#define FRF_AB_XX_HIDRVB_LBN 11 +#define FRF_AB_XX_HIDRVB_WIDTH 1 +#define FRF_AB_XX_LODRVB_LBN 10 +#define FRF_AB_XX_LODRVB_WIDTH 1 +#define FRF_AB_XX_HIDRVA_LBN 9 +#define FRF_AB_XX_HIDRVA_WIDTH 1 +#define FRF_AB_XX_LODRVA_LBN 8 +#define FRF_AB_XX_LODRVA_WIDTH 1 +#define FRF_AB_XX_LPBKD_LBN 3 +#define FRF_AB_XX_LPBKD_WIDTH 1 +#define FRF_AB_XX_LPBKC_LBN 2 +#define FRF_AB_XX_LPBKC_WIDTH 1 +#define FRF_AB_XX_LPBKB_LBN 1 +#define FRF_AB_XX_LPBKB_WIDTH 1 +#define FRF_AB_XX_LPBKA_LBN 0 +#define FRF_AB_XX_LPBKA_WIDTH 1 + +/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */ +#define FR_AB_XX_TXDRV_CTL 0x00001320 +#define FRF_AB_XX_DEQD_LBN 28 +#define FRF_AB_XX_DEQD_WIDTH 4 +#define FRF_AB_XX_DEQC_LBN 24 +#define FRF_AB_XX_DEQC_WIDTH 4 +#define FRF_AB_XX_DEQB_LBN 20 +#define FRF_AB_XX_DEQB_WIDTH 4 +#define FRF_AB_XX_DEQA_LBN 16 +#define FRF_AB_XX_DEQA_WIDTH 4 +#define FRF_AB_XX_DTXD_LBN 12 +#define FRF_AB_XX_DTXD_WIDTH 4 +#define FRF_AB_XX_DTXC_LBN 8 +#define FRF_AB_XX_DTXC_WIDTH 4 +#define FRF_AB_XX_DTXB_LBN 4 +#define FRF_AB_XX_DTXB_WIDTH 4 +#define FRF_AB_XX_DTXA_LBN 0 +#define FRF_AB_XX_DTXA_WIDTH 4 + +/* XX_PRBS_CTL_REG: documentation to be written for sum_XX_PRBS_CTL_REG */ +#define FR_AB_XX_PRBS_CTL 0x00001330 +#define FRF_AB_XX_CH3_RX_PRBS_SEL_LBN 30 +#define FRF_AB_XX_CH3_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH3_RX_PRBS_INV_LBN 29 +#define FRF_AB_XX_CH3_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_LBN 28 +#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH2_RX_PRBS_SEL_LBN 26 +#define FRF_AB_XX_CH2_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH2_RX_PRBS_INV_LBN 25 +#define FRF_AB_XX_CH2_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_LBN 24 +#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH1_RX_PRBS_SEL_LBN 22 +#define FRF_AB_XX_CH1_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH1_RX_PRBS_INV_LBN 21 +#define FRF_AB_XX_CH1_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_LBN 20 +#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH0_RX_PRBS_SEL_LBN 18 +#define FRF_AB_XX_CH0_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH0_RX_PRBS_INV_LBN 17 +#define FRF_AB_XX_CH0_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_LBN 16 +#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH3_TX_PRBS_SEL_LBN 14 +#define FRF_AB_XX_CH3_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH3_TX_PRBS_INV_LBN 13 +#define FRF_AB_XX_CH3_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_LBN 12 +#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH2_TX_PRBS_SEL_LBN 10 +#define FRF_AB_XX_CH2_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH2_TX_PRBS_INV_LBN 9 +#define FRF_AB_XX_CH2_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_LBN 8 +#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH1_TX_PRBS_SEL_LBN 6 +#define FRF_AB_XX_CH1_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH1_TX_PRBS_INV_LBN 5 +#define FRF_AB_XX_CH1_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_LBN 4 +#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH0_TX_PRBS_SEL_LBN 2 +#define FRF_AB_XX_CH0_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH0_TX_PRBS_INV_LBN 1 +#define FRF_AB_XX_CH0_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_LBN 0 +#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_WIDTH 1 + +/* XX_PRBS_CHK_REG: documentation to be written for sum_XX_PRBS_CHK_REG */ +#define FR_AB_XX_PRBS_CHK 0x00001340 +#define FRF_AB_XX_REV_LB_EN_LBN 16 +#define FRF_AB_XX_REV_LB_EN_WIDTH 1 +#define FRF_AB_XX_CH3_DEG_DET_LBN 15 +#define FRF_AB_XX_CH3_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH3_LFSR_LOCK_IND_LBN 14 +#define FRF_AB_XX_CH3_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH3_PRBS_FRUN_LBN 13 +#define FRF_AB_XX_CH3_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH3_ERR_CHK_LBN 12 +#define FRF_AB_XX_CH3_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH2_DEG_DET_LBN 11 +#define FRF_AB_XX_CH2_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH2_LFSR_LOCK_IND_LBN 10 +#define FRF_AB_XX_CH2_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH2_PRBS_FRUN_LBN 9 +#define FRF_AB_XX_CH2_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH2_ERR_CHK_LBN 8 +#define FRF_AB_XX_CH2_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH1_DEG_DET_LBN 7 +#define FRF_AB_XX_CH1_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH1_LFSR_LOCK_IND_LBN 6 +#define FRF_AB_XX_CH1_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH1_PRBS_FRUN_LBN 5 +#define FRF_AB_XX_CH1_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH1_ERR_CHK_LBN 4 +#define FRF_AB_XX_CH1_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH0_DEG_DET_LBN 3 +#define FRF_AB_XX_CH0_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH0_LFSR_LOCK_IND_LBN 2 +#define FRF_AB_XX_CH0_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH0_PRBS_FRUN_LBN 1 +#define FRF_AB_XX_CH0_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH0_ERR_CHK_LBN 0 +#define FRF_AB_XX_CH0_ERR_CHK_WIDTH 1 + +/* XX_PRBS_ERR_REG: documentation to be written for sum_XX_PRBS_ERR_REG */ +#define FR_AB_XX_PRBS_ERR 0x00001350 +#define FRF_AB_XX_CH3_PRBS_ERR_CNT_LBN 24 +#define FRF_AB_XX_CH3_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH2_PRBS_ERR_CNT_LBN 16 +#define FRF_AB_XX_CH2_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH1_PRBS_ERR_CNT_LBN 8 +#define FRF_AB_XX_CH1_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH0_PRBS_ERR_CNT_LBN 0 +#define FRF_AB_XX_CH0_PRBS_ERR_CNT_WIDTH 8 + +/* XX_CORE_STAT_REG: XAUI XGXS core status register */ +#define FR_AB_XX_CORE_STAT 0x00001360 +#define FRF_AB_XX_FORCE_SIG3_LBN 31 +#define FRF_AB_XX_FORCE_SIG3_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG3_VAL_LBN 30 +#define FRF_AB_XX_FORCE_SIG3_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG2_LBN 29 +#define FRF_AB_XX_FORCE_SIG2_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG2_VAL_LBN 28 +#define FRF_AB_XX_FORCE_SIG2_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG1_LBN 27 +#define FRF_AB_XX_FORCE_SIG1_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG1_VAL_LBN 26 +#define FRF_AB_XX_FORCE_SIG1_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG0_LBN 25 +#define FRF_AB_XX_FORCE_SIG0_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG0_VAL_LBN 24 +#define FRF_AB_XX_FORCE_SIG0_VAL_WIDTH 1 +#define FRF_AB_XX_XGXS_LB_EN_LBN 23 +#define FRF_AB_XX_XGXS_LB_EN_WIDTH 1 +#define FRF_AB_XX_XGMII_LB_EN_LBN 22 +#define FRF_AB_XX_XGMII_LB_EN_WIDTH 1 +#define FRF_AB_XX_MATCH_FAULT_LBN 21 +#define FRF_AB_XX_MATCH_FAULT_WIDTH 1 +#define FRF_AB_XX_ALIGN_DONE_LBN 20 +#define FRF_AB_XX_ALIGN_DONE_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT3_LBN 19 +#define FRF_AB_XX_SYNC_STAT3_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT2_LBN 18 +#define FRF_AB_XX_SYNC_STAT2_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT1_LBN 17 +#define FRF_AB_XX_SYNC_STAT1_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT0_LBN 16 +#define FRF_AB_XX_SYNC_STAT0_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH3_LBN 15 +#define FRF_AB_XX_COMMA_DET_CH3_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH2_LBN 14 +#define FRF_AB_XX_COMMA_DET_CH2_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH1_LBN 13 +#define FRF_AB_XX_COMMA_DET_CH1_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH0_LBN 12 +#define FRF_AB_XX_COMMA_DET_CH0_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH3_LBN 11 +#define FRF_AB_XX_CGRP_ALIGN_CH3_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH2_LBN 10 +#define FRF_AB_XX_CGRP_ALIGN_CH2_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH1_LBN 9 +#define FRF_AB_XX_CGRP_ALIGN_CH1_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH0_LBN 8 +#define FRF_AB_XX_CGRP_ALIGN_CH0_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH3_LBN 7 +#define FRF_AB_XX_CHAR_ERR_CH3_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH2_LBN 6 +#define FRF_AB_XX_CHAR_ERR_CH2_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH1_LBN 5 +#define FRF_AB_XX_CHAR_ERR_CH1_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH0_LBN 4 +#define FRF_AB_XX_CHAR_ERR_CH0_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH3_LBN 3 +#define FRF_AB_XX_DISPERR_CH3_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH2_LBN 2 +#define FRF_AB_XX_DISPERR_CH2_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH1_LBN 1 +#define FRF_AB_XX_DISPERR_CH1_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH0_LBN 0 +#define FRF_AB_XX_DISPERR_CH0_WIDTH 1 + +/* RX_DESC_PTR_TBL_KER: Receive descriptor pointer table */ +#define FR_AA_RX_DESC_PTR_TBL_KER 0x00011800 +#define FR_AA_RX_DESC_PTR_TBL_KER_STEP 16 +#define FR_AA_RX_DESC_PTR_TBL_KER_ROWS 4 +/* RX_DESC_PTR_TBL: Receive descriptor pointer table */ +#define FR_BZ_RX_DESC_PTR_TBL 0x00f40000 +#define FR_BZ_RX_DESC_PTR_TBL_STEP 16 +#define FR_BB_RX_DESC_PTR_TBL_ROWS 4096 +#define FR_CZ_RX_DESC_PTR_TBL_ROWS 1024 +#define FRF_CZ_RX_HDR_SPLIT_LBN 90 +#define FRF_CZ_RX_HDR_SPLIT_WIDTH 1 +#define FRF_AA_RX_RESET_LBN 89 +#define FRF_AA_RX_RESET_WIDTH 1 +#define FRF_AZ_RX_ISCSI_DDIG_EN_LBN 88 +#define FRF_AZ_RX_ISCSI_DDIG_EN_WIDTH 1 +#define FRF_AZ_RX_ISCSI_HDIG_EN_LBN 87 +#define FRF_AZ_RX_ISCSI_HDIG_EN_WIDTH 1 +#define FRF_AZ_RX_DESC_PREF_ACT_LBN 86 +#define FRF_AZ_RX_DESC_PREF_ACT_WIDTH 1 +#define FRF_AZ_RX_DC_HW_RPTR_LBN 80 +#define FRF_AZ_RX_DC_HW_RPTR_WIDTH 6 +#define FRF_AZ_RX_DESCQ_HW_RPTR_LBN 68 +#define FRF_AZ_RX_DESCQ_HW_RPTR_WIDTH 12 +#define FRF_AZ_RX_DESCQ_SW_WPTR_LBN 56 +#define FRF_AZ_RX_DESCQ_SW_WPTR_WIDTH 12 +#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_LBN 36 +#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FRF_AZ_RX_DESCQ_EVQ_ID_LBN 24 +#define FRF_AZ_RX_DESCQ_EVQ_ID_WIDTH 12 +#define FRF_AZ_RX_DESCQ_OWNER_ID_LBN 10 +#define FRF_AZ_RX_DESCQ_OWNER_ID_WIDTH 14 +#define FRF_AZ_RX_DESCQ_LABEL_LBN 5 +#define FRF_AZ_RX_DESCQ_LABEL_WIDTH 5 +#define FRF_AZ_RX_DESCQ_SIZE_LBN 3 +#define FRF_AZ_RX_DESCQ_SIZE_WIDTH 2 +#define FFE_AZ_RX_DESCQ_SIZE_4K 3 +#define FFE_AZ_RX_DESCQ_SIZE_2K 2 +#define FFE_AZ_RX_DESCQ_SIZE_1K 1 +#define FFE_AZ_RX_DESCQ_SIZE_512 0 +#define FRF_AZ_RX_DESCQ_TYPE_LBN 2 +#define FRF_AZ_RX_DESCQ_TYPE_WIDTH 1 +#define FRF_AZ_RX_DESCQ_JUMBO_LBN 1 +#define FRF_AZ_RX_DESCQ_JUMBO_WIDTH 1 +#define FRF_AZ_RX_DESCQ_EN_LBN 0 +#define FRF_AZ_RX_DESCQ_EN_WIDTH 1 + +/* TX_DESC_PTR_TBL_KER: Transmit descriptor pointer */ +#define FR_AA_TX_DESC_PTR_TBL_KER 0x00011900 +#define FR_AA_TX_DESC_PTR_TBL_KER_STEP 16 +#define FR_AA_TX_DESC_PTR_TBL_KER_ROWS 8 +/* TX_DESC_PTR_TBL: Transmit descriptor pointer */ +#define FR_BZ_TX_DESC_PTR_TBL 0x00f50000 +#define FR_BZ_TX_DESC_PTR_TBL_STEP 16 +#define FR_BB_TX_DESC_PTR_TBL_ROWS 4096 +#define FR_CZ_TX_DESC_PTR_TBL_ROWS 1024 +#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_LBN 94 +#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_WIDTH 2 +#define FRF_CZ_TX_DPT_ETH_FILT_EN_LBN 93 +#define FRF_CZ_TX_DPT_ETH_FILT_EN_WIDTH 1 +#define FRF_CZ_TX_DPT_IP_FILT_EN_LBN 92 +#define FRF_CZ_TX_DPT_IP_FILT_EN_WIDTH 1 +#define FRF_BZ_TX_NON_IP_DROP_DIS_LBN 91 +#define FRF_BZ_TX_NON_IP_DROP_DIS_WIDTH 1 +#define FRF_BZ_TX_IP_CHKSM_DIS_LBN 90 +#define FRF_BZ_TX_IP_CHKSM_DIS_WIDTH 1 +#define FRF_BZ_TX_TCP_CHKSM_DIS_LBN 89 +#define FRF_BZ_TX_TCP_CHKSM_DIS_WIDTH 1 +#define FRF_AZ_TX_DESCQ_EN_LBN 88 +#define FRF_AZ_TX_DESCQ_EN_WIDTH 1 +#define FRF_AZ_TX_ISCSI_DDIG_EN_LBN 87 +#define FRF_AZ_TX_ISCSI_DDIG_EN_WIDTH 1 +#define FRF_AZ_TX_ISCSI_HDIG_EN_LBN 86 +#define FRF_AZ_TX_ISCSI_HDIG_EN_WIDTH 1 +#define FRF_AZ_TX_DC_HW_RPTR_LBN 80 +#define FRF_AZ_TX_DC_HW_RPTR_WIDTH 6 +#define FRF_AZ_TX_DESCQ_HW_RPTR_LBN 68 +#define FRF_AZ_TX_DESCQ_HW_RPTR_WIDTH 12 +#define FRF_AZ_TX_DESCQ_SW_WPTR_LBN 56 +#define FRF_AZ_TX_DESCQ_SW_WPTR_WIDTH 12 +#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_LBN 36 +#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FRF_AZ_TX_DESCQ_EVQ_ID_LBN 24 +#define FRF_AZ_TX_DESCQ_EVQ_ID_WIDTH 12 +#define FRF_AZ_TX_DESCQ_OWNER_ID_LBN 10 +#define FRF_AZ_TX_DESCQ_OWNER_ID_WIDTH 14 +#define FRF_AZ_TX_DESCQ_LABEL_LBN 5 +#define FRF_AZ_TX_DESCQ_LABEL_WIDTH 5 +#define FRF_AZ_TX_DESCQ_SIZE_LBN 3 +#define FRF_AZ_TX_DESCQ_SIZE_WIDTH 2 +#define FFE_AZ_TX_DESCQ_SIZE_4K 3 +#define FFE_AZ_TX_DESCQ_SIZE_2K 2 +#define FFE_AZ_TX_DESCQ_SIZE_1K 1 +#define FFE_AZ_TX_DESCQ_SIZE_512 0 +#define FRF_AZ_TX_DESCQ_TYPE_LBN 1 +#define FRF_AZ_TX_DESCQ_TYPE_WIDTH 2 +#define FRF_AZ_TX_DESCQ_FLUSH_LBN 0 +#define FRF_AZ_TX_DESCQ_FLUSH_WIDTH 1 + +/* EVQ_PTR_TBL_KER: Event queue pointer table */ +#define FR_AA_EVQ_PTR_TBL_KER 0x00011a00 +#define FR_AA_EVQ_PTR_TBL_KER_STEP 16 +#define FR_AA_EVQ_PTR_TBL_KER_ROWS 4 +/* EVQ_PTR_TBL: Event queue pointer table */ +#define FR_BZ_EVQ_PTR_TBL 0x00f60000 +#define FR_BZ_EVQ_PTR_TBL_STEP 16 +#define FR_CZ_EVQ_PTR_TBL_ROWS 1024 +#define FR_BB_EVQ_PTR_TBL_ROWS 4096 +#define FRF_BZ_EVQ_RPTR_IGN_LBN 40 +#define FRF_BZ_EVQ_RPTR_IGN_WIDTH 1 +#define FRF_AB_EVQ_WKUP_OR_INT_EN_LBN 39 +#define FRF_AB_EVQ_WKUP_OR_INT_EN_WIDTH 1 +#define FRF_CZ_EVQ_DOS_PROTECT_EN_LBN 39 +#define FRF_CZ_EVQ_DOS_PROTECT_EN_WIDTH 1 +#define FRF_AZ_EVQ_NXT_WPTR_LBN 24 +#define FRF_AZ_EVQ_NXT_WPTR_WIDTH 15 +#define FRF_AZ_EVQ_EN_LBN 23 +#define FRF_AZ_EVQ_EN_WIDTH 1 +#define FRF_AZ_EVQ_SIZE_LBN 20 +#define FRF_AZ_EVQ_SIZE_WIDTH 3 +#define FFE_AZ_EVQ_SIZE_32K 6 +#define FFE_AZ_EVQ_SIZE_16K 5 +#define FFE_AZ_EVQ_SIZE_8K 4 +#define FFE_AZ_EVQ_SIZE_4K 3 +#define FFE_AZ_EVQ_SIZE_2K 2 +#define FFE_AZ_EVQ_SIZE_1K 1 +#define FFE_AZ_EVQ_SIZE_512 0 +#define FRF_AZ_EVQ_BUF_BASE_ID_LBN 0 +#define FRF_AZ_EVQ_BUF_BASE_ID_WIDTH 20 + +/* BUF_HALF_TBL_KER: Buffer table in half buffer table mode direct access by driver */ +#define FR_AA_BUF_HALF_TBL_KER 0x00018000 +#define FR_AA_BUF_HALF_TBL_KER_STEP 8 +#define FR_AA_BUF_HALF_TBL_KER_ROWS 4096 +/* BUF_HALF_TBL: Buffer table in half buffer table mode direct access by driver */ +#define FR_BZ_BUF_HALF_TBL 0x00800000 +#define FR_BZ_BUF_HALF_TBL_STEP 8 +#define FR_CZ_BUF_HALF_TBL_ROWS 147456 +#define FR_BB_BUF_HALF_TBL_ROWS 524288 +#define FRF_AZ_BUF_ADR_HBUF_ODD_LBN 44 +#define FRF_AZ_BUF_ADR_HBUF_ODD_WIDTH 20 +#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_LBN 32 +#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_WIDTH 12 +#define FRF_AZ_BUF_ADR_HBUF_EVEN_LBN 12 +#define FRF_AZ_BUF_ADR_HBUF_EVEN_WIDTH 20 +#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_LBN 0 +#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_WIDTH 12 + +/* BUF_FULL_TBL_KER: Buffer table in full buffer table mode direct access by driver */ +#define FR_AA_BUF_FULL_TBL_KER 0x00018000 +#define FR_AA_BUF_FULL_TBL_KER_STEP 8 +#define FR_AA_BUF_FULL_TBL_KER_ROWS 4096 +/* BUF_FULL_TBL: Buffer table in full buffer table mode direct access by driver */ +#define FR_BZ_BUF_FULL_TBL 0x00800000 +#define FR_BZ_BUF_FULL_TBL_STEP 8 +#define FR_CZ_BUF_FULL_TBL_ROWS 147456 +#define FR_BB_BUF_FULL_TBL_ROWS 917504 +#define FRF_AZ_BUF_FULL_UNUSED_LBN 51 +#define FRF_AZ_BUF_FULL_UNUSED_WIDTH 13 +#define FRF_AZ_IP_DAT_BUF_SIZE_LBN 50 +#define FRF_AZ_IP_DAT_BUF_SIZE_WIDTH 1 +#define FRF_AZ_BUF_ADR_REGION_LBN 48 +#define FRF_AZ_BUF_ADR_REGION_WIDTH 2 +#define FFE_AZ_BUF_ADR_REGN3 3 +#define FFE_AZ_BUF_ADR_REGN2 2 +#define FFE_AZ_BUF_ADR_REGN1 1 +#define FFE_AZ_BUF_ADR_REGN0 0 +#define FRF_AZ_BUF_ADR_FBUF_LBN 14 +#define FRF_AZ_BUF_ADR_FBUF_WIDTH 34 +#define FRF_AZ_BUF_OWNER_ID_FBUF_LBN 0 +#define FRF_AZ_BUF_OWNER_ID_FBUF_WIDTH 14 + +/* RX_FILTER_TBL0: TCP/IPv4 Receive filter table */ +#define FR_BZ_RX_FILTER_TBL0 0x00f00000 +#define FR_BZ_RX_FILTER_TBL0_STEP 32 +#define FR_BZ_RX_FILTER_TBL0_ROWS 8192 +/* RX_FILTER_TBL1: TCP/IPv4 Receive filter table */ +#define FR_BB_RX_FILTER_TBL1 0x00f00010 +#define FR_BB_RX_FILTER_TBL1_STEP 32 +#define FR_BB_RX_FILTER_TBL1_ROWS 8192 +#define FRF_BZ_RSS_EN_LBN 110 +#define FRF_BZ_RSS_EN_WIDTH 1 +#define FRF_BZ_SCATTER_EN_LBN 109 +#define FRF_BZ_SCATTER_EN_WIDTH 1 +#define FRF_BZ_TCP_UDP_LBN 108 +#define FRF_BZ_TCP_UDP_WIDTH 1 +#define FRF_BZ_RXQ_ID_LBN 96 +#define FRF_BZ_RXQ_ID_WIDTH 12 +#define FRF_BZ_DEST_IP_LBN 64 +#define FRF_BZ_DEST_IP_WIDTH 32 +#define FRF_BZ_DEST_PORT_TCP_LBN 48 +#define FRF_BZ_DEST_PORT_TCP_WIDTH 16 +#define FRF_BZ_SRC_IP_LBN 16 +#define FRF_BZ_SRC_IP_WIDTH 32 +#define FRF_BZ_SRC_TCP_DEST_UDP_LBN 0 +#define FRF_BZ_SRC_TCP_DEST_UDP_WIDTH 16 + +/* RX_MAC_FILTER_TBL0: Receive Ethernet filter table */ +#define FR_CZ_RX_MAC_FILTER_TBL0 0x00f00010 +#define FR_CZ_RX_MAC_FILTER_TBL0_STEP 32 +#define FR_CZ_RX_MAC_FILTER_TBL0_ROWS 512 +#define FRF_CZ_RMFT_RSS_EN_LBN 75 +#define FRF_CZ_RMFT_RSS_EN_WIDTH 1 +#define FRF_CZ_RMFT_SCATTER_EN_LBN 74 +#define FRF_CZ_RMFT_SCATTER_EN_WIDTH 1 +#define FRF_CZ_RMFT_IP_OVERRIDE_LBN 73 +#define FRF_CZ_RMFT_IP_OVERRIDE_WIDTH 1 +#define FRF_CZ_RMFT_RXQ_ID_LBN 61 +#define FRF_CZ_RMFT_RXQ_ID_WIDTH 12 +#define FRF_CZ_RMFT_WILDCARD_MATCH_LBN 60 +#define FRF_CZ_RMFT_WILDCARD_MATCH_WIDTH 1 +#define FRF_CZ_RMFT_DEST_MAC_LBN 12 +#define FRF_CZ_RMFT_DEST_MAC_WIDTH 48 +#define FRF_CZ_RMFT_VLAN_ID_LBN 0 +#define FRF_CZ_RMFT_VLAN_ID_WIDTH 12 + +/* TIMER_TBL: Timer table */ +#define FR_BZ_TIMER_TBL 0x00f70000 +#define FR_BZ_TIMER_TBL_STEP 16 +#define FR_CZ_TIMER_TBL_ROWS 1024 +#define FR_BB_TIMER_TBL_ROWS 4096 +#define FRF_CZ_TIMER_Q_EN_LBN 33 +#define FRF_CZ_TIMER_Q_EN_WIDTH 1 +#define FRF_CZ_INT_ARMD_LBN 32 +#define FRF_CZ_INT_ARMD_WIDTH 1 +#define FRF_CZ_INT_PEND_LBN 31 +#define FRF_CZ_INT_PEND_WIDTH 1 +#define FRF_CZ_HOST_NOTIFY_MODE_LBN 30 +#define FRF_CZ_HOST_NOTIFY_MODE_WIDTH 1 +#define FRF_CZ_RELOAD_TIMER_VAL_LBN 16 +#define FRF_CZ_RELOAD_TIMER_VAL_WIDTH 14 +#define FRF_CZ_TIMER_MODE_LBN 14 +#define FRF_CZ_TIMER_MODE_WIDTH 2 +#define FFE_CZ_TIMER_MODE_INT_HLDOFF 3 +#define FFE_CZ_TIMER_MODE_TRIG_START 2 +#define FFE_CZ_TIMER_MODE_IMMED_START 1 +#define FFE_CZ_TIMER_MODE_DIS 0 +#define FRF_BB_TIMER_MODE_LBN 12 +#define FRF_BB_TIMER_MODE_WIDTH 2 +#define FFE_BB_TIMER_MODE_INT_HLDOFF 2 +#define FFE_BB_TIMER_MODE_TRIG_START 2 +#define FFE_BB_TIMER_MODE_IMMED_START 1 +#define FFE_BB_TIMER_MODE_DIS 0 +#define FRF_CZ_TIMER_VAL_LBN 0 +#define FRF_CZ_TIMER_VAL_WIDTH 14 +#define FRF_BB_TIMER_VAL_LBN 0 +#define FRF_BB_TIMER_VAL_WIDTH 12 + +/* TX_PACE_TBL: Transmit pacing table */ +#define FR_BZ_TX_PACE_TBL 0x00f80000 +#define FR_BZ_TX_PACE_TBL_STEP 16 +#define FR_CZ_TX_PACE_TBL_ROWS 1024 +#define FR_BB_TX_PACE_TBL_ROWS 4096 +#define FRF_BZ_TX_PACE_LBN 0 +#define FRF_BZ_TX_PACE_WIDTH 5 + +/* RX_INDIRECTION_TBL: RX Indirection Table */ +#define FR_BZ_RX_INDIRECTION_TBL 0x00fb0000 +#define FR_BZ_RX_INDIRECTION_TBL_STEP 16 +#define FR_BZ_RX_INDIRECTION_TBL_ROWS 128 +#define FRF_BZ_IT_QUEUE_LBN 0 +#define FRF_BZ_IT_QUEUE_WIDTH 6 + +/* TX_FILTER_TBL0: TCP/IPv4 Transmit filter table */ +#define FR_CZ_TX_FILTER_TBL0 0x00fc0000 +#define FR_CZ_TX_FILTER_TBL0_STEP 16 +#define FR_CZ_TX_FILTER_TBL0_ROWS 8192 +#define FRF_CZ_TIFT_TCP_UDP_LBN 108 +#define FRF_CZ_TIFT_TCP_UDP_WIDTH 1 +#define FRF_CZ_TIFT_TXQ_ID_LBN 96 +#define FRF_CZ_TIFT_TXQ_ID_WIDTH 12 +#define FRF_CZ_TIFT_DEST_IP_LBN 64 +#define FRF_CZ_TIFT_DEST_IP_WIDTH 32 +#define FRF_CZ_TIFT_DEST_PORT_TCP_LBN 48 +#define FRF_CZ_TIFT_DEST_PORT_TCP_WIDTH 16 +#define FRF_CZ_TIFT_SRC_IP_LBN 16 +#define FRF_CZ_TIFT_SRC_IP_WIDTH 32 +#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_LBN 0 +#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_WIDTH 16 + +/* TX_MAC_FILTER_TBL0: Transmit Ethernet filter table */ +#define FR_CZ_TX_MAC_FILTER_TBL0 0x00fe0000 +#define FR_CZ_TX_MAC_FILTER_TBL0_STEP 16 +#define FR_CZ_TX_MAC_FILTER_TBL0_ROWS 512 +#define FRF_CZ_TMFT_TXQ_ID_LBN 61 +#define FRF_CZ_TMFT_TXQ_ID_WIDTH 12 +#define FRF_CZ_TMFT_WILDCARD_MATCH_LBN 60 +#define FRF_CZ_TMFT_WILDCARD_MATCH_WIDTH 1 +#define FRF_CZ_TMFT_SRC_MAC_LBN 12 +#define FRF_CZ_TMFT_SRC_MAC_WIDTH 48 +#define FRF_CZ_TMFT_VLAN_ID_LBN 0 +#define FRF_CZ_TMFT_VLAN_ID_WIDTH 12 + +/* MC_TREG_SMEM: MC Shared Memory */ +#define FR_CZ_MC_TREG_SMEM 0x00ff0000 +#define FR_CZ_MC_TREG_SMEM_STEP 4 +#define FR_CZ_MC_TREG_SMEM_ROWS 512 +#define FRF_CZ_MC_TREG_SMEM_ROW_LBN 0 +#define FRF_CZ_MC_TREG_SMEM_ROW_WIDTH 32 + +/* MSIX_VECTOR_TABLE: MSIX Vector Table */ +#define FR_BB_MSIX_VECTOR_TABLE 0x00ff0000 +#define FR_BZ_MSIX_VECTOR_TABLE_STEP 16 +#define FR_BB_MSIX_VECTOR_TABLE_ROWS 64 +/* MSIX_VECTOR_TABLE: MSIX Vector Table */ +#define FR_CZ_MSIX_VECTOR_TABLE 0x00000000 +/* FR_BZ_MSIX_VECTOR_TABLE_STEP 16 */ +#define FR_CZ_MSIX_VECTOR_TABLE_ROWS 1024 +#define FRF_BZ_MSIX_VECTOR_RESERVED_LBN 97 +#define FRF_BZ_MSIX_VECTOR_RESERVED_WIDTH 31 +#define FRF_BZ_MSIX_VECTOR_MASK_LBN 96 +#define FRF_BZ_MSIX_VECTOR_MASK_WIDTH 1 +#define FRF_BZ_MSIX_MESSAGE_DATA_LBN 64 +#define FRF_BZ_MSIX_MESSAGE_DATA_WIDTH 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_LBN 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_WIDTH 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_LBN 0 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_WIDTH 32 + +/* MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_BB_MSIX_PBA_TABLE 0x00ff2000 +#define FR_BZ_MSIX_PBA_TABLE_STEP 4 +#define FR_BB_MSIX_PBA_TABLE_ROWS 2 +/* MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_CZ_MSIX_PBA_TABLE 0x00008000 +/* FR_BZ_MSIX_PBA_TABLE_STEP 4 */ +#define FR_CZ_MSIX_PBA_TABLE_ROWS 32 +#define FRF_BZ_MSIX_PBA_PEND_DWORD_LBN 0 +#define FRF_BZ_MSIX_PBA_PEND_DWORD_WIDTH 32 + +/* SRM_DBG_REG: SRAM debug access */ +#define FR_BZ_SRM_DBG 0x03000000 +#define FR_BZ_SRM_DBG_STEP 8 +#define FR_CZ_SRM_DBG_ROWS 262144 +#define FR_BB_SRM_DBG_ROWS 2097152 +#define FRF_BZ_SRM_DBG_LBN 0 +#define FRF_BZ_SRM_DBG_WIDTH 64 + +/* TB_MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_CZ_TB_MSIX_PBA_TABLE 0x00008000 +#define FR_CZ_TB_MSIX_PBA_TABLE_STEP 4 +#define FR_CZ_TB_MSIX_PBA_TABLE_ROWS 1024 +#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_LBN 0 +#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_WIDTH 32 + +/* DRIVER_EV */ +#define FSF_AZ_DRIVER_EV_SUBCODE_LBN 56 +#define FSF_AZ_DRIVER_EV_SUBCODE_WIDTH 4 +#define FSE_BZ_TX_DSC_ERROR_EV 15 +#define FSE_BZ_RX_DSC_ERROR_EV 14 +#define FSE_AA_RX_RECOVER_EV 11 +#define FSE_AZ_TIMER_EV 10 +#define FSE_AZ_TX_PKT_NON_TCP_UDP 9 +#define FSE_AZ_WAKE_UP_EV 6 +#define FSE_AZ_SRM_UPD_DONE_EV 5 +#define FSE_AB_EVQ_NOT_EN_EV 3 +#define FSE_AZ_EVQ_INIT_DONE_EV 2 +#define FSE_AZ_RX_DESCQ_FLS_DONE_EV 1 +#define FSE_AZ_TX_DESCQ_FLS_DONE_EV 0 +#define FSF_AZ_DRIVER_EV_SUBDATA_LBN 0 +#define FSF_AZ_DRIVER_EV_SUBDATA_WIDTH 14 + +/* EVENT_ENTRY */ +#define FSF_AZ_EV_CODE_LBN 60 +#define FSF_AZ_EV_CODE_WIDTH 4 +#define FSE_CZ_EV_CODE_MCDI_EV 12 +#define FSE_CZ_EV_CODE_USER_EV 8 +#define FSE_AZ_EV_CODE_DRV_GEN_EV 7 +#define FSE_AZ_EV_CODE_GLOBAL_EV 6 +#define FSE_AZ_EV_CODE_DRIVER_EV 5 +#define FSE_AZ_EV_CODE_TX_EV 2 +#define FSE_AZ_EV_CODE_RX_EV 0 +#define FSF_AZ_EV_DATA_LBN 0 +#define FSF_AZ_EV_DATA_WIDTH 60 + +/* GLOBAL_EV */ +#define FSF_BB_GLB_EV_RX_RECOVERY_LBN 12 +#define FSF_BB_GLB_EV_RX_RECOVERY_WIDTH 1 +#define FSF_AA_GLB_EV_RX_RECOVERY_LBN 11 +#define FSF_AA_GLB_EV_RX_RECOVERY_WIDTH 1 +#define FSF_BB_GLB_EV_XG_MGT_INTR_LBN 11 +#define FSF_BB_GLB_EV_XG_MGT_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_XFP_PHY0_INTR_LBN 10 +#define FSF_AB_GLB_EV_XFP_PHY0_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_XG_PHY0_INTR_LBN 9 +#define FSF_AB_GLB_EV_XG_PHY0_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_G_PHY0_INTR_LBN 7 +#define FSF_AB_GLB_EV_G_PHY0_INTR_WIDTH 1 + +/* LEGACY_INT_VEC */ +#define FSF_AZ_NET_IVEC_FATAL_INT_LBN 64 +#define FSF_AZ_NET_IVEC_FATAL_INT_WIDTH 1 +#define FSF_AZ_NET_IVEC_INT_Q_LBN 40 +#define FSF_AZ_NET_IVEC_INT_Q_WIDTH 4 +#define FSF_AZ_NET_IVEC_INT_FLAG_LBN 32 +#define FSF_AZ_NET_IVEC_INT_FLAG_WIDTH 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_LBN 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_WIDTH 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_LBN 0 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_WIDTH 1 + +/* MC_XGMAC_FLTR_RULE_DEF */ +#define FSF_CZ_MC_XFRC_MODE_LBN 416 +#define FSF_CZ_MC_XFRC_MODE_WIDTH 1 +#define FSE_CZ_MC_XFRC_MODE_LAYERED 1 +#define FSE_CZ_MC_XFRC_MODE_SIMPLE 0 +#define FSF_CZ_MC_XFRC_HASH_LBN 384 +#define FSF_CZ_MC_XFRC_HASH_WIDTH 32 +#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_LBN 256 +#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_WIDTH 128 +#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_LBN 128 +#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_WIDTH 128 +#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_LBN 0 +#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_WIDTH 128 + +/* RX_EV */ +#define FSF_CZ_RX_EV_PKT_NOT_PARSED_LBN 58 +#define FSF_CZ_RX_EV_PKT_NOT_PARSED_WIDTH 1 +#define FSF_CZ_RX_EV_IPV6_PKT_LBN 57 +#define FSF_CZ_RX_EV_IPV6_PKT_WIDTH 1 +#define FSF_AZ_RX_EV_PKT_OK_LBN 56 +#define FSF_AZ_RX_EV_PKT_OK_WIDTH 1 +#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_LBN 55 +#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_LBN 54 +#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_IP_FRAG_ERR_LBN 53 +#define FSF_AZ_RX_EV_IP_FRAG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_LBN 52 +#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51 +#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_ETH_CRC_ERR_LBN 50 +#define FSF_AZ_RX_EV_ETH_CRC_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_FRM_TRUNC_LBN 49 +#define FSF_AZ_RX_EV_FRM_TRUNC_WIDTH 1 +#define FSF_AA_RX_EV_DRIB_NIB_LBN 49 +#define FSF_AA_RX_EV_DRIB_NIB_WIDTH 1 +#define FSF_AZ_RX_EV_TOBE_DISC_LBN 47 +#define FSF_AZ_RX_EV_TOBE_DISC_WIDTH 1 +#define FSF_AZ_RX_EV_PKT_TYPE_LBN 44 +#define FSF_AZ_RX_EV_PKT_TYPE_WIDTH 3 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_JUMBO 5 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_LLC 4 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN 3 +#define FSE_AZ_RX_EV_PKT_TYPE_JUMBO 2 +#define FSE_AZ_RX_EV_PKT_TYPE_LLC 1 +#define FSE_AZ_RX_EV_PKT_TYPE_ETH 0 +#define FSF_AZ_RX_EV_HDR_TYPE_LBN 42 +#define FSF_AZ_RX_EV_HDR_TYPE_WIDTH 2 +#define FSE_AZ_RX_EV_HDR_TYPE_OTHER 3 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_OTHER 2 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER 2 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_UDP 1 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP 1 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_TCP 0 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP 0 +#define FSF_AZ_RX_EV_DESC_Q_EMPTY_LBN 41 +#define FSF_AZ_RX_EV_DESC_Q_EMPTY_WIDTH 1 +#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_LBN 40 +#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_WIDTH 1 +#define FSF_AZ_RX_EV_MCAST_PKT_LBN 39 +#define FSF_AZ_RX_EV_MCAST_PKT_WIDTH 1 +#define FSF_AA_RX_EV_RECOVERY_FLAG_LBN 37 +#define FSF_AA_RX_EV_RECOVERY_FLAG_WIDTH 1 +#define FSF_AZ_RX_EV_Q_LABEL_LBN 32 +#define FSF_AZ_RX_EV_Q_LABEL_WIDTH 5 +#define FSF_AZ_RX_EV_JUMBO_CONT_LBN 31 +#define FSF_AZ_RX_EV_JUMBO_CONT_WIDTH 1 +#define FSF_AZ_RX_EV_PORT_LBN 30 +#define FSF_AZ_RX_EV_PORT_WIDTH 1 +#define FSF_AZ_RX_EV_BYTE_CNT_LBN 16 +#define FSF_AZ_RX_EV_BYTE_CNT_WIDTH 14 +#define FSF_AZ_RX_EV_SOP_LBN 15 +#define FSF_AZ_RX_EV_SOP_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_PKT_OK_LBN 14 +#define FSF_AZ_RX_EV_ISCSI_PKT_OK_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_LBN 13 +#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_LBN 12 +#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_DESC_PTR_LBN 0 +#define FSF_AZ_RX_EV_DESC_PTR_WIDTH 12 + +/* RX_KER_DESC */ +#define FSF_AZ_RX_KER_BUF_SIZE_LBN 48 +#define FSF_AZ_RX_KER_BUF_SIZE_WIDTH 14 +#define FSF_AZ_RX_KER_BUF_REGION_LBN 46 +#define FSF_AZ_RX_KER_BUF_REGION_WIDTH 2 +#define FSF_AZ_RX_KER_BUF_ADDR_LBN 0 +#define FSF_AZ_RX_KER_BUF_ADDR_WIDTH 46 + +/* RX_USER_DESC */ +#define FSF_AZ_RX_USER_2BYTE_OFFSET_LBN 20 +#define FSF_AZ_RX_USER_2BYTE_OFFSET_WIDTH 12 +#define FSF_AZ_RX_USER_BUF_ID_LBN 0 +#define FSF_AZ_RX_USER_BUF_ID_WIDTH 20 + +/* TX_EV */ +#define FSF_AZ_TX_EV_PKT_ERR_LBN 38 +#define FSF_AZ_TX_EV_PKT_ERR_WIDTH 1 +#define FSF_AZ_TX_EV_PKT_TOO_BIG_LBN 37 +#define FSF_AZ_TX_EV_PKT_TOO_BIG_WIDTH 1 +#define FSF_AZ_TX_EV_Q_LABEL_LBN 32 +#define FSF_AZ_TX_EV_Q_LABEL_WIDTH 5 +#define FSF_AZ_TX_EV_PORT_LBN 16 +#define FSF_AZ_TX_EV_PORT_WIDTH 1 +#define FSF_AZ_TX_EV_WQ_FF_FULL_LBN 15 +#define FSF_AZ_TX_EV_WQ_FF_FULL_WIDTH 1 +#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_LBN 14 +#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define FSF_AZ_TX_EV_COMP_LBN 12 +#define FSF_AZ_TX_EV_COMP_WIDTH 1 +#define FSF_AZ_TX_EV_DESC_PTR_LBN 0 +#define FSF_AZ_TX_EV_DESC_PTR_WIDTH 12 + +/* TX_KER_DESC */ +#define FSF_AZ_TX_KER_CONT_LBN 62 +#define FSF_AZ_TX_KER_CONT_WIDTH 1 +#define FSF_AZ_TX_KER_BYTE_COUNT_LBN 48 +#define FSF_AZ_TX_KER_BYTE_COUNT_WIDTH 14 +#define FSF_AZ_TX_KER_BUF_REGION_LBN 46 +#define FSF_AZ_TX_KER_BUF_REGION_WIDTH 2 +#define FSF_AZ_TX_KER_BUF_ADDR_LBN 0 +#define FSF_AZ_TX_KER_BUF_ADDR_WIDTH 46 + +/* TX_USER_DESC */ +#define FSF_AZ_TX_USER_SW_EV_EN_LBN 48 +#define FSF_AZ_TX_USER_SW_EV_EN_WIDTH 1 +#define FSF_AZ_TX_USER_CONT_LBN 46 +#define FSF_AZ_TX_USER_CONT_WIDTH 1 +#define FSF_AZ_TX_USER_BYTE_CNT_LBN 33 +#define FSF_AZ_TX_USER_BYTE_CNT_WIDTH 13 +#define FSF_AZ_TX_USER_BUF_ID_LBN 13 +#define FSF_AZ_TX_USER_BUF_ID_WIDTH 20 +#define FSF_AZ_TX_USER_BYTE_OFS_LBN 0 +#define FSF_AZ_TX_USER_BYTE_OFS_WIDTH 13 + +/* USER_EV */ +#define FSF_CZ_USER_QID_LBN 32 +#define FSF_CZ_USER_QID_WIDTH 10 +#define FSF_CZ_USER_EV_REG_VALUE_LBN 0 +#define FSF_CZ_USER_EV_REG_VALUE_WIDTH 32 + +/************************************************************************** + * + * Falcon B0 PCIe core indirect registers + * + ************************************************************************** + */ + +#define FPCR_BB_PCIE_DEVICE_CTRL_STAT 0x68 + +#define FPCR_BB_PCIE_LINK_CTRL_STAT 0x70 + +#define FPCR_BB_ACK_RPL_TIMER 0x700 +#define FPCRF_BB_ACK_TL_LBN 0 +#define FPCRF_BB_ACK_TL_WIDTH 16 +#define FPCRF_BB_RPL_TL_LBN 16 +#define FPCRF_BB_RPL_TL_WIDTH 16 + +#define FPCR_BB_ACK_FREQ 0x70C +#define FPCRF_BB_ACK_FREQ_LBN 0 +#define FPCRF_BB_ACK_FREQ_WIDTH 7 + +/************************************************************************** + * + * Pseudo-registers and fields + * + ************************************************************************** + */ + +/* Interrupt acknowledge work-around register (A0/A1 only) */ +#define FR_AA_WORK_AROUND_BROKEN_PCI_READS 0x0070 + +/* EE_SPI_HCMD_REG: SPI host command register */ +/* Values for the EE_SPI_HCMD_SF_SEL register field */ +#define FFE_AB_SPI_DEVICE_EEPROM 0 +#define FFE_AB_SPI_DEVICE_FLASH 1 + +/* NIC_STAT_REG: NIC status register */ +#define FRF_AB_STRAP_10G_LBN 2 +#define FRF_AB_STRAP_10G_WIDTH 1 +#define FRF_AA_STRAP_PCIE_LBN 0 +#define FRF_AA_STRAP_PCIE_WIDTH 1 + +/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */ +#define FRF_AZ_FATAL_INTR_LBN 0 +#define FRF_AZ_FATAL_INTR_WIDTH 12 + +/* SRM_CFG_REG: SRAM configuration register */ +/* We treat the number of SRAM banks and bank size as a single field */ +#define FRF_AZ_SRM_NB_SZ_LBN FRF_AZ_SRM_BANK_SIZE_LBN +#define FRF_AZ_SRM_NB_SZ_WIDTH \ + (FRF_AZ_SRM_BANK_SIZE_WIDTH + FRF_AZ_SRM_NUM_BANK_WIDTH) +#define FFE_AB_SRM_NB1_SZ2M 0 +#define FFE_AB_SRM_NB1_SZ4M 1 +#define FFE_AB_SRM_NB1_SZ8M 2 +#define FFE_AB_SRM_NB_SZ_DEF 3 +#define FFE_AB_SRM_NB2_SZ4M 4 +#define FFE_AB_SRM_NB2_SZ8M 5 +#define FFE_AB_SRM_NB2_SZ16M 6 +#define FFE_AB_SRM_NB_SZ_RES 7 + +/* RX_DESC_UPD_REGP0: Receive descriptor update register. */ +/* We write just the last dword of these registers */ +#define FR_AZ_RX_DESC_UPD_DWORD_P0 \ + (BUILD_BUG_ON_ZERO(FR_AA_RX_DESC_UPD_KER != FR_BZ_RX_DESC_UPD_P0) + \ + FR_BZ_RX_DESC_UPD_P0 + 3 * 4) +#define FRF_AZ_RX_DESC_WPTR_DWORD_LBN (FRF_AZ_RX_DESC_WPTR_LBN - 3 * 32) +#define FRF_AZ_RX_DESC_WPTR_DWORD_WIDTH FRF_AZ_RX_DESC_WPTR_WIDTH + +/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */ +#define FR_AZ_TX_DESC_UPD_DWORD_P0 \ + (BUILD_BUG_ON_ZERO(FR_AA_TX_DESC_UPD_KER != FR_BZ_TX_DESC_UPD_P0) + \ + FR_BZ_TX_DESC_UPD_P0 + 3 * 4) +#define FRF_AZ_TX_DESC_WPTR_DWORD_LBN (FRF_AZ_TX_DESC_WPTR_LBN - 3 * 32) +#define FRF_AZ_TX_DESC_WPTR_DWORD_WIDTH FRF_AZ_TX_DESC_WPTR_WIDTH + +/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */ +#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_LBN 12 +#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_WIDTH 1 + +/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */ +#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_LBN 12 +#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1 + +/* XM_TX_PARAM_REG: XGMAC transmit parameter register */ +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LBN FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN +#define FRF_AB_XM_MAX_TX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH + \ + FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH) + +/* XM_RX_PARAM_REG: XGMAC receive parameter register */ +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LBN FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN +#define FRF_AB_XM_MAX_RX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH + \ + FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH) + +/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */ +/* Default values */ +#define FFE_AB_XX_TXDRV_DEQ_DEF 0xe /* deq=.6 */ +#define FFE_AB_XX_TXDRV_DTX_DEF 0x5 /* 1.25 */ +#define FFE_AB_XX_SD_CTL_DRV_DEF 0 /* 20mA */ + +/* XX_CORE_STAT_REG: XAUI XGXS core status register */ +/* XGXS all-lanes status fields */ +#define FRF_AB_XX_SYNC_STAT_LBN FRF_AB_XX_SYNC_STAT0_LBN +#define FRF_AB_XX_SYNC_STAT_WIDTH 4 +#define FRF_AB_XX_COMMA_DET_LBN FRF_AB_XX_COMMA_DET_CH0_LBN +#define FRF_AB_XX_COMMA_DET_WIDTH 4 +#define FRF_AB_XX_CHAR_ERR_LBN FRF_AB_XX_CHAR_ERR_CH0_LBN +#define FRF_AB_XX_CHAR_ERR_WIDTH 4 +#define FRF_AB_XX_DISPERR_LBN FRF_AB_XX_DISPERR_CH0_LBN +#define FRF_AB_XX_DISPERR_WIDTH 4 +#define FFE_AB_XX_STAT_ALL_LANES 0xf +#define FRF_AB_XX_FORCE_SIG_LBN FRF_AB_XX_FORCE_SIG0_VAL_LBN +#define FRF_AB_XX_FORCE_SIG_WIDTH 8 +#define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff + +/* RX_MAC_FILTER_TBL0 */ +/* RMFT_DEST_MAC is wider than 32 bits */ +#define FRF_CZ_RMFT_DEST_MAC_LO_LBN FRF_CZ_RMFT_DEST_MAC_LBN +#define FRF_CZ_RMFT_DEST_MAC_LO_WIDTH 32 +#define FRF_CZ_RMFT_DEST_MAC_HI_LBN (FRF_CZ_RMFT_DEST_MAC_LBN + 32) +#define FRF_CZ_RMFT_DEST_MAC_HI_WIDTH (FRF_CZ_RMFT_DEST_MAC_WIDTH - 32) + +/* TX_MAC_FILTER_TBL0 */ +/* TMFT_SRC_MAC is wider than 32 bits */ +#define FRF_CZ_TMFT_SRC_MAC_LO_LBN FRF_CZ_TMFT_SRC_MAC_LBN +#define FRF_CZ_TMFT_SRC_MAC_LO_WIDTH 32 +#define FRF_CZ_TMFT_SRC_MAC_HI_LBN (FRF_CZ_TMFT_SRC_MAC_LBN + 32) +#define FRF_CZ_TMFT_SRC_MAC_HI_WIDTH (FRF_CZ_TMFT_SRC_MAC_WIDTH - 32) + +/* TX_PACE_TBL */ +/* Values >20 are documented as reserved, but will result in a queue going + * into the fast bin with a pace value of zero. */ +#define FFE_BZ_TX_PACE_OFF 0 +#define FFE_BZ_TX_PACE_RESERVED 21 + +/* DRIVER_EV */ +/* Sub-fields of an RX flush completion event */ +#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12 +#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1 +#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_LBN 0 +#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_WIDTH 12 + +/* EVENT_ENTRY */ +/* Magic number field for event test */ +#define FSF_AZ_DRV_GEN_EV_MAGIC_LBN 0 +#define FSF_AZ_DRV_GEN_EV_MAGIC_WIDTH 32 + +/* RX packet prefix */ +#define FS_BZ_RX_PREFIX_HASH_OFST 12 +#define FS_BZ_RX_PREFIX_SIZE 16 + +#endif /* EF4_FARCH_REGS_H */ diff --git a/drivers/net/ethernet/sfc/falcon/filter.h b/drivers/net/ethernet/sfc/falcon/filter.h new file mode 100644 index 0000000..647f6b2 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/filter.h @@ -0,0 +1,272 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_FILTER_H +#define EF4_FILTER_H + +#include +#include +#include + +/** + * enum ef4_filter_match_flags - Flags for hardware filter match type + * @EF4_FILTER_MATCH_REM_HOST: Match by remote IP host address + * @EF4_FILTER_MATCH_LOC_HOST: Match by local IP host address + * @EF4_FILTER_MATCH_REM_MAC: Match by remote MAC address + * @EF4_FILTER_MATCH_REM_PORT: Match by remote TCP/UDP port + * @EF4_FILTER_MATCH_LOC_MAC: Match by local MAC address + * @EF4_FILTER_MATCH_LOC_PORT: Match by local TCP/UDP port + * @EF4_FILTER_MATCH_ETHER_TYPE: Match by Ether-type + * @EF4_FILTER_MATCH_INNER_VID: Match by inner VLAN ID + * @EF4_FILTER_MATCH_OUTER_VID: Match by outer VLAN ID + * @EF4_FILTER_MATCH_IP_PROTO: Match by IP transport protocol + * @EF4_FILTER_MATCH_LOC_MAC_IG: Match by local MAC address I/G bit. + * Used for RX default unicast and multicast/broadcast filters. + * + * Only some combinations are supported, depending on NIC type: + * + * - Falcon supports RX filters matching by {TCP,UDP}/IPv4 4-tuple or + * local 2-tuple (only implemented for Falcon B0) + * + * - Siena supports RX and TX filters matching by {TCP,UDP}/IPv4 4-tuple + * or local 2-tuple, or local MAC with or without outer VID, and RX + * default filters + * + * - Huntington supports filter matching controlled by firmware, potentially + * using {TCP,UDP}/IPv{4,6} 4-tuple or local 2-tuple, local MAC or I/G bit, + * with or without outer and inner VID + */ +enum ef4_filter_match_flags { + EF4_FILTER_MATCH_REM_HOST = 0x0001, + EF4_FILTER_MATCH_LOC_HOST = 0x0002, + EF4_FILTER_MATCH_REM_MAC = 0x0004, + EF4_FILTER_MATCH_REM_PORT = 0x0008, + EF4_FILTER_MATCH_LOC_MAC = 0x0010, + EF4_FILTER_MATCH_LOC_PORT = 0x0020, + EF4_FILTER_MATCH_ETHER_TYPE = 0x0040, + EF4_FILTER_MATCH_INNER_VID = 0x0080, + EF4_FILTER_MATCH_OUTER_VID = 0x0100, + EF4_FILTER_MATCH_IP_PROTO = 0x0200, + EF4_FILTER_MATCH_LOC_MAC_IG = 0x0400, +}; + +/** + * enum ef4_filter_priority - priority of a hardware filter specification + * @EF4_FILTER_PRI_HINT: Performance hint + * @EF4_FILTER_PRI_AUTO: Automatic filter based on device address list + * or hardware requirements. This may only be used by the filter + * implementation for each NIC type. + * @EF4_FILTER_PRI_MANUAL: Manually configured filter + * @EF4_FILTER_PRI_REQUIRED: Required for correct behaviour (user-level + * networking and SR-IOV) + */ +enum ef4_filter_priority { + EF4_FILTER_PRI_HINT = 0, + EF4_FILTER_PRI_AUTO, + EF4_FILTER_PRI_MANUAL, + EF4_FILTER_PRI_REQUIRED, +}; + +/** + * enum ef4_filter_flags - flags for hardware filter specifications + * @EF4_FILTER_FLAG_RX_RSS: Use RSS to spread across multiple queues. + * By default, matching packets will be delivered only to the + * specified queue. If this flag is set, they will be delivered + * to a range of queues offset from the specified queue number + * according to the indirection table. + * @EF4_FILTER_FLAG_RX_SCATTER: Enable DMA scatter on the receiving + * queue. + * @EF4_FILTER_FLAG_RX_OVER_AUTO: Indicates a filter that is + * overriding an automatic filter (priority + * %EF4_FILTER_PRI_AUTO). This may only be set by the filter + * implementation for each type. A removal request will restore + * the automatic filter in its place. + * @EF4_FILTER_FLAG_RX: Filter is for RX + * @EF4_FILTER_FLAG_TX: Filter is for TX + */ +enum ef4_filter_flags { + EF4_FILTER_FLAG_RX_RSS = 0x01, + EF4_FILTER_FLAG_RX_SCATTER = 0x02, + EF4_FILTER_FLAG_RX_OVER_AUTO = 0x04, + EF4_FILTER_FLAG_RX = 0x08, + EF4_FILTER_FLAG_TX = 0x10, +}; + +/** + * struct ef4_filter_spec - specification for a hardware filter + * @match_flags: Match type flags, from &enum ef4_filter_match_flags + * @priority: Priority of the filter, from &enum ef4_filter_priority + * @flags: Miscellaneous flags, from &enum ef4_filter_flags + * @rss_context: RSS context to use, if %EF4_FILTER_FLAG_RX_RSS is set + * @dmaq_id: Source/target queue index, or %EF4_FILTER_RX_DMAQ_ID_DROP for + * an RX drop filter + * @outer_vid: Outer VLAN ID to match, if %EF4_FILTER_MATCH_OUTER_VID is set + * @inner_vid: Inner VLAN ID to match, if %EF4_FILTER_MATCH_INNER_VID is set + * @loc_mac: Local MAC address to match, if %EF4_FILTER_MATCH_LOC_MAC or + * %EF4_FILTER_MATCH_LOC_MAC_IG is set + * @rem_mac: Remote MAC address to match, if %EF4_FILTER_MATCH_REM_MAC is set + * @ether_type: Ether-type to match, if %EF4_FILTER_MATCH_ETHER_TYPE is set + * @ip_proto: IP transport protocol to match, if %EF4_FILTER_MATCH_IP_PROTO + * is set + * @loc_host: Local IP host to match, if %EF4_FILTER_MATCH_LOC_HOST is set + * @rem_host: Remote IP host to match, if %EF4_FILTER_MATCH_REM_HOST is set + * @loc_port: Local TCP/UDP port to match, if %EF4_FILTER_MATCH_LOC_PORT is set + * @rem_port: Remote TCP/UDP port to match, if %EF4_FILTER_MATCH_REM_PORT is set + * + * The ef4_filter_init_rx() or ef4_filter_init_tx() function *must* be + * used to initialise the structure. The ef4_filter_set_*() functions + * may then be used to set @rss_context, @match_flags and related + * fields. + * + * The @priority field is used by software to determine whether a new + * filter may replace an old one. The hardware priority of a filter + * depends on which fields are matched. + */ +struct ef4_filter_spec { + u32 match_flags:12; + u32 priority:2; + u32 flags:6; + u32 dmaq_id:12; + u32 rss_context; + __be16 outer_vid __aligned(4); /* allow jhash2() of match values */ + __be16 inner_vid; + u8 loc_mac[ETH_ALEN]; + u8 rem_mac[ETH_ALEN]; + __be16 ether_type; + u8 ip_proto; + __be32 loc_host[4]; + __be32 rem_host[4]; + __be16 loc_port; + __be16 rem_port; + /* total 64 bytes */ +}; + +enum { + EF4_FILTER_RSS_CONTEXT_DEFAULT = 0xffffffff, + EF4_FILTER_RX_DMAQ_ID_DROP = 0xfff +}; + +static inline void ef4_filter_init_rx(struct ef4_filter_spec *spec, + enum ef4_filter_priority priority, + enum ef4_filter_flags flags, + unsigned rxq_id) +{ + memset(spec, 0, sizeof(*spec)); + spec->priority = priority; + spec->flags = EF4_FILTER_FLAG_RX | flags; + spec->rss_context = EF4_FILTER_RSS_CONTEXT_DEFAULT; + spec->dmaq_id = rxq_id; +} + +static inline void ef4_filter_init_tx(struct ef4_filter_spec *spec, + unsigned txq_id) +{ + memset(spec, 0, sizeof(*spec)); + spec->priority = EF4_FILTER_PRI_REQUIRED; + spec->flags = EF4_FILTER_FLAG_TX; + spec->dmaq_id = txq_id; +} + +/** + * ef4_filter_set_ipv4_local - specify IPv4 host, transport protocol and port + * @spec: Specification to initialise + * @proto: Transport layer protocol number + * @host: Local host address (network byte order) + * @port: Local port (network byte order) + */ +static inline int +ef4_filter_set_ipv4_local(struct ef4_filter_spec *spec, u8 proto, + __be32 host, __be16 port) +{ + spec->match_flags |= + EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_LOC_PORT; + spec->ether_type = htons(ETH_P_IP); + spec->ip_proto = proto; + spec->loc_host[0] = host; + spec->loc_port = port; + return 0; +} + +/** + * ef4_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports + * @spec: Specification to initialise + * @proto: Transport layer protocol number + * @lhost: Local host address (network byte order) + * @lport: Local port (network byte order) + * @rhost: Remote host address (network byte order) + * @rport: Remote port (network byte order) + */ +static inline int +ef4_filter_set_ipv4_full(struct ef4_filter_spec *spec, u8 proto, + __be32 lhost, __be16 lport, + __be32 rhost, __be16 rport) +{ + spec->match_flags |= + EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_LOC_PORT | + EF4_FILTER_MATCH_REM_HOST | EF4_FILTER_MATCH_REM_PORT; + spec->ether_type = htons(ETH_P_IP); + spec->ip_proto = proto; + spec->loc_host[0] = lhost; + spec->loc_port = lport; + spec->rem_host[0] = rhost; + spec->rem_port = rport; + return 0; +} + +enum { + EF4_FILTER_VID_UNSPEC = 0xffff, +}; + +/** + * ef4_filter_set_eth_local - specify local Ethernet address and/or VID + * @spec: Specification to initialise + * @vid: Outer VLAN ID to match, or %EF4_FILTER_VID_UNSPEC + * @addr: Local Ethernet MAC address, or %NULL + */ +static inline int ef4_filter_set_eth_local(struct ef4_filter_spec *spec, + u16 vid, const u8 *addr) +{ + if (vid == EF4_FILTER_VID_UNSPEC && addr == NULL) + return -EINVAL; + + if (vid != EF4_FILTER_VID_UNSPEC) { + spec->match_flags |= EF4_FILTER_MATCH_OUTER_VID; + spec->outer_vid = htons(vid); + } + if (addr != NULL) { + spec->match_flags |= EF4_FILTER_MATCH_LOC_MAC; + ether_addr_copy(spec->loc_mac, addr); + } + return 0; +} + +/** + * ef4_filter_set_uc_def - specify matching otherwise-unmatched unicast + * @spec: Specification to initialise + */ +static inline int ef4_filter_set_uc_def(struct ef4_filter_spec *spec) +{ + spec->match_flags |= EF4_FILTER_MATCH_LOC_MAC_IG; + return 0; +} + +/** + * ef4_filter_set_mc_def - specify matching otherwise-unmatched multicast + * @spec: Specification to initialise + */ +static inline int ef4_filter_set_mc_def(struct ef4_filter_spec *spec) +{ + spec->match_flags |= EF4_FILTER_MATCH_LOC_MAC_IG; + spec->loc_mac[0] = 1; + return 0; +} + +#endif /* EF4_FILTER_H */ diff --git a/drivers/net/ethernet/sfc/falcon/io.h b/drivers/net/ethernet/sfc/falcon/io.h new file mode 100644 index 0000000..7085ee1 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/io.h @@ -0,0 +1,290 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_IO_H +#define EF4_IO_H + +#include +#include + +/************************************************************************** + * + * NIC register I/O + * + ************************************************************************** + * + * Notes on locking strategy for the Falcon architecture: + * + * Many CSRs are very wide and cannot be read or written atomically. + * Writes from the host are buffered by the Bus Interface Unit (BIU) + * up to 128 bits. Whenever the host writes part of such a register, + * the BIU collects the written value and does not write to the + * underlying register until all 4 dwords have been written. A + * similar buffering scheme applies to host access to the NIC's 64-bit + * SRAM. + * + * Writes to different CSRs and 64-bit SRAM words must be serialised, + * since interleaved access can result in lost writes. We use + * ef4_nic::biu_lock for this. + * + * We also serialise reads from 128-bit CSRs and SRAM with the same + * spinlock. This may not be necessary, but it doesn't really matter + * as there are no such reads on the fast path. + * + * The DMA descriptor pointers (RX_DESC_UPD and TX_DESC_UPD) are + * 128-bit but are special-cased in the BIU to avoid the need for + * locking in the host: + * + * - They are write-only. + * - The semantics of writing to these registers are such that + * replacing the low 96 bits with zero does not affect functionality. + * - If the host writes to the last dword address of such a register + * (i.e. the high 32 bits) the underlying register will always be + * written. If the collector and the current write together do not + * provide values for all 128 bits of the register, the low 96 bits + * will be written as zero. + * - If the host writes to the address of any other part of such a + * register while the collector already holds values for some other + * register, the write is discarded and the collector maintains its + * current state. + * + * The EF10 architecture exposes very few registers to the host and + * most of them are only 32 bits wide. The only exceptions are the MC + * doorbell register pair, which has its own latching, and + * TX_DESC_UPD, which works in a similar way to the Falcon + * architecture. + */ + +#if BITS_PER_LONG == 64 +#define EF4_USE_QWORD_IO 1 +#endif + +#ifdef EF4_USE_QWORD_IO +static inline void _ef4_writeq(struct ef4_nic *efx, __le64 value, + unsigned int reg) +{ + __raw_writeq((__force u64)value, efx->membase + reg); +} +static inline __le64 _ef4_readq(struct ef4_nic *efx, unsigned int reg) +{ + return (__force __le64)__raw_readq(efx->membase + reg); +} +#endif + +static inline void _ef4_writed(struct ef4_nic *efx, __le32 value, + unsigned int reg) +{ + __raw_writel((__force u32)value, efx->membase + reg); +} +static inline __le32 _ef4_readd(struct ef4_nic *efx, unsigned int reg) +{ + return (__force __le32)__raw_readl(efx->membase + reg); +} + +/* Write a normal 128-bit CSR, locking as appropriate. */ +static inline void ef4_writeo(struct ef4_nic *efx, const ef4_oword_t *value, + unsigned int reg) +{ + unsigned long flags __attribute__ ((unused)); + + netif_vdbg(efx, hw, efx->net_dev, + "writing register %x with " EF4_OWORD_FMT "\n", reg, + EF4_OWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EF4_USE_QWORD_IO + _ef4_writeq(efx, value->u64[0], reg + 0); + _ef4_writeq(efx, value->u64[1], reg + 8); +#else + _ef4_writed(efx, value->u32[0], reg + 0); + _ef4_writed(efx, value->u32[1], reg + 4); + _ef4_writed(efx, value->u32[2], reg + 8); + _ef4_writed(efx, value->u32[3], reg + 12); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write 64-bit SRAM through the supplied mapping, locking as appropriate. */ +static inline void ef4_sram_writeq(struct ef4_nic *efx, void __iomem *membase, + const ef4_qword_t *value, unsigned int index) +{ + unsigned int addr = index * sizeof(*value); + unsigned long flags __attribute__ ((unused)); + + netif_vdbg(efx, hw, efx->net_dev, + "writing SRAM address %x with " EF4_QWORD_FMT "\n", + addr, EF4_QWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EF4_USE_QWORD_IO + __raw_writeq((__force u64)value->u64[0], membase + addr); +#else + __raw_writel((__force u32)value->u32[0], membase + addr); + __raw_writel((__force u32)value->u32[1], membase + addr + 4); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write a 32-bit CSR or the last dword of a special 128-bit CSR */ +static inline void ef4_writed(struct ef4_nic *efx, const ef4_dword_t *value, + unsigned int reg) +{ + netif_vdbg(efx, hw, efx->net_dev, + "writing register %x with "EF4_DWORD_FMT"\n", + reg, EF4_DWORD_VAL(*value)); + + /* No lock required */ + _ef4_writed(efx, value->u32[0], reg); +} + +/* Read a 128-bit CSR, locking as appropriate. */ +static inline void ef4_reado(struct ef4_nic *efx, ef4_oword_t *value, + unsigned int reg) +{ + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); + value->u32[0] = _ef4_readd(efx, reg + 0); + value->u32[1] = _ef4_readd(efx, reg + 4); + value->u32[2] = _ef4_readd(efx, reg + 8); + value->u32[3] = _ef4_readd(efx, reg + 12); + spin_unlock_irqrestore(&efx->biu_lock, flags); + + netif_vdbg(efx, hw, efx->net_dev, + "read from register %x, got " EF4_OWORD_FMT "\n", reg, + EF4_OWORD_VAL(*value)); +} + +/* Read 64-bit SRAM through the supplied mapping, locking as appropriate. */ +static inline void ef4_sram_readq(struct ef4_nic *efx, void __iomem *membase, + ef4_qword_t *value, unsigned int index) +{ + unsigned int addr = index * sizeof(*value); + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EF4_USE_QWORD_IO + value->u64[0] = (__force __le64)__raw_readq(membase + addr); +#else + value->u32[0] = (__force __le32)__raw_readl(membase + addr); + value->u32[1] = (__force __le32)__raw_readl(membase + addr + 4); +#endif + spin_unlock_irqrestore(&efx->biu_lock, flags); + + netif_vdbg(efx, hw, efx->net_dev, + "read from SRAM address %x, got "EF4_QWORD_FMT"\n", + addr, EF4_QWORD_VAL(*value)); +} + +/* Read a 32-bit CSR or SRAM */ +static inline void ef4_readd(struct ef4_nic *efx, ef4_dword_t *value, + unsigned int reg) +{ + value->u32[0] = _ef4_readd(efx, reg); + netif_vdbg(efx, hw, efx->net_dev, + "read from register %x, got "EF4_DWORD_FMT"\n", + reg, EF4_DWORD_VAL(*value)); +} + +/* Write a 128-bit CSR forming part of a table */ +static inline void +ef4_writeo_table(struct ef4_nic *efx, const ef4_oword_t *value, + unsigned int reg, unsigned int index) +{ + ef4_writeo(efx, value, reg + index * sizeof(ef4_oword_t)); +} + +/* Read a 128-bit CSR forming part of a table */ +static inline void ef4_reado_table(struct ef4_nic *efx, ef4_oword_t *value, + unsigned int reg, unsigned int index) +{ + ef4_reado(efx, value, reg + index * sizeof(ef4_oword_t)); +} + +/* Page size used as step between per-VI registers */ +#define EF4_VI_PAGE_SIZE 0x2000 + +/* Calculate offset to page-mapped register */ +#define EF4_PAGED_REG(page, reg) \ + ((page) * EF4_VI_PAGE_SIZE + (reg)) + +/* Write the whole of RX_DESC_UPD or TX_DESC_UPD */ +static inline void _ef4_writeo_page(struct ef4_nic *efx, ef4_oword_t *value, + unsigned int reg, unsigned int page) +{ + reg = EF4_PAGED_REG(page, reg); + + netif_vdbg(efx, hw, efx->net_dev, + "writing register %x with " EF4_OWORD_FMT "\n", reg, + EF4_OWORD_VAL(*value)); + +#ifdef EF4_USE_QWORD_IO + _ef4_writeq(efx, value->u64[0], reg + 0); + _ef4_writeq(efx, value->u64[1], reg + 8); +#else + _ef4_writed(efx, value->u32[0], reg + 0); + _ef4_writed(efx, value->u32[1], reg + 4); + _ef4_writed(efx, value->u32[2], reg + 8); + _ef4_writed(efx, value->u32[3], reg + 12); +#endif +} +#define ef4_writeo_page(efx, value, reg, page) \ + _ef4_writeo_page(efx, value, \ + reg + \ + BUILD_BUG_ON_ZERO((reg) != 0x830 && (reg) != 0xa10), \ + page) + +/* Write a page-mapped 32-bit CSR (EVQ_RPTR, EVQ_TMR (EF10), or the + * high bits of RX_DESC_UPD or TX_DESC_UPD) + */ +static inline void +_ef4_writed_page(struct ef4_nic *efx, const ef4_dword_t *value, + unsigned int reg, unsigned int page) +{ + ef4_writed(efx, value, EF4_PAGED_REG(page, reg)); +} +#define ef4_writed_page(efx, value, reg, page) \ + _ef4_writed_page(efx, value, \ + reg + \ + BUILD_BUG_ON_ZERO((reg) != 0x400 && \ + (reg) != 0x420 && \ + (reg) != 0x830 && \ + (reg) != 0x83c && \ + (reg) != 0xa18 && \ + (reg) != 0xa1c), \ + page) + +/* Write TIMER_COMMAND. This is a page-mapped 32-bit CSR, but a bug + * in the BIU means that writes to TIMER_COMMAND[0] invalidate the + * collector register. + */ +static inline void _ef4_writed_page_locked(struct ef4_nic *efx, + const ef4_dword_t *value, + unsigned int reg, + unsigned int page) +{ + unsigned long flags __attribute__ ((unused)); + + if (page == 0) { + spin_lock_irqsave(&efx->biu_lock, flags); + ef4_writed(efx, value, EF4_PAGED_REG(page, reg)); + spin_unlock_irqrestore(&efx->biu_lock, flags); + } else { + ef4_writed(efx, value, EF4_PAGED_REG(page, reg)); + } +} +#define ef4_writed_page_locked(efx, value, reg, page) \ + _ef4_writed_page_locked(efx, value, \ + reg + BUILD_BUG_ON_ZERO((reg) != 0x420), \ + page) + +#endif /* EF4_IO_H */ diff --git a/drivers/net/ethernet/sfc/falcon/mdio_10g.c b/drivers/net/ethernet/sfc/falcon/mdio_10g.c new file mode 100644 index 0000000..e7d7c09 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/mdio_10g.c @@ -0,0 +1,323 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2006-2011 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ +/* + * Useful functions for working with MDIO clause 45 PHYs + */ +#include +#include +#include +#include "net_driver.h" +#include "mdio_10g.h" +#include "workarounds.h" + +unsigned ef4_mdio_id_oui(u32 id) +{ + unsigned oui = 0; + int i; + + /* The bits of the OUI are designated a..x, with a=0 and b variable. + * In the id register c is the MSB but the OUI is conventionally + * written as bytes h..a, p..i, x..q. Reorder the bits accordingly. */ + for (i = 0; i < 22; ++i) + if (id & (1 << (i + 10))) + oui |= 1 << (i ^ 7); + + return oui; +} + +int ef4_mdio_reset_mmd(struct ef4_nic *port, int mmd, + int spins, int spintime) +{ + u32 ctrl; + + /* Catch callers passing values in the wrong units (or just silly) */ + EF4_BUG_ON_PARANOID(spins * spintime >= 5000); + + ef4_mdio_write(port, mmd, MDIO_CTRL1, MDIO_CTRL1_RESET); + /* Wait for the reset bit to clear. */ + do { + msleep(spintime); + ctrl = ef4_mdio_read(port, mmd, MDIO_CTRL1); + spins--; + + } while (spins && (ctrl & MDIO_CTRL1_RESET)); + + return spins ? spins : -ETIMEDOUT; +} + +static int ef4_mdio_check_mmd(struct ef4_nic *efx, int mmd) +{ + int status; + + if (mmd != MDIO_MMD_AN) { + /* Read MMD STATUS2 to check it is responding. */ + status = ef4_mdio_read(efx, mmd, MDIO_STAT2); + if ((status & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL) { + netif_err(efx, hw, efx->net_dev, + "PHY MMD %d not responding.\n", mmd); + return -EIO; + } + } + + return 0; +} + +/* This ought to be ridiculous overkill. We expect it to fail rarely */ +#define MDIO45_RESET_TIME 1000 /* ms */ +#define MDIO45_RESET_ITERS 100 + +int ef4_mdio_wait_reset_mmds(struct ef4_nic *efx, unsigned int mmd_mask) +{ + const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS; + int tries = MDIO45_RESET_ITERS; + int rc = 0; + int in_reset; + + while (tries) { + int mask = mmd_mask; + int mmd = 0; + int stat; + in_reset = 0; + while (mask) { + if (mask & 1) { + stat = ef4_mdio_read(efx, mmd, MDIO_CTRL1); + if (stat < 0) { + netif_err(efx, hw, efx->net_dev, + "failed to read status of" + " MMD %d\n", mmd); + return -EIO; + } + if (stat & MDIO_CTRL1_RESET) + in_reset |= (1 << mmd); + } + mask = mask >> 1; + mmd++; + } + if (!in_reset) + break; + tries--; + msleep(spintime); + } + if (in_reset != 0) { + netif_err(efx, hw, efx->net_dev, + "not all MMDs came out of reset in time." + " MMDs still in reset: %x\n", in_reset); + rc = -ETIMEDOUT; + } + return rc; +} + +int ef4_mdio_check_mmds(struct ef4_nic *efx, unsigned int mmd_mask) +{ + int mmd = 0, probe_mmd, devs1, devs2; + u32 devices; + + /* Historically we have probed the PHYXS to find out what devices are + * present,but that doesn't work so well if the PHYXS isn't expected + * to exist, if so just find the first item in the list supplied. */ + probe_mmd = (mmd_mask & MDIO_DEVS_PHYXS) ? MDIO_MMD_PHYXS : + __ffs(mmd_mask); + + /* Check all the expected MMDs are present */ + devs1 = ef4_mdio_read(efx, probe_mmd, MDIO_DEVS1); + devs2 = ef4_mdio_read(efx, probe_mmd, MDIO_DEVS2); + if (devs1 < 0 || devs2 < 0) { + netif_err(efx, hw, efx->net_dev, + "failed to read devices present\n"); + return -EIO; + } + devices = devs1 | (devs2 << 16); + if ((devices & mmd_mask) != mmd_mask) { + netif_err(efx, hw, efx->net_dev, + "required MMDs not present: got %x, wanted %x\n", + devices, mmd_mask); + return -ENODEV; + } + netif_vdbg(efx, hw, efx->net_dev, "Devices present: %x\n", devices); + + /* Check all required MMDs are responding and happy. */ + while (mmd_mask) { + if ((mmd_mask & 1) && ef4_mdio_check_mmd(efx, mmd)) + return -EIO; + mmd_mask = mmd_mask >> 1; + mmd++; + } + + return 0; +} + +bool ef4_mdio_links_ok(struct ef4_nic *efx, unsigned int mmd_mask) +{ + /* If the port is in loopback, then we should only consider a subset + * of mmd's */ + if (LOOPBACK_INTERNAL(efx)) + return true; + else if (LOOPBACK_MASK(efx) & LOOPBACKS_WS) + return false; + else if (ef4_phy_mode_disabled(efx->phy_mode)) + return false; + else if (efx->loopback_mode == LOOPBACK_PHYXS) + mmd_mask &= ~(MDIO_DEVS_PHYXS | + MDIO_DEVS_PCS | + MDIO_DEVS_PMAPMD | + MDIO_DEVS_AN); + else if (efx->loopback_mode == LOOPBACK_PCS) + mmd_mask &= ~(MDIO_DEVS_PCS | + MDIO_DEVS_PMAPMD | + MDIO_DEVS_AN); + else if (efx->loopback_mode == LOOPBACK_PMAPMD) + mmd_mask &= ~(MDIO_DEVS_PMAPMD | + MDIO_DEVS_AN); + + return mdio45_links_ok(&efx->mdio, mmd_mask); +} + +void ef4_mdio_transmit_disable(struct ef4_nic *efx) +{ + ef4_mdio_set_flag(efx, MDIO_MMD_PMAPMD, + MDIO_PMA_TXDIS, MDIO_PMD_TXDIS_GLOBAL, + efx->phy_mode & PHY_MODE_TX_DISABLED); +} + +void ef4_mdio_phy_reconfigure(struct ef4_nic *efx) +{ + ef4_mdio_set_flag(efx, MDIO_MMD_PMAPMD, + MDIO_CTRL1, MDIO_PMA_CTRL1_LOOPBACK, + efx->loopback_mode == LOOPBACK_PMAPMD); + ef4_mdio_set_flag(efx, MDIO_MMD_PCS, + MDIO_CTRL1, MDIO_PCS_CTRL1_LOOPBACK, + efx->loopback_mode == LOOPBACK_PCS); + ef4_mdio_set_flag(efx, MDIO_MMD_PHYXS, + MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK, + efx->loopback_mode == LOOPBACK_PHYXS_WS); +} + +static void ef4_mdio_set_mmd_lpower(struct ef4_nic *efx, + int lpower, int mmd) +{ + int stat = ef4_mdio_read(efx, mmd, MDIO_STAT1); + + netif_vdbg(efx, drv, efx->net_dev, "Setting low power mode for MMD %d to %d\n", + mmd, lpower); + + if (stat & MDIO_STAT1_LPOWERABLE) { + ef4_mdio_set_flag(efx, mmd, MDIO_CTRL1, + MDIO_CTRL1_LPOWER, lpower); + } +} + +void ef4_mdio_set_mmds_lpower(struct ef4_nic *efx, + int low_power, unsigned int mmd_mask) +{ + int mmd = 0; + mmd_mask &= ~MDIO_DEVS_AN; + while (mmd_mask) { + if (mmd_mask & 1) + ef4_mdio_set_mmd_lpower(efx, low_power, mmd); + mmd_mask = (mmd_mask >> 1); + mmd++; + } +} + +/** + * ef4_mdio_set_settings - Set (some of) the PHY settings over MDIO. + * @efx: Efx NIC + * @ecmd: New settings + */ +int ef4_mdio_set_settings(struct ef4_nic *efx, struct ethtool_cmd *ecmd) +{ + struct ethtool_cmd prev = { .cmd = ETHTOOL_GSET }; + + efx->phy_op->get_settings(efx, &prev); + + if (ecmd->advertising == prev.advertising && + ethtool_cmd_speed(ecmd) == ethtool_cmd_speed(&prev) && + ecmd->duplex == prev.duplex && + ecmd->port == prev.port && + ecmd->autoneg == prev.autoneg) + return 0; + + /* We can only change these settings for -T PHYs */ + if (prev.port != PORT_TP || ecmd->port != PORT_TP) + return -EINVAL; + + /* Check that PHY supports these settings */ + if (!ecmd->autoneg || + (ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported) + return -EINVAL; + + ef4_link_set_advertising(efx, ecmd->advertising | ADVERTISED_Autoneg); + ef4_mdio_an_reconfigure(efx); + return 0; +} + +/** + * ef4_mdio_an_reconfigure - Push advertising flags and restart autonegotiation + * @efx: Efx NIC + */ +void ef4_mdio_an_reconfigure(struct ef4_nic *efx) +{ + int reg; + + WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); + + /* Set up the base page */ + reg = ADVERTISE_CSMA | ADVERTISE_RESV; + if (efx->link_advertising & ADVERTISED_Pause) + reg |= ADVERTISE_PAUSE_CAP; + if (efx->link_advertising & ADVERTISED_Asym_Pause) + reg |= ADVERTISE_PAUSE_ASYM; + ef4_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); + + /* Set up the (extended) next page */ + efx->phy_op->set_npage_adv(efx, efx->link_advertising); + + /* Enable and restart AN */ + reg = ef4_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); + reg |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART | MDIO_AN_CTRL1_XNP; + ef4_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg); +} + +u8 ef4_mdio_get_pause(struct ef4_nic *efx) +{ + BUILD_BUG_ON(EF4_FC_AUTO & (EF4_FC_RX | EF4_FC_TX)); + + if (!(efx->wanted_fc & EF4_FC_AUTO)) + return efx->wanted_fc; + + WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); + + return mii_resolve_flowctrl_fdx( + mii_advertise_flowctrl(efx->wanted_fc), + ef4_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA)); +} + +int ef4_mdio_test_alive(struct ef4_nic *efx) +{ + int rc; + int devad = __ffs(efx->mdio.mmds); + u16 physid1, physid2; + + mutex_lock(&efx->mac_lock); + + physid1 = ef4_mdio_read(efx, devad, MDIO_DEVID1); + physid2 = ef4_mdio_read(efx, devad, MDIO_DEVID2); + + if ((physid1 == 0x0000) || (physid1 == 0xffff) || + (physid2 == 0x0000) || (physid2 == 0xffff)) { + netif_err(efx, hw, efx->net_dev, + "no MDIO PHY present with ID %d\n", efx->mdio.prtad); + rc = -EINVAL; + } else { + rc = ef4_mdio_check_mmds(efx, efx->mdio.mmds); + } + + mutex_unlock(&efx->mac_lock); + return rc; +} diff --git a/drivers/net/ethernet/sfc/falcon/mdio_10g.h b/drivers/net/ethernet/sfc/falcon/mdio_10g.h new file mode 100644 index 0000000..885cf7a --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/mdio_10g.h @@ -0,0 +1,110 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2006-2011 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_MDIO_10G_H +#define EF4_MDIO_10G_H + +#include + +/* + * Helper functions for doing 10G MDIO as specified in IEEE 802.3 clause 45. + */ + +#include "efx.h" + +static inline unsigned ef4_mdio_id_rev(u32 id) { return id & 0xf; } +static inline unsigned ef4_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; } +unsigned ef4_mdio_id_oui(u32 id); + +static inline int ef4_mdio_read(struct ef4_nic *efx, int devad, int addr) +{ + return efx->mdio.mdio_read(efx->net_dev, efx->mdio.prtad, devad, addr); +} + +static inline void +ef4_mdio_write(struct ef4_nic *efx, int devad, int addr, int value) +{ + efx->mdio.mdio_write(efx->net_dev, efx->mdio.prtad, devad, addr, value); +} + +static inline u32 ef4_mdio_read_id(struct ef4_nic *efx, int mmd) +{ + u16 id_low = ef4_mdio_read(efx, mmd, MDIO_DEVID2); + u16 id_hi = ef4_mdio_read(efx, mmd, MDIO_DEVID1); + return (id_hi << 16) | (id_low); +} + +static inline bool ef4_mdio_phyxgxs_lane_sync(struct ef4_nic *efx) +{ + int i, lane_status; + bool sync; + + for (i = 0; i < 2; ++i) + lane_status = ef4_mdio_read(efx, MDIO_MMD_PHYXS, + MDIO_PHYXS_LNSTAT); + + sync = !!(lane_status & MDIO_PHYXS_LNSTAT_ALIGN); + if (!sync) + netif_dbg(efx, hw, efx->net_dev, "XGXS lane status: %x\n", + lane_status); + return sync; +} + +const char *ef4_mdio_mmd_name(int mmd); + +/* + * Reset a specific MMD and wait for reset to clear. + * Return number of spins left (>0) on success, -%ETIMEDOUT on failure. + * + * This function will sleep + */ +int ef4_mdio_reset_mmd(struct ef4_nic *efx, int mmd, int spins, int spintime); + +/* As ef4_mdio_check_mmd but for multiple MMDs */ +int ef4_mdio_check_mmds(struct ef4_nic *efx, unsigned int mmd_mask); + +/* Check the link status of specified mmds in bit mask */ +bool ef4_mdio_links_ok(struct ef4_nic *efx, unsigned int mmd_mask); + +/* Generic transmit disable support though PMAPMD */ +void ef4_mdio_transmit_disable(struct ef4_nic *efx); + +/* Generic part of reconfigure: set/clear loopback bits */ +void ef4_mdio_phy_reconfigure(struct ef4_nic *efx); + +/* Set the power state of the specified MMDs */ +void ef4_mdio_set_mmds_lpower(struct ef4_nic *efx, int low_power, + unsigned int mmd_mask); + +/* Set (some of) the PHY settings over MDIO */ +int ef4_mdio_set_settings(struct ef4_nic *efx, struct ethtool_cmd *ecmd); + +/* Push advertising flags and restart autonegotiation */ +void ef4_mdio_an_reconfigure(struct ef4_nic *efx); + +/* Get pause parameters from AN if available (otherwise return + * requested pause parameters) + */ +u8 ef4_mdio_get_pause(struct ef4_nic *efx); + +/* Wait for specified MMDs to exit reset within a timeout */ +int ef4_mdio_wait_reset_mmds(struct ef4_nic *efx, unsigned int mmd_mask); + +/* Set or clear flag, debouncing */ +static inline void +ef4_mdio_set_flag(struct ef4_nic *efx, int devad, int addr, + int mask, bool state) +{ + mdio_set_flag(&efx->mdio, efx->mdio.prtad, devad, addr, mask, state); +} + +/* Liveness self-test for MDIO PHYs */ +int ef4_mdio_test_alive(struct ef4_nic *efx); + +#endif /* EF4_MDIO_10G_H */ diff --git a/drivers/net/ethernet/sfc/falcon/mtd.c b/drivers/net/ethernet/sfc/falcon/mtd.c new file mode 100644 index 0000000..cde593c --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/mtd.c @@ -0,0 +1,133 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include + +#include "net_driver.h" +#include "efx.h" + +#define to_ef4_mtd_partition(mtd) \ + container_of(mtd, struct ef4_mtd_partition, mtd) + +/* MTD interface */ + +static int ef4_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct ef4_nic *efx = mtd->priv; + int rc; + + rc = efx->type->mtd_erase(mtd, erase->addr, erase->len); + if (rc == 0) { + erase->state = MTD_ERASE_DONE; + } else { + erase->state = MTD_ERASE_FAILED; + erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + } + mtd_erase_callback(erase); + return rc; +} + +static void ef4_mtd_sync(struct mtd_info *mtd) +{ + struct ef4_mtd_partition *part = to_ef4_mtd_partition(mtd); + struct ef4_nic *efx = mtd->priv; + int rc; + + rc = efx->type->mtd_sync(mtd); + if (rc) + pr_err("%s: %s sync failed (%d)\n", + part->name, part->dev_type_name, rc); +} + +static void ef4_mtd_remove_partition(struct ef4_mtd_partition *part) +{ + int rc; + + for (;;) { + rc = mtd_device_unregister(&part->mtd); + if (rc != -EBUSY) + break; + ssleep(1); + } + WARN_ON(rc); + list_del(&part->node); +} + +int ef4_mtd_add(struct ef4_nic *efx, struct ef4_mtd_partition *parts, + size_t n_parts, size_t sizeof_part) +{ + struct ef4_mtd_partition *part; + size_t i; + + for (i = 0; i < n_parts; i++) { + part = (struct ef4_mtd_partition *)((char *)parts + + i * sizeof_part); + + part->mtd.writesize = 1; + + part->mtd.owner = THIS_MODULE; + part->mtd.priv = efx; + part->mtd.name = part->name; + part->mtd._erase = ef4_mtd_erase; + part->mtd._read = efx->type->mtd_read; + part->mtd._write = efx->type->mtd_write; + part->mtd._sync = ef4_mtd_sync; + + efx->type->mtd_rename(part); + + if (mtd_device_register(&part->mtd, NULL, 0)) + goto fail; + + /* Add to list in order - ef4_mtd_remove() depends on this */ + list_add_tail(&part->node, &efx->mtd_list); + } + + return 0; + +fail: + while (i--) { + part = (struct ef4_mtd_partition *)((char *)parts + + i * sizeof_part); + ef4_mtd_remove_partition(part); + } + /* Failure is unlikely here, but probably means we're out of memory */ + return -ENOMEM; +} + +void ef4_mtd_remove(struct ef4_nic *efx) +{ + struct ef4_mtd_partition *parts, *part, *next; + + WARN_ON(ef4_dev_registered(efx)); + + if (list_empty(&efx->mtd_list)) + return; + + parts = list_first_entry(&efx->mtd_list, struct ef4_mtd_partition, + node); + + list_for_each_entry_safe(part, next, &efx->mtd_list, node) + ef4_mtd_remove_partition(part); + + kfree(parts); +} + +void ef4_mtd_rename(struct ef4_nic *efx) +{ + struct ef4_mtd_partition *part; + + ASSERT_RTNL(); + + list_for_each_entry(part, &efx->mtd_list, node) + efx->type->mtd_rename(part); +} diff --git a/drivers/net/ethernet/sfc/falcon/net_driver.h b/drivers/net/ethernet/sfc/falcon/net_driver.h new file mode 100644 index 0000000..210b28f --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/net_driver.h @@ -0,0 +1,1464 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +/* Common definitions for all Efx net driver code */ + +#ifndef EF4_NET_DRIVER_H +#define EF4_NET_DRIVER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "enum.h" +#include "bitfield.h" +#include "filter.h" + +/************************************************************************** + * + * Build definitions + * + **************************************************************************/ + +#define EF4_DRIVER_VERSION "4.1" + +#ifdef DEBUG +#define EF4_BUG_ON_PARANOID(x) BUG_ON(x) +#define EF4_WARN_ON_PARANOID(x) WARN_ON(x) +#else +#define EF4_BUG_ON_PARANOID(x) do {} while (0) +#define EF4_WARN_ON_PARANOID(x) do {} while (0) +#endif + +/************************************************************************** + * + * Efx data structures + * + **************************************************************************/ + +#define EF4_MAX_CHANNELS 32U +#define EF4_MAX_RX_QUEUES EF4_MAX_CHANNELS +#define EF4_EXTRA_CHANNEL_IOV 0 +#define EF4_EXTRA_CHANNEL_PTP 1 +#define EF4_MAX_EXTRA_CHANNELS 2U + +/* Checksum generation is a per-queue option in hardware, so each + * queue visible to the networking core is backed by two hardware TX + * queues. */ +#define EF4_MAX_TX_TC 2 +#define EF4_MAX_CORE_TX_QUEUES (EF4_MAX_TX_TC * EF4_MAX_CHANNELS) +#define EF4_TXQ_TYPE_OFFLOAD 1 /* flag */ +#define EF4_TXQ_TYPE_HIGHPRI 2 /* flag */ +#define EF4_TXQ_TYPES 4 +#define EF4_MAX_TX_QUEUES (EF4_TXQ_TYPES * EF4_MAX_CHANNELS) + +/* Maximum possible MTU the driver supports */ +#define EF4_MAX_MTU (9 * 1024) + +/* Minimum MTU, from RFC791 (IP) */ +#define EF4_MIN_MTU 68 + +/* Size of an RX scatter buffer. Small enough to pack 2 into a 4K page, + * and should be a multiple of the cache line size. + */ +#define EF4_RX_USR_BUF_SIZE (2048 - 256) + +/* If possible, we should ensure cache line alignment at start and end + * of every buffer. Otherwise, we just need to ensure 4-byte + * alignment of the network header. + */ +#if NET_IP_ALIGN == 0 +#define EF4_RX_BUF_ALIGNMENT L1_CACHE_BYTES +#else +#define EF4_RX_BUF_ALIGNMENT 4 +#endif + +struct ef4_self_tests; + +/** + * struct ef4_buffer - A general-purpose DMA buffer + * @addr: host base address of the buffer + * @dma_addr: DMA base address of the buffer + * @len: Buffer length, in bytes + * + * The NIC uses these buffers for its interrupt status registers and + * MAC stats dumps. + */ +struct ef4_buffer { + void *addr; + dma_addr_t dma_addr; + unsigned int len; +}; + +/** + * struct ef4_special_buffer - DMA buffer entered into buffer table + * @buf: Standard &struct ef4_buffer + * @index: Buffer index within controller;s buffer table + * @entries: Number of buffer table entries + * + * The NIC has a buffer table that maps buffers of size %EF4_BUF_SIZE. + * Event and descriptor rings are addressed via one or more buffer + * table entries (and so can be physically non-contiguous, although we + * currently do not take advantage of that). On Falcon and Siena we + * have to take care of allocating and initialising the entries + * ourselves. On later hardware this is managed by the firmware and + * @index and @entries are left as 0. + */ +struct ef4_special_buffer { + struct ef4_buffer buf; + unsigned int index; + unsigned int entries; +}; + +/** + * struct ef4_tx_buffer - buffer state for a TX descriptor + * @skb: When @flags & %EF4_TX_BUF_SKB, the associated socket buffer to be + * freed when descriptor completes + * @option: When @flags & %EF4_TX_BUF_OPTION, a NIC-specific option descriptor. + * @dma_addr: DMA address of the fragment. + * @flags: Flags for allocation and DMA mapping type + * @len: Length of this fragment. + * This field is zero when the queue slot is empty. + * @unmap_len: Length of this fragment to unmap + * @dma_offset: Offset of @dma_addr from the address of the backing DMA mapping. + * Only valid if @unmap_len != 0. + */ +struct ef4_tx_buffer { + const struct sk_buff *skb; + union { + ef4_qword_t option; + dma_addr_t dma_addr; + }; + unsigned short flags; + unsigned short len; + unsigned short unmap_len; + unsigned short dma_offset; +}; +#define EF4_TX_BUF_CONT 1 /* not last descriptor of packet */ +#define EF4_TX_BUF_SKB 2 /* buffer is last part of skb */ +#define EF4_TX_BUF_MAP_SINGLE 8 /* buffer was mapped with dma_map_single() */ +#define EF4_TX_BUF_OPTION 0x10 /* empty buffer for option descriptor */ + +/** + * struct ef4_tx_queue - An Efx TX queue + * + * This is a ring buffer of TX fragments. + * Since the TX completion path always executes on the same + * CPU and the xmit path can operate on different CPUs, + * performance is increased by ensuring that the completion + * path and the xmit path operate on different cache lines. + * This is particularly important if the xmit path is always + * executing on one CPU which is different from the completion + * path. There is also a cache line for members which are + * read but not written on the fast path. + * + * @efx: The associated Efx NIC + * @queue: DMA queue number + * @channel: The associated channel + * @core_txq: The networking core TX queue structure + * @buffer: The software buffer ring + * @cb_page: Array of pages of copy buffers. Carved up according to + * %EF4_TX_CB_ORDER into %EF4_TX_CB_SIZE-sized chunks. + * @txd: The hardware descriptor ring + * @ptr_mask: The size of the ring minus 1. + * @initialised: Has hardware queue been initialised? + * @tx_min_size: Minimum transmit size for this queue. Depends on HW. + * @read_count: Current read pointer. + * This is the number of buffers that have been removed from both rings. + * @old_write_count: The value of @write_count when last checked. + * This is here for performance reasons. The xmit path will + * only get the up-to-date value of @write_count if this + * variable indicates that the queue is empty. This is to + * avoid cache-line ping-pong between the xmit path and the + * completion path. + * @merge_events: Number of TX merged completion events + * @insert_count: Current insert pointer + * This is the number of buffers that have been added to the + * software ring. + * @write_count: Current write pointer + * This is the number of buffers that have been added to the + * hardware ring. + * @old_read_count: The value of read_count when last checked. + * This is here for performance reasons. The xmit path will + * only get the up-to-date value of read_count if this + * variable indicates that the queue is full. This is to + * avoid cache-line ping-pong between the xmit path and the + * completion path. + * @pushes: Number of times the TX push feature has been used + * @xmit_more_available: Are any packets waiting to be pushed to the NIC + * @cb_packets: Number of times the TX copybreak feature has been used + * @empty_read_count: If the completion path has seen the queue as empty + * and the transmission path has not yet checked this, the value of + * @read_count bitwise-added to %EF4_EMPTY_COUNT_VALID; otherwise 0. + */ +struct ef4_tx_queue { + /* Members which don't change on the fast path */ + struct ef4_nic *efx ____cacheline_aligned_in_smp; + unsigned queue; + struct ef4_channel *channel; + struct netdev_queue *core_txq; + struct ef4_tx_buffer *buffer; + struct ef4_buffer *cb_page; + struct ef4_special_buffer txd; + unsigned int ptr_mask; + bool initialised; + unsigned int tx_min_size; + + /* Function pointers used in the fast path. */ + int (*handle_tso)(struct ef4_tx_queue*, struct sk_buff*, bool *); + + /* Members used mainly on the completion path */ + unsigned int read_count ____cacheline_aligned_in_smp; + unsigned int old_write_count; + unsigned int merge_events; + unsigned int bytes_compl; + unsigned int pkts_compl; + + /* Members used only on the xmit path */ + unsigned int insert_count ____cacheline_aligned_in_smp; + unsigned int write_count; + unsigned int old_read_count; + unsigned int pushes; + bool xmit_more_available; + unsigned int cb_packets; + /* Statistics to supplement MAC stats */ + unsigned long tx_packets; + + /* Members shared between paths and sometimes updated */ + unsigned int empty_read_count ____cacheline_aligned_in_smp; +#define EF4_EMPTY_COUNT_VALID 0x80000000 + atomic_t flush_outstanding; +}; + +#define EF4_TX_CB_ORDER 7 +#define EF4_TX_CB_SIZE (1 << EF4_TX_CB_ORDER) - NET_IP_ALIGN + +/** + * struct ef4_rx_buffer - An Efx RX data buffer + * @dma_addr: DMA base address of the buffer + * @page: The associated page buffer. + * Will be %NULL if the buffer slot is currently free. + * @page_offset: If pending: offset in @page of DMA base address. + * If completed: offset in @page of Ethernet header. + * @len: If pending: length for DMA descriptor. + * If completed: received length, excluding hash prefix. + * @flags: Flags for buffer and packet state. These are only set on the + * first buffer of a scattered packet. + */ +struct ef4_rx_buffer { + dma_addr_t dma_addr; + struct page *page; + u16 page_offset; + u16 len; + u16 flags; +}; +#define EF4_RX_BUF_LAST_IN_PAGE 0x0001 +#define EF4_RX_PKT_CSUMMED 0x0002 +#define EF4_RX_PKT_DISCARD 0x0004 +#define EF4_RX_PKT_TCP 0x0040 +#define EF4_RX_PKT_PREFIX_LEN 0x0080 /* length is in prefix only */ + +/** + * struct ef4_rx_page_state - Page-based rx buffer state + * + * Inserted at the start of every page allocated for receive buffers. + * Used to facilitate sharing dma mappings between recycled rx buffers + * and those passed up to the kernel. + * + * @dma_addr: The dma address of this page. + */ +struct ef4_rx_page_state { + dma_addr_t dma_addr; + + unsigned int __pad[0] ____cacheline_aligned; +}; + +/** + * struct ef4_rx_queue - An Efx RX queue + * @efx: The associated Efx NIC + * @core_index: Index of network core RX queue. Will be >= 0 iff this + * is associated with a real RX queue. + * @buffer: The software buffer ring + * @rxd: The hardware descriptor ring + * @ptr_mask: The size of the ring minus 1. + * @refill_enabled: Enable refill whenever fill level is low + * @flush_pending: Set when a RX flush is pending. Has the same lifetime as + * @rxq_flush_pending. + * @added_count: Number of buffers added to the receive queue. + * @notified_count: Number of buffers given to NIC (<= @added_count). + * @removed_count: Number of buffers removed from the receive queue. + * @scatter_n: Used by NIC specific receive code. + * @scatter_len: Used by NIC specific receive code. + * @page_ring: The ring to store DMA mapped pages for reuse. + * @page_add: Counter to calculate the write pointer for the recycle ring. + * @page_remove: Counter to calculate the read pointer for the recycle ring. + * @page_recycle_count: The number of pages that have been recycled. + * @page_recycle_failed: The number of pages that couldn't be recycled because + * the kernel still held a reference to them. + * @page_recycle_full: The number of pages that were released because the + * recycle ring was full. + * @page_ptr_mask: The number of pages in the RX recycle ring minus 1. + * @max_fill: RX descriptor maximum fill level (<= ring size) + * @fast_fill_trigger: RX descriptor fill level that will trigger a fast fill + * (<= @max_fill) + * @min_fill: RX descriptor minimum non-zero fill level. + * This records the minimum fill level observed when a ring + * refill was triggered. + * @recycle_count: RX buffer recycle counter. + * @slow_fill: Timer used to defer ef4_nic_generate_fill_event(). + */ +struct ef4_rx_queue { + struct ef4_nic *efx; + int core_index; + struct ef4_rx_buffer *buffer; + struct ef4_special_buffer rxd; + unsigned int ptr_mask; + bool refill_enabled; + bool flush_pending; + + unsigned int added_count; + unsigned int notified_count; + unsigned int removed_count; + unsigned int scatter_n; + unsigned int scatter_len; + struct page **page_ring; + unsigned int page_add; + unsigned int page_remove; + unsigned int page_recycle_count; + unsigned int page_recycle_failed; + unsigned int page_recycle_full; + unsigned int page_ptr_mask; + unsigned int max_fill; + unsigned int fast_fill_trigger; + unsigned int min_fill; + unsigned int min_overfill; + unsigned int recycle_count; + struct timer_list slow_fill; + unsigned int slow_fill_count; + /* Statistics to supplement MAC stats */ + unsigned long rx_packets; +}; + +/** + * struct ef4_channel - An Efx channel + * + * A channel comprises an event queue, at least one TX queue, at least + * one RX queue, and an associated tasklet for processing the event + * queue. + * + * @efx: Associated Efx NIC + * @channel: Channel instance number + * @type: Channel type definition + * @eventq_init: Event queue initialised flag + * @enabled: Channel enabled indicator + * @irq: IRQ number (MSI and MSI-X only) + * @irq_moderation_us: IRQ moderation value (in microseconds) + * @napi_dev: Net device used with NAPI + * @napi_str: NAPI control structure + * @state: state for NAPI vs busy polling + * @state_lock: lock protecting @state + * @eventq: Event queue buffer + * @eventq_mask: Event queue pointer mask + * @eventq_read_ptr: Event queue read pointer + * @event_test_cpu: Last CPU to handle interrupt or test event for this channel + * @irq_count: Number of IRQs since last adaptive moderation decision + * @irq_mod_score: IRQ moderation score + * @rps_flow_id: Flow IDs of filters allocated for accelerated RFS, + * indexed by filter ID + * @n_rx_tobe_disc: Count of RX_TOBE_DISC errors + * @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors + * @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors + * @n_rx_mcast_mismatch: Count of unmatched multicast frames + * @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors + * @n_rx_overlength: Count of RX_OVERLENGTH errors + * @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun + * @n_rx_nodesc_trunc: Number of RX packets truncated and then dropped due to + * lack of descriptors + * @n_rx_merge_events: Number of RX merged completion events + * @n_rx_merge_packets: Number of RX packets completed by merged events + * @rx_pkt_n_frags: Number of fragments in next packet to be delivered by + * __ef4_rx_packet(), or zero if there is none + * @rx_pkt_index: Ring index of first buffer for next packet to be delivered + * by __ef4_rx_packet(), if @rx_pkt_n_frags != 0 + * @rx_queue: RX queue for this channel + * @tx_queue: TX queues for this channel + */ +struct ef4_channel { + struct ef4_nic *efx; + int channel; + const struct ef4_channel_type *type; + bool eventq_init; + bool enabled; + int irq; + unsigned int irq_moderation_us; + struct net_device *napi_dev; + struct napi_struct napi_str; +#ifdef CONFIG_NET_RX_BUSY_POLL + unsigned long busy_poll_state; +#endif + struct ef4_special_buffer eventq; + unsigned int eventq_mask; + unsigned int eventq_read_ptr; + int event_test_cpu; + + unsigned int irq_count; + unsigned int irq_mod_score; +#ifdef CONFIG_RFS_ACCEL + unsigned int rfs_filters_added; +#define RPS_FLOW_ID_INVALID 0xFFFFFFFF + u32 *rps_flow_id; +#endif + + unsigned n_rx_tobe_disc; + unsigned n_rx_ip_hdr_chksum_err; + unsigned n_rx_tcp_udp_chksum_err; + unsigned n_rx_mcast_mismatch; + unsigned n_rx_frm_trunc; + unsigned n_rx_overlength; + unsigned n_skbuff_leaks; + unsigned int n_rx_nodesc_trunc; + unsigned int n_rx_merge_events; + unsigned int n_rx_merge_packets; + + unsigned int rx_pkt_n_frags; + unsigned int rx_pkt_index; + + struct ef4_rx_queue rx_queue; + struct ef4_tx_queue tx_queue[EF4_TXQ_TYPES]; +}; + +#ifdef CONFIG_NET_RX_BUSY_POLL +enum ef4_channel_busy_poll_state { + EF4_CHANNEL_STATE_IDLE = 0, + EF4_CHANNEL_STATE_NAPI = BIT(0), + EF4_CHANNEL_STATE_NAPI_REQ_BIT = 1, + EF4_CHANNEL_STATE_NAPI_REQ = BIT(1), + EF4_CHANNEL_STATE_POLL_BIT = 2, + EF4_CHANNEL_STATE_POLL = BIT(2), + EF4_CHANNEL_STATE_DISABLE_BIT = 3, +}; + +static inline void ef4_channel_busy_poll_init(struct ef4_channel *channel) +{ + WRITE_ONCE(channel->busy_poll_state, EF4_CHANNEL_STATE_IDLE); +} + +/* Called from the device poll routine to get ownership of a channel. */ +static inline bool ef4_channel_lock_napi(struct ef4_channel *channel) +{ + unsigned long prev, old = READ_ONCE(channel->busy_poll_state); + + while (1) { + switch (old) { + case EF4_CHANNEL_STATE_POLL: + /* Ensure ef4_channel_try_lock_poll() wont starve us */ + set_bit(EF4_CHANNEL_STATE_NAPI_REQ_BIT, + &channel->busy_poll_state); + /* fallthrough */ + case EF4_CHANNEL_STATE_POLL | EF4_CHANNEL_STATE_NAPI_REQ: + return false; + default: + break; + } + prev = cmpxchg(&channel->busy_poll_state, old, + EF4_CHANNEL_STATE_NAPI); + if (unlikely(prev != old)) { + /* This is likely to mean we've just entered polling + * state. Go back round to set the REQ bit. + */ + old = prev; + continue; + } + return true; + } +} + +static inline void ef4_channel_unlock_napi(struct ef4_channel *channel) +{ + /* Make sure write has completed from ef4_channel_lock_napi() */ + smp_wmb(); + WRITE_ONCE(channel->busy_poll_state, EF4_CHANNEL_STATE_IDLE); +} + +/* Called from ef4_busy_poll(). */ +static inline bool ef4_channel_try_lock_poll(struct ef4_channel *channel) +{ + return cmpxchg(&channel->busy_poll_state, EF4_CHANNEL_STATE_IDLE, + EF4_CHANNEL_STATE_POLL) == EF4_CHANNEL_STATE_IDLE; +} + +static inline void ef4_channel_unlock_poll(struct ef4_channel *channel) +{ + clear_bit_unlock(EF4_CHANNEL_STATE_POLL_BIT, &channel->busy_poll_state); +} + +static inline bool ef4_channel_busy_polling(struct ef4_channel *channel) +{ + return test_bit(EF4_CHANNEL_STATE_POLL_BIT, &channel->busy_poll_state); +} + +static inline void ef4_channel_enable(struct ef4_channel *channel) +{ + clear_bit_unlock(EF4_CHANNEL_STATE_DISABLE_BIT, + &channel->busy_poll_state); +} + +/* Stop further polling or napi access. + * Returns false if the channel is currently busy polling. + */ +static inline bool ef4_channel_disable(struct ef4_channel *channel) +{ + set_bit(EF4_CHANNEL_STATE_DISABLE_BIT, &channel->busy_poll_state); + /* Implicit barrier in ef4_channel_busy_polling() */ + return !ef4_channel_busy_polling(channel); +} + +#else /* CONFIG_NET_RX_BUSY_POLL */ + +static inline void ef4_channel_busy_poll_init(struct ef4_channel *channel) +{ +} + +static inline bool ef4_channel_lock_napi(struct ef4_channel *channel) +{ + return true; +} + +static inline void ef4_channel_unlock_napi(struct ef4_channel *channel) +{ +} + +static inline bool ef4_channel_try_lock_poll(struct ef4_channel *channel) +{ + return false; +} + +static inline void ef4_channel_unlock_poll(struct ef4_channel *channel) +{ +} + +static inline bool ef4_channel_busy_polling(struct ef4_channel *channel) +{ + return false; +} + +static inline void ef4_channel_enable(struct ef4_channel *channel) +{ +} + +static inline bool ef4_channel_disable(struct ef4_channel *channel) +{ + return true; +} +#endif /* CONFIG_NET_RX_BUSY_POLL */ + +/** + * struct ef4_msi_context - Context for each MSI + * @efx: The associated NIC + * @index: Index of the channel/IRQ + * @name: Name of the channel/IRQ + * + * Unlike &struct ef4_channel, this is never reallocated and is always + * safe for the IRQ handler to access. + */ +struct ef4_msi_context { + struct ef4_nic *efx; + unsigned int index; + char name[IFNAMSIZ + 6]; +}; + +/** + * struct ef4_channel_type - distinguishes traffic and extra channels + * @handle_no_channel: Handle failure to allocate an extra channel + * @pre_probe: Set up extra state prior to initialisation + * @post_remove: Tear down extra state after finalisation, if allocated. + * May be called on channels that have not been probed. + * @get_name: Generate the channel's name (used for its IRQ handler) + * @copy: Copy the channel state prior to reallocation. May be %NULL if + * reallocation is not supported. + * @receive_skb: Handle an skb ready to be passed to netif_receive_skb() + * @keep_eventq: Flag for whether event queue should be kept initialised + * while the device is stopped + */ +struct ef4_channel_type { + void (*handle_no_channel)(struct ef4_nic *); + int (*pre_probe)(struct ef4_channel *); + void (*post_remove)(struct ef4_channel *); + void (*get_name)(struct ef4_channel *, char *buf, size_t len); + struct ef4_channel *(*copy)(const struct ef4_channel *); + bool (*receive_skb)(struct ef4_channel *, struct sk_buff *); + bool keep_eventq; +}; + +enum ef4_led_mode { + EF4_LED_OFF = 0, + EF4_LED_ON = 1, + EF4_LED_DEFAULT = 2 +}; + +#define STRING_TABLE_LOOKUP(val, member) \ + ((val) < member ## _max) ? member ## _names[val] : "(invalid)" + +extern const char *const ef4_loopback_mode_names[]; +extern const unsigned int ef4_loopback_mode_max; +#define LOOPBACK_MODE(efx) \ + STRING_TABLE_LOOKUP((efx)->loopback_mode, ef4_loopback_mode) + +extern const char *const ef4_reset_type_names[]; +extern const unsigned int ef4_reset_type_max; +#define RESET_TYPE(type) \ + STRING_TABLE_LOOKUP(type, ef4_reset_type) + +enum ef4_int_mode { + /* Be careful if altering to correct macro below */ + EF4_INT_MODE_MSIX = 0, + EF4_INT_MODE_MSI = 1, + EF4_INT_MODE_LEGACY = 2, + EF4_INT_MODE_MAX /* Insert any new items before this */ +}; +#define EF4_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EF4_INT_MODE_MSI) + +enum nic_state { + STATE_UNINIT = 0, /* device being probed/removed or is frozen */ + STATE_READY = 1, /* hardware ready and netdev registered */ + STATE_DISABLED = 2, /* device disabled due to hardware errors */ + STATE_RECOVERY = 3, /* device recovering from PCI error */ +}; + +/* Forward declaration */ +struct ef4_nic; + +/* Pseudo bit-mask flow control field */ +#define EF4_FC_RX FLOW_CTRL_RX +#define EF4_FC_TX FLOW_CTRL_TX +#define EF4_FC_AUTO 4 + +/** + * struct ef4_link_state - Current state of the link + * @up: Link is up + * @fd: Link is full-duplex + * @fc: Actual flow control flags + * @speed: Link speed (Mbps) + */ +struct ef4_link_state { + bool up; + bool fd; + u8 fc; + unsigned int speed; +}; + +static inline bool ef4_link_state_equal(const struct ef4_link_state *left, + const struct ef4_link_state *right) +{ + return left->up == right->up && left->fd == right->fd && + left->fc == right->fc && left->speed == right->speed; +} + +/** + * struct ef4_phy_operations - Efx PHY operations table + * @probe: Probe PHY and initialise efx->mdio.mode_support, efx->mdio.mmds, + * efx->loopback_modes. + * @init: Initialise PHY + * @fini: Shut down PHY + * @reconfigure: Reconfigure PHY (e.g. for new link parameters) + * @poll: Update @link_state and report whether it changed. + * Serialised by the mac_lock. + * @get_settings: Get ethtool settings. Serialised by the mac_lock. + * @set_settings: Set ethtool settings. Serialised by the mac_lock. + * @set_npage_adv: Set abilities advertised in (Extended) Next Page + * (only needed where AN bit is set in mmds) + * @test_alive: Test that PHY is 'alive' (online) + * @test_name: Get the name of a PHY-specific test/result + * @run_tests: Run tests and record results as appropriate (offline). + * Flags are the ethtool tests flags. + */ +struct ef4_phy_operations { + int (*probe) (struct ef4_nic *efx); + int (*init) (struct ef4_nic *efx); + void (*fini) (struct ef4_nic *efx); + void (*remove) (struct ef4_nic *efx); + int (*reconfigure) (struct ef4_nic *efx); + bool (*poll) (struct ef4_nic *efx); + void (*get_settings) (struct ef4_nic *efx, + struct ethtool_cmd *ecmd); + int (*set_settings) (struct ef4_nic *efx, + struct ethtool_cmd *ecmd); + void (*set_npage_adv) (struct ef4_nic *efx, u32); + int (*test_alive) (struct ef4_nic *efx); + const char *(*test_name) (struct ef4_nic *efx, unsigned int index); + int (*run_tests) (struct ef4_nic *efx, int *results, unsigned flags); + int (*get_module_eeprom) (struct ef4_nic *efx, + struct ethtool_eeprom *ee, + u8 *data); + int (*get_module_info) (struct ef4_nic *efx, + struct ethtool_modinfo *modinfo); +}; + +/** + * enum ef4_phy_mode - PHY operating mode flags + * @PHY_MODE_NORMAL: on and should pass traffic + * @PHY_MODE_TX_DISABLED: on with TX disabled + * @PHY_MODE_LOW_POWER: set to low power through MDIO + * @PHY_MODE_OFF: switched off through external control + * @PHY_MODE_SPECIAL: on but will not pass traffic + */ +enum ef4_phy_mode { + PHY_MODE_NORMAL = 0, + PHY_MODE_TX_DISABLED = 1, + PHY_MODE_LOW_POWER = 2, + PHY_MODE_OFF = 4, + PHY_MODE_SPECIAL = 8, +}; + +static inline bool ef4_phy_mode_disabled(enum ef4_phy_mode mode) +{ + return !!(mode & ~PHY_MODE_TX_DISABLED); +} + +/** + * struct ef4_hw_stat_desc - Description of a hardware statistic + * @name: Name of the statistic as visible through ethtool, or %NULL if + * it should not be exposed + * @dma_width: Width in bits (0 for non-DMA statistics) + * @offset: Offset within stats (ignored for non-DMA statistics) + */ +struct ef4_hw_stat_desc { + const char *name; + u16 dma_width; + u16 offset; +}; + +/* Number of bits used in a multicast filter hash address */ +#define EF4_MCAST_HASH_BITS 8 + +/* Number of (single-bit) entries in a multicast filter hash */ +#define EF4_MCAST_HASH_ENTRIES (1 << EF4_MCAST_HASH_BITS) + +/* An Efx multicast filter hash */ +union ef4_multicast_hash { + u8 byte[EF4_MCAST_HASH_ENTRIES / 8]; + ef4_oword_t oword[EF4_MCAST_HASH_ENTRIES / sizeof(ef4_oword_t) / 8]; +}; + +/** + * struct ef4_nic - an Efx NIC + * @name: Device name (net device name or bus id before net device registered) + * @pci_dev: The PCI device + * @node: List node for maintaning primary/secondary function lists + * @primary: &struct ef4_nic instance for the primary function of this + * controller. May be the same structure, and may be %NULL if no + * primary function is bound. Serialised by rtnl_lock. + * @secondary_list: List of &struct ef4_nic instances for the secondary PCI + * functions of the controller, if this is for the primary function. + * Serialised by rtnl_lock. + * @type: Controller type attributes + * @legacy_irq: IRQ number + * @workqueue: Workqueue for port reconfigures and the HW monitor. + * Work items do not hold and must not acquire RTNL. + * @workqueue_name: Name of workqueue + * @reset_work: Scheduled reset workitem + * @membase_phys: Memory BAR value as physical address + * @membase: Memory BAR value + * @interrupt_mode: Interrupt mode + * @timer_quantum_ns: Interrupt timer quantum, in nanoseconds + * @timer_max_ns: Interrupt timer maximum value, in nanoseconds + * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues + * @irq_rx_mod_step_us: Step size for IRQ moderation for RX event queues + * @irq_rx_moderation_us: IRQ moderation time for RX event queues + * @msg_enable: Log message enable flags + * @state: Device state number (%STATE_*). Serialised by the rtnl_lock. + * @reset_pending: Bitmask for pending resets + * @tx_queue: TX DMA queues + * @rx_queue: RX DMA queues + * @channel: Channels + * @msi_context: Context for each MSI + * @extra_channel_types: Types of extra (non-traffic) channels that + * should be allocated for this NIC + * @rxq_entries: Size of receive queues requested by user. + * @txq_entries: Size of transmit queues requested by user. + * @txq_stop_thresh: TX queue fill level at or above which we stop it. + * @txq_wake_thresh: TX queue fill level at or below which we wake it. + * @tx_dc_base: Base qword address in SRAM of TX queue descriptor caches + * @rx_dc_base: Base qword address in SRAM of RX queue descriptor caches + * @sram_lim_qw: Qword address limit of SRAM + * @next_buffer_table: First available buffer table id + * @n_channels: Number of channels in use + * @n_rx_channels: Number of channels used for RX (= number of RX queues) + * @n_tx_channels: Number of channels used for TX + * @rx_ip_align: RX DMA address offset to have IP header aligned in + * in accordance with NET_IP_ALIGN + * @rx_dma_len: Current maximum RX DMA length + * @rx_buffer_order: Order (log2) of number of pages for each RX buffer + * @rx_buffer_truesize: Amortised allocation size of an RX buffer, + * for use in sk_buff::truesize + * @rx_prefix_size: Size of RX prefix before packet data + * @rx_packet_hash_offset: Offset of RX flow hash from start of packet data + * (valid only if @rx_prefix_size != 0; always negative) + * @rx_packet_len_offset: Offset of RX packet length from start of packet data + * (valid only for NICs that set %EF4_RX_PKT_PREFIX_LEN; always negative) + * @rx_packet_ts_offset: Offset of timestamp from start of packet data + * (valid only if channel->sync_timestamps_enabled; always negative) + * @rx_hash_key: Toeplitz hash key for RSS + * @rx_indir_table: Indirection table for RSS + * @rx_scatter: Scatter mode enabled for receives + * @int_error_count: Number of internal errors seen recently + * @int_error_expire: Time at which error count will be expired + * @irq_soft_enabled: Are IRQs soft-enabled? If not, IRQ handler will + * acknowledge but do nothing else. + * @irq_status: Interrupt status buffer + * @irq_zero_count: Number of legacy IRQs seen with queue flags == 0 + * @irq_level: IRQ level/index for IRQs not triggered by an event queue + * @selftest_work: Work item for asynchronous self-test + * @mtd_list: List of MTDs attached to the NIC + * @nic_data: Hardware dependent state + * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode, + * ef4_monitor() and ef4_reconfigure_port() + * @port_enabled: Port enabled indicator. + * Serialises ef4_stop_all(), ef4_start_all(), ef4_monitor() and + * ef4_mac_work() with kernel interfaces. Safe to read under any + * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must + * be held to modify it. + * @port_initialized: Port initialized? + * @net_dev: Operating system network device. Consider holding the rtnl lock + * @fixed_features: Features which cannot be turned off + * @stats_buffer: DMA buffer for statistics + * @phy_type: PHY type + * @phy_op: PHY interface + * @phy_data: PHY private data (including PHY-specific stats) + * @mdio: PHY MDIO interface + * @phy_mode: PHY operating mode. Serialised by @mac_lock. + * @link_advertising: Autonegotiation advertising flags + * @link_state: Current state of the link + * @n_link_state_changes: Number of times the link has changed state + * @unicast_filter: Flag for Falcon-arch simple unicast filter. + * Protected by @mac_lock. + * @multicast_hash: Multicast hash table for Falcon-arch. + * Protected by @mac_lock. + * @wanted_fc: Wanted flow control flags + * @fc_disable: When non-zero flow control is disabled. Typically used to + * ensure that network back pressure doesn't delay dma queue flushes. + * Serialised by the rtnl lock. + * @mac_work: Work item for changing MAC promiscuity and multicast hash + * @loopback_mode: Loopback status + * @loopback_modes: Supported loopback mode bitmask + * @loopback_selftest: Offline self-test private state + * @filter_sem: Filter table rw_semaphore, for freeing the table + * @filter_lock: Filter table lock, for mere content changes + * @filter_state: Architecture-dependent filter table state + * @rps_expire_channel: Next channel to check for expiry + * @rps_expire_index: Next index to check for expiry in + * @rps_expire_channel's @rps_flow_id + * @active_queues: Count of RX and TX queues that haven't been flushed and drained. + * @rxq_flush_pending: Count of number of receive queues that need to be flushed. + * Decremented when the ef4_flush_rx_queue() is called. + * @rxq_flush_outstanding: Count of number of RX flushes started but not yet + * completed (either success or failure). Not used when MCDI is used to + * flush receive queues. + * @flush_wq: wait queue used by ef4_nic_flush_queues() to wait for flush completions. + * @vpd_sn: Serial number read from VPD + * @monitor_work: Hardware monitor workitem + * @biu_lock: BIU (bus interface unit) lock + * @last_irq_cpu: Last CPU to handle a possible test interrupt. This + * field is used by ef4_test_interrupts() to verify that an + * interrupt has occurred. + * @stats_lock: Statistics update lock. Must be held when calling + * ef4_nic_type::{update,start,stop}_stats. + * @n_rx_noskb_drops: Count of RX packets dropped due to failure to allocate an skb + * + * This is stored in the private area of the &struct net_device. + */ +struct ef4_nic { + /* The following fields should be written very rarely */ + + char name[IFNAMSIZ]; + struct list_head node; + struct ef4_nic *primary; + struct list_head secondary_list; + struct pci_dev *pci_dev; + unsigned int port_num; + const struct ef4_nic_type *type; + int legacy_irq; + bool eeh_disabled_legacy_irq; + struct workqueue_struct *workqueue; + char workqueue_name[16]; + struct work_struct reset_work; + resource_size_t membase_phys; + void __iomem *membase; + + enum ef4_int_mode interrupt_mode; + unsigned int timer_quantum_ns; + unsigned int timer_max_ns; + bool irq_rx_adaptive; + unsigned int irq_mod_step_us; + unsigned int irq_rx_moderation_us; + u32 msg_enable; + + enum nic_state state; + unsigned long reset_pending; + + struct ef4_channel *channel[EF4_MAX_CHANNELS]; + struct ef4_msi_context msi_context[EF4_MAX_CHANNELS]; + const struct ef4_channel_type * + extra_channel_type[EF4_MAX_EXTRA_CHANNELS]; + + unsigned rxq_entries; + unsigned txq_entries; + unsigned int txq_stop_thresh; + unsigned int txq_wake_thresh; + + unsigned tx_dc_base; + unsigned rx_dc_base; + unsigned sram_lim_qw; + unsigned next_buffer_table; + + unsigned int max_channels; + unsigned int max_tx_channels; + unsigned n_channels; + unsigned n_rx_channels; + unsigned rss_spread; + unsigned tx_channel_offset; + unsigned n_tx_channels; + unsigned int rx_ip_align; + unsigned int rx_dma_len; + unsigned int rx_buffer_order; + unsigned int rx_buffer_truesize; + unsigned int rx_page_buf_step; + unsigned int rx_bufs_per_page; + unsigned int rx_pages_per_batch; + unsigned int rx_prefix_size; + int rx_packet_hash_offset; + int rx_packet_len_offset; + int rx_packet_ts_offset; + u8 rx_hash_key[40]; + u32 rx_indir_table[128]; + bool rx_scatter; + + unsigned int_error_count; + unsigned long int_error_expire; + + bool irq_soft_enabled; + struct ef4_buffer irq_status; + unsigned irq_zero_count; + unsigned irq_level; + struct delayed_work selftest_work; + +#ifdef CONFIG_SFC_FALCON_MTD + struct list_head mtd_list; +#endif + + void *nic_data; + + struct mutex mac_lock; + struct work_struct mac_work; + bool port_enabled; + + bool mc_bist_for_other_fn; + bool port_initialized; + struct net_device *net_dev; + + netdev_features_t fixed_features; + + struct ef4_buffer stats_buffer; + u64 rx_nodesc_drops_total; + u64 rx_nodesc_drops_while_down; + bool rx_nodesc_drops_prev_state; + + unsigned int phy_type; + const struct ef4_phy_operations *phy_op; + void *phy_data; + struct mdio_if_info mdio; + enum ef4_phy_mode phy_mode; + + u32 link_advertising; + struct ef4_link_state link_state; + unsigned int n_link_state_changes; + + bool unicast_filter; + union ef4_multicast_hash multicast_hash; + u8 wanted_fc; + unsigned fc_disable; + + atomic_t rx_reset; + enum ef4_loopback_mode loopback_mode; + u64 loopback_modes; + + void *loopback_selftest; + + struct rw_semaphore filter_sem; + spinlock_t filter_lock; + void *filter_state; +#ifdef CONFIG_RFS_ACCEL + unsigned int rps_expire_channel; + unsigned int rps_expire_index; +#endif + + atomic_t active_queues; + atomic_t rxq_flush_pending; + atomic_t rxq_flush_outstanding; + wait_queue_head_t flush_wq; + + char *vpd_sn; + + /* The following fields may be written more often */ + + struct delayed_work monitor_work ____cacheline_aligned_in_smp; + spinlock_t biu_lock; + int last_irq_cpu; + spinlock_t stats_lock; + atomic_t n_rx_noskb_drops; +}; + +static inline int ef4_dev_registered(struct ef4_nic *efx) +{ + return efx->net_dev->reg_state == NETREG_REGISTERED; +} + +static inline unsigned int ef4_port_num(struct ef4_nic *efx) +{ + return efx->port_num; +} + +struct ef4_mtd_partition { + struct list_head node; + struct mtd_info mtd; + const char *dev_type_name; + const char *type_name; + char name[IFNAMSIZ + 20]; +}; + +/** + * struct ef4_nic_type - Efx device type definition + * @mem_bar: Get the memory BAR + * @mem_map_size: Get memory BAR mapped size + * @probe: Probe the controller + * @remove: Free resources allocated by probe() + * @init: Initialise the controller + * @dimension_resources: Dimension controller resources (buffer table, + * and VIs once the available interrupt resources are clear) + * @fini: Shut down the controller + * @monitor: Periodic function for polling link state and hardware monitor + * @map_reset_reason: Map ethtool reset reason to a reset method + * @map_reset_flags: Map ethtool reset flags to a reset method, if possible + * @reset: Reset the controller hardware and possibly the PHY. This will + * be called while the controller is uninitialised. + * @probe_port: Probe the MAC and PHY + * @remove_port: Free resources allocated by probe_port() + * @handle_global_event: Handle a "global" event (may be %NULL) + * @fini_dmaq: Flush and finalise DMA queues (RX and TX queues) + * @prepare_flush: Prepare the hardware for flushing the DMA queues + * (for Falcon architecture) + * @finish_flush: Clean up after flushing the DMA queues (for Falcon + * architecture) + * @prepare_flr: Prepare for an FLR + * @finish_flr: Clean up after an FLR + * @describe_stats: Describe statistics for ethtool + * @update_stats: Update statistics not provided by event handling. + * Either argument may be %NULL. + * @start_stats: Start the regular fetching of statistics + * @pull_stats: Pull stats from the NIC and wait until they arrive. + * @stop_stats: Stop the regular fetching of statistics + * @set_id_led: Set state of identifying LED or revert to automatic function + * @push_irq_moderation: Apply interrupt moderation value + * @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY + * @prepare_enable_fc_tx: Prepare MAC to enable pause frame TX (may be %NULL) + * @reconfigure_mac: Push MAC address, MTU, flow control and filter settings + * to the hardware. Serialised by the mac_lock. + * @check_mac_fault: Check MAC fault state. True if fault present. + * @get_wol: Get WoL configuration from driver state + * @set_wol: Push WoL configuration to the NIC + * @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume) + * @test_chip: Test registers. May use ef4_farch_test_registers(), and is + * expected to reset the NIC. + * @test_nvram: Test validity of NVRAM contents + * @irq_enable_master: Enable IRQs on the NIC. Each event queue must + * be separately enabled after this. + * @irq_test_generate: Generate a test IRQ + * @irq_disable_non_ev: Disable non-event IRQs on the NIC. Each event + * queue must be separately disabled before this. + * @irq_handle_msi: Handle MSI for a channel. The @dev_id argument is + * a pointer to the &struct ef4_msi_context for the channel. + * @irq_handle_legacy: Handle legacy interrupt. The @dev_id argument + * is a pointer to the &struct ef4_nic. + * @tx_probe: Allocate resources for TX queue + * @tx_init: Initialise TX queue on the NIC + * @tx_remove: Free resources for TX queue + * @tx_write: Write TX descriptors and doorbell + * @rx_push_rss_config: Write RSS hash key and indirection table to the NIC + * @rx_probe: Allocate resources for RX queue + * @rx_init: Initialise RX queue on the NIC + * @rx_remove: Free resources for RX queue + * @rx_write: Write RX descriptors and doorbell + * @rx_defer_refill: Generate a refill reminder event + * @ev_probe: Allocate resources for event queue + * @ev_init: Initialise event queue on the NIC + * @ev_fini: Deinitialise event queue on the NIC + * @ev_remove: Free resources for event queue + * @ev_process: Process events for a queue, up to the given NAPI quota + * @ev_read_ack: Acknowledge read events on a queue, rearming its IRQ + * @ev_test_generate: Generate a test event + * @filter_table_probe: Probe filter capabilities and set up filter software state + * @filter_table_restore: Restore filters removed from hardware + * @filter_table_remove: Remove filters from hardware and tear down software state + * @filter_update_rx_scatter: Update filters after change to rx scatter setting + * @filter_insert: add or replace a filter + * @filter_remove_safe: remove a filter by ID, carefully + * @filter_get_safe: retrieve a filter by ID, carefully + * @filter_clear_rx: Remove all RX filters whose priority is less than or + * equal to the given priority and is not %EF4_FILTER_PRI_AUTO + * @filter_count_rx_used: Get the number of filters in use at a given priority + * @filter_get_rx_id_limit: Get maximum value of a filter id, plus 1 + * @filter_get_rx_ids: Get list of RX filters at a given priority + * @filter_rfs_insert: Add or replace a filter for RFS. This must be + * atomic. The hardware change may be asynchronous but should + * not be delayed for long. It may fail if this can't be done + * atomically. + * @filter_rfs_expire_one: Consider expiring a filter inserted for RFS. + * This must check whether the specified table entry is used by RFS + * and that rps_may_expire_flow() returns true for it. + * @mtd_probe: Probe and add MTD partitions associated with this net device, + * using ef4_mtd_add() + * @mtd_rename: Set an MTD partition name using the net device name + * @mtd_read: Read from an MTD partition + * @mtd_erase: Erase part of an MTD partition + * @mtd_write: Write to an MTD partition + * @mtd_sync: Wait for write-back to complete on MTD partition. This + * also notifies the driver that a writer has finished using this + * partition. + * @set_mac_address: Set the MAC address of the device + * @revision: Hardware architecture revision + * @txd_ptr_tbl_base: TX descriptor ring base address + * @rxd_ptr_tbl_base: RX descriptor ring base address + * @buf_tbl_base: Buffer table base address + * @evq_ptr_tbl_base: Event queue pointer table base address + * @evq_rptr_tbl_base: Event queue read-pointer table base address + * @max_dma_mask: Maximum possible DMA mask + * @rx_prefix_size: Size of RX prefix before packet data + * @rx_hash_offset: Offset of RX flow hash within prefix + * @rx_ts_offset: Offset of timestamp within prefix + * @rx_buffer_padding: Size of padding at end of RX packet + * @can_rx_scatter: NIC is able to scatter packets to multiple buffers + * @always_rx_scatter: NIC will always scatter packets to multiple buffers + * @max_interrupt_mode: Highest capability interrupt mode supported + * from &enum ef4_init_mode. + * @timer_period_max: Maximum period of interrupt timer (in ticks) + * @offload_features: net_device feature flags for protocol offload + * features implemented in hardware + */ +struct ef4_nic_type { + unsigned int mem_bar; + unsigned int (*mem_map_size)(struct ef4_nic *efx); + int (*probe)(struct ef4_nic *efx); + void (*remove)(struct ef4_nic *efx); + int (*init)(struct ef4_nic *efx); + int (*dimension_resources)(struct ef4_nic *efx); + void (*fini)(struct ef4_nic *efx); + void (*monitor)(struct ef4_nic *efx); + enum reset_type (*map_reset_reason)(enum reset_type reason); + int (*map_reset_flags)(u32 *flags); + int (*reset)(struct ef4_nic *efx, enum reset_type method); + int (*probe_port)(struct ef4_nic *efx); + void (*remove_port)(struct ef4_nic *efx); + bool (*handle_global_event)(struct ef4_channel *channel, ef4_qword_t *); + int (*fini_dmaq)(struct ef4_nic *efx); + void (*prepare_flush)(struct ef4_nic *efx); + void (*finish_flush)(struct ef4_nic *efx); + void (*prepare_flr)(struct ef4_nic *efx); + void (*finish_flr)(struct ef4_nic *efx); + size_t (*describe_stats)(struct ef4_nic *efx, u8 *names); + size_t (*update_stats)(struct ef4_nic *efx, u64 *full_stats, + struct rtnl_link_stats64 *core_stats); + void (*start_stats)(struct ef4_nic *efx); + void (*pull_stats)(struct ef4_nic *efx); + void (*stop_stats)(struct ef4_nic *efx); + void (*set_id_led)(struct ef4_nic *efx, enum ef4_led_mode mode); + void (*push_irq_moderation)(struct ef4_channel *channel); + int (*reconfigure_port)(struct ef4_nic *efx); + void (*prepare_enable_fc_tx)(struct ef4_nic *efx); + int (*reconfigure_mac)(struct ef4_nic *efx); + bool (*check_mac_fault)(struct ef4_nic *efx); + void (*get_wol)(struct ef4_nic *efx, struct ethtool_wolinfo *wol); + int (*set_wol)(struct ef4_nic *efx, u32 type); + void (*resume_wol)(struct ef4_nic *efx); + int (*test_chip)(struct ef4_nic *efx, struct ef4_self_tests *tests); + int (*test_nvram)(struct ef4_nic *efx); + void (*irq_enable_master)(struct ef4_nic *efx); + int (*irq_test_generate)(struct ef4_nic *efx); + void (*irq_disable_non_ev)(struct ef4_nic *efx); + irqreturn_t (*irq_handle_msi)(int irq, void *dev_id); + irqreturn_t (*irq_handle_legacy)(int irq, void *dev_id); + int (*tx_probe)(struct ef4_tx_queue *tx_queue); + void (*tx_init)(struct ef4_tx_queue *tx_queue); + void (*tx_remove)(struct ef4_tx_queue *tx_queue); + void (*tx_write)(struct ef4_tx_queue *tx_queue); + unsigned int (*tx_limit_len)(struct ef4_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len); + int (*rx_push_rss_config)(struct ef4_nic *efx, bool user, + const u32 *rx_indir_table); + int (*rx_probe)(struct ef4_rx_queue *rx_queue); + void (*rx_init)(struct ef4_rx_queue *rx_queue); + void (*rx_remove)(struct ef4_rx_queue *rx_queue); + void (*rx_write)(struct ef4_rx_queue *rx_queue); + void (*rx_defer_refill)(struct ef4_rx_queue *rx_queue); + int (*ev_probe)(struct ef4_channel *channel); + int (*ev_init)(struct ef4_channel *channel); + void (*ev_fini)(struct ef4_channel *channel); + void (*ev_remove)(struct ef4_channel *channel); + int (*ev_process)(struct ef4_channel *channel, int quota); + void (*ev_read_ack)(struct ef4_channel *channel); + void (*ev_test_generate)(struct ef4_channel *channel); + int (*filter_table_probe)(struct ef4_nic *efx); + void (*filter_table_restore)(struct ef4_nic *efx); + void (*filter_table_remove)(struct ef4_nic *efx); + void (*filter_update_rx_scatter)(struct ef4_nic *efx); + s32 (*filter_insert)(struct ef4_nic *efx, + struct ef4_filter_spec *spec, bool replace); + int (*filter_remove_safe)(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 filter_id); + int (*filter_get_safe)(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 filter_id, struct ef4_filter_spec *); + int (*filter_clear_rx)(struct ef4_nic *efx, + enum ef4_filter_priority priority); + u32 (*filter_count_rx_used)(struct ef4_nic *efx, + enum ef4_filter_priority priority); + u32 (*filter_get_rx_id_limit)(struct ef4_nic *efx); + s32 (*filter_get_rx_ids)(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 *buf, u32 size); +#ifdef CONFIG_RFS_ACCEL + s32 (*filter_rfs_insert)(struct ef4_nic *efx, + struct ef4_filter_spec *spec); + bool (*filter_rfs_expire_one)(struct ef4_nic *efx, u32 flow_id, + unsigned int index); +#endif +#ifdef CONFIG_SFC_FALCON_MTD + int (*mtd_probe)(struct ef4_nic *efx); + void (*mtd_rename)(struct ef4_mtd_partition *part); + int (*mtd_read)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, u8 *buffer); + int (*mtd_erase)(struct mtd_info *mtd, loff_t start, size_t len); + int (*mtd_write)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u8 *buffer); + int (*mtd_sync)(struct mtd_info *mtd); +#endif + int (*get_mac_address)(struct ef4_nic *efx, unsigned char *perm_addr); + int (*set_mac_address)(struct ef4_nic *efx); + + int revision; + unsigned int txd_ptr_tbl_base; + unsigned int rxd_ptr_tbl_base; + unsigned int buf_tbl_base; + unsigned int evq_ptr_tbl_base; + unsigned int evq_rptr_tbl_base; + u64 max_dma_mask; + unsigned int rx_prefix_size; + unsigned int rx_hash_offset; + unsigned int rx_ts_offset; + unsigned int rx_buffer_padding; + bool can_rx_scatter; + bool always_rx_scatter; + unsigned int max_interrupt_mode; + unsigned int timer_period_max; + netdev_features_t offload_features; + unsigned int max_rx_ip_filters; +}; + +/************************************************************************** + * + * Prototypes and inline functions + * + *************************************************************************/ + +static inline struct ef4_channel * +ef4_get_channel(struct ef4_nic *efx, unsigned index) +{ + EF4_BUG_ON_PARANOID(index >= efx->n_channels); + return efx->channel[index]; +} + +/* Iterate over all used channels */ +#define ef4_for_each_channel(_channel, _efx) \ + for (_channel = (_efx)->channel[0]; \ + _channel; \ + _channel = (_channel->channel + 1 < (_efx)->n_channels) ? \ + (_efx)->channel[_channel->channel + 1] : NULL) + +/* Iterate over all used channels in reverse */ +#define ef4_for_each_channel_rev(_channel, _efx) \ + for (_channel = (_efx)->channel[(_efx)->n_channels - 1]; \ + _channel; \ + _channel = _channel->channel ? \ + (_efx)->channel[_channel->channel - 1] : NULL) + +static inline struct ef4_tx_queue * +ef4_get_tx_queue(struct ef4_nic *efx, unsigned index, unsigned type) +{ + EF4_BUG_ON_PARANOID(index >= efx->n_tx_channels || + type >= EF4_TXQ_TYPES); + return &efx->channel[efx->tx_channel_offset + index]->tx_queue[type]; +} + +static inline bool ef4_channel_has_tx_queues(struct ef4_channel *channel) +{ + return channel->channel - channel->efx->tx_channel_offset < + channel->efx->n_tx_channels; +} + +static inline struct ef4_tx_queue * +ef4_channel_get_tx_queue(struct ef4_channel *channel, unsigned type) +{ + EF4_BUG_ON_PARANOID(!ef4_channel_has_tx_queues(channel) || + type >= EF4_TXQ_TYPES); + return &channel->tx_queue[type]; +} + +static inline bool ef4_tx_queue_used(struct ef4_tx_queue *tx_queue) +{ + return !(tx_queue->efx->net_dev->num_tc < 2 && + tx_queue->queue & EF4_TXQ_TYPE_HIGHPRI); +} + +/* Iterate over all TX queues belonging to a channel */ +#define ef4_for_each_channel_tx_queue(_tx_queue, _channel) \ + if (!ef4_channel_has_tx_queues(_channel)) \ + ; \ + else \ + for (_tx_queue = (_channel)->tx_queue; \ + _tx_queue < (_channel)->tx_queue + EF4_TXQ_TYPES && \ + ef4_tx_queue_used(_tx_queue); \ + _tx_queue++) + +/* Iterate over all possible TX queues belonging to a channel */ +#define ef4_for_each_possible_channel_tx_queue(_tx_queue, _channel) \ + if (!ef4_channel_has_tx_queues(_channel)) \ + ; \ + else \ + for (_tx_queue = (_channel)->tx_queue; \ + _tx_queue < (_channel)->tx_queue + EF4_TXQ_TYPES; \ + _tx_queue++) + +static inline bool ef4_channel_has_rx_queue(struct ef4_channel *channel) +{ + return channel->rx_queue.core_index >= 0; +} + +static inline struct ef4_rx_queue * +ef4_channel_get_rx_queue(struct ef4_channel *channel) +{ + EF4_BUG_ON_PARANOID(!ef4_channel_has_rx_queue(channel)); + return &channel->rx_queue; +} + +/* Iterate over all RX queues belonging to a channel */ +#define ef4_for_each_channel_rx_queue(_rx_queue, _channel) \ + if (!ef4_channel_has_rx_queue(_channel)) \ + ; \ + else \ + for (_rx_queue = &(_channel)->rx_queue; \ + _rx_queue; \ + _rx_queue = NULL) + +static inline struct ef4_channel * +ef4_rx_queue_channel(struct ef4_rx_queue *rx_queue) +{ + return container_of(rx_queue, struct ef4_channel, rx_queue); +} + +static inline int ef4_rx_queue_index(struct ef4_rx_queue *rx_queue) +{ + return ef4_rx_queue_channel(rx_queue)->channel; +} + +/* Returns a pointer to the specified receive buffer in the RX + * descriptor queue. + */ +static inline struct ef4_rx_buffer *ef4_rx_buffer(struct ef4_rx_queue *rx_queue, + unsigned int index) +{ + return &rx_queue->buffer[index]; +} + +/** + * EF4_MAX_FRAME_LEN - calculate maximum frame length + * + * This calculates the maximum frame length that will be used for a + * given MTU. The frame length will be equal to the MTU plus a + * constant amount of header space and padding. This is the quantity + * that the net driver will program into the MAC as the maximum frame + * length. + * + * The 10G MAC requires 8-byte alignment on the frame + * length, so we round up to the nearest 8. + * + * Re-clocking by the XGXS on RX can reduce an IPG to 32 bits (half an + * XGMII cycle). If the frame length reaches the maximum value in the + * same cycle, the XMAC can miss the IPG altogether. We work around + * this by adding a further 16 bytes. + */ +#define EF4_FRAME_PAD 16 +#define EF4_MAX_FRAME_LEN(mtu) \ + (ALIGN(((mtu) + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN + EF4_FRAME_PAD), 8)) + +/* Get all supported features. + * If a feature is not fixed, it is present in hw_features. + * If a feature is fixed, it does not present in hw_features, but + * always in features. + */ +static inline netdev_features_t ef4_supported_features(const struct ef4_nic *efx) +{ + const struct net_device *net_dev = efx->net_dev; + + return net_dev->features | net_dev->hw_features; +} + +/* Get the current TX queue insert index. */ +static inline unsigned int +ef4_tx_queue_get_insert_index(const struct ef4_tx_queue *tx_queue) +{ + return tx_queue->insert_count & tx_queue->ptr_mask; +} + +/* Get a TX buffer. */ +static inline struct ef4_tx_buffer * +__ef4_tx_queue_get_insert_buffer(const struct ef4_tx_queue *tx_queue) +{ + return &tx_queue->buffer[ef4_tx_queue_get_insert_index(tx_queue)]; +} + +/* Get a TX buffer, checking it's not currently in use. */ +static inline struct ef4_tx_buffer * +ef4_tx_queue_get_insert_buffer(const struct ef4_tx_queue *tx_queue) +{ + struct ef4_tx_buffer *buffer = + __ef4_tx_queue_get_insert_buffer(tx_queue); + + EF4_BUG_ON_PARANOID(buffer->len); + EF4_BUG_ON_PARANOID(buffer->flags); + EF4_BUG_ON_PARANOID(buffer->unmap_len); + + return buffer; +} + +#endif /* EF4_NET_DRIVER_H */ diff --git a/drivers/net/ethernet/sfc/falcon/nic.c b/drivers/net/ethernet/sfc/falcon/nic.c new file mode 100644 index 0000000..a8ecb33 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/nic.c @@ -0,0 +1,527 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "farch_regs.h" +#include "io.h" +#include "workarounds.h" + +/************************************************************************** + * + * Generic buffer handling + * These buffers are used for interrupt status, MAC stats, etc. + * + **************************************************************************/ + +int ef4_nic_alloc_buffer(struct ef4_nic *efx, struct ef4_buffer *buffer, + unsigned int len, gfp_t gfp_flags) +{ + buffer->addr = dma_zalloc_coherent(&efx->pci_dev->dev, len, + &buffer->dma_addr, gfp_flags); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + return 0; +} + +void ef4_nic_free_buffer(struct ef4_nic *efx, struct ef4_buffer *buffer) +{ + if (buffer->addr) { + dma_free_coherent(&efx->pci_dev->dev, buffer->len, + buffer->addr, buffer->dma_addr); + buffer->addr = NULL; + } +} + +/* Check whether an event is present in the eventq at the current + * read pointer. Only useful for self-test. + */ +bool ef4_nic_event_present(struct ef4_channel *channel) +{ + return ef4_event_present(ef4_event(channel, channel->eventq_read_ptr)); +} + +void ef4_nic_event_test_start(struct ef4_channel *channel) +{ + channel->event_test_cpu = -1; + smp_wmb(); + channel->efx->type->ev_test_generate(channel); +} + +int ef4_nic_irq_test_start(struct ef4_nic *efx) +{ + efx->last_irq_cpu = -1; + smp_wmb(); + return efx->type->irq_test_generate(efx); +} + +/* Hook interrupt handler(s) + * Try MSI and then legacy interrupts. + */ +int ef4_nic_init_interrupt(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + unsigned int n_irqs; + int rc; + + if (!EF4_INT_MODE_USE_MSI(efx)) { + rc = request_irq(efx->legacy_irq, + efx->type->irq_handle_legacy, IRQF_SHARED, + efx->name, efx); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "failed to hook legacy IRQ %d\n", + efx->pci_dev->irq); + goto fail1; + } + return 0; + } + +#ifdef CONFIG_RFS_ACCEL + if (efx->interrupt_mode == EF4_INT_MODE_MSIX) { + efx->net_dev->rx_cpu_rmap = + alloc_irq_cpu_rmap(efx->n_rx_channels); + if (!efx->net_dev->rx_cpu_rmap) { + rc = -ENOMEM; + goto fail1; + } + } +#endif + + /* Hook MSI or MSI-X interrupt */ + n_irqs = 0; + ef4_for_each_channel(channel, efx) { + rc = request_irq(channel->irq, efx->type->irq_handle_msi, + IRQF_PROBE_SHARED, /* Not shared */ + efx->msi_context[channel->channel].name, + &efx->msi_context[channel->channel]); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "failed to hook IRQ %d\n", channel->irq); + goto fail2; + } + ++n_irqs; + +#ifdef CONFIG_RFS_ACCEL + if (efx->interrupt_mode == EF4_INT_MODE_MSIX && + channel->channel < efx->n_rx_channels) { + rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap, + channel->irq); + if (rc) + goto fail2; + } +#endif + } + + return 0; + + fail2: +#ifdef CONFIG_RFS_ACCEL + free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); + efx->net_dev->rx_cpu_rmap = NULL; +#endif + ef4_for_each_channel(channel, efx) { + if (n_irqs-- == 0) + break; + free_irq(channel->irq, &efx->msi_context[channel->channel]); + } + fail1: + return rc; +} + +void ef4_nic_fini_interrupt(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + +#ifdef CONFIG_RFS_ACCEL + free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap); + efx->net_dev->rx_cpu_rmap = NULL; +#endif + + if (EF4_INT_MODE_USE_MSI(efx)) { + /* Disable MSI/MSI-X interrupts */ + ef4_for_each_channel(channel, efx) + free_irq(channel->irq, + &efx->msi_context[channel->channel]); + } else { + /* Disable legacy interrupt */ + free_irq(efx->legacy_irq, efx); + } +} + +/* Register dump */ + +#define REGISTER_REVISION_FA 1 +#define REGISTER_REVISION_FB 2 +#define REGISTER_REVISION_FC 3 +#define REGISTER_REVISION_FZ 3 /* last Falcon arch revision */ +#define REGISTER_REVISION_ED 4 +#define REGISTER_REVISION_EZ 4 /* latest EF10 revision */ + +struct ef4_nic_reg { + u32 offset:24; + u32 min_revision:3, max_revision:3; +}; + +#define REGISTER(name, arch, min_rev, max_rev) { \ + arch ## R_ ## min_rev ## max_rev ## _ ## name, \ + REGISTER_REVISION_ ## arch ## min_rev, \ + REGISTER_REVISION_ ## arch ## max_rev \ +} +#define REGISTER_AA(name) REGISTER(name, F, A, A) +#define REGISTER_AB(name) REGISTER(name, F, A, B) +#define REGISTER_AZ(name) REGISTER(name, F, A, Z) +#define REGISTER_BB(name) REGISTER(name, F, B, B) +#define REGISTER_BZ(name) REGISTER(name, F, B, Z) +#define REGISTER_CZ(name) REGISTER(name, F, C, Z) + +static const struct ef4_nic_reg ef4_nic_regs[] = { + REGISTER_AZ(ADR_REGION), + REGISTER_AZ(INT_EN_KER), + REGISTER_BZ(INT_EN_CHAR), + REGISTER_AZ(INT_ADR_KER), + REGISTER_BZ(INT_ADR_CHAR), + /* INT_ACK_KER is WO */ + /* INT_ISR0 is RC */ + REGISTER_AZ(HW_INIT), + REGISTER_CZ(USR_EV_CFG), + REGISTER_AB(EE_SPI_HCMD), + REGISTER_AB(EE_SPI_HADR), + REGISTER_AB(EE_SPI_HDATA), + REGISTER_AB(EE_BASE_PAGE), + REGISTER_AB(EE_VPD_CFG0), + /* EE_VPD_SW_CNTL and EE_VPD_SW_DATA are not used */ + /* PMBX_DBG_IADDR and PBMX_DBG_IDATA are indirect */ + /* PCIE_CORE_INDIRECT is indirect */ + REGISTER_AB(NIC_STAT), + REGISTER_AB(GPIO_CTL), + REGISTER_AB(GLB_CTL), + /* FATAL_INTR_KER and FATAL_INTR_CHAR are partly RC */ + REGISTER_BZ(DP_CTRL), + REGISTER_AZ(MEM_STAT), + REGISTER_AZ(CS_DEBUG), + REGISTER_AZ(ALTERA_BUILD), + REGISTER_AZ(CSR_SPARE), + REGISTER_AB(PCIE_SD_CTL0123), + REGISTER_AB(PCIE_SD_CTL45), + REGISTER_AB(PCIE_PCS_CTL_STAT), + /* DEBUG_DATA_OUT is not used */ + /* DRV_EV is WO */ + REGISTER_AZ(EVQ_CTL), + REGISTER_AZ(EVQ_CNT1), + REGISTER_AZ(EVQ_CNT2), + REGISTER_AZ(BUF_TBL_CFG), + REGISTER_AZ(SRM_RX_DC_CFG), + REGISTER_AZ(SRM_TX_DC_CFG), + REGISTER_AZ(SRM_CFG), + /* BUF_TBL_UPD is WO */ + REGISTER_AZ(SRM_UPD_EVQ), + REGISTER_AZ(SRAM_PARITY), + REGISTER_AZ(RX_CFG), + REGISTER_BZ(RX_FILTER_CTL), + /* RX_FLUSH_DESCQ is WO */ + REGISTER_AZ(RX_DC_CFG), + REGISTER_AZ(RX_DC_PF_WM), + REGISTER_BZ(RX_RSS_TKEY), + /* RX_NODESC_DROP is RC */ + REGISTER_AA(RX_SELF_RST), + /* RX_DEBUG, RX_PUSH_DROP are not used */ + REGISTER_CZ(RX_RSS_IPV6_REG1), + REGISTER_CZ(RX_RSS_IPV6_REG2), + REGISTER_CZ(RX_RSS_IPV6_REG3), + /* TX_FLUSH_DESCQ is WO */ + REGISTER_AZ(TX_DC_CFG), + REGISTER_AA(TX_CHKSM_CFG), + REGISTER_AZ(TX_CFG), + /* TX_PUSH_DROP is not used */ + REGISTER_AZ(TX_RESERVED), + REGISTER_BZ(TX_PACE), + /* TX_PACE_DROP_QID is RC */ + REGISTER_BB(TX_VLAN), + REGISTER_BZ(TX_IPFIL_PORTEN), + REGISTER_AB(MD_TXD), + REGISTER_AB(MD_RXD), + REGISTER_AB(MD_CS), + REGISTER_AB(MD_PHY_ADR), + REGISTER_AB(MD_ID), + /* MD_STAT is RC */ + REGISTER_AB(MAC_STAT_DMA), + REGISTER_AB(MAC_CTRL), + REGISTER_BB(GEN_MODE), + REGISTER_AB(MAC_MC_HASH_REG0), + REGISTER_AB(MAC_MC_HASH_REG1), + REGISTER_AB(GM_CFG1), + REGISTER_AB(GM_CFG2), + /* GM_IPG and GM_HD are not used */ + REGISTER_AB(GM_MAX_FLEN), + /* GM_TEST is not used */ + REGISTER_AB(GM_ADR1), + REGISTER_AB(GM_ADR2), + REGISTER_AB(GMF_CFG0), + REGISTER_AB(GMF_CFG1), + REGISTER_AB(GMF_CFG2), + REGISTER_AB(GMF_CFG3), + REGISTER_AB(GMF_CFG4), + REGISTER_AB(GMF_CFG5), + REGISTER_BB(TX_SRC_MAC_CTL), + REGISTER_AB(XM_ADR_LO), + REGISTER_AB(XM_ADR_HI), + REGISTER_AB(XM_GLB_CFG), + REGISTER_AB(XM_TX_CFG), + REGISTER_AB(XM_RX_CFG), + REGISTER_AB(XM_MGT_INT_MASK), + REGISTER_AB(XM_FC), + REGISTER_AB(XM_PAUSE_TIME), + REGISTER_AB(XM_TX_PARAM), + REGISTER_AB(XM_RX_PARAM), + /* XM_MGT_INT_MSK (note no 'A') is RC */ + REGISTER_AB(XX_PWR_RST), + REGISTER_AB(XX_SD_CTL), + REGISTER_AB(XX_TXDRV_CTL), + /* XX_PRBS_CTL, XX_PRBS_CHK and XX_PRBS_ERR are not used */ + /* XX_CORE_STAT is partly RC */ +}; + +struct ef4_nic_reg_table { + u32 offset:24; + u32 min_revision:3, max_revision:3; + u32 step:6, rows:21; +}; + +#define REGISTER_TABLE_DIMENSIONS(_, offset, arch, min_rev, max_rev, step, rows) { \ + offset, \ + REGISTER_REVISION_ ## arch ## min_rev, \ + REGISTER_REVISION_ ## arch ## max_rev, \ + step, rows \ +} +#define REGISTER_TABLE(name, arch, min_rev, max_rev) \ + REGISTER_TABLE_DIMENSIONS( \ + name, arch ## R_ ## min_rev ## max_rev ## _ ## name, \ + arch, min_rev, max_rev, \ + arch ## R_ ## min_rev ## max_rev ## _ ## name ## _STEP, \ + arch ## R_ ## min_rev ## max_rev ## _ ## name ## _ROWS) +#define REGISTER_TABLE_AA(name) REGISTER_TABLE(name, F, A, A) +#define REGISTER_TABLE_AZ(name) REGISTER_TABLE(name, F, A, Z) +#define REGISTER_TABLE_BB(name) REGISTER_TABLE(name, F, B, B) +#define REGISTER_TABLE_BZ(name) REGISTER_TABLE(name, F, B, Z) +#define REGISTER_TABLE_BB_CZ(name) \ + REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, B, B, \ + FR_BZ_ ## name ## _STEP, \ + FR_BB_ ## name ## _ROWS), \ + REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, C, Z, \ + FR_BZ_ ## name ## _STEP, \ + FR_CZ_ ## name ## _ROWS) +#define REGISTER_TABLE_CZ(name) REGISTER_TABLE(name, F, C, Z) + +static const struct ef4_nic_reg_table ef4_nic_reg_tables[] = { + /* DRIVER is not used */ + /* EVQ_RPTR, TIMER_COMMAND, USR_EV and {RX,TX}_DESC_UPD are WO */ + REGISTER_TABLE_BB(TX_IPFIL_TBL), + REGISTER_TABLE_BB(TX_SRC_MAC_TBL), + REGISTER_TABLE_AA(RX_DESC_PTR_TBL_KER), + REGISTER_TABLE_BB_CZ(RX_DESC_PTR_TBL), + REGISTER_TABLE_AA(TX_DESC_PTR_TBL_KER), + REGISTER_TABLE_BB_CZ(TX_DESC_PTR_TBL), + REGISTER_TABLE_AA(EVQ_PTR_TBL_KER), + REGISTER_TABLE_BB_CZ(EVQ_PTR_TBL), + /* We can't reasonably read all of the buffer table (up to 8MB!). + * However this driver will only use a few entries. Reading + * 1K entries allows for some expansion of queue count and + * size before we need to change the version. */ + REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL_KER, FR_AA_BUF_FULL_TBL_KER, + F, A, A, 8, 1024), + REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL, FR_BZ_BUF_FULL_TBL, + F, B, Z, 8, 1024), + REGISTER_TABLE_CZ(RX_MAC_FILTER_TBL0), + REGISTER_TABLE_BB_CZ(TIMER_TBL), + REGISTER_TABLE_BB_CZ(TX_PACE_TBL), + REGISTER_TABLE_BZ(RX_INDIRECTION_TBL), + /* TX_FILTER_TBL0 is huge and not used by this driver */ + REGISTER_TABLE_CZ(TX_MAC_FILTER_TBL0), + REGISTER_TABLE_CZ(MC_TREG_SMEM), + /* MSIX_PBA_TABLE is not mapped */ + /* SRM_DBG is not mapped (and is redundant with BUF_FLL_TBL) */ + REGISTER_TABLE_BZ(RX_FILTER_TBL0), +}; + +size_t ef4_nic_get_regs_len(struct ef4_nic *efx) +{ + const struct ef4_nic_reg *reg; + const struct ef4_nic_reg_table *table; + size_t len = 0; + + for (reg = ef4_nic_regs; + reg < ef4_nic_regs + ARRAY_SIZE(ef4_nic_regs); + reg++) + if (efx->type->revision >= reg->min_revision && + efx->type->revision <= reg->max_revision) + len += sizeof(ef4_oword_t); + + for (table = ef4_nic_reg_tables; + table < ef4_nic_reg_tables + ARRAY_SIZE(ef4_nic_reg_tables); + table++) + if (efx->type->revision >= table->min_revision && + efx->type->revision <= table->max_revision) + len += table->rows * min_t(size_t, table->step, 16); + + return len; +} + +void ef4_nic_get_regs(struct ef4_nic *efx, void *buf) +{ + const struct ef4_nic_reg *reg; + const struct ef4_nic_reg_table *table; + + for (reg = ef4_nic_regs; + reg < ef4_nic_regs + ARRAY_SIZE(ef4_nic_regs); + reg++) { + if (efx->type->revision >= reg->min_revision && + efx->type->revision <= reg->max_revision) { + ef4_reado(efx, (ef4_oword_t *)buf, reg->offset); + buf += sizeof(ef4_oword_t); + } + } + + for (table = ef4_nic_reg_tables; + table < ef4_nic_reg_tables + ARRAY_SIZE(ef4_nic_reg_tables); + table++) { + size_t size, i; + + if (!(efx->type->revision >= table->min_revision && + efx->type->revision <= table->max_revision)) + continue; + + size = min_t(size_t, table->step, 16); + + for (i = 0; i < table->rows; i++) { + switch (table->step) { + case 4: /* 32-bit SRAM */ + ef4_readd(efx, buf, table->offset + 4 * i); + break; + case 8: /* 64-bit SRAM */ + ef4_sram_readq(efx, + efx->membase + table->offset, + buf, i); + break; + case 16: /* 128-bit-readable register */ + ef4_reado_table(efx, buf, table->offset, i); + break; + case 32: /* 128-bit register, interleaved */ + ef4_reado_table(efx, buf, table->offset, 2 * i); + break; + default: + WARN_ON(1); + return; + } + buf += size; + } + } +} + +/** + * ef4_nic_describe_stats - Describe supported statistics for ethtool + * @desc: Array of &struct ef4_hw_stat_desc describing the statistics + * @count: Length of the @desc array + * @mask: Bitmask of which elements of @desc are enabled + * @names: Buffer to copy names to, or %NULL. The names are copied + * starting at intervals of %ETH_GSTRING_LEN bytes. + * + * Returns the number of visible statistics, i.e. the number of set + * bits in the first @count bits of @mask for which a name is defined. + */ +size_t ef4_nic_describe_stats(const struct ef4_hw_stat_desc *desc, size_t count, + const unsigned long *mask, u8 *names) +{ + size_t visible = 0; + size_t index; + + for_each_set_bit(index, mask, count) { + if (desc[index].name) { + if (names) { + strlcpy(names, desc[index].name, + ETH_GSTRING_LEN); + names += ETH_GSTRING_LEN; + } + ++visible; + } + } + + return visible; +} + +/** + * ef4_nic_update_stats - Convert statistics DMA buffer to array of u64 + * @desc: Array of &struct ef4_hw_stat_desc describing the DMA buffer + * layout. DMA widths of 0, 16, 32 and 64 are supported; where + * the width is specified as 0 the corresponding element of + * @stats is not updated. + * @count: Length of the @desc array + * @mask: Bitmask of which elements of @desc are enabled + * @stats: Buffer to update with the converted statistics. The length + * of this array must be at least @count. + * @dma_buf: DMA buffer containing hardware statistics + * @accumulate: If set, the converted values will be added rather than + * directly stored to the corresponding elements of @stats + */ +void ef4_nic_update_stats(const struct ef4_hw_stat_desc *desc, size_t count, + const unsigned long *mask, + u64 *stats, const void *dma_buf, bool accumulate) +{ + size_t index; + + for_each_set_bit(index, mask, count) { + if (desc[index].dma_width) { + const void *addr = dma_buf + desc[index].offset; + u64 val; + + switch (desc[index].dma_width) { + case 16: + val = le16_to_cpup((__le16 *)addr); + break; + case 32: + val = le32_to_cpup((__le32 *)addr); + break; + case 64: + val = le64_to_cpup((__le64 *)addr); + break; + default: + WARN_ON(1); + val = 0; + break; + } + + if (accumulate) + stats[index] += val; + else + stats[index] = val; + } + } +} + +void ef4_nic_fix_nodesc_drop_stat(struct ef4_nic *efx, u64 *rx_nodesc_drops) +{ + /* if down, or this is the first update after coming up */ + if (!(efx->net_dev->flags & IFF_UP) || !efx->rx_nodesc_drops_prev_state) + efx->rx_nodesc_drops_while_down += + *rx_nodesc_drops - efx->rx_nodesc_drops_total; + efx->rx_nodesc_drops_total = *rx_nodesc_drops; + efx->rx_nodesc_drops_prev_state = !!(efx->net_dev->flags & IFF_UP); + *rx_nodesc_drops -= efx->rx_nodesc_drops_while_down; +} diff --git a/drivers/net/ethernet/sfc/falcon/nic.h b/drivers/net/ethernet/sfc/falcon/nic.h new file mode 100644 index 0000000..a4c4592 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/nic.h @@ -0,0 +1,513 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_NIC_H +#define EF4_NIC_H + +#include +#include +#include "net_driver.h" +#include "efx.h" + +enum { + EF4_REV_FALCON_A0 = 0, + EF4_REV_FALCON_A1 = 1, + EF4_REV_FALCON_B0 = 2, +}; + +static inline int ef4_nic_rev(struct ef4_nic *efx) +{ + return efx->type->revision; +} + +u32 ef4_farch_fpga_ver(struct ef4_nic *efx); + +/* NIC has two interlinked PCI functions for the same port. */ +static inline bool ef4_nic_is_dual_func(struct ef4_nic *efx) +{ + return ef4_nic_rev(efx) < EF4_REV_FALCON_B0; +} + +/* Read the current event from the event queue */ +static inline ef4_qword_t *ef4_event(struct ef4_channel *channel, + unsigned int index) +{ + return ((ef4_qword_t *) (channel->eventq.buf.addr)) + + (index & channel->eventq_mask); +} + +/* See if an event is present + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int ef4_event_present(ef4_qword_t *event) +{ + return !(EF4_DWORD_IS_ALL_ONES(event->dword[0]) | + EF4_DWORD_IS_ALL_ONES(event->dword[1])); +} + +/* Returns a pointer to the specified transmit descriptor in the TX + * descriptor queue belonging to the specified channel. + */ +static inline ef4_qword_t * +ef4_tx_desc(struct ef4_tx_queue *tx_queue, unsigned int index) +{ + return ((ef4_qword_t *) (tx_queue->txd.buf.addr)) + index; +} + +/* Get partner of a TX queue, seen as part of the same net core queue */ +static inline struct ef4_tx_queue *ef4_tx_queue_partner(struct ef4_tx_queue *tx_queue) +{ + if (tx_queue->queue & EF4_TXQ_TYPE_OFFLOAD) + return tx_queue - EF4_TXQ_TYPE_OFFLOAD; + else + return tx_queue + EF4_TXQ_TYPE_OFFLOAD; +} + +/* Report whether this TX queue would be empty for the given write_count. + * May return false negative. + */ +static inline bool __ef4_nic_tx_is_empty(struct ef4_tx_queue *tx_queue, + unsigned int write_count) +{ + unsigned int empty_read_count = ACCESS_ONCE(tx_queue->empty_read_count); + + if (empty_read_count == 0) + return false; + + return ((empty_read_count ^ write_count) & ~EF4_EMPTY_COUNT_VALID) == 0; +} + +/* Decide whether to push a TX descriptor to the NIC vs merely writing + * the doorbell. This can reduce latency when we are adding a single + * descriptor to an empty queue, but is otherwise pointless. Further, + * Falcon and Siena have hardware bugs (SF bug 33851) that may be + * triggered if we don't check this. + * We use the write_count used for the last doorbell push, to get the + * NIC's view of the tx queue. + */ +static inline bool ef4_nic_may_push_tx_desc(struct ef4_tx_queue *tx_queue, + unsigned int write_count) +{ + bool was_empty = __ef4_nic_tx_is_empty(tx_queue, write_count); + + tx_queue->empty_read_count = 0; + return was_empty && tx_queue->write_count - write_count == 1; +} + +/* Returns a pointer to the specified descriptor in the RX descriptor queue */ +static inline ef4_qword_t * +ef4_rx_desc(struct ef4_rx_queue *rx_queue, unsigned int index) +{ + return ((ef4_qword_t *) (rx_queue->rxd.buf.addr)) + index; +} + +enum { + PHY_TYPE_NONE = 0, + PHY_TYPE_TXC43128 = 1, + PHY_TYPE_88E1111 = 2, + PHY_TYPE_SFX7101 = 3, + PHY_TYPE_QT2022C2 = 4, + PHY_TYPE_PM8358 = 6, + PHY_TYPE_SFT9001A = 8, + PHY_TYPE_QT2025C = 9, + PHY_TYPE_SFT9001B = 10, +}; + +#define FALCON_XMAC_LOOPBACKS \ + ((1 << LOOPBACK_XGMII) | \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI)) + +/* Alignment of PCIe DMA boundaries (4KB) */ +#define EF4_PAGE_SIZE 4096 +/* Size and alignment of buffer table entries (same) */ +#define EF4_BUF_SIZE EF4_PAGE_SIZE + +/* NIC-generic software stats */ +enum { + GENERIC_STAT_rx_noskb_drops, + GENERIC_STAT_rx_nodesc_trunc, + GENERIC_STAT_COUNT +}; + +/** + * struct falcon_board_type - board operations and type information + * @id: Board type id, as found in NVRAM + * @init: Allocate resources and initialise peripheral hardware + * @init_phy: Do board-specific PHY initialisation + * @fini: Shut down hardware and free resources + * @set_id_led: Set state of identifying LED or revert to automatic function + * @monitor: Board-specific health check function + */ +struct falcon_board_type { + u8 id; + int (*init) (struct ef4_nic *nic); + void (*init_phy) (struct ef4_nic *efx); + void (*fini) (struct ef4_nic *nic); + void (*set_id_led) (struct ef4_nic *efx, enum ef4_led_mode mode); + int (*monitor) (struct ef4_nic *nic); +}; + +/** + * struct falcon_board - board information + * @type: Type of board + * @major: Major rev. ('A', 'B' ...) + * @minor: Minor rev. (0, 1, ...) + * @i2c_adap: I2C adapter for on-board peripherals + * @i2c_data: Data for bit-banging algorithm + * @hwmon_client: I2C client for hardware monitor + * @ioexp_client: I2C client for power/port control + */ +struct falcon_board { + const struct falcon_board_type *type; + int major; + int minor; + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_data; + struct i2c_client *hwmon_client, *ioexp_client; +}; + +/** + * struct falcon_spi_device - a Falcon SPI (Serial Peripheral Interface) device + * @device_id: Controller's id for the device + * @size: Size (in bytes) + * @addr_len: Number of address bytes in read/write commands + * @munge_address: Flag whether addresses should be munged. + * Some devices with 9-bit addresses (e.g. AT25040A EEPROM) + * use bit 3 of the command byte as address bit A8, rather + * than having a two-byte address. If this flag is set, then + * commands should be munged in this way. + * @erase_command: Erase command (or 0 if sector erase not needed). + * @erase_size: Erase sector size (in bytes) + * Erase commands affect sectors with this size and alignment. + * This must be a power of two. + * @block_size: Write block size (in bytes). + * Write commands are limited to blocks with this size and alignment. + */ +struct falcon_spi_device { + int device_id; + unsigned int size; + unsigned int addr_len; + unsigned int munge_address:1; + u8 erase_command; + unsigned int erase_size; + unsigned int block_size; +}; + +static inline bool falcon_spi_present(const struct falcon_spi_device *spi) +{ + return spi->size != 0; +} + +enum { + FALCON_STAT_tx_bytes = GENERIC_STAT_COUNT, + FALCON_STAT_tx_packets, + FALCON_STAT_tx_pause, + FALCON_STAT_tx_control, + FALCON_STAT_tx_unicast, + FALCON_STAT_tx_multicast, + FALCON_STAT_tx_broadcast, + FALCON_STAT_tx_lt64, + FALCON_STAT_tx_64, + FALCON_STAT_tx_65_to_127, + FALCON_STAT_tx_128_to_255, + FALCON_STAT_tx_256_to_511, + FALCON_STAT_tx_512_to_1023, + FALCON_STAT_tx_1024_to_15xx, + FALCON_STAT_tx_15xx_to_jumbo, + FALCON_STAT_tx_gtjumbo, + FALCON_STAT_tx_non_tcpudp, + FALCON_STAT_tx_mac_src_error, + FALCON_STAT_tx_ip_src_error, + FALCON_STAT_rx_bytes, + FALCON_STAT_rx_good_bytes, + FALCON_STAT_rx_bad_bytes, + FALCON_STAT_rx_packets, + FALCON_STAT_rx_good, + FALCON_STAT_rx_bad, + FALCON_STAT_rx_pause, + FALCON_STAT_rx_control, + FALCON_STAT_rx_unicast, + FALCON_STAT_rx_multicast, + FALCON_STAT_rx_broadcast, + FALCON_STAT_rx_lt64, + FALCON_STAT_rx_64, + FALCON_STAT_rx_65_to_127, + FALCON_STAT_rx_128_to_255, + FALCON_STAT_rx_256_to_511, + FALCON_STAT_rx_512_to_1023, + FALCON_STAT_rx_1024_to_15xx, + FALCON_STAT_rx_15xx_to_jumbo, + FALCON_STAT_rx_gtjumbo, + FALCON_STAT_rx_bad_lt64, + FALCON_STAT_rx_bad_gtjumbo, + FALCON_STAT_rx_overflow, + FALCON_STAT_rx_symbol_error, + FALCON_STAT_rx_align_error, + FALCON_STAT_rx_length_error, + FALCON_STAT_rx_internal_error, + FALCON_STAT_rx_nodesc_drop_cnt, + FALCON_STAT_COUNT +}; + +/** + * struct falcon_nic_data - Falcon NIC state + * @pci_dev2: Secondary function of Falcon A + * @board: Board state and functions + * @stats: Hardware statistics + * @stats_disable_count: Nest count for disabling statistics fetches + * @stats_pending: Is there a pending DMA of MAC statistics. + * @stats_timer: A timer for regularly fetching MAC statistics. + * @spi_flash: SPI flash device + * @spi_eeprom: SPI EEPROM device + * @spi_lock: SPI bus lock + * @mdio_lock: MDIO bus lock + * @xmac_poll_required: XMAC link state needs polling + */ +struct falcon_nic_data { + struct pci_dev *pci_dev2; + struct falcon_board board; + u64 stats[FALCON_STAT_COUNT]; + unsigned int stats_disable_count; + bool stats_pending; + struct timer_list stats_timer; + struct falcon_spi_device spi_flash; + struct falcon_spi_device spi_eeprom; + struct mutex spi_lock; + struct mutex mdio_lock; + bool xmac_poll_required; +}; + +static inline struct falcon_board *falcon_board(struct ef4_nic *efx) +{ + struct falcon_nic_data *data = efx->nic_data; + return &data->board; +} + +struct ethtool_ts_info; + +extern const struct ef4_nic_type falcon_a1_nic_type; +extern const struct ef4_nic_type falcon_b0_nic_type; + +/************************************************************************** + * + * Externs + * + ************************************************************************** + */ + +int falcon_probe_board(struct ef4_nic *efx, u16 revision_info); + +/* TX data path */ +static inline int ef4_nic_probe_tx(struct ef4_tx_queue *tx_queue) +{ + return tx_queue->efx->type->tx_probe(tx_queue); +} +static inline void ef4_nic_init_tx(struct ef4_tx_queue *tx_queue) +{ + tx_queue->efx->type->tx_init(tx_queue); +} +static inline void ef4_nic_remove_tx(struct ef4_tx_queue *tx_queue) +{ + tx_queue->efx->type->tx_remove(tx_queue); +} +static inline void ef4_nic_push_buffers(struct ef4_tx_queue *tx_queue) +{ + tx_queue->efx->type->tx_write(tx_queue); +} + +/* RX data path */ +static inline int ef4_nic_probe_rx(struct ef4_rx_queue *rx_queue) +{ + return rx_queue->efx->type->rx_probe(rx_queue); +} +static inline void ef4_nic_init_rx(struct ef4_rx_queue *rx_queue) +{ + rx_queue->efx->type->rx_init(rx_queue); +} +static inline void ef4_nic_remove_rx(struct ef4_rx_queue *rx_queue) +{ + rx_queue->efx->type->rx_remove(rx_queue); +} +static inline void ef4_nic_notify_rx_desc(struct ef4_rx_queue *rx_queue) +{ + rx_queue->efx->type->rx_write(rx_queue); +} +static inline void ef4_nic_generate_fill_event(struct ef4_rx_queue *rx_queue) +{ + rx_queue->efx->type->rx_defer_refill(rx_queue); +} + +/* Event data path */ +static inline int ef4_nic_probe_eventq(struct ef4_channel *channel) +{ + return channel->efx->type->ev_probe(channel); +} +static inline int ef4_nic_init_eventq(struct ef4_channel *channel) +{ + return channel->efx->type->ev_init(channel); +} +static inline void ef4_nic_fini_eventq(struct ef4_channel *channel) +{ + channel->efx->type->ev_fini(channel); +} +static inline void ef4_nic_remove_eventq(struct ef4_channel *channel) +{ + channel->efx->type->ev_remove(channel); +} +static inline int +ef4_nic_process_eventq(struct ef4_channel *channel, int quota) +{ + return channel->efx->type->ev_process(channel, quota); +} +static inline void ef4_nic_eventq_read_ack(struct ef4_channel *channel) +{ + channel->efx->type->ev_read_ack(channel); +} +void ef4_nic_event_test_start(struct ef4_channel *channel); + +/* queue operations */ +int ef4_farch_tx_probe(struct ef4_tx_queue *tx_queue); +void ef4_farch_tx_init(struct ef4_tx_queue *tx_queue); +void ef4_farch_tx_fini(struct ef4_tx_queue *tx_queue); +void ef4_farch_tx_remove(struct ef4_tx_queue *tx_queue); +void ef4_farch_tx_write(struct ef4_tx_queue *tx_queue); +unsigned int ef4_farch_tx_limit_len(struct ef4_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len); +int ef4_farch_rx_probe(struct ef4_rx_queue *rx_queue); +void ef4_farch_rx_init(struct ef4_rx_queue *rx_queue); +void ef4_farch_rx_fini(struct ef4_rx_queue *rx_queue); +void ef4_farch_rx_remove(struct ef4_rx_queue *rx_queue); +void ef4_farch_rx_write(struct ef4_rx_queue *rx_queue); +void ef4_farch_rx_defer_refill(struct ef4_rx_queue *rx_queue); +int ef4_farch_ev_probe(struct ef4_channel *channel); +int ef4_farch_ev_init(struct ef4_channel *channel); +void ef4_farch_ev_fini(struct ef4_channel *channel); +void ef4_farch_ev_remove(struct ef4_channel *channel); +int ef4_farch_ev_process(struct ef4_channel *channel, int quota); +void ef4_farch_ev_read_ack(struct ef4_channel *channel); +void ef4_farch_ev_test_generate(struct ef4_channel *channel); + +/* filter operations */ +int ef4_farch_filter_table_probe(struct ef4_nic *efx); +void ef4_farch_filter_table_restore(struct ef4_nic *efx); +void ef4_farch_filter_table_remove(struct ef4_nic *efx); +void ef4_farch_filter_update_rx_scatter(struct ef4_nic *efx); +s32 ef4_farch_filter_insert(struct ef4_nic *efx, struct ef4_filter_spec *spec, + bool replace); +int ef4_farch_filter_remove_safe(struct ef4_nic *efx, + enum ef4_filter_priority priority, + u32 filter_id); +int ef4_farch_filter_get_safe(struct ef4_nic *efx, + enum ef4_filter_priority priority, u32 filter_id, + struct ef4_filter_spec *); +int ef4_farch_filter_clear_rx(struct ef4_nic *efx, + enum ef4_filter_priority priority); +u32 ef4_farch_filter_count_rx_used(struct ef4_nic *efx, + enum ef4_filter_priority priority); +u32 ef4_farch_filter_get_rx_id_limit(struct ef4_nic *efx); +s32 ef4_farch_filter_get_rx_ids(struct ef4_nic *efx, + enum ef4_filter_priority priority, u32 *buf, + u32 size); +#ifdef CONFIG_RFS_ACCEL +s32 ef4_farch_filter_rfs_insert(struct ef4_nic *efx, + struct ef4_filter_spec *spec); +bool ef4_farch_filter_rfs_expire_one(struct ef4_nic *efx, u32 flow_id, + unsigned int index); +#endif +void ef4_farch_filter_sync_rx_mode(struct ef4_nic *efx); + +bool ef4_nic_event_present(struct ef4_channel *channel); + +/* Some statistics are computed as A - B where A and B each increase + * linearly with some hardware counter(s) and the counters are read + * asynchronously. If the counters contributing to B are always read + * after those contributing to A, the computed value may be lower than + * the true value by some variable amount, and may decrease between + * subsequent computations. + * + * We should never allow statistics to decrease or to exceed the true + * value. Since the computed value will never be greater than the + * true value, we can achieve this by only storing the computed value + * when it increases. + */ +static inline void ef4_update_diff_stat(u64 *stat, u64 diff) +{ + if ((s64)(diff - *stat) > 0) + *stat = diff; +} + +/* Interrupts */ +int ef4_nic_init_interrupt(struct ef4_nic *efx); +int ef4_nic_irq_test_start(struct ef4_nic *efx); +void ef4_nic_fini_interrupt(struct ef4_nic *efx); +void ef4_farch_irq_enable_master(struct ef4_nic *efx); +int ef4_farch_irq_test_generate(struct ef4_nic *efx); +void ef4_farch_irq_disable_master(struct ef4_nic *efx); +irqreturn_t ef4_farch_msi_interrupt(int irq, void *dev_id); +irqreturn_t ef4_farch_legacy_interrupt(int irq, void *dev_id); +irqreturn_t ef4_farch_fatal_interrupt(struct ef4_nic *efx); + +static inline int ef4_nic_event_test_irq_cpu(struct ef4_channel *channel) +{ + return ACCESS_ONCE(channel->event_test_cpu); +} +static inline int ef4_nic_irq_test_irq_cpu(struct ef4_nic *efx) +{ + return ACCESS_ONCE(efx->last_irq_cpu); +} + +/* Global Resources */ +int ef4_nic_flush_queues(struct ef4_nic *efx); +int ef4_farch_fini_dmaq(struct ef4_nic *efx); +void ef4_farch_finish_flr(struct ef4_nic *efx); +void falcon_start_nic_stats(struct ef4_nic *efx); +void falcon_stop_nic_stats(struct ef4_nic *efx); +int falcon_reset_xaui(struct ef4_nic *efx); +void ef4_farch_dimension_resources(struct ef4_nic *efx, unsigned sram_lim_qw); +void ef4_farch_init_common(struct ef4_nic *efx); +void ef4_farch_rx_push_indir_table(struct ef4_nic *efx); + +int ef4_nic_alloc_buffer(struct ef4_nic *efx, struct ef4_buffer *buffer, + unsigned int len, gfp_t gfp_flags); +void ef4_nic_free_buffer(struct ef4_nic *efx, struct ef4_buffer *buffer); + +/* Tests */ +struct ef4_farch_register_test { + unsigned address; + ef4_oword_t mask; +}; +int ef4_farch_test_registers(struct ef4_nic *efx, + const struct ef4_farch_register_test *regs, + size_t n_regs); + +size_t ef4_nic_get_regs_len(struct ef4_nic *efx); +void ef4_nic_get_regs(struct ef4_nic *efx, void *buf); + +size_t ef4_nic_describe_stats(const struct ef4_hw_stat_desc *desc, size_t count, + const unsigned long *mask, u8 *names); +void ef4_nic_update_stats(const struct ef4_hw_stat_desc *desc, size_t count, + const unsigned long *mask, u64 *stats, + const void *dma_buf, bool accumulate); +void ef4_nic_fix_nodesc_drop_stat(struct ef4_nic *efx, u64 *stat); + +#define EF4_MAX_FLUSH_TIME 5000 + +void ef4_farch_generate_event(struct ef4_nic *efx, unsigned int evq, + ef4_qword_t *event); + +#endif /* EF4_NIC_H */ diff --git a/drivers/net/ethernet/sfc/falcon/phy.h b/drivers/net/ethernet/sfc/falcon/phy.h new file mode 100644 index 0000000..362141c --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/phy.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2007-2010 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_PHY_H +#define EF4_PHY_H + +/**************************************************************************** + * 10Xpress (SFX7101) PHY + */ +extern const struct ef4_phy_operations falcon_sfx7101_phy_ops; + +void tenxpress_set_id_led(struct ef4_nic *efx, enum ef4_led_mode mode); + +/**************************************************************************** + * AMCC/Quake QT202x PHYs + */ +extern const struct ef4_phy_operations falcon_qt202x_phy_ops; + +/* These PHYs provide various H/W control states for LEDs */ +#define QUAKE_LED_LINK_INVAL (0) +#define QUAKE_LED_LINK_STAT (1) +#define QUAKE_LED_LINK_ACT (2) +#define QUAKE_LED_LINK_ACTSTAT (3) +#define QUAKE_LED_OFF (4) +#define QUAKE_LED_ON (5) +#define QUAKE_LED_LINK_INPUT (6) /* Pin is an input. */ +/* What link the LED tracks */ +#define QUAKE_LED_TXLINK (0) +#define QUAKE_LED_RXLINK (8) + +void falcon_qt202x_set_led(struct ef4_nic *p, int led, int state); + +/**************************************************************************** +* Transwitch CX4 retimer +*/ +extern const struct ef4_phy_operations falcon_txc_phy_ops; + +#define TXC_GPIO_DIR_INPUT 0 +#define TXC_GPIO_DIR_OUTPUT 1 + +void falcon_txc_set_gpio_dir(struct ef4_nic *efx, int pin, int dir); +void falcon_txc_set_gpio_val(struct ef4_nic *efx, int pin, int val); + +#endif diff --git a/drivers/net/ethernet/sfc/falcon/qt202x_phy.c b/drivers/net/ethernet/sfc/falcon/qt202x_phy.c new file mode 100644 index 0000000..d293316 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/qt202x_phy.c @@ -0,0 +1,495 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2006-2012 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ +/* + * Driver for AMCC QT202x SFP+ and XFP adapters; see www.amcc.com for details + */ + +#include +#include +#include +#include "efx.h" +#include "mdio_10g.h" +#include "phy.h" +#include "nic.h" + +#define QT202X_REQUIRED_DEVS (MDIO_DEVS_PCS | \ + MDIO_DEVS_PMAPMD | \ + MDIO_DEVS_PHYXS) + +#define QT202X_LOOPBACKS ((1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_PHYXS_WS)) + +/****************************************************************************/ +/* Quake-specific MDIO registers */ +#define MDIO_QUAKE_LED0_REG (0xD006) + +/* QT2025C only */ +#define PCS_FW_HEARTBEAT_REG 0xd7ee +#define PCS_FW_HEARTB_LBN 0 +#define PCS_FW_HEARTB_WIDTH 8 +#define PCS_FW_PRODUCT_CODE_1 0xd7f0 +#define PCS_FW_VERSION_1 0xd7f3 +#define PCS_FW_BUILD_1 0xd7f6 +#define PCS_UC8051_STATUS_REG 0xd7fd +#define PCS_UC_STATUS_LBN 0 +#define PCS_UC_STATUS_WIDTH 8 +#define PCS_UC_STATUS_FW_SAVE 0x20 +#define PMA_PMD_MODE_REG 0xc301 +#define PMA_PMD_RXIN_SEL_LBN 6 +#define PMA_PMD_FTX_CTRL2_REG 0xc309 +#define PMA_PMD_FTX_STATIC_LBN 13 +#define PMA_PMD_VEND1_REG 0xc001 +#define PMA_PMD_VEND1_LBTXD_LBN 15 +#define PCS_VEND1_REG 0xc000 +#define PCS_VEND1_LBTXD_LBN 5 + +void falcon_qt202x_set_led(struct ef4_nic *p, int led, int mode) +{ + int addr = MDIO_QUAKE_LED0_REG + led; + ef4_mdio_write(p, MDIO_MMD_PMAPMD, addr, mode); +} + +struct qt202x_phy_data { + enum ef4_phy_mode phy_mode; + bool bug17190_in_bad_state; + unsigned long bug17190_timer; + u32 firmware_ver; +}; + +#define QT2022C2_MAX_RESET_TIME 500 +#define QT2022C2_RESET_WAIT 10 + +#define QT2025C_MAX_HEARTB_TIME (5 * HZ) +#define QT2025C_HEARTB_WAIT 100 +#define QT2025C_MAX_FWSTART_TIME (25 * HZ / 10) +#define QT2025C_FWSTART_WAIT 100 + +#define BUG17190_INTERVAL (2 * HZ) + +static int qt2025c_wait_heartbeat(struct ef4_nic *efx) +{ + unsigned long timeout = jiffies + QT2025C_MAX_HEARTB_TIME; + int reg, old_counter = 0; + + /* Wait for firmware heartbeat to start */ + for (;;) { + int counter; + reg = ef4_mdio_read(efx, MDIO_MMD_PCS, PCS_FW_HEARTBEAT_REG); + if (reg < 0) + return reg; + counter = ((reg >> PCS_FW_HEARTB_LBN) & + ((1 << PCS_FW_HEARTB_WIDTH) - 1)); + if (old_counter == 0) + old_counter = counter; + else if (counter != old_counter) + break; + if (time_after(jiffies, timeout)) { + /* Some cables have EEPROMs that conflict with the + * PHY's on-board EEPROM so it cannot load firmware */ + netif_err(efx, hw, efx->net_dev, + "If an SFP+ direct attach cable is" + " connected, please check that it complies" + " with the SFP+ specification\n"); + return -ETIMEDOUT; + } + msleep(QT2025C_HEARTB_WAIT); + } + + return 0; +} + +static int qt2025c_wait_fw_status_good(struct ef4_nic *efx) +{ + unsigned long timeout = jiffies + QT2025C_MAX_FWSTART_TIME; + int reg; + + /* Wait for firmware status to look good */ + for (;;) { + reg = ef4_mdio_read(efx, MDIO_MMD_PCS, PCS_UC8051_STATUS_REG); + if (reg < 0) + return reg; + if ((reg & + ((1 << PCS_UC_STATUS_WIDTH) - 1) << PCS_UC_STATUS_LBN) >= + PCS_UC_STATUS_FW_SAVE) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + msleep(QT2025C_FWSTART_WAIT); + } + + return 0; +} + +static void qt2025c_restart_firmware(struct ef4_nic *efx) +{ + /* Restart microcontroller execution of firmware from RAM */ + ef4_mdio_write(efx, 3, 0xe854, 0x00c0); + ef4_mdio_write(efx, 3, 0xe854, 0x0040); + msleep(50); +} + +static int qt2025c_wait_reset(struct ef4_nic *efx) +{ + int rc; + + rc = qt2025c_wait_heartbeat(efx); + if (rc != 0) + return rc; + + rc = qt2025c_wait_fw_status_good(efx); + if (rc == -ETIMEDOUT) { + /* Bug 17689: occasionally heartbeat starts but firmware status + * code never progresses beyond 0x00. Try again, once, after + * restarting execution of the firmware image. */ + netif_dbg(efx, hw, efx->net_dev, + "bashing QT2025C microcontroller\n"); + qt2025c_restart_firmware(efx); + rc = qt2025c_wait_heartbeat(efx); + if (rc != 0) + return rc; + rc = qt2025c_wait_fw_status_good(efx); + } + + return rc; +} + +static void qt2025c_firmware_id(struct ef4_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + u8 firmware_id[9]; + size_t i; + + for (i = 0; i < sizeof(firmware_id); i++) + firmware_id[i] = ef4_mdio_read(efx, MDIO_MMD_PCS, + PCS_FW_PRODUCT_CODE_1 + i); + netif_info(efx, probe, efx->net_dev, + "QT2025C firmware %xr%d v%d.%d.%d.%d [20%02d-%02d-%02d]\n", + (firmware_id[0] << 8) | firmware_id[1], firmware_id[2], + firmware_id[3] >> 4, firmware_id[3] & 0xf, + firmware_id[4], firmware_id[5], + firmware_id[6], firmware_id[7], firmware_id[8]); + phy_data->firmware_ver = ((firmware_id[3] & 0xf0) << 20) | + ((firmware_id[3] & 0x0f) << 16) | + (firmware_id[4] << 8) | firmware_id[5]; +} + +static void qt2025c_bug17190_workaround(struct ef4_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + + /* The PHY can get stuck in a state where it reports PHY_XS and PMA/PMD + * layers up, but PCS down (no block_lock). If we notice this state + * persisting for a couple of seconds, we switch PMA/PMD loopback + * briefly on and then off again, which is normally sufficient to + * recover it. + */ + if (efx->link_state.up || + !ef4_mdio_links_ok(efx, MDIO_DEVS_PMAPMD | MDIO_DEVS_PHYXS)) { + phy_data->bug17190_in_bad_state = false; + return; + } + + if (!phy_data->bug17190_in_bad_state) { + phy_data->bug17190_in_bad_state = true; + phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; + return; + } + + if (time_after_eq(jiffies, phy_data->bug17190_timer)) { + netif_dbg(efx, hw, efx->net_dev, "bashing QT2025C PMA/PMD\n"); + ef4_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_PMA_CTRL1_LOOPBACK, true); + msleep(100); + ef4_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_PMA_CTRL1_LOOPBACK, false); + phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; + } +} + +static int qt2025c_select_phy_mode(struct ef4_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + struct falcon_board *board = falcon_board(efx); + int reg, rc, i; + uint16_t phy_op_mode; + + /* Only 2.0.1.0+ PHY firmware supports the more optimal SFP+ + * Self-Configure mode. Don't attempt any switching if we encounter + * older firmware. */ + if (phy_data->firmware_ver < 0x02000100) + return 0; + + /* In general we will get optimal behaviour in "SFP+ Self-Configure" + * mode; however, that powers down most of the PHY when no module is + * present, so we must use a different mode (any fixed mode will do) + * to be sure that loopbacks will work. */ + phy_op_mode = (efx->loopback_mode == LOOPBACK_NONE) ? 0x0038 : 0x0020; + + /* Only change mode if really necessary */ + reg = ef4_mdio_read(efx, 1, 0xc319); + if ((reg & 0x0038) == phy_op_mode) + return 0; + netif_dbg(efx, hw, efx->net_dev, "Switching PHY to mode 0x%04x\n", + phy_op_mode); + + /* This sequence replicates the register writes configured in the boot + * EEPROM (including the differences between board revisions), except + * that the operating mode is changed, and the PHY is prevented from + * unnecessarily reloading the main firmware image again. */ + ef4_mdio_write(efx, 1, 0xc300, 0x0000); + /* (Note: this portion of the boot EEPROM sequence, which bit-bashes 9 + * STOPs onto the firmware/module I2C bus to reset it, varies across + * board revisions, as the bus is connected to different GPIO/LED + * outputs on the PHY.) */ + if (board->major == 0 && board->minor < 2) { + ef4_mdio_write(efx, 1, 0xc303, 0x4498); + for (i = 0; i < 9; i++) { + ef4_mdio_write(efx, 1, 0xc303, 0x4488); + ef4_mdio_write(efx, 1, 0xc303, 0x4480); + ef4_mdio_write(efx, 1, 0xc303, 0x4490); + ef4_mdio_write(efx, 1, 0xc303, 0x4498); + } + } else { + ef4_mdio_write(efx, 1, 0xc303, 0x0920); + ef4_mdio_write(efx, 1, 0xd008, 0x0004); + for (i = 0; i < 9; i++) { + ef4_mdio_write(efx, 1, 0xc303, 0x0900); + ef4_mdio_write(efx, 1, 0xd008, 0x0005); + ef4_mdio_write(efx, 1, 0xc303, 0x0920); + ef4_mdio_write(efx, 1, 0xd008, 0x0004); + } + ef4_mdio_write(efx, 1, 0xc303, 0x4900); + } + ef4_mdio_write(efx, 1, 0xc303, 0x4900); + ef4_mdio_write(efx, 1, 0xc302, 0x0004); + ef4_mdio_write(efx, 1, 0xc316, 0x0013); + ef4_mdio_write(efx, 1, 0xc318, 0x0054); + ef4_mdio_write(efx, 1, 0xc319, phy_op_mode); + ef4_mdio_write(efx, 1, 0xc31a, 0x0098); + ef4_mdio_write(efx, 3, 0x0026, 0x0e00); + ef4_mdio_write(efx, 3, 0x0027, 0x0013); + ef4_mdio_write(efx, 3, 0x0028, 0xa528); + ef4_mdio_write(efx, 1, 0xd006, 0x000a); + ef4_mdio_write(efx, 1, 0xd007, 0x0009); + ef4_mdio_write(efx, 1, 0xd008, 0x0004); + /* This additional write is not present in the boot EEPROM. It + * prevents the PHY's internal boot ROM doing another pointless (and + * slow) reload of the firmware image (the microcontroller's code + * memory is not affected by the microcontroller reset). */ + ef4_mdio_write(efx, 1, 0xc317, 0x00ff); + /* PMA/PMD loopback sets RXIN to inverse polarity and the firmware + * restart doesn't reset it. We need to do that ourselves. */ + ef4_mdio_set_flag(efx, 1, PMA_PMD_MODE_REG, + 1 << PMA_PMD_RXIN_SEL_LBN, false); + ef4_mdio_write(efx, 1, 0xc300, 0x0002); + msleep(20); + + /* Restart microcontroller execution of firmware from RAM */ + qt2025c_restart_firmware(efx); + + /* Wait for the microcontroller to be ready again */ + rc = qt2025c_wait_reset(efx); + if (rc < 0) { + netif_err(efx, hw, efx->net_dev, + "PHY microcontroller reset during mode switch " + "timed out\n"); + return rc; + } + + return 0; +} + +static int qt202x_reset_phy(struct ef4_nic *efx) +{ + int rc; + + if (efx->phy_type == PHY_TYPE_QT2025C) { + /* Wait for the reset triggered by falcon_reset_hw() + * to complete */ + rc = qt2025c_wait_reset(efx); + if (rc < 0) + goto fail; + } else { + /* Reset the PHYXS MMD. This is documented as doing + * a complete soft reset. */ + rc = ef4_mdio_reset_mmd(efx, MDIO_MMD_PHYXS, + QT2022C2_MAX_RESET_TIME / + QT2022C2_RESET_WAIT, + QT2022C2_RESET_WAIT); + if (rc < 0) + goto fail; + } + + /* Wait 250ms for the PHY to complete bootup */ + msleep(250); + + falcon_board(efx)->type->init_phy(efx); + + return 0; + + fail: + netif_err(efx, hw, efx->net_dev, "PHY reset timed out\n"); + return rc; +} + +static int qt202x_phy_probe(struct ef4_nic *efx) +{ + struct qt202x_phy_data *phy_data; + + phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; + phy_data->bug17190_in_bad_state = false; + phy_data->bug17190_timer = 0; + + efx->mdio.mmds = QT202X_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + return 0; +} + +static int qt202x_phy_init(struct ef4_nic *efx) +{ + u32 devid; + int rc; + + rc = qt202x_reset_phy(efx); + if (rc) { + netif_err(efx, probe, efx->net_dev, "PHY init failed\n"); + return rc; + } + + devid = ef4_mdio_read_id(efx, MDIO_MMD_PHYXS); + netif_info(efx, probe, efx->net_dev, + "PHY ID reg %x (OUI %06x model %02x revision %x)\n", + devid, ef4_mdio_id_oui(devid), ef4_mdio_id_model(devid), + ef4_mdio_id_rev(devid)); + + if (efx->phy_type == PHY_TYPE_QT2025C) + qt2025c_firmware_id(efx); + + return 0; +} + +static int qt202x_link_ok(struct ef4_nic *efx) +{ + return ef4_mdio_links_ok(efx, QT202X_REQUIRED_DEVS); +} + +static bool qt202x_phy_poll(struct ef4_nic *efx) +{ + bool was_up = efx->link_state.up; + + efx->link_state.up = qt202x_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + + if (efx->phy_type == PHY_TYPE_QT2025C) + qt2025c_bug17190_workaround(efx); + + return efx->link_state.up != was_up; +} + +static int qt202x_phy_reconfigure(struct ef4_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + + if (efx->phy_type == PHY_TYPE_QT2025C) { + int rc = qt2025c_select_phy_mode(efx); + if (rc) + return rc; + + /* There are several different register bits which can + * disable TX (and save power) on direct-attach cables + * or optical transceivers, varying somewhat between + * firmware versions. Only 'static mode' appears to + * cover everything. */ + mdio_set_flag( + &efx->mdio, efx->mdio.prtad, MDIO_MMD_PMAPMD, + PMA_PMD_FTX_CTRL2_REG, 1 << PMA_PMD_FTX_STATIC_LBN, + efx->phy_mode & PHY_MODE_TX_DISABLED || + efx->phy_mode & PHY_MODE_LOW_POWER || + efx->loopback_mode == LOOPBACK_PCS || + efx->loopback_mode == LOOPBACK_PMAPMD); + } else { + /* Reset the PHY when moving from tx off to tx on */ + if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) && + (phy_data->phy_mode & PHY_MODE_TX_DISABLED)) + qt202x_reset_phy(efx); + + ef4_mdio_transmit_disable(efx); + } + + ef4_mdio_phy_reconfigure(efx); + + phy_data->phy_mode = efx->phy_mode; + + return 0; +} + +static void qt202x_phy_get_settings(struct ef4_nic *efx, struct ethtool_cmd *ecmd) +{ + mdio45_ethtool_gset(&efx->mdio, ecmd); +} + +static void qt202x_phy_remove(struct ef4_nic *efx) +{ + /* Free the context block */ + kfree(efx->phy_data); + efx->phy_data = NULL; +} + +static int qt202x_phy_get_module_info(struct ef4_nic *efx, + struct ethtool_modinfo *modinfo) +{ + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + return 0; +} + +static int qt202x_phy_get_module_eeprom(struct ef4_nic *efx, + struct ethtool_eeprom *ee, u8 *data) +{ + int mmd, reg_base, rc, i; + + if (efx->phy_type == PHY_TYPE_QT2025C) { + mmd = MDIO_MMD_PCS; + reg_base = 0xd000; + } else { + mmd = MDIO_MMD_PMAPMD; + reg_base = 0x8007; + } + + for (i = 0; i < ee->len; i++) { + rc = ef4_mdio_read(efx, mmd, reg_base + ee->offset + i); + if (rc < 0) + return rc; + data[i] = rc; + } + + return 0; +} + +const struct ef4_phy_operations falcon_qt202x_phy_ops = { + .probe = qt202x_phy_probe, + .init = qt202x_phy_init, + .reconfigure = qt202x_phy_reconfigure, + .poll = qt202x_phy_poll, + .fini = ef4_port_dummy_op_void, + .remove = qt202x_phy_remove, + .get_settings = qt202x_phy_get_settings, + .set_settings = ef4_mdio_set_settings, + .test_alive = ef4_mdio_test_alive, + .get_module_eeprom = qt202x_phy_get_module_eeprom, + .get_module_info = qt202x_phy_get_module_info, +}; diff --git a/drivers/net/ethernet/sfc/falcon/rx.c b/drivers/net/ethernet/sfc/falcon/rx.c new file mode 100644 index 0000000..250458c --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/rx.c @@ -0,0 +1,974 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "efx.h" +#include "filter.h" +#include "nic.h" +#include "selftest.h" +#include "workarounds.h" + +/* Preferred number of descriptors to fill at once */ +#define EF4_RX_PREFERRED_BATCH 8U + +/* Number of RX buffers to recycle pages for. When creating the RX page recycle + * ring, this number is divided by the number of buffers per page to calculate + * the number of pages to store in the RX page recycle ring. + */ +#define EF4_RECYCLE_RING_SIZE_IOMMU 4096 +#define EF4_RECYCLE_RING_SIZE_NOIOMMU (2 * EF4_RX_PREFERRED_BATCH) + +/* Size of buffer allocated for skb header area. */ +#define EF4_SKB_HEADERS 128u + +/* This is the percentage fill level below which new RX descriptors + * will be added to the RX descriptor ring. + */ +static unsigned int rx_refill_threshold; + +/* Each packet can consume up to ceil(max_frame_len / buffer_size) buffers */ +#define EF4_RX_MAX_FRAGS DIV_ROUND_UP(EF4_MAX_FRAME_LEN(EF4_MAX_MTU), \ + EF4_RX_USR_BUF_SIZE) + +/* + * RX maximum head room required. + * + * This must be at least 1 to prevent overflow, plus one packet-worth + * to allow pipelined receives. + */ +#define EF4_RXD_HEAD_ROOM (1 + EF4_RX_MAX_FRAGS) + +static inline u8 *ef4_rx_buf_va(struct ef4_rx_buffer *buf) +{ + return page_address(buf->page) + buf->page_offset; +} + +static inline u32 ef4_rx_buf_hash(struct ef4_nic *efx, const u8 *eh) +{ +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + return __le32_to_cpup((const __le32 *)(eh + efx->rx_packet_hash_offset)); +#else + const u8 *data = eh + efx->rx_packet_hash_offset; + return (u32)data[0] | + (u32)data[1] << 8 | + (u32)data[2] << 16 | + (u32)data[3] << 24; +#endif +} + +static inline struct ef4_rx_buffer * +ef4_rx_buf_next(struct ef4_rx_queue *rx_queue, struct ef4_rx_buffer *rx_buf) +{ + if (unlikely(rx_buf == ef4_rx_buffer(rx_queue, rx_queue->ptr_mask))) + return ef4_rx_buffer(rx_queue, 0); + else + return rx_buf + 1; +} + +static inline void ef4_sync_rx_buffer(struct ef4_nic *efx, + struct ef4_rx_buffer *rx_buf, + unsigned int len) +{ + dma_sync_single_for_cpu(&efx->pci_dev->dev, rx_buf->dma_addr, len, + DMA_FROM_DEVICE); +} + +void ef4_rx_config_page_split(struct ef4_nic *efx) +{ + efx->rx_page_buf_step = ALIGN(efx->rx_dma_len + efx->rx_ip_align, + EF4_RX_BUF_ALIGNMENT); + efx->rx_bufs_per_page = efx->rx_buffer_order ? 1 : + ((PAGE_SIZE - sizeof(struct ef4_rx_page_state)) / + efx->rx_page_buf_step); + efx->rx_buffer_truesize = (PAGE_SIZE << efx->rx_buffer_order) / + efx->rx_bufs_per_page; + efx->rx_pages_per_batch = DIV_ROUND_UP(EF4_RX_PREFERRED_BATCH, + efx->rx_bufs_per_page); +} + +/* Check the RX page recycle ring for a page that can be reused. */ +static struct page *ef4_reuse_page(struct ef4_rx_queue *rx_queue) +{ + struct ef4_nic *efx = rx_queue->efx; + struct page *page; + struct ef4_rx_page_state *state; + unsigned index; + + index = rx_queue->page_remove & rx_queue->page_ptr_mask; + page = rx_queue->page_ring[index]; + if (page == NULL) + return NULL; + + rx_queue->page_ring[index] = NULL; + /* page_remove cannot exceed page_add. */ + if (rx_queue->page_remove != rx_queue->page_add) + ++rx_queue->page_remove; + + /* If page_count is 1 then we hold the only reference to this page. */ + if (page_count(page) == 1) { + ++rx_queue->page_recycle_count; + return page; + } else { + state = page_address(page); + dma_unmap_page(&efx->pci_dev->dev, state->dma_addr, + PAGE_SIZE << efx->rx_buffer_order, + DMA_FROM_DEVICE); + put_page(page); + ++rx_queue->page_recycle_failed; + } + + return NULL; +} + +/** + * ef4_init_rx_buffers - create EF4_RX_BATCH page-based RX buffers + * + * @rx_queue: Efx RX queue + * + * This allocates a batch of pages, maps them for DMA, and populates + * struct ef4_rx_buffers for each one. Return a negative error code or + * 0 on success. If a single page can be used for multiple buffers, + * then the page will either be inserted fully, or not at all. + */ +static int ef4_init_rx_buffers(struct ef4_rx_queue *rx_queue, bool atomic) +{ + struct ef4_nic *efx = rx_queue->efx; + struct ef4_rx_buffer *rx_buf; + struct page *page; + unsigned int page_offset; + struct ef4_rx_page_state *state; + dma_addr_t dma_addr; + unsigned index, count; + + count = 0; + do { + page = ef4_reuse_page(rx_queue); + if (page == NULL) { + page = alloc_pages(__GFP_COLD | __GFP_COMP | + (atomic ? GFP_ATOMIC : GFP_KERNEL), + efx->rx_buffer_order); + if (unlikely(page == NULL)) + return -ENOMEM; + dma_addr = + dma_map_page(&efx->pci_dev->dev, page, 0, + PAGE_SIZE << efx->rx_buffer_order, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(&efx->pci_dev->dev, + dma_addr))) { + __free_pages(page, efx->rx_buffer_order); + return -EIO; + } + state = page_address(page); + state->dma_addr = dma_addr; + } else { + state = page_address(page); + dma_addr = state->dma_addr; + } + + dma_addr += sizeof(struct ef4_rx_page_state); + page_offset = sizeof(struct ef4_rx_page_state); + + do { + index = rx_queue->added_count & rx_queue->ptr_mask; + rx_buf = ef4_rx_buffer(rx_queue, index); + rx_buf->dma_addr = dma_addr + efx->rx_ip_align; + rx_buf->page = page; + rx_buf->page_offset = page_offset + efx->rx_ip_align; + rx_buf->len = efx->rx_dma_len; + rx_buf->flags = 0; + ++rx_queue->added_count; + get_page(page); + dma_addr += efx->rx_page_buf_step; + page_offset += efx->rx_page_buf_step; + } while (page_offset + efx->rx_page_buf_step <= PAGE_SIZE); + + rx_buf->flags = EF4_RX_BUF_LAST_IN_PAGE; + } while (++count < efx->rx_pages_per_batch); + + return 0; +} + +/* Unmap a DMA-mapped page. This function is only called for the final RX + * buffer in a page. + */ +static void ef4_unmap_rx_buffer(struct ef4_nic *efx, + struct ef4_rx_buffer *rx_buf) +{ + struct page *page = rx_buf->page; + + if (page) { + struct ef4_rx_page_state *state = page_address(page); + dma_unmap_page(&efx->pci_dev->dev, + state->dma_addr, + PAGE_SIZE << efx->rx_buffer_order, + DMA_FROM_DEVICE); + } +} + +static void ef4_free_rx_buffers(struct ef4_rx_queue *rx_queue, + struct ef4_rx_buffer *rx_buf, + unsigned int num_bufs) +{ + do { + if (rx_buf->page) { + put_page(rx_buf->page); + rx_buf->page = NULL; + } + rx_buf = ef4_rx_buf_next(rx_queue, rx_buf); + } while (--num_bufs); +} + +/* Attempt to recycle the page if there is an RX recycle ring; the page can + * only be added if this is the final RX buffer, to prevent pages being used in + * the descriptor ring and appearing in the recycle ring simultaneously. + */ +static void ef4_recycle_rx_page(struct ef4_channel *channel, + struct ef4_rx_buffer *rx_buf) +{ + struct page *page = rx_buf->page; + struct ef4_rx_queue *rx_queue = ef4_channel_get_rx_queue(channel); + struct ef4_nic *efx = rx_queue->efx; + unsigned index; + + /* Only recycle the page after processing the final buffer. */ + if (!(rx_buf->flags & EF4_RX_BUF_LAST_IN_PAGE)) + return; + + index = rx_queue->page_add & rx_queue->page_ptr_mask; + if (rx_queue->page_ring[index] == NULL) { + unsigned read_index = rx_queue->page_remove & + rx_queue->page_ptr_mask; + + /* The next slot in the recycle ring is available, but + * increment page_remove if the read pointer currently + * points here. + */ + if (read_index == index) + ++rx_queue->page_remove; + rx_queue->page_ring[index] = page; + ++rx_queue->page_add; + return; + } + ++rx_queue->page_recycle_full; + ef4_unmap_rx_buffer(efx, rx_buf); + put_page(rx_buf->page); +} + +static void ef4_fini_rx_buffer(struct ef4_rx_queue *rx_queue, + struct ef4_rx_buffer *rx_buf) +{ + /* Release the page reference we hold for the buffer. */ + if (rx_buf->page) + put_page(rx_buf->page); + + /* If this is the last buffer in a page, unmap and free it. */ + if (rx_buf->flags & EF4_RX_BUF_LAST_IN_PAGE) { + ef4_unmap_rx_buffer(rx_queue->efx, rx_buf); + ef4_free_rx_buffers(rx_queue, rx_buf, 1); + } + rx_buf->page = NULL; +} + +/* Recycle the pages that are used by buffers that have just been received. */ +static void ef4_recycle_rx_pages(struct ef4_channel *channel, + struct ef4_rx_buffer *rx_buf, + unsigned int n_frags) +{ + struct ef4_rx_queue *rx_queue = ef4_channel_get_rx_queue(channel); + + do { + ef4_recycle_rx_page(channel, rx_buf); + rx_buf = ef4_rx_buf_next(rx_queue, rx_buf); + } while (--n_frags); +} + +static void ef4_discard_rx_packet(struct ef4_channel *channel, + struct ef4_rx_buffer *rx_buf, + unsigned int n_frags) +{ + struct ef4_rx_queue *rx_queue = ef4_channel_get_rx_queue(channel); + + ef4_recycle_rx_pages(channel, rx_buf, n_frags); + + ef4_free_rx_buffers(rx_queue, rx_buf, n_frags); +} + +/** + * ef4_fast_push_rx_descriptors - push new RX descriptors quickly + * @rx_queue: RX descriptor queue + * + * This will aim to fill the RX descriptor queue up to + * @rx_queue->@max_fill. If there is insufficient atomic + * memory to do so, a slow fill will be scheduled. + * + * The caller must provide serialisation (none is used here). In practise, + * this means this function must run from the NAPI handler, or be called + * when NAPI is disabled. + */ +void ef4_fast_push_rx_descriptors(struct ef4_rx_queue *rx_queue, bool atomic) +{ + struct ef4_nic *efx = rx_queue->efx; + unsigned int fill_level, batch_size; + int space, rc = 0; + + if (!rx_queue->refill_enabled) + return; + + /* Calculate current fill level, and exit if we don't need to fill */ + fill_level = (rx_queue->added_count - rx_queue->removed_count); + EF4_BUG_ON_PARANOID(fill_level > rx_queue->efx->rxq_entries); + if (fill_level >= rx_queue->fast_fill_trigger) + goto out; + + /* Record minimum fill level */ + if (unlikely(fill_level < rx_queue->min_fill)) { + if (fill_level) + rx_queue->min_fill = fill_level; + } + + batch_size = efx->rx_pages_per_batch * efx->rx_bufs_per_page; + space = rx_queue->max_fill - fill_level; + EF4_BUG_ON_PARANOID(space < batch_size); + + netif_vdbg(rx_queue->efx, rx_status, rx_queue->efx->net_dev, + "RX queue %d fast-filling descriptor ring from" + " level %d to level %d\n", + ef4_rx_queue_index(rx_queue), fill_level, + rx_queue->max_fill); + + + do { + rc = ef4_init_rx_buffers(rx_queue, atomic); + if (unlikely(rc)) { + /* Ensure that we don't leave the rx queue empty */ + if (rx_queue->added_count == rx_queue->removed_count) + ef4_schedule_slow_fill(rx_queue); + goto out; + } + } while ((space -= batch_size) >= batch_size); + + netif_vdbg(rx_queue->efx, rx_status, rx_queue->efx->net_dev, + "RX queue %d fast-filled descriptor ring " + "to level %d\n", ef4_rx_queue_index(rx_queue), + rx_queue->added_count - rx_queue->removed_count); + + out: + if (rx_queue->notified_count != rx_queue->added_count) + ef4_nic_notify_rx_desc(rx_queue); +} + +void ef4_rx_slow_fill(unsigned long context) +{ + struct ef4_rx_queue *rx_queue = (struct ef4_rx_queue *)context; + + /* Post an event to cause NAPI to run and refill the queue */ + ef4_nic_generate_fill_event(rx_queue); + ++rx_queue->slow_fill_count; +} + +static void ef4_rx_packet__check_len(struct ef4_rx_queue *rx_queue, + struct ef4_rx_buffer *rx_buf, + int len) +{ + struct ef4_nic *efx = rx_queue->efx; + unsigned max_len = rx_buf->len - efx->type->rx_buffer_padding; + + if (likely(len <= max_len)) + return; + + /* The packet must be discarded, but this is only a fatal error + * if the caller indicated it was + */ + rx_buf->flags |= EF4_RX_PKT_DISCARD; + + if ((len > rx_buf->len) && EF4_WORKAROUND_8071(efx)) { + if (net_ratelimit()) + netif_err(efx, rx_err, efx->net_dev, + " RX queue %d seriously overlength " + "RX event (0x%x > 0x%x+0x%x). Leaking\n", + ef4_rx_queue_index(rx_queue), len, max_len, + efx->type->rx_buffer_padding); + ef4_schedule_reset(efx, RESET_TYPE_RX_RECOVERY); + } else { + if (net_ratelimit()) + netif_err(efx, rx_err, efx->net_dev, + " RX queue %d overlength RX event " + "(0x%x > 0x%x)\n", + ef4_rx_queue_index(rx_queue), len, max_len); + } + + ef4_rx_queue_channel(rx_queue)->n_rx_overlength++; +} + +/* Pass a received packet up through GRO. GRO can handle pages + * regardless of checksum state and skbs with a good checksum. + */ +static void +ef4_rx_packet_gro(struct ef4_channel *channel, struct ef4_rx_buffer *rx_buf, + unsigned int n_frags, u8 *eh) +{ + struct napi_struct *napi = &channel->napi_str; + gro_result_t gro_result; + struct ef4_nic *efx = channel->efx; + struct sk_buff *skb; + + skb = napi_get_frags(napi); + if (unlikely(!skb)) { + struct ef4_rx_queue *rx_queue; + + rx_queue = ef4_channel_get_rx_queue(channel); + ef4_free_rx_buffers(rx_queue, rx_buf, n_frags); + return; + } + + if (efx->net_dev->features & NETIF_F_RXHASH) + skb_set_hash(skb, ef4_rx_buf_hash(efx, eh), + PKT_HASH_TYPE_L3); + skb->ip_summed = ((rx_buf->flags & EF4_RX_PKT_CSUMMED) ? + CHECKSUM_UNNECESSARY : CHECKSUM_NONE); + + for (;;) { + skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, + rx_buf->page, rx_buf->page_offset, + rx_buf->len); + rx_buf->page = NULL; + skb->len += rx_buf->len; + if (skb_shinfo(skb)->nr_frags == n_frags) + break; + + rx_buf = ef4_rx_buf_next(&channel->rx_queue, rx_buf); + } + + skb->data_len = skb->len; + skb->truesize += n_frags * efx->rx_buffer_truesize; + + skb_record_rx_queue(skb, channel->rx_queue.core_index); + + gro_result = napi_gro_frags(napi); + if (gro_result != GRO_DROP) + channel->irq_mod_score += 2; +} + +/* Allocate and construct an SKB around page fragments */ +static struct sk_buff *ef4_rx_mk_skb(struct ef4_channel *channel, + struct ef4_rx_buffer *rx_buf, + unsigned int n_frags, + u8 *eh, int hdr_len) +{ + struct ef4_nic *efx = channel->efx; + struct sk_buff *skb; + + /* Allocate an SKB to store the headers */ + skb = netdev_alloc_skb(efx->net_dev, + efx->rx_ip_align + efx->rx_prefix_size + + hdr_len); + if (unlikely(skb == NULL)) { + atomic_inc(&efx->n_rx_noskb_drops); + return NULL; + } + + EF4_BUG_ON_PARANOID(rx_buf->len < hdr_len); + + memcpy(skb->data + efx->rx_ip_align, eh - efx->rx_prefix_size, + efx->rx_prefix_size + hdr_len); + skb_reserve(skb, efx->rx_ip_align + efx->rx_prefix_size); + __skb_put(skb, hdr_len); + + /* Append the remaining page(s) onto the frag list */ + if (rx_buf->len > hdr_len) { + rx_buf->page_offset += hdr_len; + rx_buf->len -= hdr_len; + + for (;;) { + skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, + rx_buf->page, rx_buf->page_offset, + rx_buf->len); + rx_buf->page = NULL; + skb->len += rx_buf->len; + skb->data_len += rx_buf->len; + if (skb_shinfo(skb)->nr_frags == n_frags) + break; + + rx_buf = ef4_rx_buf_next(&channel->rx_queue, rx_buf); + } + } else { + __free_pages(rx_buf->page, efx->rx_buffer_order); + rx_buf->page = NULL; + n_frags = 0; + } + + skb->truesize += n_frags * efx->rx_buffer_truesize; + + /* Move past the ethernet header */ + skb->protocol = eth_type_trans(skb, efx->net_dev); + + skb_mark_napi_id(skb, &channel->napi_str); + + return skb; +} + +void ef4_rx_packet(struct ef4_rx_queue *rx_queue, unsigned int index, + unsigned int n_frags, unsigned int len, u16 flags) +{ + struct ef4_nic *efx = rx_queue->efx; + struct ef4_channel *channel = ef4_rx_queue_channel(rx_queue); + struct ef4_rx_buffer *rx_buf; + + rx_queue->rx_packets++; + + rx_buf = ef4_rx_buffer(rx_queue, index); + rx_buf->flags |= flags; + + /* Validate the number of fragments and completed length */ + if (n_frags == 1) { + if (!(flags & EF4_RX_PKT_PREFIX_LEN)) + ef4_rx_packet__check_len(rx_queue, rx_buf, len); + } else if (unlikely(n_frags > EF4_RX_MAX_FRAGS) || + unlikely(len <= (n_frags - 1) * efx->rx_dma_len) || + unlikely(len > n_frags * efx->rx_dma_len) || + unlikely(!efx->rx_scatter)) { + /* If this isn't an explicit discard request, either + * the hardware or the driver is broken. + */ + WARN_ON(!(len == 0 && rx_buf->flags & EF4_RX_PKT_DISCARD)); + rx_buf->flags |= EF4_RX_PKT_DISCARD; + } + + netif_vdbg(efx, rx_status, efx->net_dev, + "RX queue %d received ids %x-%x len %d %s%s\n", + ef4_rx_queue_index(rx_queue), index, + (index + n_frags - 1) & rx_queue->ptr_mask, len, + (rx_buf->flags & EF4_RX_PKT_CSUMMED) ? " [SUMMED]" : "", + (rx_buf->flags & EF4_RX_PKT_DISCARD) ? " [DISCARD]" : ""); + + /* Discard packet, if instructed to do so. Process the + * previous receive first. + */ + if (unlikely(rx_buf->flags & EF4_RX_PKT_DISCARD)) { + ef4_rx_flush_packet(channel); + ef4_discard_rx_packet(channel, rx_buf, n_frags); + return; + } + + if (n_frags == 1 && !(flags & EF4_RX_PKT_PREFIX_LEN)) + rx_buf->len = len; + + /* Release and/or sync the DMA mapping - assumes all RX buffers + * consumed in-order per RX queue. + */ + ef4_sync_rx_buffer(efx, rx_buf, rx_buf->len); + + /* Prefetch nice and early so data will (hopefully) be in cache by + * the time we look at it. + */ + prefetch(ef4_rx_buf_va(rx_buf)); + + rx_buf->page_offset += efx->rx_prefix_size; + rx_buf->len -= efx->rx_prefix_size; + + if (n_frags > 1) { + /* Release/sync DMA mapping for additional fragments. + * Fix length for last fragment. + */ + unsigned int tail_frags = n_frags - 1; + + for (;;) { + rx_buf = ef4_rx_buf_next(rx_queue, rx_buf); + if (--tail_frags == 0) + break; + ef4_sync_rx_buffer(efx, rx_buf, efx->rx_dma_len); + } + rx_buf->len = len - (n_frags - 1) * efx->rx_dma_len; + ef4_sync_rx_buffer(efx, rx_buf, rx_buf->len); + } + + /* All fragments have been DMA-synced, so recycle pages. */ + rx_buf = ef4_rx_buffer(rx_queue, index); + ef4_recycle_rx_pages(channel, rx_buf, n_frags); + + /* Pipeline receives so that we give time for packet headers to be + * prefetched into cache. + */ + ef4_rx_flush_packet(channel); + channel->rx_pkt_n_frags = n_frags; + channel->rx_pkt_index = index; +} + +static void ef4_rx_deliver(struct ef4_channel *channel, u8 *eh, + struct ef4_rx_buffer *rx_buf, + unsigned int n_frags) +{ + struct sk_buff *skb; + u16 hdr_len = min_t(u16, rx_buf->len, EF4_SKB_HEADERS); + + skb = ef4_rx_mk_skb(channel, rx_buf, n_frags, eh, hdr_len); + if (unlikely(skb == NULL)) { + struct ef4_rx_queue *rx_queue; + + rx_queue = ef4_channel_get_rx_queue(channel); + ef4_free_rx_buffers(rx_queue, rx_buf, n_frags); + return; + } + skb_record_rx_queue(skb, channel->rx_queue.core_index); + + /* Set the SKB flags */ + skb_checksum_none_assert(skb); + if (likely(rx_buf->flags & EF4_RX_PKT_CSUMMED)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (channel->type->receive_skb) + if (channel->type->receive_skb(channel, skb)) + return; + + /* Pass the packet up */ + netif_receive_skb(skb); +} + +/* Handle a received packet. Second half: Touches packet payload. */ +void __ef4_rx_packet(struct ef4_channel *channel) +{ + struct ef4_nic *efx = channel->efx; + struct ef4_rx_buffer *rx_buf = + ef4_rx_buffer(&channel->rx_queue, channel->rx_pkt_index); + u8 *eh = ef4_rx_buf_va(rx_buf); + + /* Read length from the prefix if necessary. This already + * excludes the length of the prefix itself. + */ + if (rx_buf->flags & EF4_RX_PKT_PREFIX_LEN) + rx_buf->len = le16_to_cpup((__le16 *) + (eh + efx->rx_packet_len_offset)); + + /* If we're in loopback test, then pass the packet directly to the + * loopback layer, and free the rx_buf here + */ + if (unlikely(efx->loopback_selftest)) { + struct ef4_rx_queue *rx_queue; + + ef4_loopback_rx_packet(efx, eh, rx_buf->len); + rx_queue = ef4_channel_get_rx_queue(channel); + ef4_free_rx_buffers(rx_queue, rx_buf, + channel->rx_pkt_n_frags); + goto out; + } + + if (unlikely(!(efx->net_dev->features & NETIF_F_RXCSUM))) + rx_buf->flags &= ~EF4_RX_PKT_CSUMMED; + + if ((rx_buf->flags & EF4_RX_PKT_TCP) && !channel->type->receive_skb && + !ef4_channel_busy_polling(channel)) + ef4_rx_packet_gro(channel, rx_buf, channel->rx_pkt_n_frags, eh); + else + ef4_rx_deliver(channel, eh, rx_buf, channel->rx_pkt_n_frags); +out: + channel->rx_pkt_n_frags = 0; +} + +int ef4_probe_rx_queue(struct ef4_rx_queue *rx_queue) +{ + struct ef4_nic *efx = rx_queue->efx; + unsigned int entries; + int rc; + + /* Create the smallest power-of-two aligned ring */ + entries = max(roundup_pow_of_two(efx->rxq_entries), EF4_MIN_DMAQ_SIZE); + EF4_BUG_ON_PARANOID(entries > EF4_MAX_DMAQ_SIZE); + rx_queue->ptr_mask = entries - 1; + + netif_dbg(efx, probe, efx->net_dev, + "creating RX queue %d size %#x mask %#x\n", + ef4_rx_queue_index(rx_queue), efx->rxq_entries, + rx_queue->ptr_mask); + + /* Allocate RX buffers */ + rx_queue->buffer = kcalloc(entries, sizeof(*rx_queue->buffer), + GFP_KERNEL); + if (!rx_queue->buffer) + return -ENOMEM; + + rc = ef4_nic_probe_rx(rx_queue); + if (rc) { + kfree(rx_queue->buffer); + rx_queue->buffer = NULL; + } + + return rc; +} + +static void ef4_init_rx_recycle_ring(struct ef4_nic *efx, + struct ef4_rx_queue *rx_queue) +{ + unsigned int bufs_in_recycle_ring, page_ring_size; + + /* Set the RX recycle ring size */ +#ifdef CONFIG_PPC64 + bufs_in_recycle_ring = EF4_RECYCLE_RING_SIZE_IOMMU; +#else + if (iommu_present(&pci_bus_type)) + bufs_in_recycle_ring = EF4_RECYCLE_RING_SIZE_IOMMU; + else + bufs_in_recycle_ring = EF4_RECYCLE_RING_SIZE_NOIOMMU; +#endif /* CONFIG_PPC64 */ + + page_ring_size = roundup_pow_of_two(bufs_in_recycle_ring / + efx->rx_bufs_per_page); + rx_queue->page_ring = kcalloc(page_ring_size, + sizeof(*rx_queue->page_ring), GFP_KERNEL); + rx_queue->page_ptr_mask = page_ring_size - 1; +} + +void ef4_init_rx_queue(struct ef4_rx_queue *rx_queue) +{ + struct ef4_nic *efx = rx_queue->efx; + unsigned int max_fill, trigger, max_trigger; + + netif_dbg(rx_queue->efx, drv, rx_queue->efx->net_dev, + "initialising RX queue %d\n", ef4_rx_queue_index(rx_queue)); + + /* Initialise ptr fields */ + rx_queue->added_count = 0; + rx_queue->notified_count = 0; + rx_queue->removed_count = 0; + rx_queue->min_fill = -1U; + ef4_init_rx_recycle_ring(efx, rx_queue); + + rx_queue->page_remove = 0; + rx_queue->page_add = rx_queue->page_ptr_mask + 1; + rx_queue->page_recycle_count = 0; + rx_queue->page_recycle_failed = 0; + rx_queue->page_recycle_full = 0; + + /* Initialise limit fields */ + max_fill = efx->rxq_entries - EF4_RXD_HEAD_ROOM; + max_trigger = + max_fill - efx->rx_pages_per_batch * efx->rx_bufs_per_page; + if (rx_refill_threshold != 0) { + trigger = max_fill * min(rx_refill_threshold, 100U) / 100U; + if (trigger > max_trigger) + trigger = max_trigger; + } else { + trigger = max_trigger; + } + + rx_queue->max_fill = max_fill; + rx_queue->fast_fill_trigger = trigger; + rx_queue->refill_enabled = true; + + /* Set up RX descriptor ring */ + ef4_nic_init_rx(rx_queue); +} + +void ef4_fini_rx_queue(struct ef4_rx_queue *rx_queue) +{ + int i; + struct ef4_nic *efx = rx_queue->efx; + struct ef4_rx_buffer *rx_buf; + + netif_dbg(rx_queue->efx, drv, rx_queue->efx->net_dev, + "shutting down RX queue %d\n", ef4_rx_queue_index(rx_queue)); + + del_timer_sync(&rx_queue->slow_fill); + + /* Release RX buffers from the current read ptr to the write ptr */ + if (rx_queue->buffer) { + for (i = rx_queue->removed_count; i < rx_queue->added_count; + i++) { + unsigned index = i & rx_queue->ptr_mask; + rx_buf = ef4_rx_buffer(rx_queue, index); + ef4_fini_rx_buffer(rx_queue, rx_buf); + } + } + + /* Unmap and release the pages in the recycle ring. Remove the ring. */ + for (i = 0; i <= rx_queue->page_ptr_mask; i++) { + struct page *page = rx_queue->page_ring[i]; + struct ef4_rx_page_state *state; + + if (page == NULL) + continue; + + state = page_address(page); + dma_unmap_page(&efx->pci_dev->dev, state->dma_addr, + PAGE_SIZE << efx->rx_buffer_order, + DMA_FROM_DEVICE); + put_page(page); + } + kfree(rx_queue->page_ring); + rx_queue->page_ring = NULL; +} + +void ef4_remove_rx_queue(struct ef4_rx_queue *rx_queue) +{ + netif_dbg(rx_queue->efx, drv, rx_queue->efx->net_dev, + "destroying RX queue %d\n", ef4_rx_queue_index(rx_queue)); + + ef4_nic_remove_rx(rx_queue); + + kfree(rx_queue->buffer); + rx_queue->buffer = NULL; +} + + +module_param(rx_refill_threshold, uint, 0444); +MODULE_PARM_DESC(rx_refill_threshold, + "RX descriptor ring refill threshold (%)"); + +#ifdef CONFIG_RFS_ACCEL + +int ef4_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, + u16 rxq_index, u32 flow_id) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct ef4_channel *channel; + struct ef4_filter_spec spec; + struct flow_keys fk; + int rc; + + if (flow_id == RPS_FLOW_ID_INVALID) + return -EINVAL; + + if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) + return -EPROTONOSUPPORT; + + if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6)) + return -EPROTONOSUPPORT; + if (fk.control.flags & FLOW_DIS_IS_FRAGMENT) + return -EPROTONOSUPPORT; + + ef4_filter_init_rx(&spec, EF4_FILTER_PRI_HINT, + efx->rx_scatter ? EF4_FILTER_FLAG_RX_SCATTER : 0, + rxq_index); + spec.match_flags = + EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_IP_PROTO | + EF4_FILTER_MATCH_LOC_HOST | EF4_FILTER_MATCH_LOC_PORT | + EF4_FILTER_MATCH_REM_HOST | EF4_FILTER_MATCH_REM_PORT; + spec.ether_type = fk.basic.n_proto; + spec.ip_proto = fk.basic.ip_proto; + + if (fk.basic.n_proto == htons(ETH_P_IP)) { + spec.rem_host[0] = fk.addrs.v4addrs.src; + spec.loc_host[0] = fk.addrs.v4addrs.dst; + } else { + memcpy(spec.rem_host, &fk.addrs.v6addrs.src, sizeof(struct in6_addr)); + memcpy(spec.loc_host, &fk.addrs.v6addrs.dst, sizeof(struct in6_addr)); + } + + spec.rem_port = fk.ports.src; + spec.loc_port = fk.ports.dst; + + rc = efx->type->filter_rfs_insert(efx, &spec); + if (rc < 0) + return rc; + + /* Remember this so we can check whether to expire the filter later */ + channel = ef4_get_channel(efx, rxq_index); + channel->rps_flow_id[rc] = flow_id; + ++channel->rfs_filters_added; + + if (spec.ether_type == htons(ETH_P_IP)) + netif_info(efx, rx_status, efx->net_dev, + "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", + (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", + spec.rem_host, ntohs(spec.rem_port), spec.loc_host, + ntohs(spec.loc_port), rxq_index, flow_id, rc); + else + netif_info(efx, rx_status, efx->net_dev, + "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d]\n", + (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", + spec.rem_host, ntohs(spec.rem_port), spec.loc_host, + ntohs(spec.loc_port), rxq_index, flow_id, rc); + + return rc; +} + +bool __ef4_filter_rfs_expire(struct ef4_nic *efx, unsigned int quota) +{ + bool (*expire_one)(struct ef4_nic *efx, u32 flow_id, unsigned int index); + unsigned int channel_idx, index, size; + u32 flow_id; + + if (!spin_trylock_bh(&efx->filter_lock)) + return false; + + expire_one = efx->type->filter_rfs_expire_one; + channel_idx = efx->rps_expire_channel; + index = efx->rps_expire_index; + size = efx->type->max_rx_ip_filters; + while (quota--) { + struct ef4_channel *channel = ef4_get_channel(efx, channel_idx); + flow_id = channel->rps_flow_id[index]; + + if (flow_id != RPS_FLOW_ID_INVALID && + expire_one(efx, flow_id, index)) { + netif_info(efx, rx_status, efx->net_dev, + "expired filter %d [queue %u flow %u]\n", + index, channel_idx, flow_id); + channel->rps_flow_id[index] = RPS_FLOW_ID_INVALID; + } + if (++index == size) { + if (++channel_idx == efx->n_channels) + channel_idx = 0; + index = 0; + } + } + efx->rps_expire_channel = channel_idx; + efx->rps_expire_index = index; + + spin_unlock_bh(&efx->filter_lock); + return true; +} + +#endif /* CONFIG_RFS_ACCEL */ + +/** + * ef4_filter_is_mc_recipient - test whether spec is a multicast recipient + * @spec: Specification to test + * + * Return: %true if the specification is a non-drop RX filter that + * matches a local MAC address I/G bit value of 1 or matches a local + * IPv4 or IPv6 address value in the respective multicast address + * range. Otherwise %false. + */ +bool ef4_filter_is_mc_recipient(const struct ef4_filter_spec *spec) +{ + if (!(spec->flags & EF4_FILTER_FLAG_RX) || + spec->dmaq_id == EF4_FILTER_RX_DMAQ_ID_DROP) + return false; + + if (spec->match_flags & + (EF4_FILTER_MATCH_LOC_MAC | EF4_FILTER_MATCH_LOC_MAC_IG) && + is_multicast_ether_addr(spec->loc_mac)) + return true; + + if ((spec->match_flags & + (EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_LOC_HOST)) == + (EF4_FILTER_MATCH_ETHER_TYPE | EF4_FILTER_MATCH_LOC_HOST)) { + if (spec->ether_type == htons(ETH_P_IP) && + ipv4_is_multicast(spec->loc_host[0])) + return true; + if (spec->ether_type == htons(ETH_P_IPV6) && + ((const u8 *)spec->loc_host)[0] == 0xff) + return true; + } + + return false; +} diff --git a/drivers/net/ethernet/sfc/falcon/selftest.c b/drivers/net/ethernet/sfc/falcon/selftest.c new file mode 100644 index 0000000..92bc34c --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/selftest.c @@ -0,0 +1,808 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2012 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "efx.h" +#include "nic.h" +#include "selftest.h" +#include "workarounds.h" + +/* IRQ latency can be enormous because: + * - All IRQs may be disabled on a CPU for a *long* time by e.g. a + * slow serial console or an old IDE driver doing error recovery + * - The PREEMPT_RT patches mostly deal with this, but also allow a + * tasklet or normal task to be given higher priority than our IRQ + * threads + * Try to avoid blaming the hardware for this. + */ +#define IRQ_TIMEOUT HZ + +/* + * Loopback test packet structure + * + * The self-test should stress every RSS vector, and unfortunately + * Falcon only performs RSS on TCP/UDP packets. + */ +struct ef4_loopback_payload { + struct ethhdr header; + struct iphdr ip; + struct udphdr udp; + __be16 iteration; + char msg[64]; +} __packed; + +/* Loopback test source MAC address */ +static const u8 payload_source[ETH_ALEN] __aligned(2) = { + 0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b, +}; + +static const char payload_msg[] = + "Hello world! This is an Efx loopback test in progress!"; + +/* Interrupt mode names */ +static const unsigned int ef4_interrupt_mode_max = EF4_INT_MODE_MAX; +static const char *const ef4_interrupt_mode_names[] = { + [EF4_INT_MODE_MSIX] = "MSI-X", + [EF4_INT_MODE_MSI] = "MSI", + [EF4_INT_MODE_LEGACY] = "legacy", +}; +#define INT_MODE(efx) \ + STRING_TABLE_LOOKUP(efx->interrupt_mode, ef4_interrupt_mode) + +/** + * ef4_loopback_state - persistent state during a loopback selftest + * @flush: Drop all packets in ef4_loopback_rx_packet + * @packet_count: Number of packets being used in this test + * @skbs: An array of skbs transmitted + * @offload_csum: Checksums are being offloaded + * @rx_good: RX good packet count + * @rx_bad: RX bad packet count + * @payload: Payload used in tests + */ +struct ef4_loopback_state { + bool flush; + int packet_count; + struct sk_buff **skbs; + bool offload_csum; + atomic_t rx_good; + atomic_t rx_bad; + struct ef4_loopback_payload payload; +}; + +/* How long to wait for all the packets to arrive (in ms) */ +#define LOOPBACK_TIMEOUT_MS 1000 + +/************************************************************************** + * + * MII, NVRAM and register tests + * + **************************************************************************/ + +static int ef4_test_phy_alive(struct ef4_nic *efx, struct ef4_self_tests *tests) +{ + int rc = 0; + + if (efx->phy_op->test_alive) { + rc = efx->phy_op->test_alive(efx); + tests->phy_alive = rc ? -1 : 1; + } + + return rc; +} + +static int ef4_test_nvram(struct ef4_nic *efx, struct ef4_self_tests *tests) +{ + int rc = 0; + + if (efx->type->test_nvram) { + rc = efx->type->test_nvram(efx); + if (rc == -EPERM) + rc = 0; + else + tests->nvram = rc ? -1 : 1; + } + + return rc; +} + +/************************************************************************** + * + * Interrupt and event queue testing + * + **************************************************************************/ + +/* Test generation and receipt of interrupts */ +static int ef4_test_interrupts(struct ef4_nic *efx, + struct ef4_self_tests *tests) +{ + unsigned long timeout, wait; + int cpu; + int rc; + + netif_dbg(efx, drv, efx->net_dev, "testing interrupts\n"); + tests->interrupt = -1; + + rc = ef4_nic_irq_test_start(efx); + if (rc == -ENOTSUPP) { + netif_dbg(efx, drv, efx->net_dev, + "direct interrupt testing not supported\n"); + tests->interrupt = 0; + return 0; + } + + timeout = jiffies + IRQ_TIMEOUT; + wait = 1; + + /* Wait for arrival of test interrupt. */ + netif_dbg(efx, drv, efx->net_dev, "waiting for test interrupt\n"); + do { + schedule_timeout_uninterruptible(wait); + cpu = ef4_nic_irq_test_irq_cpu(efx); + if (cpu >= 0) + goto success; + wait *= 2; + } while (time_before(jiffies, timeout)); + + netif_err(efx, drv, efx->net_dev, "timed out waiting for interrupt\n"); + return -ETIMEDOUT; + + success: + netif_dbg(efx, drv, efx->net_dev, "%s test interrupt seen on CPU%d\n", + INT_MODE(efx), cpu); + tests->interrupt = 1; + return 0; +} + +/* Test generation and receipt of interrupting events */ +static int ef4_test_eventq_irq(struct ef4_nic *efx, + struct ef4_self_tests *tests) +{ + struct ef4_channel *channel; + unsigned int read_ptr[EF4_MAX_CHANNELS]; + unsigned long napi_ran = 0, dma_pend = 0, int_pend = 0; + unsigned long timeout, wait; + + BUILD_BUG_ON(EF4_MAX_CHANNELS > BITS_PER_LONG); + + ef4_for_each_channel(channel, efx) { + read_ptr[channel->channel] = channel->eventq_read_ptr; + set_bit(channel->channel, &dma_pend); + set_bit(channel->channel, &int_pend); + ef4_nic_event_test_start(channel); + } + + timeout = jiffies + IRQ_TIMEOUT; + wait = 1; + + /* Wait for arrival of interrupts. NAPI processing may or may + * not complete in time, but we can cope in any case. + */ + do { + schedule_timeout_uninterruptible(wait); + + ef4_for_each_channel(channel, efx) { + ef4_stop_eventq(channel); + if (channel->eventq_read_ptr != + read_ptr[channel->channel]) { + set_bit(channel->channel, &napi_ran); + clear_bit(channel->channel, &dma_pend); + clear_bit(channel->channel, &int_pend); + } else { + if (ef4_nic_event_present(channel)) + clear_bit(channel->channel, &dma_pend); + if (ef4_nic_event_test_irq_cpu(channel) >= 0) + clear_bit(channel->channel, &int_pend); + } + ef4_start_eventq(channel); + } + + wait *= 2; + } while ((dma_pend || int_pend) && time_before(jiffies, timeout)); + + ef4_for_each_channel(channel, efx) { + bool dma_seen = !test_bit(channel->channel, &dma_pend); + bool int_seen = !test_bit(channel->channel, &int_pend); + + tests->eventq_dma[channel->channel] = dma_seen ? 1 : -1; + tests->eventq_int[channel->channel] = int_seen ? 1 : -1; + + if (dma_seen && int_seen) { + netif_dbg(efx, drv, efx->net_dev, + "channel %d event queue passed (with%s NAPI)\n", + channel->channel, + test_bit(channel->channel, &napi_ran) ? + "" : "out"); + } else { + /* Report failure and whether either interrupt or DMA + * worked + */ + netif_err(efx, drv, efx->net_dev, + "channel %d timed out waiting for event queue\n", + channel->channel); + if (int_seen) + netif_err(efx, drv, efx->net_dev, + "channel %d saw interrupt " + "during event queue test\n", + channel->channel); + if (dma_seen) + netif_err(efx, drv, efx->net_dev, + "channel %d event was generated, but " + "failed to trigger an interrupt\n", + channel->channel); + } + } + + return (dma_pend || int_pend) ? -ETIMEDOUT : 0; +} + +static int ef4_test_phy(struct ef4_nic *efx, struct ef4_self_tests *tests, + unsigned flags) +{ + int rc; + + if (!efx->phy_op->run_tests) + return 0; + + mutex_lock(&efx->mac_lock); + rc = efx->phy_op->run_tests(efx, tests->phy_ext, flags); + mutex_unlock(&efx->mac_lock); + if (rc == -EPERM) + rc = 0; + else + netif_info(efx, drv, efx->net_dev, + "%s phy selftest\n", rc ? "Failed" : "Passed"); + + return rc; +} + +/************************************************************************** + * + * Loopback testing + * NB Only one loopback test can be executing concurrently. + * + **************************************************************************/ + +/* Loopback test RX callback + * This is called for each received packet during loopback testing. + */ +void ef4_loopback_rx_packet(struct ef4_nic *efx, + const char *buf_ptr, int pkt_len) +{ + struct ef4_loopback_state *state = efx->loopback_selftest; + struct ef4_loopback_payload *received; + struct ef4_loopback_payload *payload; + + BUG_ON(!buf_ptr); + + /* If we are just flushing, then drop the packet */ + if ((state == NULL) || state->flush) + return; + + payload = &state->payload; + + received = (struct ef4_loopback_payload *) buf_ptr; + received->ip.saddr = payload->ip.saddr; + if (state->offload_csum) + received->ip.check = payload->ip.check; + + /* Check that header exists */ + if (pkt_len < sizeof(received->header)) { + netif_err(efx, drv, efx->net_dev, + "saw runt RX packet (length %d) in %s loopback " + "test\n", pkt_len, LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that the ethernet header exists */ + if (memcmp(&received->header, &payload->header, ETH_HLEN) != 0) { + netif_err(efx, drv, efx->net_dev, + "saw non-loopback RX packet in %s loopback test\n", + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check packet length */ + if (pkt_len != sizeof(*payload)) { + netif_err(efx, drv, efx->net_dev, + "saw incorrect RX packet length %d (wanted %d) in " + "%s loopback test\n", pkt_len, (int)sizeof(*payload), + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that IP header matches */ + if (memcmp(&received->ip, &payload->ip, sizeof(payload->ip)) != 0) { + netif_err(efx, drv, efx->net_dev, + "saw corrupted IP header in %s loopback test\n", + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that msg and padding matches */ + if (memcmp(&received->msg, &payload->msg, sizeof(received->msg)) != 0) { + netif_err(efx, drv, efx->net_dev, + "saw corrupted RX packet in %s loopback test\n", + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that iteration matches */ + if (received->iteration != payload->iteration) { + netif_err(efx, drv, efx->net_dev, + "saw RX packet from iteration %d (wanted %d) in " + "%s loopback test\n", ntohs(received->iteration), + ntohs(payload->iteration), LOOPBACK_MODE(efx)); + goto err; + } + + /* Increase correct RX count */ + netif_vdbg(efx, drv, efx->net_dev, + "got loopback RX in %s loopback test\n", LOOPBACK_MODE(efx)); + + atomic_inc(&state->rx_good); + return; + + err: +#ifdef DEBUG + if (atomic_read(&state->rx_bad) == 0) { + netif_err(efx, drv, efx->net_dev, "received packet:\n"); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, + buf_ptr, pkt_len, 0); + netif_err(efx, drv, efx->net_dev, "expected packet:\n"); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, + &state->payload, sizeof(state->payload), 0); + } +#endif + atomic_inc(&state->rx_bad); +} + +/* Initialise an ef4_selftest_state for a new iteration */ +static void ef4_iterate_state(struct ef4_nic *efx) +{ + struct ef4_loopback_state *state = efx->loopback_selftest; + struct net_device *net_dev = efx->net_dev; + struct ef4_loopback_payload *payload = &state->payload; + + /* Initialise the layerII header */ + ether_addr_copy((u8 *)&payload->header.h_dest, net_dev->dev_addr); + ether_addr_copy((u8 *)&payload->header.h_source, payload_source); + payload->header.h_proto = htons(ETH_P_IP); + + /* saddr set later and used as incrementing count */ + payload->ip.daddr = htonl(INADDR_LOOPBACK); + payload->ip.ihl = 5; + payload->ip.check = (__force __sum16) htons(0xdead); + payload->ip.tot_len = htons(sizeof(*payload) - sizeof(struct ethhdr)); + payload->ip.version = IPVERSION; + payload->ip.protocol = IPPROTO_UDP; + + /* Initialise udp header */ + payload->udp.source = 0; + payload->udp.len = htons(sizeof(*payload) - sizeof(struct ethhdr) - + sizeof(struct iphdr)); + payload->udp.check = 0; /* checksum ignored */ + + /* Fill out payload */ + payload->iteration = htons(ntohs(payload->iteration) + 1); + memcpy(&payload->msg, payload_msg, sizeof(payload_msg)); + + /* Fill out remaining state members */ + atomic_set(&state->rx_good, 0); + atomic_set(&state->rx_bad, 0); + smp_wmb(); +} + +static int ef4_begin_loopback(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + struct ef4_loopback_state *state = efx->loopback_selftest; + struct ef4_loopback_payload *payload; + struct sk_buff *skb; + int i; + netdev_tx_t rc; + + /* Transmit N copies of buffer */ + for (i = 0; i < state->packet_count; i++) { + /* Allocate an skb, holding an extra reference for + * transmit completion counting */ + skb = alloc_skb(sizeof(state->payload), GFP_KERNEL); + if (!skb) + return -ENOMEM; + state->skbs[i] = skb; + skb_get(skb); + + /* Copy the payload in, incrementing the source address to + * exercise the rss vectors */ + payload = ((struct ef4_loopback_payload *) + skb_put(skb, sizeof(state->payload))); + memcpy(payload, &state->payload, sizeof(state->payload)); + payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2)); + + /* Ensure everything we've written is visible to the + * interrupt handler. */ + smp_wmb(); + + netif_tx_lock_bh(efx->net_dev); + rc = ef4_enqueue_skb(tx_queue, skb); + netif_tx_unlock_bh(efx->net_dev); + + if (rc != NETDEV_TX_OK) { + netif_err(efx, drv, efx->net_dev, + "TX queue %d could not transmit packet %d of " + "%d in %s loopback test\n", tx_queue->queue, + i + 1, state->packet_count, + LOOPBACK_MODE(efx)); + + /* Defer cleaning up the other skbs for the caller */ + kfree_skb(skb); + return -EPIPE; + } + } + + return 0; +} + +static int ef4_poll_loopback(struct ef4_nic *efx) +{ + struct ef4_loopback_state *state = efx->loopback_selftest; + + return atomic_read(&state->rx_good) == state->packet_count; +} + +static int ef4_end_loopback(struct ef4_tx_queue *tx_queue, + struct ef4_loopback_self_tests *lb_tests) +{ + struct ef4_nic *efx = tx_queue->efx; + struct ef4_loopback_state *state = efx->loopback_selftest; + struct sk_buff *skb; + int tx_done = 0, rx_good, rx_bad; + int i, rc = 0; + + netif_tx_lock_bh(efx->net_dev); + + /* Count the number of tx completions, and decrement the refcnt. Any + * skbs not already completed will be free'd when the queue is flushed */ + for (i = 0; i < state->packet_count; i++) { + skb = state->skbs[i]; + if (skb && !skb_shared(skb)) + ++tx_done; + dev_kfree_skb(skb); + } + + netif_tx_unlock_bh(efx->net_dev); + + /* Check TX completion and received packet counts */ + rx_good = atomic_read(&state->rx_good); + rx_bad = atomic_read(&state->rx_bad); + if (tx_done != state->packet_count) { + /* Don't free the skbs; they will be picked up on TX + * overflow or channel teardown. + */ + netif_err(efx, drv, efx->net_dev, + "TX queue %d saw only %d out of an expected %d " + "TX completion events in %s loopback test\n", + tx_queue->queue, tx_done, state->packet_count, + LOOPBACK_MODE(efx)); + rc = -ETIMEDOUT; + /* Allow to fall through so we see the RX errors as well */ + } + + /* We may always be up to a flush away from our desired packet total */ + if (rx_good != state->packet_count) { + netif_dbg(efx, drv, efx->net_dev, + "TX queue %d saw only %d out of an expected %d " + "received packets in %s loopback test\n", + tx_queue->queue, rx_good, state->packet_count, + LOOPBACK_MODE(efx)); + rc = -ETIMEDOUT; + /* Fall through */ + } + + /* Update loopback test structure */ + lb_tests->tx_sent[tx_queue->queue] += state->packet_count; + lb_tests->tx_done[tx_queue->queue] += tx_done; + lb_tests->rx_good += rx_good; + lb_tests->rx_bad += rx_bad; + + return rc; +} + +static int +ef4_test_loopback(struct ef4_tx_queue *tx_queue, + struct ef4_loopback_self_tests *lb_tests) +{ + struct ef4_nic *efx = tx_queue->efx; + struct ef4_loopback_state *state = efx->loopback_selftest; + int i, begin_rc, end_rc; + + for (i = 0; i < 3; i++) { + /* Determine how many packets to send */ + state->packet_count = efx->txq_entries / 3; + state->packet_count = min(1 << (i << 2), state->packet_count); + state->skbs = kcalloc(state->packet_count, + sizeof(state->skbs[0]), GFP_KERNEL); + if (!state->skbs) + return -ENOMEM; + state->flush = false; + + netif_dbg(efx, drv, efx->net_dev, + "TX queue %d testing %s loopback with %d packets\n", + tx_queue->queue, LOOPBACK_MODE(efx), + state->packet_count); + + ef4_iterate_state(efx); + begin_rc = ef4_begin_loopback(tx_queue); + + /* This will normally complete very quickly, but be + * prepared to wait much longer. */ + msleep(1); + if (!ef4_poll_loopback(efx)) { + msleep(LOOPBACK_TIMEOUT_MS); + ef4_poll_loopback(efx); + } + + end_rc = ef4_end_loopback(tx_queue, lb_tests); + kfree(state->skbs); + + if (begin_rc || end_rc) { + /* Wait a while to ensure there are no packets + * floating around after a failure. */ + schedule_timeout_uninterruptible(HZ / 10); + return begin_rc ? begin_rc : end_rc; + } + } + + netif_dbg(efx, drv, efx->net_dev, + "TX queue %d passed %s loopback test with a burst length " + "of %d packets\n", tx_queue->queue, LOOPBACK_MODE(efx), + state->packet_count); + + return 0; +} + +/* Wait for link up. On Falcon, we would prefer to rely on ef4_monitor, but + * any contention on the mac lock (via e.g. ef4_mac_mcast_work) causes it + * to delay and retry. Therefore, it's safer to just poll directly. Wait + * for link up and any faults to dissipate. */ +static int ef4_wait_for_link(struct ef4_nic *efx) +{ + struct ef4_link_state *link_state = &efx->link_state; + int count, link_up_count = 0; + bool link_up; + + for (count = 0; count < 40; count++) { + schedule_timeout_uninterruptible(HZ / 10); + + if (efx->type->monitor != NULL) { + mutex_lock(&efx->mac_lock); + efx->type->monitor(efx); + mutex_unlock(&efx->mac_lock); + } + + mutex_lock(&efx->mac_lock); + link_up = link_state->up; + if (link_up) + link_up = !efx->type->check_mac_fault(efx); + mutex_unlock(&efx->mac_lock); + + if (link_up) { + if (++link_up_count == 2) + return 0; + } else { + link_up_count = 0; + } + } + + return -ETIMEDOUT; +} + +static int ef4_test_loopbacks(struct ef4_nic *efx, struct ef4_self_tests *tests, + unsigned int loopback_modes) +{ + enum ef4_loopback_mode mode; + struct ef4_loopback_state *state; + struct ef4_channel *channel = + ef4_get_channel(efx, efx->tx_channel_offset); + struct ef4_tx_queue *tx_queue; + int rc = 0; + + /* Set the port loopback_selftest member. From this point on + * all received packets will be dropped. Mark the state as + * "flushing" so all inflight packets are dropped */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + BUG_ON(efx->loopback_selftest); + state->flush = true; + efx->loopback_selftest = state; + + /* Test all supported loopback modes */ + for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { + if (!(loopback_modes & (1 << mode))) + continue; + + /* Move the port into the specified loopback mode. */ + state->flush = true; + mutex_lock(&efx->mac_lock); + efx->loopback_mode = mode; + rc = __ef4_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "unable to move into %s loopback\n", + LOOPBACK_MODE(efx)); + goto out; + } + + rc = ef4_wait_for_link(efx); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "loopback %s never came up\n", + LOOPBACK_MODE(efx)); + goto out; + } + + /* Test all enabled types of TX queue */ + ef4_for_each_channel_tx_queue(tx_queue, channel) { + state->offload_csum = (tx_queue->queue & + EF4_TXQ_TYPE_OFFLOAD); + rc = ef4_test_loopback(tx_queue, + &tests->loopback[mode]); + if (rc) + goto out; + } + } + + out: + /* Remove the flush. The caller will remove the loopback setting */ + state->flush = true; + efx->loopback_selftest = NULL; + wmb(); + kfree(state); + + if (rc == -EPERM) + rc = 0; + + return rc; +} + +/************************************************************************** + * + * Entry point + * + *************************************************************************/ + +int ef4_selftest(struct ef4_nic *efx, struct ef4_self_tests *tests, + unsigned flags) +{ + enum ef4_loopback_mode loopback_mode = efx->loopback_mode; + int phy_mode = efx->phy_mode; + int rc_test = 0, rc_reset, rc; + + ef4_selftest_async_cancel(efx); + + /* Online (i.e. non-disruptive) testing + * This checks interrupt generation, event delivery and PHY presence. */ + + rc = ef4_test_phy_alive(efx, tests); + if (rc && !rc_test) + rc_test = rc; + + rc = ef4_test_nvram(efx, tests); + if (rc && !rc_test) + rc_test = rc; + + rc = ef4_test_interrupts(efx, tests); + if (rc && !rc_test) + rc_test = rc; + + rc = ef4_test_eventq_irq(efx, tests); + if (rc && !rc_test) + rc_test = rc; + + if (rc_test) + return rc_test; + + if (!(flags & ETH_TEST_FL_OFFLINE)) + return ef4_test_phy(efx, tests, flags); + + /* Offline (i.e. disruptive) testing + * This checks MAC and PHY loopback on the specified port. */ + + /* Detach the device so the kernel doesn't transmit during the + * loopback test and the watchdog timeout doesn't fire. + */ + ef4_device_detach_sync(efx); + + if (efx->type->test_chip) { + rc_reset = efx->type->test_chip(efx, tests); + if (rc_reset) { + netif_err(efx, hw, efx->net_dev, + "Unable to recover from chip test\n"); + ef4_schedule_reset(efx, RESET_TYPE_DISABLE); + return rc_reset; + } + + if ((tests->memory < 0 || tests->registers < 0) && !rc_test) + rc_test = -EIO; + } + + /* Ensure that the phy is powered and out of loopback + * for the bist and loopback tests */ + mutex_lock(&efx->mac_lock); + efx->phy_mode &= ~PHY_MODE_LOW_POWER; + efx->loopback_mode = LOOPBACK_NONE; + __ef4_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + + rc = ef4_test_phy(efx, tests, flags); + if (rc && !rc_test) + rc_test = rc; + + rc = ef4_test_loopbacks(efx, tests, efx->loopback_modes); + if (rc && !rc_test) + rc_test = rc; + + /* restore the PHY to the previous state */ + mutex_lock(&efx->mac_lock); + efx->phy_mode = phy_mode; + efx->loopback_mode = loopback_mode; + __ef4_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + + netif_device_attach(efx->net_dev); + + return rc_test; +} + +void ef4_selftest_async_start(struct ef4_nic *efx) +{ + struct ef4_channel *channel; + + ef4_for_each_channel(channel, efx) + ef4_nic_event_test_start(channel); + schedule_delayed_work(&efx->selftest_work, IRQ_TIMEOUT); +} + +void ef4_selftest_async_cancel(struct ef4_nic *efx) +{ + cancel_delayed_work_sync(&efx->selftest_work); +} + +void ef4_selftest_async_work(struct work_struct *data) +{ + struct ef4_nic *efx = container_of(data, struct ef4_nic, + selftest_work.work); + struct ef4_channel *channel; + int cpu; + + ef4_for_each_channel(channel, efx) { + cpu = ef4_nic_event_test_irq_cpu(channel); + if (cpu < 0) + netif_err(efx, ifup, efx->net_dev, + "channel %d failed to trigger an interrupt\n", + channel->channel); + else + netif_dbg(efx, ifup, efx->net_dev, + "channel %d triggered interrupt on CPU %d\n", + channel->channel, cpu); + } +} diff --git a/drivers/net/ethernet/sfc/falcon/selftest.h b/drivers/net/ethernet/sfc/falcon/selftest.h new file mode 100644 index 0000000..be52a49 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/selftest.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2012 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_SELFTEST_H +#define EF4_SELFTEST_H + +#include "net_driver.h" + +/* + * Self tests + */ + +struct ef4_loopback_self_tests { + int tx_sent[EF4_TXQ_TYPES]; + int tx_done[EF4_TXQ_TYPES]; + int rx_good; + int rx_bad; +}; + +#define EF4_MAX_PHY_TESTS 20 + +/* Efx self test results + * For fields which are not counters, 1 indicates success and -1 + * indicates failure; 0 indicates test could not be run. + */ +struct ef4_self_tests { + /* online tests */ + int phy_alive; + int nvram; + int interrupt; + int eventq_dma[EF4_MAX_CHANNELS]; + int eventq_int[EF4_MAX_CHANNELS]; + /* offline tests */ + int memory; + int registers; + int phy_ext[EF4_MAX_PHY_TESTS]; + struct ef4_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1]; +}; + +void ef4_loopback_rx_packet(struct ef4_nic *efx, const char *buf_ptr, + int pkt_len); +int ef4_selftest(struct ef4_nic *efx, struct ef4_self_tests *tests, + unsigned flags); +void ef4_selftest_async_start(struct ef4_nic *efx); +void ef4_selftest_async_cancel(struct ef4_nic *efx); +void ef4_selftest_async_work(struct work_struct *data); + +#endif /* EF4_SELFTEST_H */ diff --git a/drivers/net/ethernet/sfc/falcon/tenxpress.c b/drivers/net/ethernet/sfc/falcon/tenxpress.c new file mode 100644 index 0000000..acc548a --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/tenxpress.c @@ -0,0 +1,494 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2007-2011 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include "efx.h" +#include "mdio_10g.h" +#include "nic.h" +#include "phy.h" +#include "workarounds.h" + +/* We expect these MMDs to be in the package. */ +#define TENXPRESS_REQUIRED_DEVS (MDIO_DEVS_PMAPMD | \ + MDIO_DEVS_PCS | \ + MDIO_DEVS_PHYXS | \ + MDIO_DEVS_AN) + +#define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ + (1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_PHYXS_WS)) + +/* We complain if we fail to see the link partner as 10G capable this many + * times in a row (must be > 1 as sampling the autoneg. registers is racy) + */ +#define MAX_BAD_LP_TRIES (5) + +/* Extended control register */ +#define PMA_PMD_XCONTROL_REG 49152 +#define PMA_PMD_EXT_GMII_EN_LBN 1 +#define PMA_PMD_EXT_GMII_EN_WIDTH 1 +#define PMA_PMD_EXT_CLK_OUT_LBN 2 +#define PMA_PMD_EXT_CLK_OUT_WIDTH 1 +#define PMA_PMD_LNPGA_POWERDOWN_LBN 8 +#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1 +#define PMA_PMD_EXT_CLK312_WIDTH 1 +#define PMA_PMD_EXT_LPOWER_LBN 12 +#define PMA_PMD_EXT_LPOWER_WIDTH 1 +#define PMA_PMD_EXT_ROBUST_LBN 14 +#define PMA_PMD_EXT_ROBUST_WIDTH 1 +#define PMA_PMD_EXT_SSR_LBN 15 +#define PMA_PMD_EXT_SSR_WIDTH 1 + +/* extended status register */ +#define PMA_PMD_XSTATUS_REG 49153 +#define PMA_PMD_XSTAT_MDIX_LBN 14 +#define PMA_PMD_XSTAT_FLP_LBN (12) + +/* LED control register */ +#define PMA_PMD_LED_CTRL_REG 49159 +#define PMA_PMA_LED_ACTIVITY_LBN (3) + +/* LED function override register */ +#define PMA_PMD_LED_OVERR_REG 49161 +/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/ +#define PMA_PMD_LED_LINK_LBN (0) +#define PMA_PMD_LED_SPEED_LBN (2) +#define PMA_PMD_LED_TX_LBN (4) +#define PMA_PMD_LED_RX_LBN (6) +/* Override settings */ +#define PMA_PMD_LED_AUTO (0) /* H/W control */ +#define PMA_PMD_LED_ON (1) +#define PMA_PMD_LED_OFF (2) +#define PMA_PMD_LED_FLASH (3) +#define PMA_PMD_LED_MASK 3 +/* All LEDs under hardware control */ +/* Green and Amber under hardware control, Red off */ +#define SFX7101_PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) + +#define PMA_PMD_SPEED_ENABLE_REG 49192 +#define PMA_PMD_100TX_ADV_LBN 1 +#define PMA_PMD_100TX_ADV_WIDTH 1 +#define PMA_PMD_1000T_ADV_LBN 2 +#define PMA_PMD_1000T_ADV_WIDTH 1 +#define PMA_PMD_10000T_ADV_LBN 3 +#define PMA_PMD_10000T_ADV_WIDTH 1 +#define PMA_PMD_SPEED_LBN 4 +#define PMA_PMD_SPEED_WIDTH 4 + +/* Misc register defines */ +#define PCS_CLOCK_CTRL_REG 55297 +#define PLL312_RST_N_LBN 2 + +#define PCS_SOFT_RST2_REG 55302 +#define SERDES_RST_N_LBN 13 +#define XGXS_RST_N_LBN 12 + +#define PCS_TEST_SELECT_REG 55303 /* PRM 10.5.8 */ +#define CLK312_EN_LBN 3 + +/* PHYXS registers */ +#define PHYXS_XCONTROL_REG 49152 +#define PHYXS_RESET_LBN 15 +#define PHYXS_RESET_WIDTH 1 + +#define PHYXS_TEST1 (49162) +#define LOOPBACK_NEAR_LBN (8) +#define LOOPBACK_NEAR_WIDTH (1) + +/* Boot status register */ +#define PCS_BOOT_STATUS_REG 53248 +#define PCS_BOOT_FATAL_ERROR_LBN 0 +#define PCS_BOOT_PROGRESS_LBN 1 +#define PCS_BOOT_PROGRESS_WIDTH 2 +#define PCS_BOOT_PROGRESS_INIT 0 +#define PCS_BOOT_PROGRESS_WAIT_MDIO 1 +#define PCS_BOOT_PROGRESS_CHECKSUM 2 +#define PCS_BOOT_PROGRESS_JUMP 3 +#define PCS_BOOT_DOWNLOAD_WAIT_LBN 3 +#define PCS_BOOT_CODE_STARTED_LBN 4 + +/* 100M/1G PHY registers */ +#define GPHY_XCONTROL_REG 49152 +#define GPHY_ISOLATE_LBN 10 +#define GPHY_ISOLATE_WIDTH 1 +#define GPHY_DUPLEX_LBN 8 +#define GPHY_DUPLEX_WIDTH 1 +#define GPHY_LOOPBACK_NEAR_LBN 14 +#define GPHY_LOOPBACK_NEAR_WIDTH 1 + +#define C22EXT_STATUS_REG 49153 +#define C22EXT_STATUS_LINK_LBN 2 +#define C22EXT_STATUS_LINK_WIDTH 1 + +#define C22EXT_MSTSLV_CTRL 49161 +#define C22EXT_MSTSLV_CTRL_ADV_1000_HD_LBN 8 +#define C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN 9 + +#define C22EXT_MSTSLV_STATUS 49162 +#define C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN 10 +#define C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN 11 + +/* Time to wait between powering down the LNPGA and turning off the power + * rails */ +#define LNPGA_PDOWN_WAIT (HZ / 5) + +struct tenxpress_phy_data { + enum ef4_loopback_mode loopback_mode; + enum ef4_phy_mode phy_mode; + int bad_lp_tries; +}; + +static int tenxpress_init(struct ef4_nic *efx) +{ + /* Enable 312.5 MHz clock */ + ef4_mdio_write(efx, MDIO_MMD_PCS, PCS_TEST_SELECT_REG, + 1 << CLK312_EN_LBN); + + /* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */ + ef4_mdio_set_flag(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG, + 1 << PMA_PMA_LED_ACTIVITY_LBN, true); + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, + SFX7101_PMA_PMD_LED_DEFAULT); + + return 0; +} + +static int tenxpress_phy_probe(struct ef4_nic *efx) +{ + struct tenxpress_phy_data *phy_data; + + /* Allocate phy private storage */ + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; + + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45; + + efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + + efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full); + + return 0; +} + +static int tenxpress_phy_init(struct ef4_nic *efx) +{ + int rc; + + falcon_board(efx)->type->init_phy(efx); + + if (!(efx->phy_mode & PHY_MODE_SPECIAL)) { + rc = ef4_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); + if (rc < 0) + return rc; + + rc = ef4_mdio_check_mmds(efx, TENXPRESS_REQUIRED_DEVS); + if (rc < 0) + return rc; + } + + rc = tenxpress_init(efx); + if (rc < 0) + return rc; + + /* Reinitialise flow control settings */ + ef4_link_set_wanted_fc(efx, efx->wanted_fc); + ef4_mdio_an_reconfigure(efx); + + schedule_timeout_uninterruptible(HZ / 5); /* 200ms */ + + /* Let XGXS and SerDes out of reset */ + falcon_reset_xaui(efx); + + return 0; +} + +/* Perform a "special software reset" on the PHY. The caller is + * responsible for saving and restoring the PHY hardware registers + * properly, and masking/unmasking LASI */ +static int tenxpress_special_reset(struct ef4_nic *efx) +{ + int rc, reg; + + /* The XGMAC clock is driven from the SFX7101 312MHz clock, so + * a special software reset can glitch the XGMAC sufficiently for stats + * requests to fail. */ + falcon_stop_nic_stats(efx); + + /* Initiate reset */ + reg = ef4_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG); + reg |= (1 << PMA_PMD_EXT_SSR_LBN); + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); + + mdelay(200); + + /* Wait for the blocks to come out of reset */ + rc = ef4_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); + if (rc < 0) + goto out; + + /* Try and reconfigure the device */ + rc = tenxpress_init(efx); + if (rc < 0) + goto out; + + /* Wait for the XGXS state machine to churn */ + mdelay(10); +out: + falcon_start_nic_stats(efx); + return rc; +} + +static void sfx7101_check_bad_lp(struct ef4_nic *efx, bool link_ok) +{ + struct tenxpress_phy_data *pd = efx->phy_data; + bool bad_lp; + int reg; + + if (link_ok) { + bad_lp = false; + } else { + /* Check that AN has started but not completed. */ + reg = ef4_mdio_read(efx, MDIO_MMD_AN, MDIO_STAT1); + if (!(reg & MDIO_AN_STAT1_LPABLE)) + return; /* LP status is unknown */ + bad_lp = !(reg & MDIO_AN_STAT1_COMPLETE); + if (bad_lp) + pd->bad_lp_tries++; + } + + /* Nothing to do if all is well and was previously so. */ + if (!pd->bad_lp_tries) + return; + + /* Use the RX (red) LED as an error indicator once we've seen AN + * failure several times in a row, and also log a message. */ + if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) { + reg = ef4_mdio_read(efx, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG); + reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN); + if (!bad_lp) { + reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN; + } else { + reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN; + netif_err(efx, link, efx->net_dev, + "appears to be plugged into a port" + " that is not 10GBASE-T capable. The PHY" + " supports 10GBASE-T ONLY, so no link can" + " be established\n"); + } + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); + pd->bad_lp_tries = bad_lp; + } +} + +static bool sfx7101_link_ok(struct ef4_nic *efx) +{ + return ef4_mdio_links_ok(efx, + MDIO_DEVS_PMAPMD | + MDIO_DEVS_PCS | + MDIO_DEVS_PHYXS); +} + +static void tenxpress_ext_loopback(struct ef4_nic *efx) +{ + ef4_mdio_set_flag(efx, MDIO_MMD_PHYXS, PHYXS_TEST1, + 1 << LOOPBACK_NEAR_LBN, + efx->loopback_mode == LOOPBACK_PHYXS); +} + +static void tenxpress_low_power(struct ef4_nic *efx) +{ + ef4_mdio_set_mmds_lpower( + efx, !!(efx->phy_mode & PHY_MODE_LOW_POWER), + TENXPRESS_REQUIRED_DEVS); +} + +static int tenxpress_phy_reconfigure(struct ef4_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + bool phy_mode_change, loop_reset; + + if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) { + phy_data->phy_mode = efx->phy_mode; + return 0; + } + + phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL && + phy_data->phy_mode != PHY_MODE_NORMAL); + loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, LOOPBACKS_EXTERNAL(efx)) || + LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY)); + + if (loop_reset || phy_mode_change) { + tenxpress_special_reset(efx); + falcon_reset_xaui(efx); + } + + tenxpress_low_power(efx); + ef4_mdio_transmit_disable(efx); + ef4_mdio_phy_reconfigure(efx); + tenxpress_ext_loopback(efx); + ef4_mdio_an_reconfigure(efx); + + phy_data->loopback_mode = efx->loopback_mode; + phy_data->phy_mode = efx->phy_mode; + + return 0; +} + +static void +tenxpress_get_settings(struct ef4_nic *efx, struct ethtool_cmd *ecmd); + +/* Poll for link state changes */ +static bool tenxpress_phy_poll(struct ef4_nic *efx) +{ + struct ef4_link_state old_state = efx->link_state; + + efx->link_state.up = sfx7101_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = ef4_mdio_get_pause(efx); + + sfx7101_check_bad_lp(efx, efx->link_state.up); + + return !ef4_link_state_equal(&efx->link_state, &old_state); +} + +static void sfx7101_phy_fini(struct ef4_nic *efx) +{ + int reg; + + /* Power down the LNPGA */ + reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN); + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); + + /* Waiting here ensures that the board fini, which can turn + * off the power to the PHY, won't get run until the LNPGA + * powerdown has been given long enough to complete. */ + schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */ +} + +static void tenxpress_phy_remove(struct ef4_nic *efx) +{ + kfree(efx->phy_data); + efx->phy_data = NULL; +} + + +/* Override the RX, TX and link LEDs */ +void tenxpress_set_id_led(struct ef4_nic *efx, enum ef4_led_mode mode) +{ + int reg; + + switch (mode) { + case EF4_LED_OFF: + reg = (PMA_PMD_LED_OFF << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_OFF << PMA_PMD_LED_LINK_LBN); + break; + case EF4_LED_ON: + reg = (PMA_PMD_LED_ON << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_ON << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_ON << PMA_PMD_LED_LINK_LBN); + break; + default: + reg = SFX7101_PMA_PMD_LED_DEFAULT; + break; + } + + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, reg); +} + +static const char *const sfx7101_test_names[] = { + "bist" +}; + +static const char *sfx7101_test_name(struct ef4_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(sfx7101_test_names)) + return sfx7101_test_names[index]; + return NULL; +} + +static int +sfx7101_run_tests(struct ef4_nic *efx, int *results, unsigned flags) +{ + int rc; + + if (!(flags & ETH_TEST_FL_OFFLINE)) + return 0; + + /* BIST is automatically run after a special software reset */ + rc = tenxpress_special_reset(efx); + results[0] = rc ? -1 : 1; + + ef4_mdio_an_reconfigure(efx); + + return rc; +} + +static void +tenxpress_get_settings(struct ef4_nic *efx, struct ethtool_cmd *ecmd) +{ + u32 adv = 0, lpa = 0; + int reg; + + reg = ef4_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL); + if (reg & MDIO_AN_10GBT_CTRL_ADV10G) + adv |= ADVERTISED_10000baseT_Full; + reg = ef4_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); + if (reg & MDIO_AN_10GBT_STAT_LP10G) + lpa |= ADVERTISED_10000baseT_Full; + + mdio45_ethtool_gset_npage(&efx->mdio, ecmd, adv, lpa); + + /* In loopback, the PHY automatically brings up the correct interface, + * but doesn't advertise the correct speed. So override it */ + if (LOOPBACK_EXTERNAL(efx)) + ethtool_cmd_speed_set(ecmd, SPEED_10000); +} + +static int tenxpress_set_settings(struct ef4_nic *efx, struct ethtool_cmd *ecmd) +{ + if (!ecmd->autoneg) + return -EINVAL; + + return ef4_mdio_set_settings(efx, ecmd); +} + +static void sfx7101_set_npage_adv(struct ef4_nic *efx, u32 advertising) +{ + ef4_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV10G, + advertising & ADVERTISED_10000baseT_Full); +} + +const struct ef4_phy_operations falcon_sfx7101_phy_ops = { + .probe = tenxpress_phy_probe, + .init = tenxpress_phy_init, + .reconfigure = tenxpress_phy_reconfigure, + .poll = tenxpress_phy_poll, + .fini = sfx7101_phy_fini, + .remove = tenxpress_phy_remove, + .get_settings = tenxpress_get_settings, + .set_settings = tenxpress_set_settings, + .set_npage_adv = sfx7101_set_npage_adv, + .test_alive = ef4_mdio_test_alive, + .test_name = sfx7101_test_name, + .run_tests = sfx7101_run_tests, +}; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c new file mode 100644 index 0000000..104fb15 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -0,0 +1,649 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "efx.h" +#include "io.h" +#include "nic.h" +#include "tx.h" +#include "workarounds.h" + +static inline u8 *ef4_tx_get_copy_buffer(struct ef4_tx_queue *tx_queue, + struct ef4_tx_buffer *buffer) +{ + unsigned int index = ef4_tx_queue_get_insert_index(tx_queue); + struct ef4_buffer *page_buf = + &tx_queue->cb_page[index >> (PAGE_SHIFT - EF4_TX_CB_ORDER)]; + unsigned int offset = + ((index << EF4_TX_CB_ORDER) + NET_IP_ALIGN) & (PAGE_SIZE - 1); + + if (unlikely(!page_buf->addr) && + ef4_nic_alloc_buffer(tx_queue->efx, page_buf, PAGE_SIZE, + GFP_ATOMIC)) + return NULL; + buffer->dma_addr = page_buf->dma_addr + offset; + buffer->unmap_len = 0; + return (u8 *)page_buf->addr + offset; +} + +u8 *ef4_tx_get_copy_buffer_limited(struct ef4_tx_queue *tx_queue, + struct ef4_tx_buffer *buffer, size_t len) +{ + if (len > EF4_TX_CB_SIZE) + return NULL; + return ef4_tx_get_copy_buffer(tx_queue, buffer); +} + +static void ef4_dequeue_buffer(struct ef4_tx_queue *tx_queue, + struct ef4_tx_buffer *buffer, + unsigned int *pkts_compl, + unsigned int *bytes_compl) +{ + if (buffer->unmap_len) { + struct device *dma_dev = &tx_queue->efx->pci_dev->dev; + dma_addr_t unmap_addr = buffer->dma_addr - buffer->dma_offset; + if (buffer->flags & EF4_TX_BUF_MAP_SINGLE) + dma_unmap_single(dma_dev, unmap_addr, buffer->unmap_len, + DMA_TO_DEVICE); + else + dma_unmap_page(dma_dev, unmap_addr, buffer->unmap_len, + DMA_TO_DEVICE); + buffer->unmap_len = 0; + } + + if (buffer->flags & EF4_TX_BUF_SKB) { + (*pkts_compl)++; + (*bytes_compl) += buffer->skb->len; + dev_consume_skb_any((struct sk_buff *)buffer->skb); + netif_vdbg(tx_queue->efx, tx_done, tx_queue->efx->net_dev, + "TX queue %d transmission id %x complete\n", + tx_queue->queue, tx_queue->read_count); + } + + buffer->len = 0; + buffer->flags = 0; +} + +unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx) +{ + /* This is probably too much since we don't have any TSO support; + * it's a left-over from when we had Software TSO. But it's safer + * to leave it as-is than try to determine a new bound. + */ + /* Header and payload descriptor for each output segment, plus + * one for every input fragment boundary within a segment + */ + unsigned int max_descs = EF4_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS; + + /* Possibly one more per segment for the alignment workaround, + * or for option descriptors + */ + if (EF4_WORKAROUND_5391(efx)) + max_descs += EF4_TSO_MAX_SEGS; + + /* Possibly more for PCIe page boundaries within input fragments */ + if (PAGE_SIZE > EF4_PAGE_SIZE) + max_descs += max_t(unsigned int, MAX_SKB_FRAGS, + DIV_ROUND_UP(GSO_MAX_SIZE, EF4_PAGE_SIZE)); + + return max_descs; +} + +static void ef4_tx_maybe_stop_queue(struct ef4_tx_queue *txq1) +{ + /* We need to consider both queues that the net core sees as one */ + struct ef4_tx_queue *txq2 = ef4_tx_queue_partner(txq1); + struct ef4_nic *efx = txq1->efx; + unsigned int fill_level; + + fill_level = max(txq1->insert_count - txq1->old_read_count, + txq2->insert_count - txq2->old_read_count); + if (likely(fill_level < efx->txq_stop_thresh)) + return; + + /* We used the stale old_read_count above, which gives us a + * pessimistic estimate of the fill level (which may even + * validly be >= efx->txq_entries). Now try again using + * read_count (more likely to be a cache miss). + * + * If we read read_count and then conditionally stop the + * queue, it is possible for the completion path to race with + * us and complete all outstanding descriptors in the middle, + * after which there will be no more completions to wake it. + * Therefore we stop the queue first, then read read_count + * (with a memory barrier to ensure the ordering), then + * restart the queue if the fill level turns out to be low + * enough. + */ + netif_tx_stop_queue(txq1->core_txq); + smp_mb(); + txq1->old_read_count = ACCESS_ONCE(txq1->read_count); + txq2->old_read_count = ACCESS_ONCE(txq2->read_count); + + fill_level = max(txq1->insert_count - txq1->old_read_count, + txq2->insert_count - txq2->old_read_count); + EF4_BUG_ON_PARANOID(fill_level >= efx->txq_entries); + if (likely(fill_level < efx->txq_stop_thresh)) { + smp_mb(); + if (likely(!efx->loopback_selftest)) + netif_tx_start_queue(txq1->core_txq); + } +} + +static int ef4_enqueue_skb_copy(struct ef4_tx_queue *tx_queue, + struct sk_buff *skb) +{ + unsigned int min_len = tx_queue->tx_min_size; + unsigned int copy_len = skb->len; + struct ef4_tx_buffer *buffer; + u8 *copy_buffer; + int rc; + + EF4_BUG_ON_PARANOID(copy_len > EF4_TX_CB_SIZE); + + buffer = ef4_tx_queue_get_insert_buffer(tx_queue); + + copy_buffer = ef4_tx_get_copy_buffer(tx_queue, buffer); + if (unlikely(!copy_buffer)) + return -ENOMEM; + + rc = skb_copy_bits(skb, 0, copy_buffer, copy_len); + EF4_WARN_ON_PARANOID(rc); + if (unlikely(copy_len < min_len)) { + memset(copy_buffer + copy_len, 0, min_len - copy_len); + buffer->len = min_len; + } else { + buffer->len = copy_len; + } + + buffer->skb = skb; + buffer->flags = EF4_TX_BUF_SKB; + + ++tx_queue->insert_count; + return rc; +} + +static struct ef4_tx_buffer *ef4_tx_map_chunk(struct ef4_tx_queue *tx_queue, + dma_addr_t dma_addr, + size_t len) +{ + const struct ef4_nic_type *nic_type = tx_queue->efx->type; + struct ef4_tx_buffer *buffer; + unsigned int dma_len; + + /* Map the fragment taking account of NIC-dependent DMA limits. */ + do { + buffer = ef4_tx_queue_get_insert_buffer(tx_queue); + dma_len = nic_type->tx_limit_len(tx_queue, dma_addr, len); + + buffer->len = dma_len; + buffer->dma_addr = dma_addr; + buffer->flags = EF4_TX_BUF_CONT; + len -= dma_len; + dma_addr += dma_len; + ++tx_queue->insert_count; + } while (len); + + return buffer; +} + +/* Map all data from an SKB for DMA and create descriptors on the queue. + */ +static int ef4_tx_map_data(struct ef4_tx_queue *tx_queue, struct sk_buff *skb) +{ + struct ef4_nic *efx = tx_queue->efx; + struct device *dma_dev = &efx->pci_dev->dev; + unsigned int frag_index, nr_frags; + dma_addr_t dma_addr, unmap_addr; + unsigned short dma_flags; + size_t len, unmap_len; + + nr_frags = skb_shinfo(skb)->nr_frags; + frag_index = 0; + + /* Map header data. */ + len = skb_headlen(skb); + dma_addr = dma_map_single(dma_dev, skb->data, len, DMA_TO_DEVICE); + dma_flags = EF4_TX_BUF_MAP_SINGLE; + unmap_len = len; + unmap_addr = dma_addr; + + if (unlikely(dma_mapping_error(dma_dev, dma_addr))) + return -EIO; + + /* Add descriptors for each fragment. */ + do { + struct ef4_tx_buffer *buffer; + skb_frag_t *fragment; + + buffer = ef4_tx_map_chunk(tx_queue, dma_addr, len); + + /* The final descriptor for a fragment is responsible for + * unmapping the whole fragment. + */ + buffer->flags = EF4_TX_BUF_CONT | dma_flags; + buffer->unmap_len = unmap_len; + buffer->dma_offset = buffer->dma_addr - unmap_addr; + + if (frag_index >= nr_frags) { + /* Store SKB details with the final buffer for + * the completion. + */ + buffer->skb = skb; + buffer->flags = EF4_TX_BUF_SKB | dma_flags; + return 0; + } + + /* Move on to the next fragment. */ + fragment = &skb_shinfo(skb)->frags[frag_index++]; + len = skb_frag_size(fragment); + dma_addr = skb_frag_dma_map(dma_dev, fragment, + 0, len, DMA_TO_DEVICE); + dma_flags = 0; + unmap_len = len; + unmap_addr = dma_addr; + + if (unlikely(dma_mapping_error(dma_dev, dma_addr))) + return -EIO; + } while (1); +} + +/* Remove buffers put into a tx_queue. None of the buffers must have + * an skb attached. + */ +static void ef4_enqueue_unwind(struct ef4_tx_queue *tx_queue) +{ + struct ef4_tx_buffer *buffer; + + /* Work backwards until we hit the original insert pointer value */ + while (tx_queue->insert_count != tx_queue->write_count) { + --tx_queue->insert_count; + buffer = __ef4_tx_queue_get_insert_buffer(tx_queue); + ef4_dequeue_buffer(tx_queue, buffer, NULL, NULL); + } +} + +/* + * Add a socket buffer to a TX queue + * + * This maps all fragments of a socket buffer for DMA and adds them to + * the TX queue. The queue's insert pointer will be incremented by + * the number of fragments in the socket buffer. + * + * If any DMA mapping fails, any mapped fragments will be unmapped, + * the queue's insert pointer will be restored to its original value. + * + * This function is split out from ef4_hard_start_xmit to allow the + * loopback test to direct packets via specific TX queues. + * + * Returns NETDEV_TX_OK. + * You must hold netif_tx_lock() to call this function. + */ +netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb) +{ + bool data_mapped = false; + unsigned int skb_len; + + skb_len = skb->len; + EF4_WARN_ON_PARANOID(skb_is_gso(skb)); + + if (skb_len < tx_queue->tx_min_size || + (skb->data_len && skb_len <= EF4_TX_CB_SIZE)) { + /* Pad short packets or coalesce short fragmented packets. */ + if (ef4_enqueue_skb_copy(tx_queue, skb)) + goto err; + tx_queue->cb_packets++; + data_mapped = true; + } + + /* Map for DMA and create descriptors if we haven't done so already. */ + if (!data_mapped && (ef4_tx_map_data(tx_queue, skb))) + goto err; + + /* Update BQL */ + netdev_tx_sent_queue(tx_queue->core_txq, skb_len); + + /* Pass off to hardware */ + if (!skb->xmit_more || netif_xmit_stopped(tx_queue->core_txq)) { + struct ef4_tx_queue *txq2 = ef4_tx_queue_partner(tx_queue); + + /* There could be packets left on the partner queue if those + * SKBs had skb->xmit_more set. If we do not push those they + * could be left for a long time and cause a netdev watchdog. + */ + if (txq2->xmit_more_available) + ef4_nic_push_buffers(txq2); + + ef4_nic_push_buffers(tx_queue); + } else { + tx_queue->xmit_more_available = skb->xmit_more; + } + + tx_queue->tx_packets++; + + ef4_tx_maybe_stop_queue(tx_queue); + + return NETDEV_TX_OK; + + +err: + ef4_enqueue_unwind(tx_queue); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/* Remove packets from the TX queue + * + * This removes packets from the TX queue, up to and including the + * specified index. + */ +static void ef4_dequeue_buffers(struct ef4_tx_queue *tx_queue, + unsigned int index, + unsigned int *pkts_compl, + unsigned int *bytes_compl) +{ + struct ef4_nic *efx = tx_queue->efx; + unsigned int stop_index, read_ptr; + + stop_index = (index + 1) & tx_queue->ptr_mask; + read_ptr = tx_queue->read_count & tx_queue->ptr_mask; + + while (read_ptr != stop_index) { + struct ef4_tx_buffer *buffer = &tx_queue->buffer[read_ptr]; + + if (!(buffer->flags & EF4_TX_BUF_OPTION) && + unlikely(buffer->len == 0)) { + netif_err(efx, tx_err, efx->net_dev, + "TX queue %d spurious TX completion id %x\n", + tx_queue->queue, read_ptr); + ef4_schedule_reset(efx, RESET_TYPE_TX_SKIP); + return; + } + + ef4_dequeue_buffer(tx_queue, buffer, pkts_compl, bytes_compl); + + ++tx_queue->read_count; + read_ptr = tx_queue->read_count & tx_queue->ptr_mask; + } +} + +/* Initiate a packet transmission. We use one channel per CPU + * (sharing when we have more CPUs than channels). On Falcon, the TX + * completion events will be directed back to the CPU that transmitted + * the packet, which should be cache-efficient. + * + * Context: non-blocking. + * Note that returning anything other than NETDEV_TX_OK will cause the + * OS to free the skb. + */ +netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct ef4_tx_queue *tx_queue; + unsigned index, type; + + EF4_WARN_ON_PARANOID(!netif_device_present(net_dev)); + + index = skb_get_queue_mapping(skb); + type = skb->ip_summed == CHECKSUM_PARTIAL ? EF4_TXQ_TYPE_OFFLOAD : 0; + if (index >= efx->n_tx_channels) { + index -= efx->n_tx_channels; + type |= EF4_TXQ_TYPE_HIGHPRI; + } + tx_queue = ef4_get_tx_queue(efx, index, type); + + return ef4_enqueue_skb(tx_queue, skb); +} + +void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + + /* Must be inverse of queue lookup in ef4_hard_start_xmit() */ + tx_queue->core_txq = + netdev_get_tx_queue(efx->net_dev, + tx_queue->queue / EF4_TXQ_TYPES + + ((tx_queue->queue & EF4_TXQ_TYPE_HIGHPRI) ? + efx->n_tx_channels : 0)); +} + +int ef4_setup_tc(struct net_device *net_dev, u32 handle, __be16 proto, + struct tc_to_netdev *ntc) +{ + struct ef4_nic *efx = netdev_priv(net_dev); + struct ef4_channel *channel; + struct ef4_tx_queue *tx_queue; + unsigned tc, num_tc; + int rc; + + if (ntc->type != TC_SETUP_MQPRIO) + return -EINVAL; + + num_tc = ntc->tc; + + if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0 || num_tc > EF4_MAX_TX_TC) + return -EINVAL; + + if (num_tc == net_dev->num_tc) + return 0; + + for (tc = 0; tc < num_tc; tc++) { + net_dev->tc_to_txq[tc].offset = tc * efx->n_tx_channels; + net_dev->tc_to_txq[tc].count = efx->n_tx_channels; + } + + if (num_tc > net_dev->num_tc) { + /* Initialise high-priority queues as necessary */ + ef4_for_each_channel(channel, efx) { + ef4_for_each_possible_channel_tx_queue(tx_queue, + channel) { + if (!(tx_queue->queue & EF4_TXQ_TYPE_HIGHPRI)) + continue; + if (!tx_queue->buffer) { + rc = ef4_probe_tx_queue(tx_queue); + if (rc) + return rc; + } + if (!tx_queue->initialised) + ef4_init_tx_queue(tx_queue); + ef4_init_tx_queue_core_txq(tx_queue); + } + } + } else { + /* Reduce number of classes before number of queues */ + net_dev->num_tc = num_tc; + } + + rc = netif_set_real_num_tx_queues(net_dev, + max_t(int, num_tc, 1) * + efx->n_tx_channels); + if (rc) + return rc; + + /* Do not destroy high-priority queues when they become + * unused. We would have to flush them first, and it is + * fairly difficult to flush a subset of TX queues. Leave + * it to ef4_fini_channels(). + */ + + net_dev->num_tc = num_tc; + return 0; +} + +void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index) +{ + unsigned fill_level; + struct ef4_nic *efx = tx_queue->efx; + struct ef4_tx_queue *txq2; + unsigned int pkts_compl = 0, bytes_compl = 0; + + EF4_BUG_ON_PARANOID(index > tx_queue->ptr_mask); + + ef4_dequeue_buffers(tx_queue, index, &pkts_compl, &bytes_compl); + tx_queue->pkts_compl += pkts_compl; + tx_queue->bytes_compl += bytes_compl; + + if (pkts_compl > 1) + ++tx_queue->merge_events; + + /* See if we need to restart the netif queue. This memory + * barrier ensures that we write read_count (inside + * ef4_dequeue_buffers()) before reading the queue status. + */ + smp_mb(); + if (unlikely(netif_tx_queue_stopped(tx_queue->core_txq)) && + likely(efx->port_enabled) && + likely(netif_device_present(efx->net_dev))) { + txq2 = ef4_tx_queue_partner(tx_queue); + fill_level = max(tx_queue->insert_count - tx_queue->read_count, + txq2->insert_count - txq2->read_count); + if (fill_level <= efx->txq_wake_thresh) + netif_tx_wake_queue(tx_queue->core_txq); + } + + /* Check whether the hardware queue is now empty */ + if ((int)(tx_queue->read_count - tx_queue->old_write_count) >= 0) { + tx_queue->old_write_count = ACCESS_ONCE(tx_queue->write_count); + if (tx_queue->read_count == tx_queue->old_write_count) { + smp_mb(); + tx_queue->empty_read_count = + tx_queue->read_count | EF4_EMPTY_COUNT_VALID; + } + } +} + +static unsigned int ef4_tx_cb_page_count(struct ef4_tx_queue *tx_queue) +{ + return DIV_ROUND_UP(tx_queue->ptr_mask + 1, PAGE_SIZE >> EF4_TX_CB_ORDER); +} + +int ef4_probe_tx_queue(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + unsigned int entries; + int rc; + + /* Create the smallest power-of-two aligned ring */ + entries = max(roundup_pow_of_two(efx->txq_entries), EF4_MIN_DMAQ_SIZE); + EF4_BUG_ON_PARANOID(entries > EF4_MAX_DMAQ_SIZE); + tx_queue->ptr_mask = entries - 1; + + netif_dbg(efx, probe, efx->net_dev, + "creating TX queue %d size %#x mask %#x\n", + tx_queue->queue, efx->txq_entries, tx_queue->ptr_mask); + + /* Allocate software ring */ + tx_queue->buffer = kcalloc(entries, sizeof(*tx_queue->buffer), + GFP_KERNEL); + if (!tx_queue->buffer) + return -ENOMEM; + + tx_queue->cb_page = kcalloc(ef4_tx_cb_page_count(tx_queue), + sizeof(tx_queue->cb_page[0]), GFP_KERNEL); + if (!tx_queue->cb_page) { + rc = -ENOMEM; + goto fail1; + } + + /* Allocate hardware ring */ + rc = ef4_nic_probe_tx(tx_queue); + if (rc) + goto fail2; + + return 0; + +fail2: + kfree(tx_queue->cb_page); + tx_queue->cb_page = NULL; +fail1: + kfree(tx_queue->buffer); + tx_queue->buffer = NULL; + return rc; +} + +void ef4_init_tx_queue(struct ef4_tx_queue *tx_queue) +{ + struct ef4_nic *efx = tx_queue->efx; + + netif_dbg(efx, drv, efx->net_dev, + "initialising TX queue %d\n", tx_queue->queue); + + tx_queue->insert_count = 0; + tx_queue->write_count = 0; + tx_queue->old_write_count = 0; + tx_queue->read_count = 0; + tx_queue->old_read_count = 0; + tx_queue->empty_read_count = 0 | EF4_EMPTY_COUNT_VALID; + tx_queue->xmit_more_available = false; + + /* Some older hardware requires Tx writes larger than 32. */ + tx_queue->tx_min_size = EF4_WORKAROUND_15592(efx) ? 33 : 0; + + /* Set up TX descriptor ring */ + ef4_nic_init_tx(tx_queue); + + tx_queue->initialised = true; +} + +void ef4_fini_tx_queue(struct ef4_tx_queue *tx_queue) +{ + struct ef4_tx_buffer *buffer; + + netif_dbg(tx_queue->efx, drv, tx_queue->efx->net_dev, + "shutting down TX queue %d\n", tx_queue->queue); + + if (!tx_queue->buffer) + return; + + /* Free any buffers left in the ring */ + while (tx_queue->read_count != tx_queue->write_count) { + unsigned int pkts_compl = 0, bytes_compl = 0; + buffer = &tx_queue->buffer[tx_queue->read_count & tx_queue->ptr_mask]; + ef4_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl); + + ++tx_queue->read_count; + } + tx_queue->xmit_more_available = false; + netdev_tx_reset_queue(tx_queue->core_txq); +} + +void ef4_remove_tx_queue(struct ef4_tx_queue *tx_queue) +{ + int i; + + if (!tx_queue->buffer) + return; + + netif_dbg(tx_queue->efx, drv, tx_queue->efx->net_dev, + "destroying TX queue %d\n", tx_queue->queue); + ef4_nic_remove_tx(tx_queue); + + if (tx_queue->cb_page) { + for (i = 0; i < ef4_tx_cb_page_count(tx_queue); i++) + ef4_nic_free_buffer(tx_queue->efx, + &tx_queue->cb_page[i]); + kfree(tx_queue->cb_page); + tx_queue->cb_page = NULL; + } + + kfree(tx_queue->buffer); + tx_queue->buffer = NULL; +} diff --git a/drivers/net/ethernet/sfc/falcon/tx.h b/drivers/net/ethernet/sfc/falcon/tx.h new file mode 100644 index 0000000..a607eb0 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/tx.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2015 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_TX_H +#define EF4_TX_H + +#include + +/* Driver internal tx-path related declarations. */ + +unsigned int ef4_tx_limit_len(struct ef4_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned int len); + +u8 *ef4_tx_get_copy_buffer_limited(struct ef4_tx_queue *tx_queue, + struct ef4_tx_buffer *buffer, size_t len); + +int ef4_enqueue_skb_tso(struct ef4_tx_queue *tx_queue, struct sk_buff *skb, + bool *data_mapped); + +#endif /* EF4_TX_H */ diff --git a/drivers/net/ethernet/sfc/falcon/txc43128_phy.c b/drivers/net/ethernet/sfc/falcon/txc43128_phy.c new file mode 100644 index 0000000..18421f5 --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/txc43128_phy.c @@ -0,0 +1,560 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2006-2011 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +/* + * Driver for Transwitch/Mysticom CX4 retimer + * see www.transwitch.com, part is TXC-43128 + */ + +#include +#include +#include "efx.h" +#include "mdio_10g.h" +#include "phy.h" +#include "nic.h" + +/* We expect these MMDs to be in the package */ +#define TXC_REQUIRED_DEVS (MDIO_DEVS_PCS | \ + MDIO_DEVS_PMAPMD | \ + MDIO_DEVS_PHYXS) + +#define TXC_LOOPBACKS ((1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_PHYXS_WS)) + +/************************************************************************** + * + * Compile-time config + * + ************************************************************************** + */ +#define TXCNAME "TXC43128" +/* Total length of time we'll wait for the PHY to come out of reset (ms) */ +#define TXC_MAX_RESET_TIME 500 +/* Interval between checks (ms) */ +#define TXC_RESET_WAIT 10 +/* How long to run BIST (us) */ +#define TXC_BIST_DURATION 50 + +/************************************************************************** + * + * Register definitions + * + ************************************************************************** + */ + +/* Command register */ +#define TXC_GLRGS_GLCMD 0xc004 +/* Useful bits in command register */ +/* Lane power-down */ +#define TXC_GLCMD_L01PD_LBN 5 +#define TXC_GLCMD_L23PD_LBN 6 +/* Limited SW reset: preserves configuration but + * initiates a logic reset. Self-clearing */ +#define TXC_GLCMD_LMTSWRST_LBN 14 + +/* Signal Quality Control */ +#define TXC_GLRGS_GSGQLCTL 0xc01a +/* Enable bit */ +#define TXC_GSGQLCT_SGQLEN_LBN 15 +/* Lane selection */ +#define TXC_GSGQLCT_LNSL_LBN 13 +#define TXC_GSGQLCT_LNSL_WIDTH 2 + +/* Analog TX control */ +#define TXC_ALRGS_ATXCTL 0xc040 +/* Lane power-down */ +#define TXC_ATXCTL_TXPD3_LBN 15 +#define TXC_ATXCTL_TXPD2_LBN 14 +#define TXC_ATXCTL_TXPD1_LBN 13 +#define TXC_ATXCTL_TXPD0_LBN 12 + +/* Amplitude on lanes 0, 1 */ +#define TXC_ALRGS_ATXAMP0 0xc041 +/* Amplitude on lanes 2, 3 */ +#define TXC_ALRGS_ATXAMP1 0xc042 +/* Bit position of value for lane 0 (or 2) */ +#define TXC_ATXAMP_LANE02_LBN 3 +/* Bit position of value for lane 1 (or 3) */ +#define TXC_ATXAMP_LANE13_LBN 11 + +#define TXC_ATXAMP_1280_mV 0 +#define TXC_ATXAMP_1200_mV 8 +#define TXC_ATXAMP_1120_mV 12 +#define TXC_ATXAMP_1060_mV 14 +#define TXC_ATXAMP_0820_mV 25 +#define TXC_ATXAMP_0720_mV 26 +#define TXC_ATXAMP_0580_mV 27 +#define TXC_ATXAMP_0440_mV 28 + +#define TXC_ATXAMP_0820_BOTH \ + ((TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE02_LBN) \ + | (TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE13_LBN)) + +#define TXC_ATXAMP_DEFAULT 0x6060 /* From databook */ + +/* Preemphasis on lanes 0, 1 */ +#define TXC_ALRGS_ATXPRE0 0xc043 +/* Preemphasis on lanes 2, 3 */ +#define TXC_ALRGS_ATXPRE1 0xc044 + +#define TXC_ATXPRE_NONE 0 +#define TXC_ATXPRE_DEFAULT 0x1010 /* From databook */ + +#define TXC_ALRGS_ARXCTL 0xc045 +/* Lane power-down */ +#define TXC_ARXCTL_RXPD3_LBN 15 +#define TXC_ARXCTL_RXPD2_LBN 14 +#define TXC_ARXCTL_RXPD1_LBN 13 +#define TXC_ARXCTL_RXPD0_LBN 12 + +/* Main control */ +#define TXC_MRGS_CTL 0xc340 +/* Bits in main control */ +#define TXC_MCTL_RESET_LBN 15 /* Self clear */ +#define TXC_MCTL_TXLED_LBN 14 /* 1 to show align status */ +#define TXC_MCTL_RXLED_LBN 13 /* 1 to show align status */ + +/* GPIO output */ +#define TXC_GPIO_OUTPUT 0xc346 +#define TXC_GPIO_DIR 0xc348 + +/* Vendor-specific BIST registers */ +#define TXC_BIST_CTL 0xc280 +#define TXC_BIST_TXFRMCNT 0xc281 +#define TXC_BIST_RX0FRMCNT 0xc282 +#define TXC_BIST_RX1FRMCNT 0xc283 +#define TXC_BIST_RX2FRMCNT 0xc284 +#define TXC_BIST_RX3FRMCNT 0xc285 +#define TXC_BIST_RX0ERRCNT 0xc286 +#define TXC_BIST_RX1ERRCNT 0xc287 +#define TXC_BIST_RX2ERRCNT 0xc288 +#define TXC_BIST_RX3ERRCNT 0xc289 + +/* BIST type (controls bit patter in test) */ +#define TXC_BIST_CTRL_TYPE_LBN 10 +#define TXC_BIST_CTRL_TYPE_TSD 0 /* TranSwitch Deterministic */ +#define TXC_BIST_CTRL_TYPE_CRP 1 /* CRPAT standard */ +#define TXC_BIST_CTRL_TYPE_CJP 2 /* CJPAT standard */ +#define TXC_BIST_CTRL_TYPE_TSR 3 /* TranSwitch pseudo-random */ +/* Set this to 1 for 10 bit and 0 for 8 bit */ +#define TXC_BIST_CTRL_B10EN_LBN 12 +/* Enable BIST (write 0 to disable) */ +#define TXC_BIST_CTRL_ENAB_LBN 13 +/* Stop BIST (self-clears when stop complete) */ +#define TXC_BIST_CTRL_STOP_LBN 14 +/* Start BIST (cleared by writing 1 to STOP) */ +#define TXC_BIST_CTRL_STRT_LBN 15 + +/* Mt. Diablo test configuration */ +#define TXC_MTDIABLO_CTRL 0xc34f +#define TXC_MTDIABLO_CTRL_PMA_LOOP_LBN 10 + +struct txc43128_data { + unsigned long bug10934_timer; + enum ef4_phy_mode phy_mode; + enum ef4_loopback_mode loopback_mode; +}; + +/* The PHY sometimes needs a reset to bring the link back up. So long as + * it reports link down, we reset it every 5 seconds. + */ +#define BUG10934_RESET_INTERVAL (5 * HZ) + +/* Perform a reset that doesn't clear configuration changes */ +static void txc_reset_logic(struct ef4_nic *efx); + +/* Set the output value of a gpio */ +void falcon_txc_set_gpio_val(struct ef4_nic *efx, int pin, int on) +{ + ef4_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_OUTPUT, 1 << pin, on); +} + +/* Set up the GPIO direction register */ +void falcon_txc_set_gpio_dir(struct ef4_nic *efx, int pin, int dir) +{ + ef4_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_DIR, 1 << pin, dir); +} + +/* Reset the PMA/PMD MMD. The documentation is explicit that this does a + * global reset (it's less clear what reset of other MMDs does).*/ +static int txc_reset_phy(struct ef4_nic *efx) +{ + int rc = ef4_mdio_reset_mmd(efx, MDIO_MMD_PMAPMD, + TXC_MAX_RESET_TIME / TXC_RESET_WAIT, + TXC_RESET_WAIT); + if (rc < 0) + goto fail; + + /* Check that all the MMDs we expect are present and responding. */ + rc = ef4_mdio_check_mmds(efx, TXC_REQUIRED_DEVS); + if (rc < 0) + goto fail; + + return 0; + +fail: + netif_err(efx, hw, efx->net_dev, TXCNAME ": reset timed out!\n"); + return rc; +} + +/* Run a single BIST on one MMD */ +static int txc_bist_one(struct ef4_nic *efx, int mmd, int test) +{ + int ctrl, bctl; + int lane; + int rc = 0; + + /* Set PMA to test into loopback using Mt Diablo reg as per app note */ + ctrl = ef4_mdio_read(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL); + ctrl |= (1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN); + ef4_mdio_write(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL, ctrl); + + /* The BIST app. note lists these as 3 distinct steps. */ + /* Set the BIST type */ + bctl = (test << TXC_BIST_CTRL_TYPE_LBN); + ef4_mdio_write(efx, mmd, TXC_BIST_CTL, bctl); + + /* Set the BSTEN bit in the BIST Control register to enable */ + bctl |= (1 << TXC_BIST_CTRL_ENAB_LBN); + ef4_mdio_write(efx, mmd, TXC_BIST_CTL, bctl); + + /* Set the BSTRT bit in the BIST Control register */ + ef4_mdio_write(efx, mmd, TXC_BIST_CTL, + bctl | (1 << TXC_BIST_CTRL_STRT_LBN)); + + /* Wait. */ + udelay(TXC_BIST_DURATION); + + /* Set the BSTOP bit in the BIST Control register */ + bctl |= (1 << TXC_BIST_CTRL_STOP_LBN); + ef4_mdio_write(efx, mmd, TXC_BIST_CTL, bctl); + + /* The STOP bit should go off when things have stopped */ + while (bctl & (1 << TXC_BIST_CTRL_STOP_LBN)) + bctl = ef4_mdio_read(efx, mmd, TXC_BIST_CTL); + + /* Check all the error counts are 0 and all the frame counts are + non-zero */ + for (lane = 0; lane < 4; lane++) { + int count = ef4_mdio_read(efx, mmd, TXC_BIST_RX0ERRCNT + lane); + if (count != 0) { + netif_err(efx, hw, efx->net_dev, TXCNAME": BIST error. " + "Lane %d had %d errs\n", lane, count); + rc = -EIO; + } + count = ef4_mdio_read(efx, mmd, TXC_BIST_RX0FRMCNT + lane); + if (count == 0) { + netif_err(efx, hw, efx->net_dev, TXCNAME": BIST error. " + "Lane %d got 0 frames\n", lane); + rc = -EIO; + } + } + + if (rc == 0) + netif_info(efx, hw, efx->net_dev, TXCNAME": BIST pass\n"); + + /* Disable BIST */ + ef4_mdio_write(efx, mmd, TXC_BIST_CTL, 0); + + /* Turn off loopback */ + ctrl &= ~(1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN); + ef4_mdio_write(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL, ctrl); + + return rc; +} + +static int txc_bist(struct ef4_nic *efx) +{ + return txc_bist_one(efx, MDIO_MMD_PCS, TXC_BIST_CTRL_TYPE_TSD); +} + +/* Push the non-configurable defaults into the PHY. This must be + * done after every full reset */ +static void txc_apply_defaults(struct ef4_nic *efx) +{ + int mctrl; + + /* Turn amplitude down and preemphasis off on the host side + * (PHY<->MAC) as this is believed less likely to upset Falcon + * and no adverse effects have been noted. It probably also + * saves a picowatt or two */ + + /* Turn off preemphasis */ + ef4_mdio_write(efx, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE0, TXC_ATXPRE_NONE); + ef4_mdio_write(efx, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE1, TXC_ATXPRE_NONE); + + /* Turn down the amplitude */ + ef4_mdio_write(efx, MDIO_MMD_PHYXS, + TXC_ALRGS_ATXAMP0, TXC_ATXAMP_0820_BOTH); + ef4_mdio_write(efx, MDIO_MMD_PHYXS, + TXC_ALRGS_ATXAMP1, TXC_ATXAMP_0820_BOTH); + + /* Set the line side amplitude and preemphasis to the databook + * defaults as an erratum causes them to be 0 on at least some + * PHY rev.s */ + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXPRE0, TXC_ATXPRE_DEFAULT); + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXPRE1, TXC_ATXPRE_DEFAULT); + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXAMP0, TXC_ATXAMP_DEFAULT); + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXAMP1, TXC_ATXAMP_DEFAULT); + + /* Set up the LEDs */ + mctrl = ef4_mdio_read(efx, MDIO_MMD_PHYXS, TXC_MRGS_CTL); + + /* Set the Green and Red LEDs to their default modes */ + mctrl &= ~((1 << TXC_MCTL_TXLED_LBN) | (1 << TXC_MCTL_RXLED_LBN)); + ef4_mdio_write(efx, MDIO_MMD_PHYXS, TXC_MRGS_CTL, mctrl); + + /* Databook recommends doing this after configuration changes */ + txc_reset_logic(efx); + + falcon_board(efx)->type->init_phy(efx); +} + +static int txc43128_phy_probe(struct ef4_nic *efx) +{ + struct txc43128_data *phy_data; + + /* Allocate phy private storage */ + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; + + efx->mdio.mmds = TXC_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + efx->loopback_modes = TXC_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + + return 0; +} + +/* Initialisation entry point for this PHY driver */ +static int txc43128_phy_init(struct ef4_nic *efx) +{ + int rc; + + rc = txc_reset_phy(efx); + if (rc < 0) + return rc; + + rc = txc_bist(efx); + if (rc < 0) + return rc; + + txc_apply_defaults(efx); + + return 0; +} + +/* Set the lane power down state in the global registers */ +static void txc_glrgs_lane_power(struct ef4_nic *efx, int mmd) +{ + int pd = (1 << TXC_GLCMD_L01PD_LBN) | (1 << TXC_GLCMD_L23PD_LBN); + int ctl = ef4_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); + + if (!(efx->phy_mode & PHY_MODE_LOW_POWER)) + ctl &= ~pd; + else + ctl |= pd; + + ef4_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, ctl); +} + +/* Set the lane power down state in the analog control registers */ +static void txc_analog_lane_power(struct ef4_nic *efx, int mmd) +{ + int txpd = (1 << TXC_ATXCTL_TXPD3_LBN) | (1 << TXC_ATXCTL_TXPD2_LBN) + | (1 << TXC_ATXCTL_TXPD1_LBN) | (1 << TXC_ATXCTL_TXPD0_LBN); + int rxpd = (1 << TXC_ARXCTL_RXPD3_LBN) | (1 << TXC_ARXCTL_RXPD2_LBN) + | (1 << TXC_ARXCTL_RXPD1_LBN) | (1 << TXC_ARXCTL_RXPD0_LBN); + int txctl = ef4_mdio_read(efx, mmd, TXC_ALRGS_ATXCTL); + int rxctl = ef4_mdio_read(efx, mmd, TXC_ALRGS_ARXCTL); + + if (!(efx->phy_mode & PHY_MODE_LOW_POWER)) { + txctl &= ~txpd; + rxctl &= ~rxpd; + } else { + txctl |= txpd; + rxctl |= rxpd; + } + + ef4_mdio_write(efx, mmd, TXC_ALRGS_ATXCTL, txctl); + ef4_mdio_write(efx, mmd, TXC_ALRGS_ARXCTL, rxctl); +} + +static void txc_set_power(struct ef4_nic *efx) +{ + /* According to the data book, all the MMDs can do low power */ + ef4_mdio_set_mmds_lpower(efx, + !!(efx->phy_mode & PHY_MODE_LOW_POWER), + TXC_REQUIRED_DEVS); + + /* Global register bank is in PCS, PHY XS. These control the host + * side and line side settings respectively. */ + txc_glrgs_lane_power(efx, MDIO_MMD_PCS); + txc_glrgs_lane_power(efx, MDIO_MMD_PHYXS); + + /* Analog register bank in PMA/PMD, PHY XS */ + txc_analog_lane_power(efx, MDIO_MMD_PMAPMD); + txc_analog_lane_power(efx, MDIO_MMD_PHYXS); +} + +static void txc_reset_logic_mmd(struct ef4_nic *efx, int mmd) +{ + int val = ef4_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); + int tries = 50; + + val |= (1 << TXC_GLCMD_LMTSWRST_LBN); + ef4_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, val); + while (--tries) { + val = ef4_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); + if (!(val & (1 << TXC_GLCMD_LMTSWRST_LBN))) + break; + udelay(1); + } + if (!tries) + netif_info(efx, hw, efx->net_dev, + TXCNAME " Logic reset timed out!\n"); +} + +/* Perform a logic reset. This preserves the configuration registers + * and is needed for some configuration changes to take effect */ +static void txc_reset_logic(struct ef4_nic *efx) +{ + /* The data sheet claims we can do the logic reset on either the + * PCS or the PHYXS and the result is a reset of both host- and + * line-side logic. */ + txc_reset_logic_mmd(efx, MDIO_MMD_PCS); +} + +static bool txc43128_phy_read_link(struct ef4_nic *efx) +{ + return ef4_mdio_links_ok(efx, TXC_REQUIRED_DEVS); +} + +static int txc43128_phy_reconfigure(struct ef4_nic *efx) +{ + struct txc43128_data *phy_data = efx->phy_data; + enum ef4_phy_mode mode_change = efx->phy_mode ^ phy_data->phy_mode; + bool loop_change = LOOPBACK_CHANGED(phy_data, efx, TXC_LOOPBACKS); + + if (efx->phy_mode & mode_change & PHY_MODE_TX_DISABLED) { + txc_reset_phy(efx); + txc_apply_defaults(efx); + falcon_reset_xaui(efx); + mode_change &= ~PHY_MODE_TX_DISABLED; + } + + ef4_mdio_transmit_disable(efx); + ef4_mdio_phy_reconfigure(efx); + if (mode_change & PHY_MODE_LOW_POWER) + txc_set_power(efx); + + /* The data sheet claims this is required after every reconfiguration + * (note at end of 7.1), but we mustn't do it when nothing changes as + * it glitches the link, and reconfigure gets called on link change, + * so we get an IRQ storm on link up. */ + if (loop_change || mode_change) + txc_reset_logic(efx); + + phy_data->phy_mode = efx->phy_mode; + phy_data->loopback_mode = efx->loopback_mode; + + return 0; +} + +static void txc43128_phy_fini(struct ef4_nic *efx) +{ + /* Disable link events */ + ef4_mdio_write(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0); +} + +static void txc43128_phy_remove(struct ef4_nic *efx) +{ + kfree(efx->phy_data); + efx->phy_data = NULL; +} + +/* Periodic callback: this exists mainly to poll link status as we + * don't use LASI interrupts */ +static bool txc43128_phy_poll(struct ef4_nic *efx) +{ + struct txc43128_data *data = efx->phy_data; + bool was_up = efx->link_state.up; + + efx->link_state.up = txc43128_phy_read_link(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + + if (efx->link_state.up || (efx->loopback_mode != LOOPBACK_NONE)) { + data->bug10934_timer = jiffies; + } else { + if (time_after_eq(jiffies, (data->bug10934_timer + + BUG10934_RESET_INTERVAL))) { + data->bug10934_timer = jiffies; + txc_reset_logic(efx); + } + } + + return efx->link_state.up != was_up; +} + +static const char *const txc43128_test_names[] = { + "bist" +}; + +static const char *txc43128_test_name(struct ef4_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(txc43128_test_names)) + return txc43128_test_names[index]; + return NULL; +} + +static int txc43128_run_tests(struct ef4_nic *efx, int *results, unsigned flags) +{ + int rc; + + if (!(flags & ETH_TEST_FL_OFFLINE)) + return 0; + + rc = txc_reset_phy(efx); + if (rc < 0) + return rc; + + rc = txc_bist(efx); + txc_apply_defaults(efx); + results[0] = rc ? -1 : 1; + return rc; +} + +static void txc43128_get_settings(struct ef4_nic *efx, struct ethtool_cmd *ecmd) +{ + mdio45_ethtool_gset(&efx->mdio, ecmd); +} + +const struct ef4_phy_operations falcon_txc_phy_ops = { + .probe = txc43128_phy_probe, + .init = txc43128_phy_init, + .reconfigure = txc43128_phy_reconfigure, + .poll = txc43128_phy_poll, + .fini = txc43128_phy_fini, + .remove = txc43128_phy_remove, + .get_settings = txc43128_get_settings, + .set_settings = ef4_mdio_set_settings, + .test_alive = ef4_mdio_test_alive, + .run_tests = txc43128_run_tests, + .test_name = txc43128_test_name, +}; diff --git a/drivers/net/ethernet/sfc/falcon/workarounds.h b/drivers/net/ethernet/sfc/falcon/workarounds.h new file mode 100644 index 0000000..6af800b --- /dev/null +++ b/drivers/net/ethernet/sfc/falcon/workarounds.h @@ -0,0 +1,44 @@ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2006-2013 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EF4_WORKAROUNDS_H +#define EF4_WORKAROUNDS_H + +/* + * Hardware workarounds. + * Bug numbers are from Solarflare's Bugzilla. + */ + +#define EF4_WORKAROUND_FALCON_A(efx) (ef4_nic_rev(efx) <= EF4_REV_FALCON_A1) +#define EF4_WORKAROUND_FALCON_AB(efx) (ef4_nic_rev(efx) <= EF4_REV_FALCON_B0) +#define EF4_WORKAROUND_10G(efx) 1 + +/* Bit-bashed I2C reads cause performance drop */ +#define EF4_WORKAROUND_7884 EF4_WORKAROUND_10G +/* Truncated IPv4 packets can confuse the TX packet parser */ +#define EF4_WORKAROUND_15592 EF4_WORKAROUND_FALCON_AB + +/* Spurious parity errors in TSORT buffers */ +#define EF4_WORKAROUND_5129 EF4_WORKAROUND_FALCON_A +/* Unaligned read request >512 bytes after aligning may break TSORT */ +#define EF4_WORKAROUND_5391 EF4_WORKAROUND_FALCON_A +/* iSCSI parsing errors */ +#define EF4_WORKAROUND_5583 EF4_WORKAROUND_FALCON_A +/* RX events go missing */ +#define EF4_WORKAROUND_5676 EF4_WORKAROUND_FALCON_A +/* RX_RESET on A1 */ +#define EF4_WORKAROUND_6555 EF4_WORKAROUND_FALCON_A +/* Increase filter depth to avoid RX_RESET */ +#define EF4_WORKAROUND_7244 EF4_WORKAROUND_FALCON_A +/* Flushes may never complete */ +#define EF4_WORKAROUND_7803 EF4_WORKAROUND_FALCON_AB +/* Leak overlength packets rather than free */ +#define EF4_WORKAROUND_8071 EF4_WORKAROUND_FALCON_A + +#endif /* EF4_WORKAROUNDS_H */ diff --git a/drivers/net/ethernet/sfc/falcon_boards.c b/drivers/net/ethernet/sfc/falcon_boards.c deleted file mode 100644 index f6883b2..0000000 --- a/drivers/net/ethernet/sfc/falcon_boards.c +++ /dev/null @@ -1,764 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2007-2012 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ - -#include - -#include "net_driver.h" -#include "phy.h" -#include "efx.h" -#include "nic.h" -#include "workarounds.h" - -/* Macros for unpacking the board revision */ -/* The revision info is in host byte order. */ -#define FALCON_BOARD_TYPE(_rev) (_rev >> 8) -#define FALCON_BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) -#define FALCON_BOARD_MINOR(_rev) (_rev & 0xf) - -/* Board types */ -#define FALCON_BOARD_SFE4001 0x01 -#define FALCON_BOARD_SFE4002 0x02 -#define FALCON_BOARD_SFE4003 0x03 -#define FALCON_BOARD_SFN4112F 0x52 - -/* Board temperature is about 15°C above ambient when air flow is - * limited. The maximum acceptable ambient temperature varies - * depending on the PHY specifications but the critical temperature - * above which we should shut down to avoid damage is 80°C. */ -#define FALCON_BOARD_TEMP_BIAS 15 -#define FALCON_BOARD_TEMP_CRIT (80 + FALCON_BOARD_TEMP_BIAS) - -/* SFC4000 datasheet says: 'The maximum permitted junction temperature - * is 125°C; the thermal design of the environment for the SFC4000 - * should aim to keep this well below 100°C.' */ -#define FALCON_JUNC_TEMP_MIN 0 -#define FALCON_JUNC_TEMP_MAX 90 -#define FALCON_JUNC_TEMP_CRIT 125 - -/***************************************************************************** - * Support for LM87 sensor chip used on several boards - */ -#define LM87_REG_TEMP_HW_INT_LOCK 0x13 -#define LM87_REG_TEMP_HW_EXT_LOCK 0x14 -#define LM87_REG_TEMP_HW_INT 0x17 -#define LM87_REG_TEMP_HW_EXT 0x18 -#define LM87_REG_TEMP_EXT1 0x26 -#define LM87_REG_TEMP_INT 0x27 -#define LM87_REG_ALARMS1 0x41 -#define LM87_REG_ALARMS2 0x42 -#define LM87_IN_LIMITS(nr, _min, _max) \ - 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min -#define LM87_AIN_LIMITS(nr, _min, _max) \ - 0x3B + (nr), _max, 0x1A + (nr), _min -#define LM87_TEMP_INT_LIMITS(_min, _max) \ - 0x39, _max, 0x3A, _min -#define LM87_TEMP_EXT1_LIMITS(_min, _max) \ - 0x37, _max, 0x38, _min - -#define LM87_ALARM_TEMP_INT 0x10 -#define LM87_ALARM_TEMP_EXT1 0x20 - -#if IS_ENABLED(CONFIG_SENSORS_LM87) - -static int efx_poke_lm87(struct i2c_client *client, const u8 *reg_values) -{ - while (*reg_values) { - u8 reg = *reg_values++; - u8 value = *reg_values++; - int rc = i2c_smbus_write_byte_data(client, reg, value); - if (rc) - return rc; - } - return 0; -} - -static const u8 falcon_lm87_common_regs[] = { - LM87_REG_TEMP_HW_INT_LOCK, FALCON_BOARD_TEMP_CRIT, - LM87_REG_TEMP_HW_INT, FALCON_BOARD_TEMP_CRIT, - LM87_TEMP_EXT1_LIMITS(FALCON_JUNC_TEMP_MIN, FALCON_JUNC_TEMP_MAX), - LM87_REG_TEMP_HW_EXT_LOCK, FALCON_JUNC_TEMP_CRIT, - LM87_REG_TEMP_HW_EXT, FALCON_JUNC_TEMP_CRIT, - 0 -}; - -static int efx_init_lm87(struct efx_nic *efx, const struct i2c_board_info *info, - const u8 *reg_values) -{ - struct falcon_board *board = falcon_board(efx); - struct i2c_client *client = i2c_new_device(&board->i2c_adap, info); - int rc; - - if (!client) - return -EIO; - - /* Read-to-clear alarm/interrupt status */ - i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); - i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); - - rc = efx_poke_lm87(client, reg_values); - if (rc) - goto err; - rc = efx_poke_lm87(client, falcon_lm87_common_regs); - if (rc) - goto err; - - board->hwmon_client = client; - return 0; - -err: - i2c_unregister_device(client); - return rc; -} - -static void efx_fini_lm87(struct efx_nic *efx) -{ - i2c_unregister_device(falcon_board(efx)->hwmon_client); -} - -static int efx_check_lm87(struct efx_nic *efx, unsigned mask) -{ - struct i2c_client *client = falcon_board(efx)->hwmon_client; - bool temp_crit, elec_fault, is_failure; - u16 alarms; - s32 reg; - - /* If link is up then do not monitor temperature */ - if (EFX_WORKAROUND_7884(efx) && efx->link_state.up) - return 0; - - reg = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); - if (reg < 0) - return reg; - alarms = reg; - reg = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); - if (reg < 0) - return reg; - alarms |= reg << 8; - alarms &= mask; - - temp_crit = false; - if (alarms & LM87_ALARM_TEMP_INT) { - reg = i2c_smbus_read_byte_data(client, LM87_REG_TEMP_INT); - if (reg < 0) - return reg; - if (reg > FALCON_BOARD_TEMP_CRIT) - temp_crit = true; - } - if (alarms & LM87_ALARM_TEMP_EXT1) { - reg = i2c_smbus_read_byte_data(client, LM87_REG_TEMP_EXT1); - if (reg < 0) - return reg; - if (reg > FALCON_JUNC_TEMP_CRIT) - temp_crit = true; - } - elec_fault = alarms & ~(LM87_ALARM_TEMP_INT | LM87_ALARM_TEMP_EXT1); - is_failure = temp_crit || elec_fault; - - if (alarms) - netif_err(efx, hw, efx->net_dev, - "LM87 detected a hardware %s (status %02x:%02x)" - "%s%s%s%s\n", - is_failure ? "failure" : "problem", - alarms & 0xff, alarms >> 8, - (alarms & LM87_ALARM_TEMP_INT) ? - "; board is overheating" : "", - (alarms & LM87_ALARM_TEMP_EXT1) ? - "; controller is overheating" : "", - temp_crit ? "; reached critical temperature" : "", - elec_fault ? "; electrical fault" : ""); - - return is_failure ? -ERANGE : 0; -} - -#else /* !CONFIG_SENSORS_LM87 */ - -static inline int -efx_init_lm87(struct efx_nic *efx, const struct i2c_board_info *info, - const u8 *reg_values) -{ - return 0; -} -static inline void efx_fini_lm87(struct efx_nic *efx) -{ -} -static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask) -{ - return 0; -} - -#endif /* CONFIG_SENSORS_LM87 */ - -/***************************************************************************** - * Support for the SFE4001 NIC. - * - * The SFE4001 does not power-up fully at reset due to its high power - * consumption. We control its power via a PCA9539 I/O expander. - * It also has a MAX6647 temperature monitor which we expose to - * the lm90 driver. - * - * This also provides minimal support for reflashing the PHY, which is - * initiated by resetting it with the FLASH_CFG_1 pin pulled down. - * On SFE4001 rev A2 and later this is connected to the 3V3X output of - * the IO-expander. - * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually - * exclusive with the network device being open. - */ - -/************************************************************************** - * Support for I2C IO Expander device on SFE4001 - */ -#define PCA9539 0x74 - -#define P0_IN 0x00 -#define P0_OUT 0x02 -#define P0_INVERT 0x04 -#define P0_CONFIG 0x06 - -#define P0_EN_1V0X_LBN 0 -#define P0_EN_1V0X_WIDTH 1 -#define P0_EN_1V2_LBN 1 -#define P0_EN_1V2_WIDTH 1 -#define P0_EN_2V5_LBN 2 -#define P0_EN_2V5_WIDTH 1 -#define P0_EN_3V3X_LBN 3 -#define P0_EN_3V3X_WIDTH 1 -#define P0_EN_5V_LBN 4 -#define P0_EN_5V_WIDTH 1 -#define P0_SHORTEN_JTAG_LBN 5 -#define P0_SHORTEN_JTAG_WIDTH 1 -#define P0_X_TRST_LBN 6 -#define P0_X_TRST_WIDTH 1 -#define P0_DSP_RESET_LBN 7 -#define P0_DSP_RESET_WIDTH 1 - -#define P1_IN 0x01 -#define P1_OUT 0x03 -#define P1_INVERT 0x05 -#define P1_CONFIG 0x07 - -#define P1_AFE_PWD_LBN 0 -#define P1_AFE_PWD_WIDTH 1 -#define P1_DSP_PWD25_LBN 1 -#define P1_DSP_PWD25_WIDTH 1 -#define P1_RESERVED_LBN 2 -#define P1_RESERVED_WIDTH 2 -#define P1_SPARE_LBN 4 -#define P1_SPARE_WIDTH 4 - -/* Temperature Sensor */ -#define MAX664X_REG_RSL 0x02 -#define MAX664X_REG_WLHO 0x0B - -static void sfe4001_poweroff(struct efx_nic *efx) -{ - struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; - struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; - - /* Turn off all power rails and disable outputs */ - i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff); - i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff); - i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff); - - /* Clear any over-temperature alert */ - i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); -} - -static int sfe4001_poweron(struct efx_nic *efx) -{ - struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; - struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; - unsigned int i, j; - int rc; - u8 out; - - /* Clear any previous over-temperature alert */ - rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); - if (rc < 0) - return rc; - - /* Enable port 0 and port 1 outputs on IO expander */ - rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00); - if (rc) - return rc; - rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, - 0xff & ~(1 << P1_SPARE_LBN)); - if (rc) - goto fail_on; - - /* If PHY power is on, turn it all off and wait 1 second to - * ensure a full reset. - */ - rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT); - if (rc < 0) - goto fail_on; - out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) | - (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) | - (0 << P0_EN_1V0X_LBN)); - if (rc != out) { - netif_info(efx, hw, efx->net_dev, "power-cycling PHY\n"); - rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); - if (rc) - goto fail_on; - schedule_timeout_uninterruptible(HZ); - } - - for (i = 0; i < 20; ++i) { - /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */ - out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | - (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | - (1 << P0_X_TRST_LBN)); - if (efx->phy_mode & PHY_MODE_SPECIAL) - out |= 1 << P0_EN_3V3X_LBN; - - rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); - if (rc) - goto fail_on; - msleep(10); - - /* Turn on 1V power rail */ - out &= ~(1 << P0_EN_1V0X_LBN); - rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); - if (rc) - goto fail_on; - - netif_info(efx, hw, efx->net_dev, - "waiting for DSP boot (attempt %d)...\n", i); - - /* In flash config mode, DSP does not turn on AFE, so - * just wait 1 second. - */ - if (efx->phy_mode & PHY_MODE_SPECIAL) { - schedule_timeout_uninterruptible(HZ); - return 0; - } - - for (j = 0; j < 10; ++j) { - msleep(100); - - /* Check DSP has asserted AFE power line */ - rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN); - if (rc < 0) - goto fail_on; - if (rc & (1 << P1_AFE_PWD_LBN)) - return 0; - } - } - - netif_info(efx, hw, efx->net_dev, "timed out waiting for DSP boot\n"); - rc = -ETIMEDOUT; -fail_on: - sfe4001_poweroff(efx); - return rc; -} - -static ssize_t show_phy_flash_cfg(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); - return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL)); -} - -static ssize_t set_phy_flash_cfg(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); - enum efx_phy_mode old_mode, new_mode; - int err; - - rtnl_lock(); - old_mode = efx->phy_mode; - if (count == 0 || *buf == '0') - new_mode = old_mode & ~PHY_MODE_SPECIAL; - else - new_mode = PHY_MODE_SPECIAL; - if (!((old_mode ^ new_mode) & PHY_MODE_SPECIAL)) { - err = 0; - } else if (efx->state != STATE_READY || netif_running(efx->net_dev)) { - err = -EBUSY; - } else { - /* Reset the PHY, reconfigure the MAC and enable/disable - * MAC stats accordingly. */ - efx->phy_mode = new_mode; - if (new_mode & PHY_MODE_SPECIAL) - falcon_stop_nic_stats(efx); - err = sfe4001_poweron(efx); - if (!err) - err = efx_reconfigure_port(efx); - if (!(new_mode & PHY_MODE_SPECIAL)) - falcon_start_nic_stats(efx); - } - rtnl_unlock(); - - return err ? err : count; -} - -static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg); - -static void sfe4001_fini(struct efx_nic *efx) -{ - struct falcon_board *board = falcon_board(efx); - - netif_info(efx, drv, efx->net_dev, "%s\n", __func__); - - device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); - sfe4001_poweroff(efx); - i2c_unregister_device(board->ioexp_client); - i2c_unregister_device(board->hwmon_client); -} - -static int sfe4001_check_hw(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - s32 status; - - /* If XAUI link is up then do not monitor */ - if (EFX_WORKAROUND_7884(efx) && !nic_data->xmac_poll_required) - return 0; - - /* Check the powered status of the PHY. Lack of power implies that - * the MAX6647 has shut down power to it, probably due to a temp. - * alarm. Reading the power status rather than the MAX6647 status - * directly because the later is read-to-clear and would thus - * start to power up the PHY again when polled, causing us to blip - * the power undesirably. - * We know we can read from the IO expander because we did - * it during power-on. Assume failure now is bad news. */ - status = i2c_smbus_read_byte_data(falcon_board(efx)->ioexp_client, P1_IN); - if (status >= 0 && - (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0) - return 0; - - /* Use board power control, not PHY power control */ - sfe4001_poweroff(efx); - efx->phy_mode = PHY_MODE_OFF; - - return (status < 0) ? -EIO : -ERANGE; -} - -static const struct i2c_board_info sfe4001_hwmon_info = { - I2C_BOARD_INFO("max6647", 0x4e), -}; - -/* This board uses an I2C expander to provider power to the PHY, which needs to - * be turned on before the PHY can be used. - * Context: Process context, rtnl lock held - */ -static int sfe4001_init(struct efx_nic *efx) -{ - struct falcon_board *board = falcon_board(efx); - int rc; - -#if IS_ENABLED(CONFIG_SENSORS_LM90) - board->hwmon_client = - i2c_new_device(&board->i2c_adap, &sfe4001_hwmon_info); -#else - board->hwmon_client = - i2c_new_dummy(&board->i2c_adap, sfe4001_hwmon_info.addr); -#endif - if (!board->hwmon_client) - return -EIO; - - /* Raise board/PHY high limit from 85 to 90 degrees Celsius */ - rc = i2c_smbus_write_byte_data(board->hwmon_client, - MAX664X_REG_WLHO, 90); - if (rc) - goto fail_hwmon; - - board->ioexp_client = i2c_new_dummy(&board->i2c_adap, PCA9539); - if (!board->ioexp_client) { - rc = -EIO; - goto fail_hwmon; - } - - if (efx->phy_mode & PHY_MODE_SPECIAL) { - /* PHY won't generate a 156.25 MHz clock and MAC stats fetch - * will fail. */ - falcon_stop_nic_stats(efx); - } - rc = sfe4001_poweron(efx); - if (rc) - goto fail_ioexp; - - rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); - if (rc) - goto fail_on; - - netif_info(efx, hw, efx->net_dev, "PHY is powered on\n"); - return 0; - -fail_on: - sfe4001_poweroff(efx); -fail_ioexp: - i2c_unregister_device(board->ioexp_client); -fail_hwmon: - i2c_unregister_device(board->hwmon_client); - return rc; -} - -/***************************************************************************** - * Support for the SFE4002 - * - */ -static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */ - -static const u8 sfe4002_lm87_regs[] = { - LM87_IN_LIMITS(0, 0x7c, 0x99), /* 2.5V: 1.8V +/- 10% */ - LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */ - LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */ - LM87_IN_LIMITS(3, 0xac, 0xd4), /* 5V: 5.0V +/- 10% */ - LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */ - LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */ - LM87_AIN_LIMITS(0, 0x98, 0xbb), /* AIN1: 1.66V +/- 10% */ - LM87_AIN_LIMITS(1, 0x8a, 0xa9), /* AIN2: 1.5V +/- 10% */ - LM87_TEMP_INT_LIMITS(0, 80 + FALCON_BOARD_TEMP_BIAS), - LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX), - 0 -}; - -static const struct i2c_board_info sfe4002_hwmon_info = { - I2C_BOARD_INFO("lm87", 0x2e), - .platform_data = &sfe4002_lm87_channel, -}; - -/****************************************************************************/ -/* LED allocations. Note that on rev A0 boards the schematic and the reality - * differ: red and green are swapped. Below is the fixed (A1) layout (there - * are only 3 A0 boards in existence, so no real reason to make this - * conditional). - */ -#define SFE4002_FAULT_LED (2) /* Red */ -#define SFE4002_RX_LED (0) /* Green */ -#define SFE4002_TX_LED (1) /* Amber */ - -static void sfe4002_init_phy(struct efx_nic *efx) -{ - /* Set the TX and RX LEDs to reflect status and activity, and the - * fault LED off */ - falcon_qt202x_set_led(efx, SFE4002_TX_LED, - QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); - falcon_qt202x_set_led(efx, SFE4002_RX_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); - falcon_qt202x_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); -} - -static void sfe4002_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) -{ - falcon_qt202x_set_led( - efx, SFE4002_FAULT_LED, - (mode == EFX_LED_ON) ? QUAKE_LED_ON : QUAKE_LED_OFF); -} - -static int sfe4002_check_hw(struct efx_nic *efx) -{ - struct falcon_board *board = falcon_board(efx); - - /* A0 board rev. 4002s report a temperature fault the whole time - * (bad sensor) so we mask it out. */ - unsigned alarm_mask = - (board->major == 0 && board->minor == 0) ? - ~LM87_ALARM_TEMP_EXT1 : ~0; - - return efx_check_lm87(efx, alarm_mask); -} - -static int sfe4002_init(struct efx_nic *efx) -{ - return efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); -} - -/***************************************************************************** - * Support for the SFN4112F - * - */ -static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */ - -static const u8 sfn4112f_lm87_regs[] = { - LM87_IN_LIMITS(0, 0x7c, 0x99), /* 2.5V: 1.8V +/- 10% */ - LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */ - LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */ - LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */ - LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */ - LM87_AIN_LIMITS(1, 0x8a, 0xa9), /* AIN2: 1.5V +/- 10% */ - LM87_TEMP_INT_LIMITS(0, 60 + FALCON_BOARD_TEMP_BIAS), - LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX), - 0 -}; - -static const struct i2c_board_info sfn4112f_hwmon_info = { - I2C_BOARD_INFO("lm87", 0x2e), - .platform_data = &sfn4112f_lm87_channel, -}; - -#define SFN4112F_ACT_LED 0 -#define SFN4112F_LINK_LED 1 - -static void sfn4112f_init_phy(struct efx_nic *efx) -{ - falcon_qt202x_set_led(efx, SFN4112F_ACT_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT); - falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT); -} - -static void sfn4112f_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) -{ - int reg; - - switch (mode) { - case EFX_LED_OFF: - reg = QUAKE_LED_OFF; - break; - case EFX_LED_ON: - reg = QUAKE_LED_ON; - break; - default: - reg = QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT; - break; - } - - falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, reg); -} - -static int sfn4112f_check_hw(struct efx_nic *efx) -{ - /* Mask out unused sensors */ - return efx_check_lm87(efx, ~0x48); -} - -static int sfn4112f_init(struct efx_nic *efx) -{ - return efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs); -} - -/***************************************************************************** - * Support for the SFE4003 - * - */ -static u8 sfe4003_lm87_channel = 0x03; /* use AIN not FAN inputs */ - -static const u8 sfe4003_lm87_regs[] = { - LM87_IN_LIMITS(0, 0x67, 0x7f), /* 2.5V: 1.5V +/- 10% */ - LM87_IN_LIMITS(1, 0x4c, 0x5e), /* Vccp1: 1.2V +/- 10% */ - LM87_IN_LIMITS(2, 0xac, 0xd4), /* 3.3V: 3.3V +/- 10% */ - LM87_IN_LIMITS(4, 0xac, 0xe0), /* 12V: 10.8-14V */ - LM87_IN_LIMITS(5, 0x3f, 0x4f), /* Vccp2: 1.0V +/- 10% */ - LM87_TEMP_INT_LIMITS(0, 70 + FALCON_BOARD_TEMP_BIAS), - 0 -}; - -static const struct i2c_board_info sfe4003_hwmon_info = { - I2C_BOARD_INFO("lm87", 0x2e), - .platform_data = &sfe4003_lm87_channel, -}; - -/* Board-specific LED info. */ -#define SFE4003_RED_LED_GPIO 11 -#define SFE4003_LED_ON 1 -#define SFE4003_LED_OFF 0 - -static void sfe4003_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) -{ - struct falcon_board *board = falcon_board(efx); - - /* The LEDs were not wired to GPIOs before A3 */ - if (board->minor < 3 && board->major == 0) - return; - - falcon_txc_set_gpio_val( - efx, SFE4003_RED_LED_GPIO, - (mode == EFX_LED_ON) ? SFE4003_LED_ON : SFE4003_LED_OFF); -} - -static void sfe4003_init_phy(struct efx_nic *efx) -{ - struct falcon_board *board = falcon_board(efx); - - /* The LEDs were not wired to GPIOs before A3 */ - if (board->minor < 3 && board->major == 0) - return; - - falcon_txc_set_gpio_dir(efx, SFE4003_RED_LED_GPIO, TXC_GPIO_DIR_OUTPUT); - falcon_txc_set_gpio_val(efx, SFE4003_RED_LED_GPIO, SFE4003_LED_OFF); -} - -static int sfe4003_check_hw(struct efx_nic *efx) -{ - struct falcon_board *board = falcon_board(efx); - - /* A0/A1/A2 board rev. 4003s report a temperature fault the whole time - * (bad sensor) so we mask it out. */ - unsigned alarm_mask = - (board->major == 0 && board->minor <= 2) ? - ~LM87_ALARM_TEMP_EXT1 : ~0; - - return efx_check_lm87(efx, alarm_mask); -} - -static int sfe4003_init(struct efx_nic *efx) -{ - return efx_init_lm87(efx, &sfe4003_hwmon_info, sfe4003_lm87_regs); -} - -static const struct falcon_board_type board_types[] = { - { - .id = FALCON_BOARD_SFE4001, - .init = sfe4001_init, - .init_phy = efx_port_dummy_op_void, - .fini = sfe4001_fini, - .set_id_led = tenxpress_set_id_led, - .monitor = sfe4001_check_hw, - }, - { - .id = FALCON_BOARD_SFE4002, - .init = sfe4002_init, - .init_phy = sfe4002_init_phy, - .fini = efx_fini_lm87, - .set_id_led = sfe4002_set_id_led, - .monitor = sfe4002_check_hw, - }, - { - .id = FALCON_BOARD_SFE4003, - .init = sfe4003_init, - .init_phy = sfe4003_init_phy, - .fini = efx_fini_lm87, - .set_id_led = sfe4003_set_id_led, - .monitor = sfe4003_check_hw, - }, - { - .id = FALCON_BOARD_SFN4112F, - .init = sfn4112f_init, - .init_phy = sfn4112f_init_phy, - .fini = efx_fini_lm87, - .set_id_led = sfn4112f_set_id_led, - .monitor = sfn4112f_check_hw, - }, -}; - -int falcon_probe_board(struct efx_nic *efx, u16 revision_info) -{ - struct falcon_board *board = falcon_board(efx); - u8 type_id = FALCON_BOARD_TYPE(revision_info); - int i; - - board->major = FALCON_BOARD_MAJOR(revision_info); - board->minor = FALCON_BOARD_MINOR(revision_info); - - for (i = 0; i < ARRAY_SIZE(board_types); i++) - if (board_types[i].id == type_id) - board->type = &board_types[i]; - - if (board->type) { - return 0; - } else { - netif_err(efx, probe, efx->net_dev, "unknown board type %d\n", - type_id); - return -ENODEV; - } -} diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c index 3d5b91b..c282d66 100644 --- a/drivers/net/ethernet/sfc/farch.c +++ b/drivers/net/ethernet/sfc/farch.c @@ -25,7 +25,7 @@ #include "io.h" #include "workarounds.h" -/* Falcon-architecture (SFC4000 and SFC9000-family) support */ +/* Falcon-architecture (SFC9000-family) support */ /************************************************************************** * @@ -364,9 +364,6 @@ unsigned int efx_farch_tx_limit_len(struct efx_tx_queue *tx_queue, len = min(limit, len); - if (EFX_WORKAROUND_5391(tx_queue->efx) && (dma_addr & 0xf)) - len = min_t(unsigned int, len, 512 - (dma_addr & 0xf)); - return len; } @@ -384,6 +381,7 @@ int efx_farch_tx_probe(struct efx_tx_queue *tx_queue) void efx_farch_tx_init(struct efx_tx_queue *tx_queue) { + int csum = tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD; struct efx_nic *efx = tx_queue->efx; efx_oword_t reg; @@ -405,37 +403,18 @@ void efx_farch_tx_init(struct efx_tx_queue *tx_queue) FRF_AZ_TX_DESCQ_TYPE, 0, FRF_BZ_TX_NON_IP_DROP_DIS, 1); - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { - int csum = tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD; - EFX_SET_OWORD_FIELD(reg, FRF_BZ_TX_IP_CHKSM_DIS, !csum); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_TX_TCP_CHKSM_DIS, - !csum); - } + EFX_SET_OWORD_FIELD(reg, FRF_BZ_TX_IP_CHKSM_DIS, !csum); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_TX_TCP_CHKSM_DIS, !csum); efx_writeo_table(efx, ®, efx->type->txd_ptr_tbl_base, tx_queue->queue); - if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { - /* Only 128 bits in this register */ - BUILD_BUG_ON(EFX_MAX_TX_QUEUES > 128); - - efx_reado(efx, ®, FR_AA_TX_CHKSM_CFG); - if (tx_queue->queue & EFX_TXQ_TYPE_OFFLOAD) - __clear_bit_le(tx_queue->queue, ®); - else - __set_bit_le(tx_queue->queue, ®); - efx_writeo(efx, ®, FR_AA_TX_CHKSM_CFG); - } - - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { - EFX_POPULATE_OWORD_1(reg, - FRF_BZ_TX_PACE, - (tx_queue->queue & EFX_TXQ_TYPE_HIGHPRI) ? - FFE_BZ_TX_PACE_OFF : - FFE_BZ_TX_PACE_RESERVED); - efx_writeo_table(efx, ®, FR_BZ_TX_PACE_TBL, - tx_queue->queue); - } + EFX_POPULATE_OWORD_1(reg, + FRF_BZ_TX_PACE, + (tx_queue->queue & EFX_TXQ_TYPE_HIGHPRI) ? + FFE_BZ_TX_PACE_OFF : + FFE_BZ_TX_PACE_RESERVED); + efx_writeo_table(efx, ®, FR_BZ_TX_PACE_TBL, tx_queue->queue); } static void efx_farch_flush_tx_queue(struct efx_tx_queue *tx_queue) @@ -532,16 +511,10 @@ void efx_farch_rx_init(struct efx_rx_queue *rx_queue) { efx_oword_t rx_desc_ptr; struct efx_nic *efx = rx_queue->efx; - bool is_b0 = efx_nic_rev(efx) >= EFX_REV_FALCON_B0; - bool iscsi_digest_en = is_b0; bool jumbo_en; - /* For kernel-mode queues in Falcon A1, the JUMBO flag enables - * DMA to continue after a PCIe page boundary (and scattering - * is not possible). In Falcon B0 and Siena, it enables - * scatter. - */ - jumbo_en = !is_b0 || efx->rx_scatter; + /* For kernel-mode queues in Siena, the JUMBO flag enables scatter. */ + jumbo_en = efx->rx_scatter; netif_dbg(efx, hw, efx->net_dev, "RX queue %d ring in special buffers %d-%d\n", @@ -555,8 +528,8 @@ void efx_farch_rx_init(struct efx_rx_queue *rx_queue) /* Push RX descriptor ring to card */ EFX_POPULATE_OWORD_10(rx_desc_ptr, - FRF_AZ_RX_ISCSI_DDIG_EN, iscsi_digest_en, - FRF_AZ_RX_ISCSI_HDIG_EN, iscsi_digest_en, + FRF_AZ_RX_ISCSI_DDIG_EN, true, + FRF_AZ_RX_ISCSI_HDIG_EN, true, FRF_AZ_RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, FRF_AZ_RX_DESCQ_EVQ_ID, efx_rx_queue_channel(rx_queue)->channel, @@ -895,7 +868,7 @@ static u16 efx_farch_handle_rx_not_ok(struct efx_rx_queue *rx_queue, struct efx_nic *efx = rx_queue->efx; bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; - bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; + bool rx_ev_frm_trunc, rx_ev_tobe_disc; bool rx_ev_other_err, rx_ev_pause_frm; bool rx_ev_hdr_type, rx_ev_mcast_pkt; unsigned rx_ev_pkt_type; @@ -912,12 +885,10 @@ static u16 efx_farch_handle_rx_not_ok(struct efx_rx_queue *rx_queue, FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR); rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_ETH_CRC_ERR); rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_FRM_TRUNC); - rx_ev_drib_nib = ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) ? - 0 : EFX_QWORD_FIELD(*event, FSF_AA_RX_EV_DRIB_NIB)); rx_ev_pause_frm = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PAUSE_FRM_ERR); /* Every error apart from tobe_disc and pause_frm */ - rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | + rx_ev_other_err = (rx_ev_tcp_udp_chksum_err | rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); @@ -951,14 +922,13 @@ static u16 efx_farch_handle_rx_not_ok(struct efx_rx_queue *rx_queue, " [TCP_UDP_CHKSUM_ERR]" : "", rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", - rx_ev_drib_nib ? " [DRIB_NIB]" : "", rx_ev_tobe_disc ? " [TOBE_DISC]" : "", rx_ev_pause_frm ? " [PAUSE]" : ""); } #endif /* The frame must be discarded if any of these are true. */ - return (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | + return (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_tobe_disc | rx_ev_pause_frm) ? EFX_RX_PKT_DISCARD : 0; } @@ -987,8 +957,7 @@ efx_farch_handle_rx_bad_index(struct efx_rx_queue *rx_queue, unsigned index) "dropped %d events (index=%d expected=%d)\n", dropped, index, expected); - efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); + efx_schedule_reset(efx, RESET_TYPE_DISABLE); return false; } @@ -1254,10 +1223,7 @@ efx_farch_handle_driver_event(struct efx_channel *channel, efx_qword_t *event) "channel %d seen DRIVER RX_RESET event. " "Resetting.\n", channel->channel); atomic_inc(&efx->rx_reset); - efx_schedule_reset(efx, - EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : - RESET_TYPE_DISABLE); + efx_schedule_reset(efx, RESET_TYPE_DISABLE); break; case FSE_BZ_RX_DSC_ERROR_EV: if (ev_sub_data < EFX_VI_BASE) { @@ -1394,13 +1360,11 @@ int efx_farch_ev_init(struct efx_channel *channel) channel->channel, channel->eventq.index, channel->eventq.index + channel->eventq.entries - 1); - if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { - EFX_POPULATE_OWORD_3(reg, - FRF_CZ_TIMER_Q_EN, 1, - FRF_CZ_HOST_NOTIFY_MODE, 0, - FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS); - efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); - } + EFX_POPULATE_OWORD_3(reg, + FRF_CZ_TIMER_Q_EN, 1, + FRF_CZ_HOST_NOTIFY_MODE, 0, + FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS); + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); /* Pin event queue buffer */ efx_init_special_buffer(efx, &channel->eventq); @@ -1428,8 +1392,7 @@ void efx_farch_ev_fini(struct efx_channel *channel) EFX_ZERO_OWORD(reg); efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, channel->channel); - if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) - efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); /* Unpin event queue */ efx_fini_special_buffer(efx, &channel->eventq); @@ -1503,7 +1466,6 @@ int efx_farch_irq_test_generate(struct efx_nic *efx) */ irqreturn_t efx_farch_fatal_interrupt(struct efx_nic *efx) { - struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t *int_ker = efx->irq_status.addr; efx_oword_t fatal_intr; int error, mem_perr; @@ -1529,8 +1491,6 @@ irqreturn_t efx_farch_fatal_interrupt(struct efx_nic *efx) /* Disable both devices */ pci_clear_master(efx->pci_dev); - if (efx_nic_is_dual_func(efx)) - pci_clear_master(nic_data->pci_dev2); efx_farch_irq_disable_master(efx); /* Count errors and reset or disable the NIC accordingly */ @@ -1677,8 +1637,6 @@ void efx_farch_rx_push_indir_table(struct efx_nic *efx) size_t i = 0; efx_dword_t dword; - BUG_ON(efx_nic_rev(efx) < EFX_REV_FALCON_B0); - BUILD_BUG_ON(ARRAY_SIZE(efx->rx_indir_table) != FR_BZ_RX_INDIRECTION_TBL_ROWS); @@ -1806,8 +1764,7 @@ void efx_farch_init_common(struct efx_nic *efx) FRF_AZ_ILL_ADR_INT_KER_EN, 1, FRF_AZ_RBUF_OWN_INT_KER_EN, 1, FRF_AZ_TBUF_OWN_INT_KER_EN, 1); - if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) - EFX_SET_OWORD_FIELD(temp, FRF_CZ_SRAM_PERR_INT_P_KER_EN, 1); + EFX_SET_OWORD_FIELD(temp, FRF_CZ_SRAM_PERR_INT_P_KER_EN, 1); EFX_INVERT_OWORD(temp); efx_writeo(efx, &temp, FR_AZ_FATAL_INTR_KER); @@ -1827,22 +1784,18 @@ void efx_farch_init_common(struct efx_nic *efx) /* Disable hardware watchdog which can misfire */ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_WD_TMR, 0x3fffff); /* Squash TX of packets of 16 bytes or less */ - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) - EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { - EFX_POPULATE_OWORD_4(temp, - /* Default values */ - FRF_BZ_TX_PACE_SB_NOT_AF, 0x15, - FRF_BZ_TX_PACE_SB_AF, 0xb, - FRF_BZ_TX_PACE_FB_BASE, 0, - /* Allow large pace values in the - * fast bin. */ - FRF_BZ_TX_PACE_BIN_TH, - FFE_BZ_TX_PACE_RESERVED); - efx_writeo(efx, &temp, FR_BZ_TX_PACE); - } + EFX_POPULATE_OWORD_4(temp, + /* Default values */ + FRF_BZ_TX_PACE_SB_NOT_AF, 0x15, + FRF_BZ_TX_PACE_SB_AF, 0xb, + FRF_BZ_TX_PACE_FB_BASE, 0, + /* Allow large pace values in the fast bin. */ + FRF_BZ_TX_PACE_BIN_TH, + FFE_BZ_TX_PACE_RESERVED); + efx_writeo(efx, &temp, FR_BZ_TX_PACE); } /************************************************************************** @@ -2026,7 +1979,7 @@ static void efx_farch_filter_push_rx_config(struct efx_nic *efx) !!(table->spec[EFX_FARCH_FILTER_INDEX_UC_DEF].flags & table->spec[EFX_FARCH_FILTER_INDEX_MC_DEF].flags & EFX_FILTER_FLAG_RX_SCATTER)); - } else if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + } else { /* We don't expose 'default' filters because unmatched * packets always go to the queue number found in the * RSS table. But we still need to set the RX scatter @@ -2834,31 +2787,27 @@ int efx_farch_filter_table_probe(struct efx_nic *efx) return -ENOMEM; efx->filter_state = state; - if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { - table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP]; - table->id = EFX_FARCH_FILTER_TABLE_RX_IP; - table->offset = FR_BZ_RX_FILTER_TBL0; - table->size = FR_BZ_RX_FILTER_TBL0_ROWS; - table->step = FR_BZ_RX_FILTER_TBL0_STEP; - } + table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP]; + table->id = EFX_FARCH_FILTER_TABLE_RX_IP; + table->offset = FR_BZ_RX_FILTER_TBL0; + table->size = FR_BZ_RX_FILTER_TBL0_ROWS; + table->step = FR_BZ_RX_FILTER_TBL0_STEP; - if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { - table = &state->table[EFX_FARCH_FILTER_TABLE_RX_MAC]; - table->id = EFX_FARCH_FILTER_TABLE_RX_MAC; - table->offset = FR_CZ_RX_MAC_FILTER_TBL0; - table->size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS; - table->step = FR_CZ_RX_MAC_FILTER_TBL0_STEP; - - table = &state->table[EFX_FARCH_FILTER_TABLE_RX_DEF]; - table->id = EFX_FARCH_FILTER_TABLE_RX_DEF; - table->size = EFX_FARCH_FILTER_SIZE_RX_DEF; - - table = &state->table[EFX_FARCH_FILTER_TABLE_TX_MAC]; - table->id = EFX_FARCH_FILTER_TABLE_TX_MAC; - table->offset = FR_CZ_TX_MAC_FILTER_TBL0; - table->size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS; - table->step = FR_CZ_TX_MAC_FILTER_TBL0_STEP; - } + table = &state->table[EFX_FARCH_FILTER_TABLE_RX_MAC]; + table->id = EFX_FARCH_FILTER_TABLE_RX_MAC; + table->offset = FR_CZ_RX_MAC_FILTER_TBL0; + table->size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS; + table->step = FR_CZ_RX_MAC_FILTER_TBL0_STEP; + + table = &state->table[EFX_FARCH_FILTER_TABLE_RX_DEF]; + table->id = EFX_FARCH_FILTER_TABLE_RX_DEF; + table->size = EFX_FARCH_FILTER_SIZE_RX_DEF; + + table = &state->table[EFX_FARCH_FILTER_TABLE_TX_MAC]; + table->id = EFX_FARCH_FILTER_TABLE_TX_MAC; + table->offset = FR_CZ_TX_MAC_FILTER_TBL0; + table->size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS; + table->step = FR_CZ_TX_MAC_FILTER_TBL0_STEP; for (table_id = 0; table_id < EFX_FARCH_FILTER_TABLE_COUNT; table_id++) { table = &state->table[table_id]; diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 2415209..9956513 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -15,7 +15,6 @@ #include "io.h" #include "farch_regs.h" #include "mcdi_pcol.h" -#include "phy.h" /************************************************************************** * diff --git a/drivers/net/ethernet/sfc/mcdi_port.c b/drivers/net/ethernet/sfc/mcdi_port.c index 2a9228a..0f0eb27 100644 --- a/drivers/net/ethernet/sfc/mcdi_port.c +++ b/drivers/net/ethernet/sfc/mcdi_port.c @@ -13,7 +13,6 @@ #include #include "efx.h" -#include "phy.h" #include "mcdi.h" #include "mcdi_pcol.h" #include "nic.h" diff --git a/drivers/net/ethernet/sfc/mdio_10g.c b/drivers/net/ethernet/sfc/mdio_10g.c deleted file mode 100644 index 8ff954c..0000000 --- a/drivers/net/ethernet/sfc/mdio_10g.c +++ /dev/null @@ -1,323 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2006-2011 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ -/* - * Useful functions for working with MDIO clause 45 PHYs - */ -#include -#include -#include -#include "net_driver.h" -#include "mdio_10g.h" -#include "workarounds.h" - -unsigned efx_mdio_id_oui(u32 id) -{ - unsigned oui = 0; - int i; - - /* The bits of the OUI are designated a..x, with a=0 and b variable. - * In the id register c is the MSB but the OUI is conventionally - * written as bytes h..a, p..i, x..q. Reorder the bits accordingly. */ - for (i = 0; i < 22; ++i) - if (id & (1 << (i + 10))) - oui |= 1 << (i ^ 7); - - return oui; -} - -int efx_mdio_reset_mmd(struct efx_nic *port, int mmd, - int spins, int spintime) -{ - u32 ctrl; - - /* Catch callers passing values in the wrong units (or just silly) */ - EFX_BUG_ON_PARANOID(spins * spintime >= 5000); - - efx_mdio_write(port, mmd, MDIO_CTRL1, MDIO_CTRL1_RESET); - /* Wait for the reset bit to clear. */ - do { - msleep(spintime); - ctrl = efx_mdio_read(port, mmd, MDIO_CTRL1); - spins--; - - } while (spins && (ctrl & MDIO_CTRL1_RESET)); - - return spins ? spins : -ETIMEDOUT; -} - -static int efx_mdio_check_mmd(struct efx_nic *efx, int mmd) -{ - int status; - - if (mmd != MDIO_MMD_AN) { - /* Read MMD STATUS2 to check it is responding. */ - status = efx_mdio_read(efx, mmd, MDIO_STAT2); - if ((status & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL) { - netif_err(efx, hw, efx->net_dev, - "PHY MMD %d not responding.\n", mmd); - return -EIO; - } - } - - return 0; -} - -/* This ought to be ridiculous overkill. We expect it to fail rarely */ -#define MDIO45_RESET_TIME 1000 /* ms */ -#define MDIO45_RESET_ITERS 100 - -int efx_mdio_wait_reset_mmds(struct efx_nic *efx, unsigned int mmd_mask) -{ - const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS; - int tries = MDIO45_RESET_ITERS; - int rc = 0; - int in_reset; - - while (tries) { - int mask = mmd_mask; - int mmd = 0; - int stat; - in_reset = 0; - while (mask) { - if (mask & 1) { - stat = efx_mdio_read(efx, mmd, MDIO_CTRL1); - if (stat < 0) { - netif_err(efx, hw, efx->net_dev, - "failed to read status of" - " MMD %d\n", mmd); - return -EIO; - } - if (stat & MDIO_CTRL1_RESET) - in_reset |= (1 << mmd); - } - mask = mask >> 1; - mmd++; - } - if (!in_reset) - break; - tries--; - msleep(spintime); - } - if (in_reset != 0) { - netif_err(efx, hw, efx->net_dev, - "not all MMDs came out of reset in time." - " MMDs still in reset: %x\n", in_reset); - rc = -ETIMEDOUT; - } - return rc; -} - -int efx_mdio_check_mmds(struct efx_nic *efx, unsigned int mmd_mask) -{ - int mmd = 0, probe_mmd, devs1, devs2; - u32 devices; - - /* Historically we have probed the PHYXS to find out what devices are - * present,but that doesn't work so well if the PHYXS isn't expected - * to exist, if so just find the first item in the list supplied. */ - probe_mmd = (mmd_mask & MDIO_DEVS_PHYXS) ? MDIO_MMD_PHYXS : - __ffs(mmd_mask); - - /* Check all the expected MMDs are present */ - devs1 = efx_mdio_read(efx, probe_mmd, MDIO_DEVS1); - devs2 = efx_mdio_read(efx, probe_mmd, MDIO_DEVS2); - if (devs1 < 0 || devs2 < 0) { - netif_err(efx, hw, efx->net_dev, - "failed to read devices present\n"); - return -EIO; - } - devices = devs1 | (devs2 << 16); - if ((devices & mmd_mask) != mmd_mask) { - netif_err(efx, hw, efx->net_dev, - "required MMDs not present: got %x, wanted %x\n", - devices, mmd_mask); - return -ENODEV; - } - netif_vdbg(efx, hw, efx->net_dev, "Devices present: %x\n", devices); - - /* Check all required MMDs are responding and happy. */ - while (mmd_mask) { - if ((mmd_mask & 1) && efx_mdio_check_mmd(efx, mmd)) - return -EIO; - mmd_mask = mmd_mask >> 1; - mmd++; - } - - return 0; -} - -bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask) -{ - /* If the port is in loopback, then we should only consider a subset - * of mmd's */ - if (LOOPBACK_INTERNAL(efx)) - return true; - else if (LOOPBACK_MASK(efx) & LOOPBACKS_WS) - return false; - else if (efx_phy_mode_disabled(efx->phy_mode)) - return false; - else if (efx->loopback_mode == LOOPBACK_PHYXS) - mmd_mask &= ~(MDIO_DEVS_PHYXS | - MDIO_DEVS_PCS | - MDIO_DEVS_PMAPMD | - MDIO_DEVS_AN); - else if (efx->loopback_mode == LOOPBACK_PCS) - mmd_mask &= ~(MDIO_DEVS_PCS | - MDIO_DEVS_PMAPMD | - MDIO_DEVS_AN); - else if (efx->loopback_mode == LOOPBACK_PMAPMD) - mmd_mask &= ~(MDIO_DEVS_PMAPMD | - MDIO_DEVS_AN); - - return mdio45_links_ok(&efx->mdio, mmd_mask); -} - -void efx_mdio_transmit_disable(struct efx_nic *efx) -{ - efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, - MDIO_PMA_TXDIS, MDIO_PMD_TXDIS_GLOBAL, - efx->phy_mode & PHY_MODE_TX_DISABLED); -} - -void efx_mdio_phy_reconfigure(struct efx_nic *efx) -{ - efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, - MDIO_CTRL1, MDIO_PMA_CTRL1_LOOPBACK, - efx->loopback_mode == LOOPBACK_PMAPMD); - efx_mdio_set_flag(efx, MDIO_MMD_PCS, - MDIO_CTRL1, MDIO_PCS_CTRL1_LOOPBACK, - efx->loopback_mode == LOOPBACK_PCS); - efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, - MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK, - efx->loopback_mode == LOOPBACK_PHYXS_WS); -} - -static void efx_mdio_set_mmd_lpower(struct efx_nic *efx, - int lpower, int mmd) -{ - int stat = efx_mdio_read(efx, mmd, MDIO_STAT1); - - netif_vdbg(efx, drv, efx->net_dev, "Setting low power mode for MMD %d to %d\n", - mmd, lpower); - - if (stat & MDIO_STAT1_LPOWERABLE) { - efx_mdio_set_flag(efx, mmd, MDIO_CTRL1, - MDIO_CTRL1_LPOWER, lpower); - } -} - -void efx_mdio_set_mmds_lpower(struct efx_nic *efx, - int low_power, unsigned int mmd_mask) -{ - int mmd = 0; - mmd_mask &= ~MDIO_DEVS_AN; - while (mmd_mask) { - if (mmd_mask & 1) - efx_mdio_set_mmd_lpower(efx, low_power, mmd); - mmd_mask = (mmd_mask >> 1); - mmd++; - } -} - -/** - * efx_mdio_set_settings - Set (some of) the PHY settings over MDIO. - * @efx: Efx NIC - * @ecmd: New settings - */ -int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - struct ethtool_cmd prev = { .cmd = ETHTOOL_GSET }; - - efx->phy_op->get_settings(efx, &prev); - - if (ecmd->advertising == prev.advertising && - ethtool_cmd_speed(ecmd) == ethtool_cmd_speed(&prev) && - ecmd->duplex == prev.duplex && - ecmd->port == prev.port && - ecmd->autoneg == prev.autoneg) - return 0; - - /* We can only change these settings for -T PHYs */ - if (prev.port != PORT_TP || ecmd->port != PORT_TP) - return -EINVAL; - - /* Check that PHY supports these settings */ - if (!ecmd->autoneg || - (ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported) - return -EINVAL; - - efx_link_set_advertising(efx, ecmd->advertising | ADVERTISED_Autoneg); - efx_mdio_an_reconfigure(efx); - return 0; -} - -/** - * efx_mdio_an_reconfigure - Push advertising flags and restart autonegotiation - * @efx: Efx NIC - */ -void efx_mdio_an_reconfigure(struct efx_nic *efx) -{ - int reg; - - WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); - - /* Set up the base page */ - reg = ADVERTISE_CSMA | ADVERTISE_RESV; - if (efx->link_advertising & ADVERTISED_Pause) - reg |= ADVERTISE_PAUSE_CAP; - if (efx->link_advertising & ADVERTISED_Asym_Pause) - reg |= ADVERTISE_PAUSE_ASYM; - efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); - - /* Set up the (extended) next page */ - efx->phy_op->set_npage_adv(efx, efx->link_advertising); - - /* Enable and restart AN */ - reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); - reg |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART | MDIO_AN_CTRL1_XNP; - efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg); -} - -u8 efx_mdio_get_pause(struct efx_nic *efx) -{ - BUILD_BUG_ON(EFX_FC_AUTO & (EFX_FC_RX | EFX_FC_TX)); - - if (!(efx->wanted_fc & EFX_FC_AUTO)) - return efx->wanted_fc; - - WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); - - return mii_resolve_flowctrl_fdx( - mii_advertise_flowctrl(efx->wanted_fc), - efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA)); -} - -int efx_mdio_test_alive(struct efx_nic *efx) -{ - int rc; - int devad = __ffs(efx->mdio.mmds); - u16 physid1, physid2; - - mutex_lock(&efx->mac_lock); - - physid1 = efx_mdio_read(efx, devad, MDIO_DEVID1); - physid2 = efx_mdio_read(efx, devad, MDIO_DEVID2); - - if ((physid1 == 0x0000) || (physid1 == 0xffff) || - (physid2 == 0x0000) || (physid2 == 0xffff)) { - netif_err(efx, hw, efx->net_dev, - "no MDIO PHY present with ID %d\n", efx->mdio.prtad); - rc = -EINVAL; - } else { - rc = efx_mdio_check_mmds(efx, efx->mdio.mmds); - } - - mutex_unlock(&efx->mac_lock); - return rc; -} diff --git a/drivers/net/ethernet/sfc/mdio_10g.h b/drivers/net/ethernet/sfc/mdio_10g.h deleted file mode 100644 index 4a2dc4c..0000000 --- a/drivers/net/ethernet/sfc/mdio_10g.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2006-2011 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ - -#ifndef EFX_MDIO_10G_H -#define EFX_MDIO_10G_H - -#include - -/* - * Helper functions for doing 10G MDIO as specified in IEEE 802.3 clause 45. - */ - -#include "efx.h" - -static inline unsigned efx_mdio_id_rev(u32 id) { return id & 0xf; } -static inline unsigned efx_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; } -unsigned efx_mdio_id_oui(u32 id); - -static inline int efx_mdio_read(struct efx_nic *efx, int devad, int addr) -{ - return efx->mdio.mdio_read(efx->net_dev, efx->mdio.prtad, devad, addr); -} - -static inline void -efx_mdio_write(struct efx_nic *efx, int devad, int addr, int value) -{ - efx->mdio.mdio_write(efx->net_dev, efx->mdio.prtad, devad, addr, value); -} - -static inline u32 efx_mdio_read_id(struct efx_nic *efx, int mmd) -{ - u16 id_low = efx_mdio_read(efx, mmd, MDIO_DEVID2); - u16 id_hi = efx_mdio_read(efx, mmd, MDIO_DEVID1); - return (id_hi << 16) | (id_low); -} - -static inline bool efx_mdio_phyxgxs_lane_sync(struct efx_nic *efx) -{ - int i, lane_status; - bool sync; - - for (i = 0; i < 2; ++i) - lane_status = efx_mdio_read(efx, MDIO_MMD_PHYXS, - MDIO_PHYXS_LNSTAT); - - sync = !!(lane_status & MDIO_PHYXS_LNSTAT_ALIGN); - if (!sync) - netif_dbg(efx, hw, efx->net_dev, "XGXS lane status: %x\n", - lane_status); - return sync; -} - -const char *efx_mdio_mmd_name(int mmd); - -/* - * Reset a specific MMD and wait for reset to clear. - * Return number of spins left (>0) on success, -%ETIMEDOUT on failure. - * - * This function will sleep - */ -int efx_mdio_reset_mmd(struct efx_nic *efx, int mmd, int spins, int spintime); - -/* As efx_mdio_check_mmd but for multiple MMDs */ -int efx_mdio_check_mmds(struct efx_nic *efx, unsigned int mmd_mask); - -/* Check the link status of specified mmds in bit mask */ -bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask); - -/* Generic transmit disable support though PMAPMD */ -void efx_mdio_transmit_disable(struct efx_nic *efx); - -/* Generic part of reconfigure: set/clear loopback bits */ -void efx_mdio_phy_reconfigure(struct efx_nic *efx); - -/* Set the power state of the specified MMDs */ -void efx_mdio_set_mmds_lpower(struct efx_nic *efx, int low_power, - unsigned int mmd_mask); - -/* Set (some of) the PHY settings over MDIO */ -int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); - -/* Push advertising flags and restart autonegotiation */ -void efx_mdio_an_reconfigure(struct efx_nic *efx); - -/* Get pause parameters from AN if available (otherwise return - * requested pause parameters) - */ -u8 efx_mdio_get_pause(struct efx_nic *efx); - -/* Wait for specified MMDs to exit reset within a timeout */ -int efx_mdio_wait_reset_mmds(struct efx_nic *efx, unsigned int mmd_mask); - -/* Set or clear flag, debouncing */ -static inline void -efx_mdio_set_flag(struct efx_nic *efx, int devad, int addr, - int mask, bool state) -{ - mdio_set_flag(&efx->mdio, efx->mdio.prtad, devad, addr, mask, state); -} - -/* Liveness self-test for MDIO PHYs */ -int efx_mdio_test_alive(struct efx_nic *efx); - -#endif /* EFX_MDIO_10G_H */ diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 18aceb2..0bbd7e2 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -41,7 +41,7 @@ * **************************************************************************/ -#define EFX_DRIVER_VERSION "4.0" +#define EFX_DRIVER_VERSION "4.1" #ifdef DEBUG #define EFX_BUG_ON_PARANOID(x) BUG_ON(x) @@ -191,7 +191,6 @@ struct efx_tx_buffer { * Size of the region is efx_piobuf_size. * @piobuf_offset: Buffer offset to be specified in PIO descriptors * @initialised: Has hardware queue been initialised? - * @tx_min_size: Minimum transmit size for this queue. Depends on HW. * @handle_tso: TSO xmit preparation handler. Sets up the TSO metadata and * may also map tx data, depending on the nature of the TSO implementation. * @read_count: Current read pointer. @@ -242,7 +241,6 @@ struct efx_tx_queue { void __iomem *piobuf; unsigned int piobuf_offset; bool initialised; - unsigned int tx_min_size; /* Function pointers used in the fast path. */ int (*handle_tso)(struct efx_tx_queue*, struct sk_buff*, bool *); diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 06dd96e..2237746 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -18,11 +18,8 @@ #include "mcdi.h" enum { - EFX_REV_FALCON_A0 = 0, - EFX_REV_FALCON_A1 = 1, - EFX_REV_FALCON_B0 = 2, - EFX_REV_SIENA_A0 = 3, - EFX_REV_HUNT_A0 = 4, + EFX_REV_SIENA_A0 = 0, + EFX_REV_HUNT_A0 = 1, }; static inline int efx_nic_rev(struct efx_nic *efx) @@ -32,12 +29,6 @@ static inline int efx_nic_rev(struct efx_nic *efx) u32 efx_farch_fpga_ver(struct efx_nic *efx); -/* NIC has two interlinked PCI functions for the same port. */ -static inline bool efx_nic_is_dual_func(struct efx_nic *efx) -{ - return efx_nic_rev(efx) < EFX_REV_FALCON_B0; -} - /* Read the current event from the event queue */ static inline efx_qword_t *efx_event(struct efx_channel *channel, unsigned int index) @@ -144,11 +135,6 @@ enum { PHY_TYPE_SFT9001B = 10, }; -#define FALCON_XMAC_LOOPBACKS \ - ((1 << LOOPBACK_XGMII) | \ - (1 << LOOPBACK_XGXS) | \ - (1 << LOOPBACK_XAUI)) - /* Alignment of PCIe DMA boundaries (4KB) */ #define EFX_PAGE_SIZE 4096 /* Size and alignment of buffer table entries (same) */ @@ -161,160 +147,6 @@ enum { GENERIC_STAT_COUNT }; -/** - * struct falcon_board_type - board operations and type information - * @id: Board type id, as found in NVRAM - * @init: Allocate resources and initialise peripheral hardware - * @init_phy: Do board-specific PHY initialisation - * @fini: Shut down hardware and free resources - * @set_id_led: Set state of identifying LED or revert to automatic function - * @monitor: Board-specific health check function - */ -struct falcon_board_type { - u8 id; - int (*init) (struct efx_nic *nic); - void (*init_phy) (struct efx_nic *efx); - void (*fini) (struct efx_nic *nic); - void (*set_id_led) (struct efx_nic *efx, enum efx_led_mode mode); - int (*monitor) (struct efx_nic *nic); -}; - -/** - * struct falcon_board - board information - * @type: Type of board - * @major: Major rev. ('A', 'B' ...) - * @minor: Minor rev. (0, 1, ...) - * @i2c_adap: I2C adapter for on-board peripherals - * @i2c_data: Data for bit-banging algorithm - * @hwmon_client: I2C client for hardware monitor - * @ioexp_client: I2C client for power/port control - */ -struct falcon_board { - const struct falcon_board_type *type; - int major; - int minor; - struct i2c_adapter i2c_adap; - struct i2c_algo_bit_data i2c_data; - struct i2c_client *hwmon_client, *ioexp_client; -}; - -/** - * struct falcon_spi_device - a Falcon SPI (Serial Peripheral Interface) device - * @device_id: Controller's id for the device - * @size: Size (in bytes) - * @addr_len: Number of address bytes in read/write commands - * @munge_address: Flag whether addresses should be munged. - * Some devices with 9-bit addresses (e.g. AT25040A EEPROM) - * use bit 3 of the command byte as address bit A8, rather - * than having a two-byte address. If this flag is set, then - * commands should be munged in this way. - * @erase_command: Erase command (or 0 if sector erase not needed). - * @erase_size: Erase sector size (in bytes) - * Erase commands affect sectors with this size and alignment. - * This must be a power of two. - * @block_size: Write block size (in bytes). - * Write commands are limited to blocks with this size and alignment. - */ -struct falcon_spi_device { - int device_id; - unsigned int size; - unsigned int addr_len; - unsigned int munge_address:1; - u8 erase_command; - unsigned int erase_size; - unsigned int block_size; -}; - -static inline bool falcon_spi_present(const struct falcon_spi_device *spi) -{ - return spi->size != 0; -} - -enum { - FALCON_STAT_tx_bytes = GENERIC_STAT_COUNT, - FALCON_STAT_tx_packets, - FALCON_STAT_tx_pause, - FALCON_STAT_tx_control, - FALCON_STAT_tx_unicast, - FALCON_STAT_tx_multicast, - FALCON_STAT_tx_broadcast, - FALCON_STAT_tx_lt64, - FALCON_STAT_tx_64, - FALCON_STAT_tx_65_to_127, - FALCON_STAT_tx_128_to_255, - FALCON_STAT_tx_256_to_511, - FALCON_STAT_tx_512_to_1023, - FALCON_STAT_tx_1024_to_15xx, - FALCON_STAT_tx_15xx_to_jumbo, - FALCON_STAT_tx_gtjumbo, - FALCON_STAT_tx_non_tcpudp, - FALCON_STAT_tx_mac_src_error, - FALCON_STAT_tx_ip_src_error, - FALCON_STAT_rx_bytes, - FALCON_STAT_rx_good_bytes, - FALCON_STAT_rx_bad_bytes, - FALCON_STAT_rx_packets, - FALCON_STAT_rx_good, - FALCON_STAT_rx_bad, - FALCON_STAT_rx_pause, - FALCON_STAT_rx_control, - FALCON_STAT_rx_unicast, - FALCON_STAT_rx_multicast, - FALCON_STAT_rx_broadcast, - FALCON_STAT_rx_lt64, - FALCON_STAT_rx_64, - FALCON_STAT_rx_65_to_127, - FALCON_STAT_rx_128_to_255, - FALCON_STAT_rx_256_to_511, - FALCON_STAT_rx_512_to_1023, - FALCON_STAT_rx_1024_to_15xx, - FALCON_STAT_rx_15xx_to_jumbo, - FALCON_STAT_rx_gtjumbo, - FALCON_STAT_rx_bad_lt64, - FALCON_STAT_rx_bad_gtjumbo, - FALCON_STAT_rx_overflow, - FALCON_STAT_rx_symbol_error, - FALCON_STAT_rx_align_error, - FALCON_STAT_rx_length_error, - FALCON_STAT_rx_internal_error, - FALCON_STAT_rx_nodesc_drop_cnt, - FALCON_STAT_COUNT -}; - -/** - * struct falcon_nic_data - Falcon NIC state - * @pci_dev2: Secondary function of Falcon A - * @board: Board state and functions - * @stats: Hardware statistics - * @stats_disable_count: Nest count for disabling statistics fetches - * @stats_pending: Is there a pending DMA of MAC statistics. - * @stats_timer: A timer for regularly fetching MAC statistics. - * @spi_flash: SPI flash device - * @spi_eeprom: SPI EEPROM device - * @spi_lock: SPI bus lock - * @mdio_lock: MDIO bus lock - * @xmac_poll_required: XMAC link state needs polling - */ -struct falcon_nic_data { - struct pci_dev *pci_dev2; - struct falcon_board board; - u64 stats[FALCON_STAT_COUNT]; - unsigned int stats_disable_count; - bool stats_pending; - struct timer_list stats_timer; - struct falcon_spi_device spi_flash; - struct falcon_spi_device spi_eeprom; - struct mutex spi_lock; - struct mutex mdio_lock; - bool xmac_poll_required; -}; - -static inline struct falcon_board *falcon_board(struct efx_nic *efx) -{ - struct falcon_nic_data *data = efx->nic_data; - return &data->board; -} - enum { SIENA_STAT_tx_bytes = GENERIC_STAT_COUNT, SIENA_STAT_tx_good_bytes, diff --git a/drivers/net/ethernet/sfc/phy.h b/drivers/net/ethernet/sfc/phy.h deleted file mode 100644 index 803bf44..0000000 --- a/drivers/net/ethernet/sfc/phy.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2007-2010 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ - -#ifndef EFX_PHY_H -#define EFX_PHY_H - -/**************************************************************************** - * 10Xpress (SFX7101) PHY - */ -extern const struct efx_phy_operations falcon_sfx7101_phy_ops; - -void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); - -/**************************************************************************** - * AMCC/Quake QT202x PHYs - */ -extern const struct efx_phy_operations falcon_qt202x_phy_ops; - -/* These PHYs provide various H/W control states for LEDs */ -#define QUAKE_LED_LINK_INVAL (0) -#define QUAKE_LED_LINK_STAT (1) -#define QUAKE_LED_LINK_ACT (2) -#define QUAKE_LED_LINK_ACTSTAT (3) -#define QUAKE_LED_OFF (4) -#define QUAKE_LED_ON (5) -#define QUAKE_LED_LINK_INPUT (6) /* Pin is an input. */ -/* What link the LED tracks */ -#define QUAKE_LED_TXLINK (0) -#define QUAKE_LED_RXLINK (8) - -void falcon_qt202x_set_led(struct efx_nic *p, int led, int state); - -/**************************************************************************** -* Transwitch CX4 retimer -*/ -extern const struct efx_phy_operations falcon_txc_phy_ops; - -#define TXC_GPIO_DIR_INPUT 0 -#define TXC_GPIO_DIR_OUTPUT 1 - -void falcon_txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir); -void falcon_txc_set_gpio_val(struct efx_nic *efx, int pin, int val); - -#endif diff --git a/drivers/net/ethernet/sfc/qt202x_phy.c b/drivers/net/ethernet/sfc/qt202x_phy.c deleted file mode 100644 index efa3612..0000000 --- a/drivers/net/ethernet/sfc/qt202x_phy.c +++ /dev/null @@ -1,495 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2006-2012 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ -/* - * Driver for AMCC QT202x SFP+ and XFP adapters; see www.amcc.com for details - */ - -#include -#include -#include -#include "efx.h" -#include "mdio_10g.h" -#include "phy.h" -#include "nic.h" - -#define QT202X_REQUIRED_DEVS (MDIO_DEVS_PCS | \ - MDIO_DEVS_PMAPMD | \ - MDIO_DEVS_PHYXS) - -#define QT202X_LOOPBACKS ((1 << LOOPBACK_PCS) | \ - (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_PHYXS_WS)) - -/****************************************************************************/ -/* Quake-specific MDIO registers */ -#define MDIO_QUAKE_LED0_REG (0xD006) - -/* QT2025C only */ -#define PCS_FW_HEARTBEAT_REG 0xd7ee -#define PCS_FW_HEARTB_LBN 0 -#define PCS_FW_HEARTB_WIDTH 8 -#define PCS_FW_PRODUCT_CODE_1 0xd7f0 -#define PCS_FW_VERSION_1 0xd7f3 -#define PCS_FW_BUILD_1 0xd7f6 -#define PCS_UC8051_STATUS_REG 0xd7fd -#define PCS_UC_STATUS_LBN 0 -#define PCS_UC_STATUS_WIDTH 8 -#define PCS_UC_STATUS_FW_SAVE 0x20 -#define PMA_PMD_MODE_REG 0xc301 -#define PMA_PMD_RXIN_SEL_LBN 6 -#define PMA_PMD_FTX_CTRL2_REG 0xc309 -#define PMA_PMD_FTX_STATIC_LBN 13 -#define PMA_PMD_VEND1_REG 0xc001 -#define PMA_PMD_VEND1_LBTXD_LBN 15 -#define PCS_VEND1_REG 0xc000 -#define PCS_VEND1_LBTXD_LBN 5 - -void falcon_qt202x_set_led(struct efx_nic *p, int led, int mode) -{ - int addr = MDIO_QUAKE_LED0_REG + led; - efx_mdio_write(p, MDIO_MMD_PMAPMD, addr, mode); -} - -struct qt202x_phy_data { - enum efx_phy_mode phy_mode; - bool bug17190_in_bad_state; - unsigned long bug17190_timer; - u32 firmware_ver; -}; - -#define QT2022C2_MAX_RESET_TIME 500 -#define QT2022C2_RESET_WAIT 10 - -#define QT2025C_MAX_HEARTB_TIME (5 * HZ) -#define QT2025C_HEARTB_WAIT 100 -#define QT2025C_MAX_FWSTART_TIME (25 * HZ / 10) -#define QT2025C_FWSTART_WAIT 100 - -#define BUG17190_INTERVAL (2 * HZ) - -static int qt2025c_wait_heartbeat(struct efx_nic *efx) -{ - unsigned long timeout = jiffies + QT2025C_MAX_HEARTB_TIME; - int reg, old_counter = 0; - - /* Wait for firmware heartbeat to start */ - for (;;) { - int counter; - reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_FW_HEARTBEAT_REG); - if (reg < 0) - return reg; - counter = ((reg >> PCS_FW_HEARTB_LBN) & - ((1 << PCS_FW_HEARTB_WIDTH) - 1)); - if (old_counter == 0) - old_counter = counter; - else if (counter != old_counter) - break; - if (time_after(jiffies, timeout)) { - /* Some cables have EEPROMs that conflict with the - * PHY's on-board EEPROM so it cannot load firmware */ - netif_err(efx, hw, efx->net_dev, - "If an SFP+ direct attach cable is" - " connected, please check that it complies" - " with the SFP+ specification\n"); - return -ETIMEDOUT; - } - msleep(QT2025C_HEARTB_WAIT); - } - - return 0; -} - -static int qt2025c_wait_fw_status_good(struct efx_nic *efx) -{ - unsigned long timeout = jiffies + QT2025C_MAX_FWSTART_TIME; - int reg; - - /* Wait for firmware status to look good */ - for (;;) { - reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_UC8051_STATUS_REG); - if (reg < 0) - return reg; - if ((reg & - ((1 << PCS_UC_STATUS_WIDTH) - 1) << PCS_UC_STATUS_LBN) >= - PCS_UC_STATUS_FW_SAVE) - break; - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - msleep(QT2025C_FWSTART_WAIT); - } - - return 0; -} - -static void qt2025c_restart_firmware(struct efx_nic *efx) -{ - /* Restart microcontroller execution of firmware from RAM */ - efx_mdio_write(efx, 3, 0xe854, 0x00c0); - efx_mdio_write(efx, 3, 0xe854, 0x0040); - msleep(50); -} - -static int qt2025c_wait_reset(struct efx_nic *efx) -{ - int rc; - - rc = qt2025c_wait_heartbeat(efx); - if (rc != 0) - return rc; - - rc = qt2025c_wait_fw_status_good(efx); - if (rc == -ETIMEDOUT) { - /* Bug 17689: occasionally heartbeat starts but firmware status - * code never progresses beyond 0x00. Try again, once, after - * restarting execution of the firmware image. */ - netif_dbg(efx, hw, efx->net_dev, - "bashing QT2025C microcontroller\n"); - qt2025c_restart_firmware(efx); - rc = qt2025c_wait_heartbeat(efx); - if (rc != 0) - return rc; - rc = qt2025c_wait_fw_status_good(efx); - } - - return rc; -} - -static void qt2025c_firmware_id(struct efx_nic *efx) -{ - struct qt202x_phy_data *phy_data = efx->phy_data; - u8 firmware_id[9]; - size_t i; - - for (i = 0; i < sizeof(firmware_id); i++) - firmware_id[i] = efx_mdio_read(efx, MDIO_MMD_PCS, - PCS_FW_PRODUCT_CODE_1 + i); - netif_info(efx, probe, efx->net_dev, - "QT2025C firmware %xr%d v%d.%d.%d.%d [20%02d-%02d-%02d]\n", - (firmware_id[0] << 8) | firmware_id[1], firmware_id[2], - firmware_id[3] >> 4, firmware_id[3] & 0xf, - firmware_id[4], firmware_id[5], - firmware_id[6], firmware_id[7], firmware_id[8]); - phy_data->firmware_ver = ((firmware_id[3] & 0xf0) << 20) | - ((firmware_id[3] & 0x0f) << 16) | - (firmware_id[4] << 8) | firmware_id[5]; -} - -static void qt2025c_bug17190_workaround(struct efx_nic *efx) -{ - struct qt202x_phy_data *phy_data = efx->phy_data; - - /* The PHY can get stuck in a state where it reports PHY_XS and PMA/PMD - * layers up, but PCS down (no block_lock). If we notice this state - * persisting for a couple of seconds, we switch PMA/PMD loopback - * briefly on and then off again, which is normally sufficient to - * recover it. - */ - if (efx->link_state.up || - !efx_mdio_links_ok(efx, MDIO_DEVS_PMAPMD | MDIO_DEVS_PHYXS)) { - phy_data->bug17190_in_bad_state = false; - return; - } - - if (!phy_data->bug17190_in_bad_state) { - phy_data->bug17190_in_bad_state = true; - phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; - return; - } - - if (time_after_eq(jiffies, phy_data->bug17190_timer)) { - netif_dbg(efx, hw, efx->net_dev, "bashing QT2025C PMA/PMD\n"); - efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, - MDIO_PMA_CTRL1_LOOPBACK, true); - msleep(100); - efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, - MDIO_PMA_CTRL1_LOOPBACK, false); - phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; - } -} - -static int qt2025c_select_phy_mode(struct efx_nic *efx) -{ - struct qt202x_phy_data *phy_data = efx->phy_data; - struct falcon_board *board = falcon_board(efx); - int reg, rc, i; - uint16_t phy_op_mode; - - /* Only 2.0.1.0+ PHY firmware supports the more optimal SFP+ - * Self-Configure mode. Don't attempt any switching if we encounter - * older firmware. */ - if (phy_data->firmware_ver < 0x02000100) - return 0; - - /* In general we will get optimal behaviour in "SFP+ Self-Configure" - * mode; however, that powers down most of the PHY when no module is - * present, so we must use a different mode (any fixed mode will do) - * to be sure that loopbacks will work. */ - phy_op_mode = (efx->loopback_mode == LOOPBACK_NONE) ? 0x0038 : 0x0020; - - /* Only change mode if really necessary */ - reg = efx_mdio_read(efx, 1, 0xc319); - if ((reg & 0x0038) == phy_op_mode) - return 0; - netif_dbg(efx, hw, efx->net_dev, "Switching PHY to mode 0x%04x\n", - phy_op_mode); - - /* This sequence replicates the register writes configured in the boot - * EEPROM (including the differences between board revisions), except - * that the operating mode is changed, and the PHY is prevented from - * unnecessarily reloading the main firmware image again. */ - efx_mdio_write(efx, 1, 0xc300, 0x0000); - /* (Note: this portion of the boot EEPROM sequence, which bit-bashes 9 - * STOPs onto the firmware/module I2C bus to reset it, varies across - * board revisions, as the bus is connected to different GPIO/LED - * outputs on the PHY.) */ - if (board->major == 0 && board->minor < 2) { - efx_mdio_write(efx, 1, 0xc303, 0x4498); - for (i = 0; i < 9; i++) { - efx_mdio_write(efx, 1, 0xc303, 0x4488); - efx_mdio_write(efx, 1, 0xc303, 0x4480); - efx_mdio_write(efx, 1, 0xc303, 0x4490); - efx_mdio_write(efx, 1, 0xc303, 0x4498); - } - } else { - efx_mdio_write(efx, 1, 0xc303, 0x0920); - efx_mdio_write(efx, 1, 0xd008, 0x0004); - for (i = 0; i < 9; i++) { - efx_mdio_write(efx, 1, 0xc303, 0x0900); - efx_mdio_write(efx, 1, 0xd008, 0x0005); - efx_mdio_write(efx, 1, 0xc303, 0x0920); - efx_mdio_write(efx, 1, 0xd008, 0x0004); - } - efx_mdio_write(efx, 1, 0xc303, 0x4900); - } - efx_mdio_write(efx, 1, 0xc303, 0x4900); - efx_mdio_write(efx, 1, 0xc302, 0x0004); - efx_mdio_write(efx, 1, 0xc316, 0x0013); - efx_mdio_write(efx, 1, 0xc318, 0x0054); - efx_mdio_write(efx, 1, 0xc319, phy_op_mode); - efx_mdio_write(efx, 1, 0xc31a, 0x0098); - efx_mdio_write(efx, 3, 0x0026, 0x0e00); - efx_mdio_write(efx, 3, 0x0027, 0x0013); - efx_mdio_write(efx, 3, 0x0028, 0xa528); - efx_mdio_write(efx, 1, 0xd006, 0x000a); - efx_mdio_write(efx, 1, 0xd007, 0x0009); - efx_mdio_write(efx, 1, 0xd008, 0x0004); - /* This additional write is not present in the boot EEPROM. It - * prevents the PHY's internal boot ROM doing another pointless (and - * slow) reload of the firmware image (the microcontroller's code - * memory is not affected by the microcontroller reset). */ - efx_mdio_write(efx, 1, 0xc317, 0x00ff); - /* PMA/PMD loopback sets RXIN to inverse polarity and the firmware - * restart doesn't reset it. We need to do that ourselves. */ - efx_mdio_set_flag(efx, 1, PMA_PMD_MODE_REG, - 1 << PMA_PMD_RXIN_SEL_LBN, false); - efx_mdio_write(efx, 1, 0xc300, 0x0002); - msleep(20); - - /* Restart microcontroller execution of firmware from RAM */ - qt2025c_restart_firmware(efx); - - /* Wait for the microcontroller to be ready again */ - rc = qt2025c_wait_reset(efx); - if (rc < 0) { - netif_err(efx, hw, efx->net_dev, - "PHY microcontroller reset during mode switch " - "timed out\n"); - return rc; - } - - return 0; -} - -static int qt202x_reset_phy(struct efx_nic *efx) -{ - int rc; - - if (efx->phy_type == PHY_TYPE_QT2025C) { - /* Wait for the reset triggered by falcon_reset_hw() - * to complete */ - rc = qt2025c_wait_reset(efx); - if (rc < 0) - goto fail; - } else { - /* Reset the PHYXS MMD. This is documented as doing - * a complete soft reset. */ - rc = efx_mdio_reset_mmd(efx, MDIO_MMD_PHYXS, - QT2022C2_MAX_RESET_TIME / - QT2022C2_RESET_WAIT, - QT2022C2_RESET_WAIT); - if (rc < 0) - goto fail; - } - - /* Wait 250ms for the PHY to complete bootup */ - msleep(250); - - falcon_board(efx)->type->init_phy(efx); - - return 0; - - fail: - netif_err(efx, hw, efx->net_dev, "PHY reset timed out\n"); - return rc; -} - -static int qt202x_phy_probe(struct efx_nic *efx) -{ - struct qt202x_phy_data *phy_data; - - phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL); - if (!phy_data) - return -ENOMEM; - efx->phy_data = phy_data; - phy_data->phy_mode = efx->phy_mode; - phy_data->bug17190_in_bad_state = false; - phy_data->bug17190_timer = 0; - - efx->mdio.mmds = QT202X_REQUIRED_DEVS; - efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; - efx->loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS; - return 0; -} - -static int qt202x_phy_init(struct efx_nic *efx) -{ - u32 devid; - int rc; - - rc = qt202x_reset_phy(efx); - if (rc) { - netif_err(efx, probe, efx->net_dev, "PHY init failed\n"); - return rc; - } - - devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); - netif_info(efx, probe, efx->net_dev, - "PHY ID reg %x (OUI %06x model %02x revision %x)\n", - devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), - efx_mdio_id_rev(devid)); - - if (efx->phy_type == PHY_TYPE_QT2025C) - qt2025c_firmware_id(efx); - - return 0; -} - -static int qt202x_link_ok(struct efx_nic *efx) -{ - return efx_mdio_links_ok(efx, QT202X_REQUIRED_DEVS); -} - -static bool qt202x_phy_poll(struct efx_nic *efx) -{ - bool was_up = efx->link_state.up; - - efx->link_state.up = qt202x_link_ok(efx); - efx->link_state.speed = 10000; - efx->link_state.fd = true; - efx->link_state.fc = efx->wanted_fc; - - if (efx->phy_type == PHY_TYPE_QT2025C) - qt2025c_bug17190_workaround(efx); - - return efx->link_state.up != was_up; -} - -static int qt202x_phy_reconfigure(struct efx_nic *efx) -{ - struct qt202x_phy_data *phy_data = efx->phy_data; - - if (efx->phy_type == PHY_TYPE_QT2025C) { - int rc = qt2025c_select_phy_mode(efx); - if (rc) - return rc; - - /* There are several different register bits which can - * disable TX (and save power) on direct-attach cables - * or optical transceivers, varying somewhat between - * firmware versions. Only 'static mode' appears to - * cover everything. */ - mdio_set_flag( - &efx->mdio, efx->mdio.prtad, MDIO_MMD_PMAPMD, - PMA_PMD_FTX_CTRL2_REG, 1 << PMA_PMD_FTX_STATIC_LBN, - efx->phy_mode & PHY_MODE_TX_DISABLED || - efx->phy_mode & PHY_MODE_LOW_POWER || - efx->loopback_mode == LOOPBACK_PCS || - efx->loopback_mode == LOOPBACK_PMAPMD); - } else { - /* Reset the PHY when moving from tx off to tx on */ - if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) && - (phy_data->phy_mode & PHY_MODE_TX_DISABLED)) - qt202x_reset_phy(efx); - - efx_mdio_transmit_disable(efx); - } - - efx_mdio_phy_reconfigure(efx); - - phy_data->phy_mode = efx->phy_mode; - - return 0; -} - -static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - mdio45_ethtool_gset(&efx->mdio, ecmd); -} - -static void qt202x_phy_remove(struct efx_nic *efx) -{ - /* Free the context block */ - kfree(efx->phy_data); - efx->phy_data = NULL; -} - -static int qt202x_phy_get_module_info(struct efx_nic *efx, - struct ethtool_modinfo *modinfo) -{ - modinfo->type = ETH_MODULE_SFF_8079; - modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; - return 0; -} - -static int qt202x_phy_get_module_eeprom(struct efx_nic *efx, - struct ethtool_eeprom *ee, u8 *data) -{ - int mmd, reg_base, rc, i; - - if (efx->phy_type == PHY_TYPE_QT2025C) { - mmd = MDIO_MMD_PCS; - reg_base = 0xd000; - } else { - mmd = MDIO_MMD_PMAPMD; - reg_base = 0x8007; - } - - for (i = 0; i < ee->len; i++) { - rc = efx_mdio_read(efx, mmd, reg_base + ee->offset + i); - if (rc < 0) - return rc; - data[i] = rc; - } - - return 0; -} - -const struct efx_phy_operations falcon_qt202x_phy_ops = { - .probe = qt202x_phy_probe, - .init = qt202x_phy_init, - .reconfigure = qt202x_phy_reconfigure, - .poll = qt202x_phy_poll, - .fini = efx_port_dummy_op_void, - .remove = qt202x_phy_remove, - .get_settings = qt202x_phy_get_settings, - .set_settings = efx_mdio_set_settings, - .test_alive = efx_mdio_test_alive, - .get_module_eeprom = qt202x_phy_get_module_eeprom, - .get_module_info = qt202x_phy_get_module_info, -}; diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 02b0b527..7893a73 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -400,21 +400,10 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, */ rx_buf->flags |= EFX_RX_PKT_DISCARD; - if ((len > rx_buf->len) && EFX_WORKAROUND_8071(efx)) { - if (net_ratelimit()) - netif_err(efx, rx_err, efx->net_dev, - " RX queue %d seriously overlength " - "RX event (0x%x > 0x%x+0x%x). Leaking\n", - efx_rx_queue_index(rx_queue), len, max_len, - efx->type->rx_buffer_padding); - efx_schedule_reset(efx, RESET_TYPE_RX_RECOVERY); - } else { - if (net_ratelimit()) - netif_err(efx, rx_err, efx->net_dev, - " RX queue %d overlength RX event " - "(0x%x > 0x%x)\n", - efx_rx_queue_index(rx_queue), len, max_len); - } + if (net_ratelimit()) + netif_err(efx, rx_err, efx->net_dev, + "RX queue %d overlength RX event (%#x > %#x)\n", + efx_rx_queue_index(rx_queue), len, max_len); efx_rx_queue_channel(rx_queue)->n_rx_overlength++; } diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 3975cad..0c4a8dd 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -20,7 +20,6 @@ #include "nic.h" #include "farch_regs.h" #include "io.h" -#include "phy.h" #include "workarounds.h" #include "mcdi.h" #include "mcdi_pcol.h" diff --git a/drivers/net/ethernet/sfc/tenxpress.c b/drivers/net/ethernet/sfc/tenxpress.c deleted file mode 100644 index 2c90e6b..0000000 --- a/drivers/net/ethernet/sfc/tenxpress.c +++ /dev/null @@ -1,494 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2007-2011 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ - -#include -#include -#include -#include -#include "efx.h" -#include "mdio_10g.h" -#include "nic.h" -#include "phy.h" -#include "workarounds.h" - -/* We expect these MMDs to be in the package. */ -#define TENXPRESS_REQUIRED_DEVS (MDIO_DEVS_PMAPMD | \ - MDIO_DEVS_PCS | \ - MDIO_DEVS_PHYXS | \ - MDIO_DEVS_AN) - -#define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ - (1 << LOOPBACK_PCS) | \ - (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_PHYXS_WS)) - -/* We complain if we fail to see the link partner as 10G capable this many - * times in a row (must be > 1 as sampling the autoneg. registers is racy) - */ -#define MAX_BAD_LP_TRIES (5) - -/* Extended control register */ -#define PMA_PMD_XCONTROL_REG 49152 -#define PMA_PMD_EXT_GMII_EN_LBN 1 -#define PMA_PMD_EXT_GMII_EN_WIDTH 1 -#define PMA_PMD_EXT_CLK_OUT_LBN 2 -#define PMA_PMD_EXT_CLK_OUT_WIDTH 1 -#define PMA_PMD_LNPGA_POWERDOWN_LBN 8 -#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1 -#define PMA_PMD_EXT_CLK312_WIDTH 1 -#define PMA_PMD_EXT_LPOWER_LBN 12 -#define PMA_PMD_EXT_LPOWER_WIDTH 1 -#define PMA_PMD_EXT_ROBUST_LBN 14 -#define PMA_PMD_EXT_ROBUST_WIDTH 1 -#define PMA_PMD_EXT_SSR_LBN 15 -#define PMA_PMD_EXT_SSR_WIDTH 1 - -/* extended status register */ -#define PMA_PMD_XSTATUS_REG 49153 -#define PMA_PMD_XSTAT_MDIX_LBN 14 -#define PMA_PMD_XSTAT_FLP_LBN (12) - -/* LED control register */ -#define PMA_PMD_LED_CTRL_REG 49159 -#define PMA_PMA_LED_ACTIVITY_LBN (3) - -/* LED function override register */ -#define PMA_PMD_LED_OVERR_REG 49161 -/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/ -#define PMA_PMD_LED_LINK_LBN (0) -#define PMA_PMD_LED_SPEED_LBN (2) -#define PMA_PMD_LED_TX_LBN (4) -#define PMA_PMD_LED_RX_LBN (6) -/* Override settings */ -#define PMA_PMD_LED_AUTO (0) /* H/W control */ -#define PMA_PMD_LED_ON (1) -#define PMA_PMD_LED_OFF (2) -#define PMA_PMD_LED_FLASH (3) -#define PMA_PMD_LED_MASK 3 -/* All LEDs under hardware control */ -/* Green and Amber under hardware control, Red off */ -#define SFX7101_PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) - -#define PMA_PMD_SPEED_ENABLE_REG 49192 -#define PMA_PMD_100TX_ADV_LBN 1 -#define PMA_PMD_100TX_ADV_WIDTH 1 -#define PMA_PMD_1000T_ADV_LBN 2 -#define PMA_PMD_1000T_ADV_WIDTH 1 -#define PMA_PMD_10000T_ADV_LBN 3 -#define PMA_PMD_10000T_ADV_WIDTH 1 -#define PMA_PMD_SPEED_LBN 4 -#define PMA_PMD_SPEED_WIDTH 4 - -/* Misc register defines */ -#define PCS_CLOCK_CTRL_REG 55297 -#define PLL312_RST_N_LBN 2 - -#define PCS_SOFT_RST2_REG 55302 -#define SERDES_RST_N_LBN 13 -#define XGXS_RST_N_LBN 12 - -#define PCS_TEST_SELECT_REG 55303 /* PRM 10.5.8 */ -#define CLK312_EN_LBN 3 - -/* PHYXS registers */ -#define PHYXS_XCONTROL_REG 49152 -#define PHYXS_RESET_LBN 15 -#define PHYXS_RESET_WIDTH 1 - -#define PHYXS_TEST1 (49162) -#define LOOPBACK_NEAR_LBN (8) -#define LOOPBACK_NEAR_WIDTH (1) - -/* Boot status register */ -#define PCS_BOOT_STATUS_REG 53248 -#define PCS_BOOT_FATAL_ERROR_LBN 0 -#define PCS_BOOT_PROGRESS_LBN 1 -#define PCS_BOOT_PROGRESS_WIDTH 2 -#define PCS_BOOT_PROGRESS_INIT 0 -#define PCS_BOOT_PROGRESS_WAIT_MDIO 1 -#define PCS_BOOT_PROGRESS_CHECKSUM 2 -#define PCS_BOOT_PROGRESS_JUMP 3 -#define PCS_BOOT_DOWNLOAD_WAIT_LBN 3 -#define PCS_BOOT_CODE_STARTED_LBN 4 - -/* 100M/1G PHY registers */ -#define GPHY_XCONTROL_REG 49152 -#define GPHY_ISOLATE_LBN 10 -#define GPHY_ISOLATE_WIDTH 1 -#define GPHY_DUPLEX_LBN 8 -#define GPHY_DUPLEX_WIDTH 1 -#define GPHY_LOOPBACK_NEAR_LBN 14 -#define GPHY_LOOPBACK_NEAR_WIDTH 1 - -#define C22EXT_STATUS_REG 49153 -#define C22EXT_STATUS_LINK_LBN 2 -#define C22EXT_STATUS_LINK_WIDTH 1 - -#define C22EXT_MSTSLV_CTRL 49161 -#define C22EXT_MSTSLV_CTRL_ADV_1000_HD_LBN 8 -#define C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN 9 - -#define C22EXT_MSTSLV_STATUS 49162 -#define C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN 10 -#define C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN 11 - -/* Time to wait between powering down the LNPGA and turning off the power - * rails */ -#define LNPGA_PDOWN_WAIT (HZ / 5) - -struct tenxpress_phy_data { - enum efx_loopback_mode loopback_mode; - enum efx_phy_mode phy_mode; - int bad_lp_tries; -}; - -static int tenxpress_init(struct efx_nic *efx) -{ - /* Enable 312.5 MHz clock */ - efx_mdio_write(efx, MDIO_MMD_PCS, PCS_TEST_SELECT_REG, - 1 << CLK312_EN_LBN); - - /* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */ - efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG, - 1 << PMA_PMA_LED_ACTIVITY_LBN, true); - efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, - SFX7101_PMA_PMD_LED_DEFAULT); - - return 0; -} - -static int tenxpress_phy_probe(struct efx_nic *efx) -{ - struct tenxpress_phy_data *phy_data; - - /* Allocate phy private storage */ - phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); - if (!phy_data) - return -ENOMEM; - efx->phy_data = phy_data; - phy_data->phy_mode = efx->phy_mode; - - efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; - efx->mdio.mode_support = MDIO_SUPPORTS_C45; - - efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS; - - efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | - ADVERTISED_10000baseT_Full); - - return 0; -} - -static int tenxpress_phy_init(struct efx_nic *efx) -{ - int rc; - - falcon_board(efx)->type->init_phy(efx); - - if (!(efx->phy_mode & PHY_MODE_SPECIAL)) { - rc = efx_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); - if (rc < 0) - return rc; - - rc = efx_mdio_check_mmds(efx, TENXPRESS_REQUIRED_DEVS); - if (rc < 0) - return rc; - } - - rc = tenxpress_init(efx); - if (rc < 0) - return rc; - - /* Reinitialise flow control settings */ - efx_link_set_wanted_fc(efx, efx->wanted_fc); - efx_mdio_an_reconfigure(efx); - - schedule_timeout_uninterruptible(HZ / 5); /* 200ms */ - - /* Let XGXS and SerDes out of reset */ - falcon_reset_xaui(efx); - - return 0; -} - -/* Perform a "special software reset" on the PHY. The caller is - * responsible for saving and restoring the PHY hardware registers - * properly, and masking/unmasking LASI */ -static int tenxpress_special_reset(struct efx_nic *efx) -{ - int rc, reg; - - /* The XGMAC clock is driven from the SFX7101 312MHz clock, so - * a special software reset can glitch the XGMAC sufficiently for stats - * requests to fail. */ - falcon_stop_nic_stats(efx); - - /* Initiate reset */ - reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG); - reg |= (1 << PMA_PMD_EXT_SSR_LBN); - efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); - - mdelay(200); - - /* Wait for the blocks to come out of reset */ - rc = efx_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); - if (rc < 0) - goto out; - - /* Try and reconfigure the device */ - rc = tenxpress_init(efx); - if (rc < 0) - goto out; - - /* Wait for the XGXS state machine to churn */ - mdelay(10); -out: - falcon_start_nic_stats(efx); - return rc; -} - -static void sfx7101_check_bad_lp(struct efx_nic *efx, bool link_ok) -{ - struct tenxpress_phy_data *pd = efx->phy_data; - bool bad_lp; - int reg; - - if (link_ok) { - bad_lp = false; - } else { - /* Check that AN has started but not completed. */ - reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_STAT1); - if (!(reg & MDIO_AN_STAT1_LPABLE)) - return; /* LP status is unknown */ - bad_lp = !(reg & MDIO_AN_STAT1_COMPLETE); - if (bad_lp) - pd->bad_lp_tries++; - } - - /* Nothing to do if all is well and was previously so. */ - if (!pd->bad_lp_tries) - return; - - /* Use the RX (red) LED as an error indicator once we've seen AN - * failure several times in a row, and also log a message. */ - if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) { - reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, - PMA_PMD_LED_OVERR_REG); - reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN); - if (!bad_lp) { - reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN; - } else { - reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN; - netif_err(efx, link, efx->net_dev, - "appears to be plugged into a port" - " that is not 10GBASE-T capable. The PHY" - " supports 10GBASE-T ONLY, so no link can" - " be established\n"); - } - efx_mdio_write(efx, MDIO_MMD_PMAPMD, - PMA_PMD_LED_OVERR_REG, reg); - pd->bad_lp_tries = bad_lp; - } -} - -static bool sfx7101_link_ok(struct efx_nic *efx) -{ - return efx_mdio_links_ok(efx, - MDIO_DEVS_PMAPMD | - MDIO_DEVS_PCS | - MDIO_DEVS_PHYXS); -} - -static void tenxpress_ext_loopback(struct efx_nic *efx) -{ - efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, PHYXS_TEST1, - 1 << LOOPBACK_NEAR_LBN, - efx->loopback_mode == LOOPBACK_PHYXS); -} - -static void tenxpress_low_power(struct efx_nic *efx) -{ - efx_mdio_set_mmds_lpower( - efx, !!(efx->phy_mode & PHY_MODE_LOW_POWER), - TENXPRESS_REQUIRED_DEVS); -} - -static int tenxpress_phy_reconfigure(struct efx_nic *efx) -{ - struct tenxpress_phy_data *phy_data = efx->phy_data; - bool phy_mode_change, loop_reset; - - if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) { - phy_data->phy_mode = efx->phy_mode; - return 0; - } - - phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL && - phy_data->phy_mode != PHY_MODE_NORMAL); - loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, LOOPBACKS_EXTERNAL(efx)) || - LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY)); - - if (loop_reset || phy_mode_change) { - tenxpress_special_reset(efx); - falcon_reset_xaui(efx); - } - - tenxpress_low_power(efx); - efx_mdio_transmit_disable(efx); - efx_mdio_phy_reconfigure(efx); - tenxpress_ext_loopback(efx); - efx_mdio_an_reconfigure(efx); - - phy_data->loopback_mode = efx->loopback_mode; - phy_data->phy_mode = efx->phy_mode; - - return 0; -} - -static void -tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); - -/* Poll for link state changes */ -static bool tenxpress_phy_poll(struct efx_nic *efx) -{ - struct efx_link_state old_state = efx->link_state; - - efx->link_state.up = sfx7101_link_ok(efx); - efx->link_state.speed = 10000; - efx->link_state.fd = true; - efx->link_state.fc = efx_mdio_get_pause(efx); - - sfx7101_check_bad_lp(efx, efx->link_state.up); - - return !efx_link_state_equal(&efx->link_state, &old_state); -} - -static void sfx7101_phy_fini(struct efx_nic *efx) -{ - int reg; - - /* Power down the LNPGA */ - reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN); - efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); - - /* Waiting here ensures that the board fini, which can turn - * off the power to the PHY, won't get run until the LNPGA - * powerdown has been given long enough to complete. */ - schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */ -} - -static void tenxpress_phy_remove(struct efx_nic *efx) -{ - kfree(efx->phy_data); - efx->phy_data = NULL; -} - - -/* Override the RX, TX and link LEDs */ -void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) -{ - int reg; - - switch (mode) { - case EFX_LED_OFF: - reg = (PMA_PMD_LED_OFF << PMA_PMD_LED_TX_LBN) | - (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) | - (PMA_PMD_LED_OFF << PMA_PMD_LED_LINK_LBN); - break; - case EFX_LED_ON: - reg = (PMA_PMD_LED_ON << PMA_PMD_LED_TX_LBN) | - (PMA_PMD_LED_ON << PMA_PMD_LED_RX_LBN) | - (PMA_PMD_LED_ON << PMA_PMD_LED_LINK_LBN); - break; - default: - reg = SFX7101_PMA_PMD_LED_DEFAULT; - break; - } - - efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, reg); -} - -static const char *const sfx7101_test_names[] = { - "bist" -}; - -static const char *sfx7101_test_name(struct efx_nic *efx, unsigned int index) -{ - if (index < ARRAY_SIZE(sfx7101_test_names)) - return sfx7101_test_names[index]; - return NULL; -} - -static int -sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags) -{ - int rc; - - if (!(flags & ETH_TEST_FL_OFFLINE)) - return 0; - - /* BIST is automatically run after a special software reset */ - rc = tenxpress_special_reset(efx); - results[0] = rc ? -1 : 1; - - efx_mdio_an_reconfigure(efx); - - return rc; -} - -static void -tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - u32 adv = 0, lpa = 0; - int reg; - - reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL); - if (reg & MDIO_AN_10GBT_CTRL_ADV10G) - adv |= ADVERTISED_10000baseT_Full; - reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); - if (reg & MDIO_AN_10GBT_STAT_LP10G) - lpa |= ADVERTISED_10000baseT_Full; - - mdio45_ethtool_gset_npage(&efx->mdio, ecmd, adv, lpa); - - /* In loopback, the PHY automatically brings up the correct interface, - * but doesn't advertise the correct speed. So override it */ - if (LOOPBACK_EXTERNAL(efx)) - ethtool_cmd_speed_set(ecmd, SPEED_10000); -} - -static int tenxpress_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - if (!ecmd->autoneg) - return -EINVAL; - - return efx_mdio_set_settings(efx, ecmd); -} - -static void sfx7101_set_npage_adv(struct efx_nic *efx, u32 advertising) -{ - efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, - MDIO_AN_10GBT_CTRL_ADV10G, - advertising & ADVERTISED_10000baseT_Full); -} - -const struct efx_phy_operations falcon_sfx7101_phy_ops = { - .probe = tenxpress_phy_probe, - .init = tenxpress_phy_init, - .reconfigure = tenxpress_phy_reconfigure, - .poll = tenxpress_phy_poll, - .fini = sfx7101_phy_fini, - .remove = tenxpress_phy_remove, - .get_settings = tenxpress_get_settings, - .set_settings = tenxpress_set_settings, - .set_npage_adv = sfx7101_set_npage_adv, - .test_alive = efx_mdio_test_alive, - .test_name = sfx7101_test_name, - .run_tests = sfx7101_run_tests, -}; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index bb07034..f11a36a 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -97,10 +97,8 @@ unsigned int efx_tx_max_skb_descs(struct efx_nic *efx) */ unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS; - /* Possibly one more per segment for the alignment workaround, - * or for option descriptors - */ - if (EFX_WORKAROUND_5391(efx) || efx_nic_rev(efx) >= EFX_REV_HUNT_A0) + /* Possibly one more per segment for option descriptors */ + if (efx_nic_rev(efx) >= EFX_REV_HUNT_A0) max_descs += EFX_TSO_MAX_SEGS; /* Possibly more for PCIe page boundaries within input fragments */ @@ -155,7 +153,6 @@ static void efx_tx_maybe_stop_queue(struct efx_tx_queue *txq1) static int efx_enqueue_skb_copy(struct efx_tx_queue *tx_queue, struct sk_buff *skb) { - unsigned int min_len = tx_queue->tx_min_size; unsigned int copy_len = skb->len; struct efx_tx_buffer *buffer; u8 *copy_buffer; @@ -171,12 +168,7 @@ static int efx_enqueue_skb_copy(struct efx_tx_queue *tx_queue, rc = skb_copy_bits(skb, 0, copy_buffer, copy_len); EFX_WARN_ON_PARANOID(rc); - if (unlikely(copy_len < min_len)) { - memset(copy_buffer + copy_len, 0, min_len - copy_len); - buffer->len = min_len; - } else { - buffer->len = copy_len; - } + buffer->len = copy_len; buffer->skb = skb; buffer->flags = EFX_TX_BUF_SKB; @@ -530,8 +522,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) tx_queue->pio_packets++; data_mapped = true; #endif - } else if (skb_len < tx_queue->tx_min_size || - (skb->data_len && skb_len <= EFX_TX_CB_SIZE)) { + } else if (skb->data_len && skb_len <= EFX_TX_CB_SIZE) { /* Pad short packets or coalesce short fragmented packets. */ if (efx_enqueue_skb_copy(tx_queue, skb)) goto err; @@ -677,7 +668,7 @@ int efx_setup_tc(struct net_device *net_dev, u32 handle, __be16 proto, num_tc = ntc->tc; - if (efx_nic_rev(efx) < EFX_REV_FALCON_B0 || num_tc > EFX_MAX_TX_TC) + if (num_tc > EFX_MAX_TX_TC) return -EINVAL; if (num_tc == net_dev->num_tc) @@ -837,9 +828,6 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue) */ tx_queue->handle_tso = efx_enqueue_skb_tso; - /* Some older hardware requires Tx writes larger than 32. */ - tx_queue->tx_min_size = EFX_WORKAROUND_15592(efx) ? 33 : 0; - /* Set up TX descriptor ring */ efx_nic_init_tx(tx_queue); diff --git a/drivers/net/ethernet/sfc/txc43128_phy.c b/drivers/net/ethernet/sfc/txc43128_phy.c deleted file mode 100644 index 194f67d..0000000 --- a/drivers/net/ethernet/sfc/txc43128_phy.c +++ /dev/null @@ -1,560 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers and boards - * Copyright 2006-2011 Solarflare Communications Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - */ - -/* - * Driver for Transwitch/Mysticom CX4 retimer - * see www.transwitch.com, part is TXC-43128 - */ - -#include -#include -#include "efx.h" -#include "mdio_10g.h" -#include "phy.h" -#include "nic.h" - -/* We expect these MMDs to be in the package */ -#define TXC_REQUIRED_DEVS (MDIO_DEVS_PCS | \ - MDIO_DEVS_PMAPMD | \ - MDIO_DEVS_PHYXS) - -#define TXC_LOOPBACKS ((1 << LOOPBACK_PCS) | \ - (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_PHYXS_WS)) - -/************************************************************************** - * - * Compile-time config - * - ************************************************************************** - */ -#define TXCNAME "TXC43128" -/* Total length of time we'll wait for the PHY to come out of reset (ms) */ -#define TXC_MAX_RESET_TIME 500 -/* Interval between checks (ms) */ -#define TXC_RESET_WAIT 10 -/* How long to run BIST (us) */ -#define TXC_BIST_DURATION 50 - -/************************************************************************** - * - * Register definitions - * - ************************************************************************** - */ - -/* Command register */ -#define TXC_GLRGS_GLCMD 0xc004 -/* Useful bits in command register */ -/* Lane power-down */ -#define TXC_GLCMD_L01PD_LBN 5 -#define TXC_GLCMD_L23PD_LBN 6 -/* Limited SW reset: preserves configuration but - * initiates a logic reset. Self-clearing */ -#define TXC_GLCMD_LMTSWRST_LBN 14 - -/* Signal Quality Control */ -#define TXC_GLRGS_GSGQLCTL 0xc01a -/* Enable bit */ -#define TXC_GSGQLCT_SGQLEN_LBN 15 -/* Lane selection */ -#define TXC_GSGQLCT_LNSL_LBN 13 -#define TXC_GSGQLCT_LNSL_WIDTH 2 - -/* Analog TX control */ -#define TXC_ALRGS_ATXCTL 0xc040 -/* Lane power-down */ -#define TXC_ATXCTL_TXPD3_LBN 15 -#define TXC_ATXCTL_TXPD2_LBN 14 -#define TXC_ATXCTL_TXPD1_LBN 13 -#define TXC_ATXCTL_TXPD0_LBN 12 - -/* Amplitude on lanes 0, 1 */ -#define TXC_ALRGS_ATXAMP0 0xc041 -/* Amplitude on lanes 2, 3 */ -#define TXC_ALRGS_ATXAMP1 0xc042 -/* Bit position of value for lane 0 (or 2) */ -#define TXC_ATXAMP_LANE02_LBN 3 -/* Bit position of value for lane 1 (or 3) */ -#define TXC_ATXAMP_LANE13_LBN 11 - -#define TXC_ATXAMP_1280_mV 0 -#define TXC_ATXAMP_1200_mV 8 -#define TXC_ATXAMP_1120_mV 12 -#define TXC_ATXAMP_1060_mV 14 -#define TXC_ATXAMP_0820_mV 25 -#define TXC_ATXAMP_0720_mV 26 -#define TXC_ATXAMP_0580_mV 27 -#define TXC_ATXAMP_0440_mV 28 - -#define TXC_ATXAMP_0820_BOTH \ - ((TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE02_LBN) \ - | (TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE13_LBN)) - -#define TXC_ATXAMP_DEFAULT 0x6060 /* From databook */ - -/* Preemphasis on lanes 0, 1 */ -#define TXC_ALRGS_ATXPRE0 0xc043 -/* Preemphasis on lanes 2, 3 */ -#define TXC_ALRGS_ATXPRE1 0xc044 - -#define TXC_ATXPRE_NONE 0 -#define TXC_ATXPRE_DEFAULT 0x1010 /* From databook */ - -#define TXC_ALRGS_ARXCTL 0xc045 -/* Lane power-down */ -#define TXC_ARXCTL_RXPD3_LBN 15 -#define TXC_ARXCTL_RXPD2_LBN 14 -#define TXC_ARXCTL_RXPD1_LBN 13 -#define TXC_ARXCTL_RXPD0_LBN 12 - -/* Main control */ -#define TXC_MRGS_CTL 0xc340 -/* Bits in main control */ -#define TXC_MCTL_RESET_LBN 15 /* Self clear */ -#define TXC_MCTL_TXLED_LBN 14 /* 1 to show align status */ -#define TXC_MCTL_RXLED_LBN 13 /* 1 to show align status */ - -/* GPIO output */ -#define TXC_GPIO_OUTPUT 0xc346 -#define TXC_GPIO_DIR 0xc348 - -/* Vendor-specific BIST registers */ -#define TXC_BIST_CTL 0xc280 -#define TXC_BIST_TXFRMCNT 0xc281 -#define TXC_BIST_RX0FRMCNT 0xc282 -#define TXC_BIST_RX1FRMCNT 0xc283 -#define TXC_BIST_RX2FRMCNT 0xc284 -#define TXC_BIST_RX3FRMCNT 0xc285 -#define TXC_BIST_RX0ERRCNT 0xc286 -#define TXC_BIST_RX1ERRCNT 0xc287 -#define TXC_BIST_RX2ERRCNT 0xc288 -#define TXC_BIST_RX3ERRCNT 0xc289 - -/* BIST type (controls bit patter in test) */ -#define TXC_BIST_CTRL_TYPE_LBN 10 -#define TXC_BIST_CTRL_TYPE_TSD 0 /* TranSwitch Deterministic */ -#define TXC_BIST_CTRL_TYPE_CRP 1 /* CRPAT standard */ -#define TXC_BIST_CTRL_TYPE_CJP 2 /* CJPAT standard */ -#define TXC_BIST_CTRL_TYPE_TSR 3 /* TranSwitch pseudo-random */ -/* Set this to 1 for 10 bit and 0 for 8 bit */ -#define TXC_BIST_CTRL_B10EN_LBN 12 -/* Enable BIST (write 0 to disable) */ -#define TXC_BIST_CTRL_ENAB_LBN 13 -/* Stop BIST (self-clears when stop complete) */ -#define TXC_BIST_CTRL_STOP_LBN 14 -/* Start BIST (cleared by writing 1 to STOP) */ -#define TXC_BIST_CTRL_STRT_LBN 15 - -/* Mt. Diablo test configuration */ -#define TXC_MTDIABLO_CTRL 0xc34f -#define TXC_MTDIABLO_CTRL_PMA_LOOP_LBN 10 - -struct txc43128_data { - unsigned long bug10934_timer; - enum efx_phy_mode phy_mode; - enum efx_loopback_mode loopback_mode; -}; - -/* The PHY sometimes needs a reset to bring the link back up. So long as - * it reports link down, we reset it every 5 seconds. - */ -#define BUG10934_RESET_INTERVAL (5 * HZ) - -/* Perform a reset that doesn't clear configuration changes */ -static void txc_reset_logic(struct efx_nic *efx); - -/* Set the output value of a gpio */ -void falcon_txc_set_gpio_val(struct efx_nic *efx, int pin, int on) -{ - efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_OUTPUT, 1 << pin, on); -} - -/* Set up the GPIO direction register */ -void falcon_txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir) -{ - efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_DIR, 1 << pin, dir); -} - -/* Reset the PMA/PMD MMD. The documentation is explicit that this does a - * global reset (it's less clear what reset of other MMDs does).*/ -static int txc_reset_phy(struct efx_nic *efx) -{ - int rc = efx_mdio_reset_mmd(efx, MDIO_MMD_PMAPMD, - TXC_MAX_RESET_TIME / TXC_RESET_WAIT, - TXC_RESET_WAIT); - if (rc < 0) - goto fail; - - /* Check that all the MMDs we expect are present and responding. */ - rc = efx_mdio_check_mmds(efx, TXC_REQUIRED_DEVS); - if (rc < 0) - goto fail; - - return 0; - -fail: - netif_err(efx, hw, efx->net_dev, TXCNAME ": reset timed out!\n"); - return rc; -} - -/* Run a single BIST on one MMD */ -static int txc_bist_one(struct efx_nic *efx, int mmd, int test) -{ - int ctrl, bctl; - int lane; - int rc = 0; - - /* Set PMA to test into loopback using Mt Diablo reg as per app note */ - ctrl = efx_mdio_read(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL); - ctrl |= (1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN); - efx_mdio_write(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL, ctrl); - - /* The BIST app. note lists these as 3 distinct steps. */ - /* Set the BIST type */ - bctl = (test << TXC_BIST_CTRL_TYPE_LBN); - efx_mdio_write(efx, mmd, TXC_BIST_CTL, bctl); - - /* Set the BSTEN bit in the BIST Control register to enable */ - bctl |= (1 << TXC_BIST_CTRL_ENAB_LBN); - efx_mdio_write(efx, mmd, TXC_BIST_CTL, bctl); - - /* Set the BSTRT bit in the BIST Control register */ - efx_mdio_write(efx, mmd, TXC_BIST_CTL, - bctl | (1 << TXC_BIST_CTRL_STRT_LBN)); - - /* Wait. */ - udelay(TXC_BIST_DURATION); - - /* Set the BSTOP bit in the BIST Control register */ - bctl |= (1 << TXC_BIST_CTRL_STOP_LBN); - efx_mdio_write(efx, mmd, TXC_BIST_CTL, bctl); - - /* The STOP bit should go off when things have stopped */ - while (bctl & (1 << TXC_BIST_CTRL_STOP_LBN)) - bctl = efx_mdio_read(efx, mmd, TXC_BIST_CTL); - - /* Check all the error counts are 0 and all the frame counts are - non-zero */ - for (lane = 0; lane < 4; lane++) { - int count = efx_mdio_read(efx, mmd, TXC_BIST_RX0ERRCNT + lane); - if (count != 0) { - netif_err(efx, hw, efx->net_dev, TXCNAME": BIST error. " - "Lane %d had %d errs\n", lane, count); - rc = -EIO; - } - count = efx_mdio_read(efx, mmd, TXC_BIST_RX0FRMCNT + lane); - if (count == 0) { - netif_err(efx, hw, efx->net_dev, TXCNAME": BIST error. " - "Lane %d got 0 frames\n", lane); - rc = -EIO; - } - } - - if (rc == 0) - netif_info(efx, hw, efx->net_dev, TXCNAME": BIST pass\n"); - - /* Disable BIST */ - efx_mdio_write(efx, mmd, TXC_BIST_CTL, 0); - - /* Turn off loopback */ - ctrl &= ~(1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN); - efx_mdio_write(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL, ctrl); - - return rc; -} - -static int txc_bist(struct efx_nic *efx) -{ - return txc_bist_one(efx, MDIO_MMD_PCS, TXC_BIST_CTRL_TYPE_TSD); -} - -/* Push the non-configurable defaults into the PHY. This must be - * done after every full reset */ -static void txc_apply_defaults(struct efx_nic *efx) -{ - int mctrl; - - /* Turn amplitude down and preemphasis off on the host side - * (PHY<->MAC) as this is believed less likely to upset Falcon - * and no adverse effects have been noted. It probably also - * saves a picowatt or two */ - - /* Turn off preemphasis */ - efx_mdio_write(efx, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE0, TXC_ATXPRE_NONE); - efx_mdio_write(efx, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE1, TXC_ATXPRE_NONE); - - /* Turn down the amplitude */ - efx_mdio_write(efx, MDIO_MMD_PHYXS, - TXC_ALRGS_ATXAMP0, TXC_ATXAMP_0820_BOTH); - efx_mdio_write(efx, MDIO_MMD_PHYXS, - TXC_ALRGS_ATXAMP1, TXC_ATXAMP_0820_BOTH); - - /* Set the line side amplitude and preemphasis to the databook - * defaults as an erratum causes them to be 0 on at least some - * PHY rev.s */ - efx_mdio_write(efx, MDIO_MMD_PMAPMD, - TXC_ALRGS_ATXPRE0, TXC_ATXPRE_DEFAULT); - efx_mdio_write(efx, MDIO_MMD_PMAPMD, - TXC_ALRGS_ATXPRE1, TXC_ATXPRE_DEFAULT); - efx_mdio_write(efx, MDIO_MMD_PMAPMD, - TXC_ALRGS_ATXAMP0, TXC_ATXAMP_DEFAULT); - efx_mdio_write(efx, MDIO_MMD_PMAPMD, - TXC_ALRGS_ATXAMP1, TXC_ATXAMP_DEFAULT); - - /* Set up the LEDs */ - mctrl = efx_mdio_read(efx, MDIO_MMD_PHYXS, TXC_MRGS_CTL); - - /* Set the Green and Red LEDs to their default modes */ - mctrl &= ~((1 << TXC_MCTL_TXLED_LBN) | (1 << TXC_MCTL_RXLED_LBN)); - efx_mdio_write(efx, MDIO_MMD_PHYXS, TXC_MRGS_CTL, mctrl); - - /* Databook recommends doing this after configuration changes */ - txc_reset_logic(efx); - - falcon_board(efx)->type->init_phy(efx); -} - -static int txc43128_phy_probe(struct efx_nic *efx) -{ - struct txc43128_data *phy_data; - - /* Allocate phy private storage */ - phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); - if (!phy_data) - return -ENOMEM; - efx->phy_data = phy_data; - phy_data->phy_mode = efx->phy_mode; - - efx->mdio.mmds = TXC_REQUIRED_DEVS; - efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; - - efx->loopback_modes = TXC_LOOPBACKS | FALCON_XMAC_LOOPBACKS; - - return 0; -} - -/* Initialisation entry point for this PHY driver */ -static int txc43128_phy_init(struct efx_nic *efx) -{ - int rc; - - rc = txc_reset_phy(efx); - if (rc < 0) - return rc; - - rc = txc_bist(efx); - if (rc < 0) - return rc; - - txc_apply_defaults(efx); - - return 0; -} - -/* Set the lane power down state in the global registers */ -static void txc_glrgs_lane_power(struct efx_nic *efx, int mmd) -{ - int pd = (1 << TXC_GLCMD_L01PD_LBN) | (1 << TXC_GLCMD_L23PD_LBN); - int ctl = efx_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); - - if (!(efx->phy_mode & PHY_MODE_LOW_POWER)) - ctl &= ~pd; - else - ctl |= pd; - - efx_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, ctl); -} - -/* Set the lane power down state in the analog control registers */ -static void txc_analog_lane_power(struct efx_nic *efx, int mmd) -{ - int txpd = (1 << TXC_ATXCTL_TXPD3_LBN) | (1 << TXC_ATXCTL_TXPD2_LBN) - | (1 << TXC_ATXCTL_TXPD1_LBN) | (1 << TXC_ATXCTL_TXPD0_LBN); - int rxpd = (1 << TXC_ARXCTL_RXPD3_LBN) | (1 << TXC_ARXCTL_RXPD2_LBN) - | (1 << TXC_ARXCTL_RXPD1_LBN) | (1 << TXC_ARXCTL_RXPD0_LBN); - int txctl = efx_mdio_read(efx, mmd, TXC_ALRGS_ATXCTL); - int rxctl = efx_mdio_read(efx, mmd, TXC_ALRGS_ARXCTL); - - if (!(efx->phy_mode & PHY_MODE_LOW_POWER)) { - txctl &= ~txpd; - rxctl &= ~rxpd; - } else { - txctl |= txpd; - rxctl |= rxpd; - } - - efx_mdio_write(efx, mmd, TXC_ALRGS_ATXCTL, txctl); - efx_mdio_write(efx, mmd, TXC_ALRGS_ARXCTL, rxctl); -} - -static void txc_set_power(struct efx_nic *efx) -{ - /* According to the data book, all the MMDs can do low power */ - efx_mdio_set_mmds_lpower(efx, - !!(efx->phy_mode & PHY_MODE_LOW_POWER), - TXC_REQUIRED_DEVS); - - /* Global register bank is in PCS, PHY XS. These control the host - * side and line side settings respectively. */ - txc_glrgs_lane_power(efx, MDIO_MMD_PCS); - txc_glrgs_lane_power(efx, MDIO_MMD_PHYXS); - - /* Analog register bank in PMA/PMD, PHY XS */ - txc_analog_lane_power(efx, MDIO_MMD_PMAPMD); - txc_analog_lane_power(efx, MDIO_MMD_PHYXS); -} - -static void txc_reset_logic_mmd(struct efx_nic *efx, int mmd) -{ - int val = efx_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); - int tries = 50; - - val |= (1 << TXC_GLCMD_LMTSWRST_LBN); - efx_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, val); - while (--tries) { - val = efx_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); - if (!(val & (1 << TXC_GLCMD_LMTSWRST_LBN))) - break; - udelay(1); - } - if (!tries) - netif_info(efx, hw, efx->net_dev, - TXCNAME " Logic reset timed out!\n"); -} - -/* Perform a logic reset. This preserves the configuration registers - * and is needed for some configuration changes to take effect */ -static void txc_reset_logic(struct efx_nic *efx) -{ - /* The data sheet claims we can do the logic reset on either the - * PCS or the PHYXS and the result is a reset of both host- and - * line-side logic. */ - txc_reset_logic_mmd(efx, MDIO_MMD_PCS); -} - -static bool txc43128_phy_read_link(struct efx_nic *efx) -{ - return efx_mdio_links_ok(efx, TXC_REQUIRED_DEVS); -} - -static int txc43128_phy_reconfigure(struct efx_nic *efx) -{ - struct txc43128_data *phy_data = efx->phy_data; - enum efx_phy_mode mode_change = efx->phy_mode ^ phy_data->phy_mode; - bool loop_change = LOOPBACK_CHANGED(phy_data, efx, TXC_LOOPBACKS); - - if (efx->phy_mode & mode_change & PHY_MODE_TX_DISABLED) { - txc_reset_phy(efx); - txc_apply_defaults(efx); - falcon_reset_xaui(efx); - mode_change &= ~PHY_MODE_TX_DISABLED; - } - - efx_mdio_transmit_disable(efx); - efx_mdio_phy_reconfigure(efx); - if (mode_change & PHY_MODE_LOW_POWER) - txc_set_power(efx); - - /* The data sheet claims this is required after every reconfiguration - * (note at end of 7.1), but we mustn't do it when nothing changes as - * it glitches the link, and reconfigure gets called on link change, - * so we get an IRQ storm on link up. */ - if (loop_change || mode_change) - txc_reset_logic(efx); - - phy_data->phy_mode = efx->phy_mode; - phy_data->loopback_mode = efx->loopback_mode; - - return 0; -} - -static void txc43128_phy_fini(struct efx_nic *efx) -{ - /* Disable link events */ - efx_mdio_write(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0); -} - -static void txc43128_phy_remove(struct efx_nic *efx) -{ - kfree(efx->phy_data); - efx->phy_data = NULL; -} - -/* Periodic callback: this exists mainly to poll link status as we - * don't use LASI interrupts */ -static bool txc43128_phy_poll(struct efx_nic *efx) -{ - struct txc43128_data *data = efx->phy_data; - bool was_up = efx->link_state.up; - - efx->link_state.up = txc43128_phy_read_link(efx); - efx->link_state.speed = 10000; - efx->link_state.fd = true; - efx->link_state.fc = efx->wanted_fc; - - if (efx->link_state.up || (efx->loopback_mode != LOOPBACK_NONE)) { - data->bug10934_timer = jiffies; - } else { - if (time_after_eq(jiffies, (data->bug10934_timer + - BUG10934_RESET_INTERVAL))) { - data->bug10934_timer = jiffies; - txc_reset_logic(efx); - } - } - - return efx->link_state.up != was_up; -} - -static const char *const txc43128_test_names[] = { - "bist" -}; - -static const char *txc43128_test_name(struct efx_nic *efx, unsigned int index) -{ - if (index < ARRAY_SIZE(txc43128_test_names)) - return txc43128_test_names[index]; - return NULL; -} - -static int txc43128_run_tests(struct efx_nic *efx, int *results, unsigned flags) -{ - int rc; - - if (!(flags & ETH_TEST_FL_OFFLINE)) - return 0; - - rc = txc_reset_phy(efx); - if (rc < 0) - return rc; - - rc = txc_bist(efx); - txc_apply_defaults(efx); - results[0] = rc ? -1 : 1; - return rc; -} - -static void txc43128_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - mdio45_ethtool_gset(&efx->mdio, ecmd); -} - -const struct efx_phy_operations falcon_txc_phy_ops = { - .probe = txc43128_phy_probe, - .init = txc43128_phy_init, - .reconfigure = txc43128_phy_reconfigure, - .poll = txc43128_phy_poll, - .fini = txc43128_phy_fini, - .remove = txc43128_phy_remove, - .get_settings = txc43128_get_settings, - .set_settings = efx_mdio_set_settings, - .test_alive = efx_mdio_test_alive, - .run_tests = txc43128_run_tests, - .test_name = txc43128_test_name, -}; diff --git a/drivers/net/ethernet/sfc/workarounds.h b/drivers/net/ethernet/sfc/workarounds.h index 351cd14..103f827 100644 --- a/drivers/net/ethernet/sfc/workarounds.h +++ b/drivers/net/ethernet/sfc/workarounds.h @@ -15,35 +15,14 @@ * Bug numbers are from Solarflare's Bugzilla. */ -#define EFX_WORKAROUND_FALCON_A(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) -#define EFX_WORKAROUND_FALCON_AB(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_B0) #define EFX_WORKAROUND_SIENA(efx) (efx_nic_rev(efx) == EFX_REV_SIENA_A0) #define EFX_WORKAROUND_10G(efx) 1 /* Bit-bashed I2C reads cause performance drop */ #define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G -/* Truncated IPv4 packets can confuse the TX packet parser */ -#define EFX_WORKAROUND_15592 EFX_WORKAROUND_FALCON_AB /* Legacy interrupt storm when interrupt fifo fills */ #define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA -/* Spurious parity errors in TSORT buffers */ -#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A -/* Unaligned read request >512 bytes after aligning may break TSORT */ -#define EFX_WORKAROUND_5391 EFX_WORKAROUND_FALCON_A -/* iSCSI parsing errors */ -#define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A -/* RX events go missing */ -#define EFX_WORKAROUND_5676 EFX_WORKAROUND_FALCON_A -/* RX_RESET on A1 */ -#define EFX_WORKAROUND_6555 EFX_WORKAROUND_FALCON_A -/* Increase filter depth to avoid RX_RESET */ -#define EFX_WORKAROUND_7244 EFX_WORKAROUND_FALCON_A -/* Flushes may never complete */ -#define EFX_WORKAROUND_7803 EFX_WORKAROUND_FALCON_AB -/* Leak overlength packets rather than free */ -#define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A - /* Lockup when writing event block registers at gen2/gen3 */ #define EFX_EF10_WORKAROUND_35388(efx) \ (((struct efx_ef10_nic_data *)efx->nic_data)->workaround_35388) -- cgit v1.1 From 820ee17b8d3b2a57b1ea20b247cc6a1dddaf8b8d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Nov 2016 09:57:17 -0800 Subject: net: phy: broadcom: Add support code for reading PHY counters Broadcom PHYs expose a number of PHY error counters: receive errors, false carrier sense, SerDes BER count, local and remote receive errors. Add support code to allow retrieving these error counters. Since the Broadcom PHY library code is used by several drivers, make it possible for them to specify the storage for the software copy of the statistics. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm-phy-lib.c | 70 +++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/bcm-phy-lib.h | 5 ++++ include/linux/brcmphy.h | 3 ++ 3 files changed, 78 insertions(+) diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index 3156ce6..ab9ad68 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -17,6 +17,7 @@ #include #include #include +#include #define MII_BCM_CHANNEL_WIDTH 0x2000 #define BCM_CL45VEN_EEE_ADV 0x3c @@ -317,6 +318,75 @@ int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) } EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); +struct bcm_phy_hw_stat { + const char *string; + u8 reg; + u8 shift; + u8 bits; +}; + +/* Counters freeze at either 0xffff or 0xff, better than nothing */ +static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { + { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, + { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, + { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, + { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, + { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, +}; + +int bcm_phy_get_sset_count(struct phy_device *phydev) +{ + return ARRAY_SIZE(bcm_phy_hw_stats); +} +EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); + +void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) + memcpy(data + i * ETH_GSTRING_LEN, + bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); +} +EXPORT_SYMBOL_GPL(bcm_phy_get_strings); + +#ifndef UINT64_MAX +#define UINT64_MAX (u64)(~((u64)0)) +#endif + +/* Caller is supposed to provide appropriate storage for the library code to + * access the shadow copy + */ +static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, + unsigned int i) +{ + struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; + int val; + u64 ret; + + val = phy_read(phydev, stat.reg); + if (val < 0) { + ret = UINT64_MAX; + } else { + val >>= stat.shift; + val = val & ((1 << stat.bits) - 1); + shadow[i] += val; + ret = shadow[i]; + } + + return ret; +} + +void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, + struct ethtool_stats *stats, u64 *data) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) + data[i] = bcm_phy_get_stat(phydev, shadow, i); +} +EXPORT_SYMBOL_GPL(bcm_phy_get_stats); + MODULE_DESCRIPTION("Broadcom PHY Library"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index a117f65..7c73808 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -42,4 +42,9 @@ int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count); int bcm_phy_downshift_set(struct phy_device *phydev, u8 count); +int bcm_phy_get_sset_count(struct phy_device *phydev); +void bcm_phy_get_strings(struct phy_device *phydev, u8 *data); +void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, + struct ethtool_stats *stats, u64 *data); + #endif /* _LINUX_BCM_PHY_LIB_H */ diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index f9f8aaf..4f7d8be 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -244,6 +244,9 @@ #define LPI_FEATURE_EN_DIG1000X 0x4000 /* Core register definitions*/ +#define MII_BRCM_CORE_BASE12 0x12 +#define MII_BRCM_CORE_BASE13 0x13 +#define MII_BRCM_CORE_BASE14 0x14 #define MII_BRCM_CORE_BASE1E 0x1E #define MII_BRCM_CORE_EXPB0 0xB0 #define MII_BRCM_CORE_EXPB1 0xB1 -- cgit v1.1 From b23ce9e8b3d6e862ad2f7f818802ae4958550a51 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 29 Nov 2016 09:57:18 -0800 Subject: net: phy: bcm7xxx: Plug in support for reading PHY error counters Broadcom BCM7xxx internal PHYs can leverage the Broadcom PHY library module PHY error counters helper functions, just implement the appropriate PHY driver function calls to do so. We need to allocate some storage space for our PHY statistics, and provide it to the Broadcom PHY library, so do this in a specific probe function, and slightly wrap the get_stats function call. Signed-off-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 5b3be4c..aae00bd 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -45,6 +45,10 @@ #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) +struct bcm7xxx_phy_priv { + u64 *stats; +}; + static void r_rc_cal_reset(struct phy_device *phydev) { /* Reset R_CAL/RC_CAL Engine */ @@ -350,6 +354,33 @@ static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev, return genphy_restart_aneg(phydev); } +static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) +{ + struct bcm7xxx_phy_priv *priv = phydev->priv; + + bcm_phy_get_stats(phydev, priv->stats, stats, data); +} + +static int bcm7xxx_28nm_probe(struct phy_device *phydev) +{ + struct bcm7xxx_phy_priv *priv; + + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + priv->stats = devm_kcalloc(&phydev->mdio.dev, + bcm_phy_get_sset_count(phydev), sizeof(u64), + GFP_KERNEL); + if (!priv->stats) + return -ENOMEM; + + return 0; +} + #define BCM7XXX_28NM_GPHY(_oui, _name) \ { \ .phy_id = (_oui), \ @@ -364,6 +395,10 @@ static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev, .resume = bcm7xxx_28nm_resume, \ .get_tunable = bcm7xxx_28nm_get_tunable, \ .set_tunable = bcm7xxx_28nm_set_tunable, \ + .get_sset_count = bcm_phy_get_sset_count, \ + .get_strings = bcm_phy_get_strings, \ + .get_stats = bcm7xxx_28nm_get_phy_stats, \ + .probe = bcm7xxx_28nm_probe, \ } #define BCM7XXX_40NM_EPHY(_oui, _name) \ -- cgit v1.1 From 85de8576a0b14aecc99136cfbf90e367fa2142cb Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 28 Nov 2016 23:16:54 +0100 Subject: bpf, xdp: allow to pass flags to dev_change_xdp_fd Add an IFLA_XDP_FLAGS attribute that can be passed for setting up XDP along with IFLA_XDP_FD, which eventually allows user space to implement typical add/replace/delete logic for programs. Right now, calling into dev_change_xdp_fd() will always replace previous programs. When passed XDP_FLAGS_UPDATE_IF_NOEXIST, we can handle this more graceful when requested by returning -EBUSY in case we try to attach a new program, but we find that another one is already attached. This will be used by upcoming front-end for iproute2 as well. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- include/uapi/linux/if_link.h | 4 ++++ net/core/dev.c | 20 ++++++++++++++++++-- net/core/rtnetlink.c | 14 +++++++++++++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4ffcd87..3755317 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3253,7 +3253,7 @@ int dev_get_phys_port_id(struct net_device *dev, int dev_get_phys_port_name(struct net_device *dev, char *name, size_t len); int dev_change_proto_down(struct net_device *dev, bool proto_down); -int dev_change_xdp_fd(struct net_device *dev, int fd); +int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags); struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev); struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq, int *ret); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 92b2d49..6b13e59 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -876,10 +876,14 @@ enum { /* XDP section */ +#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) +#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST) + enum { IFLA_XDP_UNSPEC, IFLA_XDP_FD, IFLA_XDP_ATTACHED, + IFLA_XDP_FLAGS, __IFLA_XDP_MAX, }; diff --git a/net/core/dev.c b/net/core/dev.c index 048b46b..bffb525 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6692,26 +6692,42 @@ EXPORT_SYMBOL(dev_change_proto_down); * dev_change_xdp_fd - set or clear a bpf program for a device rx path * @dev: device * @fd: new program fd or negative value to clear + * @flags: xdp-related flags * * Set or clear a bpf program for a device */ -int dev_change_xdp_fd(struct net_device *dev, int fd) +int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags) { const struct net_device_ops *ops = dev->netdev_ops; struct bpf_prog *prog = NULL; - struct netdev_xdp xdp = {}; + struct netdev_xdp xdp; int err; + ASSERT_RTNL(); + if (!ops->ndo_xdp) return -EOPNOTSUPP; if (fd >= 0) { + if (flags & XDP_FLAGS_UPDATE_IF_NOEXIST) { + memset(&xdp, 0, sizeof(xdp)); + xdp.command = XDP_QUERY_PROG; + + err = ops->ndo_xdp(dev, &xdp); + if (err < 0) + return err; + if (xdp.prog_attached) + return -EBUSY; + } + prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP); if (IS_ERR(prog)) return PTR_ERR(prog); } + memset(&xdp, 0, sizeof(xdp)); xdp.command = XDP_SETUP_PROG; xdp.prog = prog; + err = ops->ndo_xdp(dev, &xdp); if (err < 0 && prog) bpf_prog_put(prog); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 4e60525..bd85570 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1505,6 +1505,7 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = { static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { [IFLA_XDP_FD] = { .type = NLA_S32 }, [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, + [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, }; static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla) @@ -2164,6 +2165,7 @@ static int do_setlink(const struct sk_buff *skb, if (tb[IFLA_XDP]) { struct nlattr *xdp[IFLA_XDP_MAX + 1]; + u32 xdp_flags = 0; err = nla_parse_nested(xdp, IFLA_XDP_MAX, tb[IFLA_XDP], ifla_xdp_policy); @@ -2174,9 +2176,19 @@ static int do_setlink(const struct sk_buff *skb, err = -EINVAL; goto errout; } + + if (xdp[IFLA_XDP_FLAGS]) { + xdp_flags = nla_get_u32(xdp[IFLA_XDP_FLAGS]); + if (xdp_flags & ~XDP_FLAGS_MASK) { + err = -EINVAL; + goto errout; + } + } + if (xdp[IFLA_XDP_FD]) { err = dev_change_xdp_fd(dev, - nla_get_s32(xdp[IFLA_XDP_FD])); + nla_get_s32(xdp[IFLA_XDP_FD]), + xdp_flags); if (err) goto errout; status |= DO_SETLINK_NOTIFY; -- cgit v1.1 From 377fa64f19ff45d49dc95d4f9d9a0a95fea7f768 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 28 Nov 2016 23:52:19 +0100 Subject: net: brocade: bna: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Acked-by: Rasesh Mody Signed-off-by: David S. Miller --- drivers/net/ethernet/brocade/bna/bnad_ethtool.c | 54 ++++++++++++++----------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index 31f61a7..2865939 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -240,40 +240,46 @@ static const char *bnad_net_stats_strings[] = { #define BNAD_ETHTOOL_STATS_NUM ARRAY_SIZE(bnad_net_stats_strings) static int -bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) +bnad_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { - cmd->supported = SUPPORTED_10000baseT_Full; - cmd->advertising = ADVERTISED_10000baseT_Full; - cmd->autoneg = AUTONEG_DISABLE; - cmd->supported |= SUPPORTED_FIBRE; - cmd->advertising |= ADVERTISED_FIBRE; - cmd->port = PORT_FIBRE; - cmd->phy_address = 0; + u32 supported, advertising; + + supported = SUPPORTED_10000baseT_Full; + advertising = ADVERTISED_10000baseT_Full; + cmd->base.autoneg = AUTONEG_DISABLE; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_FIBRE; + cmd->base.phy_address = 0; if (netif_carrier_ok(netdev)) { - ethtool_cmd_speed_set(cmd, SPEED_10000); - cmd->duplex = DUPLEX_FULL; + cmd->base.speed = SPEED_10000; + cmd->base.duplex = DUPLEX_FULL; } else { - ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); - cmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - cmd->transceiver = XCVR_EXTERNAL; - cmd->maxtxpkt = 0; - cmd->maxrxpkt = 0; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } static int -bnad_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd) +bnad_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { /* 10G full duplex setting supported only */ - if (cmd->autoneg == AUTONEG_ENABLE) - return -EOPNOTSUPP; else { - if ((ethtool_cmd_speed(cmd) == SPEED_10000) - && (cmd->duplex == DUPLEX_FULL)) - return 0; - } + if (cmd->base.autoneg == AUTONEG_ENABLE) + return -EOPNOTSUPP; + + if ((cmd->base.speed == SPEED_10000) && + (cmd->base.duplex == DUPLEX_FULL)) + return 0; return -EOPNOTSUPP; } @@ -1118,8 +1124,6 @@ out: } static const struct ethtool_ops bnad_ethtool_ops = { - .get_settings = bnad_get_settings, - .set_settings = bnad_set_settings, .get_drvinfo = bnad_get_drvinfo, .get_wol = bnad_get_wol, .get_link = ethtool_op_get_link, @@ -1137,6 +1141,8 @@ static const struct ethtool_ops bnad_ethtool_ops = { .set_eeprom = bnad_set_eeprom, .flash_device = bnad_flash_device, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = bnad_get_link_ksettings, + .set_link_ksettings = bnad_set_link_ksettings, }; void -- cgit v1.1 From 1379fd3c426ce98f2936420a43d88b54df218b69 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Mon, 28 Nov 2016 14:52:42 -0800 Subject: samples: bpf: Refactor test_cgrp2_attach -- use getopt, and add mode This patch modifies test_cgrp2_attach to use getopt so we can use standard command line parsing. It also adds an option to run the program in detach only mode. This does not attach a new filter at the cgroup, but only runs the detach command. Lastly, it changes the attach code to not detach and then attach. It relies on the 'hotswap' behaviour of CGroup BPF programs to be able to change in-place. If detach-then-attach behaviour needs to be tested, the example can be run in detach only mode prior to attachment. Signed-off-by: Sargun Dhillon Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/test_cgrp2_attach.c | 80 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c index 63ef208..a19484c 100644 --- a/samples/bpf/test_cgrp2_attach.c +++ b/samples/bpf/test_cgrp2_attach.c @@ -10,8 +10,6 @@ * incremented on each iteration by the number of bytes stored in * the skb. * - * - Detaches any eBPF program previously attached to the cgroup - * * - Attaches the new program to a cgroup using BPF_PROG_ATTACH * * - Every second, reads map[0] and map[1] to see how many bytes and @@ -75,35 +73,16 @@ static int prog_load(int map_fd, int verdict) static int usage(const char *argv0) { - printf("Usage: %s [drop]\n", argv0); + printf("Usage: %s [-d] [-D] \n", argv0); + printf(" -d Drop Traffic\n"); + printf(" -D Detach filter, and exit\n"); return EXIT_FAILURE; } -int main(int argc, char **argv) +static int attach_filter(int cg_fd, int type, int verdict) { - int cg_fd, map_fd, prog_fd, key, ret; + int prog_fd, map_fd, ret, key; long long pkt_cnt, byte_cnt; - enum bpf_attach_type type; - int verdict = 1; - - if (argc < 3) - return usage(argv[0]); - - if (strcmp(argv[2], "ingress") == 0) - type = BPF_CGROUP_INET_INGRESS; - else if (strcmp(argv[2], "egress") == 0) - type = BPF_CGROUP_INET_EGRESS; - else - return usage(argv[0]); - - if (argc > 3 && strcmp(argv[3], "drop") == 0) - verdict = 0; - - cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY); - if (cg_fd < 0) { - printf("Failed to open cgroup path: '%s'\n", strerror(errno)); - return EXIT_FAILURE; - } map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(byte_cnt), @@ -121,16 +100,12 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - ret = bpf_prog_detach(cg_fd, type); - printf("bpf_prog_detach() returned '%s' (%d)\n", strerror(errno), errno); - ret = bpf_prog_attach(prog_fd, cg_fd, type); if (ret < 0) { printf("Failed to attach prog to cgroup: '%s'\n", strerror(errno)); return EXIT_FAILURE; } - while (1) { key = MAP_KEY_PACKETS; assert(bpf_lookup_elem(map_fd, &key, &pkt_cnt) == 0); @@ -145,3 +120,48 @@ int main(int argc, char **argv) return EXIT_SUCCESS; } + +int main(int argc, char **argv) +{ + int detach_only = 0, verdict = 1; + enum bpf_attach_type type; + int opt, cg_fd, ret; + + while ((opt = getopt(argc, argv, "Dd")) != -1) { + switch (opt) { + case 'd': + verdict = 0; + break; + case 'D': + detach_only = 1; + break; + default: + return usage(argv[0]); + } + } + + if (argc - optind < 2) + return usage(argv[0]); + + if (strcmp(argv[optind + 1], "ingress") == 0) + type = BPF_CGROUP_INET_INGRESS; + else if (strcmp(argv[optind + 1], "egress") == 0) + type = BPF_CGROUP_INET_EGRESS; + else + return usage(argv[0]); + + cg_fd = open(argv[optind], O_DIRECTORY | O_RDONLY); + if (cg_fd < 0) { + printf("Failed to open cgroup path: '%s'\n", strerror(errno)); + return EXIT_FAILURE; + } + + if (detach_only) { + ret = bpf_prog_detach(cg_fd, type); + printf("bpf_prog_detach() returned '%s' (%d)\n", + strerror(errno), errno); + } else + ret = attach_filter(cg_fd, type, verdict); + + return ret; +} -- cgit v1.1 From 547be9ec12731bc8dff7fcf6c96c8fed805aa61d Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:33 -0800 Subject: liquidio CN23XX: VF register definitions Adds support for CN23xx VF registers. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../net/ethernet/cavium/liquidio/cn23xx_vf_regs.h | 274 +++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h new file mode 100644 index 0000000..d33dd8f --- /dev/null +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h @@ -0,0 +1,274 @@ +/********************************************************************** + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ +/*! \file cn23xx_vf_regs.h + * \brief Host Driver: Register Address and Register Mask values for + * Octeon CN23XX vf functions. + */ + +#ifndef __CN23XX_VF_REGS_H__ +#define __CN23XX_VF_REGS_H__ + +#define CN23XX_CONFIG_XPANSION_BAR 0x38 + +#define CN23XX_CONFIG_PCIE_CAP 0x70 +#define CN23XX_CONFIG_PCIE_DEVCAP 0x74 +#define CN23XX_CONFIG_PCIE_DEVCTL 0x78 +#define CN23XX_CONFIG_PCIE_LINKCAP 0x7C +#define CN23XX_CONFIG_PCIE_LINKCTL 0x80 +#define CN23XX_CONFIG_PCIE_SLOTCAP 0x84 +#define CN23XX_CONFIG_PCIE_SLOTCTL 0x88 + +#define CN23XX_CONFIG_PCIE_FLTMSK 0x720 + +/* The input jabber is used to determine the TSO max size. + * Due to H/W limitation, this need to be reduced to 60000 + * in order to to H/W TSO and avoid the WQE malfarmation + * PKO_BUG_24989_WQE_LEN + */ +#define CN23XX_DEFAULT_INPUT_JABBER 0xEA60 /*60000*/ + +/* ############## BAR0 Registers ################ */ + +/* Each Input Queue register is at a 16-byte Offset in BAR0 */ +#define CN23XX_VF_IQ_OFFSET 0x20000 + +/*###################### REQUEST QUEUE #########################*/ + +/* 64 registers for Input Queue Instr Count - SLI_PKT_IN_DONE0_CNTS */ +#define CN23XX_VF_SLI_IQ_INSTR_COUNT_START64 0x10040 + +/* 64 registers for Input Queues Start Addr - SLI_PKT0_INSTR_BADDR */ +#define CN23XX_VF_SLI_IQ_BASE_ADDR_START64 0x10010 + +/* 64 registers for Input Doorbell - SLI_PKT0_INSTR_BAOFF_DBELL */ +#define CN23XX_VF_SLI_IQ_DOORBELL_START 0x10020 + +/* 64 registers for Input Queue size - SLI_PKT0_INSTR_FIFO_RSIZE */ +#define CN23XX_VF_SLI_IQ_SIZE_START 0x10030 + +/* 64 registers (64-bit) - ES, RO, NS, Arbitration for Input Queue Data & + * gather list fetches. SLI_PKT(0..63)_INPUT_CONTROL. + */ +#define CN23XX_VF_SLI_IQ_PKT_CONTROL_START64 0x10000 + +/*------- Request Queue Macros ---------*/ +#define CN23XX_VF_SLI_IQ_PKT_CONTROL64(iq) \ + (CN23XX_VF_SLI_IQ_PKT_CONTROL_START64 + ((iq) * CN23XX_VF_IQ_OFFSET)) + +#define CN23XX_VF_SLI_IQ_BASE_ADDR64(iq) \ + (CN23XX_VF_SLI_IQ_BASE_ADDR_START64 + ((iq) * CN23XX_VF_IQ_OFFSET)) + +#define CN23XX_VF_SLI_IQ_SIZE(iq) \ + (CN23XX_VF_SLI_IQ_SIZE_START + ((iq) * CN23XX_VF_IQ_OFFSET)) + +#define CN23XX_VF_SLI_IQ_DOORBELL(iq) \ + (CN23XX_VF_SLI_IQ_DOORBELL_START + ((iq) * CN23XX_VF_IQ_OFFSET)) + +#define CN23XX_VF_SLI_IQ_INSTR_COUNT64(iq) \ + (CN23XX_VF_SLI_IQ_INSTR_COUNT_START64 + ((iq) * CN23XX_VF_IQ_OFFSET)) + +/*------------------ Masks ----------------*/ +#define CN23XX_PKT_INPUT_CTL_VF_NUM BIT_ULL(32) +#define CN23XX_PKT_INPUT_CTL_MAC_NUM BIT(29) +/* Number of instructions to be read in one MAC read request. + * setting to Max value(4) + */ +#define CN23XX_PKT_INPUT_CTL_RDSIZE (3 << 25) +#define CN23XX_PKT_INPUT_CTL_IS_64B BIT(24) +#define CN23XX_PKT_INPUT_CTL_RST BIT(23) +#define CN23XX_PKT_INPUT_CTL_QUIET BIT(28) +#define CN23XX_PKT_INPUT_CTL_RING_ENB BIT(22) +#define CN23XX_PKT_INPUT_CTL_DATA_NS BIT(8) +#define CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP BIT(6) +#define CN23XX_PKT_INPUT_CTL_DATA_RO BIT(5) +#define CN23XX_PKT_INPUT_CTL_USE_CSR BIT(4) +#define CN23XX_PKT_INPUT_CTL_GATHER_NS BIT(3) +#define CN23XX_PKT_INPUT_CTL_GATHER_ES_64B_SWAP (2) +#define CN23XX_PKT_INPUT_CTL_GATHER_RO (1) + +/** Rings per Virtual Function [RO] **/ +#define CN23XX_PKT_INPUT_CTL_RPVF_MASK (0x3F) +#define CN23XX_PKT_INPUT_CTL_RPVF_POS (48) +/* These bits[47:44][RO] give the Physical function number info within the MAC*/ +#define CN23XX_PKT_INPUT_CTL_PF_NUM_MASK (0x7) +#define CN23XX_PKT_INPUT_CTL_PF_NUM_POS (45) +/** These bits[43:32][RO] give the virtual function number info within the PF*/ +#define CN23XX_PKT_INPUT_CTL_VF_NUM_MASK (0x1FFF) +#define CN23XX_PKT_INPUT_CTL_VF_NUM_POS (32) +#define CN23XX_PKT_INPUT_CTL_MAC_NUM_MASK (0x3) +#define CN23XX_PKT_INPUT_CTL_MAC_NUM_POS (29) +#define CN23XX_PKT_IN_DONE_WMARK_MASK (0xFFFFULL) +#define CN23XX_PKT_IN_DONE_WMARK_BIT_POS (32) +#define CN23XX_PKT_IN_DONE_CNT_MASK (0x00000000FFFFFFFFULL) + +#ifdef __LITTLE_ENDIAN_BITFIELD +#define CN23XX_PKT_INPUT_CTL_MASK \ + (CN23XX_PKT_INPUT_CTL_RDSIZE \ + | CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP \ + | CN23XX_PKT_INPUT_CTL_USE_CSR) +#else +#define CN23XX_PKT_INPUT_CTL_MASK \ + (CN23XX_PKT_INPUT_CTL_RDSIZE \ + | CN23XX_PKT_INPUT_CTL_DATA_ES_64B_SWAP \ + | CN23XX_PKT_INPUT_CTL_USE_CSR \ + | CN23XX_PKT_INPUT_CTL_GATHER_ES_64B_SWAP) +#endif + +/** Masks for SLI_PKT_IN_DONE(0..63)_CNTS Register */ +#define CN23XX_IN_DONE_CNTS_PI_INT BIT_ULL(62) +#define CN23XX_IN_DONE_CNTS_CINT_ENB BIT_ULL(48) + +/*############################ OUTPUT QUEUE #########################*/ + +/* 64 registers for Output queue control - SLI_PKT(0..63)_OUTPUT_CONTROL */ +#define CN23XX_VF_SLI_OQ_PKT_CONTROL_START 0x10050 + +/* 64 registers for Output queue buffer and info size - SLI_PKT0_OUT_SIZE */ +#define CN23XX_VF_SLI_OQ0_BUFF_INFO_SIZE 0x10060 + +/* 64 registers for Output Queue Start Addr - SLI_PKT0_SLIST_BADDR */ +#define CN23XX_VF_SLI_OQ_BASE_ADDR_START64 0x10070 + +/* 64 registers for Output Queue Packet Credits - SLI_PKT0_SLIST_BAOFF_DBELL */ +#define CN23XX_VF_SLI_OQ_PKT_CREDITS_START 0x10080 + +/* 64 registers for Output Queue size - SLI_PKT0_SLIST_FIFO_RSIZE */ +#define CN23XX_VF_SLI_OQ_SIZE_START 0x10090 + +/* 64 registers for Output Queue Packet Count - SLI_PKT0_CNTS */ +#define CN23XX_VF_SLI_OQ_PKT_SENT_START 0x100B0 + +/* 64 registers for Output Queue INT Levels - SLI_PKT0_INT_LEVELS */ +#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 0x100A0 + +/* Each Output Queue register is at a 16-byte Offset in BAR0 */ +#define CN23XX_VF_OQ_OFFSET 0x20000 + +/*------- Output Queue Macros ---------*/ + +#define CN23XX_VF_SLI_OQ_PKT_CONTROL(oq) \ + (CN23XX_VF_SLI_OQ_PKT_CONTROL_START + ((oq) * CN23XX_VF_OQ_OFFSET)) + +#define CN23XX_VF_SLI_OQ_BASE_ADDR64(oq) \ + (CN23XX_VF_SLI_OQ_BASE_ADDR_START64 + ((oq) * CN23XX_VF_OQ_OFFSET)) + +#define CN23XX_VF_SLI_OQ_SIZE(oq) \ + (CN23XX_VF_SLI_OQ_SIZE_START + ((oq) * CN23XX_VF_OQ_OFFSET)) + +#define CN23XX_VF_SLI_OQ_BUFF_INFO_SIZE(oq) \ + (CN23XX_VF_SLI_OQ0_BUFF_INFO_SIZE + ((oq) * CN23XX_VF_OQ_OFFSET)) + +#define CN23XX_VF_SLI_OQ_PKTS_SENT(oq) \ + (CN23XX_VF_SLI_OQ_PKT_SENT_START + ((oq) * CN23XX_VF_OQ_OFFSET)) + +#define CN23XX_VF_SLI_OQ_PKTS_CREDIT(oq) \ + (CN23XX_VF_SLI_OQ_PKT_CREDITS_START + ((oq) * CN23XX_VF_OQ_OFFSET)) + +#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(oq) \ + (CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 + ((oq) * CN23XX_VF_OQ_OFFSET)) + +/* Macro's for accessing CNT and TIME separately from INT_LEVELS */ +#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_CNT(oq) \ + (CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 + ((oq) * CN23XX_VF_OQ_OFFSET)) + +#define CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_TIME(oq) \ + (CN23XX_VF_SLI_OQ_PKT_INT_LEVELS_START64 + \ + ((oq) * CN23XX_VF_OQ_OFFSET) + 4) + +/*------------------ Masks ----------------*/ +#define CN23XX_PKT_OUTPUT_CTL_TENB BIT(13) +#define CN23XX_PKT_OUTPUT_CTL_CENB BIT(12) +#define CN23XX_PKT_OUTPUT_CTL_IPTR BIT(11) +#define CN23XX_PKT_OUTPUT_CTL_ES BIT(9) +#define CN23XX_PKT_OUTPUT_CTL_NSR BIT(8) +#define CN23XX_PKT_OUTPUT_CTL_ROR BIT(7) +#define CN23XX_PKT_OUTPUT_CTL_DPTR BIT(6) +#define CN23XX_PKT_OUTPUT_CTL_BMODE BIT(5) +#define CN23XX_PKT_OUTPUT_CTL_ES_P BIT(3) +#define CN23XX_PKT_OUTPUT_CTL_NSR_P BIT(2) +#define CN23XX_PKT_OUTPUT_CTL_ROR_P BIT(1) +#define CN23XX_PKT_OUTPUT_CTL_RING_ENB BIT(0) + +/*######################### Mailbox Reg Macros ########################*/ +#define CN23XX_VF_SLI_PKT_MBOX_INT_START 0x10210 +#define CN23XX_SLI_PKT_PF_VF_MBOX_SIG_START 0x10200 + +#define CN23XX_SLI_MBOX_OFFSET 0x20000 +#define CN23XX_SLI_MBOX_SIG_IDX_OFFSET 0x8 + +#define CN23XX_VF_SLI_PKT_MBOX_INT(q) \ + (CN23XX_VF_SLI_PKT_MBOX_INT_START + ((q) * CN23XX_SLI_MBOX_OFFSET)) + +#define CN23XX_SLI_PKT_PF_VF_MBOX_SIG(q, idx) \ + (CN23XX_SLI_PKT_PF_VF_MBOX_SIG_START + \ + ((q) * CN23XX_SLI_MBOX_OFFSET + \ + (idx) * CN23XX_SLI_MBOX_SIG_IDX_OFFSET)) + +/*######################## INTERRUPTS #########################*/ + +#define CN23XX_VF_SLI_INT_SUM_START 0x100D0 + +#define CN23XX_VF_SLI_INT_SUM(q) \ + (CN23XX_VF_SLI_INT_SUM_START + ((q) * CN23XX_VF_IQ_OFFSET)) + +/*------------------ Interrupt Masks ----------------*/ + +#define CN23XX_INTR_PO_INT BIT_ULL(63) +#define CN23XX_INTR_PI_INT BIT_ULL(62) +#define CN23XX_INTR_MBOX_INT BIT_ULL(61) +#define CN23XX_INTR_RESEND BIT_ULL(60) + +#define CN23XX_INTR_CINT_ENB BIT_ULL(48) +#define CN23XX_INTR_MBOX_ENB BIT(0) + +/*############################ MIO #########################*/ +#define CN23XX_MIO_PTP_CLOCK_CFG 0x0001070000000f00ULL +#define CN23XX_MIO_PTP_CLOCK_LO 0x0001070000000f08ULL +#define CN23XX_MIO_PTP_CLOCK_HI 0x0001070000000f10ULL +#define CN23XX_MIO_PTP_CLOCK_COMP 0x0001070000000f18ULL +#define CN23XX_MIO_PTP_TIMESTAMP 0x0001070000000f20ULL +#define CN23XX_MIO_PTP_EVT_CNT 0x0001070000000f28ULL +#define CN23XX_MIO_PTP_CKOUT_THRESH_LO 0x0001070000000f30ULL +#define CN23XX_MIO_PTP_CKOUT_THRESH_HI 0x0001070000000f38ULL +#define CN23XX_MIO_PTP_CKOUT_HI_INCR 0x0001070000000f40ULL +#define CN23XX_MIO_PTP_CKOUT_LO_INCR 0x0001070000000f48ULL +#define CN23XX_MIO_PTP_PPS_THRESH_LO 0x0001070000000f50ULL +#define CN23XX_MIO_PTP_PPS_THRESH_HI 0x0001070000000f58ULL +#define CN23XX_MIO_PTP_PPS_HI_INCR 0x0001070000000f60ULL +#define CN23XX_MIO_PTP_PPS_LO_INCR 0x0001070000000f68ULL + +/*############################ RST #########################*/ +#define CN23XX_RST_BOOT 0x0001180006001600ULL + +/*######################## MSIX TABLE #########################*/ + +#define CN23XX_MSIX_TABLE_ADDR_START 0x0 +#define CN23XX_MSIX_TABLE_DATA_START 0x8 + +#define CN23XX_MSIX_TABLE_SIZE 0x10 +#define CN23XX_MSIX_TABLE_ENTRIES 0x41 + +#define CN23XX_MSIX_ENTRY_VECTOR_CTL BIT_ULL(32) + +#define CN23XX_MSIX_TABLE_ADDR(idx) \ + (CN23XX_MSIX_TABLE_ADDR_START + ((idx) * CN23XX_MSIX_TABLE_SIZE)) + +#define CN23XX_MSIX_TABLE_DATA(idx) \ + (CN23XX_MSIX_TABLE_DATA_START + ((idx) * CN23XX_MSIX_TABLE_SIZE)) + +#endif -- cgit v1.1 From 111fc64a237f231bc2d3187bdf8358eb7966e6a9 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:34 -0800 Subject: liquidio CN23XX: VF registration Adds support for cn23xx VF probe and registration. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/Kconfig | 12 +++ drivers/net/ethernet/cavium/liquidio/Makefile | 21 ++++ .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 34 ++++++ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 120 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/octeon_device.c | 4 + 5 files changed, 191 insertions(+) create mode 100644 drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h create mode 100644 drivers/net/ethernet/cavium/liquidio/lio_vf_main.c diff --git a/drivers/net/ethernet/cavium/Kconfig b/drivers/net/ethernet/cavium/Kconfig index 92f411c..c0679c2 100644 --- a/drivers/net/ethernet/cavium/Kconfig +++ b/drivers/net/ethernet/cavium/Kconfig @@ -74,4 +74,16 @@ config OCTEON_MGMT_ETHERNET port on Cavium Networks' Octeon CN57XX, CN56XX, CN55XX, CN54XX, CN52XX, and CN6XXX chips. +config LIQUIDIO_VF + tristate "Cavium LiquidIO VF support" + depends on 64BIT && PCI_MSI + select PTP_1588_CLOCK + ---help--- + This driver supports Cavium LiquidIO Intelligent Server Adapter + based on CN23XX chips. + + To compile this driver as a module, choose M here: The module + will be called liquidio_vf. MSI-X interrupt support is required + for this driver to work correctly + endif # NET_VENDOR_CAVIUM diff --git a/drivers/net/ethernet/cavium/liquidio/Makefile b/drivers/net/ethernet/cavium/liquidio/Makefile index 14958de..69d23fc 100644 --- a/drivers/net/ethernet/cavium/liquidio/Makefile +++ b/drivers/net/ethernet/cavium/liquidio/Makefile @@ -17,3 +17,24 @@ liquidio-$(CONFIG_LIQUIDIO) += lio_ethtool.o \ octeon_nic.o liquidio-objs := lio_main.o octeon_console.o $(liquidio-y) + +obj-$(CONFIG_LIQUIDIO_VF) += liquidio_vf.o + +ifeq ($(CONFIG_LIQUIDIO)$(CONFIG_LIQUIDIO_VF), yy) + liquidio_vf-objs := lio_vf_main.o +else +liquidio_vf-$(CONFIG_LIQUIDIO_VF) += lio_ethtool.o \ + lio_core.o \ + request_manager.o \ + response_manager.o \ + octeon_device.o \ + cn66xx_device.o \ + cn68xx_device.o \ + cn23xx_pf_device.o \ + octeon_mailbox.o \ + octeon_mem_ops.o \ + octeon_droq.o \ + octeon_nic.o + +liquidio_vf-objs := lio_vf_main.o $(liquidio_vf-y) +endif diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h new file mode 100644 index 0000000..015b6d4 --- /dev/null +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -0,0 +1,34 @@ +/********************************************************************** + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ +/*! \file cn23xx_device.h + * \brief Host Driver: Routines that perform CN23XX specific operations. + */ + +#ifndef __CN23XX_VF_DEVICE_H__ +#define __CN23XX_VF_DEVICE_H__ + +#include "cn23xx_vf_regs.h" + +/* Register address and configuration for a CN23XX devices. + * If device specific changes need to be made then add a struct to include + * device specific fields as shown in the commented section + */ +struct octeon_cn23xx_vf { + struct octeon_config *conf; +}; +#endif diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c new file mode 100644 index 0000000..fd108cd --- /dev/null +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -0,0 +1,120 @@ +/********************************************************************** + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ +#include +#include +#include "liquidio_common.h" +#include "octeon_droq.h" +#include "octeon_iq.h" +#include "response_manager.h" +#include "octeon_device.h" + +MODULE_AUTHOR("Cavium Networks, "); +MODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Virtual Function Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(LIQUIDIO_VERSION); + +struct octeon_device_priv { + /* Tasklet structures for this device. */ + struct tasklet_struct droq_tasklet; + unsigned long napi_mask; +}; + +static int +liquidio_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void liquidio_vf_remove(struct pci_dev *pdev); + +static const struct pci_device_id liquidio_vf_pci_tbl[] = { + { + PCI_VENDOR_ID_CAVIUM, OCTEON_CN23XX_VF_VID, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0 + } +}; +MODULE_DEVICE_TABLE(pci, liquidio_vf_pci_tbl); + +static struct pci_driver liquidio_vf_pci_driver = { + .name = "LiquidIO_VF", + .id_table = liquidio_vf_pci_tbl, + .probe = liquidio_vf_probe, + .remove = liquidio_vf_remove, +}; + +/** + * \brief PCI probe handler + * @param pdev PCI device structure + * @param ent unused + */ +static int +liquidio_vf_probe(struct pci_dev *pdev, + const struct pci_device_id *ent __attribute__((unused))) +{ + struct octeon_device *oct_dev = NULL; + + oct_dev = octeon_allocate_device(pdev->device, + sizeof(struct octeon_device_priv)); + + if (!oct_dev) { + dev_err(&pdev->dev, "Unable to allocate device\n"); + return -ENOMEM; + } + + dev_info(&pdev->dev, "Initializing device %x:%x.\n", + (u32)pdev->vendor, (u32)pdev->device); + + /* Assign octeon_device for this device to the private data area. */ + pci_set_drvdata(pdev, oct_dev); + + /* set linux specific device pointer */ + oct_dev->pci_dev = pdev; + + return 0; +} + +/** + * \brief Cleans up resources at unload time + * @param pdev PCI device structure + */ +static void liquidio_vf_remove(struct pci_dev *pdev) +{ + struct octeon_device *oct_dev = pci_get_drvdata(pdev); + + dev_dbg(&oct_dev->pci_dev->dev, "Stopping device\n"); + + /* This octeon device has been removed. Update the global + * data structure to reflect this. Free the device structure. + */ + octeon_free_device_mem(oct_dev); +} + +static int __init liquidio_vf_init(void) +{ + octeon_init_device_list(0); + return pci_register_driver(&liquidio_vf_pci_driver); +} + +static void __exit liquidio_vf_exit(void) +{ + pci_unregister_driver(&liquidio_vf_pci_driver); + + pr_info("LiquidIO_VF network module is now unloaded\n"); +} + +module_init(liquidio_vf_init); +module_exit(liquidio_vf_exit); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 79c8875..05bb0fd 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -28,6 +28,7 @@ #include "cn66xx_regs.h" #include "cn66xx_device.h" #include "cn23xx_pf_device.h" +#include "cn23xx_vf_device.h" /** Default configuration * for CN66XX OCTEON Models. @@ -672,6 +673,9 @@ static struct octeon_device *octeon_allocate_device_mem(u32 pci_id, case OCTEON_CN23XX_PF_VID: configsize = sizeof(struct octeon_cn23xx_pf); break; + case OCTEON_CN23XX_VF_VID: + configsize = sizeof(struct octeon_cn23xx_vf); + break; default: pr_err("%s: Unknown PCI Device: 0x%x\n", __func__, -- cgit v1.1 From 69c69da33d1d01b81eb56ac42142daa01777abd6 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:35 -0800 Subject: liquidio CN23XX: VF config setup Adds support for setting up VF configuration. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/Makefile | 2 + .../ethernet/cavium/liquidio/cn23xx_vf_device.c | 44 +++++++ .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 2 + drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 136 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/octeon_device.c | 11 +- 5 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c diff --git a/drivers/net/ethernet/cavium/liquidio/Makefile b/drivers/net/ethernet/cavium/liquidio/Makefile index 69d23fc..c4d411d 100644 --- a/drivers/net/ethernet/cavium/liquidio/Makefile +++ b/drivers/net/ethernet/cavium/liquidio/Makefile @@ -11,6 +11,7 @@ liquidio-$(CONFIG_LIQUIDIO) += lio_ethtool.o \ cn66xx_device.o \ cn68xx_device.o \ cn23xx_pf_device.o \ + cn23xx_vf_device.o \ octeon_mailbox.o \ octeon_mem_ops.o \ octeon_droq.o \ @@ -31,6 +32,7 @@ liquidio_vf-$(CONFIG_LIQUIDIO_VF) += lio_ethtool.o \ cn66xx_device.o \ cn68xx_device.o \ cn23xx_pf_device.o \ + cn23xx_vf_device.o \ octeon_mailbox.o \ octeon_mem_ops.o \ octeon_droq.o \ diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c new file mode 100644 index 0000000..d683bda --- /dev/null +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c @@ -0,0 +1,44 @@ +/********************************************************************** + * Author: Cavium, Inc. + * + * Contact: support@cavium.com + * Please include "LiquidIO" in the subject. + * + * Copyright (c) 2003-2016 Cavium, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + ***********************************************************************/ +#include +#include +#include "liquidio_common.h" +#include "octeon_droq.h" +#include "octeon_iq.h" +#include "response_manager.h" +#include "octeon_device.h" +#include "cn23xx_vf_device.h" +#include "octeon_main.h" + +int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) +{ + struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip; + + if (octeon_map_pci_barx(oct, 0, 0)) + return 1; + + cn23xx->conf = oct_get_config_info(oct, LIO_23XX); + if (!cn23xx->conf) { + dev_err(&oct->pci_dev->dev, "%s No Config found for CN23XX\n", + __func__); + octeon_unmap_pci_barx(oct, 0); + return 1; + } + + return 0; +} diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index 015b6d4..9e4fb50 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -31,4 +31,6 @@ struct octeon_cn23xx_vf { struct octeon_config *conf; }; + +int cn23xx_setup_octeon_vf_device(struct octeon_device *oct); #endif diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index fd108cd..dd1dad1 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -22,6 +22,8 @@ #include "octeon_iq.h" #include "response_manager.h" #include "octeon_device.h" +#include "octeon_main.h" +#include "cn23xx_vf_device.h" MODULE_AUTHOR("Cavium Networks, "); MODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Virtual Function Driver"); @@ -37,6 +39,7 @@ struct octeon_device_priv { static int liquidio_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void liquidio_vf_remove(struct pci_dev *pdev); +static int octeon_device_init(struct octeon_device *oct); static const struct pci_device_id liquidio_vf_pci_tbl[] = { { @@ -84,10 +87,78 @@ liquidio_vf_probe(struct pci_dev *pdev, /* set linux specific device pointer */ oct_dev->pci_dev = pdev; + if (octeon_device_init(oct_dev)) { + liquidio_vf_remove(pdev); + return -ENOMEM; + } + + dev_dbg(&oct_dev->pci_dev->dev, "Device is ready\n"); + return 0; } /** + * \brief PCI FLR for each Octeon device. + * @param oct octeon device + */ +static void octeon_pci_flr(struct octeon_device *oct) +{ + u16 status; + + pci_save_state(oct->pci_dev); + + pci_cfg_access_lock(oct->pci_dev); + + /* Quiesce the device completely */ + pci_write_config_word(oct->pci_dev, PCI_COMMAND, + PCI_COMMAND_INTX_DISABLE); + + /* Wait for Transaction Pending bit clean */ + msleep(100); + pcie_capability_read_word(oct->pci_dev, PCI_EXP_DEVSTA, &status); + if (status & PCI_EXP_DEVSTA_TRPND) { + dev_info(&oct->pci_dev->dev, "Function reset incomplete after 100ms, sleeping for 5 seconds\n"); + ssleep(5); + pcie_capability_read_word(oct->pci_dev, PCI_EXP_DEVSTA, + &status); + if (status & PCI_EXP_DEVSTA_TRPND) + dev_info(&oct->pci_dev->dev, "Function reset still incomplete after 5s, reset anyway\n"); + } + pcie_capability_set_word(oct->pci_dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_BCR_FLR); + mdelay(100); + + pci_cfg_access_unlock(oct->pci_dev); + + pci_restore_state(oct->pci_dev); +} + +/** + *\brief Destroy resources associated with octeon device + * @param pdev PCI device structure + * @param ent unused + */ +static void octeon_destroy_resources(struct octeon_device *oct) +{ + switch (atomic_read(&oct->status)) { + case OCT_DEV_PCI_MAP_DONE: + octeon_unmap_pci_barx(oct, 0); + octeon_unmap_pci_barx(oct, 1); + + /* fallthrough */ + case OCT_DEV_PCI_ENABLE_DONE: + pci_clear_master(oct->pci_dev); + /* Disable the device, releasing the PCI INT */ + pci_disable_device(oct->pci_dev); + + /* fallthrough */ + case OCT_DEV_BEGIN_STATE: + /* Nothing to be done here either */ + break; + } +} + +/** * \brief Cleans up resources at unload time * @param pdev PCI device structure */ @@ -97,12 +168,77 @@ static void liquidio_vf_remove(struct pci_dev *pdev) dev_dbg(&oct_dev->pci_dev->dev, "Stopping device\n"); + /* Reset the octeon device and cleanup all memory allocated for + * the octeon device by driver. + */ + octeon_destroy_resources(oct_dev); + + dev_info(&oct_dev->pci_dev->dev, "Device removed\n"); + /* This octeon device has been removed. Update the global * data structure to reflect this. Free the device structure. */ octeon_free_device_mem(oct_dev); } +/** + * \brief PCI initialization for each Octeon device. + * @param oct octeon device + */ +static int octeon_pci_os_setup(struct octeon_device *oct) +{ +#ifdef CONFIG_PCI_IOV + /* setup PCI stuff first */ + if (!oct->pci_dev->physfn) + octeon_pci_flr(oct); +#endif + + if (pci_enable_device(oct->pci_dev)) { + dev_err(&oct->pci_dev->dev, "pci_enable_device failed\n"); + return 1; + } + + if (dma_set_mask_and_coherent(&oct->pci_dev->dev, DMA_BIT_MASK(64))) { + dev_err(&oct->pci_dev->dev, "Unexpected DMA device capability\n"); + pci_disable_device(oct->pci_dev); + return 1; + } + + /* Enable PCI DMA Master. */ + pci_set_master(oct->pci_dev); + + return 0; +} + +/** + * \brief Device initialization for each Octeon device that is probed + * @param octeon_dev octeon device + */ +static int octeon_device_init(struct octeon_device *oct) +{ + u32 rev_id; + + atomic_set(&oct->status, OCT_DEV_BEGIN_STATE); + + /* Enable access to the octeon device and make its DMA capability + * known to the OS. + */ + if (octeon_pci_os_setup(oct)) + return 1; + atomic_set(&oct->status, OCT_DEV_PCI_ENABLE_DONE); + + oct->chip_id = OCTEON_CN23XX_VF_VID; + pci_read_config_dword(oct->pci_dev, 8, &rev_id); + oct->rev_id = rev_id & 0xff; + + if (cn23xx_setup_octeon_vf_device(oct)) + return 1; + + atomic_set(&oct->status, OCT_DEV_PCI_MAP_DONE); + + return 0; +} + static int __init liquidio_vf_init(void) { octeon_init_device_list(0); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 05bb0fd..7e6c8b8 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -572,15 +572,17 @@ static void *__retrieve_octeon_config_info(struct octeon_device *oct, switch (oct_conf_info[oct_id].conf_type) { case OCTEON_CONFIG_TYPE_DEFAULT: if (oct->chip_id == OCTEON_CN66XX) { - ret = (void *)&default_cn66xx_conf; + ret = &default_cn66xx_conf; } else if ((oct->chip_id == OCTEON_CN68XX) && (card_type == LIO_210NV)) { - ret = (void *)&default_cn68xx_210nv_conf; + ret = &default_cn68xx_210nv_conf; } else if ((oct->chip_id == OCTEON_CN68XX) && (card_type == LIO_410NV)) { - ret = (void *)&default_cn68xx_conf; + ret = &default_cn68xx_conf; } else if (oct->chip_id == OCTEON_CN23XX_PF_VID) { - ret = (void *)&default_cn23xx_conf; + ret = &default_cn23xx_conf; + } else if (oct->chip_id == OCTEON_CN23XX_VF_VID) { + ret = &default_cn23xx_conf; } break; default: @@ -596,6 +598,7 @@ static int __verify_octeon_config_info(struct octeon_device *oct, void *conf) case OCTEON_CN68XX: return lio_validate_cn6xxx_config_info(oct, conf); case OCTEON_CN23XX_PF_VID: + case OCTEON_CN23XX_VF_VID: return 0; default: break; -- cgit v1.1 From c865cdf13ac83d4e66e9f84f95252830180efb98 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:36 -0800 Subject: liquidio CN23XX: VF queue setup Adds support for configuring VF input/output queues. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.c | 144 +++++++++++++++++++++ .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 2 + drivers/net/ethernet/cavium/liquidio/lio_main.c | 6 +- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 5 + .../net/ethernet/cavium/liquidio/octeon_device.c | 43 +++++- .../net/ethernet/cavium/liquidio/octeon_device.h | 7 +- .../net/ethernet/cavium/liquidio/request_manager.c | 4 +- 7 files changed, 207 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c index d683bda..60fd138 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c @@ -25,13 +25,134 @@ #include "cn23xx_vf_device.h" #include "octeon_main.h" +static int cn23xx_vf_reset_io_queues(struct octeon_device *oct, u32 num_queues) +{ + u32 loop = BUSY_READING_REG_VF_LOOP_COUNT; + int ret_val = 0; + u32 q_no; + u64 d64; + + for (q_no = 0; q_no < num_queues; q_no++) { + /* set RST bit to 1. This bit applies to both IQ and OQ */ + d64 = octeon_read_csr64(oct, + CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)); + d64 |= CN23XX_PKT_INPUT_CTL_RST; + octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), + d64); + } + + /* wait until the RST bit is clear or the RST and QUIET bits are set */ + for (q_no = 0; q_no < num_queues; q_no++) { + u64 reg_val = octeon_read_csr64(oct, + CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)); + while ((READ_ONCE(reg_val) & CN23XX_PKT_INPUT_CTL_RST) && + !(READ_ONCE(reg_val) & CN23XX_PKT_INPUT_CTL_QUIET) && + loop) { + WRITE_ONCE(reg_val, octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no))); + loop--; + } + if (!loop) { + dev_err(&oct->pci_dev->dev, + "clearing the reset reg failed or setting the quiet reg failed for qno: %u\n", + q_no); + return -1; + } + WRITE_ONCE(reg_val, READ_ONCE(reg_val) & + ~CN23XX_PKT_INPUT_CTL_RST); + octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), + READ_ONCE(reg_val)); + + WRITE_ONCE(reg_val, octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no))); + if (READ_ONCE(reg_val) & CN23XX_PKT_INPUT_CTL_RST) { + dev_err(&oct->pci_dev->dev, + "clearing the reset failed for qno: %u\n", + q_no); + ret_val = -1; + } + } + + return ret_val; +} + +static int cn23xx_enable_vf_io_queues(struct octeon_device *oct) +{ + u32 q_no; + + for (q_no = 0; q_no < oct->num_iqs; q_no++) { + u64 reg_val; + + /* set the corresponding IQ IS_64B bit */ + if (oct->io_qmask.iq64B & BIT_ULL(q_no)) { + reg_val = octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)); + reg_val |= CN23XX_PKT_INPUT_CTL_IS_64B; + octeon_write_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), reg_val); + } + + /* set the corresponding IQ ENB bit */ + if (oct->io_qmask.iq & BIT_ULL(q_no)) { + reg_val = octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)); + reg_val |= CN23XX_PKT_INPUT_CTL_RING_ENB; + octeon_write_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), reg_val); + } + } + for (q_no = 0; q_no < oct->num_oqs; q_no++) { + u32 reg_val; + + /* set the corresponding OQ ENB bit */ + if (oct->io_qmask.oq & BIT_ULL(q_no)) { + reg_val = octeon_read_csr( + oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no)); + reg_val |= CN23XX_PKT_OUTPUT_CTL_RING_ENB; + octeon_write_csr( + oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no), reg_val); + } + } + + return 0; +} + +static void cn23xx_disable_vf_io_queues(struct octeon_device *oct) +{ + u32 num_queues = oct->num_iqs; + + /* per HRM, rings can only be disabled via reset operation, + * NOT via SLI_PKT()_INPUT/OUTPUT_CONTROL[ENB] + */ + if (num_queues < oct->num_oqs) + num_queues = oct->num_oqs; + + cn23xx_vf_reset_io_queues(oct, num_queues); +} + int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) { struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip; + u32 rings_per_vf, ring_flag; + u64 reg_val; if (octeon_map_pci_barx(oct, 0, 0)) return 1; + /* INPUT_CONTROL[RPVF] gives the VF IOq count */ + reg_val = octeon_read_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(0)); + + oct->pf_num = (reg_val >> CN23XX_PKT_INPUT_CTL_PF_NUM_POS) & + CN23XX_PKT_INPUT_CTL_PF_NUM_MASK; + oct->vf_num = (reg_val >> CN23XX_PKT_INPUT_CTL_VF_NUM_POS) & + CN23XX_PKT_INPUT_CTL_VF_NUM_MASK; + + reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS; + + rings_per_vf = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK; + + ring_flag = 0; + cn23xx->conf = oct_get_config_info(oct, LIO_23XX); if (!cn23xx->conf) { dev_err(&oct->pci_dev->dev, "%s No Config found for CN23XX\n", @@ -40,5 +161,28 @@ int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) return 1; } + if (oct->sriov_info.rings_per_vf > rings_per_vf) { + dev_warn(&oct->pci_dev->dev, + "num_queues:%d greater than PF configured rings_per_vf:%d. Reducing to %d.\n", + oct->sriov_info.rings_per_vf, rings_per_vf, + rings_per_vf); + oct->sriov_info.rings_per_vf = rings_per_vf; + } else { + if (rings_per_vf > num_present_cpus()) { + dev_warn(&oct->pci_dev->dev, + "PF configured rings_per_vf:%d greater than num_cpu:%d. Using rings_per_vf:%d equal to num cpus\n", + rings_per_vf, + num_present_cpus(), + num_present_cpus()); + oct->sriov_info.rings_per_vf = + num_present_cpus(); + } else { + oct->sriov_info.rings_per_vf = rings_per_vf; + } + } + + oct->fn_list.enable_io_queues = cn23xx_enable_vf_io_queues; + oct->fn_list.disable_io_queues = cn23xx_disable_vf_io_queues; + return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index 9e4fb50..6785796 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -32,5 +32,7 @@ struct octeon_cn23xx_vf { struct octeon_config *conf; }; +#define BUSY_READING_REG_VF_LOOP_COUNT 10000 + int cn23xx_setup_octeon_vf_device(struct octeon_device *oct); #endif diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 3d05b2f..39a9665 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -40,6 +40,7 @@ MODULE_VERSION(LIQUIDIO_VERSION); MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210SV_NAME LIO_FW_NAME_SUFFIX); MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210NV_NAME LIO_FW_NAME_SUFFIX); MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_410NV_NAME LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_23XX_NAME LIO_FW_NAME_SUFFIX); static int ddr_timeout = 10000; module_param(ddr_timeout, int, 0644); @@ -4484,7 +4485,10 @@ static int octeon_device_init(struct octeon_device *octeon_dev) atomic_set(&octeon_dev->status, OCT_DEV_DISPATCH_INIT_DONE); - octeon_set_io_queues_off(octeon_dev); + if (octeon_set_io_queues_off(octeon_dev)) { + dev_err(&octeon_dev->pci_dev->dev, "setting io queues off failed\n"); + return 1; + } if (OCTEON_CN23XX_PF(octeon_dev)) { ret = octeon_dev->fn_list.setup_device_regs(octeon_dev); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index dd1dad1..41fc9d2 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -236,6 +236,11 @@ static int octeon_device_init(struct octeon_device *oct) atomic_set(&oct->status, OCT_DEV_PCI_MAP_DONE); + if (octeon_set_io_queues_off(oct)) { + dev_err(&oct->pci_dev->dev, "setting io queues off failed\n"); + return 1; + } + return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 7e6c8b8..fe84e90 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -860,12 +860,53 @@ int octeon_setup_output_queues(struct octeon_device *oct) return 0; } -void octeon_set_io_queues_off(struct octeon_device *oct) +int octeon_set_io_queues_off(struct octeon_device *oct) { + int loop = BUSY_READING_REG_VF_LOOP_COUNT; + if (OCTEON_CN6XXX(oct)) { octeon_write_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB, 0); octeon_write_csr(oct, CN6XXX_SLI_PKT_OUT_ENB, 0); + } else if (oct->chip_id == OCTEON_CN23XX_VF_VID) { + u32 q_no; + + /* IOQs will already be in reset. + * If RST bit is set, wait for quiet bit to be set. + * Once quiet bit is set, clear the RST bit. + */ + for (q_no = 0; q_no < oct->sriov_info.rings_per_vf; q_no++) { + u64 reg_val = octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)); + + while ((reg_val & CN23XX_PKT_INPUT_CTL_RST) && + !(reg_val & CN23XX_PKT_INPUT_CTL_QUIET) && + loop) { + reg_val = octeon_read_csr64( + oct, CN23XX_SLI_IQ_PKT_CONTROL64(q_no)); + loop--; + } + if (!loop) { + dev_err(&oct->pci_dev->dev, + "clearing the reset reg failed or setting the quiet reg failed for qno: %u\n", + q_no); + return -1; + } + + reg_val = reg_val & ~CN23XX_PKT_INPUT_CTL_RST; + octeon_write_csr64(oct, + CN23XX_SLI_IQ_PKT_CONTROL64(q_no), + reg_val); + + reg_val = octeon_read_csr64( + oct, CN23XX_SLI_IQ_PKT_CONTROL64(q_no)); + if (reg_val & CN23XX_PKT_INPUT_CTL_RST) { + dev_err(&oct->pci_dev->dev, + "unable to reset qno %u\n", q_no); + return -1; + } + } } + return 0; } void octeon_set_droq_pkt_op(struct octeon_device *oct, diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 5ce2048..1e6bfa1 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -401,8 +401,13 @@ struct octeon_device { /** Octeon Chip type. */ u16 chip_id; + u16 rev_id; + u16 pf_num; + + u16 vf_num; + /** This device's id - set by the driver. */ u32 octeon_id; @@ -766,7 +771,7 @@ int octeon_get_rx_qsize(struct octeon_device *oct, u32 q_no); /** Turns off the input and output queues for the device * @param oct which octeon to disable */ -void octeon_set_io_queues_off(struct octeon_device *oct); +int octeon_set_io_queues_off(struct octeon_device *oct); /** Turns on or off the given output queue for the device * @param oct which octeon to change diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 8531a00..0e10e2a 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -235,7 +235,9 @@ int octeon_setup_iq(struct octeon_device *oct, } oct->num_iqs++; - oct->fn_list.enable_io_queues(oct); + if (oct->fn_list.enable_io_queues(oct)) + return 1; + return 0; } -- cgit v1.1 From da15c78b56646137a9827c59a5e9e312f8905417 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:37 -0800 Subject: liquidio CN23XX: VF register access This patch adds support for VF device register access. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.c | 189 +++++++++++++++++++++ .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 2 + drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 5 + 3 files changed, 196 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c index 60fd138..ad4e442 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c @@ -76,6 +76,161 @@ static int cn23xx_vf_reset_io_queues(struct octeon_device *oct, u32 num_queues) return ret_val; } +static int cn23xx_vf_setup_global_input_regs(struct octeon_device *oct) +{ + struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip; + struct octeon_instr_queue *iq; + u64 q_no, intr_threshold; + u64 d64; + + if (cn23xx_vf_reset_io_queues(oct, oct->sriov_info.rings_per_vf)) + return -1; + + for (q_no = 0; q_no < (oct->sriov_info.rings_per_vf); q_no++) { + void __iomem *inst_cnt_reg; + + octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_DOORBELL(q_no), + 0xFFFFFFFF); + iq = oct->instr_queue[q_no]; + + if (iq) + inst_cnt_reg = iq->inst_cnt_reg; + else + inst_cnt_reg = (u8 *)oct->mmio[0].hw_addr + + CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no); + + d64 = octeon_read_csr64(oct, + CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no)); + + d64 &= 0xEFFFFFFFFFFFFFFFL; + + octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no), + d64); + + /* Select ES, RO, NS, RDSIZE,DPTR Fomat#0 for + * the Input Queues + */ + octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), + CN23XX_PKT_INPUT_CTL_MASK); + + /* set the wmark level to trigger PI_INT */ + intr_threshold = CFG_GET_IQ_INTR_PKT(cn23xx->conf) & + CN23XX_PKT_IN_DONE_WMARK_MASK; + + writeq((readq(inst_cnt_reg) & + ~(CN23XX_PKT_IN_DONE_WMARK_MASK << + CN23XX_PKT_IN_DONE_WMARK_BIT_POS)) | + (intr_threshold << CN23XX_PKT_IN_DONE_WMARK_BIT_POS), + inst_cnt_reg); + } + return 0; +} + +static void cn23xx_vf_setup_global_output_regs(struct octeon_device *oct) +{ + u32 reg_val; + u32 q_no; + + for (q_no = 0; q_no < (oct->sriov_info.rings_per_vf); q_no++) { + octeon_write_csr(oct, CN23XX_VF_SLI_OQ_PKTS_CREDIT(q_no), + 0xFFFFFFFF); + + reg_val = + octeon_read_csr(oct, CN23XX_VF_SLI_OQ_PKTS_SENT(q_no)); + + reg_val &= 0xEFFFFFFFFFFFFFFFL; + + reg_val = + octeon_read_csr(oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no)); + + /* set IPTR & DPTR */ + reg_val |= + (CN23XX_PKT_OUTPUT_CTL_IPTR | CN23XX_PKT_OUTPUT_CTL_DPTR); + + /* reset BMODE */ + reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_BMODE); + + /* No Relaxed Ordering, No Snoop, 64-bit Byte swap + * for Output Queue ScatterList reset ROR_P, NSR_P + */ + reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_ROR_P); + reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_NSR_P); + +#ifdef __LITTLE_ENDIAN_BITFIELD + reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_ES_P); +#else + reg_val |= (CN23XX_PKT_OUTPUT_CTL_ES_P); +#endif + /* No Relaxed Ordering, No Snoop, 64-bit Byte swap + * for Output Queue Data reset ROR, NSR + */ + reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_ROR); + reg_val &= ~(CN23XX_PKT_OUTPUT_CTL_NSR); + /* set the ES bit */ + reg_val |= (CN23XX_PKT_OUTPUT_CTL_ES); + + /* write all the selected settings */ + octeon_write_csr(oct, CN23XX_VF_SLI_OQ_PKT_CONTROL(q_no), + reg_val); + } +} + +static int cn23xx_setup_vf_device_regs(struct octeon_device *oct) +{ + if (cn23xx_vf_setup_global_input_regs(oct)) + return -1; + + cn23xx_vf_setup_global_output_regs(oct); + + return 0; +} + +static void cn23xx_setup_vf_iq_regs(struct octeon_device *oct, u32 iq_no) +{ + struct octeon_instr_queue *iq = oct->instr_queue[iq_no]; + u64 pkt_in_done; + + /* Write the start of the input queue's ring and its size */ + octeon_write_csr64(oct, CN23XX_VF_SLI_IQ_BASE_ADDR64(iq_no), + iq->base_addr_dma); + octeon_write_csr(oct, CN23XX_VF_SLI_IQ_SIZE(iq_no), iq->max_count); + + /* Remember the doorbell & instruction count register addr + * for this queue + */ + iq->doorbell_reg = + (u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_IQ_DOORBELL(iq_no); + iq->inst_cnt_reg = + (u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_IQ_INSTR_COUNT64(iq_no); + dev_dbg(&oct->pci_dev->dev, "InstQ[%d]:dbell reg @ 0x%p instcnt_reg @ 0x%p\n", + iq_no, iq->doorbell_reg, iq->inst_cnt_reg); + + /* Store the current instruction counter (used in flush_iq + * calculation) + */ + pkt_in_done = readq(iq->inst_cnt_reg); + + iq->reset_instr_cnt = 0; +} + +static void cn23xx_setup_vf_oq_regs(struct octeon_device *oct, u32 oq_no) +{ + struct octeon_droq *droq = oct->droq[oq_no]; + + octeon_write_csr64(oct, CN23XX_VF_SLI_OQ_BASE_ADDR64(oq_no), + droq->desc_ring_dma); + octeon_write_csr(oct, CN23XX_VF_SLI_OQ_SIZE(oq_no), droq->max_count); + + octeon_write_csr(oct, CN23XX_VF_SLI_OQ_BUFF_INFO_SIZE(oq_no), + (droq->buffer_size | (OCT_RH_SIZE << 16))); + + /* Get the mapped address of the pkt_sent and pkts_credit regs */ + droq->pkts_sent_reg = + (u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_OQ_PKTS_SENT(oq_no); + droq->pkts_credit_reg = + (u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_OQ_PKTS_CREDIT(oq_no); +} + static int cn23xx_enable_vf_io_queues(struct octeon_device *oct) { u32 q_no; @@ -181,8 +336,42 @@ int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) } } + oct->fn_list.setup_iq_regs = cn23xx_setup_vf_iq_regs; + oct->fn_list.setup_oq_regs = cn23xx_setup_vf_oq_regs; + oct->fn_list.setup_device_regs = cn23xx_setup_vf_device_regs; + oct->fn_list.enable_io_queues = cn23xx_enable_vf_io_queues; oct->fn_list.disable_io_queues = cn23xx_disable_vf_io_queues; return 0; } + +void cn23xx_dump_vf_iq_regs(struct octeon_device *oct) +{ + u32 regval, q_no; + + dev_dbg(&oct->pci_dev->dev, "SLI_IQ_DOORBELL_0 [0x%x]: 0x%016llx\n", + CN23XX_VF_SLI_IQ_DOORBELL(0), + CVM_CAST64(octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_DOORBELL(0)))); + + dev_dbg(&oct->pci_dev->dev, "SLI_IQ_BASEADDR_0 [0x%x]: 0x%016llx\n", + CN23XX_VF_SLI_IQ_BASE_ADDR64(0), + CVM_CAST64(octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_BASE_ADDR64(0)))); + + dev_dbg(&oct->pci_dev->dev, "SLI_IQ_FIFO_RSIZE_0 [0x%x]: 0x%016llx\n", + CN23XX_VF_SLI_IQ_SIZE(0), + CVM_CAST64(octeon_read_csr64(oct, CN23XX_VF_SLI_IQ_SIZE(0)))); + + for (q_no = 0; q_no < oct->sriov_info.rings_per_vf; q_no++) { + dev_dbg(&oct->pci_dev->dev, "SLI_PKT[%d]_INPUT_CTL [0x%x]: 0x%016llx\n", + q_no, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no), + CVM_CAST64(octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_PKT_CONTROL64(q_no)))); + } + + pci_read_config_dword(oct->pci_dev, CN23XX_CONFIG_PCIE_DEVCTL, ®val); + dev_dbg(&oct->pci_dev->dev, "Config DevCtl [0x%x]: 0x%08x\n", + CN23XX_CONFIG_PCIE_DEVCTL, regval); +} diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index 6785796..d17c1ce 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -35,4 +35,6 @@ struct octeon_cn23xx_vf { #define BUSY_READING_REG_VF_LOOP_COUNT 10000 int cn23xx_setup_octeon_vf_device(struct octeon_device *oct); + +void cn23xx_dump_vf_initialized_regs(struct octeon_device *oct); #endif diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 41fc9d2..61c8b78 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -241,6 +241,11 @@ static int octeon_device_init(struct octeon_device *oct) return 1; } + if (oct->fn_list.setup_device_regs(oct)) { + dev_err(&oct->pci_dev->dev, "device registers configuration failed\n"); + return 1; + } + return 0; } -- cgit v1.1 From 9003baf09ea6b8226a73bbdbf54952fa4012d7f5 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:38 -0800 Subject: liquidio CN23XX: init VF softcommand queues Adds support for initializing softcommand, dispatch and instructions queues for VF. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 74 +++++++++++++++++++++- .../net/ethernet/cavium/liquidio/octeon_device.c | 5 ++ .../net/ethernet/cavium/liquidio/request_manager.c | 7 ++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 61c8b78..162e47b 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -140,18 +140,51 @@ static void octeon_pci_flr(struct octeon_device *oct) */ static void octeon_destroy_resources(struct octeon_device *oct) { + int i; + switch (atomic_read(&oct->status)) { + case OCT_DEV_IN_RESET: + case OCT_DEV_DROQ_INIT_DONE: + mdelay(100); + for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { + if (!(oct->io_qmask.oq & BIT_ULL(i))) + continue; + octeon_delete_droq(oct, i); + } + + /* fallthrough */ + case OCT_DEV_RESP_LIST_INIT_DONE: + octeon_delete_response_list(oct); + + /* fallthrough */ + case OCT_DEV_INSTR_QUEUE_INIT_DONE: + for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { + if (!(oct->io_qmask.iq & BIT_ULL(i))) + continue; + octeon_delete_instr_queue(oct, i); + } + + /* fallthrough */ + case OCT_DEV_SC_BUFF_POOL_INIT_DONE: + octeon_free_sc_buffer_pool(oct); + + /* fallthrough */ + case OCT_DEV_DISPATCH_INIT_DONE: + octeon_delete_dispatch_list(oct); + cancel_delayed_work_sync(&oct->nic_poll_work.work); + + /* fallthrough */ case OCT_DEV_PCI_MAP_DONE: octeon_unmap_pci_barx(oct, 0); octeon_unmap_pci_barx(oct, 1); - /* fallthrough */ + /* fallthrough */ case OCT_DEV_PCI_ENABLE_DONE: pci_clear_master(oct->pci_dev); /* Disable the device, releasing the PCI INT */ pci_disable_device(oct->pci_dev); - /* fallthrough */ + /* fallthrough */ case OCT_DEV_BEGIN_STATE: /* Nothing to be done here either */ break; @@ -236,6 +269,14 @@ static int octeon_device_init(struct octeon_device *oct) atomic_set(&oct->status, OCT_DEV_PCI_MAP_DONE); + /* Initialize the dispatch mechanism used to push packets arriving on + * Octeon Output queues. + */ + if (octeon_init_dispatch_list(oct)) + return 1; + + atomic_set(&oct->status, OCT_DEV_DISPATCH_INIT_DONE); + if (octeon_set_io_queues_off(oct)) { dev_err(&oct->pci_dev->dev, "setting io queues off failed\n"); return 1; @@ -246,6 +287,35 @@ static int octeon_device_init(struct octeon_device *oct) return 1; } + /* Initialize soft command buffer pool */ + if (octeon_setup_sc_buffer_pool(oct)) { + dev_err(&oct->pci_dev->dev, "sc buffer pool allocation failed\n"); + return 1; + } + atomic_set(&oct->status, OCT_DEV_SC_BUFF_POOL_INIT_DONE); + + /* Setup the data structures that manage this Octeon's Input queues. */ + if (octeon_setup_instr_queues(oct)) { + dev_err(&oct->pci_dev->dev, "instruction queue initialization failed\n"); + return 1; + } + atomic_set(&oct->status, OCT_DEV_INSTR_QUEUE_INIT_DONE); + + /* Initialize lists to manage the requests of different types that + * arrive from user & kernel applications for this octeon device. + */ + if (octeon_setup_response_list(oct)) { + dev_err(&oct->pci_dev->dev, "Response list allocation failed\n"); + return 1; + } + atomic_set(&oct->status, OCT_DEV_RESP_LIST_INIT_DONE); + + if (octeon_setup_output_queues(oct)) { + dev_err(&oct->pci_dev->dev, "Output queue initialization failed\n"); + return 1; + } + atomic_set(&oct->status, OCT_DEV_DROQ_INIT_DONE); + return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index fe84e90..fcc5f10 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -797,6 +797,8 @@ int octeon_setup_instr_queues(struct octeon_device *oct) CFG_GET_NUM_DEF_TX_DESCS(CHIP_CONF(oct, cn6xxx)); else if (OCTEON_CN23XX_PF(oct)) num_descs = CFG_GET_NUM_DEF_TX_DESCS(CHIP_CONF(oct, cn23xx_pf)); + else if (OCTEON_CN23XX_VF(oct)) + num_descs = CFG_GET_NUM_DEF_TX_DESCS(CHIP_CONF(oct, cn23xx_vf)); oct->num_iqs = 0; @@ -842,6 +844,9 @@ int octeon_setup_output_queues(struct octeon_device *oct) } else if (OCTEON_CN23XX_PF(oct)) { num_descs = CFG_GET_NUM_DEF_RX_DESCS(CHIP_CONF(oct, cn23xx_pf)); desc_size = CFG_GET_DEF_RX_BUF_SIZE(CHIP_CONF(oct, cn23xx_pf)); + } else if (OCTEON_CN23XX_VF(oct)) { + num_descs = CFG_GET_NUM_DEF_RX_DESCS(CHIP_CONF(oct, cn23xx_vf)); + desc_size = CFG_GET_DEF_RX_BUF_SIZE(CHIP_CONF(oct, cn23xx_vf)); } oct->num_oqs = 0; oct->droq[0] = vmalloc_node(sizeof(*oct->droq[0]), numa_node); diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 0e10e2a..ea2b7e4 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -28,6 +28,7 @@ #include "octeon_network.h" #include "cn66xx_device.h" #include "cn23xx_pf_device.h" +#include "cn23xx_vf_device.h" struct iq_post_status { int status; @@ -68,6 +69,9 @@ int octeon_init_instr_queue(struct octeon_device *oct, conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn6xxx))); else if (OCTEON_CN23XX_PF(oct)) conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_pf))); + else if (OCTEON_CN23XX_VF(oct)) + conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_vf))); + if (!conf) { dev_err(&oct->pci_dev->dev, "Unsupported Chip %x\n", oct->chip_id); @@ -183,6 +187,9 @@ int octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no) else if (OCTEON_CN23XX_PF(oct)) desc_size = CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_pf)); + else if (OCTEON_CN23XX_VF(oct)) + desc_size = + CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_vf)); vfree(iq->request_list); -- cgit v1.1 From f7cdd64bede8af6d86eeae6f640838ba4a039085 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:39 -0800 Subject: liquidio CN23XX: VF mailbox Adds support for VF mailbox setup. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.c | 59 ++++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 10 ++++ 2 files changed, 69 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c index ad4e442..7dfec44 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c @@ -17,6 +17,7 @@ ***********************************************************************/ #include #include +#include #include "liquidio_common.h" #include "octeon_droq.h" #include "octeon_iq.h" @@ -24,6 +25,7 @@ #include "octeon_device.h" #include "cn23xx_vf_device.h" #include "octeon_main.h" +#include "octeon_mailbox.h" static int cn23xx_vf_reset_io_queues(struct octeon_device *oct, u32 num_queues) { @@ -231,6 +233,61 @@ static void cn23xx_setup_vf_oq_regs(struct octeon_device *oct, u32 oq_no) (u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_OQ_PKTS_CREDIT(oq_no); } +static void cn23xx_vf_mbox_thread(struct work_struct *work) +{ + struct cavium_wk *wk = (struct cavium_wk *)work; + struct octeon_mbox *mbox = (struct octeon_mbox *)wk->ctxptr; + + octeon_mbox_process_message(mbox); +} + +static int cn23xx_free_vf_mbox(struct octeon_device *oct) +{ + cancel_delayed_work_sync(&oct->mbox[0]->mbox_poll_wk.work); + vfree(oct->mbox[0]); + return 0; +} + +static int cn23xx_setup_vf_mbox(struct octeon_device *oct) +{ + struct octeon_mbox *mbox = NULL; + + mbox = vmalloc(sizeof(*mbox)); + if (!mbox) + return 1; + + memset(mbox, 0, sizeof(struct octeon_mbox)); + + spin_lock_init(&mbox->lock); + + mbox->oct_dev = oct; + + mbox->q_no = 0; + + mbox->state = OCTEON_MBOX_STATE_IDLE; + + /* VF mbox interrupt reg */ + mbox->mbox_int_reg = + (u8 *)oct->mmio[0].hw_addr + CN23XX_VF_SLI_PKT_MBOX_INT(0); + /* VF reads from SIG0 reg */ + mbox->mbox_read_reg = + (u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_PKT_PF_VF_MBOX_SIG(0, 0); + /* VF writes into SIG1 reg */ + mbox->mbox_write_reg = + (u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_PKT_PF_VF_MBOX_SIG(0, 1); + + INIT_DELAYED_WORK(&mbox->mbox_poll_wk.work, + cn23xx_vf_mbox_thread); + + mbox->mbox_poll_wk.ctxptr = mbox; + + oct->mbox[0] = mbox; + + writeq(OCTEON_PFVFSIG, mbox->mbox_read_reg); + + return 0; +} + static int cn23xx_enable_vf_io_queues(struct octeon_device *oct) { u32 q_no; @@ -338,6 +395,8 @@ int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) oct->fn_list.setup_iq_regs = cn23xx_setup_vf_iq_regs; oct->fn_list.setup_oq_regs = cn23xx_setup_vf_oq_regs; + oct->fn_list.setup_mbox = cn23xx_setup_vf_mbox; + oct->fn_list.free_mbox = cn23xx_free_vf_mbox; oct->fn_list.setup_device_regs = cn23xx_setup_vf_device_regs; oct->fn_list.enable_io_queues = cn23xx_enable_vf_io_queues; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 162e47b..43a1e3f 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -143,6 +143,10 @@ static void octeon_destroy_resources(struct octeon_device *oct) int i; switch (atomic_read(&oct->status)) { + case OCT_DEV_MBOX_SETUP_DONE: + oct->fn_list.free_mbox(oct); + + /* fallthrough */ case OCT_DEV_IN_RESET: case OCT_DEV_DROQ_INIT_DONE: mdelay(100); @@ -316,6 +320,12 @@ static int octeon_device_init(struct octeon_device *oct) } atomic_set(&oct->status, OCT_DEV_DROQ_INIT_DONE); + if (oct->fn_list.setup_mbox(oct)) { + dev_err(&oct->pci_dev->dev, "Mailbox setup failed\n"); + return 1; + } + atomic_set(&oct->status, OCT_DEV_MBOX_SETUP_DONE); + return 0; } -- cgit v1.1 From cf39faf542893652e9f325f21ce757475b7fd9f5 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:40 -0800 Subject: liquidio CN23XX: VF interrupt Adds support for VF interrupt processing. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.c | 265 +++++++++++++++++++++ .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 6 + drivers/net/ethernet/cavium/liquidio/lio_core.c | 7 - drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 162 +++++++++++++ .../net/ethernet/cavium/liquidio/octeon_device.c | 3 + .../net/ethernet/cavium/liquidio/octeon_device.h | 2 + 6 files changed, 438 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c index 7dfec44..108e487 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c @@ -27,6 +27,26 @@ #include "octeon_main.h" #include "octeon_mailbox.h" +u32 cn23xx_vf_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us) +{ + /* This gives the SLI clock per microsec */ + u32 oqticks_per_us = (u32)oct->pfvf_hsword.coproc_tics_per_us; + + /* This gives the clock cycles per millisecond */ + oqticks_per_us *= 1000; + + /* This gives the oq ticks (1024 core clock cycles) per millisecond */ + oqticks_per_us /= 1024; + + /* time_intr is in microseconds. The next 2 steps gives the oq ticks + * corressponding to time_intr. + */ + oqticks_per_us *= time_intr_in_us; + oqticks_per_us /= 1000; + + return oqticks_per_us; +} + static int cn23xx_vf_reset_io_queues(struct octeon_device *oct, u32 num_queues) { u32 loop = BUSY_READING_REG_VF_LOOP_COUNT; @@ -212,6 +232,11 @@ static void cn23xx_setup_vf_iq_regs(struct octeon_device *oct, u32 iq_no) */ pkt_in_done = readq(iq->inst_cnt_reg); + if (oct->msix_on) { + /* Set CINT_ENB to enable IQ interrupt */ + writeq((pkt_in_done | CN23XX_INTR_CINT_ENB), + iq->inst_cnt_reg); + } iq->reset_instr_cnt = 0; } @@ -342,6 +367,240 @@ static void cn23xx_disable_vf_io_queues(struct octeon_device *oct) cn23xx_vf_reset_io_queues(oct, num_queues); } +void cn23xx_vf_ask_pf_to_do_flr(struct octeon_device *oct) +{ + struct octeon_mbox_cmd mbox_cmd; + + mbox_cmd.msg.u64 = 0; + mbox_cmd.msg.s.type = OCTEON_MBOX_REQUEST; + mbox_cmd.msg.s.resp_needed = 0; + mbox_cmd.msg.s.cmd = OCTEON_VF_FLR_REQUEST; + mbox_cmd.msg.s.len = 1; + mbox_cmd.q_no = 0; + mbox_cmd.recv_len = 0; + mbox_cmd.recv_status = 0; + mbox_cmd.fn = NULL; + mbox_cmd.fn_arg = 0; + + octeon_mbox_write(oct, &mbox_cmd); +} + +static void octeon_pfvf_hs_callback(struct octeon_device *oct, + struct octeon_mbox_cmd *cmd, + void *arg) +{ + u32 major = 0; + + memcpy((uint8_t *)&oct->pfvf_hsword, cmd->msg.s.params, + CN23XX_MAILBOX_MSGPARAM_SIZE); + if (cmd->recv_len > 1) { + major = ((struct lio_version *)(cmd->data))->major; + major = major << 16; + } + + atomic_set((atomic_t *)arg, major | 1); +} + +int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct) +{ + struct octeon_mbox_cmd mbox_cmd; + u32 q_no, count = 0; + atomic_t status; + u32 pfmajor; + u32 vfmajor; + u32 ret; + + /* Sending VF_ACTIVE indication to the PF driver */ + dev_dbg(&oct->pci_dev->dev, "requesting info from pf\n"); + + mbox_cmd.msg.u64 = 0; + mbox_cmd.msg.s.type = OCTEON_MBOX_REQUEST; + mbox_cmd.msg.s.resp_needed = 1; + mbox_cmd.msg.s.cmd = OCTEON_VF_ACTIVE; + mbox_cmd.msg.s.len = 2; + mbox_cmd.data[0] = 0; + ((struct lio_version *)&mbox_cmd.data[0])->major = + LIQUIDIO_BASE_MAJOR_VERSION; + ((struct lio_version *)&mbox_cmd.data[0])->minor = + LIQUIDIO_BASE_MINOR_VERSION; + ((struct lio_version *)&mbox_cmd.data[0])->micro = + LIQUIDIO_BASE_MICRO_VERSION; + mbox_cmd.q_no = 0; + mbox_cmd.recv_len = 0; + mbox_cmd.recv_status = 0; + mbox_cmd.fn = (octeon_mbox_callback_t)octeon_pfvf_hs_callback; + mbox_cmd.fn_arg = &status; + + /* Interrupts are not enabled at this point. + * Enable them with default oq ticks + */ + oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); + + octeon_mbox_write(oct, &mbox_cmd); + + atomic_set(&status, 0); + + do { + schedule_timeout_uninterruptible(1); + } while ((!atomic_read(&status)) && (count++ < 100000)); + + /* Disable the interrupt so that the interrupsts will be reenabled + * with the oq ticks received from the PF + */ + oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); + + ret = atomic_read(&status); + if (!ret) { + dev_err(&oct->pci_dev->dev, "octeon_pfvf_handshake timeout\n"); + return 1; + } + + for (q_no = 0 ; q_no < oct->num_iqs ; q_no++) + oct->instr_queue[q_no]->txpciq.s.pkind = oct->pfvf_hsword.pkind; + + vfmajor = LIQUIDIO_BASE_MAJOR_VERSION; + pfmajor = ret >> 16; + if (pfmajor != vfmajor) { + dev_err(&oct->pci_dev->dev, + "VF Liquidio driver (major version %d) is not compatible with Liquidio PF driver (major version %d)\n", + vfmajor, pfmajor); + return 1; + } + + dev_dbg(&oct->pci_dev->dev, + "VF Liquidio driver (major version %d), Liquidio PF driver (major version %d)\n", + vfmajor, pfmajor); + + dev_dbg(&oct->pci_dev->dev, "got data from pf pkind is %d\n", + oct->pfvf_hsword.pkind); + + return 0; +} + +static void cn23xx_handle_vf_mbox_intr(struct octeon_ioq_vector *ioq_vector) +{ + struct octeon_device *oct = ioq_vector->oct_dev; + u64 mbox_int_val; + + if (!ioq_vector->droq_index) { + /* read and clear by writing 1 */ + mbox_int_val = readq(oct->mbox[0]->mbox_int_reg); + writeq(mbox_int_val, oct->mbox[0]->mbox_int_reg); + if (octeon_mbox_read(oct->mbox[0])) + schedule_delayed_work(&oct->mbox[0]->mbox_poll_wk.work, + msecs_to_jiffies(0)); + } +} + +static u64 cn23xx_vf_msix_interrupt_handler(void *dev) +{ + struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; + struct octeon_device *oct = ioq_vector->oct_dev; + struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; + u64 pkts_sent; + u64 ret = 0; + + dev_dbg(&oct->pci_dev->dev, "In %s octeon_dev @ %p\n", __func__, oct); + pkts_sent = readq(droq->pkts_sent_reg); + + /* If our device has interrupted, then proceed. Also check + * for all f's if interrupt was triggered on an error + * and the PCI read fails. + */ + if (!pkts_sent || (pkts_sent == 0xFFFFFFFFFFFFFFFFULL)) + return ret; + + /* Write count reg in sli_pkt_cnts to clear these int. */ + if ((pkts_sent & CN23XX_INTR_PO_INT) || + (pkts_sent & CN23XX_INTR_PI_INT)) { + if (pkts_sent & CN23XX_INTR_PO_INT) + ret |= MSIX_PO_INT; + } + + if (pkts_sent & CN23XX_INTR_PI_INT) + /* We will clear the count when we update the read_index. */ + ret |= MSIX_PI_INT; + + if (pkts_sent & CN23XX_INTR_MBOX_INT) { + cn23xx_handle_vf_mbox_intr(ioq_vector); + ret |= MSIX_MBOX_INT; + } + + return ret; +} + +static void cn23xx_enable_vf_interrupt(struct octeon_device *oct, u8 intr_flag) +{ + struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip; + u32 q_no, time_threshold; + + if (intr_flag & OCTEON_OUTPUT_INTR) { + for (q_no = 0; q_no < oct->num_oqs; q_no++) { + /* Set up interrupt packet and time thresholds + * for all the OQs + */ + time_threshold = cn23xx_vf_get_oq_ticks( + oct, (u32)CFG_GET_OQ_INTR_TIME(cn23xx->conf)); + + octeon_write_csr64( + oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no), + (CFG_GET_OQ_INTR_PKT(cn23xx->conf) | + ((u64)time_threshold << 32))); + } + } + + if (intr_flag & OCTEON_INPUT_INTR) { + for (q_no = 0; q_no < oct->num_oqs; q_no++) { + /* Set CINT_ENB to enable IQ interrupt */ + octeon_write_csr64( + oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no), + ((octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no)) & + ~CN23XX_PKT_IN_DONE_CNT_MASK) | + CN23XX_INTR_CINT_ENB)); + } + } + + /* Set queue-0 MBOX_ENB to enable VF mailbox interrupt */ + if (intr_flag & OCTEON_MBOX_INTR) { + octeon_write_csr64( + oct, CN23XX_VF_SLI_PKT_MBOX_INT(0), + (octeon_read_csr64(oct, CN23XX_VF_SLI_PKT_MBOX_INT(0)) | + CN23XX_INTR_MBOX_ENB)); + } +} + +static void cn23xx_disable_vf_interrupt(struct octeon_device *oct, u8 intr_flag) +{ + u32 q_no; + + if (intr_flag & OCTEON_OUTPUT_INTR) { + for (q_no = 0; q_no < oct->num_oqs; q_no++) { + /* Write all 1's in INT_LEVEL reg to disable PO_INT */ + octeon_write_csr64( + oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no), + 0x3fffffffffffff); + } + } + if (intr_flag & OCTEON_INPUT_INTR) { + for (q_no = 0; q_no < oct->num_oqs; q_no++) { + octeon_write_csr64( + oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no), + (octeon_read_csr64( + oct, CN23XX_VF_SLI_IQ_INSTR_COUNT64(q_no)) & + ~(CN23XX_INTR_CINT_ENB | + CN23XX_PKT_IN_DONE_CNT_MASK))); + } + } + + if (intr_flag & OCTEON_MBOX_INTR) { + octeon_write_csr64( + oct, CN23XX_VF_SLI_PKT_MBOX_INT(0), + (octeon_read_csr64(oct, CN23XX_VF_SLI_PKT_MBOX_INT(0)) & + ~CN23XX_INTR_MBOX_ENB)); + } +} + int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) { struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip; @@ -397,8 +656,14 @@ int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) oct->fn_list.setup_oq_regs = cn23xx_setup_vf_oq_regs; oct->fn_list.setup_mbox = cn23xx_setup_vf_mbox; oct->fn_list.free_mbox = cn23xx_free_vf_mbox; + + oct->fn_list.msix_interrupt_handler = cn23xx_vf_msix_interrupt_handler; + oct->fn_list.setup_device_regs = cn23xx_setup_vf_device_regs; + oct->fn_list.enable_interrupt = cn23xx_enable_vf_interrupt; + oct->fn_list.disable_interrupt = cn23xx_disable_vf_interrupt; + oct->fn_list.enable_io_queues = cn23xx_enable_vf_io_queues; oct->fn_list.disable_io_queues = cn23xx_disable_vf_io_queues; diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index d17c1ce..8590bdb 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -34,6 +34,12 @@ struct octeon_cn23xx_vf { #define BUSY_READING_REG_VF_LOOP_COUNT 10000 +#define CN23XX_MAILBOX_MSGPARAM_SIZE 6 + +void cn23xx_vf_ask_pf_to_do_flr(struct octeon_device *oct); + +int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct); + int cn23xx_setup_octeon_vf_device(struct octeon_device *oct); void cn23xx_dump_vf_initialized_regs(struct octeon_device *oct); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 403bcaa..f629c2f 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -85,13 +85,6 @@ void octeon_update_tx_completion_counters(void *buf, int reqtype, } (*pkts_compl)++; -/*TODO, Use some other pound define to suggest - * the fact that iqs are not tied to netdevs - * and can take traffic from different netdevs - * hence bql reporting is done per packet - * than in bulk. Usage of NO_NAPI in txq completion is - * a little confusing - */ *bytes_compl += skb->len; } diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 43a1e3f..3d5c61a 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -59,6 +59,118 @@ static struct pci_driver liquidio_vf_pci_driver = { .remove = liquidio_vf_remove, }; +static +int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) +{ + struct octeon_device *oct = droq->oct_dev; + struct octeon_device_priv *oct_priv = + (struct octeon_device_priv *)oct->priv; + + if (droq->ops.poll_mode) { + droq->ops.napi_fn(droq); + } else { + if (ret & MSIX_PO_INT) { + dev_err(&oct->pci_dev->dev, + "should not come here should not get rx when poll mode = 0 for vf\n"); + tasklet_schedule(&oct_priv->droq_tasklet); + return 1; + } + /* this will be flushed periodically by check iq db */ + if (ret & MSIX_PI_INT) + return 0; + } + return 0; +} + +static irqreturn_t +liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) +{ + struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; + struct octeon_device *oct = ioq_vector->oct_dev; + struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; + u64 ret; + + ret = oct->fn_list.msix_interrupt_handler(ioq_vector); + + if ((ret & MSIX_PO_INT) || (ret & MSIX_PI_INT)) + liquidio_schedule_msix_droq_pkt_handler(droq, ret); + + return IRQ_HANDLED; +} + +/** + * \brief Setup interrupt for octeon device + * @param oct octeon device + * + * Enable interrupt in Octeon device as given in the PCI interrupt mask. + */ +static int octeon_setup_interrupt(struct octeon_device *oct) +{ + struct msix_entry *msix_entries; + int num_alloc_ioq_vectors; + int num_ioq_vectors; + int irqret; + int i; + + if (oct->msix_on) { + oct->num_msix_irqs = oct->sriov_info.rings_per_vf; + + oct->msix_entries = kcalloc( + oct->num_msix_irqs, sizeof(struct msix_entry), GFP_KERNEL); + if (!oct->msix_entries) + return 1; + + msix_entries = (struct msix_entry *)oct->msix_entries; + + for (i = 0; i < oct->num_msix_irqs; i++) + msix_entries[i].entry = i; + num_alloc_ioq_vectors = pci_enable_msix_range( + oct->pci_dev, msix_entries, + oct->num_msix_irqs, + oct->num_msix_irqs); + if (num_alloc_ioq_vectors < 0) { + dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + return 1; + } + dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); + + num_ioq_vectors = oct->num_msix_irqs; + + for (i = 0; i < num_ioq_vectors; i++) { + irqret = request_irq(msix_entries[i].vector, + liquidio_msix_intr_handler, 0, + "octeon", &oct->ioq_vector[i]); + if (irqret) { + dev_err(&oct->pci_dev->dev, + "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", + irqret); + + while (i) { + i--; + irq_set_affinity_hint( + msix_entries[i].vector, NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + } + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + return 1; + } + oct->ioq_vector[i].vector = msix_entries[i].vector; + /* assign the cpu mask for this msix interrupt vector */ + irq_set_affinity_hint( + msix_entries[i].vector, + (&oct->ioq_vector[i].affinity_mask)); + } + dev_dbg(&oct->pci_dev->dev, + "OCTEON[%d]: MSI-X enabled\n", oct->octeon_id); + } + return 0; +} + /** * \brief PCI probe handler * @param pdev PCI device structure @@ -77,6 +189,7 @@ liquidio_vf_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "Unable to allocate device\n"); return -ENOMEM; } + oct_dev->msix_on = LIO_FLAG_MSIX_ENABLED; dev_info(&pdev->dev, "Initializing device %x:%x.\n", (u32)pdev->vendor, (u32)pdev->device); @@ -140,9 +253,37 @@ static void octeon_pci_flr(struct octeon_device *oct) */ static void octeon_destroy_resources(struct octeon_device *oct) { + struct msix_entry *msix_entries; int i; switch (atomic_read(&oct->status)) { + case OCT_DEV_INTR_SET_DONE: + /* Disable interrupts */ + oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); + + if (oct->msix_on) { + msix_entries = (struct msix_entry *)oct->msix_entries; + for (i = 0; i < oct->num_msix_irqs; i++) { + irq_set_affinity_hint(msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + } + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + } + /* Soft reset the octeon device before exiting */ + if (oct->pci_dev->reset_fn) + octeon_pci_flr(oct); + else + cn23xx_vf_ask_pf_to_do_flr(oct); + + /* fallthrough */ + case OCT_DEV_MSIX_ALLOC_VECTOR_DONE: + octeon_free_ioq_vector(oct); + + /* fallthrough */ case OCT_DEV_MBOX_SETUP_DONE: oct->fn_list.free_mbox(oct); @@ -326,6 +467,27 @@ static int octeon_device_init(struct octeon_device *oct) } atomic_set(&oct->status, OCT_DEV_MBOX_SETUP_DONE); + if (octeon_allocate_ioq_vector(oct)) { + dev_err(&oct->pci_dev->dev, "ioq vector allocation failed\n"); + return 1; + } + atomic_set(&oct->status, OCT_DEV_MSIX_ALLOC_VECTOR_DONE); + + dev_info(&oct->pci_dev->dev, "OCTEON_CN23XX VF Version: %s, %d ioqs\n", + LIQUIDIO_VERSION, oct->sriov_info.rings_per_vf); + + /* Setup the interrupt handler and record the INT SUM register address*/ + if (octeon_setup_interrupt(oct)) + return 1; + + if (cn23xx_octeon_pfvf_handshake(oct)) + return 1; + + /* Enable Octeon device interrupts */ + oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); + + atomic_set(&oct->status, OCT_DEV_INTR_SET_DONE); + return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index fcc5f10..6d54032 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -754,6 +754,9 @@ octeon_allocate_ioq_vector(struct octeon_device *oct) if (OCTEON_CN23XX_PF(oct)) num_ioqs = oct->sriov_info.num_pf_rings; + else if (OCTEON_CN23XX_VF(oct)) + num_ioqs = oct->sriov_info.rings_per_vf; + size = sizeof(struct octeon_ioq_vector) * num_ioqs; oct->ioq_vector = vmalloc(size); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 1e6bfa1..18f6836 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -53,6 +53,7 @@ enum { NUM_OCTEON_CONFS, }; +#define OCTEON_INPUT_INTR (1) #define OCTEON_OUTPUT_INTR (2) #define OCTEON_MBOX_INTR (4) #define OCTEON_ALL_INTR 0xff @@ -294,6 +295,7 @@ struct octdev_props { #define LIO_FLAG_MSIX_ENABLED 0x1 #define MSIX_PO_INT 0x1 #define MSIX_PI_INT 0x2 +#define MSIX_MBOX_INT 0x4 struct octeon_pf_vf_hs_word { #ifdef __LITTLE_ENDIAN_BITFIELD -- cgit v1.1 From b3c35973b8ac0f0906ef38e9b07b0a01a3d357a7 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Mon, 28 Nov 2016 16:54:41 -0800 Subject: liquidio CN23XX: VF init and destroy Adds support for VF initialization and destroy resources. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 2 + drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 106 +++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index 8590bdb..6715df3 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -36,6 +36,8 @@ struct octeon_cn23xx_vf { #define CN23XX_MAILBOX_MSGPARAM_SIZE 6 +#define MAX_VF_IP_OP_PENDING_PKT_COUNT 100 + void cn23xx_vf_ask_pf_to_do_flr(struct octeon_device *oct); int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 3d5c61a..e6321f3 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -41,6 +41,60 @@ liquidio_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void liquidio_vf_remove(struct pci_dev *pdev); static int octeon_device_init(struct octeon_device *oct); +static int lio_wait_for_oq_pkts(struct octeon_device *oct) +{ + struct octeon_device_priv *oct_priv = + (struct octeon_device_priv *)oct->priv; + int retry = MAX_VF_IP_OP_PENDING_PKT_COUNT; + int pkt_cnt = 0, pending_pkts; + int i; + + do { + pending_pkts = 0; + + for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { + if (!(oct->io_qmask.oq & BIT_ULL(i))) + continue; + pkt_cnt += octeon_droq_check_hw_for_pkts(oct->droq[i]); + } + if (pkt_cnt > 0) { + pending_pkts += pkt_cnt; + tasklet_schedule(&oct_priv->droq_tasklet); + } + pkt_cnt = 0; + schedule_timeout_uninterruptible(1); + + } while (retry-- && pending_pkts); + + return pkt_cnt; +} + +/** + * \brief wait for all pending requests to complete + * @param oct Pointer to Octeon device + * + * Called during shutdown sequence + */ +static int wait_for_pending_requests(struct octeon_device *oct) +{ + int i, pcount = 0; + + for (i = 0; i < MAX_VF_IP_OP_PENDING_PKT_COUNT; i++) { + pcount = atomic_read( + &oct->response_list[OCTEON_ORDERED_SC_LIST] + .pending_req_count); + if (pcount) + schedule_timeout_uninterruptible(HZ / 10); + else + break; + } + + if (pcount) + return 1; + + return 0; +} + static const struct pci_device_id liquidio_vf_pci_tbl[] = { { PCI_VENDOR_ID_CAVIUM, OCTEON_CN23XX_VF_VID, @@ -257,6 +311,35 @@ static void octeon_destroy_resources(struct octeon_device *oct) int i; switch (atomic_read(&oct->status)) { + case OCT_DEV_RUNNING: + case OCT_DEV_CORE_OK: + /* No more instructions will be forwarded. */ + atomic_set(&oct->status, OCT_DEV_IN_RESET); + + dev_dbg(&oct->pci_dev->dev, "Device state is now %s\n", + lio_get_state_string(&oct->status)); + + schedule_timeout_uninterruptible(HZ / 10); + + /* fallthrough */ + case OCT_DEV_HOST_OK: + /* fallthrough */ + case OCT_DEV_IO_QUEUES_DONE: + if (wait_for_pending_requests(oct)) + dev_err(&oct->pci_dev->dev, "There were pending requests\n"); + + if (lio_wait_for_instr_fetch(oct)) + dev_err(&oct->pci_dev->dev, "IQ had pending instructions\n"); + + /* Disable the input and output queues now. No more packets will + * arrive from Octeon, but we should wait for all packet + * processing to finish. + */ + oct->fn_list.disable_io_queues(oct); + + if (lio_wait_for_oq_pkts(oct)) + dev_err(&oct->pci_dev->dev, "OQ had pending packets\n"); + case OCT_DEV_INTR_SET_DONE: /* Disable interrupts */ oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); @@ -395,6 +478,7 @@ static int octeon_pci_os_setup(struct octeon_device *oct) static int octeon_device_init(struct octeon_device *oct) { u32 rev_id; + int j; atomic_set(&oct->status, OCT_DEV_BEGIN_STATE); @@ -488,6 +572,28 @@ static int octeon_device_init(struct octeon_device *oct) atomic_set(&oct->status, OCT_DEV_INTR_SET_DONE); + /* Enable the input and output queues for this Octeon device */ + if (oct->fn_list.enable_io_queues(oct)) { + dev_err(&oct->pci_dev->dev, "enabling io queues failed\n"); + return 1; + } + + atomic_set(&oct->status, OCT_DEV_IO_QUEUES_DONE); + + atomic_set(&oct->status, OCT_DEV_HOST_OK); + + /* Send Credit for Octeon Output queues. Credits are always sent after + * the output queue is enabled. + */ + for (j = 0; j < oct->num_oqs; j++) + writel(oct->droq[j]->max_count, oct->droq[j]->pkts_credit_reg); + + /* Packets can start arriving on the output queues from this point. */ + + atomic_set(&oct->status, OCT_DEV_CORE_OK); + + atomic_set(&oct->status, OCT_DEV_RUNNING); + return 0; } -- cgit v1.1 From 763dfa276c9a280b92543b1862954dba98ef2682 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Tue, 29 Nov 2016 11:26:32 +0800 Subject: macvtap: replace printk with netdev_err This patch replaces printk() with netdev_err() for macvtap device. Signed-off-by: Zhang Shengju Acked-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 5da9861..2513939 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -437,7 +437,7 @@ static int macvtap_get_minor(struct macvlan_dev *vlan) if (retval >= 0) { vlan->minor = retval; } else if (retval == -ENOSPC) { - printk(KERN_ERR "too many macvtap devices\n"); + netdev_err(vlan->dev, "Too many macvtap devices\n"); retval = -EINVAL; } mutex_unlock(&minor_lock); -- cgit v1.1 From 9bee294f53ef588fdc909e53d7273d428e2887aa Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Mon, 28 Nov 2016 22:07:22 -0800 Subject: samples/bpf: fix include path Fix the following build error: HOSTCC samples/bpf/test_lru_dist.o ../samples/bpf/test_lru_dist.c:25:22: fatal error: bpf_util.h: No such file or directory This is due to objtree != srctree. Use srctree, since that's where bpf_util.h is located. Fixes: e00c7b216f34 ("bpf: fix multiple issues in selftest suite and samples") Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Tested-by: David Ahern Signed-off-by: David S. Miller --- samples/bpf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 22b6407e..3ceb5a9 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -91,7 +91,7 @@ always += trace_event_kern.o always += sampleip_kern.o HOSTCFLAGS += -I$(objtree)/usr/include -HOSTCFLAGS += -I$(objtree)/tools/testing/selftests/bpf/ +HOSTCFLAGS += -I$(srctree)/tools/testing/selftests/bpf/ HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable HOSTLOADLIBES_fds_example += -lelf -- cgit v1.1 From 18502acd9a9663f25eb484025b7ab98f08d30d20 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 30 Nov 2016 11:24:42 +0800 Subject: neigh: remove duplicate check for same neigh Currently loop index 'idx' is used as the index in the neigh list of interest. It's increased only when the neigh is dumped. It's not the absolute index in the list. Because there is no info to record which neigh has already be scanned by previous loop. This will cause the filtered out neighs to be scanned mulitple times. This patch make idx as the absolute index in the list, it will increase no matter whether the neigh is filtered. This will prevent the above problem. And this is in line with other dump functions. v2: - take David Ahern's advice to do simple change Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- net/core/neighbour.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 2ae929f..782dd86 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2291,13 +2291,10 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0; n != NULL; n = rcu_dereference_bh(n->next)) { - if (!net_eq(dev_net(n->dev), net)) - continue; - if (neigh_ifindex_filtered(n->dev, filter_idx)) - continue; - if (neigh_master_filtered(n->dev, filter_master_idx)) - continue; - if (idx < s_idx) + if (idx < s_idx || !net_eq(dev_net(n->dev), net)) + goto next; + if (neigh_ifindex_filtered(n->dev, filter_idx) || + neigh_master_filtered(n->dev, filter_master_idx)) goto next; if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, @@ -2332,9 +2329,7 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, if (h > s_h) s_idx = 0; for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { - if (pneigh_net(n) != net) - continue; - if (idx < s_idx) + if (idx < s_idx || pneigh_net(n) != net) goto next; if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, -- cgit v1.1 From b634d30a79ecc2d28e61cbe5b1f4443952f37a8f Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 30 Nov 2016 10:16:08 -0800 Subject: cgroup, bpf: remove unnecessary #include this #include is unnecessary and brings whole set of other headers into cgroup-defs.h. Remove it. Fixes: 3007098494be ("cgroup: add support for eBPF programs") Signed-off-by: Alexei Starovoitov Acked-by: Rami Rosen Acked-by: Daniel Borkmann Acked-by: Daniel Mack Signed-off-by: David S. Miller --- include/linux/bpf-cgroup.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index ec80d0c..0cf1adf 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -1,7 +1,6 @@ #ifndef _BPF_CGROUP_H #define _BPF_CGROUP_H -#include #include #include -- cgit v1.1 From f54b8cd6efed57a598e004be04cc53a6771b07cd Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 29 Nov 2016 14:35:07 +0100 Subject: ehea: Remove unnecessary memset of stats in netdev private data The memory for netdev private data is allocated using kzalloc/vzalloc in alloc_netdev_mqs, thus there is no need to zero the stats portion of it again in the driver's probe function. In any case, the size for the memset is wrong as the stats member is of type rtnl_link_stats64, not net_device_stats. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ehea/ehea_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index fa66fa6..702446a 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3044,7 +3044,6 @@ static struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, init_waitqueue_head(&port->swqe_avail_wq); init_waitqueue_head(&port->restart_wq); - memset(&port->stats, 0, sizeof(struct net_device_stats)); ret = register_netdev(dev); if (ret) { pr_err("register_netdev failed. ret=%d\n", ret); -- cgit v1.1 From 01e23015a97c46ea376aacde549510b336bd5987 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:00 +0200 Subject: qede: Optimize aggregation information size Driver needs to maintain a structure per-each concurrent possible open aggregation, but the structure storing that metadata is far from being optimized - biggest waste in it is that there are 2 buffer metadata, one for a replacement buffer when the aggregation begins and the other for holding the first aggregation's buffer after it begins [as firmware might still update it]. Those 2 can safely be united into a single metadata structure. struct qede_agg_info changes the following: /* size: 120, cachelines: 2, members: 9 */ /* sum members: 114, holes: 1, sum holes: 4 */ /* padding: 2 */ /* paddings: 2, sum paddings: 8 */ /* last cacheline: 56 bytes */ --> /* size: 48, cachelines: 1, members: 9 */ /* paddings: 1, sum paddings: 4 */ /* last cacheline: 48 bytes */ Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 29 +++++++++---- drivers/net/ethernet/qlogic/qede/qede_main.c | 63 ++++++++++++---------------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 0cba21b..efd6cfe 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -225,15 +225,30 @@ enum qede_agg_state { }; struct qede_agg_info { - struct sw_rx_data replace_buf; - dma_addr_t replace_buf_mapping; - struct sw_rx_data start_buf; - dma_addr_t start_buf_mapping; - struct eth_fast_path_rx_tpa_start_cqe start_cqe; - enum qede_agg_state agg_state; + /* rx_buf is a data buffer that can be placed / consumed from rx bd + * chain. It has two purposes: We will preallocate the data buffer + * for each aggregation when we open the interface and will place this + * buffer on the rx-bd-ring when we receive TPA_START. We don't want + * to be in a state where allocation fails, as we can't reuse the + * consumer buffer in the rx-chain since FW may still be writing to it + * (since header needs to be modified for TPA). + * The second purpose is to keep a pointer to the bd buffer during + * aggregation. + */ + struct sw_rx_data buffer; + dma_addr_t buffer_mapping; + struct sk_buff *skb; - int frag_id; + + /* We need some structs from the start cookie until termination */ u16 vlan_tag; + u16 start_cqe_bd_len; + u8 start_cqe_placement_offset; + + u8 state; + u8 frag_id; + + u8 tunnel_type; }; struct qede_rx_queue { diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index b84a2c4..653be22 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1058,7 +1058,7 @@ static int qede_fill_frag_skb(struct qede_dev *edev, struct qede_agg_info *tpa_info = &rxq->tpa_info[tpa_agg_index]; struct sk_buff *skb = tpa_info->skb; - if (unlikely(tpa_info->agg_state != QEDE_AGG_STATE_START)) + if (unlikely(tpa_info->state != QEDE_AGG_STATE_START)) goto out; /* Add one frag and update the appropriate fields in the skb */ @@ -1084,7 +1084,7 @@ static int qede_fill_frag_skb(struct qede_dev *edev, return 0; out: - tpa_info->agg_state = QEDE_AGG_STATE_ERROR; + tpa_info->state = QEDE_AGG_STATE_ERROR; qede_recycle_rx_bd_ring(rxq, edev, 1); return -ENOMEM; } @@ -1096,8 +1096,8 @@ static void qede_tpa_start(struct qede_dev *edev, struct qede_agg_info *tpa_info = &rxq->tpa_info[cqe->tpa_agg_index]; struct eth_rx_bd *rx_bd_cons = qed_chain_consume(&rxq->rx_bd_ring); struct eth_rx_bd *rx_bd_prod = qed_chain_produce(&rxq->rx_bd_ring); - struct sw_rx_data *replace_buf = &tpa_info->replace_buf; - dma_addr_t mapping = tpa_info->replace_buf_mapping; + struct sw_rx_data *replace_buf = &tpa_info->buffer; + dma_addr_t mapping = tpa_info->buffer_mapping; struct sw_rx_data *sw_rx_data_cons; struct sw_rx_data *sw_rx_data_prod; enum pkt_hash_types rxhash_type; @@ -1122,11 +1122,11 @@ static void qede_tpa_start(struct qede_dev *edev, /* move partial skb from cons to pool (don't unmap yet) * save mapping, incase we drop the packet later on. */ - tpa_info->start_buf = *sw_rx_data_cons; + tpa_info->buffer = *sw_rx_data_cons; mapping = HILO_U64(le32_to_cpu(rx_bd_cons->addr.hi), le32_to_cpu(rx_bd_cons->addr.lo)); - tpa_info->start_buf_mapping = mapping; + tpa_info->buffer_mapping = mapping; rxq->sw_rx_cons++; /* set tpa state to start only if we are able to allocate skb @@ -1137,23 +1137,25 @@ static void qede_tpa_start(struct qede_dev *edev, le16_to_cpu(cqe->len_on_first_bd)); if (unlikely(!tpa_info->skb)) { DP_NOTICE(edev, "Failed to allocate SKB for gro\n"); - tpa_info->agg_state = QEDE_AGG_STATE_ERROR; + tpa_info->state = QEDE_AGG_STATE_ERROR; goto cons_buf; } - skb_put(tpa_info->skb, le16_to_cpu(cqe->len_on_first_bd)); - memcpy(&tpa_info->start_cqe, cqe, sizeof(tpa_info->start_cqe)); - /* Start filling in the aggregation info */ + skb_put(tpa_info->skb, le16_to_cpu(cqe->len_on_first_bd)); tpa_info->frag_id = 0; - tpa_info->agg_state = QEDE_AGG_STATE_START; + tpa_info->state = QEDE_AGG_STATE_START; rxhash = qede_get_rxhash(edev, cqe->bitfields, cqe->rss_hash, &rxhash_type); skb_set_hash(tpa_info->skb, rxhash, rxhash_type); + + /* Store some information from first CQE */ + tpa_info->start_cqe_placement_offset = cqe->placement_offset; + tpa_info->start_cqe_bd_len = le16_to_cpu(cqe->len_on_first_bd); if ((le16_to_cpu(cqe->pars_flags.flags) >> PARSING_AND_ERR_FLAGS_TAG8021QEXIST_SHIFT) & - PARSING_AND_ERR_FLAGS_TAG8021QEXIST_MASK) + PARSING_AND_ERR_FLAGS_TAG8021QEXIST_MASK) tpa_info->vlan_tag = le16_to_cpu(cqe->vlan_tag); else tpa_info->vlan_tag = 0; @@ -1169,7 +1171,7 @@ cons_buf: /* We still need to handle bd_len_list to consume buffers */ if (unlikely(cqe->ext_bd_len_list[1])) { DP_ERR(edev, "Unlikely - got a TPA aggregation with more than one ext_bd_len_list entry in the TPA start\n"); - tpa_info->agg_state = QEDE_AGG_STATE_ERROR; + tpa_info->state = QEDE_AGG_STATE_ERROR; } } @@ -1276,7 +1278,7 @@ static void qede_tpa_end(struct qede_dev *edev, DP_ERR(edev, "Strange - TPA emd with more than a single len_list entry\n"); - if (unlikely(tpa_info->agg_state != QEDE_AGG_STATE_START)) + if (unlikely(tpa_info->state != QEDE_AGG_STATE_START)) goto err; /* Sanity */ @@ -1290,14 +1292,9 @@ static void qede_tpa_end(struct qede_dev *edev, le16_to_cpu(cqe->total_packet_len), skb->len); memcpy(skb->data, - page_address(tpa_info->start_buf.data) + - tpa_info->start_cqe.placement_offset + - tpa_info->start_buf.page_offset, - le16_to_cpu(tpa_info->start_cqe.len_on_first_bd)); - - /* Recycle [mapped] start buffer for the next replacement */ - tpa_info->replace_buf = tpa_info->start_buf; - tpa_info->replace_buf_mapping = tpa_info->start_buf_mapping; + page_address(tpa_info->buffer.data) + + tpa_info->start_cqe_placement_offset + + tpa_info->buffer.page_offset, tpa_info->start_cqe_bd_len); /* Finalize the SKB */ skb->protocol = eth_type_trans(skb, edev->ndev); @@ -1310,18 +1307,11 @@ static void qede_tpa_end(struct qede_dev *edev, qede_gro_receive(edev, fp, skb, tpa_info->vlan_tag); - tpa_info->agg_state = QEDE_AGG_STATE_NONE; + tpa_info->state = QEDE_AGG_STATE_NONE; return; err: - /* The BD starting the aggregation is still mapped; Re-use it for - * future aggregations [as replacement buffer] - */ - memcpy(&tpa_info->replace_buf, &tpa_info->start_buf, - sizeof(struct sw_rx_data)); - tpa_info->replace_buf_mapping = tpa_info->start_buf_mapping; - tpa_info->start_buf.data = NULL; - tpa_info->agg_state = QEDE_AGG_STATE_NONE; + tpa_info->state = QEDE_AGG_STATE_NONE; dev_kfree_skb_any(tpa_info->skb); tpa_info->skb = NULL; } @@ -2823,7 +2813,7 @@ static void qede_free_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq) for (i = 0; i < ETH_TPA_MAX_AGGS_NUM; i++) { struct qede_agg_info *tpa_info = &rxq->tpa_info[i]; - struct sw_rx_data *replace_buf = &tpa_info->replace_buf; + struct sw_rx_data *replace_buf = &tpa_info->buffer; if (replace_buf->data) { dma_unmap_page(&edev->pdev->dev, @@ -2905,7 +2895,7 @@ static int qede_alloc_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq) for (i = 0; i < ETH_TPA_MAX_AGGS_NUM; i++) { struct qede_agg_info *tpa_info = &rxq->tpa_info[i]; - struct sw_rx_data *replace_buf = &tpa_info->replace_buf; + struct sw_rx_data *replace_buf = &tpa_info->buffer; replace_buf->data = alloc_pages(GFP_ATOMIC, 0); if (unlikely(!replace_buf->data)) { @@ -2923,10 +2913,9 @@ static int qede_alloc_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq) } replace_buf->mapping = mapping; - tpa_info->replace_buf.page_offset = 0; - - tpa_info->replace_buf_mapping = mapping; - tpa_info->agg_state = QEDE_AGG_STATE_NONE; + tpa_info->buffer.page_offset = 0; + tpa_info->buffer_mapping = mapping; + tpa_info->state = QEDE_AGG_STATE_NONE; } return 0; -- cgit v1.1 From 6d937acfb3f166f6e10abd978fafafa120d6f0d7 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:01 +0200 Subject: qed: Optimize qed_chain datapath usage The chain structure and functions are widely used by the qed* modules, both for configuration and datapath. E.g., qede's Tx has one such chain and its Rx has two. Currently, the strucutre's fields which are required for datapath related functions [produce/consume] are intertwined with fields which are required only for configuration purposes [init/destroy/etc.]. This patch re-arranges the chain structure so that all the fields which are required for datapath usage could reside in a single cacheline instead of the two which are required today. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 7 +- drivers/net/ethernet/qlogic/qed/qed_sp_commands.c | 4 +- include/linux/qed/qed_chain.h | 144 ++++++++++++---------- 3 files changed, 86 insertions(+), 69 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 5be7b8a..80162ee 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -2283,12 +2283,12 @@ static void qed_chain_free_pbl(struct qed_dev *cdev, struct qed_chain *p_chain) { void **pp_virt_addr_tbl = p_chain->pbl.pp_virt_addr_tbl; u32 page_cnt = p_chain->page_cnt, i, pbl_size; - u8 *p_pbl_virt = p_chain->pbl.p_virt_table; + u8 *p_pbl_virt = p_chain->pbl_sp.p_virt_table; if (!pp_virt_addr_tbl) return; - if (!p_chain->pbl.p_virt_table) + if (!p_pbl_virt) goto out; for (i = 0; i < page_cnt; i++) { @@ -2306,7 +2306,8 @@ static void qed_chain_free_pbl(struct qed_dev *cdev, struct qed_chain *p_chain) pbl_size = page_cnt * QED_CHAIN_PBL_ENTRY_SIZE; dma_free_coherent(&cdev->pdev->dev, pbl_size, - p_chain->pbl.p_virt_table, p_chain->pbl.p_phys_table); + p_chain->pbl_sp.p_virt_table, + p_chain->pbl_sp.p_phys_table); out: vfree(p_chain->pbl.pp_virt_addr_tbl); } diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c index 2888eb0..d0a5828 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c @@ -347,11 +347,11 @@ int qed_sp_pf_start(struct qed_hwfn *p_hwfn, /* Place EQ address in RAMROD */ DMA_REGPAIR_LE(p_ramrod->event_ring_pbl_addr, - p_hwfn->p_eq->chain.pbl.p_phys_table); + p_hwfn->p_eq->chain.pbl_sp.p_phys_table); page_cnt = (u8)qed_chain_get_page_cnt(&p_hwfn->p_eq->chain); p_ramrod->event_ring_num_pages = page_cnt; DMA_REGPAIR_LE(p_ramrod->consolid_q_pbl_addr, - p_hwfn->p_consq->chain.pbl.p_phys_table); + p_hwfn->p_consq->chain.pbl_sp.p_phys_table); qed_tunn_set_pf_start_params(p_hwfn, p_tunn, &p_ramrod->tunnel_config); diff --git a/include/linux/qed/qed_chain.h b/include/linux/qed/qed_chain.h index 72d88cf..37dfba1 100644 --- a/include/linux/qed/qed_chain.h +++ b/include/linux/qed/qed_chain.h @@ -56,23 +56,6 @@ struct qed_chain_pbl_u32 { u32 cons_page_idx; }; -struct qed_chain_pbl { - /* Base address of a pre-allocated buffer for pbl */ - dma_addr_t p_phys_table; - void *p_virt_table; - - /* Table for keeping the virtual addresses of the chain pages, - * respectively to the physical addresses in the pbl table. - */ - void **pp_virt_addr_tbl; - - /* Index to current used page by producer/consumer */ - union { - struct qed_chain_pbl_u16 pbl16; - struct qed_chain_pbl_u32 pbl32; - } u; -}; - struct qed_chain_u16 { /* Cyclic index of next element to produce/consme */ u16 prod_idx; @@ -86,46 +69,78 @@ struct qed_chain_u32 { }; struct qed_chain { - void *p_virt_addr; - dma_addr_t p_phys_addr; - void *p_prod_elem; - void *p_cons_elem; + /* fastpath portion of the chain - required for commands such + * as produce / consume. + */ + /* Point to next element to produce/consume */ + void *p_prod_elem; + void *p_cons_elem; + + /* Fastpath portions of the PBL [if exists] */ + struct { + /* Table for keeping the virtual addresses of the chain pages, + * respectively to the physical addresses in the pbl table. + */ + void **pp_virt_addr_tbl; - enum qed_chain_mode mode; - enum qed_chain_use_mode intended_use; /* used to produce/consume */ - enum qed_chain_cnt_type cnt_type; + union { + struct qed_chain_pbl_u16 u16; + struct qed_chain_pbl_u32 u32; + } c; + } pbl; union { struct qed_chain_u16 chain16; struct qed_chain_u32 chain32; } u; + /* Capacity counts only usable elements */ + u32 capacity; u32 page_cnt; - /* Number of elements - capacity is for usable elements only, - * while size will contain total number of elements [for entire chain]. + enum qed_chain_mode mode; + + /* Elements information for fast calculations */ + u16 elem_per_page; + u16 elem_per_page_mask; + u16 elem_size; + u16 next_page_mask; + u16 usable_per_page; + u8 elem_unusable; + + u8 cnt_type; + + /* Slowpath of the chain - required for initialization and destruction, + * but isn't involved in regular functionality. */ - u32 capacity; + + /* Base address of a pre-allocated buffer for pbl */ + struct { + dma_addr_t p_phys_table; + void *p_virt_table; + } pbl_sp; + + /* Address of first page of the chain - the address is required + * for fastpath operation [consume/produce] but only for the the SINGLE + * flavour which isn't considered fastpath [== SPQ]. + */ + void *p_virt_addr; + dma_addr_t p_phys_addr; + + /* Total number of elements [for entire chain] */ u32 size; - /* Elements information for fast calculations */ - u16 elem_per_page; - u16 elem_per_page_mask; - u16 elem_unusable; - u16 usable_per_page; - u16 elem_size; - u16 next_page_mask; - struct qed_chain_pbl pbl; + u8 intended_use; }; #define QED_CHAIN_PBL_ENTRY_SIZE (8) #define QED_CHAIN_PAGE_SIZE (0x1000) #define ELEMS_PER_PAGE(elem_size) (QED_CHAIN_PAGE_SIZE / (elem_size)) -#define UNUSABLE_ELEMS_PER_PAGE(elem_size, mode) \ - ((mode == QED_CHAIN_MODE_NEXT_PTR) ? \ - (1 + ((sizeof(struct qed_chain_next) - 1) / \ - (elem_size))) : 0) +#define UNUSABLE_ELEMS_PER_PAGE(elem_size, mode) \ + (((mode) == QED_CHAIN_MODE_NEXT_PTR) ? \ + (u8)(1 + ((sizeof(struct qed_chain_next) - 1) / \ + (elem_size))) : 0) #define USABLE_ELEMS_PER_PAGE(elem_size, mode) \ ((u32)(ELEMS_PER_PAGE(elem_size) - \ @@ -186,7 +201,7 @@ static inline u16 qed_chain_get_usable_per_page(struct qed_chain *p_chain) return p_chain->usable_per_page; } -static inline u16 qed_chain_get_unusable_per_page(struct qed_chain *p_chain) +static inline u8 qed_chain_get_unusable_per_page(struct qed_chain *p_chain) { return p_chain->elem_unusable; } @@ -198,7 +213,7 @@ static inline u32 qed_chain_get_page_cnt(struct qed_chain *p_chain) static inline dma_addr_t qed_chain_get_pbl_phys(struct qed_chain *p_chain) { - return p_chain->pbl.p_phys_table; + return p_chain->pbl_sp.p_phys_table; } /** @@ -214,10 +229,10 @@ static inline dma_addr_t qed_chain_get_pbl_phys(struct qed_chain *p_chain) static inline void qed_chain_advance_page(struct qed_chain *p_chain, void **p_next_elem, void *idx_to_inc, void *page_to_inc) - { struct qed_chain_next *p_next = NULL; u32 page_index = 0; + switch (p_chain->mode) { case QED_CHAIN_MODE_NEXT_PTR: p_next = *p_next_elem; @@ -305,7 +320,7 @@ static inline void *qed_chain_produce(struct qed_chain *p_chain) if ((p_chain->u.chain16.prod_idx & p_chain->elem_per_page_mask) == p_chain->next_page_mask) { p_prod_idx = &p_chain->u.chain16.prod_idx; - p_prod_page_idx = &p_chain->pbl.u.pbl16.prod_page_idx; + p_prod_page_idx = &p_chain->pbl.c.u16.prod_page_idx; qed_chain_advance_page(p_chain, &p_chain->p_prod_elem, p_prod_idx, p_prod_page_idx); } @@ -314,7 +329,7 @@ static inline void *qed_chain_produce(struct qed_chain *p_chain) if ((p_chain->u.chain32.prod_idx & p_chain->elem_per_page_mask) == p_chain->next_page_mask) { p_prod_idx = &p_chain->u.chain32.prod_idx; - p_prod_page_idx = &p_chain->pbl.u.pbl32.prod_page_idx; + p_prod_page_idx = &p_chain->pbl.c.u32.prod_page_idx; qed_chain_advance_page(p_chain, &p_chain->p_prod_elem, p_prod_idx, p_prod_page_idx); } @@ -378,7 +393,7 @@ static inline void *qed_chain_consume(struct qed_chain *p_chain) if ((p_chain->u.chain16.cons_idx & p_chain->elem_per_page_mask) == p_chain->next_page_mask) { p_cons_idx = &p_chain->u.chain16.cons_idx; - p_cons_page_idx = &p_chain->pbl.u.pbl16.cons_page_idx; + p_cons_page_idx = &p_chain->pbl.c.u16.cons_page_idx; qed_chain_advance_page(p_chain, &p_chain->p_cons_elem, p_cons_idx, p_cons_page_idx); } @@ -387,8 +402,8 @@ static inline void *qed_chain_consume(struct qed_chain *p_chain) if ((p_chain->u.chain32.cons_idx & p_chain->elem_per_page_mask) == p_chain->next_page_mask) { p_cons_idx = &p_chain->u.chain32.cons_idx; - p_cons_page_idx = &p_chain->pbl.u.pbl32.cons_page_idx; - qed_chain_advance_page(p_chain, &p_chain->p_cons_elem, + p_cons_page_idx = &p_chain->pbl.c.u32.cons_page_idx; + qed_chain_advance_page(p_chain, &p_chain->p_cons_elem, p_cons_idx, p_cons_page_idx); } p_chain->u.chain32.cons_idx++; @@ -429,25 +444,26 @@ static inline void qed_chain_reset(struct qed_chain *p_chain) u32 reset_val = p_chain->page_cnt - 1; if (is_chain_u16(p_chain)) { - p_chain->pbl.u.pbl16.prod_page_idx = (u16)reset_val; - p_chain->pbl.u.pbl16.cons_page_idx = (u16)reset_val; + p_chain->pbl.c.u16.prod_page_idx = (u16)reset_val; + p_chain->pbl.c.u16.cons_page_idx = (u16)reset_val; } else { - p_chain->pbl.u.pbl32.prod_page_idx = reset_val; - p_chain->pbl.u.pbl32.cons_page_idx = reset_val; + p_chain->pbl.c.u32.prod_page_idx = reset_val; + p_chain->pbl.c.u32.cons_page_idx = reset_val; } } switch (p_chain->intended_use) { - case QED_CHAIN_USE_TO_CONSUME_PRODUCE: - case QED_CHAIN_USE_TO_PRODUCE: - /* Do nothing */ - break; - case QED_CHAIN_USE_TO_CONSUME: /* produce empty elements */ for (i = 0; i < p_chain->capacity; i++) qed_chain_recycle_consumed(p_chain); break; + + case QED_CHAIN_USE_TO_CONSUME_PRODUCE: + case QED_CHAIN_USE_TO_PRODUCE: + default: + /* Do nothing */ + break; } } @@ -473,13 +489,13 @@ static inline void qed_chain_init_params(struct qed_chain *p_chain, p_chain->p_virt_addr = NULL; p_chain->p_phys_addr = 0; p_chain->elem_size = elem_size; - p_chain->intended_use = intended_use; + p_chain->intended_use = (u8)intended_use; p_chain->mode = mode; - p_chain->cnt_type = cnt_type; + p_chain->cnt_type = (u8)cnt_type; - p_chain->elem_per_page = ELEMS_PER_PAGE(elem_size); + p_chain->elem_per_page = ELEMS_PER_PAGE(elem_size); p_chain->usable_per_page = USABLE_ELEMS_PER_PAGE(elem_size, mode); - p_chain->elem_per_page_mask = p_chain->elem_per_page - 1; + p_chain->elem_per_page_mask = p_chain->elem_per_page - 1; p_chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(elem_size, mode); p_chain->next_page_mask = (p_chain->usable_per_page & p_chain->elem_per_page_mask); @@ -488,8 +504,8 @@ static inline void qed_chain_init_params(struct qed_chain *p_chain, p_chain->capacity = p_chain->usable_per_page * page_cnt; p_chain->size = p_chain->elem_per_page * page_cnt; - p_chain->pbl.p_phys_table = 0; - p_chain->pbl.p_virt_table = NULL; + p_chain->pbl_sp.p_phys_table = 0; + p_chain->pbl_sp.p_virt_table = NULL; p_chain->pbl.pp_virt_addr_tbl = NULL; } @@ -530,8 +546,8 @@ static inline void qed_chain_init_pbl_mem(struct qed_chain *p_chain, dma_addr_t p_phys_pbl, void **pp_virt_addr_tbl) { - p_chain->pbl.p_phys_table = p_phys_pbl; - p_chain->pbl.p_virt_table = p_virt_pbl; + p_chain->pbl_sp.p_phys_table = p_phys_pbl; + p_chain->pbl_sp.p_virt_table = p_virt_pbl; p_chain->pbl.pp_virt_addr_tbl = pp_virt_addr_tbl; } -- cgit v1.1 From 80439a1704e811697ee01fd09dd95dd10790bc93 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:02 +0200 Subject: qede: Remove 'num_tc'. Driver currently doesn't support multi-CoS, but it contains logic where multiple transmission queues could be theoretically manipulated. No point in maintaining the infrastructure at the moment. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 17 +-- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 40 +++---- drivers/net/ethernet/qlogic/qede/qede_main.c | 132 ++++++++---------------- 3 files changed, 62 insertions(+), 127 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index efd6cfe..b274ccd 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -127,10 +127,9 @@ struct qede_dev { const struct qed_eth_ops *ops; - struct qed_dev_eth_info dev_info; + struct qed_dev_eth_info dev_info; #define QEDE_MAX_RSS_CNT(edev) ((edev)->dev_info.num_queues) -#define QEDE_MAX_TSS_CNT(edev) ((edev)->dev_info.num_queues * \ - (edev)->dev_info.num_tc) +#define QEDE_MAX_TSS_CNT(edev) ((edev)->dev_info.num_queues) struct qede_fastpath *fp_array; u8 req_num_tx; @@ -139,17 +138,9 @@ struct qede_dev { u8 fp_num_rx; u16 req_queues; u16 num_queues; - u8 num_tc; #define QEDE_QUEUE_CNT(edev) ((edev)->num_queues) #define QEDE_RSS_COUNT(edev) ((edev)->num_queues - (edev)->fp_num_tx) -#define QEDE_TSS_COUNT(edev) (((edev)->num_queues - (edev)->fp_num_rx) * \ - (edev)->num_tc) -#define QEDE_TX_IDX(edev, txqidx) ((edev)->fp_num_rx + (txqidx) % \ - QEDE_TSS_COUNT(edev)) -#define QEDE_TC_IDX(edev, txqidx) ((txqidx) / QEDE_TSS_COUNT(edev)) -#define QEDE_TX_QUEUE(edev, txqidx) \ - (&(edev)->fp_array[QEDE_TX_IDX((edev), (txqidx))].txqs[QEDE_TC_IDX(\ - (edev), (txqidx))]) +#define QEDE_TSS_COUNT(edev) ((edev)->num_queues - (edev)->fp_num_rx) struct qed_int_info int_info; unsigned char primary_mac[ETH_ALEN]; @@ -324,7 +315,7 @@ struct qede_fastpath { struct napi_struct napi; struct qed_sb_info *sb_info; struct qede_rx_queue *rxq; - struct qede_tx_queue *txqs; + struct qede_tx_queue *txq; #define VEC_NAME_SIZE (sizeof(((struct net_device *)0)->name) + 8) char name[VEC_NAME_SIZE]; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 8a3debe..a892843 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -59,8 +59,8 @@ static const struct { QEDE_TQSTAT(stopped_cnt), }; -#define QEDE_TQSTATS_DATA(dev, sindex, tssid, tcid) \ - (*((u64 *)(((void *)(&dev->fp_array[tssid].txqs[tcid])) +\ +#define QEDE_TQSTATS_DATA(dev, sindex, tssid) \ + (*((u64 *)(((void *)((dev)->fp_array[tssid].txq)) + \ qede_tqstats_arr[(sindex)].offset))) static const struct { @@ -175,7 +175,6 @@ static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) int i, j, k; for (i = 0, k = 0; i < QEDE_QUEUE_CNT(edev); i++) { - int tc; if (edev->fp_array[i].type & QEDE_FASTPATH_RX) { for (j = 0; j < QEDE_NUM_RQSTATS; j++) @@ -186,14 +185,12 @@ static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) } if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { - for (tc = 0; tc < edev->num_tc; tc++) { - for (j = 0; j < QEDE_NUM_TQSTATS; j++) - sprintf(buf + (k + j) * - ETH_GSTRING_LEN, - "%d.%d: %s", i, tc, - qede_tqstats_arr[j].string); - k += QEDE_NUM_TQSTATS; - } + for (j = 0; j < QEDE_NUM_TQSTATS; j++) + sprintf(buf + (k + j) * + ETH_GSTRING_LEN, + "%d: %s", i, + qede_tqstats_arr[j].string); + k += QEDE_NUM_TQSTATS; } } @@ -240,21 +237,16 @@ static void qede_get_ethtool_stats(struct net_device *dev, mutex_lock(&edev->qede_lock); for (qid = 0; qid < QEDE_QUEUE_CNT(edev); qid++) { - int tc; - if (edev->fp_array[qid].type & QEDE_FASTPATH_RX) { + if (edev->fp_array[qid].type & QEDE_FASTPATH_RX) for (sidx = 0; sidx < QEDE_NUM_RQSTATS; sidx++) buf[cnt++] = QEDE_RQSTATS_DATA(edev, sidx, qid); - } - if (edev->fp_array[qid].type & QEDE_FASTPATH_TX) { - for (tc = 0; tc < edev->num_tc; tc++) { - for (sidx = 0; sidx < QEDE_NUM_TQSTATS; sidx++) - buf[cnt++] = QEDE_TQSTATS_DATA(edev, - sidx, - qid, tc); - } - } + if (edev->fp_array[qid].type & QEDE_FASTPATH_TX) + for (sidx = 0; sidx < QEDE_NUM_TQSTATS; sidx++) + buf[cnt++] = QEDE_TQSTATS_DATA(edev, + sidx, + qid); } for (sidx = 0; sidx < QEDE_NUM_STATS; sidx++) { @@ -281,7 +273,7 @@ static int qede_get_sset_count(struct net_device *dev, int stringset) num_stats--; } return num_stats + QEDE_RSS_COUNT(edev) * QEDE_NUM_RQSTATS + - QEDE_TSS_COUNT(edev) * QEDE_NUM_TQSTATS * edev->num_tc; + QEDE_TSS_COUNT(edev) * QEDE_NUM_TQSTATS; case ETH_SS_PRIV_FLAGS: return QEDE_PRI_FLAG_LEN; case ETH_SS_TEST: @@ -1178,7 +1170,7 @@ static int qede_selftest_transmit_traffic(struct qede_dev *edev, for_each_queue(i) { if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { - txq = edev->fp_array[i].txqs; + txq = edev->fp_array[i].txq; break; } } diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 653be22..2006dd4 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -547,7 +547,7 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, /* Get tx-queue context and netdev index */ txq_index = skb_get_queue_mapping(skb); WARN_ON(txq_index >= QEDE_TSS_COUNT(edev)); - txq = QEDE_TX_QUEUE(edev, txq_index); + txq = edev->fp_array[edev->fp_num_rx + txq_index].txq; netdev_txq = netdev_get_tx_queue(ndev, txq_index); WARN_ON(qed_chain_get_elem_left(&txq->tx_pbl) < (MAX_SKB_FRAGS + 1)); @@ -881,16 +881,6 @@ bool qede_has_rx_work(struct qede_rx_queue *rxq) return hw_comp_cons != sw_comp_cons; } -static bool qede_has_tx_work(struct qede_fastpath *fp) -{ - u8 tc; - - for (tc = 0; tc < fp->edev->num_tc; tc++) - if (qede_txq_has_work(&fp->txqs[tc])) - return true; - return false; -} - static inline void qede_rx_bd_ring_consume(struct qede_rx_queue *rxq) { qed_chain_consume(&rxq->rx_bd_ring); @@ -1633,12 +1623,9 @@ static int qede_poll(struct napi_struct *napi, int budget) napi); struct qede_dev *edev = fp->edev; int rx_work_done = 0; - u8 tc; - for (tc = 0; tc < edev->num_tc; tc++) - if (likely(fp->type & QEDE_FASTPATH_TX) && - qede_txq_has_work(&fp->txqs[tc])) - qede_tx_int(edev, &fp->txqs[tc]); + if (likely(fp->type & QEDE_FASTPATH_TX) && qede_txq_has_work(fp->txq)) + qede_tx_int(edev, fp->txq); rx_work_done = (likely(fp->type & QEDE_FASTPATH_RX) && qede_has_rx_work(fp->rxq)) ? @@ -1664,7 +1651,7 @@ static int qede_poll(struct napi_struct *napi, int budget) if (!((likely(fp->type & QEDE_FASTPATH_RX) && qede_has_rx_work(fp->rxq)) || (likely(fp->type & QEDE_FASTPATH_TX) && - qede_has_tx_work(fp)))) { + qede_txq_has_work(fp->txq)))) { napi_complete(napi); /* Update and reenable interrupts */ @@ -2330,8 +2317,6 @@ static struct qede_dev *qede_alloc_etherdev(struct qed_dev *cdev, memset(&edev->stats, 0, sizeof(edev->stats)); memcpy(&edev->dev_info, info, sizeof(*info)); - edev->num_tc = edev->dev_info.num_tc; - INIT_LIST_HEAD(&edev->vlan_list); return edev; @@ -2429,7 +2414,7 @@ static void qede_free_fp_array(struct qede_dev *edev) kfree(fp->sb_info); kfree(fp->rxq); - kfree(fp->txqs); + kfree(fp->txq); } kfree(edev->fp_array); } @@ -2462,7 +2447,7 @@ static int qede_alloc_fp_array(struct qede_dev *edev) for_each_queue(i) { fp = &edev->fp_array[i]; - fp->sb_info = kcalloc(1, sizeof(*fp->sb_info), GFP_KERNEL); + fp->sb_info = kzalloc(sizeof(*fp->sb_info), GFP_KERNEL); if (!fp->sb_info) { DP_NOTICE(edev, "sb info struct allocation failed\n"); goto err; @@ -2479,22 +2464,15 @@ static int qede_alloc_fp_array(struct qede_dev *edev) } if (fp->type & QEDE_FASTPATH_TX) { - fp->txqs = kcalloc(edev->num_tc, sizeof(*fp->txqs), - GFP_KERNEL); - if (!fp->txqs) { - DP_NOTICE(edev, - "TXQ array allocation failed\n"); + fp->txq = kzalloc(sizeof(*fp->txq), GFP_KERNEL); + if (!fp->txq) goto err; - } } if (fp->type & QEDE_FASTPATH_RX) { - fp->rxq = kcalloc(1, sizeof(*fp->rxq), GFP_KERNEL); - if (!fp->rxq) { - DP_NOTICE(edev, - "RXQ struct allocation failed\n"); + fp->rxq = kzalloc(sizeof(*fp->rxq), GFP_KERNEL); + if (!fp->rxq) goto err; - } } } @@ -3031,16 +3009,13 @@ err: /* This function frees all memory of a single fp */ static void qede_free_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp) { - int tc; - qede_free_mem_sb(edev, fp->sb_info); if (fp->type & QEDE_FASTPATH_RX) qede_free_mem_rxq(edev, fp->rxq); if (fp->type & QEDE_FASTPATH_TX) - for (tc = 0; tc < edev->num_tc; tc++) - qede_free_mem_txq(edev, &fp->txqs[tc]); + qede_free_mem_txq(edev, fp->txq); } /* This function allocates all memory needed for a single fp (i.e. an entity @@ -3048,7 +3023,7 @@ static void qede_free_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp) */ static int qede_alloc_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp) { - int rc, tc; + int rc; rc = qede_alloc_mem_sb(edev, fp->sb_info, fp->id); if (rc) @@ -3061,11 +3036,9 @@ static int qede_alloc_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp) } if (fp->type & QEDE_FASTPATH_TX) { - for (tc = 0; tc < edev->num_tc; tc++) { - rc = qede_alloc_mem_txq(edev, &fp->txqs[tc]); - if (rc) - goto err; - } + rc = qede_alloc_mem_txq(edev, fp->txq); + if (rc) + goto err; } return 0; @@ -3108,7 +3081,7 @@ static int qede_alloc_mem_load(struct qede_dev *edev) /* This function inits fp content and resets the SB, RXQ and TXQ structures */ static void qede_init_fp(struct qede_dev *edev) { - int queue_id, rxq_index = 0, txq_index = 0, tc; + int queue_id, rxq_index = 0, txq_index = 0; struct qede_fastpath *fp; for_each_queue(queue_id) { @@ -3117,25 +3090,15 @@ static void qede_init_fp(struct qede_dev *edev) fp->edev = edev; fp->id = queue_id; - memset((void *)&fp->napi, 0, sizeof(fp->napi)); - - memset((void *)fp->sb_info, 0, sizeof(*fp->sb_info)); if (fp->type & QEDE_FASTPATH_RX) { - memset((void *)fp->rxq, 0, sizeof(*fp->rxq)); fp->rxq->rxq_id = rxq_index++; } if (fp->type & QEDE_FASTPATH_TX) { - memset((void *)fp->txqs, 0, - (edev->num_tc * sizeof(*fp->txqs))); - for (tc = 0; tc < edev->num_tc; tc++) { - fp->txqs[tc].index = txq_index + - tc * QEDE_TSS_COUNT(edev); - if (edev->dev_info.is_legacy) - fp->txqs[tc].is_legacy = true; - } - txq_index++; + fp->txq->index = txq_index++; + if (edev->dev_info.is_legacy) + fp->txq->is_legacy = 1; } snprintf(fp->name, sizeof(fp->name), "%s-fp-%d", @@ -3307,7 +3270,8 @@ static int qede_stop_queues(struct qede_dev *edev) { struct qed_update_vport_params vport_update_params; struct qed_dev *cdev = edev->cdev; - int rc, tc, i; + struct qede_fastpath *fp; + int rc, i; /* Disable the vport */ memset(&vport_update_params, 0, sizeof(vport_update_params)); @@ -3324,16 +3288,12 @@ static int qede_stop_queues(struct qede_dev *edev) /* Flush Tx queues. If needed, request drain from MCP */ for_each_queue(i) { - struct qede_fastpath *fp = &edev->fp_array[i]; + fp = &edev->fp_array[i]; if (fp->type & QEDE_FASTPATH_TX) { - for (tc = 0; tc < edev->num_tc; tc++) { - struct qede_tx_queue *txq = &fp->txqs[tc]; - - rc = qede_drain_txq(edev, txq, true); - if (rc) - return rc; - } + rc = qede_drain_txq(edev, fp->txq, true); + if (rc) + return rc; } } @@ -3341,29 +3301,24 @@ static int qede_stop_queues(struct qede_dev *edev) for (i = QEDE_QUEUE_CNT(edev) - 1; i >= 0; i--) { struct qed_stop_rxq_params rx_params; + fp = &edev->fp_array[i]; + /* Stop the Tx Queue(s) */ - if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { - for (tc = 0; tc < edev->num_tc; tc++) { - struct qed_stop_txq_params tx_params; - u8 val; - - tx_params.rss_id = i; - val = edev->fp_array[i].txqs[tc].index; - tx_params.tx_queue_id = val; + if (fp->type & QEDE_FASTPATH_TX) { + struct qed_stop_txq_params tx_params; + + tx_params.rss_id = i; + tx_params.tx_queue_id = fp->txq->index; rc = edev->ops->q_tx_stop(cdev, &tx_params); - if (rc) { - DP_ERR(edev, "Failed to stop TXQ #%d\n", - tx_params.tx_queue_id); + if (rc) return rc; - } - } } /* Stop the Rx Queue */ - if (edev->fp_array[i].type & QEDE_FASTPATH_RX) { + if (fp->type & QEDE_FASTPATH_RX) { memset(&rx_params, 0, sizeof(rx_params)); rx_params.rss_id = i; - rx_params.rx_queue_id = edev->fp_array[i].rxq->rxq_id; + rx_params.rx_queue_id = fp->rxq->rxq_id; rc = edev->ops->q_rx_stop(cdev, &rx_params); if (rc) { @@ -3383,7 +3338,6 @@ static int qede_stop_queues(struct qede_dev *edev) static int qede_start_queues(struct qede_dev *edev, bool clear_stats) { - int rc, tc, i; int vlan_removal_en = 1; struct qed_dev *cdev = edev->cdev; struct qed_update_vport_params vport_update_params; @@ -3391,6 +3345,7 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) struct qed_dev_info *qed_info = &edev->dev_info.common; struct qed_start_vport_params start = {0}; bool reset_rss_indir = false; + int rc, i; if (!edev->num_queues) { DP_ERR(edev, @@ -3454,11 +3409,8 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) qede_update_rx_prod(edev, rxq); } - if (!(fp->type & QEDE_FASTPATH_TX)) - continue; - - for (tc = 0; tc < edev->num_tc; tc++) { - struct qede_tx_queue *txq = &fp->txqs[tc]; + if (fp->type & QEDE_FASTPATH_TX) { + struct qede_tx_queue *txq = fp->txq; p_phys_table = qed_chain_get_pbl_phys(&txq->tx_pbl); page_cnt = qed_chain_get_page_cnt(&txq->tx_pbl); @@ -3468,7 +3420,7 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) q_params.queue_id = txq->index; q_params.vport_id = 0; q_params.sb = fp->sb_info->igu_sb_id; - q_params.sb_idx = TX_PI(tc); + q_params.sb_idx = TX_PI(0); rc = edev->ops->q_tx_start(cdev, &q_params, p_phys_table, page_cnt, @@ -3480,7 +3432,7 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) } txq->hw_cons_ptr = - &fp->sb_info->sb_virt->pi_array[TX_PI(tc)]; + &fp->sb_info->sb_virt->pi_array[TX_PI(0)]; SET_FIELD(txq->tx_db.data.params, ETH_DB_DATA_DEST, DB_DEST_XCM); SET_FIELD(txq->tx_db.data.params, ETH_DB_DATA_AGG_CMD, @@ -3654,8 +3606,8 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode) rc = qede_alloc_mem_load(edev); if (rc) goto err1; - DP_INFO(edev, "Allocated %d RSS queues on %d TC/s\n", - QEDE_QUEUE_CNT(edev), edev->num_tc); + DP_INFO(edev, "Allocated %d Rx, %d Tx queues\n", + QEDE_RSS_COUNT(edev), QEDE_TSS_COUNT(edev)); rc = qede_set_real_num_queues(edev); if (rc) -- cgit v1.1 From 4dbcd6400244a073f579665a957c6c56c99cc3b6 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:03 +0200 Subject: qede: Refactor statistics gathering Refactor logic for gathering statistics into a per-queue function. This improves readability of the driver statistics' flows. In addition, this would be required by the XDP forwarding queues [as we'll need the Txq statistics gathering methods for those as well]. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 142 ++++++++++++++---------- 1 file changed, 86 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index a892843..d2c23d1 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -16,13 +16,6 @@ #include #include "qede.h" -#define QEDE_STAT_OFFSET(stat_name) (offsetof(struct qede_stats, stat_name)) -#define QEDE_STAT_STRING(stat_name) (#stat_name) -#define _QEDE_STAT(stat_name, pf_only) \ - {QEDE_STAT_OFFSET(stat_name), QEDE_STAT_STRING(stat_name), pf_only} -#define QEDE_PF_STAT(stat_name) _QEDE_STAT(stat_name, true) -#define QEDE_STAT(stat_name) _QEDE_STAT(stat_name, false) - #define QEDE_RQSTAT_OFFSET(stat_name) \ (offsetof(struct qede_rx_queue, stat_name)) #define QEDE_RQSTAT_STRING(stat_name) (#stat_name) @@ -42,9 +35,6 @@ static const struct { }; #define QEDE_NUM_RQSTATS ARRAY_SIZE(qede_rqstats_arr) -#define QEDE_RQSTATS_DATA(dev, sindex, rqindex) \ - (*((u64 *)(((char *)(dev->fp_array[(rqindex)].rxq)) +\ - qede_rqstats_arr[(sindex)].offset))) #define QEDE_TQSTAT_OFFSET(stat_name) \ (offsetof(struct qede_tx_queue, stat_name)) #define QEDE_TQSTAT_STRING(stat_name) (#stat_name) @@ -59,10 +49,12 @@ static const struct { QEDE_TQSTAT(stopped_cnt), }; -#define QEDE_TQSTATS_DATA(dev, sindex, tssid) \ - (*((u64 *)(((void *)((dev)->fp_array[tssid].txq)) + \ - qede_tqstats_arr[(sindex)].offset))) - +#define QEDE_STAT_OFFSET(stat_name) (offsetof(struct qede_stats, stat_name)) +#define QEDE_STAT_STRING(stat_name) (#stat_name) +#define _QEDE_STAT(stat_name, pf_only) \ + {QEDE_STAT_OFFSET(stat_name), QEDE_STAT_STRING(stat_name), pf_only} +#define QEDE_PF_STAT(stat_name) _QEDE_STAT(stat_name, true) +#define QEDE_STAT(stat_name) _QEDE_STAT(stat_name, false) static const struct { u64 offset; char string[ETH_GSTRING_LEN]; @@ -136,10 +128,6 @@ static const struct { QEDE_STAT(coalesced_bytes), }; -#define QEDE_STATS_DATA(dev, index) \ - (*((u64 *)(((char *)(dev)) + offsetof(struct qede_dev, stats) \ - + qede_stats_arr[(index)].offset))) - #define QEDE_NUM_STATS ARRAY_SIZE(qede_stats_arr) enum { @@ -170,36 +158,52 @@ static const char qede_tests_str_arr[QEDE_ETHTOOL_TEST_MAX][ETH_GSTRING_LEN] = { "Nvram (online)\t\t", }; +static void qede_get_strings_stats_txq(struct qede_dev *edev, + struct qede_tx_queue *txq, u8 **buf) +{ + int i; + + for (i = 0; i < QEDE_NUM_TQSTATS; i++) { + sprintf(*buf, "%d: %s", txq->index, + qede_tqstats_arr[i].string); + *buf += ETH_GSTRING_LEN; + } +} + +static void qede_get_strings_stats_rxq(struct qede_dev *edev, + struct qede_rx_queue *rxq, u8 **buf) +{ + int i; + + for (i = 0; i < QEDE_NUM_RQSTATS; i++) { + sprintf(*buf, "%d: %s", rxq->rxq_id, + qede_rqstats_arr[i].string); + *buf += ETH_GSTRING_LEN; + } +} + static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) { - int i, j, k; + struct qede_fastpath *fp; + int i; - for (i = 0, k = 0; i < QEDE_QUEUE_CNT(edev); i++) { + /* Account for queue statistics */ + for (i = 0; i < QEDE_QUEUE_CNT(edev); i++) { + fp = &edev->fp_array[i]; - if (edev->fp_array[i].type & QEDE_FASTPATH_RX) { - for (j = 0; j < QEDE_NUM_RQSTATS; j++) - sprintf(buf + (k + j) * ETH_GSTRING_LEN, - "%d: %s", i, - qede_rqstats_arr[j].string); - k += QEDE_NUM_RQSTATS; - } + if (fp->type & QEDE_FASTPATH_RX) + qede_get_strings_stats_rxq(edev, fp->rxq, &buf); - if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { - for (j = 0; j < QEDE_NUM_TQSTATS; j++) - sprintf(buf + (k + j) * - ETH_GSTRING_LEN, - "%d: %s", i, - qede_tqstats_arr[j].string); - k += QEDE_NUM_TQSTATS; - } + if (fp->type & QEDE_FASTPATH_TX) + qede_get_strings_stats_txq(edev, fp->txq, &buf); } - for (i = 0, j = 0; i < QEDE_NUM_STATS; i++) { + /* Account for non-queue statistics */ + for (i = 0; i < QEDE_NUM_STATS; i++) { if (IS_VF(edev) && qede_stats_arr[i].pf_only) continue; - strcpy(buf + (k + j) * ETH_GSTRING_LEN, - qede_stats_arr[i].string); - j++; + strcpy(buf, qede_stats_arr[i].string); + buf += ETH_GSTRING_LEN; } } @@ -225,34 +229,53 @@ static void qede_get_strings(struct net_device *dev, u32 stringset, u8 *buf) } } +static void qede_get_ethtool_stats_txq(struct qede_tx_queue *txq, u64 **buf) +{ + int i; + + for (i = 0; i < QEDE_NUM_TQSTATS; i++) { + **buf = *((u64 *)(((void *)txq) + qede_tqstats_arr[i].offset)); + (*buf)++; + } +} + +static void qede_get_ethtool_stats_rxq(struct qede_rx_queue *rxq, u64 **buf) +{ + int i; + + for (i = 0; i < QEDE_NUM_RQSTATS; i++) { + **buf = *((u64 *)(((void *)rxq) + qede_rqstats_arr[i].offset)); + (*buf)++; + } +} + static void qede_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *buf) { struct qede_dev *edev = netdev_priv(dev); - int sidx, cnt = 0; - int qid; + struct qede_fastpath *fp; + int i; qede_fill_by_demand_stats(edev); mutex_lock(&edev->qede_lock); + for (i = 0; i < QEDE_QUEUE_CNT(edev); i++) { + fp = &edev->fp_array[i]; - for (qid = 0; qid < QEDE_QUEUE_CNT(edev); qid++) { - - if (edev->fp_array[qid].type & QEDE_FASTPATH_RX) - for (sidx = 0; sidx < QEDE_NUM_RQSTATS; sidx++) - buf[cnt++] = QEDE_RQSTATS_DATA(edev, sidx, qid); + if (fp->type & QEDE_FASTPATH_RX) + qede_get_ethtool_stats_rxq(fp->rxq, &buf); - if (edev->fp_array[qid].type & QEDE_FASTPATH_TX) - for (sidx = 0; sidx < QEDE_NUM_TQSTATS; sidx++) - buf[cnt++] = QEDE_TQSTATS_DATA(edev, - sidx, - qid); + if (fp->type & QEDE_FASTPATH_TX) + qede_get_ethtool_stats_txq(fp->txq, &buf); } - for (sidx = 0; sidx < QEDE_NUM_STATS; sidx++) { - if (IS_VF(edev) && qede_stats_arr[sidx].pf_only) + for (i = 0; i < QEDE_NUM_STATS; i++) { + if (IS_VF(edev) && qede_stats_arr[i].pf_only) continue; - buf[cnt++] = QEDE_STATS_DATA(edev, sidx); + *buf = *((u64 *)(((void *)&edev->stats) + + qede_stats_arr[i].offset)); + + buf++; } mutex_unlock(&edev->qede_lock); @@ -272,8 +295,15 @@ static int qede_get_sset_count(struct net_device *dev, int stringset) if (qede_stats_arr[i].pf_only) num_stats--; } - return num_stats + QEDE_RSS_COUNT(edev) * QEDE_NUM_RQSTATS + - QEDE_TSS_COUNT(edev) * QEDE_NUM_TQSTATS; + + /* Account for the Regular Tx statistics */ + num_stats += QEDE_TSS_COUNT(edev) * QEDE_NUM_TQSTATS; + + /* Account for the Regular Rx statistics */ + num_stats += QEDE_RSS_COUNT(edev) * QEDE_NUM_RQSTATS; + + return num_stats; + case ETH_SS_PRIV_FLAGS: return QEDE_PRI_FLAG_LEN; case ETH_SS_TEST: -- cgit v1.1 From f4fad34c0e45b3e30d2b5312d545e2d416778c7b Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:04 +0200 Subject: qede: Refactor data-path Rx flow Driver's NAPI poll is using a long sequence for processing ingress packets, and it's going to get even longer once we do XDP. Break down the main loop into a series of sub-functions to allow better readability of the function. While we're at it, correct the accounting of the NAPI budget - currently we're counting only packets passed to the stack against the budget, even in case those are actually aggregations. After refactoring every CQE processed would be counted against the budget. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_main.c | 488 +++++++++++++++------------ 1 file changed, 264 insertions(+), 224 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 2006dd4..ac2a5e9 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1021,6 +1021,7 @@ static inline void qede_skb_receive(struct qede_dev *edev, __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); napi_gro_receive(&fp->napi, skb); + fp->rxq->rcv_pkts++; } static void qede_set_gro_params(struct qede_dev *edev, @@ -1383,238 +1384,298 @@ static bool qede_pkt_is_ip_fragmented(struct eth_fast_path_rx_reg_cqe *cqe, return false; } -static int qede_rx_int(struct qede_fastpath *fp, int budget) +static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev, + struct qede_rx_queue *rxq, + struct sw_rx_data *bd, u16 len, + u16 pad) { - struct qede_dev *edev = fp->edev; - struct qede_rx_queue *rxq = fp->rxq; - - u16 hw_comp_cons, sw_comp_cons, sw_rx_index, parse_flag; - int rx_pkt = 0; - u8 csum_flag; + unsigned int offset = bd->page_offset; + struct skb_frag_struct *frag; + struct page *page = bd->data; + unsigned int pull_len; + struct sk_buff *skb; + unsigned char *va; - hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr); - sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring); + /* Allocate a new SKB with a sufficient large header len */ + skb = netdev_alloc_skb(edev->ndev, QEDE_RX_HDR_SIZE); + if (unlikely(!skb)) + return NULL; - /* Memory barrier to prevent the CPU from doing speculative reads of CQE - * / BD in the while-loop before reading hw_comp_cons. If the CQE is - * read before it is written by FW, then FW writes CQE and SB, and then - * the CPU reads the hw_comp_cons, it will use an old CQE. + /* Copy data into SKB - if it's small, we can simply copy it and + * re-use the already allcoated & mapped memory. */ - rmb(); + if (len + pad <= edev->rx_copybreak) { + memcpy(skb_put(skb, len), + page_address(page) + pad + offset, len); + qede_reuse_page(edev, rxq, bd); + goto out; + } - /* Loop to complete all indicated BDs */ - while (sw_comp_cons != hw_comp_cons) { - struct eth_fast_path_rx_reg_cqe *fp_cqe; - enum pkt_hash_types rxhash_type; - enum eth_rx_cqe_type cqe_type; - struct sw_rx_data *sw_rx_data; - union eth_rx_cqe *cqe; - struct sk_buff *skb; - struct page *data; - __le16 flags; - u16 len, pad; - u32 rx_hash; - - /* Get the CQE from the completion ring */ - cqe = (union eth_rx_cqe *) - qed_chain_consume(&rxq->rx_comp_ring); - cqe_type = cqe->fast_path_regular.type; - - if (unlikely(cqe_type == ETH_RX_CQE_TYPE_SLOW_PATH)) { - edev->ops->eth_cqe_completion( - edev->cdev, fp->id, - (struct eth_slow_path_rx_cqe *)cqe); - goto next_cqe; - } + frag = &skb_shinfo(skb)->frags[0]; - if (cqe_type != ETH_RX_CQE_TYPE_REGULAR) { - switch (cqe_type) { - case ETH_RX_CQE_TYPE_TPA_START: - qede_tpa_start(edev, rxq, - &cqe->fast_path_tpa_start); - goto next_cqe; - case ETH_RX_CQE_TYPE_TPA_CONT: - qede_tpa_cont(edev, rxq, - &cqe->fast_path_tpa_cont); - goto next_cqe; - case ETH_RX_CQE_TYPE_TPA_END: - qede_tpa_end(edev, fp, - &cqe->fast_path_tpa_end); - goto next_rx_only; - default: - break; - } - } + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + page, pad + offset, len, rxq->rx_buf_seg_size); - /* Get the data from the SW ring */ - sw_rx_index = rxq->sw_rx_cons & NUM_RX_BDS_MAX; - sw_rx_data = &rxq->sw_rx_ring[sw_rx_index]; - data = sw_rx_data->data; - - fp_cqe = &cqe->fast_path_regular; - len = le16_to_cpu(fp_cqe->len_on_first_bd); - pad = fp_cqe->placement_offset; - flags = cqe->fast_path_regular.pars_flags.flags; - - /* If this is an error packet then drop it */ - parse_flag = le16_to_cpu(flags); - - csum_flag = qede_check_csum(parse_flag); - if (unlikely(csum_flag == QEDE_CSUM_ERROR)) { - if (qede_pkt_is_ip_fragmented(&cqe->fast_path_regular, - parse_flag)) { - rxq->rx_ip_frags++; - goto alloc_skb; - } + va = skb_frag_address(frag); + pull_len = eth_get_headlen(va, QEDE_RX_HDR_SIZE); - DP_NOTICE(edev, - "CQE in CONS = %u has error, flags = %x, dropping incoming packet\n", - sw_comp_cons, parse_flag); - rxq->rx_hw_errors++; - qede_recycle_rx_bd_ring(rxq, edev, fp_cqe->bd_num); - goto next_cqe; - } + /* Align the pull_len to optimize memcpy */ + memcpy(skb->data, va, ALIGN(pull_len, sizeof(long))); -alloc_skb: - skb = netdev_alloc_skb(edev->ndev, QEDE_RX_HDR_SIZE); - if (unlikely(!skb)) { - DP_NOTICE(edev, - "skb allocation failed, dropping incoming packet\n"); - qede_recycle_rx_bd_ring(rxq, edev, fp_cqe->bd_num); - rxq->rx_alloc_errors++; - goto next_cqe; + /* Correct the skb & frag sizes offset after the pull */ + skb_frag_size_sub(frag, pull_len); + frag->page_offset += pull_len; + skb->data_len -= pull_len; + skb->tail += pull_len; + + if (unlikely(qede_realloc_rx_buffer(edev, rxq, bd))) { + /* Incr page ref count to reuse on allocation failure so + * that it doesn't get freed while freeing SKB [as its + * already mapped there]. + */ + page_ref_inc(page); + dev_kfree_skb_any(skb); + return NULL; + } + +out: + /* We've consumed the first BD and prepared an SKB */ + qede_rx_bd_ring_consume(rxq); + return skb; +} + +static int qede_rx_build_jumbo(struct qede_dev *edev, + struct qede_rx_queue *rxq, + struct sk_buff *skb, + struct eth_fast_path_rx_reg_cqe *cqe, + u16 first_bd_len) +{ + u16 pkt_len = le16_to_cpu(cqe->pkt_len); + struct sw_rx_data *bd; + u16 bd_cons_idx; + u8 num_frags; + + pkt_len -= first_bd_len; + + /* We've already used one BD for the SKB. Now take care of the rest */ + for (num_frags = cqe->bd_num - 1; num_frags > 0; num_frags--) { + u16 cur_size = pkt_len > rxq->rx_buf_size ? rxq->rx_buf_size : + pkt_len; + + if (unlikely(!cur_size)) { + DP_ERR(edev, + "Still got %d BDs for mapping jumbo, but length became 0\n", + num_frags); + goto out; } - /* Copy data into SKB */ - if (len + pad <= edev->rx_copybreak) { - memcpy(skb_put(skb, len), - page_address(data) + pad + - sw_rx_data->page_offset, len); - qede_reuse_page(edev, rxq, sw_rx_data); + /* We need a replacement buffer for each BD */ + if (unlikely(qede_alloc_rx_buffer(edev, rxq))) + goto out; + + /* Now that we've allocated the replacement buffer, + * we can safely consume the next BD and map it to the SKB. + */ + bd_cons_idx = rxq->sw_rx_cons & NUM_RX_BDS_MAX; + bd = &rxq->sw_rx_ring[bd_cons_idx]; + qede_rx_bd_ring_consume(rxq); + + dma_unmap_page(&edev->pdev->dev, bd->mapping, + PAGE_SIZE, DMA_FROM_DEVICE); + + skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags++, + bd->data, 0, cur_size); + + skb->truesize += PAGE_SIZE; + skb->data_len += cur_size; + skb->len += cur_size; + pkt_len -= cur_size; + } + + if (unlikely(pkt_len)) + DP_ERR(edev, + "Mapped all BDs of jumbo, but still have %d bytes\n", + pkt_len); + +out: + return num_frags; +} + +static int qede_rx_process_tpa_cqe(struct qede_dev *edev, + struct qede_fastpath *fp, + struct qede_rx_queue *rxq, + union eth_rx_cqe *cqe, + enum eth_rx_cqe_type type) +{ + switch (type) { + case ETH_RX_CQE_TYPE_TPA_START: + qede_tpa_start(edev, rxq, &cqe->fast_path_tpa_start); + return 0; + case ETH_RX_CQE_TYPE_TPA_CONT: + qede_tpa_cont(edev, rxq, &cqe->fast_path_tpa_cont); + return 0; + case ETH_RX_CQE_TYPE_TPA_END: + qede_tpa_end(edev, fp, &cqe->fast_path_tpa_end); + return 1; + default: + return 0; + } +} + +static int qede_rx_process_cqe(struct qede_dev *edev, + struct qede_fastpath *fp, + struct qede_rx_queue *rxq) +{ + struct eth_fast_path_rx_reg_cqe *fp_cqe; + u16 len, pad, bd_cons_idx, parse_flag; + enum pkt_hash_types rxhash_type; + enum eth_rx_cqe_type cqe_type; + union eth_rx_cqe *cqe; + struct sw_rx_data *bd; + struct sk_buff *skb; + __le16 flags; + u8 csum_flag; + u32 rx_hash; + + /* Get the CQE from the completion ring */ + cqe = (union eth_rx_cqe *)qed_chain_consume(&rxq->rx_comp_ring); + cqe_type = cqe->fast_path_regular.type; + + /* Process an unlikely slowpath event */ + if (unlikely(cqe_type == ETH_RX_CQE_TYPE_SLOW_PATH)) { + struct eth_slow_path_rx_cqe *sp_cqe; + + sp_cqe = (struct eth_slow_path_rx_cqe *)cqe; + edev->ops->eth_cqe_completion(edev->cdev, fp->id, sp_cqe); + return 0; + } + + /* Handle TPA cqes */ + if (cqe_type != ETH_RX_CQE_TYPE_REGULAR) + return qede_rx_process_tpa_cqe(edev, fp, rxq, cqe, cqe_type); + + /* Get the data from the SW ring; Consume it only after it's evident + * we wouldn't recycle it. + */ + bd_cons_idx = rxq->sw_rx_cons & NUM_RX_BDS_MAX; + bd = &rxq->sw_rx_ring[bd_cons_idx]; + + fp_cqe = &cqe->fast_path_regular; + len = le16_to_cpu(fp_cqe->len_on_first_bd); + pad = fp_cqe->placement_offset; + + /* If this is an error packet then drop it */ + flags = cqe->fast_path_regular.pars_flags.flags; + parse_flag = le16_to_cpu(flags); + + csum_flag = qede_check_csum(parse_flag); + if (unlikely(csum_flag == QEDE_CSUM_ERROR)) { + if (qede_pkt_is_ip_fragmented(fp_cqe, parse_flag)) { + rxq->rx_ip_frags++; } else { - struct skb_frag_struct *frag; - unsigned int pull_len; - unsigned char *va; - - frag = &skb_shinfo(skb)->frags[0]; - - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, data, - pad + sw_rx_data->page_offset, - len, rxq->rx_buf_seg_size); - - va = skb_frag_address(frag); - pull_len = eth_get_headlen(va, QEDE_RX_HDR_SIZE); - - /* Align the pull_len to optimize memcpy */ - memcpy(skb->data, va, ALIGN(pull_len, sizeof(long))); - - skb_frag_size_sub(frag, pull_len); - frag->page_offset += pull_len; - skb->data_len -= pull_len; - skb->tail += pull_len; - - if (unlikely(qede_realloc_rx_buffer(edev, rxq, - sw_rx_data))) { - DP_ERR(edev, "Failed to allocate rx buffer\n"); - /* Incr page ref count to reuse on allocation - * failure so that it doesn't get freed while - * freeing SKB. - */ - - page_ref_inc(sw_rx_data->data); - rxq->rx_alloc_errors++; - qede_recycle_rx_bd_ring(rxq, edev, - fp_cqe->bd_num); - dev_kfree_skb_any(skb); - goto next_cqe; - } + DP_NOTICE(edev, + "CQE has error, flags = %x, dropping incoming packet\n", + parse_flag); + rxq->rx_hw_errors++; + qede_recycle_rx_bd_ring(rxq, edev, fp_cqe->bd_num); + return 0; } + } - qede_rx_bd_ring_consume(rxq); + /* Basic validation passed; Need to prepare an SKB. This would also + * guarantee to finally consume the first BD upon success. + */ + skb = qede_rx_allocate_skb(edev, rxq, bd, len, pad); + if (!skb) { + rxq->rx_alloc_errors++; + qede_recycle_rx_bd_ring(rxq, edev, fp_cqe->bd_num); + return 0; + } - if (fp_cqe->bd_num != 1) { - u16 pkt_len = le16_to_cpu(fp_cqe->pkt_len); - u8 num_frags; - - pkt_len -= len; - - for (num_frags = fp_cqe->bd_num - 1; num_frags > 0; - num_frags--) { - u16 cur_size = pkt_len > rxq->rx_buf_size ? - rxq->rx_buf_size : pkt_len; - if (unlikely(!cur_size)) { - DP_ERR(edev, - "Still got %d BDs for mapping jumbo, but length became 0\n", - num_frags); - qede_recycle_rx_bd_ring(rxq, edev, - num_frags); - dev_kfree_skb_any(skb); - goto next_cqe; - } - - if (unlikely(qede_alloc_rx_buffer(edev, rxq))) { - qede_recycle_rx_bd_ring(rxq, edev, - num_frags); - dev_kfree_skb_any(skb); - goto next_cqe; - } - - sw_rx_index = rxq->sw_rx_cons & NUM_RX_BDS_MAX; - sw_rx_data = &rxq->sw_rx_ring[sw_rx_index]; - qede_rx_bd_ring_consume(rxq); - - dma_unmap_page(&edev->pdev->dev, - sw_rx_data->mapping, - PAGE_SIZE, DMA_FROM_DEVICE); - - skb_fill_page_desc(skb, - skb_shinfo(skb)->nr_frags++, - sw_rx_data->data, 0, - cur_size); - - skb->truesize += PAGE_SIZE; - skb->data_len += cur_size; - skb->len += cur_size; - pkt_len -= cur_size; - } + /* In case of Jumbo packet, several PAGE_SIZEd buffers will be pointed + * by a single cqe. + */ + if (fp_cqe->bd_num > 1) { + u16 unmapped_frags = qede_rx_build_jumbo(edev, rxq, skb, + fp_cqe, len); - if (unlikely(pkt_len)) - DP_ERR(edev, - "Mapped all BDs of jumbo, but still have %d bytes\n", - pkt_len); + if (unlikely(unmapped_frags > 0)) { + qede_recycle_rx_bd_ring(rxq, edev, unmapped_frags); + dev_kfree_skb_any(skb); + return 0; } + } - skb->protocol = eth_type_trans(skb, edev->ndev); + /* The SKB contains all the data. Now prepare meta-magic */ + skb->protocol = eth_type_trans(skb, edev->ndev); + rx_hash = qede_get_rxhash(edev, fp_cqe->bitfields, + fp_cqe->rss_hash, &rxhash_type); + skb_set_hash(skb, rx_hash, rxhash_type); + qede_set_skb_csum(skb, csum_flag); + skb_record_rx_queue(skb, rxq->rxq_id); - rx_hash = qede_get_rxhash(edev, fp_cqe->bitfields, - fp_cqe->rss_hash, &rxhash_type); + /* SKB is prepared - pass it to stack */ + qede_skb_receive(edev, fp, skb, le16_to_cpu(fp_cqe->vlan_tag)); - skb_set_hash(skb, rx_hash, rxhash_type); + return 1; +} - qede_set_skb_csum(skb, csum_flag); +static int qede_rx_int(struct qede_fastpath *fp, int budget) +{ + struct qede_rx_queue *rxq = fp->rxq; + struct qede_dev *edev = fp->edev; + u16 hw_comp_cons, sw_comp_cons; + int work_done = 0; - skb_record_rx_queue(skb, fp->rxq->rxq_id); + hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr); + sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring); - qede_skb_receive(edev, fp, skb, le16_to_cpu(fp_cqe->vlan_tag)); -next_rx_only: - rx_pkt++; + /* Memory barrier to prevent the CPU from doing speculative reads of CQE + * / BD in the while-loop before reading hw_comp_cons. If the CQE is + * read before it is written by FW, then FW writes CQE and SB, and then + * the CPU reads the hw_comp_cons, it will use an old CQE. + */ + rmb(); -next_cqe: /* don't consume bd rx buffer */ + /* Loop to complete all indicated BDs */ + while ((sw_comp_cons != hw_comp_cons) && (work_done < budget)) { + qede_rx_process_cqe(edev, fp, rxq); qed_chain_recycle_consumed(&rxq->rx_comp_ring); sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring); - /* CR TPA - revisit how to handle budget in TPA perhaps - * increase on "end" - */ - if (rx_pkt == budget) - break; - } /* repeat while sw_comp_cons != hw_comp_cons... */ + work_done++; + } /* Update producers */ qede_update_rx_prod(edev, rxq); - rxq->rcv_pkts += rx_pkt; + return work_done; +} + +static bool qede_poll_is_more_work(struct qede_fastpath *fp) +{ + qed_sb_update_sb_idx(fp->sb_info); - return rx_pkt; + /* *_has_*_work() reads the status block, thus we need to ensure that + * status block indices have been actually read (qed_sb_update_sb_idx) + * prior to this check (*_has_*_work) so that we won't write the + * "newer" value of the status block to HW (if there was a DMA right + * after qede_has_rx_work and if there is no rmb, the memory reading + * (qed_sb_update_sb_idx) may be postponed to right before *_ack_sb). + * In this case there will never be another interrupt until there is + * another update of the status block, while there is still unhandled + * work. + */ + rmb(); + + if (likely(fp->type & QEDE_FASTPATH_RX)) + if (qede_has_rx_work(fp->rxq)) + return true; + + if (likely(fp->type & QEDE_FASTPATH_TX)) + if (qede_txq_has_work(fp->txq)) + return true; + + return false; } static int qede_poll(struct napi_struct *napi, int budget) @@ -1631,32 +1692,11 @@ static int qede_poll(struct napi_struct *napi, int budget) qede_has_rx_work(fp->rxq)) ? qede_rx_int(fp, budget) : 0; if (rx_work_done < budget) { - qed_sb_update_sb_idx(fp->sb_info); - /* *_has_*_work() reads the status block, - * thus we need to ensure that status block indices - * have been actually read (qed_sb_update_sb_idx) - * prior to this check (*_has_*_work) so that - * we won't write the "newer" value of the status block - * to HW (if there was a DMA right after - * qede_has_rx_work and if there is no rmb, the memory - * reading (qed_sb_update_sb_idx) may be postponed - * to right before *_ack_sb). In this case there - * will never be another interrupt until there is - * another update of the status block, while there - * is still unhandled work. - */ - rmb(); - - /* Fall out from the NAPI loop if needed */ - if (!((likely(fp->type & QEDE_FASTPATH_RX) && - qede_has_rx_work(fp->rxq)) || - (likely(fp->type & QEDE_FASTPATH_TX) && - qede_txq_has_work(fp->txq)))) { + if (!qede_poll_is_more_work(fp)) { napi_complete(napi); /* Update and reenable interrupts */ - qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, - 1 /*update*/); + qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, 1); } else { rx_work_done = budget; } -- cgit v1.1 From 567b3c127a79277bac31a9609734b355d30e7905 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:05 +0200 Subject: qede: Revise state locking scheme As qede utilizes an internal-reload sequence as result of various configuration changes, the netif state wouldn't always accurately describe the status of the configuration. To compensate, we're storing an internal state of the device, which should only be accessed under the qede_lock. This patch fixes and improves several state/lock interactions: - The internal state should only be checked while locked. - While holding lock, it's preferable to check state rather than the netdevice's state. - The reload sequence is not 'atomic' - unload and subsequent load are not in the same critical section. This also add the 'locked' variant for the reload, which would later be used by XDP - useful in the case where the correct sequence is 'lock, check state and re-configure if good', instead of allowing the reload itself to make the decision regarding the configurability of the device. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 14 ++- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 37 +++--- drivers/net/ethernet/qlogic/qede/qede_main.c | 146 +++++++++++++++--------- 3 files changed, 122 insertions(+), 75 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index b274ccd..2116c4c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -338,8 +338,12 @@ struct qede_fastpath { #define QEDE_SP_VXLAN_PORT_CONFIG 2 #define QEDE_SP_GENEVE_PORT_CONFIG 3 -union qede_reload_args { - u16 mtu; +struct qede_reload_args { + void (*func)(struct qede_dev *edev, struct qede_reload_args *args); + union { + netdev_features_t features; + u16 mtu; + } u; }; #ifdef CONFIG_DCB @@ -348,11 +352,11 @@ void qede_set_dcbnl_ops(struct net_device *ndev); void qede_config_debug(uint debug, u32 *p_dp_module, u8 *p_dp_level); void qede_set_ethtool_ops(struct net_device *netdev); void qede_reload(struct qede_dev *edev, - void (*func)(struct qede_dev *edev, - union qede_reload_args *args), - union qede_reload_args *args); + struct qede_reload_args *args, bool is_locked); int qede_change_mtu(struct net_device *dev, int new_mtu); void qede_fill_by_demand_stats(struct qede_dev *edev); +void __qede_lock(struct qede_dev *edev); +void __qede_unlock(struct qede_dev *edev); bool qede_has_rx_work(struct qede_rx_queue *rxq); int qede_txq_has_work(struct qede_tx_queue *txq); void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, struct qede_dev *edev, diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index d2c23d1..ef8c327 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -258,7 +258,9 @@ static void qede_get_ethtool_stats(struct net_device *dev, qede_fill_by_demand_stats(edev); - mutex_lock(&edev->qede_lock); + /* Need to protect the access to the fastpath array */ + __qede_lock(edev); + for (i = 0; i < QEDE_QUEUE_CNT(edev); i++) { fp = &edev->fp_array[i]; @@ -278,7 +280,7 @@ static void qede_get_ethtool_stats(struct net_device *dev, buf++; } - mutex_unlock(&edev->qede_lock); + __qede_unlock(edev); } static int qede_get_sset_count(struct net_device *dev, int stringset) @@ -374,6 +376,8 @@ static int qede_get_link_ksettings(struct net_device *dev, struct qede_dev *edev = netdev_priv(dev); struct qed_link_output current_link; + __qede_lock(edev); + memset(¤t_link, 0, sizeof(current_link)); edev->ops->common->get_link(edev->cdev, ¤t_link); @@ -393,6 +397,9 @@ static int qede_get_link_ksettings(struct net_device *dev, base->speed = SPEED_UNKNOWN; base->duplex = DUPLEX_UNKNOWN; } + + __qede_unlock(edev); + base->port = current_link.port; base->autoneg = (current_link.autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE; @@ -701,8 +708,7 @@ static int qede_set_ringparam(struct net_device *dev, edev->q_num_rx_buffers = ering->rx_pending; edev->q_num_tx_buffers = ering->tx_pending; - if (netif_running(edev->ndev)) - qede_reload(edev, NULL, NULL); + qede_reload(edev, NULL, false); return 0; } @@ -787,29 +793,27 @@ static int qede_get_regs_len(struct net_device *ndev) return -EINVAL; } -static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args) +static void qede_update_mtu(struct qede_dev *edev, + struct qede_reload_args *args) { - edev->ndev->mtu = args->mtu; + edev->ndev->mtu = args->u.mtu; } /* Netdevice NDOs */ int qede_change_mtu(struct net_device *ndev, int new_mtu) { struct qede_dev *edev = netdev_priv(ndev); - union qede_reload_args args; + struct qede_reload_args args; DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), "Configuring MTU size of %d\n", new_mtu); - /* Set the mtu field and re-start the interface if needed*/ - args.mtu = new_mtu; - - if (netif_running(edev->ndev)) - qede_reload(edev, &qede_update_mtu, &args); - - qede_update_mtu(edev, &args); + /* Set the mtu field and re-start the interface if needed */ + args.u.mtu = new_mtu; + args.func = &qede_update_mtu; + qede_reload(edev, &args, false); - edev->ops->common->update_mtu(edev->cdev, args.mtu); + edev->ops->common->update_mtu(edev->cdev, new_mtu); return 0; } @@ -893,8 +897,7 @@ static int qede_set_channels(struct net_device *dev, sizeof(edev->rss_params.rss_ind_table)); } - if (netif_running(dev)) - qede_reload(edev, NULL, NULL); + qede_reload(edev, NULL, false); return 0; } diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index ac2a5e9..64c7f3b 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -100,6 +100,19 @@ static int qede_alloc_rx_buffer(struct qede_dev *edev, struct qede_rx_queue *rxq); static void qede_link_update(void *dev, struct qed_link_output *link); +/* The qede lock is used to protect driver state change and driver flows that + * are not reentrant. + */ +void __qede_lock(struct qede_dev *edev) +{ + mutex_lock(&edev->qede_lock); +} + +void __qede_unlock(struct qede_dev *edev) +{ + mutex_unlock(&edev->qede_lock); +} + #ifdef CONFIG_QED_SRIOV static int qede_set_vf_vlan(struct net_device *ndev, int vf, u16 vlan, u8 qos, __be16 vlan_proto) @@ -1952,7 +1965,7 @@ static int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { struct qede_dev *edev = netdev_priv(dev); struct qede_vlan *vlan, *tmp; - int rc; + int rc = 0; DP_VERBOSE(edev, NETIF_MSG_IFUP, "Adding vlan 0x%04x\n", vid); @@ -1976,6 +1989,7 @@ static int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) } /* If interface is down, cache this VLAN ID and return */ + __qede_lock(edev); if (edev->state != QEDE_STATE_OPEN) { DP_VERBOSE(edev, NETIF_MSG_IFDOWN, "Interface is down, VLAN %d will be configured when interface is up\n", @@ -1983,8 +1997,7 @@ static int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) if (vid != 0) edev->non_configured_vlans++; list_add(&vlan->list, &edev->vlan_list); - - return 0; + goto out; } /* Check for the filter limit. @@ -2000,7 +2013,7 @@ static int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) DP_ERR(edev, "Failed to configure VLAN %d\n", vlan->vid); kfree(vlan); - return -EINVAL; + goto out; } vlan->configured = true; @@ -2017,7 +2030,9 @@ static int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) list_add(&vlan->list, &edev->vlan_list); - return 0; +out: + __qede_unlock(edev); + return rc; } static void qede_del_vlan_from_list(struct qede_dev *edev, @@ -2094,11 +2109,12 @@ static int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) { struct qede_dev *edev = netdev_priv(dev); struct qede_vlan *vlan = NULL; - int rc; + int rc = 0; DP_VERBOSE(edev, NETIF_MSG_IFDOWN, "Removing vlan 0x%04x\n", vid); /* Find whether entry exists */ + __qede_lock(edev); list_for_each_entry(vlan, &edev->vlan_list, list) if (vlan->vid == vid) break; @@ -2106,7 +2122,7 @@ static int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) if (!vlan || (vlan->vid != vid)) { DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), "Vlan isn't configured\n"); - return 0; + goto out; } if (edev->state != QEDE_STATE_OPEN) { @@ -2116,7 +2132,7 @@ static int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) DP_VERBOSE(edev, NETIF_MSG_IFDOWN, "Interface is down, removing VLAN from list only\n"); qede_del_vlan_from_list(edev, vlan); - return 0; + goto out; } /* Remove vlan */ @@ -2125,7 +2141,7 @@ static int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) vid); if (rc) { DP_ERR(edev, "Failed to remove VLAN %d\n", vid); - return -EINVAL; + goto out; } } @@ -2136,6 +2152,8 @@ static int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) */ rc = qede_configure_vlan_filters(edev); +out: + __qede_unlock(edev); return rc; } @@ -2165,7 +2183,13 @@ static void qede_vlan_mark_nonconfigured(struct qede_dev *edev) edev->accept_any_vlan = false; } -static int qede_set_features(struct net_device *dev, netdev_features_t features) +static void qede_set_features_reload(struct qede_dev *edev, + struct qede_reload_args *args) +{ + edev->ndev->features = args->u.features; +} + +int qede_set_features(struct net_device *dev, netdev_features_t features) { struct qede_dev *edev = netdev_priv(dev); netdev_features_t changes = features ^ dev->features; @@ -2179,9 +2203,14 @@ static int qede_set_features(struct net_device *dev, netdev_features_t features) need_reload = edev->gro_disable; } - if (need_reload && netif_running(edev->ndev)) { - dev->features = features; - qede_reload(edev, NULL, NULL); + if (need_reload) { + struct qede_reload_args args; + + args.u.features = features; + args.func = &qede_set_features_reload; + + qede_reload(edev, &args, false); + return 1; } @@ -2528,12 +2557,11 @@ static void qede_sp_task(struct work_struct *work) sp_task.work); struct qed_dev *cdev = edev->cdev; - mutex_lock(&edev->qede_lock); + __qede_lock(edev); - if (edev->state == QEDE_STATE_OPEN) { - if (test_and_clear_bit(QEDE_SP_RX_MODE, &edev->sp_flags)) + if (test_and_clear_bit(QEDE_SP_RX_MODE, &edev->sp_flags)) + if (edev->state == QEDE_STATE_OPEN) qede_config_rx_mode(edev->ndev); - } if (test_and_clear_bit(QEDE_SP_VXLAN_PORT_CONFIG, &edev->sp_flags)) { struct qed_tunn_params tunn_params; @@ -2553,7 +2581,7 @@ static void qede_sp_task(struct work_struct *work) qed_ops->tunn_config(cdev, &tunn_params); } - mutex_unlock(&edev->qede_lock); + __qede_unlock(edev); } static void qede_update_pf_params(struct qed_dev *cdev) @@ -3576,15 +3604,18 @@ enum qede_unload_mode { QEDE_UNLOAD_NORMAL, }; -static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode) +static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode, + bool is_locked) { struct qed_link_params link_params; int rc; DP_INFO(edev, "Starting qede unload\n"); + if (!is_locked) + __qede_lock(edev); + qede_roce_dev_event_close(edev); - mutex_lock(&edev->qede_lock); edev->state = QEDE_STATE_CLOSED; /* Close OS Tx */ @@ -3616,7 +3647,8 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode) qede_free_fp_array(edev); out: - mutex_unlock(&edev->qede_lock); + if (!is_locked) + __qede_unlock(edev); DP_INFO(edev, "Ending qede unload\n"); } @@ -3625,7 +3657,8 @@ enum qede_load_mode { QEDE_LOAD_RELOAD, }; -static int qede_load(struct qede_dev *edev, enum qede_load_mode mode) +static int qede_load(struct qede_dev *edev, enum qede_load_mode mode, + bool is_locked) { struct qed_link_params link_params; struct qed_link_output link_output; @@ -3633,13 +3666,16 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode) DP_INFO(edev, "Starting qede load\n"); + if (!is_locked) + __qede_lock(edev); + rc = qede_set_num_queues(edev); if (rc) - goto err0; + goto out; rc = qede_alloc_fp_array(edev); if (rc) - goto err0; + goto out; qede_init_fp(edev); @@ -3669,10 +3705,6 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode) /* Add primary mac and set Rx filters */ ether_addr_copy(edev->primary_mac, edev->ndev->dev_addr); - mutex_lock(&edev->qede_lock); - edev->state = QEDE_STATE_OPEN; - mutex_unlock(&edev->qede_lock); - /* Program un-configured VLANs */ qede_configure_vlan_filters(edev); @@ -3687,10 +3719,12 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode) qede_roce_dev_event_open(edev); qede_link_update(edev, &link_output); + edev->state = QEDE_STATE_OPEN; + DP_INFO(edev, "Ending successfully qede load\n"); - return 0; + goto out; err4: qede_sync_free_irqs(edev); memset(&edev->int_info.msix_cnt, 0, sizeof(struct qed_int_info)); @@ -3704,26 +3738,40 @@ err1: edev->num_queues = 0; edev->fp_num_tx = 0; edev->fp_num_rx = 0; -err0: +out: + if (!is_locked) + __qede_unlock(edev); + return rc; } +/* 'func' should be able to run between unload and reload assuming interface + * is actually running, or afterwards in case it's currently DOWN. + */ void qede_reload(struct qede_dev *edev, - void (*func)(struct qede_dev *, union qede_reload_args *), - union qede_reload_args *args) + struct qede_reload_args *args, bool is_locked) { - qede_unload(edev, QEDE_UNLOAD_NORMAL); - /* Call function handler to update parameters - * needed for function load. + if (!is_locked) + __qede_lock(edev); + + /* Since qede_lock is held, internal state wouldn't change even + * if netdev state would start transitioning. Check whether current + * internal configuration indicates device is up, then reload. */ - if (func) - func(edev, args); + if (edev->state == QEDE_STATE_OPEN) { + qede_unload(edev, QEDE_UNLOAD_NORMAL, true); + if (args) + args->func(edev, args); + qede_load(edev, QEDE_LOAD_RELOAD, true); - qede_load(edev, QEDE_LOAD_RELOAD); + /* Since no one is going to do it for us, re-configure */ + qede_config_rx_mode(edev->ndev); + } else if (args) { + args->func(edev, args); + } - mutex_lock(&edev->qede_lock); - qede_config_rx_mode(edev->ndev); - mutex_unlock(&edev->qede_lock); + if (!is_locked) + __qede_unlock(edev); } /* called with rtnl_lock */ @@ -3736,8 +3784,7 @@ static int qede_open(struct net_device *ndev) edev->ops->common->set_power_state(edev->cdev, PCI_D0); - rc = qede_load(edev, QEDE_LOAD_NORMAL); - + rc = qede_load(edev, QEDE_LOAD_NORMAL, false); if (rc) return rc; @@ -3752,7 +3799,7 @@ static int qede_close(struct net_device *ndev) { struct qede_dev *edev = netdev_priv(ndev); - qede_unload(edev, QEDE_UNLOAD_NORMAL); + qede_unload(edev, QEDE_UNLOAD_NORMAL, false); edev->ops->common->update_drv_state(edev->cdev, false); @@ -3884,15 +3931,8 @@ static void qede_set_rx_mode(struct net_device *ndev) { struct qede_dev *edev = netdev_priv(ndev); - DP_INFO(edev, "qede_set_rx_mode called\n"); - - if (edev->state != QEDE_STATE_OPEN) { - DP_INFO(edev, - "qede_set_rx_mode called while interface is down\n"); - } else { - set_bit(QEDE_SP_RX_MODE, &edev->sp_flags); - schedule_delayed_work(&edev->sp_task, 0); - } + set_bit(QEDE_SP_RX_MODE, &edev->sp_flags); + schedule_delayed_work(&edev->sp_task, 0); } /* Must be called with qede_lock held */ -- cgit v1.1 From 3da7a37ae6886cfba9ef35428eb976fc2ef561fa Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:06 +0200 Subject: qed*: Handle-based L2-queues. The driver needs to maintain several FW/HW-indices for each one of its queues. Currently, that mapping is done by the QED where it uses an rx/tx array of so-called hw-cids, populating them whenever a new queue is opened and clearing them upon destruction of said queues. This maintenance is far from ideal - there's no real reason why QED needs to maintain such a data-structure. It becomes even worse when considering the fact that the PF's queues and its child VFs' queues are all mapped into the same data-structure. As a by-product, the set of parameters an interface needs to supply for queue APIs is non-trivial, and some of the variables in the API structures have different meaning depending on their exact place in the configuration flow. This patch re-organizes the way L2 queues are configured and maintained. In short: - Required parameters for queue init are now well-defined. - Qed would allocate a queue-cid based on parameters. Upon initialization success, it would return a handle to caller. - Queue-handle would be maintained by entity requesting queue-init, not necessarily qed. - All further queue-APIs [update, destroy] would use the opaque handle as reference for the queue instead of various indices. The possible owners of such handles: - PF queues [qede] - complete handles based on provided configuration. - VF queues [qede] - fw-context-less handles, containing only relative information; Only the PF-side would need the absolute indices for configuration, so they're omitted here. - VF queues [qed, PF-side] - complete handles based on VF initialization. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 12 - drivers/net/ethernet/qlogic/qed/qed_dev.c | 26 -- drivers/net/ethernet/qlogic/qed/qed_l2.c | 595 +++++++++++++++------------ drivers/net/ethernet/qlogic/qed/qed_l2.h | 133 ++++-- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 275 +++++++++---- drivers/net/ethernet/qlogic/qed/qed_sriov.h | 21 +- drivers/net/ethernet/qlogic/qed/qed_vf.c | 90 ++-- drivers/net/ethernet/qlogic/qed/qed_vf.h | 40 +- drivers/net/ethernet/qlogic/qede/qede.h | 4 + drivers/net/ethernet/qlogic/qede/qede_main.c | 109 ++--- include/linux/qed/qed_eth_if.h | 56 +-- 11 files changed, 791 insertions(+), 570 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 50b8a01..244dd40 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -241,15 +241,6 @@ struct qed_hw_info { enum qed_wol_support b_wol_support; }; -struct qed_hw_cid_data { - u32 cid; - bool b_cid_allocated; - - /* Additional identifiers */ - u16 opaque_fid; - u8 vport_id; -}; - /* maximun size of read/write commands (HW limit) */ #define DMAE_MAX_RW_SIZE 0x2000 @@ -416,9 +407,6 @@ struct qed_hwfn { struct qed_dcbx_info *p_dcbx_info; - struct qed_hw_cid_data *p_tx_cids; - struct qed_hw_cid_data *p_rx_cids; - struct qed_dmae_info dmae_info; /* QM init */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 80162ee..00b9a67 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -137,15 +137,6 @@ void qed_resc_free(struct qed_dev *cdev) for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; - kfree(p_hwfn->p_tx_cids); - p_hwfn->p_tx_cids = NULL; - kfree(p_hwfn->p_rx_cids); - p_hwfn->p_rx_cids = NULL; - } - - for_each_hwfn(cdev, i) { - struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; - qed_cxt_mngr_free(p_hwfn); qed_qm_info_free(p_hwfn); qed_spq_free(p_hwfn); @@ -425,23 +416,6 @@ int qed_resc_alloc(struct qed_dev *cdev) if (!cdev->fw_data) return -ENOMEM; - /* Allocate Memory for the Queue->CID mapping */ - for_each_hwfn(cdev, i) { - struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; - int tx_size = sizeof(struct qed_hw_cid_data) * - RESC_NUM(p_hwfn, QED_L2_QUEUE); - int rx_size = sizeof(struct qed_hw_cid_data) * - RESC_NUM(p_hwfn, QED_L2_QUEUE); - - p_hwfn->p_tx_cids = kzalloc(tx_size, GFP_KERNEL); - if (!p_hwfn->p_tx_cids) - goto alloc_no_mem; - - p_hwfn->p_rx_cids = kzalloc(rx_size, GFP_KERNEL); - if (!p_hwfn->p_rx_cids) - goto alloc_no_mem; - } - for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; u32 n_eqes, num_cons; diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 900b253..6a3727c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "qed.h" #include #include "qed_cxt.h" @@ -41,6 +42,124 @@ #define QED_MAX_SGES_NUM 16 #define CRC32_POLY 0x1edc6f41 +void qed_eth_queue_cid_release(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid) +{ + /* VFs' CIDs are 0-based in PF-view, and uninitialized on VF */ + if (!p_cid->is_vf && IS_PF(p_hwfn->cdev)) + qed_cxt_release_cid(p_hwfn, p_cid->cid); + vfree(p_cid); +} + +/* The internal is only meant to be directly called by PFs initializeing CIDs + * for their VFs. + */ +struct qed_queue_cid * +_qed_eth_queue_to_cid(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + u32 cid, + u8 vf_qid, + struct qed_queue_start_common_params *p_params) +{ + bool b_is_same = (p_hwfn->hw_info.opaque_fid == opaque_fid); + struct qed_queue_cid *p_cid; + int rc; + + p_cid = vmalloc(sizeof(*p_cid)); + if (!p_cid) + return NULL; + memset(p_cid, 0, sizeof(*p_cid)); + + p_cid->opaque_fid = opaque_fid; + p_cid->cid = cid; + p_cid->vf_qid = vf_qid; + p_cid->rel = *p_params; + + /* Don't try calculating the absolute indices for VFs */ + if (IS_VF(p_hwfn->cdev)) { + p_cid->abs = p_cid->rel; + goto out; + } + + /* Calculate the engine-absolute indices of the resources. + * This would guarantee they're valid later on. + * In some cases [SBs] we already have the right values. + */ + rc = qed_fw_vport(p_hwfn, p_cid->rel.vport_id, &p_cid->abs.vport_id); + if (rc) + goto fail; + + rc = qed_fw_l2_queue(p_hwfn, p_cid->rel.queue_id, &p_cid->abs.queue_id); + if (rc) + goto fail; + + /* In case of a PF configuring its VF's queues, the stats-id is already + * absolute [since there's a single index that's suitable per-VF]. + */ + if (b_is_same) { + rc = qed_fw_vport(p_hwfn, p_cid->rel.stats_id, + &p_cid->abs.stats_id); + if (rc) + goto fail; + } else { + p_cid->abs.stats_id = p_cid->rel.stats_id; + } + + /* SBs relevant information was already provided as absolute */ + p_cid->abs.sb = p_cid->rel.sb; + p_cid->abs.sb_idx = p_cid->rel.sb_idx; + + /* This is tricky - we're actually interested in whehter this is a PF + * entry meant for the VF. + */ + if (!b_is_same) + p_cid->is_vf = true; +out: + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "opaque_fid: %04x CID %08x vport %02x [%02x] qzone %04x [%04x] stats %02x [%02x] SB %04x PI %02x\n", + p_cid->opaque_fid, + p_cid->cid, + p_cid->rel.vport_id, + p_cid->abs.vport_id, + p_cid->rel.queue_id, + p_cid->abs.queue_id, + p_cid->rel.stats_id, + p_cid->abs.stats_id, p_cid->abs.sb, p_cid->abs.sb_idx); + + return p_cid; + +fail: + vfree(p_cid); + return NULL; +} + +static struct qed_queue_cid *qed_eth_queue_to_cid(struct qed_hwfn *p_hwfn, + u16 opaque_fid, struct + qed_queue_start_common_params + *p_params) +{ + struct qed_queue_cid *p_cid; + u32 cid = 0; + + /* Get a unique firmware CID for this queue, in case it's a PF. + * VF's don't need a CID as the queue configuration will be done + * by PF. + */ + if (IS_PF(p_hwfn->cdev)) { + if (qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_ETH, &cid)) { + DP_NOTICE(p_hwfn, "Failed to acquire cid\n"); + return NULL; + } + } + + p_cid = _qed_eth_queue_to_cid(p_hwfn, opaque_fid, cid, 0, p_params); + if (!p_cid && IS_PF(p_hwfn->cdev)) + qed_cxt_release_cid(p_hwfn, cid); + + return p_cid; +} + int qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn, struct qed_sp_vport_start_params *p_params) { @@ -496,61 +615,26 @@ static int qed_filter_accept_cmd(struct qed_dev *cdev, return 0; } -static int qed_sp_release_queue_cid( - struct qed_hwfn *p_hwfn, - struct qed_hw_cid_data *p_cid_data) -{ - if (!p_cid_data->b_cid_allocated) - return 0; - - qed_cxt_release_cid(p_hwfn, p_cid_data->cid); - - p_cid_data->b_cid_allocated = false; - - return 0; -} - -int qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - u32 cid, - struct qed_queue_start_common_params *p_params, - u8 stats_id, - u16 bd_max_bytes, - dma_addr_t bd_chain_phys_addr, - dma_addr_t cqe_pbl_addr, - u16 cqe_pbl_size, bool b_use_zone_a_prod) +int qed_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size) { struct rx_queue_start_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - struct qed_hw_cid_data *p_rx_cid; - u16 abs_rx_q_id = 0; - u8 abs_vport_id = 0; int rc = -EINVAL; - /* Store information for the stop */ - p_rx_cid = &p_hwfn->p_rx_cids[p_params->queue_id]; - p_rx_cid->cid = cid; - p_rx_cid->opaque_fid = opaque_fid; - p_rx_cid->vport_id = p_params->vport_id; - - rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_vport_id); - if (rc) - return rc; - - rc = qed_fw_l2_queue(p_hwfn, p_params->queue_id, &abs_rx_q_id); - if (rc) - return rc; - DP_VERBOSE(p_hwfn, QED_MSG_SP, - "opaque_fid=0x%x, cid=0x%x, rx_qid=0x%x, vport_id=0x%x, sb_id=0x%x\n", - opaque_fid, - cid, p_params->queue_id, p_params->vport_id, p_params->sb); + "opaque_fid=0x%x, cid=0x%x, rx_qzone=0x%x, vport_id=0x%x, sb_id=0x%x\n", + p_cid->opaque_fid, p_cid->cid, + p_cid->abs.queue_id, p_cid->abs.vport_id, p_cid->abs.sb); /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); - init_data.cid = cid; - init_data.opaque_fid = opaque_fid; + init_data.cid = p_cid->cid; + init_data.opaque_fid = p_cid->opaque_fid; init_data.comp_mode = QED_SPQ_MODE_EBLOCK; rc = qed_sp_init_request(p_hwfn, &p_ent, @@ -561,11 +645,11 @@ int qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, p_ramrod = &p_ent->ramrod.rx_queue_start; - p_ramrod->sb_id = cpu_to_le16(p_params->sb); - p_ramrod->sb_index = p_params->sb_idx; - p_ramrod->vport_id = abs_vport_id; - p_ramrod->stats_counter_id = stats_id; - p_ramrod->rx_queue_id = cpu_to_le16(abs_rx_q_id); + p_ramrod->sb_id = cpu_to_le16(p_cid->abs.sb); + p_ramrod->sb_index = p_cid->abs.sb_idx; + p_ramrod->vport_id = p_cid->abs.vport_id; + p_ramrod->stats_counter_id = p_cid->abs.stats_id; + p_ramrod->rx_queue_id = cpu_to_le16(p_cid->abs.queue_id); p_ramrod->complete_cqe_flg = 0; p_ramrod->complete_event_flg = 1; @@ -575,85 +659,85 @@ int qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, p_ramrod->num_of_pbl_pages = cpu_to_le16(cqe_pbl_size); DMA_REGPAIR_LE(p_ramrod->cqe_pbl_addr, cqe_pbl_addr); - if (p_params->vf_qid || b_use_zone_a_prod) { - p_ramrod->vf_rx_prod_index = p_params->vf_qid; + if (p_cid->is_vf) { + p_ramrod->vf_rx_prod_index = p_cid->vf_qid; DP_VERBOSE(p_hwfn, QED_MSG_SP, "Queue%s is meant for VF rxq[%02x]\n", - b_use_zone_a_prod ? " [legacy]" : "", - p_params->vf_qid); - p_ramrod->vf_rx_prod_use_zone_a = b_use_zone_a_prod; + !!p_cid->b_legacy_vf ? " [legacy]" : "", + p_cid->vf_qid); + p_ramrod->vf_rx_prod_use_zone_a = !!p_cid->b_legacy_vf; } return qed_spq_post(p_hwfn, p_ent, NULL); } static int -qed_sp_eth_rx_queue_start(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - struct qed_queue_start_common_params *p_params, +qed_eth_pf_rx_queue_start(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, u16 bd_max_bytes, dma_addr_t bd_chain_phys_addr, dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size, void __iomem **pp_prod) { - struct qed_hw_cid_data *p_rx_cid; u32 init_prod_val = 0; - u16 abs_l2_queue = 0; - u8 abs_stats_id = 0; - int rc; - if (IS_VF(p_hwfn->cdev)) { - return qed_vf_pf_rxq_start(p_hwfn, - p_params->queue_id, - p_params->sb, - (u8)p_params->sb_idx, - bd_max_bytes, - bd_chain_phys_addr, - cqe_pbl_addr, cqe_pbl_size, pp_prod); - } - - rc = qed_fw_l2_queue(p_hwfn, p_params->queue_id, &abs_l2_queue); - if (rc) - return rc; - - rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_stats_id); - if (rc) - return rc; - - *pp_prod = (u8 __iomem *)p_hwfn->regview + - GTT_BAR0_MAP_REG_MSDM_RAM + - MSTORM_ETH_PF_PRODS_OFFSET(abs_l2_queue); + *pp_prod = p_hwfn->regview + + GTT_BAR0_MAP_REG_MSDM_RAM + + MSTORM_ETH_PF_PRODS_OFFSET(p_cid->abs.queue_id); /* Init the rcq, rx bd and rx sge (if valid) producers to 0 */ __internal_ram_wr(p_hwfn, *pp_prod, sizeof(u32), (u32 *)(&init_prod_val)); + return qed_eth_rxq_start_ramrod(p_hwfn, p_cid, + bd_max_bytes, + bd_chain_phys_addr, + cqe_pbl_addr, cqe_pbl_size); +} + +static int +qed_eth_rx_queue_start(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + struct qed_queue_start_common_params *p_params, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, + u16 cqe_pbl_size, + struct qed_rxq_start_ret_params *p_ret_params) +{ + struct qed_queue_cid *p_cid; + int rc; + /* Allocate a CID for the queue */ - p_rx_cid = &p_hwfn->p_rx_cids[p_params->queue_id]; - rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_ETH, &p_rx_cid->cid); - if (rc) { - DP_NOTICE(p_hwfn, "Failed to acquire cid\n"); - return rc; - } - p_rx_cid->b_cid_allocated = true; + p_cid = qed_eth_queue_to_cid(p_hwfn, opaque_fid, p_params); + if (!p_cid) + return -ENOMEM; - rc = qed_sp_eth_rxq_start_ramrod(p_hwfn, - opaque_fid, - p_rx_cid->cid, - p_params, - abs_stats_id, + if (IS_PF(p_hwfn->cdev)) { + rc = qed_eth_pf_rx_queue_start(p_hwfn, p_cid, + bd_max_bytes, + bd_chain_phys_addr, + cqe_pbl_addr, cqe_pbl_size, + &p_ret_params->p_prod); + } else { + rc = qed_vf_pf_rxq_start(p_hwfn, p_cid, bd_max_bytes, bd_chain_phys_addr, - cqe_pbl_addr, cqe_pbl_size, false); + cqe_pbl_addr, + cqe_pbl_size, &p_ret_params->p_prod); + } + /* Provide the caller with a reference to as handler */ if (rc) - qed_sp_release_queue_cid(p_hwfn, p_rx_cid); + qed_eth_queue_cid_release(p_hwfn, p_cid); + else + p_ret_params->p_handle = (void *)p_cid; return rc; } int qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, - u16 rx_queue_id, + void **pp_rxq_handles, u8 num_rxqs, u8 complete_cqe_flg, u8 complete_event_flg, @@ -663,8 +747,7 @@ int qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, struct rx_queue_update_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - struct qed_hw_cid_data *p_rx_cid; - u16 qid, abs_rx_q_id = 0; + struct qed_queue_cid *p_cid; int rc = -EINVAL; u8 i; @@ -673,12 +756,11 @@ int qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, init_data.p_comp_data = p_comp_data; for (i = 0; i < num_rxqs; i++) { - qid = rx_queue_id + i; - p_rx_cid = &p_hwfn->p_rx_cids[qid]; + p_cid = ((struct qed_queue_cid **)pp_rxq_handles)[i]; /* Get SPQ entry */ - init_data.cid = p_rx_cid->cid; - init_data.opaque_fid = p_rx_cid->opaque_fid; + init_data.cid = p_cid->cid; + init_data.opaque_fid = p_cid->opaque_fid; rc = qed_sp_init_request(p_hwfn, &p_ent, ETH_RAMROD_RX_QUEUE_UPDATE, @@ -687,10 +769,9 @@ int qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, return rc; p_ramrod = &p_ent->ramrod.rx_queue_update; + p_ramrod->vport_id = p_cid->abs.vport_id; - qed_fw_vport(p_hwfn, p_rx_cid->vport_id, &p_ramrod->vport_id); - qed_fw_l2_queue(p_hwfn, qid, &abs_rx_q_id); - p_ramrod->rx_queue_id = cpu_to_le16(abs_rx_q_id); + p_ramrod->rx_queue_id = cpu_to_le16(p_cid->abs.queue_id); p_ramrod->complete_cqe_flg = complete_cqe_flg; p_ramrod->complete_event_flg = complete_event_flg; @@ -702,24 +783,19 @@ int qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, return rc; } -int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, - u16 rx_queue_id, - bool eq_completion_only, bool cqe_completion) +static int +qed_eth_pf_rx_queue_stop(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + bool b_eq_completion_only, bool b_cqe_completion) { - struct qed_hw_cid_data *p_rx_cid = &p_hwfn->p_rx_cids[rx_queue_id]; struct rx_queue_stop_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - u16 abs_rx_q_id = 0; - int rc = -EINVAL; - - if (IS_VF(p_hwfn->cdev)) - return qed_vf_pf_rxq_stop(p_hwfn, rx_queue_id, cqe_completion); + int rc; - /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); - init_data.cid = p_rx_cid->cid; - init_data.opaque_fid = p_rx_cid->opaque_fid; + init_data.cid = p_cid->cid; + init_data.opaque_fid = p_cid->opaque_fid; init_data.comp_mode = QED_SPQ_MODE_EBLOCK; rc = qed_sp_init_request(p_hwfn, &p_ent, @@ -729,62 +805,53 @@ int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, return rc; p_ramrod = &p_ent->ramrod.rx_queue_stop; - - qed_fw_vport(p_hwfn, p_rx_cid->vport_id, &p_ramrod->vport_id); - qed_fw_l2_queue(p_hwfn, rx_queue_id, &abs_rx_q_id); - p_ramrod->rx_queue_id = cpu_to_le16(abs_rx_q_id); + p_ramrod->vport_id = p_cid->abs.vport_id; + p_ramrod->rx_queue_id = cpu_to_le16(p_cid->abs.queue_id); /* Cleaning the queue requires the completion to arrive there. * In addition, VFs require the answer to come as eqe to PF. */ - p_ramrod->complete_cqe_flg = - (!!(p_rx_cid->opaque_fid == p_hwfn->hw_info.opaque_fid) && - !eq_completion_only) || cqe_completion; - p_ramrod->complete_event_flg = - !(p_rx_cid->opaque_fid == p_hwfn->hw_info.opaque_fid) || - eq_completion_only; + p_ramrod->complete_cqe_flg = (!p_cid->is_vf && + !b_eq_completion_only) || + b_cqe_completion; + p_ramrod->complete_event_flg = p_cid->is_vf || b_eq_completion_only; - rc = qed_spq_post(p_hwfn, p_ent, NULL); - if (rc) - return rc; + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +int qed_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, + void *p_rxq, + bool eq_completion_only, bool cqe_completion) +{ + struct qed_queue_cid *p_cid = (struct qed_queue_cid *)p_rxq; + int rc = -EINVAL; - return qed_sp_release_queue_cid(p_hwfn, p_rx_cid); + if (IS_PF(p_hwfn->cdev)) + rc = qed_eth_pf_rx_queue_stop(p_hwfn, p_cid, + eq_completion_only, + cqe_completion); + else + rc = qed_vf_pf_rxq_stop(p_hwfn, p_cid, cqe_completion); + + if (!rc) + qed_eth_queue_cid_release(p_hwfn, p_cid); + return rc; } -int qed_sp_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - u32 cid, - struct qed_queue_start_common_params *p_params, - u8 stats_id, - dma_addr_t pbl_addr, - u16 pbl_size, - union qed_qm_pq_params *p_pq_params) +int +qed_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + dma_addr_t pbl_addr, u16 pbl_size, u16 pq_id) { struct tx_queue_start_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - struct qed_hw_cid_data *p_tx_cid; - u16 pq_id, abs_tx_q_id = 0; int rc = -EINVAL; - u8 abs_vport_id; - - /* Store information for the stop */ - p_tx_cid = &p_hwfn->p_tx_cids[p_params->queue_id]; - p_tx_cid->cid = cid; - p_tx_cid->opaque_fid = opaque_fid; - - rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_vport_id); - if (rc) - return rc; - - rc = qed_fw_l2_queue(p_hwfn, p_params->queue_id, &abs_tx_q_id); - if (rc) - return rc; /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); - init_data.cid = cid; - init_data.opaque_fid = opaque_fid; + init_data.cid = p_cid->cid; + init_data.opaque_fid = p_cid->opaque_fid; init_data.comp_mode = QED_SPQ_MODE_EBLOCK; rc = qed_sp_init_request(p_hwfn, &p_ent, @@ -794,96 +861,92 @@ int qed_sp_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, return rc; p_ramrod = &p_ent->ramrod.tx_queue_start; - p_ramrod->vport_id = abs_vport_id; + p_ramrod->vport_id = p_cid->abs.vport_id; - p_ramrod->sb_id = cpu_to_le16(p_params->sb); - p_ramrod->sb_index = p_params->sb_idx; - p_ramrod->stats_counter_id = stats_id; + p_ramrod->sb_id = cpu_to_le16(p_cid->abs.sb); + p_ramrod->sb_index = p_cid->abs.sb_idx; + p_ramrod->stats_counter_id = p_cid->abs.stats_id; - p_ramrod->queue_zone_id = cpu_to_le16(abs_tx_q_id); + p_ramrod->queue_zone_id = cpu_to_le16(p_cid->abs.queue_id); + p_ramrod->same_as_last_id = cpu_to_le16(p_cid->abs.queue_id); p_ramrod->pbl_size = cpu_to_le16(pbl_size); DMA_REGPAIR_LE(p_ramrod->pbl_base_addr, pbl_addr); - pq_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_ETH, p_pq_params); p_ramrod->qm_pq_id = cpu_to_le16(pq_id); return qed_spq_post(p_hwfn, p_ent, NULL); } static int -qed_sp_eth_tx_queue_start(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - struct qed_queue_start_common_params *p_params, +qed_eth_pf_tx_queue_start(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + u8 tc, dma_addr_t pbl_addr, u16 pbl_size, void __iomem **pp_doorbell) { - struct qed_hw_cid_data *p_tx_cid; union qed_qm_pq_params pq_params; - u8 abs_stats_id = 0; int rc; - if (IS_VF(p_hwfn->cdev)) { - return qed_vf_pf_txq_start(p_hwfn, - p_params->queue_id, - p_params->sb, - p_params->sb_idx, - pbl_addr, pbl_size, pp_doorbell); - } + memset(&pq_params, 0, sizeof(pq_params)); - rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_stats_id); + rc = qed_eth_txq_start_ramrod(p_hwfn, p_cid, + pbl_addr, pbl_size, + qed_get_qm_pq(p_hwfn, PROTOCOLID_ETH, + &pq_params)); if (rc) return rc; - p_tx_cid = &p_hwfn->p_tx_cids[p_params->queue_id]; - memset(p_tx_cid, 0, sizeof(*p_tx_cid)); - memset(&pq_params, 0, sizeof(pq_params)); + /* Provide the caller with the necessary return values */ + *pp_doorbell = p_hwfn->doorbells + + qed_db_addr(p_cid->cid, DQ_DEMS_LEGACY); - /* Allocate a CID for the queue */ - rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_ETH, &p_tx_cid->cid); - if (rc) { - DP_NOTICE(p_hwfn, "Failed to acquire cid\n"); - return rc; - } - p_tx_cid->b_cid_allocated = true; + return 0; +} - DP_VERBOSE(p_hwfn, QED_MSG_SP, - "opaque_fid=0x%x, cid=0x%x, tx_qid=0x%x, vport_id=0x%x, sb_id=0x%x\n", - opaque_fid, p_tx_cid->cid, - p_params->queue_id, p_params->vport_id, p_params->sb); - - rc = qed_sp_eth_txq_start_ramrod(p_hwfn, - opaque_fid, - p_tx_cid->cid, - p_params, - abs_stats_id, - pbl_addr, - pbl_size, - &pq_params); - - *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells + - qed_db_addr(p_tx_cid->cid, DQ_DEMS_LEGACY); +static int +qed_eth_tx_queue_start(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + struct qed_queue_start_common_params *p_params, + u8 tc, + dma_addr_t pbl_addr, + u16 pbl_size, + struct qed_txq_start_ret_params *p_ret_params) +{ + struct qed_queue_cid *p_cid; + int rc; + + p_cid = qed_eth_queue_to_cid(p_hwfn, opaque_fid, p_params); + if (!p_cid) + return -EINVAL; + + if (IS_PF(p_hwfn->cdev)) + rc = qed_eth_pf_tx_queue_start(p_hwfn, p_cid, tc, + pbl_addr, pbl_size, + &p_ret_params->p_doorbell); + else + rc = qed_vf_pf_txq_start(p_hwfn, p_cid, + pbl_addr, pbl_size, + &p_ret_params->p_doorbell); if (rc) - qed_sp_release_queue_cid(p_hwfn, p_tx_cid); + qed_eth_queue_cid_release(p_hwfn, p_cid); + else + p_ret_params->p_handle = (void *)p_cid; return rc; } -int qed_sp_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, u16 tx_queue_id) +static int +qed_eth_pf_tx_queue_stop(struct qed_hwfn *p_hwfn, struct qed_queue_cid *p_cid) { - struct qed_hw_cid_data *p_tx_cid = &p_hwfn->p_tx_cids[tx_queue_id]; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - int rc = -EINVAL; - - if (IS_VF(p_hwfn->cdev)) - return qed_vf_pf_txq_stop(p_hwfn, tx_queue_id); + int rc; - /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); - init_data.cid = p_tx_cid->cid; - init_data.opaque_fid = p_tx_cid->opaque_fid; + init_data.cid = p_cid->cid; + init_data.opaque_fid = p_cid->opaque_fid; init_data.comp_mode = QED_SPQ_MODE_EBLOCK; rc = qed_sp_init_request(p_hwfn, &p_ent, @@ -892,11 +955,22 @@ int qed_sp_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, u16 tx_queue_id) if (rc) return rc; - rc = qed_spq_post(p_hwfn, p_ent, NULL); - if (rc) - return rc; + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +int qed_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, void *p_handle) +{ + struct qed_queue_cid *p_cid = (struct qed_queue_cid *)p_handle; + int rc; + + if (IS_PF(p_hwfn->cdev)) + rc = qed_eth_pf_tx_queue_stop(p_hwfn, p_cid); + else + rc = qed_vf_pf_txq_stop(p_hwfn, p_cid); - return qed_sp_release_queue_cid(p_hwfn, p_tx_cid); + if (!rc) + qed_eth_queue_cid_release(p_hwfn, p_cid); + return rc; } static enum eth_filter_action qed_filter_action(enum qed_filter_opcode opcode) @@ -1880,58 +1954,53 @@ static int qed_update_vport(struct qed_dev *cdev, } static int qed_start_rxq(struct qed_dev *cdev, - struct qed_queue_start_common_params *params, + u8 rss_num, + struct qed_queue_start_common_params *p_params, u16 bd_max_bytes, dma_addr_t bd_chain_phys_addr, dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size, - void __iomem **pp_prod) + struct qed_rxq_start_ret_params *ret_params) { struct qed_hwfn *p_hwfn; int rc, hwfn_index; - hwfn_index = params->rss_id % cdev->num_hwfns; + hwfn_index = rss_num % cdev->num_hwfns; p_hwfn = &cdev->hwfns[hwfn_index]; - /* Fix queue ID in 100g mode */ - params->queue_id /= cdev->num_hwfns; - - rc = qed_sp_eth_rx_queue_start(p_hwfn, - p_hwfn->hw_info.opaque_fid, - params, - bd_max_bytes, - bd_chain_phys_addr, - cqe_pbl_addr, - cqe_pbl_size, - pp_prod); + p_params->queue_id = p_params->queue_id / cdev->num_hwfns; + p_params->stats_id = p_params->vport_id; + rc = qed_eth_rx_queue_start(p_hwfn, + p_hwfn->hw_info.opaque_fid, + p_params, + bd_max_bytes, + bd_chain_phys_addr, + cqe_pbl_addr, cqe_pbl_size, ret_params); if (rc) { - DP_ERR(cdev, "Failed to start RXQ#%d\n", params->queue_id); + DP_ERR(cdev, "Failed to start RXQ#%d\n", p_params->queue_id); return rc; } DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP), - "Started RX-Q %d [rss %d] on V-PORT %d and SB %d\n", - params->queue_id, params->rss_id, params->vport_id, - params->sb); + "Started RX-Q %d [rss_num %d] on V-PORT %d and SB %d\n", + p_params->queue_id, rss_num, p_params->vport_id, + p_params->sb); return 0; } -static int qed_stop_rxq(struct qed_dev *cdev, - struct qed_stop_rxq_params *params) +static int qed_stop_rxq(struct qed_dev *cdev, u8 rss_id, void *handle) { int rc, hwfn_index; struct qed_hwfn *p_hwfn; - hwfn_index = params->rss_id % cdev->num_hwfns; - p_hwfn = &cdev->hwfns[hwfn_index]; + hwfn_index = rss_id % cdev->num_hwfns; + p_hwfn = &cdev->hwfns[hwfn_index]; - rc = qed_sp_eth_rx_queue_stop(p_hwfn, - params->rx_queue_id / cdev->num_hwfns, - params->eq_completion_only, false); + rc = qed_eth_rx_queue_stop(p_hwfn, handle, false, false); if (rc) { - DP_ERR(cdev, "Failed to stop RXQ#%d\n", params->rx_queue_id); + DP_ERR(cdev, "Failed to stop RXQ#%02x\n", rss_id); return rc; } @@ -1939,26 +2008,24 @@ static int qed_stop_rxq(struct qed_dev *cdev, } static int qed_start_txq(struct qed_dev *cdev, + u8 rss_num, struct qed_queue_start_common_params *p_params, dma_addr_t pbl_addr, u16 pbl_size, - void __iomem **pp_doorbell) + struct qed_txq_start_ret_params *ret_params) { struct qed_hwfn *p_hwfn; int rc, hwfn_index; - hwfn_index = p_params->rss_id % cdev->num_hwfns; - p_hwfn = &cdev->hwfns[hwfn_index]; - - /* Fix queue ID in 100g mode */ - p_params->queue_id /= cdev->num_hwfns; + hwfn_index = rss_num % cdev->num_hwfns; + p_hwfn = &cdev->hwfns[hwfn_index]; + p_params->queue_id = p_params->queue_id / cdev->num_hwfns; + p_params->stats_id = p_params->vport_id; - rc = qed_sp_eth_tx_queue_start(p_hwfn, - p_hwfn->hw_info.opaque_fid, - p_params, - pbl_addr, - pbl_size, - pp_doorbell); + rc = qed_eth_tx_queue_start(p_hwfn, + p_hwfn->hw_info.opaque_fid, + p_params, 0, + pbl_addr, pbl_size, ret_params); if (rc) { DP_ERR(cdev, "Failed to start TXQ#%d\n", p_params->queue_id); @@ -1966,8 +2033,8 @@ static int qed_start_txq(struct qed_dev *cdev, } DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP), - "Started TX-Q %d [rss %d] on V-PORT %d and SB %d\n", - p_params->queue_id, p_params->rss_id, p_params->vport_id, + "Started TX-Q %d [rss_num %d] on V-PORT %d and SB %d\n", + p_params->queue_id, rss_num, p_params->vport_id, p_params->sb); return 0; @@ -1981,19 +2048,17 @@ static int qed_fastpath_stop(struct qed_dev *cdev) return 0; } -static int qed_stop_txq(struct qed_dev *cdev, - struct qed_stop_txq_params *params) +static int qed_stop_txq(struct qed_dev *cdev, u8 rss_id, void *handle) { struct qed_hwfn *p_hwfn; int rc, hwfn_index; - hwfn_index = params->rss_id % cdev->num_hwfns; - p_hwfn = &cdev->hwfns[hwfn_index]; + hwfn_index = rss_id % cdev->num_hwfns; + p_hwfn = &cdev->hwfns[hwfn_index]; - rc = qed_sp_eth_tx_queue_stop(p_hwfn, - params->tx_queue_id / cdev->num_hwfns); + rc = qed_eth_tx_queue_stop(p_hwfn, handle); if (rc) { - DP_ERR(cdev, "Failed to stop TXQ#%d\n", params->tx_queue_id); + DP_ERR(cdev, "Failed to stop TXQ#%02x\n", rss_id); return rc; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h index e495d62..48c9bfc 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h @@ -78,11 +78,34 @@ struct qed_filter_mcast { unsigned char mac[QED_MAX_MC_ADDRS][ETH_ALEN]; }; -int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, - u16 rx_queue_id, - bool eq_completion_only, bool cqe_completion); +/** + * @brief qed_eth_rx_queue_stop - This ramrod closes an Rx queue + * + * @param p_hwfn + * @param p_rxq Handler of queue to close + * @param eq_completion_only If True completion will be on + * EQe, if False completion will be + * on EQe if p_hwfn opaque + * different from the RXQ opaque + * otherwise on CQe. + * @param cqe_completion If True completion will be + * receive on CQe. + * @return int + */ +int +qed_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, + void *p_rxq, + bool eq_completion_only, bool cqe_completion); -int qed_sp_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, u16 tx_queue_id); +/** + * @brief qed_eth_tx_queue_stop - closes a Tx queue + * + * @param p_hwfn + * @param p_txq - handle to Tx queue needed to be closed + * + * @return int + */ +int qed_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, void *p_txq); enum qed_tpa_mode { QED_TPA_MODE_NONE, @@ -196,19 +219,19 @@ int qed_sp_eth_filter_ucast(struct qed_hwfn *p_hwfn, * @note At the moment - only used by non-linux VFs. * * @param p_hwfn - * @param rx_queue_id RX Queue ID - * @param num_rxqs Allow to update multiple rx - * queues, from rx_queue_id to - * (rx_queue_id + num_rxqs) + * @param pp_rxq_handlers An array of queue handlers to be updated. + * @param num_rxqs number of queues to update. * @param complete_cqe_flg Post completion to the CQE Ring if set * @param complete_event_flg Post completion to the Event Ring if set + * @param comp_mode + * @param p_comp_data * * @return int */ int qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, - u16 rx_queue_id, + void **pp_rxq_handlers, u8 num_rxqs, u8 complete_cqe_flg, u8 complete_event_flg, @@ -217,27 +240,79 @@ qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats); -int qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn, - struct qed_sp_vport_start_params *p_params); +void qed_reset_vport_stats(struct qed_dev *cdev); + +struct qed_queue_cid { + /* 'Relative' is a relative term ;-). Usually the indices [not counting + * SBs] would be PF-relative, but there are some cases where that isn't + * the case - specifically for a PF configuring its VF indices it's + * possible some fields [E.g., stats-id] in 'rel' would already be abs. + */ + struct qed_queue_start_common_params rel; + struct qed_queue_start_common_params abs; + u32 cid; + u16 opaque_fid; + + /* VFs queues are mapped differently, so we need to know the + * relative queue associated with them [0-based]. + * Notice this is relevant on the *PF* queue-cid of its VF's queues, + * and not on the VF itself. + */ + bool is_vf; + u8 vf_qid; + + /* Legacy VFs might have Rx producer located elsewhere */ + bool b_legacy_vf; +}; -int qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - u32 cid, - struct qed_queue_start_common_params *params, - u8 stats_id, - u16 bd_max_bytes, - dma_addr_t bd_chain_phys_addr, - dma_addr_t cqe_pbl_addr, - u16 cqe_pbl_size, bool b_use_zone_a_prod); - -int qed_sp_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - u32 cid, - struct qed_queue_start_common_params *p_params, - u8 stats_id, - dma_addr_t pbl_addr, - u16 pbl_size, - union qed_qm_pq_params *p_pq_params); +void qed_eth_queue_cid_release(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid); + +struct qed_queue_cid *_qed_eth_queue_to_cid(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + u32 cid, + u8 vf_qid, + struct qed_queue_start_common_params + *p_params); + +int +qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_start_params *p_params); + +/** + * @brief - Starts an Rx queue, when queue_cid is already prepared + * + * @param p_hwfn + * @param p_cid + * @param bd_max_bytes + * @param bd_chain_phys_addr + * @param cqe_pbl_addr + * @param cqe_pbl_size + * + * @return int + */ +int +qed_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size); + +/** + * @brief - Starts a Tx queue, where queue_cid is already prepared + * + * @param p_hwfn + * @param p_cid + * @param pbl_addr + * @param pbl_size + * @param p_pq_params - parameters for choosing the PQ for this Tx queue + * + * @return int + */ +int +qed_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + dma_addr_t pbl_addr, u16 pbl_size, u16 pq_id); u8 qed_mcast_bin_from_mac(u8 *mac); diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index f3f742a..85b09dd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -808,37 +808,70 @@ static void qed_iov_free_vf_igu_sbs(struct qed_hwfn *p_hwfn, static int qed_iov_init_hw_for_vf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 rel_vf_id, u16 num_rx_queues) + struct qed_iov_vf_init_params *p_params) { u8 num_of_vf_avaiable_chains = 0; struct qed_vf_info *vf = NULL; + u16 qid, num_irqs; int rc = 0; u32 cids; u8 i; - vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, false); + vf = qed_iov_get_vf_info(p_hwfn, p_params->rel_vf_id, false); if (!vf) { DP_ERR(p_hwfn, "qed_iov_init_hw_for_vf : vf is NULL\n"); return -EINVAL; } if (vf->b_init) { - DP_NOTICE(p_hwfn, "VF[%d] is already active.\n", rel_vf_id); + DP_NOTICE(p_hwfn, "VF[%d] is already active.\n", + p_params->rel_vf_id); return -EINVAL; } + /* Perform sanity checking on the requested queue_id */ + for (i = 0; i < p_params->num_queues; i++) { + u16 min_vf_qzone = FEAT_NUM(p_hwfn, QED_PF_L2_QUE); + u16 max_vf_qzone = min_vf_qzone + + FEAT_NUM(p_hwfn, QED_VF_L2_QUE) - 1; + + qid = p_params->req_rx_queue[i]; + if (qid < min_vf_qzone || qid > max_vf_qzone) { + DP_NOTICE(p_hwfn, + "Can't enable Rx qid [%04x] for VF[%d]: qids [0x%04x,...,0x%04x] available\n", + qid, + p_params->rel_vf_id, + min_vf_qzone, max_vf_qzone); + return -EINVAL; + } + + qid = p_params->req_tx_queue[i]; + if (qid > max_vf_qzone) { + DP_NOTICE(p_hwfn, + "Can't enable Tx qid [%04x] for VF[%d]: max qid 0x%04x\n", + qid, p_params->rel_vf_id, max_vf_qzone); + return -EINVAL; + } + + /* If client *really* wants, Tx qid can be shared with PF */ + if (qid < min_vf_qzone) + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d] is using PF qid [0x%04x] for Txq[0x%02x]\n", + p_params->rel_vf_id, qid, i); + } + /* Limit number of queues according to number of CIDs */ qed_cxt_get_proto_cid_count(p_hwfn, PROTOCOLID_ETH, &cids); DP_VERBOSE(p_hwfn, QED_MSG_IOV, "VF[%d] - requesting to initialize for 0x%04x queues [0x%04x CIDs available]\n", - vf->relative_vf_id, num_rx_queues, (u16) cids); - num_rx_queues = min_t(u16, num_rx_queues, ((u16) cids)); + vf->relative_vf_id, p_params->num_queues, (u16)cids); + num_irqs = min_t(u16, p_params->num_queues, ((u16)cids)); num_of_vf_avaiable_chains = qed_iov_alloc_vf_igu_sbs(p_hwfn, p_ptt, - vf, - num_rx_queues); + vf, num_irqs); if (!num_of_vf_avaiable_chains) { DP_ERR(p_hwfn, "no available igu sbs\n"); return -ENOMEM; @@ -849,25 +882,22 @@ static int qed_iov_init_hw_for_vf(struct qed_hwfn *p_hwfn, vf->num_txqs = num_of_vf_avaiable_chains; for (i = 0; i < vf->num_rxqs; i++) { - u16 queue_id = qed_int_queue_id_from_sb_id(p_hwfn, - vf->igu_sbs[i]); + struct qed_vf_q_info *p_queue = &vf->vf_queues[i]; - if (queue_id > RESC_NUM(p_hwfn, QED_L2_QUEUE)) { - DP_NOTICE(p_hwfn, - "VF[%d] will require utilizing of out-of-bounds queues - %04x\n", - vf->relative_vf_id, queue_id); - return -EINVAL; - } + p_queue->fw_rx_qid = p_params->req_rx_queue[i]; + p_queue->fw_tx_qid = p_params->req_tx_queue[i]; /* CIDs are per-VF, so no problem having them 0-based. */ - vf->vf_queues[i].fw_rx_qid = queue_id; - vf->vf_queues[i].fw_tx_qid = queue_id; - vf->vf_queues[i].fw_cid = i; + p_queue->fw_cid = i; DP_VERBOSE(p_hwfn, QED_MSG_IOV, - "VF[%d] - [%d] SB %04x, Tx/Rx queue %04x CID %04x\n", - vf->relative_vf_id, i, vf->igu_sbs[i], queue_id, i); + "VF[%d] - Q[%d] SB %04x, qid [Rx %04x Tx %04x] CID %04x\n", + vf->relative_vf_id, + i, vf->igu_sbs[i], + p_queue->fw_rx_qid, + p_queue->fw_tx_qid, p_queue->fw_cid); } + rc = qed_iov_enable_vf_access(p_hwfn, p_ptt, vf); if (!rc) { vf->b_init = true; @@ -1187,8 +1217,19 @@ static void qed_iov_vf_cleanup(struct qed_hwfn *p_hwfn, p_vf->num_active_rxqs = 0; - for (i = 0; i < QED_MAX_VF_CHAINS_PER_PF; i++) - p_vf->vf_queues[i].rxq_active = 0; + for (i = 0; i < QED_MAX_VF_CHAINS_PER_PF; i++) { + struct qed_vf_q_info *p_queue = &p_vf->vf_queues[i]; + + if (p_queue->p_rx_cid) { + qed_eth_queue_cid_release(p_hwfn, p_queue->p_rx_cid); + p_queue->p_rx_cid = NULL; + } + + if (p_queue->p_tx_cid) { + qed_eth_queue_cid_release(p_hwfn, p_queue->p_tx_cid); + p_queue->p_tx_cid = NULL; + } + } memset(&p_vf->shadow_config, 0, sizeof(p_vf->shadow_config)); memset(&p_vf->acquire, 0, sizeof(p_vf->acquire)); @@ -1594,21 +1635,21 @@ static int qed_iov_configure_vport_forced(struct qed_hwfn *p_hwfn, /* Update all the Rx queues */ for (i = 0; i < QED_MAX_VF_CHAINS_PER_PF; i++) { - u16 qid; + struct qed_queue_cid *p_cid; - if (!p_vf->vf_queues[i].rxq_active) + p_cid = p_vf->vf_queues[i].p_rx_cid; + if (!p_cid) continue; - qid = p_vf->vf_queues[i].fw_rx_qid; - - rc = qed_sp_eth_rx_queues_update(p_hwfn, qid, + rc = qed_sp_eth_rx_queues_update(p_hwfn, + (void **)&p_cid, 1, 0, 1, QED_SPQ_MODE_EBLOCK, NULL); if (rc) { DP_NOTICE(p_hwfn, "Failed to send Rx update fo queue[0x%04x]\n", - qid); + p_cid->rel.queue_id); return rc; } } @@ -1782,23 +1823,34 @@ static void qed_iov_vf_mbx_start_rxq(struct qed_hwfn *p_hwfn, struct qed_queue_start_common_params params; struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; u8 status = PFVF_STATUS_NO_RESOURCE; + struct qed_vf_q_info *p_queue; struct vfpf_start_rxq_tlv *req; bool b_legacy_vf = false; int rc; - memset(¶ms, 0, sizeof(params)); req = &mbx->req_virt->start_rxq; if (!qed_iov_validate_rxq(p_hwfn, vf, req->rx_qid) || !qed_iov_validate_sb(p_hwfn, vf, req->hw_sb)) goto out; - params.queue_id = vf->vf_queues[req->rx_qid].fw_rx_qid; - params.vf_qid = req->rx_qid; + /* Acquire a new queue-cid */ + p_queue = &vf->vf_queues[req->rx_qid]; + + memset(¶ms, 0, sizeof(params)); + params.queue_id = p_queue->fw_rx_qid; params.vport_id = vf->vport_id; + params.stats_id = vf->abs_vf_id + 0x10; params.sb = req->hw_sb; params.sb_idx = req->sb_index; + p_queue->p_rx_cid = _qed_eth_queue_to_cid(p_hwfn, + vf->opaque_fid, + p_queue->fw_cid, + req->rx_qid, ¶ms); + if (!p_queue->p_rx_cid) + goto out; + /* Legacy VFs have their Producers in a different location, which they * calculate on their own and clean the producer prior to this. */ @@ -1811,21 +1863,19 @@ static void qed_iov_vf_mbx_start_rxq(struct qed_hwfn *p_hwfn, MSTORM_ETH_VF_PRODS_OFFSET(vf->abs_vf_id, req->rx_qid), 0); } + p_queue->p_rx_cid->b_legacy_vf = b_legacy_vf; - rc = qed_sp_eth_rxq_start_ramrod(p_hwfn, vf->opaque_fid, - vf->vf_queues[req->rx_qid].fw_cid, - ¶ms, - vf->abs_vf_id + 0x10, - req->bd_max_bytes, - req->rxq_addr, - req->cqe_pbl_addr, req->cqe_pbl_size, - b_legacy_vf); - + rc = qed_eth_rxq_start_ramrod(p_hwfn, + p_queue->p_rx_cid, + req->bd_max_bytes, + req->rxq_addr, + req->cqe_pbl_addr, req->cqe_pbl_size); if (rc) { status = PFVF_STATUS_FAILURE; + qed_eth_queue_cid_release(p_hwfn, p_queue->p_rx_cid); + p_queue->p_rx_cid = NULL; } else { status = PFVF_STATUS_SUCCESS; - vf->vf_queues[req->rx_qid].rxq_active = true; vf->num_active_rxqs++; } @@ -1882,7 +1932,9 @@ static void qed_iov_vf_mbx_start_txq(struct qed_hwfn *p_hwfn, u8 status = PFVF_STATUS_NO_RESOURCE; union qed_qm_pq_params pq_params; struct vfpf_start_txq_tlv *req; + struct qed_vf_q_info *p_queue; int rc; + u16 pq; /* Prepare the parameters which would choose the right PQ */ memset(&pq_params, 0, sizeof(pq_params)); @@ -1896,24 +1948,31 @@ static void qed_iov_vf_mbx_start_txq(struct qed_hwfn *p_hwfn, !qed_iov_validate_sb(p_hwfn, vf, req->hw_sb)) goto out; - params.queue_id = vf->vf_queues[req->tx_qid].fw_tx_qid; + /* Acquire a new queue-cid */ + p_queue = &vf->vf_queues[req->tx_qid]; + + params.queue_id = p_queue->fw_tx_qid; params.vport_id = vf->vport_id; + params.stats_id = vf->abs_vf_id + 0x10; params.sb = req->hw_sb; params.sb_idx = req->sb_index; - rc = qed_sp_eth_txq_start_ramrod(p_hwfn, - vf->opaque_fid, - vf->vf_queues[req->tx_qid].fw_cid, - ¶ms, - vf->abs_vf_id + 0x10, - req->pbl_addr, - req->pbl_size, &pq_params); + p_queue->p_tx_cid = _qed_eth_queue_to_cid(p_hwfn, + vf->opaque_fid, + p_queue->fw_cid, + req->tx_qid, ¶ms); + if (!p_queue->p_tx_cid) + goto out; + pq = qed_get_qm_pq(p_hwfn, PROTOCOLID_ETH, &pq_params); + rc = qed_eth_txq_start_ramrod(p_hwfn, p_queue->p_tx_cid, + req->pbl_addr, req->pbl_size, pq); if (rc) { status = PFVF_STATUS_FAILURE; + qed_eth_queue_cid_release(p_hwfn, p_queue->p_tx_cid); + p_queue->p_tx_cid = NULL; } else { status = PFVF_STATUS_SUCCESS; - vf->vf_queues[req->tx_qid].txq_active = true; } out: @@ -1924,6 +1983,7 @@ static int qed_iov_vf_stop_rxqs(struct qed_hwfn *p_hwfn, struct qed_vf_info *vf, u16 rxq_id, u8 num_rxqs, bool cqe_completion) { + struct qed_vf_q_info *p_queue; int rc = 0; int qid; @@ -1931,16 +1991,18 @@ static int qed_iov_vf_stop_rxqs(struct qed_hwfn *p_hwfn, return -EINVAL; for (qid = rxq_id; qid < rxq_id + num_rxqs; qid++) { - if (vf->vf_queues[qid].rxq_active) { - rc = qed_sp_eth_rx_queue_stop(p_hwfn, - vf->vf_queues[qid]. - fw_rx_qid, false, - cqe_completion); + p_queue = &vf->vf_queues[qid]; - if (rc) - return rc; - } - vf->vf_queues[qid].rxq_active = false; + if (!p_queue->p_rx_cid) + continue; + + rc = qed_eth_rx_queue_stop(p_hwfn, + p_queue->p_rx_cid, + false, cqe_completion); + if (rc) + return rc; + + vf->vf_queues[qid].p_rx_cid = NULL; vf->num_active_rxqs--; } @@ -1951,22 +2013,24 @@ static int qed_iov_vf_stop_txqs(struct qed_hwfn *p_hwfn, struct qed_vf_info *vf, u16 txq_id, u8 num_txqs) { int rc = 0; + struct qed_vf_q_info *p_queue; int qid; if (txq_id + num_txqs > ARRAY_SIZE(vf->vf_queues)) return -EINVAL; for (qid = txq_id; qid < txq_id + num_txqs; qid++) { - if (vf->vf_queues[qid].txq_active) { - rc = qed_sp_eth_tx_queue_stop(p_hwfn, - vf->vf_queues[qid]. - fw_tx_qid); + p_queue = &vf->vf_queues[qid]; + if (!p_queue->p_tx_cid) + continue; - if (rc) - return rc; - } - vf->vf_queues[qid].txq_active = false; + rc = qed_eth_tx_queue_stop(p_hwfn, p_queue->p_tx_cid); + if (rc) + return rc; + + p_queue->p_tx_cid = NULL; } + return rc; } @@ -2021,10 +2085,11 @@ static void qed_iov_vf_mbx_update_rxqs(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_vf_info *vf) { + struct qed_queue_cid *handlers[QED_MAX_VF_CHAINS_PER_PF]; u16 length = sizeof(struct pfvf_def_resp_tlv); struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; struct vfpf_update_rxq_tlv *req; - u8 status = PFVF_STATUS_SUCCESS; + u8 status = PFVF_STATUS_FAILURE; u8 complete_event_flg; u8 complete_cqe_flg; u16 qid; @@ -2035,29 +2100,36 @@ static void qed_iov_vf_mbx_update_rxqs(struct qed_hwfn *p_hwfn, complete_cqe_flg = !!(req->flags & VFPF_RXQ_UPD_COMPLETE_CQE_FLAG); complete_event_flg = !!(req->flags & VFPF_RXQ_UPD_COMPLETE_EVENT_FLAG); + /* Validate inputs */ + if (req->num_rxqs + req->rx_qid > QED_MAX_VF_CHAINS_PER_PF || + !qed_iov_validate_rxq(p_hwfn, vf, req->rx_qid)) { + DP_INFO(p_hwfn, "VF[%d]: Incorrect Rxqs [%04x, %02x]\n", + vf->relative_vf_id, req->rx_qid, req->num_rxqs); + goto out; + } + for (i = 0; i < req->num_rxqs; i++) { qid = req->rx_qid + i; - - if (!vf->vf_queues[qid].rxq_active) { - DP_NOTICE(p_hwfn, "VF rx_qid = %d isn`t active!\n", - qid); - status = PFVF_STATUS_FAILURE; - break; + if (!vf->vf_queues[qid].p_rx_cid) { + DP_INFO(p_hwfn, + "VF[%d] rx_qid = %d isn`t active!\n", + vf->relative_vf_id, qid); + goto out; } - rc = qed_sp_eth_rx_queues_update(p_hwfn, - vf->vf_queues[qid].fw_rx_qid, - 1, - complete_cqe_flg, - complete_event_flg, - QED_SPQ_MODE_EBLOCK, NULL); - - if (rc) { - status = PFVF_STATUS_FAILURE; - break; - } + handlers[i] = vf->vf_queues[qid].p_rx_cid; } + rc = qed_sp_eth_rx_queues_update(p_hwfn, (void **)&handlers, + req->num_rxqs, + complete_cqe_flg, + complete_event_flg, + QED_SPQ_MODE_EBLOCK, NULL); + if (rc) + goto out; + + status = PFVF_STATUS_SUCCESS; +out: qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_UPDATE_RXQ, length, status); } @@ -2268,7 +2340,7 @@ qed_iov_vp_update_rss_param(struct qed_hwfn *p_hwfn, DP_NOTICE(p_hwfn, "rss_ind_table[%d] = %d, rxq is out of range\n", i, q_idx); - else if (!vf->vf_queues[q_idx].rxq_active) + else if (!vf->vf_queues[q_idx].p_rx_cid) DP_NOTICE(p_hwfn, "rss_ind_table[%d] = %d, rxq is not active\n", i, q_idx); @@ -3468,8 +3540,28 @@ int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled) return 0; } +static void qed_sriov_enable_qid_config(struct qed_hwfn *hwfn, + u16 vfid, + struct qed_iov_vf_init_params *params) +{ + u16 base, i; + + /* Since we have an equal resource distribution per-VF, and we assume + * PF has acquired the QED_PF_L2_QUE first queues, we start setting + * sequentially from there. + */ + base = FEAT_NUM(hwfn, QED_PF_L2_QUE) + vfid * params->num_queues; + + params->rel_vf_id = vfid; + for (i = 0; i < params->num_queues; i++) { + params->req_rx_queue[i] = base + i; + params->req_tx_queue[i] = base + i; + } +} + static int qed_sriov_enable(struct qed_dev *cdev, int num) { + struct qed_iov_vf_init_params params; int i, j, rc; if (num >= RESC_NUM(&cdev->hwfns[0], QED_VPORT)) { @@ -3478,15 +3570,17 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num) return -EINVAL; } + memset(¶ms, 0, sizeof(params)); + /* Initialize HW for VF access */ for_each_hwfn(cdev, j) { struct qed_hwfn *hwfn = &cdev->hwfns[j]; struct qed_ptt *ptt = qed_ptt_acquire(hwfn); - int num_queues; /* Make sure not to use more than 16 queues per VF */ - num_queues = min_t(int, - FEAT_NUM(hwfn, QED_VF_L2_QUE) / num, 16); + params.num_queues = min_t(int, + FEAT_NUM(hwfn, QED_VF_L2_QUE) / num, + 16); if (!ptt) { DP_ERR(hwfn, "Failed to acquire ptt\n"); @@ -3498,7 +3592,8 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num) if (!qed_iov_is_valid_vfid(hwfn, i, false, true)) continue; - rc = qed_iov_init_hw_for_vf(hwfn, ptt, i, num_queues); + qed_sriov_enable_qid_config(hwfn, i, ¶ms); + rc = qed_iov_init_hw_for_vf(hwfn, ptt, ¶ms); if (rc) { DP_ERR(cdev, "Failed to enable VF[%d]\n", i); qed_ptt_release(hwfn, ptt); diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index 3cf515b..509c02b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -58,6 +58,23 @@ struct qed_public_vf_info { int tx_rate; }; +struct qed_iov_vf_init_params { + u16 rel_vf_id; + + /* Number of requested Queues; Currently, don't support different + * number of Rx/Tx queues. + */ + + u16 num_queues; + + /* Allow the client to choose which qzones to use for Rx/Tx, + * and which queue_base to use for Tx queues on a per-queue basis. + * Notice values should be relative to the PF resources. + */ + u16 req_rx_queue[QED_MAX_VF_CHAINS_PER_PF]; + u16 req_tx_queue[QED_MAX_VF_CHAINS_PER_PF]; +}; + /* This struct is part of qed_dev and contains data relevant to all hwfns; * Initialized only if SR-IOV cpabability is exposed in PCIe config space. */ @@ -99,10 +116,10 @@ struct qed_iov_vf_mbx { struct qed_vf_q_info { u16 fw_rx_qid; + struct qed_queue_cid *p_rx_cid; u16 fw_tx_qid; + struct qed_queue_cid *p_tx_cid; u8 fw_cid; - u8 rxq_active; - u8 txq_active; }; enum vf_state { diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index 3c06336..60b31a8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -388,18 +388,18 @@ free_p_iov: #define MSTORM_QZONE_START(dev) (TSTORM_QZONE_START + \ (TSTORM_QZONE_SIZE * NUM_OF_L2_QUEUES(dev))) -int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, - u8 rx_qid, - u16 sb, - u8 sb_index, - u16 bd_max_bytes, - dma_addr_t bd_chain_phys_addr, - dma_addr_t cqe_pbl_addr, - u16 cqe_pbl_size, void __iomem **pp_prod) +int +qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, + u16 cqe_pbl_size, void __iomem **pp_prod) { struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; struct pfvf_start_queue_resp_tlv *resp; struct vfpf_start_rxq_tlv *req; + u8 rx_qid = p_cid->rel.queue_id; int rc; /* clear mailbox and prep first tlv */ @@ -409,21 +409,22 @@ int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, req->cqe_pbl_addr = cqe_pbl_addr; req->cqe_pbl_size = cqe_pbl_size; req->rxq_addr = bd_chain_phys_addr; - req->hw_sb = sb; - req->sb_index = sb_index; + req->hw_sb = p_cid->rel.sb; + req->sb_index = p_cid->rel.sb_idx; req->bd_max_bytes = bd_max_bytes; req->stat_id = -1; /* If PF is legacy, we'll need to calculate producers ourselves * as well as clean them. */ - if (pp_prod && p_iov->b_pre_fp_hsi) { + if (p_iov->b_pre_fp_hsi) { u8 hw_qid = p_iov->acquire_resp.resc.hw_qid[rx_qid]; u32 init_prod_val = 0; - *pp_prod = (u8 __iomem *)p_hwfn->regview + - MSTORM_QZONE_START(p_hwfn->cdev) + - hw_qid * MSTORM_QZONE_SIZE; + *pp_prod = (u8 __iomem *) + p_hwfn->regview + + MSTORM_QZONE_START(p_hwfn->cdev) + + hw_qid * MSTORM_QZONE_SIZE; /* Init the rcq, rx bd and rx sge (if valid) producers to 0 */ __internal_ram_wr(p_hwfn, *pp_prod, sizeof(u32), @@ -444,7 +445,7 @@ int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, } /* Learn the address of the producer from the response */ - if (pp_prod && !p_iov->b_pre_fp_hsi) { + if (!p_iov->b_pre_fp_hsi) { u32 init_prod_val = 0; *pp_prod = (u8 __iomem *)p_hwfn->regview + resp->offset; @@ -462,7 +463,8 @@ exit: return rc; } -int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, u16 rx_qid, bool cqe_completion) +int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, bool cqe_completion) { struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; struct vfpf_stop_rxqs_tlv *req; @@ -472,7 +474,7 @@ int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, u16 rx_qid, bool cqe_completion) /* clear mailbox and prep first tlv */ req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_STOP_RXQS, sizeof(*req)); - req->rx_qid = rx_qid; + req->rx_qid = p_cid->rel.queue_id; req->num_rxqs = 1; req->cqe_completion = cqe_completion; @@ -496,28 +498,28 @@ exit: return rc; } -int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, - u16 tx_queue_id, - u16 sb, - u8 sb_index, - dma_addr_t pbl_addr, - u16 pbl_size, void __iomem **pp_doorbell) +int +qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + dma_addr_t pbl_addr, + u16 pbl_size, void __iomem **pp_doorbell) { struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; struct pfvf_start_queue_resp_tlv *resp; struct vfpf_start_txq_tlv *req; + u16 qid = p_cid->rel.queue_id; int rc; /* clear mailbox and prep first tlv */ req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_START_TXQ, sizeof(*req)); - req->tx_qid = tx_queue_id; + req->tx_qid = qid; /* Tx */ req->pbl_addr = pbl_addr; req->pbl_size = pbl_size; - req->hw_sb = sb; - req->sb_index = sb_index; + req->hw_sb = p_cid->rel.sb; + req->sb_index = p_cid->rel.sb_idx; /* add list termination tlv */ qed_add_tlv(p_hwfn, &p_iov->offset, @@ -533,33 +535,29 @@ int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, goto exit; } - if (pp_doorbell) { - /* Modern PFs provide the actual offsets, while legacy - * provided only the queue id. - */ - if (!p_iov->b_pre_fp_hsi) { - *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells + - resp->offset; - } else { - u8 cid = p_iov->acquire_resp.resc.cid[tx_queue_id]; - u32 db_addr; - - db_addr = qed_db_addr_vf(cid, DQ_DEMS_LEGACY); - *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells + - db_addr; - } + /* Modern PFs provide the actual offsets, while legacy + * provided only the queue id. + */ + if (!p_iov->b_pre_fp_hsi) { + *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells + resp->offset; + } else { + u8 cid = p_iov->acquire_resp.resc.cid[qid]; - DP_VERBOSE(p_hwfn, QED_MSG_IOV, - "Txq[0x%02x]: doorbell at %p [offset 0x%08x]\n", - tx_queue_id, *pp_doorbell, resp->offset); + *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells + + qed_db_addr_vf(cid, + DQ_DEMS_LEGACY); } + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Txq[0x%02x]: doorbell at %p [offset 0x%08x]\n", + qid, *pp_doorbell, resp->offset); exit: qed_vf_pf_req_end(p_hwfn, rc); return rc; } -int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, u16 tx_qid) +int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, struct qed_queue_cid *p_cid) { struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; struct vfpf_stop_txqs_tlv *req; @@ -569,7 +567,7 @@ int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, u16 tx_qid) /* clear mailbox and prep first tlv */ req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_STOP_TXQS, sizeof(*req)); - req->tx_qid = tx_qid; + req->tx_qid = p_cid->rel.queue_id; req->num_txqs = 1; /* add list termination tlv */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 325c250..11eb385 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -666,10 +666,7 @@ int qed_vf_hw_prepare(struct qed_hwfn *p_hwfn); /** * @brief VF - start the RX Queue by sending a message to the PF * @param p_hwfn - * @param cid - zero based within the VF - * @param rx_queue_id - zero based within the VF - * @param sb - VF status block for this queue - * @param sb_index - Index within the status block + * @param p_cid - Only relative fields are relevant * @param bd_max_bytes - maximum number of bytes per bd * @param bd_chain_phys_addr - physical address of bd chain * @param cqe_pbl_addr - physical address of pbl @@ -680,9 +677,7 @@ int qed_vf_hw_prepare(struct qed_hwfn *p_hwfn); * @return int */ int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, - u8 rx_queue_id, - u16 sb, - u8 sb_index, + struct qed_queue_cid *p_cid, u16 bd_max_bytes, dma_addr_t bd_chain_phys_addr, dma_addr_t cqe_pbl_addr, @@ -702,24 +697,23 @@ int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, * * @return int */ -int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, - u16 tx_queue_id, - u16 sb, - u8 sb_index, - dma_addr_t pbl_addr, - u16 pbl_size, void __iomem **pp_doorbell); +int +qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid, + dma_addr_t pbl_addr, + u16 pbl_size, void __iomem **pp_doorbell); /** * @brief VF - stop the RX queue by sending a message to the PF * * @param p_hwfn - * @param rx_qid + * @param p_cid * @param cqe_completion * * @return int */ int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, - u16 rx_qid, bool cqe_completion); + struct qed_queue_cid *p_cid, bool cqe_completion); /** * @brief VF - stop the TX queue by sending a message to the PF @@ -729,7 +723,7 @@ int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, * * @return int */ -int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, u16 tx_qid); +int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, struct qed_queue_cid *p_cid); /** * @brief VF - send a vport update command @@ -902,9 +896,7 @@ static inline int qed_vf_hw_prepare(struct qed_hwfn *p_hwfn) } static inline int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, - u8 rx_queue_id, - u16 sb, - u8 sb_index, + struct qed_queue_cid *p_cid, u16 bd_max_bytes, dma_addr_t bd_chain_phys_adr, dma_addr_t cqe_pbl_addr, @@ -914,9 +906,7 @@ static inline int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, } static inline int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, - u16 tx_queue_id, - u16 sb, - u8 sb_index, + struct qed_queue_cid *p_cid, dma_addr_t pbl_addr, u16 pbl_size, void __iomem **pp_doorbell) { @@ -924,12 +914,14 @@ static inline int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, } static inline int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, - u16 rx_qid, bool cqe_completion) + struct qed_queue_cid *p_cid, + bool cqe_completion) { return -EINVAL; } -static inline int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, u16 tx_qid) +static inline int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, + struct qed_queue_cid *p_cid) { return -EINVAL; } diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 2116c4c..c213576 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -264,6 +264,8 @@ struct qede_rx_queue { u64 rx_hw_errors; u64 rx_alloc_errors; u64 rx_ip_frags; + + void *handle; }; union db_prod { @@ -293,6 +295,8 @@ struct qede_tx_queue { u64 stopped_cnt; bool is_legacy; + void *handle; + }; #define BD_UNMAP_ADDR(bd) HILO_U64(le32_to_cpu((bd)->addr.hi), \ diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 64c7f3b..8349211 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -3334,6 +3334,12 @@ static int qede_drain_txq(struct qede_dev *edev, return 0; } +static int qede_stop_txq(struct qede_dev *edev, + struct qede_tx_queue *txq, int rss_id) +{ + return edev->ops->q_tx_stop(edev->cdev, rss_id, txq->handle); +} + static int qede_stop_queues(struct qede_dev *edev) { struct qed_update_vport_params vport_update_params; @@ -3367,28 +3373,18 @@ static int qede_stop_queues(struct qede_dev *edev) /* Stop all Queues in reverse order */ for (i = QEDE_QUEUE_CNT(edev) - 1; i >= 0; i--) { - struct qed_stop_rxq_params rx_params; - fp = &edev->fp_array[i]; /* Stop the Tx Queue(s) */ if (fp->type & QEDE_FASTPATH_TX) { - struct qed_stop_txq_params tx_params; - - tx_params.rss_id = i; - tx_params.tx_queue_id = fp->txq->index; - rc = edev->ops->q_tx_stop(cdev, &tx_params); - if (rc) - return rc; + rc = qede_stop_txq(edev, fp->txq, i); + if (rc) + return rc; } /* Stop the Rx Queue */ if (fp->type & QEDE_FASTPATH_RX) { - memset(&rx_params, 0, sizeof(rx_params)); - rx_params.rss_id = i; - rx_params.rx_queue_id = fp->rxq->rxq_id; - - rc = edev->ops->q_rx_stop(cdev, &rx_params); + rc = edev->ops->q_rx_stop(cdev, i, fp->rxq->handle); if (rc) { DP_ERR(edev, "Failed to stop RXQ #%d\n", i); return rc; @@ -3404,6 +3400,46 @@ static int qede_stop_queues(struct qede_dev *edev) return rc; } +static int qede_start_txq(struct qede_dev *edev, + struct qede_fastpath *fp, + struct qede_tx_queue *txq, u8 rss_id, u16 sb_idx) +{ + dma_addr_t phys_table = qed_chain_get_pbl_phys(&txq->tx_pbl); + u32 page_cnt = qed_chain_get_page_cnt(&txq->tx_pbl); + struct qed_queue_start_common_params params; + struct qed_txq_start_ret_params ret_params; + int rc; + + memset(¶ms, 0, sizeof(params)); + memset(&ret_params, 0, sizeof(ret_params)); + + params.queue_id = txq->index; + params.sb = fp->sb_info->igu_sb_id; + params.sb_idx = sb_idx; + + rc = edev->ops->q_tx_start(edev->cdev, rss_id, ¶ms, phys_table, + page_cnt, &ret_params); + if (rc) { + DP_ERR(edev, "Start TXQ #%d failed %d\n", txq->index, rc); + return rc; + } + + txq->doorbell_addr = ret_params.p_doorbell; + txq->handle = ret_params.p_handle; + + /* Determine the FW consumer address associated */ + txq->hw_cons_ptr = &fp->sb_info->sb_virt->pi_array[sb_idx]; + + /* Prepare the doorbell parameters */ + SET_FIELD(txq->tx_db.data.params, ETH_DB_DATA_DEST, DB_DEST_XCM); + SET_FIELD(txq->tx_db.data.params, ETH_DB_DATA_AGG_CMD, DB_AGG_CMD_SET); + SET_FIELD(txq->tx_db.data.params, ETH_DB_DATA_AGG_VAL_SEL, + DQ_XCM_ETH_TX_BD_PROD_CMD); + txq->tx_db.data.agg_flags = DQ_XCM_ETH_DQ_CF_CMD; + + return rc; +} + static int qede_start_queues(struct qede_dev *edev, bool clear_stats) { int vlan_removal_en = 1; @@ -3445,11 +3481,12 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) u32 page_cnt; if (fp->type & QEDE_FASTPATH_RX) { + struct qed_rxq_start_ret_params ret_params; struct qede_rx_queue *rxq = fp->rxq; __le16 *val; + memset(&ret_params, 0, sizeof(ret_params)); memset(&q_params, 0, sizeof(q_params)); - q_params.rss_id = i; q_params.queue_id = rxq->rxq_id; q_params.vport_id = 0; q_params.sb = fp->sb_info->igu_sb_id; @@ -3459,18 +3496,21 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) qed_chain_get_pbl_phys(&rxq->rx_comp_ring); page_cnt = qed_chain_get_page_cnt(&rxq->rx_comp_ring); - rc = edev->ops->q_rx_start(cdev, &q_params, + rc = edev->ops->q_rx_start(cdev, i, &q_params, rxq->rx_buf_size, rxq->rx_bd_ring.p_phys_addr, p_phys_table, - page_cnt, - &rxq->hw_rxq_prod_addr); + page_cnt, &ret_params); if (rc) { DP_ERR(edev, "Start RXQ #%d failed %d\n", i, rc); return rc; } + /* Use the return parameters */ + rxq->hw_rxq_prod_addr = ret_params.p_prod; + rxq->handle = ret_params.p_handle; + val = &fp->sb_info->sb_virt->pi_array[RX_PI]; rxq->hw_cons_ptr = val; @@ -3478,38 +3518,9 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) } if (fp->type & QEDE_FASTPATH_TX) { - struct qede_tx_queue *txq = fp->txq; - - p_phys_table = qed_chain_get_pbl_phys(&txq->tx_pbl); - page_cnt = qed_chain_get_page_cnt(&txq->tx_pbl); - - memset(&q_params, 0, sizeof(q_params)); - q_params.rss_id = i; - q_params.queue_id = txq->index; - q_params.vport_id = 0; - q_params.sb = fp->sb_info->igu_sb_id; - q_params.sb_idx = TX_PI(0); - - rc = edev->ops->q_tx_start(cdev, &q_params, - p_phys_table, page_cnt, - &txq->doorbell_addr); - if (rc) { - DP_ERR(edev, "Start TXQ #%d failed %d\n", - txq->index, rc); + rc = qede_start_txq(edev, fp, fp->txq, i, TX_PI(0)); + if (rc) return rc; - } - - txq->hw_cons_ptr = - &fp->sb_info->sb_virt->pi_array[TX_PI(0)]; - SET_FIELD(txq->tx_db.data.params, - ETH_DB_DATA_DEST, DB_DEST_XCM); - SET_FIELD(txq->tx_db.data.params, ETH_DB_DATA_AGG_CMD, - DB_AGG_CMD_SET); - SET_FIELD(txq->tx_db.data.params, - ETH_DB_DATA_AGG_VAL_SEL, - DQ_XCM_ETH_TX_BD_PROD_CMD); - - txq->tx_db.data.agg_flags = DQ_XCM_ETH_DQ_CF_CMD; } } diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 9755a3f..7a52f7c 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -15,6 +15,29 @@ #include #include +struct qed_queue_start_common_params { + /* Should always be relative to entity sending this. */ + u8 vport_id; + u16 queue_id; + + /* Relative, but relevant only for PFs */ + u8 stats_id; + + /* These are always absolute */ + u16 sb; + u8 sb_idx; +}; + +struct qed_rxq_start_ret_params { + void __iomem *p_prod; + void *p_handle; +}; + +struct qed_txq_start_ret_params { + void __iomem *p_doorbell; + void *p_handle; +}; + struct qed_dev_eth_info { struct qed_dev_info common; @@ -56,18 +79,6 @@ struct qed_start_vport_params { bool clear_stats; }; -struct qed_stop_rxq_params { - u8 rss_id; - u8 rx_queue_id; - u8 vport_id; - bool eq_completion_only; -}; - -struct qed_stop_txq_params { - u8 rss_id; - u8 tx_queue_id; -}; - enum qed_filter_rx_mode_type { QED_FILTER_RX_MODE_TYPE_REGULAR, QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC, @@ -112,15 +123,6 @@ struct qed_filter_params { union qed_filter_type_params filter; }; -struct qed_queue_start_common_params { - u8 rss_id; - u8 queue_id; - u8 vport_id; - u16 sb; - u16 sb_idx; - u16 vf_qid; -}; - struct qed_tunn_params { u16 vxlan_port; u8 update_vxlan_port; @@ -220,24 +222,24 @@ struct qed_eth_ops { struct qed_update_vport_params *params); int (*q_rx_start)(struct qed_dev *cdev, + u8 rss_num, struct qed_queue_start_common_params *params, u16 bd_max_bytes, dma_addr_t bd_chain_phys_addr, dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size, - void __iomem **pp_prod); + struct qed_rxq_start_ret_params *ret_params); - int (*q_rx_stop)(struct qed_dev *cdev, - struct qed_stop_rxq_params *params); + int (*q_rx_stop)(struct qed_dev *cdev, u8 rss_id, void *handle); int (*q_tx_start)(struct qed_dev *cdev, + u8 rss_num, struct qed_queue_start_common_params *params, dma_addr_t pbl_addr, u16 pbl_size, - void __iomem **pp_doorbell); + struct qed_txq_start_ret_params *ret_params); - int (*q_tx_stop)(struct qed_dev *cdev, - struct qed_stop_txq_params *params); + int (*q_tx_stop)(struct qed_dev *cdev, u8 rss_id, void *handle); int (*filter_config)(struct qed_dev *cdev, struct qed_filter_params *params); -- cgit v1.1 From 8a4725306522c875fca4bff4bd14a46e97690f48 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:07 +0200 Subject: qede: Don't check netdevice for rx-hash Receive-hashing is a fixed feature, so there's no need to check during the ingress datapath whether it's set or not. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_main.c | 34 ++++++++++------------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 8349211..c9cae3e 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -997,22 +997,20 @@ void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq) mmiowb(); } -static u32 qede_get_rxhash(struct qede_dev *edev, - u8 bitfields, - __le32 rss_hash, enum pkt_hash_types *rxhash_type) +static void qede_get_rxhash(struct sk_buff *skb, u8 bitfields, __le32 rss_hash) { + enum pkt_hash_types hash_type = PKT_HASH_TYPE_NONE; enum rss_hash_type htype; + u32 hash = 0; htype = GET_FIELD(bitfields, ETH_FAST_PATH_RX_REG_CQE_RSS_HASH_TYPE); - - if ((edev->ndev->features & NETIF_F_RXHASH) && htype) { - *rxhash_type = ((htype == RSS_HASH_TYPE_IPV4) || - (htype == RSS_HASH_TYPE_IPV6)) ? - PKT_HASH_TYPE_L3 : PKT_HASH_TYPE_L4; - return le32_to_cpu(rss_hash); + if (htype) { + hash_type = ((htype == RSS_HASH_TYPE_IPV4) || + (htype == RSS_HASH_TYPE_IPV6)) ? + PKT_HASH_TYPE_L3 : PKT_HASH_TYPE_L4; + hash = le32_to_cpu(rss_hash); } - *rxhash_type = PKT_HASH_TYPE_NONE; - return 0; + skb_set_hash(skb, hash, hash_type); } static void qede_set_skb_csum(struct sk_buff *skb, u8 csum_flag) @@ -1104,8 +1102,6 @@ static void qede_tpa_start(struct qede_dev *edev, dma_addr_t mapping = tpa_info->buffer_mapping; struct sw_rx_data *sw_rx_data_cons; struct sw_rx_data *sw_rx_data_prod; - enum pkt_hash_types rxhash_type; - u32 rxhash; sw_rx_data_cons = &rxq->sw_rx_ring[rxq->sw_rx_cons & NUM_RX_BDS_MAX]; sw_rx_data_prod = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX]; @@ -1150,10 +1146,6 @@ static void qede_tpa_start(struct qede_dev *edev, tpa_info->frag_id = 0; tpa_info->state = QEDE_AGG_STATE_START; - rxhash = qede_get_rxhash(edev, cqe->bitfields, - cqe->rss_hash, &rxhash_type); - skb_set_hash(tpa_info->skb, rxhash, rxhash_type); - /* Store some information from first CQE */ tpa_info->start_cqe_placement_offset = cqe->placement_offset; tpa_info->start_cqe_bd_len = le16_to_cpu(cqe->len_on_first_bd); @@ -1164,6 +1156,8 @@ static void qede_tpa_start(struct qede_dev *edev, else tpa_info->vlan_tag = 0; + qede_get_rxhash(tpa_info->skb, cqe->bitfields, cqe->rss_hash); + /* This is needed in order to enable forwarding support */ qede_set_gro_params(edev, tpa_info->skb, cqe); @@ -1541,14 +1535,12 @@ static int qede_rx_process_cqe(struct qede_dev *edev, { struct eth_fast_path_rx_reg_cqe *fp_cqe; u16 len, pad, bd_cons_idx, parse_flag; - enum pkt_hash_types rxhash_type; enum eth_rx_cqe_type cqe_type; union eth_rx_cqe *cqe; struct sw_rx_data *bd; struct sk_buff *skb; __le16 flags; u8 csum_flag; - u32 rx_hash; /* Get the CQE from the completion ring */ cqe = (union eth_rx_cqe *)qed_chain_consume(&rxq->rx_comp_ring); @@ -1621,9 +1613,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev, /* The SKB contains all the data. Now prepare meta-magic */ skb->protocol = eth_type_trans(skb, edev->ndev); - rx_hash = qede_get_rxhash(edev, fp_cqe->bitfields, - fp_cqe->rss_hash, &rxhash_type); - skb_set_hash(skb, rx_hash, rxhash_type); + qede_get_rxhash(skb, fp_cqe->bitfields, fp_cqe->rss_hash); qede_set_skb_csum(skb, csum_flag); skb_record_rx_queue(skb, rxq->rxq_id); -- cgit v1.1 From 9eb22357d568aee18f7ce4d0797d96fe7fcd2f71 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:08 +0200 Subject: qede: Better utilize the qede_[rt]x_queue Improve the cacheline usage of both queues by reordering - This reduces the cachelines required for egress datapath processing from 3 to 2 and those required by ingress datapath processing by 2. It also changes a couple of datapath related functions that currently require either the fastpath or the qede_dev, changing them to be based on the tx/rx queue instead. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 79 ++++++----- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 4 +- drivers/net/ethernet/qlogic/qede/qede_main.c | 166 +++++++++++------------- 3 files changed, 124 insertions(+), 125 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index c213576..ec372da 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -243,27 +243,33 @@ struct qede_agg_info { }; struct qede_rx_queue { - __le16 *hw_cons_ptr; - struct sw_rx_data *sw_rx_ring; - u16 sw_rx_cons; - u16 sw_rx_prod; - struct qed_chain rx_bd_ring; - struct qed_chain rx_comp_ring; - void __iomem *hw_rxq_prod_addr; + __le16 *hw_cons_ptr; + void __iomem *hw_rxq_prod_addr; - /* GRO */ - struct qede_agg_info tpa_info[ETH_TPA_MAX_AGGS_NUM]; + /* Required for the allocation of replacement buffers */ + struct device *dev; + + u16 sw_rx_cons; + u16 sw_rx_prod; - int rx_buf_size; - unsigned int rx_buf_seg_size; + u16 num_rx_buffers; /* Slowpath */ + u8 rxq_id; - u16 num_rx_buffers; - u16 rxq_id; + u32 rx_buf_size; + u32 rx_buf_seg_size; - u64 rcv_pkts; - u64 rx_hw_errors; - u64 rx_alloc_errors; - u64 rx_ip_frags; + u64 rcv_pkts; + + struct sw_rx_data *sw_rx_ring; + struct qed_chain rx_bd_ring; + struct qed_chain rx_comp_ring ____cacheline_aligned; + + /* GRO */ + struct qede_agg_info tpa_info[ETH_TPA_MAX_AGGS_NUM]; + + u64 rx_hw_errors; + u64 rx_alloc_errors; + u64 rx_ip_frags; void *handle; }; @@ -281,22 +287,28 @@ struct sw_tx_bd { }; struct qede_tx_queue { - int index; /* Queue index */ - __le16 *hw_cons_ptr; - struct sw_tx_bd *sw_tx_ring; - u16 sw_tx_cons; - u16 sw_tx_prod; - struct qed_chain tx_pbl; - void __iomem *doorbell_addr; - union db_prod tx_db; - - u16 num_tx_buffers; - u64 xmit_pkts; - u64 stopped_cnt; - - bool is_legacy; - void *handle; + bool is_legacy; + u16 sw_tx_cons; + u16 sw_tx_prod; + u16 num_tx_buffers; /* Slowpath only */ + u64 xmit_pkts; + u64 stopped_cnt; + + __le16 *hw_cons_ptr; + + /* Needed for the mapping of packets */ + struct device *dev; + + void __iomem *doorbell_addr; + union db_prod tx_db; + int index; /* Slowpath only */ + + struct sw_tx_bd *sw_tx_ring; + struct qed_chain tx_pbl; + + /* Slowpath; Should be kept in end [unless missing padding] */ + void *handle; }; #define BD_UNMAP_ADDR(bd) HILO_U64(le32_to_cpu((bd)->addr.hi), \ @@ -363,8 +375,7 @@ void __qede_lock(struct qede_dev *edev); void __qede_unlock(struct qede_dev *edev); bool qede_has_rx_work(struct qede_rx_queue *rxq); int qede_txq_has_work(struct qede_tx_queue *txq); -void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, struct qede_dev *edev, - u8 count); +void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, u8 count); void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq); #define RX_RING_SIZE_POW 13 diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index ef8c327..60a2e58 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -1337,13 +1337,13 @@ static int qede_selftest_receive_traffic(struct qede_dev *edev) break; } - qede_recycle_rx_bd_ring(rxq, edev, 1); + qede_recycle_rx_bd_ring(rxq, 1); qed_chain_recycle_consumed(&rxq->rx_comp_ring); break; } DP_INFO(edev, "Not the transmitted packet\n"); - qede_recycle_rx_bd_ring(rxq, edev, 1); + qede_recycle_rx_bd_ring(rxq, 1); qed_chain_recycle_consumed(&rxq->rx_comp_ring); } diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index c9cae3e..78beef2 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -96,8 +96,6 @@ static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id); static void qede_remove(struct pci_dev *pdev); static void qede_shutdown(struct pci_dev *pdev); -static int qede_alloc_rx_buffer(struct qede_dev *edev, - struct qede_rx_queue *rxq); static void qede_link_update(void *dev, struct qed_link_output *link); /* The qede lock is used to protect driver state change and driver flows that @@ -355,8 +353,7 @@ static int qede_free_tx_pkt(struct qede_dev *edev, } /* Unmap the data and free skb when mapping failed during start_xmit */ -static void qede_free_failed_tx_pkt(struct qede_dev *edev, - struct qede_tx_queue *txq, +static void qede_free_failed_tx_pkt(struct qede_tx_queue *txq, struct eth_tx_1st_bd *first_bd, int nbd, bool data_split) { @@ -378,7 +375,7 @@ static void qede_free_failed_tx_pkt(struct qede_dev *edev, nbd--; } - dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd), + dma_unmap_single(txq->dev, BD_UNMAP_ADDR(first_bd), BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE); /* Unmap the data of the skb frags */ @@ -386,7 +383,7 @@ static void qede_free_failed_tx_pkt(struct qede_dev *edev, tx_data_bd = (struct eth_tx_bd *) qed_chain_produce(&txq->tx_pbl); if (tx_data_bd->nbytes) - dma_unmap_page(&edev->pdev->dev, + dma_unmap_page(txq->dev, BD_UNMAP_ADDR(tx_data_bd), BD_UNMAP_LEN(tx_data_bd), DMA_TO_DEVICE); } @@ -401,8 +398,7 @@ static void qede_free_failed_tx_pkt(struct qede_dev *edev, txq->sw_tx_ring[idx].flags = 0; } -static u32 qede_xmit_type(struct qede_dev *edev, - struct sk_buff *skb, int *ipv6_ext) +static u32 qede_xmit_type(struct sk_buff *skb, int *ipv6_ext) { u32 rc = XMIT_L4_CSUM; __be16 l3_proto; @@ -469,18 +465,16 @@ static void qede_set_params_for_ipv6_ext(struct sk_buff *skb, second_bd->data.bitfields2 = cpu_to_le16(bd2_bits2); } -static int map_frag_to_bd(struct qede_dev *edev, +static int map_frag_to_bd(struct qede_tx_queue *txq, skb_frag_t *frag, struct eth_tx_bd *bd) { dma_addr_t mapping; /* Map skb non-linear frag data for DMA */ - mapping = skb_frag_dma_map(&edev->pdev->dev, frag, 0, + mapping = skb_frag_dma_map(txq->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(&edev->pdev->dev, mapping))) { - DP_NOTICE(edev, "Unable to map frag - dropping packet\n"); + if (unlikely(dma_mapping_error(txq->dev, mapping))) return -ENOMEM; - } /* Setup the data pointer of the frag data */ BD_SET_UNMAP_ADDR_LEN(bd, mapping, skb_frag_size(frag)); @@ -500,8 +494,7 @@ static u16 qede_get_skb_hlen(struct sk_buff *skb, bool is_encap_pkt) /* +2 for 1st BD for headers and 2nd BD for headlen (if required) */ #if ((MAX_SKB_FRAGS + 2) > ETH_TX_MAX_BDS_PER_NON_LSO_PACKET) -static bool qede_pkt_req_lin(struct qede_dev *edev, struct sk_buff *skb, - u8 xmit_type) +static bool qede_pkt_req_lin(struct sk_buff *skb, u8 xmit_type) { int allowed_frags = ETH_TX_MAX_BDS_PER_NON_LSO_PACKET - 1; @@ -565,10 +558,10 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, WARN_ON(qed_chain_get_elem_left(&txq->tx_pbl) < (MAX_SKB_FRAGS + 1)); - xmit_type = qede_xmit_type(edev, skb, &ipv6_ext); + xmit_type = qede_xmit_type(skb, &ipv6_ext); #if ((MAX_SKB_FRAGS + 2) > ETH_TX_MAX_BDS_PER_NON_LSO_PACKET) - if (qede_pkt_req_lin(edev, skb, xmit_type)) { + if (qede_pkt_req_lin(skb, xmit_type)) { if (skb_linearize(skb)) { DP_NOTICE(edev, "SKB linearization failed - silently dropping this SKB\n"); @@ -588,11 +581,11 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, 1 << ETH_TX_1ST_BD_FLAGS_START_BD_SHIFT; /* Map skb linear data for DMA and set in the first BD */ - mapping = dma_map_single(&edev->pdev->dev, skb->data, + mapping = dma_map_single(txq->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(&edev->pdev->dev, mapping))) { + if (unlikely(dma_mapping_error(txq->dev, mapping))) { DP_NOTICE(edev, "SKB mapping failed\n"); - qede_free_failed_tx_pkt(edev, txq, first_bd, 0, false); + qede_free_failed_tx_pkt(txq, first_bd, 0, false); qede_update_tx_producer(txq); return NETDEV_TX_OK; } @@ -716,12 +709,11 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, /* Handle fragmented skb */ /* special handle for frags inside 2nd and 3rd bds.. */ while (tx_data_bd && frag_idx < skb_shinfo(skb)->nr_frags) { - rc = map_frag_to_bd(edev, + rc = map_frag_to_bd(txq, &skb_shinfo(skb)->frags[frag_idx], tx_data_bd); if (rc) { - qede_free_failed_tx_pkt(edev, txq, first_bd, nbd, - data_split); + qede_free_failed_tx_pkt(txq, first_bd, nbd, data_split); qede_update_tx_producer(txq); return NETDEV_TX_OK; } @@ -741,12 +733,11 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, memset(tx_data_bd, 0, sizeof(*tx_data_bd)); - rc = map_frag_to_bd(edev, + rc = map_frag_to_bd(txq, &skb_shinfo(skb)->frags[frag_idx], tx_data_bd); if (rc) { - qede_free_failed_tx_pkt(edev, txq, first_bd, nbd, - data_split); + qede_free_failed_tx_pkt(txq, first_bd, nbd, data_split); qede_update_tx_producer(txq); return NETDEV_TX_OK; } @@ -903,8 +894,7 @@ static inline void qede_rx_bd_ring_consume(struct qede_rx_queue *rxq) /* This function reuses the buffer(from an offset) from * consumer index to producer index in the bd ring */ -static inline void qede_reuse_page(struct qede_dev *edev, - struct qede_rx_queue *rxq, +static inline void qede_reuse_page(struct qede_rx_queue *rxq, struct sw_rx_data *curr_cons) { struct eth_rx_bd *rx_bd_prod = qed_chain_produce(&rxq->rx_bd_ring); @@ -926,27 +916,62 @@ static inline void qede_reuse_page(struct qede_dev *edev, /* In case of allocation failures reuse buffers * from consumer index to produce buffers for firmware */ -void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, - struct qede_dev *edev, u8 count) +void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, u8 count) { struct sw_rx_data *curr_cons; for (; count > 0; count--) { curr_cons = &rxq->sw_rx_ring[rxq->sw_rx_cons & NUM_RX_BDS_MAX]; - qede_reuse_page(edev, rxq, curr_cons); + qede_reuse_page(rxq, curr_cons); qede_rx_bd_ring_consume(rxq); } } -static inline int qede_realloc_rx_buffer(struct qede_dev *edev, - struct qede_rx_queue *rxq, +static int qede_alloc_rx_buffer(struct qede_rx_queue *rxq) +{ + struct sw_rx_data *sw_rx_data; + struct eth_rx_bd *rx_bd; + dma_addr_t mapping; + struct page *data; + + data = alloc_pages(GFP_ATOMIC, 0); + if (unlikely(!data)) + return -ENOMEM; + + /* Map the entire page as it would be used + * for multiple RX buffer segment size mapping. + */ + mapping = dma_map_page(rxq->dev, data, 0, + PAGE_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(rxq->dev, mapping))) { + __free_page(data); + return -ENOMEM; + } + + sw_rx_data = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX]; + sw_rx_data->page_offset = 0; + sw_rx_data->data = data; + sw_rx_data->mapping = mapping; + + /* Advance PROD and get BD pointer */ + rx_bd = (struct eth_rx_bd *)qed_chain_produce(&rxq->rx_bd_ring); + WARN_ON(!rx_bd); + rx_bd->addr.hi = cpu_to_le32(upper_32_bits(mapping)); + rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping)); + + rxq->sw_rx_prod++; + + return 0; +} + +static inline int qede_realloc_rx_buffer(struct qede_rx_queue *rxq, struct sw_rx_data *curr_cons) { /* Move to the next segment in the page */ curr_cons->page_offset += rxq->rx_buf_seg_size; if (curr_cons->page_offset == PAGE_SIZE) { - if (unlikely(qede_alloc_rx_buffer(edev, rxq))) { + if (unlikely(qede_alloc_rx_buffer(rxq))) { /* Since we failed to allocate new buffer * current buffer can be used again. */ @@ -955,7 +980,7 @@ static inline int qede_realloc_rx_buffer(struct qede_dev *edev, return -ENOMEM; } - dma_unmap_page(&edev->pdev->dev, curr_cons->mapping, + dma_unmap_page(rxq->dev, curr_cons->mapping, PAGE_SIZE, DMA_FROM_DEVICE); } else { /* Increment refcount of the page as we don't want @@ -963,7 +988,7 @@ static inline int qede_realloc_rx_buffer(struct qede_dev *edev, * which can be recycled multiple times by the driver. */ page_ref_inc(curr_cons->data); - qede_reuse_page(edev, rxq, curr_cons); + qede_reuse_page(rxq, curr_cons); } return 0; @@ -1026,6 +1051,7 @@ static void qede_set_skb_csum(struct sk_buff *skb, u8 csum_flag) static inline void qede_skb_receive(struct qede_dev *edev, struct qede_fastpath *fp, + struct qede_rx_queue *rxq, struct sk_buff *skb, u16 vlan_tag) { if (vlan_tag) @@ -1068,7 +1094,7 @@ static int qede_fill_frag_skb(struct qede_dev *edev, current_bd->data, current_bd->page_offset, len_on_bd); - if (unlikely(qede_realloc_rx_buffer(edev, rxq, current_bd))) { + if (unlikely(qede_realloc_rx_buffer(rxq, current_bd))) { /* Incr page ref count to reuse on allocation failure * so that it doesn't get freed while freeing SKB. */ @@ -1087,7 +1113,8 @@ static int qede_fill_frag_skb(struct qede_dev *edev, out: tpa_info->state = QEDE_AGG_STATE_ERROR; - qede_recycle_rx_bd_ring(rxq, edev, 1); + qede_recycle_rx_bd_ring(rxq, 1); + return -ENOMEM; } @@ -1239,7 +1266,7 @@ static void qede_gro_receive(struct qede_dev *edev, send_skb: skb_record_rx_queue(skb, fp->rxq->rxq_id); - qede_skb_receive(edev, fp, skb, vlan_tag); + qede_skb_receive(edev, fp, fp->rxq, skb, vlan_tag); } static inline void qede_tpa_cont(struct qede_dev *edev, @@ -1414,7 +1441,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev, if (len + pad <= edev->rx_copybreak) { memcpy(skb_put(skb, len), page_address(page) + pad + offset, len); - qede_reuse_page(edev, rxq, bd); + qede_reuse_page(rxq, bd); goto out; } @@ -1435,7 +1462,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev, skb->data_len -= pull_len; skb->tail += pull_len; - if (unlikely(qede_realloc_rx_buffer(edev, rxq, bd))) { + if (unlikely(qede_realloc_rx_buffer(rxq, bd))) { /* Incr page ref count to reuse on allocation failure so * that it doesn't get freed while freeing SKB [as its * already mapped there]. @@ -1477,7 +1504,7 @@ static int qede_rx_build_jumbo(struct qede_dev *edev, } /* We need a replacement buffer for each BD */ - if (unlikely(qede_alloc_rx_buffer(edev, rxq))) + if (unlikely(qede_alloc_rx_buffer(rxq))) goto out; /* Now that we've allocated the replacement buffer, @@ -1487,7 +1514,7 @@ static int qede_rx_build_jumbo(struct qede_dev *edev, bd = &rxq->sw_rx_ring[bd_cons_idx]; qede_rx_bd_ring_consume(rxq); - dma_unmap_page(&edev->pdev->dev, bd->mapping, + dma_unmap_page(rxq->dev, bd->mapping, PAGE_SIZE, DMA_FROM_DEVICE); skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags++, @@ -1582,7 +1609,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev, "CQE has error, flags = %x, dropping incoming packet\n", parse_flag); rxq->rx_hw_errors++; - qede_recycle_rx_bd_ring(rxq, edev, fp_cqe->bd_num); + qede_recycle_rx_bd_ring(rxq, fp_cqe->bd_num); return 0; } } @@ -1593,7 +1620,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev, skb = qede_rx_allocate_skb(edev, rxq, bd, len, pad); if (!skb) { rxq->rx_alloc_errors++; - qede_recycle_rx_bd_ring(rxq, edev, fp_cqe->bd_num); + qede_recycle_rx_bd_ring(rxq, fp_cqe->bd_num); return 0; } @@ -1605,7 +1632,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev, fp_cqe, len); if (unlikely(unmapped_frags > 0)) { - qede_recycle_rx_bd_ring(rxq, edev, unmapped_frags); + qede_recycle_rx_bd_ring(rxq, unmapped_frags); dev_kfree_skb_any(skb); return 0; } @@ -1618,7 +1645,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev, skb_record_rx_queue(skb, rxq->rxq_id); /* SKB is prepared - pass it to stack */ - qede_skb_receive(edev, fp, skb, le16_to_cpu(fp_cqe->vlan_tag)); + qede_skb_receive(edev, fp, rxq, skb, le16_to_cpu(fp_cqe->vlan_tag)); return 1; } @@ -2875,47 +2902,6 @@ static void qede_free_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq) edev->ops->common->chain_free(edev->cdev, &rxq->rx_comp_ring); } -static int qede_alloc_rx_buffer(struct qede_dev *edev, - struct qede_rx_queue *rxq) -{ - struct sw_rx_data *sw_rx_data; - struct eth_rx_bd *rx_bd; - dma_addr_t mapping; - struct page *data; - - data = alloc_pages(GFP_ATOMIC, 0); - if (unlikely(!data)) { - DP_NOTICE(edev, "Failed to allocate Rx data [page]\n"); - return -ENOMEM; - } - - /* Map the entire page as it would be used - * for multiple RX buffer segment size mapping. - */ - mapping = dma_map_page(&edev->pdev->dev, data, 0, - PAGE_SIZE, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(&edev->pdev->dev, mapping))) { - __free_page(data); - DP_NOTICE(edev, "Failed to map Rx buffer\n"); - return -ENOMEM; - } - - sw_rx_data = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX]; - sw_rx_data->page_offset = 0; - sw_rx_data->data = data; - sw_rx_data->mapping = mapping; - - /* Advance PROD and get BD pointer */ - rx_bd = (struct eth_rx_bd *)qed_chain_produce(&rxq->rx_bd_ring); - WARN_ON(!rx_bd); - rx_bd->addr.hi = cpu_to_le32(upper_32_bits(mapping)); - rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping)); - - rxq->sw_rx_prod++; - - return 0; -} - static int qede_alloc_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq) { dma_addr_t mapping; @@ -3010,7 +2996,7 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq) /* Allocate buffers for the Rx ring */ for (i = 0; i < rxq->num_rx_buffers; i++) { - rc = qede_alloc_rx_buffer(edev, rxq); + rc = qede_alloc_rx_buffer(rxq); if (rc) { DP_ERR(edev, "Rx buffers allocation failed at index %d\n", i); @@ -3151,12 +3137,14 @@ static void qede_init_fp(struct qede_dev *edev) if (fp->type & QEDE_FASTPATH_RX) { fp->rxq->rxq_id = rxq_index++; + fp->rxq->dev = &edev->pdev->dev; } if (fp->type & QEDE_FASTPATH_TX) { fp->txq->index = txq_index++; if (edev->dev_info.is_legacy) fp->txq->is_legacy = 1; + fp->txq->dev = &edev->pdev->dev; } snprintf(fp->name, sizeof(fp->name), "%s-fp-%d", -- cgit v1.1 From 496e051709588f832d7a6a420f44f8642b308a87 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:09 +0200 Subject: qede: Add basic XDP support Add support for the ndo_xdp callback. This patch would support XDP_PASS, XDP_DROP and XDP_ABORTED commands. This also adds a per Rx queue statistic which counts number of packets which didn't reach the stack [due to XDP]. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 9 ++ drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 1 + drivers/net/ethernet/qlogic/qede/qede_main.c | 120 +++++++++++++++++++++++- 3 files changed, 127 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index ec372da..cc14aed 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +188,8 @@ struct qede_dev { bool wol_enabled; struct qede_rdma_dev rdma_info; + + struct bpf_prog *xdp_prog; }; enum QEDE_STATE { @@ -249,6 +252,8 @@ struct qede_rx_queue { /* Required for the allocation of replacement buffers */ struct device *dev; + struct bpf_prog *xdp_prog; + u16 sw_rx_cons; u16 sw_rx_prod; @@ -271,6 +276,8 @@ struct qede_rx_queue { u64 rx_alloc_errors; u64 rx_ip_frags; + u64 xdp_no_pass; + void *handle; }; @@ -325,6 +332,7 @@ struct qede_fastpath { struct qede_dev *edev; #define QEDE_FASTPATH_TX BIT(0) #define QEDE_FASTPATH_RX BIT(1) +#define QEDE_FASTPATH_XDP BIT(2) #define QEDE_FASTPATH_COMBINED (QEDE_FASTPATH_TX | QEDE_FASTPATH_RX) u8 type; u8 id; @@ -358,6 +366,7 @@ struct qede_reload_args { void (*func)(struct qede_dev *edev, struct qede_reload_args *args); union { netdev_features_t features; + struct bpf_prog *new_prog; u16 mtu; } u; }; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 60a2e58..6c70e29 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -32,6 +32,7 @@ static const struct { QEDE_RQSTAT(rx_hw_errors), QEDE_RQSTAT(rx_alloc_errors), QEDE_RQSTAT(rx_ip_frags), + QEDE_RQSTAT(xdp_no_pass), }; #define QEDE_NUM_RQSTATS ARRAY_SIZE(qede_rqstats_arr) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 78beef2..71c68c2 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1418,6 +1418,39 @@ static bool qede_pkt_is_ip_fragmented(struct eth_fast_path_rx_reg_cqe *cqe, return false; } +/* Return true iff packet is to be passed to stack */ +static bool qede_rx_xdp(struct qede_dev *edev, + struct qede_fastpath *fp, + struct qede_rx_queue *rxq, + struct bpf_prog *prog, + struct sw_rx_data *bd, + struct eth_fast_path_rx_reg_cqe *cqe) +{ + u16 len = le16_to_cpu(cqe->len_on_first_bd); + struct xdp_buff xdp; + enum xdp_action act; + + xdp.data = page_address(bd->data) + cqe->placement_offset; + xdp.data_end = xdp.data + len; + act = bpf_prog_run_xdp(prog, &xdp); + + if (act == XDP_PASS) + return true; + + /* Count number of packets not to be passed to stack */ + rxq->xdp_no_pass++; + + switch (act) { + default: + bpf_warn_invalid_xdp_action(act); + case XDP_ABORTED: + case XDP_DROP: + qede_recycle_rx_bd_ring(rxq, cqe->bd_num); + } + + return false; +} + static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev, struct qede_rx_queue *rxq, struct sw_rx_data *bd, u16 len, @@ -1560,6 +1593,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev, struct qede_fastpath *fp, struct qede_rx_queue *rxq) { + struct bpf_prog *xdp_prog = READ_ONCE(rxq->xdp_prog); struct eth_fast_path_rx_reg_cqe *fp_cqe; u16 len, pad, bd_cons_idx, parse_flag; enum eth_rx_cqe_type cqe_type; @@ -1596,6 +1630,11 @@ static int qede_rx_process_cqe(struct qede_dev *edev, len = le16_to_cpu(fp_cqe->len_on_first_bd); pad = fp_cqe->placement_offset; + /* Run eBPF program if one is attached */ + if (xdp_prog) + if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe)) + return 1; + /* If this is an error packet then drop it */ flags = cqe->fast_path_regular.pars_flags.flags; parse_flag = le16_to_cpu(flags); @@ -2226,7 +2265,16 @@ int qede_set_features(struct net_device *dev, netdev_features_t features) args.u.features = features; args.func = &qede_set_features_reload; - qede_reload(edev, &args, false); + /* Make sure that we definitely need to reload. + * In case of an eBPF attached program, there will be no FW + * aggregations, so no need to actually reload. + */ + __qede_lock(edev); + if (edev->xdp_prog) + args.func(edev, &args); + else + qede_reload(edev, &args, true); + __qede_unlock(edev); return 1; } @@ -2338,6 +2386,43 @@ static netdev_features_t qede_features_check(struct sk_buff *skb, return features; } +static void qede_xdp_reload_func(struct qede_dev *edev, + struct qede_reload_args *args) +{ + struct bpf_prog *old; + + old = xchg(&edev->xdp_prog, args->u.new_prog); + if (old) + bpf_prog_put(old); +} + +static int qede_xdp_set(struct qede_dev *edev, struct bpf_prog *prog) +{ + struct qede_reload_args args; + + /* If we're called, there was already a bpf reference increment */ + args.func = &qede_xdp_reload_func; + args.u.new_prog = prog; + qede_reload(edev, &args, false); + + return 0; +} + +static int qede_xdp(struct net_device *dev, struct netdev_xdp *xdp) +{ + struct qede_dev *edev = netdev_priv(dev); + + switch (xdp->command) { + case XDP_SETUP_PROG: + return qede_xdp_set(edev, xdp->prog); + case XDP_QUERY_PROG: + xdp->prog_attached = !!edev->xdp_prog; + return 0; + default: + return -EINVAL; + } +} + static const struct net_device_ops qede_netdev_ops = { .ndo_open = qede_open, .ndo_stop = qede_close, @@ -2363,6 +2448,7 @@ static const struct net_device_ops qede_netdev_ops = { .ndo_udp_tunnel_add = qede_udp_tunnel_add, .ndo_udp_tunnel_del = qede_udp_tunnel_del, .ndo_features_check = qede_features_check, + .ndo_xdp = qede_xdp, }; /* ------------------------------------------------------------------------- @@ -2559,6 +2645,9 @@ static int qede_alloc_fp_array(struct qede_dev *edev) fp->rxq = kzalloc(sizeof(*fp->rxq), GFP_KERNEL); if (!fp->rxq) goto err; + + if (edev->xdp_prog) + fp->type |= QEDE_FASTPATH_XDP; } } @@ -2756,6 +2845,10 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) pci_set_drvdata(pdev, NULL); + /* Release edev's reference to XDP's bpf if such exist */ + if (edev->xdp_prog) + bpf_prog_put(edev->xdp_prog); + free_netdev(ndev); /* Use global ops since we've freed edev */ @@ -2907,6 +3000,10 @@ static int qede_alloc_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq) dma_addr_t mapping; int i; + /* Don't perform FW aggregations in case of XDP */ + if (edev->xdp_prog) + edev->gro_disable = 1; + if (edev->gro_disable) return 0; @@ -2959,8 +3056,13 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq) if (rxq->rx_buf_size > PAGE_SIZE) rxq->rx_buf_size = PAGE_SIZE; - /* Segment size to spilt a page in multiple equal parts */ - rxq->rx_buf_seg_size = roundup_pow_of_two(rxq->rx_buf_size); + /* Segment size to spilt a page in multiple equal parts, + * unless XDP is used in which case we'd use the entire page. + */ + if (!edev->xdp_prog) + rxq->rx_buf_seg_size = roundup_pow_of_two(rxq->rx_buf_size); + else + rxq->rx_buf_seg_size = PAGE_SIZE; /* Allocate the parallel driver ring for Rx buffers */ size = sizeof(*rxq->sw_rx_ring) * RX_RING_SIZE; @@ -3368,6 +3470,9 @@ static int qede_stop_queues(struct qede_dev *edev) return rc; } } + + if (fp->type & QEDE_FASTPATH_XDP) + bpf_prog_put(fp->rxq->xdp_prog); } /* Stop the vport */ @@ -3495,6 +3600,15 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) qede_update_rx_prod(edev, rxq); } + if (fp->type & QEDE_FASTPATH_XDP) { + fp->rxq->xdp_prog = bpf_prog_add(edev->xdp_prog, 1); + if (IS_ERR(fp->rxq->xdp_prog)) { + rc = PTR_ERR(fp->rxq->xdp_prog); + fp->rxq->xdp_prog = NULL; + return rc; + } + } + if (fp->type & QEDE_FASTPATH_TX) { rc = qede_start_txq(edev, fp, fp->txq, i, TX_PI(0)); if (rc) -- cgit v1.1 From cb6aeb07929453d5ae127b536b14f6bd3d4c5942 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 29 Nov 2016 16:47:10 +0200 Subject: qede: Add support for XDP_TX Add support for forwarding via XDP. Once the eBPF is attached, driver would allocate & configure a designated transmission queue meant solely for forwarding packets. Said queue would share the receive-queue's interrupt line, and would have it's own Tx statistics. Infrastructure changes required for this [spread-out through the code]: - Determine the DMA direction of the receive buffers based on the presence of the eBPF program. - Turn the sw Tx ring into a union, as regular/XDP queues have different needs for releasing resources after completion [regular requires the SKB, XDP requires the transmitted page]. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 16 +- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 22 ++- drivers/net/ethernet/qlogic/qede/qede_main.c | 213 ++++++++++++++++++++---- 3 files changed, 216 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index cc14aed..c79dc787 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -258,6 +258,7 @@ struct qede_rx_queue { u16 sw_rx_prod; u16 num_rx_buffers; /* Slowpath */ + u8 data_direction; u8 rxq_id; u32 rx_buf_size; @@ -294,6 +295,7 @@ struct sw_tx_bd { }; struct qede_tx_queue { + u8 is_xdp; bool is_legacy; u16 sw_tx_cons; u16 sw_tx_prod; @@ -310,8 +312,18 @@ struct qede_tx_queue { void __iomem *doorbell_addr; union db_prod tx_db; int index; /* Slowpath only */ +#define QEDE_TXQ_XDP_TO_IDX(edev, txq) ((txq)->index - \ + QEDE_MAX_TSS_CNT(edev)) +#define QEDE_TXQ_IDX_TO_XDP(edev, idx) ((idx) + QEDE_MAX_TSS_CNT(edev)) + + /* Regular Tx requires skb + metadata for release purpose, + * while XDP requires only the pages themselves. + */ + union { + struct sw_tx_bd *skbs; + struct page **pages; + } sw_tx_ring; - struct sw_tx_bd *sw_tx_ring; struct qed_chain tx_pbl; /* Slowpath; Should be kept in end [unless missing padding] */ @@ -336,10 +348,12 @@ struct qede_fastpath { #define QEDE_FASTPATH_COMBINED (QEDE_FASTPATH_TX | QEDE_FASTPATH_RX) u8 type; u8 id; + u8 xdp_xmit; struct napi_struct napi; struct qed_sb_info *sb_info; struct qede_rx_queue *rxq; struct qede_tx_queue *txq; + struct qede_tx_queue *xdp_tx; #define VEC_NAME_SIZE (sizeof(((struct net_device *)0)->name) + 8) char name[VEC_NAME_SIZE]; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 6c70e29..1c48f44 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -165,8 +165,13 @@ static void qede_get_strings_stats_txq(struct qede_dev *edev, int i; for (i = 0; i < QEDE_NUM_TQSTATS; i++) { - sprintf(*buf, "%d: %s", txq->index, - qede_tqstats_arr[i].string); + if (txq->is_xdp) + sprintf(*buf, "%d [XDP]: %s", + QEDE_TXQ_XDP_TO_IDX(edev, txq), + qede_tqstats_arr[i].string); + else + sprintf(*buf, "%d: %s", txq->index, + qede_tqstats_arr[i].string); *buf += ETH_GSTRING_LEN; } } @@ -195,6 +200,9 @@ static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) if (fp->type & QEDE_FASTPATH_RX) qede_get_strings_stats_rxq(edev, fp->rxq, &buf); + if (fp->type & QEDE_FASTPATH_XDP) + qede_get_strings_stats_txq(edev, fp->xdp_tx, &buf); + if (fp->type & QEDE_FASTPATH_TX) qede_get_strings_stats_txq(edev, fp->txq, &buf); } @@ -268,6 +276,9 @@ static void qede_get_ethtool_stats(struct net_device *dev, if (fp->type & QEDE_FASTPATH_RX) qede_get_ethtool_stats_rxq(fp->rxq, &buf); + if (fp->type & QEDE_FASTPATH_XDP) + qede_get_ethtool_stats_txq(fp->xdp_tx, &buf); + if (fp->type & QEDE_FASTPATH_TX) qede_get_ethtool_stats_txq(fp->txq, &buf); } @@ -305,6 +316,9 @@ static int qede_get_sset_count(struct net_device *dev, int stringset) /* Account for the Regular Rx statistics */ num_stats += QEDE_RSS_COUNT(edev) * QEDE_NUM_RQSTATS; + /* Account for XDP statistics [if needed] */ + if (edev->xdp_prog) + num_stats += QEDE_RSS_COUNT(edev) * QEDE_NUM_TQSTATS; return num_stats; case ETH_SS_PRIV_FLAGS: @@ -1216,7 +1230,7 @@ static int qede_selftest_transmit_traffic(struct qede_dev *edev, /* Fill the entry in the SW ring and the BDs in the FW ring */ idx = txq->sw_tx_prod & NUM_TX_BDS_MAX; - txq->sw_tx_ring[idx].skb = skb; + txq->sw_tx_ring.skbs[idx].skb = skb; first_bd = qed_chain_produce(&txq->tx_pbl); memset(first_bd, 0, sizeof(*first_bd)); val = 1 << ETH_TX_1ST_BD_FLAGS_START_BD_SHIFT; @@ -1270,7 +1284,7 @@ static int qede_selftest_transmit_traffic(struct qede_dev *edev, dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd), BD_UNMAP_LEN(first_bd), DMA_TO_DEVICE); txq->sw_tx_cons++; - txq->sw_tx_ring[idx].skb = NULL; + txq->sw_tx_ring.skbs[idx].skb = NULL; return 0; } diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 71c68c2..172ff6d 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -94,6 +94,9 @@ static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id); #define TX_TIMEOUT (5 * HZ) +/* Utilize last protocol index for XDP */ +#define XDP_PI 11 + static void qede_remove(struct pci_dev *pdev); static void qede_shutdown(struct pci_dev *pdev); static void qede_link_update(void *dev, struct qed_link_output *link); @@ -301,12 +304,12 @@ static int qede_free_tx_pkt(struct qede_dev *edev, struct qede_tx_queue *txq, int *len) { u16 idx = txq->sw_tx_cons & NUM_TX_BDS_MAX; - struct sk_buff *skb = txq->sw_tx_ring[idx].skb; + struct sk_buff *skb = txq->sw_tx_ring.skbs[idx].skb; struct eth_tx_1st_bd *first_bd; struct eth_tx_bd *tx_data_bd; int bds_consumed = 0; int nbds; - bool data_split = txq->sw_tx_ring[idx].flags & QEDE_TSO_SPLIT_BD; + bool data_split = txq->sw_tx_ring.skbs[idx].flags & QEDE_TSO_SPLIT_BD; int i, split_bd_len = 0; if (unlikely(!skb)) { @@ -346,8 +349,8 @@ static int qede_free_tx_pkt(struct qede_dev *edev, /* Free skb */ dev_kfree_skb_any(skb); - txq->sw_tx_ring[idx].skb = NULL; - txq->sw_tx_ring[idx].flags = 0; + txq->sw_tx_ring.skbs[idx].skb = NULL; + txq->sw_tx_ring.skbs[idx].flags = 0; return 0; } @@ -358,7 +361,7 @@ static void qede_free_failed_tx_pkt(struct qede_tx_queue *txq, int nbd, bool data_split) { u16 idx = txq->sw_tx_prod & NUM_TX_BDS_MAX; - struct sk_buff *skb = txq->sw_tx_ring[idx].skb; + struct sk_buff *skb = txq->sw_tx_ring.skbs[idx].skb; struct eth_tx_bd *tx_data_bd; int i, split_bd_len = 0; @@ -394,8 +397,8 @@ static void qede_free_failed_tx_pkt(struct qede_tx_queue *txq, /* Free skb */ dev_kfree_skb_any(skb); - txq->sw_tx_ring[idx].skb = NULL; - txq->sw_tx_ring[idx].flags = 0; + txq->sw_tx_ring.skbs[idx].skb = NULL; + txq->sw_tx_ring.skbs[idx].flags = 0; } static u32 qede_xmit_type(struct sk_buff *skb, int *ipv6_ext) @@ -530,6 +533,47 @@ static inline void qede_update_tx_producer(struct qede_tx_queue *txq) mmiowb(); } +static int qede_xdp_xmit(struct qede_dev *edev, struct qede_fastpath *fp, + struct sw_rx_data *metadata, u16 padding, u16 length) +{ + struct qede_tx_queue *txq = fp->xdp_tx; + u16 idx = txq->sw_tx_prod & NUM_TX_BDS_MAX; + struct eth_tx_1st_bd *first_bd; + + if (!qed_chain_get_elem_left(&txq->tx_pbl)) { + txq->stopped_cnt++; + return -ENOMEM; + } + + first_bd = (struct eth_tx_1st_bd *)qed_chain_produce(&txq->tx_pbl); + + memset(first_bd, 0, sizeof(*first_bd)); + first_bd->data.bd_flags.bitfields = + BIT(ETH_TX_1ST_BD_FLAGS_START_BD_SHIFT); + first_bd->data.bitfields |= + (length & ETH_TX_DATA_1ST_BD_PKT_LEN_MASK) << + ETH_TX_DATA_1ST_BD_PKT_LEN_SHIFT; + first_bd->data.nbds = 1; + + /* We can safely ignore the offset, as it's 0 for XDP */ + BD_SET_UNMAP_ADDR_LEN(first_bd, metadata->mapping + padding, length); + + /* Synchronize the buffer back to device, as program [probably] + * has changed it. + */ + dma_sync_single_for_device(&edev->pdev->dev, + metadata->mapping + padding, + length, PCI_DMA_TODEVICE); + + txq->sw_tx_ring.pages[idx] = metadata->data; + txq->sw_tx_prod++; + + /* Mark the fastpath for future XDP doorbell */ + fp->xdp_xmit = 1; + + return 0; +} + /* Main transmit function */ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, struct net_device *ndev) @@ -573,7 +617,7 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, /* Fill the entry in the SW ring and the BDs in the FW ring */ idx = txq->sw_tx_prod & NUM_TX_BDS_MAX; - txq->sw_tx_ring[idx].skb = skb; + txq->sw_tx_ring.skbs[idx].skb = skb; first_bd = (struct eth_tx_1st_bd *) qed_chain_produce(&txq->tx_pbl); memset(first_bd, 0, sizeof(*first_bd)); @@ -693,7 +737,7 @@ static netdev_tx_t qede_start_xmit(struct sk_buff *skb, /* this marks the BD as one that has no * individual mapping */ - txq->sw_tx_ring[idx].flags |= QEDE_TSO_SPLIT_BD; + txq->sw_tx_ring.skbs[idx].flags |= QEDE_TSO_SPLIT_BD; first_bd->nbytes = cpu_to_le16(hlen); @@ -802,6 +846,27 @@ int qede_txq_has_work(struct qede_tx_queue *txq) return hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl); } +static void qede_xdp_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq) +{ + struct eth_tx_1st_bd *bd; + u16 hw_bd_cons; + + hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr); + barrier(); + + while (hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl)) { + bd = (struct eth_tx_1st_bd *)qed_chain_consume(&txq->tx_pbl); + + dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(bd), + PAGE_SIZE, DMA_BIDIRECTIONAL); + __free_page(txq->sw_tx_ring.pages[txq->sw_tx_cons & + NUM_TX_BDS_MAX]); + + txq->sw_tx_cons++; + txq->xmit_pkts++; + } +} + static int qede_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq) { struct netdev_queue *netdev_txq; @@ -942,7 +1007,7 @@ static int qede_alloc_rx_buffer(struct qede_rx_queue *rxq) * for multiple RX buffer segment size mapping. */ mapping = dma_map_page(rxq->dev, data, 0, - PAGE_SIZE, DMA_FROM_DEVICE); + PAGE_SIZE, rxq->data_direction); if (unlikely(dma_mapping_error(rxq->dev, mapping))) { __free_page(data); return -ENOMEM; @@ -981,7 +1046,7 @@ static inline int qede_realloc_rx_buffer(struct qede_rx_queue *rxq, } dma_unmap_page(rxq->dev, curr_cons->mapping, - PAGE_SIZE, DMA_FROM_DEVICE); + PAGE_SIZE, rxq->data_direction); } else { /* Increment refcount of the page as we don't want * network stack to take the ownership of the page @@ -1441,6 +1506,26 @@ static bool qede_rx_xdp(struct qede_dev *edev, rxq->xdp_no_pass++; switch (act) { + case XDP_TX: + /* We need the replacement buffer before transmit. */ + if (qede_alloc_rx_buffer(rxq)) { + qede_recycle_rx_bd_ring(rxq, 1); + return false; + } + + /* Now if there's a transmission problem, we'd still have to + * throw current buffer, as replacement was already allocated. + */ + if (qede_xdp_xmit(edev, fp, bd, cqe->placement_offset, len)) { + dma_unmap_page(rxq->dev, bd->mapping, + PAGE_SIZE, DMA_BIDIRECTIONAL); + __free_page(bd->data); + } + + /* Regardless, we've consumed an Rx BD */ + qede_rx_bd_ring_consume(rxq); + return false; + default: bpf_warn_invalid_xdp_action(act); case XDP_ABORTED: @@ -1740,6 +1825,10 @@ static bool qede_poll_is_more_work(struct qede_fastpath *fp) if (qede_has_rx_work(fp->rxq)) return true; + if (fp->type & QEDE_FASTPATH_XDP) + if (qede_txq_has_work(fp->xdp_tx)) + return true; + if (likely(fp->type & QEDE_FASTPATH_TX)) if (qede_txq_has_work(fp->txq)) return true; @@ -1757,6 +1846,9 @@ static int qede_poll(struct napi_struct *napi, int budget) if (likely(fp->type & QEDE_FASTPATH_TX) && qede_txq_has_work(fp->txq)) qede_tx_int(edev, fp->txq); + if ((fp->type & QEDE_FASTPATH_XDP) && qede_txq_has_work(fp->xdp_tx)) + qede_xdp_tx_int(edev, fp->xdp_tx); + rx_work_done = (likely(fp->type & QEDE_FASTPATH_RX) && qede_has_rx_work(fp->rxq)) ? qede_rx_int(fp, budget) : 0; @@ -1771,6 +1863,14 @@ static int qede_poll(struct napi_struct *napi, int budget) } } + if (fp->xdp_xmit) { + u16 xdp_prod = qed_chain_get_prod_idx(&fp->xdp_tx->tx_pbl); + + fp->xdp_xmit = 0; + fp->xdp_tx->tx_db.data.bd_prod = cpu_to_le16(xdp_prod); + qede_update_tx_producer(fp->xdp_tx); + } + return rx_work_done; } @@ -2586,6 +2686,7 @@ static void qede_free_fp_array(struct qede_dev *edev) kfree(fp->sb_info); kfree(fp->rxq); + kfree(fp->xdp_tx); kfree(fp->txq); } kfree(edev->fp_array); @@ -2646,8 +2747,13 @@ static int qede_alloc_fp_array(struct qede_dev *edev) if (!fp->rxq) goto err; - if (edev->xdp_prog) + if (edev->xdp_prog) { + fp->xdp_tx = kzalloc(sizeof(*fp->xdp_tx), + GFP_KERNEL); + if (!fp->xdp_tx) + goto err; fp->type |= QEDE_FASTPATH_XDP; + } } } @@ -2694,9 +2800,9 @@ static void qede_update_pf_params(struct qed_dev *cdev) { struct qed_pf_params pf_params; - /* 64 rx + 64 tx */ + /* 64 rx + 64 tx + 64 XDP */ memset(&pf_params, 0, sizeof(struct qed_pf_params)); - pf_params.eth_pf_params.num_cons = 128; + pf_params.eth_pf_params.num_cons = 192; qed_ops->common->update_pf_params(cdev, &pf_params); } @@ -2953,7 +3059,7 @@ static void qede_free_rx_buffers(struct qede_dev *edev, data = rx_buf->data; dma_unmap_page(&edev->pdev->dev, - rx_buf->mapping, PAGE_SIZE, DMA_FROM_DEVICE); + rx_buf->mapping, PAGE_SIZE, rxq->data_direction); rx_buf->data = NULL; __free_page(data); @@ -3114,7 +3220,10 @@ err: static void qede_free_mem_txq(struct qede_dev *edev, struct qede_tx_queue *txq) { /* Free the parallel SW ring */ - kfree(txq->sw_tx_ring); + if (txq->is_xdp) + kfree(txq->sw_tx_ring.pages); + else + kfree(txq->sw_tx_ring.skbs); /* Free the real RQ ring used by FW */ edev->ops->common->chain_free(edev->cdev, &txq->tx_pbl); @@ -3123,17 +3232,22 @@ static void qede_free_mem_txq(struct qede_dev *edev, struct qede_tx_queue *txq) /* This function allocates all memory needed per Tx queue */ static int qede_alloc_mem_txq(struct qede_dev *edev, struct qede_tx_queue *txq) { - int size, rc; union eth_tx_bd_types *p_virt; + int size, rc; txq->num_tx_buffers = edev->q_num_tx_buffers; /* Allocate the parallel driver ring for Tx buffers */ - size = sizeof(*txq->sw_tx_ring) * TX_RING_SIZE; - txq->sw_tx_ring = kzalloc(size, GFP_KERNEL); - if (!txq->sw_tx_ring) { - DP_NOTICE(edev, "Tx buffers ring allocation failed\n"); - goto err; + if (txq->is_xdp) { + size = sizeof(*txq->sw_tx_ring.pages) * TX_RING_SIZE; + txq->sw_tx_ring.pages = kzalloc(size, GFP_KERNEL); + if (!txq->sw_tx_ring.pages) + goto err; + } else { + size = sizeof(*txq->sw_tx_ring.skbs) * TX_RING_SIZE; + txq->sw_tx_ring.skbs = kzalloc(size, GFP_KERNEL); + if (!txq->sw_tx_ring.skbs) + goto err; } rc = edev->ops->common->chain_alloc(edev->cdev, @@ -3169,26 +3283,31 @@ static void qede_free_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp) */ static int qede_alloc_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp) { - int rc; + int rc = 0; rc = qede_alloc_mem_sb(edev, fp->sb_info, fp->id); if (rc) - goto err; + goto out; if (fp->type & QEDE_FASTPATH_RX) { rc = qede_alloc_mem_rxq(edev, fp->rxq); if (rc) - goto err; + goto out; + } + + if (fp->type & QEDE_FASTPATH_XDP) { + rc = qede_alloc_mem_txq(edev, fp->xdp_tx); + if (rc) + goto out; } if (fp->type & QEDE_FASTPATH_TX) { rc = qede_alloc_mem_txq(edev, fp->txq); if (rc) - goto err; + goto out; } - return 0; -err: +out: return rc; } @@ -3236,9 +3355,20 @@ static void qede_init_fp(struct qede_dev *edev) fp->edev = edev; fp->id = queue_id; + if (fp->type & QEDE_FASTPATH_XDP) { + fp->xdp_tx->index = QEDE_TXQ_IDX_TO_XDP(edev, + rxq_index); + fp->xdp_tx->is_xdp = 1; + } if (fp->type & QEDE_FASTPATH_RX) { fp->rxq->rxq_id = rxq_index++; + + /* Determine how to map buffers for this queue */ + if (fp->type & QEDE_FASTPATH_XDP) + fp->rxq->data_direction = DMA_BIDIRECTIONAL; + else + fp->rxq->data_direction = DMA_FROM_DEVICE; fp->rxq->dev = &edev->pdev->dev; } @@ -3449,6 +3579,12 @@ static int qede_stop_queues(struct qede_dev *edev) if (rc) return rc; } + + if (fp->type & QEDE_FASTPATH_XDP) { + rc = qede_drain_txq(edev, fp->xdp_tx, true); + if (rc) + return rc; + } } /* Stop all Queues in reverse order */ @@ -3471,8 +3607,14 @@ static int qede_stop_queues(struct qede_dev *edev) } } - if (fp->type & QEDE_FASTPATH_XDP) + /* Stop the XDP forwarding queue */ + if (fp->type & QEDE_FASTPATH_XDP) { + rc = qede_stop_txq(edev, fp->xdp_tx, i); + if (rc) + return rc; + bpf_prog_put(fp->rxq->xdp_prog); + } } /* Stop the vport */ @@ -3496,7 +3638,14 @@ static int qede_start_txq(struct qede_dev *edev, memset(¶ms, 0, sizeof(params)); memset(&ret_params, 0, sizeof(ret_params)); - params.queue_id = txq->index; + /* Let the XDP queue share the queue-zone with one of the regular txq. + * We don't really care about its coalescing. + */ + if (txq->is_xdp) + params.queue_id = QEDE_TXQ_XDP_TO_IDX(edev, txq); + else + params.queue_id = txq->index; + params.sb = fp->sb_info->igu_sb_id; params.sb_idx = sb_idx; @@ -3601,6 +3750,10 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats) } if (fp->type & QEDE_FASTPATH_XDP) { + rc = qede_start_txq(edev, fp, fp->xdp_tx, i, XDP_PI); + if (rc) + return rc; + fp->rxq->xdp_prog = bpf_prog_add(edev->xdp_prog, 1); if (IS_ERR(fp->rxq->xdp_prog)) { rc = PTR_ERR(fp->rxq->xdp_prog); -- cgit v1.1 From 0fc6432cc78d8dc683474d8e28ea30543ae033b3 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Tue, 29 Nov 2016 17:00:47 +0200 Subject: net: ethernet: ti: davinci_cpdma: add weight function for channels The weight of a channel is needed to split descriptors between channels. The weight can depend on maximum rate of channels, maximum rate of an interface or other reasons. The channel weight is in percentage and is independent for rx and tx channels. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 124 +++++++++++++++++++++++++++++--- drivers/net/ethernet/ti/davinci_cpdma.h | 1 + 2 files changed, 115 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 56708a7..87456a9 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -122,6 +122,7 @@ struct cpdma_chan { struct cpdma_chan_stats stats; /* offsets into dmaregs */ int int_set, int_clear, td; + int weight; }; struct cpdma_control_info { @@ -474,29 +475,131 @@ u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr) } EXPORT_SYMBOL_GPL(cpdma_ctrl_txchs_state); +static void cpdma_chan_set_descs(struct cpdma_ctlr *ctlr, + int rx, int desc_num, + int per_ch_desc) +{ + struct cpdma_chan *chan, *most_chan = NULL; + int desc_cnt = desc_num; + int most_dnum = 0; + int min, max, i; + + if (!desc_num) + return; + + if (rx) { + min = rx_chan_num(0); + max = rx_chan_num(CPDMA_MAX_CHANNELS); + } else { + min = tx_chan_num(0); + max = tx_chan_num(CPDMA_MAX_CHANNELS); + } + + for (i = min; i < max; i++) { + chan = ctlr->channels[i]; + if (!chan) + continue; + + if (chan->weight) + chan->desc_num = (chan->weight * desc_num) / 100; + else + chan->desc_num = per_ch_desc; + + desc_cnt -= chan->desc_num; + + if (most_dnum < chan->desc_num) { + most_dnum = chan->desc_num; + most_chan = chan; + } + } + /* use remains */ + most_chan->desc_num += desc_cnt; +} + /** * cpdma_chan_split_pool - Splits ctrl pool between all channels. * Has to be called under ctlr lock */ -static void cpdma_chan_split_pool(struct cpdma_ctlr *ctlr) +static int cpdma_chan_split_pool(struct cpdma_ctlr *ctlr) { + int tx_per_ch_desc = 0, rx_per_ch_desc = 0; struct cpdma_desc_pool *pool = ctlr->pool; + int free_rx_num = 0, free_tx_num = 0; + int rx_weight = 0, tx_weight = 0; + int tx_desc_num, rx_desc_num; struct cpdma_chan *chan; - int ch_desc_num; - int i; + int i, tx_num = 0; if (!ctlr->chan_num) - return; - - /* calculate average size of pool slice */ - ch_desc_num = pool->num_desc / ctlr->chan_num; + return 0; - /* split ctlr pool */ for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { chan = ctlr->channels[i]; - if (chan) - chan->desc_num = ch_desc_num; + if (!chan) + continue; + + if (is_rx_chan(chan)) { + if (!chan->weight) + free_rx_num++; + rx_weight += chan->weight; + } else { + if (!chan->weight) + free_tx_num++; + tx_weight += chan->weight; + tx_num++; + } + } + + if (rx_weight > 100 || tx_weight > 100) + return -EINVAL; + + tx_desc_num = (tx_num * pool->num_desc) / ctlr->chan_num; + rx_desc_num = pool->num_desc - tx_desc_num; + + if (free_tx_num) { + tx_per_ch_desc = tx_desc_num - (tx_weight * tx_desc_num) / 100; + tx_per_ch_desc /= free_tx_num; + } + if (free_rx_num) { + rx_per_ch_desc = rx_desc_num - (rx_weight * rx_desc_num) / 100; + rx_per_ch_desc /= free_rx_num; } + + cpdma_chan_set_descs(ctlr, 0, tx_desc_num, tx_per_ch_desc); + cpdma_chan_set_descs(ctlr, 1, rx_desc_num, rx_per_ch_desc); + + return 0; +} + +/* cpdma_chan_set_weight - set weight of a channel in percentage. + * Tx and Rx channels have separate weights. That is 100% for RX + * and 100% for Tx. The weight is used to split cpdma resources + * in correct proportion required by the channels, including number + * of descriptors. The channel rate is not enough to know the + * weight of a channel as the maximum rate of an interface is needed. + * If weight = 0, then channel uses rest of descriptors leaved by + * weighted channels. + */ +int cpdma_chan_set_weight(struct cpdma_chan *ch, int weight) +{ + struct cpdma_ctlr *ctlr = ch->ctlr; + unsigned long flags, ch_flags; + int ret; + + spin_lock_irqsave(&ctlr->lock, flags); + spin_lock_irqsave(&ch->lock, ch_flags); + if (ch->weight == weight) { + spin_unlock_irqrestore(&ch->lock, ch_flags); + spin_unlock_irqrestore(&ctlr->lock, flags); + return 0; + } + ch->weight = weight; + spin_unlock_irqrestore(&ch->lock, ch_flags); + + /* re-split pool using new channel weight */ + ret = cpdma_chan_split_pool(ctlr); + spin_unlock_irqrestore(&ctlr->lock, flags); + return ret; } struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, @@ -527,6 +630,7 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, chan->chan_num = chan_num; chan->handler = handler; chan->desc_num = ctlr->pool->num_desc / 2; + chan->weight = 0; if (is_rx_chan(chan)) { chan->hdp = ctlr->params.rxhdp + offset; diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h index a07b22b..629020c 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.h +++ b/drivers/net/ethernet/ti/davinci_cpdma.h @@ -90,6 +90,7 @@ int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable); u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr); u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr); bool cpdma_check_free_tx_desc(struct cpdma_chan *chan); +int cpdma_chan_set_weight(struct cpdma_chan *ch, int weight); enum cpdma_control { CPDMA_CMD_IDLE, /* write-only */ -- cgit v1.1 From 8f32b90981dcdb355516fb95953133f8d4e6b11d Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Tue, 29 Nov 2016 17:00:48 +0200 Subject: net: ethernet: ti: davinci_cpdma: add set rate for a channel The cpdma has 8 rate limited tx channels. This patch adds ability for cpdma driver to use 8 tx h/w shapers. If at least one channel is not rate limited then it must have higher number, this is because the rate limited channels have to have higher priority then not rate limited channels. The channel priority is set in low-hi direction already, so that when a new channel is added with ethtool and it doesn't have rate yet, it cannot affect on rate limited channels. It can be useful for TSN streams and just in cases when h/w rate limited channels are needed. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 329 +++++++++++++++++++++++++++----- drivers/net/ethernet/ti/davinci_cpdma.h | 5 + 2 files changed, 289 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 87456a9..c776e45 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -32,6 +32,7 @@ #define CPDMA_RXCONTROL 0x14 #define CPDMA_SOFTRESET 0x1c #define CPDMA_RXTEARDOWN 0x18 +#define CPDMA_TX_PRI0_RATE 0x30 #define CPDMA_TXINTSTATRAW 0x80 #define CPDMA_TXINTSTATMASKED 0x84 #define CPDMA_TXINTMASKSET 0x88 @@ -68,6 +69,8 @@ #define CPDMA_TEARDOWN_VALUE 0xfffffffc +#define CPDMA_MAX_RLIM_CNT 16384 + struct cpdma_desc { /* hardware fields */ u32 hw_next; @@ -123,6 +126,8 @@ struct cpdma_chan { /* offsets into dmaregs */ int int_set, int_clear, td; int weight; + u32 rate_factor; + u32 rate; }; struct cpdma_control_info { @@ -135,6 +140,7 @@ struct cpdma_control_info { }; static struct cpdma_control_info controls[] = { + [CPDMA_TX_RLIM] = {CPDMA_DMACONTROL, 8, 0xffff, ACCESS_RW}, [CPDMA_CMD_IDLE] = {CPDMA_DMACONTROL, 3, 1, ACCESS_WO}, [CPDMA_COPY_ERROR_FRAMES] = {CPDMA_DMACONTROL, 4, 1, ACCESS_RW}, [CPDMA_RX_OFF_LEN_UPDATE] = {CPDMA_DMACONTROL, 2, 1, ACCESS_RW}, @@ -302,6 +308,186 @@ static int _cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value) return 0; } +static int _cpdma_control_get(struct cpdma_ctlr *ctlr, int control) +{ + struct cpdma_control_info *info = &controls[control]; + int ret; + + if (!ctlr->params.has_ext_regs) + return -ENOTSUPP; + + if (ctlr->state != CPDMA_STATE_ACTIVE) + return -EINVAL; + + if (control < 0 || control >= ARRAY_SIZE(controls)) + return -ENOENT; + + if ((info->access & ACCESS_RO) != ACCESS_RO) + return -EPERM; + + ret = (dma_reg_read(ctlr, info->reg) >> info->shift) & info->mask; + return ret; +} + +/* cpdma_chan_set_chan_shaper - set shaper for a channel + * Has to be called under ctlr lock + */ +static int cpdma_chan_set_chan_shaper(struct cpdma_chan *chan) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + u32 rate_reg; + u32 rmask; + int ret; + + if (!chan->rate) + return 0; + + rate_reg = CPDMA_TX_PRI0_RATE + 4 * chan->chan_num; + dma_reg_write(ctlr, rate_reg, chan->rate_factor); + + rmask = _cpdma_control_get(ctlr, CPDMA_TX_RLIM); + rmask |= chan->mask; + + ret = _cpdma_control_set(ctlr, CPDMA_TX_RLIM, rmask); + return ret; +} + +static int cpdma_chan_on(struct cpdma_chan *chan) +{ + struct cpdma_ctlr *ctlr = chan->ctlr; + struct cpdma_desc_pool *pool = ctlr->pool; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + if (chan->state != CPDMA_STATE_IDLE) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EBUSY; + } + if (ctlr->state != CPDMA_STATE_ACTIVE) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EINVAL; + } + dma_reg_write(ctlr, chan->int_set, chan->mask); + chan->state = CPDMA_STATE_ACTIVE; + if (chan->head) { + chan_write(chan, hdp, desc_phys(pool, chan->head)); + if (chan->rxfree) + chan_write(chan, rxfree, chan->count); + } + + spin_unlock_irqrestore(&chan->lock, flags); + return 0; +} + +/* cpdma_chan_fit_rate - set rate for a channel and check if it's possible. + * rmask - mask of rate limited channels + * Returns min rate in Kb/s + */ +static int cpdma_chan_fit_rate(struct cpdma_chan *ch, u32 rate, + u32 *rmask, int *prio_mode) +{ + struct cpdma_ctlr *ctlr = ch->ctlr; + struct cpdma_chan *chan; + u32 old_rate = ch->rate; + u32 new_rmask = 0; + int rlim = 1; + int i; + + *prio_mode = 0; + for (i = tx_chan_num(0); i < tx_chan_num(CPDMA_MAX_CHANNELS); i++) { + chan = ctlr->channels[i]; + if (!chan) { + rlim = 0; + continue; + } + + if (chan == ch) + chan->rate = rate; + + if (chan->rate) { + if (rlim) { + new_rmask |= chan->mask; + } else { + ch->rate = old_rate; + dev_err(ctlr->dev, "Prev channel of %dch is not rate limited\n", + chan->chan_num); + return -EINVAL; + } + } else { + *prio_mode = 1; + rlim = 0; + } + } + + *rmask = new_rmask; + return 0; +} + +static u32 cpdma_chan_set_factors(struct cpdma_ctlr *ctlr, + struct cpdma_chan *ch) +{ + u32 delta = UINT_MAX, prev_delta = UINT_MAX, best_delta = UINT_MAX; + u32 best_send_cnt = 0, best_idle_cnt = 0; + u32 new_rate, best_rate = 0, rate_reg; + u64 send_cnt, idle_cnt; + u32 min_send_cnt, freq; + u64 divident, divisor; + + if (!ch->rate) { + ch->rate_factor = 0; + goto set_factor; + } + + freq = ctlr->params.bus_freq_mhz * 1000 * 32; + if (!freq) { + dev_err(ctlr->dev, "The bus frequency is not set\n"); + return -EINVAL; + } + + min_send_cnt = freq - ch->rate; + send_cnt = DIV_ROUND_UP(min_send_cnt, ch->rate); + while (send_cnt <= CPDMA_MAX_RLIM_CNT) { + divident = ch->rate * send_cnt; + divisor = min_send_cnt; + idle_cnt = DIV_ROUND_CLOSEST_ULL(divident, divisor); + + divident = freq * idle_cnt; + divisor = idle_cnt + send_cnt; + new_rate = DIV_ROUND_CLOSEST_ULL(divident, divisor); + + delta = new_rate >= ch->rate ? new_rate - ch->rate : delta; + if (delta < best_delta) { + best_delta = delta; + best_send_cnt = send_cnt; + best_idle_cnt = idle_cnt; + best_rate = new_rate; + + if (!delta) + break; + } + + if (prev_delta >= delta) { + prev_delta = delta; + send_cnt++; + continue; + } + + idle_cnt++; + divident = freq * idle_cnt; + send_cnt = DIV_ROUND_CLOSEST_ULL(divident, ch->rate); + send_cnt -= idle_cnt; + prev_delta = UINT_MAX; + } + + ch->rate = best_rate; + ch->rate_factor = best_send_cnt | (best_idle_cnt << 16); + +set_factor: + rate_reg = CPDMA_TX_PRI0_RATE + 4 * ch->chan_num; + dma_reg_write(ctlr, rate_reg, ch->rate_factor); + return 0; +} + struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params) { struct cpdma_ctlr *ctlr; @@ -332,8 +518,9 @@ EXPORT_SYMBOL_GPL(cpdma_ctlr_create); int cpdma_ctlr_start(struct cpdma_ctlr *ctlr) { + struct cpdma_chan *chan; unsigned long flags; - int i; + int i, prio_mode; spin_lock_irqsave(&ctlr->lock, flags); if (ctlr->state != CPDMA_STATE_IDLE) { @@ -369,12 +556,20 @@ int cpdma_ctlr_start(struct cpdma_ctlr *ctlr) ctlr->state = CPDMA_STATE_ACTIVE; + prio_mode = 0; for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) { - if (ctlr->channels[i]) - cpdma_chan_start(ctlr->channels[i]); + chan = ctlr->channels[i]; + if (chan) { + cpdma_chan_set_chan_shaper(chan); + cpdma_chan_on(chan); + + /* off prio mode if all tx channels are rate limited */ + if (is_tx_chan(chan) && !chan->rate) + prio_mode = 1; + } } - _cpdma_control_set(ctlr, CPDMA_TX_PRIO_FIXED, 1); + _cpdma_control_set(ctlr, CPDMA_TX_PRIO_FIXED, prio_mode); _cpdma_control_set(ctlr, CPDMA_RX_BUFFER_OFFSET, 0); spin_unlock_irqrestore(&ctlr->lock, flags); @@ -602,6 +797,75 @@ int cpdma_chan_set_weight(struct cpdma_chan *ch, int weight) return ret; } +/* cpdma_chan_get_min_rate - get minimum allowed rate for channel + * Should be called before cpdma_chan_set_rate. + * Returns min rate in Kb/s + */ +u32 cpdma_chan_get_min_rate(struct cpdma_ctlr *ctlr) +{ + unsigned int divident, divisor; + + divident = ctlr->params.bus_freq_mhz * 32 * 1000; + divisor = 1 + CPDMA_MAX_RLIM_CNT; + + return DIV_ROUND_UP(divident, divisor); +} + +/* cpdma_chan_set_rate - limits bandwidth for transmit channel. + * The bandwidth * limited channels have to be in order beginning from lowest. + * ch - transmit channel the bandwidth is configured for + * rate - bandwidth in Kb/s, if 0 - then off shaper + */ +int cpdma_chan_set_rate(struct cpdma_chan *ch, u32 rate) +{ + struct cpdma_ctlr *ctlr = ch->ctlr; + unsigned long flags, ch_flags; + int ret, prio_mode; + u32 rmask; + + if (!ch || !is_tx_chan(ch)) + return -EINVAL; + + if (ch->rate == rate) + return rate; + + spin_lock_irqsave(&ctlr->lock, flags); + spin_lock_irqsave(&ch->lock, ch_flags); + + ret = cpdma_chan_fit_rate(ch, rate, &rmask, &prio_mode); + if (ret) + goto err; + + ret = cpdma_chan_set_factors(ctlr, ch); + if (ret) + goto err; + + spin_unlock_irqrestore(&ch->lock, ch_flags); + + /* on shapers */ + _cpdma_control_set(ctlr, CPDMA_TX_RLIM, rmask); + _cpdma_control_set(ctlr, CPDMA_TX_PRIO_FIXED, prio_mode); + spin_unlock_irqrestore(&ctlr->lock, flags); + return ret; + +err: + spin_unlock_irqrestore(&ch->lock, ch_flags); + spin_unlock_irqrestore(&ctlr->lock, flags); + return ret; +} + +u32 cpdma_chan_get_rate(struct cpdma_chan *ch) +{ + unsigned long flags; + u32 rate; + + spin_lock_irqsave(&ch->lock, flags); + rate = ch->rate; + spin_unlock_irqrestore(&ch->lock, flags); + + return rate; +} + struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, cpdma_handler_fn handler, int rx_type) { @@ -629,6 +893,7 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, chan->state = CPDMA_STATE_IDLE; chan->chan_num = chan_num; chan->handler = handler; + chan->rate = 0; chan->desc_num = ctlr->pool->num_desc / 2; chan->weight = 0; @@ -924,28 +1189,20 @@ EXPORT_SYMBOL_GPL(cpdma_chan_process); int cpdma_chan_start(struct cpdma_chan *chan) { - struct cpdma_ctlr *ctlr = chan->ctlr; - struct cpdma_desc_pool *pool = ctlr->pool; - unsigned long flags; + struct cpdma_ctlr *ctlr = chan->ctlr; + unsigned long flags; + int ret; - spin_lock_irqsave(&chan->lock, flags); - if (chan->state != CPDMA_STATE_IDLE) { - spin_unlock_irqrestore(&chan->lock, flags); - return -EBUSY; - } - if (ctlr->state != CPDMA_STATE_ACTIVE) { - spin_unlock_irqrestore(&chan->lock, flags); - return -EINVAL; - } - dma_reg_write(ctlr, chan->int_set, chan->mask); - chan->state = CPDMA_STATE_ACTIVE; - if (chan->head) { - chan_write(chan, hdp, desc_phys(pool, chan->head)); - if (chan->rxfree) - chan_write(chan, rxfree, chan->count); - } + spin_lock_irqsave(&ctlr->lock, flags); + ret = cpdma_chan_set_chan_shaper(chan); + spin_unlock_irqrestore(&ctlr->lock, flags); + if (ret) + return ret; + + ret = cpdma_chan_on(chan); + if (ret) + return ret; - spin_unlock_irqrestore(&chan->lock, flags); return 0; } EXPORT_SYMBOL_GPL(cpdma_chan_start); @@ -1033,31 +1290,12 @@ int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable) int cpdma_control_get(struct cpdma_ctlr *ctlr, int control) { unsigned long flags; - struct cpdma_control_info *info = &controls[control]; int ret; spin_lock_irqsave(&ctlr->lock, flags); - - ret = -ENOTSUPP; - if (!ctlr->params.has_ext_regs) - goto unlock_ret; - - ret = -EINVAL; - if (ctlr->state != CPDMA_STATE_ACTIVE) - goto unlock_ret; - - ret = -ENOENT; - if (control < 0 || control >= ARRAY_SIZE(controls)) - goto unlock_ret; - - ret = -EPERM; - if ((info->access & ACCESS_RO) != ACCESS_RO) - goto unlock_ret; - - ret = (dma_reg_read(ctlr, info->reg) >> info->shift) & info->mask; - -unlock_ret: + ret = _cpdma_control_get(ctlr, control); spin_unlock_irqrestore(&ctlr->lock, flags); + return ret; } @@ -1069,6 +1307,7 @@ int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value) spin_lock_irqsave(&ctlr->lock, flags); ret = _cpdma_control_set(ctlr, control, value); spin_unlock_irqrestore(&ctlr->lock, flags); + return ret; } EXPORT_SYMBOL_GPL(cpdma_control_set); diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h index 629020c..4a167db 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.h +++ b/drivers/net/ethernet/ti/davinci_cpdma.h @@ -36,6 +36,7 @@ struct cpdma_params { u32 desc_hw_addr; int desc_mem_size; int desc_align; + u32 bus_freq_mhz; /* * Some instances of embedded cpdma controllers have extra control and @@ -91,8 +92,12 @@ u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr); u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr); bool cpdma_check_free_tx_desc(struct cpdma_chan *chan); int cpdma_chan_set_weight(struct cpdma_chan *ch, int weight); +int cpdma_chan_set_rate(struct cpdma_chan *ch, u32 rate); +u32 cpdma_chan_get_rate(struct cpdma_chan *ch); +u32 cpdma_chan_get_min_rate(struct cpdma_ctlr *ctlr); enum cpdma_control { + CPDMA_TX_RLIM, /* read-write */ CPDMA_CMD_IDLE, /* write-only */ CPDMA_COPY_ERROR_FRAMES, /* read-write */ CPDMA_RX_OFF_LEN_UPDATE, /* read-write */ -- cgit v1.1 From 83fcad0c986d9dbbb71e8c433d6c40dc6cb810ad Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Tue, 29 Nov 2016 17:00:49 +0200 Subject: net: ethernet: ti: cpsw: add .ndo to set per-queue rate This patch allows to rate limit queues tx queues for cpsw interface. The rate is set in absolute Mb/s units and cannot be more a speed an interface is connected with. The rate for a tx queue can be tested with: ethtool -L eth0 rx 4 tx 4 echo 100 > /sys/class/net/eth0/queues/tx-0/tx_maxrate echo 200 > /sys/class/net/eth0/queues/tx-1/tx_maxrate echo 50 > /sys/class/net/eth0/queues/tx-2/tx_maxrate echo 30 > /sys/class/net/eth0/queues/tx-3/tx_maxrate tc qdisc add dev eth0 root handle 1: multiq tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip\ dport 5001 0xffff action skbedit queue_mapping 0 tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip\ dport 5002 0xffff action skbedit queue_mapping 1 tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip\ dport 5003 0xffff action skbedit queue_mapping 2 tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip\ dport 5004 0xffff action skbedit queue_mapping 3 iperf -c 192.168.2.1 -b 110M -p 5001 -f m -t 60 iperf -c 192.168.2.1 -b 215M -p 5002 -f m -t 60 iperf -c 192.168.2.1 -b 55M -p 5003 -f m -t 60 iperf -c 192.168.2.1 -b 32M -p 5004 -f m -t 60 Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 87 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index caec6ac..27379fa 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1872,6 +1872,88 @@ static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev, return ret; } +static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + int tx_ch_num = ndev->real_num_tx_queues; + u32 consumed_rate, min_rate, max_rate; + struct cpsw_common *cpsw = priv->cpsw; + struct cpsw_slave *slave; + int ret, i, weight; + int rlim_num = 0; + u32 ch_rate; + + ch_rate = netdev_get_tx_queue(ndev, queue)->tx_maxrate; + if (ch_rate == rate) + return 0; + + if (cpsw->data.dual_emac) + slave = &cpsw->slaves[priv->emac_port]; + else + slave = &cpsw->slaves[cpsw->data.active_slave]; + max_rate = slave->phy->speed; + + consumed_rate = 0; + for (i = 0; i < tx_ch_num; i++) { + if (i == queue) + ch_rate = rate; + else + ch_rate = netdev_get_tx_queue(ndev, i)->tx_maxrate; + + if (!ch_rate) + continue; + + rlim_num++; + consumed_rate += ch_rate; + } + + if (consumed_rate > max_rate) + dev_info(priv->dev, "The common rate shouldn't be more than %dMbps", + max_rate); + + if (consumed_rate > max_rate) { + if (max_rate == 10 && consumed_rate <= 100) { + max_rate = 100; + } else if (max_rate <= 100 && consumed_rate <= 1000) { + max_rate = 1000; + } else { + dev_err(priv->dev, "The common rate cannot be more than %dMbps", + max_rate); + return -EINVAL; + } + } + + if (consumed_rate > max_rate) { + dev_err(priv->dev, "The common rate cannot be more than %dMbps", + max_rate); + return -EINVAL; + } + + rate *= 1000; + min_rate = cpdma_chan_get_min_rate(cpsw->dma); + if ((rate < min_rate && rate)) { + dev_err(priv->dev, "The common rate cannot be less than %dMbps", + min_rate); + return -EINVAL; + } + + ret = pm_runtime_get_sync(cpsw->dev); + if (ret < 0) { + pm_runtime_put_noidle(cpsw->dev); + return ret; + } + + if (rlim_num == tx_ch_num) + max_rate = consumed_rate; + + weight = (rate * 100) / (max_rate * 1000); + cpdma_chan_set_weight(cpsw->txch[queue], weight); + + ret = cpdma_chan_set_rate(cpsw->txch[queue], rate); + pm_runtime_put(cpsw->dev); + return ret; +} + static const struct net_device_ops cpsw_netdev_ops = { .ndo_open = cpsw_ndo_open, .ndo_stop = cpsw_ndo_stop, @@ -1881,6 +1963,7 @@ static const struct net_device_ops cpsw_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = cpsw_ndo_tx_timeout, .ndo_set_rx_mode = cpsw_ndo_set_rx_mode, + .ndo_set_tx_maxrate = cpsw_ndo_set_tx_maxrate, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = cpsw_ndo_poll_controller, #endif @@ -2100,6 +2183,7 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) int (*poll)(struct napi_struct *, int); struct cpsw_common *cpsw = priv->cpsw; void (*handler)(void *, int, int); + struct netdev_queue *queue; struct cpdma_chan **chan; int ret, *ch; @@ -2117,6 +2201,8 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) while (*ch < ch_num) { chan[*ch] = cpdma_chan_create(cpsw->dma, *ch, handler, rx); + queue = netdev_get_tx_queue(priv->ndev, *ch); + queue->tx_maxrate = 0; if (IS_ERR(chan[*ch])) return PTR_ERR(chan[*ch]); @@ -2791,6 +2877,7 @@ static int cpsw_probe(struct platform_device *pdev) dma_params.desc_align = 16; dma_params.has_ext_regs = true; dma_params.desc_hw_addr = dma_params.desc_mem_phys; + dma_params.bus_freq_mhz = cpsw->bus_freq_mhz; cpsw->dma = cpdma_ctlr_create(&dma_params); if (!cpsw->dma) { -- cgit v1.1 From 342934a55898f830c38f066e6776e8996589fef3 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Tue, 29 Nov 2016 17:00:50 +0200 Subject: net: ethernet: ti: cpsw: optimize end of poll cycle Check budget fullness only after it's updated and update channel mask only once to keep budget balance between channels. It's also needed for farther changes. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 27379fa..5ea2e3d 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -788,19 +788,13 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) /* process every unprocessed channel */ ch_map = cpdma_ctrl_txchs_state(cpsw->dma); - for (ch = 0, num_tx = 0; num_tx < budget; ch_map >>= 1, ch++) { - if (!ch_map) { - ch_map = cpdma_ctrl_txchs_state(cpsw->dma); - if (!ch_map) - break; - - ch = 0; - } - + for (ch = 0, num_tx = 0; ch_map; ch_map >>= 1, ch++) { if (!(ch_map & 0x01)) continue; num_tx += cpdma_chan_process(cpsw->txch[ch], budget - num_tx); + if (num_tx >= budget) + break; } if (num_tx < budget) { @@ -823,19 +817,13 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) /* process every unprocessed channel */ ch_map = cpdma_ctrl_rxchs_state(cpsw->dma); - for (ch = 0, num_rx = 0; num_rx < budget; ch_map >>= 1, ch++) { - if (!ch_map) { - ch_map = cpdma_ctrl_rxchs_state(cpsw->dma); - if (!ch_map) - break; - - ch = 0; - } - + for (ch = 0, num_rx = 0; ch_map; ch_map >>= 1, ch++) { if (!(ch_map & 0x01)) continue; num_rx += cpdma_chan_process(cpsw->rxch[ch], budget - num_rx); + if (num_rx >= budget) + break; } if (num_rx < budget) { -- cgit v1.1 From 8feb0a1965072e2b19e05792b61f81f982eea5e8 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Tue, 29 Nov 2016 17:00:51 +0200 Subject: net: ethernet: ti: cpsw: split tx budget according between channels Split device budget between channels according to channel rate. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 159 +++++++++++++++++++++++++++++++++-------- 1 file changed, 130 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 5ea2e3d..dd5d830 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -365,6 +365,11 @@ static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) __raw_writel(val, slave->regs + offset); } +struct cpsw_vector { + struct cpdma_chan *ch; + int budget; +}; + struct cpsw_common { struct device *dev; struct cpsw_platform_data data; @@ -380,8 +385,8 @@ struct cpsw_common { int rx_packet_max; struct cpsw_slave *slaves; struct cpdma_ctlr *dma; - struct cpdma_chan *txch[CPSW_MAX_QUEUES]; - struct cpdma_chan *rxch[CPSW_MAX_QUEUES]; + struct cpsw_vector txv[CPSW_MAX_QUEUES]; + struct cpsw_vector rxv[CPSW_MAX_QUEUES]; struct cpsw_ale *ale; bool quirk_irq; bool rx_irq_disabled; @@ -741,7 +746,7 @@ requeue: return; } - ch = cpsw->rxch[skb_get_queue_mapping(new_skb)]; + ch = cpsw->rxv[skb_get_queue_mapping(new_skb)].ch; ret = cpdma_chan_submit(ch, new_skb, new_skb->data, skb_tailroom(new_skb), 0); if (WARN_ON(ret < 0)) @@ -783,8 +788,9 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) { u32 ch_map; - int num_tx, ch; + int num_tx, cur_budget, ch; struct cpsw_common *cpsw = napi_to_cpsw(napi_tx); + struct cpsw_vector *txv; /* process every unprocessed channel */ ch_map = cpdma_ctrl_txchs_state(cpsw->dma); @@ -792,7 +798,13 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) if (!(ch_map & 0x01)) continue; - num_tx += cpdma_chan_process(cpsw->txch[ch], budget - num_tx); + txv = &cpsw->txv[ch]; + if (unlikely(txv->budget > budget - num_tx)) + cur_budget = budget - num_tx; + else + cur_budget = txv->budget; + + num_tx += cpdma_chan_process(txv->ch, cur_budget); if (num_tx >= budget) break; } @@ -812,8 +824,9 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget) static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) { u32 ch_map; - int num_rx, ch; + int num_rx, cur_budget, ch; struct cpsw_common *cpsw = napi_to_cpsw(napi_rx); + struct cpsw_vector *rxv; /* process every unprocessed channel */ ch_map = cpdma_ctrl_rxchs_state(cpsw->dma); @@ -821,7 +834,13 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget) if (!(ch_map & 0x01)) continue; - num_rx += cpdma_chan_process(cpsw->rxch[ch], budget - num_rx); + rxv = &cpsw->rxv[ch]; + if (unlikely(rxv->budget > budget - num_rx)) + cur_budget = budget - num_rx; + else + cur_budget = rxv->budget; + + num_rx += cpdma_chan_process(rxv->ch, cur_budget); if (num_rx >= budget) break; } @@ -1063,7 +1082,7 @@ static void cpsw_get_ethtool_stats(struct net_device *ndev, cpsw_gstrings_stats[l].stat_offset); for (ch = 0; ch < cpsw->rx_ch_num; ch++) { - cpdma_chan_get_stats(cpsw->rxch[ch], &ch_stats); + cpdma_chan_get_stats(cpsw->rxv[ch].ch, &ch_stats); for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) { p = (u8 *)&ch_stats + cpsw_gstrings_ch_stats[i].stat_offset; @@ -1072,7 +1091,7 @@ static void cpsw_get_ethtool_stats(struct net_device *ndev, } for (ch = 0; ch < cpsw->tx_ch_num; ch++) { - cpdma_chan_get_stats(cpsw->txch[ch], &ch_stats); + cpdma_chan_get_stats(cpsw->txv[ch].ch, &ch_stats); for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) { p = (u8 *)&ch_stats + cpsw_gstrings_ch_stats[i].stat_offset; @@ -1261,6 +1280,82 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) } } +/* split budget depending on channel rates */ +static void cpsw_split_budget(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + struct cpsw_vector *txv = cpsw->txv; + u32 consumed_rate, bigest_rate = 0; + int budget, bigest_rate_ch = 0; + struct cpsw_slave *slave; + int i, rlim_ch_num = 0; + u32 ch_rate, max_rate; + int ch_budget = 0; + + if (cpsw->data.dual_emac) + slave = &cpsw->slaves[priv->emac_port]; + else + slave = &cpsw->slaves[cpsw->data.active_slave]; + + max_rate = slave->phy->speed * 1000; + + consumed_rate = 0; + for (i = 0; i < cpsw->tx_ch_num; i++) { + ch_rate = cpdma_chan_get_rate(txv[i].ch); + if (!ch_rate) + continue; + + rlim_ch_num++; + consumed_rate += ch_rate; + } + + if (cpsw->tx_ch_num == rlim_ch_num) { + max_rate = consumed_rate; + } else { + ch_budget = (consumed_rate * CPSW_POLL_WEIGHT) / max_rate; + ch_budget = (CPSW_POLL_WEIGHT - ch_budget) / + (cpsw->tx_ch_num - rlim_ch_num); + bigest_rate = (max_rate - consumed_rate) / + (cpsw->tx_ch_num - rlim_ch_num); + } + + /* split tx budget */ + budget = CPSW_POLL_WEIGHT; + for (i = 0; i < cpsw->tx_ch_num; i++) { + ch_rate = cpdma_chan_get_rate(txv[i].ch); + if (ch_rate) { + txv[i].budget = (ch_rate * CPSW_POLL_WEIGHT) / max_rate; + if (!txv[i].budget) + txv[i].budget = 1; + if (ch_rate > bigest_rate) { + bigest_rate_ch = i; + bigest_rate = ch_rate; + } + } else { + txv[i].budget = ch_budget; + if (!bigest_rate_ch) + bigest_rate_ch = i; + } + + budget -= txv[i].budget; + } + + if (budget) + txv[bigest_rate_ch].budget += budget; + + /* split rx budget */ + budget = CPSW_POLL_WEIGHT; + ch_budget = budget / cpsw->rx_ch_num; + for (i = 0; i < cpsw->rx_ch_num; i++) { + cpsw->rxv[i].budget = ch_budget; + budget -= ch_budget; + } + + if (budget) + cpsw->rxv[0].budget += budget; +} + static int cpsw_fill_rx_channels(struct cpsw_priv *priv) { struct cpsw_common *cpsw = priv->cpsw; @@ -1269,7 +1364,7 @@ static int cpsw_fill_rx_channels(struct cpsw_priv *priv) int ch, i, ret; for (ch = 0; ch < cpsw->rx_ch_num; ch++) { - ch_buf_num = cpdma_chan_get_rx_buf_num(cpsw->rxch[ch]); + ch_buf_num = cpdma_chan_get_rx_buf_num(cpsw->rxv[ch].ch); for (i = 0; i < ch_buf_num; i++) { skb = __netdev_alloc_skb_ip_align(priv->ndev, cpsw->rx_packet_max, @@ -1280,8 +1375,9 @@ static int cpsw_fill_rx_channels(struct cpsw_priv *priv) } skb_set_queue_mapping(skb, ch); - ret = cpdma_chan_submit(cpsw->rxch[ch], skb, skb->data, - skb_tailroom(skb), 0); + ret = cpdma_chan_submit(cpsw->rxv[ch].ch, skb, + skb->data, skb_tailroom(skb), + 0); if (ret < 0) { cpsw_err(priv, ifup, "cannot submit skb to channel %d rx, error %d\n", @@ -1405,6 +1501,7 @@ static int cpsw_ndo_open(struct net_device *ndev) cpsw_set_coalesce(ndev, &coal); } + cpsw_split_budget(ndev); cpdma_ctlr_start(cpsw->dma); cpsw_intr_enable(cpsw); @@ -1474,7 +1571,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, if (q_idx >= cpsw->tx_ch_num) q_idx = q_idx % cpsw->tx_ch_num; - txch = cpsw->txch[q_idx]; + txch = cpsw->txv[q_idx].ch; ret = cpsw_tx_packet_submit(priv, skb, txch); if (unlikely(ret != 0)) { cpsw_err(priv, tx_err, "desc submit failed\n"); @@ -1681,8 +1778,8 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev) ndev->stats.tx_errors++; cpsw_intr_disable(cpsw); for (ch = 0; ch < cpsw->tx_ch_num; ch++) { - cpdma_chan_stop(cpsw->txch[ch]); - cpdma_chan_start(cpsw->txch[ch]); + cpdma_chan_stop(cpsw->txv[ch].ch); + cpdma_chan_start(cpsw->txv[ch].ch); } cpsw_intr_enable(cpsw); @@ -1887,7 +1984,6 @@ static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) ch_rate = rate; else ch_rate = netdev_get_tx_queue(ndev, i)->tx_maxrate; - if (!ch_rate) continue; @@ -1935,9 +2031,12 @@ static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) max_rate = consumed_rate; weight = (rate * 100) / (max_rate * 1000); - cpdma_chan_set_weight(cpsw->txch[queue], weight); + cpdma_chan_set_weight(cpsw->txv[queue].ch, weight); + ret = cpdma_chan_set_rate(cpsw->txv[queue].ch, rate); - ret = cpdma_chan_set_rate(cpsw->txch[queue], rate); + /* re-split budget between channels */ + if (!rate) + cpsw_split_budget(ndev); pm_runtime_put(cpsw->dev); return ret; } @@ -2172,30 +2271,30 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) struct cpsw_common *cpsw = priv->cpsw; void (*handler)(void *, int, int); struct netdev_queue *queue; - struct cpdma_chan **chan; + struct cpsw_vector *vec; int ret, *ch; if (rx) { ch = &cpsw->rx_ch_num; - chan = cpsw->rxch; + vec = cpsw->rxv; handler = cpsw_rx_handler; poll = cpsw_rx_poll; } else { ch = &cpsw->tx_ch_num; - chan = cpsw->txch; + vec = cpsw->txv; handler = cpsw_tx_handler; poll = cpsw_tx_poll; } while (*ch < ch_num) { - chan[*ch] = cpdma_chan_create(cpsw->dma, *ch, handler, rx); + vec[*ch].ch = cpdma_chan_create(cpsw->dma, *ch, handler, rx); queue = netdev_get_tx_queue(priv->ndev, *ch); queue->tx_maxrate = 0; - if (IS_ERR(chan[*ch])) - return PTR_ERR(chan[*ch]); + if (IS_ERR(vec[*ch].ch)) + return PTR_ERR(vec[*ch].ch); - if (!chan[*ch]) + if (!vec[*ch].ch) return -EINVAL; cpsw_info(priv, ifup, "created new %d %s channel\n", *ch, @@ -2206,7 +2305,7 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) while (*ch > ch_num) { (*ch)--; - ret = cpdma_chan_destroy(chan[*ch]); + ret = cpdma_chan_destroy(vec[*ch].ch); if (ret) return ret; @@ -2293,6 +2392,8 @@ static int cpsw_set_channels(struct net_device *ndev, if (ret) goto err; + cpsw_split_budget(ndev); + /* After this receive is started */ cpdma_ctlr_start(cpsw->dma); cpsw_intr_enable(cpsw); @@ -2874,9 +2975,9 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_dt_ret; } - cpsw->txch[0] = cpdma_chan_create(cpsw->dma, 0, cpsw_tx_handler, 0); - cpsw->rxch[0] = cpdma_chan_create(cpsw->dma, 0, cpsw_rx_handler, 1); - if (WARN_ON(!cpsw->rxch[0] || !cpsw->txch[0])) { + cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_tx_handler, 0); + cpsw->rxv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_rx_handler, 1); + if (WARN_ON(!cpsw->rxv[0].ch || !cpsw->txv[0].ch)) { dev_err(priv->dev, "error initializing dma channels\n"); ret = -ENOMEM; goto clean_dma_ret; -- cgit v1.1 From e95489010b7abf6d360dfb7d88a41dfa9e0cc802 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 29 Nov 2016 12:35:19 -0500 Subject: bpf: add test for the verifier equal logic bug This is a test to verify that bpf: fix states equal logic for varlen access actually fixed the problem. The problem was if the register we added to our map register was UNKNOWN in both the false and true branches and the only thing that changed was the range then we'd incorrectly assume that the true branch was valid, which it really wasnt. This tests this case and properly fails without my fix in place and passes with it in place. Signed-off-by: Josef Bacik Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_verifier.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 3c4a1fb..5da2e9d 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -2660,6 +2660,29 @@ static struct bpf_test tests[] = { .result = ACCEPT, .prog_type = BPF_PROG_TYPE_SCHED_CLS }, + { + "invalid map access from else condition", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, MAX_ENTRIES-1, 1), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R0 unbounded memory access, make sure to bounds check any array access into a map", + .result = REJECT, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .result_unpriv = REJECT, + }, }; static int probe_filter_length(const struct bpf_insn *fp) -- cgit v1.1 From d109e61bfe7a468fd8df4a7ceb65635e7aa909a0 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 30 Nov 2016 02:56:47 +0900 Subject: net: ipv4: Don't crash if passing a null sk to ip_rt_update_pmtu. Commit e2d118a1cb5e ("net: inet: Support UID-based routing in IP protocols.") made __build_flow_key call sock_net(sk) to determine the network namespace of the passed-in socket. This crashes if sk is NULL. Fix this by getting the network namespace from the skb instead. Fixes: e2d118a1cb5e ("net: inet: Support UID-based routing in IP protocols.") Reported-by: Erez Shitrit Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- net/ipv4/route.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d37fc6f..6402d74 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -531,13 +531,14 @@ static void __build_flow_key(const struct net *net, struct flowi4 *fl4, static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, const struct sock *sk) { + const struct net *net = dev_net(skb->dev); const struct iphdr *iph = ip_hdr(skb); int oif = skb->dev->ifindex; u8 tos = RT_TOS(iph->tos); u8 prot = iph->protocol; u32 mark = skb->mark; - __build_flow_key(sock_net(sk), fl4, sk, iph, oif, tos, prot, mark, 0); + __build_flow_key(net, fl4, sk, iph, oif, tos, prot, mark, 0); } static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) -- cgit v1.1 From 77d1337bf6350f5f47aad6a917bee8a2a06d561e Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 30 Nov 2016 01:16:12 +0530 Subject: ethernet :mellanox :mlx4: Replace pci_pool_alloc by pci_pool_zalloc In mlx4_alloc_cmd_mailbox(), pci_pool_alloc() followed by memset will be replaced by pci_pool_zalloc() Signed-off-by: Souptick joarder Reviewed-by: Yuval Shaia Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index e36bebc..a49072b4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2679,15 +2679,13 @@ struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) if (!mailbox) return ERR_PTR(-ENOMEM); - mailbox->buf = pci_pool_alloc(mlx4_priv(dev)->cmd.pool, GFP_KERNEL, - &mailbox->dma); + mailbox->buf = pci_pool_zalloc(mlx4_priv(dev)->cmd.pool, GFP_KERNEL, + &mailbox->dma); if (!mailbox->buf) { kfree(mailbox); return ERR_PTR(-ENOMEM); } - memset(mailbox->buf, 0, MLX4_MAILBOX_SIZE); - return mailbox; } EXPORT_SYMBOL_GPL(mlx4_alloc_cmd_mailbox); -- cgit v1.1 From fec668d36d10f30310ced672df65ec5abf0c0032 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 30 Nov 2016 02:50:18 +0530 Subject: ethernet :mellanox :mlx5: Replace pci_pool_alloc by pci_pool_zalloc In alloc_cmd_box(), pci_pool_alloc() followed by memset will be replaced by pci_pool_zalloc() Signed-off-by: Souptick joarder Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 0fe7a60..b0448b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1066,14 +1066,13 @@ static struct mlx5_cmd_mailbox *alloc_cmd_box(struct mlx5_core_dev *dev, if (!mailbox) return ERR_PTR(-ENOMEM); - mailbox->buf = pci_pool_alloc(dev->cmd.pool, flags, - &mailbox->dma); + mailbox->buf = pci_pool_zalloc(dev->cmd.pool, flags, + &mailbox->dma); if (!mailbox->buf) { mlx5_core_dbg(dev, "failed allocation\n"); kfree(mailbox); return ERR_PTR(-ENOMEM); } - memset(mailbox->buf, 0, sizeof(struct mlx5_cmd_prot_block)); mailbox->next = NULL; return mailbox; -- cgit v1.1 From 8f679ed88f8860206edddff725e2749b4cdbb0e8 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 30 Nov 2016 08:48:44 +0800 Subject: driver: ipvlan: Remove useless member mtu_adj of struct ipvl_dev The mtu_adj is initialized to zero when alloc mem, there is no any assignment to mtu_adj. It is only used in ipvlan_adjust_mtu as one right value. So it is useless member of struct ipvl_dev, then remove it. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan.h | 1 - drivers/net/ipvlan/ipvlan_main.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 7e0732f..05a62d2 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -73,7 +73,6 @@ struct ipvl_dev { DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE); netdev_features_t sfeatures; u32 msg_enable; - u16 mtu_adj; }; struct ipvl_addr { diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index ab90b22..c6aa667 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -32,7 +32,7 @@ static const struct l3mdev_ops ipvl_l3mdev_ops = { static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev) { - ipvlan->dev->mtu = dev->mtu - ipvlan->mtu_adj; + ipvlan->dev->mtu = dev->mtu; } static int ipvlan_register_nf_hook(void) -- cgit v1.1 From 87fedb974e0ceaf2a4200b7abb64054fa031cf85 Mon Sep 17 00:00:00 2001 From: Zefir Kurtisi Date: Fri, 25 Nov 2016 17:51:58 +0200 Subject: ath9k: feed only active spectral / dfs-detector Radar pulse and spectral scan reports are provided by the HW with the ATH9K_RXERR_PHY flag set. Those are forwarded to the dfs-detector and spectral module for further processing. For some older chips, the pre-conditions checked in those modules are ambiguous, since ATH9K_PHYERR_RADAR is used to tag both types. As a result, spectral frames are fed into the dfs-detector and vice versa. This could lead to a false radar detection on a non-DFS channel (which is uncritical), but more relevant it causes useless CPU load for processing invalid frames. This commit ensures that the dfs-detector and spectral collector are only fed when they are active. Signed-off-by: Zefir Kurtisi Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/recv.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 6697342..fb4ba27 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -867,10 +867,21 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, * can be dropped. */ if (rx_stats->rs_status & ATH9K_RXERR_PHY) { - ath9k_dfs_process_phyerr(sc, hdr, rx_stats, rx_status->mactime); - if (ath_cmn_process_fft(&sc->spec_priv, hdr, rx_stats, rx_status->mactime)) + /* + * DFS and spectral are mutually exclusive + * + * Since some chips use PHYERR_RADAR as indication for both, we + * need to double check which feature is enabled to prevent + * feeding spectral or dfs-detector with wrong frames. + */ + if (hw->conf.radar_enabled) { + ath9k_dfs_process_phyerr(sc, hdr, rx_stats, + rx_status->mactime); + } else if (sc->spec_priv.spectral_mode != SPECTRAL_DISABLED && + ath_cmn_process_fft(&sc->spec_priv, hdr, rx_stats, + rx_status->mactime)) { RX_STAT_INC(rx_spectral); - + } return -EINVAL; } -- cgit v1.1 From 4e322f7db51a52caa3d2c1f795ba26dacdcf8fde Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Sun, 27 Nov 2016 20:37:20 +0100 Subject: ath10k: fix TLV set regdomain command There is a typo bug in the current implementation of ath10k_wmi_tlv_op_gen_pdev_set_rd. The conformance test limits are not set up properly. The two arguments ctl2g and ctl5g were not used at all. Instead, the regdomain arguments rd2g and rd5g were used for the ctl settings as well. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 97b536e..f304f66 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -1313,8 +1313,8 @@ ath10k_wmi_tlv_op_gen_pdev_set_rd(struct ath10k *ar, cmd->regd = __cpu_to_le32(rd); cmd->regd_2ghz = __cpu_to_le32(rd2g); cmd->regd_5ghz = __cpu_to_le32(rd5g); - cmd->conform_limit_2ghz = __cpu_to_le32(rd2g); - cmd->conform_limit_5ghz = __cpu_to_le32(rd5g); + cmd->conform_limit_2ghz = __cpu_to_le32(ctl2g); + cmd->conform_limit_5ghz = __cpu_to_le32(ctl5g); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set rd\n"); return skb; -- cgit v1.1 From c2cac2f74ab4bcf0db0dcf3a612f1e5b52d145c8 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 30 Nov 2016 10:59:29 +0530 Subject: ath10k: fix soft lockup during firmware crash/hw-restart During firmware crash (or) user requested manual restart the system gets into a soft lock up state because of the below root cause. During user requested hardware restart / firmware crash the system goes into a soft lockup state as 'napi_synchronize' is called after 'napi_disable' (which sets 'NAPI_STATE_SCHED' bit) and it sleeps into infinite loop as it waits for 'NAPI_STATE_SCHED' to be cleared. This condition is hit because 'ath10k_hif_stop' is called twice as below (resulting in calling 'napi_synchronize' after 'napi_disable') 'ath10k_core_restart' -> 'ath10k_hif_stop' (ATH10K_STATE_ON) -> -> 'ieee80211_restart_hw' -> 'ath10k_start' -> 'ath10k_halt' -> 'ath10k_core_stop' -> 'ath10k_hif_stop' (ATH10K_STATE_RESTARTING) Fix this by calling 'ath10k_halt' in ath10k_core_restart itself as it makes more sense before informing mac80211 to restart h/w Also remove 'ath10k_halt' in ath10k_start for the state of 'restarting' Fixes: 3c97f5de1f28 ("ath10k: implement NAPI support") Cc: # v4.9 Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 +- drivers/net/wireless/ath/ath10k/mac.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b796be1..71650ed 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1537,7 +1537,7 @@ static void ath10k_core_restart(struct work_struct *work) switch (ar->state) { case ATH10K_STATE_ON: ar->state = ATH10K_STATE_RESTARTING; - ath10k_hif_stop(ar); + ath10k_halt(ar); ath10k_scan_finish(ar); ieee80211_restart_hw(ar->hw); break; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index d06a12d..db6ddf9 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4451,7 +4451,6 @@ static int ath10k_start(struct ieee80211_hw *hw) ar->state = ATH10K_STATE_ON; break; case ATH10K_STATE_RESTARTING: - ath10k_halt(ar); ar->state = ATH10K_STATE_RESTARTED; break; case ATH10K_STATE_ON: -- cgit v1.1 From 9ec34a86195a65953269d3ee97b90308f469e14c Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 30 Nov 2016 15:20:14 +0530 Subject: ath10k: fix Tx DMA alloc failure during continuous wifi down/up With maximum number of vap's configured in a two radio supported systems of ~256 Mb RAM, doing a continuous wifi down/up and intermittent traffic streaming from the connected stations results in failure to allocate contiguous memory for tx buffers. This results in the disappearance of all VAP's and a manual reboot is needed as this is not a crash (or) OOM(for OOM killer to be invoked). To address this allocate contiguous memory for tx buffers one time and re-use them until the modules are unloaded but this results in a slight increase in memory footprint of ath10k when the wifi is down, but the modules are still loaded. Also as of now we use a separate bool 'tx_mem_allocated' to keep track of the one time memory allocation, as we cannot come up with something like 'ath10k_tx_{register,unregister}' before 'ath10k_probe_fw' is called as 'ath10k_htt_tx_alloc_cont_frag_desc' memory allocation is dependent on the hw_param 'continuous_frag_desc' a) memory footprint of ath10k without the change lsmod | grep ath10k ath10k_core 414498 1 ath10k_pci ath10k_pci 38236 0 b) memory footprint of ath10k with the change ath10k_core 414980 1 ath10k_pci ath10k_pci 38236 0 Memory Failure Call trace: hostapd: page allocation failure: order:6, mode:0xd0 [] (__dma_alloc_buffer.isra.23) from [] (__alloc_remap_buffer.isra.26+0x14/0xb8) [] (__alloc_remap_buffer.isra.26) from [] (__dma_alloc+0x224/0x2b8) [] (__dma_alloc) from [] (arm_dma_alloc+0x84/0x90) [] (arm_dma_alloc) from [] (ath10k_htt_tx_alloc+0xe0/0x2e4 [ath10k_core]) [] (ath10k_htt_tx_alloc [ath10k_core]) from [] (ath10k_core_start+0x538/0xcf8 [ath10k_core]) [] (ath10k_core_start [ath10k_core]) from [] (ath10k_start+0xbc/0x56c [ath10k_core]) [] (ath10k_start [ath10k_core]) from [] (drv_start+0x40/0x5c [mac80211]) [] (drv_start [mac80211]) from [] (ieee80211_do_open+0x170/0x82c [mac80211]) [] (ieee80211_do_open [mac80211]) from [] (__dev_open+0xa0/0xf4) [21053.491752] Normal: 641*4kB (UEMR) 505*8kB (UEMR) 330*16kB (UEMR) 126*32kB (UEMR) 762*64kB (UEMR) 237*128kB (UEMR) 1*256kB (M) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 95276kB Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 5 +-- drivers/net/wireless/ath/ath10k/htt.h | 6 +++- drivers/net/wireless/ath/ath10k/htt_tx.c | 54 +++++++++++++++++++++++++------- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 71650ed..749e381 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1858,7 +1858,7 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err_wmi_detach; } - status = ath10k_htt_tx_alloc(&ar->htt); + status = ath10k_htt_tx_start(&ar->htt); if (status) { ath10k_err(ar, "failed to alloc htt tx: %d\n", status); goto err_wmi_detach; @@ -2053,7 +2053,7 @@ void ath10k_core_stop(struct ath10k *ar) ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR); ath10k_hif_stop(ar); - ath10k_htt_tx_free(&ar->htt); + ath10k_htt_tx_stop(&ar->htt); ath10k_htt_rx_free(&ar->htt); ath10k_wmi_detach(ar); } @@ -2386,6 +2386,7 @@ void ath10k_core_destroy(struct ath10k *ar) destroy_workqueue(ar->workqueue_aux); ath10k_debug_destroy(ar); + ath10k_htt_tx_destroy(&ar->htt); ath10k_wmi_free_host_mem(ar); ath10k_mac_destroy(ar); } diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 164eb3a..44b25cf 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1717,6 +1717,8 @@ struct ath10k_htt { enum htt_tx_mode_switch_mode mode; enum htt_q_depth_type type; } tx_q_state; + + bool tx_mem_allocated; }; #define RX_HTT_HDR_STATUS_LEN 64 @@ -1779,7 +1781,9 @@ int ath10k_htt_connect(struct ath10k_htt *htt); int ath10k_htt_init(struct ath10k *ar); int ath10k_htt_setup(struct ath10k_htt *htt); -int ath10k_htt_tx_alloc(struct ath10k_htt *htt); +int ath10k_htt_tx_start(struct ath10k_htt *htt); +void ath10k_htt_tx_stop(struct ath10k_htt *htt); +void ath10k_htt_tx_destroy(struct ath10k_htt *htt); void ath10k_htt_tx_free(struct ath10k_htt *htt); int ath10k_htt_rx_alloc(struct ath10k_htt *htt); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index ccbc8c03..27e49db 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -350,21 +350,15 @@ static int ath10k_htt_tx_alloc_txdone_fifo(struct ath10k_htt *htt) return ret; } -int ath10k_htt_tx_alloc(struct ath10k_htt *htt) +static int ath10k_htt_tx_alloc_buf(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; int ret; - ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", - htt->max_num_pending_tx); - - spin_lock_init(&htt->tx_lock); - idr_init(&htt->pending_tx); - ret = ath10k_htt_tx_alloc_cont_txbuf(htt); if (ret) { ath10k_err(ar, "failed to alloc cont tx buffer: %d\n", ret); - goto free_idr_pending_tx; + return ret; } ret = ath10k_htt_tx_alloc_cont_frag_desc(htt); @@ -396,6 +390,31 @@ free_frag_desc: free_txbuf: ath10k_htt_tx_free_cont_txbuf(htt); + return ret; +} + +int ath10k_htt_tx_start(struct ath10k_htt *htt) +{ + struct ath10k *ar = htt->ar; + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", + htt->max_num_pending_tx); + + spin_lock_init(&htt->tx_lock); + idr_init(&htt->pending_tx); + + if (htt->tx_mem_allocated) + return 0; + + ret = ath10k_htt_tx_alloc_buf(htt); + if (ret) + goto free_idr_pending_tx; + + htt->tx_mem_allocated = true; + + return 0; + free_idr_pending_tx: idr_destroy(&htt->pending_tx); @@ -418,15 +437,28 @@ static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx) return 0; } -void ath10k_htt_tx_free(struct ath10k_htt *htt) +void ath10k_htt_tx_destroy(struct ath10k_htt *htt) { - idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar); - idr_destroy(&htt->pending_tx); + if (!htt->tx_mem_allocated) + return; ath10k_htt_tx_free_cont_txbuf(htt); ath10k_htt_tx_free_txq(htt); ath10k_htt_tx_free_cont_frag_desc(htt); ath10k_htt_tx_free_txdone_fifo(htt); + htt->tx_mem_allocated = false; +} + +void ath10k_htt_tx_stop(struct ath10k_htt *htt) +{ + idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar); + idr_destroy(&htt->pending_tx); +} + +void ath10k_htt_tx_free(struct ath10k_htt *htt) +{ + ath10k_htt_tx_stop(htt); + ath10k_htt_tx_destroy(htt); } void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) -- cgit v1.1 From 43ca92d380a85996429601f0d337cbd58b4e5bf9 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 29 Nov 2016 14:00:28 -0800 Subject: ath10k: wmi-alloc-chunk should use DMA_BIDIRECTIONAL These memory chunks are often used as 'swap' by the NIC, so it will be both reading and writing to these areas. This seems to fix errors like this on my x86-64 machine: kernel: DMAR: DMAR:[DMA Write] Request device [05:00.0] fault addr ff5de000 DMAR:[fault reason 05] PTE Write access is not set Tested-by: Marek Behun Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 95be2ba..c893314 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4487,7 +4487,7 @@ static int ath10k_wmi_alloc_chunk(struct ath10k *ar, u32 req_id, if (!num_units) return -ENOMEM; - paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_TO_DEVICE); + paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_BIDIRECTIONAL); if (dma_mapping_error(ar->dev, paddr)) { kfree(vaddr); return -ENOMEM; -- cgit v1.1 From 8ca5a6078d6d09ac50d9c39c214df3284f0d2efb Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sun, 27 Nov 2016 22:33:26 +0530 Subject: ath9k: constify ath_bus_ops structure Declare the structure ath_bus_ops as const as it is only passed as an argument to the function ath9k_init_device. This argument is of type const struct ath_bus_ops *, so ath_bus_ops structures with this property can be declared as const. Done using Coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct ath_bus_ops i@p = {...}; @ok1@ identifier r1.i; position p; expression e1,e2; @@ ath9k_init_device(e1,e2,&i@p) @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct ath_bus_ops i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct ath_bus_ops i; File size before: text data bss dec hex filename 1295 232 0 1527 5f7 ath/ath9k/ahb.o File size after: text data bss dec hex filename 1359 176 0 1535 5ff ath/ath9k/ahb.o Signed-off-by: Bhumika Goyal Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/ahb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index bea6186..2bd982c 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -62,7 +62,7 @@ static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) return false; } -static struct ath_bus_ops ath_ahb_bus_ops = { +static const struct ath_bus_ops ath_ahb_bus_ops = { .ath_bus_type = ATH_AHB, .read_cachesize = ath_ahb_read_cachesize, .eeprom_read = ath_ahb_eeprom_read, -- cgit v1.1 From 982a6151f6f1296725de241a17932a3ecdd359d2 Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Sun, 27 Nov 2016 20:27:57 -0800 Subject: ath9k_htc: don't use HZ for usb msg timeouts The usb_*_msg() functions expect a timeout in msecs but are given HZ, which is ticks per second. If HZ=100, firmware download often times out when there is modest USB utilization and the device fails to initialize. Replaces HZ in usb_*_msg timeouts with 1000 msec since HZ is one second for timeouts in jiffies. Signed-off-by: Anthony Romano Acked-by: Oleksij Rempel Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hif_usb.c | 9 +++++---- drivers/net/wireless/ath/ath9k/hif_usb.h | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index e1c338c..de2d212 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -997,7 +997,8 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0), FIRMWARE_DOWNLOAD, 0x40 | USB_DIR_OUT, - addr >> 8, 0, buf, transfer, HZ); + addr >> 8, 0, buf, transfer, + USB_MSG_TIMEOUT); if (err < 0) { kfree(buf); return err; @@ -1020,7 +1021,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0), FIRMWARE_DOWNLOAD_COMP, 0x40 | USB_DIR_OUT, - firm_offset >> 8, 0, NULL, 0, HZ); + firm_offset >> 8, 0, NULL, 0, USB_MSG_TIMEOUT); if (err) return -EIO; @@ -1249,7 +1250,7 @@ static int send_eject_command(struct usb_interface *interface) dev_info(&udev->dev, "Ejecting storage device...\n"); r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), - cmd, 31, NULL, 2000); + cmd, 31, NULL, 2 * USB_MSG_TIMEOUT); kfree(cmd); if (r) return r; @@ -1314,7 +1315,7 @@ static void ath9k_hif_usb_reboot(struct usb_device *udev) return; ret = usb_interrupt_msg(udev, usb_sndintpipe(udev, USB_REG_OUT_PIPE), - buf, 4, NULL, HZ); + buf, 4, NULL, USB_MSG_TIMEOUT); if (ret) dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n"); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index 7c2ef7e..7846916 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -71,6 +71,8 @@ extern int htc_use_dev_fw; #define USB_REG_IN_PIPE 3 #define USB_REG_OUT_PIPE 4 +#define USB_MSG_TIMEOUT 1000 /* (ms) */ + #define HIF_USB_MAX_RXPIPES 2 #define HIF_USB_MAX_TXPIPES 4 -- cgit v1.1 From bb6743f7c2ffc467002dfe24bea5dedc96c7da04 Mon Sep 17 00:00:00 2001 From: Lior David Date: Mon, 28 Nov 2016 13:49:00 +0200 Subject: wil6210: delay remain on channel when scan is active Currently it was possible to call remain_on_channel(ROC) while scan was active and this caused a crash in the FW. In order to fix this problem and make the behavior consistent with other drivers, queue the ROC in case a scan is active and try it again when scan is done. As part of the fix, clean up some locking issues and return error if scan is called while ROC is active. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 45 ++++----- drivers/net/wireless/ath/wil6210/main.c | 2 + drivers/net/wireless/ath/wil6210/p2p.c | 150 +++++++++++++++++++++------- drivers/net/wireless/ath/wil6210/wil6210.h | 9 +- drivers/net/wireless/ath/wil6210/wmi.c | 4 + 5 files changed, 149 insertions(+), 61 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 22078b0..6aa3ff4 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -354,14 +354,6 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, wil_dbg_misc(wil, "%s(), wdev=0x%p iftype=%d\n", __func__, wdev, wdev->iftype); - mutex_lock(&wil->p2p_wdev_mutex); - if (wil->scan_request) { - wil_err(wil, "Already scanning\n"); - mutex_unlock(&wil->p2p_wdev_mutex); - return -EAGAIN; - } - mutex_unlock(&wil->p2p_wdev_mutex); - /* check we are client side */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -378,12 +370,24 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, return -EBUSY; } + mutex_lock(&wil->mutex); + + mutex_lock(&wil->p2p_wdev_mutex); + if (wil->scan_request || wil->p2p.discovery_started) { + wil_err(wil, "Already scanning\n"); + mutex_unlock(&wil->p2p_wdev_mutex); + rc = -EAGAIN; + goto out; + } + mutex_unlock(&wil->p2p_wdev_mutex); + /* social scan on P2P_DEVICE is handled as p2p search */ if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE && wil_p2p_is_social_scan(request)) { if (!wil->p2p.p2p_dev_started) { wil_err(wil, "P2P search requested on stopped P2P device\n"); - return -EIO; + rc = -EIO; + goto out; } wil->scan_request = request; wil->radio_wdev = wdev; @@ -392,7 +396,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, wil->radio_wdev = wil_to_wdev(wil); wil->scan_request = NULL; } - return rc; + goto out; } (void)wil_p2p_stop_discovery(wil); @@ -415,7 +419,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, if (rc) { wil_err(wil, "set SSID for scan request failed: %d\n", rc); - return rc; + goto out; } wil->scan_request = request; @@ -448,7 +452,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie); if (rc) - goto out; + goto out_restore; if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) { cmd.cmd.discovery_mode = 1; @@ -459,13 +463,14 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); -out: +out_restore: if (rc) { del_timer_sync(&wil->scan_timer); wil->radio_wdev = wil_to_wdev(wil); wil->scan_request = NULL; } - +out: + mutex_unlock(&wil->mutex); return rc; } @@ -988,16 +993,8 @@ static int wil_remain_on_channel(struct wiphy *wiphy, wil_dbg_misc(wil, "%s() center_freq=%d, duration=%d iftype=%d\n", __func__, chan->center_freq, duration, wdev->iftype); - rc = wil_p2p_listen(wil, duration, chan, cookie); - if (rc) - return rc; - - wil->radio_wdev = wdev; - - cfg80211_ready_on_channel(wdev, *cookie, chan, duration, - GFP_KERNEL); - - return 0; + rc = wil_p2p_listen(wil, wdev, duration, chan, cookie); + return rc; } static int wil_cancel_remain_on_channel(struct wiphy *wiphy, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 70f9c07..e2e021b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -518,6 +518,7 @@ int wil_priv_init(struct wil6210_priv *wil) INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker); + INIT_WORK(&wil->p2p.delayed_listen_work, wil_p2p_delayed_listen_work); INIT_LIST_HEAD(&wil->pending_wmi_ev); INIT_LIST_HEAD(&wil->probe_client_pending); @@ -579,6 +580,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) cancel_work_sync(&wil->disconnect_worker); cancel_work_sync(&wil->fw_error_worker); cancel_work_sync(&wil->p2p.discovery_expired_work); + cancel_work_sync(&wil->p2p.delayed_listen_work); mutex_lock(&wil->mutex); wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); mutex_unlock(&wil->mutex); diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index 4f0eab0..fbae995 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -22,6 +22,43 @@ #define P2P_SEARCH_DURATION_MS 500 #define P2P_DEFAULT_BI 100 +static int wil_p2p_start_listen(struct wil6210_priv *wil) +{ + struct wil_p2p_info *p2p = &wil->p2p; + u8 channel = p2p->listen_chan.hw_value; + int rc; + + lockdep_assert_held(&wil->mutex); + + rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI); + if (rc) { + wil_err(wil, "wmi_p2p_cfg failed\n"); + goto out; + } + + rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); + if (rc) { + wil_err(wil, "wmi_set_ssid failed\n"); + goto out_stop; + } + + rc = wmi_start_listen(wil); + if (rc) { + wil_err(wil, "wmi_start_listen failed\n"); + goto out_stop; + } + + INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired); + mod_timer(&p2p->discovery_timer, + jiffies + msecs_to_jiffies(p2p->listen_duration)); +out_stop: + if (rc) + wmi_stop_discovery(wil); + +out: + return rc; +} + bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request) { return (request->n_channels == 1) && @@ -46,7 +83,7 @@ int wil_p2p_search(struct wil6210_priv *wil, wil_dbg_misc(wil, "%s: channel %d\n", __func__, P2P_DMG_SOCIAL_CHANNEL); - mutex_lock(&wil->mutex); + lockdep_assert_held(&wil->mutex); if (p2p->discovery_started) { wil_err(wil, "%s: search failed. discovery already ongoing\n", @@ -103,22 +140,19 @@ out_stop: wmi_stop_discovery(wil); out: - mutex_unlock(&wil->mutex); return rc; } -int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, - struct ieee80211_channel *chan, u64 *cookie) +int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, + unsigned int duration, struct ieee80211_channel *chan, + u64 *cookie) { struct wil_p2p_info *p2p = &wil->p2p; - u8 channel = P2P_DMG_SOCIAL_CHANNEL; int rc; if (!chan) return -EINVAL; - channel = chan->hw_value; - wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration); mutex_lock(&wil->mutex); @@ -129,35 +163,30 @@ int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, goto out; } - rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI); - if (rc) { - wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__); - goto out; - } - - rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); - if (rc) { - wil_err(wil, "%s: wmi_set_ssid failed\n", __func__); - goto out_stop; - } + memcpy(&p2p->listen_chan, chan, sizeof(*chan)); + *cookie = ++p2p->cookie; + p2p->listen_duration = duration; - rc = wmi_start_listen(wil); - if (rc) { - wil_err(wil, "%s: wmi_start_listen failed\n", __func__); - goto out_stop; + mutex_lock(&wil->p2p_wdev_mutex); + if (wil->scan_request) { + wil_dbg_misc(wil, "Delaying p2p listen until scan done\n"); + p2p->pending_listen_wdev = wdev; + p2p->discovery_started = 1; + rc = 0; + mutex_unlock(&wil->p2p_wdev_mutex); + goto out; } + mutex_unlock(&wil->p2p_wdev_mutex); - memcpy(&p2p->listen_chan, chan, sizeof(*chan)); - *cookie = ++p2p->cookie; + rc = wil_p2p_start_listen(wil); + if (rc) + goto out; p2p->discovery_started = 1; - INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired); - mod_timer(&p2p->discovery_timer, - jiffies + msecs_to_jiffies(duration)); + wil->radio_wdev = wdev; -out_stop: - if (rc) - wmi_stop_discovery(wil); + cfg80211_ready_on_channel(wdev, *cookie, chan, duration, + GFP_KERNEL); out: mutex_unlock(&wil->mutex); @@ -170,9 +199,14 @@ u8 wil_p2p_stop_discovery(struct wil6210_priv *wil) u8 started = p2p->discovery_started; if (p2p->discovery_started) { - del_timer_sync(&p2p->discovery_timer); + if (p2p->pending_listen_wdev) { + /* discovery not really started, only pending */ + p2p->pending_listen_wdev = NULL; + } else { + del_timer_sync(&p2p->discovery_timer); + wmi_stop_discovery(wil); + } p2p->discovery_started = 0; - wmi_stop_discovery(wil); } return started; @@ -257,11 +291,57 @@ void wil_p2p_search_expired(struct work_struct *work) }; mutex_lock(&wil->p2p_wdev_mutex); - cfg80211_scan_done(wil->scan_request, &info); - wil->scan_request = NULL; - wil->radio_wdev = wil->wdev; + if (wil->scan_request) { + cfg80211_scan_done(wil->scan_request, &info); + wil->scan_request = NULL; + wil->radio_wdev = wil->wdev; + } + mutex_unlock(&wil->p2p_wdev_mutex); + } +} + +void wil_p2p_delayed_listen_work(struct work_struct *work) +{ + struct wil_p2p_info *p2p = container_of(work, + struct wil_p2p_info, delayed_listen_work); + struct wil6210_priv *wil = container_of(p2p, + struct wil6210_priv, p2p); + int rc; + + mutex_lock(&wil->mutex); + + wil_dbg_misc(wil, "Checking delayed p2p listen\n"); + if (!p2p->discovery_started || !p2p->pending_listen_wdev) + goto out; + + mutex_lock(&wil->p2p_wdev_mutex); + if (wil->scan_request) { + /* another scan started, wait again... */ mutex_unlock(&wil->p2p_wdev_mutex); + goto out; } + mutex_unlock(&wil->p2p_wdev_mutex); + + rc = wil_p2p_start_listen(wil); + + mutex_lock(&wil->p2p_wdev_mutex); + if (rc) { + cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev, + p2p->cookie, + &p2p->listen_chan, + GFP_KERNEL); + wil->radio_wdev = wil->wdev; + } else { + cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie, + &p2p->listen_chan, + p2p->listen_duration, GFP_KERNEL); + wil->radio_wdev = p2p->pending_listen_wdev; + } + p2p->pending_listen_wdev = NULL; + mutex_unlock(&wil->p2p_wdev_mutex); + +out: + mutex_unlock(&wil->mutex); } void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index b449f84..ef95db9 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -461,8 +461,11 @@ struct wil_p2p_info { u8 discovery_started; u8 p2p_dev_started; u64 cookie; + struct wireless_dev *pending_listen_wdev; + unsigned int listen_duration; struct timer_list discovery_timer; /* listen/search duration */ struct work_struct discovery_expired_work; /* listen/search expire */ + struct work_struct delayed_listen_work; /* listen after scan done */ }; enum wil_sta_status { @@ -843,13 +846,15 @@ bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request); void wil_p2p_discovery_timer_fn(ulong x); int wil_p2p_search(struct wil6210_priv *wil, struct cfg80211_scan_request *request); -int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, - struct ieee80211_channel *chan, u64 *cookie); +int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, + unsigned int duration, struct ieee80211_channel *chan, + u64 *cookie); u8 wil_p2p_stop_discovery(struct wil6210_priv *wil); int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie); void wil_p2p_listen_expired(struct work_struct *work); void wil_p2p_search_expired(struct work_struct *work); void wil_p2p_stop_radio_operations(struct wil6210_priv *wil); +void wil_p2p_delayed_listen_work(struct work_struct *work); /* WMI for P2P */ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 2971ddf..d289a4d 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -441,6 +441,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, wil->radio_wdev = wil->wdev; wil->scan_request = NULL; wake_up_interruptible(&wil->wq); + if (wil->p2p.pending_listen_wdev) { + wil_dbg_misc(wil, "Scheduling delayed listen\n"); + schedule_work(&wil->p2p.delayed_listen_work); + } } else { wil_err(wil, "SCAN_COMPLETE while not scanning\n"); } -- cgit v1.1 From 1db226ffe1c2ab900cffb1d71f042e565f77775d Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Mon, 28 Nov 2016 13:49:01 +0200 Subject: wil6210: validate wil_pmc_alloc parameters num_descriptors and descriptor_size needs to be checked for: 1) not being negative values 2) no overflow occurs when these are multiplied together as done in wil_pmc_read. An overflow of two signed integers is undefined behavior. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pmc.c | 55 ++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index 5ca0307..b9faae0 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -54,6 +54,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil, struct pmc_ctx *pmc = &wil->pmc; struct device *dev = wil_to_dev(wil); struct wmi_pmc_cmd pmc_cmd = {0}; + int last_cmd_err = -ENOMEM; mutex_lock(&pmc->lock); @@ -62,6 +63,29 @@ void wil_pmc_alloc(struct wil6210_priv *wil, wil_err(wil, "%s: ERROR pmc is already allocated\n", __func__); goto no_release_err; } + if ((num_descriptors <= 0) || (descriptor_size <= 0)) { + wil_err(wil, + "Invalid params num_descriptors(%d), descriptor_size(%d)\n", + num_descriptors, descriptor_size); + last_cmd_err = -EINVAL; + goto no_release_err; + } + + if (num_descriptors > (1 << WIL_RING_SIZE_ORDER_MAX)) { + wil_err(wil, + "num_descriptors(%d) exceeds max ring size %d\n", + num_descriptors, 1 << WIL_RING_SIZE_ORDER_MAX); + last_cmd_err = -EINVAL; + goto no_release_err; + } + + if (num_descriptors > INT_MAX / descriptor_size) { + wil_err(wil, + "Overflow in num_descriptors(%d)*descriptor_size(%d)\n", + num_descriptors, descriptor_size); + last_cmd_err = -EINVAL; + goto no_release_err; + } pmc->num_descriptors = num_descriptors; pmc->descriptor_size = descriptor_size; @@ -189,7 +213,7 @@ release_pmc_skb_list: pmc->descriptors = NULL; no_release_err: - pmc->last_cmd_status = -ENOMEM; + pmc->last_cmd_status = last_cmd_err; mutex_unlock(&pmc->lock); } @@ -295,7 +319,7 @@ ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count, size_t retval = 0; unsigned long long idx; loff_t offset; - size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors; + size_t pmc_size; mutex_lock(&pmc->lock); @@ -306,6 +330,8 @@ ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count, return -EPERM; } + pmc_size = pmc->descriptor_size * pmc->num_descriptors; + wil_dbg_misc(wil, "%s: size %u, pos %lld\n", __func__, (unsigned)count, *f_pos); @@ -345,7 +371,18 @@ loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence) loff_t newpos; struct wil6210_priv *wil = filp->private_data; struct pmc_ctx *pmc = &wil->pmc; - size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors; + size_t pmc_size; + + mutex_lock(&pmc->lock); + + if (!wil_is_pmc_allocated(pmc)) { + wil_err(wil, "error, pmc is not allocated!\n"); + pmc->last_cmd_status = -EPERM; + mutex_unlock(&pmc->lock); + return -EPERM; + } + + pmc_size = pmc->descriptor_size * pmc->num_descriptors; switch (whence) { case 0: /* SEEK_SET */ @@ -361,15 +398,21 @@ loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence) break; default: /* can't happen */ - return -EINVAL; + newpos = -EINVAL; + goto out; } - if (newpos < 0) - return -EINVAL; + if (newpos < 0) { + newpos = -EINVAL; + goto out; + } if (newpos > pmc_size) newpos = pmc_size; filp->f_pos = newpos; +out: + mutex_unlock(&pmc->lock); + return newpos; } -- cgit v1.1 From 615788200557d8a555bf7f02a365aee627400912 Mon Sep 17 00:00:00 2001 From: Lior David Date: Mon, 28 Nov 2016 13:49:02 +0200 Subject: wil6210: add debugfs blobs for UCODE code and data Added new areas to fw_mappings area for UCODE code and data areas. The new areas are only exposed through debugfs blobs, and mainly needed to access UCODE logs. The change does not affect crash dumps because the newly added areas overlap with the "upper" area which is already dumped. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wil6210.h | 3 +- drivers/net/wireless/ath/wil6210/wil_crash_dump.c | 6 ++++ drivers/net/wireless/ath/wil6210/wmi.c | 39 +++++++++++++++-------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index ef95db9..237e166 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -276,10 +276,11 @@ struct fw_map { u32 to; /* linker address - to, exclusive */ u32 host; /* PCI/Host address - BAR0 + 0x880000 */ const char *name; /* for debugfs */ + bool fw; /* true if FW mapping, false if UCODE mapping */ }; /* array size should be in sync with actual definition in the wmi.c */ -extern const struct fw_map fw_mapping[8]; +extern const struct fw_map fw_mapping[10]; /** * mk_cidxtid - construct @cidxtid field diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c index b57d280..d051eea 100644 --- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c +++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c @@ -36,6 +36,9 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil, for (i = 1; i < ARRAY_SIZE(fw_mapping); i++) { map = &fw_mapping[i]; + if (!map->fw) + continue; + if (map->host < host_min) host_min = map->host; @@ -73,6 +76,9 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { map = &fw_mapping[i]; + if (!map->fw) + continue; + data = (void * __force)wil->csr + HOSTADDR(map->host); len = map->to - map->from; offset = map->host - host_min; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index d289a4d..7585003 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -84,19 +84,29 @@ MODULE_PARM_DESC(led_id, * array size should be in sync with the declaration in the wil6210.h */ const struct fw_map fw_mapping[] = { - {0x000000, 0x040000, 0x8c0000, "fw_code"}, /* FW code RAM 256k */ - {0x800000, 0x808000, 0x900000, "fw_data"}, /* FW data RAM 32k */ - {0x840000, 0x860000, 0x908000, "fw_peri"}, /* periph. data RAM 128k */ - {0x880000, 0x88a000, 0x880000, "rgf"}, /* various RGF 40k */ - {0x88a000, 0x88b000, 0x88a000, "AGC_tbl"}, /* AGC table 4k */ - {0x88b000, 0x88c000, 0x88b000, "rgf_ext"}, /* Pcie_ext_rgf 4k */ - {0x88c000, 0x88c200, 0x88c000, "mac_rgf_ext"}, /* mac_ext_rgf 512b */ - {0x8c0000, 0x949000, 0x8c0000, "upper"}, /* upper area 548k */ - /* - * 920000..930000 ucode code RAM - * 930000..932000 ucode data RAM - * 932000..949000 back-door debug data + /* FW code RAM 256k */ + {0x000000, 0x040000, 0x8c0000, "fw_code", true}, + /* FW data RAM 32k */ + {0x800000, 0x808000, 0x900000, "fw_data", true}, + /* periph data 128k */ + {0x840000, 0x860000, 0x908000, "fw_peri", true}, + /* various RGF 40k */ + {0x880000, 0x88a000, 0x880000, "rgf", true}, + /* AGC table 4k */ + {0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true}, + /* Pcie_ext_rgf 4k */ + {0x88b000, 0x88c000, 0x88b000, "rgf_ext", true}, + /* mac_ext_rgf 512b */ + {0x88c000, 0x88c200, 0x88c000, "mac_rgf_ext", true}, + /* upper area 548k */ + {0x8c0000, 0x949000, 0x8c0000, "upper", true}, + /* UCODE areas - accessible by debugfs blobs but not by + * wmi_addr_remap. UCODE areas MUST be added AFTER FW areas! */ + /* ucode code RAM 128k */ + {0x000000, 0x020000, 0x920000, "uc_code", false}, + /* ucode data RAM 16k */ + {0x800000, 0x804000, 0x940000, "uc_data", false}, }; struct blink_on_off_time led_blink_time[] = { @@ -108,7 +118,7 @@ struct blink_on_off_time led_blink_time[] = { u8 led_polarity = LED_POLARITY_LOW_ACTIVE; /** - * return AHB address for given firmware/ucode internal (linker) address + * return AHB address for given firmware internal (linker) address * @x - internal address * If address have no valid AHB mapping, return 0 */ @@ -117,7 +127,8 @@ static u32 wmi_addr_remap(u32 x) uint i; for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { - if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) + if (fw_mapping[i].fw && + ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to))) return x + fw_mapping[i].host - fw_mapping[i].from; } -- cgit v1.1 From 8ae5d62c7ebafb24d9795c5ce873b45bec6b3f03 Mon Sep 17 00:00:00 2001 From: Lior David Date: Mon, 28 Nov 2016 13:49:03 +0200 Subject: wil6210: align to latest auto generated wmi.h Align to latest version of the auto generated wmi file describing the interface with FW. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.h | 392 ++++++++++++++++++++++----------- 1 file changed, 264 insertions(+), 128 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 2cc7775..d93a4d4 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -35,6 +35,7 @@ #define WMI_MAC_LEN (6) #define WMI_PROX_RANGE_NUM (3) #define WMI_MAX_LOSS_DMG_BEACONS (20) +#define MAX_NUM_OF_SECTORS (128) /* Mailbox interface * used for commands and events @@ -68,144 +69,149 @@ struct wmi_cmd_hdr { /* List of Commands */ enum wmi_command_id { - WMI_CONNECT_CMDID = 0x01, - WMI_DISCONNECT_CMDID = 0x03, - WMI_DISCONNECT_STA_CMDID = 0x04, - WMI_START_SCAN_CMDID = 0x07, - WMI_SET_BSS_FILTER_CMDID = 0x09, - WMI_SET_PROBED_SSID_CMDID = 0x0A, - WMI_SET_LISTEN_INT_CMDID = 0x0B, - WMI_BCON_CTRL_CMDID = 0x0F, - WMI_ADD_CIPHER_KEY_CMDID = 0x16, - WMI_DELETE_CIPHER_KEY_CMDID = 0x17, - WMI_PCP_CONF_CMDID = 0x18, - WMI_SET_APPIE_CMDID = 0x3F, - WMI_SET_WSC_STATUS_CMDID = 0x41, - WMI_PXMT_RANGE_CFG_CMDID = 0x42, - WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x43, - WMI_MEM_READ_CMDID = 0x800, - WMI_MEM_WR_CMDID = 0x801, - WMI_ECHO_CMDID = 0x803, - WMI_DEEP_ECHO_CMDID = 0x804, - WMI_CONFIG_MAC_CMDID = 0x805, - WMI_CONFIG_PHY_DEBUG_CMDID = 0x806, - WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x808, - WMI_PHY_GET_STATISTICS_CMDID = 0x809, - WMI_FS_TUNE_CMDID = 0x80A, - WMI_CORR_MEASURE_CMDID = 0x80B, - WMI_READ_RSSI_CMDID = 0x80C, - WMI_TEMP_SENSE_CMDID = 0x80E, - WMI_DC_CALIB_CMDID = 0x80F, - WMI_SEND_TONE_CMDID = 0x810, - WMI_IQ_TX_CALIB_CMDID = 0x811, - WMI_IQ_RX_CALIB_CMDID = 0x812, - WMI_SET_UCODE_IDLE_CMDID = 0x813, - WMI_SET_WORK_MODE_CMDID = 0x815, - WMI_LO_LEAKAGE_CALIB_CMDID = 0x816, - WMI_MARLON_R_READ_CMDID = 0x818, - WMI_MARLON_R_WRITE_CMDID = 0x819, - WMI_MARLON_R_TXRX_SEL_CMDID = 0x81A, - MAC_IO_STATIC_PARAMS_CMDID = 0x81B, - MAC_IO_DYNAMIC_PARAMS_CMDID = 0x81C, - WMI_SILENT_RSSI_CALIB_CMDID = 0x81D, - WMI_RF_RX_TEST_CMDID = 0x81E, - WMI_CFG_RX_CHAIN_CMDID = 0x820, - WMI_VRING_CFG_CMDID = 0x821, - WMI_BCAST_VRING_CFG_CMDID = 0x822, - WMI_VRING_BA_EN_CMDID = 0x823, - WMI_VRING_BA_DIS_CMDID = 0x824, - WMI_RCP_ADDBA_RESP_CMDID = 0x825, - WMI_RCP_DELBA_CMDID = 0x826, - WMI_SET_SSID_CMDID = 0x827, - WMI_GET_SSID_CMDID = 0x828, - WMI_SET_PCP_CHANNEL_CMDID = 0x829, - WMI_GET_PCP_CHANNEL_CMDID = 0x82A, - WMI_SW_TX_REQ_CMDID = 0x82B, - WMI_READ_MAC_RXQ_CMDID = 0x830, - WMI_READ_MAC_TXQ_CMDID = 0x831, - WMI_WRITE_MAC_RXQ_CMDID = 0x832, - WMI_WRITE_MAC_TXQ_CMDID = 0x833, - WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x834, - WMI_MLME_PUSH_CMDID = 0x835, - WMI_BEAMFORMING_MGMT_CMDID = 0x836, - WMI_BF_TXSS_MGMT_CMDID = 0x837, - WMI_BF_SM_MGMT_CMDID = 0x838, - WMI_BF_RXSS_MGMT_CMDID = 0x839, - WMI_BF_TRIG_CMDID = 0x83A, - WMI_LINK_MAINTAIN_CFG_WRITE_CMDID = 0x842, - WMI_LINK_MAINTAIN_CFG_READ_CMDID = 0x843, - WMI_SET_SECTORS_CMDID = 0x849, - WMI_MAINTAIN_PAUSE_CMDID = 0x850, - WMI_MAINTAIN_RESUME_CMDID = 0x851, - WMI_RS_MGMT_CMDID = 0x852, - WMI_RF_MGMT_CMDID = 0x853, - WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854, - WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855, - WMI_OTP_READ_CMDID = 0x856, - WMI_OTP_WRITE_CMDID = 0x857, - WMI_LED_CFG_CMDID = 0x858, + WMI_CONNECT_CMDID = 0x01, + WMI_DISCONNECT_CMDID = 0x03, + WMI_DISCONNECT_STA_CMDID = 0x04, + WMI_START_SCAN_CMDID = 0x07, + WMI_SET_BSS_FILTER_CMDID = 0x09, + WMI_SET_PROBED_SSID_CMDID = 0x0A, + WMI_SET_LISTEN_INT_CMDID = 0x0B, + WMI_BCON_CTRL_CMDID = 0x0F, + WMI_ADD_CIPHER_KEY_CMDID = 0x16, + WMI_DELETE_CIPHER_KEY_CMDID = 0x17, + WMI_PCP_CONF_CMDID = 0x18, + WMI_SET_APPIE_CMDID = 0x3F, + WMI_SET_WSC_STATUS_CMDID = 0x41, + WMI_PXMT_RANGE_CFG_CMDID = 0x42, + WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x43, + WMI_MEM_READ_CMDID = 0x800, + WMI_MEM_WR_CMDID = 0x801, + WMI_ECHO_CMDID = 0x803, + WMI_DEEP_ECHO_CMDID = 0x804, + WMI_CONFIG_MAC_CMDID = 0x805, + WMI_CONFIG_PHY_DEBUG_CMDID = 0x806, + WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x808, + WMI_PHY_GET_STATISTICS_CMDID = 0x809, + WMI_FS_TUNE_CMDID = 0x80A, + WMI_CORR_MEASURE_CMDID = 0x80B, + WMI_READ_RSSI_CMDID = 0x80C, + WMI_TEMP_SENSE_CMDID = 0x80E, + WMI_DC_CALIB_CMDID = 0x80F, + WMI_SEND_TONE_CMDID = 0x810, + WMI_IQ_TX_CALIB_CMDID = 0x811, + WMI_IQ_RX_CALIB_CMDID = 0x812, + WMI_SET_UCODE_IDLE_CMDID = 0x813, + WMI_SET_WORK_MODE_CMDID = 0x815, + WMI_LO_LEAKAGE_CALIB_CMDID = 0x816, + WMI_MARLON_R_READ_CMDID = 0x818, + WMI_MARLON_R_WRITE_CMDID = 0x819, + WMI_MARLON_R_TXRX_SEL_CMDID = 0x81A, + MAC_IO_STATIC_PARAMS_CMDID = 0x81B, + MAC_IO_DYNAMIC_PARAMS_CMDID = 0x81C, + WMI_SILENT_RSSI_CALIB_CMDID = 0x81D, + WMI_RF_RX_TEST_CMDID = 0x81E, + WMI_CFG_RX_CHAIN_CMDID = 0x820, + WMI_VRING_CFG_CMDID = 0x821, + WMI_BCAST_VRING_CFG_CMDID = 0x822, + WMI_VRING_BA_EN_CMDID = 0x823, + WMI_VRING_BA_DIS_CMDID = 0x824, + WMI_RCP_ADDBA_RESP_CMDID = 0x825, + WMI_RCP_DELBA_CMDID = 0x826, + WMI_SET_SSID_CMDID = 0x827, + WMI_GET_SSID_CMDID = 0x828, + WMI_SET_PCP_CHANNEL_CMDID = 0x829, + WMI_GET_PCP_CHANNEL_CMDID = 0x82A, + WMI_SW_TX_REQ_CMDID = 0x82B, + WMI_READ_MAC_RXQ_CMDID = 0x830, + WMI_READ_MAC_TXQ_CMDID = 0x831, + WMI_WRITE_MAC_RXQ_CMDID = 0x832, + WMI_WRITE_MAC_TXQ_CMDID = 0x833, + WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x834, + WMI_MLME_PUSH_CMDID = 0x835, + WMI_BEAMFORMING_MGMT_CMDID = 0x836, + WMI_BF_TXSS_MGMT_CMDID = 0x837, + WMI_BF_SM_MGMT_CMDID = 0x838, + WMI_BF_RXSS_MGMT_CMDID = 0x839, + WMI_BF_TRIG_CMDID = 0x83A, + WMI_LINK_MAINTAIN_CFG_WRITE_CMDID = 0x842, + WMI_LINK_MAINTAIN_CFG_READ_CMDID = 0x843, + WMI_SET_SECTORS_CMDID = 0x849, + WMI_MAINTAIN_PAUSE_CMDID = 0x850, + WMI_MAINTAIN_RESUME_CMDID = 0x851, + WMI_RS_MGMT_CMDID = 0x852, + WMI_RF_MGMT_CMDID = 0x853, + WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854, + WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855, + WMI_OTP_READ_CMDID = 0x856, + WMI_OTP_WRITE_CMDID = 0x857, + WMI_LED_CFG_CMDID = 0x858, /* Performance monitoring commands */ - WMI_BF_CTRL_CMDID = 0x862, - WMI_NOTIFY_REQ_CMDID = 0x863, - WMI_GET_STATUS_CMDID = 0x864, - WMI_GET_RF_STATUS_CMDID = 0x866, - WMI_GET_BASEBAND_TYPE_CMDID = 0x867, - WMI_UNIT_TEST_CMDID = 0x900, - WMI_HICCUP_CMDID = 0x901, - WMI_FLASH_READ_CMDID = 0x902, - WMI_FLASH_WRITE_CMDID = 0x903, + WMI_BF_CTRL_CMDID = 0x862, + WMI_NOTIFY_REQ_CMDID = 0x863, + WMI_GET_STATUS_CMDID = 0x864, + WMI_GET_RF_STATUS_CMDID = 0x866, + WMI_GET_BASEBAND_TYPE_CMDID = 0x867, + WMI_UNIT_TEST_CMDID = 0x900, + WMI_HICCUP_CMDID = 0x901, + WMI_FLASH_READ_CMDID = 0x902, + WMI_FLASH_WRITE_CMDID = 0x903, /* Power management */ - WMI_TRAFFIC_DEFERRAL_CMDID = 0x904, - WMI_TRAFFIC_RESUME_CMDID = 0x905, + WMI_TRAFFIC_DEFERRAL_CMDID = 0x904, + WMI_TRAFFIC_RESUME_CMDID = 0x905, /* P2P */ - WMI_P2P_CFG_CMDID = 0x910, - WMI_PORT_ALLOCATE_CMDID = 0x911, - WMI_PORT_DELETE_CMDID = 0x912, - WMI_POWER_MGMT_CFG_CMDID = 0x913, - WMI_START_LISTEN_CMDID = 0x914, - WMI_START_SEARCH_CMDID = 0x915, - WMI_DISCOVERY_START_CMDID = 0x916, - WMI_DISCOVERY_STOP_CMDID = 0x917, - WMI_PCP_START_CMDID = 0x918, - WMI_PCP_STOP_CMDID = 0x919, - WMI_GET_PCP_FACTOR_CMDID = 0x91B, + WMI_P2P_CFG_CMDID = 0x910, + WMI_PORT_ALLOCATE_CMDID = 0x911, + WMI_PORT_DELETE_CMDID = 0x912, + WMI_POWER_MGMT_CFG_CMDID = 0x913, + WMI_START_LISTEN_CMDID = 0x914, + WMI_START_SEARCH_CMDID = 0x915, + WMI_DISCOVERY_START_CMDID = 0x916, + WMI_DISCOVERY_STOP_CMDID = 0x917, + WMI_PCP_START_CMDID = 0x918, + WMI_PCP_STOP_CMDID = 0x919, + WMI_GET_PCP_FACTOR_CMDID = 0x91B, /* Power Save Configuration Commands */ - WMI_PS_DEV_PROFILE_CFG_CMDID = 0x91C, + WMI_PS_DEV_PROFILE_CFG_CMDID = 0x91C, /* Not supported yet */ - WMI_PS_DEV_CFG_CMDID = 0x91D, + WMI_PS_DEV_CFG_CMDID = 0x91D, /* Not supported yet */ - WMI_PS_DEV_CFG_READ_CMDID = 0x91E, + WMI_PS_DEV_CFG_READ_CMDID = 0x91E, /* Per MAC Power Save Configuration commands * Not supported yet */ - WMI_PS_MID_CFG_CMDID = 0x91F, + WMI_PS_MID_CFG_CMDID = 0x91F, /* Not supported yet */ - WMI_PS_MID_CFG_READ_CMDID = 0x920, - WMI_RS_CFG_CMDID = 0x921, - WMI_GET_DETAILED_RS_RES_CMDID = 0x922, - WMI_AOA_MEAS_CMDID = 0x923, - WMI_SET_MGMT_RETRY_LIMIT_CMDID = 0x930, - WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931, - WMI_TOF_SESSION_START_CMDID = 0x991, - WMI_TOF_GET_CAPABILITIES_CMDID = 0x992, - WMI_TOF_SET_LCR_CMDID = 0x993, - WMI_TOF_SET_LCI_CMDID = 0x994, - WMI_TOF_CHANNEL_INFO_CMDID = 0x995, - WMI_GET_RF_SECTOR_PARAMS_CMDID = 0x9A0, - WMI_SET_RF_SECTOR_PARAMS_CMDID = 0x9A1, - WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A2, - WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A3, - WMI_SET_RF_SECTOR_ON_CMDID = 0x9A4, - WMI_SET_MAC_ADDRESS_CMDID = 0xF003, - WMI_ABORT_SCAN_CMDID = 0xF007, - WMI_SET_PROMISCUOUS_MODE_CMDID = 0xF041, - WMI_GET_PMK_CMDID = 0xF048, - WMI_SET_PASSPHRASE_CMDID = 0xF049, - WMI_SEND_ASSOC_RES_CMDID = 0xF04A, - WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xF04B, - WMI_MAC_ADDR_REQ_CMDID = 0xF04D, - WMI_FW_VER_CMDID = 0xF04E, - WMI_PMC_CMDID = 0xF04F, + WMI_PS_MID_CFG_READ_CMDID = 0x920, + WMI_RS_CFG_CMDID = 0x921, + WMI_GET_DETAILED_RS_RES_CMDID = 0x922, + WMI_AOA_MEAS_CMDID = 0x923, + WMI_SET_MGMT_RETRY_LIMIT_CMDID = 0x930, + WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931, + WMI_TOF_SESSION_START_CMDID = 0x991, + WMI_TOF_GET_CAPABILITIES_CMDID = 0x992, + WMI_TOF_SET_LCR_CMDID = 0x993, + WMI_TOF_SET_LCI_CMDID = 0x994, + WMI_TOF_CHANNEL_INFO_CMDID = 0x995, + WMI_TOF_SET_TX_RX_OFFSET_CMDID = 0x997, + WMI_TOF_GET_TX_RX_OFFSET_CMDID = 0x998, + WMI_GET_RF_SECTOR_PARAMS_CMDID = 0x9A0, + WMI_SET_RF_SECTOR_PARAMS_CMDID = 0x9A1, + WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A2, + WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID = 0x9A3, + WMI_SET_RF_SECTOR_ON_CMDID = 0x9A4, + WMI_PRIO_TX_SECTORS_ORDER_CMDID = 0x9A5, + WMI_PRIO_TX_SECTORS_NUMBER_CMDID = 0x9A6, + WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_CMDID = 0x9A7, + WMI_SET_MAC_ADDRESS_CMDID = 0xF003, + WMI_ABORT_SCAN_CMDID = 0xF007, + WMI_SET_PROMISCUOUS_MODE_CMDID = 0xF041, + WMI_GET_PMK_CMDID = 0xF048, + WMI_SET_PASSPHRASE_CMDID = 0xF049, + WMI_SEND_ASSOC_RES_CMDID = 0xF04A, + WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xF04B, + WMI_MAC_ADDR_REQ_CMDID = 0xF04D, + WMI_FW_VER_CMDID = 0xF04E, + WMI_PMC_CMDID = 0xF04F, }; /* WMI_CONNECT_CMDID */ @@ -959,6 +965,15 @@ struct wmi_tof_channel_info_cmd { __le32 channel_info_report_request; } __packed; +/* WMI_TOF_SET_TX_RX_OFFSET_CMDID */ +struct wmi_tof_set_tx_rx_offset_cmd { + /* TX delay offset */ + __le32 tx_offset; + /* RX delay offset */ + __le32 rx_offset; + __le32 reserved[2]; +} __packed; + /* WMI Events * List of Events (target to host) */ @@ -1060,11 +1075,16 @@ enum wmi_event_id { WMI_TOF_SET_LCI_EVENTID = 0x1994, WMI_TOF_FTM_PER_DEST_RES_EVENTID = 0x1995, WMI_TOF_CHANNEL_INFO_EVENTID = 0x1996, + WMI_TOF_SET_TX_RX_OFFSET_EVENTID = 0x1997, + WMI_TOF_GET_TX_RX_OFFSET_EVENTID = 0x1998, WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A0, WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID = 0x19A1, WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID = 0x19A2, WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID = 0x19A3, WMI_SET_RF_SECTOR_ON_DONE_EVENTID = 0x19A4, + WMI_PRIO_TX_SECTORS_ORDER_EVENTID = 0x19A5, + WMI_PRIO_TX_SECTORS_NUMBER_EVENTID = 0x19A6, + WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_EVENTID = 0x19A7, WMI_SET_CHANNEL_EVENTID = 0x9000, WMI_ASSOC_REQ_EVENTID = 0x9001, WMI_EAPOL_RX_EVENTID = 0x9002, @@ -1190,6 +1210,7 @@ enum baseband_type { BASEBAND_SPARROW_M_B0 = 0x05, BASEBAND_SPARROW_M_C0 = 0x06, BASEBAND_SPARROW_M_D0 = 0x07, + BASEBAND_TALYN_M_A0 = 0x08, }; /* WMI_GET_BASEBAND_TYPE_EVENTID */ @@ -2224,6 +2245,25 @@ struct wmi_tof_channel_info_event { u8 report[0]; } __packed; +/* WMI_TOF_SET_TX_RX_OFFSET_EVENTID */ +struct wmi_tof_set_tx_rx_offset_event { + /* enum wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_TOF_GET_TX_RX_OFFSET_EVENTID */ +struct wmi_tof_get_tx_rx_offset_event { + /* enum wmi_fw_status */ + u8 status; + u8 reserved1[3]; + /* TX delay offset */ + __le32 tx_offset; + /* RX delay offset */ + __le32 rx_offset; + __le32 reserved2[2]; +} __packed; + /* Result status codes for WMI commands */ enum wmi_rf_sector_status { WMI_RF_SECTOR_STATUS_SUCCESS = 0x00, @@ -2388,4 +2428,100 @@ struct wmi_set_rf_sector_on_done_event { u8 reserved[3]; } __packed; +enum wmi_sector_sweep_type { + WMI_SECTOR_SWEEP_TYPE_TXSS = 0x00, + WMI_SECTOR_SWEEP_TYPE_BCON = 0x01, + WMI_SECTOR_SWEEP_TYPE_TXSS_AND_BCON = 0x02, + WMI_SECTOR_SWEEP_TYPE_NUM = 0x03, +}; + +/* WMI_PRIO_TX_SECTORS_ORDER_CMDID + * + * Set the order of TX sectors in TXSS and/or Beacon(AP). + * + * Returned event: + * - WMI_PRIO_TX_SECTORS_ORDER_EVENTID + */ +struct wmi_prio_tx_sectors_order_cmd { + /* tx sectors order to be applied, 0xFF for end of array */ + u8 tx_sectors_priority_array[MAX_NUM_OF_SECTORS]; + /* enum wmi_sector_sweep_type, TXSS and/or Beacon */ + u8 sector_sweep_type; + /* needed only for TXSS configuration */ + u8 cid; + /* alignment to 32b */ + u8 reserved[2]; +} __packed; + +/* completion status codes */ +enum wmi_prio_tx_sectors_cmd_status { + WMI_PRIO_TX_SECT_CMD_STATUS_SUCCESS = 0x00, + WMI_PRIO_TX_SECT_CMD_STATUS_BAD_PARAM = 0x01, + /* other error */ + WMI_PRIO_TX_SECT_CMD_STATUS_ERROR = 0x02, +}; + +/* WMI_PRIO_TX_SECTORS_ORDER_EVENTID */ +struct wmi_prio_tx_sectors_order_event { + /* enum wmi_prio_tx_sectors_cmd_status */ + u8 status; + /* alignment to 32b */ + u8 reserved[3]; +} __packed; + +struct wmi_prio_tx_sectors_num_cmd { + /* [0-128], 0 = No changes */ + u8 beacon_number_of_sectors; + /* [0-128], 0 = No changes */ + u8 txss_number_of_sectors; + /* [0-8] needed only for TXSS configuration */ + u8 cid; +} __packed; + +/* WMI_PRIO_TX_SECTORS_NUMBER_CMDID + * + * Set the number of active sectors in TXSS and/or Beacon. + * + * Returned event: + * - WMI_PRIO_TX_SECTORS_NUMBER_EVENTID + */ +struct wmi_prio_tx_sectors_number_cmd { + struct wmi_prio_tx_sectors_num_cmd active_sectors_num; + /* alignment to 32b */ + u8 reserved; +} __packed; + +/* WMI_PRIO_TX_SECTORS_NUMBER_EVENTID */ +struct wmi_prio_tx_sectors_number_event { + /* enum wmi_prio_tx_sectors_cmd_status */ + u8 status; + /* alignment to 32b */ + u8 reserved[3]; +} __packed; + +/* WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_CMDID + * + * Set default sectors order and number (hard coded in board file) + * in TXSS and/or Beacon. + * + * Returned event: + * - WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_EVENTID + */ +struct wmi_prio_tx_sectors_set_default_cfg_cmd { + /* enum wmi_sector_sweep_type, TXSS and/or Beacon */ + u8 sector_sweep_type; + /* needed only for TXSS configuration */ + u8 cid; + /* alignment to 32b */ + u8 reserved[2]; +} __packed; + +/* WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_EVENTID */ +struct wmi_prio_tx_sectors_set_default_cfg_event { + /* enum wmi_prio_tx_sectors_cmd_status */ + u8 status; + /* alignment to 32b */ + u8 reserved[3]; +} __packed; + #endif /* __WILOCITY_WMI_H__ */ -- cgit v1.1 From 384abd33d5d571361a0d8eb67e130aeccf2cb3db Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 24 Nov 2016 21:58:28 +0800 Subject: ath5k: drop duplicate header vmalloc.h Drop duplicate header vmalloc.h from ath5k/debug.c. Signed-off-by: Geliang Tang Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath5k/debug.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 4f8d9ed..d068df5 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -66,7 +66,6 @@ #include #include -#include #include "debug.h" #include "ath5k.h" #include "reg.h" -- cgit v1.1 From c59f13bbead475096bdfebc7ef59c12e180858de Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Tue, 29 Nov 2016 18:59:02 -0500 Subject: rtl8xxxu: Work around issue with 8192eu and 8723bu devices not reconnecting The H2C MEDIA_STATUS_RPT command for some reason causes 8192eu and 8723bu devices not being able to reconnect. Reported-by: Barry Day Cc: #4.8+ Signed-off-by: Jes Sorensen Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index a9137ab..3a86675 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4372,6 +4372,13 @@ void rtl8xxxu_gen1_report_connect(struct rtl8xxxu_priv *priv, void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv, u8 macid, bool connect) { +#ifdef RTL8XXXU_GEN2_REPORT_CONNECT + /* + * Barry Day reports this causes issues with 8192eu and 8723bu + * devices reconnecting. The reason for this is unclear, but + * until it is better understood, leave the code in place but + * disabled, so it is not lost. + */ struct h2c_cmd h2c; memset(&h2c, 0, sizeof(struct h2c_cmd)); @@ -4383,6 +4390,7 @@ void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv, h2c.media_status_rpt.parm &= ~BIT(0); rtl8xxxu_gen2_h2c_cmd(priv, &h2c, sizeof(h2c.media_status_rpt)); +#endif } void rtl8xxxu_gen1_init_aggregation(struct rtl8xxxu_priv *priv) -- cgit v1.1 From 2f500e39ca96e3d2da911b79c9b63eb6134548de Mon Sep 17 00:00:00 2001 From: Chris Paterson Date: Thu, 24 Nov 2016 16:11:56 +0000 Subject: can: rcar_can: Add r8a7796 support Signed-off-by: Chris Paterson Reviewed-by: Geert Uytterhoeven Acked-by: Marc Kleine-Budde Acked-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- Documentation/devicetree/bindings/net/can/rcar_can.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/net/can/rcar_can.txt b/Documentation/devicetree/bindings/net/can/rcar_can.txt index 8d40ab2..06bb7cc 100644 --- a/Documentation/devicetree/bindings/net/can/rcar_can.txt +++ b/Documentation/devicetree/bindings/net/can/rcar_can.txt @@ -10,6 +10,7 @@ Required properties: "renesas,can-r8a7793" if CAN controller is a part of R8A7793 SoC. "renesas,can-r8a7794" if CAN controller is a part of R8A7794 SoC. "renesas,can-r8a7795" if CAN controller is a part of R8A7795 SoC. + "renesas,can-r8a7796" if CAN controller is a part of R8A7796 SoC. "renesas,rcar-gen1-can" for a generic R-Car Gen1 compatible device. "renesas,rcar-gen2-can" for a generic R-Car Gen2 compatible device. "renesas,rcar-gen3-can" for a generic R-Car Gen3 compatible device. @@ -24,11 +25,12 @@ Required properties: - pinctrl-0: pin control group to be used for this controller. - pinctrl-names: must be "default". -Required properties for "renesas,can-r8a7795" compatible: -In R8A7795 SoC, "clkp2" can be CANFD clock. This is a div6 clock and can be -used by both CAN and CAN FD controller at the same time. It needs to be scaled -to maximum frequency if any of these controllers use it. This is done using -the below properties. +Required properties for "renesas,can-r8a7795" and "renesas,can-r8a7796" +compatible: +In R8A7795 and R8A7796 SoCs, "clkp2" can be CANFD clock. This is a div6 clock +and can be used by both CAN and CAN FD controller at the same time. It needs to +be scaled to maximum frequency if any of these controllers use it. This is done +using the below properties: - assigned-clocks: phandle of clkp2(CANFD) clock. - assigned-clock-rates: maximum frequency of this clock. -- cgit v1.1 From b4e79e46936f713ca59120c68977e06be325d11c Mon Sep 17 00:00:00 2001 From: Chris Paterson Date: Thu, 24 Nov 2016 16:11:57 +0000 Subject: can: rcar_canfd: Add r8a7796 support Signed-off-by: Chris Paterson Reviewed-by: Geert Uytterhoeven Acked-by: Marc Kleine-Budde Acked-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- Documentation/devicetree/bindings/net/can/rcar_canfd.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/net/can/rcar_canfd.txt b/Documentation/devicetree/bindings/net/can/rcar_canfd.txt index 22a6f10..788f273 100644 --- a/Documentation/devicetree/bindings/net/can/rcar_canfd.txt +++ b/Documentation/devicetree/bindings/net/can/rcar_canfd.txt @@ -5,6 +5,7 @@ Required properties: - compatible: Must contain one or more of the following: - "renesas,rcar-gen3-canfd" for R-Car Gen3 compatible controller. - "renesas,r8a7795-canfd" for R8A7795 (R-Car H3) compatible controller. + - "renesas,r8a7796-canfd" for R8A7796 (R-Car M3) compatible controller. When compatible with the generic version, nodes must list the SoC-specific version corresponding to the platform first, followed by the @@ -23,11 +24,12 @@ The name of the child nodes are "channel0" and "channel1" respectively. Each child node supports the "status" property only, which is used to enable/disable the respective channel. -Required properties for "renesas,r8a7795-canfd" compatible: -In R8A7795 SoC, canfd clock is a div6 clock and can be used by both CAN -and CAN FD controller at the same time. It needs to be scaled to maximum -frequency if any of these controllers use it. This is done using the -below properties. +Required properties for "renesas,r8a7795-canfd" and "renesas,r8a7796-canfd" +compatible: +In R8A7795 and R8A7796 SoCs, canfd clock is a div6 clock and can be used by both +CAN and CAN FD controller at the same time. It needs to be scaled to maximum +frequency if any of these controllers use it. This is done using the below +properties: - assigned-clocks: phandle of canfd clock. - assigned-clock-rates: maximum frequency of this clock. -- cgit v1.1 From 08469bba2852da651ce7a047495685c10164c63b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 23 Nov 2016 14:44:46 +0100 Subject: can: rcar_canfd: Correct order of interrupt specifiers According to both DTS (example and actual files), and Linux driver code, the first interrupt specifier should be the Channel interrupt, while the second interrupt specifier should be the Global interrupt. Signed-off-by: Geert Uytterhoeven Acked-by: Ramesh Shanmugasundaram Acked-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- Documentation/devicetree/bindings/net/can/rcar_canfd.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/can/rcar_canfd.txt b/Documentation/devicetree/bindings/net/can/rcar_canfd.txt index 788f273..93c3a6a 100644 --- a/Documentation/devicetree/bindings/net/can/rcar_canfd.txt +++ b/Documentation/devicetree/bindings/net/can/rcar_canfd.txt @@ -12,7 +12,7 @@ Required properties: family-specific and/or generic versions. - reg: physical base address and size of the R-Car CAN FD register map. -- interrupts: interrupt specifier for the Global & Channel interrupts +- interrupts: interrupt specifiers for the Channel & Global interrupts - clocks: phandles and clock specifiers for 3 clock inputs. - clock-names: 3 clock input name strings: "fck", "canfd", "can_clk". - pinctrl-0: pin control group to be used for this controller. -- cgit v1.1 From 0d8f8efd32bace9f222fcc92d4a3132d877e5df6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 29 Nov 2016 16:27:17 +0000 Subject: net: can: usb: kvaser_usb: fix spelling mistake of "outstanding" Trivial fix to spelling mistake "oustanding" to "outstanding" in comment and dev_dbg message. Signed-off-by: Colin Ian King Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index d51e0c4..18cc529 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -459,7 +459,7 @@ struct kvaser_usb { struct usb_endpoint_descriptor *bulk_in, *bulk_out; struct usb_anchor rx_submitted; - /* @max_tx_urbs: Firmware-reported maximum number of oustanding, + /* @max_tx_urbs: Firmware-reported maximum number of outstanding, * not yet ACKed, transmissions on this device. This value is * also used as a sentinel for marking free tx contexts. */ @@ -2027,7 +2027,7 @@ static int kvaser_usb_probe(struct usb_interface *intf, ((dev->fw_version >> 16) & 0xff), (dev->fw_version & 0xffff)); - dev_dbg(&intf->dev, "Max oustanding tx = %d URBs\n", dev->max_tx_urbs); + dev_dbg(&intf->dev, "Max outstanding tx = %d URBs\n", dev->max_tx_urbs); err = kvaser_usb_get_card_info(dev); if (err) { -- cgit v1.1 From f4ed2fe34fb793755ef8cfc3509e783c4709ffc1 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Tue, 29 Nov 2016 15:16:46 +0530 Subject: net: phy: add mdix_ctrl to hold the user configuration. Add new parameter mdix_ctrl to hold the user configuration. Existing mdix maintain the current status of MDI(X) crossover performed or not. mdix_ctrl can configure either ETH_TP_MDI or ETH_TP_MDI_X orETH_TP_MDI_AUTO. Signed-off-by: Raju Lakkaraju Signed-off-by: David S. Miller --- include/linux/phy.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index b53177f..feb8a98 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -450,6 +450,7 @@ struct phy_device { struct net_device *attached_dev; u8 mdix; + u8 mdix_ctrl; void (*adjust_link)(struct net_device *dev); }; -- cgit v1.1 From 1004ee619625a3332f7d3d41a522d799d6cce982 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Tue, 29 Nov 2016 15:16:47 +0530 Subject: net: phy: update the mdix_ctrl with correct value. Update the mdix and mdix_ctrl with corresponding ethtool configuration parameters. Signed-off-by: Raju Lakkaraju Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a3981cc..25f93a9 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -389,7 +389,7 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) phydev->duplex = cmd->duplex; - phydev->mdix = cmd->eth_tp_mdix_ctrl; + phydev->mdix_ctrl = cmd->eth_tp_mdix_ctrl; /* Restart the PHY */ phy_start_aneg(phydev); @@ -443,7 +443,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev, phydev->duplex = duplex; - phydev->mdix = cmd->base.eth_tp_mdix_ctrl; + phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl; /* Restart the PHY */ phy_start_aneg(phydev); @@ -469,7 +469,8 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) cmd->transceiver = phy_is_internal(phydev) ? XCVR_INTERNAL : XCVR_EXTERNAL; cmd->autoneg = phydev->autoneg; - cmd->eth_tp_mdix_ctrl = phydev->mdix; + cmd->eth_tp_mdix_ctrl = phydev->mdix_ctrl; + cmd->eth_tp_mdix = phydev->mdix; return 0; } @@ -496,7 +497,8 @@ int phy_ethtool_ksettings_get(struct phy_device *phydev, cmd->base.phy_address = phydev->mdio.addr; cmd->base.autoneg = phydev->autoneg; - cmd->base.eth_tp_mdix_ctrl = phydev->mdix; + cmd->base.eth_tp_mdix_ctrl = phydev->mdix_ctrl; + cmd->base.eth_tp_mdix = phydev->mdix; return 0; } -- cgit v1.1 From 233275ecf3c1047f69e5def0303a4e615c526568 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Tue, 29 Nov 2016 15:16:48 +0530 Subject: net: phy: Add mdi(x) support in Microsemi PHYs driver To connect two ports of the same configuration (MDI to MDI or MDI-X to MDI-X) with a 10/100/1000 Mbit/s connection, an Ethernet crossover cable is needed to cross over the transmit and receive signals in the cable, so that they are matched at the connector level. When connecting an MDI port to an MDI-X port a straight through cable is used while to connect two MDI ports or two MDI-X ports a crossover cable must be used. Conventionally MDI is used on end devices while MDI-X is used on hubs and switches Auto MDI-X automatically detects the required cable connection type and configures the connection appropriately, removing the need for crossover cables to interconnect switches or connecting PCs peer-to-peer. Signed-off-by: Raju Lakkaraju Signed-off-by: David S. Miller --- drivers/net/phy/mscc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 97 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index 7a3740c..e03ead8 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -27,6 +27,11 @@ enum rgmii_rx_clock_delay { /* Microsemi VSC85xx PHY registers */ /* IEEE 802. Std Registers */ +#define MSCC_PHY_BYPASS_CONTROL 18 +#define DISABLE_HP_AUTO_MDIX_MASK 0x0080 +#define DISABLE_PAIR_SWAP_CORR_MASK 0x0020 +#define DISABLE_POLARITY_CORR_MASK 0x0010 + #define MSCC_PHY_EXT_PHY_CNTL_1 23 #define MAC_IF_SELECTION_MASK 0x1800 #define MAC_IF_SELECTION_GMII 0 @@ -44,12 +49,20 @@ enum rgmii_rx_clock_delay { #define EDGE_RATE_CNTL_POS 5 #define EDGE_RATE_CNTL_MASK 0x00E0 +#define MSCC_PHY_DEV_AUX_CNTL 28 +#define HP_AUTO_MDIX_X_OVER_IND_MASK 0x2000 + #define MSCC_EXT_PAGE_ACCESS 31 #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */ #define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */ #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */ /* Extended Page 1 Registers */ +#define MSCC_PHY_EXT_MODE_CNTL 19 +#define FORCE_MDI_CROSSOVER_MASK 0x000C +#define FORCE_MDI_CROSSOVER_MDIX 0x000C +#define FORCE_MDI_CROSSOVER_MDI 0x0008 + #define MSCC_PHY_ACTIPHY_CNTL 20 #define DOWNSHIFT_CNTL_MASK 0x001C #define DOWNSHIFT_EN 0x0010 @@ -110,6 +123,59 @@ static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page) return rc; } +static int vsc85xx_mdix_get(struct phy_device *phydev, u8 *mdix) +{ + u16 reg_val; + + reg_val = phy_read(phydev, MSCC_PHY_DEV_AUX_CNTL); + if (reg_val & HP_AUTO_MDIX_X_OVER_IND_MASK) + *mdix = ETH_TP_MDI_X; + else + *mdix = ETH_TP_MDI; + + return 0; +} + +static int vsc85xx_mdix_set(struct phy_device *phydev, u8 mdix) +{ + int rc; + u16 reg_val; + + reg_val = phy_read(phydev, MSCC_PHY_BYPASS_CONTROL); + if ((mdix == ETH_TP_MDI) || (mdix == ETH_TP_MDI_X)) { + reg_val |= (DISABLE_PAIR_SWAP_CORR_MASK | + DISABLE_POLARITY_CORR_MASK | + DISABLE_HP_AUTO_MDIX_MASK); + } else { + reg_val &= ~(DISABLE_PAIR_SWAP_CORR_MASK | + DISABLE_POLARITY_CORR_MASK | + DISABLE_HP_AUTO_MDIX_MASK); + } + rc = phy_write(phydev, MSCC_PHY_BYPASS_CONTROL, reg_val); + if (rc != 0) + return rc; + + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED); + if (rc != 0) + return rc; + + reg_val = phy_read(phydev, MSCC_PHY_EXT_MODE_CNTL); + reg_val &= ~(FORCE_MDI_CROSSOVER_MASK); + if (mdix == ETH_TP_MDI) + reg_val |= FORCE_MDI_CROSSOVER_MDI; + else if (mdix == ETH_TP_MDI_X) + reg_val |= FORCE_MDI_CROSSOVER_MDIX; + rc = phy_write(phydev, MSCC_PHY_EXT_MODE_CNTL, reg_val); + if (rc != 0) + return rc; + + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD); + if (rc != 0) + return rc; + + return genphy_restart_aneg(phydev); +} + static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count) { int rc; @@ -375,6 +441,7 @@ static int vsc85xx_default_config(struct phy_device *phydev) int rc; u16 reg_val; + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; mutex_lock(&phydev->lock); rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2); if (rc != 0) @@ -464,6 +531,28 @@ static int vsc85xx_config_intr(struct phy_device *phydev) return rc; } +static int vsc85xx_config_aneg(struct phy_device *phydev) +{ + int rc; + + rc = vsc85xx_mdix_set(phydev, phydev->mdix_ctrl); + if (rc < 0) + return rc; + + return genphy_config_aneg(phydev); +} + +static int vsc85xx_read_status(struct phy_device *phydev) +{ + int rc; + + rc = vsc85xx_mdix_get(phydev, &phydev->mdix); + if (rc < 0) + return rc; + + return genphy_read_status(phydev); +} + static int vsc85xx_probe(struct phy_device *phydev) { int rate_magic; @@ -494,9 +583,9 @@ static struct phy_driver vsc85xx_driver[] = { .flags = PHY_HAS_INTERRUPT, .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, - .config_aneg = &genphy_config_aneg, + .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, - .read_status = &genphy_read_status, + .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .suspend = &genphy_suspend, @@ -515,9 +604,9 @@ static struct phy_driver vsc85xx_driver[] = { .flags = PHY_HAS_INTERRUPT, .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, - .config_aneg = &genphy_config_aneg, + .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, - .read_status = &genphy_read_status, + .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .suspend = &genphy_suspend, @@ -536,9 +625,9 @@ static struct phy_driver vsc85xx_driver[] = { .flags = PHY_HAS_INTERRUPT, .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, - .config_aneg = &genphy_config_aneg, + .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, - .read_status = &genphy_read_status, + .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .suspend = &genphy_suspend, @@ -557,9 +646,9 @@ static struct phy_driver vsc85xx_driver[] = { .flags = PHY_HAS_INTERRUPT, .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, - .config_aneg = &genphy_config_aneg, + .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, - .read_status = &genphy_read_status, + .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .suspend = &genphy_suspend, -- cgit v1.1 From 4e26c5c31570b2b4d8dd45b73e0c4e358f1eb2b4 Mon Sep 17 00:00:00 2001 From: Raju Lakkaraju Date: Tue, 29 Nov 2016 15:16:49 +0530 Subject: net: phy: Fix the mdix_ctrl changes PHY drivers to have an eth_tp_mdix_ctrl to indicate what is the configured MDI setting, and read eth_tp_mdi to indicate what is the current status, Add new parameter mdix_ctrl in phy_device structure and fix driver. Signed-off-by: Raju Lakkaraju Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 4 ++-- drivers/net/phy/microchip.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index fa31f50..e269262 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -268,7 +268,7 @@ static int marvell_config_aneg(struct phy_device *phydev) if (err < 0) return err; - err = marvell_set_polarity(phydev, phydev->mdix); + err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) return err; @@ -311,7 +311,7 @@ static int m88e1111_config_aneg(struct phy_device *phydev) */ err = phy_write(phydev, MII_BMCR, BMCR_RESET); - err = marvell_set_polarity(phydev, phydev->mdix); + err = marvell_set_polarity(phydev, phydev->mdix_ctrl); if (err < 0) return err; diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index eb4db22..12825a5 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -111,7 +111,7 @@ static void lan88xx_set_mdix(struct phy_device *phydev) int buf; int val; - switch (phydev->mdix) { + switch (phydev->mdix_ctrl) { case ETH_TP_MDI: val = LAN88XX_EXT_MODE_CTRL_MDI_; break; -- cgit v1.1 From 60602982720f3a77366ee3e493a6e3d15e7e84f5 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 29 Nov 2016 09:14:56 -0800 Subject: audit: remove useless synchronize_net() netlink kernel socket is protected by refcount, not RCU. Its rcv path is neither protected by RCU. So the synchronize_net() is just pointless. Cc: Richard Guy Briggs Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- kernel/audit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 92c463d..67b9fbd8 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1172,9 +1172,8 @@ static void __net_exit audit_net_exit(struct net *net) audit_sock = NULL; } - RCU_INIT_POINTER(aunet->nlsk, NULL); - synchronize_net(); netlink_kernel_release(sock); + aunet->nlsk = NULL; } static struct pernet_operations audit_net_ops __net_initdata = { -- cgit v1.1 From 2934c9dbd385c6be08a1f4a33820d132979d61a7 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 30 Nov 2016 16:37:34 +0800 Subject: rtnetlink: return the correct error code Before this patch, function ndo_dflt_fdb_dump() will always return code from uc fdb dump. The reture code of mc fdb dump is lost. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index bd85570..4cb84f0 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -3177,7 +3177,7 @@ int ndo_dflt_fdb_dump(struct sk_buff *skb, err = nlmsg_populate_fdb(skb, cb, dev, idx, &dev->uc); if (err) goto out; - nlmsg_populate_fdb(skb, cb, dev, idx, &dev->mc); + err = nlmsg_populate_fdb(skb, cb, dev, idx, &dev->mc); out: netif_addr_unlock_bh(dev); return err; -- cgit v1.1 From edd96fa0defd3266f6508eda9acb016b71e77c2e Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 1 Dec 2016 17:00:54 +0000 Subject: sfc: fix debug message format string in efx_farch_handle_rx_not_ok Defalconisation removed one of the string arguments, but missed the corresponding %s. Fixes: 5a6681e22c14 ("sfc: separate out SFC4000 ("Falcon") support into new sfc-falcon driver") Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/farch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c index c282d66..91aa3ec 100644 --- a/drivers/net/ethernet/sfc/farch.c +++ b/drivers/net/ethernet/sfc/farch.c @@ -913,7 +913,7 @@ static u16 efx_farch_handle_rx_not_ok(struct efx_rx_queue *rx_queue, if (rx_ev_other_err && net_ratelimit()) { netif_dbg(efx, rx_err, efx->net_dev, " RX queue %d unexpected RX event " - EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n", + EFX_QWORD_FMT "%s%s%s%s%s%s%s\n", efx_rx_queue_index(rx_queue), EFX_QWORD_VAL(*event), rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", rx_ev_ip_hdr_chksum_err ? -- cgit v1.1 From d7d6cabaa1a5295b5a6836b84cc599488ab5c84d Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 1 Dec 2016 17:01:07 +0000 Subject: sfc: don't select SFC_FALCON Easy enough for Falcon users to enable it when making oldconfig. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig index 0e16197..605ebc7 100644 --- a/drivers/net/ethernet/sfc/Kconfig +++ b/drivers/net/ethernet/sfc/Kconfig @@ -6,7 +6,6 @@ config SFC select I2C select I2C_ALGOBIT select PTP_1588_CLOCK - select SFC_FALCON ---help--- This driver supports 10/40-gigabit Ethernet cards based on the Solarflare SFC9000-family and SFC9100-family controllers. -- cgit v1.1 From 99831b1ea5084d2bd0575d2985e8263f7eee54b7 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 1 Dec 2016 17:01:19 +0000 Subject: sfc: remove RESET_TYPE_RX_RECOVERY It's no longer used now that Falcon is gone. Also remove a reference in a comment to an ioctl that doesn't exist. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 1 - drivers/net/ethernet/sfc/enum.h | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 4a234a8..da7028d 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -82,7 +82,6 @@ const char *const efx_reset_type_names[] = { [RESET_TYPE_DISABLE] = "DISABLE", [RESET_TYPE_TX_WATCHDOG] = "TX_WATCHDOG", [RESET_TYPE_INT_ERROR] = "INT_ERROR", - [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY", [RESET_TYPE_DMA_ERROR] = "DMA_ERROR", [RESET_TYPE_TX_SKIP] = "TX_SKIP", [RESET_TYPE_MC_FAILURE] = "MC_FAILURE", diff --git a/drivers/net/ethernet/sfc/enum.h b/drivers/net/ethernet/sfc/enum.h index c94f562..6fa8242 100644 --- a/drivers/net/ethernet/sfc/enum.h +++ b/drivers/net/ethernet/sfc/enum.h @@ -148,7 +148,6 @@ enum efx_loopback_mode { * @RESET_TYPE_DISABLE: Reset datapath, MAC and PHY; leave NIC disabled * @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog * @RESET_TYPE_INT_ERROR: reset due to internal error - * @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors * @RESET_TYPE_DMA_ERROR: DMA error * @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors * @RESET_TYPE_MC_FAILURE: MC reboot/assertion @@ -166,15 +165,13 @@ enum reset_type { RESET_TYPE_MAX_METHOD, RESET_TYPE_TX_WATCHDOG, RESET_TYPE_INT_ERROR, - RESET_TYPE_RX_RECOVERY, RESET_TYPE_DMA_ERROR, RESET_TYPE_TX_SKIP, RESET_TYPE_MC_FAILURE, /* RESET_TYPE_MCDI_TIMEOUT is actually a method, not just a reason, but * it doesn't fit the scope hierarchy (not well-ordered by inclusion). * We encode this by having its enum value be greater than - * RESET_TYPE_MAX_METHOD. This also prevents issuing it with - * efx_ioctl_reset. + * RESET_TYPE_MAX_METHOD. */ RESET_TYPE_MCDI_TIMEOUT, RESET_TYPE_MAX, -- cgit v1.1 From dc855b3b74187475db2243356d85d2ebb447b54c Mon Sep 17 00:00:00 2001 From: Stephen Agate Date: Wed, 30 Nov 2016 13:41:04 +0000 Subject: net: phy: vitesse: add support for VSC8572 Add support for the Vitesse VSC8572 which is functionally equivalent to the already supported VSC8574. As such, all the same handling functions are used since the VSC8572 merely has half the number of phy blocks internally. Signed-off-by: Stephen Agate Signed-off-by: Neill Whillans Reviewed-by: Daniel Silverstone Signed-off-by: David S. Miller --- drivers/net/phy/vitesse.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 24b4a09..f78ff02 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -69,6 +69,7 @@ #define PHY_ID_VSC8234 0x000fc620 #define PHY_ID_VSC8244 0x000fc6c0 #define PHY_ID_VSC8514 0x00070670 +#define PHY_ID_VSC8572 0x000704d0 #define PHY_ID_VSC8574 0x000704a0 #define PHY_ID_VSC8601 0x00070420 #define PHY_ID_VSC8662 0x00070660 @@ -166,6 +167,7 @@ static int vsc82xx_config_intr(struct phy_device *phydev) (phydev->drv->phy_id == PHY_ID_VSC8234 || phydev->drv->phy_id == PHY_ID_VSC8244 || phydev->drv->phy_id == PHY_ID_VSC8514 || + phydev->drv->phy_id == PHY_ID_VSC8572 || phydev->drv->phy_id == PHY_ID_VSC8574 || phydev->drv->phy_id == PHY_ID_VSC8601) ? MII_VSC8244_IMASK_MASK : @@ -291,6 +293,17 @@ static struct phy_driver vsc82xx_driver[] = { .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, }, { + .phy_id = PHY_ID_VSC8572, + .name = "Vitesse VSC8572", + .phy_id_mask = 0x000ffff0, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &vsc824x_config_init, + .config_aneg = &vsc82x4_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc824x_ack_interrupt, + .config_intr = &vsc82xx_config_intr, +}, { .phy_id = PHY_ID_VSC8574, .name = "Vitesse VSC8574", .phy_id_mask = 0x000ffff0, @@ -355,6 +368,7 @@ static struct mdio_device_id __maybe_unused vitesse_tbl[] = { { PHY_ID_VSC8234, 0x000ffff0 }, { PHY_ID_VSC8244, 0x000fffc0 }, { PHY_ID_VSC8514, 0x000ffff0 }, + { PHY_ID_VSC8572, 0x000ffff0 }, { PHY_ID_VSC8574, 0x000ffff0 }, { PHY_ID_VSC8662, 0x000ffff0 }, { PHY_ID_VSC8221, 0x000ffff0 }, -- cgit v1.1 From 3b804564333f312143f36460f5fa90085428b5af Mon Sep 17 00:00:00 2001 From: Neill Whillans Date: Wed, 30 Nov 2016 13:41:05 +0000 Subject: net: ethernet: altera_tse: add support for SGMII PCS Add support for the (optional) SGMII PCS functionality of the Altera TSE MAC. If the phy-mode is set to 'sgmii' then we attempt to discover and initialise the PCS so that the MAC can communicate to the PHY. The PCS IP block provides a scratch register for testing presence of the PCS, which is mapped into one of the two MDIO spaces present in the MAC's register space. Once we have determined that the scratch register is functioning, we attempt to initialise the PCS to auto-negotiate an SGMII link with the PHY. There is no need to monitor or manage the SGMII link beyond this, since the normal PHY MDIO will then be used to monitor the media layer. The Altera TSE MAC has only one way in which it can be configured with an SGMII PCS, and as such, this patch only looks to the phy-mode to select whether or not to attempt to initialise the PCS registers. During initialisation, we report the PCS's equivalent of a PHY ID register. This can be parameterised during the IP instantiation and is often left as '0x00000000' which is not an error. Signed-off-by: Neill Whillans Reviewed-by: Daniel Silverstone Signed-off-by: David S. Miller --- drivers/net/ethernet/altera/altera_tse.h | 11 ++++ drivers/net/ethernet/altera/altera_tse_main.c | 91 +++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h index 9b640c8..e2feee8 100644 --- a/drivers/net/ethernet/altera/altera_tse.h +++ b/drivers/net/ethernet/altera/altera_tse.h @@ -120,6 +120,17 @@ #define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27) #define MAC_CMDCFG_CNT_RESET_GET(v) GET_BIT_VALUE(v, 31) +/* SGMII PCS register addresses + */ +#define SGMII_PCS_SCRATCH 0x10 +#define SGMII_PCS_REV 0x11 +#define SGMII_PCS_LINK_TIMER_0 0x12 +#define SGMII_PCS_LINK_TIMER_1 0x13 +#define SGMII_PCS_IF_MODE 0x14 +#define SGMII_PCS_DIS_READ_TO 0x15 +#define SGMII_PCS_READ_TO 0x16 +#define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */ + /* MDIO registers within MAC register Space */ struct altera_tse_mdio { diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 8e92084..d8bca94 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,27 @@ static inline u32 tse_tx_avail(struct altera_tse_private *priv) return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1; } +/* PCS Register read/write functions + */ +static u16 sgmii_pcs_read(struct altera_tse_private *priv, int regnum) +{ + return csrrd32(priv->mac_dev, + tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff; +} + +static void sgmii_pcs_write(struct altera_tse_private *priv, int regnum, + u16 value) +{ + csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4); +} + +/* Check PCS scratch memory */ +static int sgmii_pcs_scratch_test(struct altera_tse_private *priv, u16 value) +{ + sgmii_pcs_write(priv, SGMII_PCS_SCRATCH, value); + return (sgmii_pcs_read(priv, SGMII_PCS_SCRATCH) == value); +} + /* MDIO specific functions */ static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum) @@ -1083,6 +1105,66 @@ static void tse_set_rx_mode(struct net_device *dev) spin_unlock(&priv->mac_cfg_lock); } +/* Initialise (if necessary) the SGMII PCS component + */ +static int init_sgmii_pcs(struct net_device *dev) +{ + struct altera_tse_private *priv = netdev_priv(dev); + int n; + unsigned int tmp_reg = 0; + + if (priv->phy_iface != PHY_INTERFACE_MODE_SGMII) + return 0; /* Nothing to do, not in SGMII mode */ + + /* The TSE SGMII PCS block looks a little like a PHY, it is + * mapped into the zeroth MDIO space of the MAC and it has + * ID registers like a PHY would. Sadly this is often + * configured to zeroes, so don't be surprised if it does + * show 0x00000000. + */ + + if (sgmii_pcs_scratch_test(priv, 0x0000) && + sgmii_pcs_scratch_test(priv, 0xffff) && + sgmii_pcs_scratch_test(priv, 0xa5a5) && + sgmii_pcs_scratch_test(priv, 0x5a5a)) { + netdev_info(dev, "PCS PHY ID: 0x%04x%04x\n", + sgmii_pcs_read(priv, MII_PHYSID1), + sgmii_pcs_read(priv, MII_PHYSID2)); + } else { + netdev_err(dev, "SGMII PCS Scratch memory test failed.\n"); + return -ENOMEM; + } + + /* Starting on page 5-29 of the MegaCore Function User Guide + * Set SGMII Link timer to 1.6ms + */ + sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_0, 0x0D40); + sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_1, 0x03); + + /* Enable SGMII Interface and Enable SGMII Auto Negotiation */ + sgmii_pcs_write(priv, SGMII_PCS_IF_MODE, 0x3); + + /* Enable Autonegotiation */ + tmp_reg = sgmii_pcs_read(priv, MII_BMCR); + tmp_reg |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE); + sgmii_pcs_write(priv, MII_BMCR, tmp_reg); + + /* Reset PCS block */ + tmp_reg |= BMCR_RESET; + sgmii_pcs_write(priv, MII_BMCR, tmp_reg); + for (n = 0; n < SGMII_PCS_SW_RESET_TIMEOUT; n++) { + if (!(sgmii_pcs_read(priv, MII_BMCR) & BMCR_RESET)) { + netdev_info(dev, "SGMII PCS block initialised OK\n"); + return 0; + } + udelay(1); + } + + /* We failed to reset the block, return a timeout */ + netdev_err(dev, "SGMII PCS block reset failed.\n"); + return -ETIMEDOUT; +} + /* Open and initialize the interface */ static int tse_open(struct net_device *dev) @@ -1107,6 +1189,15 @@ static int tse_open(struct net_device *dev) netdev_warn(dev, "TSE revision %x\n", priv->revision); spin_lock(&priv->mac_cfg_lock); + /* no-op if MAC not operating in SGMII mode*/ + ret = init_sgmii_pcs(dev); + if (ret) { + netdev_err(dev, + "Cannot init the SGMII PCS (error: %d)\n", ret); + spin_unlock(&priv->mac_cfg_lock); + goto phy_error; + } + ret = reset_mac(priv); /* Note that reset_mac will fail if the clocks are gated by the PHY * due to the PHY being put into isolation or power down mode. -- cgit v1.1 From 1c1b522808a18402f043c1418b4e48c7355480cc Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 30 Nov 2016 17:59:37 +0200 Subject: net/mlx5e: Implement Fragmented Work Queue (WQ) Add new type of struct mlx5_frag_buf which is used to allocate fragmented buffers rather than contiguous, and make the Completion Queues (CQs) use it as they are big (default of 2MB per CQ in Striding RQ). This fixes the failures of type: "mlx5e_open_locked: mlx5e_open_channels failed, -12" due to dma_zalloc_coherent insufficient contiguous coherent memory to satisfy the driver's request when the user tries to setup more or larger rings. Signed-off-by: Tariq Toukan Reported-by: Sebastian Ott Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/alloc.c | 66 +++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 10 ++-- drivers/net/ethernet/mellanox/mlx5/core/wq.c | 26 ++++++--- drivers/net/ethernet/mellanox/mlx5/core/wq.h | 18 +++++-- include/linux/mlx5/driver.h | 11 ++++ 6 files changed, 116 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 2c6e3c7..44791de 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -106,6 +106,63 @@ void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf) } EXPORT_SYMBOL_GPL(mlx5_buf_free); +int mlx5_frag_buf_alloc_node(struct mlx5_core_dev *dev, int size, + struct mlx5_frag_buf *buf, int node) +{ + int i; + + buf->size = size; + buf->npages = 1 << get_order(size); + buf->page_shift = PAGE_SHIFT; + buf->frags = kcalloc(buf->npages, sizeof(struct mlx5_buf_list), + GFP_KERNEL); + if (!buf->frags) + goto err_out; + + for (i = 0; i < buf->npages; i++) { + struct mlx5_buf_list *frag = &buf->frags[i]; + int frag_sz = min_t(int, size, PAGE_SIZE); + + frag->buf = mlx5_dma_zalloc_coherent_node(dev, frag_sz, + &frag->map, node); + if (!frag->buf) + goto err_free_buf; + if (frag->map & ((1 << buf->page_shift) - 1)) { + dma_free_coherent(&dev->pdev->dev, frag_sz, + buf->frags[i].buf, buf->frags[i].map); + mlx5_core_warn(dev, "unexpected map alignment: 0x%p, page_shift=%d\n", + (void *)frag->map, buf->page_shift); + goto err_free_buf; + } + size -= frag_sz; + } + + return 0; + +err_free_buf: + while (i--) + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, buf->frags[i].buf, + buf->frags[i].map); + kfree(buf->frags); +err_out: + return -ENOMEM; +} + +void mlx5_frag_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf) +{ + int size = buf->size; + int i; + + for (i = 0; i < buf->npages; i++) { + int frag_sz = min_t(int, size, PAGE_SIZE); + + dma_free_coherent(&dev->pdev->dev, frag_sz, buf->frags[i].buf, + buf->frags[i].map); + size -= frag_sz; + } + kfree(buf->frags); +} + static struct mlx5_db_pgdir *mlx5_alloc_db_pgdir(struct mlx5_core_dev *dev, int node) { @@ -230,3 +287,12 @@ void mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas) } } EXPORT_SYMBOL_GPL(mlx5_fill_page_array); + +void mlx5_fill_page_frag_array(struct mlx5_frag_buf *buf, __be64 *pas) +{ + int i; + + for (i = 0; i < buf->npages; i++) + pas[i] = cpu_to_be64(buf->frags[i].map); +} +EXPORT_SYMBOL_GPL(mlx5_fill_page_frag_array); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 442dbc3..f16f7fb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -286,7 +286,7 @@ struct mlx5e_cq { u16 decmprs_wqe_counter; /* control */ - struct mlx5_wq_ctrl wq_ctrl; + struct mlx5_frag_wq_ctrl wq_ctrl; } ____cacheline_aligned_in_smp; struct mlx5e_rq; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 6b492ca..ba25cd3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1201,7 +1201,7 @@ static int mlx5e_create_cq(struct mlx5e_channel *c, static void mlx5e_destroy_cq(struct mlx5e_cq *cq) { - mlx5_wq_destroy(&cq->wq_ctrl); + mlx5_cqwq_destroy(&cq->wq_ctrl); } static int mlx5e_enable_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) @@ -1218,7 +1218,7 @@ static int mlx5e_enable_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) int err; inlen = MLX5_ST_SZ_BYTES(create_cq_in) + - sizeof(u64) * cq->wq_ctrl.buf.npages; + sizeof(u64) * cq->wq_ctrl.frag_buf.npages; in = mlx5_vzalloc(inlen); if (!in) return -ENOMEM; @@ -1227,15 +1227,15 @@ static int mlx5e_enable_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) memcpy(cqc, param->cqc, sizeof(param->cqc)); - mlx5_fill_page_array(&cq->wq_ctrl.buf, - (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas)); + mlx5_fill_page_frag_array(&cq->wq_ctrl.frag_buf, + (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas)); mlx5_vector2eqn(mdev, param->eq_ix, &eqn, &irqn_not_used); MLX5_SET(cqc, cqc, cq_period_mode, param->cq_period_mode); MLX5_SET(cqc, cqc, c_eqn, eqn); MLX5_SET(cqc, cqc, uar_page, mcq->uar->index); - MLX5_SET(cqc, cqc, log_page_size, cq->wq_ctrl.buf.page_shift - + MLX5_SET(cqc, cqc, log_page_size, cq->wq_ctrl.frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT); MLX5_SET64(cqc, cqc, dbr_addr, cq->wq_ctrl.db.dma); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/wq.c b/drivers/net/ethernet/mellanox/mlx5/core/wq.c index 821a087..921673c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/wq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/wq.c @@ -101,13 +101,15 @@ err_db_free: int mlx5_cqwq_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param, void *cqc, struct mlx5_cqwq *wq, - struct mlx5_wq_ctrl *wq_ctrl) + struct mlx5_frag_wq_ctrl *wq_ctrl) { int err; - wq->log_stride = 6 + MLX5_GET(cqc, cqc, cqe_sz); - wq->log_sz = MLX5_GET(cqc, cqc, log_cq_size); - wq->sz_m1 = (1 << wq->log_sz) - 1; + wq->log_stride = 6 + MLX5_GET(cqc, cqc, cqe_sz); + wq->log_sz = MLX5_GET(cqc, cqc, log_cq_size); + wq->sz_m1 = (1 << wq->log_sz) - 1; + wq->log_frag_strides = PAGE_SHIFT - wq->log_stride; + wq->frag_sz_m1 = (1 << wq->log_frag_strides) - 1; err = mlx5_db_alloc_node(mdev, &wq_ctrl->db, param->db_numa_node); if (err) { @@ -115,14 +117,16 @@ int mlx5_cqwq_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param, return err; } - err = mlx5_buf_alloc_node(mdev, mlx5_cqwq_get_byte_size(wq), - &wq_ctrl->buf, param->buf_numa_node); + err = mlx5_frag_buf_alloc_node(mdev, mlx5_cqwq_get_byte_size(wq), + &wq_ctrl->frag_buf, + param->buf_numa_node); if (err) { - mlx5_core_warn(mdev, "mlx5_buf_alloc_node() failed, %d\n", err); + mlx5_core_warn(mdev, "mlx5_frag_buf_alloc_node() failed, %d\n", + err); goto err_db_free; } - wq->buf = wq_ctrl->buf.direct.buf; + wq->frag_buf = wq_ctrl->frag_buf; wq->db = wq_ctrl->db.db; wq_ctrl->mdev = mdev; @@ -184,3 +188,9 @@ void mlx5_wq_destroy(struct mlx5_wq_ctrl *wq_ctrl) mlx5_buf_free(wq_ctrl->mdev, &wq_ctrl->buf); mlx5_db_free(wq_ctrl->mdev, &wq_ctrl->db); } + +void mlx5_cqwq_destroy(struct mlx5_frag_wq_ctrl *wq_ctrl) +{ + mlx5_frag_buf_free(wq_ctrl->mdev, &wq_ctrl->frag_buf); + mlx5_db_free(wq_ctrl->mdev, &wq_ctrl->db); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/wq.h b/drivers/net/ethernet/mellanox/mlx5/core/wq.h index 6c2a8f9..d8afed8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/wq.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/wq.h @@ -47,6 +47,12 @@ struct mlx5_wq_ctrl { struct mlx5_db db; }; +struct mlx5_frag_wq_ctrl { + struct mlx5_core_dev *mdev; + struct mlx5_frag_buf frag_buf; + struct mlx5_db db; +}; + struct mlx5_wq_cyc { void *buf; __be32 *db; @@ -55,12 +61,14 @@ struct mlx5_wq_cyc { }; struct mlx5_cqwq { - void *buf; + struct mlx5_frag_buf frag_buf; __be32 *db; u32 sz_m1; + u32 frag_sz_m1; u32 cc; /* consumer counter */ u8 log_sz; u8 log_stride; + u8 log_frag_strides; }; struct mlx5_wq_ll { @@ -81,7 +89,7 @@ u32 mlx5_wq_cyc_get_size(struct mlx5_wq_cyc *wq); int mlx5_cqwq_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param, void *cqc, struct mlx5_cqwq *wq, - struct mlx5_wq_ctrl *wq_ctrl); + struct mlx5_frag_wq_ctrl *wq_ctrl); u32 mlx5_cqwq_get_size(struct mlx5_cqwq *wq); int mlx5_wq_ll_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param, @@ -90,6 +98,7 @@ int mlx5_wq_ll_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param, u32 mlx5_wq_ll_get_size(struct mlx5_wq_ll *wq); void mlx5_wq_destroy(struct mlx5_wq_ctrl *wq_ctrl); +void mlx5_cqwq_destroy(struct mlx5_frag_wq_ctrl *wq_ctrl); static inline u16 mlx5_wq_cyc_ctr2ix(struct mlx5_wq_cyc *wq, u16 ctr) { @@ -116,7 +125,10 @@ static inline u32 mlx5_cqwq_get_ci(struct mlx5_cqwq *wq) static inline void *mlx5_cqwq_get_wqe(struct mlx5_cqwq *wq, u32 ix) { - return wq->buf + (ix << wq->log_stride); + unsigned int frag = (ix >> wq->log_frag_strides); + + return wq->frag_buf.frags[frag].buf + + ((wq->frag_sz_m1 & ix) << wq->log_stride); } static inline u32 mlx5_cqwq_get_wrap_cnt(struct mlx5_cqwq *wq) diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 68b85ef..0ae5536 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -318,6 +318,13 @@ struct mlx5_buf { u8 page_shift; }; +struct mlx5_frag_buf { + struct mlx5_buf_list *frags; + int npages; + int size; + u8 page_shift; +}; + struct mlx5_eq_tasklet { struct list_head list; struct list_head process_list; @@ -822,6 +829,9 @@ int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size, struct mlx5_buf *buf, int node); int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, struct mlx5_buf *buf); void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf); +int mlx5_frag_buf_alloc_node(struct mlx5_core_dev *dev, int size, + struct mlx5_frag_buf *buf, int node); +void mlx5_frag_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf); struct mlx5_cmd_mailbox *mlx5_alloc_cmd_mailbox_chain(struct mlx5_core_dev *dev, gfp_t flags, int npages); void mlx5_free_cmd_mailbox_chain(struct mlx5_core_dev *dev, @@ -866,6 +876,7 @@ void mlx5_unregister_debugfs(void); int mlx5_eq_init(struct mlx5_core_dev *dev); void mlx5_eq_cleanup(struct mlx5_core_dev *dev); void mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas); +void mlx5_fill_page_frag_array(struct mlx5_frag_buf *frag_buf, __be64 *pas); void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn); void mlx5_rsc_event(struct mlx5_core_dev *dev, u32 rsn, int event_type); #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING -- cgit v1.1 From 3608ae77c098dfe134103a9dec4c78687896708e Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 30 Nov 2016 17:59:38 +0200 Subject: net/mlx5e: Move function mlx5e_create_umr_mkey In next patch we are going to create a UMR MKey per RQ, we need mlx5e_create_umr_mkey declared before mlx5e_create_rq. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 74 +++++++++++------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index ba25cd3..49ca30b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -471,6 +471,43 @@ static void mlx5e_rq_free_mpwqe_info(struct mlx5e_rq *rq) kfree(rq->mpwqe.info); } +static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv) +{ + struct mlx5_core_dev *mdev = priv->mdev; + u64 npages = MLX5E_REQUIRED_MTTS(priv->profile->max_nch(mdev), + BIT(MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW)); + int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); + void *mkc; + u32 *in; + int err; + + in = mlx5_vzalloc(inlen); + if (!in) + return -ENOMEM; + + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); + + npages = min_t(u32, ALIGN(U16_MAX, 4) * 2, npages); + + MLX5_SET(mkc, mkc, free, 1); + MLX5_SET(mkc, mkc, umr_en, 1); + MLX5_SET(mkc, mkc, lw, 1); + MLX5_SET(mkc, mkc, lr, 1); + MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_MTT); + + MLX5_SET(mkc, mkc, qpn, 0xffffff); + MLX5_SET(mkc, mkc, pd, mdev->mlx5e_res.pdn); + MLX5_SET64(mkc, mkc, len, npages << PAGE_SHIFT); + MLX5_SET(mkc, mkc, translations_octword_size, + MLX5_MTT_OCTW(npages)); + MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); + + err = mlx5_core_create_mkey(mdev, &priv->umr_mkey, in, inlen); + + kvfree(in); + return err; +} + static int mlx5e_create_rq(struct mlx5e_channel *c, struct mlx5e_rq_param *param, struct mlx5e_rq *rq) @@ -3625,43 +3662,6 @@ static void mlx5e_destroy_q_counter(struct mlx5e_priv *priv) mlx5_core_dealloc_q_counter(priv->mdev, priv->q_counter); } -static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv) -{ - struct mlx5_core_dev *mdev = priv->mdev; - u64 npages = MLX5E_REQUIRED_MTTS(priv->profile->max_nch(mdev), - BIT(MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW)); - int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - void *mkc; - u32 *in; - int err; - - in = mlx5_vzalloc(inlen); - if (!in) - return -ENOMEM; - - mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); - - npages = min_t(u32, ALIGN(U16_MAX, 4) * 2, npages); - - MLX5_SET(mkc, mkc, free, 1); - MLX5_SET(mkc, mkc, umr_en, 1); - MLX5_SET(mkc, mkc, lw, 1); - MLX5_SET(mkc, mkc, lr, 1); - MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_MTT); - - MLX5_SET(mkc, mkc, qpn, 0xffffff); - MLX5_SET(mkc, mkc, pd, mdev->mlx5e_res.pdn); - MLX5_SET64(mkc, mkc, len, npages << PAGE_SHIFT); - MLX5_SET(mkc, mkc, translations_octword_size, - MLX5_MTT_OCTW(npages)); - MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); - - err = mlx5_core_create_mkey(mdev, &priv->umr_mkey, in, inlen); - - kvfree(in); - return err; -} - static void mlx5e_nic_init(struct mlx5_core_dev *mdev, struct net_device *netdev, const struct mlx5e_profile *profile, -- cgit v1.1 From ec8b9981ad3f3eeb5dcc4f237266e897c363f896 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 30 Nov 2016 17:59:39 +0200 Subject: net/mlx5e: Create UMR MKey per RQ In Striding RQ implementation, we used a single UMR (User-Mode Memory Registration) memory key for all RQs. When the product of RQs number*size gets high, we hit a limitation of u16 field size in FW. Here we move to using a UMR memory key per RQ, so we can scale to any number of rings, with the maximum buffer size in each. Signed-off-by: Tariq Toukan Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++--- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 12 +---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 53 ++++++++++++---------- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index f16f7fb..63dd639 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -77,9 +77,9 @@ MLX5_MPWRQ_WQE_PAGE_ORDER) #define MLX5_MTT_OCTW(npages) (ALIGN(npages, 8) / 2) -#define MLX5E_REQUIRED_MTTS(rqs, wqes)\ - (rqs * wqes * ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8)) -#define MLX5E_VALID_NUM_MTTS(num_mtts) (MLX5_MTT_OCTW(num_mtts) <= U16_MAX) +#define MLX5E_REQUIRED_MTTS(wqes) \ + (wqes * ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8)) +#define MLX5E_VALID_NUM_MTTS(num_mtts) (MLX5_MTT_OCTW(num_mtts) - 1 <= U16_MAX) #define MLX5_UMR_ALIGN (2048) #define MLX5_MPWRQ_SMALL_PACKET_THRESHOLD (128) @@ -347,7 +347,6 @@ struct mlx5e_rq { struct { struct mlx5e_mpw_info *info; void *mtt_no_align; - u32 mtt_offset; } mpwqe; }; struct { @@ -382,6 +381,7 @@ struct mlx5e_rq { u32 rqn; struct mlx5e_channel *channel; struct mlx5e_priv *priv; + struct mlx5_core_mkey umr_mkey; } ____cacheline_aligned_in_smp; struct mlx5e_umr_dma_info { @@ -689,7 +689,6 @@ struct mlx5e_priv { unsigned long state; struct mutex state_lock; /* Protects Interface state */ - struct mlx5_core_mkey umr_mkey; struct mlx5e_rq drop_rq; struct mlx5e_channel **channel; @@ -838,8 +837,7 @@ static inline void mlx5e_cq_arm(struct mlx5e_cq *cq) static inline u32 mlx5e_get_wqe_mtt_offset(struct mlx5e_rq *rq, u16 wqe_ix) { - return rq->mpwqe.mtt_offset + - wqe_ix * ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8); + return wqe_ix * ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8); } static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index aa963d7..352462a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -499,8 +499,7 @@ static int mlx5e_set_ringparam(struct net_device *dev, return -EINVAL; } - num_mtts = MLX5E_REQUIRED_MTTS(priv->params.num_channels, - rx_pending_wqes); + num_mtts = MLX5E_REQUIRED_MTTS(rx_pending_wqes); if (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && !MLX5E_VALID_NUM_MTTS(num_mtts)) { netdev_info(dev, "%s: rx_pending (%d) request can't be satisfied, try to reduce.\n", @@ -565,7 +564,6 @@ static int mlx5e_set_channels(struct net_device *dev, unsigned int count = ch->combined_count; bool arfs_enabled; bool was_opened; - u32 num_mtts; int err = 0; if (!count) { @@ -584,14 +582,6 @@ static int mlx5e_set_channels(struct net_device *dev, return -EINVAL; } - num_mtts = MLX5E_REQUIRED_MTTS(count, BIT(priv->params.log_rq_size)); - if (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && - !MLX5E_VALID_NUM_MTTS(num_mtts)) { - netdev_info(dev, "%s: rx count (%d) request can't be satisfied, try to reduce.\n", - __func__, count); - return -EINVAL; - } - if (priv->params.num_channels == count) return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 49ca30b..84a4adb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -471,24 +471,25 @@ static void mlx5e_rq_free_mpwqe_info(struct mlx5e_rq *rq) kfree(rq->mpwqe.info); } -static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv) +static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv, + u64 npages, u8 page_shift, + struct mlx5_core_mkey *umr_mkey) { struct mlx5_core_dev *mdev = priv->mdev; - u64 npages = MLX5E_REQUIRED_MTTS(priv->profile->max_nch(mdev), - BIT(MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW)); int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); void *mkc; u32 *in; int err; + if (!MLX5E_VALID_NUM_MTTS(npages)) + return -EINVAL; + in = mlx5_vzalloc(inlen); if (!in) return -ENOMEM; mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); - npages = min_t(u32, ALIGN(U16_MAX, 4) * 2, npages); - MLX5_SET(mkc, mkc, free, 1); MLX5_SET(mkc, mkc, umr_en, 1); MLX5_SET(mkc, mkc, lw, 1); @@ -497,17 +498,25 @@ static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv) MLX5_SET(mkc, mkc, qpn, 0xffffff); MLX5_SET(mkc, mkc, pd, mdev->mlx5e_res.pdn); - MLX5_SET64(mkc, mkc, len, npages << PAGE_SHIFT); + MLX5_SET64(mkc, mkc, len, npages << page_shift); MLX5_SET(mkc, mkc, translations_octword_size, MLX5_MTT_OCTW(npages)); - MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); + MLX5_SET(mkc, mkc, log_page_size, page_shift); - err = mlx5_core_create_mkey(mdev, &priv->umr_mkey, in, inlen); + err = mlx5_core_create_mkey(mdev, umr_mkey, in, inlen); kvfree(in); return err; } +static int mlx5e_create_rq_umr_mkey(struct mlx5e_rq *rq) +{ + struct mlx5e_priv *priv = rq->priv; + u64 num_mtts = MLX5E_REQUIRED_MTTS(BIT(priv->params.log_rq_size)); + + return mlx5e_create_umr_mkey(priv, num_mtts, PAGE_SHIFT, &rq->umr_mkey); +} + static int mlx5e_create_rq(struct mlx5e_channel *c, struct mlx5e_rq_param *param, struct mlx5e_rq *rq) @@ -564,18 +573,20 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, rq->alloc_wqe = mlx5e_alloc_rx_mpwqe; rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe; - rq->mpwqe.mtt_offset = c->ix * - MLX5E_REQUIRED_MTTS(1, BIT(priv->params.log_rq_size)); - rq->mpwqe_stride_sz = BIT(priv->params.mpwqe_log_stride_sz); rq->mpwqe_num_strides = BIT(priv->params.mpwqe_log_num_strides); rq->buff.wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides; byte_count = rq->buff.wqe_sz; - rq->mkey_be = cpu_to_be32(c->priv->umr_mkey.key); - err = mlx5e_rq_alloc_mpwqe_info(rq, c); + + err = mlx5e_create_rq_umr_mkey(rq); if (err) goto err_rq_wq_destroy; + rq->mkey_be = cpu_to_be32(rq->umr_mkey.key); + + err = mlx5e_rq_alloc_mpwqe_info(rq, c); + if (err) + goto err_destroy_umr_mkey; break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ rq->dma_info = kzalloc_node(wq_sz * sizeof(*rq->dma_info), @@ -626,6 +637,9 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, return 0; +err_destroy_umr_mkey: + mlx5_core_destroy_mkey(mdev, &rq->umr_mkey); + err_rq_wq_destroy: if (rq->xdp_prog) bpf_prog_put(rq->xdp_prog); @@ -644,6 +658,7 @@ static void mlx5e_destroy_rq(struct mlx5e_rq *rq) switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: mlx5e_rq_free_mpwqe_info(rq); + mlx5_core_destroy_mkey(rq->priv->mdev, &rq->umr_mkey); break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ kfree(rq->dma_info); @@ -3868,15 +3883,9 @@ int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) profile = priv->profile; clear_bit(MLX5E_STATE_DESTROYING, &priv->state); - err = mlx5e_create_umr_mkey(priv); - if (err) { - mlx5_core_err(mdev, "create umr mkey failed, %d\n", err); - goto out; - } - err = profile->init_tx(priv); if (err) - goto err_destroy_umr_mkey; + goto out; err = mlx5e_open_drop_rq(priv); if (err) { @@ -3916,9 +3925,6 @@ err_close_drop_rq: err_cleanup_tx: profile->cleanup_tx(priv); -err_destroy_umr_mkey: - mlx5_core_destroy_mkey(mdev, &priv->umr_mkey); - out: return err; } @@ -3967,7 +3973,6 @@ void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) profile->cleanup_rx(priv); mlx5e_close_drop_rq(priv); profile->cleanup_tx(priv); - mlx5_core_destroy_mkey(priv->mdev, &priv->umr_mkey); cancel_delayed_work_sync(&priv->update_stats_work); } -- cgit v1.1 From 53636068d8d6a08ca99949e2094b2b04ececa2d8 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Wed, 30 Nov 2016 17:59:40 +0200 Subject: net/mlx5e: Remove redundant hashtable lookup in configure flower We will never find a flow with the same cookie as cls_flower always allocates a new flow and the cookie is the allocated memory address. Fixes: e3a2b7ed018e ("net/mlx5e: Support offload cls_flower with drop action") Signed-off-by: Roi Dayan Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 26 +++++++------------------ 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 4d06fab..dd6d954 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -915,25 +915,17 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, u32 flow_tag, action; struct mlx5e_tc_flow *flow; struct mlx5_flow_spec *spec; - struct mlx5_flow_handle *old = NULL; - struct mlx5_esw_flow_attr *old_attr = NULL; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; if (esw && esw->mode == SRIOV_OFFLOADS) fdb_flow = true; - flow = rhashtable_lookup_fast(&tc->ht, &f->cookie, - tc->ht_params); - if (flow) { - old = flow->rule; - old_attr = flow->attr; - } else { - if (fdb_flow) - flow = kzalloc(sizeof(*flow) + sizeof(struct mlx5_esw_flow_attr), - GFP_KERNEL); - else - flow = kzalloc(sizeof(*flow), GFP_KERNEL); - } + if (fdb_flow) + flow = kzalloc(sizeof(*flow) + + sizeof(struct mlx5_esw_flow_attr), + GFP_KERNEL); + else + flow = kzalloc(sizeof(*flow), GFP_KERNEL); spec = mlx5_vzalloc(sizeof(*spec)); if (!spec || !flow) { @@ -970,17 +962,13 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, if (err) goto err_del_rule; - if (old) - mlx5e_tc_del_flow(priv, old, old_attr); - goto out; err_del_rule: mlx5_del_flow_rules(flow->rule); err_free: - if (!old) - kfree(flow); + kfree(flow); out: kvfree(spec); return err; -- cgit v1.1 From 86a33ae1ca06a58629c4d0d80ac6d099ff932ae0 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Wed, 30 Nov 2016 17:59:41 +0200 Subject: net/mlx5e: Correct cleanup order when deleting offloaded TC rules According to the reverse unwinding principle, on delete time we should first handle deletion of the steering rule and later handle the vlan deletion from the eswitch. Fixes: 8b32580df1cb ("net/mlx5e: Add TC vlan action for SRIOV offloads") Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index dd6d954..4d71445 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -151,11 +151,11 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, counter = mlx5_flow_rule_counter(rule); + mlx5_del_flow_rules(rule); + if (esw && esw->mode == SRIOV_OFFLOADS) mlx5_eswitch_del_vlan_action(esw, attr); - mlx5_del_flow_rules(rule); - mlx5_fc_destroy(priv->mdev, counter); if (!mlx5e_tc_num_filters(priv) && (priv->fs.tc.t)) { -- cgit v1.1 From 961e8979ec3578ef4c441b69a5b7b9febc89fd18 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Wed, 30 Nov 2016 17:59:42 +0200 Subject: net/mlx5e: Refactor tc del flow to accept mlx5e_tc_flow instance Change the function that deletes offloaded TC rule to get struct mlx5e_tc_flow instance which contains both the flow handle and flow attributes. This is a cleanup needed for downstream patches, it doesn't change any functionality. Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 4d71445..3875c1c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -143,18 +143,17 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, } static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, - struct mlx5_flow_handle *rule, - struct mlx5_esw_flow_attr *attr) + struct mlx5e_tc_flow *flow) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_fc *counter = NULL; - counter = mlx5_flow_rule_counter(rule); + counter = mlx5_flow_rule_counter(flow->rule); - mlx5_del_flow_rules(rule); + mlx5_del_flow_rules(flow->rule); if (esw && esw->mode == SRIOV_OFFLOADS) - mlx5_eswitch_del_vlan_action(esw, attr); + mlx5_eswitch_del_vlan_action(esw, flow->attr); mlx5_fc_destroy(priv->mdev, counter); @@ -1005,7 +1004,7 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv, rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params); - mlx5e_tc_del_flow(priv, flow->rule, flow->attr); + mlx5e_tc_del_flow(priv, flow); if (flow->attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) mlx5e_detach_encap(priv, flow); @@ -1065,7 +1064,7 @@ static void _mlx5e_tc_del_flow(void *ptr, void *arg) struct mlx5e_tc_flow *flow = ptr; struct mlx5e_priv *priv = arg; - mlx5e_tc_del_flow(priv, flow->rule, flow->attr); + mlx5e_tc_del_flow(priv, flow); kfree(flow); } -- cgit v1.1 From 5067b6020770ef7c8102f47079c9e577d175ef2c Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Wed, 30 Nov 2016 17:59:43 +0200 Subject: net/mlx5e: Remove flow encap entry in the correct place Handling flow encap entry should be inside tc del flow and is only relevant for offloaded eswitch TC rules. Fixes: 11a457e9b6c1 ("net/mlx5e: Add basic TC tunnel set action for SRIOV offloads") Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 43 +++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 3875c1c..f07ef8c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -142,6 +142,24 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, return mlx5_eswitch_add_offloaded_rule(esw, spec, attr); } +static void mlx5e_detach_encap(struct mlx5e_priv *priv, + struct mlx5e_tc_flow *flow) { + struct list_head *next = flow->encap.next; + + list_del(&flow->encap); + if (list_empty(next)) { + struct mlx5_encap_entry *e; + + e = list_entry(next, struct mlx5_encap_entry, flows); + if (e->n) { + mlx5_encap_dealloc(priv->mdev, e->encap_id); + neigh_release(e->n); + } + hlist_del_rcu(&e->encap_hlist); + kfree(e); + } +} + static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { @@ -152,8 +170,11 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, mlx5_del_flow_rules(flow->rule); - if (esw && esw->mode == SRIOV_OFFLOADS) + if (esw && esw->mode == SRIOV_OFFLOADS) { mlx5_eswitch_del_vlan_action(esw, flow->attr); + if (flow->attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) + mlx5e_detach_encap(priv, flow); + } mlx5_fc_destroy(priv->mdev, counter); @@ -973,24 +994,6 @@ out: return err; } -static void mlx5e_detach_encap(struct mlx5e_priv *priv, - struct mlx5e_tc_flow *flow) { - struct list_head *next = flow->encap.next; - - list_del(&flow->encap); - if (list_empty(next)) { - struct mlx5_encap_entry *e; - - e = list_entry(next, struct mlx5_encap_entry, flows); - if (e->n) { - mlx5_encap_dealloc(priv->mdev, e->encap_id); - neigh_release(e->n); - } - hlist_del_rcu(&e->encap_hlist); - kfree(e); - } -} - int mlx5e_delete_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f) { @@ -1006,8 +1009,6 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv, mlx5e_tc_del_flow(priv, flow); - if (flow->attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) - mlx5e_detach_encap(priv, flow); kfree(flow); -- cgit v1.1 From 11b3d9c5869bc96997cada33be6a56ebd179786b Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 30 Nov 2016 17:10:08 +0100 Subject: route: Set orig_output when redirecting to lwt on locally generated traffic orig_output for IPv4 was only set for dsts which hit an input route. Set it consistently for locally generated traffic as well to allow lwt to continue the dst_output() path as configured by the nexthop. Fixes: 2536862311d ("lwt: Add support to redirect dst.input") Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/ipv4/route.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6402d74..f098663 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2155,8 +2155,10 @@ add: } rt_set_nexthop(rth, fl4->daddr, res, fnhe, fi, type, 0); - if (lwtunnel_output_redirect(rth->dst.lwtstate)) + if (lwtunnel_output_redirect(rth->dst.lwtstate)) { + rth->dst.lwtstate->orig_output = rth->dst.output; rth->dst.output = lwtunnel_output; + } return rth; } -- cgit v1.1 From efd857008142017ff1b4ff70d98f4a5f6003cbea Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 30 Nov 2016 17:10:09 +0100 Subject: route: Set lwtstate for local traffic and cached input dsts A route on the output path hitting a RTN_LOCAL route will keep the dst associated on its way through the loopback device. On the receive path, the dst_input() call will thus invoke the input handler of the route created in the output path. Thus, lwt redirection for input must be done for dsts allocated in the otuput path as well. Also, if a route is cached in the input path, the allocated dst should respect lwtunnel configuration on the nexthop as well. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/ipv4/route.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index f098663..fa5c037 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1603,6 +1603,19 @@ static void ip_del_fnhe(struct fib_nh *nh, __be32 daddr) spin_unlock_bh(&fnhe_lock); } +static void set_lwt_redirect(struct rtable *rth) +{ + if (lwtunnel_output_redirect(rth->dst.lwtstate)) { + rth->dst.lwtstate->orig_output = rth->dst.output; + rth->dst.output = lwtunnel_output; + } + + if (lwtunnel_input_redirect(rth->dst.lwtstate)) { + rth->dst.lwtstate->orig_input = rth->dst.input; + rth->dst.input = lwtunnel_input; + } +} + /* called in rcu_read_lock() section */ static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res, @@ -1692,14 +1705,7 @@ rt_cache: rth->dst.input = ip_forward; rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag); - if (lwtunnel_output_redirect(rth->dst.lwtstate)) { - rth->dst.lwtstate->orig_output = rth->dst.output; - rth->dst.output = lwtunnel_output; - } - if (lwtunnel_input_redirect(rth->dst.lwtstate)) { - rth->dst.lwtstate->orig_input = rth->dst.input; - rth->dst.input = lwtunnel_input; - } + set_lwt_redirect(rth); skb_dst_set(skb, &rth->dst); out: err = 0; @@ -1926,8 +1932,18 @@ local_input: rth->dst.error= -err; rth->rt_flags &= ~RTCF_LOCAL; } + if (do_cache) { - if (unlikely(!rt_cache_route(&FIB_RES_NH(res), rth))) { + struct fib_nh *nh = &FIB_RES_NH(res); + + rth->dst.lwtstate = lwtstate_get(nh->nh_lwtstate); + if (lwtunnel_input_redirect(rth->dst.lwtstate)) { + WARN_ON(rth->dst.input == lwtunnel_input); + rth->dst.lwtstate->orig_input = rth->dst.input; + rth->dst.input = lwtunnel_input; + } + + if (unlikely(!rt_cache_route(nh, rth))) { rth->dst.flags |= DST_NOCACHE; rt_add_uncached_list(rth); } @@ -2155,10 +2171,7 @@ add: } rt_set_nexthop(rth, fl4->daddr, res, fnhe, fi, type, 0); - if (lwtunnel_output_redirect(rth->dst.lwtstate)) { - rth->dst.lwtstate->orig_output = rth->dst.output; - rth->dst.output = lwtunnel_output; - } + set_lwt_redirect(rth); return rth; } -- cgit v1.1 From 3a0af8fd61f90920f6fa04e4f1e9a6a73c1b4fd2 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 30 Nov 2016 17:10:10 +0100 Subject: bpf: BPF for lightweight tunnel infrastructure Registers new BPF program types which correspond to the LWT hooks: - BPF_PROG_TYPE_LWT_IN => dst_input() - BPF_PROG_TYPE_LWT_OUT => dst_output() - BPF_PROG_TYPE_LWT_XMIT => lwtunnel_xmit() The separate program types are required to differentiate between the capabilities each LWT hook allows: * Programs attached to dst_input() or dst_output() are restricted and may only read the data of an skb. This prevent modification and possible invalidation of already validated packet headers on receive and the construction of illegal headers while the IP headers are still being assembled. * Programs attached to lwtunnel_xmit() are allowed to modify packet content as well as prepending an L2 header via a newly introduced helper bpf_skb_change_head(). This is safe as lwtunnel_xmit() is invoked after the IP header has been assembled completely. All BPF programs receive an skb with L3 headers attached and may return one of the following error codes: BPF_OK - Continue routing as per nexthop BPF_DROP - Drop skb and return EPERM BPF_REDIRECT - Redirect skb to device as per redirect() helper. (Only valid in lwtunnel_xmit() context) The return codes are binary compatible with their TC_ACT_ relatives to ease compatibility. Signed-off-by: Thomas Graf Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/filter.h | 2 +- include/uapi/linux/bpf.h | 32 +++- include/uapi/linux/lwtunnel.h | 23 +++ kernel/bpf/verifier.c | 14 +- net/Kconfig | 8 + net/core/Makefile | 1 + net/core/filter.c | 173 ++++++++++++++++++ net/core/lwt_bpf.c | 396 ++++++++++++++++++++++++++++++++++++++++++ net/core/lwtunnel.c | 2 + 9 files changed, 646 insertions(+), 5 deletions(-) create mode 100644 net/core/lwt_bpf.c diff --git a/include/linux/filter.h b/include/linux/filter.h index 7f246a2..7ba6446 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -438,7 +438,7 @@ struct xdp_buff { }; /* compute the linear packet data range [data, data_end) which - * will be accessed by cls_bpf and act_bpf programs + * will be accessed by cls_bpf, act_bpf and lwt programs */ static inline void bpf_compute_data_end(struct sk_buff *skb) { diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1370a9d..22ac827 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -101,6 +101,9 @@ enum bpf_prog_type { BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, BPF_PROG_TYPE_CGROUP_SKB, + BPF_PROG_TYPE_LWT_IN, + BPF_PROG_TYPE_LWT_OUT, + BPF_PROG_TYPE_LWT_XMIT, }; enum bpf_attach_type { @@ -409,6 +412,16 @@ union bpf_attr { * * int bpf_get_numa_node_id() * Return: Id of current NUMA node. + * + * int bpf_skb_change_head() + * Grows headroom of skb and adjusts MAC header offset accordingly. + * Will extends/reallocae as required automatically. + * May change skb data pointer and will thus invalidate any check + * performed for direct packet access. + * @skb: pointer to skb + * @len: length of header to be pushed in front + * @flags: Flags (unused for now) + * Return: 0 on success or negative error */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -453,7 +466,8 @@ union bpf_attr { FN(skb_pull_data), \ FN(csum_update), \ FN(set_hash_invalid), \ - FN(get_numa_node_id), + FN(get_numa_node_id), \ + FN(skb_change_head), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -537,6 +551,22 @@ struct bpf_tunnel_key { __u32 tunnel_label; }; +/* Generic BPF return codes which all BPF program types may support. + * The values are binary compatible with their TC_ACT_* counter-part to + * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT + * programs. + * + * XDP is handled seprately, see XDP_*. + */ +enum bpf_ret_code { + BPF_OK = 0, + /* 1 reserved */ + BPF_DROP = 2, + /* 3-6 reserved */ + BPF_REDIRECT = 7, + /* >127 are reserved for prog type specific return codes */ +}; + /* User return codes for XDP prog type. * A valid XDP program must return one of these defined values. All other * return codes are reserved for future use. Unknown return codes will result diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h index 453cc62..92724cba 100644 --- a/include/uapi/linux/lwtunnel.h +++ b/include/uapi/linux/lwtunnel.h @@ -10,6 +10,7 @@ enum lwtunnel_encap_types { LWTUNNEL_ENCAP_ILA, LWTUNNEL_ENCAP_IP6, LWTUNNEL_ENCAP_SEG6, + LWTUNNEL_ENCAP_BPF, __LWTUNNEL_ENCAP_MAX, }; @@ -43,4 +44,26 @@ enum lwtunnel_ip6_t { #define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1) +enum { + LWT_BPF_PROG_UNSPEC, + LWT_BPF_PROG_FD, + LWT_BPF_PROG_NAME, + __LWT_BPF_PROG_MAX, +}; + +#define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1) + +enum { + LWT_BPF_UNSPEC, + LWT_BPF_IN, + LWT_BPF_OUT, + LWT_BPF_XMIT, + LWT_BPF_XMIT_HEADROOM, + __LWT_BPF_MAX, +}; + +#define LWT_BPF_MAX (__LWT_BPF_MAX - 1) + +#define LWT_BPF_MAX_HEADROOM 256 + #endif /* _UAPI_LWTUNNEL_H_ */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8740c5f..8135cb1 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -633,12 +633,19 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, int off, #define MAX_PACKET_OFF 0xffff static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, - const struct bpf_call_arg_meta *meta) + const struct bpf_call_arg_meta *meta, + enum bpf_access_type t) { switch (env->prog->type) { + case BPF_PROG_TYPE_LWT_IN: + case BPF_PROG_TYPE_LWT_OUT: + /* dst_input() and dst_output() can't write for now */ + if (t == BPF_WRITE) + return false; case BPF_PROG_TYPE_SCHED_CLS: case BPF_PROG_TYPE_SCHED_ACT: case BPF_PROG_TYPE_XDP: + case BPF_PROG_TYPE_LWT_XMIT: if (meta) return meta->pkt_access; @@ -837,7 +844,7 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off, err = check_stack_read(state, off, size, value_regno); } } else if (state->regs[regno].type == PTR_TO_PACKET) { - if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL)) { + if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) { verbose("cannot write into packet\n"); return -EACCES; } @@ -970,7 +977,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, return 0; } - if (type == PTR_TO_PACKET && !may_access_direct_pkt_data(env, meta)) { + if (type == PTR_TO_PACKET && + !may_access_direct_pkt_data(env, meta, BPF_READ)) { verbose("helper access to the packet is not allowed\n"); return -EACCES; } diff --git a/net/Kconfig b/net/Kconfig index 7b6cd34..a100500 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -402,6 +402,14 @@ config LWTUNNEL weight tunnel endpoint. Tunnel encapsulation parameters are stored with light weight tunnel state associated with fib routes. +config LWTUNNEL_BPF + bool "Execute BPF program as route nexthop action" + depends on LWTUNNEL + default y if LWTUNNEL=y + ---help--- + Allows to run BPF programs as a nexthop action following a route + lookup for incoming and outgoing packets. + config DST_CACHE bool default n diff --git a/net/core/Makefile b/net/core/Makefile index d6508c2..f6761b6 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o obj-$(CONFIG_LWTUNNEL) += lwtunnel.o +obj-$(CONFIG_LWTUNNEL_BPF) += lwt_bpf.o obj-$(CONFIG_DST_CACHE) += dst_cache.o obj-$(CONFIG_HWBM) += hwbm.o obj-$(CONFIG_NET_DEVLINK) += devlink.o diff --git a/net/core/filter.c b/net/core/filter.c index 698a262..1c4d0fa 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1689,6 +1689,12 @@ static int __bpf_redirect_no_mac(struct sk_buff *skb, struct net_device *dev, static int __bpf_redirect_common(struct sk_buff *skb, struct net_device *dev, u32 flags) { + /* Verify that a link layer header is carried */ + if (unlikely(skb->mac_header >= skb->network_header)) { + kfree_skb(skb); + return -ERANGE; + } + bpf_push_mac_rcsum(skb); return flags & BPF_F_INGRESS ? __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb); @@ -2188,12 +2194,53 @@ static const struct bpf_func_proto bpf_skb_change_tail_proto = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_skb_change_head, struct sk_buff *, skb, u32, head_room, + u64, flags) +{ + u32 max_len = __bpf_skb_max_len(skb); + u32 new_len = skb->len + head_room; + int ret; + + if (unlikely(flags || (!skb_is_gso(skb) && new_len > max_len) || + new_len < skb->len)) + return -EINVAL; + + ret = skb_cow(skb, head_room); + if (likely(!ret)) { + /* Idea for this helper is that we currently only + * allow to expand on mac header. This means that + * skb->protocol network header, etc, stay as is. + * Compared to bpf_skb_change_tail(), we're more + * flexible due to not needing to linearize or + * reset GSO. Intention for this helper is to be + * used by an L3 skb that needs to push mac header + * for redirection into L2 device. + */ + __skb_push(skb, head_room); + memset(skb->data, 0, head_room); + skb_reset_mac_header(skb); + } + + bpf_compute_data_end(skb); + return 0; +} + +static const struct bpf_func_proto bpf_skb_change_head_proto = { + .func = bpf_skb_change_head, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; + bool bpf_helper_changes_skb_data(void *func) { if (func == bpf_skb_vlan_push || func == bpf_skb_vlan_pop || func == bpf_skb_store_bytes || func == bpf_skb_change_proto || + func == bpf_skb_change_head || func == bpf_skb_change_tail || func == bpf_skb_pull_data || func == bpf_l3_csum_replace || @@ -2639,6 +2686,68 @@ cg_skb_func_proto(enum bpf_func_id func_id) } } +static const struct bpf_func_proto * +lwt_inout_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_skb_load_bytes: + return &bpf_skb_load_bytes_proto; + case BPF_FUNC_skb_pull_data: + return &bpf_skb_pull_data_proto; + case BPF_FUNC_csum_diff: + return &bpf_csum_diff_proto; + case BPF_FUNC_get_cgroup_classid: + return &bpf_get_cgroup_classid_proto; + case BPF_FUNC_get_route_realm: + return &bpf_get_route_realm_proto; + case BPF_FUNC_get_hash_recalc: + return &bpf_get_hash_recalc_proto; + case BPF_FUNC_perf_event_output: + return &bpf_skb_event_output_proto; + case BPF_FUNC_get_smp_processor_id: + return &bpf_get_smp_processor_id_proto; + case BPF_FUNC_skb_under_cgroup: + return &bpf_skb_under_cgroup_proto; + default: + return sk_filter_func_proto(func_id); + } +} + +static const struct bpf_func_proto * +lwt_xmit_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_skb_get_tunnel_key: + return &bpf_skb_get_tunnel_key_proto; + case BPF_FUNC_skb_set_tunnel_key: + return bpf_get_skb_set_tunnel_proto(func_id); + case BPF_FUNC_skb_get_tunnel_opt: + return &bpf_skb_get_tunnel_opt_proto; + case BPF_FUNC_skb_set_tunnel_opt: + return bpf_get_skb_set_tunnel_proto(func_id); + case BPF_FUNC_redirect: + return &bpf_redirect_proto; + case BPF_FUNC_clone_redirect: + return &bpf_clone_redirect_proto; + case BPF_FUNC_skb_change_tail: + return &bpf_skb_change_tail_proto; + case BPF_FUNC_skb_change_head: + return &bpf_skb_change_head_proto; + case BPF_FUNC_skb_store_bytes: + return &bpf_skb_store_bytes_proto; + case BPF_FUNC_csum_update: + return &bpf_csum_update_proto; + case BPF_FUNC_l3_csum_replace: + return &bpf_l3_csum_replace_proto; + case BPF_FUNC_l4_csum_replace: + return &bpf_l4_csum_replace_proto; + case BPF_FUNC_set_hash_invalid: + return &bpf_set_hash_invalid_proto; + default: + return lwt_inout_func_proto(func_id); + } +} + static bool __is_valid_access(int off, int size, enum bpf_access_type type) { if (off < 0 || off >= sizeof(struct __sk_buff)) @@ -2676,6 +2785,39 @@ static bool sk_filter_is_valid_access(int off, int size, return __is_valid_access(off, size, type); } +static bool lwt_is_valid_access(int off, int size, + enum bpf_access_type type, + enum bpf_reg_type *reg_type) +{ + switch (off) { + case offsetof(struct __sk_buff, tc_classid): + return false; + } + + if (type == BPF_WRITE) { + switch (off) { + case offsetof(struct __sk_buff, mark): + case offsetof(struct __sk_buff, priority): + case offsetof(struct __sk_buff, cb[0]) ... + offsetof(struct __sk_buff, cb[4]): + break; + default: + return false; + } + } + + switch (off) { + case offsetof(struct __sk_buff, data): + *reg_type = PTR_TO_PACKET; + break; + case offsetof(struct __sk_buff, data_end): + *reg_type = PTR_TO_PACKET_END; + break; + } + + return __is_valid_access(off, size, type); +} + static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, const struct bpf_prog *prog) { @@ -3007,6 +3149,19 @@ static const struct bpf_verifier_ops cg_skb_ops = { .convert_ctx_access = sk_filter_convert_ctx_access, }; +static const struct bpf_verifier_ops lwt_inout_ops = { + .get_func_proto = lwt_inout_func_proto, + .is_valid_access = lwt_is_valid_access, + .convert_ctx_access = sk_filter_convert_ctx_access, +}; + +static const struct bpf_verifier_ops lwt_xmit_ops = { + .get_func_proto = lwt_xmit_func_proto, + .is_valid_access = lwt_is_valid_access, + .convert_ctx_access = sk_filter_convert_ctx_access, + .gen_prologue = tc_cls_act_prologue, +}; + static struct bpf_prog_type_list sk_filter_type __read_mostly = { .ops = &sk_filter_ops, .type = BPF_PROG_TYPE_SOCKET_FILTER, @@ -3032,6 +3187,21 @@ static struct bpf_prog_type_list cg_skb_type __read_mostly = { .type = BPF_PROG_TYPE_CGROUP_SKB, }; +static struct bpf_prog_type_list lwt_in_type __read_mostly = { + .ops = &lwt_inout_ops, + .type = BPF_PROG_TYPE_LWT_IN, +}; + +static struct bpf_prog_type_list lwt_out_type __read_mostly = { + .ops = &lwt_inout_ops, + .type = BPF_PROG_TYPE_LWT_OUT, +}; + +static struct bpf_prog_type_list lwt_xmit_type __read_mostly = { + .ops = &lwt_xmit_ops, + .type = BPF_PROG_TYPE_LWT_XMIT, +}; + static int __init register_sk_filter_ops(void) { bpf_register_prog_type(&sk_filter_type); @@ -3039,6 +3209,9 @@ static int __init register_sk_filter_ops(void) bpf_register_prog_type(&sched_act_type); bpf_register_prog_type(&xdp_type); bpf_register_prog_type(&cg_skb_type); + bpf_register_prog_type(&lwt_in_type); + bpf_register_prog_type(&lwt_out_type); + bpf_register_prog_type(&lwt_xmit_type); return 0; } diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c new file mode 100644 index 0000000..71bb3e2 --- /dev/null +++ b/net/core/lwt_bpf.c @@ -0,0 +1,396 @@ +/* Copyright (c) 2016 Thomas Graf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +struct bpf_lwt_prog { + struct bpf_prog *prog; + char *name; +}; + +struct bpf_lwt { + struct bpf_lwt_prog in; + struct bpf_lwt_prog out; + struct bpf_lwt_prog xmit; + int family; +}; + +#define MAX_PROG_NAME 256 + +static inline struct bpf_lwt *bpf_lwt_lwtunnel(struct lwtunnel_state *lwt) +{ + return (struct bpf_lwt *)lwt->data; +} + +#define NO_REDIRECT false +#define CAN_REDIRECT true + +static int run_lwt_bpf(struct sk_buff *skb, struct bpf_lwt_prog *lwt, + struct dst_entry *dst, bool can_redirect) +{ + int ret; + + /* Preempt disable is needed to protect per-cpu redirect_info between + * BPF prog and skb_do_redirect(). The call_rcu in bpf_prog_put() and + * access to maps strictly require a rcu_read_lock() for protection, + * mixing with BH RCU lock doesn't work. + */ + preempt_disable(); + rcu_read_lock(); + bpf_compute_data_end(skb); + ret = bpf_prog_run_save_cb(lwt->prog, skb); + rcu_read_unlock(); + + switch (ret) { + case BPF_OK: + break; + + case BPF_REDIRECT: + if (unlikely(!can_redirect)) { + pr_warn_once("Illegal redirect return code in prog %s\n", + lwt->name ? : ""); + ret = BPF_OK; + } else { + ret = skb_do_redirect(skb); + if (ret == 0) + ret = BPF_REDIRECT; + } + break; + + case BPF_DROP: + kfree_skb(skb); + ret = -EPERM; + break; + + default: + pr_warn_once("bpf-lwt: Illegal return value %u, expect packet loss\n", ret); + kfree_skb(skb); + ret = -EINVAL; + break; + } + + preempt_enable(); + + return ret; +} + +static int bpf_input(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct bpf_lwt *bpf; + int ret; + + bpf = bpf_lwt_lwtunnel(dst->lwtstate); + if (bpf->in.prog) { + ret = run_lwt_bpf(skb, &bpf->in, dst, NO_REDIRECT); + if (ret < 0) + return ret; + } + + if (unlikely(!dst->lwtstate->orig_input)) { + pr_warn_once("orig_input not set on dst for prog %s\n", + bpf->out.name); + kfree_skb(skb); + return -EINVAL; + } + + return dst->lwtstate->orig_input(skb); +} + +static int bpf_output(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct bpf_lwt *bpf; + int ret; + + bpf = bpf_lwt_lwtunnel(dst->lwtstate); + if (bpf->out.prog) { + ret = run_lwt_bpf(skb, &bpf->out, dst, NO_REDIRECT); + if (ret < 0) + return ret; + } + + if (unlikely(!dst->lwtstate->orig_output)) { + pr_warn_once("orig_output not set on dst for prog %s\n", + bpf->out.name); + kfree_skb(skb); + return -EINVAL; + } + + return dst->lwtstate->orig_output(net, sk, skb); +} + +static int xmit_check_hhlen(struct sk_buff *skb) +{ + int hh_len = skb_dst(skb)->dev->hard_header_len; + + if (skb_headroom(skb) < hh_len) { + int nhead = HH_DATA_ALIGN(hh_len - skb_headroom(skb)); + + if (pskb_expand_head(skb, nhead, 0, GFP_ATOMIC)) + return -ENOMEM; + } + + return 0; +} + +static int bpf_xmit(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct bpf_lwt *bpf; + + bpf = bpf_lwt_lwtunnel(dst->lwtstate); + if (bpf->xmit.prog) { + int ret; + + ret = run_lwt_bpf(skb, &bpf->xmit, dst, CAN_REDIRECT); + switch (ret) { + case BPF_OK: + /* If the header was expanded, headroom might be too + * small for L2 header to come, expand as needed. + */ + ret = xmit_check_hhlen(skb); + if (unlikely(ret)) + return ret; + + return LWTUNNEL_XMIT_CONTINUE; + case BPF_REDIRECT: + return LWTUNNEL_XMIT_DONE; + default: + return ret; + } + } + + return LWTUNNEL_XMIT_CONTINUE; +} + +static void bpf_lwt_prog_destroy(struct bpf_lwt_prog *prog) +{ + if (prog->prog) + bpf_prog_put(prog->prog); + + kfree(prog->name); +} + +static void bpf_destroy_state(struct lwtunnel_state *lwt) +{ + struct bpf_lwt *bpf = bpf_lwt_lwtunnel(lwt); + + bpf_lwt_prog_destroy(&bpf->in); + bpf_lwt_prog_destroy(&bpf->out); + bpf_lwt_prog_destroy(&bpf->xmit); +} + +static const struct nla_policy bpf_prog_policy[LWT_BPF_PROG_MAX + 1] = { + [LWT_BPF_PROG_FD] = { .type = NLA_U32, }, + [LWT_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, + .len = MAX_PROG_NAME }, +}; + +static int bpf_parse_prog(struct nlattr *attr, struct bpf_lwt_prog *prog, + enum bpf_prog_type type) +{ + struct nlattr *tb[LWT_BPF_PROG_MAX + 1]; + struct bpf_prog *p; + int ret; + u32 fd; + + ret = nla_parse_nested(tb, LWT_BPF_PROG_MAX, attr, bpf_prog_policy); + if (ret < 0) + return ret; + + if (!tb[LWT_BPF_PROG_FD] || !tb[LWT_BPF_PROG_NAME]) + return -EINVAL; + + prog->name = nla_memdup(tb[LWT_BPF_PROG_NAME], GFP_KERNEL); + if (!prog->name) + return -ENOMEM; + + fd = nla_get_u32(tb[LWT_BPF_PROG_FD]); + p = bpf_prog_get_type(fd, type); + if (IS_ERR(p)) + return PTR_ERR(p); + + prog->prog = p; + + return 0; +} + +static const struct nla_policy bpf_nl_policy[LWT_BPF_MAX + 1] = { + [LWT_BPF_IN] = { .type = NLA_NESTED, }, + [LWT_BPF_OUT] = { .type = NLA_NESTED, }, + [LWT_BPF_XMIT] = { .type = NLA_NESTED, }, + [LWT_BPF_XMIT_HEADROOM] = { .type = NLA_U32 }, +}; + +static int bpf_build_state(struct net_device *dev, struct nlattr *nla, + unsigned int family, const void *cfg, + struct lwtunnel_state **ts) +{ + struct nlattr *tb[LWT_BPF_MAX + 1]; + struct lwtunnel_state *newts; + struct bpf_lwt *bpf; + int ret; + + if (family != AF_INET && family != AF_INET6) + return -EAFNOSUPPORT; + + ret = nla_parse_nested(tb, LWT_BPF_MAX, nla, bpf_nl_policy); + if (ret < 0) + return ret; + + if (!tb[LWT_BPF_IN] && !tb[LWT_BPF_OUT] && !tb[LWT_BPF_XMIT]) + return -EINVAL; + + newts = lwtunnel_state_alloc(sizeof(*bpf)); + if (!newts) + return -ENOMEM; + + newts->type = LWTUNNEL_ENCAP_BPF; + bpf = bpf_lwt_lwtunnel(newts); + + if (tb[LWT_BPF_IN]) { + newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; + ret = bpf_parse_prog(tb[LWT_BPF_IN], &bpf->in, + BPF_PROG_TYPE_LWT_IN); + if (ret < 0) + goto errout; + } + + if (tb[LWT_BPF_OUT]) { + newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; + ret = bpf_parse_prog(tb[LWT_BPF_OUT], &bpf->out, + BPF_PROG_TYPE_LWT_OUT); + if (ret < 0) + goto errout; + } + + if (tb[LWT_BPF_XMIT]) { + newts->flags |= LWTUNNEL_STATE_XMIT_REDIRECT; + ret = bpf_parse_prog(tb[LWT_BPF_XMIT], &bpf->xmit, + BPF_PROG_TYPE_LWT_XMIT); + if (ret < 0) + goto errout; + } + + if (tb[LWT_BPF_XMIT_HEADROOM]) { + u32 headroom = nla_get_u32(tb[LWT_BPF_XMIT_HEADROOM]); + + if (headroom > LWT_BPF_MAX_HEADROOM) { + ret = -ERANGE; + goto errout; + } + + newts->headroom = headroom; + } + + bpf->family = family; + *ts = newts; + + return 0; + +errout: + bpf_destroy_state(newts); + kfree(newts); + return ret; +} + +static int bpf_fill_lwt_prog(struct sk_buff *skb, int attr, + struct bpf_lwt_prog *prog) +{ + struct nlattr *nest; + + if (!prog->prog) + return 0; + + nest = nla_nest_start(skb, attr); + if (!nest) + return -EMSGSIZE; + + if (prog->name && + nla_put_string(skb, LWT_BPF_PROG_NAME, prog->name)) + return -EMSGSIZE; + + return nla_nest_end(skb, nest); +} + +static int bpf_fill_encap_info(struct sk_buff *skb, struct lwtunnel_state *lwt) +{ + struct bpf_lwt *bpf = bpf_lwt_lwtunnel(lwt); + + if (bpf_fill_lwt_prog(skb, LWT_BPF_IN, &bpf->in) < 0 || + bpf_fill_lwt_prog(skb, LWT_BPF_OUT, &bpf->out) < 0 || + bpf_fill_lwt_prog(skb, LWT_BPF_XMIT, &bpf->xmit) < 0) + return -EMSGSIZE; + + return 0; +} + +static int bpf_encap_nlsize(struct lwtunnel_state *lwtstate) +{ + int nest_len = nla_total_size(sizeof(struct nlattr)) + + nla_total_size(MAX_PROG_NAME) + /* LWT_BPF_PROG_NAME */ + 0; + + return nest_len + /* LWT_BPF_IN */ + nest_len + /* LWT_BPF_OUT */ + nest_len + /* LWT_BPF_XMIT */ + 0; +} + +int bpf_lwt_prog_cmp(struct bpf_lwt_prog *a, struct bpf_lwt_prog *b) +{ + /* FIXME: + * The LWT state is currently rebuilt for delete requests which + * results in a new bpf_prog instance. Comparing names for now. + */ + if (!a->name && !b->name) + return 0; + + if (!a->name || !b->name) + return 1; + + return strcmp(a->name, b->name); +} + +static int bpf_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) +{ + struct bpf_lwt *a_bpf = bpf_lwt_lwtunnel(a); + struct bpf_lwt *b_bpf = bpf_lwt_lwtunnel(b); + + return bpf_lwt_prog_cmp(&a_bpf->in, &b_bpf->in) || + bpf_lwt_prog_cmp(&a_bpf->out, &b_bpf->out) || + bpf_lwt_prog_cmp(&a_bpf->xmit, &b_bpf->xmit); +} + +static const struct lwtunnel_encap_ops bpf_encap_ops = { + .build_state = bpf_build_state, + .destroy_state = bpf_destroy_state, + .input = bpf_input, + .output = bpf_output, + .xmit = bpf_xmit, + .fill_encap = bpf_fill_encap_info, + .get_encap_size = bpf_encap_nlsize, + .cmp_encap = bpf_encap_cmp, +}; + +static int __init bpf_lwt_init(void) +{ + return lwtunnel_encap_add_ops(&bpf_encap_ops, LWTUNNEL_ENCAP_BPF); +} + +subsys_initcall(bpf_lwt_init) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 03976e9..a5d4e86 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -41,6 +41,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) return "ILA"; case LWTUNNEL_ENCAP_SEG6: return "SEG6"; + case LWTUNNEL_ENCAP_BPF: + return "BPF"; case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP: case LWTUNNEL_ENCAP_NONE: -- cgit v1.1 From f74599f7c5309b21151233b98139e9b723fd1110 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 30 Nov 2016 17:10:11 +0100 Subject: bpf: Add tests and samples for LWT-BPF Adds a series of tests to verify the functionality of attaching BPF programs at LWT hooks. Also adds a sample which collects a histogram of packet sizes which pass through an LWT hook. $ ./lwt_len_hist.sh Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.253.2 () port 0 AF_INET : demo Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 39857.69 1 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 22 | | 64 -> 127 : 98 | | 128 -> 255 : 213 | | 256 -> 511 : 1444251 |******** | 512 -> 1023 : 660610 |*** | 1024 -> 2047 : 535241 |** | 2048 -> 4095 : 19 | | 4096 -> 8191 : 180 | | 8192 -> 16383 : 5578023 |************************************* | 16384 -> 32767 : 632099 |*** | 32768 -> 65535 : 6575 | | Signed-off-by: Thomas Graf Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 4 + samples/bpf/bpf_helpers.h | 4 + samples/bpf/lwt_len_hist.sh | 37 ++++ samples/bpf/lwt_len_hist_kern.c | 82 +++++++++ samples/bpf/lwt_len_hist_user.c | 76 ++++++++ samples/bpf/test_lwt_bpf.c | 253 +++++++++++++++++++++++++ samples/bpf/test_lwt_bpf.sh | 399 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 855 insertions(+) create mode 100644 samples/bpf/lwt_len_hist.sh create mode 100644 samples/bpf/lwt_len_hist_kern.c create mode 100644 samples/bpf/lwt_len_hist_user.c create mode 100644 samples/bpf/test_lwt_bpf.c create mode 100644 samples/bpf/test_lwt_bpf.sh diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 3ceb5a9..44532fb 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -29,6 +29,7 @@ hostprogs-y += test_current_task_under_cgroup hostprogs-y += trace_event hostprogs-y += sampleip hostprogs-y += tc_l2_redirect +hostprogs-y += lwt_len_hist test_lru_dist-objs := test_lru_dist.o libbpf.o sock_example-objs := sock_example.o libbpf.o @@ -59,6 +60,7 @@ test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \ trace_event-objs := bpf_load.o libbpf.o trace_event_user.o sampleip-objs := bpf_load.o libbpf.o sampleip_user.o tc_l2_redirect-objs := bpf_load.o libbpf.o tc_l2_redirect_user.o +lwt_len_hist-objs := bpf_load.o libbpf.o lwt_len_hist_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -89,6 +91,7 @@ always += xdp2_kern.o always += test_current_task_under_cgroup_kern.o always += trace_event_kern.o always += sampleip_kern.o +always += lwt_len_hist_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/testing/selftests/bpf/ @@ -117,6 +120,7 @@ HOSTLOADLIBES_test_current_task_under_cgroup += -lelf HOSTLOADLIBES_trace_event += -lelf HOSTLOADLIBES_sampleip += -lelf HOSTLOADLIBES_tc_l2_redirect += -l elf +HOSTLOADLIBES_lwt_len_hist += -l elf # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index 90f44bd..a246c61 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h @@ -80,6 +80,8 @@ struct bpf_map_def { unsigned int map_flags; }; +static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = + (void *) BPF_FUNC_skb_load_bytes; static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = (void *) BPF_FUNC_skb_store_bytes; static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = @@ -88,6 +90,8 @@ static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flag (void *) BPF_FUNC_l4_csum_replace; static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = (void *) BPF_FUNC_skb_under_cgroup; +static int (*bpf_skb_change_head)(void *, int len, int flags) = + (void *) BPF_FUNC_skb_change_head; #if defined(__x86_64__) diff --git a/samples/bpf/lwt_len_hist.sh b/samples/bpf/lwt_len_hist.sh new file mode 100644 index 0000000..7d56774 --- /dev/null +++ b/samples/bpf/lwt_len_hist.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +NS1=lwt_ns1 +VETH0=tst_lwt1a +VETH1=tst_lwt1b + +TRACE_ROOT=/sys/kernel/debug/tracing + +function cleanup { + ip route del 192.168.253.2/32 dev $VETH0 2> /dev/null + ip link del $VETH0 2> /dev/null + ip link del $VETH1 2> /dev/null + ip netns exec $NS1 killall netserver + ip netns delete $NS1 2> /dev/null +} + +cleanup + +ip netns add $NS1 +ip link add $VETH0 type veth peer name $VETH1 +ip link set dev $VETH0 up +ip addr add 192.168.253.1/24 dev $VETH0 +ip link set $VETH1 netns $NS1 +ip netns exec $NS1 ip link set dev $VETH1 up +ip netns exec $NS1 ip addr add 192.168.253.2/24 dev $VETH1 +ip netns exec $NS1 netserver + +echo 1 > ${TRACE_ROOT}/tracing_on +cp /dev/null ${TRACE_ROOT}/trace +ip route add 192.168.253.2/32 encap bpf out obj lwt_len_hist_kern.o section len_hist dev $VETH0 +netperf -H 192.168.253.2 -t TCP_STREAM +cat ${TRACE_ROOT}/trace | grep -v '^#' +./lwt_len_hist +cleanup +echo 0 > ${TRACE_ROOT}/tracing_on + +exit 0 diff --git a/samples/bpf/lwt_len_hist_kern.c b/samples/bpf/lwt_len_hist_kern.c new file mode 100644 index 0000000..df75383 --- /dev/null +++ b/samples/bpf/lwt_len_hist_kern.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2016 Thomas Graf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include "bpf_helpers.h" + +# define printk(fmt, ...) \ + ({ \ + char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ + }) + +struct bpf_elf_map { + __u32 type; + __u32 size_key; + __u32 size_value; + __u32 max_elem; + __u32 flags; + __u32 id; + __u32 pinning; +}; + +struct bpf_elf_map SEC("maps") lwt_len_hist_map = { + .type = BPF_MAP_TYPE_PERCPU_HASH, + .size_key = sizeof(__u64), + .size_value = sizeof(__u64), + .pinning = 2, + .max_elem = 1024, +}; + +static unsigned int log2(unsigned int v) +{ + unsigned int r; + unsigned int shift; + + r = (v > 0xFFFF) << 4; v >>= r; + shift = (v > 0xFF) << 3; v >>= shift; r |= shift; + shift = (v > 0xF) << 2; v >>= shift; r |= shift; + shift = (v > 0x3) << 1; v >>= shift; r |= shift; + r |= (v >> 1); + return r; +} + +static unsigned int log2l(unsigned long v) +{ + unsigned int hi = v >> 32; + if (hi) + return log2(hi) + 32; + else + return log2(v); +} + +SEC("len_hist") +int do_len_hist(struct __sk_buff *skb) +{ + __u64 *value, key, init_val = 1; + + key = log2l(skb->len); + + value = bpf_map_lookup_elem(&lwt_len_hist_map, &key); + if (value) + __sync_fetch_and_add(value, 1); + else + bpf_map_update_elem(&lwt_len_hist_map, &key, &init_val, BPF_ANY); + + return BPF_OK; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/lwt_len_hist_user.c b/samples/bpf/lwt_len_hist_user.c new file mode 100644 index 0000000..05d783f --- /dev/null +++ b/samples/bpf/lwt_len_hist_user.c @@ -0,0 +1,76 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "libbpf.h" +#include "bpf_util.h" + +#define MAX_INDEX 64 +#define MAX_STARS 38 + +static void stars(char *str, long val, long max, int width) +{ + int i; + + for (i = 0; i < (width * val / max) - 1 && i < width - 1; i++) + str[i] = '*'; + if (val > max) + str[i - 1] = '+'; + str[i] = '\0'; +} + +int main(int argc, char **argv) +{ + unsigned int nr_cpus = bpf_num_possible_cpus(); + const char *map_filename = "/sys/fs/bpf/tc/globals/lwt_len_hist_map"; + uint64_t values[nr_cpus], sum, max_value = 0, data[MAX_INDEX] = {}; + uint64_t key = 0, next_key, max_key = 0; + char starstr[MAX_STARS]; + int i, map_fd; + + map_fd = bpf_obj_get(map_filename); + if (map_fd < 0) { + fprintf(stderr, "bpf_obj_get(%s): %s(%d)\n", + map_filename, strerror(errno), errno); + return -1; + } + + while (bpf_get_next_key(map_fd, &key, &next_key) == 0) { + if (next_key >= MAX_INDEX) { + fprintf(stderr, "Key %lu out of bounds\n", next_key); + continue; + } + + bpf_lookup_elem(map_fd, &next_key, values); + + sum = 0; + for (i = 0; i < nr_cpus; i++) + sum += values[i]; + + data[next_key] = sum; + if (sum && next_key > max_key) + max_key = next_key; + + if (sum > max_value) + max_value = sum; + + key = next_key; + } + + for (i = 1; i <= max_key + 1; i++) { + stars(starstr, data[i - 1], max_value, MAX_STARS); + printf("%8ld -> %-8ld : %-8ld |%-*s|\n", + (1l << i) >> 1, (1l << i) - 1, data[i - 1], + MAX_STARS, starstr); + } + + close(map_fd); + + return 0; +} diff --git a/samples/bpf/test_lwt_bpf.c b/samples/bpf/test_lwt_bpf.c new file mode 100644 index 0000000..bacc801 --- /dev/null +++ b/samples/bpf/test_lwt_bpf.c @@ -0,0 +1,253 @@ +/* Copyright (c) 2016 Thomas Graf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_helpers.h" +#include + +# define printk(fmt, ...) \ + ({ \ + char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ + }) + +#define CB_MAGIC 1234 + +/* Test: Pass all packets through */ +SEC("nop") +int do_nop(struct __sk_buff *skb) +{ + return BPF_OK; +} + +/* Test: Verify context information can be accessed */ +SEC("test_ctx") +int do_test_ctx(struct __sk_buff *skb) +{ + skb->cb[0] = CB_MAGIC; + printk("len %d hash %d protocol %d\n", skb->len, skb->hash, + skb->protocol); + printk("cb %d ingress_ifindex %d ifindex %d\n", skb->cb[0], + skb->ingress_ifindex, skb->ifindex); + + return BPF_OK; +} + +/* Test: Ensure skb->cb[] buffer is cleared */ +SEC("test_cb") +int do_test_cb(struct __sk_buff *skb) +{ + printk("cb0: %x cb1: %x cb2: %x\n", skb->cb[0], skb->cb[1], + skb->cb[2]); + printk("cb3: %x cb4: %x\n", skb->cb[3], skb->cb[4]); + + return BPF_OK; +} + +/* Test: Verify skb data can be read */ +SEC("test_data") +int do_test_data(struct __sk_buff *skb) +{ + void *data = (void *)(long)skb->data; + void *data_end = (void *)(long)skb->data_end; + struct iphdr *iph = data; + + if (data + sizeof(*iph) > data_end) { + printk("packet truncated\n"); + return BPF_DROP; + } + + printk("src: %x dst: %x\n", iph->saddr, iph->daddr); + + return BPF_OK; +} + +#define IP_CSUM_OFF offsetof(struct iphdr, check) +#define IP_DST_OFF offsetof(struct iphdr, daddr) +#define IP_SRC_OFF offsetof(struct iphdr, saddr) +#define IP_PROTO_OFF offsetof(struct iphdr, protocol) +#define TCP_CSUM_OFF offsetof(struct tcphdr, check) +#define UDP_CSUM_OFF offsetof(struct udphdr, check) +#define IS_PSEUDO 0x10 + +static inline int rewrite(struct __sk_buff *skb, uint32_t old_ip, + uint32_t new_ip, int rw_daddr) +{ + int ret, off = 0, flags = IS_PSEUDO; + uint8_t proto; + + ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1); + if (ret < 0) { + printk("bpf_l4_csum_replace failed: %d\n", ret); + return BPF_DROP; + } + + switch (proto) { + case IPPROTO_TCP: + off = TCP_CSUM_OFF; + break; + + case IPPROTO_UDP: + off = UDP_CSUM_OFF; + flags |= BPF_F_MARK_MANGLED_0; + break; + + case IPPROTO_ICMPV6: + off = offsetof(struct icmp6hdr, icmp6_cksum); + break; + } + + if (off) { + ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip, + flags | sizeof(new_ip)); + if (ret < 0) { + printk("bpf_l4_csum_replace failed: %d\n"); + return BPF_DROP; + } + } + + ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip)); + if (ret < 0) { + printk("bpf_l3_csum_replace failed: %d\n", ret); + return BPF_DROP; + } + + if (rw_daddr) + ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0); + else + ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0); + + if (ret < 0) { + printk("bpf_skb_store_bytes() failed: %d\n", ret); + return BPF_DROP; + } + + return BPF_OK; +} + +/* Test: Verify skb data can be modified */ +SEC("test_rewrite") +int do_test_rewrite(struct __sk_buff *skb) +{ + uint32_t old_ip, new_ip = 0x3fea8c0; + int ret; + + ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4); + if (ret < 0) { + printk("bpf_skb_load_bytes failed: %d\n", ret); + return BPF_DROP; + } + + if (old_ip == 0x2fea8c0) { + printk("out: rewriting from %x to %x\n", old_ip, new_ip); + return rewrite(skb, old_ip, new_ip, 1); + } + + return BPF_OK; +} + +static inline int __do_push_ll_and_redirect(struct __sk_buff *skb) +{ + uint64_t smac = SRC_MAC, dmac = DST_MAC; + int ret, ifindex = DST_IFINDEX; + struct ethhdr ehdr; + + ret = bpf_skb_change_head(skb, 14, 0); + if (ret < 0) { + printk("skb_change_head() failed: %d\n", ret); + } + + ehdr.h_proto = __constant_htons(ETH_P_IP); + memcpy(&ehdr.h_source, &smac, 6); + memcpy(&ehdr.h_dest, &dmac, 6); + + ret = bpf_skb_store_bytes(skb, 0, &ehdr, sizeof(ehdr), 0); + if (ret < 0) { + printk("skb_store_bytes() failed: %d\n", ret); + return BPF_DROP; + } + + return bpf_redirect(ifindex, 0); +} + +SEC("push_ll_and_redirect_silent") +int do_push_ll_and_redirect_silent(struct __sk_buff *skb) +{ + return __do_push_ll_and_redirect(skb); +} + +SEC("push_ll_and_redirect") +int do_push_ll_and_redirect(struct __sk_buff *skb) +{ + int ret, ifindex = DST_IFINDEX; + + ret = __do_push_ll_and_redirect(skb); + if (ret >= 0) + printk("redirected to %d\n", ifindex); + + return ret; +} + +static inline void __fill_garbage(struct __sk_buff *skb) +{ + uint64_t f = 0xFFFFFFFFFFFFFFFF; + + bpf_skb_store_bytes(skb, 0, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 8, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 16, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 24, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 32, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 40, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 48, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 56, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 64, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 72, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 80, &f, sizeof(f), 0); + bpf_skb_store_bytes(skb, 88, &f, sizeof(f), 0); +} + +SEC("fill_garbage") +int do_fill_garbage(struct __sk_buff *skb) +{ + __fill_garbage(skb); + printk("Set initial 96 bytes of header to FF\n"); + return BPF_OK; +} + +SEC("fill_garbage_and_redirect") +int do_fill_garbage_and_redirect(struct __sk_buff *skb) +{ + int ifindex = DST_IFINDEX; + __fill_garbage(skb); + printk("redirected to %d\n", ifindex); + return bpf_redirect(ifindex, 0); +} + +/* Drop all packets */ +SEC("drop_all") +int do_drop_all(struct __sk_buff *skb) +{ + printk("dropping with: %d\n", BPF_DROP); + return BPF_DROP; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/test_lwt_bpf.sh b/samples/bpf/test_lwt_bpf.sh new file mode 100644 index 0000000..a695ae2 --- /dev/null +++ b/samples/bpf/test_lwt_bpf.sh @@ -0,0 +1,399 @@ +#!/bin/bash + +# Uncomment to see generated bytecode +#VERBOSE=verbose + +NS1=lwt_ns1 +NS2=lwt_ns2 +VETH0=tst_lwt1a +VETH1=tst_lwt1b +VETH2=tst_lwt2a +VETH3=tst_lwt2b +IPVETH0="192.168.254.1" +IPVETH1="192.168.254.2" +IPVETH1b="192.168.254.3" + +IPVETH2="192.168.111.1" +IPVETH3="192.168.111.2" + +IP_LOCAL="192.168.99.1" + +TRACE_ROOT=/sys/kernel/debug/tracing + +function lookup_mac() +{ + set +x + if [ ! -z "$2" ]; then + MAC=$(ip netns exec $2 ip link show $1 | grep ether | awk '{print $2}') + else + MAC=$(ip link show $1 | grep ether | awk '{print $2}') + fi + MAC="${MAC//:/}" + echo "0x${MAC:10:2}${MAC:8:2}${MAC:6:2}${MAC:4:2}${MAC:2:2}${MAC:0:2}" + set -x +} + +function cleanup { + set +ex + rm test_lwt_bpf.o 2> /dev/null + ip link del $VETH0 2> /dev/null + ip link del $VETH1 2> /dev/null + ip link del $VETH2 2> /dev/null + ip link del $VETH3 2> /dev/null + ip netns exec $NS1 killall netserver + ip netns delete $NS1 2> /dev/null + ip netns delete $NS2 2> /dev/null + set -ex +} + +function setup_one_veth { + ip netns add $1 + ip link add $2 type veth peer name $3 + ip link set dev $2 up + ip addr add $4/24 dev $2 + ip link set $3 netns $1 + ip netns exec $1 ip link set dev $3 up + ip netns exec $1 ip addr add $5/24 dev $3 + + if [ "$6" ]; then + ip netns exec $1 ip addr add $6/32 dev $3 + fi +} + +function get_trace { + set +x + cat ${TRACE_ROOT}/trace | grep -v '^#' + set -x +} + +function cleanup_routes { + ip route del ${IPVETH1}/32 dev $VETH0 2> /dev/null || true + ip route del table local local ${IP_LOCAL}/32 dev lo 2> /dev/null || true +} + +function install_test { + cleanup_routes + cp /dev/null ${TRACE_ROOT}/trace + + OPTS="encap bpf headroom 14 $1 obj test_lwt_bpf.o section $2 $VERBOSE" + + if [ "$1" == "in" ]; then + ip route add table local local ${IP_LOCAL}/32 $OPTS dev lo + else + ip route add ${IPVETH1}/32 $OPTS dev $VETH0 + fi +} + +function remove_prog { + if [ "$1" == "in" ]; then + ip route del table local local ${IP_LOCAL}/32 dev lo + else + ip route del ${IPVETH1}/32 dev $VETH0 + fi +} + +function filter_trace { + # Add newline to allow starting EXPECT= variables on newline + NL=$'\n' + echo "${NL}$*" | sed -e 's/^.*: : //g' +} + +function expect_fail { + set +x + echo "FAIL:" + echo "Expected: $1" + echo "Got: $2" + set -x + exit 1 +} + +function match_trace { + set +x + RET=0 + TRACE=$1 + EXPECT=$2 + GOT="$(filter_trace "$TRACE")" + + [ "$GOT" != "$EXPECT" ] && { + expect_fail "$EXPECT" "$GOT" + RET=1 + } + set -x + return $RET +} + +function test_start { + set +x + echo "----------------------------------------------------------------" + echo "Starting test: $*" + echo "----------------------------------------------------------------" + set -x +} + +function failure { + get_trace + echo "FAIL: $*" + exit 1 +} + +function test_ctx_xmit { + test_start "test_ctx on lwt xmit" + install_test xmit test_ctx + ping -c 3 $IPVETH1 || { + failure "test_ctx xmit: packets are dropped" + } + match_trace "$(get_trace)" " +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX" || exit 1 + remove_prog xmit +} + +function test_ctx_out { + test_start "test_ctx on lwt out" + install_test out test_ctx + ping -c 3 $IPVETH1 || { + failure "test_ctx out: packets are dropped" + } + match_trace "$(get_trace)" " +len 84 hash 0 protocol 0 +cb 1234 ingress_ifindex 0 ifindex 0 +len 84 hash 0 protocol 0 +cb 1234 ingress_ifindex 0 ifindex 0 +len 84 hash 0 protocol 0 +cb 1234 ingress_ifindex 0 ifindex 0" || exit 1 + remove_prog out +} + +function test_ctx_in { + test_start "test_ctx on lwt in" + install_test in test_ctx + ping -c 3 $IP_LOCAL || { + failure "test_ctx out: packets are dropped" + } + # We will both request & reply packets as the packets will + # be from $IP_LOCAL => $IP_LOCAL + match_trace "$(get_trace)" " +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 1 ifindex 1 +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 1 ifindex 1 +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 1 ifindex 1 +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 1 ifindex 1 +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 1 ifindex 1 +len 84 hash 0 protocol 8 +cb 1234 ingress_ifindex 1 ifindex 1" || exit 1 + remove_prog in +} + +function test_data { + test_start "test_data on lwt $1" + install_test $1 test_data + ping -c 3 $IPVETH1 || { + failure "test_data ${1}: packets are dropped" + } + match_trace "$(get_trace)" " +src: 1fea8c0 dst: 2fea8c0 +src: 1fea8c0 dst: 2fea8c0 +src: 1fea8c0 dst: 2fea8c0" || exit 1 + remove_prog $1 +} + +function test_data_in { + test_start "test_data on lwt in" + install_test in test_data + ping -c 3 $IP_LOCAL || { + failure "test_data in: packets are dropped" + } + # We will both request & reply packets as the packets will + # be from $IP_LOCAL => $IP_LOCAL + match_trace "$(get_trace)" " +src: 163a8c0 dst: 163a8c0 +src: 163a8c0 dst: 163a8c0 +src: 163a8c0 dst: 163a8c0 +src: 163a8c0 dst: 163a8c0 +src: 163a8c0 dst: 163a8c0 +src: 163a8c0 dst: 163a8c0" || exit 1 + remove_prog in +} + +function test_cb { + test_start "test_cb on lwt $1" + install_test $1 test_cb + ping -c 3 $IPVETH1 || { + failure "test_cb ${1}: packets are dropped" + } + match_trace "$(get_trace)" " +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0 +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0 +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0" || exit 1 + remove_prog $1 +} + +function test_cb_in { + test_start "test_cb on lwt in" + install_test in test_cb + ping -c 3 $IP_LOCAL || { + failure "test_cb in: packets are dropped" + } + # We will both request & reply packets as the packets will + # be from $IP_LOCAL => $IP_LOCAL + match_trace "$(get_trace)" " +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0 +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0 +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0 +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0 +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0 +cb0: 0 cb1: 0 cb2: 0 +cb3: 0 cb4: 0" || exit 1 + remove_prog in +} + +function test_drop_all { + test_start "test_drop_all on lwt $1" + install_test $1 drop_all + ping -c 3 $IPVETH1 && { + failure "test_drop_all ${1}: Unexpected success of ping" + } + match_trace "$(get_trace)" " +dropping with: 2 +dropping with: 2 +dropping with: 2" || exit 1 + remove_prog $1 +} + +function test_drop_all_in { + test_start "test_drop_all on lwt in" + install_test in drop_all + ping -c 3 $IP_LOCAL && { + failure "test_drop_all in: Unexpected success of ping" + } + match_trace "$(get_trace)" " +dropping with: 2 +dropping with: 2 +dropping with: 2" || exit 1 + remove_prog in +} + +function test_push_ll_and_redirect { + test_start "test_push_ll_and_redirect on lwt xmit" + install_test xmit push_ll_and_redirect + ping -c 3 $IPVETH1 || { + failure "Redirected packets appear to be dropped" + } + match_trace "$(get_trace)" " +redirected to $DST_IFINDEX +redirected to $DST_IFINDEX +redirected to $DST_IFINDEX" || exit 1 + remove_prog xmit +} + +function test_no_l2_and_redirect { + test_start "test_no_l2_and_redirect on lwt xmit" + install_test xmit fill_garbage_and_redirect + ping -c 3 $IPVETH1 && { + failure "Unexpected success despite lack of L2 header" + } + match_trace "$(get_trace)" " +redirected to $DST_IFINDEX +redirected to $DST_IFINDEX +redirected to $DST_IFINDEX" || exit 1 + remove_prog xmit +} + +function test_rewrite { + test_start "test_rewrite on lwt xmit" + install_test xmit test_rewrite + ping -c 3 $IPVETH1 || { + failure "Rewritten packets appear to be dropped" + } + match_trace "$(get_trace)" " +out: rewriting from 2fea8c0 to 3fea8c0 +out: rewriting from 2fea8c0 to 3fea8c0 +out: rewriting from 2fea8c0 to 3fea8c0" || exit 1 + remove_prog out +} + +function test_fill_garbage { + test_start "test_fill_garbage on lwt xmit" + install_test xmit fill_garbage + ping -c 3 $IPVETH1 && { + failure "test_drop_all ${1}: Unexpected success of ping" + } + match_trace "$(get_trace)" " +Set initial 96 bytes of header to FF +Set initial 96 bytes of header to FF +Set initial 96 bytes of header to FF" || exit 1 + remove_prog xmit +} + +function test_netperf_nop { + test_start "test_netperf_nop on lwt xmit" + install_test xmit nop + netperf -H $IPVETH1 -t TCP_STREAM || { + failure "packets appear to be dropped" + } + match_trace "$(get_trace)" ""|| exit 1 + remove_prog xmit +} + +function test_netperf_redirect { + test_start "test_netperf_redirect on lwt xmit" + install_test xmit push_ll_and_redirect_silent + netperf -H $IPVETH1 -t TCP_STREAM || { + failure "Rewritten packets appear to be dropped" + } + match_trace "$(get_trace)" ""|| exit 1 + remove_prog xmit +} + +cleanup +setup_one_veth $NS1 $VETH0 $VETH1 $IPVETH0 $IPVETH1 $IPVETH1b +setup_one_veth $NS2 $VETH2 $VETH3 $IPVETH2 $IPVETH3 +ip netns exec $NS1 netserver +echo 1 > ${TRACE_ROOT}/tracing_on + +DST_MAC=$(lookup_mac $VETH1 $NS1) +SRC_MAC=$(lookup_mac $VETH0) +DST_IFINDEX=$(cat /sys/class/net/$VETH0/ifindex) + +CLANG_OPTS="-O2 -target bpf -I ../include/" +CLANG_OPTS+=" -DSRC_MAC=$SRC_MAC -DDST_MAC=$DST_MAC -DDST_IFINDEX=$DST_IFINDEX" +clang $CLANG_OPTS -c test_lwt_bpf.c -o test_lwt_bpf.o + +test_ctx_xmit +test_ctx_out +test_ctx_in +test_data "xmit" +test_data "out" +test_data_in +test_cb "xmit" +test_cb "out" +test_cb_in +test_drop_all "xmit" +test_drop_all "out" +test_drop_all_in +test_rewrite +test_push_ll_and_redirect +test_no_l2_and_redirect +test_fill_garbage +test_netperf_nop +test_netperf_redirect + +cleanup +echo 0 > ${TRACE_ROOT}/tracing_on +exit 0 -- cgit v1.1 From 83a1a1a70e87f676fbb6086b26b6ac7f7fdd107d Mon Sep 17 00:00:00 2001 From: Soheil Hassas Yeganeh Date: Wed, 30 Nov 2016 14:01:08 -0500 Subject: sock: reset sk_err for ICMP packets read from error queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only when ICMP packets are enqueued onto the error queue, sk_err is also set. Before f5f99309fa74 (sock: do not set sk_err in sock_dequeue_err_skb), a subsequent error queue read would set sk_err to the next error on the queue, or 0 if empty. As no error types other than ICMP set this field, sk_err should not be modified upon dequeuing them. Only for ICMP errors, reset the (racy) sk_err. Some applications, like traceroute, rely on it and go into a futile busy POLLERR loop otherwise. In principle, sk_err has to be set while an ICMP error is queued. Testing is_icmp_err_skb(skb_next) approximates this without requiring a full queue walk. Applications that receive both ICMP and other errors cannot rely on this legacy behavior, as other errors do not set sk_err in the first place. Fixes: f5f99309fa74 (sock: do not set sk_err in sock_dequeue_err_skb) Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: Willem de Bruijn Acked-by: Eric Dumazet Acked-by: Maciej Å»enczykowski Signed-off-by: David S. Miller --- net/core/skbuff.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ea6fa95..b45cd14 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3714,20 +3714,29 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(sock_queue_err_skb); +static bool is_icmp_err_skb(const struct sk_buff *skb) +{ + return skb && (SKB_EXT_ERR(skb)->ee.ee_origin == SO_EE_ORIGIN_ICMP || + SKB_EXT_ERR(skb)->ee.ee_origin == SO_EE_ORIGIN_ICMP6); +} + struct sk_buff *sock_dequeue_err_skb(struct sock *sk) { struct sk_buff_head *q = &sk->sk_error_queue; - struct sk_buff *skb, *skb_next; + struct sk_buff *skb, *skb_next = NULL; + bool icmp_next = false; unsigned long flags; - int err = 0; spin_lock_irqsave(&q->lock, flags); skb = __skb_dequeue(q); if (skb && (skb_next = skb_peek(q))) - err = SKB_EXT_ERR(skb_next)->ee.ee_errno; + icmp_next = is_icmp_err_skb(skb_next); spin_unlock_irqrestore(&q->lock, flags); - if (err) + if (is_icmp_err_skb(skb) && !icmp_next) + sk->sk_err = 0; + + if (skb_next) sk->sk_error_report(sk); return skb; -- cgit v1.1 From 366cbf2f46048d70005c6c33dc289330f24b54b0 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 30 Nov 2016 22:16:06 +0100 Subject: bpf, xdp: drop rcu_read_lock from bpf_prog_run_xdp and move to caller After 326fe02d1ed6 ("net/mlx4_en: protect ring->xdp_prog with rcu_read_lock"), the rcu_read_lock() in bpf_prog_run_xdp() is superfluous, since callers need to hold rcu_read_lock() already to make sure BPF program doesn't get released in the background. Thus, drop it from bpf_prog_run_xdp(), as it can otherwise be misleading. Still keeping the bpf_prog_run_xdp() is useful as it allows for grepping in XDP supported drivers and to keep the typecheck on the context intact. For mlx4, this means we don't have a double rcu_read_lock() anymore. nfp can just make use of bpf_prog_run_xdp(), too. For qede, just move rcu_read_lock() out of the helper. When the driver gets atomic replace support, this will move to call-sites eventually. mlx5 needs actual fixing as it has the same issue as described already in 326fe02d1ed6 ("net/mlx4_en: protect ring->xdp_prog with rcu_read_lock"), that is, we're under RCU bh at this time, BPF programs are released via call_rcu(), and call_rcu() != call_rcu_bh(), so we need to properly mark read side as programs can get xchg()'ed in mlx5e_xdp_set() without queue reset. Fixes: 86994156c736 ("net/mlx5e: XDP fast RX drop bpf programs support") Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 8 ++++++-- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 2 +- drivers/net/ethernet/qlogic/qede/qede_main.c | 7 +++++++ include/linux/filter.h | 18 +++++++++--------- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index b036710..42cd687 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -737,10 +737,10 @@ static inline struct sk_buff *skb_from_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, u16 wqe_counter, u32 cqe_bcnt) { - struct bpf_prog *xdp_prog = READ_ONCE(rq->xdp_prog); struct mlx5e_dma_info *di; struct sk_buff *skb; void *va, *data; + bool consumed; di = &rq->dma_info[wqe_counter]; va = page_address(di->page); @@ -759,7 +759,11 @@ struct sk_buff *skb_from_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, return NULL; } - if (mlx5e_xdp_handle(rq, xdp_prog, di, data, cqe_bcnt)) + rcu_read_lock(); + consumed = mlx5e_xdp_handle(rq, READ_ONCE(rq->xdp_prog), di, data, + cqe_bcnt); + rcu_read_unlock(); + if (consumed) return NULL; /* page/packet was consumed by XDP */ skb = build_skb(va, RQ_PAGE_SIZE(rq)); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 876ab3a..00d9a03 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1518,7 +1518,7 @@ static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, unsigned int len) xdp.data = data; xdp.data_end = data + len; - return BPF_PROG_RUN(prog, &xdp); + return bpf_prog_run_xdp(prog, &xdp); } /** diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 172ff6d..faeaa9f 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1497,7 +1497,14 @@ static bool qede_rx_xdp(struct qede_dev *edev, xdp.data = page_address(bd->data) + cqe->placement_offset; xdp.data_end = xdp.data + len; + + /* Queues always have a full reset currently, so for the time + * being until there's atomic program replace just mark read + * side for map helpers. + */ + rcu_read_lock(); act = bpf_prog_run_xdp(prog, &xdp); + rcu_read_unlock(); if (act == XDP_PASS) return true; diff --git a/include/linux/filter.h b/include/linux/filter.h index 7ba6446..9733813 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -498,16 +498,16 @@ static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog, return BPF_PROG_RUN(prog, skb); } -static inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog, - struct xdp_buff *xdp) +static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog, + struct xdp_buff *xdp) { - u32 ret; - - rcu_read_lock(); - ret = BPF_PROG_RUN(prog, xdp); - rcu_read_unlock(); - - return ret; + /* Caller needs to hold rcu_read_lock() (!), otherwise program + * can be released while still running, or map elements could be + * freed early while still having concurrent users. XDP fastpath + * already takes rcu_read_lock() when fetching the program, so + * it's not necessary here anymore. + */ + return BPF_PROG_RUN(prog, xdp); } static inline unsigned int bpf_prog_size(unsigned int proglen) -- cgit v1.1 From d709b2a186761bc038c99c2f744c7218f2d249bd Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 30 Nov 2016 22:05:39 +0100 Subject: net/mlx5e: skip loopback selftest with !CONFIG_INET When CONFIG_INET is disabled, the new selftest results in a link error: drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.o: In function `mlx5e_test_loopback': en_selftest.c:(.text.mlx5e_test_loopback+0x2ec): undefined reference to `ip_send_check' en_selftest.c:(.text.mlx5e_test_loopback+0x34c): undefined reference to `udp4_hwcsum' This hides the specific test in that configuration. Fixes: 0952da791c97 ("net/mlx5e: Add support for loopback selftest") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c index c32af7d..65442c3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c @@ -39,7 +39,9 @@ enum { MLX5E_ST_LINK_STATE, MLX5E_ST_LINK_SPEED, MLX5E_ST_HEALTH_INFO, +#ifdef CONFIG_INET MLX5E_ST_LOOPBACK, +#endif MLX5E_ST_NUM, }; @@ -47,7 +49,9 @@ const char mlx5e_self_tests[MLX5E_ST_NUM][ETH_GSTRING_LEN] = { "Link Test", "Speed Test", "Health Test", +#ifdef CONFIG_INET "Loopback Test", +#endif }; int mlx5e_self_test_num(struct mlx5e_priv *priv) @@ -93,6 +97,7 @@ static int mlx5e_test_link_speed(struct mlx5e_priv *priv) return 1; } +#ifdef CONFIG_INET /* loopback test */ #define MLX5E_TEST_PKT_SIZE (MLX5_MPWRQ_SMALL_PACKET_THRESHOLD - NET_IP_ALIGN) static const char mlx5e_test_text[ETH_GSTRING_LEN] = "MLX5E SELF TEST"; @@ -304,12 +309,15 @@ out: kfree(lbtp); return err; } +#endif static int (*mlx5e_st_func[MLX5E_ST_NUM])(struct mlx5e_priv *) = { mlx5e_test_link_state, mlx5e_test_link_speed, mlx5e_test_health_info, - mlx5e_test_loopback +#ifdef CONFIG_INET + mlx5e_test_loopback, +#endif }; void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest, -- cgit v1.1 From b14945ac3efdf5217182a344b037f96d6b0afae1 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 30 Nov 2016 23:02:54 +0100 Subject: net: atarilance: use %8ph for printing hex string This is already using the %pM printf extension; might as well also use %ph to make the code smaller. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/atarilance.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c index e53ccc3..796c37a 100644 --- a/drivers/net/ethernet/amd/atarilance.c +++ b/drivers/net/ethernet/amd/atarilance.c @@ -1012,13 +1012,9 @@ static int lance_rx( struct net_device *dev ) u_char *data = PKTBUF_ADDR(head); printk(KERN_DEBUG "%s: RX pkt type 0x%04x from %pM to %pM " - "data %02x %02x %02x %02x %02x %02x %02x %02x " - "len %d\n", + "data %8ph len %d\n", dev->name, ((u_short *)data)[6], - &data[6], data, - data[15], data[16], data[17], data[18], - data[19], data[20], data[21], data[22], - pkt_len); + &data[6], data, &data[15], pkt_len); } skb_reserve( skb, 2 ); /* 16 byte align */ -- cgit v1.1 From fc831825f99eb3a2f1bf3fe7307b392513b642a5 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Thu, 1 Dec 2016 00:21:06 -0800 Subject: qed: Add support for hardware offloaded iSCSI. This adds the backbone required for the various HW initalizations which are necessary for the iSCSI driver (qedi) for QLogic FastLinQ 4xxxx line of adapters - FW notification, resource initializations, etc. Signed-off-by: Arun Easi Signed-off-by: Yuval Mintz Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/Kconfig | 3 + drivers/net/ethernet/qlogic/qed/Makefile | 1 + drivers/net/ethernet/qlogic/qed/qed.h | 7 +- drivers/net/ethernet/qlogic/qed/qed_dev.c | 12 + drivers/net/ethernet/qlogic/qed/qed_iscsi.c | 1277 ++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_iscsi.h | 52 + drivers/net/ethernet/qlogic/qed/qed_ll2.c | 4 +- drivers/net/ethernet/qlogic/qed/qed_reg_addr.h | 2 + drivers/net/ethernet/qlogic/qed/qed_spq.c | 15 + include/linux/qed/qed_if.h | 2 + include/linux/qed/qed_iscsi_if.h | 229 +++++ 11 files changed, 1602 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/qlogic/qed/qed_iscsi.c create mode 100644 drivers/net/ethernet/qlogic/qed/qed_iscsi.h create mode 100644 include/linux/qed/qed_iscsi_if.h diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig index 32f2a45..3cfd105 100644 --- a/drivers/net/ethernet/qlogic/Kconfig +++ b/drivers/net/ethernet/qlogic/Kconfig @@ -110,4 +110,7 @@ config QEDE config QED_RDMA bool +config QED_ISCSI + bool + endif # NET_VENDOR_QLOGIC diff --git a/drivers/net/ethernet/qlogic/qed/Makefile b/drivers/net/ethernet/qlogic/qed/Makefile index 967acf3..597e15c 100644 --- a/drivers/net/ethernet/qlogic/qed/Makefile +++ b/drivers/net/ethernet/qlogic/qed/Makefile @@ -6,3 +6,4 @@ qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \ qed-$(CONFIG_QED_SRIOV) += qed_sriov.o qed_vf.o qed-$(CONFIG_QED_LL2) += qed_ll2.o qed-$(CONFIG_QED_RDMA) += qed_roce.o +qed-$(CONFIG_QED_ISCSI) += qed_iscsi.o diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 244dd40..1f423b3 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -35,6 +35,7 @@ extern const struct qed_common_ops qed_common_ops_pass; #define QED_WFQ_UNIT 100 +#define ISCSI_BDQ_ID(_port_id) (_port_id) #define QED_WID_SIZE (1024) #define QED_PF_DEMS_SIZE (4) @@ -383,6 +384,7 @@ struct qed_hwfn { bool using_ll2; struct qed_ll2_info *p_ll2_info; struct qed_rdma_info *p_rdma_info; + struct qed_iscsi_info *p_iscsi_info; struct qed_pf_params pf_params; bool b_rdma_enabled_in_prs; @@ -581,6 +583,8 @@ struct qed_dev { /* Linux specific here */ struct qede_dev *edev; struct pci_dev *pdev; + u32 flags; +#define QED_FLAG_STORAGE_STARTED (BIT(0)) int msg_enable; struct pci_params pci_params; @@ -594,6 +598,7 @@ struct qed_dev { union { struct qed_common_cb_ops *common; struct qed_eth_cb_ops *eth; + struct qed_iscsi_cb_ops *iscsi; } protocol_ops; void *ops_cookie; @@ -603,7 +608,7 @@ struct qed_dev { struct qed_cb_ll2_info *ll2; u8 ll2_mac_address[ETH_ALEN]; #endif - + DECLARE_HASHTABLE(connections, 10); const struct firmware *firmware; u32 rdma_max_sge; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 00b9a67..cd98102 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -29,6 +29,7 @@ #include "qed_hw.h" #include "qed_init_ops.h" #include "qed_int.h" +#include "qed_iscsi.h" #include "qed_ll2.h" #include "qed_mcp.h" #include "qed_reg_addr.h" @@ -146,6 +147,8 @@ void qed_resc_free(struct qed_dev *cdev) #ifdef CONFIG_QED_LL2 qed_ll2_free(p_hwfn, p_hwfn->p_ll2_info); #endif + if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) + qed_iscsi_free(p_hwfn, p_hwfn->p_iscsi_info); qed_iov_free(p_hwfn); qed_dmae_info_free(p_hwfn); qed_dcbx_info_free(p_hwfn, p_hwfn->p_dcbx_info); @@ -402,6 +405,7 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) int qed_resc_alloc(struct qed_dev *cdev) { + struct qed_iscsi_info *p_iscsi_info; #ifdef CONFIG_QED_LL2 struct qed_ll2_info *p_ll2_info; #endif @@ -507,6 +511,12 @@ int qed_resc_alloc(struct qed_dev *cdev) p_hwfn->p_ll2_info = p_ll2_info; } #endif + if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) { + p_iscsi_info = qed_iscsi_alloc(p_hwfn); + if (!p_iscsi_info) + goto alloc_no_mem; + p_hwfn->p_iscsi_info = p_iscsi_info; + } /* DMA info initialization */ rc = qed_dmae_info_alloc(p_hwfn); @@ -560,6 +570,8 @@ void qed_resc_setup(struct qed_dev *cdev) if (p_hwfn->using_ll2) qed_ll2_setup(p_hwfn, p_hwfn->p_ll2_info); #endif + if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) + qed_iscsi_setup(p_hwfn, p_hwfn->p_iscsi_info); } } diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c new file mode 100644 index 0000000..00efb1c --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -0,0 +1,1277 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qed.h" +#include "qed_cxt.h" +#include "qed_dev_api.h" +#include "qed_hsi.h" +#include "qed_hw.h" +#include "qed_int.h" +#include "qed_iscsi.h" +#include "qed_ll2.h" +#include "qed_mcp.h" +#include "qed_sp.h" +#include "qed_sriov.h" +#include "qed_reg_addr.h" + +struct qed_iscsi_conn { + struct list_head list_entry; + bool free_on_delete; + + u16 conn_id; + u32 icid; + u32 fw_cid; + + u8 layer_code; + u8 offl_flags; + u8 connect_mode; + u32 initial_ack; + dma_addr_t sq_pbl_addr; + struct qed_chain r2tq; + struct qed_chain xhq; + struct qed_chain uhq; + + struct tcp_upload_params *tcp_upload_params_virt_addr; + dma_addr_t tcp_upload_params_phys_addr; + struct scsi_terminate_extra_params *queue_cnts_virt_addr; + dma_addr_t queue_cnts_phys_addr; + dma_addr_t syn_phy_addr; + + u16 syn_ip_payload_length; + u8 local_mac[6]; + u8 remote_mac[6]; + u16 vlan_id; + u8 tcp_flags; + u8 ip_version; + u32 remote_ip[4]; + u32 local_ip[4]; + u8 ka_max_probe_cnt; + u8 dup_ack_theshold; + u32 rcv_next; + u32 snd_una; + u32 snd_next; + u32 snd_max; + u32 snd_wnd; + u32 rcv_wnd; + u32 snd_wl1; + u32 cwnd; + u32 ss_thresh; + u16 srtt; + u16 rtt_var; + u32 ts_time; + u32 ts_recent; + u32 ts_recent_age; + u32 total_rt; + u32 ka_timeout_delta; + u32 rt_timeout_delta; + u8 dup_ack_cnt; + u8 snd_wnd_probe_cnt; + u8 ka_probe_cnt; + u8 rt_cnt; + u32 flow_label; + u32 ka_timeout; + u32 ka_interval; + u32 max_rt_time; + u32 initial_rcv_wnd; + u8 ttl; + u8 tos_or_tc; + u16 remote_port; + u16 local_port; + u16 mss; + u8 snd_wnd_scale; + u8 rcv_wnd_scale; + u32 ts_ticks_per_second; + u16 da_timeout_value; + u8 ack_frequency; + + u8 update_flag; + u8 default_cq; + u32 max_seq_size; + u32 max_recv_pdu_length; + u32 max_send_pdu_length; + u32 first_seq_length; + u32 exp_stat_sn; + u32 stat_sn; + u16 physical_q0; + u16 physical_q1; + u8 abortive_dsconnect; +}; + +static int +qed_sp_iscsi_func_start(struct qed_hwfn *p_hwfn, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_addr, + void *event_context, iscsi_event_cb_t async_event_cb) +{ + struct iscsi_init_ramrod_params *p_ramrod = NULL; + struct scsi_init_func_queues *p_queue = NULL; + struct qed_iscsi_pf_params *p_params = NULL; + struct iscsi_spe_func_init *p_init = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = 0; + u32 dval; + u16 val; + u8 i; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = qed_spq_get_cid(p_hwfn); + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + init_data.comp_mode = comp_mode; + init_data.p_comp_data = p_comp_addr; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ISCSI_RAMROD_CMD_ID_INIT_FUNC, + PROTOCOLID_ISCSI, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.iscsi_init; + p_init = &p_ramrod->iscsi_init_spe; + p_params = &p_hwfn->pf_params.iscsi_pf_params; + p_queue = &p_init->q_params; + + SET_FIELD(p_init->hdr.flags, + ISCSI_SLOW_PATH_HDR_LAYER_CODE, ISCSI_SLOW_PATH_LAYER_CODE); + p_init->hdr.op_code = ISCSI_RAMROD_CMD_ID_INIT_FUNC; + + val = p_params->half_way_close_timeout; + p_init->half_way_close_timeout = cpu_to_le16(val); + p_init->num_sq_pages_in_ring = p_params->num_sq_pages_in_ring; + p_init->num_r2tq_pages_in_ring = p_params->num_r2tq_pages_in_ring; + p_init->num_uhq_pages_in_ring = p_params->num_uhq_pages_in_ring; + p_init->func_params.log_page_size = p_params->log_page_size; + val = p_params->num_tasks; + p_init->func_params.num_tasks = cpu_to_le16(val); + p_init->debug_mode.flags = p_params->debug_mode; + + DMA_REGPAIR_LE(p_queue->glbl_q_params_addr, + p_params->glbl_q_params_addr); + + val = p_params->cq_num_entries; + p_queue->cq_num_entries = cpu_to_le16(val); + val = p_params->cmdq_num_entries; + p_queue->cmdq_num_entries = cpu_to_le16(val); + p_queue->num_queues = p_params->num_queues; + dval = (u8)p_hwfn->hw_info.resc_start[QED_CMDQS_CQS]; + p_queue->queue_relative_offset = (u8)dval; + p_queue->cq_sb_pi = p_params->gl_rq_pi; + p_queue->cmdq_sb_pi = p_params->gl_cmd_pi; + + for (i = 0; i < p_params->num_queues; i++) { + val = p_hwfn->sbs_info[i]->igu_sb_id; + p_queue->cq_cmdq_sb_num_arr[i] = cpu_to_le16(val); + } + + p_queue->bdq_resource_id = ISCSI_BDQ_ID(p_hwfn->port_id); + + DMA_REGPAIR_LE(p_queue->bdq_pbl_base_address[BDQ_ID_RQ], + p_params->bdq_pbl_base_addr[BDQ_ID_RQ]); + p_queue->bdq_pbl_num_entries[BDQ_ID_RQ] = + p_params->bdq_pbl_num_entries[BDQ_ID_RQ]; + val = p_params->bdq_xoff_threshold[BDQ_ID_RQ]; + p_queue->bdq_xoff_threshold[BDQ_ID_RQ] = cpu_to_le16(val); + val = p_params->bdq_xon_threshold[BDQ_ID_RQ]; + p_queue->bdq_xon_threshold[BDQ_ID_RQ] = cpu_to_le16(val); + + DMA_REGPAIR_LE(p_queue->bdq_pbl_base_address[BDQ_ID_IMM_DATA], + p_params->bdq_pbl_base_addr[BDQ_ID_IMM_DATA]); + p_queue->bdq_pbl_num_entries[BDQ_ID_IMM_DATA] = + p_params->bdq_pbl_num_entries[BDQ_ID_IMM_DATA]; + val = p_params->bdq_xoff_threshold[BDQ_ID_IMM_DATA]; + p_queue->bdq_xoff_threshold[BDQ_ID_IMM_DATA] = cpu_to_le16(val); + val = p_params->bdq_xon_threshold[BDQ_ID_IMM_DATA]; + p_queue->bdq_xon_threshold[BDQ_ID_IMM_DATA] = cpu_to_le16(val); + val = p_params->rq_buffer_size; + p_queue->rq_buffer_size = cpu_to_le16(val); + if (p_params->is_target) { + SET_FIELD(p_queue->q_validity, + SCSI_INIT_FUNC_QUEUES_RQ_VALID, 1); + if (p_queue->bdq_pbl_num_entries[BDQ_ID_IMM_DATA]) + SET_FIELD(p_queue->q_validity, + SCSI_INIT_FUNC_QUEUES_IMM_DATA_VALID, 1); + SET_FIELD(p_queue->q_validity, + SCSI_INIT_FUNC_QUEUES_CMD_VALID, 1); + } else { + SET_FIELD(p_queue->q_validity, + SCSI_INIT_FUNC_QUEUES_RQ_VALID, 1); + } + p_ramrod->tcp_init.two_msl_timer = cpu_to_le32(p_params->two_msl_timer); + val = p_params->tx_sws_timer; + p_ramrod->tcp_init.tx_sws_timer = cpu_to_le16(val); + p_ramrod->tcp_init.maxfinrt = p_params->max_fin_rt; + + p_hwfn->p_iscsi_info->event_context = event_context; + p_hwfn->p_iscsi_info->event_cb = async_event_cb; + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +static int qed_sp_iscsi_conn_offload(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn *p_conn, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_addr) +{ + struct iscsi_spe_conn_offload *p_ramrod = NULL; + struct tcp_offload_params_opt2 *p_tcp2 = NULL; + struct tcp_offload_params *p_tcp = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + union qed_qm_pq_params pq_params; + u16 pq0_id = 0, pq1_id = 0; + dma_addr_t r2tq_pbl_addr; + dma_addr_t xhq_pbl_addr; + dma_addr_t uhq_pbl_addr; + int rc = 0; + u32 dval; + u16 wval; + u8 i; + u16 *p; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = p_conn->icid; + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + init_data.comp_mode = comp_mode; + init_data.p_comp_data = p_comp_addr; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ISCSI_RAMROD_CMD_ID_OFFLOAD_CONN, + PROTOCOLID_ISCSI, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.iscsi_conn_offload; + + /* Transmission PQ is the first of the PF */ + memset(&pq_params, 0, sizeof(pq_params)); + pq0_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_ISCSI, &pq_params); + p_conn->physical_q0 = cpu_to_le16(pq0_id); + p_ramrod->iscsi.physical_q0 = cpu_to_le16(pq0_id); + + /* iSCSI Pure-ACK PQ */ + pq_params.iscsi.q_idx = 1; + pq1_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_ISCSI, &pq_params); + p_conn->physical_q1 = cpu_to_le16(pq1_id); + p_ramrod->iscsi.physical_q1 = cpu_to_le16(pq1_id); + + p_ramrod->hdr.op_code = ISCSI_RAMROD_CMD_ID_OFFLOAD_CONN; + SET_FIELD(p_ramrod->hdr.flags, ISCSI_SLOW_PATH_HDR_LAYER_CODE, + p_conn->layer_code); + + p_ramrod->conn_id = cpu_to_le16(p_conn->conn_id); + p_ramrod->fw_cid = cpu_to_le32(p_conn->icid); + + DMA_REGPAIR_LE(p_ramrod->iscsi.sq_pbl_addr, p_conn->sq_pbl_addr); + + r2tq_pbl_addr = qed_chain_get_pbl_phys(&p_conn->r2tq); + DMA_REGPAIR_LE(p_ramrod->iscsi.r2tq_pbl_addr, r2tq_pbl_addr); + + xhq_pbl_addr = qed_chain_get_pbl_phys(&p_conn->xhq); + DMA_REGPAIR_LE(p_ramrod->iscsi.xhq_pbl_addr, xhq_pbl_addr); + + uhq_pbl_addr = qed_chain_get_pbl_phys(&p_conn->uhq); + DMA_REGPAIR_LE(p_ramrod->iscsi.uhq_pbl_addr, uhq_pbl_addr); + + p_ramrod->iscsi.initial_ack = cpu_to_le32(p_conn->initial_ack); + p_ramrod->iscsi.flags = p_conn->offl_flags; + p_ramrod->iscsi.default_cq = p_conn->default_cq; + p_ramrod->iscsi.stat_sn = cpu_to_le32(p_conn->stat_sn); + + if (!GET_FIELD(p_ramrod->iscsi.flags, + ISCSI_CONN_OFFLOAD_PARAMS_TCP_ON_CHIP_1B)) { + p_tcp = &p_ramrod->tcp; + + p = (u16 *)p_conn->local_mac; + p_tcp->local_mac_addr_hi = swab16(get_unaligned(p)); + p_tcp->local_mac_addr_mid = swab16(get_unaligned(p + 1)); + p_tcp->local_mac_addr_lo = swab16(get_unaligned(p + 2)); + + p = (u16 *)p_conn->remote_mac; + p_tcp->remote_mac_addr_hi = swab16(get_unaligned(p)); + p_tcp->remote_mac_addr_mid = swab16(get_unaligned(p + 1)); + p_tcp->remote_mac_addr_lo = swab16(get_unaligned(p + 2)); + + p_tcp->vlan_id = cpu_to_le16(p_conn->vlan_id); + + p_tcp->flags = p_conn->tcp_flags; + p_tcp->ip_version = p_conn->ip_version; + for (i = 0; i < 4; i++) { + dval = p_conn->remote_ip[i]; + p_tcp->remote_ip[i] = cpu_to_le32(dval); + dval = p_conn->local_ip[i]; + p_tcp->local_ip[i] = cpu_to_le32(dval); + } + p_tcp->ka_max_probe_cnt = p_conn->ka_max_probe_cnt; + p_tcp->dup_ack_theshold = p_conn->dup_ack_theshold; + + p_tcp->rcv_next = cpu_to_le32(p_conn->rcv_next); + p_tcp->snd_una = cpu_to_le32(p_conn->snd_una); + p_tcp->snd_next = cpu_to_le32(p_conn->snd_next); + p_tcp->snd_max = cpu_to_le32(p_conn->snd_max); + p_tcp->snd_wnd = cpu_to_le32(p_conn->snd_wnd); + p_tcp->rcv_wnd = cpu_to_le32(p_conn->rcv_wnd); + p_tcp->snd_wl1 = cpu_to_le32(p_conn->snd_wl1); + p_tcp->cwnd = cpu_to_le32(p_conn->cwnd); + p_tcp->ss_thresh = cpu_to_le32(p_conn->ss_thresh); + p_tcp->srtt = cpu_to_le16(p_conn->srtt); + p_tcp->rtt_var = cpu_to_le16(p_conn->rtt_var); + p_tcp->ts_time = cpu_to_le32(p_conn->ts_time); + p_tcp->ts_recent = cpu_to_le32(p_conn->ts_recent); + p_tcp->ts_recent_age = cpu_to_le32(p_conn->ts_recent_age); + p_tcp->total_rt = cpu_to_le32(p_conn->total_rt); + dval = p_conn->ka_timeout_delta; + p_tcp->ka_timeout_delta = cpu_to_le32(dval); + dval = p_conn->rt_timeout_delta; + p_tcp->rt_timeout_delta = cpu_to_le32(dval); + p_tcp->dup_ack_cnt = p_conn->dup_ack_cnt; + p_tcp->snd_wnd_probe_cnt = p_conn->snd_wnd_probe_cnt; + p_tcp->ka_probe_cnt = p_conn->ka_probe_cnt; + p_tcp->rt_cnt = p_conn->rt_cnt; + p_tcp->flow_label = cpu_to_le32(p_conn->flow_label); + p_tcp->ka_timeout = cpu_to_le32(p_conn->ka_timeout); + p_tcp->ka_interval = cpu_to_le32(p_conn->ka_interval); + p_tcp->max_rt_time = cpu_to_le32(p_conn->max_rt_time); + dval = p_conn->initial_rcv_wnd; + p_tcp->initial_rcv_wnd = cpu_to_le32(dval); + p_tcp->ttl = p_conn->ttl; + p_tcp->tos_or_tc = p_conn->tos_or_tc; + p_tcp->remote_port = cpu_to_le16(p_conn->remote_port); + p_tcp->local_port = cpu_to_le16(p_conn->local_port); + p_tcp->mss = cpu_to_le16(p_conn->mss); + p_tcp->snd_wnd_scale = p_conn->snd_wnd_scale; + p_tcp->rcv_wnd_scale = p_conn->rcv_wnd_scale; + dval = p_conn->ts_ticks_per_second; + p_tcp->ts_ticks_per_second = cpu_to_le32(dval); + wval = p_conn->da_timeout_value; + p_tcp->da_timeout_value = cpu_to_le16(wval); + p_tcp->ack_frequency = p_conn->ack_frequency; + p_tcp->connect_mode = p_conn->connect_mode; + } else { + p_tcp2 = + &((struct iscsi_spe_conn_offload_option2 *)p_ramrod)->tcp; + + p = (u16 *)p_conn->local_mac; + p_tcp2->local_mac_addr_hi = swab16(get_unaligned(p)); + p_tcp2->local_mac_addr_mid = swab16(get_unaligned(p + 1)); + p_tcp2->local_mac_addr_lo = swab16(get_unaligned(p + 2)); + + p = (u16 *)p_conn->remote_mac; + p_tcp2->remote_mac_addr_hi = swab16(get_unaligned(p)); + p_tcp2->remote_mac_addr_mid = swab16(get_unaligned(p + 1)); + p_tcp2->remote_mac_addr_lo = swab16(get_unaligned(p + 2)); + + p_tcp2->vlan_id = cpu_to_le16(p_conn->vlan_id); + p_tcp2->flags = p_conn->tcp_flags; + + p_tcp2->ip_version = p_conn->ip_version; + for (i = 0; i < 4; i++) { + dval = p_conn->remote_ip[i]; + p_tcp2->remote_ip[i] = cpu_to_le32(dval); + dval = p_conn->local_ip[i]; + p_tcp2->local_ip[i] = cpu_to_le32(dval); + } + + p_tcp2->flow_label = cpu_to_le32(p_conn->flow_label); + p_tcp2->ttl = p_conn->ttl; + p_tcp2->tos_or_tc = p_conn->tos_or_tc; + p_tcp2->remote_port = cpu_to_le16(p_conn->remote_port); + p_tcp2->local_port = cpu_to_le16(p_conn->local_port); + p_tcp2->mss = cpu_to_le16(p_conn->mss); + p_tcp2->rcv_wnd_scale = p_conn->rcv_wnd_scale; + p_tcp2->connect_mode = p_conn->connect_mode; + wval = p_conn->syn_ip_payload_length; + p_tcp2->syn_ip_payload_length = cpu_to_le16(wval); + p_tcp2->syn_phy_addr_lo = DMA_LO_LE(p_conn->syn_phy_addr); + p_tcp2->syn_phy_addr_hi = DMA_HI_LE(p_conn->syn_phy_addr); + } + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +static int qed_sp_iscsi_conn_update(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn *p_conn, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_addr) +{ + struct iscsi_conn_update_ramrod_params *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = -EINVAL; + u32 dval; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = p_conn->icid; + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + init_data.comp_mode = comp_mode; + init_data.p_comp_data = p_comp_addr; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ISCSI_RAMROD_CMD_ID_UPDATE_CONN, + PROTOCOLID_ISCSI, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.iscsi_conn_update; + p_ramrod->hdr.op_code = ISCSI_RAMROD_CMD_ID_UPDATE_CONN; + SET_FIELD(p_ramrod->hdr.flags, + ISCSI_SLOW_PATH_HDR_LAYER_CODE, p_conn->layer_code); + + p_ramrod->conn_id = cpu_to_le16(p_conn->conn_id); + p_ramrod->fw_cid = cpu_to_le32(p_conn->icid); + p_ramrod->flags = p_conn->update_flag; + p_ramrod->max_seq_size = cpu_to_le32(p_conn->max_seq_size); + dval = p_conn->max_recv_pdu_length; + p_ramrod->max_recv_pdu_length = cpu_to_le32(dval); + dval = p_conn->max_send_pdu_length; + p_ramrod->max_send_pdu_length = cpu_to_le32(dval); + dval = p_conn->first_seq_length; + p_ramrod->first_seq_length = cpu_to_le32(dval); + p_ramrod->exp_stat_sn = cpu_to_le32(p_conn->exp_stat_sn); + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +static int qed_sp_iscsi_conn_terminate(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn *p_conn, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_addr) +{ + struct iscsi_spe_conn_termination *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = -EINVAL; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = p_conn->icid; + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + init_data.comp_mode = comp_mode; + init_data.p_comp_data = p_comp_addr; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ISCSI_RAMROD_CMD_ID_TERMINATION_CONN, + PROTOCOLID_ISCSI, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.iscsi_conn_terminate; + p_ramrod->hdr.op_code = ISCSI_RAMROD_CMD_ID_TERMINATION_CONN; + SET_FIELD(p_ramrod->hdr.flags, + ISCSI_SLOW_PATH_HDR_LAYER_CODE, p_conn->layer_code); + + p_ramrod->conn_id = cpu_to_le16(p_conn->conn_id); + p_ramrod->fw_cid = cpu_to_le32(p_conn->icid); + p_ramrod->abortive = p_conn->abortive_dsconnect; + + DMA_REGPAIR_LE(p_ramrod->query_params_addr, + p_conn->tcp_upload_params_phys_addr); + DMA_REGPAIR_LE(p_ramrod->queue_cnts_addr, p_conn->queue_cnts_phys_addr); + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +static int qed_sp_iscsi_conn_clear_sq(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn *p_conn, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_addr) +{ + struct iscsi_slow_path_hdr *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = -EINVAL; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = p_conn->icid; + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + init_data.comp_mode = comp_mode; + init_data.p_comp_data = p_comp_addr; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ISCSI_RAMROD_CMD_ID_CLEAR_SQ, + PROTOCOLID_ISCSI, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.iscsi_empty; + p_ramrod->op_code = ISCSI_RAMROD_CMD_ID_CLEAR_SQ; + SET_FIELD(p_ramrod->flags, + ISCSI_SLOW_PATH_HDR_LAYER_CODE, p_conn->layer_code); + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +static int qed_sp_iscsi_func_stop(struct qed_hwfn *p_hwfn, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_addr) +{ + struct iscsi_spe_func_dstry *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = 0; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = qed_spq_get_cid(p_hwfn); + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + init_data.comp_mode = comp_mode; + init_data.p_comp_data = p_comp_addr; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ISCSI_RAMROD_CMD_ID_DESTROY_FUNC, + PROTOCOLID_ISCSI, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.iscsi_destroy; + p_ramrod->hdr.op_code = ISCSI_RAMROD_CMD_ID_DESTROY_FUNC; + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +static void __iomem *qed_iscsi_get_db_addr(struct qed_hwfn *p_hwfn, u32 cid) +{ + return (u8 __iomem *)p_hwfn->doorbells + + qed_db_addr(cid, DQ_DEMS_LEGACY); +} + +static void __iomem *qed_iscsi_get_primary_bdq_prod(struct qed_hwfn *p_hwfn, + u8 bdq_id) +{ + u8 bdq_function_id = ISCSI_BDQ_ID(p_hwfn->port_id); + + return (u8 __iomem *)p_hwfn->regview + GTT_BAR0_MAP_REG_MSDM_RAM + + MSTORM_SCSI_BDQ_EXT_PROD_OFFSET(bdq_function_id, + bdq_id); +} + +static void __iomem *qed_iscsi_get_secondary_bdq_prod(struct qed_hwfn *p_hwfn, + u8 bdq_id) +{ + u8 bdq_function_id = ISCSI_BDQ_ID(p_hwfn->port_id); + + return (u8 __iomem *)p_hwfn->regview + GTT_BAR0_MAP_REG_TSDM_RAM + + TSTORM_SCSI_BDQ_EXT_PROD_OFFSET(bdq_function_id, + bdq_id); +} + +static int qed_iscsi_setup_connection(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn *p_conn) +{ + if (!p_conn->queue_cnts_virt_addr) + goto nomem; + memset(p_conn->queue_cnts_virt_addr, 0, + sizeof(*p_conn->queue_cnts_virt_addr)); + + if (!p_conn->tcp_upload_params_virt_addr) + goto nomem; + memset(p_conn->tcp_upload_params_virt_addr, 0, + sizeof(*p_conn->tcp_upload_params_virt_addr)); + + if (!p_conn->r2tq.p_virt_addr) + goto nomem; + qed_chain_pbl_zero_mem(&p_conn->r2tq); + + if (!p_conn->uhq.p_virt_addr) + goto nomem; + qed_chain_pbl_zero_mem(&p_conn->uhq); + + if (!p_conn->xhq.p_virt_addr) + goto nomem; + qed_chain_pbl_zero_mem(&p_conn->xhq); + + return 0; +nomem: + return -ENOMEM; +} + +static int qed_iscsi_allocate_connection(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn **p_out_conn) +{ + u16 uhq_num_elements = 0, xhq_num_elements = 0, r2tq_num_elements = 0; + struct scsi_terminate_extra_params *p_q_cnts = NULL; + struct qed_iscsi_pf_params *p_params = NULL; + struct tcp_upload_params *p_tcp = NULL; + struct qed_iscsi_conn *p_conn = NULL; + int rc = 0; + + /* Try finding a free connection that can be used */ + spin_lock_bh(&p_hwfn->p_iscsi_info->lock); + if (!list_empty(&p_hwfn->p_iscsi_info->free_list)) + p_conn = list_first_entry(&p_hwfn->p_iscsi_info->free_list, + struct qed_iscsi_conn, list_entry); + if (p_conn) { + list_del(&p_conn->list_entry); + spin_unlock_bh(&p_hwfn->p_iscsi_info->lock); + *p_out_conn = p_conn; + return 0; + } + spin_unlock_bh(&p_hwfn->p_iscsi_info->lock); + + /* Need to allocate a new connection */ + p_params = &p_hwfn->pf_params.iscsi_pf_params; + + p_conn = kzalloc(sizeof(*p_conn), GFP_KERNEL); + if (!p_conn) + return -ENOMEM; + + p_q_cnts = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(*p_q_cnts), + &p_conn->queue_cnts_phys_addr, + GFP_KERNEL); + if (!p_q_cnts) + goto nomem_queue_cnts_param; + p_conn->queue_cnts_virt_addr = p_q_cnts; + + p_tcp = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(*p_tcp), + &p_conn->tcp_upload_params_phys_addr, + GFP_KERNEL); + if (!p_tcp) + goto nomem_upload_param; + p_conn->tcp_upload_params_virt_addr = p_tcp; + + r2tq_num_elements = p_params->num_r2tq_pages_in_ring * + QED_CHAIN_PAGE_SIZE / 0x80; + rc = qed_chain_alloc(p_hwfn->cdev, + QED_CHAIN_USE_TO_CONSUME_PRODUCE, + QED_CHAIN_MODE_PBL, + QED_CHAIN_CNT_TYPE_U16, + r2tq_num_elements, 0x80, &p_conn->r2tq); + if (rc) + goto nomem_r2tq; + + uhq_num_elements = p_params->num_uhq_pages_in_ring * + QED_CHAIN_PAGE_SIZE / sizeof(struct iscsi_uhqe); + rc = qed_chain_alloc(p_hwfn->cdev, + QED_CHAIN_USE_TO_CONSUME_PRODUCE, + QED_CHAIN_MODE_PBL, + QED_CHAIN_CNT_TYPE_U16, + uhq_num_elements, + sizeof(struct iscsi_uhqe), &p_conn->uhq); + if (rc) + goto nomem_uhq; + + xhq_num_elements = uhq_num_elements; + rc = qed_chain_alloc(p_hwfn->cdev, + QED_CHAIN_USE_TO_CONSUME_PRODUCE, + QED_CHAIN_MODE_PBL, + QED_CHAIN_CNT_TYPE_U16, + xhq_num_elements, + sizeof(struct iscsi_xhqe), &p_conn->xhq); + if (rc) + goto nomem; + + p_conn->free_on_delete = true; + *p_out_conn = p_conn; + return 0; + +nomem: + qed_chain_free(p_hwfn->cdev, &p_conn->uhq); +nomem_uhq: + qed_chain_free(p_hwfn->cdev, &p_conn->r2tq); +nomem_r2tq: + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(struct tcp_upload_params), + p_conn->tcp_upload_params_virt_addr, + p_conn->tcp_upload_params_phys_addr); +nomem_upload_param: + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(struct scsi_terminate_extra_params), + p_conn->queue_cnts_virt_addr, + p_conn->queue_cnts_phys_addr); +nomem_queue_cnts_param: + kfree(p_conn); + + return -ENOMEM; +} + +static int qed_iscsi_acquire_connection(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn *p_in_conn, + struct qed_iscsi_conn **p_out_conn) +{ + struct qed_iscsi_conn *p_conn = NULL; + int rc = 0; + u32 icid; + + spin_lock_bh(&p_hwfn->p_iscsi_info->lock); + rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_ISCSI, &icid); + spin_unlock_bh(&p_hwfn->p_iscsi_info->lock); + if (rc) + return rc; + + /* Use input connection or allocate a new one */ + if (p_in_conn) + p_conn = p_in_conn; + else + rc = qed_iscsi_allocate_connection(p_hwfn, &p_conn); + + if (!rc) + rc = qed_iscsi_setup_connection(p_hwfn, p_conn); + + if (rc) { + spin_lock_bh(&p_hwfn->p_iscsi_info->lock); + qed_cxt_release_cid(p_hwfn, icid); + spin_unlock_bh(&p_hwfn->p_iscsi_info->lock); + return rc; + } + + p_conn->icid = icid; + p_conn->conn_id = (u16)icid; + p_conn->fw_cid = (p_hwfn->hw_info.opaque_fid << 16) | icid; + + *p_out_conn = p_conn; + + return rc; +} + +static void qed_iscsi_release_connection(struct qed_hwfn *p_hwfn, + struct qed_iscsi_conn *p_conn) +{ + spin_lock_bh(&p_hwfn->p_iscsi_info->lock); + list_add_tail(&p_conn->list_entry, &p_hwfn->p_iscsi_info->free_list); + qed_cxt_release_cid(p_hwfn, p_conn->icid); + spin_unlock_bh(&p_hwfn->p_iscsi_info->lock); +} + +struct qed_iscsi_info *qed_iscsi_alloc(struct qed_hwfn *p_hwfn) +{ + struct qed_iscsi_info *p_iscsi_info; + + p_iscsi_info = kzalloc(sizeof(*p_iscsi_info), GFP_KERNEL); + if (!p_iscsi_info) + return NULL; + + INIT_LIST_HEAD(&p_iscsi_info->free_list); + return p_iscsi_info; +} + +void qed_iscsi_setup(struct qed_hwfn *p_hwfn, + struct qed_iscsi_info *p_iscsi_info) +{ + spin_lock_init(&p_iscsi_info->lock); +} + +void qed_iscsi_free(struct qed_hwfn *p_hwfn, + struct qed_iscsi_info *p_iscsi_info) +{ + kfree(p_iscsi_info); +} + +static void _qed_iscsi_get_tstats(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_iscsi_stats *p_stats) +{ + struct tstorm_iscsi_stats_drv tstats; + u32 tstats_addr; + + memset(&tstats, 0, sizeof(tstats)); + tstats_addr = BAR0_MAP_REG_TSDM_RAM + + TSTORM_ISCSI_RX_STATS_OFFSET(p_hwfn->rel_pf_id); + qed_memcpy_from(p_hwfn, p_ptt, &tstats, tstats_addr, sizeof(tstats)); + + p_stats->iscsi_rx_bytes_cnt = + HILO_64_REGPAIR(tstats.iscsi_rx_bytes_cnt); + p_stats->iscsi_rx_packet_cnt = + HILO_64_REGPAIR(tstats.iscsi_rx_packet_cnt); + p_stats->iscsi_cmdq_threshold_cnt = + le32_to_cpu(tstats.iscsi_cmdq_threshold_cnt); + p_stats->iscsi_rq_threshold_cnt = + le32_to_cpu(tstats.iscsi_rq_threshold_cnt); + p_stats->iscsi_immq_threshold_cnt = + le32_to_cpu(tstats.iscsi_immq_threshold_cnt); +} + +static void _qed_iscsi_get_mstats(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_iscsi_stats *p_stats) +{ + struct mstorm_iscsi_stats_drv mstats; + u32 mstats_addr; + + memset(&mstats, 0, sizeof(mstats)); + mstats_addr = BAR0_MAP_REG_MSDM_RAM + + MSTORM_ISCSI_RX_STATS_OFFSET(p_hwfn->rel_pf_id); + qed_memcpy_from(p_hwfn, p_ptt, &mstats, mstats_addr, sizeof(mstats)); + + p_stats->iscsi_rx_dropped_pdus_task_not_valid = + HILO_64_REGPAIR(mstats.iscsi_rx_dropped_pdus_task_not_valid); +} + +static void _qed_iscsi_get_ustats(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_iscsi_stats *p_stats) +{ + struct ustorm_iscsi_stats_drv ustats; + u32 ustats_addr; + + memset(&ustats, 0, sizeof(ustats)); + ustats_addr = BAR0_MAP_REG_USDM_RAM + + USTORM_ISCSI_RX_STATS_OFFSET(p_hwfn->rel_pf_id); + qed_memcpy_from(p_hwfn, p_ptt, &ustats, ustats_addr, sizeof(ustats)); + + p_stats->iscsi_rx_data_pdu_cnt = + HILO_64_REGPAIR(ustats.iscsi_rx_data_pdu_cnt); + p_stats->iscsi_rx_r2t_pdu_cnt = + HILO_64_REGPAIR(ustats.iscsi_rx_r2t_pdu_cnt); + p_stats->iscsi_rx_total_pdu_cnt = + HILO_64_REGPAIR(ustats.iscsi_rx_total_pdu_cnt); +} + +static void _qed_iscsi_get_xstats(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_iscsi_stats *p_stats) +{ + struct xstorm_iscsi_stats_drv xstats; + u32 xstats_addr; + + memset(&xstats, 0, sizeof(xstats)); + xstats_addr = BAR0_MAP_REG_XSDM_RAM + + XSTORM_ISCSI_TX_STATS_OFFSET(p_hwfn->rel_pf_id); + qed_memcpy_from(p_hwfn, p_ptt, &xstats, xstats_addr, sizeof(xstats)); + + p_stats->iscsi_tx_go_to_slow_start_event_cnt = + HILO_64_REGPAIR(xstats.iscsi_tx_go_to_slow_start_event_cnt); + p_stats->iscsi_tx_fast_retransmit_event_cnt = + HILO_64_REGPAIR(xstats.iscsi_tx_fast_retransmit_event_cnt); +} + +static void _qed_iscsi_get_ystats(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_iscsi_stats *p_stats) +{ + struct ystorm_iscsi_stats_drv ystats; + u32 ystats_addr; + + memset(&ystats, 0, sizeof(ystats)); + ystats_addr = BAR0_MAP_REG_YSDM_RAM + + YSTORM_ISCSI_TX_STATS_OFFSET(p_hwfn->rel_pf_id); + qed_memcpy_from(p_hwfn, p_ptt, &ystats, ystats_addr, sizeof(ystats)); + + p_stats->iscsi_tx_data_pdu_cnt = + HILO_64_REGPAIR(ystats.iscsi_tx_data_pdu_cnt); + p_stats->iscsi_tx_r2t_pdu_cnt = + HILO_64_REGPAIR(ystats.iscsi_tx_r2t_pdu_cnt); + p_stats->iscsi_tx_total_pdu_cnt = + HILO_64_REGPAIR(ystats.iscsi_tx_total_pdu_cnt); +} + +static void _qed_iscsi_get_pstats(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_iscsi_stats *p_stats) +{ + struct pstorm_iscsi_stats_drv pstats; + u32 pstats_addr; + + memset(&pstats, 0, sizeof(pstats)); + pstats_addr = BAR0_MAP_REG_PSDM_RAM + + PSTORM_ISCSI_TX_STATS_OFFSET(p_hwfn->rel_pf_id); + qed_memcpy_from(p_hwfn, p_ptt, &pstats, pstats_addr, sizeof(pstats)); + + p_stats->iscsi_tx_bytes_cnt = + HILO_64_REGPAIR(pstats.iscsi_tx_bytes_cnt); + p_stats->iscsi_tx_packet_cnt = + HILO_64_REGPAIR(pstats.iscsi_tx_packet_cnt); +} + +static int qed_iscsi_get_stats(struct qed_hwfn *p_hwfn, + struct qed_iscsi_stats *stats) +{ + struct qed_ptt *p_ptt; + + memset(stats, 0, sizeof(*stats)); + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) { + DP_ERR(p_hwfn, "Failed to acquire ptt\n"); + return -EAGAIN; + } + + _qed_iscsi_get_tstats(p_hwfn, p_ptt, stats); + _qed_iscsi_get_mstats(p_hwfn, p_ptt, stats); + _qed_iscsi_get_ustats(p_hwfn, p_ptt, stats); + + _qed_iscsi_get_xstats(p_hwfn, p_ptt, stats); + _qed_iscsi_get_ystats(p_hwfn, p_ptt, stats); + _qed_iscsi_get_pstats(p_hwfn, p_ptt, stats); + + qed_ptt_release(p_hwfn, p_ptt); + + return 0; +} + +struct qed_hash_iscsi_con { + struct hlist_node node; + struct qed_iscsi_conn *con; +}; + +static int qed_fill_iscsi_dev_info(struct qed_dev *cdev, + struct qed_dev_iscsi_info *info) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + + int rc; + + memset(info, 0, sizeof(*info)); + rc = qed_fill_dev_info(cdev, &info->common); + + info->primary_dbq_rq_addr = + qed_iscsi_get_primary_bdq_prod(hwfn, BDQ_ID_RQ); + info->secondary_bdq_rq_addr = + qed_iscsi_get_secondary_bdq_prod(hwfn, BDQ_ID_RQ); + + return rc; +} + +static void qed_register_iscsi_ops(struct qed_dev *cdev, + struct qed_iscsi_cb_ops *ops, void *cookie) +{ + cdev->protocol_ops.iscsi = ops; + cdev->ops_cookie = cookie; +} + +static struct qed_hash_iscsi_con *qed_iscsi_get_hash(struct qed_dev *cdev, + u32 handle) +{ + struct qed_hash_iscsi_con *hash_con = NULL; + + if (!(cdev->flags & QED_FLAG_STORAGE_STARTED)) + return NULL; + + hash_for_each_possible(cdev->connections, hash_con, node, handle) { + if (hash_con->con->icid == handle) + break; + } + + if (!hash_con || (hash_con->con->icid != handle)) + return NULL; + + return hash_con; +} + +static int qed_iscsi_stop(struct qed_dev *cdev) +{ + int rc; + + if (!(cdev->flags & QED_FLAG_STORAGE_STARTED)) { + DP_NOTICE(cdev, "iscsi already stopped\n"); + return 0; + } + + if (!hash_empty(cdev->connections)) { + DP_NOTICE(cdev, + "Can't stop iscsi - not all connections were returned\n"); + return -EINVAL; + } + + /* Stop the iscsi */ + rc = qed_sp_iscsi_func_stop(QED_LEADING_HWFN(cdev), + QED_SPQ_MODE_EBLOCK, NULL); + cdev->flags &= ~QED_FLAG_STORAGE_STARTED; + + return rc; +} + +static int qed_iscsi_start(struct qed_dev *cdev, + struct qed_iscsi_tid *tasks, + void *event_context, + iscsi_event_cb_t async_event_cb) +{ + int rc; + struct qed_tid_mem *tid_info; + + if (cdev->flags & QED_FLAG_STORAGE_STARTED) { + DP_NOTICE(cdev, "iscsi already started;\n"); + return 0; + } + + rc = qed_sp_iscsi_func_start(QED_LEADING_HWFN(cdev), + QED_SPQ_MODE_EBLOCK, NULL, event_context, + async_event_cb); + if (rc) { + DP_NOTICE(cdev, "Failed to start iscsi\n"); + return rc; + } + + cdev->flags |= QED_FLAG_STORAGE_STARTED; + hash_init(cdev->connections); + + if (!tasks) + return 0; + + tid_info = kzalloc(sizeof(*tid_info), GFP_KERNEL); + + if (!tid_info) { + qed_iscsi_stop(cdev); + return -ENOMEM; + } + + rc = qed_cxt_get_tid_mem_info(QED_LEADING_HWFN(cdev), + tid_info); + if (rc) { + DP_NOTICE(cdev, "Failed to gather task information\n"); + qed_iscsi_stop(cdev); + kfree(tid_info); + return rc; + } + + /* Fill task information */ + tasks->size = tid_info->tid_size; + tasks->num_tids_per_block = tid_info->num_tids_per_block; + memcpy(tasks->blocks, tid_info->blocks, + MAX_TID_BLOCKS_ISCSI * sizeof(u8 *)); + + kfree(tid_info); + + return 0; +} + +static int qed_iscsi_acquire_conn(struct qed_dev *cdev, + u32 *handle, + u32 *fw_cid, void __iomem **p_doorbell) +{ + struct qed_hash_iscsi_con *hash_con; + int rc; + + /* Allocate a hashed connection */ + hash_con = kzalloc(sizeof(*hash_con), GFP_ATOMIC); + if (!hash_con) + return -ENOMEM; + + /* Acquire the connection */ + rc = qed_iscsi_acquire_connection(QED_LEADING_HWFN(cdev), NULL, + &hash_con->con); + if (rc) { + DP_NOTICE(cdev, "Failed to acquire Connection\n"); + kfree(hash_con); + return rc; + } + + /* Added the connection to hash table */ + *handle = hash_con->con->icid; + *fw_cid = hash_con->con->fw_cid; + hash_add(cdev->connections, &hash_con->node, *handle); + + if (p_doorbell) + *p_doorbell = qed_iscsi_get_db_addr(QED_LEADING_HWFN(cdev), + *handle); + + return 0; +} + +static int qed_iscsi_release_conn(struct qed_dev *cdev, u32 handle) +{ + struct qed_hash_iscsi_con *hash_con; + + hash_con = qed_iscsi_get_hash(cdev, handle); + if (!hash_con) { + DP_NOTICE(cdev, "Failed to find connection for handle %d\n", + handle); + return -EINVAL; + } + + hlist_del(&hash_con->node); + qed_iscsi_release_connection(QED_LEADING_HWFN(cdev), hash_con->con); + kfree(hash_con); + + return 0; +} + +static int qed_iscsi_offload_conn(struct qed_dev *cdev, + u32 handle, + struct qed_iscsi_params_offload *conn_info) +{ + struct qed_hash_iscsi_con *hash_con; + struct qed_iscsi_conn *con; + + hash_con = qed_iscsi_get_hash(cdev, handle); + if (!hash_con) { + DP_NOTICE(cdev, "Failed to find connection for handle %d\n", + handle); + return -EINVAL; + } + + /* Update the connection with information from the params */ + con = hash_con->con; + + ether_addr_copy(con->local_mac, conn_info->src.mac); + ether_addr_copy(con->remote_mac, conn_info->dst.mac); + memcpy(con->local_ip, conn_info->src.ip, sizeof(con->local_ip)); + memcpy(con->remote_ip, conn_info->dst.ip, sizeof(con->remote_ip)); + con->local_port = conn_info->src.port; + con->remote_port = conn_info->dst.port; + + con->layer_code = conn_info->layer_code; + con->sq_pbl_addr = conn_info->sq_pbl_addr; + con->initial_ack = conn_info->initial_ack; + con->vlan_id = conn_info->vlan_id; + con->tcp_flags = conn_info->tcp_flags; + con->ip_version = conn_info->ip_version; + con->default_cq = conn_info->default_cq; + con->ka_max_probe_cnt = conn_info->ka_max_probe_cnt; + con->dup_ack_theshold = conn_info->dup_ack_theshold; + con->rcv_next = conn_info->rcv_next; + con->snd_una = conn_info->snd_una; + con->snd_next = conn_info->snd_next; + con->snd_max = conn_info->snd_max; + con->snd_wnd = conn_info->snd_wnd; + con->rcv_wnd = conn_info->rcv_wnd; + con->snd_wl1 = conn_info->snd_wl1; + con->cwnd = conn_info->cwnd; + con->ss_thresh = conn_info->ss_thresh; + con->srtt = conn_info->srtt; + con->rtt_var = conn_info->rtt_var; + con->ts_time = conn_info->ts_time; + con->ts_recent = conn_info->ts_recent; + con->ts_recent_age = conn_info->ts_recent_age; + con->total_rt = conn_info->total_rt; + con->ka_timeout_delta = conn_info->ka_timeout_delta; + con->rt_timeout_delta = conn_info->rt_timeout_delta; + con->dup_ack_cnt = conn_info->dup_ack_cnt; + con->snd_wnd_probe_cnt = conn_info->snd_wnd_probe_cnt; + con->ka_probe_cnt = conn_info->ka_probe_cnt; + con->rt_cnt = conn_info->rt_cnt; + con->flow_label = conn_info->flow_label; + con->ka_timeout = conn_info->ka_timeout; + con->ka_interval = conn_info->ka_interval; + con->max_rt_time = conn_info->max_rt_time; + con->initial_rcv_wnd = conn_info->initial_rcv_wnd; + con->ttl = conn_info->ttl; + con->tos_or_tc = conn_info->tos_or_tc; + con->remote_port = conn_info->remote_port; + con->local_port = conn_info->local_port; + con->mss = conn_info->mss; + con->snd_wnd_scale = conn_info->snd_wnd_scale; + con->rcv_wnd_scale = conn_info->rcv_wnd_scale; + con->ts_ticks_per_second = conn_info->ts_ticks_per_second; + con->da_timeout_value = conn_info->da_timeout_value; + con->ack_frequency = conn_info->ack_frequency; + + /* Set default values on other connection fields */ + con->offl_flags = 0x1; + + return qed_sp_iscsi_conn_offload(QED_LEADING_HWFN(cdev), con, + QED_SPQ_MODE_EBLOCK, NULL); +} + +static int qed_iscsi_update_conn(struct qed_dev *cdev, + u32 handle, + struct qed_iscsi_params_update *conn_info) +{ + struct qed_hash_iscsi_con *hash_con; + struct qed_iscsi_conn *con; + + hash_con = qed_iscsi_get_hash(cdev, handle); + if (!hash_con) { + DP_NOTICE(cdev, "Failed to find connection for handle %d\n", + handle); + return -EINVAL; + } + + /* Update the connection with information from the params */ + con = hash_con->con; + con->update_flag = conn_info->update_flag; + con->max_seq_size = conn_info->max_seq_size; + con->max_recv_pdu_length = conn_info->max_recv_pdu_length; + con->max_send_pdu_length = conn_info->max_send_pdu_length; + con->first_seq_length = conn_info->first_seq_length; + con->exp_stat_sn = conn_info->exp_stat_sn; + + return qed_sp_iscsi_conn_update(QED_LEADING_HWFN(cdev), con, + QED_SPQ_MODE_EBLOCK, NULL); +} + +static int qed_iscsi_clear_conn_sq(struct qed_dev *cdev, u32 handle) +{ + struct qed_hash_iscsi_con *hash_con; + + hash_con = qed_iscsi_get_hash(cdev, handle); + if (!hash_con) { + DP_NOTICE(cdev, "Failed to find connection for handle %d\n", + handle); + return -EINVAL; + } + + return qed_sp_iscsi_conn_clear_sq(QED_LEADING_HWFN(cdev), + hash_con->con, + QED_SPQ_MODE_EBLOCK, NULL); +} + +static int qed_iscsi_destroy_conn(struct qed_dev *cdev, + u32 handle, u8 abrt_conn) +{ + struct qed_hash_iscsi_con *hash_con; + + hash_con = qed_iscsi_get_hash(cdev, handle); + if (!hash_con) { + DP_NOTICE(cdev, "Failed to find connection for handle %d\n", + handle); + return -EINVAL; + } + + hash_con->con->abortive_dsconnect = abrt_conn; + + return qed_sp_iscsi_conn_terminate(QED_LEADING_HWFN(cdev), + hash_con->con, + QED_SPQ_MODE_EBLOCK, NULL); +} + +static int qed_iscsi_stats(struct qed_dev *cdev, struct qed_iscsi_stats *stats) +{ + return qed_iscsi_get_stats(QED_LEADING_HWFN(cdev), stats); +} + +static const struct qed_iscsi_ops qed_iscsi_ops_pass = { + .common = &qed_common_ops_pass, + .ll2 = &qed_ll2_ops_pass, + .fill_dev_info = &qed_fill_iscsi_dev_info, + .register_ops = &qed_register_iscsi_ops, + .start = &qed_iscsi_start, + .stop = &qed_iscsi_stop, + .acquire_conn = &qed_iscsi_acquire_conn, + .release_conn = &qed_iscsi_release_conn, + .offload_conn = &qed_iscsi_offload_conn, + .update_conn = &qed_iscsi_update_conn, + .destroy_conn = &qed_iscsi_destroy_conn, + .clear_sq = &qed_iscsi_clear_conn_sq, + .get_stats = &qed_iscsi_stats, +}; + +const struct qed_iscsi_ops *qed_get_iscsi_ops() +{ + return &qed_iscsi_ops_pass; +} +EXPORT_SYMBOL(qed_get_iscsi_ops); + +void qed_put_iscsi_ops(void) +{ +} +EXPORT_SYMBOL(qed_put_iscsi_ops); diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.h b/drivers/net/ethernet/qlogic/qed/qed_iscsi.h new file mode 100644 index 0000000..67c25f3 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.h @@ -0,0 +1,52 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QED_ISCSI_H +#define _QED_ISCSI_H +#include +#include +#include +#include +#include +#include +#include +#include "qed.h" +#include "qed_hsi.h" +#include "qed_mcp.h" +#include "qed_sp.h" + +struct qed_iscsi_info { + spinlock_t lock; /* Connection resources. */ + struct list_head free_list; + u16 max_num_outstanding_tasks; + void *event_context; + iscsi_event_cb_t event_cb; +}; + +#ifdef CONFIG_QED_LL2 +extern const struct qed_ll2_ops qed_ll2_ops_pass; +#endif + +#if IS_ENABLED(CONFIG_QED_ISCSI) +struct qed_iscsi_info *qed_iscsi_alloc(struct qed_hwfn *p_hwfn); + +void qed_iscsi_setup(struct qed_hwfn *p_hwfn, + struct qed_iscsi_info *p_iscsi_info); + +void qed_iscsi_free(struct qed_hwfn *p_hwfn, + struct qed_iscsi_info *p_iscsi_info); +#else /* IS_ENABLED(CONFIG_QED_ISCSI) */ +static inline struct qed_iscsi_info *qed_iscsi_alloc( + struct qed_hwfn *p_hwfn) { return NULL; } +static inline void qed_iscsi_setup(struct qed_hwfn *p_hwfn, + struct qed_iscsi_info *p_iscsi_info) {} +static inline void qed_iscsi_free(struct qed_hwfn *p_hwfn, + struct qed_iscsi_info *p_iscsi_info) {} +#endif /* IS_ENABLED(CONFIG_QED_ISCSI) */ + +#endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index f95385c..84d7e91 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -1517,6 +1517,7 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params) enum qed_ll2_conn_type conn_type; struct qed_ptt *p_ptt; int rc, i; + u8 gsi_enable = 1; /* Initialize LL2 locks & lists */ INIT_LIST_HEAD(&cdev->ll2->list); @@ -1548,6 +1549,7 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params) switch (QED_LEADING_HWFN(cdev)->hw_info.personality) { case QED_PCI_ISCSI: conn_type = QED_LL2_TYPE_ISCSI; + gsi_enable = 0; break; case QED_PCI_ETH_ROCE: conn_type = QED_LL2_TYPE_ROCE; @@ -1564,7 +1566,7 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params) ll2_info.rx_vlan_removal_en = params->rx_vlan_stripping; ll2_info.tx_tc = 0; ll2_info.tx_dest = CORE_TX_DEST_NW; - ll2_info.gsi_enable = 1; + ll2_info.gsi_enable = gsi_enable; rc = qed_ll2_acquire_connection(QED_LEADING_HWFN(cdev), &ll2_info, QED_LL2_RX_SIZE, QED_LL2_TX_SIZE, diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h index b414a05..9754420 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h +++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h @@ -82,6 +82,8 @@ 0x1c80000UL #define BAR0_MAP_REG_XSDM_RAM \ 0x1e00000UL +#define BAR0_MAP_REG_YSDM_RAM \ + 0x1e80000UL #define NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF \ 0x5011f4UL #define PRS_REG_SEARCH_TCP \ diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index 019960b..56d2f64 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -24,6 +24,7 @@ #include "qed_hsi.h" #include "qed_hw.h" #include "qed_int.h" +#include "qed_iscsi.h" #include "qed_mcp.h" #include "qed_reg_addr.h" #include "qed_sp.h" @@ -277,6 +278,20 @@ qed_async_event_completion(struct qed_hwfn *p_hwfn, return qed_sriov_eqe_event(p_hwfn, p_eqe->opcode, p_eqe->echo, &p_eqe->data); + case PROTOCOLID_ISCSI: + if (!IS_ENABLED(CONFIG_QED_ISCSI)) + return -EINVAL; + + if (p_hwfn->p_iscsi_info->event_cb) { + struct qed_iscsi_info *p_iscsi = p_hwfn->p_iscsi_info; + + return p_iscsi->event_cb(p_iscsi->event_context, + p_eqe->opcode, &p_eqe->data); + } else { + DP_NOTICE(p_hwfn, + "iSCSI async completion is not set\n"); + return -EINVAL; + } default: DP_NOTICE(p_hwfn, "Unknown Async completion for protocol: %d\n", diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index ea095b4..4b454f4 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -166,6 +166,7 @@ struct qed_iscsi_pf_params { u32 max_cwnd; u16 cq_num_entries; u16 cmdq_num_entries; + u32 two_msl_timer; u16 dup_ack_threshold; u16 tx_sws_timer; u16 min_rto; @@ -275,6 +276,7 @@ struct qed_dev_info { enum qed_sb_type { QED_SB_TYPE_L2_QUEUE, QED_SB_TYPE_CNQ, + QED_SB_TYPE_STORAGE, }; enum qed_protocol { diff --git a/include/linux/qed/qed_iscsi_if.h b/include/linux/qed/qed_iscsi_if.h new file mode 100644 index 0000000..d279124 --- /dev/null +++ b/include/linux/qed/qed_iscsi_if.h @@ -0,0 +1,229 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QED_ISCSI_IF_H +#define _QED_ISCSI_IF_H +#include +#include + +typedef int (*iscsi_event_cb_t) (void *context, + u8 fw_event_code, void *fw_handle); +struct qed_iscsi_stats { + u64 iscsi_rx_bytes_cnt; + u64 iscsi_rx_packet_cnt; + u64 iscsi_rx_new_ooo_isle_events_cnt; + u32 iscsi_cmdq_threshold_cnt; + u32 iscsi_rq_threshold_cnt; + u32 iscsi_immq_threshold_cnt; + + u64 iscsi_rx_dropped_pdus_task_not_valid; + + u64 iscsi_rx_data_pdu_cnt; + u64 iscsi_rx_r2t_pdu_cnt; + u64 iscsi_rx_total_pdu_cnt; + + u64 iscsi_tx_go_to_slow_start_event_cnt; + u64 iscsi_tx_fast_retransmit_event_cnt; + + u64 iscsi_tx_data_pdu_cnt; + u64 iscsi_tx_r2t_pdu_cnt; + u64 iscsi_tx_total_pdu_cnt; + + u64 iscsi_tx_bytes_cnt; + u64 iscsi_tx_packet_cnt; +}; + +struct qed_dev_iscsi_info { + struct qed_dev_info common; + + void __iomem *primary_dbq_rq_addr; + void __iomem *secondary_bdq_rq_addr; +}; + +struct qed_iscsi_id_params { + u8 mac[ETH_ALEN]; + u32 ip[4]; + u16 port; +}; + +struct qed_iscsi_params_offload { + u8 layer_code; + dma_addr_t sq_pbl_addr; + u32 initial_ack; + + struct qed_iscsi_id_params src; + struct qed_iscsi_id_params dst; + u16 vlan_id; + u8 tcp_flags; + u8 ip_version; + u8 default_cq; + + u8 ka_max_probe_cnt; + u8 dup_ack_theshold; + u32 rcv_next; + u32 snd_una; + u32 snd_next; + u32 snd_max; + u32 snd_wnd; + u32 rcv_wnd; + u32 snd_wl1; + u32 cwnd; + u32 ss_thresh; + u16 srtt; + u16 rtt_var; + u32 ts_time; + u32 ts_recent; + u32 ts_recent_age; + u32 total_rt; + u32 ka_timeout_delta; + u32 rt_timeout_delta; + u8 dup_ack_cnt; + u8 snd_wnd_probe_cnt; + u8 ka_probe_cnt; + u8 rt_cnt; + u32 flow_label; + u32 ka_timeout; + u32 ka_interval; + u32 max_rt_time; + u32 initial_rcv_wnd; + u8 ttl; + u8 tos_or_tc; + u16 remote_port; + u16 local_port; + u16 mss; + u8 snd_wnd_scale; + u8 rcv_wnd_scale; + u32 ts_ticks_per_second; + u16 da_timeout_value; + u8 ack_frequency; +}; + +struct qed_iscsi_params_update { + u8 update_flag; +#define QED_ISCSI_CONN_HD_EN BIT(0) +#define QED_ISCSI_CONN_DD_EN BIT(1) +#define QED_ISCSI_CONN_INITIAL_R2T BIT(2) +#define QED_ISCSI_CONN_IMMEDIATE_DATA BIT(3) + + u32 max_seq_size; + u32 max_recv_pdu_length; + u32 max_send_pdu_length; + u32 first_seq_length; + u32 exp_stat_sn; +}; + +#define MAX_TID_BLOCKS_ISCSI (512) +struct qed_iscsi_tid { + u32 size; /* In bytes per task */ + u32 num_tids_per_block; + u8 *blocks[MAX_TID_BLOCKS_ISCSI]; +}; + +struct qed_iscsi_cb_ops { + struct qed_common_cb_ops common; +}; + +/** + * struct qed_iscsi_ops - qed iSCSI operations. + * @common: common operations pointer + * @ll2: light L2 operations pointer + * @fill_dev_info: fills iSCSI specific information + * @param cdev + * @param info + * @return 0 on sucesss, otherwise error value. + * @register_ops: register iscsi operations + * @param cdev + * @param ops - specified using qed_iscsi_cb_ops + * @param cookie - driver private + * @start: iscsi in FW + * @param cdev + * @param tasks - qed will fill information about tasks + * return 0 on success, otherwise error value. + * @stop: iscsi in FW + * @param cdev + * return 0 on success, otherwise error value. + * @acquire_conn: acquire a new iscsi connection + * @param cdev + * @param handle - qed will fill handle that should be + * used henceforth as identifier of the + * connection. + * @param p_doorbell - qed will fill the address of the + * doorbell. + * @return 0 on sucesss, otherwise error value. + * @release_conn: release a previously acquired iscsi connection + * @param cdev + * @param handle - the connection handle. + * @return 0 on success, otherwise error value. + * @offload_conn: configures an offloaded connection + * @param cdev + * @param handle - the connection handle. + * @param conn_info - the configuration to use for the + * offload. + * @return 0 on success, otherwise error value. + * @update_conn: updates an offloaded connection + * @param cdev + * @param handle - the connection handle. + * @param conn_info - the configuration to use for the + * offload. + * @return 0 on success, otherwise error value. + * @destroy_conn: stops an offloaded connection + * @param cdev + * @param handle - the connection handle. + * @return 0 on success, otherwise error value. + * @clear_sq: clear all task in sq + * @param cdev + * @param handle - the connection handle. + * @return 0 on success, otherwise error value. + * @get_stats: iSCSI related statistics + * @param cdev + * @param stats - pointer to struck that would be filled + * we stats + * @return 0 on success, error otherwise. + */ +struct qed_iscsi_ops { + const struct qed_common_ops *common; + + const struct qed_ll2_ops *ll2; + + int (*fill_dev_info)(struct qed_dev *cdev, + struct qed_dev_iscsi_info *info); + + void (*register_ops)(struct qed_dev *cdev, + struct qed_iscsi_cb_ops *ops, void *cookie); + + int (*start)(struct qed_dev *cdev, + struct qed_iscsi_tid *tasks, + void *event_context, iscsi_event_cb_t async_event_cb); + + int (*stop)(struct qed_dev *cdev); + + int (*acquire_conn)(struct qed_dev *cdev, + u32 *handle, + u32 *fw_cid, void __iomem **p_doorbell); + + int (*release_conn)(struct qed_dev *cdev, u32 handle); + + int (*offload_conn)(struct qed_dev *cdev, + u32 handle, + struct qed_iscsi_params_offload *conn_info); + + int (*update_conn)(struct qed_dev *cdev, + u32 handle, + struct qed_iscsi_params_update *conn_info); + + int (*destroy_conn)(struct qed_dev *cdev, u32 handle, u8 abrt_conn); + + int (*clear_sq)(struct qed_dev *cdev, u32 handle); + + int (*get_stats)(struct qed_dev *cdev, + struct qed_iscsi_stats *stats); +}; + +const struct qed_iscsi_ops *qed_get_iscsi_ops(void); +void qed_put_iscsi_ops(void); +#endif -- cgit v1.1 From 1d6cff4fca4366d0529dbce170e0f33cfe213790 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Thu, 1 Dec 2016 00:21:07 -0800 Subject: qed: Add iSCSI out of order packet handling. This patch adds out of order packet handling for hardware offloaded iSCSI. Out of order packet handling requires driver buffer allocation and assistance. Signed-off-by: Arun Easi Signed-off-by: Yuval Mintz Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/Makefile | 2 +- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 14 +- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 551 +++++++++++++++++++++++++++-- drivers/net/ethernet/qlogic/qed/qed_ll2.h | 9 + drivers/net/ethernet/qlogic/qed/qed_ooo.c | 501 ++++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_ooo.h | 176 +++++++++ drivers/net/ethernet/qlogic/qed/qed_roce.c | 1 + drivers/net/ethernet/qlogic/qed/qed_spq.c | 9 + 9 files changed, 1234 insertions(+), 30 deletions(-) create mode 100644 drivers/net/ethernet/qlogic/qed/qed_ooo.c create mode 100644 drivers/net/ethernet/qlogic/qed/qed_ooo.h diff --git a/drivers/net/ethernet/qlogic/qed/Makefile b/drivers/net/ethernet/qlogic/qed/Makefile index 597e15c..729e437 100644 --- a/drivers/net/ethernet/qlogic/qed/Makefile +++ b/drivers/net/ethernet/qlogic/qed/Makefile @@ -6,4 +6,4 @@ qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \ qed-$(CONFIG_QED_SRIOV) += qed_sriov.o qed_vf.o qed-$(CONFIG_QED_LL2) += qed_ll2.o qed-$(CONFIG_QED_RDMA) += qed_roce.o -qed-$(CONFIG_QED_ISCSI) += qed_iscsi.o +qed-$(CONFIG_QED_ISCSI) += qed_iscsi.o qed_ooo.o diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 1f423b3..44c184e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -383,6 +383,7 @@ struct qed_hwfn { /* Protocol related */ bool using_ll2; struct qed_ll2_info *p_ll2_info; + struct qed_ooo_info *p_ooo_info; struct qed_rdma_info *p_rdma_info; struct qed_iscsi_info *p_iscsi_info; struct qed_pf_params pf_params; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index cd98102..3b22500 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -32,6 +32,7 @@ #include "qed_iscsi.h" #include "qed_ll2.h" #include "qed_mcp.h" +#include "qed_ooo.h" #include "qed_reg_addr.h" #include "qed_sp.h" #include "qed_sriov.h" @@ -147,8 +148,10 @@ void qed_resc_free(struct qed_dev *cdev) #ifdef CONFIG_QED_LL2 qed_ll2_free(p_hwfn, p_hwfn->p_ll2_info); #endif - if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) + if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) { qed_iscsi_free(p_hwfn, p_hwfn->p_iscsi_info); + qed_ooo_free(p_hwfn, p_hwfn->p_ooo_info); + } qed_iov_free(p_hwfn); qed_dmae_info_free(p_hwfn); qed_dcbx_info_free(p_hwfn, p_hwfn->p_dcbx_info); @@ -406,6 +409,7 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) int qed_resc_alloc(struct qed_dev *cdev) { struct qed_iscsi_info *p_iscsi_info; + struct qed_ooo_info *p_ooo_info; #ifdef CONFIG_QED_LL2 struct qed_ll2_info *p_ll2_info; #endif @@ -516,6 +520,10 @@ int qed_resc_alloc(struct qed_dev *cdev) if (!p_iscsi_info) goto alloc_no_mem; p_hwfn->p_iscsi_info = p_iscsi_info; + p_ooo_info = qed_ooo_alloc(p_hwfn); + if (!p_ooo_info) + goto alloc_no_mem; + p_hwfn->p_ooo_info = p_ooo_info; } /* DMA info initialization */ @@ -570,8 +578,10 @@ void qed_resc_setup(struct qed_dev *cdev) if (p_hwfn->using_ll2) qed_ll2_setup(p_hwfn, p_hwfn->p_ll2_info); #endif - if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) + if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) { qed_iscsi_setup(p_hwfn, p_hwfn->p_iscsi_info); + qed_ooo_setup(p_hwfn, p_hwfn->p_ooo_info); + } } } diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 84d7e91..de4e2a2 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -36,6 +36,7 @@ #include "qed_int.h" #include "qed_ll2.h" #include "qed_mcp.h" +#include "qed_ooo.h" #include "qed_reg_addr.h" #include "qed_sp.h" #include "qed_roce.h" @@ -296,25 +297,34 @@ static void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle) list_del(&p_pkt->list_entry); b_last_packet = list_empty(&p_tx->active_descq); list_add_tail(&p_pkt->list_entry, &p_tx->free_descq); - p_tx->cur_completing_packet = *p_pkt; - p_tx->cur_completing_bd_idx = 1; - b_last_frag = p_tx->cur_completing_bd_idx == p_pkt->bd_used; - tx_frag = p_pkt->bds_set[0].tx_frag; - if (p_ll2_conn->gsi_enable) - qed_ll2b_release_tx_gsi_packet(p_hwfn, - p_ll2_conn->my_id, - p_pkt->cookie, - tx_frag, - b_last_frag, - b_last_packet); - else - qed_ll2b_complete_tx_packet(p_hwfn, - p_ll2_conn->my_id, - p_pkt->cookie, - tx_frag, - b_last_frag, - b_last_packet); + if (p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO) { + struct qed_ooo_buffer *p_buffer; + p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie; + qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info, + p_buffer); + } else { + p_tx->cur_completing_packet = *p_pkt; + p_tx->cur_completing_bd_idx = 1; + b_last_frag = + p_tx->cur_completing_bd_idx == p_pkt->bd_used; + tx_frag = p_pkt->bds_set[0].tx_frag; + if (p_ll2_conn->gsi_enable) + qed_ll2b_release_tx_gsi_packet(p_hwfn, + p_ll2_conn-> + my_id, + p_pkt->cookie, + tx_frag, + b_last_frag, + b_last_packet); + else + qed_ll2b_complete_tx_packet(p_hwfn, + p_ll2_conn->my_id, + p_pkt->cookie, + tx_frag, + b_last_frag, + b_last_packet); + } } } @@ -540,12 +550,457 @@ static void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle) list_move_tail(&p_pkt->list_entry, &p_rx->free_descq); - rx_buf_addr = p_pkt->rx_buf_addr; - cookie = p_pkt->cookie; + if (p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO) { + struct qed_ooo_buffer *p_buffer; + + p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie; + qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info, + p_buffer); + } else { + rx_buf_addr = p_pkt->rx_buf_addr; + cookie = p_pkt->cookie; + + b_last = list_empty(&p_rx->active_descq); + } + } +} + +#if IS_ENABLED(CONFIG_QED_ISCSI) +static u8 qed_ll2_convert_rx_parse_to_tx_flags(u16 parse_flags) +{ + u8 bd_flags = 0; + + if (GET_FIELD(parse_flags, PARSING_AND_ERR_FLAGS_TAG8021QEXIST)) + SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_VLAN_INSERTION, 1); + + return bd_flags; +} + +static int qed_ll2_lb_rxq_handler(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn) +{ + struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue; + u16 packet_length = 0, parse_flags = 0, vlan = 0; + struct qed_ll2_rx_packet *p_pkt = NULL; + u32 num_ooo_add_to_peninsula = 0, cid; + union core_rx_cqe_union *cqe = NULL; + u16 cq_new_idx = 0, cq_old_idx = 0; + struct qed_ooo_buffer *p_buffer; + struct ooo_opaque *iscsi_ooo; + u8 placement_offset = 0; + u8 cqe_type; + + cq_new_idx = le16_to_cpu(*p_rx->p_fw_cons); + cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain); + if (cq_new_idx == cq_old_idx) + return 0; + + while (cq_new_idx != cq_old_idx) { + struct core_rx_fast_path_cqe *p_cqe_fp; + + cqe = qed_chain_consume(&p_rx->rcq_chain); + cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain); + cqe_type = cqe->rx_cqe_sp.type; + + if (cqe_type != CORE_RX_CQE_TYPE_REGULAR) { + DP_NOTICE(p_hwfn, + "Got a non-regular LB LL2 completion [type 0x%02x]\n", + cqe_type); + return -EINVAL; + } + p_cqe_fp = &cqe->rx_cqe_fp; + + placement_offset = p_cqe_fp->placement_offset; + parse_flags = le16_to_cpu(p_cqe_fp->parse_flags.flags); + packet_length = le16_to_cpu(p_cqe_fp->packet_length); + vlan = le16_to_cpu(p_cqe_fp->vlan); + iscsi_ooo = (struct ooo_opaque *)&p_cqe_fp->opaque_data; + qed_ooo_save_history_entry(p_hwfn, p_hwfn->p_ooo_info, + iscsi_ooo); + cid = le32_to_cpu(iscsi_ooo->cid); + + /* Process delete isle first */ + if (iscsi_ooo->drop_size) + qed_ooo_delete_isles(p_hwfn, p_hwfn->p_ooo_info, cid, + iscsi_ooo->drop_isle, + iscsi_ooo->drop_size); + + if (iscsi_ooo->ooo_opcode == TCP_EVENT_NOP) + continue; + + /* Now process create/add/join isles */ + if (list_empty(&p_rx->active_descq)) { + DP_NOTICE(p_hwfn, + "LL2 OOO RX chain has no submitted buffers\n" + ); + return -EIO; + } + + p_pkt = list_first_entry(&p_rx->active_descq, + struct qed_ll2_rx_packet, list_entry); + + if ((iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_NEW_ISLE) || + (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_ISLE_RIGHT) || + (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_ISLE_LEFT) || + (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_PEN) || + (iscsi_ooo->ooo_opcode == TCP_EVENT_JOIN)) { + if (!p_pkt) { + DP_NOTICE(p_hwfn, + "LL2 OOO RX packet is not valid\n"); + return -EIO; + } + list_del(&p_pkt->list_entry); + p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie; + p_buffer->packet_length = packet_length; + p_buffer->parse_flags = parse_flags; + p_buffer->vlan = vlan; + p_buffer->placement_offset = placement_offset; + qed_chain_consume(&p_rx->rxq_chain); + list_add_tail(&p_pkt->list_entry, &p_rx->free_descq); + + switch (iscsi_ooo->ooo_opcode) { + case TCP_EVENT_ADD_NEW_ISLE: + qed_ooo_add_new_isle(p_hwfn, + p_hwfn->p_ooo_info, + cid, + iscsi_ooo->ooo_isle, + p_buffer); + break; + case TCP_EVENT_ADD_ISLE_RIGHT: + qed_ooo_add_new_buffer(p_hwfn, + p_hwfn->p_ooo_info, + cid, + iscsi_ooo->ooo_isle, + p_buffer, + QED_OOO_RIGHT_BUF); + break; + case TCP_EVENT_ADD_ISLE_LEFT: + qed_ooo_add_new_buffer(p_hwfn, + p_hwfn->p_ooo_info, + cid, + iscsi_ooo->ooo_isle, + p_buffer, + QED_OOO_LEFT_BUF); + break; + case TCP_EVENT_JOIN: + qed_ooo_add_new_buffer(p_hwfn, + p_hwfn->p_ooo_info, + cid, + iscsi_ooo->ooo_isle + + 1, + p_buffer, + QED_OOO_LEFT_BUF); + qed_ooo_join_isles(p_hwfn, + p_hwfn->p_ooo_info, + cid, iscsi_ooo->ooo_isle); + break; + case TCP_EVENT_ADD_PEN: + num_ooo_add_to_peninsula++; + qed_ooo_put_ready_buffer(p_hwfn, + p_hwfn->p_ooo_info, + p_buffer, true); + break; + } + } else { + DP_NOTICE(p_hwfn, + "Unexpected event (%d) TX OOO completion\n", + iscsi_ooo->ooo_opcode); + } + } + + return 0; +} + +static void +qed_ooo_submit_tx_buffers(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn) +{ + struct qed_ooo_buffer *p_buffer; + int rc; + u16 l4_hdr_offset_w; + dma_addr_t first_frag; + u16 parse_flags; + u8 bd_flags; + + /* Submit Tx buffers here */ + while ((p_buffer = qed_ooo_get_ready_buffer(p_hwfn, + p_hwfn->p_ooo_info))) { + l4_hdr_offset_w = 0; + bd_flags = 0; + + first_frag = p_buffer->rx_buffer_phys_addr + + p_buffer->placement_offset; + parse_flags = p_buffer->parse_flags; + bd_flags = qed_ll2_convert_rx_parse_to_tx_flags(parse_flags); + SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_FORCE_VLAN_MODE, 1); + SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_L4_PROTOCOL, 1); + + rc = qed_ll2_prepare_tx_packet(p_hwfn, p_ll2_conn->my_id, 1, + p_buffer->vlan, bd_flags, + l4_hdr_offset_w, + p_ll2_conn->tx_dest, 0, + first_frag, + p_buffer->packet_length, + p_buffer, true); + if (rc) { + qed_ooo_put_ready_buffer(p_hwfn, p_hwfn->p_ooo_info, + p_buffer, false); + break; + } + } +} + +static void +qed_ooo_submit_rx_buffers(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn) +{ + struct qed_ooo_buffer *p_buffer; + int rc; + + while ((p_buffer = qed_ooo_get_free_buffer(p_hwfn, + p_hwfn->p_ooo_info))) { + rc = qed_ll2_post_rx_buffer(p_hwfn, + p_ll2_conn->my_id, + p_buffer->rx_buffer_phys_addr, + 0, p_buffer, true); + if (rc) { + qed_ooo_put_free_buffer(p_hwfn, + p_hwfn->p_ooo_info, p_buffer); + break; + } + } +} + +static int qed_ll2_lb_rxq_completion(struct qed_hwfn *p_hwfn, void *p_cookie) +{ + struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)p_cookie; + int rc; + + rc = qed_ll2_lb_rxq_handler(p_hwfn, p_ll2_conn); + if (rc) + return rc; + + qed_ooo_submit_rx_buffers(p_hwfn, p_ll2_conn); + qed_ooo_submit_tx_buffers(p_hwfn, p_ll2_conn); + + return 0; +} + +static int qed_ll2_lb_txq_completion(struct qed_hwfn *p_hwfn, void *p_cookie) +{ + struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)p_cookie; + struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue; + struct qed_ll2_tx_packet *p_pkt = NULL; + struct qed_ooo_buffer *p_buffer; + bool b_dont_submit_rx = false; + u16 new_idx = 0, num_bds = 0; + int rc; + + new_idx = le16_to_cpu(*p_tx->p_fw_cons); + num_bds = ((s16)new_idx - (s16)p_tx->bds_idx); + + if (!num_bds) + return 0; + + while (num_bds) { + if (list_empty(&p_tx->active_descq)) + return -EINVAL; + + p_pkt = list_first_entry(&p_tx->active_descq, + struct qed_ll2_tx_packet, list_entry); + if (!p_pkt) + return -EINVAL; + + if (p_pkt->bd_used != 1) { + DP_NOTICE(p_hwfn, + "Unexpectedly many BDs(%d) in TX OOO completion\n", + p_pkt->bd_used); + return -EINVAL; + } + + list_del(&p_pkt->list_entry); + + num_bds--; + p_tx->bds_idx++; + qed_chain_consume(&p_tx->txq_chain); + + p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie; + list_add_tail(&p_pkt->list_entry, &p_tx->free_descq); + + if (b_dont_submit_rx) { + qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info, + p_buffer); + continue; + } + + rc = qed_ll2_post_rx_buffer(p_hwfn, p_ll2_conn->my_id, + p_buffer->rx_buffer_phys_addr, 0, + p_buffer, true); + if (rc != 0) { + qed_ooo_put_free_buffer(p_hwfn, + p_hwfn->p_ooo_info, p_buffer); + b_dont_submit_rx = true; + } + } + + qed_ooo_submit_tx_buffers(p_hwfn, p_ll2_conn); + + return 0; +} + +static int +qed_ll2_acquire_connection_ooo(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_info, + u16 rx_num_ooo_buffers, u16 mtu) +{ + struct qed_ooo_buffer *p_buf = NULL; + void *p_virt; + u16 buf_idx; + int rc = 0; + + if (p_ll2_info->conn_type != QED_LL2_TYPE_ISCSI_OOO) + return rc; + + if (!rx_num_ooo_buffers) + return -EINVAL; + + for (buf_idx = 0; buf_idx < rx_num_ooo_buffers; buf_idx++) { + p_buf = kzalloc(sizeof(*p_buf), GFP_KERNEL); + if (!p_buf) { + rc = -ENOMEM; + goto out; + } + + p_buf->rx_buffer_size = mtu + 26 + ETH_CACHE_LINE_SIZE; + p_buf->rx_buffer_size = (p_buf->rx_buffer_size + + ETH_CACHE_LINE_SIZE - 1) & + ~(ETH_CACHE_LINE_SIZE - 1); + p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + p_buf->rx_buffer_size, + &p_buf->rx_buffer_phys_addr, + GFP_KERNEL); + if (!p_virt) { + kfree(p_buf); + rc = -ENOMEM; + goto out; + } + + p_buf->rx_buffer_virt_addr = p_virt; + qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info, p_buf); + } + + DP_VERBOSE(p_hwfn, QED_MSG_LL2, + "Allocated [%04x] LL2 OOO buffers [each of size 0x%08x]\n", + rx_num_ooo_buffers, p_buf->rx_buffer_size); + +out: + return rc; +} + +static void +qed_ll2_establish_connection_ooo(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn) +{ + if (p_ll2_conn->conn_type != QED_LL2_TYPE_ISCSI_OOO) + return; + + qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info); + qed_ooo_submit_rx_buffers(p_hwfn, p_ll2_conn); +} + +static void qed_ll2_release_connection_ooo(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn) +{ + struct qed_ooo_buffer *p_buffer; + + if (p_ll2_conn->conn_type != QED_LL2_TYPE_ISCSI_OOO) + return; + + qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info); + while ((p_buffer = qed_ooo_get_free_buffer(p_hwfn, + p_hwfn->p_ooo_info))) { + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + p_buffer->rx_buffer_size, + p_buffer->rx_buffer_virt_addr, + p_buffer->rx_buffer_phys_addr); + kfree(p_buffer); + } +} + +static void qed_ll2_stop_ooo(struct qed_dev *cdev) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + u8 *handle = &hwfn->pf_params.iscsi_pf_params.ll2_ooo_queue_id; + + DP_VERBOSE(cdev, QED_MSG_STORAGE, "Stopping LL2 OOO queue [%02x]\n", + *handle); + + qed_ll2_terminate_connection(hwfn, *handle); + qed_ll2_release_connection(hwfn, *handle); + *handle = QED_LL2_UNUSED_HANDLE; +} + +static int qed_ll2_start_ooo(struct qed_dev *cdev, + struct qed_ll2_params *params) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + u8 *handle = &hwfn->pf_params.iscsi_pf_params.ll2_ooo_queue_id; + struct qed_ll2_info *ll2_info; + int rc; + + ll2_info = kzalloc(sizeof(*ll2_info), GFP_KERNEL); + if (!ll2_info) + return -ENOMEM; + ll2_info->conn_type = QED_LL2_TYPE_ISCSI_OOO; + ll2_info->mtu = params->mtu; + ll2_info->rx_drop_ttl0_flg = params->drop_ttl0_packets; + ll2_info->rx_vlan_removal_en = params->rx_vlan_stripping; + ll2_info->tx_tc = OOO_LB_TC; + ll2_info->tx_dest = CORE_TX_DEST_LB; + + rc = qed_ll2_acquire_connection(hwfn, ll2_info, + QED_LL2_RX_SIZE, QED_LL2_TX_SIZE, + handle); + kfree(ll2_info); + if (rc) { + DP_INFO(cdev, "Failed to acquire LL2 OOO connection\n"); + goto out; + } - b_last = list_empty(&p_rx->active_descq); + rc = qed_ll2_establish_connection(hwfn, *handle); + if (rc) { + DP_INFO(cdev, "Failed to establist LL2 OOO connection\n"); + goto fail; } + + return 0; + +fail: + qed_ll2_release_connection(hwfn, *handle); +out: + *handle = QED_LL2_UNUSED_HANDLE; + return rc; } +#else /* IS_ENABLED(CONFIG_QED_ISCSI) */ +static int qed_ll2_lb_rxq_completion(struct qed_hwfn *p_hwfn, + void *p_cookie) { return -EINVAL; } +static int qed_ll2_lb_txq_completion(struct qed_hwfn *p_hwfn, + void *p_cookie) { return -EINVAL; } +static inline int +qed_ll2_acquire_connection_ooo(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_info, + u16 rx_num_ooo_buffers, u16 mtu) { return 0; } +static inline void +qed_ll2_establish_connection_ooo(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn) { return; } +static inline void +qed_ll2_release_connection_ooo(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn) { return; } +static inline void qed_ll2_stop_ooo(struct qed_dev *cdev) { return; } +static inline int qed_ll2_start_ooo(struct qed_dev *cdev, + struct qed_ll2_params *params) + { return -EINVAL; } +#endif /* IS_ENABLED(CONFIG_QED_ISCSI) */ static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn, struct qed_ll2_info *p_ll2_conn, @@ -588,7 +1043,8 @@ static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn, p_ramrod->drop_ttl0_flg = p_ll2_conn->rx_drop_ttl0_flg; p_ramrod->inner_vlan_removal_en = p_ll2_conn->rx_vlan_removal_en; p_ramrod->queue_id = p_ll2_conn->queue_id; - p_ramrod->main_func_queue = 1; + p_ramrod->main_func_queue = (conn_type == QED_LL2_TYPE_ISCSI_OOO) ? 0 + : 1; if ((IS_MF_DEFAULT(p_hwfn) || IS_MF_SI(p_hwfn)) && p_ramrod->main_func_queue && (conn_type != QED_LL2_TYPE_ROCE)) { @@ -619,6 +1075,11 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn, if (!QED_LL2_TX_REGISTERED(p_ll2_conn)) return 0; + if (p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO) + p_ll2_conn->tx_stats_en = 0; + else + p_ll2_conn->tx_stats_en = 1; + /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); init_data.cid = p_ll2_conn->cid; @@ -636,7 +1097,6 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn, p_ramrod->sb_id = cpu_to_le16(qed_int_get_sp_sb_id(p_hwfn)); p_ramrod->sb_index = p_tx->tx_sb_index; p_ramrod->mtu = cpu_to_le16(p_ll2_conn->mtu); - p_ll2_conn->tx_stats_en = 1; p_ramrod->stats_en = p_ll2_conn->tx_stats_en; p_ramrod->stats_id = p_ll2_conn->tx_stats_id; @@ -860,9 +1320,19 @@ int qed_ll2_acquire_connection(struct qed_hwfn *p_hwfn, if (rc) goto q_allocate_fail; + rc = qed_ll2_acquire_connection_ooo(p_hwfn, p_ll2_info, + rx_num_desc * 2, p_params->mtu); + if (rc) + goto q_allocate_fail; + /* Register callbacks for the Rx/Tx queues */ - comp_rx_cb = qed_ll2_rxq_completion; - comp_tx_cb = qed_ll2_txq_completion; + if (p_params->conn_type == QED_LL2_TYPE_ISCSI_OOO) { + comp_rx_cb = qed_ll2_lb_rxq_completion; + comp_tx_cb = qed_ll2_lb_txq_completion; + } else { + comp_rx_cb = qed_ll2_rxq_completion; + comp_tx_cb = qed_ll2_txq_completion; + } if (rx_num_desc) { qed_int_register_cb(p_hwfn, comp_rx_cb, @@ -975,6 +1445,8 @@ int qed_ll2_establish_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) if (p_hwfn->hw_info.personality != QED_PCI_ETH_ROCE) qed_wr(p_hwfn, p_hwfn->p_main_ptt, PRS_REG_USE_LIGHT_L2, 1); + qed_ll2_establish_connection_ooo(p_hwfn, p_ll2_conn); + return rc; } @@ -1213,6 +1685,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn, u16 vlan, u8 bd_flags, u16 l4_hdr_offset_w, + enum qed_ll2_tx_dest e_tx_dest, enum qed_ll2_roce_flavor_type qed_roce_flavor, dma_addr_t first_frag, u16 first_frag_len, void *cookie, u8 notify_fw) @@ -1222,6 +1695,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn, enum core_roce_flavor_type roce_flavor; struct qed_ll2_tx_queue *p_tx; struct qed_chain *p_tx_chain; + enum core_tx_dest tx_dest; unsigned long flags; int rc = 0; @@ -1252,6 +1726,8 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn, goto out; } + tx_dest = e_tx_dest == QED_LL2_TX_DEST_NW ? CORE_TX_DEST_NW : + CORE_TX_DEST_LB; if (qed_roce_flavor == QED_LL2_ROCE) { roce_flavor = CORE_ROCE; } else if (qed_roce_flavor == QED_LL2_RROCE) { @@ -1266,7 +1742,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn, num_of_bds, first_frag, first_frag_len, cookie, notify_fw); qed_ll2_prepare_tx_packet_set_bd(p_hwfn, p_ll2_conn, p_curp, - num_of_bds, CORE_TX_DEST_NW, + num_of_bds, tx_dest, vlan, bd_flags, l4_hdr_offset_w, roce_flavor, first_frag, first_frag_len); @@ -1341,6 +1817,9 @@ int qed_ll2_terminate_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) qed_ll2_rxq_flush(p_hwfn, connection_handle); } + if (p_ll2_conn->conn_type == QED_LL2_TYPE_ISCSI_OOO) + qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info); + return rc; } @@ -1371,6 +1850,8 @@ void qed_ll2_release_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) qed_cxt_release_cid(p_hwfn, p_ll2_conn->cid); + qed_ll2_release_connection_ooo(p_hwfn, p_ll2_conn); + mutex_lock(&p_ll2_conn->mutex); p_ll2_conn->b_active = false; mutex_unlock(&p_ll2_conn->mutex); @@ -1613,6 +2094,17 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params) goto release_terminate; } + if (cdev->hwfns[0].hw_info.personality == QED_PCI_ISCSI && + cdev->hwfns[0].pf_params.iscsi_pf_params.ooo_enable) { + DP_VERBOSE(cdev, QED_MSG_STORAGE, "Starting OOO LL2 queue\n"); + rc = qed_ll2_start_ooo(cdev, params); + if (rc) { + DP_INFO(cdev, + "Failed to initialize the OOO LL2 queue\n"); + goto release_terminate; + } + } + p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); if (!p_ptt) { DP_INFO(cdev, "Failed to acquire PTT\n"); @@ -1662,6 +2154,10 @@ static int qed_ll2_stop(struct qed_dev *cdev) qed_ptt_release(QED_LEADING_HWFN(cdev), p_ptt); eth_zero_addr(cdev->ll2_mac_address); + if (cdev->hwfns[0].hw_info.personality == QED_PCI_ISCSI && + cdev->hwfns[0].pf_params.iscsi_pf_params.ooo_enable) + qed_ll2_stop_ooo(cdev); + rc = qed_ll2_terminate_connection(QED_LEADING_HWFN(cdev), cdev->ll2->handle); if (rc) @@ -1716,7 +2212,8 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb) rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev), cdev->ll2->handle, 1 + skb_shinfo(skb)->nr_frags, - vlan, flags, 0, 0 /* RoCE FLAVOR */, + vlan, flags, 0, QED_LL2_TX_DEST_NW, + 0 /* RoCE FLAVOR */, mapping, skb->len, skb, 1); if (rc) goto err; diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.h b/drivers/net/ethernet/qlogic/qed/qed_ll2.h index 4e3d62a..6625a3a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.h @@ -41,6 +41,12 @@ enum qed_ll2_conn_type { MAX_QED_LL2_RX_CONN_TYPE }; +enum qed_ll2_tx_dest { + QED_LL2_TX_DEST_NW, /* Light L2 TX Destination to the Network */ + QED_LL2_TX_DEST_LB, /* Light L2 TX Destination to the Loopback */ + QED_LL2_TX_DEST_MAX +}; + struct qed_ll2_rx_packet { struct list_head list_entry; struct core_rx_bd_with_buff_len *rxq_bd; @@ -192,6 +198,8 @@ int qed_ll2_post_rx_buffer(struct qed_hwfn *p_hwfn, * @param l4_hdr_offset_w L4 Header Offset from start of packet * (in words). This is needed if both l4_csum * and ipv6_ext are set + * @param e_tx_dest indicates if the packet is to be transmitted via + * loopback or to the network * @param first_frag * @param first_frag_len * @param cookie @@ -206,6 +214,7 @@ int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn, u16 vlan, u8 bd_flags, u16 l4_hdr_offset_w, + enum qed_ll2_tx_dest e_tx_dest, enum qed_ll2_roce_flavor_type qed_roce_flavor, dma_addr_t first_frag, u16 first_frag_len, void *cookie, u8 notify_fw); diff --git a/drivers/net/ethernet/qlogic/qed/qed_ooo.c b/drivers/net/ethernet/qlogic/qed/qed_ooo.c new file mode 100644 index 0000000..155abcb --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_ooo.c @@ -0,0 +1,501 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "qed.h" +#include "qed_iscsi.h" +#include "qed_ll2.h" +#include "qed_ooo.h" + +static struct qed_ooo_archipelago +*qed_ooo_seek_archipelago(struct qed_hwfn *p_hwfn, + struct qed_ooo_info + *p_ooo_info, + u32 cid) +{ + struct qed_ooo_archipelago *p_archipelago = NULL; + + list_for_each_entry(p_archipelago, + &p_ooo_info->archipelagos_list, list_entry) { + if (p_archipelago->cid == cid) + return p_archipelago; + } + + return NULL; +} + +static struct qed_ooo_isle *qed_ooo_seek_isle(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, u8 isle) +{ + struct qed_ooo_archipelago *p_archipelago = NULL; + struct qed_ooo_isle *p_isle = NULL; + u8 the_num_of_isle = 1; + + p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid); + if (!p_archipelago) { + DP_NOTICE(p_hwfn, + "Connection %d is not found in OOO list\n", cid); + return NULL; + } + + list_for_each_entry(p_isle, &p_archipelago->isles_list, list_entry) { + if (the_num_of_isle == isle) + return p_isle; + the_num_of_isle++; + } + + return NULL; +} + +void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct ooo_opaque *p_cqe) +{ + struct qed_ooo_history *p_history = &p_ooo_info->ooo_history; + + if (p_history->head_idx == p_history->num_of_cqes) + p_history->head_idx = 0; + p_history->p_cqes[p_history->head_idx] = *p_cqe; + p_history->head_idx++; +} + +struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn) +{ + struct qed_ooo_info *p_ooo_info; + u16 max_num_archipelagos = 0; + u16 max_num_isles = 0; + u32 i; + + if (p_hwfn->hw_info.personality != QED_PCI_ISCSI) { + DP_NOTICE(p_hwfn, + "Failed to allocate qed_ooo_info: unknown personality\n"); + return NULL; + } + + max_num_archipelagos = p_hwfn->pf_params.iscsi_pf_params.num_cons; + max_num_isles = QED_MAX_NUM_ISLES + max_num_archipelagos; + + if (!max_num_archipelagos) { + DP_NOTICE(p_hwfn, + "Failed to allocate qed_ooo_info: unknown amount of connections\n"); + return NULL; + } + + p_ooo_info = kzalloc(sizeof(*p_ooo_info), GFP_KERNEL); + if (!p_ooo_info) + return NULL; + + INIT_LIST_HEAD(&p_ooo_info->free_buffers_list); + INIT_LIST_HEAD(&p_ooo_info->ready_buffers_list); + INIT_LIST_HEAD(&p_ooo_info->free_isles_list); + INIT_LIST_HEAD(&p_ooo_info->free_archipelagos_list); + INIT_LIST_HEAD(&p_ooo_info->archipelagos_list); + + p_ooo_info->p_isles_mem = kcalloc(max_num_isles, + sizeof(struct qed_ooo_isle), + GFP_KERNEL); + if (!p_ooo_info->p_isles_mem) + goto no_isles_mem; + + for (i = 0; i < max_num_isles; i++) { + INIT_LIST_HEAD(&p_ooo_info->p_isles_mem[i].buffers_list); + list_add_tail(&p_ooo_info->p_isles_mem[i].list_entry, + &p_ooo_info->free_isles_list); + } + + p_ooo_info->p_archipelagos_mem = + kcalloc(max_num_archipelagos, + sizeof(struct qed_ooo_archipelago), + GFP_KERNEL); + if (!p_ooo_info->p_archipelagos_mem) + goto no_archipelagos_mem; + + for (i = 0; i < max_num_archipelagos; i++) { + INIT_LIST_HEAD(&p_ooo_info->p_archipelagos_mem[i].isles_list); + list_add_tail(&p_ooo_info->p_archipelagos_mem[i].list_entry, + &p_ooo_info->free_archipelagos_list); + } + + p_ooo_info->ooo_history.p_cqes = + kcalloc(QED_MAX_NUM_OOO_HISTORY_ENTRIES, + sizeof(struct ooo_opaque), + GFP_KERNEL); + if (!p_ooo_info->ooo_history.p_cqes) + goto no_history_mem; + + return p_ooo_info; + +no_history_mem: + kfree(p_ooo_info->p_archipelagos_mem); +no_archipelagos_mem: + kfree(p_ooo_info->p_isles_mem); +no_isles_mem: + kfree(p_ooo_info); + return NULL; +} + +void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, u32 cid) +{ + struct qed_ooo_archipelago *p_archipelago; + struct qed_ooo_buffer *p_buffer; + struct qed_ooo_isle *p_isle; + bool b_found = false; + + if (list_empty(&p_ooo_info->archipelagos_list)) + return; + + list_for_each_entry(p_archipelago, + &p_ooo_info->archipelagos_list, list_entry) { + if (p_archipelago->cid == cid) { + list_del(&p_archipelago->list_entry); + b_found = true; + break; + } + } + + if (!b_found) + return; + + while (!list_empty(&p_archipelago->isles_list)) { + p_isle = list_first_entry(&p_archipelago->isles_list, + struct qed_ooo_isle, list_entry); + + list_del(&p_isle->list_entry); + + while (!list_empty(&p_isle->buffers_list)) { + p_buffer = list_first_entry(&p_isle->buffers_list, + struct qed_ooo_buffer, + list_entry); + + if (!p_buffer) + break; + + list_del(&p_buffer->list_entry); + list_add_tail(&p_buffer->list_entry, + &p_ooo_info->free_buffers_list); + } + list_add_tail(&p_isle->list_entry, + &p_ooo_info->free_isles_list); + } + + list_add_tail(&p_archipelago->list_entry, + &p_ooo_info->free_archipelagos_list); +} + +void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) +{ + struct qed_ooo_archipelago *p_arch; + struct qed_ooo_buffer *p_buffer; + struct qed_ooo_isle *p_isle; + + while (!list_empty(&p_ooo_info->archipelagos_list)) { + p_arch = list_first_entry(&p_ooo_info->archipelagos_list, + struct qed_ooo_archipelago, + list_entry); + + list_del(&p_arch->list_entry); + + while (!list_empty(&p_arch->isles_list)) { + p_isle = list_first_entry(&p_arch->isles_list, + struct qed_ooo_isle, + list_entry); + + list_del(&p_isle->list_entry); + + while (!list_empty(&p_isle->buffers_list)) { + p_buffer = + list_first_entry(&p_isle->buffers_list, + struct qed_ooo_buffer, + list_entry); + + if (!p_buffer) + break; + + list_del(&p_buffer->list_entry); + list_add_tail(&p_buffer->list_entry, + &p_ooo_info->free_buffers_list); + } + list_add_tail(&p_isle->list_entry, + &p_ooo_info->free_isles_list); + } + list_add_tail(&p_arch->list_entry, + &p_ooo_info->free_archipelagos_list); + } + if (!list_empty(&p_ooo_info->ready_buffers_list)) + list_splice_tail_init(&p_ooo_info->ready_buffers_list, + &p_ooo_info->free_buffers_list); +} + +void qed_ooo_setup(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info) +{ + qed_ooo_release_all_isles(p_hwfn, p_ooo_info); + memset(p_ooo_info->ooo_history.p_cqes, 0, + p_ooo_info->ooo_history.num_of_cqes * + sizeof(struct ooo_opaque)); + p_ooo_info->ooo_history.head_idx = 0; +} + +void qed_ooo_free(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info) +{ + struct qed_ooo_buffer *p_buffer; + + qed_ooo_release_all_isles(p_hwfn, p_ooo_info); + while (!list_empty(&p_ooo_info->free_buffers_list)) { + p_buffer = list_first_entry(&p_ooo_info->free_buffers_list, + struct qed_ooo_buffer, list_entry); + + if (!p_buffer) + break; + + list_del(&p_buffer->list_entry); + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + p_buffer->rx_buffer_size, + p_buffer->rx_buffer_virt_addr, + p_buffer->rx_buffer_phys_addr); + kfree(p_buffer); + } + + kfree(p_ooo_info->p_isles_mem); + kfree(p_ooo_info->p_archipelagos_mem); + kfree(p_ooo_info->ooo_history.p_cqes); + kfree(p_ooo_info); +} + +void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct qed_ooo_buffer *p_buffer) +{ + list_add_tail(&p_buffer->list_entry, &p_ooo_info->free_buffers_list); +} + +struct qed_ooo_buffer *qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) +{ + struct qed_ooo_buffer *p_buffer = NULL; + + if (!list_empty(&p_ooo_info->free_buffers_list)) { + p_buffer = list_first_entry(&p_ooo_info->free_buffers_list, + struct qed_ooo_buffer, list_entry); + + list_del(&p_buffer->list_entry); + } + + return p_buffer; +} + +void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct qed_ooo_buffer *p_buffer, u8 on_tail) +{ + if (on_tail) + list_add_tail(&p_buffer->list_entry, + &p_ooo_info->ready_buffers_list); + else + list_add(&p_buffer->list_entry, + &p_ooo_info->ready_buffers_list); +} + +struct qed_ooo_buffer *qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) +{ + struct qed_ooo_buffer *p_buffer = NULL; + + if (!list_empty(&p_ooo_info->ready_buffers_list)) { + p_buffer = list_first_entry(&p_ooo_info->ready_buffers_list, + struct qed_ooo_buffer, list_entry); + + list_del(&p_buffer->list_entry); + } + + return p_buffer; +} + +void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, u8 drop_isle, u8 drop_size) +{ + struct qed_ooo_archipelago *p_archipelago = NULL; + struct qed_ooo_isle *p_isle = NULL; + u8 isle_idx; + + p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid); + for (isle_idx = 0; isle_idx < drop_size; isle_idx++) { + p_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, drop_isle); + if (!p_isle) { + DP_NOTICE(p_hwfn, + "Isle %d is not found(cid %d)\n", + drop_isle, cid); + return; + } + if (list_empty(&p_isle->buffers_list)) + DP_NOTICE(p_hwfn, + "Isle %d is empty(cid %d)\n", drop_isle, cid); + else + list_splice_tail_init(&p_isle->buffers_list, + &p_ooo_info->free_buffers_list); + + list_del(&p_isle->list_entry); + p_ooo_info->cur_isles_number--; + list_add(&p_isle->list_entry, &p_ooo_info->free_isles_list); + } + + if (list_empty(&p_archipelago->isles_list)) { + list_del(&p_archipelago->list_entry); + list_add(&p_archipelago->list_entry, + &p_ooo_info->free_archipelagos_list); + } +} + +void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, u8 ooo_isle, + struct qed_ooo_buffer *p_buffer) +{ + struct qed_ooo_archipelago *p_archipelago = NULL; + struct qed_ooo_isle *p_prev_isle = NULL; + struct qed_ooo_isle *p_isle = NULL; + + if (ooo_isle > 1) { + p_prev_isle = qed_ooo_seek_isle(p_hwfn, + p_ooo_info, cid, ooo_isle - 1); + if (!p_prev_isle) { + DP_NOTICE(p_hwfn, + "Isle %d is not found(cid %d)\n", + ooo_isle - 1, cid); + return; + } + } + p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid); + if (!p_archipelago && (ooo_isle != 1)) { + DP_NOTICE(p_hwfn, + "Connection %d is not found in OOO list\n", cid); + return; + } + + if (!list_empty(&p_ooo_info->free_isles_list)) { + p_isle = list_first_entry(&p_ooo_info->free_isles_list, + struct qed_ooo_isle, list_entry); + + list_del(&p_isle->list_entry); + if (!list_empty(&p_isle->buffers_list)) { + DP_NOTICE(p_hwfn, "Free isle is not empty\n"); + INIT_LIST_HEAD(&p_isle->buffers_list); + } + } else { + DP_NOTICE(p_hwfn, "No more free isles\n"); + return; + } + + if (!p_archipelago && + !list_empty(&p_ooo_info->free_archipelagos_list)) { + p_archipelago = + list_first_entry(&p_ooo_info->free_archipelagos_list, + struct qed_ooo_archipelago, list_entry); + + list_del(&p_archipelago->list_entry); + if (!list_empty(&p_archipelago->isles_list)) { + DP_NOTICE(p_hwfn, + "Free OOO connection is not empty\n"); + INIT_LIST_HEAD(&p_archipelago->isles_list); + } + p_archipelago->cid = cid; + list_add(&p_archipelago->list_entry, + &p_ooo_info->archipelagos_list); + } else if (!p_archipelago) { + DP_NOTICE(p_hwfn, "No more free OOO connections\n"); + list_add(&p_isle->list_entry, + &p_ooo_info->free_isles_list); + list_add(&p_buffer->list_entry, + &p_ooo_info->free_buffers_list); + return; + } + + list_add(&p_buffer->list_entry, &p_isle->buffers_list); + p_ooo_info->cur_isles_number++; + p_ooo_info->gen_isles_number++; + + if (p_ooo_info->cur_isles_number > p_ooo_info->max_isles_number) + p_ooo_info->max_isles_number = p_ooo_info->cur_isles_number; + + if (!p_prev_isle) + list_add(&p_isle->list_entry, &p_archipelago->isles_list); + else + list_add(&p_isle->list_entry, &p_prev_isle->list_entry); +} + +void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, + u8 ooo_isle, + struct qed_ooo_buffer *p_buffer, u8 buffer_side) +{ + struct qed_ooo_isle *p_isle = NULL; + + p_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, ooo_isle); + if (!p_isle) { + DP_NOTICE(p_hwfn, + "Isle %d is not found(cid %d)\n", ooo_isle, cid); + return; + } + + if (buffer_side == QED_OOO_LEFT_BUF) + list_add(&p_buffer->list_entry, &p_isle->buffers_list); + else + list_add_tail(&p_buffer->list_entry, &p_isle->buffers_list); +} + +void qed_ooo_join_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, u32 cid, u8 left_isle) +{ + struct qed_ooo_archipelago *p_archipelago = NULL; + struct qed_ooo_isle *p_right_isle = NULL; + struct qed_ooo_isle *p_left_isle = NULL; + + p_right_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, + left_isle + 1); + if (!p_right_isle) { + DP_NOTICE(p_hwfn, + "Right isle %d is not found(cid %d)\n", + left_isle + 1, cid); + return; + } + + p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid); + list_del(&p_right_isle->list_entry); + p_ooo_info->cur_isles_number--; + if (left_isle) { + p_left_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, + left_isle); + if (!p_left_isle) { + DP_NOTICE(p_hwfn, + "Left isle %d is not found(cid %d)\n", + left_isle, cid); + return; + } + list_splice_tail_init(&p_right_isle->buffers_list, + &p_left_isle->buffers_list); + } else { + list_splice_tail_init(&p_right_isle->buffers_list, + &p_ooo_info->ready_buffers_list); + if (list_empty(&p_archipelago->isles_list)) { + list_del(&p_archipelago->list_entry); + list_add(&p_archipelago->list_entry, + &p_ooo_info->free_archipelagos_list); + } + } + list_add_tail(&p_right_isle->list_entry, &p_ooo_info->free_isles_list); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_ooo.h b/drivers/net/ethernet/qlogic/qed/qed_ooo.h new file mode 100644 index 0000000..7a0670a --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_ooo.h @@ -0,0 +1,176 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QED_OOO_H +#define _QED_OOO_H +#include +#include +#include +#include "qed.h" + +#define QED_MAX_NUM_ISLES 256 +#define QED_MAX_NUM_OOO_HISTORY_ENTRIES 512 + +#define QED_OOO_LEFT_BUF 0 +#define QED_OOO_RIGHT_BUF 1 + +struct qed_ooo_buffer { + struct list_head list_entry; + void *rx_buffer_virt_addr; + dma_addr_t rx_buffer_phys_addr; + u32 rx_buffer_size; + u16 packet_length; + u16 parse_flags; + u16 vlan; + u8 placement_offset; +}; + +struct qed_ooo_isle { + struct list_head list_entry; + struct list_head buffers_list; +}; + +struct qed_ooo_archipelago { + struct list_head list_entry; + struct list_head isles_list; + u32 cid; +}; + +struct qed_ooo_history { + struct ooo_opaque *p_cqes; + u32 head_idx; + u32 num_of_cqes; +}; + +struct qed_ooo_info { + struct list_head free_buffers_list; + struct list_head ready_buffers_list; + struct list_head free_isles_list; + struct list_head free_archipelagos_list; + struct list_head archipelagos_list; + struct qed_ooo_archipelago *p_archipelagos_mem; + struct qed_ooo_isle *p_isles_mem; + struct qed_ooo_history ooo_history; + u32 cur_isles_number; + u32 max_isles_number; + u32 gen_isles_number; +}; + +#if IS_ENABLED(CONFIG_QED_ISCSI) +void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct ooo_opaque *p_cqe); + +struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn); + +void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid); + +void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info); + +void qed_ooo_setup(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info); + +void qed_ooo_free(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info); + +void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct qed_ooo_buffer *p_buffer); + +struct qed_ooo_buffer * +qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info); + +void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct qed_ooo_buffer *p_buffer, u8 on_tail); + +struct qed_ooo_buffer * +qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info); + +void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, u8 drop_isle, u8 drop_size); + +void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, + u8 ooo_isle, struct qed_ooo_buffer *p_buffer); + +void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, + u8 ooo_isle, + struct qed_ooo_buffer *p_buffer, u8 buffer_side); + +void qed_ooo_join_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, u32 cid, + u8 left_isle); +#else /* IS_ENABLED(CONFIG_QED_ISCSI) */ +static inline void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct ooo_opaque *p_cqe) {} + +static inline struct qed_ooo_info *qed_ooo_alloc( + struct qed_hwfn *p_hwfn) { return NULL; } + +static inline void +qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid) {} + +static inline void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) + {} + +static inline void qed_ooo_setup(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) {} + +static inline void qed_ooo_free(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) {} + +static inline void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct qed_ooo_buffer *p_buffer) {} + +static inline struct qed_ooo_buffer * +qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) { return NULL; } + +static inline void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + struct qed_ooo_buffer *p_buffer, + u8 on_tail) {} + +static inline struct qed_ooo_buffer * +qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info) { return NULL; } + +static inline void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, u8 drop_isle, u8 drop_size) {} + +static inline void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, u8 ooo_isle, + struct qed_ooo_buffer *p_buffer) {} + +static inline void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, + u32 cid, u8 ooo_isle, + struct qed_ooo_buffer *p_buffer, + u8 buffer_side) {} + +static inline void qed_ooo_join_isles(struct qed_hwfn *p_hwfn, + struct qed_ooo_info *p_ooo_info, u32 cid, + u8 left_isle) {} +#endif /* IS_ENABLED(CONFIG_QED_ISCSI) */ + +#endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 6a353ff..2a16547 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -2771,6 +2771,7 @@ static int qed_roce_ll2_tx(struct qed_dev *cdev, /* Tx header */ rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev), roce_ll2->handle, 1 + pkt->n_seg, 0, flags, 0, + QED_LL2_TX_DEST_NW, qed_roce_flavor, pkt->header.baddr, pkt->header.len, pkt, 1); if (rc) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index 56d2f64..f022469 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -26,6 +26,7 @@ #include "qed_int.h" #include "qed_iscsi.h" #include "qed_mcp.h" +#include "qed_ooo.h" #include "qed_reg_addr.h" #include "qed_sp.h" #include "qed_sriov.h" @@ -281,6 +282,14 @@ qed_async_event_completion(struct qed_hwfn *p_hwfn, case PROTOCOLID_ISCSI: if (!IS_ENABLED(CONFIG_QED_ISCSI)) return -EINVAL; + if (p_eqe->opcode == ISCSI_EVENT_TYPE_ASYN_DELETE_OOO_ISLES) { + u32 cid = le32_to_cpu(p_eqe->data.iscsi_info.cid); + + qed_ooo_release_connection_isles(p_hwfn, + p_hwfn->p_ooo_info, + cid); + return 0; + } if (p_hwfn->p_iscsi_info->event_cb) { struct qed_iscsi_info *p_iscsi = p_hwfn->p_iscsi_info; -- cgit v1.1 From 95a22caee396cef0bb2ca8fafdd82966a49367bb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 1 Dec 2016 11:32:06 +0100 Subject: tcp: randomize tcp timestamp offsets for each connection jiffies based timestamps allow for easy inference of number of devices behind NAT translators and also makes tracking of hosts simpler. commit ceaa1fef65a7c2e ("tcp: adding a per-socket timestamp offset") added the main infrastructure that is needed for per-connection ts randomization, in particular writing/reading the on-wire tcp header format takes the offset into account so rest of stack can use normal tcp_time_stamp (jiffies). So only two items are left: - add a tsoffset for request sockets - extend the tcp isn generator to also return another 32bit number in addition to the ISN. Re-use of ISN generator also means timestamps are still monotonically increasing for same connection quadruple, i.e. PAWS will still work. Includes fixes from Eric Dumazet. Signed-off-by: Florian Westphal Acked-by: Eric Dumazet Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + include/net/secure_seq.h | 8 ++++---- include/net/tcp.h | 2 +- net/core/secure_seq.c | 10 ++++++---- net/ipv4/syncookies.c | 1 + net/ipv4/tcp_input.c | 7 ++++++- net/ipv4/tcp_ipv4.c | 9 +++++---- net/ipv4/tcp_minisocks.c | 4 +++- net/ipv4/tcp_output.c | 2 +- net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 10 ++++++---- 11 files changed, 35 insertions(+), 20 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 00e0ee8..734bab4 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -123,6 +123,7 @@ struct tcp_request_sock { u32 txhash; u32 rcv_isn; u32 snt_isn; + u32 ts_off; u32 last_oow_ack_time; /* last SYNACK */ u32 rcv_nxt; /* the ack # by SYNACK. For * FastOpen it's the seq# diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h index 3f36d45..0caee63 100644 --- a/include/net/secure_seq.h +++ b/include/net/secure_seq.h @@ -6,10 +6,10 @@ u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, __be16 dport); -__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport); -__u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, - __be16 sport, __be16 dport); +u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport, u32 *tsoff); +u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport, u32 *tsoff); u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport); u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, diff --git a/include/net/tcp.h b/include/net/tcp.h index 3e097e3..207147b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1827,7 +1827,7 @@ struct tcp_request_sock_ops { struct dst_entry *(*route_req)(const struct sock *sk, struct flowi *fl, const struct request_sock *req, bool *strict); - __u32 (*init_seq)(const struct sk_buff *skb); + __u32 (*init_seq)(const struct sk_buff *skb, u32 *tsoff); int (*send_synack)(const struct sock *sk, struct dst_entry *dst, struct flowi *fl, struct request_sock *req, struct tcp_fastopen_cookie *foc, diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index fd3ce46..a8d6062 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -40,8 +40,8 @@ static u32 seq_scale(u32 seq) #endif #if IS_ENABLED(CONFIG_IPV6) -__u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, - __be16 sport, __be16 dport) +u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport, u32 *tsoff) { u32 secret[MD5_MESSAGE_BYTES / 4]; u32 hash[MD5_DIGEST_WORDS]; @@ -58,6 +58,7 @@ __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, md5_transform(hash, secret); + *tsoff = hash[1]; return seq_scale(hash[0]); } EXPORT_SYMBOL(secure_tcpv6_sequence_number); @@ -86,8 +87,8 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral); #ifdef CONFIG_INET -__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport) +u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport, u32 *tsoff) { u32 hash[MD5_DIGEST_WORDS]; @@ -99,6 +100,7 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, md5_transform(hash, net_secret); + *tsoff = hash[1]; return seq_scale(hash[0]); } diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 0dc6286..3e88467 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -334,6 +334,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) treq = tcp_rsk(req); treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = cookie; + treq->ts_off = 0; req->mss = mss; ireq->ir_num = ntohs(th->dest); ireq->ir_rmt_port = th->source; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 56fe736..2257de2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6307,6 +6307,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop; tcp_rsk(req)->af_specific = af_ops; + tcp_rsk(req)->ts_off = 0; tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = af_ops->mss_clamp; @@ -6328,6 +6329,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; + if (isn && tmp_opt.tstamp_ok) + af_ops->init_seq(skb, &tcp_rsk(req)->ts_off); + if (!want_cookie && !isn) { /* VJ's idea. We save last timestamp seen * from the destination in peer table, when entering @@ -6368,7 +6372,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop_and_release; } - isn = af_ops->init_seq(skb); + isn = af_ops->init_seq(skb, &tcp_rsk(req)->ts_off); } if (!dst) { dst = af_ops->route_req(sk, &fl, req, NULL); @@ -6380,6 +6384,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, if (want_cookie) { isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); + tcp_rsk(req)->ts_off = 0; req->cookie_ts = tmp_opt.tstamp_ok; if (!tmp_opt.tstamp_ok) inet_rsk(req)->ecn_ok = 0; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5555eb8..b50f059 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -95,12 +95,12 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, struct inet_hashinfo tcp_hashinfo; EXPORT_SYMBOL(tcp_hashinfo); -static __u32 tcp_v4_init_sequence(const struct sk_buff *skb) +static u32 tcp_v4_init_sequence(const struct sk_buff *skb, u32 *tsoff) { return secure_tcp_sequence_number(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, tcp_hdr(skb)->dest, - tcp_hdr(skb)->source); + tcp_hdr(skb)->source, tsoff); } int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp) @@ -237,7 +237,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr, inet->inet_daddr, inet->inet_sport, - usin->sin_port); + usin->sin_port, + &tp->tsoffset); inet->inet_id = tp->write_seq ^ jiffies; @@ -824,7 +825,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, tcp_v4_send_ack(sk, skb, seq, tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, - tcp_time_stamp, + tcp_time_stamp + tcp_rsk(req)->ts_off, req->ts_recent, 0, tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr, diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 6234eba..28ce5ee 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -532,7 +532,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->rx_opt.ts_recent_stamp = 0; newtp->tcp_header_len = sizeof(struct tcphdr); } - newtp->tsoffset = 0; + newtp->tsoffset = treq->ts_off; #ifdef CONFIG_TCP_MD5SIG newtp->md5sig_info = NULL; /*XXX*/ if (newtp->af_specific->md5_lookup(sk, newsk)) @@ -581,6 +581,8 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, if (tmp_opt.saw_tstamp) { tmp_opt.ts_recent = req->ts_recent; + if (tmp_opt.rcv_tsecr) + tmp_opt.rcv_tsecr -= tcp_rsk(req)->ts_off; /* We do not store true stamp, but it is not required, * it can be estimated (approximately) * from another data. diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d3545d0..c7adcb5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -640,7 +640,7 @@ static unsigned int tcp_synack_options(struct request_sock *req, } if (likely(ireq->tstamp_ok)) { opts->options |= OPTION_TS; - opts->tsval = tcp_skb_timestamp(skb); + opts->tsval = tcp_skb_timestamp(skb) + tcp_rsk(req)->ts_off; opts->tsecr = req->ts_recent; remaining -= TCPOLEN_TSTAMP_ALIGNED; } diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 97830a6..a4d4976 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -209,6 +209,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) treq->snt_synack.v64 = 0; treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = cookie; + treq->ts_off = 0; /* * We need to lookup the dst_entry to get the correct window size. diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 28ec0a2..a2185a2 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -101,12 +101,12 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) } } -static __u32 tcp_v6_init_sequence(const struct sk_buff *skb) +static u32 tcp_v6_init_sequence(const struct sk_buff *skb, u32 *tsoff) { return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, ipv6_hdr(skb)->saddr.s6_addr32, tcp_hdr(skb)->dest, - tcp_hdr(skb)->source); + tcp_hdr(skb)->source, tsoff); } static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, @@ -283,7 +283,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32, sk->sk_v6_daddr.s6_addr32, inet->inet_sport, - inet->inet_dport); + inet->inet_dport, + &tp->tsoffset); err = tcp_connect(sk); if (err) @@ -956,7 +957,8 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, - tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if, + tcp_time_stamp + tcp_rsk(req)->ts_off, + req->ts_recent, sk->sk_bound_dev_if, tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), 0, 0); } -- cgit v1.1 From 25429d7b7dca01dc4f17205de023a30ca09390d0 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 1 Dec 2016 11:32:07 +0100 Subject: tcp: allow to turn tcp timestamp randomization off Eric says: "By looking at tcpdump, and TS val of xmit packets of multiple flows, we can deduct the relative qdisc delays (think of fq pacing). This should work even if we have one flow per remote peer." Having random per flow (or host) offsets doesn't allow that anymore so add a way to turn this off. Suggested-by: Eric Dumazet Signed-off-by: Florian Westphal Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 9 +++++++-- net/core/secure_seq.c | 5 +++-- net/ipv4/tcp_input.c | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 5af48dd..5ca567f 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -610,8 +610,13 @@ tcp_syn_retries - INTEGER with the current initial RTO of 1second. With this the final timeout for an active TCP connection attempt will happen after 127seconds. -tcp_timestamps - BOOLEAN - Enable timestamps as defined in RFC1323. +tcp_timestamps - INTEGER +Enable timestamps as defined in RFC1323. + 0: Disabled. + 1: Enable timestamps as defined in RFC1323 and use random offset for + each connection rather than only using the current time. + 2: Like 1, but without random offsets. + Default: 1 tcp_min_tso_segs - INTEGER Minimal number of segments per TSO frame. diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index a8d6062..88a8e42 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -12,6 +12,7 @@ #include #if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET) +#include #define NET_SECRET_SIZE (MD5_MESSAGE_BYTES / 4) static u32 net_secret[NET_SECRET_SIZE] ____cacheline_aligned; @@ -58,7 +59,7 @@ u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, md5_transform(hash, secret); - *tsoff = hash[1]; + *tsoff = sysctl_tcp_timestamps == 1 ? hash[1] : 0; return seq_scale(hash[0]); } EXPORT_SYMBOL(secure_tcpv6_sequence_number); @@ -100,7 +101,7 @@ u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, md5_transform(hash, net_secret); - *tsoff = hash[1]; + *tsoff = sysctl_tcp_timestamps == 1 ? hash[1] : 0; return seq_scale(hash[0]); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2257de2..fe668c1 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -85,6 +85,7 @@ int sysctl_tcp_dsack __read_mostly = 1; int sysctl_tcp_app_win __read_mostly = 31; int sysctl_tcp_adv_win_scale __read_mostly = 1; EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); +EXPORT_SYMBOL(sysctl_tcp_timestamps); /* rfc5961 challenge ack rate limiting */ int sysctl_tcp_challenge_ack_limit = 1000; -- cgit v1.1 From 55330f05969437c5d22fcc2ae2e54810b5236b7b Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:33 +0200 Subject: net/sched: Add separate check for skip_hw flag Creating a difference between two possible cases: 1. Not offloading tc rule since the user sets 'skip_hw' flag. 2. Not offloading tc rule since the device doesn't support offloading. This patch doesn't add any new functionality. Signed-off-by: Hadar Hen Zion Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 767b03a..45ad9aa 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -425,16 +425,14 @@ struct tc_cls_u32_offload { }; }; -static inline bool tc_should_offload(const struct net_device *dev, - const struct tcf_proto *tp, u32 flags) +static inline bool tc_can_offload(const struct net_device *dev, + const struct tcf_proto *tp) { const struct Qdisc *sch = tp->q; const struct Qdisc_class_ops *cops = sch->ops->cl_ops; if (!(dev->features & NETIF_F_HW_TC)) return false; - if (flags & TCA_CLS_FLAGS_SKIP_HW) - return false; if (!dev->netdev_ops->ndo_setup_tc) return false; if (cops && cops->tcf_cl_offload) @@ -443,6 +441,19 @@ static inline bool tc_should_offload(const struct net_device *dev, return true; } +static inline bool tc_skip_hw(u32 flags) +{ + return (flags & TCA_CLS_FLAGS_SKIP_HW) ? true : false; +} + +static inline bool tc_should_offload(const struct net_device *dev, + const struct tcf_proto *tp, u32 flags) +{ + if (tc_skip_hw(flags)) + return false; + return tc_can_offload(dev, tp); +} + static inline bool tc_skip_sw(u32 flags) { return (flags & TCA_CLS_FLAGS_SKIP_SW) ? true : false; -- cgit v1.1 From 796852197c7a1a3a69ee5184d68bf16885bef65b Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:34 +0200 Subject: net/sched: cls_flower: Try to offload only if skip_hw flag isn't set Check skip_hw flag isn't set before calling fl_hw_{replace/destroy}_filter and fl_hw_update_stats functions. Replace the call to tc_should_offload with tc_can_offload. tc_can_offload only checks if the device supports offloading, the check for skip_hw flag is done earlier in the flow. Signed-off-by: Hadar Hen Zion Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index e8dd09a..5e70f65 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -207,7 +207,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, unsigned long cookie) struct tc_cls_flower_offload offload = {0}; struct tc_to_netdev tc; - if (!tc_should_offload(dev, tp, 0)) + if (!tc_can_offload(dev, tp)) return; offload.command = TC_CLSFLOWER_DESTROY; @@ -231,7 +231,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, struct tc_to_netdev tc; int err; - if (!tc_should_offload(dev, tp, flags)) + if (!tc_can_offload(dev, tp)) return tc_skip_sw(flags) ? -EINVAL : 0; offload.command = TC_CLSFLOWER_REPLACE; @@ -259,7 +259,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) struct tc_cls_flower_offload offload = {0}; struct tc_to_netdev tc; - if (!tc_should_offload(dev, tp, 0)) + if (!tc_can_offload(dev, tp)) return; offload.command = TC_CLSFLOWER_STATS; @@ -275,7 +275,8 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) { list_del_rcu(&f->list); - fl_hw_destroy_filter(tp, (unsigned long)f); + if (!tc_skip_hw(f->flags)) + fl_hw_destroy_filter(tp, (unsigned long)f); tcf_unbind_filter(tp, &f->res); call_rcu(&f->rcu, fl_destroy_filter); } @@ -743,20 +744,23 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, goto errout; } - err = fl_hw_replace_filter(tp, - &head->dissector, - &mask.key, - &fnew->key, - &fnew->exts, - (unsigned long)fnew, - fnew->flags); - if (err) - goto errout; + if (!tc_skip_hw(fnew->flags)) { + err = fl_hw_replace_filter(tp, + &head->dissector, + &mask.key, + &fnew->key, + &fnew->exts, + (unsigned long)fnew, + fnew->flags); + if (err) + goto errout; + } if (fold) { rhashtable_remove_fast(&head->ht, &fold->ht_node, head->ht_params); - fl_hw_destroy_filter(tp, (unsigned long)fold); + if (!tc_skip_hw(fold->flags)) + fl_hw_destroy_filter(tp, (unsigned long)fold); } *arg = (unsigned long) fnew; @@ -879,7 +883,8 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, goto nla_put_failure; } - fl_hw_update_stats(tp, f); + if (!tc_skip_hw(f->flags)) + fl_hw_update_stats(tp, f); if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, -- cgit v1.1 From 3036dab670bae38f2ece608e69232f5b20aa6015 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:35 +0200 Subject: net/sched: cls_flower: Provide a filter to replace/destroy hardware filter functions Instead of providing many arguments to fl_hw_{replace/destroy}_filter functions, just provide cls_fl_filter struct that includes all the relevant args. This patches doesn't add any new functionality. Signed-off-by: Hadar Hen Zion Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 5e70f65..13b349f 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -201,7 +201,7 @@ static void fl_destroy_filter(struct rcu_head *head) kfree(f); } -static void fl_hw_destroy_filter(struct tcf_proto *tp, unsigned long cookie) +static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_flower_offload offload = {0}; @@ -211,7 +211,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, unsigned long cookie) return; offload.command = TC_CLSFLOWER_DESTROY; - offload.cookie = cookie; + offload.cookie = (unsigned long)f; tc.type = TC_SETUP_CLSFLOWER; tc.cls_flower = &offload; @@ -222,9 +222,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, unsigned long cookie) static int fl_hw_replace_filter(struct tcf_proto *tp, struct flow_dissector *dissector, struct fl_flow_key *mask, - struct fl_flow_key *key, - struct tcf_exts *actions, - unsigned long cookie, u32 flags) + struct cls_fl_filter *f) { struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_flower_offload offload = {0}; @@ -232,14 +230,14 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, int err; if (!tc_can_offload(dev, tp)) - return tc_skip_sw(flags) ? -EINVAL : 0; + return tc_skip_sw(f->flags) ? -EINVAL : 0; offload.command = TC_CLSFLOWER_REPLACE; - offload.cookie = cookie; + offload.cookie = (unsigned long)f; offload.dissector = dissector; offload.mask = mask; - offload.key = key; - offload.exts = actions; + offload.key = &f->key; + offload.exts = &f->exts; tc.type = TC_SETUP_CLSFLOWER; tc.cls_flower = &offload; @@ -247,7 +245,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, &tc); - if (tc_skip_sw(flags)) + if (tc_skip_sw(f->flags)) return err; return 0; @@ -276,7 +274,7 @@ static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) { list_del_rcu(&f->list); if (!tc_skip_hw(f->flags)) - fl_hw_destroy_filter(tp, (unsigned long)f); + fl_hw_destroy_filter(tp, f); tcf_unbind_filter(tp, &f->res); call_rcu(&f->rcu, fl_destroy_filter); } @@ -748,10 +746,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, err = fl_hw_replace_filter(tp, &head->dissector, &mask.key, - &fnew->key, - &fnew->exts, - (unsigned long)fnew, - fnew->flags); + fnew); if (err) goto errout; } @@ -760,7 +755,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, rhashtable_remove_fast(&head->ht, &fold->ht_node, head->ht_params); if (!tc_skip_hw(fold->flags)) - fl_hw_destroy_filter(tp, (unsigned long)fold); + fl_hw_destroy_filter(tp, fold); } *arg = (unsigned long) fnew; -- cgit v1.1 From 255cb30425c0ced57d6d85f3e7cddb99b9576046 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:36 +0200 Subject: net/sched: act_mirred: Add new tc_action_ops get_dev() Adding support to a new tc_action_ops. get_dev is a general option which allows to get the underline device when trying to offload a tc rule. In case of mirred action the returned device is the mirred (egress) device. Signed-off-by: Hadar Hen Zion Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/act_api.h | 2 ++ net/sched/act_mirred.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/net/act_api.h b/include/net/act_api.h index d8eae87..9dddf77 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -119,6 +119,8 @@ struct tc_action_ops { int (*walk)(struct net *, struct sk_buff *, struct netlink_callback *, int, const struct tc_action_ops *); void (*stats_update)(struct tc_action *, u64, u32, u64); + int (*get_dev)(const struct tc_action *a, struct net *net, + struct net_device **mirred_dev); }; struct tc_action_net { diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 1af7baa..bb09ba3 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -315,6 +315,17 @@ static struct notifier_block mirred_device_notifier = { .notifier_call = mirred_device_event, }; +static int tcf_mirred_device(const struct tc_action *a, struct net *net, + struct net_device **mirred_dev) +{ + int ifindex = tcf_mirred_ifindex(a); + + *mirred_dev = __dev_get_by_index(net, ifindex); + if (!mirred_dev) + return -EINVAL; + return 0; +} + static struct tc_action_ops act_mirred_ops = { .kind = "mirred", .type = TCA_ACT_MIRRED, @@ -327,6 +338,7 @@ static struct tc_action_ops act_mirred_ops = { .walk = tcf_mirred_walker, .lookup = tcf_mirred_search, .size = sizeof(struct tcf_mirred), + .get_dev = tcf_mirred_device, }; static __net_init int mirred_init_net(struct net *net) -- cgit v1.1 From 7091d8c7055d7310339435ae3af2fb490a92524d Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:37 +0200 Subject: net/sched: cls_flower: Add offload support using egress Hardware device In order to support hardware offloading when the device given by the tc rule is different from the Hardware underline device, extract the mirred (egress) device from the tc action when a filter is added, using the new tc_action_ops, get_dev(). Flower caches the information about the mirred device and use it for calling ndo_setup_tc in filter change, update stats and delete. Calling ndo_setup_tc of the mirred (egress) device instead of the ingress device will allow a resolution between the software ingress device and the underline hardware device. The resolution will take place inside the offloading driver using 'egress_device' flag added to tc_to_netdev struct which is provided to the offloading driver. Signed-off-by: Hadar Hen Zion Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/pkt_cls.h | 2 ++ net/sched/cls_api.c | 24 ++++++++++++++++++++++++ net/sched/cls_flower.c | 41 ++++++++++++++++++++++++----------------- 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3755317..1ff5ea6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -802,6 +802,7 @@ struct tc_to_netdev { struct tc_cls_matchall_offload *cls_mall; struct tc_cls_bpf_offload *cls_bpf; }; + bool egress_dev; }; /* These structures hold the attributes of xdp state that are being passed diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 45ad9aa..f0a0514 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -171,6 +171,8 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, struct tcf_exts *src); int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts); +int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, + struct net_device **hw_dev); /** * struct tcf_pkt_info - packet information diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b05d4a2..3fbba79 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -682,6 +682,30 @@ int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) } EXPORT_SYMBOL(tcf_exts_dump_stats); +int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, + struct net_device **hw_dev) +{ +#ifdef CONFIG_NET_CLS_ACT + const struct tc_action *a; + LIST_HEAD(actions); + + if (tc_no_actions(exts)) + return -EINVAL; + + tcf_exts_to_list(exts, &actions); + list_for_each_entry(a, &actions, list) { + if (a->ops->get_dev) { + a->ops->get_dev(a, dev_net(dev), hw_dev); + break; + } + } + if (*hw_dev) + return 0; +#endif + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(tcf_exts_get_dev); + static int __init tc_filter_init(void) { rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL); diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 13b349f..1cacfa5 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -78,6 +78,8 @@ struct cls_fl_filter { u32 handle; u32 flags; struct rcu_head rcu; + struct tc_to_netdev tc; + struct net_device *hw_dev; }; static unsigned short int fl_mask_range(const struct fl_flow_mask *mask) @@ -203,9 +205,9 @@ static void fl_destroy_filter(struct rcu_head *head) static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { - struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_flower_offload offload = {0}; - struct tc_to_netdev tc; + struct net_device *dev = f->hw_dev; + struct tc_to_netdev *tc = &f->tc; if (!tc_can_offload(dev, tp)) return; @@ -213,10 +215,10 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) offload.command = TC_CLSFLOWER_DESTROY; offload.cookie = (unsigned long)f; - tc.type = TC_SETUP_CLSFLOWER; - tc.cls_flower = &offload; + tc->type = TC_SETUP_CLSFLOWER; + tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, &tc); + dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, tc); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -226,11 +228,17 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, { struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_flower_offload offload = {0}; - struct tc_to_netdev tc; + struct tc_to_netdev *tc = &f->tc; int err; - if (!tc_can_offload(dev, tp)) - return tc_skip_sw(f->flags) ? -EINVAL : 0; + if (!tc_can_offload(dev, tp)) { + if (tcf_exts_get_dev(dev, &f->exts, &f->hw_dev)) + return tc_skip_sw(f->flags) ? -EINVAL : 0; + dev = f->hw_dev; + tc->egress_dev = true; + } else { + f->hw_dev = dev; + } offload.command = TC_CLSFLOWER_REPLACE; offload.cookie = (unsigned long)f; @@ -239,23 +247,22 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, offload.key = &f->key; offload.exts = &f->exts; - tc.type = TC_SETUP_CLSFLOWER; - tc.cls_flower = &offload; + tc->type = TC_SETUP_CLSFLOWER; + tc->cls_flower = &offload; err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, - &tc); + tc); if (tc_skip_sw(f->flags)) return err; - return 0; } static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) { - struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_flower_offload offload = {0}; - struct tc_to_netdev tc; + struct net_device *dev = f->hw_dev; + struct tc_to_netdev *tc = &f->tc; if (!tc_can_offload(dev, tp)) return; @@ -264,10 +271,10 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) offload.cookie = (unsigned long)f; offload.exts = &f->exts; - tc.type = TC_SETUP_CLSFLOWER; - tc.cls_flower = &offload; + tc->type = TC_SETUP_CLSFLOWER; + tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, &tc); + dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, tc); } static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) -- cgit v1.1 From 718f13e72be48e2e088f8a3993017c281f275dcd Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:38 +0200 Subject: net/mlx5e: Bring back representor's ndos that were accidentally removed The VF Representor udp tunnel ndo entries were removed by mistake, return them. Fixes: 370bad0f9a52 ('net/mlx5e: Support HW (offloaded) and SW counters for SRIOV switchdev mode') Signed-off-by: Hadar Hen Zion Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 5e33f6b..9b1e351 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -384,6 +384,8 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = { .ndo_get_phys_port_name = mlx5e_rep_get_phys_port_name, .ndo_setup_tc = mlx5e_rep_ndo_setup_tc, .ndo_get_stats64 = mlx5e_rep_get_stats, + .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, + .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, .ndo_has_offload_stats = mlx5e_has_offload_stats, .ndo_get_offload_stats = mlx5e_get_offload_stats, }; -- cgit v1.1 From 726293f1f851242c1c308e71ef9dfd0c2251f94a Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:39 +0200 Subject: net/mlx5e: Save the represntor netdevice as part of the representor Replace the representor private data to a net_device pointer holding the representor netdevice, instead of void pointer holding mlx5e_priv. It will be used by a new eswitch service function, returning the uplink representor netdevice. Signed-off-by: Hadar Hen Zion Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 15 ++++++++------- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 3 ++- .../net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 12 +++++++++++- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 84a4adb..9def5cc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3811,7 +3811,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) rep.load = mlx5e_nic_rep_load; rep.unload = mlx5e_nic_rep_unload; rep.vport = FDB_UPLINK_VPORT; - rep.priv_data = priv; + rep.netdev = netdev; mlx5_eswitch_register_vport_rep(esw, 0, &rep); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 9b1e351..0868677 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -208,7 +208,8 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) int mlx5e_nic_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { - struct mlx5e_priv *priv = rep->priv_data; + struct net_device *netdev = rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); if (test_bit(MLX5E_STATE_OPENED, &priv->state)) return mlx5e_add_sqs_fwd_rules(priv); @@ -226,7 +227,8 @@ void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) void mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { - struct mlx5e_priv *priv = rep->priv_data; + struct net_device *netdev = rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); if (test_bit(MLX5E_STATE_OPENED, &priv->state)) mlx5e_remove_sqs_fwd_rules(priv); @@ -555,7 +557,7 @@ int mlx5e_vport_rep_load(struct mlx5_eswitch *esw, return -EINVAL; } - rep->priv_data = netdev_priv(netdev); + rep->netdev = netdev; err = mlx5e_attach_netdev(esw->dev, netdev); if (err) { @@ -577,7 +579,7 @@ err_detach_netdev: mlx5e_detach_netdev(esw->dev, netdev); err_destroy_netdev: - mlx5e_destroy_netdev(esw->dev, rep->priv_data); + mlx5e_destroy_netdev(esw->dev, netdev_priv(netdev)); return err; @@ -586,10 +588,9 @@ err_destroy_netdev: void mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { - struct mlx5e_priv *priv = rep->priv_data; - struct net_device *netdev = priv->netdev; + struct net_device *netdev = rep->netdev; unregister_netdev(netdev); mlx5e_detach_netdev(esw->dev, netdev); - mlx5e_destroy_netdev(esw->dev, priv); + mlx5e_destroy_netdev(esw->dev, netdev_priv(netdev)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index cf1aa56..8661dd3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -186,7 +186,7 @@ struct mlx5_eswitch_rep { struct mlx5_eswitch_rep *rep); u16 vport; u8 hw_id[ETH_ALEN]; - void *priv_data; + struct net_device *netdev; struct mlx5_flow_handle *vport_rx_rule; struct list_head vport_sqs_list; @@ -318,6 +318,7 @@ void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep); void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw, int vport_index); +struct net_device *mlx5_eswitch_get_uplink_netdev(struct mlx5_eswitch *esw); int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw, struct mlx5_esw_flow_attr *attr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 5c01550..466e161 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -970,7 +970,7 @@ void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw, rep->load = __rep->load; rep->unload = __rep->unload; rep->vport = __rep->vport; - rep->priv_data = __rep->priv_data; + rep->netdev = __rep->netdev; ether_addr_copy(rep->hw_id, __rep->hw_id); INIT_LIST_HEAD(&rep->vport_sqs_list); @@ -990,3 +990,13 @@ void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw, rep->valid = false; } + +struct net_device *mlx5_eswitch_get_uplink_netdev(struct mlx5_eswitch *esw) +{ +#define UPLINK_REP_INDEX 0 + struct mlx5_esw_offload *offloads = &esw->offloads; + struct mlx5_eswitch_rep *rep; + + rep = &offloads->vport_reps[UPLINK_REP_INDEX]; + return rep->netdev; +} -- cgit v1.1 From ebe06875ff1f3d5257c4da7bc74a87ee9e0ce491 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 1 Dec 2016 14:06:40 +0200 Subject: net/mlx5e: Support adding ingress tc rule when egress device flag is set When ndo_setup_tc is called with an egress_dev flag set, it means that the ndo call was executed on the mirred action (egress) device and not on the ingress device. In order to support this kind of ndo_setup_tc call, and insert the correct decap rule to the hardware, the uplink device on the same eswitch should be found. Currently, we use this resolution between the mirred device and the uplink on the same eswitch to offload vxlan shared device decap rules. Signed-off-by: Hadar Hen Zion Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 0868677..8503788 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -289,6 +289,14 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) return -EOPNOTSUPP; + if (tc->egress_dev) { + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct net_device *uplink_dev = mlx5_eswitch_get_uplink_netdev(esw); + + return uplink_dev->netdev_ops->ndo_setup_tc(uplink_dev, handle, + proto, tc); + } + switch (tc->type) { case TC_SETUP_CLSFLOWER: switch (tc->cls_flower->command) { -- cgit v1.1 From bd3ad7d3a14b07aeeb4f92abc757672719e2a0eb Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Thu, 1 Dec 2016 18:24:28 +0530 Subject: net: thunderx: Fix transmit queue timeout issue Transmit queue timeout issue is seen in two cases - Due to a race condition btw setting stop_queue at xmit() and checking for stopped_queue in NAPI poll routine, at times transmission from a SQ comes to a halt. This is fixed by using barriers and also added a check for SQ free descriptors, incase SQ is stopped and there are only CQE_RX i.e no CQE_TX. - Contrary to an assumption, a HW errata where HW doesn't stop transmission even though there are not enough CQEs available for a CQE_TX is not fixed in T88 pass 2.x. This results in a Qset error with 'CQ_WR_FULL' stalling transmission. This is fixed by adjusting RXQ's RED levels for CQ level such that there is always enough space left for CQE_TXs. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 52 ++++++++++++++++++---- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 24 ++-------- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 15 ++++--- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 1eacec8..2006f58 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -644,6 +644,7 @@ static int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx, struct cmp_queue *cq = &qs->cq[cq_idx]; struct cqe_rx_t *cq_desc; struct netdev_queue *txq; + struct snd_queue *sq; unsigned int tx_pkts = 0, tx_bytes = 0; spin_lock_bh(&cq->lock); @@ -709,16 +710,20 @@ loop: done: /* Wakeup TXQ if its stopped earlier due to SQ full */ - if (tx_done) { + sq = &nic->qs->sq[cq_idx]; + if (tx_done || + (atomic_read(&sq->free_cnt) >= MIN_SQ_DESC_PER_PKT_XMIT)) { netdev = nic->pnicvf->netdev; txq = netdev_get_tx_queue(netdev, nicvf_netdev_qidx(nic, cq_idx)); if (tx_pkts) netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); - nic = nic->pnicvf; + /* To read updated queue and carrier status */ + smp_mb(); if (netif_tx_queue_stopped(txq) && netif_carrier_ok(netdev)) { - netif_tx_start_queue(txq); + netif_tx_wake_queue(txq); + nic = nic->pnicvf; this_cpu_inc(nic->drv_stats->txq_wake); if (netif_msg_tx_err(nic)) netdev_warn(netdev, @@ -1054,6 +1059,9 @@ static netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev) struct nicvf *nic = netdev_priv(netdev); int qid = skb_get_queue_mapping(skb); struct netdev_queue *txq = netdev_get_tx_queue(netdev, qid); + struct nicvf *snic; + struct snd_queue *sq; + int tmp; /* Check for minimum packet length */ if (skb->len <= ETH_HLEN) { @@ -1061,13 +1069,39 @@ static netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } - if (!netif_tx_queue_stopped(txq) && !nicvf_sq_append_skb(nic, skb)) { + snic = nic; + /* Get secondary Qset's SQ structure */ + if (qid >= MAX_SND_QUEUES_PER_QS) { + tmp = qid / MAX_SND_QUEUES_PER_QS; + snic = (struct nicvf *)nic->snicvf[tmp - 1]; + if (!snic) { + netdev_warn(nic->netdev, + "Secondary Qset#%d's ptr not initialized\n", + tmp - 1); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + qid = qid % MAX_SND_QUEUES_PER_QS; + } + + sq = &snic->qs->sq[qid]; + if (!netif_tx_queue_stopped(txq) && + !nicvf_sq_append_skb(snic, sq, skb, qid)) { netif_tx_stop_queue(txq); - this_cpu_inc(nic->drv_stats->txq_stop); - if (netif_msg_tx_err(nic)) - netdev_warn(netdev, - "%s: Transmit ring full, stopping SQ%d\n", - netdev->name, qid); + + /* Barrier, so that stop_queue visible to other cpus */ + smp_mb(); + + /* Check again, incase another cpu freed descriptors */ + if (atomic_read(&sq->free_cnt) > MIN_SQ_DESC_PER_PKT_XMIT) { + netif_tx_wake_queue(txq); + } else { + this_cpu_inc(nic->drv_stats->txq_stop); + if (netif_msg_tx_err(nic)) + netdev_warn(netdev, + "%s: Transmit ring full, stopping SQ%d\n", + netdev->name, qid); + } return NETDEV_TX_BUSY; } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 7b336cd..d2ac133 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -1190,30 +1190,12 @@ static int nicvf_sq_append_tso(struct nicvf *nic, struct snd_queue *sq, } /* Append an skb to a SQ for packet transfer. */ -int nicvf_sq_append_skb(struct nicvf *nic, struct sk_buff *skb) +int nicvf_sq_append_skb(struct nicvf *nic, struct snd_queue *sq, + struct sk_buff *skb, u8 sq_num) { int i, size; int subdesc_cnt, tso_sqe = 0; - int sq_num, qentry; - struct queue_set *qs; - struct snd_queue *sq; - - sq_num = skb_get_queue_mapping(skb); - if (sq_num >= MAX_SND_QUEUES_PER_QS) { - /* Get secondary Qset's SQ structure */ - i = sq_num / MAX_SND_QUEUES_PER_QS; - if (!nic->snicvf[i - 1]) { - netdev_warn(nic->netdev, - "Secondary Qset#%d's ptr not initialized\n", - i - 1); - return 1; - } - nic = (struct nicvf *)nic->snicvf[i - 1]; - sq_num = sq_num % MAX_SND_QUEUES_PER_QS; - } - - qs = nic->qs; - sq = &qs->sq[sq_num]; + int qentry; subdesc_cnt = nicvf_sq_subdesc_required(nic, skb); if (subdesc_cnt > atomic_read(&sq->free_cnt)) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 20511f2..9e21046 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -88,13 +88,13 @@ /* RED and Backpressure levels of CQ for pkt reception * For CQ, level is a measure of emptiness i.e 0x0 means full - * eg: For CQ of size 4K, and for pass/drop levels of 128/96 - * HW accepts pkt if unused CQE >= 2048 - * RED accepts pkt if unused CQE < 2048 & >= 1536 - * DROPs pkts if unused CQE < 1536 + * eg: For CQ of size 4K, and for pass/drop levels of 160/144 + * HW accepts pkt if unused CQE >= 2560 + * RED accepts pkt if unused CQE < 2304 & >= 2560 + * DROPs pkts if unused CQE < 2304 */ -#define RQ_PASS_CQ_LVL 128ULL -#define RQ_DROP_CQ_LVL 96ULL +#define RQ_PASS_CQ_LVL 160ULL +#define RQ_DROP_CQ_LVL 144ULL /* RED and Backpressure levels of RBDR for pkt reception * For RBDR, level is a measure of fullness i.e 0x0 means empty @@ -306,7 +306,8 @@ void nicvf_sq_disable(struct nicvf *nic, int qidx); void nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt); void nicvf_sq_free_used_descs(struct net_device *netdev, struct snd_queue *sq, int qidx); -int nicvf_sq_append_skb(struct nicvf *nic, struct sk_buff *skb); +int nicvf_sq_append_skb(struct nicvf *nic, struct snd_queue *sq, + struct sk_buff *skb, u8 sq_num); struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx); void nicvf_rbdr_task(unsigned long data); -- cgit v1.1 From 7f7bf1606fa8fa0e3aecdeac0ba8005f2a0fbdef Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 1 Dec 2016 05:02:06 -0800 Subject: mlx4: fix use-after-free in mlx4_en_fold_software_stats() My recent commit to get more precise rx/tx counters in ndo_get_stats64() can lead to crashes at device dismantle, as Jesper found out. We must prevent mlx4_en_fold_software_stats() trying to access tx/rx rings if they are deleted. Fix this by adding a test against priv->port_up in mlx4_en_fold_software_stats() Calling mlx4_en_fold_software_stats() from mlx4_en_stop_port() allows us to eventually broadcast the latest/current counters to rtnetlink monitors. Fixes: 40931b85113d ("mlx4: give precise rx/tx bytes/packets counters") Signed-off-by: Eric Dumazet Reported-and-bisected-by: Jesper Dangaard Brouer Tested-by: Jesper Dangaard Brouer Cc: Tariq Toukan Cc: Saeed Mahameed Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 4 ++++ drivers/net/ethernet/mellanox/mlx4/en_port.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 12ea3405..091b904 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1809,8 +1809,12 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) netif_tx_disable(dev); + spin_lock_bh(&priv->stats_lock); + mlx4_en_fold_software_stats(dev); /* Set port as not active */ priv->port_up = false; + spin_unlock_bh(&priv->stats_lock); + priv->counter_index = MLX4_SINK_COUNTER_INDEX(mdev->dev); /* Promsicuous mode */ diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c index c6c4f12..9166d90 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c @@ -154,7 +154,7 @@ void mlx4_en_fold_software_stats(struct net_device *dev) unsigned long packets, bytes; int i; - if (mlx4_is_master(mdev->dev)) + if (!priv->port_up || mlx4_is_master(mdev->dev)) return; packets = 0; -- cgit v1.1 From b2cd12574aa3e1625f471ff57cde7f628a18a46b Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 1 Dec 2016 08:48:03 -0800 Subject: bpf: Refactor cgroups code in prep for new type Code move and rename only; no functional change intended. Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf-cgroup.h | 46 +++++++++++++++++++++++----------------------- kernel/bpf/cgroup.c | 10 +++++----- kernel/bpf/syscall.c | 28 +++++++++++++++------------- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 0cf1adf..af2ca8b 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -36,31 +36,31 @@ void cgroup_bpf_update(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_attach_type type); -int __cgroup_bpf_run_filter(struct sock *sk, - struct sk_buff *skb, - enum bpf_attach_type type); - -/* Wrappers for __cgroup_bpf_run_filter() guarded by cgroup_bpf_enabled. */ -#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) \ -({ \ - int __ret = 0; \ - if (cgroup_bpf_enabled) \ - __ret = __cgroup_bpf_run_filter(sk, skb, \ - BPF_CGROUP_INET_INGRESS); \ - \ - __ret; \ +int __cgroup_bpf_run_filter_skb(struct sock *sk, + struct sk_buff *skb, + enum bpf_attach_type type); + +/* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */ +#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled) \ + __ret = __cgroup_bpf_run_filter_skb(sk, skb, \ + BPF_CGROUP_INET_INGRESS); \ + \ + __ret; \ }) -#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) \ -({ \ - int __ret = 0; \ - if (cgroup_bpf_enabled && sk && sk == skb->sk) { \ - typeof(sk) __sk = sk_to_full_sk(sk); \ - if (sk_fullsock(__sk)) \ - __ret = __cgroup_bpf_run_filter(__sk, skb, \ - BPF_CGROUP_INET_EGRESS); \ - } \ - __ret; \ +#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled && sk && sk == skb->sk) { \ + typeof(sk) __sk = sk_to_full_sk(sk); \ + if (sk_fullsock(__sk)) \ + __ret = __cgroup_bpf_run_filter_skb(__sk, skb, \ + BPF_CGROUP_INET_EGRESS); \ + } \ + __ret; \ }) #else diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 8c784f8..8fe55ff 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -118,7 +118,7 @@ void __cgroup_bpf_update(struct cgroup *cgrp, } /** - * __cgroup_bpf_run_filter() - Run a program for packet filtering + * __cgroup_bpf_run_filter_skb() - Run a program for packet filtering * @sk: The socken sending or receiving traffic * @skb: The skb that is being sent or received * @type: The type of program to be exectuted @@ -132,9 +132,9 @@ void __cgroup_bpf_update(struct cgroup *cgrp, * This function will return %-EPERM if any if an attached program was found * and if it returned != 1 during execution. In all other cases, 0 is returned. */ -int __cgroup_bpf_run_filter(struct sock *sk, - struct sk_buff *skb, - enum bpf_attach_type type) +int __cgroup_bpf_run_filter_skb(struct sock *sk, + struct sk_buff *skb, + enum bpf_attach_type type) { struct bpf_prog *prog; struct cgroup *cgrp; @@ -164,4 +164,4 @@ int __cgroup_bpf_run_filter(struct sock *sk, return ret; } -EXPORT_SYMBOL(__cgroup_bpf_run_filter); +EXPORT_SYMBOL(__cgroup_bpf_run_filter_skb); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 4caa18e..5518a68 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -856,6 +856,7 @@ static int bpf_prog_attach(const union bpf_attr *attr) { struct bpf_prog *prog; struct cgroup *cgrp; + enum bpf_prog_type ptype; if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -866,25 +867,26 @@ static int bpf_prog_attach(const union bpf_attr *attr) switch (attr->attach_type) { case BPF_CGROUP_INET_INGRESS: case BPF_CGROUP_INET_EGRESS: - prog = bpf_prog_get_type(attr->attach_bpf_fd, - BPF_PROG_TYPE_CGROUP_SKB); - if (IS_ERR(prog)) - return PTR_ERR(prog); - - cgrp = cgroup_get_from_fd(attr->target_fd); - if (IS_ERR(cgrp)) { - bpf_prog_put(prog); - return PTR_ERR(cgrp); - } - - cgroup_bpf_update(cgrp, prog, attr->attach_type); - cgroup_put(cgrp); + ptype = BPF_PROG_TYPE_CGROUP_SKB; break; default: return -EINVAL; } + prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + cgrp = cgroup_get_from_fd(attr->target_fd); + if (IS_ERR(cgrp)) { + bpf_prog_put(prog); + return PTR_ERR(cgrp); + } + + cgroup_bpf_update(cgrp, prog, attr->attach_type); + cgroup_put(cgrp); + return 0; } -- cgit v1.1 From 61023658760032e97869b07d54be9681d2529e77 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 1 Dec 2016 08:48:04 -0800 Subject: bpf: Add new cgroup attach type to enable sock modifications Add new cgroup based program type, BPF_PROG_TYPE_CGROUP_SOCK. Similar to BPF_PROG_TYPE_CGROUP_SKB programs can be attached to a cgroup and run any time a process in the cgroup opens an AF_INET or AF_INET6 socket. Currently only sk_bound_dev_if is exported to userspace for modification by a bpf program. This allows a cgroup to be configured such that AF_INET{6} sockets opened by processes are automatically bound to a specific device. In turn, this enables the running of programs that do not support SO_BINDTODEVICE in a specific VRF context / L3 domain. Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf-cgroup.h | 14 +++++++++++ include/uapi/linux/bpf.h | 6 +++++ kernel/bpf/cgroup.c | 33 ++++++++++++++++++++++++ kernel/bpf/syscall.c | 5 +++- net/core/filter.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/af_inet.c | 12 ++++++++- net/ipv6/af_inet6.c | 8 ++++++ 7 files changed, 138 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index af2ca8b..7b6e5d1 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -40,6 +40,9 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb, enum bpf_attach_type type); +int __cgroup_bpf_run_filter_sk(struct sock *sk, + enum bpf_attach_type type); + /* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */ #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb) \ ({ \ @@ -63,6 +66,16 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, __ret; \ }) +#define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled && sk) { \ + __ret = __cgroup_bpf_run_filter_sk(sk, \ + BPF_CGROUP_INET_SOCK_CREATE); \ + } \ + __ret; \ +}) + #else struct cgroup_bpf {}; @@ -72,6 +85,7 @@ static inline void cgroup_bpf_inherit(struct cgroup *cgrp, #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; }) #endif /* CONFIG_CGROUP_BPF */ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 22ac827..bfe5e31 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -101,6 +101,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, BPF_PROG_TYPE_CGROUP_SKB, + BPF_PROG_TYPE_CGROUP_SOCK, BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, @@ -109,6 +110,7 @@ enum bpf_prog_type { enum bpf_attach_type { BPF_CGROUP_INET_INGRESS, BPF_CGROUP_INET_EGRESS, + BPF_CGROUP_INET_SOCK_CREATE, __MAX_BPF_ATTACH_TYPE }; @@ -567,6 +569,10 @@ enum bpf_ret_code { /* >127 are reserved for prog type specific return codes */ }; +struct bpf_sock { + __u32 bound_dev_if; +}; + /* User return codes for XDP prog type. * A valid XDP program must return one of these defined values. All other * return codes are reserved for future use. Unknown return codes will result diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 8fe55ff..a515f7b 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -165,3 +165,36 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, return ret; } EXPORT_SYMBOL(__cgroup_bpf_run_filter_skb); + +/** + * __cgroup_bpf_run_filter_sk() - Run a program on a sock + * @sk: sock structure to manipulate + * @type: The type of program to be exectuted + * + * socket is passed is expected to be of type INET or INET6. + * + * The program type passed in via @type must be suitable for sock + * filtering. No further check is performed to assert that. + * + * This function will return %-EPERM if any if an attached program was found + * and if it returned != 1 during execution. In all other cases, 0 is returned. + */ +int __cgroup_bpf_run_filter_sk(struct sock *sk, + enum bpf_attach_type type) +{ + struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); + struct bpf_prog *prog; + int ret = 0; + + + rcu_read_lock(); + + prog = rcu_dereference(cgrp->bpf.effective[type]); + if (prog) + ret = BPF_PROG_RUN(prog, sk) == 1 ? 0 : -EPERM; + + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 5518a68..85af86c 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -869,7 +869,9 @@ static int bpf_prog_attach(const union bpf_attr *attr) case BPF_CGROUP_INET_EGRESS: ptype = BPF_PROG_TYPE_CGROUP_SKB; break; - + case BPF_CGROUP_INET_SOCK_CREATE: + ptype = BPF_PROG_TYPE_CGROUP_SOCK; + break; default: return -EINVAL; } @@ -905,6 +907,7 @@ static int bpf_prog_detach(const union bpf_attr *attr) switch (attr->attach_type) { case BPF_CGROUP_INET_INGRESS: case BPF_CGROUP_INET_EGRESS: + case BPF_CGROUP_INET_SOCK_CREATE: cgrp = cgroup_get_from_fd(attr->target_fd); if (IS_ERR(cgrp)) return PTR_ERR(cgrp); diff --git a/net/core/filter.c b/net/core/filter.c index 1c4d0fa..0ab252e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2818,6 +2818,32 @@ static bool lwt_is_valid_access(int off, int size, return __is_valid_access(off, size, type); } +static bool sock_filter_is_valid_access(int off, int size, + enum bpf_access_type type, + enum bpf_reg_type *reg_type) +{ + if (type == BPF_WRITE) { + switch (off) { + case offsetof(struct bpf_sock, bound_dev_if): + break; + default: + return false; + } + } + + if (off < 0 || off + size > sizeof(struct bpf_sock)) + return false; + + /* The verifier guarantees that size > 0. */ + if (off % size != 0) + return false; + + if (size != sizeof(__u32)) + return false; + + return true; +} + static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, const struct bpf_prog *prog) { @@ -3076,6 +3102,30 @@ static u32 sk_filter_convert_ctx_access(enum bpf_access_type type, int dst_reg, return insn - insn_buf; } +static u32 sock_filter_convert_ctx_access(enum bpf_access_type type, + int dst_reg, int src_reg, + int ctx_off, + struct bpf_insn *insn_buf, + struct bpf_prog *prog) +{ + struct bpf_insn *insn = insn_buf; + + switch (ctx_off) { + case offsetof(struct bpf_sock, bound_dev_if): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_bound_dev_if) != 4); + + if (type == BPF_WRITE) + *insn++ = BPF_STX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sock, sk_bound_dev_if)); + else + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sock, sk_bound_dev_if)); + break; + } + + return insn - insn_buf; +} + static u32 tc_cls_act_convert_ctx_access(enum bpf_access_type type, int dst_reg, int src_reg, int ctx_off, struct bpf_insn *insn_buf, @@ -3162,6 +3212,12 @@ static const struct bpf_verifier_ops lwt_xmit_ops = { .gen_prologue = tc_cls_act_prologue, }; +static const struct bpf_verifier_ops cg_sock_ops = { + .get_func_proto = sk_filter_func_proto, + .is_valid_access = sock_filter_is_valid_access, + .convert_ctx_access = sock_filter_convert_ctx_access, +}; + static struct bpf_prog_type_list sk_filter_type __read_mostly = { .ops = &sk_filter_ops, .type = BPF_PROG_TYPE_SOCKET_FILTER, @@ -3202,6 +3258,11 @@ static struct bpf_prog_type_list lwt_xmit_type __read_mostly = { .type = BPF_PROG_TYPE_LWT_XMIT, }; +static struct bpf_prog_type_list cg_sock_type __read_mostly = { + .ops = &cg_sock_ops, + .type = BPF_PROG_TYPE_CGROUP_SOCK +}; + static int __init register_sk_filter_ops(void) { bpf_register_prog_type(&sk_filter_type); @@ -3209,6 +3270,7 @@ static int __init register_sk_filter_ops(void) bpf_register_prog_type(&sched_act_type); bpf_register_prog_type(&xdp_type); bpf_register_prog_type(&cg_skb_type); + bpf_register_prog_type(&cg_sock_type); bpf_register_prog_type(&lwt_in_type); bpf_register_prog_type(&lwt_out_type); bpf_register_prog_type(&lwt_xmit_type); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 5ddf5cd..24d2550 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -374,8 +374,18 @@ lookup_protocol: if (sk->sk_prot->init) { err = sk->sk_prot->init(sk); - if (err) + if (err) { + sk_common_release(sk); + goto out; + } + } + + if (!kern) { + err = BPF_CGROUP_RUN_PROG_INET_SOCK(sk); + if (err) { sk_common_release(sk); + goto out; + } } out: return err; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index d424f3a..237e654 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -258,6 +258,14 @@ lookup_protocol: goto out; } } + + if (!kern) { + err = BPF_CGROUP_RUN_PROG_INET_SOCK(sk); + if (err) { + sk_common_release(sk); + goto out; + } + } out: return err; out_rcu_unlock: -- cgit v1.1 From ad2805dc79e647ec2aee931a51924fda9d03b2fc Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 1 Dec 2016 08:48:05 -0800 Subject: samples: bpf: add userspace example for modifying sk_bound_dev_if Add a simple program to demonstrate the ability to attach a bpf program to a cgroup that sets sk_bound_dev_if for AF_INET{6} sockets when they are created. Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 2 + samples/bpf/test_cgrp2_sock.c | 83 ++++++++++++++++++++++++++++++++++++++++++ samples/bpf/test_cgrp2_sock.sh | 47 ++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 samples/bpf/test_cgrp2_sock.c create mode 100755 samples/bpf/test_cgrp2_sock.sh diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 44532fb..e1700c8 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -23,6 +23,7 @@ hostprogs-y += map_perf_test hostprogs-y += test_overhead hostprogs-y += test_cgrp2_array_pin hostprogs-y += test_cgrp2_attach +hostprogs-y += test_cgrp2_sock hostprogs-y += xdp1 hostprogs-y += xdp2 hostprogs-y += test_current_task_under_cgroup @@ -52,6 +53,7 @@ map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o +test_cgrp2_sock-objs := libbpf.o test_cgrp2_sock.o xdp1-objs := bpf_load.o libbpf.o xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := bpf_load.o libbpf.o xdp1_user.o diff --git a/samples/bpf/test_cgrp2_sock.c b/samples/bpf/test_cgrp2_sock.c new file mode 100644 index 0000000..d467b3c --- /dev/null +++ b/samples/bpf/test_cgrp2_sock.c @@ -0,0 +1,83 @@ +/* eBPF example program: + * + * - Loads eBPF program + * + * The eBPF program sets the sk_bound_dev_if index in new AF_INET{6} + * sockets opened by processes in the cgroup. + * + * - Attaches the new program to a cgroup using BPF_PROG_ATTACH + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libbpf.h" + +static int prog_load(int idx) +{ + struct bpf_insn prog[] = { + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + BPF_MOV64_IMM(BPF_REG_3, idx), + BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)), + BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ + BPF_EXIT_INSN(), + }; + + return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog), + "GPL", 0); +} + +static int usage(const char *argv0) +{ + printf("Usage: %s cg-path device-index\n", argv0); + return EXIT_FAILURE; +} + +int main(int argc, char **argv) +{ + int cg_fd, prog_fd, ret; + unsigned int idx; + + if (argc < 2) + return usage(argv[0]); + + idx = if_nametoindex(argv[2]); + if (!idx) { + printf("Invalid device name\n"); + return EXIT_FAILURE; + } + + cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY); + if (cg_fd < 0) { + printf("Failed to open cgroup path: '%s'\n", strerror(errno)); + return EXIT_FAILURE; + } + + prog_fd = prog_load(idx); + printf("Output from kernel verifier:\n%s\n-------\n", bpf_log_buf); + + if (prog_fd < 0) { + printf("Failed to load prog: '%s'\n", strerror(errno)); + return EXIT_FAILURE; + } + + ret = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE); + if (ret < 0) { + printf("Failed to attach prog to cgroup: '%s'\n", + strerror(errno)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/samples/bpf/test_cgrp2_sock.sh b/samples/bpf/test_cgrp2_sock.sh new file mode 100755 index 0000000..925fd46 --- /dev/null +++ b/samples/bpf/test_cgrp2_sock.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +function config_device { + ip netns add at_ns0 + ip link add veth0 type veth peer name veth0b + ip link set veth0b up + ip link set veth0 netns at_ns0 + ip netns exec at_ns0 ip addr add 172.16.1.100/24 dev veth0 + ip netns exec at_ns0 ip addr add 2401:db00::1/64 dev veth0 nodad + ip netns exec at_ns0 ip link set dev veth0 up + ip link add foo type vrf table 1234 + ip link set foo up + ip addr add 172.16.1.101/24 dev veth0b + ip addr add 2401:db00::2/64 dev veth0b nodad + ip link set veth0b master foo +} + +function attach_bpf { + rm -rf /tmp/cgroupv2 + mkdir -p /tmp/cgroupv2 + mount -t cgroup2 none /tmp/cgroupv2 + mkdir -p /tmp/cgroupv2/foo + test_cgrp2_sock /tmp/cgroupv2/foo foo + echo $$ >> /tmp/cgroupv2/foo/cgroup.procs +} + +function cleanup { + set +ex + ip netns delete at_ns0 + ip link del veth0 + ip link del foo + umount /tmp/cgroupv2 + rm -rf /tmp/cgroupv2 + set -ex +} + +function do_test { + ping -c1 -w1 172.16.1.100 + ping6 -c1 -w1 2401:db00::1 +} + +cleanup 2>/dev/null +config_device +attach_bpf +do_test +cleanup +echo "*** PASS ***" -- cgit v1.1 From aa4c1037a30f4e88f444e83d42c2befbe0d5caf5 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 1 Dec 2016 08:48:06 -0800 Subject: bpf: Add support for reading socket family, type, protocol Add socket family, type and protocol to bpf_sock allowing bpf programs read-only access. Add __sk_flags_offset[0] to struct sock before the bitfield to programmtically determine the offset of the unsigned int containing protocol and type. Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/net/sock.h | 15 +++++++++++++++ include/uapi/linux/bpf.h | 3 +++ net/core/filter.c | 21 +++++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/include/net/sock.h b/include/net/sock.h index 442cbb1..69afda6 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -389,6 +389,21 @@ struct sock { * Because of non atomicity rules, all * changes are protected by socket lock. */ + unsigned int __sk_flags_offset[0]; +#ifdef __BIG_ENDIAN_BITFIELD +#define SK_FL_PROTO_SHIFT 16 +#define SK_FL_PROTO_MASK 0x00ff0000 + +#define SK_FL_TYPE_SHIFT 0 +#define SK_FL_TYPE_MASK 0x0000ffff +#else +#define SK_FL_PROTO_SHIFT 8 +#define SK_FL_PROTO_MASK 0x0000ff00 + +#define SK_FL_TYPE_SHIFT 16 +#define SK_FL_TYPE_MASK 0xffff0000 +#endif + kmemcheck_bitfield_begin(flags); unsigned int sk_padding : 2, sk_no_check_tx : 1, diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index bfe5e31..6123d9b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -571,6 +571,9 @@ enum bpf_ret_code { struct bpf_sock { __u32 bound_dev_if; + __u32 family; + __u32 type; + __u32 protocol; }; /* User return codes for XDP prog type. diff --git a/net/core/filter.c b/net/core/filter.c index 0ab252e..56b4358 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3121,6 +3121,27 @@ static u32 sock_filter_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, offsetof(struct sock, sk_bound_dev_if)); break; + + case offsetof(struct bpf_sock, family): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_family) != 2); + + *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, + offsetof(struct sock, sk_family)); + break; + + case offsetof(struct bpf_sock, type): + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sock, __sk_flags_offset)); + *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, SK_FL_TYPE_MASK); + *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, SK_FL_TYPE_SHIFT); + break; + + case offsetof(struct bpf_sock, protocol): + *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, + offsetof(struct sock, __sk_flags_offset)); + *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, SK_FL_PROTO_MASK); + *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, SK_FL_PROTO_SHIFT); + break; } return insn - insn_buf; -- cgit v1.1 From 4f2e7ae56e04cfe670cf39152a8d015984c90351 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 1 Dec 2016 08:48:07 -0800 Subject: samples/bpf: Update bpf loader for cgroup section names Add support for section names starting with cgroup/skb and cgroup/sock. Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/bpf_load.c | 14 +++++++++++--- samples/bpf/bpf_load.h | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index 62f54d6..49b45cc 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -52,6 +52,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) bool is_tracepoint = strncmp(event, "tracepoint/", 11) == 0; bool is_xdp = strncmp(event, "xdp", 3) == 0; bool is_perf_event = strncmp(event, "perf_event", 10) == 0; + bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0; + bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0; enum bpf_prog_type prog_type; char buf[256]; int fd, efd, err, id; @@ -72,6 +74,10 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) prog_type = BPF_PROG_TYPE_XDP; } else if (is_perf_event) { prog_type = BPF_PROG_TYPE_PERF_EVENT; + } else if (is_cgroup_skb) { + prog_type = BPF_PROG_TYPE_CGROUP_SKB; + } else if (is_cgroup_sk) { + prog_type = BPF_PROG_TYPE_CGROUP_SOCK; } else { printf("Unknown event '%s'\n", event); return -1; @@ -85,7 +91,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) prog_fd[prog_cnt++] = fd; - if (is_xdp || is_perf_event) + if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk) return 0; if (is_socket) { @@ -334,7 +340,8 @@ int load_bpf_file(char *path) memcmp(shname_prog, "tracepoint/", 11) == 0 || memcmp(shname_prog, "xdp", 3) == 0 || memcmp(shname_prog, "perf_event", 10) == 0 || - memcmp(shname_prog, "socket", 6) == 0) + memcmp(shname_prog, "socket", 6) == 0 || + memcmp(shname_prog, "cgroup/", 7) == 0) load_and_attach(shname_prog, insns, data_prog->d_size); } } @@ -353,7 +360,8 @@ int load_bpf_file(char *path) memcmp(shname, "tracepoint/", 11) == 0 || memcmp(shname, "xdp", 3) == 0 || memcmp(shname, "perf_event", 10) == 0 || - memcmp(shname, "socket", 6) == 0) + memcmp(shname, "socket", 6) == 0 || + memcmp(shname, "cgroup/", 7) == 0) load_and_attach(shname, data->d_buf, data->d_size); } diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h index dfa57fe..4adeeef 100644 --- a/samples/bpf/bpf_load.h +++ b/samples/bpf/bpf_load.h @@ -7,6 +7,7 @@ extern int map_fd[MAX_MAPS]; extern int prog_fd[MAX_PROGS]; extern int event_fd[MAX_PROGS]; +extern int prog_cnt; /* parses elf file compiled by llvm .c->.o * . parses 'maps' section and creates maps via BPF syscall -- cgit v1.1 From 554ae6e792ef38020b80b4d5127c51d510c0918f Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 1 Dec 2016 08:48:08 -0800 Subject: samples/bpf: add userspace example for prohibiting sockets Add examples preventing a process in a cgroup from opening a socket based family, protocol and type. Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 4 ++ samples/bpf/sock_flags_kern.c | 44 ++++++++++++++++++++++ samples/bpf/test_cgrp2_sock2.c | 66 +++++++++++++++++++++++++++++++++ samples/bpf/test_cgrp2_sock2.sh | 81 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 samples/bpf/sock_flags_kern.c create mode 100644 samples/bpf/test_cgrp2_sock2.c create mode 100755 samples/bpf/test_cgrp2_sock2.sh diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index e1700c8..bfc2cb8 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -24,6 +24,7 @@ hostprogs-y += test_overhead hostprogs-y += test_cgrp2_array_pin hostprogs-y += test_cgrp2_attach hostprogs-y += test_cgrp2_sock +hostprogs-y += test_cgrp2_sock2 hostprogs-y += xdp1 hostprogs-y += xdp2 hostprogs-y += test_current_task_under_cgroup @@ -54,6 +55,7 @@ test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o test_cgrp2_sock-objs := libbpf.o test_cgrp2_sock.o +test_cgrp2_sock2-objs := bpf_load.o libbpf.o test_cgrp2_sock2.o xdp1-objs := bpf_load.o libbpf.o xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := bpf_load.o libbpf.o xdp1_user.o @@ -75,6 +77,7 @@ always += tracex3_kern.o always += tracex4_kern.o always += tracex5_kern.o always += tracex6_kern.o +always += sock_flags_kern.o always += test_probe_write_user_kern.o always += trace_output_kern.o always += tcbpf1_kern.o @@ -109,6 +112,7 @@ HOSTLOADLIBES_tracex3 += -lelf HOSTLOADLIBES_tracex4 += -lelf -lrt HOSTLOADLIBES_tracex5 += -lelf HOSTLOADLIBES_tracex6 += -lelf +HOSTLOADLIBES_test_cgrp2_sock2 += -lelf HOSTLOADLIBES_test_probe_write_user += -lelf HOSTLOADLIBES_trace_output += -lelf -lrt HOSTLOADLIBES_lathist += -lelf diff --git a/samples/bpf/sock_flags_kern.c b/samples/bpf/sock_flags_kern.c new file mode 100644 index 0000000..533dd11a6 --- /dev/null +++ b/samples/bpf/sock_flags_kern.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include "bpf_helpers.h" + +SEC("cgroup/sock1") +int bpf_prog1(struct bpf_sock *sk) +{ + char fmt[] = "socket: family %d type %d protocol %d\n"; + + bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol); + + /* block PF_INET6, SOCK_RAW, IPPROTO_ICMPV6 sockets + * ie., make ping6 fail + */ + if (sk->family == PF_INET6 && + sk->type == SOCK_RAW && + sk->protocol == IPPROTO_ICMPV6) + return 0; + + return 1; +} + +SEC("cgroup/sock2") +int bpf_prog2(struct bpf_sock *sk) +{ + char fmt[] = "socket: family %d type %d protocol %d\n"; + + bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol); + + /* block PF_INET, SOCK_RAW, IPPROTO_ICMP sockets + * ie., make ping fail + */ + if (sk->family == PF_INET && + sk->type == SOCK_RAW && + sk->protocol == IPPROTO_ICMP) + return 0; + + return 1; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/test_cgrp2_sock2.c b/samples/bpf/test_cgrp2_sock2.c new file mode 100644 index 0000000..455ef0d --- /dev/null +++ b/samples/bpf/test_cgrp2_sock2.c @@ -0,0 +1,66 @@ +/* eBPF example program: + * + * - Loads eBPF program + * + * The eBPF program loads a filter from file and attaches the + * program to a cgroup using BPF_PROG_ATTACH + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libbpf.h" +#include "bpf_load.h" + +static int usage(const char *argv0) +{ + printf("Usage: %s cg-path filter-path [filter-id]\n", argv0); + return EXIT_FAILURE; +} + +int main(int argc, char **argv) +{ + int cg_fd, ret, filter_id = 0; + + if (argc < 3) + return usage(argv[0]); + + cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY); + if (cg_fd < 0) { + printf("Failed to open cgroup path: '%s'\n", strerror(errno)); + return EXIT_FAILURE; + } + + if (load_bpf_file(argv[2])) + return EXIT_FAILURE; + + printf("Output from kernel verifier:\n%s\n-------\n", bpf_log_buf); + + if (argc > 3) + filter_id = atoi(argv[3]); + + if (filter_id > prog_cnt) { + printf("Invalid program id; program not found in file\n"); + return EXIT_FAILURE; + } + + ret = bpf_prog_attach(prog_fd[filter_id], cg_fd, + BPF_CGROUP_INET_SOCK_CREATE); + if (ret < 0) { + printf("Failed to attach prog to cgroup: '%s'\n", + strerror(errno)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/samples/bpf/test_cgrp2_sock2.sh b/samples/bpf/test_cgrp2_sock2.sh new file mode 100755 index 0000000..891f12a --- /dev/null +++ b/samples/bpf/test_cgrp2_sock2.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +function config_device { + ip netns add at_ns0 + ip link add veth0 type veth peer name veth0b + ip link set veth0b up + ip link set veth0 netns at_ns0 + ip netns exec at_ns0 ip addr add 172.16.1.100/24 dev veth0 + ip netns exec at_ns0 ip addr add 2401:db00::1/64 dev veth0 nodad + ip netns exec at_ns0 ip link set dev veth0 up + ip addr add 172.16.1.101/24 dev veth0b + ip addr add 2401:db00::2/64 dev veth0b nodad +} + +function config_cgroup { + rm -rf /tmp/cgroupv2 + mkdir -p /tmp/cgroupv2 + mount -t cgroup2 none /tmp/cgroupv2 + mkdir -p /tmp/cgroupv2/foo + echo $$ >> /tmp/cgroupv2/foo/cgroup.procs +} + + +function attach_bpf { + test_cgrp2_sock2 /tmp/cgroupv2/foo sock_flags_kern.o $1 + [ $? -ne 0 ] && exit 1 +} + +function cleanup { + ip link del veth0b + ip netns delete at_ns0 + umount /tmp/cgroupv2 + rm -rf /tmp/cgroupv2 +} + +cleanup 2>/dev/null + +set -e +config_device +config_cgroup +set +e + +# +# Test 1 - fail ping6 +# +attach_bpf 0 +ping -c1 -w1 172.16.1.100 +if [ $? -ne 0 ]; then + echo "ping failed when it should succeed" + cleanup + exit 1 +fi + +ping6 -c1 -w1 2401:db00::1 +if [ $? -eq 0 ]; then + echo "ping6 succeeded when it should not" + cleanup + exit 1 +fi + +# +# Test 2 - fail ping +# +attach_bpf 1 +ping6 -c1 -w1 2401:db00::1 +if [ $? -ne 0 ]; then + echo "ping6 failed when it should succeed" + cleanup + exit 1 +fi + +ping -c1 -w1 172.16.1.100 +if [ $? -eq 0 ]; then + echo "ping succeeded when it should not" + cleanup + exit 1 +fi + +cleanup +echo +echo "*** PASS ***" -- cgit v1.1 From ac83b7ddf227d85542fc2e186884b003cfdb99f7 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 1 Dec 2016 18:03:04 +0100 Subject: net: mvneta: Optimize rx path for small frame For small frame reuse the phys_addr variable instead of accessing the uncacheable value in the rx descriptor. Signed-off-by: Gregory CLEMENT Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 0a24571..2445428 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -1918,7 +1918,7 @@ err_drop_frame: goto err_drop_frame; dma_sync_single_range_for_cpu(dev->dev.parent, - rx_desc->buf_phys_addr, + phys_addr, MVNETA_MH_SIZE + NET_SKB_PAD, rx_bytes, DMA_FROM_DEVICE); -- cgit v1.1 From e9f6499965c64109e296f61e3aa7fd91f9f2233c Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 1 Dec 2016 18:03:05 +0100 Subject: net: mvneta: Do not allocate buffer in rxq init with HWBM For HWBM all buffers are allocated in mvneta_bm_construct() and in runtime they are put into descriptors by hardware. There is no need to fill them at this point. Suggested-by: Marcin Wojtas Signed-off-by: Gregory CLEMENT Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 2445428..2c5fe53 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2784,14 +2784,14 @@ static int mvneta_rxq_init(struct mvneta_port *pp, mvneta_rxq_buf_size_set(pp, rxq, MVNETA_RX_BUF_SIZE(pp->pkt_size)); mvneta_rxq_bm_disable(pp, rxq); + mvneta_rxq_fill(pp, rxq, rxq->size); } else { mvneta_rxq_bm_enable(pp, rxq); mvneta_rxq_long_pool_set(pp, rxq); mvneta_rxq_short_pool_set(pp, rxq); + mvneta_rxq_non_occup_desc_add(pp, rxq, rxq->size); } - mvneta_rxq_fill(pp, rxq, rxq->size); - return 0; } -- cgit v1.1 From f88bee1c4bfa4faad8aeab75502cc76244a06291 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 1 Dec 2016 18:03:06 +0100 Subject: net: mvneta: Use cacheable memory to store the rx buffer virtual address Until now the virtual address of the received buffer were stored in the cookie field of the rx descriptor. However, this field is 32-bits only which prevents to use the driver on a 64-bits architecture. With this patch the virtual address is stored in an array not shared with the hardware (no more need to use the DMA API). Thanks to this, it is possible to use cache contrary to the access of the rx descriptor member. The change is done in the swbm path only because the hwbm uses the cookie field, this also means that currently the hwbm is not usable in 64-bits. Signed-off-by: Gregory CLEMENT Reviewed-by: Jisheng Zhang Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 2c5fe53..7e586e6 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -561,6 +561,9 @@ struct mvneta_rx_queue { u32 pkts_coal; u32 time_coal; + /* Virtual address of the RX buffer */ + void **buf_virt_addr; + /* Virtual address of the RX DMA descriptors array */ struct mvneta_rx_desc *descs; @@ -1573,10 +1576,14 @@ static void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp, /* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */ static void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc, - u32 phys_addr, u32 cookie) + u32 phys_addr, void *virt_addr, + struct mvneta_rx_queue *rxq) { - rx_desc->buf_cookie = cookie; + int i; + rx_desc->buf_phys_addr = phys_addr; + i = rx_desc - rxq->descs; + rxq->buf_virt_addr[i] = virt_addr; } /* Decrement sent descriptors counter */ @@ -1781,7 +1788,8 @@ EXPORT_SYMBOL_GPL(mvneta_frag_free); /* Refill processing for SW buffer management */ static int mvneta_rx_refill(struct mvneta_port *pp, - struct mvneta_rx_desc *rx_desc) + struct mvneta_rx_desc *rx_desc, + struct mvneta_rx_queue *rxq) { dma_addr_t phys_addr; @@ -1799,7 +1807,7 @@ static int mvneta_rx_refill(struct mvneta_port *pp, return -ENOMEM; } - mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)data); + mvneta_rx_desc_fill(rx_desc, phys_addr, data, rxq); return 0; } @@ -1861,7 +1869,7 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp, for (i = 0; i < rxq->size; i++) { struct mvneta_rx_desc *rx_desc = rxq->descs + i; - void *data = (void *)rx_desc->buf_cookie; + void *data = rxq->buf_virt_addr[i]; dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr, MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE); @@ -1894,12 +1902,13 @@ static int mvneta_rx_swbm(struct mvneta_port *pp, int rx_todo, unsigned char *data; dma_addr_t phys_addr; u32 rx_status, frag_size; - int rx_bytes, err; + int rx_bytes, err, index; rx_done++; rx_status = rx_desc->status; rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE); - data = (unsigned char *)rx_desc->buf_cookie; + index = rx_desc - rxq->descs; + data = rxq->buf_virt_addr[index]; phys_addr = rx_desc->buf_phys_addr; if (!mvneta_rxq_desc_is_first_last(rx_status) || @@ -1938,7 +1947,7 @@ err_drop_frame: } /* Refill processing */ - err = mvneta_rx_refill(pp, rx_desc); + err = mvneta_rx_refill(pp, rx_desc, rxq); if (err) { netdev_err(dev, "Linux processing - Can't refill\n"); rxq->missed++; @@ -2020,7 +2029,7 @@ static int mvneta_rx_hwbm(struct mvneta_port *pp, int rx_todo, rx_done++; rx_status = rx_desc->status; rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE); - data = (unsigned char *)rx_desc->buf_cookie; + data = (u8 *)(uintptr_t)rx_desc->buf_cookie; phys_addr = rx_desc->buf_phys_addr; pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc); bm_pool = &pp->bm_priv->bm_pools[pool_id]; @@ -2716,7 +2725,7 @@ static int mvneta_rxq_fill(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, for (i = 0; i < num; i++) { memset(rxq->descs + i, 0, sizeof(struct mvneta_rx_desc)); - if (mvneta_rx_refill(pp, rxq->descs + i) != 0) { + if (mvneta_rx_refill(pp, rxq->descs + i, rxq) != 0) { netdev_err(pp->dev, "%s:rxq %d, %d of %d buffs filled\n", __func__, rxq->id, i, num); break; @@ -3865,6 +3874,11 @@ static int mvneta_init(struct device *dev, struct mvneta_port *pp) rxq->size = pp->rx_ring_size; rxq->pkts_coal = MVNETA_RX_COAL_PKTS; rxq->time_coal = MVNETA_RX_COAL_USEC; + rxq->buf_virt_addr = devm_kmalloc(pp->dev->dev.parent, + rxq->size * sizeof(void *), + GFP_KERNEL); + if (!rxq->buf_virt_addr) + return -ENOMEM; } return 0; -- cgit v1.1 From 8d5047cf9ca2a0adc60a9e334ee5cc7ed891c6c2 Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Thu, 1 Dec 2016 18:03:07 +0100 Subject: net: mvneta: Convert to be 64 bits compatible Prepare the mvneta driver in order to be usable on the 64 bits platform such as the Armada 3700. [gregory.clement@free-electrons.com]: this patch was extract from a larger one to ease review and maintenance. Signed-off-by: Marcin Wojtas Signed-off-by: Gregory CLEMENT Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 7e586e6..cb5eee9 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -296,6 +296,12 @@ /* descriptor aligned size */ #define MVNETA_DESC_ALIGNED_SIZE 32 +/* Number of bytes to be taken into account by HW when putting incoming data + * to the buffers. It is needed in case NET_SKB_PAD exceeds maximum packet + * offset supported in MVNETA_RXQ_CONFIG_REG(q) registers. + */ +#define MVNETA_RX_PKT_OFFSET_CORRECTION 64 + #define MVNETA_RX_PKT_SIZE(mtu) \ ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \ ETH_HLEN + ETH_FCS_LEN, \ @@ -416,6 +422,7 @@ struct mvneta_port { u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; u32 indir[MVNETA_RSS_LU_TABLE_SIZE]; + u16 rx_offset_correction; }; /* The mvneta_tx_desc and mvneta_rx_desc structures describe the @@ -1807,6 +1814,7 @@ static int mvneta_rx_refill(struct mvneta_port *pp, return -ENOMEM; } + phys_addr += pp->rx_offset_correction; mvneta_rx_desc_fill(rx_desc, phys_addr, data, rxq); return 0; } @@ -2782,7 +2790,7 @@ static int mvneta_rxq_init(struct mvneta_port *pp, mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size); /* Set Offset */ - mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD); + mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD - pp->rx_offset_correction); /* Set coalescing pkts and time */ mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal); @@ -4033,6 +4041,13 @@ static int mvneta_probe(struct platform_device *pdev) pp->rxq_def = rxq_def; + /* Set RX packet offset correction for platforms, whose + * NET_SKB_PAD, exceeds 64B. It should be 64B for 64-bit + * platforms and 0B for 32-bit ones. + */ + pp->rx_offset_correction = + max(0, NET_SKB_PAD - MVNETA_RX_PKT_OFFSET_CORRECTION); + pp->indir[0] = rxq_def; pp->clk = devm_clk_get(&pdev->dev, "core"); -- cgit v1.1 From f34dacccb412a8716405a18a25ca1fa97046660c Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 1 Dec 2016 18:03:08 +0100 Subject: net: mvneta: Only disable mvneta_bm for 64-bits Actually only the mvneta_bm support is not 64-bits compatible. The mvneta code itself can run on 64-bits architecture. Signed-off-by: Gregory CLEMENT Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 66fd9db..2ccea9d 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -44,6 +44,7 @@ config MVMDIO config MVNETA_BM_ENABLE tristate "Marvell Armada 38x/XP network interface BM support" depends on MVNETA + depends on !64BIT ---help--- This driver supports auxiliary block of the network interface units in the Marvell ARMADA XP and ARMADA 38x SoC @@ -58,7 +59,6 @@ config MVNETA tristate "Marvell Armada 370/38x/XP network interface support" depends on PLAT_ORION || COMPILE_TEST depends on HAS_DMA - depends on !64BIT select MVMDIO select FIXED_PHY ---help--- @@ -71,6 +71,7 @@ config MVNETA config MVNETA_BM tristate + depends on !64BIT default y if MVNETA=y && MVNETA_BM_ENABLE!=n default MVNETA_BM_ENABLE select HWBM -- cgit v1.1 From 2636ac3cc2b47e189894300602043aefbe699521 Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Thu, 1 Dec 2016 18:03:09 +0100 Subject: net: mvneta: Add network support for Armada 3700 SoC Armada 3700 is a new ARMv8 SoC from Marvell using same network controller as older Armada 370/38x/XP. There are however some differences that needed taking into account when adding support for it: * open default MBUS window to 4GB of DRAM - Armada 3700 SoC's Mbus configuration for network controller has to be done on two levels: global and per-port. The first one is inherited from the bootloader. The latter can be opened in a default way, leaving arbitration to the bus controller. Hence filled mbus_dram_target_info structure is not needed * make per-CPU operation optional - Recent patches adding RSS and XPS support for Armada 38x/XP enabled per-CPU operation of the controller by default. Contrary to older SoC's Armada 3700 SoC's network controller is not capable of per-CPU processing due to interrupt lines' connectivity. This patch restores non-per-CPU operation, which is now optional and depends on neta_armada3700 flag value in mvneta_port structure. In order not to complicate the code, separate interrupt subroutine is implemented. For now, on the Armada 3700, RSS is disabled as the current implementation depend on the per cpu interrupts. [gregory.clement@free-electrons.com: extract from a larger patch, replace some ifdef and port to net-next for v4.10] Signed-off-by: Marcin Wojtas Signed-off-by: Gregory CLEMENT Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- .../bindings/net/marvell-armada-370-neta.txt | 7 +- drivers/net/ethernet/marvell/Kconfig | 7 +- drivers/net/ethernet/marvell/mvneta.c | 287 +++++++++++++++------ 3 files changed, 214 insertions(+), 87 deletions(-) diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt index 73be897..7aa840c 100644 --- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt +++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt @@ -1,7 +1,10 @@ -* Marvell Armada 370 / Armada XP Ethernet Controller (NETA) +* Marvell Armada 370 / Armada XP / Armada 3700 Ethernet Controller (NETA) Required properties: -- compatible: "marvell,armada-370-neta" or "marvell,armada-xp-neta". +- compatible: could be one of the followings + "marvell,armada-370-neta" + "marvell,armada-xp-neta" + "marvell,armada-3700-neta" - reg: address and length of the register set for the device. - interrupts: interrupt for the device - phy: See ethernet.txt file in the same directory. diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 2ccea9d..3b8f11f 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -56,14 +56,15 @@ config MVNETA_BM_ENABLE buffer management. config MVNETA - tristate "Marvell Armada 370/38x/XP network interface support" - depends on PLAT_ORION || COMPILE_TEST + tristate "Marvell Armada 370/38x/XP/37xx network interface support" + depends on ARCH_MVEBU || COMPILE_TEST depends on HAS_DMA select MVMDIO select FIXED_PHY ---help--- This driver supports the network interface units in the - Marvell ARMADA XP, ARMADA 370 and ARMADA 38x SoC family. + Marvell ARMADA XP, ARMADA 370, ARMADA 38x and + ARMADA 37xx SoC family. Note that this driver is distinct from the mv643xx_eth driver, which should be used for the older Marvell SoCs diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index cb5eee9..aa87c68 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -397,6 +397,9 @@ struct mvneta_port { spinlock_t lock; bool is_stopped; + u32 cause_rx_tx; + struct napi_struct napi; + /* Core clock */ struct clk *clk; /* AXI clock */ @@ -422,6 +425,9 @@ struct mvneta_port { u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; u32 indir[MVNETA_RSS_LU_TABLE_SIZE]; + + /* Flags for special SoC configurations */ + bool neta_armada3700; u16 rx_offset_correction; }; @@ -965,14 +971,9 @@ static int mvneta_mbus_io_win_set(struct mvneta_port *pp, u32 base, u32 wsize, return 0; } -/* Assign and initialize pools for port. In case of fail - * buffer manager will remain disabled for current port. - */ -static int mvneta_bm_port_init(struct platform_device *pdev, - struct mvneta_port *pp) +static int mvneta_bm_port_mbus_init(struct mvneta_port *pp) { - struct device_node *dn = pdev->dev.of_node; - u32 long_pool_id, short_pool_id, wsize; + u32 wsize; u8 target, attr; int err; @@ -991,6 +992,25 @@ static int mvneta_bm_port_init(struct platform_device *pdev, netdev_info(pp->dev, "fail to configure mbus window to BM\n"); return err; } + return 0; +} + +/* Assign and initialize pools for port. In case of fail + * buffer manager will remain disabled for current port. + */ +static int mvneta_bm_port_init(struct platform_device *pdev, + struct mvneta_port *pp) +{ + struct device_node *dn = pdev->dev.of_node; + u32 long_pool_id, short_pool_id; + + if (!pp->neta_armada3700) { + int ret; + + ret = mvneta_bm_port_mbus_init(pp); + if (ret) + return ret; + } if (of_property_read_u32(dn, "bm,pool-long", &long_pool_id)) { netdev_info(pp->dev, "missing long pool id\n"); @@ -1359,22 +1379,27 @@ static void mvneta_defaults_set(struct mvneta_port *pp) for_each_present_cpu(cpu) { int rxq_map = 0, txq_map = 0; int rxq, txq; + if (!pp->neta_armada3700) { + for (rxq = 0; rxq < rxq_number; rxq++) + if ((rxq % max_cpu) == cpu) + rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); + + for (txq = 0; txq < txq_number; txq++) + if ((txq % max_cpu) == cpu) + txq_map |= MVNETA_CPU_TXQ_ACCESS(txq); + + /* With only one TX queue we configure a special case + * which will allow to get all the irq on a single + * CPU + */ + if (txq_number == 1) + txq_map = (cpu == pp->rxq_def) ? + MVNETA_CPU_TXQ_ACCESS(1) : 0; - for (rxq = 0; rxq < rxq_number; rxq++) - if ((rxq % max_cpu) == cpu) - rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); - - for (txq = 0; txq < txq_number; txq++) - if ((txq % max_cpu) == cpu) - txq_map |= MVNETA_CPU_TXQ_ACCESS(txq); - - /* With only one TX queue we configure a special case - * which will allow to get all the irq on a single - * CPU - */ - if (txq_number == 1) - txq_map = (cpu == pp->rxq_def) ? - MVNETA_CPU_TXQ_ACCESS(1) : 0; + } else { + txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; + rxq_map = MVNETA_CPU_RXQ_ACCESS_ALL_MASK; + } mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); } @@ -2627,6 +2652,17 @@ static void mvneta_set_rx_mode(struct net_device *dev) /* Interrupt handling - the callback for request_irq() */ static irqreturn_t mvneta_isr(int irq, void *dev_id) { + struct mvneta_port *pp = (struct mvneta_port *)dev_id; + + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + napi_schedule(&pp->napi); + + return IRQ_HANDLED; +} + +/* Interrupt handling - the callback for request_percpu_irq() */ +static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id) +{ struct mvneta_pcpu_port *port = (struct mvneta_pcpu_port *)dev_id; disable_percpu_irq(port->pp->dev->irq); @@ -2674,7 +2710,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget) struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); if (!netif_running(pp->dev)) { - napi_complete(&port->napi); + napi_complete(napi); return rx_done; } @@ -2703,7 +2739,8 @@ static int mvneta_poll(struct napi_struct *napi, int budget) */ rx_queue = fls(((cause_rx_tx >> 8) & 0xff)); - cause_rx_tx |= port->cause_rx_tx; + cause_rx_tx |= pp->neta_armada3700 ? pp->cause_rx_tx : + port->cause_rx_tx; if (rx_queue) { rx_queue = rx_queue - 1; @@ -2717,11 +2754,27 @@ static int mvneta_poll(struct napi_struct *napi, int budget) if (budget > 0) { cause_rx_tx = 0; - napi_complete(&port->napi); - enable_percpu_irq(pp->dev->irq, 0); + napi_complete(napi); + + if (pp->neta_armada3700) { + unsigned long flags; + + local_irq_save(flags); + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + MVNETA_RX_INTR_MASK(rxq_number) | + MVNETA_TX_INTR_MASK(txq_number) | + MVNETA_MISCINTR_INTR_MASK); + local_irq_restore(flags); + } else { + enable_percpu_irq(pp->dev->irq, 0); + } } - port->cause_rx_tx = cause_rx_tx; + if (pp->neta_armada3700) + pp->cause_rx_tx = cause_rx_tx; + else + port->cause_rx_tx = cause_rx_tx; + return rx_done; } @@ -2991,11 +3044,16 @@ static void mvneta_start_dev(struct mvneta_port *pp) /* start the Rx/Tx activity */ mvneta_port_enable(pp); - /* Enable polling on the port */ - for_each_online_cpu(cpu) { - struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + if (!pp->neta_armada3700) { + /* Enable polling on the port */ + for_each_online_cpu(cpu) { + struct mvneta_pcpu_port *port = + per_cpu_ptr(pp->ports, cpu); - napi_enable(&port->napi); + napi_enable(&port->napi); + } + } else { + napi_enable(&pp->napi); } /* Unmask interrupts. It has to be done from each CPU */ @@ -3017,10 +3075,15 @@ static void mvneta_stop_dev(struct mvneta_port *pp) phy_stop(ndev->phydev); - for_each_online_cpu(cpu) { - struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + if (!pp->neta_armada3700) { + for_each_online_cpu(cpu) { + struct mvneta_pcpu_port *port = + per_cpu_ptr(pp->ports, cpu); - napi_disable(&port->napi); + napi_disable(&port->napi); + } + } else { + napi_disable(&pp->napi); } netif_carrier_off(pp->dev); @@ -3430,31 +3493,37 @@ static int mvneta_open(struct net_device *dev) goto err_cleanup_rxqs; /* Connect to port interrupt line */ - ret = request_percpu_irq(pp->dev->irq, mvneta_isr, - MVNETA_DRIVER_NAME, pp->ports); + if (pp->neta_armada3700) + ret = request_irq(pp->dev->irq, mvneta_isr, 0, + dev->name, pp); + else + ret = request_percpu_irq(pp->dev->irq, mvneta_percpu_isr, + dev->name, pp->ports); if (ret) { netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq); goto err_cleanup_txqs; } - /* Enable per-CPU interrupt on all the CPU to handle our RX - * queue interrupts - */ - on_each_cpu(mvneta_percpu_enable, pp, true); + if (!pp->neta_armada3700) { + /* Enable per-CPU interrupt on all the CPU to handle our RX + * queue interrupts + */ + on_each_cpu(mvneta_percpu_enable, pp, true); - pp->is_stopped = false; - /* Register a CPU notifier to handle the case where our CPU - * might be taken offline. - */ - ret = cpuhp_state_add_instance_nocalls(online_hpstate, - &pp->node_online); - if (ret) - goto err_free_irq; + pp->is_stopped = false; + /* Register a CPU notifier to handle the case where our CPU + * might be taken offline. + */ + ret = cpuhp_state_add_instance_nocalls(online_hpstate, + &pp->node_online); + if (ret) + goto err_free_irq; - ret = cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD, - &pp->node_dead); - if (ret) - goto err_free_online_hp; + ret = cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD, + &pp->node_dead); + if (ret) + goto err_free_online_hp; + } /* In default link is down */ netif_carrier_off(pp->dev); @@ -3470,13 +3539,20 @@ static int mvneta_open(struct net_device *dev) return 0; err_free_dead_hp: - cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, - &pp->node_dead); + if (!pp->neta_armada3700) + cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, + &pp->node_dead); err_free_online_hp: - cpuhp_state_remove_instance_nocalls(online_hpstate, &pp->node_online); + if (!pp->neta_armada3700) + cpuhp_state_remove_instance_nocalls(online_hpstate, + &pp->node_online); err_free_irq: - on_each_cpu(mvneta_percpu_disable, pp, true); - free_percpu_irq(pp->dev->irq, pp->ports); + if (pp->neta_armada3700) { + free_irq(pp->dev->irq, pp); + } else { + on_each_cpu(mvneta_percpu_disable, pp, true); + free_percpu_irq(pp->dev->irq, pp->ports); + } err_cleanup_txqs: mvneta_cleanup_txqs(pp); err_cleanup_rxqs: @@ -3489,23 +3565,30 @@ static int mvneta_stop(struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); - /* Inform that we are stopping so we don't want to setup the - * driver for new CPUs in the notifiers. The code of the - * notifier for CPU online is protected by the same spinlock, - * so when we get the lock, the notifer work is done. - */ - spin_lock(&pp->lock); - pp->is_stopped = true; - spin_unlock(&pp->lock); + if (!pp->neta_armada3700) { + /* Inform that we are stopping so we don't want to setup the + * driver for new CPUs in the notifiers. The code of the + * notifier for CPU online is protected by the same spinlock, + * so when we get the lock, the notifer work is done. + */ + spin_lock(&pp->lock); + pp->is_stopped = true; + spin_unlock(&pp->lock); - mvneta_stop_dev(pp); - mvneta_mdio_remove(pp); + mvneta_stop_dev(pp); + mvneta_mdio_remove(pp); cpuhp_state_remove_instance_nocalls(online_hpstate, &pp->node_online); cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, &pp->node_dead); - on_each_cpu(mvneta_percpu_disable, pp, true); - free_percpu_irq(dev->irq, pp->ports); + on_each_cpu(mvneta_percpu_disable, pp, true); + free_percpu_irq(dev->irq, pp->ports); + } else { + mvneta_stop_dev(pp); + mvneta_mdio_remove(pp); + free_irq(dev->irq, pp); + } + mvneta_cleanup_rxqs(pp); mvneta_cleanup_txqs(pp); @@ -3784,6 +3867,11 @@ static int mvneta_ethtool_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key, const u8 hfunc) { struct mvneta_port *pp = netdev_priv(dev); + + /* Current code for Armada 3700 doesn't support RSS features yet */ + if (pp->neta_armada3700) + return -EOPNOTSUPP; + /* We require at least one supported parameter to be changed * and no change in any of the unsupported parameters */ @@ -3804,6 +3892,10 @@ static int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, { struct mvneta_port *pp = netdev_priv(dev); + /* Current code for Armada 3700 doesn't support RSS features yet */ + if (pp->neta_armada3700) + return -EOPNOTSUPP; + if (hfunc) *hfunc = ETH_RSS_HASH_TOP; @@ -3911,16 +4003,29 @@ static void mvneta_conf_mbus_windows(struct mvneta_port *pp, win_enable = 0x3f; win_protect = 0; - for (i = 0; i < dram->num_cs; i++) { - const struct mbus_dram_window *cs = dram->cs + i; - mvreg_write(pp, MVNETA_WIN_BASE(i), (cs->base & 0xffff0000) | - (cs->mbus_attr << 8) | dram->mbus_dram_target_id); + if (dram) { + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + + mvreg_write(pp, MVNETA_WIN_BASE(i), + (cs->base & 0xffff0000) | + (cs->mbus_attr << 8) | + dram->mbus_dram_target_id); - mvreg_write(pp, MVNETA_WIN_SIZE(i), - (cs->size - 1) & 0xffff0000); + mvreg_write(pp, MVNETA_WIN_SIZE(i), + (cs->size - 1) & 0xffff0000); - win_enable &= ~(1 << i); - win_protect |= 3 << (2 * i); + win_enable &= ~(1 << i); + win_protect |= 3 << (2 * i); + } + } else { + /* For Armada3700 open default 4GB Mbus window, leaving + * arbitration of target/attribute to a different layer + * of configuration. + */ + mvreg_write(pp, MVNETA_WIN_SIZE(0), 0xffff0000); + win_enable &= ~BIT(0); + win_protect = 3; } mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable); @@ -4050,6 +4155,10 @@ static int mvneta_probe(struct platform_device *pdev) pp->indir[0] = rxq_def; + /* Get special SoC configurations */ + if (of_device_is_compatible(dn, "marvell,armada-3700-neta")) + pp->neta_armada3700 = true; + pp->clk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(pp->clk)) pp->clk = devm_clk_get(&pdev->dev, NULL); @@ -4117,7 +4226,11 @@ static int mvneta_probe(struct platform_device *pdev) pp->tx_csum_limit = tx_csum_limit; dram_target_info = mv_mbus_dram_info(); - if (dram_target_info) + /* Armada3700 requires setting default configuration of Mbus + * windows, however without using filled mbus_dram_target_info + * structure. + */ + if (dram_target_info || pp->neta_armada3700) mvneta_conf_mbus_windows(pp, dram_target_info); pp->tx_ring_size = MVNETA_MAX_TXD; @@ -4150,11 +4263,20 @@ static int mvneta_probe(struct platform_device *pdev) goto err_netdev; } - for_each_present_cpu(cpu) { - struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + /* Armada3700 network controller does not support per-cpu + * operation, so only single NAPI should be initialized. + */ + if (pp->neta_armada3700) { + netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT); + } else { + for_each_present_cpu(cpu) { + struct mvneta_pcpu_port *port = + per_cpu_ptr(pp->ports, cpu); - netif_napi_add(dev, &port->napi, mvneta_poll, NAPI_POLL_WEIGHT); - port->pp = pp; + netif_napi_add(dev, &port->napi, mvneta_poll, + NAPI_POLL_WEIGHT); + port->pp = pp; + } } dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; @@ -4239,6 +4361,7 @@ static int mvneta_remove(struct platform_device *pdev) static const struct of_device_id mvneta_match[] = { { .compatible = "marvell,armada-370-neta" }, { .compatible = "marvell,armada-xp-neta" }, + { .compatible = "marvell,armada-3700-neta" }, { } }; MODULE_DEVICE_TABLE(of, mvneta_match); -- cgit v1.1 From ea7ae8854a3565a2da84fa91adf2e310c51c6e99 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 1 Dec 2016 18:03:10 +0100 Subject: ARM64: dts: marvell: Add network support for Armada 3700 Add neta nodes for network support both in device tree for the SoC and the board. Signed-off-by: Gregory CLEMENT Signed-off-by: David S. Miller --- arch/arm64/boot/dts/marvell/armada-3720-db.dts | 23 +++++++++++++++++++++++ arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts index 1372e9a6..a59d36c 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts +++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts @@ -81,3 +81,26 @@ &pcie0 { status = "okay"; }; + +&mdio { + status = "okay"; + phy0: ethernet-phy@0 { + reg = <0>; + }; + + phy1: ethernet-phy@1 { + reg = <1>; + }; +}; + +ð0 { + phy-mode = "rgmii-id"; + phy = <&phy0>; + status = "okay"; +}; + +ð1 { + phy-mode = "sgmii"; + phy = <&phy1>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi index e9bd587..3b8eb45 100644 --- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi @@ -140,6 +140,29 @@ }; }; + eth0: ethernet@30000 { + compatible = "marvell,armada-3700-neta"; + reg = <0x30000 0x4000>; + interrupts = ; + clocks = <&sb_periph_clk 8>; + status = "disabled"; + }; + + mdio: mdio@32004 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "marvell,orion-mdio"; + reg = <0x32004 0x4>; + }; + + eth1: ethernet@40000 { + compatible = "marvell,armada-3700-neta"; + reg = <0x40000 0x4000>; + interrupts = ; + clocks = <&sb_periph_clk 7>; + status = "disabled"; + }; + usb3: usb@58000 { compatible = "marvell,armada3700-xhci", "generic-xhci"; -- cgit v1.1 From f114dca2533ca770aebebffb5ed56e5e7d1fb3fb Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 25 Oct 2016 16:08:46 -0700 Subject: i40e: Be much more verbose about what we can and cannot offload This change makes it so that we are much more robust about defining what we can and cannot offload. Previously we were just checking for the L4 tunnel header length, however there are other fields we should be verifying as there are multiple scenarios in which we cannot perform hardware offloads. In addition the device only supports GSO as long as the MSS is 64 or greater. We were not checking this so an MSS less than that was resulting in Tx hangs. Change-ID: I5e2fd5f3075c73601b4b36327b771c64fcb6c31b Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers --- drivers/net/ethernet/intel/i40e/i40e_main.c | 52 ++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5c6a5ce..2d91274 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9070,10 +9070,6 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 0, 0, nlflags, filter_mask, NULL); } -/* Hardware supports L4 tunnel length of 128B (=2^7) which includes - * inner mac plus all inner ethertypes. - */ -#define I40E_MAX_TUNNEL_HDR_LEN 128 /** * i40e_features_check - Validate encapsulated packet conforms to limits * @skb: skb buff @@ -9084,12 +9080,52 @@ static netdev_features_t i40e_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { - if (skb->encapsulation && - ((skb_inner_network_header(skb) - skb_transport_header(skb)) > - I40E_MAX_TUNNEL_HDR_LEN)) - return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); + size_t len; + + /* No point in doing any of this if neither checksum nor GSO are + * being requested for this frame. We can rule out both by just + * checking for CHECKSUM_PARTIAL + */ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return features; + + /* We cannot support GSO if the MSS is going to be less than + * 64 bytes. If it is then we need to drop support for GSO. + */ + if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_size < 64)) + features &= ~NETIF_F_GSO_MASK; + + /* MACLEN can support at most 63 words */ + len = skb_network_header(skb) - skb->data; + if (len & ~(63 * 2)) + goto out_err; + + /* IPLEN and EIPLEN can support at most 127 dwords */ + len = skb_transport_header(skb) - skb_network_header(skb); + if (len & ~(127 * 4)) + goto out_err; + + if (skb->encapsulation) { + /* L4TUNLEN can support 127 words */ + len = skb_inner_network_header(skb) - skb_transport_header(skb); + if (len & ~(127 * 2)) + goto out_err; + + /* IPLEN can support at most 127 dwords */ + len = skb_inner_transport_header(skb) - + skb_inner_network_header(skb); + if (len & ~(127 * 4)) + goto out_err; + } + + /* No need to validate L4LEN as TCP is the only protocol with a + * a flexible value and we support all possible values supported + * by TCP, which is at most 15 dwords + */ return features; +out_err: + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); } static const struct net_device_ops i40e_netdev_ops = { -- cgit v1.1 From 06fc016c43eac5d88a53677ed0db7e28a7a1b4bd Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 25 Oct 2016 16:08:47 -0700 Subject: i40evf: Be much more verbose about what we can and cannot offload This change makes it so that we are much more robust about defining what we can and cannot offload. Previously we were performing no checks. This should bring us up to parity with the i40e PF driver. In addition the device only supports GSO as long as the MSS is 64 or greater. We were not checking this so an MSS less than that was resulting in Tx hangs. Change-ID: If533553ec92fc6ba694eab6ac81fdaf3004f3592 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index db36744..7bbb55f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2172,6 +2172,64 @@ static int i40evf_change_mtu(struct net_device *netdev, int new_mtu) return 0; } +/** + * i40evf_features_check - Validate encapsulated packet conforms to limits + * @skb: skb buff + * @netdev: This physical port's netdev + * @features: Offload features that the stack believes apply + **/ +static netdev_features_t i40evf_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + size_t len; + + /* No point in doing any of this if neither checksum nor GSO are + * being requested for this frame. We can rule out both by just + * checking for CHECKSUM_PARTIAL + */ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return features; + + /* We cannot support GSO if the MSS is going to be less than + * 64 bytes. If it is then we need to drop support for GSO. + */ + if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_size < 64)) + features &= ~NETIF_F_GSO_MASK; + + /* MACLEN can support at most 63 words */ + len = skb_network_header(skb) - skb->data; + if (len & ~(63 * 2)) + goto out_err; + + /* IPLEN and EIPLEN can support at most 127 dwords */ + len = skb_transport_header(skb) - skb_network_header(skb); + if (len & ~(127 * 4)) + goto out_err; + + if (skb->encapsulation) { + /* L4TUNLEN can support 127 words */ + len = skb_inner_network_header(skb) - skb_transport_header(skb); + if (len & ~(127 * 2)) + goto out_err; + + /* IPLEN can support at most 127 dwords */ + len = skb_inner_transport_header(skb) - + skb_inner_network_header(skb); + if (len & ~(127 * 4)) + goto out_err; + } + + /* No need to validate L4LEN as TCP is the only protocol with a + * a flexible value and we support all possible values supported + * by TCP, which is at most 15 dwords + */ + + return features; +out_err: + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); +} + #define I40EVF_VLAN_FEATURES (NETIF_F_HW_VLAN_CTAG_TX |\ NETIF_F_HW_VLAN_CTAG_RX |\ NETIF_F_HW_VLAN_CTAG_FILTER) @@ -2206,6 +2264,7 @@ static const struct net_device_ops i40evf_netdev_ops = { .ndo_tx_timeout = i40evf_tx_timeout, .ndo_vlan_rx_add_vid = i40evf_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = i40evf_vlan_rx_kill_vid, + .ndo_features_check = i40evf_features_check, .ndo_fix_features = i40evf_fix_features, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = i40evf_netpoll, -- cgit v1.1 From 7429c0bd0155c73e0c6ec8f16e440735209d24a5 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 25 Oct 2016 16:08:48 -0700 Subject: i40e: remove error_param_int label from i40e_vc_config_promiscuous_mode_msg This label is unnecessary, as are jumping to a block that checks aq_ret and then immediately skipping it and returning. So just jump straight to the error_param and remove this unnecessary label. Also use goto error_param even in the last check for style consistency. Change-ID: If487c7d10c4048e37c594e5eca167693aaed45f6 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 53b46553..46908c0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1536,7 +1536,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, vf->vf_id, i40e_stat_str(&pf->hw, aq_ret), i40e_aq_str(&pf->hw, aq_err)); - goto error_param_int; + goto error_param; } } @@ -1581,15 +1581,16 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, allmulti, NULL, true); aq_err = pf->hw.aq.asq_last_status; - if (aq_ret) + if (aq_ret) { dev_err(&pf->pdev->dev, "VF %d failed to set unicast promiscuous mode %8.8x err %s aq_err %s\n", vf->vf_id, info->flags, i40e_stat_str(&pf->hw, aq_ret), i40e_aq_str(&pf->hw, aq_err)); + goto error_param; + } } -error_param_int: if (!aq_ret) { dev_info(&pf->pdev->dev, "VF %d successfully set unicast promiscuous mode\n", -- cgit v1.1 From 6a112785fd18a954033fd030ce156501f15c8fa7 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 25 Oct 2016 16:08:49 -0700 Subject: i40e: remove second check of VLAN_N_VID in i40e_vlan_rx_add_vid Replace a check of magic number 4095 with VLAN_N_VID. This makes it obvious that a later check against VLAN_N_VID is always true and can be removed. Change-ID: I28998f127a61a529480ce63d8a07e266f6c63b7b Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2d91274..fe5939a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2549,7 +2549,7 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev, struct i40e_vsi *vsi = np->vsi; int ret = 0; - if (vid > 4095) + if (vid >= VLAN_N_VID) return -EINVAL; /* If the network stack called us with vid = 0 then @@ -2561,7 +2561,7 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev, if (vid) ret = i40e_vsi_add_vlan(vsi, vid); - if (!ret && (vid < VLAN_N_VID)) + if (!ret) set_bit(vid, vsi->active_vlans); return ret; -- cgit v1.1 From d182a5ca1f72ab4ef5ad118c64fb80aac659b122 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 25 Oct 2016 16:08:50 -0700 Subject: i40e: avoid duplicate private flags definitions Separate the global private flags and the regular private flags per interface into two arrays. Future additions of private flags will not need to be duplicated which may lead to buggy code. Also rename "i40e_priv_flags_strings_gl" to "i40e_gl_priv_flags_strings" for clarity, as it reads more naturally. Change-ID: I68caef3c9954eb7da342d7f9d20f2873186f2758 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 42 +++++++++++--------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index b9e1162..f99c135 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -228,26 +228,22 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { #define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN) -static const char i40e_priv_flags_strings_gl[][ETH_GSTRING_LEN] = { +static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "MFP", "LinkPolling", "flow-director-atr", "veb-stats", "hw-atr-eviction", - "vf-true-promisc-support", }; -#define I40E_PRIV_FLAGS_GL_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings_gl) +#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) -static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { - "NPAR", - "LinkPolling", - "flow-director-atr", - "veb-stats", - "hw-atr-eviction", +/* Private flags with a global effect, restricted to PF 0 */ +static const char i40e_gl_priv_flags_strings[][ETH_GSTRING_LEN] = { + "vf-true-promisc-support", }; -#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) +#define I40E_GL_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gl_priv_flags_strings) /** * i40e_partition_setting_complaint - generic complaint for MFP restriction @@ -1194,10 +1190,9 @@ static void i40e_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(pf->pdev), sizeof(drvinfo->bus_info)); + drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN; if (pf->hw.pf_id == 0) - drvinfo->n_priv_flags = I40E_PRIV_FLAGS_GL_STR_LEN; - else - drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN; + drvinfo->n_priv_flags += I40E_GL_PRIV_FLAGS_STR_LEN; } static void i40e_get_ringparam(struct net_device *netdev, @@ -1425,10 +1420,8 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset) return I40E_VSI_STATS_LEN(netdev); } case ETH_SS_PRIV_FLAGS: - if (pf->hw.pf_id == 0) - return I40E_PRIV_FLAGS_GL_STR_LEN; - else - return I40E_PRIV_FLAGS_STR_LEN; + return I40E_PRIV_FLAGS_STR_LEN + + (pf->hw.pf_id == 0 ? I40E_GL_PRIV_FLAGS_STR_LEN : 0); default: return -EOPNOTSUPP; } @@ -1626,15 +1619,14 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */ break; case ETH_SS_PRIV_FLAGS: + for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { + memcpy(data, i40e_priv_flags_strings[i], + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } if (pf->hw.pf_id == 0) { - for (i = 0; i < I40E_PRIV_FLAGS_GL_STR_LEN; i++) { - memcpy(data, i40e_priv_flags_strings_gl[i], - ETH_GSTRING_LEN); - data += ETH_GSTRING_LEN; - } - } else { - for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { - memcpy(data, i40e_priv_flags_strings[i], + for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++) { + memcpy(data, i40e_gl_priv_flags_strings[i], ETH_GSTRING_LEN); data += ETH_GSTRING_LEN; } -- cgit v1.1 From 91dc1e5d3dbe05046a1d5b7539e5ab08e96b2dd2 Mon Sep 17 00:00:00 2001 From: Michal Kosiarz Date: Tue, 25 Oct 2016 16:08:51 -0700 Subject: i40e: Add Clause22 implementation Some external PHYs require Clause22 method for accessing registers. This patch also adds some defines to support blink led on devices using 10CBaseT PHY. Change-ID: I868a4326911900f6c89e7e522fda4968b0825f14 Signed-off-by: Michal Kosiarz Signed-off-by: Matt Jared Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 180 +++++++++++++++++------ drivers/net/ethernet/intel/i40e/i40e_prototype.h | 12 +- drivers/net/ethernet/intel/i40e/i40e_type.h | 19 ++- 3 files changed, 159 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 98791ba..5cef393 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -4396,7 +4396,99 @@ i40e_status i40e_aq_configure_partition_bw(struct i40e_hw *hw, } /** - * i40e_read_phy_register + * i40e_read_phy_register_clause22 + * @hw: pointer to the HW structure + * @reg: register address in the page + * @phy_adr: PHY address on MDIO interface + * @value: PHY register value + * + * Reads specified PHY register value + **/ +i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw, + u16 reg, u8 phy_addr, u16 *value) +{ + i40e_status status = I40E_ERR_TIMEOUT; + u8 port_num = (u8)hw->func_caps.mdio_port_num; + u32 command = 0; + u16 retry = 1000; + + command = (reg << I40E_GLGEN_MSCA_DEVADD_SHIFT) | + (phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) | + (I40E_MDIO_CLAUSE22_OPCODE_READ_MASK) | + (I40E_MDIO_CLAUSE22_STCODE_MASK) | + (I40E_GLGEN_MSCA_MDICMD_MASK); + wr32(hw, I40E_GLGEN_MSCA(port_num), command); + do { + command = rd32(hw, I40E_GLGEN_MSCA(port_num)); + if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { + status = 0; + break; + } + udelay(10); + retry--; + } while (retry); + + if (status) { + i40e_debug(hw, I40E_DEBUG_PHY, + "PHY: Can't write command to external PHY.\n"); + goto phy_read_end; + } + + if (!status) { + command = rd32(hw, I40E_GLGEN_MSRWD(port_num)); + *value = (command & I40E_GLGEN_MSRWD_MDIRDDATA_MASK) >> + I40E_GLGEN_MSRWD_MDIRDDATA_SHIFT; + } else { + i40e_debug(hw, I40E_DEBUG_PHY, + "PHY: Can't read register value from external PHY.\n"); + } + +phy_read_end: + return status; +} + +/** + * i40e_write_phy_register_clause22 + * @hw: pointer to the HW structure + * @reg: register address in the page + * @phy_adr: PHY address on MDIO interface + * @value: PHY register value + * + * Writes specified PHY register value + **/ +i40e_status i40e_write_phy_register_clause22(struct i40e_hw *hw, + u16 reg, u8 phy_addr, u16 value) +{ + i40e_status status = I40E_ERR_TIMEOUT; + u8 port_num = (u8)hw->func_caps.mdio_port_num; + u32 command = 0; + u16 retry = 1000; + + command = value << I40E_GLGEN_MSRWD_MDIWRDATA_SHIFT; + wr32(hw, I40E_GLGEN_MSRWD(port_num), command); + + command = (reg << I40E_GLGEN_MSCA_DEVADD_SHIFT) | + (phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) | + (I40E_MDIO_CLAUSE22_OPCODE_WRITE_MASK) | + (I40E_MDIO_CLAUSE22_STCODE_MASK) | + (I40E_GLGEN_MSCA_MDICMD_MASK); + + wr32(hw, I40E_GLGEN_MSCA(port_num), command); + do { + command = rd32(hw, I40E_GLGEN_MSCA(port_num)); + if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { + status = 0; + break; + } + udelay(10); + retry--; + } while (retry); + + return status; +} + +/** + * i40e_read_phy_register_clause45 * @hw: pointer to the HW structure * @page: registers page number * @reg: register address in the page @@ -4405,9 +4497,8 @@ i40e_status i40e_aq_configure_partition_bw(struct i40e_hw *hw, * * Reads specified PHY register value **/ -i40e_status i40e_read_phy_register(struct i40e_hw *hw, - u8 page, u16 reg, u8 phy_addr, - u16 *value) +i40e_status i40e_read_phy_register_clause45(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 *value) { i40e_status status = I40E_ERR_TIMEOUT; u32 command = 0; @@ -4417,8 +4508,8 @@ i40e_status i40e_read_phy_register(struct i40e_hw *hw, command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) | (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) | (phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) | - (I40E_MDIO_OPCODE_ADDRESS) | - (I40E_MDIO_STCODE) | + (I40E_MDIO_CLAUSE45_OPCODE_ADDRESS_MASK) | + (I40E_MDIO_CLAUSE45_STCODE_MASK) | (I40E_GLGEN_MSCA_MDICMD_MASK) | (I40E_GLGEN_MSCA_MDIINPROGEN_MASK); wr32(hw, I40E_GLGEN_MSCA(port_num), command); @@ -4440,8 +4531,8 @@ i40e_status i40e_read_phy_register(struct i40e_hw *hw, command = (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) | (phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) | - (I40E_MDIO_OPCODE_READ) | - (I40E_MDIO_STCODE) | + (I40E_MDIO_CLAUSE45_OPCODE_READ_MASK) | + (I40E_MDIO_CLAUSE45_STCODE_MASK) | (I40E_GLGEN_MSCA_MDICMD_MASK) | (I40E_GLGEN_MSCA_MDIINPROGEN_MASK); status = I40E_ERR_TIMEOUT; @@ -4471,7 +4562,7 @@ phy_read_end: } /** - * i40e_write_phy_register + * i40e_write_phy_register_clause45 * @hw: pointer to the HW structure * @page: registers page number * @reg: register address in the page @@ -4480,9 +4571,8 @@ phy_read_end: * * Writes value to specified PHY register **/ -i40e_status i40e_write_phy_register(struct i40e_hw *hw, - u8 page, u16 reg, u8 phy_addr, - u16 value) +i40e_status i40e_write_phy_register_clause45(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 value) { i40e_status status = I40E_ERR_TIMEOUT; u32 command = 0; @@ -4492,8 +4582,8 @@ i40e_status i40e_write_phy_register(struct i40e_hw *hw, command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) | (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) | (phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) | - (I40E_MDIO_OPCODE_ADDRESS) | - (I40E_MDIO_STCODE) | + (I40E_MDIO_CLAUSE45_OPCODE_ADDRESS_MASK) | + (I40E_MDIO_CLAUSE45_STCODE_MASK) | (I40E_GLGEN_MSCA_MDICMD_MASK) | (I40E_GLGEN_MSCA_MDIINPROGEN_MASK); wr32(hw, I40E_GLGEN_MSCA(port_num), command); @@ -4517,8 +4607,8 @@ i40e_status i40e_write_phy_register(struct i40e_hw *hw, command = (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) | (phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) | - (I40E_MDIO_OPCODE_WRITE) | - (I40E_MDIO_STCODE) | + (I40E_MDIO_CLAUSE45_OPCODE_WRITE_MASK) | + (I40E_MDIO_CLAUSE45_STCODE_MASK) | (I40E_GLGEN_MSCA_MDICMD_MASK) | (I40E_GLGEN_MSCA_MDIINPROGEN_MASK); status = I40E_ERR_TIMEOUT; @@ -4580,14 +4670,16 @@ i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, for (gpio_led_port = 0; gpio_led_port < 3; gpio_led_port++, led_addr++) { - status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, &led_reg); + status = i40e_read_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, + &led_reg); if (status) goto phy_blinking_end; led_ctl = led_reg; if (led_reg & I40E_PHY_LED_LINK_MODE_MASK) { led_reg = 0; - status = i40e_write_phy_register(hw, + status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, led_addr, phy_addr, led_reg); @@ -4599,20 +4691,18 @@ i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, if (time > 0 && interval > 0) { for (i = 0; i < time * 1000; i += interval) { - status = i40e_read_phy_register(hw, - I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, - &led_reg); + status = i40e_read_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, &led_reg); if (status) goto restore_config; if (led_reg & I40E_PHY_LED_MANUAL_ON) led_reg = 0; else led_reg = I40E_PHY_LED_MANUAL_ON; - status = i40e_write_phy_register(hw, - I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, - led_reg); + status = i40e_write_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, led_reg); if (status) goto restore_config; msleep(interval); @@ -4620,8 +4710,9 @@ i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, } restore_config: - status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE, led_addr, - phy_addr, led_ctl); + status = i40e_write_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, led_ctl); phy_blinking_end: return status; @@ -4652,8 +4743,10 @@ i40e_status i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr, for (gpio_led_port = 0; gpio_led_port < 3; gpio_led_port++, temp_addr++) { - status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE, - temp_addr, phy_addr, ®_val); + status = i40e_read_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + temp_addr, phy_addr, + ®_val); if (status) return status; *val = reg_val; @@ -4686,41 +4779,42 @@ i40e_status i40e_led_set_phy(struct i40e_hw *hw, bool on, i = rd32(hw, I40E_PFGEN_PORTNUM); port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK); phy_addr = i40e_get_phy_address(hw, port_num); - - status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE, led_addr, - phy_addr, &led_reg); + status = i40e_read_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, &led_reg); if (status) return status; led_ctl = led_reg; if (led_reg & I40E_PHY_LED_LINK_MODE_MASK) { led_reg = 0; - status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, led_reg); + status = i40e_write_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, + led_reg); if (status) return status; } - status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, &led_reg); + status = i40e_read_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, &led_reg); if (status) goto restore_config; if (on) led_reg = I40E_PHY_LED_MANUAL_ON; else led_reg = 0; - status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, led_reg); + status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, led_reg); if (status) goto restore_config; if (mode & I40E_PHY_LED_MODE_ORIG) { led_ctl = (mode & I40E_PHY_LED_MODE_MASK); - status = i40e_write_phy_register(hw, + status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, led_addr, phy_addr, led_ctl); } return status; restore_config: - status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE, led_addr, - phy_addr, led_ctl); + status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, led_ctl); return status; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 4660c5a..82dedc9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -362,10 +362,14 @@ i40e_status i40e_aq_rx_ctl_write_register(struct i40e_hw *hw, u32 reg_addr, u32 reg_val, struct i40e_asq_cmd_details *cmd_details); void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val); -i40e_status i40e_read_phy_register(struct i40e_hw *hw, u8 page, - u16 reg, u8 phy_addr, u16 *value); -i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page, - u16 reg, u8 phy_addr, u16 value); +i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw, + u16 reg, u8 phy_addr, u16 *value); +i40e_status i40e_write_phy_register_clause22(struct i40e_hw *hw, + u16 reg, u8 phy_addr, u16 value); +i40e_status i40e_read_phy_register_clause45(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 *value); +i40e_status i40e_write_phy_register_clause45(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 value); u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num); i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, u32 time, u32 interval); diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index d9a2660..671969c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -90,14 +90,23 @@ enum i40e_debug_mask { I40E_DEBUG_ALL = 0xFFFFFFFF }; -#define I40E_MDIO_STCODE 0 -#define I40E_MDIO_OPCODE_ADDRESS 0 -#define I40E_MDIO_OPCODE_WRITE I40E_MASK(1, \ +#define I40E_MDIO_CLAUSE22_STCODE_MASK I40E_MASK(1, \ + I40E_GLGEN_MSCA_STCODE_SHIFT) +#define I40E_MDIO_CLAUSE22_OPCODE_WRITE_MASK I40E_MASK(1, \ I40E_GLGEN_MSCA_OPCODE_SHIFT) -#define I40E_MDIO_OPCODE_READ_INC_ADDR I40E_MASK(2, \ +#define I40E_MDIO_CLAUSE22_OPCODE_READ_MASK I40E_MASK(2, \ I40E_GLGEN_MSCA_OPCODE_SHIFT) -#define I40E_MDIO_OPCODE_READ I40E_MASK(3, \ + +#define I40E_MDIO_CLAUSE45_STCODE_MASK I40E_MASK(0, \ + I40E_GLGEN_MSCA_STCODE_SHIFT) +#define I40E_MDIO_CLAUSE45_OPCODE_ADDRESS_MASK I40E_MASK(0, \ + I40E_GLGEN_MSCA_OPCODE_SHIFT) +#define I40E_MDIO_CLAUSE45_OPCODE_WRITE_MASK I40E_MASK(1, \ I40E_GLGEN_MSCA_OPCODE_SHIFT) +#define I40E_MDIO_CLAUSE45_OPCODE_READ_INC_ADDR_MASK I40E_MASK(2, \ + I40E_GLGEN_MSCA_OPCODE_SHIFT) +#define I40E_MDIO_CLAUSE45_OPCODE_READ_MASK I40E_MASK(3, \ + I40E_GLGEN_MSCA_OPCODE_SHIFT) #define I40E_PHY_COM_REG_PAGE 0x1E #define I40E_PHY_LED_LINK_MODE_MASK 0xF0 -- cgit v1.1 From 0b7c8b5d5436317a5f4509e2a150c6cec017f348 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 25 Oct 2016 16:08:52 -0700 Subject: i40e: fix trivial typo in naming of i40e_sync_filters_subtask A comment incorrectly referred to i40e_vsi_sync_filters_subtask which does not actually exist. Reference the correct function instead. Change-ID: I6bd805c605741ffb6fe34377259bb0d597edfafd Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index fe5939a..6832ab1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1211,12 +1211,12 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) * i40e_add_filter. * * 2) the only place where filters are actually removed is in - * i40e_vsi_sync_filters_subtask. + * i40e_sync_filters_subtask. * * Thus, we can simply use a boolean value, has_vlan_filters which we * will set to true when we add a VLAN filter in i40e_add_filter. Then * we have to perform the full search after deleting filters in - * i40e_vsi_sync_filters_subtask, but we already have to search + * i40e_sync_filters_subtask, but we already have to search * filters here and can perform the check at the same time. This * results in avoiding embedding a loop for VLAN mode inside another * loop over all the filters, and should maintain correctness as noted -- cgit v1.1 From 64f5ead95ade8d43e7f3ee79261b5525fff1feac Mon Sep 17 00:00:00 2001 From: Piotr Raczynski Date: Tue, 25 Oct 2016 16:08:53 -0700 Subject: i40e: Add protocols over MCTP to i40e_aq_discover_capabilities Add logical_id to I40E_AQ_CAP_ID_MNG_MODE capability starting from major version 2. Change-ID: Idb29214b172ea5c70cbd45a99e6745c0215af7e4 Signed-off-by: Piotr Raczynski Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 8 ++++++++ drivers/net/ethernet/intel/i40e/i40e_type.h | 4 ++++ drivers/net/ethernet/intel/i40evf/i40e_type.h | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 5cef393..e83e8e3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -3147,6 +3147,14 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, break; case I40E_AQ_CAP_ID_MNG_MODE: p->management_mode = number; + if (major_rev > 1) { + p->mng_protocols_over_mctp = logical_id; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Protocols over MCTP = %d\n", + p->mng_protocols_over_mctp); + } else { + p->mng_protocols_over_mctp = 0; + } break; case I40E_AQ_CAP_ID_NPAR_ACTIVE: p->npar_enable = number; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 671969c..bd1ffae 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -263,6 +263,10 @@ struct i40e_hw_capabilities { #define I40E_NVM_IMAGE_TYPE_UDP_CLOUD 0x3 u32 management_mode; + u32 mng_protocols_over_mctp; +#define I40E_MNG_PROTOCOL_PLDM 0x2 +#define I40E_MNG_PROTOCOL_OEM_COMMANDS 0x4 +#define I40E_MNG_PROTOCOL_NCSI 0x8 u32 npar_enable; u32 os2bmc; u32 valid_functions; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index ca7afe5..515484c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -237,6 +237,10 @@ struct i40e_hw_capabilities { #define I40E_NVM_IMAGE_TYPE_UDP_CLOUD 0x3 u32 management_mode; + u32 mng_protocols_over_mctp; +#define I40E_MNG_PROTOCOL_PLDM 0x2 +#define I40E_MNG_PROTOCOL_OEM_COMMANDS 0x4 +#define I40E_MNG_PROTOCOL_NCSI 0x8 u32 npar_enable; u32 os2bmc; u32 valid_functions; -- cgit v1.1 From 2f7679ee2ec1bfc5042ff334bade395ef208d4fe Mon Sep 17 00:00:00 2001 From: Tushar Dave Date: Wed, 26 Oct 2016 10:49:27 -0700 Subject: i40e: fix panic on SPARC while changing num of desc On SPARC, writel() should not be used to write directly to memory address but only to memory mapped I/O address otherwise it causes data access exception. Commit 147e81ec75689 ("i40e: Test memory before ethtool alloc succeeds") introduced a code that uses memory address to fake the HW tail address and attempt to write to that address using writel() causes kernel panic on SPARC. The issue is reproduced while changing number of descriptors using ethtool. This change resolves the panic by using HW read-only memory mapped I/O register to fake HW tail address instead memory address. e.g. > ethtool -G eth2 tx 2048 rx 2048 i40e 0000:03:00.2 eth2: Changing Tx descriptor count from 512 to 2048. i40e 0000:03:00.2 eth2: Changing Rx descriptor count from 512 to 2048 sun4v_data_access_exception: ADDR[fff8001f9734a000] CTX[0000] TYPE[0004], going. \|/ ____ \|/ "@'/ .. \`@" /_| \__/ |_\ \__U_/ ethtool(3273): Dax [#1] CPU: 9 PID: 3273 Comm: ethtool Tainted: G E 4.8.0-linux-net_temp+ #7 task: fff8001f96d7a660 task.stack: fff8001f97348000 TSTATE: 0000009911001601 TPC: 00000000103189e4 TNPC: 00000000103189e8 Y: 00000000 Tainted: G E TPC: g0: fff8001f4eb64000 g1: 00000000000007ff g2: fff8001f9734b92c g3: 00203e0000000000 g4: fff8001f96d7a660 g5: fff8001fa6704000 g6: fff8001f97348000 g7: 0000000000000001 o0: 0006000046706928 o1: 00000000db3e2000 o2: fff8001f00000000 o3: 0000000000002000 o4: 0000000000002000 o5: 0000000000000001 sp: fff8001f9734afc1 ret_pc: 0000000010318a64 RPC: l0: fff8001f4e8bffe0 l1: fff8001f4e8cffe0 l2: 00000000000007ff l3: 00000000ff000000 l4: 0000000000ff0000 l5: 000000000000ff00 l6: 0000000000cda6a8 l7: 0000000000e822f0 i0: fff8001f96380000 i1: 0000000000000000 i2: 00203edb00000000 i3: 0006000046706928 i4: 0000000002086320 i5: 0000000000e82370 i6: fff8001f9734b071 i7: 00000000103062d4 I7: Call Trace: [00000000103062d4] i40e_set_ringparam+0x3b4/0x540 [i40e] [000000000094e2f8] dev_ethtool+0x898/0xbe0 [0000000000965570] dev_ioctl+0x250/0x300 [0000000000923800] sock_do_ioctl+0x40/0x60 [000000000092427c] sock_ioctl+0x7c/0x280 [00000000005ef040] vfs_ioctl+0x20/0x60 [00000000005ef5d4] do_vfs_ioctl+0x194/0x4c0 [00000000005ef974] SyS_ioctl+0x74/0xa0 [0000000000406214] linux_sparc_syscall+0x34/0x44 Disabling lock debugging due to kernel taint Caller[00000000103062d4]: i40e_set_ringparam+0x3b4/0x540 [i40e] Caller[000000000094e2f8]: dev_ethtool+0x898/0xbe0 Caller[0000000000965570]: dev_ioctl+0x250/0x300 Caller[0000000000923800]: sock_do_ioctl+0x40/0x60 Caller[000000000092427c]: sock_ioctl+0x7c/0x280 Caller[00000000005ef040]: vfs_ioctl+0x20/0x60 Caller[00000000005ef5d4]: do_vfs_ioctl+0x194/0x4c0 Caller[00000000005ef974]: SyS_ioctl+0x74/0xa0 Caller[0000000000406214]: linux_sparc_syscall+0x34/0x44 Caller[0000000000107154]: 0x107154 Instruction DUMP: e43620c8 e436204a c45e2038 82102000 81cfe008 90086001 82102000 81cfe008 Kernel panic - not syncing: Fatal exception Signed-off-by: Tushar Dave Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index f99c135..92a2f03 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1217,6 +1217,7 @@ static int i40e_set_ringparam(struct net_device *netdev, { struct i40e_ring *tx_rings = NULL, *rx_rings = NULL; struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_hw *hw = &np->vsi->back->hw; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; u32 new_rx_count, new_tx_count; @@ -1309,10 +1310,6 @@ static int i40e_set_ringparam(struct net_device *netdev, } for (i = 0; i < vsi->num_queue_pairs; i++) { - /* this is to allow wr32 to have something to write to - * during early allocation of Rx buffers - */ - u32 __iomem faketail = 0; struct i40e_ring *ring; u16 unused; @@ -1324,7 +1321,10 @@ static int i40e_set_ringparam(struct net_device *netdev, */ rx_rings[i].desc = NULL; rx_rings[i].rx_bi = NULL; - rx_rings[i].tail = (u8 __iomem *)&faketail; + /* this is to allow wr32 to have something to write to + * during early allocation of Rx buffers + */ + rx_rings[i].tail = hw->hw_addr + I40E_PRTGEN_STATUS; err = i40e_setup_rx_descriptors(&rx_rings[i]); if (err) goto rx_unwind; -- cgit v1.1 From dedecb6d429bd3311bb24ea1379b47c8471c88b9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 1 Nov 2016 15:35:14 -0700 Subject: i40evf: Move some i40evf_reset_task code to separate function The i40evf_reset_task function is a couple hundred lines and it has a separable block that disables VF. Move that block to a new i40evf_disable_vf function to shorten i40evf_reset_task a bit. Signed-off-by: Joe Perches Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 86 +++++++++++++------------ 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 7bbb55f..f04ac36 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1693,6 +1693,49 @@ restart_watchdog: schedule_work(&adapter->adminq_task); } +static void i40evf_disable_vf(struct i40evf_adapter *adapter) +{ + struct i40evf_mac_filter *f, *ftmp; + struct i40evf_vlan_filter *fv, *fvtmp; + + adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; + + if (netif_running(adapter->netdev)) { + set_bit(__I40E_DOWN, &adapter->vsi.state); + netif_carrier_off(adapter->netdev); + netif_tx_disable(adapter->netdev); + adapter->link_up = false; + i40evf_napi_disable_all(adapter); + i40evf_irq_disable(adapter); + i40evf_free_traffic_irqs(adapter); + i40evf_free_all_tx_resources(adapter); + i40evf_free_all_rx_resources(adapter); + } + + /* Delete all of the filters, both MAC and VLAN. */ + list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) { + list_del(&f->list); + kfree(f); + } + + list_for_each_entry_safe(fv, fvtmp, &adapter->vlan_filter_list, list) { + list_del(&fv->list); + kfree(fv); + } + + i40evf_free_misc_irq(adapter); + i40evf_reset_interrupt_capability(adapter); + i40evf_free_queues(adapter); + i40evf_free_q_vectors(adapter); + kfree(adapter->vf_res); + i40evf_shutdown_adminq(&adapter->hw); + adapter->netdev->flags &= ~IFF_UP; + clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); + adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; + adapter->state = __I40EVF_DOWN; + dev_info(&adapter->pdev->dev, "Reset task did not complete, VF disabled\n"); +} + #define I40EVF_RESET_WAIT_MS 10 #define I40EVF_RESET_WAIT_COUNT 500 /** @@ -1758,50 +1801,9 @@ static void i40evf_reset_task(struct work_struct *work) pci_set_master(adapter->pdev); if (i == I40EVF_RESET_WAIT_COUNT) { - struct i40evf_mac_filter *ftmp; - struct i40evf_vlan_filter *fv, *fvtmp; - - /* reset never finished */ dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n", reg_val); - adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; - - if (netif_running(adapter->netdev)) { - set_bit(__I40E_DOWN, &adapter->vsi.state); - netif_carrier_off(netdev); - netif_tx_disable(netdev); - adapter->link_up = false; - i40evf_napi_disable_all(adapter); - i40evf_irq_disable(adapter); - i40evf_free_traffic_irqs(adapter); - i40evf_free_all_tx_resources(adapter); - i40evf_free_all_rx_resources(adapter); - } - - /* Delete all of the filters, both MAC and VLAN. */ - list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, - list) { - list_del(&f->list); - kfree(f); - } - - list_for_each_entry_safe(fv, fvtmp, &adapter->vlan_filter_list, - list) { - list_del(&fv->list); - kfree(fv); - } - - i40evf_free_misc_irq(adapter); - i40evf_reset_interrupt_capability(adapter); - i40evf_free_queues(adapter); - i40evf_free_q_vectors(adapter); - kfree(adapter->vf_res); - i40evf_shutdown_adminq(hw); - adapter->netdev->flags &= ~IFF_UP; - clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); - adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; - adapter->state = __I40EVF_DOWN; - dev_info(&adapter->pdev->dev, "Reset task did not complete, VF disabled\n"); + i40evf_disable_vf(adapter); return; /* Do not attempt to reinit. It's dead, Jim. */ } -- cgit v1.1 From 47d2a5d8279dcdcaec2c67b3d2c72cfa62979c58 Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Tue, 8 Nov 2016 13:05:05 -0800 Subject: i40evf: check for msix_entries null dereference It is possible for msix_entries to be freed by a previous suspend/remove before a VF is closed. This patch fixes the issue by checking for NULL before dereferencing msix_entries and returning early in the case where it is NULL within the i40evf_close code path. Without this patch it is possible to trigger a kernel panic through NULL dereference. Change-ID: I92a2746e82533a889e25f91578eac9abd0388ae2 Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index f04ac36..420e545 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -631,6 +631,9 @@ static void i40evf_free_traffic_irqs(struct i40evf_adapter *adapter) { int vector, irq_num, q_vectors; + if (!adapter->msix_entries) + return; + q_vectors = adapter->num_msix_vectors - NONQ_VECS; for (vector = 0; vector < q_vectors; vector++) { @@ -1443,6 +1446,9 @@ static void i40evf_free_q_vectors(struct i40evf_adapter *adapter) **/ void i40evf_reset_interrupt_capability(struct i40evf_adapter *adapter) { + if (!adapter->msix_entries) + return; + pci_disable_msix(adapter->pdev); kfree(adapter->msix_entries); adapter->msix_entries = NULL; -- cgit v1.1 From 27e5f25b917b4d388ffba8dbfae4952834d48e49 Mon Sep 17 00:00:00 2001 From: Henry Tieman Date: Tue, 8 Nov 2016 13:05:06 -0800 Subject: i40e: Remove unreachable code The code at the end of i40e_read_phy_register_clause22() contained unreachable code and redundant control statements. This change removes the unreachable code. And deletes the redundant goto statement and if statement. Change-ID: I713032b1585396f40f903cbcfdea987abd874400 Signed-off-by: Henry Tieman Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index e83e8e3..fc7ce02 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -4439,19 +4439,12 @@ i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw, if (status) { i40e_debug(hw, I40E_DEBUG_PHY, "PHY: Can't write command to external PHY.\n"); - goto phy_read_end; - } - - if (!status) { + } else { command = rd32(hw, I40E_GLGEN_MSRWD(port_num)); *value = (command & I40E_GLGEN_MSRWD_MDIRDDATA_MASK) >> I40E_GLGEN_MSRWD_MDIRDDATA_SHIFT; - } else { - i40e_debug(hw, I40E_DEBUG_PHY, - "PHY: Can't read register value from external PHY.\n"); } -phy_read_end: return status; } -- cgit v1.1 From e586bb66b57c2a50d464d0f87cf8c7c9c3b36835 Mon Sep 17 00:00:00 2001 From: Henry Tieman Date: Tue, 8 Nov 2016 13:05:07 -0800 Subject: i40e: Pass unknown PHY type for unknown PHYs The PHY type value for unrecognized PHYs and cables was changed based on firmware version number. Newer hardware use lower firmware version numbers and this was causing some PHYs to be identified as type 0x16 instead of 0xe (unknown). Without this patch, newer card will incorrectly identify unknown PHYs and cables. This change adds hardware type to the check for firmware version so the PHY type is reported correctly. Change-ID: I0723cbfd263c76fc73ff1a5275d1639051376c9a Signed-off-by: Henry Tieman Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index fc7ce02..f6fee79 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1854,7 +1854,8 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, else hw_link_info->lse_enable = false; - if ((hw->aq.fw_maj_ver < 4 || (hw->aq.fw_maj_ver == 4 && + if ((hw->mac.type == I40E_MAC_XL710) && + (hw->aq.fw_maj_ver < 4 || (hw->aq.fw_maj_ver == 4 && hw->aq.fw_min_ver < 40)) && hw_link_info->phy_type == 0xE) hw_link_info->phy_type = I40E_PHY_TYPE_10GBASE_SFPP_CU; -- cgit v1.1 From ef4603e82f64605e73495c26050f3f236609d991 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 8 Nov 2016 13:05:08 -0800 Subject: i40evf: protect against NULL msix_entries and q_vectors pointers Update the functions which free msix_entries and q_vectors so that they are safe against NULL values. This allows calling code to not care whether these have already been freed when disabling and freeing them. Change-ID: I31bfd1c0da18023d971b618edc6fb049721f3298 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 420e545..ca85021 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -207,6 +207,9 @@ static void i40evf_misc_irq_disable(struct i40evf_adapter *adapter) { struct i40e_hw *hw = &adapter->hw; + if (!adapter->msix_entries) + return; + wr32(hw, I40E_VFINT_DYN_CTL01, 0); /* read flush */ @@ -654,6 +657,9 @@ static void i40evf_free_misc_irq(struct i40evf_adapter *adapter) { struct net_device *netdev = adapter->netdev; + if (!adapter->msix_entries) + return; + free_irq(adapter->msix_entries[0].vector, netdev); } @@ -1428,6 +1434,9 @@ static void i40evf_free_q_vectors(struct i40evf_adapter *adapter) int q_idx, num_q_vectors; int napi_vectors; + if (!adapter->q_vectors) + return; + num_q_vectors = adapter->num_msix_vectors - NONQ_VECS; napi_vectors = adapter->num_active_queues; @@ -1437,6 +1446,7 @@ static void i40evf_free_q_vectors(struct i40evf_adapter *adapter) netif_napi_del(&q_vector->napi); } kfree(adapter->q_vectors); + adapter->q_vectors = NULL; } /** @@ -2862,12 +2872,10 @@ static void i40evf_remove(struct pci_dev *pdev) msleep(50); } - if (adapter->msix_entries) { - i40evf_misc_irq_disable(adapter); - i40evf_free_misc_irq(adapter); - i40evf_reset_interrupt_capability(adapter); - i40evf_free_q_vectors(adapter); - } + i40evf_misc_irq_disable(adapter); + i40evf_free_misc_irq(adapter); + i40evf_reset_interrupt_capability(adapter); + i40evf_free_q_vectors(adapter); if (adapter->watchdog_timer.function) del_timer_sync(&adapter->watchdog_timer); -- cgit v1.1 From 4ad9f4f9e2873ca999135b151dbb7d80f57ea0a9 Mon Sep 17 00:00:00 2001 From: Harshitha Ramamurthy Date: Tue, 8 Nov 2016 13:05:09 -0800 Subject: i40e: Fix for ethtool Supported link modes This patch fixes the problem where the ethtool Supported link modes list backplane interfaces on X722 devices for 10GbE with SFP+ and Cortina retimer. This patch fixes the problem by setting and using a flag for this particular device since the backplane interface is only between the internal PHY and the retimer and it should not be seen by the user as they cannot use it. Without this patch, the user wrongly thinks that backplane interfaces are supported on their device when they actually are not. Change-ID: I3882bc2928431d48a2db03a51a713a1f681a79e9 Signed-off-by: Harshitha Ramamurthy Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 16 ++++++++++------ drivers/net/ethernet/intel/i40e/i40e_main.c | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 29c2318..4cb8fb3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -359,6 +359,7 @@ struct i40e_pf { #define I40E_FLAG_HAVE_10GBASET_PHY BIT_ULL(48) #define I40E_FLAG_PF_MAC BIT_ULL(50) #define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) +#define I40E_FLAG_HAVE_CRT_RETIMER BIT_ULL(52) /* tracks features that get auto disabled by errors */ u64 auto_disable_flags; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 92a2f03..9500e7b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -345,11 +345,13 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_20000baseKR2_Full; } if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { - *supported |= SUPPORTED_10000baseKR_Full | - SUPPORTED_Autoneg; + if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + *supported |= SUPPORTED_10000baseKR_Full | + SUPPORTED_Autoneg; *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - *advertising |= ADVERTISED_10000baseKR_Full; + if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + *advertising |= ADVERTISED_10000baseKR_Full; } if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { *supported |= SUPPORTED_10000baseKX4_Full | @@ -359,11 +361,13 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_10000baseKX4_Full; } if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { - *supported |= SUPPORTED_1000baseKX_Full | - SUPPORTED_Autoneg; + if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + *supported |= SUPPORTED_1000baseKX_Full | + SUPPORTED_Autoneg; *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - *advertising |= ADVERTISED_1000baseKX_Full; + if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + *advertising |= ADVERTISED_1000baseKX_Full; } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 6832ab1..17dde85 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -11302,7 +11302,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if ((pf->hw.device_id == I40E_DEV_ID_10G_BASE_T) || (pf->hw.device_id == I40E_DEV_ID_10G_BASE_T4)) pf->flags |= I40E_FLAG_HAVE_10GBASET_PHY; - + if (pf->hw.device_id == I40E_DEV_ID_SFP_I_X722) + pf->flags |= I40E_FLAG_HAVE_CRT_RETIMER; /* print a string summarizing features */ i40e_print_features(pf); -- cgit v1.1 From 435c084aad9615c7c77464e7c04600da920fc208 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 8 Nov 2016 13:05:10 -0800 Subject: i40e: set broadcast promiscuous mode for each active VLAN A previous workaround added to ensure receipt of all broadcast frames incorrectly set the broadcast promiscuous mode unconditionally regardless of active VLAN status. Replace this partial workaround with a complete solution that sets the broadcast promiscuous filters in i40e_sync_vsi_filters. This new method sets the promiscuous mode based on when broadcast filters are added or removed. I40E_VLAN_ANY will request a broadcast filter for all VLANs, (as we're in untagged mode) while a broadcast filter on a specific VLAN will only request broadcast for that VLAN. Thus, we restore addition of broadcast filter to the array, but we add special handling for these such that they enable the broadcast promiscuous mode instead of being sent as regular filters. The end result is that we will correctly receive all broadcast packets (even those with a *source* address equal to the broadcast address) but will not receive packets for which we don't have an active VLAN filter. Change-ID: I7d0585c5cec1a5bf55bf533b42e5e817d5db6a2d Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 34 +++++++ drivers/net/ethernet/intel/i40e/i40e_main.c | 106 +++++++++++++++++---- drivers/net/ethernet/intel/i40e/i40e_prototype.h | 3 + drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 7 ++ 4 files changed, 129 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index f6fee79..eb392d6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -2170,6 +2170,40 @@ enum i40e_status_code i40e_aq_set_vsi_uc_promisc_on_vlan(struct i40e_hw *hw, } /** + * i40e_aq_set_vsi_bc_promisc_on_vlan + * @hw: pointer to the hw struct + * @seid: vsi number + * @enable: set broadcast promiscuous enable/disable for a given VLAN + * @vid: The VLAN tag filter - capture any broadcast packet with this VLAN tag + * @cmd_details: pointer to command details structure or NULL + **/ +i40e_status i40e_aq_set_vsi_bc_promisc_on_vlan(struct i40e_hw *hw, + u16 seid, bool enable, u16 vid, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_set_vsi_promiscuous_modes *cmd = + (struct i40e_aqc_set_vsi_promiscuous_modes *)&desc.params.raw; + i40e_status status; + u16 flags = 0; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_set_vsi_promiscuous_modes); + + if (enable) + flags |= I40E_AQC_SET_VSI_PROMISC_BROADCAST; + + cmd->promiscuous_flags = cpu_to_le16(flags); + cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_BROADCAST); + cmd->seid = cpu_to_le16(seid); + cmd->vlan_tag = cpu_to_le16(vid | I40E_AQC_SET_VSI_VLAN_VALID); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + return status; +} + +/** * i40e_aq_set_vsi_broadcast * @hw: pointer to the hw struct * @seid: vsi number diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 17dde85..594856d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1245,13 +1245,6 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, if (!vsi || !macaddr) return NULL; - /* Do not allow broadcast filter to be added since broadcast filter - * is added as part of add VSI for any newly created VSI except - * FDIR VSI - */ - if (is_broadcast_ether_addr(macaddr)) - return NULL; - f = i40e_find_filter(vsi, macaddr, vlan); if (!f) { f = kzalloc(sizeof(*f), GFP_ATOMIC); @@ -1857,6 +1850,47 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, } /** + * i40e_aqc_broadcast_filter - Set promiscuous broadcast flags + * @vsi: pointer to the VSI + * @f: filter data + * + * This function sets or clears the promiscuous broadcast flags for VLAN + * filters in order to properly receive broadcast frames. Assumes that only + * broadcast filters are passed. + **/ +static +void i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name, + struct i40e_mac_filter *f) +{ + bool enable = f->state == I40E_FILTER_NEW; + struct i40e_hw *hw = &vsi->back->hw; + i40e_status aq_ret; + + if (f->vlan == I40E_VLAN_ANY) { + aq_ret = i40e_aq_set_vsi_broadcast(hw, + vsi->seid, + enable, + NULL); + } else { + aq_ret = i40e_aq_set_vsi_bc_promisc_on_vlan(hw, + vsi->seid, + enable, + f->vlan, + NULL); + } + + if (aq_ret) { + dev_warn(&vsi->back->pdev->dev, + "Error %s setting broadcast promiscuous mode on %s\n", + i40e_aq_str(hw, hw->aq.asq_last_status), + vsi_name); + f->state = I40E_FILTER_FAILED; + } else if (enable) { + f->state = I40E_FILTER_ACTIVE; + } +} + +/** * i40e_sync_vsi_filters - Update the VSI filter list to the HW * @vsi: ptr to the VSI * @@ -2004,6 +2038,17 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) hlist_for_each_entry_safe(f, h, &tmp_del_list, hlist) { cmd_flags = 0; + /* handle broadcast filters by updating the broadcast + * promiscuous flag instead of deleting a MAC filter. + */ + if (is_broadcast_ether_addr(f->macaddr)) { + i40e_aqc_broadcast_filter(vsi, vsi_name, f); + + hlist_del(&f->hlist); + kfree(f); + continue; + } + /* add to delete list */ ether_addr_copy(del_list[num_del].mac_addr, f->macaddr); if (f->vlan == I40E_VLAN_ANY) { @@ -2060,12 +2105,25 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) goto err_no_memory; num_add = 0; - hlist_for_each_entry(f, &tmp_add_list, hlist) { + hlist_for_each_entry_safe(f, h, &tmp_add_list, hlist) { if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state)) { f->state = I40E_FILTER_FAILED; continue; } + + /* handle broadcast filters by updating the broadcast + * promiscuous flag instead of adding a MAC filter. + */ + if (is_broadcast_ether_addr(f->macaddr)) { + u64 key = i40e_addr_to_hkey(f->macaddr); + i40e_aqc_broadcast_filter(vsi, vsi_name, f); + + hlist_del(&f->hlist); + hash_add(vsi->mac_filter_hash, &f->hlist, key); + continue; + } + /* add to add array */ if (num_add == 0) add_head = f; @@ -9178,6 +9236,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) struct i40e_hw *hw = &pf->hw; struct i40e_netdev_priv *np; struct net_device *netdev; + u8 broadcast[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; int etherdev_size; @@ -9246,6 +9305,24 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) spin_unlock_bh(&vsi->mac_filter_hash_lock); } + /* Add the broadcast filter so that we initially will receive + * broadcast packets. Note that when a new VLAN is first added the + * driver will convert all filters marked I40E_VLAN_ANY into VLAN + * specific filters as part of transitioning into "vlan" operation. + * When more VLANs are added, the driver will copy each existing MAC + * filter and add it for the new VLAN. + * + * Broadcast filters are handled specially by + * i40e_sync_filters_subtask, as the driver must to set the broadcast + * promiscuous bit instead of adding this directly as a MAC/VLAN + * filter. The subtask will update the correct broadcast promiscuous + * bits as VLANs become active or inactive. + */ + eth_broadcast_addr(broadcast); + spin_lock_bh(&vsi->mac_filter_hash_lock); + i40e_add_filter(vsi, broadcast, I40E_VLAN_ANY); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + ether_addr_copy(netdev->dev_addr, mac_addr); ether_addr_copy(netdev->perm_addr, mac_addr); @@ -9328,7 +9405,6 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi) static int i40e_add_vsi(struct i40e_vsi *vsi) { int ret = -ENODEV; - i40e_status aq_ret = 0; struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; struct i40e_vsi_context ctxt; @@ -9518,18 +9594,6 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) vsi->seid = ctxt.seid; vsi->id = ctxt.vsi_number; } - /* Except FDIR VSI, for all othet VSI set the broadcast filter */ - if (vsi->type != I40E_VSI_FDIR) { - aq_ret = i40e_aq_set_vsi_broadcast(hw, vsi->seid, true, NULL); - if (aq_ret) { - ret = i40e_aq_rc_to_posix(aq_ret, - hw->aq.asq_last_status); - dev_info(&pf->pdev->dev, - "set brdcast promisc failed, err %s, aq_err %s\n", - i40e_stat_str(hw, aq_ret), - i40e_aq_str(hw, hw->aq.asq_last_status)); - } - } vsi->active_filters = 0; clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 82dedc9..37d67e7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -144,6 +144,9 @@ enum i40e_status_code i40e_aq_set_vsi_uc_promisc_on_vlan(struct i40e_hw *hw, u16 seid, bool enable, u16 vid, struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_set_vsi_bc_promisc_on_vlan(struct i40e_hw *hw, + u16 seid, bool enable, u16 vid, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_vsi_vlan_promisc(struct i40e_hw *hw, u16 seid, bool enable, struct i40e_asq_cmd_details *cmd_details); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 46908c0..05ed49b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -674,6 +674,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) } if (type == I40E_VSI_SRIOV) { u64 hena = i40e_pf_get_default_rss_hena(pf); + u8 broadcast[ETH_ALEN]; vf->lan_vsi_idx = vsi->idx; vf->lan_vsi_id = vsi->id; @@ -696,6 +697,12 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) "Could not add MAC filter %pM for VF %d\n", vf->default_lan_addr.addr, vf->vf_id); } + eth_broadcast_addr(broadcast); + f = i40e_add_filter(vsi, broadcast, + vf->port_vlan_id ? vf->port_vlan_id : -1); + if (!f) + dev_info(&pf->pdev->dev, + "Could not allocate VF broadcast filter\n"); spin_unlock_bh(&vsi->mac_filter_hash_lock); i40e_write_rx_ctl(&pf->hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)hena); -- cgit v1.1 From e5d32205555da77ec8d0d129ffa27c20f3d60230 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 8 Nov 2016 13:05:11 -0800 Subject: i40e/i40evf: replace for memcpy with single memcpy call in ethtool memcpy replaced with single memcpy call in ethtool. Change-ID: I3f5bef6bcc593412c56592c6459784db41575a0a Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 9500e7b..76753e1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1536,10 +1536,8 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, switch (stringset) { case ETH_SS_TEST: - for (i = 0; i < I40E_TEST_LEN; i++) { - memcpy(data, i40e_gstrings_test[i], ETH_GSTRING_LEN); - data += ETH_GSTRING_LEN; - } + memcpy(data, i40e_gstrings_test, + I40E_TEST_LEN * ETH_GSTRING_LEN); break; case ETH_SS_STATS: for (i = 0; i < I40E_NETDEV_STATS_LEN; i++) { @@ -1623,18 +1621,12 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */ break; case ETH_SS_PRIV_FLAGS: - for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { - memcpy(data, i40e_priv_flags_strings[i], - ETH_GSTRING_LEN); - data += ETH_GSTRING_LEN; - } - if (pf->hw.pf_id == 0) { - for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++) { - memcpy(data, i40e_gl_priv_flags_strings[i], - ETH_GSTRING_LEN); - data += ETH_GSTRING_LEN; - } - } + memcpy(data, i40e_priv_flags_strings, + I40E_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); + data += I40E_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN; + if (pf->hw.pf_id == 0) + memcpy(data, i40e_gl_priv_flags_strings, + I40E_GL_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); break; default: break; -- cgit v1.1 From 23bb6dc3644a6705c989e326362c7ccf05807477 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Tue, 8 Nov 2016 13:05:12 -0800 Subject: i40e: change message to only appear when extra debug info is wanted This patch changes an X722 informational message so that it only appears when extra messages are desired. Without this patch, on X722 devices, this message appears at load, potentially causing unnecessary alarm. Change-ID: I94f7aae15dc5b2723cc9728c630c72538a3e670e Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 594856d..5777e49 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3579,7 +3579,7 @@ static irqreturn_t i40e_intr(int irq, void *data) (ena_mask & I40E_PFINT_ICR0_ENA_PE_CRITERR_MASK)) { ena_mask &= ~I40E_PFINT_ICR0_ENA_PE_CRITERR_MASK; icr0 &= ~I40E_PFINT_ICR0_ENA_PE_CRITERR_MASK; - dev_info(&pf->pdev->dev, "cleared PE_CRITERR\n"); + dev_dbg(&pf->pdev->dev, "cleared PE_CRITERR\n"); } /* only q0 is used in MSI/Legacy mode, and none are used in MSIX */ -- cgit v1.1 From c6c60dae81708aa2cb4166d3530cb9348c5f6b8c Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 1 Dec 2016 11:32:18 +0100 Subject: stmmac: cleanup documenation, make it match reality Fix english in documentation, make documentation match reality, remove options that were removed from code. Signed-off-by: Pavel Machek Signed-off-by: David S. Miller --- Documentation/networking/stmmac.txt | 47 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index e226f89..014f4f7 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -28,8 +28,6 @@ CONFIG_STMMAC_PCI: is to enable the pci driver. 2) Driver parameters list: debug: message level (0: no output, 16: all); phyaddr: to manually provide the physical address to the PHY device; - dma_rxsize: DMA rx ring size; - dma_txsize: DMA tx ring size; buf_sz: DMA buffer size; tc: control the HW FIFO threshold; watchdog: transmit timeout (in milliseconds); @@ -40,31 +38,31 @@ CONFIG_STMMAC_PCI: is to enable the pci driver. 3) Command line options Driver parameters can be also passed in command line by using: - stmmaceth=dma_rxsize:128,dma_txsize:512 + stmmaceth=watchdog:100,chain_mode=1 4) Driver information and notes 4.1) Transmit process The xmit method is invoked when the kernel needs to transmit a packet; it sets -the descriptors in the ring and informs the DMA engine that there is a packet +the descriptors in the ring and informs the DMA engine, that there is a packet ready to be transmitted. By default, the driver sets the NETIF_F_SG bit in the features field of the -net_device structure enabling the scatter-gather feature. This is true on +net_device structure, enabling the scatter-gather feature. This is true on chips and configurations where the checksum can be done in hardware. -Once the controller has finished transmitting the packet, napi will be +Once the controller has finished transmitting the packet, timer will be scheduled to release the transmit resources. 4.2) Receive process When one or more packets are received, an interrupt happens. The interrupts -are not queued so the driver has to scan all the descriptors in the ring during +are not queued, so the driver has to scan all the descriptors in the ring during the receive process. -This is based on NAPI so the interrupt handler signals only if there is work +This is based on NAPI, so the interrupt handler signals only if there is work to be done, and it exits. Then the poll method will be scheduled at some future point. The incoming packets are stored, by the DMA, in a list of pre-allocated socket buffers in order to avoid the memcpy (zero-copy). -4.3) Interrupt Mitigation +4.3) Interrupt mitigation The driver is able to mitigate the number of its DMA interrupts using NAPI for the reception on chips older than the 3.50. New chips have an HW RX-Watchdog used for this mitigation. @@ -88,19 +86,20 @@ the list, hence creating the explicit chaining in the descriptor itself, whereas such explicit chaining is not possible in RING mode. 4.5.1) Extended descriptors - The extended descriptors give us information about the Ethernet payload - when it is carrying PTP packets or TCP/UDP/ICMP over IP. - These are not available on GMAC Synopsys chips older than the 3.50. - At probe time the driver will decide if these can be actually used. - This support also is mandatory for PTPv2 because the extra descriptors - are used for saving the hardware timestamps and Extended Status. +The extended descriptors give us information about the Ethernet payload +when it is carrying PTP packets or TCP/UDP/ICMP over IP. +These are not available on GMAC Synopsys chips older than the 3.50. +At probe time the driver will decide if these can be actually used. +This support also is mandatory for PTPv2 because the extra descriptors +are used for saving the hardware timestamps and Extended Status. 4.6) Ethtool support Ethtool is supported. For example, driver statistics (including RMON), internal errors can be taken using: - # ethtool -S ethX command + # ethtool -S ethX +command 4.7) Jumbo and Segmentation Offloading Jumbo frames are supported and tested for the GMAC. @@ -275,11 +274,11 @@ Please see the following document: Documentation/devicetree/bindings/net/stmmac.txt 4.11) This is a summary of the content of some relevant files: - o stmmac_main.c: to implement the main network device driver; - o stmmac_mdio.c: to provide mdio functions; - o stmmac_pci: this the PCI driver; - o stmmac_platform.c: this the platform driver (OF supported) - o stmmac_ethtool.c: to implement the ethtool support; + o stmmac_main.c: implements the main network device driver; + o stmmac_mdio.c: provides MDIO functions; + o stmmac_pci: this is the PCI driver; + o stmmac_platform.c: this the platform driver (OF supported); + o stmmac_ethtool.c: implements the ethtool support; o stmmac.h: private driver structure; o common.h: common definitions and VFTs; o mmc_core.c/mmc.h: Management MAC Counters; @@ -381,12 +380,12 @@ In addition to the basic timestamp features mentioned in IEEE 1588-2002 Timestamps, new GMAC cores support the advanced timestamp features. IEEE 1588-2008 that can be enabled when configure the Kernel. -8) SGMII/RGMII supports +8) SGMII/RGMII support New GMAC devices provide own way to manage RGMII/SGMII. This information is available at run-time by looking at the HW capability register. This means that the stmmac can manage -auto-negotiation and link status w/o using the PHYLIB stuff +auto-negotiation and link status w/o using the PHYLIB stuff. In fact, the HW provides a subset of extended registers to restart the ANE, verify Full/Half duplex mode and Speed. -Also thanks to these registers it is possible to look at the +Thanks to these registers, it is possible to look at the Auto-negotiated Link Parter Ability. -- cgit v1.1 From 7b2024fdec0559050ca001c0128f90eec4236468 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 28 Nov 2016 15:19:43 +0100 Subject: irda: w83977af_ir: fix damaged whitespace As David Miller pointed out for for the previous patch, the whitespace in some functions looks rather odd. This was caused by commit 6329da5f258a ("obsolete config in kernel source: USE_INTERNAL_TIMER"), which removed some conditions but did not reindent the code. This fixes the indentation in the file and removes extraneous whitespace at the end of the lines and before tabs. There are many other minor coding style problems in the driver, but I'm not touching those here. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 404 ++++++++++++++++++++--------------------- 1 file changed, 202 insertions(+), 202 deletions(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index e8c3a8c..9674588 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -1,5 +1,5 @@ /********************************************************************* - * + * * Filename: w83977af_ir.c * Version: 1.0 * Description: FIR driver for the Winbond W83977AF Super I/O chip @@ -8,31 +8,31 @@ * Created at: Wed Nov 4 11:46:16 1998 * Modified at: Fri Jan 28 12:10:59 2000 * Modified by: Dag Brattli - * + * * Copyright (c) 1998-2000 Dag Brattli * Copyright (c) 1998-1999 Rebel.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. - * + * * Neither Paul VanderSpek nor Rebel.com admit liability nor provide * warranty for any of this software. This material is provided "AS-IS" * and at no charge. - * + * * If you find bugs in this file, its very likely that the same bug * will also be in pc87108.c since the implementations are quite * similar. * * Notice that all functions that needs to access the chip in _any_ - * way, must save BSR register on entry, and restore it on exit. + * way, must save BSR register on entry, and restore it on exit. * It is _very_ important to follow this policy! * * __u8 bank; - * + * * bank = inb( iobase+BSR); - * + * * do_your_stuff_here(); * * outb( bank, iobase+BSR); @@ -63,7 +63,7 @@ #include "w83977af_ir.h" #define CONFIG_USE_W977_PNP /* Currently needed */ -#define PIO_MAX_SPEED 115200 +#define PIO_MAX_SPEED 115200 static char *driver_name = "w83977af_ir"; static int qos_mtt_bits = 0x07; /* 1 ms or more */ @@ -83,11 +83,11 @@ static unsigned int efio = W977_EFIO_BASE; static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL}; /* Some prototypes */ -static int w83977af_open(int i, unsigned int iobase, unsigned int irq, +static int w83977af_open(int i, unsigned int iobase, unsigned int irq, unsigned int dma); static int w83977af_close(struct w83977af_ir *self); static int w83977af_probe(int iobase, int irq, int dma); -static int w83977af_dma_receive(struct w83977af_ir *self); +static int w83977af_dma_receive(struct w83977af_ir *self); static int w83977af_dma_receive_complete(struct w83977af_ir *self); static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev); @@ -108,7 +108,7 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd */ static int __init w83977af_init(void) { - int i; + int i; for (i=0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) { if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) @@ -150,7 +150,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, unsigned int dma) { struct net_device *dev; - struct w83977af_ir *self; + struct w83977af_ir *self; int err; /* Lock the port that we need */ @@ -177,18 +177,18 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, self = netdev_priv(dev); spin_lock_init(&self->lock); - + /* Initialize IO */ - self->io.fir_base = iobase; - self->io.irq = irq; - self->io.fir_ext = CHIP_IO_EXTENT; - self->io.dma = dma; - self->io.fifo_size = 32; + self->io.fir_base = iobase; + self->io.irq = irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = dma; + self->io.fifo_size = 32; /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); - + /* The only value we must override it the baudrate */ /* FIXME: The HP HDLS-1100 does not support 1152000! */ @@ -198,11 +198,11 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, /* The HP HDLS-1100 needs 1 ms according to the specs */ self->qos.min_turn_time.bits = qos_mtt_bits; irda_qos_bits_to_value(&self->qos); - + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ - self->rx_buff.truesize = 14384; + self->rx_buff.truesize = 14384; self->tx_buff.truesize = 4000; - + /* Allocate memory if needed */ self->rx_buff.head = dma_zalloc_coherent(NULL, self->rx_buff.truesize, @@ -238,12 +238,12 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, /* Need to store self somewhere */ dev_self[i] = self; - + return 0; err_out3: dma_free_coherent(NULL, self->tx_buff.truesize, self->tx_buff.head, self->tx_buff_dma); -err_out2: +err_out2: dma_free_coherent(NULL, self->rx_buff.truesize, self->rx_buff.head, self->rx_buff_dma); err_out1: @@ -288,7 +288,7 @@ static int w83977af_close(struct w83977af_ir *self) if (self->tx_buff.head) dma_free_coherent(NULL, self->tx_buff.truesize, self->tx_buff.head, self->tx_buff_dma); - + if (self->rx_buff.head) dma_free_coherent(NULL, self->rx_buff.truesize, self->rx_buff.head, self->rx_buff_dma); @@ -300,106 +300,106 @@ static int w83977af_close(struct w83977af_ir *self) static int w83977af_probe(int iobase, int irq, int dma) { - int version; + int version; int i; - - for (i=0; i < 2; i++) { + + for (i=0; i < 2; i++) { #ifdef CONFIG_USE_W977_PNP - /* Enter PnP configuration mode */ + /* Enter PnP configuration mode */ w977_efm_enter(efbase[i]); - - w977_select_device(W977_DEVICE_IR, efbase[i]); - - /* Configure PnP port, IRQ, and DMA channel */ - w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]); - w977_write_reg(0x61, (iobase) & 0xff, efbase[i]); - - w977_write_reg(0x70, irq, efbase[i]); + + w977_select_device(W977_DEVICE_IR, efbase[i]); + + /* Configure PnP port, IRQ, and DMA channel */ + w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]); + w977_write_reg(0x61, (iobase) & 0xff, efbase[i]); + + w977_write_reg(0x70, irq, efbase[i]); #ifdef CONFIG_ARCH_NETWINDER /* Netwinder uses 1 higher than Linux */ - w977_write_reg(0x74, dma+1, efbase[i]); + w977_write_reg(0x74, dma+1, efbase[i]); #else - w977_write_reg(0x74, dma, efbase[i]); + w977_write_reg(0x74, dma, efbase[i]); #endif /* CONFIG_ARCH_NETWINDER */ - w977_write_reg(0x75, 0x04, efbase[i]); /* Disable Tx DMA */ - - /* Set append hardware CRC, enable IR bank selection */ - w977_write_reg(0xf0, APEDCRC|ENBNKSEL, efbase[i]); - - /* Activate device */ - w977_write_reg(0x30, 0x01, efbase[i]); - - w977_efm_exit(efbase[i]); + w977_write_reg(0x75, 0x04, efbase[i]);/* Disable Tx DMA */ + + /* Set append hardware CRC, enable IR bank selection */ + w977_write_reg(0xf0, APEDCRC | ENBNKSEL, efbase[i]); + + /* Activate device */ + w977_write_reg(0x30, 0x01, efbase[i]); + + w977_efm_exit(efbase[i]); #endif /* CONFIG_USE_W977_PNP */ - /* Disable Advanced mode */ - switch_bank(iobase, SET2); - outb(iobase+2, 0x00); - - /* Turn on UART (global) interrupts */ - switch_bank(iobase, SET0); - outb(HCR_EN_IRQ, iobase+HCR); - - /* Switch to advanced mode */ - switch_bank(iobase, SET2); - outb(inb(iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1); - - /* Set default IR-mode */ - switch_bank(iobase, SET0); - outb(HCR_SIR, iobase+HCR); - - /* Read the Advanced IR ID */ - switch_bank(iobase, SET3); - version = inb(iobase+AUID); - - /* Should be 0x1? */ - if (0x10 == (version & 0xf0)) { - efio = efbase[i]; - - /* Set FIFO size to 32 */ - switch_bank(iobase, SET2); - outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); - - /* Set FIFO threshold to TX17, RX16 */ - switch_bank(iobase, SET0); - outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST| + /* Disable Advanced mode */ + switch_bank(iobase, SET2); + outb(iobase+2, 0x00); + + /* Turn on UART (global) interrupts */ + switch_bank(iobase, SET0); + outb(HCR_EN_IRQ, iobase+HCR); + + /* Switch to advanced mode */ + switch_bank(iobase, SET2); + outb(inb(iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1); + + /* Set default IR-mode */ + switch_bank(iobase, SET0); + outb(HCR_SIR, iobase+HCR); + + /* Read the Advanced IR ID */ + switch_bank(iobase, SET3); + version = inb(iobase+AUID); + + /* Should be 0x1? */ + if (0x10 == (version & 0xf0)) { + efio = efbase[i]; + + /* Set FIFO size to 32 */ + switch_bank(iobase, SET2); + outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + + /* Set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, SET0); + outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST| UFR_EN_FIFO,iobase+UFR); - - /* Receiver frame length */ - switch_bank(iobase, SET4); + + /* Receiver frame length */ + switch_bank(iobase, SET4); outb(2048 & 0xff, iobase+6); outb((2048 >> 8) & 0x1f, iobase+7); - /* - * Init HP HSDL-1100 transceiver. - * - * Set IRX_MSL since we have 2 * receive paths IRRX, - * and IRRXH. Clear IRSL0D since we want IRSL0 * to - * be a input pin used for IRRXH + /* + * Init HP HSDL-1100 transceiver. * - * IRRX pin 37 connected to receiver + * Set IRX_MSL since we have 2 * receive paths IRRX, + * and IRRXH. Clear IRSL0D since we want IRSL0 * to + * be a input pin used for IRRXH + * + * IRRX pin 37 connected to receiver * IRTX pin 38 connected to transmitter - * FIRRX pin 39 connected to receiver (IRSL0) + * FIRRX pin 39 connected to receiver (IRSL0) * CIRRX pin 40 connected to pin 37 */ switch_bank(iobase, SET7); outb(0x40, iobase+7); - + net_info_ratelimited("W83977AF (IR) driver loaded. Version: 0x%02x\n", version); - + return 0; } else { /* Try next extented function register address */ pr_debug("%s(), Wrong chip version", __func__); } - } + } return -1; } static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) { int ir_mode = HCR_SIR; - int iobase; + int iobase; __u8 set; iobase = self->io.fir_base; @@ -448,8 +448,8 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) /* set FIFO size to 32 */ switch_bank(iobase, SET2); - outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); - + outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + /* set FIFO threshold to TX17, RX16 */ switch_bank(iobase, SET0); outb(0x00, iobase+UFR); /* Reset */ @@ -457,7 +457,7 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) outb(0xa7, iobase+UFR); netif_wake_queue(self->netdev); - + /* Enable some interrupts so we can receive frames */ switch_bank(iobase, SET0); if (speed > PIO_MAX_SPEED) { @@ -465,7 +465,7 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) w83977af_dma_receive(self); } else outb(ICR_ERBRI, iobase+ICR); - + /* Restore SSR */ outb(set, iobase+SSR); } @@ -484,23 +484,23 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, int iobase; __u8 set; int mtt; - + self = netdev_priv(dev); iobase = self->io.fir_base; pr_debug("%s(%ld), skb->len=%d\n", __func__ , jiffies, (int)skb->len); - + /* Lock transmit buffer */ netif_stop_queue(dev); - + /* Check if we need to change the speed */ speed = irda_get_next_speed(skb); if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { - w83977af_change_speed(self, speed); + w83977af_change_speed(self, speed); dev_kfree_skb(skb); return NETDEV_TX_OK; } else @@ -509,29 +509,29 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, /* Save current set */ set = inb(iobase+SSR); - + /* Decide if we should use PIO or DMA transfer */ if (self->io.speed > PIO_MAX_SPEED) { self->tx_buff.data = self->tx_buff.head; skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len); self->tx_buff.len = skb->len; - + mtt = irda_get_mtt(skb); pr_debug("%s(%ld), mtt=%d\n", __func__ , jiffies, mtt); - if (mtt > 1000) - mdelay(mtt/1000); - else if (mtt) - udelay(mtt); + if (mtt > 1000) + mdelay(mtt/1000); + else if (mtt) + udelay(mtt); - /* Enable DMA interrupt */ - switch_bank(iobase, SET0); - outb(ICR_EDMAI, iobase+ICR); - w83977af_dma_write(self, iobase); + /* Enable DMA interrupt */ + switch_bank(iobase, SET0); + outb(ICR_EDMAI, iobase+ICR); + w83977af_dma_write(self, iobase); } else { self->tx_buff.data = self->tx_buff.head; - self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - + /* Add interrupt on tx low level (will fire immediately) */ switch_bank(iobase, SET0); outb(ICR_ETXTHI, iobase+ICR); @@ -562,15 +562,15 @@ static void w83977af_dma_write(struct w83977af_ir *self, int iobase) switch_bank(iobase, SET0); outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); - /* Choose transmit DMA channel */ + /* Choose transmit DMA channel */ switch_bank(iobase, SET2); outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1); irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, - DMA_MODE_WRITE); + DMA_MODE_WRITE); self->io.direction = IO_XMIT; - + /* Enable DMA */ - switch_bank(iobase, SET0); + switch_bank(iobase, SET0); outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR); /* Restore set register */ @@ -580,14 +580,14 @@ static void w83977af_dma_write(struct w83977af_ir *self, int iobase) /* * Function w83977af_pio_write (iobase, buf, len, fifo_size) * - * + * * */ static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) { int actual = 0; __u8 set; - + /* Save current bank */ set = inb(iobase+SSR); @@ -605,7 +605,7 @@ static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) /* Transmit next byte */ outb(buf[actual++], iobase+TBR); } - + pr_debug("%s(), fifo_size %d ; %d sent of %d\n", __func__ , fifo_size, actual, len); @@ -620,7 +620,7 @@ static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) * * The transfer of a frame in finished. So do the necessary things * - * + * */ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) { @@ -639,11 +639,11 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) /* Disable DMA */ switch_bank(iobase, SET0); outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); - + /* Check for underrun! */ if (inb(iobase+AUDR) & AUDR_UNDR) { pr_debug("%s(), Transmit underrun!\n", __func__); - + self->netdev->stats.tx_errors++; self->netdev->stats.tx_fifo_errors++; @@ -652,7 +652,7 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) } else self->netdev->stats.tx_packets++; - + if (self->new_speed) { w83977af_change_speed(self, self->new_speed); self->new_speed = 0; @@ -661,7 +661,7 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) /* Unlock tx_buff and request another frame */ /* Tell the network layer, that we want more frames */ netif_wake_queue(self->netdev); - + /* Restore set */ outb(set, iobase+SSR); } @@ -714,15 +714,15 @@ static int w83977af_dma_receive(struct w83977af_ir *self) irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, DMA_MODE_READ); #endif - /* - * Reset Rx FIFO. This will also flush the ST_FIFO, it's very + /* + * Reset Rx FIFO. This will also flush the ST_FIFO, it's very * important that we don't reset the Tx FIFO since it might not * be finished transmitting yet */ switch_bank(iobase, SET0); outb(UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR); self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; - + /* Enable DMA */ switch_bank(iobase, SET0); #ifdef CONFIG_ARCH_NETWINDER @@ -730,7 +730,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self) outb(hcr | HCR_EN_DMA, iobase+HCR); enable_dma(self->io.dma); spin_unlock_irqrestore(&self->lock, flags); -#else +#else outb(inb(iobase+HCR) | HCR_EN_DMA, iobase+HCR); #endif /* Restore set */ @@ -762,21 +762,21 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) /* Save current set */ set = inb(iobase+SSR); - + iobase = self->io.fir_base; /* Read status FIFO */ switch_bank(iobase, SET5); while ((status = inb(iobase+FS_FO)) & FS_FO_FSFDR) { st_fifo->entries[st_fifo->tail].status = status; - + st_fifo->entries[st_fifo->tail].len = inb(iobase+RFLFL); st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8; - + st_fifo->tail++; st_fifo->len++; } - + while (st_fifo->len) { /* Get first entry */ status = st_fifo->entries[st_fifo->head].status; @@ -792,32 +792,32 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) } else { /* Skip frame */ self->netdev->stats.rx_errors++; - + self->rx_buff.data += len; - + if (status & FS_FO_MX_LEX) self->netdev->stats.rx_length_errors++; - - if (status & FS_FO_PHY_ERR) + + if (status & FS_FO_PHY_ERR) self->netdev->stats.rx_frame_errors++; - - if (status & FS_FO_CRC_ERR) + + if (status & FS_FO_CRC_ERR) self->netdev->stats.rx_crc_errors++; } /* The errors below can be reported in both cases */ if (status & FS_FO_RX_OV) self->netdev->stats.rx_fifo_errors++; - + if (status & FS_FO_FSF_OV) self->netdev->stats.rx_fifo_errors++; - + } else { /* Check if we have transferred all data to memory */ switch_bank(iobase, SET0); if (inb(iobase+USR) & USR_RDR) { udelay(80); /* Should be enough!? */ } - + skb = dev_alloc_skb(len+1); if (skb == NULL) { printk(KERN_INFO @@ -827,10 +827,10 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) return FALSE; } - + /* Align to 20 bytes */ - skb_reserve(skb, 1); - + skb_reserve(skb, 1); + /* Copy frame without CRC */ if (self->io.speed < 4000000) { skb_put(skb, len-2); @@ -847,7 +847,7 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) /* Move to next frame */ self->rx_buff.data += len; self->netdev->stats.rx_packets++; - + skb->dev = self->netdev; skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); @@ -866,21 +866,21 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) * Receive all data in receiver FIFO * */ -static void w83977af_pio_receive(struct w83977af_ir *self) +static void w83977af_pio_receive(struct w83977af_ir *self) { __u8 byte = 0x00; int iobase; IRDA_ASSERT(self != NULL, return;); - + iobase = self->io.fir_base; - + /* Receive all characters in Rx FIFO */ do { byte = inb(iobase+RBR); async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, byte); - } while (inb(iobase+USR) & USR_RDR); /* Data available */ + } while (inb(iobase+USR) & USR_RDR); /* Data available */ } /* @@ -897,19 +897,19 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) int iobase; pr_debug("%s(), isr=%#x\n", __func__ , isr); - + iobase = self->io.fir_base; /* Transmit FIFO low on data */ if (isr & ISR_TXTH_I) { /* Write data left in transmit buffer */ - actual = w83977af_pio_write(self->io.fir_base, - self->tx_buff.data, - self->tx_buff.len, + actual = w83977af_pio_write(self->io.fir_base, + self->tx_buff.data, + self->tx_buff.len, self->io.fifo_size); self->tx_buff.data += actual; self->tx_buff.len -= actual; - + self->io.direction = IO_XMIT; /* Check if finished */ @@ -919,7 +919,7 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) set = inb(iobase+SSR); switch_bank(iobase, SET0); outb(AUDR_SFEND, iobase+AUDR); - outb(set, iobase+SSR); + outb(set, iobase+SSR); self->netdev->stats.tx_packets++; @@ -929,7 +929,7 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) } } /* Check if transmission has completed */ - if (isr & ISR_TXEMP_I) { + if (isr & ISR_TXEMP_I) { /* Check if we need to change the speed? */ if (self->new_speed) { pr_debug("%s(), Changing speed!\n", __func__); @@ -966,11 +966,11 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) iobase = self->io.fir_base; set = inb(iobase+SSR); - + /* End of frame detected in FIFO */ if (isr & (ISR_FEND_I|ISR_FSF_I)) { if (w83977af_dma_receive_complete(self)) { - + /* Wait for next status FIFO interrupt */ new_icr |= ICR_EFSFI; } else { @@ -995,7 +995,7 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) /* Clear timer event */ /* switch_bank(iobase, SET0); */ -/* outb(ASCR_CTE, iobase+ASCR); */ +/* outb(ASCR_CTE, iobase+ASCR); */ /* Check if this is a TX timer interrupt */ if (self->io.direction == IO_XMIT) { @@ -1008,23 +1008,23 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) new_icr |= ICR_EFSFI; } - } + } /* Finished with DMA */ if (isr & ISR_DMA_I) { w83977af_dma_xmit_complete(self); /* Check if there are more frames to be transmitted */ /* if (irda_device_txqueue_empty(self)) { */ - - /* Prepare for receive - * + + /* Prepare for receive + * * ** Netwinder Tx DMA likes that we do this anyway ** */ w83977af_dma_receive(self); new_icr = ICR_EFSFI; - /* } */ + /* } */ } - + /* Restore set */ outb(set, iobase+SSR); @@ -1051,12 +1051,12 @@ static irqreturn_t w83977af_interrupt(int irq, void *dev_id) /* Save current bank */ set = inb(iobase+SSR); switch_bank(iobase, SET0); - - icr = inb(iobase+ICR); - isr = inb(iobase+ISR) & icr; /* Mask out the interesting ones */ + + icr = inb(iobase+ICR); + isr = inb(iobase+ISR) & icr; /* Mask out the interesting ones */ outb(0, iobase+ICR); /* Disable interrupts */ - + if (isr) { /* Dispatch interrupt handler for the current speed */ if (self->io.speed > PIO_MAX_SPEED ) @@ -1095,9 +1095,9 @@ static int w83977af_is_receiving(struct w83977af_ir *self) status = TRUE; } outb(set, iobase+SSR); - } else + } else status = (self->rx_buff.state != OUTSIDE_FRAME); - + return status; } @@ -1113,16 +1113,16 @@ static int w83977af_net_open(struct net_device *dev) int iobase; char hwname[32]; __u8 set; - - + + IRDA_ASSERT(dev != NULL, return -1;); self = netdev_priv(dev); - + IRDA_ASSERT(self != NULL, return 0;); - + iobase = self->io.fir_base; - if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, + if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, (void *) dev)) { return -EAGAIN; } @@ -1134,30 +1134,30 @@ static int w83977af_net_open(struct net_device *dev) free_irq(self->io.irq, dev); return -EAGAIN; } - + /* Save current set */ set = inb(iobase+SSR); - /* Enable some interrupts so we can receive frames again */ - switch_bank(iobase, SET0); - if (self->io.speed > 115200) { - outb(ICR_EFSFI, iobase+ICR); - w83977af_dma_receive(self); - } else - outb(ICR_ERBRI, iobase+ICR); + /* Enable some interrupts so we can receive frames again */ + switch_bank(iobase, SET0); + if (self->io.speed > 115200) { + outb(ICR_EFSFI, iobase+ICR); + w83977af_dma_receive(self); + } else + outb(ICR_ERBRI, iobase+ICR); /* Restore bank register */ outb(set, iobase+SSR); /* Ready to play! */ netif_start_queue(dev); - + /* Give self a hardware name */ sprintf(hwname, "w83977af @ 0x%03x", self->io.fir_base); - /* + /* * Open new IrLAP layer instance, now that everything should be - * initialized properly + * initialized properly */ self->irlap = irlap_open(dev, &self->qos, hwname); @@ -1177,16 +1177,16 @@ static int w83977af_net_close(struct net_device *dev) __u8 set; IRDA_ASSERT(dev != NULL, return -1;); - + self = netdev_priv(dev); - + IRDA_ASSERT(self != NULL, return 0;); - + iobase = self->io.fir_base; /* Stop device */ netif_stop_queue(dev); - + /* Stop and remove instance of IrLAP */ if (self->irlap) irlap_close(self->irlap); @@ -1196,10 +1196,10 @@ static int w83977af_net_close(struct net_device *dev) /* Save current set */ set = inb(iobase+SSR); - + /* Disable interrupts */ switch_bank(iobase, SET0); - outb(0, iobase+ICR); + outb(0, iobase+ICR); free_irq(self->io.irq, dev); free_dma(self->io.dma); @@ -1230,7 +1230,7 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) IRDA_ASSERT(self != NULL, return -1;); pr_debug("%s(), %s, (cmd=0x%X)\n", __func__ , dev->name, cmd); - + spin_lock_irqsave(&self->lock, flags); switch (cmd) { @@ -1274,7 +1274,7 @@ MODULE_PARM_DESC(irq, "IRQ lines"); /* * Function init_module (void) * - * + * * */ module_init(w83977af_init); @@ -1282,7 +1282,7 @@ module_init(w83977af_init); /* * Function cleanup_module (void) * - * + * * */ module_exit(w83977af_cleanup); -- cgit v1.1 From 01f1f615bdc5a887d39e158129e0851956fb6e05 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Thu, 1 Dec 2016 16:19:40 +0100 Subject: net: stmmac: avoid Camelcase naming This patch simply rename regValue to value, like it was named in other mdio functions. Signed-off-by: Corentin Labbe Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index e3216e5..6796c28 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -83,14 +83,14 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) unsigned int mii_data = priv->hw->mii.data; int data; - u16 regValue = (((phyaddr << 11) & (0x0000F800)) | + u16 value = (((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0))); - regValue |= MII_BUSY | ((priv->clk_csr & 0xF) << 2); + value |= MII_BUSY | ((priv->clk_csr & 0xF) << 2); if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) return -EBUSY; - writel(regValue, priv->ioaddr + mii_address); + writel(value, priv->ioaddr + mii_address); if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) return -EBUSY; -- cgit v1.1 From b91dce4c5bc9b4454e25695c0dae5355df79a4e9 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Thu, 1 Dec 2016 16:19:41 +0100 Subject: net: stmmac: unify mdio functions stmmac_mdio_{read|write} and stmmac_mdio_{read|write}_gmac4 are not enought different for being split. The only differences between thoses two functions are shift/mask for addr/reg/clk_csr. This patch introduce a per platform set of variable for setting thoses shift/mask and unify mdio read and write functions. Signed-off-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 6 + .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 6 + .../net/ethernet/stmicro/stmmac/dwmac100_core.c | 7 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 6 + drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 121 ++++----------------- 5 files changed, 48 insertions(+), 98 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 5bd4c05..3ced2e1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -507,6 +507,12 @@ struct mac_link { struct mii_regs { unsigned int addr; /* MII Address */ unsigned int data; /* MII Data */ + unsigned int addr_shift; /* MII address shift */ + unsigned int reg_shift; /* MII reg shift */ + unsigned int addr_mask; /* MII address mask */ + unsigned int reg_mask; /* MII reg mask */ + unsigned int clk_csr_shift; + unsigned int clk_csr_mask; }; /* Helpers to manage the descriptors for chain and ring modes */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 7df4ff1..b21d03f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -534,6 +534,12 @@ struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, mac->link.speed = GMAC_CONTROL_FES; mac->mii.addr = GMAC_MII_ADDR; mac->mii.data = GMAC_MII_DATA; + mac->mii.addr_shift = 11; + mac->mii.addr_mask = 0x0000F800; + mac->mii.reg_shift = 6; + mac->mii.reg_mask = 0x000007C0; + mac->mii.clk_csr_shift = 2; + mac->mii.clk_csr_mask = 0xF; /* Get and dump the chip ID */ *synopsys_id = stmmac_get_synopsys_id(hwid); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 6418b2e..a1d582f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -192,6 +192,13 @@ struct mac_device_info *dwmac100_setup(void __iomem *ioaddr, int *synopsys_id) mac->link.speed = 0; mac->mii.addr = MAC_MII_ADDR; mac->mii.data = MAC_MII_DATA; + mac->mii.addr_shift = 11; + mac->mii.addr_mask = 0x0000F800; + mac->mii.reg_shift = 6; + mac->mii.reg_mask = 0x000007C0; + mac->mii.clk_csr_shift = 2; + mac->mii.clk_csr_mask = 0xF; + /* Synopsys Id is not available on old chips */ *synopsys_id = 0; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 51019b7..eaed7cb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -430,6 +430,12 @@ struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins, mac->link.speed = GMAC_CONFIG_FES; mac->mii.addr = GMAC_MDIO_ADDR; mac->mii.data = GMAC_MDIO_DATA; + mac->mii.addr_shift = 21; + mac->mii.addr_mask = GENMASK(25, 21); + mac->mii.reg_shift = 16; + mac->mii.reg_mask = GENMASK(20, 16); + mac->mii.clk_csr_shift = 8; + mac->mii.clk_csr_mask = GENMASK(11, 8); /* Get and dump the chip ID */ *synopsys_id = stmmac_get_synopsys_id(hwid); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 6796c28..23322fd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -42,13 +42,6 @@ #define MII_GMAC4_WRITE (1 << MII_GMAC4_GOC_SHIFT) #define MII_GMAC4_READ (3 << MII_GMAC4_GOC_SHIFT) -#define MII_PHY_ADDR_GMAC4_SHIFT 21 -#define MII_PHY_ADDR_GMAC4_MASK GENMASK(25, 21) -#define MII_PHY_REG_GMAC4_SHIFT 16 -#define MII_PHY_REG_GMAC4_MASK GENMASK(20, 16) -#define MII_CSR_CLK_GMAC4_SHIFT 8 -#define MII_CSR_CLK_GMAC4_MASK GENMASK(11, 8) - static int stmmac_mdio_busy_wait(void __iomem *ioaddr, unsigned int mii_addr) { unsigned long curr; @@ -68,8 +61,8 @@ static int stmmac_mdio_busy_wait(void __iomem *ioaddr, unsigned int mii_addr) /** * stmmac_mdio_read * @bus: points to the mii_bus structure - * @phyaddr: MII addr reg bits 15-11 - * @phyreg: MII addr reg bits 10-6 + * @phyaddr: MII addr + * @phyreg: MII reg * Description: it reads data from the MII register from within the phy device. * For the 7111 GMAC, we must set the bit 0 in the MII address register while * accessing the PHY registers. @@ -83,9 +76,15 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) unsigned int mii_data = priv->hw->mii.data; int data; - u16 value = (((phyaddr << 11) & (0x0000F800)) | - ((phyreg << 6) & (0x000007C0))); - value |= MII_BUSY | ((priv->clk_csr & 0xF) << 2); + u32 value = MII_BUSY; + + value |= (phyaddr << priv->hw->mii.addr_shift) + & priv->hw->mii.addr_mask; + value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; + value |= (priv->clk_csr & priv->hw->mii.clk_csr_mask) + << priv->hw->mii.clk_csr_shift; + if (priv->plat->has_gmac4) + value |= MII_GMAC4_READ; if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) return -EBUSY; @@ -104,8 +103,8 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) /** * stmmac_mdio_write * @bus: points to the mii_bus structure - * @phyaddr: MII addr reg bits 15-11 - * @phyreg: MII addr reg bits 10-6 + * @phyaddr: MII addr + * @phyreg: MII reg * @phydata: phy data * Description: it writes the data into the MII register from within the device. */ @@ -117,85 +116,16 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, unsigned int mii_address = priv->hw->mii.addr; unsigned int mii_data = priv->hw->mii.data; - u16 value = - (((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0))) - | MII_WRITE; + u32 value = MII_WRITE | MII_BUSY; - value |= MII_BUSY | ((priv->clk_csr & 0xF) << 2); + value |= (phyaddr << priv->hw->mii.addr_shift) + & priv->hw->mii.addr_mask; + value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; - /* Wait until any existing MII operation is complete */ - if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) - return -EBUSY; - - /* Set the MII address register to write */ - writel(phydata, priv->ioaddr + mii_data); - writel(value, priv->ioaddr + mii_address); - - /* Wait until any existing MII operation is complete */ - return stmmac_mdio_busy_wait(priv->ioaddr, mii_address); -} - -/** - * stmmac_mdio_read_gmac4 - * @bus: points to the mii_bus structure - * @phyaddr: MII addr reg bits 25-21 - * @phyreg: MII addr reg bits 20-16 - * Description: it reads data from the MII register of GMAC4 from within - * the phy device. - */ -static int stmmac_mdio_read_gmac4(struct mii_bus *bus, int phyaddr, int phyreg) -{ - struct net_device *ndev = bus->priv; - struct stmmac_priv *priv = netdev_priv(ndev); - unsigned int mii_address = priv->hw->mii.addr; - unsigned int mii_data = priv->hw->mii.data; - int data; - u32 value = (((phyaddr << MII_PHY_ADDR_GMAC4_SHIFT) & - (MII_PHY_ADDR_GMAC4_MASK)) | - ((phyreg << MII_PHY_REG_GMAC4_SHIFT) & - (MII_PHY_REG_GMAC4_MASK))) | MII_GMAC4_READ; - - value |= MII_BUSY | ((priv->clk_csr & MII_CSR_CLK_GMAC4_MASK) - << MII_CSR_CLK_GMAC4_SHIFT); - - if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) - return -EBUSY; - - writel(value, priv->ioaddr + mii_address); - - if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) - return -EBUSY; - - /* Read the data from the MII data register */ - data = (int)readl(priv->ioaddr + mii_data); - - return data; -} - -/** - * stmmac_mdio_write_gmac4 - * @bus: points to the mii_bus structure - * @phyaddr: MII addr reg bits 25-21 - * @phyreg: MII addr reg bits 20-16 - * @phydata: phy data - * Description: it writes the data into the MII register of GMAC4 from within - * the device. - */ -static int stmmac_mdio_write_gmac4(struct mii_bus *bus, int phyaddr, int phyreg, - u16 phydata) -{ - struct net_device *ndev = bus->priv; - struct stmmac_priv *priv = netdev_priv(ndev); - unsigned int mii_address = priv->hw->mii.addr; - unsigned int mii_data = priv->hw->mii.data; - - u32 value = (((phyaddr << MII_PHY_ADDR_GMAC4_SHIFT) & - (MII_PHY_ADDR_GMAC4_MASK)) | - ((phyreg << MII_PHY_REG_GMAC4_SHIFT) & - (MII_PHY_REG_GMAC4_MASK))) | MII_GMAC4_WRITE; - - value |= MII_BUSY | ((priv->clk_csr & MII_CSR_CLK_GMAC4_MASK) - << MII_CSR_CLK_GMAC4_SHIFT); + value |= ((priv->clk_csr & priv->hw->mii.clk_csr_mask) + << priv->hw->mii.clk_csr_shift); + if (priv->plat->has_gmac4) + value |= MII_GMAC4_WRITE; /* Wait until any existing MII operation is complete */ if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) @@ -305,13 +235,8 @@ int stmmac_mdio_register(struct net_device *ndev) #endif new_bus->name = "stmmac"; - if (priv->plat->has_gmac4) { - new_bus->read = &stmmac_mdio_read_gmac4; - new_bus->write = &stmmac_mdio_write_gmac4; - } else { - new_bus->read = &stmmac_mdio_read; - new_bus->write = &stmmac_mdio_write; - } + new_bus->read = &stmmac_mdio_read; + new_bus->write = &stmmac_mdio_write; new_bus->reset = &stmmac_mdio_reset; snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", -- cgit v1.1 From 397c5ad153f0ea62023accb67b3d2b07e5039948 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 1 Dec 2016 15:25:28 -0500 Subject: net: ethernet: ti: davinci_cpdma: add missing EXPORTs As of commit 8f32b90981dcdb355516fb95953133f8d4e6b11d ("net: ethernet: ti: davinci_cpdma: add set rate for a channel") the ARM allmodconfig builds would fail modpost with: ERROR: "cpdma_chan_set_weight" [drivers/net/ethernet/ti/ti_cpsw.ko] undefined! ERROR: "cpdma_chan_get_rate" [drivers/net/ethernet/ti/ti_cpsw.ko] undefined! ERROR: "cpdma_chan_get_min_rate" [drivers/net/ethernet/ti/ti_cpsw.ko] undefined! ERROR: "cpdma_chan_set_rate" [drivers/net/ethernet/ti/ti_cpsw.ko] undefined! Since these weren't declared as static, it is assumed they were meant to be shared outside the file, and that modular build testing was simply overlooked. Fixes: 8f32b90981dc ("net: ethernet: ti: davinci_cpdma: add set rate for a channel") Cc: Ivan Khoronzhuk Cc: Mugunthan V N Cc: Grygorii Strashko Cc: linux-omap@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index c776e45..36518fc 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -796,6 +796,7 @@ int cpdma_chan_set_weight(struct cpdma_chan *ch, int weight) spin_unlock_irqrestore(&ctlr->lock, flags); return ret; } +EXPORT_SYMBOL_GPL(cpdma_chan_set_weight); /* cpdma_chan_get_min_rate - get minimum allowed rate for channel * Should be called before cpdma_chan_set_rate. @@ -810,6 +811,7 @@ u32 cpdma_chan_get_min_rate(struct cpdma_ctlr *ctlr) return DIV_ROUND_UP(divident, divisor); } +EXPORT_SYMBOL_GPL(cpdma_chan_get_min_rate); /* cpdma_chan_set_rate - limits bandwidth for transmit channel. * The bandwidth * limited channels have to be in order beginning from lowest. @@ -853,6 +855,7 @@ err: spin_unlock_irqrestore(&ctlr->lock, flags); return ret; } +EXPORT_SYMBOL_GPL(cpdma_chan_set_rate); u32 cpdma_chan_get_rate(struct cpdma_chan *ch) { @@ -865,6 +868,7 @@ u32 cpdma_chan_get_rate(struct cpdma_chan *ch) return rate; } +EXPORT_SYMBOL_GPL(cpdma_chan_get_rate); struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, cpdma_handler_fn handler, int rx_type) -- cgit v1.1 From 2c839337520b22225dfccf6546814c18d9108b4d Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:37 -0800 Subject: drivers: net: xgene: Add helper function This is a prepartion patch and adds xgene_enet_get_fpsel() helper function to get buffer pool number. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_cle.c | 4 ++-- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 19 +++++++------------ drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 8 ++++++++ drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c | 20 +++++++------------- drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 20 +++++++------------- 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c index 23d72af..7aac0fb 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c @@ -346,7 +346,7 @@ static int xgene_cle_set_rss_idt(struct xgene_enet_pdata *pdata) for (i = 0; i < XGENE_CLE_IDT_ENTRIES; i++) { idx = i % pdata->rxq_cnt; pool_id = pdata->rx_ring[idx]->buf_pool->id; - fpsel = xgene_enet_ring_bufnum(pool_id) - 0x20; + fpsel = xgene_enet_get_fpsel(pool_id); dstqid = xgene_enet_dst_ring_num(pdata->rx_ring[idx]); nfpsel = 0; idt_reg = 0; @@ -706,7 +706,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) def_qid = xgene_enet_dst_ring_num(pdata->rx_ring[0]); pool_id = pdata->rx_ring[0]->buf_pool->id; - def_fpsel = xgene_enet_ring_bufnum(pool_id) - 0x20; + def_fpsel = xgene_enet_get_fpsel(pool_id); memset(dbptr, 0, sizeof(struct xgene_cle_dbptr) * DB_MAX_PTRS); dbptr[DB_RES_ACCEPT].fpsel = def_fpsel; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 5390ae8..1007074 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -555,7 +555,7 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *pdata, u32 cb; u32 fpsel; - fpsel = xgene_enet_ring_bufnum(bufpool_id) - 0x20; + fpsel = xgene_enet_get_fpsel(bufpool_id); xgene_enet_rd_csr(pdata, CLE_BYPASS_REG0_0_ADDR, &cb); cb |= CFG_CLE_BYPASS_EN0; @@ -652,16 +652,14 @@ static int xgene_enet_reset(struct xgene_enet_pdata *pdata) static void xgene_enet_clear(struct xgene_enet_pdata *pdata, struct xgene_enet_desc_ring *ring) { - u32 addr, val, data; - - val = xgene_enet_ring_bufnum(ring->id); + u32 addr, data; if (xgene_enet_is_bufpool(ring->id)) { addr = ENET_CFGSSQMIFPRESET_ADDR; - data = BIT(val - 0x20); + data = BIT(xgene_enet_get_fpsel(ring->id)); } else { addr = ENET_CFGSSQMIWQRESET_ADDR; - data = BIT(val); + data = BIT(xgene_enet_ring_bufnum(ring->id)); } xgene_enet_wr_ring_if(pdata, addr, data); @@ -671,24 +669,21 @@ static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata) { struct device *dev = &pdata->pdev->dev; struct xgene_enet_desc_ring *ring; - u32 pb, val; + u32 pb; int i; pb = 0; for (i = 0; i < pdata->rxq_cnt; i++) { ring = pdata->rx_ring[i]->buf_pool; + pb |= BIT(xgene_enet_get_fpsel(ring->id)); - val = xgene_enet_ring_bufnum(ring->id); - pb |= BIT(val - 0x20); } xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb); pb = 0; for (i = 0; i < pdata->txq_cnt; i++) { ring = pdata->tx_ring[i]; - - val = xgene_enet_ring_bufnum(ring->id); - pb |= BIT(val); + pb |= BIT(xgene_enet_ring_bufnum(ring->id)); } xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index 06e598c..e73cbb1 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -346,6 +346,14 @@ static inline bool xgene_enet_is_bufpool(u16 id) return ((id & RING_BUFNUM_MASK) >= 0x20) ? true : false; } +static inline u8 xgene_enet_get_fpsel(u16 id) +{ + if (xgene_enet_is_bufpool(id)) + return xgene_enet_ring_bufnum(id) - RING_BUFNUM_BUFPOOL; + + return 0; +} + static inline u16 xgene_enet_get_numslots(u16 id, u32 size) { bool is_bufpool = xgene_enet_is_bufpool(id); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index d12e9cb..8e4209c 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -501,7 +501,7 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p, data = CFG_CLE_BYPASS_EN0; xgene_enet_wr_csr(p, cle_bypass_reg0 + offset, data); - fpsel = xgene_enet_ring_bufnum(bufpool_id) - 0x20; + fpsel = xgene_enet_get_fpsel(bufpool_id); data = CFG_CLE_DSTQID0(dst_ring_num) | CFG_CLE_FPSEL0(fpsel); xgene_enet_wr_csr(p, cle_bypass_reg1 + offset, data); } @@ -509,16 +509,14 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p, static void xgene_enet_clear(struct xgene_enet_pdata *pdata, struct xgene_enet_desc_ring *ring) { - u32 addr, val, data; - - val = xgene_enet_ring_bufnum(ring->id); + u32 addr, data; if (xgene_enet_is_bufpool(ring->id)) { addr = ENET_CFGSSQMIFPRESET_ADDR; - data = BIT(val - 0x20); + data = BIT(xgene_enet_get_fpsel(ring->id)); } else { addr = ENET_CFGSSQMIWQRESET_ADDR; - data = BIT(val); + data = BIT(xgene_enet_ring_bufnum(ring->id)); } xgene_enet_wr_ring_if(pdata, addr, data); @@ -528,24 +526,20 @@ static void xgene_enet_shutdown(struct xgene_enet_pdata *p) { struct device *dev = &p->pdev->dev; struct xgene_enet_desc_ring *ring; - u32 pb, val; + u32 pb; int i; pb = 0; for (i = 0; i < p->rxq_cnt; i++) { ring = p->rx_ring[i]->buf_pool; - - val = xgene_enet_ring_bufnum(ring->id); - pb |= BIT(val - 0x20); + pb |= BIT(xgene_enet_get_fpsel(ring->id)); } xgene_enet_wr_ring_if(p, ENET_CFGSSQMIFPRESET_ADDR, pb); pb = 0; for (i = 0; i < p->txq_cnt; i++) { ring = p->tx_ring[i]; - - val = xgene_enet_ring_bufnum(ring->id); - pb |= BIT(val); + pb |= BIT(xgene_enet_ring_bufnum(ring->id)); } xgene_enet_wr_ring_if(p, ENET_CFGSSQMIWQRESET_ADDR, pb); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index d1758b0..f97e599 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -359,7 +359,7 @@ static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata, CFG_CLE_IP_PROTOCOL0_SET(&cb, 3); xgene_enet_wr_csr(pdata, XCLE_BYPASS_REG0_ADDR, cb); - fpsel = xgene_enet_ring_bufnum(bufpool_id) - 0x20; + fpsel = xgene_enet_get_fpsel(bufpool_id); xgene_enet_rd_csr(pdata, XCLE_BYPASS_REG1_ADDR, &cb); CFG_CLE_DSTQID0_SET(&cb, dst_ring_num); CFG_CLE_FPSEL0_SET(&cb, fpsel); @@ -370,24 +370,20 @@ static void xgene_enet_shutdown(struct xgene_enet_pdata *pdata) { struct device *dev = &pdata->pdev->dev; struct xgene_enet_desc_ring *ring; - u32 pb, val; + u32 pb; int i; pb = 0; for (i = 0; i < pdata->rxq_cnt; i++) { ring = pdata->rx_ring[i]->buf_pool; - - val = xgene_enet_ring_bufnum(ring->id); - pb |= BIT(val - 0x20); + pb |= BIT(xgene_enet_get_fpsel(ring->id)); } xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb); pb = 0; for (i = 0; i < pdata->txq_cnt; i++) { ring = pdata->tx_ring[i]; - - val = xgene_enet_ring_bufnum(ring->id); - pb |= BIT(val); + pb |= BIT(xgene_enet_ring_bufnum(ring->id)); } xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb); @@ -400,16 +396,14 @@ static void xgene_enet_shutdown(struct xgene_enet_pdata *pdata) static void xgene_enet_clear(struct xgene_enet_pdata *pdata, struct xgene_enet_desc_ring *ring) { - u32 addr, val, data; - - val = xgene_enet_ring_bufnum(ring->id); + u32 addr, data; if (xgene_enet_is_bufpool(ring->id)) { addr = ENET_CFGSSQMIFPRESET_ADDR; - data = BIT(val - 0x20); + data = BIT(xgene_enet_get_fpsel(ring->id)); } else { addr = ENET_CFGSSQMIWQRESET_ADDR; - data = BIT(val); + data = BIT(xgene_enet_ring_bufnum(ring->id)); } xgene_enet_wr_ring_if(pdata, addr, data); -- cgit v1.1 From d6d489694fda7aff86c8fd572744d0dbbca3f64e Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:38 -0800 Subject: drivers: net: xgene: Configure classifier with pagepool This patch configures classifier with the pagepool information. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_cle.c | 16 ++++++++++++++-- drivers/net/ethernet/apm/xgene/xgene_enet_cle.h | 2 ++ drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 7 +++++-- drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 6 ++++-- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 11 +++++++++-- drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 3 ++- drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c | 9 ++++++--- drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 7 +++++-- 8 files changed, 47 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c index 7aac0fb..caa55bd 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c @@ -52,6 +52,7 @@ static void xgene_cle_dbptr_to_hw(struct xgene_enet_pdata *pdata, { buf[0] = SET_VAL(CLE_DROP, dbptr->drop); buf[4] = SET_VAL(CLE_FPSEL, dbptr->fpsel) | + SET_VAL(CLE_NFPSEL, dbptr->nxtfpsel) | SET_VAL(CLE_DSTQIDL, dbptr->dstqid); buf[5] = SET_VAL(CLE_DSTQIDH, (u32)dbptr->dstqid >> CLE_DSTQIDL_LEN) | @@ -349,8 +350,12 @@ static int xgene_cle_set_rss_idt(struct xgene_enet_pdata *pdata) fpsel = xgene_enet_get_fpsel(pool_id); dstqid = xgene_enet_dst_ring_num(pdata->rx_ring[idx]); nfpsel = 0; - idt_reg = 0; + if (pdata->rx_ring[idx]->page_pool) { + pool_id = pdata->rx_ring[idx]->page_pool->id; + nfpsel = xgene_enet_get_fpsel(pool_id); + } + idt_reg = 0; xgene_cle_idt_to_hw(pdata, dstqid, fpsel, nfpsel, &idt_reg); ret = xgene_cle_dram_wr(&pdata->cle, &idt_reg, 1, i, RSS_IDT, CLE_CMD_WR); @@ -400,9 +405,9 @@ static int xgene_cle_setup_rss(struct xgene_enet_pdata *pdata) static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) { struct xgene_enet_cle *enet_cle = &pdata->cle; + u32 def_qid, def_fpsel, def_nxtfpsel, pool_id; struct xgene_cle_dbptr dbptr[DB_MAX_PTRS]; struct xgene_cle_ptree_branch *br; - u32 def_qid, def_fpsel, pool_id; struct xgene_cle_ptree *ptree; struct xgene_cle_ptree_kn kn; int ret; @@ -707,13 +712,20 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) def_qid = xgene_enet_dst_ring_num(pdata->rx_ring[0]); pool_id = pdata->rx_ring[0]->buf_pool->id; def_fpsel = xgene_enet_get_fpsel(pool_id); + def_nxtfpsel = 0; + if (pdata->rx_ring[0]->page_pool) { + pool_id = pdata->rx_ring[0]->page_pool->id; + def_nxtfpsel = xgene_enet_get_fpsel(pool_id); + } memset(dbptr, 0, sizeof(struct xgene_cle_dbptr) * DB_MAX_PTRS); dbptr[DB_RES_ACCEPT].fpsel = def_fpsel; + dbptr[DB_RES_ACCEPT].nxtfpsel = def_nxtfpsel; dbptr[DB_RES_ACCEPT].dstqid = def_qid; dbptr[DB_RES_ACCEPT].cle_priority = 1; dbptr[DB_RES_DEF].fpsel = def_fpsel; + dbptr[DB_RES_DEF].nxtfpsel = def_nxtfpsel; dbptr[DB_RES_DEF].dstqid = def_qid; dbptr[DB_RES_DEF].cle_priority = 7; xgene_cle_setup_def_dbptr(pdata, enet_cle, &dbptr[DB_RES_DEF], diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h index 9ac9f8e..903be0c 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h @@ -91,6 +91,8 @@ #define CLE_DSTQIDH_LEN 5 #define CLE_FPSEL_POS 21 #define CLE_FPSEL_LEN 4 +#define CLE_NFPSEL_POS 17 +#define CLE_NFPSEL_LEN 4 #define CLE_PRIORITY_POS 5 #define CLE_PRIORITY_LEN 3 diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 1007074..c395df3 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -550,12 +550,14 @@ static void xgene_enet_config_ring_if_assoc(struct xgene_enet_pdata *pdata) } static void xgene_enet_cle_bypass(struct xgene_enet_pdata *pdata, - u32 dst_ring_num, u16 bufpool_id) + u32 dst_ring_num, u16 bufpool_id, + u16 nxtbufpool_id) { u32 cb; - u32 fpsel; + u32 fpsel, nxtfpsel; fpsel = xgene_enet_get_fpsel(bufpool_id); + nxtfpsel = xgene_enet_get_fpsel(nxtbufpool_id); xgene_enet_rd_csr(pdata, CLE_BYPASS_REG0_0_ADDR, &cb); cb |= CFG_CLE_BYPASS_EN0; @@ -565,6 +567,7 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *pdata, xgene_enet_rd_csr(pdata, CLE_BYPASS_REG1_0_ADDR, &cb); CFG_CLE_DSTQID0_SET(&cb, dst_ring_num); CFG_CLE_FPSEL0_SET(&cb, fpsel); + CFG_CLE_NXTFPSEL0_SET(&cb, nxtfpsel); xgene_enet_wr_csr(pdata, CLE_BYPASS_REG1_0_ADDR, cb); } diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index e73cbb1..bd6cb6c 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -165,10 +165,12 @@ enum xgene_enet_rm { #define CFG_CLE_IP_PROTOCOL0_SET(dst, val) xgene_set_bits(dst, val, 16, 2) #define CFG_CLE_DSTQID0_SET(dst, val) xgene_set_bits(dst, val, 0, 12) #define CFG_CLE_FPSEL0_SET(dst, val) xgene_set_bits(dst, val, 16, 4) +#define CFG_CLE_NXTFPSEL0_SET(dst, val) xgene_set_bits(dst, val, 20, 4) #define CFG_MACMODE_SET(dst, val) xgene_set_bits(dst, val, 18, 2) #define CFG_WAITASYNCRD_SET(dst, val) xgene_set_bits(dst, val, 0, 16) -#define CFG_CLE_DSTQID0(val) (val & GENMASK(11, 0)) -#define CFG_CLE_FPSEL0(val) ((val << 16) & GENMASK(19, 16)) +#define CFG_CLE_DSTQID0(val) ((val) & GENMASK(11, 0)) +#define CFG_CLE_FPSEL0(val) (((val) << 16) & GENMASK(19, 16)) +#define CFG_CLE_NXTFPSEL0(val) (((val) << 20) & GENMASK(23, 20)) #define ICM_CONFIG0_REG_0_ADDR 0x0400 #define ICM_CONFIG2_REG_0_ADDR 0x0410 #define RX_DV_GATE_REG_0_ADDR 0x05fc diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 1352b52..c89acf5 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1518,9 +1518,10 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) { struct xgene_enet_cle *enet_cle = &pdata->cle; + struct xgene_enet_desc_ring *page_pool; struct net_device *ndev = pdata->ndev; struct xgene_enet_desc_ring *buf_pool; - u16 dst_ring_num; + u16 dst_ring_num, ring_id; int i, ret; ret = pdata->port_ops->reset(pdata); @@ -1558,8 +1559,14 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) netdev_err(ndev, "Preclass Tree init error\n"); goto err; } + } else { - pdata->port_ops->cle_bypass(pdata, dst_ring_num, buf_pool->id); + dst_ring_num = xgene_enet_dst_ring_num(pdata->rx_ring[0]); + buf_pool = pdata->rx_ring[0]->buf_pool; + page_pool = pdata->rx_ring[0]->page_pool; + ring_id = (page_pool) ? page_pool->id : 0; + pdata->port_ops->cle_bypass(pdata, dst_ring_num, + buf_pool->id, ring_id); } pdata->phy_speed = SPEED_UNKNOWN; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 011965b..1fe3942 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -115,6 +115,7 @@ struct xgene_enet_desc_ring { enum xgene_enet_ring_cfgsize cfgsize; struct xgene_enet_desc_ring *cp_ring; struct xgene_enet_desc_ring *buf_pool; + struct xgene_enet_desc_ring *page_pool; struct napi_struct napi; union { void *desc_addr; @@ -152,7 +153,7 @@ struct xgene_port_ops { void (*clear)(struct xgene_enet_pdata *pdata, struct xgene_enet_desc_ring *ring); void (*cle_bypass)(struct xgene_enet_pdata *pdata, - u32 dst_ring_num, u16 bufpool_id); + u32 dst_ring_num, u16 bufpool_id, u16 nxtbufpool_id); void (*shutdown)(struct xgene_enet_pdata *pdata); }; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index 8e4209c..82b7a5e 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -484,11 +484,12 @@ static int xgene_enet_reset(struct xgene_enet_pdata *p) } static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p, - u32 dst_ring_num, u16 bufpool_id) + u32 dst_ring_num, u16 bufpool_id, + u16 nxtbufpool_id) { - u32 data, fpsel; u32 cle_bypass_reg0, cle_bypass_reg1; u32 offset = p->port_id * MAC_OFFSET; + u32 data, fpsel, nxtfpsel; if (p->enet_id == XGENE_ENET1) { cle_bypass_reg0 = CLE_BYPASS_REG0_0_ADDR; @@ -502,7 +503,9 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p, xgene_enet_wr_csr(p, cle_bypass_reg0 + offset, data); fpsel = xgene_enet_get_fpsel(bufpool_id); - data = CFG_CLE_DSTQID0(dst_ring_num) | CFG_CLE_FPSEL0(fpsel); + nxtfpsel = xgene_enet_get_fpsel(nxtbufpool_id); + data = CFG_CLE_DSTQID0(dst_ring_num) | CFG_CLE_FPSEL0(fpsel) | + CFG_CLE_NXTFPSEL0(nxtfpsel); xgene_enet_wr_csr(p, cle_bypass_reg1 + offset, data); } diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index f97e599..e4adba6 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -350,9 +350,10 @@ static int xgene_enet_reset(struct xgene_enet_pdata *pdata) } static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata, - u32 dst_ring_num, u16 bufpool_id) + u32 dst_ring_num, u16 bufpool_id, + u16 nxtbufpool_id) { - u32 cb, fpsel; + u32 cb, fpsel, nxtfpsel; xgene_enet_rd_csr(pdata, XCLE_BYPASS_REG0_ADDR, &cb); cb |= CFG_CLE_BYPASS_EN0; @@ -360,9 +361,11 @@ static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata, xgene_enet_wr_csr(pdata, XCLE_BYPASS_REG0_ADDR, cb); fpsel = xgene_enet_get_fpsel(bufpool_id); + nxtfpsel = xgene_enet_get_fpsel(nxtbufpool_id); xgene_enet_rd_csr(pdata, XCLE_BYPASS_REG1_ADDR, &cb); CFG_CLE_DSTQID0_SET(&cb, dst_ring_num); CFG_CLE_FPSEL0_SET(&cb, fpsel); + CFG_CLE_NXTFPSEL0_SET(&cb, nxtfpsel); xgene_enet_wr_csr(pdata, XCLE_BYPASS_REG1_ADDR, cb); } -- cgit v1.1 From a9380b0f7be818168775a71fedacec6299d95a09 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:39 -0800 Subject: drivers: net: xgene: Add support for Jumbo frame This patch adds support for jumbo frame, by allocating additional buffer (page) pool and configuring the hardware. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 3 + drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 305 ++++++++++++++++++++-- drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 20 +- drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c | 1 + drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c | 3 + drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 4 + 6 files changed, 311 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index c395df3..fc9010f 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -679,6 +679,9 @@ static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata) for (i = 0; i < pdata->rxq_cnt; i++) { ring = pdata->rx_ring[i]->buf_pool; pb |= BIT(xgene_enet_get_fpsel(ring->id)); + ring = pdata->rx_ring[i]->page_pool; + if (ring) + pb |= BIT(xgene_enet_get_fpsel(ring->id)); } xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index c89acf5..698df27 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -37,6 +37,9 @@ static void xgene_enet_init_bufpool(struct xgene_enet_desc_ring *buf_pool) struct xgene_enet_raw_desc16 *raw_desc; int i; + if (!buf_pool) + return; + for (i = 0; i < buf_pool->slots; i++) { raw_desc = &buf_pool->raw_desc16[i]; @@ -47,6 +50,86 @@ static void xgene_enet_init_bufpool(struct xgene_enet_desc_ring *buf_pool) } } +static u16 xgene_enet_get_data_len(u64 bufdatalen) +{ + u16 hw_len, mask; + + hw_len = GET_VAL(BUFDATALEN, bufdatalen); + + if (unlikely(hw_len == 0x7800)) { + return 0; + } else if (!(hw_len & BIT(14))) { + mask = GENMASK(13, 0); + return (hw_len & mask) ? (hw_len & mask) : SIZE_16K; + } else if (!(hw_len & GENMASK(13, 12))) { + mask = GENMASK(11, 0); + return (hw_len & mask) ? (hw_len & mask) : SIZE_4K; + } else { + mask = GENMASK(11, 0); + return (hw_len & mask) ? (hw_len & mask) : SIZE_2K; + } +} + +static u16 xgene_enet_set_data_len(u32 size) +{ + u16 hw_len; + + hw_len = (size == SIZE_4K) ? BIT(14) : 0; + + return hw_len; +} + +static int xgene_enet_refill_pagepool(struct xgene_enet_desc_ring *buf_pool, + u32 nbuf) +{ + struct xgene_enet_raw_desc16 *raw_desc; + struct xgene_enet_pdata *pdata; + struct net_device *ndev; + dma_addr_t dma_addr; + struct device *dev; + struct page *page; + u32 slots, tail; + u16 hw_len; + int i; + + if (unlikely(!buf_pool)) + return 0; + + ndev = buf_pool->ndev; + pdata = netdev_priv(ndev); + dev = ndev_to_dev(ndev); + slots = buf_pool->slots - 1; + tail = buf_pool->tail; + + for (i = 0; i < nbuf; i++) { + raw_desc = &buf_pool->raw_desc16[tail]; + + page = dev_alloc_page(); + if (unlikely(!page)) + return -ENOMEM; + + dma_addr = dma_map_page(dev, page, 0, + PAGE_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, dma_addr))) { + put_page(page); + return -ENOMEM; + } + + hw_len = xgene_enet_set_data_len(PAGE_SIZE); + raw_desc->m1 = cpu_to_le64(SET_VAL(DATAADDR, dma_addr) | + SET_VAL(BUFDATALEN, hw_len) | + SET_BIT(COHERENT)); + + buf_pool->frag_page[tail] = page; + tail = (tail + 1) & slots; + } + + pdata->ring_ops->wr_cmd(buf_pool, nbuf); + buf_pool->tail = tail; + + return 0; +} + static int xgene_enet_refill_bufpool(struct xgene_enet_desc_ring *buf_pool, u32 nbuf) { @@ -64,8 +147,9 @@ static int xgene_enet_refill_bufpool(struct xgene_enet_desc_ring *buf_pool, ndev = buf_pool->ndev; dev = ndev_to_dev(buf_pool->ndev); pdata = netdev_priv(ndev); + bufdatalen = BUF_LEN_CODE_2K | (SKB_BUFFER_SIZE & GENMASK(11, 0)); - len = XGENE_ENET_MAX_MTU; + len = XGENE_ENET_STD_MTU; for (i = 0; i < nbuf; i++) { raw_desc = &buf_pool->raw_desc16[tail]; @@ -122,6 +206,25 @@ static void xgene_enet_delete_bufpool(struct xgene_enet_desc_ring *buf_pool) } } +static void xgene_enet_delete_pagepool(struct xgene_enet_desc_ring *buf_pool) +{ + struct device *dev = ndev_to_dev(buf_pool->ndev); + dma_addr_t dma_addr; + struct page *page; + int i; + + /* Free up the buffers held by hardware */ + for (i = 0; i < buf_pool->slots; i++) { + page = buf_pool->frag_page[i]; + if (page) { + dma_addr = buf_pool->frag_dma_addr[i]; + dma_unmap_page(dev, dma_addr, PAGE_SIZE, + DMA_FROM_DEVICE); + put_page(page); + } + } +} + static irqreturn_t xgene_enet_rx_irq(const int irq, void *data) { struct xgene_enet_desc_ring *rx_ring = data; @@ -515,23 +618,66 @@ static void xgene_enet_skip_csum(struct sk_buff *skb) } } +static void xgene_enet_free_pagepool(struct xgene_enet_desc_ring *buf_pool, + struct xgene_enet_raw_desc *raw_desc, + struct xgene_enet_raw_desc *exp_desc) +{ + __le64 *desc = (void *)exp_desc; + dma_addr_t dma_addr; + struct device *dev; + struct page *page; + u16 slots, head; + u32 frag_size; + int i; + + if (!buf_pool || !raw_desc || !exp_desc || + (!GET_VAL(NV, le64_to_cpu(raw_desc->m0)))) + return; + + dev = ndev_to_dev(buf_pool->ndev); + head = buf_pool->head; + + for (i = 0; i < 4; i++) { + frag_size = xgene_enet_get_data_len(le64_to_cpu(desc[i ^ 1])); + if (!frag_size) + break; + + dma_addr = GET_VAL(DATAADDR, le64_to_cpu(desc[i ^ 1])); + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); + + page = buf_pool->frag_page[head]; + put_page(page); + + buf_pool->frag_page[head] = NULL; + head = (head + 1) & slots; + } + buf_pool->head = head; +} + static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, - struct xgene_enet_raw_desc *raw_desc) + struct xgene_enet_raw_desc *raw_desc, + struct xgene_enet_raw_desc *exp_desc) { + struct xgene_enet_desc_ring *buf_pool, *page_pool; + u32 datalen, frag_size, skb_index; struct net_device *ndev; - struct device *dev; - struct xgene_enet_desc_ring *buf_pool; - u32 datalen, skb_index; + dma_addr_t dma_addr; struct sk_buff *skb; + struct device *dev; + struct page *page; + u16 slots, head; + int i, ret = 0; + __le64 *desc; u8 status; - int ret = 0; + bool nv; ndev = rx_ring->ndev; dev = ndev_to_dev(rx_ring->ndev); buf_pool = rx_ring->buf_pool; + page_pool = rx_ring->page_pool; dma_unmap_single(dev, GET_VAL(DATAADDR, le64_to_cpu(raw_desc->m1)), - XGENE_ENET_MAX_MTU, DMA_FROM_DEVICE); + XGENE_ENET_STD_MTU, DMA_FROM_DEVICE); skb_index = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0)); skb = buf_pool->rx_skb[skb_index]; buf_pool->rx_skb[skb_index] = NULL; @@ -541,6 +687,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); if (unlikely(status > 2)) { dev_kfree_skb_any(skb); + xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc); xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev), status); ret = -EIO; @@ -548,11 +695,44 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, } /* strip off CRC as HW isn't doing this */ - datalen = GET_VAL(BUFDATALEN, le64_to_cpu(raw_desc->m1)); - datalen = (datalen & DATALEN_MASK) - 4; - prefetch(skb->data - NET_IP_ALIGN); + datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1)); + + nv = GET_VAL(NV, le64_to_cpu(raw_desc->m0)); + if (!nv) + datalen -= 4; + skb_put(skb, datalen); + prefetch(skb->data - NET_IP_ALIGN); + + if (!nv) + goto skip_jumbo; + + slots = page_pool->slots - 1; + head = page_pool->head; + desc = (void *)exp_desc; + + for (i = 0; i < 4; i++) { + frag_size = xgene_enet_get_data_len(le64_to_cpu(desc[i ^ 1])); + if (!frag_size) + break; + + dma_addr = GET_VAL(DATAADDR, le64_to_cpu(desc[i ^ 1])); + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); + + page = page_pool->frag_page[head]; + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 0, + frag_size, PAGE_SIZE); + + datalen += frag_size; + + page_pool->frag_page[head] = NULL; + head = (head + 1) & slots; + } + + page_pool->head = head; + rx_ring->npagepool -= skb_shinfo(skb)->nr_frags; +skip_jumbo: skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, ndev); if (likely((ndev->features & NETIF_F_IP_CSUM) && @@ -563,7 +743,15 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, rx_ring->rx_packets++; rx_ring->rx_bytes += datalen; napi_gro_receive(&rx_ring->napi, skb); + out: + if (rx_ring->npagepool <= 0) { + ret = xgene_enet_refill_pagepool(page_pool, NUM_NXTBUFPOOL); + rx_ring->npagepool = NUM_NXTBUFPOOL; + if (ret) + return ret; + } + if (--rx_ring->nbufpool == 0) { ret = xgene_enet_refill_bufpool(buf_pool, NUM_BUFPOOL); rx_ring->nbufpool = NUM_BUFPOOL; @@ -611,7 +799,7 @@ static int xgene_enet_process_ring(struct xgene_enet_desc_ring *ring, desc_count++; } if (is_rx_desc(raw_desc)) { - ret = xgene_enet_rx_frame(ring, raw_desc); + ret = xgene_enet_rx_frame(ring, raw_desc, exp_desc); } else { ret = xgene_enet_tx_completion(ring, raw_desc); is_completion = true; @@ -854,7 +1042,7 @@ static void xgene_enet_delete_ring(struct xgene_enet_desc_ring *ring) static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata) { - struct xgene_enet_desc_ring *buf_pool; + struct xgene_enet_desc_ring *buf_pool, *page_pool; struct xgene_enet_desc_ring *ring; int i; @@ -867,18 +1055,28 @@ static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata) xgene_enet_delete_ring(ring->cp_ring); pdata->tx_ring[i] = NULL; } + } for (i = 0; i < pdata->rxq_cnt; i++) { ring = pdata->rx_ring[i]; if (ring) { + page_pool = ring->page_pool; + if (page_pool) { + xgene_enet_delete_pagepool(page_pool); + xgene_enet_delete_ring(page_pool); + pdata->port_ops->clear(pdata, page_pool); + } + buf_pool = ring->buf_pool; xgene_enet_delete_bufpool(buf_pool); xgene_enet_delete_ring(buf_pool); pdata->port_ops->clear(pdata, buf_pool); + xgene_enet_delete_ring(ring); pdata->rx_ring[i] = NULL; } + } } @@ -931,8 +1129,10 @@ static void xgene_enet_free_desc_ring(struct xgene_enet_desc_ring *ring) static void xgene_enet_free_desc_rings(struct xgene_enet_pdata *pdata) { + struct xgene_enet_desc_ring *page_pool; struct device *dev = &pdata->pdev->dev; struct xgene_enet_desc_ring *ring; + void *p; int i; for (i = 0; i < pdata->txq_cnt; i++) { @@ -940,10 +1140,13 @@ static void xgene_enet_free_desc_rings(struct xgene_enet_pdata *pdata) if (ring) { if (ring->cp_ring && ring->cp_ring->cp_skb) devm_kfree(dev, ring->cp_ring->cp_skb); + if (ring->cp_ring && pdata->cq_cnt) xgene_enet_free_desc_ring(ring->cp_ring); + xgene_enet_free_desc_ring(ring); } + } for (i = 0; i < pdata->rxq_cnt; i++) { @@ -952,8 +1155,21 @@ static void xgene_enet_free_desc_rings(struct xgene_enet_pdata *pdata) if (ring->buf_pool) { if (ring->buf_pool->rx_skb) devm_kfree(dev, ring->buf_pool->rx_skb); + xgene_enet_free_desc_ring(ring->buf_pool); } + + page_pool = ring->page_pool; + if (page_pool) { + p = page_pool->frag_page; + if (p) + devm_kfree(dev, p); + + p = page_pool->frag_dma_addr; + if (p) + devm_kfree(dev, p); + } + xgene_enet_free_desc_ring(ring); } } @@ -1071,19 +1287,20 @@ static u8 xgene_start_cpu_bufnum(struct xgene_enet_pdata *pdata) static int xgene_enet_create_desc_rings(struct net_device *ndev) { - struct xgene_enet_pdata *pdata = netdev_priv(ndev); - struct device *dev = ndev_to_dev(ndev); struct xgene_enet_desc_ring *rx_ring, *tx_ring, *cp_ring; + struct xgene_enet_pdata *pdata = netdev_priv(ndev); + struct xgene_enet_desc_ring *page_pool = NULL; struct xgene_enet_desc_ring *buf_pool = NULL; - enum xgene_ring_owner owner; - dma_addr_t dma_exp_bufs; - u8 cpu_bufnum; + struct device *dev = ndev_to_dev(ndev); u8 eth_bufnum = pdata->eth_bufnum; u8 bp_bufnum = pdata->bp_bufnum; u16 ring_num = pdata->ring_num; + enum xgene_ring_owner owner; + dma_addr_t dma_exp_bufs; + u16 ring_id, slots; __le64 *exp_bufs; - u16 ring_id; int i, ret, size; + u8 cpu_bufnum; cpu_bufnum = xgene_start_cpu_bufnum(pdata); @@ -1103,7 +1320,7 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev) owner = xgene_derive_ring_owner(pdata); ring_id = xgene_enet_get_ring_id(owner, bp_bufnum++); buf_pool = xgene_enet_create_desc_ring(ndev, ring_num++, - RING_CFGSIZE_2KB, + RING_CFGSIZE_16KB, ring_id); if (!buf_pool) { ret = -ENOMEM; @@ -1111,7 +1328,7 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev) } rx_ring->nbufpool = NUM_BUFPOOL; - rx_ring->buf_pool = buf_pool; + rx_ring->npagepool = NUM_NXTBUFPOOL; rx_ring->irq = pdata->irqs[i]; buf_pool->rx_skb = devm_kcalloc(dev, buf_pool->slots, sizeof(struct sk_buff *), @@ -1124,6 +1341,42 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev) buf_pool->dst_ring_num = xgene_enet_dst_ring_num(buf_pool); rx_ring->buf_pool = buf_pool; pdata->rx_ring[i] = rx_ring; + + if ((pdata->enet_id == XGENE_ENET1 && pdata->rxq_cnt > 4) || + (pdata->enet_id == XGENE_ENET2 && pdata->rxq_cnt > 16)) { + break; + } + + /* allocate next buffer pool for jumbo packets */ + owner = xgene_derive_ring_owner(pdata); + ring_id = xgene_enet_get_ring_id(owner, bp_bufnum++); + page_pool = xgene_enet_create_desc_ring(ndev, ring_num++, + RING_CFGSIZE_16KB, + ring_id); + if (!page_pool) { + ret = -ENOMEM; + goto err; + } + + slots = page_pool->slots; + page_pool->frag_page = devm_kcalloc(dev, slots, + sizeof(struct page *), + GFP_KERNEL); + if (!page_pool->frag_page) { + ret = -ENOMEM; + goto err; + } + + page_pool->frag_dma_addr = devm_kcalloc(dev, slots, + sizeof(dma_addr_t), + GFP_KERNEL); + if (!page_pool->frag_dma_addr) { + ret = -ENOMEM; + goto err; + } + + page_pool->dst_ring_num = xgene_enet_dst_ring_num(page_pool); + rx_ring->page_pool = page_pool; } for (i = 0; i < pdata->txq_cnt; i++) { @@ -1523,6 +1776,7 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) struct xgene_enet_desc_ring *buf_pool; u16 dst_ring_num, ring_id; int i, ret; + u32 count; ret = pdata->port_ops->reset(pdata); if (ret) @@ -1538,9 +1792,18 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) for (i = 0; i < pdata->rxq_cnt; i++) { buf_pool = pdata->rx_ring[i]->buf_pool; xgene_enet_init_bufpool(buf_pool); - ret = xgene_enet_refill_bufpool(buf_pool, pdata->rx_buff_cnt); + page_pool = pdata->rx_ring[i]->page_pool; + xgene_enet_init_bufpool(page_pool); + + count = pdata->rx_buff_cnt; + ret = xgene_enet_refill_bufpool(buf_pool, count); if (ret) goto err; + + ret = xgene_enet_refill_pagepool(page_pool, count); + if (ret) + goto err; + } dst_ring_num = xgene_enet_dst_ring_num(pdata->rx_ring[0]); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 1fe3942..023ed76 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -41,11 +41,14 @@ #include "../../../phy/mdio-xgene.h" #define XGENE_DRV_VERSION "v1.0" -#define XGENE_ENET_MAX_MTU 1536 -#define SKB_BUFFER_SIZE (XGENE_ENET_MAX_MTU - NET_IP_ALIGN) +#define XGENE_ENET_STD_MTU 1536 +#define XGENE_ENET_MAX_MTU 9600 +#define SKB_BUFFER_SIZE (XGENE_ENET_STD_MTU - NET_IP_ALIGN) + #define BUFLEN_16K (16 * 1024) -#define NUM_PKT_BUF 64 +#define NUM_PKT_BUF 1024 #define NUM_BUFPOOL 32 +#define NUM_NXTBUFPOOL 8 #define MAX_EXP_BUFFS 256 #define NUM_MSS_REG 4 #define XGENE_MIN_ENET_FRAME_SIZE 60 @@ -88,6 +91,12 @@ enum xgene_enet_id { XGENE_ENET2 }; +enum xgene_enet_buf_len { + SIZE_2K = 2048, + SIZE_4K = 4096, + SIZE_16K = 16384 +}; + /* software context of a descriptor ring */ struct xgene_enet_desc_ring { struct net_device *ndev; @@ -107,11 +116,14 @@ struct xgene_enet_desc_ring { dma_addr_t irq_mbox_dma; void *irq_mbox_addr; u16 dst_ring_num; - u8 nbufpool; + u16 nbufpool; + int npagepool; u8 index; + u32 flags; struct sk_buff *(*rx_skb); struct sk_buff *(*cp_skb); dma_addr_t *frag_dma_addr; + struct page *(*frag_page); enum xgene_enet_ring_cfgsize cfgsize; struct xgene_enet_desc_ring *cp_ring; struct xgene_enet_desc_ring *buf_pool; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c b/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c index af51dd5..4ff40559 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c @@ -119,6 +119,7 @@ static void xgene_enet_set_ring_id(struct xgene_enet_desc_ring *ring) ring_id_buf = (ring->num << 9) & GENMASK(18, 9); ring_id_buf |= PREFETCH_BUF_EN; + if (is_bufpool) ring_id_buf |= IS_BUFFER_POOL; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index 82b7a5e..a10ab64 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -536,6 +536,9 @@ static void xgene_enet_shutdown(struct xgene_enet_pdata *p) for (i = 0; i < p->rxq_cnt; i++) { ring = p->rx_ring[i]->buf_pool; pb |= BIT(xgene_enet_get_fpsel(ring->id)); + ring = p->rx_ring[i]->page_pool; + if (ring) + pb |= BIT(xgene_enet_get_fpsel(ring->id)); } xgene_enet_wr_ring_if(p, ENET_CFGSSQMIFPRESET_ADDR, pb); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index e4adba6..4109776 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -367,6 +367,7 @@ static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata, CFG_CLE_FPSEL0_SET(&cb, fpsel); CFG_CLE_NXTFPSEL0_SET(&cb, nxtfpsel); xgene_enet_wr_csr(pdata, XCLE_BYPASS_REG1_ADDR, cb); + pr_info("+ cle_bypass: fpsel: %d nxtfpsel: %d\n", fpsel, nxtfpsel); } static void xgene_enet_shutdown(struct xgene_enet_pdata *pdata) @@ -380,6 +381,9 @@ static void xgene_enet_shutdown(struct xgene_enet_pdata *pdata) for (i = 0; i < pdata->rxq_cnt; i++) { ring = pdata->rx_ring[i]->buf_pool; pb |= BIT(xgene_enet_get_fpsel(ring->id)); + ring = pdata->rx_ring[i]->page_pool; + if (ring) + pb |= BIT(xgene_enet_get_fpsel(ring->id)); } xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb); -- cgit v1.1 From 350b4e33b89378c5e5572a76c5e600999ad4d463 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:40 -0800 Subject: drivers: net: xgene: Add change_mtu function This patch implements ndo_change_mtu() callback function that enables mtu change. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 6 ++++++ drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 20 ++++++++++++++++++++ drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 1 + drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c | 6 ++++++ drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 7 +++++++ 5 files changed, 40 insertions(+) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index fc9010f..92cc7e5 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -504,6 +504,11 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) xgene_enet_wr_mcx_csr(pdata, ICM_CONFIG2_REG_0_ADDR, icm2); } +static void xgene_enet_set_frame_size(struct xgene_enet_pdata *pdata, int size) +{ + xgene_enet_wr_mcx_mac(pdata, MAX_FRAME_LEN_ADDR, size); +} + static void xgene_gmac_init(struct xgene_enet_pdata *pdata) { u32 value; @@ -903,6 +908,7 @@ const struct xgene_mac_ops xgene_gmac_ops = { .tx_disable = xgene_gmac_tx_disable, .set_speed = xgene_gmac_set_speed, .set_mac_addr = xgene_gmac_set_mac_addr, + .set_framesize = xgene_enet_set_frame_size, }; const struct xgene_port_ops xgene_gport_ops = { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 698df27..6c7eea8 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1500,12 +1500,31 @@ static int xgene_enet_set_mac_address(struct net_device *ndev, void *addr) return ret; } +static int xgene_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct xgene_enet_pdata *pdata = netdev_priv(ndev); + int frame_size; + + if (!netif_running(ndev)) + return 0; + + frame_size = (new_mtu > ETH_DATA_LEN) ? (new_mtu + 18) : 0x600; + + xgene_enet_close(ndev); + ndev->mtu = new_mtu; + pdata->mac_ops->set_framesize(pdata, frame_size); + xgene_enet_open(ndev); + + return 0; +} + static const struct net_device_ops xgene_ndev_ops = { .ndo_open = xgene_enet_open, .ndo_stop = xgene_enet_close, .ndo_start_xmit = xgene_enet_start_xmit, .ndo_tx_timeout = xgene_enet_timeout, .ndo_get_stats64 = xgene_enet_get_stats64, + .ndo_change_mtu = xgene_change_mtu, .ndo_set_mac_address = xgene_enet_set_mac_address, }; @@ -1832,6 +1851,7 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) buf_pool->id, ring_id); } + ndev->max_mtu = XGENE_ENET_MAX_MTU; pdata->phy_speed = SPEED_UNKNOWN; pdata->mac_ops->init(pdata); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 023ed76..61aa987 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -156,6 +156,7 @@ struct xgene_mac_ops { void (*rx_disable)(struct xgene_enet_pdata *pdata); void (*set_speed)(struct xgene_enet_pdata *pdata); void (*set_mac_addr)(struct xgene_enet_pdata *pdata); + void (*set_framesize)(struct xgene_enet_pdata *pdata, int framesize); void (*set_mss)(struct xgene_enet_pdata *pdata, u16 mss, u8 index); void (*link_state)(struct work_struct *work); }; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index a10ab64..6283f2b 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -343,6 +343,11 @@ static void xgene_sgmac_set_speed(struct xgene_enet_pdata *p) xgene_enet_wr_mcx_csr(p, icm2_addr, icm2); } +static void xgene_sgmac_set_frame_size(struct xgene_enet_pdata *pdata, int size) +{ + xgene_enet_wr_mac(pdata, MAX_FRAME_LEN_ADDR, size); +} + static void xgene_sgmii_enable_autoneg(struct xgene_enet_pdata *p) { u32 data, loop = 10; @@ -595,6 +600,7 @@ const struct xgene_mac_ops xgene_sgmac_ops = { .tx_disable = xgene_sgmac_tx_disable, .set_speed = xgene_sgmac_set_speed, .set_mac_addr = xgene_sgmac_set_mac_addr, + .set_framesize = xgene_sgmac_set_frame_size, .link_state = xgene_enet_link_state }; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index 4109776..2a9761b 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -250,6 +250,12 @@ static void xgene_xgmac_set_mss(struct xgene_enet_pdata *pdata, xgene_enet_wr_csr(pdata, XG_TSIF_MSS_REG0_ADDR + offset, data); } +static void xgene_xgmac_set_frame_size(struct xgene_enet_pdata *pdata, int size) +{ + xgene_enet_wr_mac(pdata, HSTMAXFRAME_LENGTH_ADDR, + ((((size + 2) >> 2) << 16) | size)); +} + static u32 xgene_enet_link_status(struct xgene_enet_pdata *pdata) { u32 data; @@ -474,6 +480,7 @@ const struct xgene_mac_ops xgene_xgmac_ops = { .rx_disable = xgene_xgmac_rx_disable, .tx_disable = xgene_xgmac_tx_disable, .set_mac_addr = xgene_xgmac_set_mac_addr, + .set_framesize = xgene_xgmac_set_frame_size, .set_mss = xgene_xgmac_set_mss, .link_state = xgene_enet_link_state }; -- cgit v1.1 From a809701fed15ff49cb87883030e5c63af03d7889 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:41 -0800 Subject: drivers: net: xgene: fix: RSS for non-TCP/UDP This patch fixes RSS feature, for non-TCP/UDP packets. Signed-off-by: Khuong Dinh Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_cle.c | 90 ++++++++++++++++++++++++- drivers/net/ethernet/apm/xgene/xgene_enet_cle.h | 1 + 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c index caa55bd..1dc6c20 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c @@ -485,11 +485,11 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) }, { .valid = 0, - .next_packet_pointer = 260, + .next_packet_pointer = 26, .jump_bw = JMP_FW, .jump_rel = JMP_ABS, .operation = EQT, - .next_node = LAST_NODE, + .next_node = RSS_IPV4_OTHERS_NODE, .next_branch = 0, .data = 0x0, .mask = 0xffff @@ -667,6 +667,92 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) } }, { + /* RSS_IPV4_OTHERS_NODE */ + .node_type = EWDN, + .last_node = 0, + .hdr_len_store = 1, + .hdr_extn = NO_BYTE, + .byte_store = NO_BYTE, + .search_byte_store = BOTH_BYTES, + .result_pointer = DB_RES_DROP, + .num_branches = 6, + .branch = { + { + /* SRC IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 28, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 1, + .data = 0x0, + .mask = 0xffff + }, + { + /* SRC IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 30, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 2, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 32, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 3, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 34, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 4, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP SRC Port */ + .valid = 0, + .next_packet_pointer = 36, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 5, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP DST Port */ + .valid = 0, + .next_packet_pointer = 260, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = LAST_NODE, + .next_branch = 0, + .data = 0x0, + .mask = 0xffff + } + } + }, + + { /* LAST NODE */ .node_type = EWDN, .last_node = 1, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h index 903be0c..290d5d1 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h @@ -106,6 +106,7 @@ enum xgene_cle_ptree_nodes { PKT_PROT_NODE, RSS_IPV4_TCP_NODE, RSS_IPV4_UDP_NODE, + RSS_IPV4_OTHERS_NODE, LAST_NODE, MAX_NODES }; -- cgit v1.1 From bb64fa09ac1b22515dc04d9dec3096da812f758b Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:42 -0800 Subject: drivers: net: xgene: Add flow control configuration This patch adds functions to configure mac, when flow control and pause frame settings change. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 48 +++++++++++++++++ drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 6 +++ drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 6 +++ drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c | 64 ++++++++++++++++------ drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 66 ++++++++++++++++++++++- drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h | 2 + 6 files changed, 176 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 92cc7e5..23a0175 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -509,6 +509,51 @@ static void xgene_enet_set_frame_size(struct xgene_enet_pdata *pdata, int size) xgene_enet_wr_mcx_mac(pdata, MAX_FRAME_LEN_ADDR, size); } +static void xgene_gmac_enable_tx_pause(struct xgene_enet_pdata *pdata, + bool enable) +{ + u32 data; + + xgene_enet_rd_mcx_csr(pdata, CSR_ECM_CFG_0_ADDR, &data); + + if (enable) + data |= MULTI_DPF_AUTOCTRL | PAUSE_XON_EN; + else + data &= ~(MULTI_DPF_AUTOCTRL | PAUSE_XON_EN); + + xgene_enet_wr_mcx_csr(pdata, CSR_ECM_CFG_0_ADDR, data); +} + +static void xgene_gmac_flowctl_tx(struct xgene_enet_pdata *pdata, bool enable) +{ + u32 data; + + xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); + + if (enable) + data |= TX_FLOW_EN; + else + data &= ~TX_FLOW_EN; + + xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data); + + pdata->mac_ops->enable_tx_pause(pdata, enable); +} + +static void xgene_gmac_flowctl_rx(struct xgene_enet_pdata *pdata, bool enable) +{ + u32 data; + + xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); + + if (enable) + data |= RX_FLOW_EN; + else + data &= ~RX_FLOW_EN; + + xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data); +} + static void xgene_gmac_init(struct xgene_enet_pdata *pdata) { u32 value; @@ -909,6 +954,9 @@ const struct xgene_mac_ops xgene_gmac_ops = { .set_speed = xgene_gmac_set_speed, .set_mac_addr = xgene_gmac_set_mac_addr, .set_framesize = xgene_enet_set_frame_size, + .enable_tx_pause = xgene_gmac_enable_tx_pause, + .flowctl_tx = xgene_gmac_flowctl_tx, + .flowctl_rx = xgene_gmac_flowctl_rx, }; const struct xgene_port_ops xgene_gport_ops = { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index bd6cb6c..7ba649d 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -170,6 +170,10 @@ enum xgene_enet_rm { #define CFG_WAITASYNCRD_SET(dst, val) xgene_set_bits(dst, val, 0, 16) #define CFG_CLE_DSTQID0(val) ((val) & GENMASK(11, 0)) #define CFG_CLE_FPSEL0(val) (((val) << 16) & GENMASK(19, 16)) +#define CSR_ECM_CFG_0_ADDR 0x0220 +#define CSR_ECM_CFG_1_ADDR 0x0224 +#define PAUSE_XON_EN BIT(30) +#define MULTI_DPF_AUTOCTRL BIT(28) #define CFG_CLE_NXTFPSEL0(val) (((val) << 20) & GENMASK(23, 20)) #define ICM_CONFIG0_REG_0_ADDR 0x0400 #define ICM_CONFIG2_REG_0_ADDR 0x0410 @@ -198,6 +202,8 @@ enum xgene_enet_rm { #define SOFT_RESET1 BIT(31) #define TX_EN BIT(0) #define RX_EN BIT(2) +#define TX_FLOW_EN BIT(4) +#define RX_FLOW_EN BIT(5) #define ENET_LHD_MODE BIT(25) #define ENET_GHD_MODE BIT(26) #define FULL_DUPLEX2 BIT(0) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 61aa987..5257174 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -159,6 +159,9 @@ struct xgene_mac_ops { void (*set_framesize)(struct xgene_enet_pdata *pdata, int framesize); void (*set_mss)(struct xgene_enet_pdata *pdata, u16 mss, u8 index); void (*link_state)(struct work_struct *work); + void (*enable_tx_pause)(struct xgene_enet_pdata *pdata, bool enable); + void (*flowctl_rx)(struct xgene_enet_pdata *pdata, bool enable); + void (*flowctl_tx)(struct xgene_enet_pdata *pdata, bool enable); }; struct xgene_port_ops { @@ -234,6 +237,9 @@ struct xgene_enet_pdata { bool mdio_driver; struct gpio_desc *sfp_rdy; bool sfp_gpio_en; + u32 pause_autoneg; + bool tx_pause; + bool rx_pause; }; struct xgene_indirect_ctl { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index 6283f2b..1c9b8ba 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -365,6 +365,32 @@ static void xgene_sgmii_enable_autoneg(struct xgene_enet_pdata *p) netdev_err(p->ndev, "Auto-negotiation failed\n"); } +static void xgene_sgmac_rxtx(struct xgene_enet_pdata *p, u32 bits, bool set) +{ + u32 data; + + data = xgene_enet_rd_mac(p, MAC_CONFIG_1_ADDR); + + if (set) + data |= bits; + else + data &= ~bits; + + xgene_enet_wr_mac(p, MAC_CONFIG_1_ADDR, data); +} + +static void xgene_sgmac_flowctl_tx(struct xgene_enet_pdata *p, bool enable) +{ + xgene_sgmac_rxtx(p, TX_FLOW_EN, enable); + + p->mac_ops->enable_tx_pause(p, enable); +} + +static void xgene_sgmac_flowctl_rx(struct xgene_enet_pdata *pdata, bool enable) +{ + xgene_sgmac_rxtx(pdata, RX_FLOW_EN, enable); +} + static void xgene_sgmac_init(struct xgene_enet_pdata *p) { u32 enet_spare_cfg_reg, rsif_config_reg; @@ -411,20 +437,6 @@ static void xgene_sgmac_init(struct xgene_enet_pdata *p) xgene_enet_wr_mcx_csr(p, rx_dv_gate_reg, RESUME_RX0); } -static void xgene_sgmac_rxtx(struct xgene_enet_pdata *p, u32 bits, bool set) -{ - u32 data; - - data = xgene_enet_rd_mac(p, MAC_CONFIG_1_ADDR); - - if (set) - data |= bits; - else - data &= ~bits; - - xgene_enet_wr_mac(p, MAC_CONFIG_1_ADDR, data); -} - static void xgene_sgmac_rx_enable(struct xgene_enet_pdata *p) { xgene_sgmac_rxtx(p, RX_EN, true); @@ -591,6 +603,25 @@ static void xgene_enet_link_state(struct work_struct *work) schedule_delayed_work(&p->link_work, poll_interval); } +static void xgene_sgmac_enable_tx_pause(struct xgene_enet_pdata *p, bool enable) +{ + u32 data, ecm_cfg_addr; + + if (p->enet_id == XGENE_ENET1) { + ecm_cfg_addr = (!(p->port_id % 2)) ? CSR_ECM_CFG_0_ADDR : + CSR_ECM_CFG_1_ADDR; + } else { + ecm_cfg_addr = XG_MCX_ECM_CFG_0_ADDR; + } + + data = xgene_enet_rd_mcx_csr(p, ecm_cfg_addr); + if (enable) + data |= MULTI_DPF_AUTOCTRL | PAUSE_XON_EN; + else + data &= ~(MULTI_DPF_AUTOCTRL | PAUSE_XON_EN); + xgene_enet_wr_mcx_csr(p, ecm_cfg_addr, data); +} + const struct xgene_mac_ops xgene_sgmac_ops = { .init = xgene_sgmac_init, .reset = xgene_sgmac_reset, @@ -601,7 +632,10 @@ const struct xgene_mac_ops xgene_sgmac_ops = { .set_speed = xgene_sgmac_set_speed, .set_mac_addr = xgene_sgmac_set_mac_addr, .set_framesize = xgene_sgmac_set_frame_size, - .link_state = xgene_enet_link_state + .link_state = xgene_enet_link_state, + .enable_tx_pause = xgene_sgmac_enable_tx_pause, + .flowctl_tx = xgene_sgmac_flowctl_tx, + .flowctl_rx = xgene_sgmac_flowctl_rx }; const struct xgene_port_ops xgene_sgport_ops = { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index 2a9761b..727ef4b 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -101,6 +101,14 @@ static void xgene_enet_wr_pcs(struct xgene_enet_pdata *pdata, wr_addr); } +static void xgene_enet_wr_axg_csr(struct xgene_enet_pdata *pdata, + u32 offset, u32 val) +{ + void __iomem *addr = pdata->mcx_mac_csr_addr + offset; + + iowrite32(val, addr); +} + static void xgene_enet_rd_csr(struct xgene_enet_pdata *pdata, u32 offset, u32 *val) { @@ -174,6 +182,14 @@ static bool xgene_enet_rd_pcs(struct xgene_enet_pdata *pdata, return success; } +static void xgene_enet_rd_axg_csr(struct xgene_enet_pdata *pdata, + u32 offset, u32 *val) +{ + void __iomem *addr = pdata->mcx_mac_csr_addr + offset; + + *val = ioread32(addr); +} + static int xgene_enet_ecc_init(struct xgene_enet_pdata *pdata) { struct net_device *ndev = pdata->ndev; @@ -265,6 +281,51 @@ static u32 xgene_enet_link_status(struct xgene_enet_pdata *pdata) return data; } +static void xgene_xgmac_enable_tx_pause(struct xgene_enet_pdata *pdata, + bool enable) +{ + u32 data; + + xgene_enet_rd_axg_csr(pdata, XGENET_CSR_ECM_CFG_0_ADDR, &data); + + if (enable) + data |= MULTI_DPF_AUTOCTRL | PAUSE_XON_EN; + else + data &= ~(MULTI_DPF_AUTOCTRL | PAUSE_XON_EN); + + xgene_enet_wr_axg_csr(pdata, XGENET_CSR_ECM_CFG_0_ADDR, data); +} + +static void xgene_xgmac_flowctl_tx(struct xgene_enet_pdata *pdata, bool enable) +{ + u32 data; + + xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + + if (enable) + data |= HSTTCTLEN; + else + data &= ~HSTTCTLEN; + + xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data); + + pdata->mac_ops->enable_tx_pause(pdata, enable); +} + +static void xgene_xgmac_flowctl_rx(struct xgene_enet_pdata *pdata, bool enable) +{ + u32 data; + + xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + + if (enable) + data |= HSTRCTLEN; + else + data &= ~HSTRCTLEN; + + xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data); +} + static void xgene_xgmac_init(struct xgene_enet_pdata *pdata) { u32 data; @@ -482,7 +543,10 @@ const struct xgene_mac_ops xgene_xgmac_ops = { .set_mac_addr = xgene_xgmac_set_mac_addr, .set_framesize = xgene_xgmac_set_frame_size, .set_mss = xgene_xgmac_set_mss, - .link_state = xgene_enet_link_state + .link_state = xgene_enet_link_state, + .enable_tx_pause = xgene_xgmac_enable_tx_pause, + .flowctl_rx = xgene_xgmac_flowctl_rx, + .flowctl_tx = xgene_xgmac_flowctl_tx }; const struct xgene_port_ops xgene_xgport_ops = { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h index 360ccbd..9d75020 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h @@ -59,6 +59,7 @@ #define HSTMAXFRAME_LENGTH_ADDR 0x0020 #define XG_MCX_RX_DV_GATE_REG_0_ADDR 0x0004 +#define XG_MCX_ECM_CFG_0_ADDR 0x0074 #define XG_RSIF_CONFIG_REG_ADDR 0x00a0 #define XCLE_BYPASS_REG0_ADDR 0x0160 #define XCLE_BYPASS_REG1_ADDR 0x0164 @@ -70,6 +71,7 @@ #define XG_ENET_SPARE_CFG_REG_ADDR 0x040c #define XG_ENET_SPARE_CFG_REG_1_ADDR 0x0410 #define XGENET_RX_DV_GATE_REG_0_ADDR 0x0804 +#define XGENET_CSR_ECM_CFG_0_ADDR 0x0880 #define XG_MCX_ICM_CONFIG0_REG_0_ADDR 0x00e0 #define XG_MCX_ICM_CONFIG2_REG_0_ADDR 0x00e8 -- cgit v1.1 From 56090b12283d40b02879377ac8f734520ce6e6b2 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:43 -0800 Subject: drivers: net: xgene: Add flow control initialization This patch adds flow control/pause frame initialization and advertising capabilities. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 57 +++++++++++++++++++++++ drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 7 +++ drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c | 44 ++++++++++++++++- drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 17 +++++++ drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h | 7 +++ 5 files changed, 131 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 23a0175..06e6816 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -577,6 +577,17 @@ static void xgene_gmac_init(struct xgene_enet_pdata *pdata) /* Rtype should be copied from FP */ xgene_enet_wr_csr(pdata, RSIF_RAM_DBG_REG0_ADDR, 0); + /* Configure HW pause frame generation */ + xgene_enet_rd_mcx_csr(pdata, CSR_MULTI_DPF0_ADDR, &value); + value = (DEF_QUANTA << 16) | (value & 0xFFFF); + xgene_enet_wr_mcx_csr(pdata, CSR_MULTI_DPF0_ADDR, value); + + xgene_enet_wr_csr(pdata, RXBUF_PAUSE_THRESH, DEF_PAUSE_THRES); + xgene_enet_wr_csr(pdata, RXBUF_PAUSE_OFF_THRESH, DEF_PAUSE_OFF_THRES); + + xgene_gmac_flowctl_tx(pdata, pdata->tx_pause); + xgene_gmac_flowctl_rx(pdata, pdata->rx_pause); + /* Rx-Tx traffic resume */ xgene_enet_wr_csr(pdata, CFG_LINK_AGGR_RESUME_0_ADDR, TX_PORT0); @@ -749,6 +760,48 @@ static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata) } } +static u32 xgene_enet_flowctrl_cfg(struct net_device *ndev) +{ + struct xgene_enet_pdata *pdata = netdev_priv(ndev); + struct phy_device *phydev = ndev->phydev; + u16 lcladv, rmtadv = 0; + u32 rx_pause, tx_pause; + u8 flowctl = 0; + + if (!phydev->duplex || !pdata->pause_autoneg) + return 0; + + if (pdata->tx_pause) + flowctl |= FLOW_CTRL_TX; + + if (pdata->rx_pause) + flowctl |= FLOW_CTRL_RX; + + lcladv = mii_advertise_flowctrl(flowctl); + + if (phydev->pause) + rmtadv = LPA_PAUSE_CAP; + + if (phydev->asym_pause) + rmtadv |= LPA_PAUSE_ASYM; + + flowctl = mii_resolve_flowctrl_fdx(lcladv, rmtadv); + tx_pause = !!(flowctl & FLOW_CTRL_TX); + rx_pause = !!(flowctl & FLOW_CTRL_RX); + + if (tx_pause != pdata->tx_pause) { + pdata->tx_pause = tx_pause; + pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause); + } + + if (rx_pause != pdata->rx_pause) { + pdata->rx_pause = rx_pause; + pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause); + } + + return 0; +} + static void xgene_enet_adjust_link(struct net_device *ndev) { struct xgene_enet_pdata *pdata = netdev_priv(ndev); @@ -763,6 +816,8 @@ static void xgene_enet_adjust_link(struct net_device *ndev) mac_ops->tx_enable(pdata); phy_print_status(phydev); } + + xgene_enet_flowctrl_cfg(ndev); } else { mac_ops->rx_disable(pdata); mac_ops->tx_disable(pdata); @@ -836,6 +891,8 @@ int xgene_enet_phy_connect(struct net_device *ndev) phy_dev->supported &= ~SUPPORTED_10baseT_Half & ~SUPPORTED_100baseT_Half & ~SUPPORTED_1000baseT_Half; + phy_dev->supported |= SUPPORTED_Pause | + SUPPORTED_Asym_Pause; phy_dev->advertising = phy_dev->supported; return 0; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index 7ba649d..5f83037 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -172,6 +172,13 @@ enum xgene_enet_rm { #define CFG_CLE_FPSEL0(val) (((val) << 16) & GENMASK(19, 16)) #define CSR_ECM_CFG_0_ADDR 0x0220 #define CSR_ECM_CFG_1_ADDR 0x0224 +#define CSR_MULTI_DPF0_ADDR 0x0230 +#define RXBUF_PAUSE_THRESH 0x0534 +#define RXBUF_PAUSE_OFF_THRESH 0x0540 +#define DEF_PAUSE_THRES 0x7d +#define DEF_PAUSE_OFF_THRES 0x6d +#define DEF_QUANTA 0x8000 +#define NORM_PAUSE_OPCODE 0x0001 #define PAUSE_XON_EN BIT(30) #define MULTI_DPF_AUTOCTRL BIT(28) #define CFG_CLE_NXTFPSEL0(val) (((val) << 20) & GENMASK(23, 20)) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index 1c9b8ba..a8e063b 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -393,9 +393,11 @@ static void xgene_sgmac_flowctl_rx(struct xgene_enet_pdata *pdata, bool enable) static void xgene_sgmac_init(struct xgene_enet_pdata *p) { + u32 pause_thres_reg, pause_off_thres_reg; u32 enet_spare_cfg_reg, rsif_config_reg; u32 cfg_bypass_reg, rx_dv_gate_reg; - u32 data, offset; + u32 data, data1, data2, offset; + u32 multi_dpf_reg; if (!(p->enet_id == XGENE_ENET2 && p->mdio_driver)) xgene_sgmac_reset(p); @@ -431,6 +433,46 @@ static void xgene_sgmac_init(struct xgene_enet_pdata *p) data |= CFG_RSIF_FPBUFF_TIMEOUT_EN; xgene_enet_wr_csr(p, rsif_config_reg, data); + /* Configure HW pause frame generation */ + multi_dpf_reg = (p->enet_id == XGENE_ENET1) ? CSR_MULTI_DPF0_ADDR : + XG_MCX_MULTI_DPF0_ADDR; + data = xgene_enet_rd_mcx_csr(p, multi_dpf_reg); + data = (DEF_QUANTA << 16) | (data & 0xffff); + xgene_enet_wr_mcx_csr(p, multi_dpf_reg, data); + + if (p->enet_id != XGENE_ENET1) { + data = xgene_enet_rd_mcx_csr(p, XG_MCX_MULTI_DPF1_ADDR); + data = (NORM_PAUSE_OPCODE << 16) | (data & 0xFFFF); + xgene_enet_wr_mcx_csr(p, XG_MCX_MULTI_DPF1_ADDR, data); + } + + pause_thres_reg = (p->enet_id == XGENE_ENET1) ? RXBUF_PAUSE_THRESH : + XG_RXBUF_PAUSE_THRESH; + pause_off_thres_reg = (p->enet_id == XGENE_ENET1) ? + RXBUF_PAUSE_OFF_THRESH : 0; + + if (p->enet_id == XGENE_ENET1) { + data1 = xgene_enet_rd_csr(p, pause_thres_reg); + data2 = xgene_enet_rd_csr(p, pause_off_thres_reg); + + if (!(p->port_id % 2)) { + data1 = (data1 & 0xffff0000) | DEF_PAUSE_THRES; + data2 = (data2 & 0xffff0000) | DEF_PAUSE_OFF_THRES; + } else { + data1 = (data1 & 0xffff) | (DEF_PAUSE_THRES << 16); + data2 = (data2 & 0xffff) | (DEF_PAUSE_OFF_THRES << 16); + } + + xgene_enet_wr_csr(p, pause_thres_reg, data1); + xgene_enet_wr_csr(p, pause_off_thres_reg, data2); + } else { + data = (DEF_PAUSE_OFF_THRES << 16) | DEF_PAUSE_THRES; + xgene_enet_wr_csr(p, pause_thres_reg, data); + } + + xgene_sgmac_flowctl_tx(p, p->tx_pause); + xgene_sgmac_flowctl_rx(p, p->rx_pause); + /* Bypass traffic gating */ xgene_enet_wr_csr(p, XG_ENET_SPARE_CFG_REG_1_ADDR, 0x84); xgene_enet_wr_csr(p, cfg_bypass_reg, RESUME_TX); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index 727ef4b..ece19e6 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -349,6 +349,23 @@ static void xgene_xgmac_init(struct xgene_enet_pdata *pdata) xgene_enet_wr_csr(pdata, XG_ENET_SPARE_CFG_REG_1_ADDR, 0x82); xgene_enet_wr_csr(pdata, XGENET_RX_DV_GATE_REG_0_ADDR, 0); xgene_enet_wr_csr(pdata, XG_CFG_BYPASS_ADDR, RESUME_TX); + + /* Configure HW pause frame generation */ + xgene_enet_rd_axg_csr(pdata, XGENET_CSR_MULTI_DPF0_ADDR, &data); + data = (DEF_QUANTA << 16) | (data & 0xFFFF); + xgene_enet_wr_axg_csr(pdata, XGENET_CSR_MULTI_DPF0_ADDR, data); + + if (pdata->enet_id != XGENE_ENET1) { + xgene_enet_rd_axg_csr(pdata, XGENET_CSR_MULTI_DPF1_ADDR, &data); + data = (NORM_PAUSE_OPCODE << 16) | (data & 0xFFFF); + xgene_enet_wr_axg_csr(pdata, XGENET_CSR_MULTI_DPF1_ADDR, data); + } + + data = (XG_DEF_PAUSE_OFF_THRES << 16) | XG_DEF_PAUSE_THRES; + xgene_enet_wr_csr(pdata, XG_RXBUF_PAUSE_THRESH, data); + + xgene_xgmac_flowctl_tx(pdata, pdata->tx_pause); + xgene_xgmac_flowctl_rx(pdata, pdata->rx_pause); } static void xgene_xgmac_rx_enable(struct xgene_enet_pdata *pdata) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h index 9d75020..03b847a 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h @@ -60,6 +60,10 @@ #define XG_MCX_RX_DV_GATE_REG_0_ADDR 0x0004 #define XG_MCX_ECM_CFG_0_ADDR 0x0074 +#define XG_MCX_MULTI_DPF0_ADDR 0x007c +#define XG_MCX_MULTI_DPF1_ADDR 0x0080 +#define XG_DEF_PAUSE_THRES 0x390 +#define XG_DEF_PAUSE_OFF_THRES 0x2c0 #define XG_RSIF_CONFIG_REG_ADDR 0x00a0 #define XCLE_BYPASS_REG0_ADDR 0x0160 #define XCLE_BYPASS_REG1_ADDR 0x0164 @@ -72,6 +76,9 @@ #define XG_ENET_SPARE_CFG_REG_1_ADDR 0x0410 #define XGENET_RX_DV_GATE_REG_0_ADDR 0x0804 #define XGENET_CSR_ECM_CFG_0_ADDR 0x0880 +#define XGENET_CSR_MULTI_DPF0_ADDR 0x0888 +#define XGENET_CSR_MULTI_DPF1_ADDR 0x088c +#define XG_RXBUF_PAUSE_THRESH 0x0020 #define XG_MCX_ICM_CONFIG0_REG_0_ADDR 0x00e0 #define XG_MCX_ICM_CONFIG2_REG_0_ADDR 0x00e8 -- cgit v1.1 From 0296fe4da6505bb9eb36f85f157b0219ef1e2e04 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Thu, 1 Dec 2016 16:41:44 -0800 Subject: drivers: net: xgene: ethtool: Add get/set_pauseparam This patch adds get_pauseparam and set_pauseparam functions. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- .../net/ethernet/apm/xgene/xgene_enet_ethtool.c | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c index d372d42..28fdedc 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c @@ -163,6 +163,74 @@ static void xgene_get_ethtool_stats(struct net_device *ndev, *data++ = *(u64 *)(pdata + gstrings_stats[i].offset); } +static void xgene_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pp) +{ + struct xgene_enet_pdata *pdata = netdev_priv(ndev); + + pp->autoneg = pdata->pause_autoneg; + pp->tx_pause = pdata->tx_pause; + pp->rx_pause = pdata->rx_pause; +} + +static int xgene_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pp) +{ + struct xgene_enet_pdata *pdata = netdev_priv(ndev); + struct phy_device *phydev = ndev->phydev; + u32 oldadv, newadv; + + if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII || + pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { + if (!phydev) + return -EINVAL; + + if (!(phydev->supported & SUPPORTED_Pause) || + (!(phydev->supported & SUPPORTED_Asym_Pause) && + pp->rx_pause != pp->tx_pause)) + return -EINVAL; + + pdata->pause_autoneg = pp->autoneg; + pdata->tx_pause = pp->tx_pause; + pdata->rx_pause = pp->rx_pause; + + oldadv = phydev->advertising; + newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); + + if (pp->rx_pause) + newadv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; + + if (pp->tx_pause) + newadv ^= ADVERTISED_Asym_Pause; + + if (oldadv ^ newadv) { + phydev->advertising = newadv; + + if (phydev->autoneg) + return phy_start_aneg(phydev); + + if (!pp->autoneg) { + pdata->mac_ops->flowctl_tx(pdata, + pdata->tx_pause); + pdata->mac_ops->flowctl_rx(pdata, + pdata->rx_pause); + } + } + + } else { + if (pp->autoneg) + return -EINVAL; + + pdata->tx_pause = pp->tx_pause; + pdata->rx_pause = pp->rx_pause; + + pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause); + pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause); + } + + return 0; +} + static const struct ethtool_ops xgene_ethtool_ops = { .get_drvinfo = xgene_get_drvinfo, .get_link = ethtool_op_get_link, @@ -171,6 +239,8 @@ static const struct ethtool_ops xgene_ethtool_ops = { .get_ethtool_stats = xgene_get_ethtool_stats, .get_link_ksettings = xgene_get_link_ksettings, .set_link_ksettings = xgene_set_link_ksettings, + .get_pauseparam = xgene_get_pauseparam, + .set_pauseparam = xgene_set_pauseparam }; void xgene_enet_set_ethtool_ops(struct net_device *ndev) -- cgit v1.1 From 90caf3c9d46dd9a24adc7dae6e1909348b3c37c4 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Fri, 2 Dec 2016 09:51:03 +0800 Subject: qede: use reset to set network header Since offset is zero, it's not necessary to use set function. Reset function is straightforward, and will remove the unnecessary add operation in set function. Signed-off-by: Zhang Shengju Acked-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index faeaa9f..cf1dd14 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1312,7 +1312,7 @@ static void qede_gro_receive(struct qede_dev *edev, #ifdef CONFIG_INET if (skb_shinfo(skb)->gso_size) { - skb_set_network_header(skb, 0); + skb_reset_network_header(skb); switch (skb->protocol) { case htons(ETH_P_IP): -- cgit v1.1 From 0e24c0ad2b95f971c7af039080e7bd0a34b8b0dc Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Fri, 2 Dec 2016 09:51:04 +0800 Subject: bnx2x: use reset to set network header Since offset is zero, it's not necessary to use set function. Reset function is straightforward, and will remove the unnecessary add operation in set function. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 3fd36b4..3e199d3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -724,7 +724,7 @@ static void bnx2x_gro_ipv6_csum(struct bnx2x *bp, struct sk_buff *skb) static void bnx2x_gro_csum(struct bnx2x *bp, struct sk_buff *skb, void (*gro_func)(struct bnx2x*, struct sk_buff*)) { - skb_set_network_header(skb, 0); + skb_reset_network_header(skb); gro_func(bp, skb); tcp_gro_complete(skb); } -- cgit v1.1 From 690291093a54535d63084e4da63fd44599cf02e8 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Fri, 2 Dec 2016 09:51:05 +0800 Subject: mlx4: use reset to set mac header Since offset is zero, it's not necessary to use set function. Reset function is straightforward, and will remove the unnecessary add operation in set function. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_selftest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c index c06346a..95290e1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c @@ -68,7 +68,7 @@ static int mlx4_en_test_loopback_xmit(struct mlx4_en_priv *priv) memcpy(ethh->h_dest, priv->dev->dev_addr, ETH_ALEN); eth_zero_addr(ethh->h_source); ethh->h_proto = htons(ETH_P_ARP); - skb_set_mac_header(skb, 0); + skb_reset_mac_header(skb); for (i = 0; i < packet_size; ++i) /* fill our packet */ packet[i] = (unsigned char)(i & 0xff); -- cgit v1.1 From a52a8a4d98f82478d8ef0d121115b514a7f72f85 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Fri, 2 Dec 2016 09:51:06 +0800 Subject: iwlwifi: use reset to set transport header Since offset is zero, it's not necessary to use set function. Reset function is straightforward, and will remove the unnecessary add operation in set function. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 5f840f1..e44e5ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -2196,7 +2196,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, memcpy(skb_put(csum_skb, tcp_hdrlen(skb)), tcph, tcp_hdrlen(skb)); - skb_set_transport_header(csum_skb, 0); + skb_reset_transport_header(csum_skb); csum_skb->csum_start = (unsigned char *)tcp_hdr(csum_skb) - csum_skb->head; -- cgit v1.1 From fe211cd8d850c3f9ced08f8e88d6fdaeb8a2af8a Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Fri, 2 Dec 2016 09:51:07 +0800 Subject: staging: wilc1000: use reset to set mac header Since offset is zero, it's not necessary to use set function. Reset function is straightforward, and will remove the unnecessary add operation in set function. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- drivers/staging/wilc1000/linux_mon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/wilc1000/linux_mon.c b/drivers/staging/wilc1000/linux_mon.c index 242f82f..f328d75 100644 --- a/drivers/staging/wilc1000/linux_mon.c +++ b/drivers/staging/wilc1000/linux_mon.c @@ -111,7 +111,7 @@ void WILC_WFI_monitor_rx(u8 *buff, u32 size) } skb->dev = wilc_wfi_mon; - skb_set_mac_header(skb, 0); + skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); @@ -215,7 +215,7 @@ static netdev_tx_t WILC_WFI_mon_xmit(struct sk_buff *skb, cb_hdr->tx_flags = 0x0004; skb2->dev = wilc_wfi_mon; - skb_set_mac_header(skb2, 0); + skb_reset_mac_header(skb2); skb2->ip_summed = CHECKSUM_UNNECESSARY; skb2->pkt_type = PACKET_OTHERHOST; skb2->protocol = htons(ETH_P_802_2); -- cgit v1.1 From 4f7df337fe79bba1e4c2d525525d63b5ba186bbd Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 2 Dec 2016 03:59:06 +0300 Subject: netlink: 2-clause nla_ok() nla_ok() consists of 3 clauses: 1) int rem >= (int)sizeof(struct nlattr) 2) u16 nla_len >= sizeof(struct nlattr) 3) u16 nla_len <= int rem The statement is that clause (1) is redundant. What it does is ensuring that "rem" is a positive number, so that in clause (3) positive number will be compared to positive number with no problems. However, "u16" fully fits into "int" and integers do not change value when upcasting even to signed type. Negative integers will be rejected by clause (3) just fine. Small positive integers will be rejected by transitivity of comparison operator. NOTE: all of the above DOES NOT apply to nlmsg_ok() where ->nlmsg_len is u32(!), so 3 clauses AND A CAST TO INT are necessary. Obligatory space savings report: -1.6 KB $ ./scripts/bloat-o-meter ../vmlinux-000* ../vmlinux-001* add/remove: 0/0 grow/shrink: 3/63 up/down: 35/-1692 (-1657) function old new delta validate_scan_freqs 142 155 +13 tcf_em_tree_validate 867 879 +12 dcbnl_ieee_del 328 338 +10 netlbl_cipsov4_add_common.isra 218 215 -3 ... ovs_nla_put_actions 888 806 -82 netlbl_cipsov4_add_std 1648 1566 -82 nl80211_parse_sched_scan 2889 2780 -109 ip_tun_from_nlattr 3086 2945 -141 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/net/netlink.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/net/netlink.h b/include/net/netlink.h index d3938f1..dd657a3 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -698,8 +698,7 @@ static inline int nla_len(const struct nlattr *nla) */ static inline int nla_ok(const struct nlattr *nla, int remaining) { - return remaining >= (int) sizeof(*nla) && - nla->nla_len >= sizeof(*nla) && + return nla->nla_len >= sizeof(*nla) && nla->nla_len <= remaining; } -- cgit v1.1 From 1a9a059203299ed42b984e91b87086bafeea36c6 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 2 Dec 2016 04:11:34 +0300 Subject: netns: publish net_generic correctly Publishing net_generic pointer is done with silly mistake: new array is published BEFORE setting freshly acquired pernet subsystem pointer. memcpy rcu_assign_pointer kfree_rcu ng->ptr[id - 1] = data; This bug was introduced with commit dec827d174d7f76c457238800183ca864a639365 ("[NETNS]: The generic per-net pointers.") in the glorious days of chopping networking stack into containers proper 8.5 years ago (whee...) How it didn't trigger for so long? Well, you need quite specific set of conditions: *) race window opens once per pernet subsystem addition (read: modprobe or boot) *) not every pernet subsystem is eligible (need ->id and ->size) *) not every pernet subsystem is vulnerable (need incorrect or absense of ordering of register_pernet_sybsys() and actually using net_generic()) *) to hide the bug even more, default is to preallocate 13 pointers which is actually quite a lot. You need IPv6, netfilter, bridging etc together loaded to trigger reallocation in the first place. Trimmed down config are OK. Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/core/net_namespace.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index a38feac..707b3c25 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -64,9 +64,10 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data) old_ng = rcu_dereference_protected(net->gen, lockdep_is_held(&net_mutex)); - ng = old_ng; - if (old_ng->len >= id) - goto assign; + if (old_ng->len >= id) { + old_ng->ptr[id - 1] = data; + return 0; + } ng = net_alloc_generic(); if (ng == NULL) @@ -84,11 +85,10 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data) */ memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); + ng->ptr[id - 1] = data; rcu_assign_pointer(net->gen, ng); kfree_rcu(old_ng, rcu); -assign: - ng->ptr[id - 1] = data; return 0; } -- cgit v1.1 From 9bfc7b9969dbb800460e2577f1dea59336269ce4 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 2 Dec 2016 04:12:58 +0300 Subject: netns: add dummy struct inside "struct net_generic" This is precursor to fixing "[id - 1]" bloat inside net_generic(). Name "s" is chosen to complement name "u" often used for dummy unions. Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/net/netns/generic.h | 6 ++++-- net/core/net_namespace.c | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/net/netns/generic.h b/include/net/netns/generic.h index d315786..65ccce6 100644 --- a/include/net/netns/generic.h +++ b/include/net/netns/generic.h @@ -25,8 +25,10 @@ */ struct net_generic { - unsigned int len; - struct rcu_head rcu; + struct { + unsigned int len; + struct rcu_head rcu; + } s; void *ptr[0]; }; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 707b3c25..55e28a8 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -50,7 +50,7 @@ static struct net_generic *net_alloc_generic(void) ng = kzalloc(generic_size, GFP_KERNEL); if (ng) - ng->len = max_gen_ptrs; + ng->s.len = max_gen_ptrs; return ng; } @@ -64,7 +64,7 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data) old_ng = rcu_dereference_protected(net->gen, lockdep_is_held(&net_mutex)); - if (old_ng->len >= id) { + if (old_ng->s.len >= id) { old_ng->ptr[id - 1] = data; return 0; } @@ -84,11 +84,11 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data) * the old copy for kfree after a grace period. */ - memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); + memcpy(&ng->ptr, &old_ng->ptr, old_ng->s.len * sizeof(void*)); ng->ptr[id - 1] = data; rcu_assign_pointer(net->gen, ng); - kfree_rcu(old_ng, rcu); + kfree_rcu(old_ng, s.rcu); return 0; } -- cgit v1.1 From 6af2d5fff2fdcd481cb9a4f354a0880142b17c60 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 2 Dec 2016 04:21:32 +0300 Subject: netns: fix net_generic() "id - 1" bloat net_generic() function is both a) inline and b) used ~600 times. It has the following code inside ... ptr = ng->ptr[id - 1]; ... "id" is never compile time constant so compiler is forced to subtract 1. And those decrements or LEA [r32 - 1] instructions add up. We also start id'ing from 1 to catch bugs where pernet sybsystem id is not initialized and 0. This is quite pointless idea (nothing will work or immediate interference with first registered subsystem) in general but it hints what needs to be done for code size reduction. Namely, overlaying allocation of pointer array and fixed part of structure in the beginning and using usual base-0 addressing. Ids are just cookies, their exact values do not matter, so lets start with 3 on x86_64. Code size savings (oh boy): -4.2 KB As usual, ignore the initial compiler stupidity part of the table. add/remove: 0/0 grow/shrink: 12/670 up/down: 89/-4297 (-4208) function old new delta tipc_nametbl_insert_publ 1250 1270 +20 nlmclnt_lookup_host 686 703 +17 nfsd4_encode_fattr 5930 5941 +11 nfs_get_client 1050 1061 +11 register_pernet_operations 333 342 +9 tcf_mirred_init 843 849 +6 tcf_bpf_init 1143 1149 +6 gss_setup_upcall 990 994 +4 idmap_name_to_id 432 434 +2 ops_init 274 275 +1 nfsd_inject_forget_client 259 260 +1 nfs4_alloc_client 612 613 +1 tunnel_key_walker 164 163 -1 ... tipc_bcbase_select_primary 392 360 -32 mac80211_hwsim_new_radio 2808 2767 -41 ipip6_tunnel_ioctl 2228 2186 -42 tipc_bcast_rcv 715 672 -43 tipc_link_build_proto_msg 1140 1089 -51 nfsd4_lock 3851 3796 -55 tipc_mon_rcv 1012 956 -56 Total: Before=156643951, After=156639743, chg -0.00% Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/net/netns/generic.h | 16 +++++++++------- net/core/net_namespace.c | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/net/netns/generic.h b/include/net/netns/generic.h index 65ccce6..f15daaa 100644 --- a/include/net/netns/generic.h +++ b/include/net/netns/generic.h @@ -25,12 +25,14 @@ */ struct net_generic { - struct { - unsigned int len; - struct rcu_head rcu; - } s; - - void *ptr[0]; + union { + struct { + unsigned int len; + struct rcu_head rcu; + } s; + + void *ptr[0]; + }; }; static inline void *net_generic(const struct net *net, unsigned int id) @@ -40,7 +42,7 @@ static inline void *net_generic(const struct net *net, unsigned int id) rcu_read_lock(); ng = rcu_dereference(net->gen); - ptr = ng->ptr[id - 1]; + ptr = ng->ptr[id]; rcu_read_unlock(); return ptr; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 55e28a8..50fdc1b 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -39,6 +39,9 @@ EXPORT_SYMBOL(init_net); static bool init_net_initialized; +#define MIN_PERNET_OPS_ID \ + ((sizeof(struct net_generic) + sizeof(void *) - 1) / sizeof(void *)) + #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS; @@ -46,7 +49,7 @@ static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS; static struct net_generic *net_alloc_generic(void) { struct net_generic *ng; - size_t generic_size = offsetof(struct net_generic, ptr[max_gen_ptrs]); + unsigned int generic_size = offsetof(struct net_generic, ptr[max_gen_ptrs]); ng = kzalloc(generic_size, GFP_KERNEL); if (ng) @@ -60,12 +63,12 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data) struct net_generic *ng, *old_ng; BUG_ON(!mutex_is_locked(&net_mutex)); - BUG_ON(id == 0); + BUG_ON(id < MIN_PERNET_OPS_ID); old_ng = rcu_dereference_protected(net->gen, lockdep_is_held(&net_mutex)); - if (old_ng->s.len >= id) { - old_ng->ptr[id - 1] = data; + if (old_ng->s.len > id) { + old_ng->ptr[id] = data; return 0; } @@ -84,8 +87,9 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data) * the old copy for kfree after a grace period. */ - memcpy(&ng->ptr, &old_ng->ptr, old_ng->s.len * sizeof(void*)); - ng->ptr[id - 1] = data; + memcpy(&ng->ptr[MIN_PERNET_OPS_ID], &old_ng->ptr[MIN_PERNET_OPS_ID], + (old_ng->s.len - MIN_PERNET_OPS_ID) * sizeof(void *)); + ng->ptr[id] = data; rcu_assign_pointer(net->gen, ng); kfree_rcu(old_ng, s.rcu); @@ -874,7 +878,7 @@ static int register_pernet_operations(struct list_head *list, if (ops->id) { again: - error = ida_get_new_above(&net_generic_ids, 1, ops->id); + error = ida_get_new_above(&net_generic_ids, MIN_PERNET_OPS_ID, ops->id); if (error < 0) { if (error == -EAGAIN) { ida_pre_get(&net_generic_ids, GFP_KERNEL); @@ -882,7 +886,7 @@ again: } return error; } - max_gen_ptrs = max(max_gen_ptrs, *ops->id); + max_gen_ptrs = max(max_gen_ptrs, *ops->id + 1); } error = __register_pernet_operations(list, ops); if (error) { -- cgit v1.1 From 69a9d09b2251aeb968e76f39f2b89774312daa3d Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 1 Dec 2016 18:31:12 -0800 Subject: samples/bpf: silence compiler warnings silence some of the clang compiler warnings like: include/linux/fs.h:2693:9: warning: comparison of unsigned enum expression < 0 is always false arch/x86/include/asm/processor.h:491:30: warning: taking address of packed member 'sp0' of class or structure 'x86_hw_tss' may result in an unaligned pointer value include/linux/cgroup-defs.h:326:16: warning: field 'cgrp' with variable sized type 'struct cgroup' not at the end of a struct or class is a GNU extension since they add too much noise to samples/bpf/ build. Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index bfc2cb8..13c3e18 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -168,4 +168,6 @@ $(obj)/%.o: $(src)/%.c $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \ -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ -Wno-compare-distinct-pointer-types \ + -Wno-gnu-variable-sized-type-not-at-end \ + -Wno-address-of-packed-member -Wno-tautological-compare \ -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@ -- cgit v1.1 From 1a922fee66c8a691bfec738b6a5694b2dbb2177d Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Fri, 2 Dec 2016 02:42:18 -0800 Subject: samples, bpf: Refactor test_current_task_under_cgroup - separate out helpers This patch modifies test_current_task_under_cgroup_user. The test has several helpers around creating a temporary environment for cgroup testing, and moving the current task around cgroups. This set of helpers can then be used in other tests. Signed-off-by: Sargun Dhillon Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 2 +- samples/bpf/cgroup_helpers.c | 177 ++++++++++++++++++++++ samples/bpf/cgroup_helpers.h | 16 ++ samples/bpf/test_current_task_under_cgroup_user.c | 108 +++---------- 4 files changed, 218 insertions(+), 85 deletions(-) create mode 100644 samples/bpf/cgroup_helpers.c create mode 100644 samples/bpf/cgroup_helpers.h diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 13c3e18..57d8494 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -59,7 +59,7 @@ test_cgrp2_sock2-objs := bpf_load.o libbpf.o test_cgrp2_sock2.o xdp1-objs := bpf_load.o libbpf.o xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := bpf_load.o libbpf.o xdp1_user.o -test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \ +test_current_task_under_cgroup-objs := bpf_load.o libbpf.o cgroup_helpers.o \ test_current_task_under_cgroup_user.o trace_event-objs := bpf_load.o libbpf.o trace_event_user.o sampleip-objs := bpf_load.o libbpf.o sampleip_user.o diff --git a/samples/bpf/cgroup_helpers.c b/samples/bpf/cgroup_helpers.c new file mode 100644 index 0000000..9d1be94 --- /dev/null +++ b/samples/bpf/cgroup_helpers.c @@ -0,0 +1,177 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "cgroup_helpers.h" + +/* + * To avoid relying on the system setup, when setup_cgroup_env is called + * we create a new mount namespace, and cgroup namespace. The cgroup2 + * root is mounted at CGROUP_MOUNT_PATH + * + * Unfortunately, most people don't have cgroupv2 enabled at this point in time. + * It's easier to create our own mount namespace and manage it ourselves. + * + * We assume /mnt exists. + */ + +#define WALK_FD_LIMIT 16 +#define CGROUP_MOUNT_PATH "/mnt" +#define CGROUP_WORK_DIR "/cgroup-test-work-dir" +#define format_cgroup_path(buf, path) \ + snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \ + CGROUP_WORK_DIR, path) + +/** + * setup_cgroup_environment() - Setup the cgroup environment + * + * After calling this function, cleanup_cgroup_environment should be called + * once testing is complete. + * + * This function will print an error to stderr and return 1 if it is unable + * to setup the cgroup environment. If setup is successful, 0 is returned. + */ +int setup_cgroup_environment(void) +{ + char cgroup_workdir[PATH_MAX + 1]; + + format_cgroup_path(cgroup_workdir, ""); + + if (unshare(CLONE_NEWNS)) { + log_err("unshare"); + return 1; + } + + if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) { + log_err("mount fakeroot"); + return 1; + } + + if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL)) { + log_err("mount cgroup2"); + return 1; + } + + /* Cleanup existing failed runs, now that the environment is setup */ + cleanup_cgroup_environment(); + + if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) { + log_err("mkdir cgroup work dir"); + return 1; + } + + return 0; +} + +static int nftwfunc(const char *filename, const struct stat *statptr, + int fileflags, struct FTW *pfwt) +{ + if ((fileflags & FTW_D) && rmdir(filename)) + log_err("Removing cgroup: %s", filename); + return 0; +} + + +static int join_cgroup_from_top(char *cgroup_path) +{ + char cgroup_procs_path[PATH_MAX + 1]; + pid_t pid = getpid(); + int fd, rc = 0; + + snprintf(cgroup_procs_path, sizeof(cgroup_procs_path), + "%s/cgroup.procs", cgroup_path); + + fd = open(cgroup_procs_path, O_WRONLY); + if (fd < 0) { + log_err("Opening Cgroup Procs: %s", cgroup_procs_path); + return 1; + } + + if (dprintf(fd, "%d\n", pid) < 0) { + log_err("Joining Cgroup"); + rc = 1; + } + + close(fd); + return rc; +} + +/** + * join_cgroup() - Join a cgroup + * @path: The cgroup path, relative to the workdir, to join + * + * This function expects a cgroup to already be created, relative to the cgroup + * work dir, and it joins it. For example, passing "/my-cgroup" as the path + * would actually put the calling process into the cgroup + * "/cgroup-test-work-dir/my-cgroup" + * + * On success, it returns 0, otherwise on failure it returns 1. + */ +int join_cgroup(char *path) +{ + char cgroup_path[PATH_MAX + 1]; + + format_cgroup_path(cgroup_path, path); + return join_cgroup_from_top(cgroup_path); +} + +/** + * cleanup_cgroup_environment() - Cleanup Cgroup Testing Environment + * + * This is an idempotent function to delete all temporary cgroups that + * have been created during the test, including the cgroup testing work + * directory. + * + * At call time, it moves the calling process to the root cgroup, and then + * runs the deletion process. It is idempotent, and should not fail, unless + * a process is lingering. + * + * On failure, it will print an error to stderr, and try to continue. + */ +void cleanup_cgroup_environment(void) +{ + char cgroup_workdir[PATH_MAX + 1]; + + format_cgroup_path(cgroup_workdir, ""); + join_cgroup_from_top(CGROUP_MOUNT_PATH); + nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT); +} + +/** + * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD + * @path: The cgroup path, relative to the workdir, to join + * + * This function creates a cgroup under the top level workdir and returns the + * file descriptor. It is idempotent. + * + * On success, it returns the file descriptor. On failure it returns 0. + * If there is a failure, it prints the error to stderr. + */ +int create_and_get_cgroup(char *path) +{ + char cgroup_path[PATH_MAX + 1]; + int fd; + + format_cgroup_path(cgroup_path, path); + if (mkdir(cgroup_path, 0777) && errno != EEXIST) { + log_err("mkdiring cgroup"); + return 0; + } + + fd = open(cgroup_path, O_RDONLY); + if (fd < 0) { + log_err("Opening Cgroup"); + return 0; + } + + return fd; +} diff --git a/samples/bpf/cgroup_helpers.h b/samples/bpf/cgroup_helpers.h new file mode 100644 index 0000000..78c5520 --- /dev/null +++ b/samples/bpf/cgroup_helpers.h @@ -0,0 +1,16 @@ +#ifndef __CGROUP_HELPERS_H +#define __CGROUP_HELPERS_H +#include +#include + +#define clean_errno() (errno == 0 ? "None" : strerror(errno)) +#define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ + __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) + + +int create_and_get_cgroup(char *path); +int join_cgroup(char *path); +int setup_cgroup_environment(void); +void cleanup_cgroup_environment(void); + +#endif diff --git a/samples/bpf/test_current_task_under_cgroup_user.c b/samples/bpf/test_current_task_under_cgroup_user.c index 30b0bce..95aaaa8 100644 --- a/samples/bpf/test_current_task_under_cgroup_user.c +++ b/samples/bpf/test_current_task_under_cgroup_user.c @@ -11,50 +11,16 @@ #include #include "libbpf.h" #include "bpf_load.h" -#include -#include -#include #include -#include -#include -#include -#include -#include +#include "cgroup_helpers.h" -#define CGROUP_MOUNT_PATH "/mnt" -#define CGROUP_PATH "/mnt/my-cgroup" - -#define clean_errno() (errno == 0 ? "None" : strerror(errno)) -#define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ - __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) - -static int join_cgroup(char *path) -{ - int fd, rc = 0; - pid_t pid = getpid(); - char cgroup_path[PATH_MAX + 1]; - - snprintf(cgroup_path, sizeof(cgroup_path), "%s/cgroup.procs", path); - - fd = open(cgroup_path, O_WRONLY); - if (fd < 0) { - log_err("Opening Cgroup"); - return 1; - } - - if (dprintf(fd, "%d\n", pid) < 0) { - log_err("Joining Cgroup"); - rc = 1; - } - close(fd); - return rc; -} +#define CGROUP_PATH "/my-cgroup" int main(int argc, char **argv) { - char filename[256]; - int cg2, idx = 0; pid_t remote_pid, local_pid = getpid(); + int cg2, idx = 0, rc = 0; + char filename[256]; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); if (load_bpf_file(filename)) { @@ -62,47 +28,22 @@ int main(int argc, char **argv) return 1; } - /* - * This is to avoid interfering with existing cgroups. Unfortunately, - * most people don't have cgroupv2 enabled at this point in time. - * It's easier to create our own mount namespace and manage it - * ourselves. - */ - if (unshare(CLONE_NEWNS)) { - log_err("unshare"); - return 1; - } - - if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) { - log_err("mount fakeroot"); - return 1; - } - - if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL)) { - log_err("mount cgroup2"); - return 1; - } + if (setup_cgroup_environment()) + goto err; - if (mkdir(CGROUP_PATH, 0777) && errno != EEXIST) { - log_err("mkdir cgroup"); - return 1; - } + cg2 = create_and_get_cgroup(CGROUP_PATH); - cg2 = open(CGROUP_PATH, O_RDONLY); - if (cg2 < 0) { - log_err("opening target cgroup"); - goto cleanup_cgroup_err; - } + if (!cg2) + goto err; if (bpf_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) { log_err("Adding target cgroup to map"); - goto cleanup_cgroup_err; - } - if (join_cgroup("/mnt/my-cgroup")) { - log_err("Leaving target cgroup"); - goto cleanup_cgroup_err; + goto err; } + if (join_cgroup(CGROUP_PATH)) + goto err; + /* * The installed helper program catched the sync call, and should * write it to the map. @@ -115,12 +56,12 @@ int main(int argc, char **argv) fprintf(stderr, "BPF Helper didn't write correct PID to map, but: %d\n", remote_pid); - goto leave_cgroup_err; + goto err; } /* Verify the negative scenario; leave the cgroup */ - if (join_cgroup(CGROUP_MOUNT_PATH)) - goto leave_cgroup_err; + if (join_cgroup("/")) + goto err; remote_pid = 0; bpf_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY); @@ -130,16 +71,15 @@ int main(int argc, char **argv) if (local_pid == remote_pid) { fprintf(stderr, "BPF cgroup negative test did not work\n"); - goto cleanup_cgroup_err; + goto err; } - rmdir(CGROUP_PATH); - return 0; + goto out; +err: + rc = 1; - /* Error condition, cleanup */ -leave_cgroup_err: - join_cgroup(CGROUP_MOUNT_PATH); -cleanup_cgroup_err: - rmdir(CGROUP_PATH); - return 1; +out: + close(cg2); + cleanup_cgroup_environment(); + return rc; } -- cgit v1.1 From 9b474ecee5e154d88520306412b2de9d48c97dba Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Fri, 2 Dec 2016 02:42:32 -0800 Subject: samples, bpf: Add automated test for cgroup filter attachments This patch adds the sample program test_cgrp2_attach2. This program is similar to test_cgrp2_attach, but it performs automated testing of the cgroupv2 BPF attached filters. It runs the following checks: * Simple filter attachment * Application of filters to child cgroups * Overriding filters on child cgroups * Checking that this still works when the parent filter is removed The filters that are used here are simply allow all / deny all filters, so it isn't checking the actual functionality of the filters, but rather the behaviour around detachment / attachment. If net_cls is enabled, this test will fail. Signed-off-by: Sargun Dhillon Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 2 + samples/bpf/test_cgrp2_attach2.c | 132 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 samples/bpf/test_cgrp2_attach2.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 57d8494..00cd308 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -23,6 +23,7 @@ hostprogs-y += map_perf_test hostprogs-y += test_overhead hostprogs-y += test_cgrp2_array_pin hostprogs-y += test_cgrp2_attach +hostprogs-y += test_cgrp2_attach2 hostprogs-y += test_cgrp2_sock hostprogs-y += test_cgrp2_sock2 hostprogs-y += xdp1 @@ -54,6 +55,7 @@ map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o +test_cgrp2_attach2-objs := libbpf.o test_cgrp2_attach2.o cgroup_helpers.o test_cgrp2_sock-objs := libbpf.o test_cgrp2_sock.o test_cgrp2_sock2-objs := bpf_load.o libbpf.o test_cgrp2_sock2.o xdp1-objs := bpf_load.o libbpf.o xdp1_user.o diff --git a/samples/bpf/test_cgrp2_attach2.c b/samples/bpf/test_cgrp2_attach2.c new file mode 100644 index 0000000..ddfac42 --- /dev/null +++ b/samples/bpf/test_cgrp2_attach2.c @@ -0,0 +1,132 @@ +/* eBPF example program: + * + * - Creates arraymap in kernel with 4 bytes keys and 8 byte values + * + * - Loads eBPF program + * + * The eBPF program accesses the map passed in to store two pieces of + * information. The number of invocations of the program, which maps + * to the number of packets received, is stored to key 0. Key 1 is + * incremented on each iteration by the number of bytes stored in + * the skb. + * + * - Attaches the new program to a cgroup using BPF_PROG_ATTACH + * + * - Every second, reads map[0] and map[1] to see how many bytes and + * packets were seen on any socket of tasks in the given cgroup. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include + +#include "libbpf.h" +#include "cgroup_helpers.h" + +#define FOO "/foo" +#define BAR "/foo/bar/" +#define PING_CMD "ping -c1 -w1 127.0.0.1" + +static int prog_load(int verdict) +{ + int ret; + struct bpf_insn prog[] = { + BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ + BPF_EXIT_INSN(), + }; + + ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SKB, + prog, sizeof(prog), "GPL", 0); + + if (ret < 0) { + log_err("Loading program"); + printf("Output from verifier:\n%s\n-------\n", bpf_log_buf); + return 0; + } + return ret; +} + + +int main(int argc, char **argv) +{ + int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0; + + allow_prog = prog_load(1); + if (!allow_prog) + goto err; + + drop_prog = prog_load(0); + if (!drop_prog) + goto err; + + if (setup_cgroup_environment()) + goto err; + + /* Create cgroup /foo, get fd, and join it */ + foo = create_and_get_cgroup(FOO); + if (!foo) + goto err; + + if (join_cgroup(FOO)) + goto err; + + if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS)) { + log_err("Attaching prog to /foo"); + goto err; + } + + assert(system(PING_CMD) != 0); + + /* Create cgroup /foo/bar, get fd, and join it */ + bar = create_and_get_cgroup(BAR); + if (!bar) + goto err; + + if (join_cgroup(BAR)) + goto err; + + assert(system(PING_CMD) != 0); + + if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS)) { + log_err("Attaching prog to /foo/bar"); + goto err; + } + + assert(system(PING_CMD) == 0); + + + if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { + log_err("Detaching program from /foo/bar"); + goto err; + } + + assert(system(PING_CMD) != 0); + + if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS)) { + log_err("Attaching prog to /foo/bar"); + goto err; + } + + if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { + log_err("Detaching program from /foo"); + goto err; + } + + assert(system(PING_CMD) == 0); + + goto out; + +err: + rc = 1; + +out: + close(foo); + close(bar); + cleanup_cgroup_environment(); + return rc; +} -- cgit v1.1 From e01b16a7e217a23eb97e08d0a591735d5aee5efc Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Fri, 2 Dec 2016 15:51:33 +0000 Subject: sfc: remove EFX_BUG_ON_PARANOID, use EFX_WARN_ON_[ONCE_]PARANOID instead Logically, EFX_BUG_ON_PARANOID can never be correct. For, BUG_ON should only be used if it is not possible to continue without potential harm; and since the non-DEBUG driver will continue regardless (as the BUG_ON is compiled out), clearly the BUG_ON cannot be needed in the DEBUG driver. So, replace every EFX_BUG_ON_PARANOID with either an EFX_WARN_ON_PARANOID or the newly defined EFX_WARN_ON_ONCE_PARANOID. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 2 +- drivers/net/ethernet/sfc/efx.c | 2 +- drivers/net/ethernet/sfc/ethtool.c | 4 ++-- drivers/net/ethernet/sfc/farch.c | 6 ++--- drivers/net/ethernet/sfc/mcdi.h | 4 ++-- drivers/net/ethernet/sfc/mcdi_mon.c | 4 ++-- drivers/net/ethernet/sfc/mcdi_port.c | 2 +- drivers/net/ethernet/sfc/net_driver.h | 22 +++++++++---------- drivers/net/ethernet/sfc/ptp.c | 2 +- drivers/net/ethernet/sfc/rx.c | 8 +++---- drivers/net/ethernet/sfc/siena.c | 2 +- drivers/net/ethernet/sfc/tx.c | 12 +++++----- drivers/net/ethernet/sfc/tx_tso.c | 41 +++++++++++++++++------------------ 13 files changed, 55 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 0f58ea8..de2947c 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2100,7 +2100,7 @@ static int efx_ef10_tx_tso_desc(struct efx_tx_queue *tx_queue, u32 seqnum; u32 mss; - EFX_BUG_ON_PARANOID(tx_queue->tso_version != 2); + EFX_WARN_ON_ONCE_PARANOID(tx_queue->tso_version != 2); mss = skb_shinfo(skb)->gso_size; diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index da7028d..5a5dcad 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -355,7 +355,7 @@ static int efx_probe_eventq(struct efx_channel *channel) /* Build an event queue with room for one event per tx and rx buffer, * plus some extra for link state events and MCDI completions. */ entries = roundup_pow_of_two(efx->rxq_entries + efx->txq_entries + 128); - EFX_BUG_ON_PARANOID(entries > EFX_MAX_EVQ_SIZE); + EFX_WARN_ON_PARANOID(entries > EFX_MAX_EVQ_SIZE); channel->eventq_mask = max(entries, EFX_MIN_EVQ_SIZE) - 1; return efx_nic_probe_eventq(channel); diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index ca29d3d..f644216 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -333,12 +333,12 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx, "core", 0, "registers", NULL); if (efx->phy_op->run_tests != NULL) { - EFX_BUG_ON_PARANOID(efx->phy_op->test_name == NULL); + EFX_WARN_ON_PARANOID(efx->phy_op->test_name == NULL); for (i = 0; true; ++i) { const char *name; - EFX_BUG_ON_PARANOID(i >= EFX_MAX_PHY_TESTS); + EFX_WARN_ON_PARANOID(i >= EFX_MAX_PHY_TESTS); name = efx->phy_op->test_name(efx, i); if (name == NULL) break; diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c index 91aa3ec..e4ca216 100644 --- a/drivers/net/ethernet/sfc/farch.c +++ b/drivers/net/ethernet/sfc/farch.c @@ -177,7 +177,7 @@ efx_init_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) dma_addr_t dma_addr; int i; - EFX_BUG_ON_PARANOID(!buffer->buf.addr); + EFX_WARN_ON_PARANOID(!buffer->buf.addr); /* Write buffer descriptors to NIC */ for (i = 0; i < buffer->entries; i++) { @@ -332,7 +332,7 @@ void efx_farch_tx_write(struct efx_tx_queue *tx_queue) txd = efx_tx_desc(tx_queue, write_ptr); ++tx_queue->write_count; - EFX_BUG_ON_PARANOID(buffer->flags & EFX_TX_BUF_OPTION); + EFX_WARN_ON_ONCE_PARANOID(buffer->flags & EFX_TX_BUF_OPTION); /* Create TX descriptor ring entry */ BUILD_BUG_ON(EFX_TX_BUF_CONT != 1); @@ -2041,7 +2041,7 @@ efx_farch_filter_from_gen_spec(struct efx_farch_filter_spec *spec, __be32 rhost, host1, host2; __be16 rport, port1, port2; - EFX_BUG_ON_PARANOID(!(gen_spec->flags & EFX_FILTER_FLAG_RX)); + EFX_WARN_ON_PARANOID(!(gen_spec->flags & EFX_FILTER_FLAG_RX)); if (gen_spec->ether_type != htons(ETH_P_IP)) return -EPROTONOSUPPORT; diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index c9aeb07..4472107 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -129,14 +129,14 @@ struct efx_mcdi_data { static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx) { - EFX_BUG_ON_PARANOID(!efx->mcdi); + EFX_WARN_ON_PARANOID(!efx->mcdi); return &efx->mcdi->iface; } #ifdef CONFIG_SFC_MCDI_MON static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx) { - EFX_BUG_ON_PARANOID(!efx->mcdi); + EFX_WARN_ON_PARANOID(!efx->mcdi); return &efx->mcdi->hwmon; } #endif diff --git a/drivers/net/ethernet/sfc/mcdi_mon.c b/drivers/net/ethernet/sfc/mcdi_mon.c index bc27d5b..f97da05 100644 --- a/drivers/net/ethernet/sfc/mcdi_mon.c +++ b/drivers/net/ethernet/sfc/mcdi_mon.c @@ -121,9 +121,9 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev) } if (!name) name = "No sensor name available"; - EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); + EFX_WARN_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); state_txt = sensor_status_names[state]; - EFX_BUG_ON_PARANOID(hwmon_type >= EFX_HWMON_TYPES_COUNT); + EFX_WARN_ON_PARANOID(hwmon_type >= EFX_HWMON_TYPES_COUNT); unit = efx_hwmon_unit[hwmon_type]; if (!unit) unit = ""; diff --git a/drivers/net/ethernet/sfc/mcdi_port.c b/drivers/net/ethernet/sfc/mcdi_port.c index 0f0eb27..9dcd396 100644 --- a/drivers/net/ethernet/sfc/mcdi_port.c +++ b/drivers/net/ethernet/sfc/mcdi_port.c @@ -840,7 +840,7 @@ void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev) u32 flags, fcntl, speed, lpa; speed = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_SPEED); - EFX_BUG_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed)); + EFX_WARN_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed)); speed = efx_mcdi_event_link_speed[speed]; flags = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LINK_FLAGS); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 0bbd7e2..8692e82 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -44,10 +44,10 @@ #define EFX_DRIVER_VERSION "4.1" #ifdef DEBUG -#define EFX_BUG_ON_PARANOID(x) BUG_ON(x) +#define EFX_WARN_ON_ONCE_PARANOID(x) WARN_ON_ONCE(x) #define EFX_WARN_ON_PARANOID(x) WARN_ON(x) #else -#define EFX_BUG_ON_PARANOID(x) do {} while (0) +#define EFX_WARN_ON_ONCE_PARANOID(x) do {} while (0) #define EFX_WARN_ON_PARANOID(x) do {} while (0) #endif @@ -1409,7 +1409,7 @@ struct efx_nic_type { static inline struct efx_channel * efx_get_channel(struct efx_nic *efx, unsigned index) { - EFX_BUG_ON_PARANOID(index >= efx->n_channels); + EFX_WARN_ON_ONCE_PARANOID(index >= efx->n_channels); return efx->channel[index]; } @@ -1430,8 +1430,8 @@ efx_get_channel(struct efx_nic *efx, unsigned index) static inline struct efx_tx_queue * efx_get_tx_queue(struct efx_nic *efx, unsigned index, unsigned type) { - EFX_BUG_ON_PARANOID(index >= efx->n_tx_channels || - type >= EFX_TXQ_TYPES); + EFX_WARN_ON_ONCE_PARANOID(index >= efx->n_tx_channels || + type >= EFX_TXQ_TYPES); return &efx->channel[efx->tx_channel_offset + index]->tx_queue[type]; } @@ -1444,8 +1444,8 @@ static inline bool efx_channel_has_tx_queues(struct efx_channel *channel) static inline struct efx_tx_queue * efx_channel_get_tx_queue(struct efx_channel *channel, unsigned type) { - EFX_BUG_ON_PARANOID(!efx_channel_has_tx_queues(channel) || - type >= EFX_TXQ_TYPES); + EFX_WARN_ON_ONCE_PARANOID(!efx_channel_has_tx_queues(channel) || + type >= EFX_TXQ_TYPES); return &channel->tx_queue[type]; } @@ -1482,7 +1482,7 @@ static inline bool efx_channel_has_rx_queue(struct efx_channel *channel) static inline struct efx_rx_queue * efx_channel_get_rx_queue(struct efx_channel *channel) { - EFX_BUG_ON_PARANOID(!efx_channel_has_rx_queue(channel)); + EFX_WARN_ON_ONCE_PARANOID(!efx_channel_has_rx_queue(channel)); return &channel->rx_queue; } @@ -1578,9 +1578,9 @@ efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue) struct efx_tx_buffer *buffer = __efx_tx_queue_get_insert_buffer(tx_queue); - EFX_BUG_ON_PARANOID(buffer->len); - EFX_BUG_ON_PARANOID(buffer->flags); - EFX_BUG_ON_PARANOID(buffer->unmap_len); + EFX_WARN_ON_ONCE_PARANOID(buffer->len); + EFX_WARN_ON_ONCE_PARANOID(buffer->flags); + EFX_WARN_ON_ONCE_PARANOID(buffer->unmap_len); return buffer; } diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index 77a5364..60cdb97 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c @@ -835,7 +835,7 @@ static int efx_ptp_synchronize(struct efx_nic *efx, unsigned int num_readings) ACCESS_ONCE(*start) = 0; rc = efx_mcdi_rpc_start(efx, MC_CMD_PTP, synch_buf, MC_CMD_PTP_IN_SYNCHRONIZE_LEN); - EFX_BUG_ON_PARANOID(rc); + EFX_WARN_ON_ONCE_PARANOID(rc); /* Wait for start from MCDI (or timeout) */ timeout = jiffies + msecs_to_jiffies(MAX_SYNCHRONISE_WAIT_MS); diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 7893a73..5f4ad4f 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -335,7 +335,7 @@ void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, bool atomic) /* Calculate current fill level, and exit if we don't need to fill */ fill_level = (rx_queue->added_count - rx_queue->removed_count); - EFX_BUG_ON_PARANOID(fill_level > rx_queue->efx->rxq_entries); + EFX_WARN_ON_ONCE_PARANOID(fill_level > rx_queue->efx->rxq_entries); if (fill_level >= rx_queue->fast_fill_trigger) goto out; @@ -347,7 +347,7 @@ void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, bool atomic) batch_size = efx->rx_pages_per_batch * efx->rx_bufs_per_page; space = rx_queue->max_fill - fill_level; - EFX_BUG_ON_PARANOID(space < batch_size); + EFX_WARN_ON_ONCE_PARANOID(space < batch_size); netif_vdbg(rx_queue->efx, rx_status, rx_queue->efx->net_dev, "RX queue %d fast-filling descriptor ring from" @@ -475,7 +475,7 @@ static struct sk_buff *efx_rx_mk_skb(struct efx_channel *channel, return NULL; } - EFX_BUG_ON_PARANOID(rx_buf->len < hdr_len); + EFX_WARN_ON_ONCE_PARANOID(rx_buf->len < hdr_len); memcpy(skb->data + efx->rx_ip_align, eh - efx->rx_prefix_size, efx->rx_prefix_size + hdr_len); @@ -682,7 +682,7 @@ int efx_probe_rx_queue(struct efx_rx_queue *rx_queue) /* Create the smallest power-of-two aligned ring */ entries = max(roundup_pow_of_two(efx->rxq_entries), EFX_MIN_DMAQ_SIZE); - EFX_BUG_ON_PARANOID(entries > EFX_MAX_DMAQ_SIZE); + EFX_WARN_ON_PARANOID(entries > EFX_MAX_DMAQ_SIZE); rx_queue->ptr_mask = entries - 1; netif_dbg(efx, probe, efx->net_dev, diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 0c4a8dd..a3901bc 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -717,7 +717,7 @@ static void siena_mcdi_request(struct efx_nic *efx, unsigned int i; unsigned int inlen_dw = DIV_ROUND_UP(sdu_len, 4); - EFX_BUG_ON_PARANOID(hdr_len != 4); + EFX_WARN_ON_PARANOID(hdr_len != 4); efx_writed(efx, hdr, pdu); diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index f11a36a..3c01514 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -142,7 +142,7 @@ static void efx_tx_maybe_stop_queue(struct efx_tx_queue *txq1) fill_level = max(txq1->insert_count - txq1->old_read_count, txq2->insert_count - txq2->old_read_count); - EFX_BUG_ON_PARANOID(fill_level >= efx->txq_entries); + EFX_WARN_ON_ONCE_PARANOID(fill_level >= efx->txq_entries); if (likely(fill_level < efx->txq_stop_thresh)) { smp_mb(); if (likely(!efx->loopback_selftest)) @@ -158,7 +158,7 @@ static int efx_enqueue_skb_copy(struct efx_tx_queue *tx_queue, u8 *copy_buffer; int rc; - EFX_BUG_ON_PARANOID(copy_len > EFX_TX_CB_SIZE); + EFX_WARN_ON_ONCE_PARANOID(copy_len > EFX_TX_CB_SIZE); buffer = efx_tx_queue_get_insert_buffer(tx_queue); @@ -268,7 +268,7 @@ static void efx_skb_copy_bits_to_pio(struct efx_nic *efx, struct sk_buff *skb, kunmap_atomic(vaddr); } - EFX_BUG_ON_PARANOID(skb_shinfo(skb)->frag_list); + EFX_WARN_ON_ONCE_PARANOID(skb_shinfo(skb)->frag_list); } static int efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, @@ -503,7 +503,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) * size limit. */ if (segments) { - EFX_BUG_ON_PARANOID(!tx_queue->handle_tso); + EFX_WARN_ON_ONCE_PARANOID(!tx_queue->handle_tso); rc = tx_queue->handle_tso(tx_queue, skb, &data_mapped); if (rc == -EINVAL) { rc = efx_tx_tso_fallback(tx_queue, skb); @@ -724,7 +724,7 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) struct efx_tx_queue *txq2; unsigned int pkts_compl = 0, bytes_compl = 0; - EFX_BUG_ON_PARANOID(index > tx_queue->ptr_mask); + EFX_WARN_ON_ONCE_PARANOID(index > tx_queue->ptr_mask); efx_dequeue_buffers(tx_queue, index, &pkts_compl, &bytes_compl); tx_queue->pkts_compl += pkts_compl; @@ -772,7 +772,7 @@ int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) /* Create the smallest power-of-two aligned ring */ entries = max(roundup_pow_of_two(efx->txq_entries), EFX_MIN_DMAQ_SIZE); - EFX_BUG_ON_PARANOID(entries > EFX_MAX_DMAQ_SIZE); + EFX_WARN_ON_PARANOID(entries > EFX_MAX_DMAQ_SIZE); tx_queue->ptr_mask = entries - 1; netif_dbg(efx, probe, efx->net_dev, diff --git a/drivers/net/ethernet/sfc/tx_tso.c b/drivers/net/ethernet/sfc/tx_tso.c index 6032887..e0cbda9 100644 --- a/drivers/net/ethernet/sfc/tx_tso.c +++ b/drivers/net/ethernet/sfc/tx_tso.c @@ -109,15 +109,15 @@ static void efx_tx_queue_insert(struct efx_tx_queue *tx_queue, struct efx_tx_buffer *buffer; unsigned int dma_len; - EFX_BUG_ON_PARANOID(len <= 0); + EFX_WARN_ON_ONCE_PARANOID(len <= 0); while (1) { buffer = efx_tx_queue_get_insert_buffer(tx_queue); ++tx_queue->insert_count; - EFX_BUG_ON_PARANOID(tx_queue->insert_count - - tx_queue->read_count >= - tx_queue->efx->txq_entries); + EFX_WARN_ON_ONCE_PARANOID(tx_queue->insert_count - + tx_queue->read_count >= + tx_queue->efx->txq_entries); buffer->dma_addr = dma_addr; @@ -134,7 +134,7 @@ static void efx_tx_queue_insert(struct efx_tx_queue *tx_queue, len -= dma_len; } - EFX_BUG_ON_PARANOID(!len); + EFX_WARN_ON_ONCE_PARANOID(!len); buffer->len = len; *final_buffer = buffer; } @@ -147,8 +147,8 @@ static __be16 efx_tso_check_protocol(struct sk_buff *skb) { __be16 protocol = skb->protocol; - EFX_BUG_ON_PARANOID(((struct ethhdr *)skb->data)->h_proto != - protocol); + EFX_WARN_ON_ONCE_PARANOID(((struct ethhdr *)skb->data)->h_proto != + protocol); if (protocol == htons(ETH_P_8021Q)) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; @@ -156,19 +156,18 @@ static __be16 efx_tso_check_protocol(struct sk_buff *skb) } if (protocol == htons(ETH_P_IP)) { - EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); + EFX_WARN_ON_ONCE_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); } else { - EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6)); - EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); + EFX_WARN_ON_ONCE_PARANOID(protocol != htons(ETH_P_IPV6)); + EFX_WARN_ON_ONCE_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); } - EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) - + (tcp_hdr(skb)->doff << 2u)) > - skb_headlen(skb)); + EFX_WARN_ON_ONCE_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) + + (tcp_hdr(skb)->doff << 2u)) > + skb_headlen(skb)); return protocol; } - /* Parse the SKB header and initialise state. */ static int tso_start(struct tso_state *st, struct efx_nic *efx, struct efx_tx_queue *tx_queue, @@ -193,9 +192,9 @@ static int tso_start(struct tso_state *st, struct efx_nic *efx, } st->seqnum = ntohl(tcp_hdr(skb)->seq); - EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); - EFX_BUG_ON_PARANOID(tcp_hdr(skb)->syn); - EFX_BUG_ON_PARANOID(tcp_hdr(skb)->rst); + EFX_WARN_ON_ONCE_PARANOID(tcp_hdr(skb)->urg); + EFX_WARN_ON_ONCE_PARANOID(tcp_hdr(skb)->syn); + EFX_WARN_ON_ONCE_PARANOID(tcp_hdr(skb)->rst); st->out_len = skb->len - header_len; @@ -245,8 +244,8 @@ static void tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue, if (st->packet_space == 0) return; - EFX_BUG_ON_PARANOID(st->in_len <= 0); - EFX_BUG_ON_PARANOID(st->packet_space <= 0); + EFX_WARN_ON_ONCE_PARANOID(st->in_len <= 0); + EFX_WARN_ON_ONCE_PARANOID(st->packet_space <= 0); n = min(st->in_len, st->packet_space); @@ -379,7 +378,7 @@ int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, /* Find the packet protocol and sanity-check it */ state.protocol = efx_tso_check_protocol(skb); - EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); + EFX_WARN_ON_ONCE_PARANOID(tx_queue->write_count != tx_queue->insert_count); rc = tso_start(&state, efx, tx_queue, skb); if (rc) @@ -387,7 +386,7 @@ int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, if (likely(state.in_len == 0)) { /* Grab the first payload fragment. */ - EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags < 1); + EFX_WARN_ON_ONCE_PARANOID(skb_shinfo(skb)->nr_frags < 1); frag_i = 0; rc = tso_get_fragment(&state, efx, skb_shinfo(skb)->frags + frag_i); -- cgit v1.1 From 12efa1fa43968fcb707e806dc119df499c17ac2c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 2 Dec 2016 08:11:00 -0800 Subject: net_sched: gen_estimator: account for timer drifts Under heavy stress, timer used in estimators tend to slowly be delayed by a few jiffies, leading to inaccuracies. Lets remember what was the last scheduled jiffies so that we get more precise estimations, without having to add a multiply/divide in the loop to account for the drifts. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/gen_estimator.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index cad8e79..0993844 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -78,8 +78,7 @@ #define EST_MAX_INTERVAL 5 -struct gen_estimator -{ +struct gen_estimator { struct list_head list; struct gnet_stats_basic_packed *bstats; struct gnet_stats_rate_est64 *rate_est; @@ -96,8 +95,8 @@ struct gen_estimator struct rcu_head head; }; -struct gen_estimator_head -{ +struct gen_estimator_head { + unsigned long next_jiffies; struct timer_list timer; struct list_head list; }; @@ -146,8 +145,15 @@ skip: spin_unlock(e->stats_lock); } - if (!list_empty(&elist[idx].list)) - mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx)); + if (!list_empty(&elist[idx].list)) { + elist[idx].next_jiffies += ((HZ/4) << idx); + + if (unlikely(time_after_eq(jiffies, elist[idx].next_jiffies))) { + /* Ouch... timer was delayed. */ + elist[idx].next_jiffies = jiffies + 1; + } + mod_timer(&elist[idx].timer, elist[idx].next_jiffies); + } rcu_read_unlock(); } @@ -251,9 +257,10 @@ int gen_new_estimator(struct gnet_stats_basic_packed *bstats, setup_timer(&elist[idx].timer, est_timer, idx); } - if (list_empty(&elist[idx].list)) - mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx)); - + if (list_empty(&elist[idx].list)) { + elist[idx].next_jiffies = jiffies + ((HZ/4) << idx); + mod_timer(&elist[idx].timer, elist[idx].next_jiffies); + } list_add_rcu(&est->list, &elist[idx].list); gen_add_node(est); spin_unlock_bh(&est_tree_lock); -- cgit v1.1 From 363dc73acacbbcdae98acf5612303e9770e04b1d Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 2 Dec 2016 17:35:49 +0100 Subject: udp: be less conservative with sock rmem accounting Before commit 850cbaddb52d ("udp: use it's own memory accounting schema"), the udp protocol allowed sk_rmem_alloc to grow beyond the rcvbuf by the whole current packet's truesize. After said commit we allow sk_rmem_alloc to exceed the rcvbuf only if the receive queue is empty. As reported by Jesper this cause a performance regression for some (small) values of rcvbuf. This commit is intended to fix the regression restoring the old handling of the rcvbuf limit. Reported-by: Jesper Dangaard Brouer Fixes: 850cbaddb52d ("udp: use it's own memory accounting schema") Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- net/ipv4/udp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index e1d0bf8..16d88ba 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1205,14 +1205,14 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) * queue is full; always allow at least a packet */ rmem = atomic_read(&sk->sk_rmem_alloc); - if (rmem && (rmem + size > sk->sk_rcvbuf)) + if (rmem > sk->sk_rcvbuf) goto drop; /* we drop only if the receive buf is full and the receive * queue contains some other skb */ rmem = atomic_add_return(size, &sk->sk_rmem_alloc); - if ((rmem > sk->sk_rcvbuf) && (rmem > size)) + if (rmem > (size + sk->sk_rcvbuf)) goto uncharge_drop; spin_lock(&list->lock); -- cgit v1.1 From 2bb148785ed75435eb39529d74e63b5e602ccd61 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Sat, 3 Dec 2016 14:46:22 -0800 Subject: tcp: fix the missing avr32 SOF_TIMESTAMPING_OPT_STATS The commit of SOF_TIMESTAMPING_OPT_STATS didn't include the new header for avr32, causing build to break. The patch fixes it. Fixes: 1c885808e456 ("tcp: SOF_TIMESTAMPING_OPT_STATS option for SO_TIMESTAMPING") Reported-by: Paul Gortmaker Signed-off-by: Yuchung Cheng Signed-off-by: David S. Miller --- arch/avr32/include/uapi/asm/socket.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index 1fd147f..5a65042 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -90,4 +90,6 @@ #define SO_CNX_ADVICE 53 +#define SCM_TIMESTAMPING_OPT_STATS 54 + #endif /* _UAPI__ASM_AVR32_SOCKET_H */ -- cgit v1.1 From 548ed722465b763841252bf74b7eda52cfc9131c Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sat, 3 Dec 2016 10:36:01 -0800 Subject: act_mirred: fix a typo in get_dev Fixes: 255cb30425c0 ("net/sched: act_mirred: Add new tc_action_ops get_dev()") Cc: Hadar Hen Zion Cc: Jiri Pirko Signed-off-by: Cong Wang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/act_mirred.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index bb09ba3..2d9fa6e 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -321,7 +321,7 @@ static int tcf_mirred_device(const struct tc_action *a, struct net *net, int ifindex = tcf_mirred_ifindex(a); *mirred_dev = __dev_get_by_index(net, ifindex); - if (!mirred_dev) + if (!*mirred_dev) return -EINVAL; return 0; } -- cgit v1.1 From b423cb10807b3a24b909c40f4372df88118a9875 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:44:58 +0100 Subject: ipv4: fib: Export free_fib_info() The FIB notification chain is going to be converted to an atomic chain, which means switchdev drivers will have to offload FIB entries in deferred work, as hardware operations entail sleeping. However, while the work is queued fib info might be freed, so a reference must be taken. To release the reference (and potentially free the fib info) fib_info_put() will be called, which in turn calls free_fib_info(). Export free_fib_info() so that modules will be able to invoke fib_info_put(). Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/ipv4/fib_semantics.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 388d3e2..c1bc1e9 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -234,6 +234,7 @@ void free_fib_info(struct fib_info *fi) #endif call_rcu(&fi->rcu, free_fib_info_rcu); } +EXPORT_SYMBOL_GPL(free_fib_info); void fib_release_info(struct fib_info *fi) { -- cgit v1.1 From 1c677b3d2828a84e0360e1a07d1204531540dc03 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:44:59 +0100 Subject: ipv4: fib: Add fib_info_hold() helper As explained in the previous commit, modules are going to need to take a reference on fib info and then drop it using fib_info_put(). Add the fib_info_hold() helper to make the code more readable and also symmetric with fib_info_put(). Signed-off-by: Ido Schimmel Suggested-by: Jiri Pirko Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip_fib.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index f390c3b..6c67b93 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -397,6 +397,11 @@ static inline void fib_combine_itag(u32 *itag, const struct fib_result *res) void free_fib_info(struct fib_info *fi); +static inline void fib_info_hold(struct fib_info *fi) +{ + atomic_inc(&fi->fib_clntref); +} + static inline void fib_info_put(struct fib_info *fi) { if (atomic_dec_and_test(&fi->fib_clntref)) -- cgit v1.1 From a3832b31898f6936816257c020b1d4f1a07622f7 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:00 +0100 Subject: mlxsw: core: Create an ordered workqueue for FIB offload We're going to start processing FIB entries addition / deletion events in deferred work. These work items must be processed in the order they were submitted or otherwise we can have differences between the kernel's FIB table and the device's. Solve this by creating an ordered workqueue to which these work items will be submitted to. Note that we can't simply convert the current workqueue to be ordered, as EMADs re-transmissions are also processed in deferred work. Later on, we can migrate other work items to this workqueue, such as FDB notification processing and nexthop resolution, since they all take the same lock anyway. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 22 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/core.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 4dc028b..57a9884 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -77,6 +77,7 @@ static const char mlxsw_core_driver_name[] = "mlxsw_core"; static struct dentry *mlxsw_core_dbg_root; static struct workqueue_struct *mlxsw_wq; +static struct workqueue_struct *mlxsw_owq; struct mlxsw_core_pcpu_stats { u64 trap_rx_packets[MLXSW_TRAP_ID_MAX]; @@ -1900,6 +1901,18 @@ int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay) } EXPORT_SYMBOL(mlxsw_core_schedule_dw); +int mlxsw_core_schedule_odw(struct delayed_work *dwork, unsigned long delay) +{ + return queue_delayed_work(mlxsw_owq, dwork, delay); +} +EXPORT_SYMBOL(mlxsw_core_schedule_odw); + +void mlxsw_core_flush_owq(void) +{ + flush_workqueue(mlxsw_owq); +} +EXPORT_SYMBOL(mlxsw_core_flush_owq); + static int __init mlxsw_core_module_init(void) { int err; @@ -1907,6 +1920,12 @@ static int __init mlxsw_core_module_init(void) mlxsw_wq = alloc_workqueue(mlxsw_core_driver_name, WQ_MEM_RECLAIM, 0); if (!mlxsw_wq) return -ENOMEM; + mlxsw_owq = alloc_ordered_workqueue("%s_ordered", WQ_MEM_RECLAIM, + mlxsw_core_driver_name); + if (!mlxsw_owq) { + err = -ENOMEM; + goto err_alloc_ordered_workqueue; + } mlxsw_core_dbg_root = debugfs_create_dir(mlxsw_core_driver_name, NULL); if (!mlxsw_core_dbg_root) { err = -ENOMEM; @@ -1915,6 +1934,8 @@ static int __init mlxsw_core_module_init(void) return 0; err_debugfs_create_dir: + destroy_workqueue(mlxsw_owq); +err_alloc_ordered_workqueue: destroy_workqueue(mlxsw_wq); return err; } @@ -1922,6 +1943,7 @@ err_debugfs_create_dir: static void __exit mlxsw_core_module_exit(void) { debugfs_remove_recursive(mlxsw_core_dbg_root); + destroy_workqueue(mlxsw_owq); destroy_workqueue(mlxsw_wq); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index e856b49..a7f94fb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -207,6 +207,8 @@ enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core, u8 local_port); int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay); +int mlxsw_core_schedule_odw(struct delayed_work *dwork, unsigned long delay); +void mlxsw_core_flush_owq(void); #define MLXSW_CONFIG_PROFILE_SWID_COUNT 8 -- cgit v1.1 From 3057224e014c14921de5ab534d5147eb109fd12b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:01 +0100 Subject: mlxsw: spectrum_router: Implement FIB offload in deferred work FIB offload is currently done in process context with RTNL held, but we're about to dump the FIB tables in RCU critical section, so we can no longer sleep. Instead, defer the operation to process context using deferred work. Make sure fib info isn't freed while the work is queued by taking a reference on it and releasing it after the operation is done. Deferring the operation is valid because the upper layers always assume the operation was successful. If it's not, then the driver-specific abort mechanism is called and all routed traffic is directed to slow path. The work items are submitted to an ordered workqueue to prevent a mismatch between the kernel's FIB table and the device's. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 72 +++++++++++++++++++--- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 683f045..14bed1d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -593,6 +593,14 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp); static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp) { + /* At this stage we're guaranteed not to have new incoming + * FIB notifications and the work queue is free from FIBs + * sitting on top of mlxsw netdevs. However, we can still + * have other FIBs queued. Flush the queue before flushing + * the device's tables. No need for locks, as we're the only + * writer. + */ + mlxsw_core_flush_owq(); mlxsw_sp_router_fib_flush(mlxsw_sp); kfree(mlxsw_sp->router.vrs); } @@ -1948,30 +1956,74 @@ static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->rifs); } -static int mlxsw_sp_router_fib_event(struct notifier_block *nb, - unsigned long event, void *ptr) +struct mlxsw_sp_fib_event_work { + struct delayed_work dw; + struct fib_entry_notifier_info fen_info; + struct mlxsw_sp *mlxsw_sp; + unsigned long event; +}; + +static void mlxsw_sp_router_fib_event_work(struct work_struct *work) { - struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb); - struct fib_entry_notifier_info *fen_info = ptr; + struct mlxsw_sp_fib_event_work *fib_work = + container_of(work, struct mlxsw_sp_fib_event_work, dw.work); + struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; int err; - if (!net_eq(fen_info->info.net, &init_net)) - return NOTIFY_DONE; - - switch (event) { + /* Protect internal structures from changes */ + rtnl_lock(); + switch (fib_work->event) { case FIB_EVENT_ENTRY_ADD: - err = mlxsw_sp_router_fib4_add(mlxsw_sp, fen_info); + err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info); if (err) mlxsw_sp_router_fib4_abort(mlxsw_sp); + fib_info_put(fib_work->fen_info.fi); break; case FIB_EVENT_ENTRY_DEL: - mlxsw_sp_router_fib4_del(mlxsw_sp, fen_info); + mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info); + fib_info_put(fib_work->fen_info.fi); break; case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: mlxsw_sp_router_fib4_abort(mlxsw_sp); break; } + rtnl_unlock(); + kfree(fib_work); +} + +/* Called with rcu_read_lock() */ +static int mlxsw_sp_router_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb); + struct mlxsw_sp_fib_event_work *fib_work; + struct fib_notifier_info *info = ptr; + + if (!net_eq(info->net, &init_net)) + return NOTIFY_DONE; + + fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); + if (WARN_ON(!fib_work)) + return NOTIFY_BAD; + + INIT_DELAYED_WORK(&fib_work->dw, mlxsw_sp_router_fib_event_work); + fib_work->mlxsw_sp = mlxsw_sp; + fib_work->event = event; + + switch (event) { + case FIB_EVENT_ENTRY_ADD: /* fall through */ + case FIB_EVENT_ENTRY_DEL: + memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info)); + /* Take referece on fib_info to prevent it from being + * freed while work is queued. Release it afterwards. + */ + fib_info_hold(fib_work->fen_info.fi); + break; + } + + mlxsw_core_schedule_odw(&fib_work->dw, 0); + return NOTIFY_DONE; } -- cgit v1.1 From c1bb279cfa9d079bdee35402f5d13210f25f5c80 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:02 +0100 Subject: rocker: Create an ordered workqueue for FIB offload As explained in the previous commits, we need to process FIB entries addition / deletion events in FIFO order or otherwise we can have a mismatch between the kernel's FIB table and the device's. Create an ordered workqueue for rocker to which these work items will be submitted to. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker.h | 1 + drivers/net/ethernet/rocker/rocker_main.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index 2eb9b49..ee9675d 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -72,6 +72,7 @@ struct rocker { struct rocker_dma_ring_info event_ring; struct notifier_block fib_nb; struct rocker_world_ops *wops; + struct workqueue_struct *rocker_owq; void *wpriv; }; diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 67df4cf..424be96 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -2754,6 +2755,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_request_event_irq; } + rocker->rocker_owq = alloc_ordered_workqueue(rocker_driver_name, + WQ_MEM_RECLAIM); + if (!rocker->rocker_owq) { + err = -ENOMEM; + goto err_alloc_ordered_workqueue; + } + rocker->hw.id = rocker_read64(rocker, SWITCH_ID); err = rocker_probe_ports(rocker); @@ -2771,6 +2779,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_probe_ports: + destroy_workqueue(rocker->rocker_owq); +err_alloc_ordered_workqueue: free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); err_request_event_irq: free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker); @@ -2799,6 +2809,7 @@ static void rocker_remove(struct pci_dev *pdev) unregister_fib_notifier(&rocker->fib_nb); rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); rocker_remove_ports(rocker); + destroy_workqueue(rocker->rocker_owq); free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker); rocker_dma_rings_fini(rocker); -- cgit v1.1 From db7019557cb48508f3ff9d6b40c2e967702897a6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:03 +0100 Subject: rocker: Implement FIB offload in deferred work Convert rocker to offload FIBs in deferred work in a similar fashion to mlxsw, which was converted in the previous commits. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_main.c | 58 +++++++++++++++++++++++++----- drivers/net/ethernet/rocker/rocker_ofdpa.c | 1 + 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 424be96..914e9e1 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2166,28 +2166,70 @@ static const struct switchdev_ops rocker_port_switchdev_ops = { .switchdev_port_obj_dump = rocker_port_obj_dump, }; -static int rocker_router_fib_event(struct notifier_block *nb, - unsigned long event, void *ptr) +struct rocker_fib_event_work { + struct work_struct work; + struct fib_entry_notifier_info fen_info; + struct rocker *rocker; + unsigned long event; +}; + +static void rocker_router_fib_event_work(struct work_struct *work) { - struct rocker *rocker = container_of(nb, struct rocker, fib_nb); - struct fib_entry_notifier_info *fen_info = ptr; + struct rocker_fib_event_work *fib_work = + container_of(work, struct rocker_fib_event_work, work); + struct rocker *rocker = fib_work->rocker; int err; - switch (event) { + /* Protect internal structures from changes */ + rtnl_lock(); + switch (fib_work->event) { case FIB_EVENT_ENTRY_ADD: - err = rocker_world_fib4_add(rocker, fen_info); + err = rocker_world_fib4_add(rocker, &fib_work->fen_info); if (err) rocker_world_fib4_abort(rocker); - else + fib_info_put(fib_work->fen_info.fi); break; case FIB_EVENT_ENTRY_DEL: - rocker_world_fib4_del(rocker, fen_info); + rocker_world_fib4_del(rocker, &fib_work->fen_info); + fib_info_put(fib_work->fen_info.fi); break; case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: rocker_world_fib4_abort(rocker); break; } + rtnl_unlock(); + kfree(fib_work); +} + +/* Called with rcu_read_lock() */ +static int rocker_router_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct rocker *rocker = container_of(nb, struct rocker, fib_nb); + struct rocker_fib_event_work *fib_work; + + fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); + if (WARN_ON(!fib_work)) + return NOTIFY_BAD; + + INIT_WORK(&fib_work->work, rocker_router_fib_event_work); + fib_work->rocker = rocker; + fib_work->event = event; + + switch (event) { + case FIB_EVENT_ENTRY_ADD: /* fall through */ + case FIB_EVENT_ENTRY_DEL: + memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info)); + /* Take referece on fib_info to prevent it from being + * freed while work is queued. Release it afterwards. + */ + fib_info_hold(fib_work->fen_info.fi); + break; + } + + queue_work(rocker->rocker_owq, &fib_work->work); + return NOTIFY_DONE; } diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 4ca4613..7cd76b6 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -2516,6 +2516,7 @@ static void ofdpa_fini(struct rocker *rocker) int bkt; del_timer_sync(&ofdpa->fdb_cleanup_timer); + flush_workqueue(rocker->rocker_owq); spin_lock_irqsave(&ofdpa->flow_tbl_lock, flags); hash_for_each_safe(ofdpa->flow_tbl, bkt, tmp, flow_entry, entry) -- cgit v1.1 From 17f8be7dafce4e4b92fbe7bfec605944df290546 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:04 +0100 Subject: rocker: Register FIB notifier before creating ports We can miss FIB notifications sent between the time the ports were created and the FIB notification block registered. Instead of receiving these notifications only when they are replayed for the FIB notification block during registration, just register the notification block before the ports are created. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 914e9e1..8c9c90a 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2804,6 +2804,9 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_alloc_ordered_workqueue; } + rocker->fib_nb.notifier_call = rocker_router_fib_event; + register_fib_notifier(&rocker->fib_nb); + rocker->hw.id = rocker_read64(rocker, SWITCH_ID); err = rocker_probe_ports(rocker); @@ -2812,15 +2815,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_probe_ports; } - rocker->fib_nb.notifier_call = rocker_router_fib_event; - register_fib_notifier(&rocker->fib_nb); - dev_info(&pdev->dev, "Rocker switch with id %*phN\n", (int)sizeof(rocker->hw.id), &rocker->hw.id); return 0; err_probe_ports: + unregister_fib_notifier(&rocker->fib_nb); destroy_workqueue(rocker->rocker_owq); err_alloc_ordered_workqueue: free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); @@ -2848,9 +2849,9 @@ static void rocker_remove(struct pci_dev *pdev) { struct rocker *rocker = pci_get_drvdata(pdev); + rocker_remove_ports(rocker); unregister_fib_notifier(&rocker->fib_nb); rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); - rocker_remove_ports(rocker); destroy_workqueue(rocker->rocker_owq); free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker); -- cgit v1.1 From d3f706f68e2fd93f1172fe7fd6f16ba70cc52b31 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:05 +0100 Subject: ipv4: fib: Convert FIB notification chain to be atomic In order not to hold RTNL for long periods of time we're going to dump the FIB tables using RCU. Convert the FIB notification chain to be atomic, as we can't block in RCU critical sections. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 026f309..9bfce0d 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -84,17 +84,17 @@ #include #include "fib_lookup.h" -static BLOCKING_NOTIFIER_HEAD(fib_chain); +static ATOMIC_NOTIFIER_HEAD(fib_chain); int register_fib_notifier(struct notifier_block *nb) { - return blocking_notifier_chain_register(&fib_chain, nb); + return atomic_notifier_chain_register(&fib_chain, nb); } EXPORT_SYMBOL(register_fib_notifier); int unregister_fib_notifier(struct notifier_block *nb) { - return blocking_notifier_chain_unregister(&fib_chain, nb); + return atomic_notifier_chain_unregister(&fib_chain, nb); } EXPORT_SYMBOL(unregister_fib_notifier); @@ -102,7 +102,7 @@ int call_fib_notifiers(struct net *net, enum fib_event_type event_type, struct fib_notifier_info *info) { info->net = net; - return blocking_notifier_call_chain(&fib_chain, event_type, info); + return atomic_notifier_call_chain(&fib_chain, event_type, info); } static int call_fib_entry_notifiers(struct net *net, -- cgit v1.1 From cacaad11f43aefbbe5fca00af3b9c16e6aee1ba4 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:06 +0100 Subject: ipv4: fib: Allow for consistent FIB dumping The next patch will enable listeners of the FIB notification chain to request a dump of the FIB tables. However, since RTNL isn't taken during the dump, it's possible for the FIB tables to change mid-dump, which will result in inconsistency between the listener's table and the kernel's. Allow listeners to know about changes that occurred mid-dump, by adding a change sequence counter to each net namespace. The counter is incremented just before a notification is sent in the FIB chain. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 3 +++ net/ipv4/fib_frontend.c | 2 ++ net/ipv4/fib_trie.c | 1 + 3 files changed, 6 insertions(+) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 7adf438..f0cf5a1 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -135,6 +135,9 @@ struct netns_ipv4 { #ifdef CONFIG_IP_ROUTE_MULTIPATH int sysctl_fib_multipath_use_neigh; #endif + + unsigned int fib_seq; /* protected by rtnl_mutex */ + atomic_t rt_genid; }; #endif diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 121384b..dbad5a1 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1219,6 +1219,8 @@ static int __net_init ip_fib_net_init(struct net *net) int err; size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ; + net->ipv4.fib_seq = 0; + /* Avoid false sharing : Use at least a full cache line */ size = max_t(size_t, size, L1_CACHE_BYTES); diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 9bfce0d..2891356 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -101,6 +101,7 @@ EXPORT_SYMBOL(unregister_fib_notifier); int call_fib_notifiers(struct net *net, enum fib_event_type event_type, struct fib_notifier_info *info) { + net->ipv4.fib_seq++; info->net = net; return atomic_notifier_call_chain(&fib_chain, event_type, info); } -- cgit v1.1 From c3852ef7f2f8f75a9f85a864bec1f6f5a3068eea Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 3 Dec 2016 16:45:07 +0100 Subject: ipv4: fib: Replay events when registering FIB notifier Commit b90eb7549499 ("fib: introduce FIB notification infrastructure") introduced a new notification chain to notify listeners (f.e., switchdev drivers) about addition and deletion of routes. However, upon registration to the chain the FIB tables can already be populated, which means potential listeners will have an incomplete view of the tables. Solve that by dumping the FIB tables and replaying the events to the passed notification block. The dump itself is done using RCU in order not to starve consumers that need RTNL to make progress. The integrity of the dump is ensured by reading the FIB change sequence counter before and after the dump under RTNL. This allows us to avoid the problematic situation in which the dumping process sends a ENTRY_ADD notification following ENTRY_DEL generated by another process holding RTNL. Callers of the registration function may pass a callback that is executed in case the dump was inconsistent with current FIB tables. The number of retries until a consistent dump is achieved is set to a fixed number to prevent callers from looping for long periods of time. In case current limit proves to be problematic in the future, it can be easily converted to be configurable using a sysctl. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 20 ++- drivers/net/ethernet/rocker/rocker_main.c | 8 +- include/net/ip_fib.h | 3 +- net/ipv4/fib_trie.c | 148 ++++++++++++++++++++- 4 files changed, 174 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 14bed1d..53126bf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2027,6 +2027,18 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, return NOTIFY_DONE; } +static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) +{ + struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb); + + /* Flush pending FIB notifications and then flush the device's + * table before requesting another dump. The FIB notification + * block is unregistered, so no need to take RTNL. + */ + mlxsw_core_flush_owq(); + mlxsw_sp_router_fib_flush(mlxsw_sp); +} + int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) { int err; @@ -2047,9 +2059,15 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) goto err_neigh_init; mlxsw_sp->fib_nb.notifier_call = mlxsw_sp_router_fib_event; - register_fib_notifier(&mlxsw_sp->fib_nb); + err = register_fib_notifier(&mlxsw_sp->fib_nb, + mlxsw_sp_router_fib_dump_flush); + if (err) + goto err_register_fib_notifier; + return 0; +err_register_fib_notifier: + mlxsw_sp_neigh_fini(mlxsw_sp); err_neigh_init: mlxsw_sp_vrs_fini(mlxsw_sp); err_vrs_init: diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 8c9c90a..7c450b5 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2804,8 +2804,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_alloc_ordered_workqueue; } + /* Only FIBs pointing to our own netdevs are programmed into + * the device, so no need to pass a callback. + */ rocker->fib_nb.notifier_call = rocker_router_fib_event; - register_fib_notifier(&rocker->fib_nb); + err = register_fib_notifier(&rocker->fib_nb, NULL); + if (err) + goto err_register_fib_notifier; rocker->hw.id = rocker_read64(rocker, SWITCH_ID); @@ -2822,6 +2827,7 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) err_probe_ports: unregister_fib_notifier(&rocker->fib_nb); +err_register_fib_notifier: destroy_workqueue(rocker->rocker_owq); err_alloc_ordered_workqueue: free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 6c67b93..5f376af 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -221,7 +221,8 @@ enum fib_event_type { FIB_EVENT_RULE_DEL, }; -int register_fib_notifier(struct notifier_block *nb); +int register_fib_notifier(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb)); int unregister_fib_notifier(struct notifier_block *nb); int call_fib_notifiers(struct net *net, enum fib_event_type event_type, struct fib_notifier_info *info); diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 2891356..73a6270 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -84,11 +84,99 @@ #include #include "fib_lookup.h" +static unsigned int fib_seq_sum(void) +{ + unsigned int fib_seq = 0; + struct net *net; + + rtnl_lock(); + for_each_net(net) + fib_seq += net->ipv4.fib_seq; + rtnl_unlock(); + + return fib_seq; +} + static ATOMIC_NOTIFIER_HEAD(fib_chain); -int register_fib_notifier(struct notifier_block *nb) +static int call_fib_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info) { - return atomic_notifier_chain_register(&fib_chain, nb); + info->net = net; + return nb->notifier_call(nb, event_type, info); +} + +static void fib_rules_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type) +{ +#ifdef CONFIG_IP_MULTIPLE_TABLES + struct fib_notifier_info info; + + if (net->ipv4.fib_has_custom_rules) + call_fib_notifier(nb, net, event_type, &info); +#endif +} + +static void fib_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type); + +static int call_fib_entry_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, u32 dst, + int dst_len, struct fib_info *fi, + u8 tos, u8 type, u32 tb_id, u32 nlflags) +{ + struct fib_entry_notifier_info info = { + .dst = dst, + .dst_len = dst_len, + .fi = fi, + .tos = tos, + .type = type, + .tb_id = tb_id, + .nlflags = nlflags, + }; + return call_fib_notifier(nb, net, event_type, &info.info); +} + +static bool fib_dump_is_consistent(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb), + unsigned int fib_seq) +{ + atomic_notifier_chain_register(&fib_chain, nb); + if (fib_seq == fib_seq_sum()) + return true; + atomic_notifier_chain_unregister(&fib_chain, nb); + if (cb) + cb(nb); + return false; +} + +#define FIB_DUMP_MAX_RETRIES 5 +int register_fib_notifier(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb)) +{ + int retries = 0; + + do { + unsigned int fib_seq = fib_seq_sum(); + struct net *net; + + /* Mutex semantics guarantee that every change done to + * FIB tries before we read the change sequence counter + * is now visible to us. + */ + rcu_read_lock(); + for_each_net_rcu(net) { + fib_rules_notify(net, nb, FIB_EVENT_RULE_ADD); + fib_notify(net, nb, FIB_EVENT_ENTRY_ADD); + } + rcu_read_unlock(); + + if (fib_dump_is_consistent(nb, cb, fib_seq)) + return 0; + } while (++retries < FIB_DUMP_MAX_RETRIES); + + return -EBUSY; } EXPORT_SYMBOL(register_fib_notifier); @@ -1902,6 +1990,62 @@ int fib_table_flush(struct net *net, struct fib_table *tb) return found; } +static void fib_leaf_notify(struct net *net, struct key_vector *l, + struct fib_table *tb, struct notifier_block *nb, + enum fib_event_type event_type) +{ + struct fib_alias *fa; + + hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { + struct fib_info *fi = fa->fa_info; + + if (!fi) + continue; + + /* local and main table can share the same trie, + * so don't notify twice for the same entry. + */ + if (tb->tb_id != fa->tb_id) + continue; + + call_fib_entry_notifier(nb, net, event_type, l->key, + KEYLENGTH - fa->fa_slen, fi, fa->fa_tos, + fa->fa_type, fa->tb_id, 0); + } +} + +static void fib_table_notify(struct net *net, struct fib_table *tb, + struct notifier_block *nb, + enum fib_event_type event_type) +{ + struct trie *t = (struct trie *)tb->tb_data; + struct key_vector *l, *tp = t->kv; + t_key key = 0; + + while ((l = leaf_walk_rcu(&tp, key)) != NULL) { + fib_leaf_notify(net, l, tb, nb, event_type); + + key = l->key + 1; + /* stop in case of wrap around */ + if (key < l->key) + break; + } +} + +static void fib_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type) +{ + unsigned int h; + + for (h = 0; h < FIB_TABLE_HASHSZ; h++) { + struct hlist_head *head = &net->ipv4.fib_table_hash[h]; + struct fib_table *tb; + + hlist_for_each_entry_rcu(tb, head, tb_hlist) + fib_table_notify(net, tb, nb, event_type); + } +} + static void __trie_free_rcu(struct rcu_head *head) { struct fib_table *tb = container_of(head, struct fib_table, rcu); -- cgit v1.1 From ef0a731882a2bf120511072a08d973f65d00d979 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:35:16 +0100 Subject: net: dsa: mv88e6xxx: Implement mv88e6390 tag remap The mv88e6390 does not have the two registers to set the frame priority map. Instead it has an indirection registers for setting a number of different priority maps. Refactor the old code into an function, implement the mv88e6390 version, and use an op to call the right one. Signed-off-by: Andrew Lunn Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 37 ++++++++++++-------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 12 +++++++ drivers/net/dsa/mv88e6xxx/port.c | 63 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 4 files changed, 101 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ce2f7ff..ff4bd2f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2617,20 +2617,10 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; } + } - /* Tag Remap: use an identity 802.1p prio -> switch - * prio mapping. - */ - err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, - 0x3210); - if (err) - return err; - - /* Tag Remap 2: use an identity 802.1p prio -> switch - * prio mapping. - */ - err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, - 0x7654); + if (chip->info->ops->port_tag_remap) { + err = chip->info->ops->port_tag_remap(chip, port); if (err) return err; } @@ -3189,6 +3179,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3217,6 +3208,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3245,6 +3237,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3259,6 +3252,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3288,6 +3282,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3305,6 +3300,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3320,6 +3316,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3337,6 +3334,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3366,6 +3364,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_tag_remap = mv88e6390_port_tag_remap, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3382,6 +3381,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, + .port_tag_remap = mv88e6390_port_tag_remap, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3398,6 +3398,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_tag_remap = mv88e6390_port_tag_remap, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3416,6 +3417,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3431,6 +3433,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_tag_remap = mv88e6390_port_tag_remap, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3448,6 +3451,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3464,6 +3468,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3479,6 +3484,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3494,6 +3500,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3511,6 +3518,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3526,6 +3534,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_tag_remap = mv88e6390_port_tag_remap, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3542,6 +3551,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, + .port_tag_remap = mv88e6390_port_tag_remap, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3558,6 +3568,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, + .port_tag_remap = mv88e6390_port_tag_remap, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index ab52c37..a6e9dba 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -172,6 +172,16 @@ #define PORT_OUT_FILTERED 0x13 #define PORT_TAG_REGMAP_0123 0x18 #define PORT_TAG_REGMAP_4567 0x19 +#define PORT_IEEE_PRIO_MAP_TABLE 0x18 /* 6390 */ +#define PORT_IEEE_PRIO_MAP_TABLE_UPDATE BIT(15) +#define PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP (0x0 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP (0x1 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP (0x2 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP (0x3 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_DSCP (0x5 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_DSCP (0x6 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_DSCP (0x7 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT 9 #define GLOBAL_STATUS 0x00 #define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */ @@ -800,6 +810,8 @@ struct mv88e6xxx_ops { */ int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); + int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port); + /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. */ diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index af4772d8..3d03ea3 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -496,3 +496,66 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, return 0; } + +/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3] + * Offset 0x19: Port IEEE Priority Remapping Registers [4-7] + */ + +int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + /* Use a direct priority mapping for all IEEE tagged frames */ + err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 0x3210); + if (err) + return err; + + return mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 0x7654); +} + +static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip, + int port, u16 table, + u8 pointer, u16 data) +{ + u16 reg; + + reg = PORT_IEEE_PRIO_MAP_TABLE_UPDATE | + table | + (pointer << PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT) | + data; + + return mv88e6xxx_port_write(chip, port, PORT_IEEE_PRIO_MAP_TABLE, reg); +} + +int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port) +{ + int err, i; + + for (i = 0; i <= 7; i++) { + err = mv88e6xxx_port_ieeepmt_write( + chip, port, PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP, + i, (i | i << 4)); + if (err) + return err; + + err = mv88e6xxx_port_ieeepmt_write( + chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP, + i, i); + if (err) + return err; + + err = mv88e6xxx_port_ieeepmt_write( + chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP, + i, i); + if (err) + return err; + + err = mv88e6xxx_port_ieeepmt_write( + chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP, + i, i); + if (err) + return err; + } + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 499129c..5fab460 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -48,5 +48,7 @@ int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid); int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, u16 mode); +int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From 33641994a676f43f59d5fe02916c3965983522c2 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:35:17 +0100 Subject: net: dsa: mv88e6xxx: Monitor and Management tables The mv88e6390 changes the monitor control register into the Monitor and Management control, which is an indirection register to various registers. Add ops to set the CPU port and the ingress/egress port for both register layouts, to global1 Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 68 +++++++++++++++++++++++++++++----- drivers/net/dsa/mv88e6xxx/global1.c | 69 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 4 ++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 13 +++++++ 4 files changed, 145 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ff4bd2f..6e981be 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2747,15 +2747,17 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err) return err; - /* Configure the upstream port, and configure it as the port to which - * ingress and egress and ARP monitor frames are to be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; - err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); - if (err) - return err; + if (chip->info->ops->g1_set_cpu_port) { + err = chip->info->ops->g1_set_cpu_port(chip, upstream_port); + if (err) + return err; + } + + if (chip->info->ops->g1_set_egress_port) { + err = chip->info->ops->g1_set_egress_port(chip, upstream_port); + if (err) + return err; + } /* Disable remote management, and set the switch's DSA device number. */ err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, @@ -3184,6 +3186,8 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3213,6 +3217,8 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3227,6 +3233,8 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3242,6 +3250,8 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3257,6 +3267,8 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3271,6 +3283,8 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3287,6 +3301,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3305,6 +3321,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3321,6 +3339,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3339,6 +3359,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3353,6 +3375,8 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3370,6 +3394,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3387,6 +3413,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3404,6 +3432,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3422,6 +3452,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3439,6 +3471,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3456,6 +3490,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6320_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3473,6 +3509,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6320_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3489,6 +3527,8 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3505,6 +3545,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3523,6 +3565,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, + .g1_set_egress_port = mv88e6095_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -3540,6 +3584,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3557,6 +3603,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; static const struct mv88e6xxx_ops mv88e6391_ops = { @@ -3574,6 +3622,8 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 5fcf23d..6885470 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -33,6 +33,75 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask); } +/* Offset 0x1a: Monitor Control */ +/* Offset 0x1a: Monitor & MGMT Control on some devices */ + +int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port) +{ + u16 reg; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, ®); + if (err) + return err; + + reg &= ~(GLOBAL_MONITOR_CONTROL_INGRESS_MASK | + GLOBAL_MONITOR_CONTROL_EGRESS_MASK); + + reg |= port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | + port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT; + + return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); +} + +/* Older generations also call this the ARP destination. It has been + * generalized in more modern devices such that more than ARP can + * egress it + */ +int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port) +{ + u16 reg; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, ®); + if (err) + return err; + + reg &= ~GLOBAL_MONITOR_CONTROL_ARP_MASK; + reg |= port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; + + return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); +} + +static int mv88e6390_g1_monitor_write(struct mv88e6xxx_chip *chip, + u16 pointer, u8 data) +{ + u16 reg; + + reg = GLOBAL_MONITOR_CONTROL_UPDATE | pointer | data; + + return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); +} + +int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + err = mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_INGRESS, + port); + if (err) + return err; + + return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_EGRESS, + port); +} + +int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_CPU_DEST, + port); +} + /* Offset 0x1c: Global Control 2 */ int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index df3794c..0c97955 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -25,5 +25,9 @@ int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val); +int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index a6e9dba..a6dd192 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -287,10 +287,21 @@ #define GLOBAL_CORE_TAG_TYPE 0x19 #define GLOBAL_MONITOR_CONTROL 0x1a #define GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT 12 +#define GLOBAL_MONITOR_CONTROL_INGRESS_MASK (0xf << 12) #define GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT 8 +#define GLOBAL_MONITOR_CONTROL_EGRESS_MASK (0xf << 8) #define GLOBAL_MONITOR_CONTROL_ARP_SHIFT 4 +#define GLOBAL_MONITOR_CONTROL_ARP_MASK (0xf << 4) #define GLOBAL_MONITOR_CONTROL_MIRROR_SHIFT 0 #define GLOBAL_MONITOR_CONTROL_ARP_DISABLED (0xf0) +#define GLOBAL_MONITOR_CONTROL_UPDATE BIT(15) +#define GLOBAL_MONITOR_CONTROL_0180C280000000XLO (0x00 << 8) +#define GLOBAL_MONITOR_CONTROL_0180C280000000XHI (0x01 << 8) +#define GLOBAL_MONITOR_CONTROL_0180C280000002XLO (0x02 << 8) +#define GLOBAL_MONITOR_CONTROL_0180C280000002XHI (0x03 << 8) +#define GLOBAL_MONITOR_CONTROL_INGRESS (0x20 << 8) +#define GLOBAL_MONITOR_CONTROL_EGRESS (0x21 << 8) +#define GLOBAL_MONITOR_CONTROL_CPU_DEST (0x30 << 8) #define GLOBAL_CONTROL_2 0x1c #define GLOBAL_CONTROL_2_NO_CASCADE 0xe000 #define GLOBAL_CONTROL_2_MULTIPLE_CASCADE 0xf000 @@ -827,6 +838,8 @@ struct mv88e6xxx_ops { void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data); void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port, uint64_t *data); + int (*g1_set_cpu_port)(struct mv88e6xxx_chip *chip, int port); + int (*g1_set_egress_port)(struct mv88e6xxx_chip *chip, int port); }; #define STATS_TYPE_PORT BIT(0) -- cgit v1.1 From 443d5a1b7d2a8727a7e7d7836a784a5decd34dc0 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:35:18 +0100 Subject: net: dsa: mv88e6xxx: Move the tagging protocol into info Older chips support a single tagging protocol, DSA. New chips support both DSA and EDSA, an enhanced version. Having both as an option changes the register layouts. Up until now, it has been assumed that if EDSA is supported, it will be used. Hence the register layout has been determined by which protocol should be used. However, mv88e6390 has a different implementation of EDSA, which requires we need to use the DSA tagging. Hence separate the selection of the protocol from the register layout. Signed-off-by: Andrew Lunn Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 33 +++++++++++++++++++++++++++------ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 17 ++++------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 6e981be..80efee6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2482,7 +2482,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP | PORT_CONTROL_STATE_FORWARDING; if (dsa_is_cpu_port(ds, port)) { - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) + if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA) reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA | PORT_CONTROL_FORWARD_UNKNOWN_MC; else @@ -2611,7 +2611,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) /* Port Ethertype: use the Ethertype DSA Ethertype * value. */ - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) { + if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA) { err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, ETH_P_EDSA); if (err) @@ -3637,6 +3637,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6085_ops, }, @@ -3651,6 +3652,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6095, .ops = &mv88e6095_ops, }, @@ -3679,6 +3681,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6123_ops, }, @@ -3693,6 +3696,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6131_ops, }, @@ -3707,6 +3711,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6161_ops, }, @@ -3721,6 +3726,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6165_ops, }, @@ -3735,6 +3741,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6171_ops, }, @@ -3749,6 +3756,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6172_ops, }, @@ -3763,6 +3771,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6175_ops, }, @@ -3777,6 +3786,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6176_ops, }, @@ -3791,6 +3801,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6185_ops, }, @@ -3803,6 +3814,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, .global1_addr = 0x1b, + .tag_protocol = DSA_TAG_PROTO_DSA, .age_time_coeff = 15000, .g1_irqs = 9, .flags = MV88E6XXX_FLAGS_FAMILY_6390, @@ -3819,6 +3831,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6190x_ops, }, @@ -3832,6 +3845,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x0, .global1_addr = 0x1b, .age_time_coeff = 15000, + .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6391_ops, }, @@ -3846,6 +3861,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6240_ops, }, @@ -3860,6 +3876,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6290_ops, }, @@ -3874,6 +3891,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6320_ops, }, @@ -3888,6 +3906,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6321_ops, }, @@ -3902,6 +3921,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6350_ops, }, @@ -3916,6 +3936,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6351_ops, }, @@ -3930,6 +3951,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6352_ops, }, @@ -3943,6 +3965,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390_ops, }, @@ -3956,6 +3979,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390x_ops, }, @@ -4056,10 +4080,7 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) - return DSA_TAG_PROTO_EDSA; - - return DSA_TAG_PROTO_DSA; + return chip->info->tag_protocol; } static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index a6dd192..2c51e3a 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -438,12 +438,6 @@ enum mv88e6xxx_family { }; enum mv88e6xxx_cap { - /* Two different tag protocols can be used by the driver. All - * switches support DSA, but only later generations support - * EDSA. - */ - MV88E6XXX_CAP_EDSA, - /* Energy Efficient Ethernet. */ MV88E6XXX_CAP_EEE, @@ -506,7 +500,6 @@ enum mv88e6xxx_cap { }; /* Bitmask of capabilities */ -#define MV88E6XXX_FLAG_EDSA BIT_ULL(MV88E6XXX_CAP_EDSA) #define MV88E6XXX_FLAG_EEE BIT_ULL(MV88E6XXX_CAP_EEE) #define MV88E6XXX_FLAG_SMI_CMD BIT_ULL(MV88E6XXX_CAP_SMI_CMD) @@ -601,8 +594,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_VTU) #define MV88E6XXX_FLAGS_FAMILY_6320 \ - (MV88E6XXX_FLAG_EDSA | \ - MV88E6XXX_FLAG_EEE | \ + (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ @@ -616,8 +608,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAGS_PVT) #define MV88E6XXX_FLAGS_FAMILY_6351 \ - (MV88E6XXX_FLAG_EDSA | \ - MV88E6XXX_FLAG_G1_ATU_FID | \ + (MV88E6XXX_FLAG_G1_ATU_FID | \ MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ @@ -633,8 +624,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAGS_PVT) #define MV88E6XXX_FLAGS_FAMILY_6352 \ - (MV88E6XXX_FLAG_EDSA | \ - MV88E6XXX_FLAG_EEE | \ + (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_G1_ATU_FID | \ MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ @@ -676,6 +666,7 @@ struct mv88e6xxx_info { unsigned int global1_addr; unsigned int age_time_coeff; unsigned int g1_irqs; + enum dsa_tag_protocol tag_protocol; unsigned long long flags; const struct mv88e6xxx_ops *ops; }; -- cgit v1.1 From 56995cbc3540797142ebdcd25b55c74bd362c450 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:35:19 +0100 Subject: net: dsa: mv88e6xxx: Refactor CPU and DSA port setup Older chips only support DSA tagging. Newer chips have both DSA and EDSA tagging. Refactor the code by adding port functions for setting the frame mode, egress mode, and if to forward unknown frames. This results in the helper mv88e6xxx_6065_family() becoming unused, so remove it. Signed-off-by: Andrew Lunn v3: Verify mandatory ops for port setup Don't set ether type for DSA port. Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 217 ++++++++++++++++++++++++++-------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 20 ++++ drivers/net/dsa/mv88e6xxx/port.c | 118 ++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 13 ++ 4 files changed, 319 insertions(+), 49 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 80efee6..9c14aaa 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -677,11 +677,6 @@ static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr, return err; } -static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip) -{ - return chip->info->family == MV88E6XXX_FAMILY_6065; -} - static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip) { return chip->info->family == MV88E6XXX_FAMILY_6095; @@ -2438,6 +2433,72 @@ static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip) return err; } +static int mv88e6xxx_setup_port_dsa(struct mv88e6xxx_chip *chip, int port, + int upstream_port) +{ + int err; + + err = chip->info->ops->port_set_frame_mode( + chip, port, MV88E6XXX_FRAME_MODE_DSA); + if (err) + return err; + + return chip->info->ops->port_set_egress_unknowns( + chip, port, port == upstream_port); +} + +static int mv88e6xxx_setup_port_cpu(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + switch (chip->info->tag_protocol) { + case DSA_TAG_PROTO_EDSA: + err = chip->info->ops->port_set_frame_mode( + chip, port, MV88E6XXX_FRAME_MODE_ETHERTYPE); + if (err) + return err; + + err = mv88e6xxx_port_set_egress_mode( + chip, port, PORT_CONTROL_EGRESS_ADD_TAG); + if (err) + return err; + + if (chip->info->ops->port_set_ether_type) + err = chip->info->ops->port_set_ether_type( + chip, port, ETH_P_EDSA); + break; + + case DSA_TAG_PROTO_DSA: + err = chip->info->ops->port_set_frame_mode( + chip, port, MV88E6XXX_FRAME_MODE_DSA); + if (err) + return err; + + err = mv88e6xxx_port_set_egress_mode( + chip, port, PORT_CONTROL_EGRESS_UNMODIFIED); + break; + default: + err = -EINVAL; + } + + if (err) + return err; + + return chip->info->ops->port_set_egress_unknowns(chip, port, true); +} + +static int mv88e6xxx_setup_port_normal(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + err = chip->info->ops->port_set_frame_mode( + chip, port, MV88E6XXX_FRAME_MODE_NORMAL); + if (err) + return err; + + return chip->info->ops->port_set_egress_unknowns(chip, port, false); +} + static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { struct dsa_switch *ds = chip->ds; @@ -2473,44 +2534,23 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) * If this is the upstream port for this switch, enable * forwarding of unknown unicasts and multicasts. */ - reg = 0; - if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || - mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || - mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) || - mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip)) - reg = PORT_CONTROL_IGMP_MLD_SNOOP | + reg = PORT_CONTROL_IGMP_MLD_SNOOP | PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP | PORT_CONTROL_STATE_FORWARDING; - if (dsa_is_cpu_port(ds, port)) { - if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA) - reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA | - PORT_CONTROL_FORWARD_UNKNOWN_MC; - else - reg |= PORT_CONTROL_DSA_TAG; - reg |= PORT_CONTROL_EGRESS_ADD_TAG | - PORT_CONTROL_FORWARD_UNKNOWN; - } - if (dsa_is_dsa_port(ds, port)) { - if (mv88e6xxx_6095_family(chip) || - mv88e6xxx_6185_family(chip)) - reg |= PORT_CONTROL_DSA_TAG; - if (mv88e6xxx_6352_family(chip) || - mv88e6xxx_6351_family(chip) || - mv88e6xxx_6165_family(chip) || - mv88e6xxx_6097_family(chip) || - mv88e6xxx_6320_family(chip)) { - reg |= PORT_CONTROL_FRAME_MODE_DSA; - } + err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); + if (err) + return err; - if (port == dsa_upstream_port(ds)) - reg |= PORT_CONTROL_FORWARD_UNKNOWN | - PORT_CONTROL_FORWARD_UNKNOWN_MC; - } - if (reg) { - err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); - if (err) - return err; + if (dsa_is_cpu_port(ds, port)) { + err = mv88e6xxx_setup_port_cpu(chip, port); + } else if (dsa_is_dsa_port(ds, port)) { + err = mv88e6xxx_setup_port_dsa(chip, port, + dsa_upstream_port(ds)); + } else { + err = mv88e6xxx_setup_port_normal(chip, port); } + if (err) + return err; /* If this port is connected to a SerDes, make sure the SerDes is not * powered down. @@ -2607,16 +2647,6 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) 0x0000); if (err) return err; - - /* Port Ethertype: use the Ethertype DSA Ethertype - * value. - */ - if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA) { - err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, - ETH_P_EDSA); - if (err) - return err; - } } if (chip->info->ops->port_tag_remap) { @@ -3182,6 +3212,9 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3198,6 +3231,8 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_set_frame_mode = mv88e6085_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3213,6 +3248,9 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3229,6 +3267,8 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_set_frame_mode = mv88e6085_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3246,6 +3286,9 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3263,6 +3306,9 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3297,6 +3343,9 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3317,6 +3366,9 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3335,6 +3387,9 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3355,6 +3410,9 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3371,6 +3429,8 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_set_frame_mode = mv88e6085_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3389,6 +3449,9 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3408,6 +3471,9 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3427,6 +3493,9 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3448,6 +3517,9 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3466,6 +3538,9 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3486,6 +3561,9 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3505,6 +3583,9 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3523,6 +3604,9 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3541,6 +3625,9 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3561,6 +3648,9 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3579,6 +3669,9 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3598,6 +3691,9 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390x_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3617,6 +3713,9 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_ether_type = mv88e6351_port_set_ether_type, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3626,6 +3725,22 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .g1_set_egress_port = mv88e6390_g1_set_egress_port, }; +static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip, + const struct mv88e6xxx_ops *ops) +{ + if (!ops->port_set_frame_mode) { + dev_err(chip->dev, "Missing port_set_frame_mode"); + return -EINVAL; + } + + if (!ops->port_set_egress_unknowns) { + dev_err(chip->dev, "Missing port_set_egress_mode"); + return -EINVAL; + } + + return 0; +} + static const struct mv88e6xxx_info mv88e6xxx_table[] = { [MV88E6085] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, @@ -4268,6 +4383,10 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) chip->info = compat_info; + err = mv88e6xxx_verify_madatory_ops(chip, chip->info->ops); + if (err) + return err; + err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 2c51e3a..9dd94d7 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -110,6 +110,7 @@ #define PORT_CONTROL_EGRESS_UNTAGGED (0x1 << 12) #define PORT_CONTROL_EGRESS_TAGGED (0x2 << 12) #define PORT_CONTROL_EGRESS_ADD_TAG (0x3 << 12) +#define PORT_CONTROL_EGRESS_MASK (0x3 << 12) #define PORT_CONTROL_HEADER BIT(11) #define PORT_CONTROL_IGMP_MLD_SNOOP BIT(10) #define PORT_CONTROL_DOUBLE_TAG BIT(9) @@ -117,6 +118,7 @@ #define PORT_CONTROL_FRAME_MODE_DSA (0x1 << 8) #define PORT_CONTROL_FRAME_MODE_PROVIDER (0x2 << 8) #define PORT_CONTROL_FRAME_ETHER_TYPE_DSA (0x3 << 8) +#define PORT_CONTROL_FRAME_MASK (0x3 << 8) #define PORT_CONTROL_DSA_TAG BIT(8) #define PORT_CONTROL_VLAN_TUNNEL BIT(7) #define PORT_CONTROL_TAG_IF_BOTH BIT(6) @@ -124,6 +126,10 @@ #define PORT_CONTROL_USE_TAG BIT(4) #define PORT_CONTROL_FORWARD_UNKNOWN_MC BIT(3) #define PORT_CONTROL_FORWARD_UNKNOWN BIT(2) +#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_DA (0x0 << 2) +#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_MULTICAST_DA (0x1 << 2) +#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_UNITCAST_DA (0x2 << 2) +#define PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA (0x3 << 2) #define PORT_CONTROL_STATE_MASK 0x03 #define PORT_CONTROL_STATE_DISABLED 0x00 #define PORT_CONTROL_STATE_BLOCKING 0x01 @@ -396,6 +402,13 @@ #define MV88E6XXX_N_FID 4096 +enum mv88e6xxx_frame_mode { + MV88E6XXX_FRAME_MODE_NORMAL, + MV88E6XXX_FRAME_MODE_DSA, + MV88E6XXX_FRAME_MODE_PROVIDER, + MV88E6XXX_FRAME_MODE_ETHERTYPE, +}; + /* List of supported models */ enum mv88e6xxx_model { MV88E6085, @@ -814,6 +827,13 @@ struct mv88e6xxx_ops { int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port); + int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_frame_mode mode); + int (*port_set_egress_unknowns)(struct mv88e6xxx_chip *chip, int port, + bool on); + int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port, + u16 etype); + /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. */ diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 3d03ea3..cf66749 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -335,6 +335,116 @@ int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state) return 0; } +int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port, + u16 mode) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); + if (err) + return err; + + reg &= ~PORT_CONTROL_EGRESS_MASK; + reg |= mode; + + return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_frame_mode mode) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); + if (err) + return err; + + reg &= ~PORT_CONTROL_FRAME_MODE_DSA; + + switch (mode) { + case MV88E6XXX_FRAME_MODE_NORMAL: + reg |= PORT_CONTROL_FRAME_MODE_NORMAL; + break; + case MV88E6XXX_FRAME_MODE_DSA: + reg |= PORT_CONTROL_FRAME_MODE_DSA; + break; + default: + return -EINVAL; + } + + return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_frame_mode mode) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); + if (err) + return err; + + reg &= ~PORT_CONTROL_FRAME_MASK; + + switch (mode) { + case MV88E6XXX_FRAME_MODE_NORMAL: + reg |= PORT_CONTROL_FRAME_MODE_NORMAL; + break; + case MV88E6XXX_FRAME_MODE_DSA: + reg |= PORT_CONTROL_FRAME_MODE_DSA; + break; + case MV88E6XXX_FRAME_MODE_PROVIDER: + reg |= PORT_CONTROL_FRAME_MODE_PROVIDER; + break; + case MV88E6XXX_FRAME_MODE_ETHERTYPE: + reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA; + break; + default: + return -EINVAL; + } + + return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, + bool on) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); + if (err) + return err; + + if (on) + reg |= PORT_CONTROL_FORWARD_UNKNOWN; + else + reg &= ~PORT_CONTROL_FORWARD_UNKNOWN; + + return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, + bool on) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); + if (err) + return err; + + if (on) + reg |= PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA; + else + reg &= ~PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA; + + return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + /* Offset 0x05: Port Control 1 */ /* Offset 0x06: Port Based VLAN Map */ @@ -497,6 +607,14 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, return 0; } +/* Offset 0x0f: Port Ether type */ + +int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, + u16 etype) +{ + return mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, etype); +} + /* Offset 0x18: Port IEEE Priority Remapping Registers [0-3] * Offset 0x19: Port IEEE Priority Remapping Registers [4-7] */ diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 5fab460..83cb344 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -50,5 +50,18 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, u16 mode); int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port); int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port); +int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port, + u16 mode); +int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_frame_mode mode); +int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_frame_mode mode); +int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, + bool on); +int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, + bool on); +int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, + u16 etype); + #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From 6e55f69846f0b11dd59a5d9ade44f4cc40eaa0ec Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:45:16 +0100 Subject: net: dsa: mv88e6xxx: Reserved Management frames to CPU Older devices have a couple of registers in global2. The mv88e6390 family has a single register in global1 behind which hides similar configuration. Implement and op for this. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 35 ++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.c | 27 ++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 1 + drivers/net/dsa/mv88e6xxx/global2.c | 43 ++++++++++++++++++++--------------- drivers/net/dsa/mv88e6xxx/global2.h | 6 +++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 3 +++ 6 files changed, 97 insertions(+), 18 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 9c14aaa..b2b6fe3 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2899,6 +2899,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) goto unlock; } + /* Some generations have the configuration of sending reserved + * management frames to the CPU in global2, others in + * global1. Hence it does not fit the two setup functions + * above. + */ + if (chip->info->ops->mgmt_rsvd2cpu) { + err = chip->info->ops->mgmt_rsvd2cpu(chip); + if (err) + goto unlock; + } + unlock: mutex_unlock(&chip->reg_lock); @@ -3221,6 +3232,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3237,6 +3249,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -3257,6 +3270,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3275,6 +3289,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3295,6 +3310,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3315,6 +3331,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3331,6 +3348,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3352,6 +3370,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3375,6 +3394,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3396,6 +3416,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3419,6 +3440,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3437,6 +3459,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3459,6 +3482,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .stats_get_stats = mv88e6390_stats_get_stats, .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3481,6 +3505,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .stats_get_stats = mv88e6390_stats_get_stats, .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3503,6 +3528,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .stats_get_stats = mv88e6390_stats_get_stats, .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3526,6 +3552,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3548,6 +3575,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .stats_get_stats = mv88e6390_stats_get_stats, .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3570,6 +3598,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .stats_get_stats = mv88e6320_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3613,6 +3642,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3634,6 +3664,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3657,6 +3688,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .stats_get_stats = mv88e6095_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -3679,6 +3711,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .stats_get_stats = mv88e6390_stats_get_stats, .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3701,6 +3734,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .stats_get_stats = mv88e6390_stats_get_stats, .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, }; static const struct mv88e6xxx_ops mv88e6391_ops = { @@ -3723,6 +3757,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .stats_get_stats = mv88e6390_stats_get_stats, .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, }; static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 6885470..44136ee 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -102,6 +102,33 @@ int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port) port); } +int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ + int err; + + /* 01:c2:80:00:00:00:00-01:c2:80:00:00:00:07 are Management */ + err = mv88e6390_g1_monitor_write( + chip, GLOBAL_MONITOR_CONTROL_0180C280000000XLO, 0xff); + if (err) + return err; + + /* 01:c2:80:00:00:00:08-01:c2:80:00:00:00:0f are Management */ + err = mv88e6390_g1_monitor_write( + chip, GLOBAL_MONITOR_CONTROL_0180C280000000XHI, 0xff); + if (err) + return err; + + /* 01:c2:80:00:00:00:20-01:c2:80:00:00:00:27 are Management */ + err = mv88e6390_g1_monitor_write( + chip, GLOBAL_MONITOR_CONTROL_0180C280000002XLO, 0xff); + if (err) + return err; + + /* 01:c2:80:00:00:00:28-01:c2:80:00:00:00:2f are Management */ + return mv88e6390_g1_monitor_write( + chip, GLOBAL_MONITOR_CONTROL_0180C280000002XHI, 0xff); +} + /* Offset 0x1c: Global Control 2 */ int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 0c97955..cb61378 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -29,5 +29,6 @@ int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 536a27c..3e77071 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -38,6 +38,31 @@ static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask); } +/* Offset 0x02: Management Enable 2x */ +/* Offset 0x03: Management Enable 0x */ + +int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ + int err; + + /* Consider the frames with reserved multicast destination + * addresses matching 01:80:c2:00:00:2x as MGMT. + */ + if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { + err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff); + if (err) + return err; + } + + /* Consider the frames with reserved multicast destination + * addresses matching 01:80:c2:00:00:0x as MGMT. + */ + if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) + return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff); + + return 0; +} + /* Offset 0x06: Device Mapping Table register */ static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, @@ -567,24 +592,6 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) u16 reg; int err; - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { - /* Consider the frames with reserved multicast destination - * addresses matching 01:80:c2:00:00:2x as MGMT. - */ - err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff); - if (err) - return err; - } - - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) { - /* Consider the frames with reserved multicast destination - * addresses matching 01:80:c2:00:00:0x as MGMT. - */ - err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff); - if (err) - return err; - } - /* Ignore removed tag data on doubly tagged packets, disable * flow control messages, force flow control priority to the * highest, and send all special multicast frames to the CPU diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 1eb3ddd..9aefb7d 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -35,6 +35,7 @@ int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip); +int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); #else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ @@ -94,6 +95,11 @@ static inline void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) { } +static inline int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ + return -EOPNOTSUPP; +} + #endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ #endif /* _MV88E6XXX_GLOBAL2_H */ diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 9dd94d7..d9ffa96 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -851,6 +851,9 @@ struct mv88e6xxx_ops { uint64_t *data); int (*g1_set_cpu_port)(struct mv88e6xxx_chip *chip, int port); int (*g1_set_egress_port)(struct mv88e6xxx_chip *chip, int port); + + /* Can be either in g1 or g2, so don't use a prefix */ + int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip); }; #define STATS_TYPE_PORT BIT(0) -- cgit v1.1 From 5f4366660d65aca15e5887bd04c9c0140a658e40 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:45:17 +0100 Subject: net: dsa: mv88e6xxx: Refactor setting of jumbo frames Some switches support jumbo frames. Refactor this code into operations in the ops structure. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 26 ++++++++++++++++++++++---- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + drivers/net/dsa/mv88e6xxx/port.c | 14 ++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 +- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b2b6fe3..db1542e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2582,10 +2582,6 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) mv88e6xxx_6185_family(chip)) reg = PORT_CONTROL_2_MAP_DA; - if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || - mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip)) - reg |= PORT_CONTROL_2_JUMBO_10240; - if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) { /* Set the upstream port this port should use */ reg |= dsa_upstream_port(ds); @@ -2604,6 +2600,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } + if (chip->info->ops->port_jumbo_config) { + err = chip->info->ops->port_jumbo_config(chip, port); + if (err) + return err; + } + /* Port Association Vector: when learning source addresses * of packets, add the address to the address database using * a port bitmap that has only the bit for this port set and @@ -2663,6 +2665,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) 0x0001); if (err) return err; + } else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) { err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0000); @@ -3264,6 +3267,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3304,6 +3308,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3325,6 +3330,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3364,6 +3370,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3388,6 +3395,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3410,6 +3418,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3434,6 +3443,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3546,6 +3556,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3592,6 +3603,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3615,6 +3627,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3636,6 +3649,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3658,6 +3672,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3682,6 +3697,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3704,6 +3720,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3727,6 +3744,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index d9ffa96..d4f18b8 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -833,6 +833,7 @@ struct mv88e6xxx_ops { bool on); int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port, u16 etype); + int (*port_jumbo_config)(struct mv88e6xxx_chip *chip, int port); /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index cf66749..a739129 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -607,6 +607,20 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, return 0; } +int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); + if (err) + return err; + + reg |= PORT_CONTROL_2_JUMBO_10240; + + return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); +} + /* Offset 0x0f: Port Ether type */ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 83cb344..9e25735 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -62,6 +62,6 @@ int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, bool on); int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, u16 etype); - +int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From ef70b1119ee8e788f7f4ac22fe1f016363d7921f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:45:18 +0100 Subject: net: dsa: mv88e6xxx: Refactor egress rate limiting There are two different rate limiting configurations, depending on the switch generation. Refactor this into ops. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 31 +++++++++++++++++++------------ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 2 ++ drivers/net/dsa/mv88e6xxx/port.c | 12 ++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index db1542e..1b0917e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2657,18 +2657,8 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } - /* Rate Control: disable ingress rate limiting. */ - if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || - mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || - mv88e6xxx_6320_family(chip)) { - err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, - 0x0001); - if (err) - return err; - - } else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) { - err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, - 0x0000); + if (chip->info->ops->port_egress_rate_limiting) { + err = chip->info->ops->port_egress_rate_limiting(chip, port); if (err) return err; } @@ -3229,6 +3219,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3268,6 +3259,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3309,6 +3301,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3331,6 +3324,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3371,6 +3365,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3396,6 +3391,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3419,6 +3415,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3444,6 +3441,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3463,6 +3461,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, + .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3557,6 +3556,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3604,6 +3604,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3628,6 +3629,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3650,6 +3652,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3673,6 +3676,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3698,6 +3702,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3721,6 +3726,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3745,6 +3751,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index d4f18b8..65aa564 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -835,6 +835,8 @@ struct mv88e6xxx_ops { u16 etype); int (*port_jumbo_config)(struct mv88e6xxx_chip *chip, int port); + int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port); + /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. */ diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index a739129..d22c7b0 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -621,6 +621,18 @@ int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port) return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); } +/* Offset 0x09: Port Rate Control */ + +int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0000); +} + +int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0001); +} + /* Offset 0x0f: Port Ether type */ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 9e25735..9bad597 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -63,5 +63,7 @@ int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, u16 etype); int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port); +int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); +int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From b35d322a1d9330597754d5e8d5f76932fad79c1a Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:45:19 +0100 Subject: net: dsa: mv88e6xxx: Refactor pause configuration The mv88e6390 has a different mechanism for configuring pause. Refactor the code into an ops function, and for the moment, don't add any mv88e6390 code yet. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 28 ++++++++++++++++++++-------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + drivers/net/dsa/mv88e6xxx/port.c | 11 +++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 1 + 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 1b0917e..3ddb1f7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2625,17 +2625,15 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; - if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || - mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || - mv88e6xxx_6320_family(chip)) { - /* Do not limit the period of time that this port can - * be paused for by the remote end or the period of - * time that this port can pause the remote end. - */ - err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000); + if (chip->info->ops->port_pause_config) { + err = chip->info->ops->port_pause_config(chip, port); if (err) return err; + } + if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || + mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || + mv88e6xxx_6320_family(chip)) { /* Port ATU control: disable limiting the number of * address database entries that this port is allowed * to use. @@ -3220,6 +3218,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3260,6 +3259,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3302,6 +3302,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3325,6 +3326,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3366,6 +3368,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3392,6 +3395,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3416,6 +3420,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3442,6 +3447,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3557,6 +3563,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3605,6 +3612,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3630,6 +3638,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3653,6 +3662,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3677,6 +3687,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3703,6 +3714,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 65aa564..3b1f3ab 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -836,6 +836,7 @@ struct mv88e6xxx_ops { int (*port_jumbo_config)(struct mv88e6xxx_chip *chip, int port); int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port); + int (*port_pause_config)(struct mv88e6xxx_chip *chip, int port); /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index d22c7b0..8d14833 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -304,6 +304,17 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return mv88e6xxx_port_set_speed(chip, port, speed, true, true); } +/* Offset 0x02: Pause Control + * + * Do not limit the period of time that this port can be paused for by + * the remote end or the period of time that this port can pause the + * remote end. + */ +int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000); +} + /* Offset 0x04: Port Control Register */ static const char * const mv88e6xxx_port_state_names[] = { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 9bad597..8c964cfd 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -65,5 +65,6 @@ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port); int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); +int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From 3ce0e65eb6059d3d4e56bbb17aee34f2740844be Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 Dec 2016 04:45:20 +0100 Subject: net: dsa: mv88e6xxx: Implement mv88e6390 pause control The mv88e6390 has a number flow control registers accessed via the Flow Control register. Use these to set the pause control. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 7 +++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 2 ++ drivers/net/dsa/mv88e6xxx/port.c | 13 +++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 1 + 4 files changed, 23 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3ddb1f7..ca453f3 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3490,6 +3490,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3513,6 +3514,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3536,6 +3538,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3586,6 +3589,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3739,6 +3743,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3764,6 +3769,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3787,6 +3793,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 3b1f3ab..13c7cc4 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -78,6 +78,8 @@ #define PORT_PCS_CTRL_SPEED_10000 (0x03) /* 6390X */ #define PORT_PCS_CTRL_SPEED_UNFORCED (0x03) #define PORT_PAUSE_CTRL 0x02 +#define PORT_FLOW_CTRL_LIMIT_IN ((0x00 << 8) | BIT(15)) +#define PORT_FLOW_CTRL_LIMIT_OUT ((0x01 << 8) | BIT(15)) #define PORT_SWITCH_ID 0x03 #define PORT_SWITCH_ID_PROD_NUM_6085 0x04a #define PORT_SWITCH_ID_PROD_NUM_6095 0x095 diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 8d14833..0db7fa0 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -315,6 +315,19 @@ int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port) return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000); } +int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port) +{ + int err; + + err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, + PORT_FLOW_CTRL_LIMIT_IN | 0); + if (err) + return err; + + return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, + PORT_FLOW_CTRL_LIMIT_OUT | 0); +} + /* Offset 0x04: Port Control Register */ static const char * const mv88e6xxx_port_state_names[] = { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 8c964cfd..7b3baca 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -66,5 +66,6 @@ int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port); int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_PORT_H */ -- cgit v1.1 From adc176c5472214971d77c1a61c83db9b01e9cdc7 Mon Sep 17 00:00:00 2001 From: Erik Nordmark Date: Fri, 2 Dec 2016 14:00:08 -0800 Subject: ipv6 addrconf: Implemented enhanced DAD (RFC7527) Implemented RFC7527 Enhanced DAD. IPv6 duplicate address detection can fail if there is some temporary loopback of Ethernet frames. RFC7527 solves this by including a random nonce in the NS messages used for DAD, and if an NS is received with the same nonce it is assumed to be a looped back DAD probe and is ignored. RFC7527 is enabled by default. Can be disabled by setting both of conf/{all,interface}/enhanced_dad to zero. Signed-off-by: Erik Nordmark Signed-off-by: Bob Gilligan Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 9 +++++++++ include/linux/ipv6.h | 1 + include/net/if_inet6.h | 1 + include/net/ndisc.h | 5 ++++- include/uapi/linux/ipv6.h | 1 + net/ipv6/addrconf.c | 22 +++++++++++++++++++++- net/ipv6/ndisc.c | 29 ++++++++++++++++++++++++++--- net/ipv6/route.c | 2 +- 8 files changed, 64 insertions(+), 6 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 5ca567f..7dd65c9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1734,6 +1734,15 @@ drop_unsolicited_na - BOOLEAN By default this is turned off. +enhanced_dad - BOOLEAN + Include a nonce option in the IPv6 neighbor solicitation messages used for + duplicate address detection per RFC7527. A received DAD NS will only signal + a duplicate address if the nonce is different. This avoids any false + detection of duplicates due to loopback of the NS messages that we send. + The nonce option will be sent on an interface unless both of + conf/{all,interface}/enhanced_dad are set to FALSE. + Default: TRUE + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 3f95233..671d014 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -68,6 +68,7 @@ struct ipv6_devconf { #ifdef CONFIG_IPV6_SEG6_HMAC __s32 seg6_require_hmac; #endif + __u32 enhanced_dad; struct ctl_table_header *sysctl_header; }; diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index b0576cb..0fa4c32 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -55,6 +55,7 @@ struct inet6_ifaddr { __u8 stable_privacy_retry; __u16 scope; + __u64 dad_nonce; unsigned long cstamp; /* created timestamp */ unsigned long tstamp; /* updated timestamp */ diff --git a/include/net/ndisc.h b/include/net/ndisc.h index be1fe228..d562a2f 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -31,6 +31,7 @@ enum { ND_OPT_PREFIX_INFO = 3, /* RFC2461 */ ND_OPT_REDIRECT_HDR = 4, /* RFC2461 */ ND_OPT_MTU = 5, /* RFC2461 */ + ND_OPT_NONCE = 14, /* RFC7527 */ __ND_OPT_ARRAY_MAX, ND_OPT_ROUTE_INFO = 24, /* RFC4191 */ ND_OPT_RDNSS = 25, /* RFC5006 */ @@ -121,6 +122,7 @@ struct ndisc_options { #define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END] #define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR] #define nd_opts_mtu nd_opt_array[ND_OPT_MTU] +#define nd_opts_nonce nd_opt_array[ND_OPT_NONCE] #define nd_802154_opts_src_lladdr nd_802154_opt_array[ND_OPT_SOURCE_LL_ADDR] #define nd_802154_opts_tgt_lladdr nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR] @@ -398,7 +400,8 @@ void ndisc_cleanup(void); int ndisc_rcv(struct sk_buff *skb); void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, - const struct in6_addr *daddr, const struct in6_addr *saddr); + const struct in6_addr *daddr, const struct in6_addr *saddr, + u64 nonce); void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr); diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 53561be..eaf65dc 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -181,6 +181,7 @@ enum { DEVCONF_RTR_SOLICIT_MAX_INTERVAL, DEVCONF_SEG6_ENABLED, DEVCONF_SEG6_REQUIRE_HMAC, + DEVCONF_ENHANCED_DAD, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4c387dc..c1e124b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -242,6 +242,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { #ifdef CONFIG_IPV6_SEG6_HMAC .seg6_require_hmac = 0, #endif + .enhanced_dad = 1, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -292,6 +293,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { #ifdef CONFIG_IPV6_SEG6_HMAC .seg6_require_hmac = 0, #endif + .enhanced_dad = 1, }; /* Check if a valid qdisc is available */ @@ -3735,12 +3737,21 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp) { unsigned long rand_num; struct inet6_dev *idev = ifp->idev; + u64 nonce; if (ifp->flags & IFA_F_OPTIMISTIC) rand_num = 0; else rand_num = prandom_u32() % (idev->cnf.rtr_solicit_delay ? : 1); + nonce = 0; + if (idev->cnf.enhanced_dad || + dev_net(idev->dev)->ipv6.devconf_all->enhanced_dad) { + do + get_random_bytes(&nonce, 6); + while (nonce == 0); + } + ifp->dad_nonce = nonce; ifp->dad_probes = idev->cnf.dad_transmits; addrconf_mod_dad_work(ifp, rand_num); } @@ -3918,7 +3929,8 @@ static void addrconf_dad_work(struct work_struct *w) /* send a neighbour solicitation for our addr */ addrconf_addr_solict_mult(&ifp->addr, &mcaddr); - ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any); + ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any, + ifp->dad_nonce); out: in6_ifa_put(ifp); rtnl_unlock(); @@ -4962,6 +4974,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, #ifdef CONFIG_IPV6_SEG6_HMAC array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac; #endif + array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad; } static inline size_t inet6_ifla6_size(void) @@ -6070,6 +6083,13 @@ static const struct ctl_table addrconf_sysctl[] = { }, #endif { + .procname = "enhanced_dad", + .data = &ipv6_devconf.enhanced_dad, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { /* sentinel */ } }; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index d8e6714..7ebac63 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -233,6 +233,7 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev, case ND_OPT_SOURCE_LL_ADDR: case ND_OPT_TARGET_LL_ADDR: case ND_OPT_MTU: + case ND_OPT_NONCE: case ND_OPT_REDIRECT_HDR: if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { ND_PRINTK(2, warn, @@ -568,7 +569,8 @@ static void ndisc_send_unsol_na(struct net_device *dev) } void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, - const struct in6_addr *daddr, const struct in6_addr *saddr) + const struct in6_addr *daddr, const struct in6_addr *saddr, + u64 nonce) { struct sk_buff *skb; struct in6_addr addr_buf; @@ -588,6 +590,8 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, if (inc_opt) optlen += ndisc_opt_addr_space(dev, NDISC_NEIGHBOUR_SOLICITATION); + if (nonce != 0) + optlen += 8; skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); if (!skb) @@ -605,6 +609,13 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, NDISC_NEIGHBOUR_SOLICITATION); + if (nonce != 0) { + u8 *opt = skb_put(skb, 8); + + opt[0] = ND_OPT_NONCE; + opt[1] = 8 >> 3; + memcpy(opt + 2, &nonce, 6); + } ndisc_send_skb(skb, daddr, saddr); } @@ -693,12 +704,12 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) "%s: trying to ucast probe in NUD_INVALID: %pI6\n", __func__, target); } - ndisc_send_ns(dev, target, target, saddr); + ndisc_send_ns(dev, target, target, saddr, 0); } else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) { neigh_app_ns(neigh); } else { addrconf_addr_solict_mult(target, &mcaddr); - ndisc_send_ns(dev, target, &mcaddr, saddr); + ndisc_send_ns(dev, target, &mcaddr, saddr, 0); } } @@ -742,6 +753,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) int dad = ipv6_addr_any(saddr); bool inc; int is_router = -1; + u64 nonce = 0; if (skb->len < sizeof(struct nd_msg)) { ND_PRINTK(2, warn, "NS: packet too short\n"); @@ -786,6 +798,8 @@ static void ndisc_recv_ns(struct sk_buff *skb) return; } } + if (ndopts.nd_opts_nonce) + memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6); inc = ipv6_addr_is_multicast(daddr); @@ -794,6 +808,15 @@ static void ndisc_recv_ns(struct sk_buff *skb) have_ifp: if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) { if (dad) { + if (nonce != 0 && ifp->dad_nonce == nonce) { + u8 *np = (u8 *)&nonce; + /* Matching nonce if looped back */ + ND_PRINTK(2, notice, + "%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n", + ifp->idev->dev->name, + &ifp->addr, np); + goto out; + } /* * We are colliding with another node * who is doing DAD diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b317bb1..aac7818 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -527,7 +527,7 @@ static void rt6_probe_deferred(struct work_struct *w) container_of(w, struct __rt6_probe_work, work); addrconf_addr_solict_mult(&work->target, &mcaddr); - ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL); + ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0); dev_put(work->dev); kfree(work); } -- cgit v1.1 From cd7275146885e7bc532ee2e0186dfd2b5c0b745f Mon Sep 17 00:00:00 2001 From: Arturo Borrero Gonzalez Date: Tue, 18 Oct 2016 14:02:29 +0200 Subject: netfilter: update Arturo Borrero Gonzalez email address The email address has changed, let's update the copyright statements. Signed-off-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nft_masq_ipv4.c | 4 ++-- net/ipv4/netfilter/nft_redir_ipv4.c | 4 ++-- net/ipv6/netfilter/nft_masq_ipv6.c | 4 ++-- net/ipv6/netfilter/nft_redir_ipv6.c | 4 ++-- net/netfilter/nft_masq.c | 4 ++-- net/netfilter/nft_redir.c | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c index 4f697e4..4d69f99 100644 --- a/net/ipv4/netfilter/nft_masq_ipv4.c +++ b/net/ipv4/netfilter/nft_masq_ipv4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Arturo Borrero Gonzalez + * Copyright (c) 2014 Arturo Borrero Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -77,5 +77,5 @@ module_init(nft_masq_ipv4_module_init); module_exit(nft_masq_ipv4_module_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arturo Borrero Gonzalez "); +MODULE_AUTHOR("Arturo Borrero Gonzalez + * Copyright (c) 2014 Arturo Borrero Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -71,5 +71,5 @@ module_init(nft_redir_ipv4_module_init); module_exit(nft_redir_ipv4_module_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arturo Borrero Gonzalez "); +MODULE_AUTHOR("Arturo Borrero Gonzalez "); MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "redir"); diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c index a2aff12..93d758f 100644 --- a/net/ipv6/netfilter/nft_masq_ipv6.c +++ b/net/ipv6/netfilter/nft_masq_ipv6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Arturo Borrero Gonzalez + * Copyright (c) 2014 Arturo Borrero Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -78,5 +78,5 @@ module_init(nft_masq_ipv6_module_init); module_exit(nft_masq_ipv6_module_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arturo Borrero Gonzalez "); +MODULE_AUTHOR("Arturo Borrero Gonzalez "); MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "masq"); diff --git a/net/ipv6/netfilter/nft_redir_ipv6.c b/net/ipv6/netfilter/nft_redir_ipv6.c index bfcd5af..2850fcd 100644 --- a/net/ipv6/netfilter/nft_redir_ipv6.c +++ b/net/ipv6/netfilter/nft_redir_ipv6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Arturo Borrero Gonzalez + * Copyright (c) 2014 Arturo Borrero Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -72,5 +72,5 @@ module_init(nft_redir_ipv6_module_init); module_exit(nft_redir_ipv6_module_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arturo Borrero Gonzalez "); +MODULE_AUTHOR("Arturo Borrero Gonzalez "); MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "redir"); diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 81b5ad6..bf92de0 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Arturo Borrero Gonzalez + * Copyright (c) 2014 Arturo Borrero Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -105,4 +105,4 @@ nla_put_failure: EXPORT_SYMBOL_GPL(nft_masq_dump); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arturo Borrero Gonzalez "); +MODULE_AUTHOR("Arturo Borrero Gonzalez "); diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index 03f7bf4..967e09b 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Arturo Borrero Gonzalez + * Copyright (c) 2014 Arturo Borrero Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -108,4 +108,4 @@ nla_put_failure: EXPORT_SYMBOL_GPL(nft_redir_dump); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arturo Borrero Gonzalez "); +MODULE_AUTHOR("Arturo Borrero Gonzalez "); -- cgit v1.1 From 0c4e966eafff8253bec545d8c27b9efa231c1f62 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Thu, 20 Oct 2016 18:33:01 +0200 Subject: netfilter: built-in NAT support for DCCP CONFIG_NF_NAT_PROTO_DCCP is no more a tristate. When set to y, NAT support for DCCP protocol is built-in into nf_nat.ko. footprint test: (nf_nat_proto_) | dccp || nf_nat --------------------------+--------++-------- no builtin | 409800 || 2241312 DCCP builtin | - || 2578968 Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_nat_l4proto.h | 3 +++ net/netfilter/Kconfig | 2 +- net/netfilter/Makefile | 3 ++- net/netfilter/nf_nat_core.c | 4 ++++ net/netfilter/nf_nat_proto_dccp.c | 36 +--------------------------------- 5 files changed, 11 insertions(+), 37 deletions(-) diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h index 12f4cc8..92b147b 100644 --- a/include/net/netfilter/nf_nat_l4proto.h +++ b/include/net/netfilter/nf_nat_l4proto.h @@ -54,6 +54,9 @@ extern const struct nf_nat_l4proto nf_nat_l4proto_udp; extern const struct nf_nat_l4proto nf_nat_l4proto_icmp; extern const struct nf_nat_l4proto nf_nat_l4proto_icmpv6; extern const struct nf_nat_l4proto nf_nat_l4proto_unknown; +#ifdef CONFIG_NF_NAT_PROTO_DCCP +extern const struct nf_nat_l4proto nf_nat_l4proto_dccp; +#endif bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype, diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 44410d3..13092e5 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -384,7 +384,7 @@ config NF_NAT_NEEDED default y config NF_NAT_PROTO_DCCP - tristate + bool depends on NF_NAT && NF_CT_PROTO_DCCP default NF_NAT && NF_CT_PROTO_DCCP diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 5bbf767..9ea0c98 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -45,6 +45,8 @@ obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ nf_nat_proto_udp.o nf_nat_proto_tcp.o nf_nat_helper.o +nf_nat-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o + # generic transport layer logging obj-$(CONFIG_NF_LOG_COMMON) += nf_log_common.o @@ -55,7 +57,6 @@ obj-$(CONFIG_NF_NAT) += nf_nat.o obj-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o # NAT protocols (nf_nat) -obj-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o obj-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o obj-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 5b9c884..69b121d 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -682,6 +682,10 @@ int nf_nat_l3proto_register(const struct nf_nat_l3proto *l3proto) &nf_nat_l4proto_tcp); RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_UDP], &nf_nat_l4proto_udp); +#ifdef CONFIG_NF_NAT_PROTO_DCCP + RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_DCCP], + &nf_nat_l4proto_dccp); +#endif mutex_unlock(&nf_nat_proto_mutex); RCU_INIT_POINTER(nf_nat_l3protos[l3proto->l3proto], l3proto); diff --git a/net/netfilter/nf_nat_proto_dccp.c b/net/netfilter/nf_nat_proto_dccp.c index 15c47b2..269fcd5 100644 --- a/net/netfilter/nf_nat_proto_dccp.c +++ b/net/netfilter/nf_nat_proto_dccp.c @@ -10,8 +10,6 @@ */ #include -#include -#include #include #include @@ -73,7 +71,7 @@ dccp_manip_pkt(struct sk_buff *skb, return true; } -static const struct nf_nat_l4proto nf_nat_l4proto_dccp = { +const struct nf_nat_l4proto nf_nat_l4proto_dccp = { .l4proto = IPPROTO_DCCP, .manip_pkt = dccp_manip_pkt, .in_range = nf_nat_l4proto_in_range, @@ -82,35 +80,3 @@ static const struct nf_nat_l4proto nf_nat_l4proto_dccp = { .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, #endif }; - -static int __init nf_nat_proto_dccp_init(void) -{ - int err; - - err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_dccp); - if (err < 0) - goto err1; - err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_dccp); - if (err < 0) - goto err2; - return 0; - -err2: - nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_dccp); -err1: - return err; -} - -static void __exit nf_nat_proto_dccp_fini(void) -{ - nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_dccp); - nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_dccp); - -} - -module_init(nf_nat_proto_dccp_init); -module_exit(nf_nat_proto_dccp_fini); - -MODULE_AUTHOR("Patrick McHardy "); -MODULE_DESCRIPTION("DCCP NAT protocol helper"); -MODULE_LICENSE("GPL"); -- cgit v1.1 From 7a2dd28c703408ef27d6fe6a4fcd7c58968ce3bf Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Thu, 20 Oct 2016 18:33:02 +0200 Subject: netfilter: built-in NAT support for SCTP CONFIG_NF_NAT_PROTO_SCTP is no more a tristate. When set to y, NAT support for SCTP protocol is built-in into nf_nat.ko. footprint test: (nf_nat_proto_) | sctp || nf_nat --------------------------+--------++-------- no builtin | 428344 || 2241312 SCTP builtin | - || 2597032 Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_nat_l4proto.h | 3 +++ net/netfilter/Kconfig | 2 +- net/netfilter/Makefile | 2 +- net/netfilter/nf_nat_core.c | 4 ++++ net/netfilter/nf_nat_proto_sctp.c | 35 +--------------------------------- 5 files changed, 10 insertions(+), 36 deletions(-) diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h index 92b147b..2cbaf38 100644 --- a/include/net/netfilter/nf_nat_l4proto.h +++ b/include/net/netfilter/nf_nat_l4proto.h @@ -57,6 +57,9 @@ extern const struct nf_nat_l4proto nf_nat_l4proto_unknown; #ifdef CONFIG_NF_NAT_PROTO_DCCP extern const struct nf_nat_l4proto nf_nat_l4proto_dccp; #endif +#ifdef CONFIG_NF_NAT_PROTO_SCTP +extern const struct nf_nat_l4proto nf_nat_l4proto_sctp; +#endif bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype, diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 13092e5..ad72edf 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -394,7 +394,7 @@ config NF_NAT_PROTO_UDPLITE default NF_NAT && NF_CT_PROTO_UDPLITE config NF_NAT_PROTO_SCTP - tristate + bool default NF_NAT && NF_CT_PROTO_SCTP depends on NF_NAT && NF_CT_PROTO_SCTP select LIBCRC32C diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 9ea0c98..02ef6de 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -46,6 +46,7 @@ nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ nf_nat_proto_udp.o nf_nat_proto_tcp.o nf_nat_helper.o nf_nat-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o +nf_nat-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o # generic transport layer logging obj-$(CONFIG_NF_LOG_COMMON) += nf_log_common.o @@ -58,7 +59,6 @@ obj-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o # NAT protocols (nf_nat) obj-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o -obj-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o # NAT helpers obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 69b121d..80858bd 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -686,6 +686,10 @@ int nf_nat_l3proto_register(const struct nf_nat_l3proto *l3proto) RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_DCCP], &nf_nat_l4proto_dccp); #endif +#ifdef CONFIG_NF_NAT_PROTO_SCTP + RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_SCTP], + &nf_nat_l4proto_sctp); +#endif mutex_unlock(&nf_nat_proto_mutex); RCU_INIT_POINTER(nf_nat_l3protos[l3proto->l3proto], l3proto); diff --git a/net/netfilter/nf_nat_proto_sctp.c b/net/netfilter/nf_nat_proto_sctp.c index cbc7ade..2e14108 100644 --- a/net/netfilter/nf_nat_proto_sctp.c +++ b/net/netfilter/nf_nat_proto_sctp.c @@ -7,9 +7,7 @@ */ #include -#include #include -#include #include #include @@ -54,7 +52,7 @@ sctp_manip_pkt(struct sk_buff *skb, return true; } -static const struct nf_nat_l4proto nf_nat_l4proto_sctp = { +const struct nf_nat_l4proto nf_nat_l4proto_sctp = { .l4proto = IPPROTO_SCTP, .manip_pkt = sctp_manip_pkt, .in_range = nf_nat_l4proto_in_range, @@ -63,34 +61,3 @@ static const struct nf_nat_l4proto nf_nat_l4proto_sctp = { .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, #endif }; - -static int __init nf_nat_proto_sctp_init(void) -{ - int err; - - err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_sctp); - if (err < 0) - goto err1; - err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_sctp); - if (err < 0) - goto err2; - return 0; - -err2: - nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_sctp); -err1: - return err; -} - -static void __exit nf_nat_proto_sctp_exit(void) -{ - nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_sctp); - nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_sctp); -} - -module_init(nf_nat_proto_sctp_init); -module_exit(nf_nat_proto_sctp_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("SCTP NAT protocol helper"); -MODULE_AUTHOR("Patrick McHardy "); -- cgit v1.1 From b8ad652f9779976d0300ae199961e413859d5378 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Thu, 20 Oct 2016 18:33:03 +0200 Subject: netfilter: built-in NAT support for UDPlite CONFIG_NF_NAT_PROTO_UDPLITE is no more a tristate. When set to y, NAT support for UDPlite protocol is built-in into nf_nat.ko. footprint test: (nf_nat_proto_) |udplite || nf_nat --------------------------+--------++-------- no builtin | 408048 || 2241312 UDPLITE builtin | - || 2577256 Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_nat_l4proto.h | 3 +++ net/netfilter/Kconfig | 2 +- net/netfilter/Makefile | 5 ++--- net/netfilter/nf_nat_core.c | 4 ++++ net/netfilter/nf_nat_proto_udplite.c | 35 +--------------------------------- 5 files changed, 11 insertions(+), 38 deletions(-) diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h index 2cbaf38..3923150 100644 --- a/include/net/netfilter/nf_nat_l4proto.h +++ b/include/net/netfilter/nf_nat_l4proto.h @@ -60,6 +60,9 @@ extern const struct nf_nat_l4proto nf_nat_l4proto_dccp; #ifdef CONFIG_NF_NAT_PROTO_SCTP extern const struct nf_nat_l4proto nf_nat_l4proto_sctp; #endif +#ifdef CONFIG_NF_NAT_PROTO_UDPLITE +extern const struct nf_nat_l4proto nf_nat_l4proto_udplite; +#endif bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype, diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index ad72edf..496e1dc 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -389,7 +389,7 @@ config NF_NAT_PROTO_DCCP default NF_NAT && NF_CT_PROTO_DCCP config NF_NAT_PROTO_UDPLITE - tristate + bool depends on NF_NAT && NF_CT_PROTO_UDPLITE default NF_NAT && NF_CT_PROTO_UDPLITE diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 02ef6de..3b97d89 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -45,8 +45,10 @@ obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ nf_nat_proto_udp.o nf_nat_proto_tcp.o nf_nat_helper.o +# NAT protocols (nf_nat) nf_nat-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o nf_nat-$(CONFIG_NF_NAT_PROTO_SCTP) += nf_nat_proto_sctp.o +nf_nat-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o # generic transport layer logging obj-$(CONFIG_NF_LOG_COMMON) += nf_log_common.o @@ -57,9 +59,6 @@ obj-$(CONFIG_NF_LOG_NETDEV) += nf_log_netdev.o obj-$(CONFIG_NF_NAT) += nf_nat.o obj-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o -# NAT protocols (nf_nat) -obj-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o - # NAT helpers obj-$(CONFIG_NF_NAT_AMANDA) += nf_nat_amanda.o obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 80858bd..94b14c5 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -690,6 +690,10 @@ int nf_nat_l3proto_register(const struct nf_nat_l3proto *l3proto) RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_SCTP], &nf_nat_l4proto_sctp); #endif +#ifdef CONFIG_NF_NAT_PROTO_UDPLITE + RCU_INIT_POINTER(nf_nat_l4protos[l3proto->l3proto][IPPROTO_UDPLITE], + &nf_nat_l4proto_udplite); +#endif mutex_unlock(&nf_nat_proto_mutex); RCU_INIT_POINTER(nf_nat_l3protos[l3proto->l3proto], l3proto); diff --git a/net/netfilter/nf_nat_proto_udplite.c b/net/netfilter/nf_nat_proto_udplite.c index 58340c97..366bfbf 100644 --- a/net/netfilter/nf_nat_proto_udplite.c +++ b/net/netfilter/nf_nat_proto_udplite.c @@ -8,11 +8,9 @@ */ #include -#include #include #include -#include #include #include #include @@ -64,7 +62,7 @@ udplite_manip_pkt(struct sk_buff *skb, return true; } -static const struct nf_nat_l4proto nf_nat_l4proto_udplite = { +const struct nf_nat_l4proto nf_nat_l4proto_udplite = { .l4proto = IPPROTO_UDPLITE, .manip_pkt = udplite_manip_pkt, .in_range = nf_nat_l4proto_in_range, @@ -73,34 +71,3 @@ static const struct nf_nat_l4proto nf_nat_l4proto_udplite = { .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, #endif }; - -static int __init nf_nat_proto_udplite_init(void) -{ - int err; - - err = nf_nat_l4proto_register(NFPROTO_IPV4, &nf_nat_l4proto_udplite); - if (err < 0) - goto err1; - err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_udplite); - if (err < 0) - goto err2; - return 0; - -err2: - nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_udplite); -err1: - return err; -} - -static void __exit nf_nat_proto_udplite_fini(void) -{ - nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_udplite); - nf_nat_l4proto_unregister(NFPROTO_IPV4, &nf_nat_l4proto_udplite); -} - -module_init(nf_nat_proto_udplite_init); -module_exit(nf_nat_proto_udplite_fini); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("UDP-Lite NAT protocol helper"); -MODULE_AUTHOR("Patrick McHardy "); -- cgit v1.1 From 673ab46f345557e9d741e97ca0301280360d1af1 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Mon, 14 Nov 2016 22:39:25 +0800 Subject: netfilter: nf_log: do not assume ethernet header in netdev family In netdev family, we will handle non ethernet packets, so using eth_hdr(skb)->h_proto is incorrect. Meanwhile, we can use socket(AF_PACKET...) to sending packets, so skb->protocol is not always set in bridge family. Add an extra parameter into nf_log_l2packet to solve this issue. Fixes: 1fddf4bad0ac ("netfilter: nf_log: add packet logging for netdev family") Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_log.h | 4 +++- net/bridge/netfilter/nf_log_bridge.c | 3 ++- net/netfilter/nf_log_common.c | 3 ++- net/netfilter/nf_log_netdev.c | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index a559aa4..450f87f 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -109,7 +109,9 @@ void nf_log_dump_packet_common(struct nf_log_buf *m, u_int8_t pf, const struct net_device *out, const struct nf_loginfo *loginfo, const char *prefix); -void nf_log_l2packet(struct net *net, u_int8_t pf, unsigned int hooknum, +void nf_log_l2packet(struct net *net, u_int8_t pf, + __be16 protocol, + unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, diff --git a/net/bridge/netfilter/nf_log_bridge.c b/net/bridge/netfilter/nf_log_bridge.c index c197b1f..bd2b3c7 100644 --- a/net/bridge/netfilter/nf_log_bridge.c +++ b/net/bridge/netfilter/nf_log_bridge.c @@ -24,7 +24,8 @@ static void nf_log_bridge_packet(struct net *net, u_int8_t pf, const struct nf_loginfo *loginfo, const char *prefix) { - nf_log_l2packet(net, pf, hooknum, skb, in, out, loginfo, prefix); + nf_log_l2packet(net, pf, eth_hdr(skb)->h_proto, hooknum, skb, + in, out, loginfo, prefix); } static struct nf_logger nf_bridge_logger __read_mostly = { diff --git a/net/netfilter/nf_log_common.c b/net/netfilter/nf_log_common.c index ed9b808..dc61399 100644 --- a/net/netfilter/nf_log_common.c +++ b/net/netfilter/nf_log_common.c @@ -177,6 +177,7 @@ EXPORT_SYMBOL_GPL(nf_log_dump_packet_common); /* bridge and netdev logging families share this code. */ void nf_log_l2packet(struct net *net, u_int8_t pf, + __be16 protocol, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, @@ -184,7 +185,7 @@ void nf_log_l2packet(struct net *net, u_int8_t pf, const struct nf_loginfo *loginfo, const char *prefix) { - switch (eth_hdr(skb)->h_proto) { + switch (protocol) { case htons(ETH_P_IP): nf_log_packet(net, NFPROTO_IPV4, hooknum, skb, in, out, loginfo, "%s", prefix); diff --git a/net/netfilter/nf_log_netdev.c b/net/netfilter/nf_log_netdev.c index 1f64594..350eb14 100644 --- a/net/netfilter/nf_log_netdev.c +++ b/net/netfilter/nf_log_netdev.c @@ -23,7 +23,8 @@ static void nf_log_netdev_packet(struct net *net, u_int8_t pf, const struct nf_loginfo *loginfo, const char *prefix) { - nf_log_l2packet(net, pf, hooknum, skb, in, out, loginfo, prefix); + nf_log_l2packet(net, pf, skb->protocol, hooknum, skb, in, out, + loginfo, prefix); } static struct nf_logger nf_netdev_logger __read_mostly = { -- cgit v1.1 From a7647080d3657ac35f084d603e294d7624989793 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Mon, 14 Nov 2016 22:41:08 +0800 Subject: netfilter: nfnetlink_log: add "nf-logger-5-1" module alias name So we can autoload nfnetlink_log.ko when the user adding nft log group X rule in netdev family. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 763cb4d..200922b 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -1152,6 +1152,7 @@ MODULE_ALIAS_NF_LOGGER(AF_INET, 1); MODULE_ALIAS_NF_LOGGER(AF_INET6, 1); MODULE_ALIAS_NF_LOGGER(AF_BRIDGE, 1); MODULE_ALIAS_NF_LOGGER(3, 1); /* NFPROTO_ARP */ +MODULE_ALIAS_NF_LOGGER(5, 1); /* NFPROTO_NETDEV */ module_init(nfnetlink_log_init); module_exit(nfnetlink_log_fini); -- cgit v1.1 From 3fefeb88d002850e591339fed291eb6a795d9f21 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Tue, 15 Nov 2016 15:08:24 +0100 Subject: netfilter: nf_conntrack_tuple_common.h: fix #include To allow usage of enum ip_conntrack_dir in include/net/netns/conntrack.h, this patch encloses #include in a #ifndef __KERNEL__ directive, so that compiler errors caused by unwanted inclusion of include/linux/netfilter.h are avoided. In addition, #include line has been added to resolve correctly CTINFO2DIR macro. Signed-off-by: Davide Caratti Acked-by: Mikko Rapeli Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_conntrack_tuple_common.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h index a9c3834..526b424 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h @@ -2,7 +2,10 @@ #define _NF_CONNTRACK_TUPLE_COMMON_H #include +#ifndef __KERNEL__ #include +#endif +#include /* IP_CT_IS_REPLY */ enum ip_conntrack_dir { IP_CT_DIR_ORIGINAL, -- cgit v1.1 From c51d39010a1bccc9c1294e2d7c00005aefeb2b5c Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Tue, 15 Nov 2016 15:08:25 +0100 Subject: netfilter: conntrack: built-in support for DCCP CONFIG_NF_CT_PROTO_DCCP is no more a tristate. When set to y, connection tracking support for DCCP protocol is built-in into nf_conntrack.ko. footprint test: $ ls -l net/netfilter/nf_conntrack{_proto_dccp,}.ko \ net/ipv4/netfilter/nf_conntrack_ipv4.ko \ net/ipv6/netfilter/nf_conntrack_ipv6.ko (builtin)|| dccp | ipv4 | ipv6 | nf_conntrack ---------++--------+--------+--------+-------------- none || 469140 | 828755 | 828676 | 6141434 DCCP || - | 830566 | 829935 | 6533526 Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_dccp.h | 2 +- include/net/netfilter/ipv4/nf_conntrack_ipv4.h | 3 + include/net/netfilter/ipv6/nf_conntrack_ipv6.h | 3 + include/net/netns/conntrack.h | 14 +++++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 3 + net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 3 + net/netfilter/Kconfig | 6 +- net/netfilter/Makefile | 3 +- net/netfilter/nf_conntrack_proto_dccp.c | 79 ++++---------------------- 9 files changed, 41 insertions(+), 75 deletions(-) diff --git a/include/linux/netfilter/nf_conntrack_dccp.h b/include/linux/netfilter/nf_conntrack_dccp.h index 40dcc82..ff721d7 100644 --- a/include/linux/netfilter/nf_conntrack_dccp.h +++ b/include/linux/netfilter/nf_conntrack_dccp.h @@ -25,7 +25,7 @@ enum ct_dccp_roles { #define CT_DCCP_ROLE_MAX (__CT_DCCP_ROLE_MAX - 1) #ifdef __KERNEL__ -#include +#include struct nf_ct_dccp { u_int8_t role[IP_CT_DIR_MAX]; diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index 981c327..c2f155f 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -15,6 +15,9 @@ extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp; +#ifdef CONFIG_NF_CT_PROTO_DCCP +extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4; +#endif int nf_conntrack_ipv4_compat_init(void); void nf_conntrack_ipv4_compat_fini(void); diff --git a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h index a4c9936..5ec66c0 100644 --- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h +++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h @@ -6,6 +6,9 @@ extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6; +#ifdef CONFIG_NF_CT_PROTO_DCCP +extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6; +#endif #include extern struct ctl_table nf_ct_ipv6_sysctl_table[]; diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 3d06d94..440b781 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -6,6 +6,9 @@ #include #include #include +#ifdef CONFIG_NF_CT_PROTO_DCCP +#include +#endif #include struct ctl_table_header; @@ -48,12 +51,23 @@ struct nf_icmp_net { unsigned int timeout; }; +#ifdef CONFIG_NF_CT_PROTO_DCCP +struct nf_dccp_net { + struct nf_proto_net pn; + int dccp_loose; + unsigned int dccp_timeout[CT_DCCP_MAX + 1]; +}; +#endif + struct nf_ip_net { struct nf_generic_net generic; struct nf_tcp_net tcp; struct nf_udp_net udp; struct nf_icmp_net icmp; struct nf_icmp_net icmpv6; +#ifdef CONFIG_NF_CT_PROTO_DCCP + struct nf_dccp_net dccp; +#endif }; struct ct_pcpu { diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 7130ed5..cb3cf77 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -340,6 +340,9 @@ static struct nf_conntrack_l4proto *builtin_l4proto4[] = { &nf_conntrack_l4proto_tcp4, &nf_conntrack_l4proto_udp4, &nf_conntrack_l4proto_icmp, +#ifdef CONFIG_NF_CT_PROTO_DCCP + &nf_conntrack_l4proto_dccp4, +#endif }; static int ipv4_net_init(struct net *net) diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 500be28..f52338d 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -340,6 +340,9 @@ static struct nf_conntrack_l4proto *builtin_l4proto6[] = { &nf_conntrack_l4proto_tcp6, &nf_conntrack_l4proto_udp6, &nf_conntrack_l4proto_icmpv6, +#ifdef CONFIG_NF_CT_PROTO_DCCP + &nf_conntrack_l4proto_dccp6, +#endif }; static int ipv6_net_init(struct net *net) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 496e1dc..27a3d8c 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -146,14 +146,14 @@ config NF_CONNTRACK_LABELS to connection tracking entries. It selected by the connlabel match. config NF_CT_PROTO_DCCP - tristate 'DCCP protocol connection tracking support' + bool 'DCCP protocol connection tracking support' depends on NETFILTER_ADVANCED - default IP_DCCP + default y help With this option enabled, the layer 3 independent connection tracking code will be able to do state tracking on DCCP connections. - If unsure, say 'N'. + If unsure, say Y. config NF_CT_PROTO_GRE tristate diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 3b97d89..bbd0cc0 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -5,6 +5,7 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o +nf_conntrack-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o obj-$(CONFIG_NETFILTER) = netfilter.o @@ -16,8 +17,6 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o # connection tracking obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o -# SCTP protocol connection tracking -obj-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 073b047..b68ce6a 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -9,7 +9,6 @@ * */ #include -#include #include #include #include @@ -384,17 +383,9 @@ dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = }, }; -/* this module per-net specifics */ -static unsigned int dccp_net_id __read_mostly; -struct dccp_net { - struct nf_proto_net pn; - int dccp_loose; - unsigned int dccp_timeout[CT_DCCP_MAX + 1]; -}; - -static inline struct dccp_net *dccp_pernet(struct net *net) +static inline struct nf_dccp_net *dccp_pernet(struct net *net) { - return net_generic(net, dccp_net_id); + return &net->ct.nf_ct_proto.dccp; } static bool dccp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, @@ -424,7 +415,7 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, unsigned int *timeouts) { struct net *net = nf_ct_net(ct); - struct dccp_net *dn; + struct nf_dccp_net *dn; struct dccp_hdr _dh, *dh; const char *msg; u_int8_t state; @@ -719,7 +710,7 @@ static int dccp_nlattr_size(void) static int dccp_timeout_nlattr_to_obj(struct nlattr *tb[], struct net *net, void *data) { - struct dccp_net *dn = dccp_pernet(net); + struct nf_dccp_net *dn = dccp_pernet(net); unsigned int *timeouts = data; int i; @@ -820,7 +811,7 @@ static struct ctl_table dccp_sysctl_table[] = { #endif /* CONFIG_SYSCTL */ static int dccp_kmemdup_sysctl_table(struct net *net, struct nf_proto_net *pn, - struct dccp_net *dn) + struct nf_dccp_net *dn) { #ifdef CONFIG_SYSCTL if (pn->ctl_table) @@ -850,7 +841,7 @@ static int dccp_kmemdup_sysctl_table(struct net *net, struct nf_proto_net *pn, static int dccp_init_net(struct net *net, u_int16_t proto) { - struct dccp_net *dn = dccp_pernet(net); + struct nf_dccp_net *dn = dccp_pernet(net); struct nf_proto_net *pn = &dn->pn; if (!pn->users) { @@ -868,7 +859,7 @@ static int dccp_init_net(struct net *net, u_int16_t proto) return dccp_kmemdup_sysctl_table(net, pn, dn); } -static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = { +struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4 __read_mostly = { .l3proto = AF_INET, .l4proto = IPPROTO_DCCP, .name = "dccp", @@ -898,11 +889,11 @@ static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = { .nla_policy = dccp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ - .net_id = &dccp_net_id, .init_net = dccp_init_net, }; +EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_dccp4); -static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { +struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6 __read_mostly = { .l3proto = AF_INET6, .l4proto = IPPROTO_DCCP, .name = "dccp", @@ -932,56 +923,6 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { .nla_policy = dccp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ - .net_id = &dccp_net_id, .init_net = dccp_init_net, }; - -static struct nf_conntrack_l4proto *dccp_proto[] = { - &dccp_proto4, - &dccp_proto6, -}; - -static __net_init int dccp_net_init(struct net *net) -{ - return nf_ct_l4proto_pernet_register(net, dccp_proto, - ARRAY_SIZE(dccp_proto)); -} - -static __net_exit void dccp_net_exit(struct net *net) -{ - nf_ct_l4proto_pernet_unregister(net, dccp_proto, - ARRAY_SIZE(dccp_proto)); -} - -static struct pernet_operations dccp_net_ops = { - .init = dccp_net_init, - .exit = dccp_net_exit, - .id = &dccp_net_id, - .size = sizeof(struct dccp_net), -}; - -static int __init nf_conntrack_proto_dccp_init(void) -{ - int ret; - - ret = register_pernet_subsys(&dccp_net_ops); - if (ret < 0) - return ret; - ret = nf_ct_l4proto_register(dccp_proto, ARRAY_SIZE(dccp_proto)); - if (ret < 0) - unregister_pernet_subsys(&dccp_net_ops); - return ret; -} - -static void __exit nf_conntrack_proto_dccp_fini(void) -{ - nf_ct_l4proto_unregister(dccp_proto, ARRAY_SIZE(dccp_proto)); - unregister_pernet_subsys(&dccp_net_ops); -} - -module_init(nf_conntrack_proto_dccp_init); -module_exit(nf_conntrack_proto_dccp_fini); - -MODULE_AUTHOR("Patrick McHardy "); -MODULE_DESCRIPTION("DCCP connection tracking protocol helper"); -MODULE_LICENSE("GPL"); +EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_dccp6); -- cgit v1.1 From a85406afeb3e045b001b2aac5b4f89f4266fede3 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Tue, 15 Nov 2016 15:08:26 +0100 Subject: netfilter: conntrack: built-in support for SCTP CONFIG_NF_CT_PROTO_SCTP is no more a tristate. When set to y, connection tracking support for SCTP protocol is built-in into nf_conntrack.ko. footprint test: $ ls -l net/netfilter/nf_conntrack{_proto_sctp,}.ko \ net/ipv4/netfilter/nf_conntrack_ipv4.ko \ net/ipv6/netfilter/nf_conntrack_ipv6.ko (builtin)|| sctp | ipv4 | ipv6 | nf_conntrack ---------++--------+--------+--------+-------------- none || 498243 | 828755 | 828676 | 6141434 SCTP || - | 829254 | 829175 | 6547872 Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_conntrack_ipv4.h | 3 + include/net/netfilter/ipv6/nf_conntrack_ipv6.h | 3 + include/net/netns/conntrack.h | 13 +++++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 3 + net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 3 + net/netfilter/Kconfig | 7 +-- net/netfilter/Makefile | 2 +- net/netfilter/nf_conntrack_proto_sctp.c | 76 +++----------------------- 8 files changed, 38 insertions(+), 72 deletions(-) diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index c2f155f..5f1fc15 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -18,6 +18,9 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp; #ifdef CONFIG_NF_CT_PROTO_DCCP extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4; #endif +#ifdef CONFIG_NF_CT_PROTO_SCTP +extern struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4; +#endif int nf_conntrack_ipv4_compat_init(void); void nf_conntrack_ipv4_compat_fini(void); diff --git a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h index 5ec66c0..f70d191 100644 --- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h +++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h @@ -9,6 +9,9 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6; #ifdef CONFIG_NF_CT_PROTO_DCCP extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6; #endif +#ifdef CONFIG_NF_CT_PROTO_SCTP +extern struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6; +#endif #include extern struct ctl_table nf_ct_ipv6_sysctl_table[]; diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 440b781..17724c6 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -9,6 +9,9 @@ #ifdef CONFIG_NF_CT_PROTO_DCCP #include #endif +#ifdef CONFIG_NF_CT_PROTO_SCTP +#include +#endif #include struct ctl_table_header; @@ -59,6 +62,13 @@ struct nf_dccp_net { }; #endif +#ifdef CONFIG_NF_CT_PROTO_SCTP +struct nf_sctp_net { + struct nf_proto_net pn; + unsigned int timeouts[SCTP_CONNTRACK_MAX]; +}; +#endif + struct nf_ip_net { struct nf_generic_net generic; struct nf_tcp_net tcp; @@ -68,6 +78,9 @@ struct nf_ip_net { #ifdef CONFIG_NF_CT_PROTO_DCCP struct nf_dccp_net dccp; #endif +#ifdef CONFIG_NF_CT_PROTO_SCTP + struct nf_sctp_net sctp; +#endif }; struct ct_pcpu { diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index cb3cf77..0a9d354 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -343,6 +343,9 @@ static struct nf_conntrack_l4proto *builtin_l4proto4[] = { #ifdef CONFIG_NF_CT_PROTO_DCCP &nf_conntrack_l4proto_dccp4, #endif +#ifdef CONFIG_NF_CT_PROTO_SCTP + &nf_conntrack_l4proto_sctp4, +#endif }; static int ipv4_net_init(struct net *net) diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index f52338d..1d8daaf 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -343,6 +343,9 @@ static struct nf_conntrack_l4proto *builtin_l4proto6[] = { #ifdef CONFIG_NF_CT_PROTO_DCCP &nf_conntrack_l4proto_dccp6, #endif +#ifdef CONFIG_NF_CT_PROTO_SCTP + &nf_conntrack_l4proto_sctp6, +#endif }; static int ipv6_net_init(struct net *net) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 27a3d8c..29c0bf0 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -159,15 +159,14 @@ config NF_CT_PROTO_GRE tristate config NF_CT_PROTO_SCTP - tristate 'SCTP protocol connection tracking support' + bool 'SCTP protocol connection tracking support' depends on NETFILTER_ADVANCED - default IP_SCTP + default y help With this option enabled, the layer 3 independent connection tracking code will be able to do state tracking on SCTP connections. - If you want to compile it as a module, say M here and read - . If unsure, say `N'. + If unsure, say Y. config NF_CT_PROTO_UDPLITE tristate 'UDP-Lite protocol connection tracking support' diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index bbd0cc0..6545c28 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -6,6 +6,7 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o nf_conntrack-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o +nf_conntrack-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o obj-$(CONFIG_NETFILTER) = netfilter.o @@ -18,7 +19,6 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o -obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o # netlink interface for nf_conntrack diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index d096c2d..a0efde3 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -144,15 +143,9 @@ static const u8 sctp_conntracks[2][11][SCTP_CONNTRACK_MAX] = { } }; -static unsigned int sctp_net_id __read_mostly; -struct sctp_net { - struct nf_proto_net pn; - unsigned int timeouts[SCTP_CONNTRACK_MAX]; -}; - -static inline struct sctp_net *sctp_pernet(struct net *net) +static inline struct nf_sctp_net *sctp_pernet(struct net *net) { - return net_generic(net, sctp_net_id); + return &net->ct.nf_ct_proto.sctp; } static bool sctp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, @@ -600,7 +593,7 @@ static int sctp_timeout_nlattr_to_obj(struct nlattr *tb[], struct net *net, void *data) { unsigned int *timeouts = data; - struct sctp_net *sn = sctp_pernet(net); + struct nf_sctp_net *sn = sctp_pernet(net); int i; /* set default SCTP timeouts. */ @@ -708,7 +701,7 @@ static struct ctl_table sctp_sysctl_table[] = { #endif static int sctp_kmemdup_sysctl_table(struct nf_proto_net *pn, - struct sctp_net *sn) + struct nf_sctp_net *sn) { #ifdef CONFIG_SYSCTL if (pn->ctl_table) @@ -735,7 +728,7 @@ static int sctp_kmemdup_sysctl_table(struct nf_proto_net *pn, static int sctp_init_net(struct net *net, u_int16_t proto) { - struct sctp_net *sn = sctp_pernet(net); + struct nf_sctp_net *sn = sctp_pernet(net); struct nf_proto_net *pn = &sn->pn; if (!pn->users) { @@ -748,7 +741,7 @@ static int sctp_init_net(struct net *net, u_int16_t proto) return sctp_kmemdup_sysctl_table(pn, sn); } -static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { +struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .l3proto = PF_INET, .l4proto = IPPROTO_SCTP, .name = "sctp", @@ -778,11 +771,11 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .nla_policy = sctp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ - .net_id = &sctp_net_id, .init_net = sctp_init_net, }; +EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_sctp4); -static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { +struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .l3proto = PF_INET6, .l4proto = IPPROTO_SCTP, .name = "sctp", @@ -812,57 +805,6 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #endif - .net_id = &sctp_net_id, .init_net = sctp_init_net, }; - -static struct nf_conntrack_l4proto *sctp_proto[] = { - &nf_conntrack_l4proto_sctp4, - &nf_conntrack_l4proto_sctp6, -}; - -static int sctp_net_init(struct net *net) -{ - return nf_ct_l4proto_pernet_register(net, sctp_proto, - ARRAY_SIZE(sctp_proto)); -} - -static void sctp_net_exit(struct net *net) -{ - nf_ct_l4proto_pernet_unregister(net, sctp_proto, - ARRAY_SIZE(sctp_proto)); -} - -static struct pernet_operations sctp_net_ops = { - .init = sctp_net_init, - .exit = sctp_net_exit, - .id = &sctp_net_id, - .size = sizeof(struct sctp_net), -}; - -static int __init nf_conntrack_proto_sctp_init(void) -{ - int ret; - - ret = register_pernet_subsys(&sctp_net_ops); - if (ret < 0) - return ret; - ret = nf_ct_l4proto_register(sctp_proto, ARRAY_SIZE(sctp_proto)); - if (ret < 0) - unregister_pernet_subsys(&sctp_net_ops); - return ret; -} - -static void __exit nf_conntrack_proto_sctp_fini(void) -{ - nf_ct_l4proto_unregister(sctp_proto, ARRAY_SIZE(sctp_proto)); - unregister_pernet_subsys(&sctp_net_ops); -} - -module_init(nf_conntrack_proto_sctp_init); -module_exit(nf_conntrack_proto_sctp_fini); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kiran Kumar Immidi"); -MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP"); -MODULE_ALIAS("ip_conntrack_proto_sctp"); +EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_sctp6); -- cgit v1.1 From 9b91c96c5d1f9da79438292f8c82f65cbf078645 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Tue, 15 Nov 2016 15:08:27 +0100 Subject: netfilter: conntrack: built-in support for UDPlite CONFIG_NF_CT_PROTO_UDPLITE is no more a tristate. When set to y, connection tracking support for UDPlite protocol is built-in into nf_conntrack.ko. footprint test: $ ls -l net/netfilter/nf_conntrack{_proto_udplite,}.ko \ net/ipv4/netfilter/nf_conntrack_ipv4.ko \ net/ipv6/netfilter/nf_conntrack_ipv6.ko (builtin)|| udplite| ipv4 | ipv6 |nf_conntrack ---------++--------+--------+--------+-------------- none || 432538 | 828755 | 828676 | 6141434 UDPlite || - | 829649 | 829362 | 6498204 Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_conntrack_ipv4.h | 3 + include/net/netfilter/ipv6/nf_conntrack_ipv6.h | 3 + include/net/netns/conntrack.h | 16 ++++++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 3 + net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 3 + net/netfilter/Kconfig | 5 +- net/netfilter/Makefile | 2 +- net/netfilter/nf_conntrack_proto_udplite.c | 79 +++----------------------- 8 files changed, 41 insertions(+), 73 deletions(-) diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index 5f1fc15..919e4e8 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -21,6 +21,9 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4; #ifdef CONFIG_NF_CT_PROTO_SCTP extern struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4; #endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE +extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4; +#endif int nf_conntrack_ipv4_compat_init(void); void nf_conntrack_ipv4_compat_fini(void); diff --git a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h index f70d191..eaea968 100644 --- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h +++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h @@ -12,6 +12,9 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6; #ifdef CONFIG_NF_CT_PROTO_SCTP extern struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6; #endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE +extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6; +#endif #include extern struct ctl_table nf_ct_ipv6_sysctl_table[]; diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 17724c6..cf799fc 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -69,6 +69,19 @@ struct nf_sctp_net { }; #endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE +enum udplite_conntrack { + UDPLITE_CT_UNREPLIED, + UDPLITE_CT_REPLIED, + UDPLITE_CT_MAX +}; + +struct nf_udplite_net { + struct nf_proto_net pn; + unsigned int timeouts[UDPLITE_CT_MAX]; +}; +#endif + struct nf_ip_net { struct nf_generic_net generic; struct nf_tcp_net tcp; @@ -81,6 +94,9 @@ struct nf_ip_net { #ifdef CONFIG_NF_CT_PROTO_SCTP struct nf_sctp_net sctp; #endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE + struct nf_udplite_net udplite; +#endif }; struct ct_pcpu { diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 0a9d354..22fce4f 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -346,6 +346,9 @@ static struct nf_conntrack_l4proto *builtin_l4proto4[] = { #ifdef CONFIG_NF_CT_PROTO_SCTP &nf_conntrack_l4proto_sctp4, #endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE + &nf_conntrack_l4proto_udplite4, +#endif }; static int ipv4_net_init(struct net *net) diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 1d8daaf..389f712 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -346,6 +346,9 @@ static struct nf_conntrack_l4proto *builtin_l4proto6[] = { #ifdef CONFIG_NF_CT_PROTO_SCTP &nf_conntrack_l4proto_sctp6, #endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE + &nf_conntrack_l4proto_udplite6, +#endif }; static int ipv6_net_init(struct net *net) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 29c0bf0..def4be0 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -169,14 +169,15 @@ config NF_CT_PROTO_SCTP If unsure, say Y. config NF_CT_PROTO_UDPLITE - tristate 'UDP-Lite protocol connection tracking support' + bool 'UDP-Lite protocol connection tracking support' depends on NETFILTER_ADVANCED + default y help With this option enabled, the layer 3 independent connection tracking code will be able to do state tracking on UDP-Lite connections. - To compile it as a module, choose M here. If unsure, say N. + If unsure, say Y. config NF_CONNTRACK_AMANDA tristate "Amanda backup protocol support" diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 6545c28..e4c8c1d 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -7,6 +7,7 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o nf_conntrack-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o nf_conntrack-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o +nf_conntrack-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o obj-$(CONFIG_NETFILTER) = netfilter.o @@ -19,7 +20,6 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o -obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o # netlink interface for nf_conntrack obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 7808604..c35f7bf 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -24,26 +23,14 @@ #include #include -enum udplite_conntrack { - UDPLITE_CT_UNREPLIED, - UDPLITE_CT_REPLIED, - UDPLITE_CT_MAX -}; - static unsigned int udplite_timeouts[UDPLITE_CT_MAX] = { [UDPLITE_CT_UNREPLIED] = 30*HZ, [UDPLITE_CT_REPLIED] = 180*HZ, }; -static unsigned int udplite_net_id __read_mostly; -struct udplite_net { - struct nf_proto_net pn; - unsigned int timeouts[UDPLITE_CT_MAX]; -}; - -static inline struct udplite_net *udplite_pernet(struct net *net) +static inline struct nf_udplite_net *udplite_pernet(struct net *net) { - return net_generic(net, udplite_net_id); + return &net->ct.nf_ct_proto.udplite; } static bool udplite_pkt_to_tuple(const struct sk_buff *skb, @@ -178,7 +165,7 @@ static int udplite_timeout_nlattr_to_obj(struct nlattr *tb[], struct net *net, void *data) { unsigned int *timeouts = data; - struct udplite_net *un = udplite_pernet(net); + struct nf_udplite_net *un = udplite_pernet(net); /* set default timeouts for UDPlite. */ timeouts[UDPLITE_CT_UNREPLIED] = un->timeouts[UDPLITE_CT_UNREPLIED]; @@ -237,7 +224,7 @@ static struct ctl_table udplite_sysctl_table[] = { #endif /* CONFIG_SYSCTL */ static int udplite_kmemdup_sysctl_table(struct nf_proto_net *pn, - struct udplite_net *un) + struct nf_udplite_net *un) { #ifdef CONFIG_SYSCTL if (pn->ctl_table) @@ -257,7 +244,7 @@ static int udplite_kmemdup_sysctl_table(struct nf_proto_net *pn, static int udplite_init_net(struct net *net, u_int16_t proto) { - struct udplite_net *un = udplite_pernet(net); + struct nf_udplite_net *un = udplite_pernet(net); struct nf_proto_net *pn = &un->pn; if (!pn->users) { @@ -270,7 +257,7 @@ static int udplite_init_net(struct net *net, u_int16_t proto) return udplite_kmemdup_sysctl_table(pn, un); } -static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = +struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = { .l3proto = PF_INET, .l4proto = IPPROTO_UDPLITE, @@ -299,11 +286,11 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = .nla_policy = udplite_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ - .net_id = &udplite_net_id, .init_net = udplite_init_net, }; +EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udplite4); -static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = +struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = { .l3proto = PF_INET6, .l4proto = IPPROTO_UDPLITE, @@ -332,54 +319,6 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = .nla_policy = udplite_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ - .net_id = &udplite_net_id, .init_net = udplite_init_net, }; - -static struct nf_conntrack_l4proto *udplite_proto[] = { - &nf_conntrack_l4proto_udplite4, - &nf_conntrack_l4proto_udplite6, -}; - -static int udplite_net_init(struct net *net) -{ - return nf_ct_l4proto_pernet_register(net, udplite_proto, - ARRAY_SIZE(udplite_proto)); -} - -static void udplite_net_exit(struct net *net) -{ - nf_ct_l4proto_pernet_unregister(net, udplite_proto, - ARRAY_SIZE(udplite_proto)); -} - -static struct pernet_operations udplite_net_ops = { - .init = udplite_net_init, - .exit = udplite_net_exit, - .id = &udplite_net_id, - .size = sizeof(struct udplite_net), -}; - -static int __init nf_conntrack_proto_udplite_init(void) -{ - int ret; - - ret = register_pernet_subsys(&udplite_net_ops); - if (ret < 0) - return ret; - ret = nf_ct_l4proto_register(udplite_proto, ARRAY_SIZE(udplite_proto)); - if (ret < 0) - unregister_pernet_subsys(&udplite_net_ops); - return ret; -} - -static void __exit nf_conntrack_proto_udplite_exit(void) -{ - nf_ct_l4proto_unregister(udplite_proto, ARRAY_SIZE(udplite_proto)); - unregister_pernet_subsys(&udplite_net_ops); -} - -module_init(nf_conntrack_proto_udplite_init); -module_exit(nf_conntrack_proto_udplite_exit); - -MODULE_LICENSE("GPL"); +EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udplite6); -- cgit v1.1 From a379854d91b2cb0af07b0f62845449f4dacbd673 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Nov 2016 21:36:39 +0100 Subject: netfilter: conntrack: remove unused init_net hook since adf0516845bcd0 ("netfilter: remove ip_conntrack* sysctl compat code") the only user (ipv4 tracker) sets this to an empty stub function. After this change nf_ct_l3proto_pernet_register() is also empty, but this will change in a followup patch to add conditional register of the hooks. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 3 --- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 6 ------ net/netfilter/nf_conntrack_proto.c | 8 -------- 3 files changed, 17 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 8992e42..cf8f3df 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -63,9 +63,6 @@ struct nf_conntrack_l3proto { size_t nla_size; - /* Init l3proto pernet data */ - int (*init_net)(struct net *net); - /* Module (if any) which this is connected to. */ struct module *me; }; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 22fce4f..a006b65 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -307,11 +307,6 @@ static struct nf_sockopt_ops so_getorigdst = { .owner = THIS_MODULE, }; -static int ipv4_init_net(struct net *net) -{ - return 0; -} - struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { .l3proto = PF_INET, .name = "ipv4", @@ -325,7 +320,6 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { .nlattr_to_tuple = ipv4_nlattr_to_tuple, .nla_policy = ipv4_nla_policy, #endif - .init_net = ipv4_init_net, .me = THIS_MODULE, }; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 9bd3464..b218e70 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -193,14 +193,6 @@ EXPORT_SYMBOL_GPL(nf_ct_l3proto_register); int nf_ct_l3proto_pernet_register(struct net *net, struct nf_conntrack_l3proto *proto) { - int ret; - - if (proto->init_net) { - ret = proto->init_net(net); - if (ret < 0) - return ret; - } - return 0; } EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register); -- cgit v1.1 From ecb2421b5ddf48e6e116fced7f74c985bb546138 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Nov 2016 21:36:40 +0100 Subject: netfilter: add and use nf_ct_netns_get/put currently aliased to try_module_get/_put. Will be changed in next patch when we add functions to make use of ->net argument to store usercount per l3proto tracker. This is needed to avoid registering the conntrack hooks in all netns and later only enable connection tracking in those that need conntrack. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack.h | 4 ++++ net/ipv4/netfilter/ipt_CLUSTERIP.c | 4 ++-- net/ipv4/netfilter/ipt_SYNPROXY.c | 4 ++-- net/ipv6/netfilter/ip6t_SYNPROXY.c | 4 ++-- net/netfilter/nf_conntrack_proto.c | 12 ++++++++++++ net/netfilter/nft_ct.c | 26 +++++++++++++------------- net/netfilter/xt_CONNSECMARK.c | 4 ++-- net/netfilter/xt_CT.c | 6 +++--- net/netfilter/xt_connbytes.c | 4 ++-- net/netfilter/xt_connlabel.c | 6 +++--- net/netfilter/xt_connlimit.c | 6 +++--- net/netfilter/xt_connmark.c | 8 ++++---- net/netfilter/xt_conntrack.c | 4 ++-- net/netfilter/xt_helper.c | 4 ++-- net/netfilter/xt_state.c | 4 ++-- 15 files changed, 58 insertions(+), 42 deletions(-) diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index d9d52c0..5916aa9 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -181,6 +181,10 @@ static inline void nf_ct_put(struct nf_conn *ct) int nf_ct_l3proto_try_module_get(unsigned short l3proto); void nf_ct_l3proto_module_put(unsigned short l3proto); +/* load module; enable/disable conntrack in this namespace */ +int nf_ct_netns_get(struct net *net, u8 nfproto); +void nf_ct_netns_put(struct net *net, u8 nfproto); + /* * Allocate a hashtable of hlist_head (if nulls == 0), * or hlist_nulls_head (if nulls == 1) diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index e6e206f..21db00d 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -419,7 +419,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) } cipinfo->config = config; - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -444,7 +444,7 @@ static void clusterip_tg_destroy(const struct xt_tgdtor_param *par) clusterip_config_put(cipinfo->config); - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_get(par->net, par->family); } #ifdef CONFIG_COMPAT diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index 3614116..30c0de5 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -418,12 +418,12 @@ static int synproxy_tg4_check(const struct xt_tgchk_param *par) e->ip.invflags & XT_INV_PROTO) return -EINVAL; - return nf_ct_l3proto_try_module_get(par->family); + return nf_ct_netns_get(par->net, par->family); } static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_target synproxy_tg4_reg __read_mostly = { diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 99a1216..98c8dd3 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -440,12 +440,12 @@ static int synproxy_tg6_check(const struct xt_tgchk_param *par) e->ipv6.invflags & XT_INV_PROTO) return -EINVAL; - return nf_ct_l3proto_try_module_get(par->family); + return nf_ct_netns_get(par->net, par->family); } static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_target synproxy_tg6_reg __read_mostly = { diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index b218e70..948f1e2 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -125,6 +125,18 @@ void nf_ct_l3proto_module_put(unsigned short l3proto) } EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); +int nf_ct_netns_get(struct net *net, u8 nfproto) +{ + return nf_ct_l3proto_try_module_get(nfproto); +} +EXPORT_SYMBOL_GPL(nf_ct_netns_get); + +void nf_ct_netns_put(struct net *net, u8 nfproto) +{ + nf_ct_l3proto_module_put(nfproto); +} +EXPORT_SYMBOL_GPL(nf_ct_netns_put); + struct nf_conntrack_l4proto * nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num) { diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 6837348..e6baeae 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -208,37 +208,37 @@ static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { [NFTA_CT_SREG] = { .type = NLA_U32 }, }; -static int nft_ct_l3proto_try_module_get(uint8_t family) +static int nft_ct_netns_get(struct net *net, uint8_t family) { int err; if (family == NFPROTO_INET) { - err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4); + err = nf_ct_netns_get(net, NFPROTO_IPV4); if (err < 0) goto err1; - err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6); + err = nf_ct_netns_get(net, NFPROTO_IPV6); if (err < 0) goto err2; } else { - err = nf_ct_l3proto_try_module_get(family); + err = nf_ct_netns_get(net, family); if (err < 0) goto err1; } return 0; err2: - nf_ct_l3proto_module_put(NFPROTO_IPV4); + nf_ct_netns_put(net, NFPROTO_IPV4); err1: return err; } -static void nft_ct_l3proto_module_put(uint8_t family) +static void nft_ct_netns_put(struct net *net, uint8_t family) { if (family == NFPROTO_INET) { - nf_ct_l3proto_module_put(NFPROTO_IPV4); - nf_ct_l3proto_module_put(NFPROTO_IPV6); + nf_ct_netns_put(net, NFPROTO_IPV4); + nf_ct_netns_put(net, NFPROTO_IPV6); } else - nf_ct_l3proto_module_put(family); + nf_ct_netns_put(net, family); } static int nft_ct_get_init(const struct nft_ctx *ctx, @@ -342,7 +342,7 @@ static int nft_ct_get_init(const struct nft_ctx *ctx, if (err < 0) return err; - err = nft_ct_l3proto_try_module_get(ctx->afi->family); + err = nft_ct_netns_get(ctx->net, ctx->afi->family); if (err < 0) return err; @@ -390,7 +390,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx, if (err < 0) goto err1; - err = nft_ct_l3proto_try_module_get(ctx->afi->family); + err = nft_ct_netns_get(ctx->net, ctx->afi->family); if (err < 0) goto err1; @@ -405,7 +405,7 @@ err1: static void nft_ct_get_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) { - nft_ct_l3proto_module_put(ctx->afi->family); + nf_ct_netns_put(ctx->net, ctx->afi->family); } static void nft_ct_set_destroy(const struct nft_ctx *ctx, @@ -423,7 +423,7 @@ static void nft_ct_set_destroy(const struct nft_ctx *ctx, break; } - nft_ct_l3proto_module_put(ctx->afi->family); + nft_ct_netns_put(ctx->net, ctx->afi->family); } static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index e04dc28..da56c06 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -106,7 +106,7 @@ static int connsecmark_tg_check(const struct xt_tgchk_param *par) return -EINVAL; } - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -115,7 +115,7 @@ static int connsecmark_tg_check(const struct xt_tgchk_param *par) static void connsecmark_tg_destroy(const struct xt_tgdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_target connsecmark_tg_reg __read_mostly = { diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index 6669e68..95c7503 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -216,7 +216,7 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, goto err1; #endif - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) goto err1; @@ -260,7 +260,7 @@ out: err3: nf_ct_tmpl_free(ct); err2: - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); err1: return ret; } @@ -341,7 +341,7 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, if (help) module_put(help->helper->me); - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); xt_ct_destroy_timeout(ct); nf_ct_put(info->ct); diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index d4bec26..cad0b7b 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -110,7 +110,7 @@ static int connbytes_mt_check(const struct xt_mtchk_param *par) sinfo->direction != XT_CONNBYTES_DIR_BOTH) return -EINVAL; - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -129,7 +129,7 @@ static int connbytes_mt_check(const struct xt_mtchk_param *par) static void connbytes_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_match connbytes_mt_reg __read_mostly = { diff --git a/net/netfilter/xt_connlabel.c b/net/netfilter/xt_connlabel.c index 03d66f1..7827128 100644 --- a/net/netfilter/xt_connlabel.c +++ b/net/netfilter/xt_connlabel.c @@ -61,7 +61,7 @@ static int connlabel_mt_check(const struct xt_mtchk_param *par) return -EINVAL; } - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) { pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -70,14 +70,14 @@ static int connlabel_mt_check(const struct xt_mtchk_param *par) ret = nf_connlabels_get(par->net, info->bit); if (ret < 0) - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); return ret; } static void connlabel_mt_destroy(const struct xt_mtdtor_param *par) { nf_connlabels_put(par->net); - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_match connlabels_mt_reg __read_mostly = { diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index bb38453..2aff2b7 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -368,7 +368,7 @@ static int connlimit_mt_check(const struct xt_mtchk_param *par) net_get_random_once(&connlimit_rnd, sizeof(connlimit_rnd)); - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) { pr_info("cannot load conntrack support for " "address family %u\n", par->family); @@ -378,7 +378,7 @@ static int connlimit_mt_check(const struct xt_mtchk_param *par) /* init private data */ info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL); if (info->data == NULL) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); return -ENOMEM; } @@ -414,7 +414,7 @@ static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) const struct xt_connlimit_info *info = par->matchinfo; unsigned int i; - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i) destroy_tree(&info->data->climit_root4[i]); diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index b83e158..9935d50 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -77,7 +77,7 @@ static int connmark_tg_check(const struct xt_tgchk_param *par) { int ret; - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -86,7 +86,7 @@ static int connmark_tg_check(const struct xt_tgchk_param *par) static void connmark_tg_destroy(const struct xt_tgdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static bool @@ -107,7 +107,7 @@ static int connmark_mt_check(const struct xt_mtchk_param *par) { int ret; - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -116,7 +116,7 @@ static int connmark_mt_check(const struct xt_mtchk_param *par) static void connmark_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_target connmark_tg_reg __read_mostly = { diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 2dea15e..c0fb217 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -271,7 +271,7 @@ static int conntrack_mt_check(const struct xt_mtchk_param *par) { int ret; - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -280,7 +280,7 @@ static int conntrack_mt_check(const struct xt_mtchk_param *par) static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_match conntrack_mt_reg[] __read_mostly = { diff --git a/net/netfilter/xt_helper.c b/net/netfilter/xt_helper.c index f679dd4..38a7815 100644 --- a/net/netfilter/xt_helper.c +++ b/net/netfilter/xt_helper.c @@ -59,7 +59,7 @@ static int helper_mt_check(const struct xt_mtchk_param *par) struct xt_helper_info *info = par->matchinfo; int ret; - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) { pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -71,7 +71,7 @@ static int helper_mt_check(const struct xt_mtchk_param *par) static void helper_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_match helper_mt_reg __read_mostly = { diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index a507922..5746a33 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -43,7 +43,7 @@ static int state_mt_check(const struct xt_mtchk_param *par) { int ret; - ret = nf_ct_l3proto_try_module_get(par->family); + ret = nf_ct_netns_get(par->net, par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", par->family); @@ -52,7 +52,7 @@ static int state_mt_check(const struct xt_mtchk_param *par) static void state_mt_destroy(const struct xt_mtdtor_param *par) { - nf_ct_l3proto_module_put(par->family); + nf_ct_netns_put(par->net, par->family); } static struct xt_match state_mt_reg __read_mostly = { -- cgit v1.1 From a357b3f80bc8d785ac7bdb99eb10ad0ba51275c9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Nov 2016 21:36:41 +0100 Subject: netfilter: nat: add dependencies on conntrack module MASQUERADE, S/DNAT and REDIRECT already call functions that depend on the conntrack module. However, since the conntrack hooks are now registered in a lazy fashion (i.e., only when needed) a symbol reference is not enough. Thus, when something is added to a nat table, make sure that it will see packets by calling nf_ct_netns_get() which will register the conntrack hooks in the current netns. An alternative would be to add these dependencies to the NAT table. However, that has problems when using non-modular builds -- we might register e.g. ipv6 conntrack before its initcall has run, leading to NULL deref crashes since its per-netns storage has not yet been allocated. Adding the dependency in the modules instead has the advantage that nat table also does not register its hooks until rules are added. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/ipt_MASQUERADE.c | 8 +++++++- net/netfilter/xt_NETMAP.c | 11 +++++++++-- net/netfilter/xt_REDIRECT.c | 12 ++++++++++-- net/netfilter/xt_nat.c | 18 +++++++++++++++++- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 34cfb9b..a03e4e7 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -41,7 +41,7 @@ static int masquerade_tg_check(const struct xt_tgchk_param *par) pr_debug("bad rangesize %u\n", mr->rangesize); return -EINVAL; } - return 0; + return nf_ct_netns_get(par->net, par->family); } static unsigned int @@ -59,6 +59,11 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) xt_out(par)); } +static void masquerade_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_netns_put(par->net, par->family); +} + static struct xt_target masquerade_tg_reg __read_mostly = { .name = "MASQUERADE", .family = NFPROTO_IPV4, @@ -67,6 +72,7 @@ static struct xt_target masquerade_tg_reg __read_mostly = { .table = "nat", .hooks = 1 << NF_INET_POST_ROUTING, .checkentry = masquerade_tg_check, + .destroy = masquerade_tg_destroy, .me = THIS_MODULE, }; diff --git a/net/netfilter/xt_NETMAP.c b/net/netfilter/xt_NETMAP.c index 94d0b54..e45a012 100644 --- a/net/netfilter/xt_NETMAP.c +++ b/net/netfilter/xt_NETMAP.c @@ -60,7 +60,12 @@ static int netmap_tg6_checkentry(const struct xt_tgchk_param *par) if (!(range->flags & NF_NAT_RANGE_MAP_IPS)) return -EINVAL; - return 0; + return nf_ct_netns_get(par->net, par->family); +} + +static void netmap_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_netns_put(par->net, par->family); } static unsigned int @@ -111,7 +116,7 @@ static int netmap_tg4_check(const struct xt_tgchk_param *par) pr_debug("bad rangesize %u.\n", mr->rangesize); return -EINVAL; } - return 0; + return nf_ct_netns_get(par->net, par->family); } static struct xt_target netmap_tg_reg[] __read_mostly = { @@ -127,6 +132,7 @@ static struct xt_target netmap_tg_reg[] __read_mostly = { (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN), .checkentry = netmap_tg6_checkentry, + .destroy = netmap_tg_destroy, .me = THIS_MODULE, }, { @@ -141,6 +147,7 @@ static struct xt_target netmap_tg_reg[] __read_mostly = { (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN), .checkentry = netmap_tg4_check, + .destroy = netmap_tg_destroy, .me = THIS_MODULE, }, }; diff --git a/net/netfilter/xt_REDIRECT.c b/net/netfilter/xt_REDIRECT.c index 651dce6..98a4c6d 100644 --- a/net/netfilter/xt_REDIRECT.c +++ b/net/netfilter/xt_REDIRECT.c @@ -40,7 +40,13 @@ static int redirect_tg6_checkentry(const struct xt_tgchk_param *par) if (range->flags & NF_NAT_RANGE_MAP_IPS) return -EINVAL; - return 0; + + return nf_ct_netns_get(par->net, par->family); +} + +static void redirect_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_netns_put(par->net, par->family); } /* FIXME: Take multiple ranges --RR */ @@ -56,7 +62,7 @@ static int redirect_tg4_check(const struct xt_tgchk_param *par) pr_debug("bad rangesize %u.\n", mr->rangesize); return -EINVAL; } - return 0; + return nf_ct_netns_get(par->net, par->family); } static unsigned int @@ -72,6 +78,7 @@ static struct xt_target redirect_tg_reg[] __read_mostly = { .revision = 0, .table = "nat", .checkentry = redirect_tg6_checkentry, + .destroy = redirect_tg_destroy, .target = redirect_tg6, .targetsize = sizeof(struct nf_nat_range), .hooks = (1 << NF_INET_PRE_ROUTING) | @@ -85,6 +92,7 @@ static struct xt_target redirect_tg_reg[] __read_mostly = { .table = "nat", .target = redirect_tg4, .checkentry = redirect_tg4_check, + .destroy = redirect_tg_destroy, .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c index bea7464c..8107b3e 100644 --- a/net/netfilter/xt_nat.c +++ b/net/netfilter/xt_nat.c @@ -23,7 +23,17 @@ static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par) par->target->name); return -EINVAL; } - return 0; + return nf_ct_netns_get(par->net, par->family); +} + +static int xt_nat_checkentry(const struct xt_tgchk_param *par) +{ + return nf_ct_netns_get(par->net, par->family); +} + +static void xt_nat_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_netns_put(par->net, par->family); } static void xt_nat_convert_range(struct nf_nat_range *dst, @@ -106,6 +116,7 @@ static struct xt_target xt_nat_target_reg[] __read_mostly = { .name = "SNAT", .revision = 0, .checkentry = xt_nat_checkentry_v0, + .destroy = xt_nat_destroy, .target = xt_snat_target_v0, .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), .family = NFPROTO_IPV4, @@ -118,6 +129,7 @@ static struct xt_target xt_nat_target_reg[] __read_mostly = { .name = "DNAT", .revision = 0, .checkentry = xt_nat_checkentry_v0, + .destroy = xt_nat_destroy, .target = xt_dnat_target_v0, .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), .family = NFPROTO_IPV4, @@ -129,6 +141,8 @@ static struct xt_target xt_nat_target_reg[] __read_mostly = { { .name = "SNAT", .revision = 1, + .checkentry = xt_nat_checkentry, + .destroy = xt_nat_destroy, .target = xt_snat_target_v1, .targetsize = sizeof(struct nf_nat_range), .table = "nat", @@ -139,6 +153,8 @@ static struct xt_target xt_nat_target_reg[] __read_mostly = { { .name = "DNAT", .revision = 1, + .checkentry = xt_nat_checkentry, + .destroy = xt_nat_destroy, .target = xt_dnat_target_v1, .targetsize = sizeof(struct nf_nat_range), .table = "nat", -- cgit v1.1 From 20afd423976ef3df0ce5719909ffd4416f9ef986 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Nov 2016 21:36:42 +0100 Subject: netfilter: nf_tables: add conntrack dependencies for nat/masq/redir expressions so that conntrack core will add the needed hooks in this namespace. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nft_masq_ipv4.c | 7 +++++++ net/ipv4/netfilter/nft_redir_ipv4.c | 7 +++++++ net/ipv6/netfilter/nft_masq_ipv6.c | 7 +++++++ net/ipv6/netfilter/nft_redir_ipv6.c | 7 +++++++ net/netfilter/nft_masq.c | 2 +- net/netfilter/nft_nat.c | 11 ++++++++++- net/netfilter/nft_redir.c | 2 +- 7 files changed, 40 insertions(+), 3 deletions(-) diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c index 4d69f99..a0ea8aa 100644 --- a/net/ipv4/netfilter/nft_masq_ipv4.c +++ b/net/ipv4/netfilter/nft_masq_ipv4.c @@ -35,12 +35,19 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr, &range, nft_out(pkt)); } +static void +nft_masq_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) +{ + nf_ct_netns_put(ctx->net, NFPROTO_IPV4); +} + static struct nft_expr_type nft_masq_ipv4_type; static const struct nft_expr_ops nft_masq_ipv4_ops = { .type = &nft_masq_ipv4_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), .eval = nft_masq_ipv4_eval, .init = nft_masq_init, + .destroy = nft_masq_ipv4_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, }; diff --git a/net/ipv4/netfilter/nft_redir_ipv4.c b/net/ipv4/netfilter/nft_redir_ipv4.c index 62c18e6..1650ed2 100644 --- a/net/ipv4/netfilter/nft_redir_ipv4.c +++ b/net/ipv4/netfilter/nft_redir_ipv4.c @@ -38,12 +38,19 @@ static void nft_redir_ipv4_eval(const struct nft_expr *expr, regs->verdict.code = nf_nat_redirect_ipv4(pkt->skb, &mr, nft_hook(pkt)); } +static void +nft_redir_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) +{ + nf_ct_netns_put(ctx->net, NFPROTO_IPV4); +} + static struct nft_expr_type nft_redir_ipv4_type; static const struct nft_expr_ops nft_redir_ipv4_ops = { .type = &nft_redir_ipv4_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)), .eval = nft_redir_ipv4_eval, .init = nft_redir_init, + .destroy = nft_redir_ipv4_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, }; diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c index 93d758f..6c5b5b1 100644 --- a/net/ipv6/netfilter/nft_masq_ipv6.c +++ b/net/ipv6/netfilter/nft_masq_ipv6.c @@ -36,12 +36,19 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr, nft_out(pkt)); } +static void +nft_masq_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) +{ + nf_ct_netns_put(ctx->net, NFPROTO_IPV6); +} + static struct nft_expr_type nft_masq_ipv6_type; static const struct nft_expr_ops nft_masq_ipv6_ops = { .type = &nft_masq_ipv6_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), .eval = nft_masq_ipv6_eval, .init = nft_masq_init, + .destroy = nft_masq_ipv6_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, }; diff --git a/net/ipv6/netfilter/nft_redir_ipv6.c b/net/ipv6/netfilter/nft_redir_ipv6.c index 2850fcd..f5ac080 100644 --- a/net/ipv6/netfilter/nft_redir_ipv6.c +++ b/net/ipv6/netfilter/nft_redir_ipv6.c @@ -39,12 +39,19 @@ static void nft_redir_ipv6_eval(const struct nft_expr *expr, nf_nat_redirect_ipv6(pkt->skb, &range, nft_hook(pkt)); } +static void +nft_redir_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) +{ + nf_ct_netns_put(ctx->net, NFPROTO_IPV6); +} + static struct nft_expr_type nft_redir_ipv6_type; static const struct nft_expr_ops nft_redir_ipv6_ops = { .type = &nft_redir_ipv6_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)), .eval = nft_redir_ipv6_eval, .init = nft_redir_init, + .destroy = nft_redir_ipv6_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, }; diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index bf92de0..11ce016 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -77,7 +77,7 @@ int nft_masq_init(const struct nft_ctx *ctx, } } - return 0; + return nf_ct_netns_get(ctx->net, ctx->afi->family); } EXPORT_SYMBOL_GPL(nft_masq_init); diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index ee2d717..19a7bf3 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -209,7 +209,7 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return -EINVAL; } - return 0; + return nf_ct_netns_get(ctx->net, family); } static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) @@ -257,12 +257,21 @@ nla_put_failure: return -1; } +static void +nft_nat_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) +{ + const struct nft_nat *priv = nft_expr_priv(expr); + + nf_ct_netns_put(ctx->net, priv->family); +} + static struct nft_expr_type nft_nat_type; static const struct nft_expr_ops nft_nat_ops = { .type = &nft_nat_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), .eval = nft_nat_eval, .init = nft_nat_init, + .destroy = nft_nat_destroy, .dump = nft_nat_dump, .validate = nft_nat_validate, }; diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index 967e09b..40dcd05 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -79,7 +79,7 @@ int nft_redir_init(const struct nft_ctx *ctx, return -EINVAL; } - return 0; + return nf_ct_netns_get(ctx->net, ctx->afi->family); } EXPORT_SYMBOL_GPL(nft_redir_init); -- cgit v1.1 From 0c66dc1ea3f0366221f8a5a16c73f01ea9259678 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Nov 2016 21:36:43 +0100 Subject: netfilter: conntrack: register hooks in netns when needed by ruleset This makes use of nf_ct_netns_get/put added in previous patch. We add get/put functions to nf_conntrack_l3proto structure, ipv4 and ipv6 then implement use-count to track how many users (nft or xtables modules) have a dependency on ipv4 and/or ipv6 connection tracking functionality. When count reaches zero, the hooks are unregistered. This delays activation of connection tracking inside a namespace until stateful firewall rule or nat rule gets added. This patch breaks backwards compatibility in the sense that connection tracking won't be active anymore when the protocol tracker module is loaded. This breaks e.g. setups that ctnetlink for flow accounting and the like, without any '-m conntrack' packet filter rules. Followup patch restores old behavour and makes new delayed scheme optional via sysctl. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 4 ++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 55 ++++++++++++++++++++------ net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 54 +++++++++++++++++++------ net/netfilter/nf_conntrack_proto.c | 38 +++++++++++++++++- 4 files changed, 127 insertions(+), 24 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index cf8f3df..e7dcd72 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -52,6 +52,10 @@ struct nf_conntrack_l3proto { int (*tuple_to_nlattr)(struct sk_buff *skb, const struct nf_conntrack_tuple *t); + /* Called when netns wants to use connection tracking */ + int (*net_ns_get)(struct net *); + void (*net_ns_put)(struct net *); + /* * Calculate size of tuple nlattr */ diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index a006b65..6f37544 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -31,6 +31,13 @@ #include #include +static int conntrack4_net_id __read_mostly; +static DEFINE_MUTEX(register_ipv4_hooks); + +struct conntrack4_net { + unsigned int users; +}; + static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_tuple *tuple) { @@ -307,6 +314,38 @@ static struct nf_sockopt_ops so_getorigdst = { .owner = THIS_MODULE, }; +static int ipv4_hooks_register(struct net *net) +{ + struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id); + int err = 0; + + mutex_lock(®ister_ipv4_hooks); + + cnet->users++; + if (cnet->users > 1) + goto out_unlock; + + err = nf_register_net_hooks(net, ipv4_conntrack_ops, + ARRAY_SIZE(ipv4_conntrack_ops)); + + if (err) + cnet->users = 0; + out_unlock: + mutex_unlock(®ister_ipv4_hooks); + return err; +} + +static void ipv4_hooks_unregister(struct net *net) +{ + struct conntrack4_net *cnet = net_generic(net, conntrack4_net_id); + + mutex_lock(®ister_ipv4_hooks); + if (cnet->users && (--cnet->users == 0)) + nf_unregister_net_hooks(net, ipv4_conntrack_ops, + ARRAY_SIZE(ipv4_conntrack_ops)); + mutex_unlock(®ister_ipv4_hooks); +} + struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { .l3proto = PF_INET, .name = "ipv4", @@ -320,6 +359,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { .nlattr_to_tuple = ipv4_nlattr_to_tuple, .nla_policy = ipv4_nla_policy, #endif + .net_ns_get = ipv4_hooks_register, + .net_ns_put = ipv4_hooks_unregister, .me = THIS_MODULE, }; @@ -372,6 +413,8 @@ static void ipv4_net_exit(struct net *net) static struct pernet_operations ipv4_net_ops = { .init = ipv4_net_init, .exit = ipv4_net_exit, + .id = &conntrack4_net_id, + .size = sizeof(struct conntrack4_net), }; static int __init nf_conntrack_l3proto_ipv4_init(void) @@ -393,17 +436,10 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) goto cleanup_sockopt; } - ret = nf_register_hooks(ipv4_conntrack_ops, - ARRAY_SIZE(ipv4_conntrack_ops)); - if (ret < 0) { - pr_err("nf_conntrack_ipv4: can't register hooks.\n"); - goto cleanup_pernet; - } - ret = nf_ct_l4proto_register(builtin_l4proto4, ARRAY_SIZE(builtin_l4proto4)); if (ret < 0) - goto cleanup_hooks; + goto cleanup_pernet; ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4); if (ret < 0) { @@ -415,8 +451,6 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) cleanup_l4proto: nf_ct_l4proto_unregister(builtin_l4proto4, ARRAY_SIZE(builtin_l4proto4)); - cleanup_hooks: - nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); cleanup_pernet: unregister_pernet_subsys(&ipv4_net_ops); cleanup_sockopt: @@ -430,7 +464,6 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4); nf_ct_l4proto_unregister(builtin_l4proto4, ARRAY_SIZE(builtin_l4proto4)); - nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); unregister_pernet_subsys(&ipv4_net_ops); nf_unregister_sockopt(&so_getorigdst); } diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 389f712..72fe480 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -34,6 +34,13 @@ #include #include +static int conntrack6_net_id; +static DEFINE_MUTEX(register_ipv6_hooks); + +struct conntrack6_net { + unsigned int users; +}; + static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_tuple *tuple) { @@ -308,6 +315,36 @@ static int ipv6_nlattr_tuple_size(void) } #endif +static int ipv6_hooks_register(struct net *net) +{ + struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id); + int err = 0; + + mutex_lock(®ister_ipv6_hooks); + cnet->users++; + if (cnet->users > 1) + goto out_unlock; + + err = nf_register_net_hooks(net, ipv6_conntrack_ops, + ARRAY_SIZE(ipv6_conntrack_ops)); + if (err) + cnet->users = 0; + out_unlock: + mutex_unlock(®ister_ipv6_hooks); + return err; +} + +static void ipv6_hooks_unregister(struct net *net) +{ + struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id); + + mutex_lock(®ister_ipv6_hooks); + if (cnet->users && (--cnet->users == 0)) + nf_unregister_net_hooks(net, ipv6_conntrack_ops, + ARRAY_SIZE(ipv6_conntrack_ops)); + mutex_unlock(®ister_ipv6_hooks); +} + struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { .l3proto = PF_INET6, .name = "ipv6", @@ -321,6 +358,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { .nlattr_to_tuple = ipv6_nlattr_to_tuple, .nla_policy = ipv6_nla_policy, #endif + .net_ns_get = ipv6_hooks_register, + .net_ns_put = ipv6_hooks_unregister, .me = THIS_MODULE, }; @@ -379,6 +418,8 @@ static void ipv6_net_exit(struct net *net) static struct pernet_operations ipv6_net_ops = { .init = ipv6_net_init, .exit = ipv6_net_exit, + .id = &conntrack6_net_id, + .size = sizeof(struct conntrack6_net), }; static int __init nf_conntrack_l3proto_ipv6_init(void) @@ -398,18 +439,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) if (ret < 0) goto cleanup_sockopt; - ret = nf_register_hooks(ipv6_conntrack_ops, - ARRAY_SIZE(ipv6_conntrack_ops)); - if (ret < 0) { - pr_err("nf_conntrack_ipv6: can't register pre-routing defrag " - "hook.\n"); - goto cleanup_pernet; - } - ret = nf_ct_l4proto_register(builtin_l4proto6, ARRAY_SIZE(builtin_l4proto6)); if (ret < 0) - goto cleanup_hooks; + goto cleanup_pernet; ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6); if (ret < 0) { @@ -420,8 +453,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) cleanup_l4proto: nf_ct_l4proto_unregister(builtin_l4proto6, ARRAY_SIZE(builtin_l4proto6)); - cleanup_hooks: - nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); cleanup_pernet: unregister_pernet_subsys(&ipv6_net_ops); cleanup_sockopt: @@ -435,7 +466,6 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6); nf_ct_l4proto_unregister(builtin_l4proto6, ARRAY_SIZE(builtin_l4proto6)); - nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); unregister_pernet_subsys(&ipv6_net_ops); nf_unregister_sockopt(&so_getorigdst6); } diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 948f1e2..758688b 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -127,12 +127,48 @@ EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); int nf_ct_netns_get(struct net *net, u8 nfproto) { - return nf_ct_l3proto_try_module_get(nfproto); + const struct nf_conntrack_l3proto *l3proto; + int ret; + + might_sleep(); + + ret = nf_ct_l3proto_try_module_get(nfproto); + if (ret < 0) + return ret; + + /* we already have a reference, can't fail */ + rcu_read_lock(); + l3proto = __nf_ct_l3proto_find(nfproto); + rcu_read_unlock(); + + if (!l3proto->net_ns_get) + return 0; + + ret = l3proto->net_ns_get(net); + if (ret < 0) + nf_ct_l3proto_module_put(nfproto); + + return ret; } EXPORT_SYMBOL_GPL(nf_ct_netns_get); void nf_ct_netns_put(struct net *net, u8 nfproto) { + const struct nf_conntrack_l3proto *l3proto; + + might_sleep(); + + /* same as nf_conntrack_netns_get(), reference assumed */ + rcu_read_lock(); + l3proto = __nf_ct_l3proto_find(nfproto); + rcu_read_unlock(); + + if (WARN_ON(!l3proto)) + return; + + if (l3proto->net_ns_put) + l3proto->net_ns_put(net); + nf_ct_l3proto_module_put(nfproto); } EXPORT_SYMBOL_GPL(nf_ct_netns_put); -- cgit v1.1 From 481fa3734769b67f00ed09a42f2a6a8cbd00b869 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Nov 2016 21:36:44 +0100 Subject: netfilter: conntrack: add nf_conntrack_default_on sysctl This switch (default on) can be used to disable automatic registration of connection tracking functionality in newly created network namespaces. This means that when net namespace goes down (or the tracker protocol module is unloaded) we *might* have to unregister the hooks. We can either add another per-netns variable that tells if the hooks got registered by default, or, alternatively, just call the protocol _put() function and have the callee deal with a possible 'extra' put() operation that doesn't pair with a get() one. This uses the latter approach, i.e. a put() without a get has no effect. Conntrack is still enabled automatically regardless of the new sysctl setting if the new net namespace requires connection tracking, e.g. when NAT rules are created. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- Documentation/networking/nf_conntrack-sysctl.txt | 11 +++++++++++ include/net/netfilter/nf_conntrack_l3proto.h | 9 +++++++++ net/netfilter/nf_conntrack_proto.c | 19 ++++++++++++++++++- net/netfilter/nf_conntrack_standalone.c | 10 ++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/nf_conntrack-sysctl.txt b/Documentation/networking/nf_conntrack-sysctl.txt index 433b672..497d668 100644 --- a/Documentation/networking/nf_conntrack-sysctl.txt +++ b/Documentation/networking/nf_conntrack-sysctl.txt @@ -96,6 +96,17 @@ nf_conntrack_max - INTEGER Size of connection tracking table. Default value is nf_conntrack_buckets value * 4. +nf_conntrack_default_on - BOOLEAN + 0 - don't register conntrack in new net namespaces + 1 - register conntrack in new net namespaces (default) + + This controls wheter newly created network namespaces have connection + tracking enabled by default. It will be enabled automatically + regardless of this setting if the new net namespace requires + connection tracking, e.g. when NAT rules are created. + This setting is only visible in initial user namespace, it has no + effect on existing namespaces. + nf_conntrack_tcp_be_liberal - BOOLEAN 0 - disabled (default) not 0 - enabled diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index e7dcd72..e01559b 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -73,9 +73,18 @@ struct nf_conntrack_l3proto { extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX]; +#ifdef CONFIG_SYSCTL /* Protocol pernet registration. */ int nf_ct_l3proto_pernet_register(struct net *net, struct nf_conntrack_l3proto *proto); +#else +static inline int nf_ct_l3proto_pernet_register(struct net *n, + struct nf_conntrack_l3proto *p) +{ + return 0; +} +#endif + void nf_ct_l3proto_pernet_unregister(struct net *net, struct nf_conntrack_l3proto *proto); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 758688b..2d6ee18 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -238,12 +238,19 @@ out_unlock: } EXPORT_SYMBOL_GPL(nf_ct_l3proto_register); +#ifdef CONFIG_SYSCTL +extern unsigned int nf_conntrack_default_on; + int nf_ct_l3proto_pernet_register(struct net *net, struct nf_conntrack_l3proto *proto) { - return 0; + if (nf_conntrack_default_on == 0) + return 0; + + return proto->net_ns_get ? proto->net_ns_get(net) : 0; } EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register); +#endif void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) { @@ -264,6 +271,16 @@ EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister); void nf_ct_l3proto_pernet_unregister(struct net *net, struct nf_conntrack_l3proto *proto) { + /* + * nf_conntrack_default_on *might* have registered hooks. + * ->net_ns_put must cope with more puts() than get(), i.e. + * if nf_conntrack_default_on was 0 at time of + * nf_ct_l3proto_pernet_register invocation this net_ns_put() + * should be a noop. + */ + if (proto->net_ns_put) + proto->net_ns_put(net); + /* Remove all contrack entries for this protocol */ nf_ct_iterate_cleanup(net, kill_l3proto, proto, 0, 0); } diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 5f446cd..d009ae6 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -452,6 +452,9 @@ static int log_invalid_proto_max __read_mostly = 255; /* size the user *wants to set */ static unsigned int nf_conntrack_htable_size_user __read_mostly; +extern unsigned int nf_conntrack_default_on; +unsigned int nf_conntrack_default_on __read_mostly = 1; + static int nf_conntrack_hash_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -517,6 +520,13 @@ static struct ctl_table nf_ct_sysctl_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "nf_conntrack_default_on", + .data = &nf_conntrack_default_on, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; -- cgit v1.1 From c7a61319ad35ef74630662010231dbbcc1ade1b0 Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Fri, 2 Dec 2016 15:57:49 +0100 Subject: net: phy: dp83848: Support ethernet pause frames According to the documentation, the PHYs supported by this driver can also support pause frames. Announce this to be so. Tested with a TI83822I. Acked-by: Andrew F. Davis Signed-off-by: Jesper Nilsson Signed-off-by: David S. Miller --- drivers/net/phy/dp83848.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c index 800b39f..320d0dc 100644 --- a/drivers/net/phy/dp83848.c +++ b/drivers/net/phy/dp83848.c @@ -88,7 +88,9 @@ MODULE_DEVICE_TABLE(mdio, dp83848_tbl); .phy_id = _id, \ .phy_id_mask = 0xfffffff0, \ .name = _name, \ - .features = PHY_BASIC_FEATURES, \ + .features = (PHY_BASIC_FEATURES | \ + SUPPORTED_Pause | \ + SUPPORTED_Asym_Pause), \ .flags = PHY_HAS_INTERRUPT, \ \ .soft_reset = genphy_soft_reset, \ -- cgit v1.1 From c5e3deb8a38453037b89e0b0485d3b031896e8eb Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 2 Dec 2016 21:17:15 -0500 Subject: bnxt_en: Re-factor bnxt_setup_tc(). Add a new function bnxt_setup_mq_tc() to handle MQPRIO. This new function will be called during ETS setup when we add DCBNL in the next patch. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 18 ++++++++++-------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 0e4f168..7664281 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6337,17 +6337,10 @@ static int bnxt_change_mtu(struct net_device *dev, int new_mtu) return 0; } -static int bnxt_setup_tc(struct net_device *dev, u32 handle, __be16 proto, - struct tc_to_netdev *ntc) +int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) { struct bnxt *bp = netdev_priv(dev); bool sh = false; - u8 tc; - - if (ntc->type != TC_SETUP_MQPRIO) - return -EINVAL; - - tc = ntc->tc; if (tc > bp->max_tc) { netdev_err(dev, "too many traffic classes requested: %d Max supported is %d\n", @@ -6390,6 +6383,15 @@ static int bnxt_setup_tc(struct net_device *dev, u32 handle, __be16 proto, return 0; } +static int bnxt_setup_tc(struct net_device *dev, u32 handle, __be16 proto, + struct tc_to_netdev *ntc) +{ + if (ntc->type != TC_SETUP_MQPRIO) + return -EINVAL; + + return bnxt_setup_mq_tc(dev, ntc->tc); +} + #ifdef CONFIG_RFS_ACCEL static bool bnxt_fltr_match(struct bnxt_ntuple_filter *f1, struct bnxt_ntuple_filter *f2) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 47be789..fcd07ee 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1225,5 +1225,6 @@ int bnxt_hwrm_set_link_setting(struct bnxt *, bool, bool); int bnxt_hwrm_fw_set_time(struct bnxt *); int bnxt_open_nic(struct bnxt *, bool, bool); int bnxt_close_nic(struct bnxt *, bool, bool); +int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); int bnxt_get_max_rings(struct bnxt *, int *, int *, bool); #endif -- cgit v1.1 From 87c374ded0b2cfe50bb1e7648a4ca06df13fa399 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 2 Dec 2016 21:17:16 -0500 Subject: bnxt_en: Update firmware header file to latest 1.6.0. Latest interface has the latest DCB command structs. Get and store the max number of lossless TCs the hardware can support. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 28 +- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 5 +- drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 1725 ++++++++++++++--------- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 8 +- 4 files changed, 1069 insertions(+), 697 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7664281..7ba5a99 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -186,11 +186,11 @@ static const u16 bnxt_vf_req_snif[] = { }; static const u16 bnxt_async_events_arr[] = { - HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE, - HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD, - HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED, - HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE, - HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE, + ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE, + ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD, + ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED, + ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE, + ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE, }; static bool bnxt_vf_pciid(enum board_idx idx) @@ -1476,8 +1476,8 @@ next_rx_no_prod: } #define BNXT_GET_EVENT_PORT(data) \ - ((data) & \ - HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK) + ((data) & \ + ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK) static int bnxt_async_event_process(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl) @@ -1486,7 +1486,7 @@ static int bnxt_async_event_process(struct bnxt *bp, /* TODO CHIMP_FW: Define event id's for link change, error etc */ switch (event_id) { - case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE: { + case ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE: { u32 data1 = le32_to_cpu(cmpl->event_data1); struct bnxt_link_info *link_info = &bp->link_info; @@ -1502,13 +1502,13 @@ static int bnxt_async_event_process(struct bnxt *bp, set_bit(BNXT_LINK_SPEED_CHNG_SP_EVENT, &bp->sp_event); /* fall thru */ } - case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE: + case ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE: set_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event); break; - case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD: + case ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD: set_bit(BNXT_HWRM_PF_UNLOAD_SP_EVENT, &bp->sp_event); break; - case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED: { + case ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED: { u32 data1 = le32_to_cpu(cmpl->event_data1); u16 port_id = BNXT_GET_EVENT_PORT(data1); @@ -1521,7 +1521,7 @@ static int bnxt_async_event_process(struct bnxt *bp, set_bit(BNXT_HWRM_PORT_MODULE_SP_EVENT, &bp->sp_event); break; } - case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE: + case ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE: if (BNXT_PF(bp)) goto async_event_process_exit; set_bit(BNXT_RESET_TASK_SILENT_SP_EVENT, &bp->sp_event); @@ -4261,12 +4261,16 @@ static int bnxt_hwrm_queue_qportcfg(struct bnxt *bp) goto qportcfg_exit; } bp->max_tc = resp->max_configurable_queues; + bp->max_lltc = resp->max_configurable_lossless_queues; if (bp->max_tc > BNXT_MAX_QUEUE) bp->max_tc = BNXT_MAX_QUEUE; if (resp->queue_cfg_info & QUEUE_QPORTCFG_RESP_QUEUE_CFG_INFO_ASYM_CFG) bp->max_tc = 1; + if (bp->max_lltc > bp->max_tc) + bp->max_lltc = bp->max_tc; + qptr = &resp->queue_id0; for (i = 0; i < bp->max_tc; i++) { bp->q_info[i].queue_id = *qptr++; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index fcd07ee..1f3d852 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -11,10 +11,10 @@ #define BNXT_H #define DRV_MODULE_NAME "bnxt_en" -#define DRV_MODULE_VERSION "1.5.0" +#define DRV_MODULE_VERSION "1.6.0" #define DRV_VER_MAJ 1 -#define DRV_VER_MIN 5 +#define DRV_VER_MIN 6 #define DRV_VER_UPD 0 struct tx_bd { @@ -1010,6 +1010,7 @@ struct bnxt { u32 rss_hash_cfg; u8 max_tc; + u8 max_lltc; /* lossless TCs */ struct bnxt_queue_info q_info[BNXT_MAX_QUEUE]; unsigned int current_interval; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 0456d5b..2ddfa51 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -1,6 +1,7 @@ /* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2014-2016 Broadcom Corporation + * Copyright (c) 2016 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -10,29 +11,22 @@ #ifndef BNXT_HSI_H #define BNXT_HSI_H -/* per-context HW statistics -- chip view */ -struct ctx_hw_stats { - __le64 rx_ucast_pkts; - __le64 rx_mcast_pkts; - __le64 rx_bcast_pkts; - __le64 rx_discard_pkts; - __le64 rx_drop_pkts; - __le64 rx_ucast_bytes; - __le64 rx_mcast_bytes; - __le64 rx_bcast_bytes; - __le64 tx_ucast_pkts; - __le64 tx_mcast_pkts; - __le64 tx_bcast_pkts; - __le64 tx_discard_pkts; - __le64 tx_drop_pkts; - __le64 tx_ucast_bytes; - __le64 tx_mcast_bytes; - __le64 tx_bcast_bytes; - __le64 tpa_pkts; - __le64 tpa_bytes; - __le64 tpa_events; - __le64 tpa_aborts; -}; +/* HSI and HWRM Specification 1.6.0 */ +#define HWRM_VERSION_MAJOR 1 +#define HWRM_VERSION_MINOR 6 +#define HWRM_VERSION_UPDATE 0 + +#define HWRM_VERSION_STR "1.6.0" +/* + * Following is the signature for HWRM message field that indicates not + * applicable (All F's). Need to cast it the size of the field if needed. + */ +#define HWRM_NA_SIGNATURE ((__le32)(-1)) +#define HWRM_MAX_REQ_LEN (128) /* hwrm_func_buf_rgtr */ +#define HWRM_MAX_RESP_LEN (176) /* hwrm_func_qstats */ +#define HW_HASH_INDEX_SIZE 0x80 /* 7 bit indirection table index. */ +#define HW_HASH_KEY_SIZE 40 +#define HWRM_RESP_VALID_KEY 1 /* valid key for HWRM response */ /* Statistics Ejection Buffer Completion Record (16 bytes) */ struct eject_cmpl { @@ -50,77 +44,77 @@ struct eject_cmpl { /* HWRM Completion Record (16 bytes) */ struct hwrm_cmpl { __le16 type; - #define HWRM_CMPL_TYPE_MASK 0x3fUL - #define HWRM_CMPL_TYPE_SFT 0 - #define HWRM_CMPL_TYPE_HWRM_DONE 0x20UL + #define CMPL_TYPE_MASK 0x3fUL + #define CMPL_TYPE_SFT 0 + #define CMPL_TYPE_HWRM_DONE 0x20UL __le16 sequence_id; __le32 unused_1; __le32 v; - #define HWRM_CMPL_V 0x1UL + #define CMPL_V 0x1UL __le32 unused_3; }; /* HWRM Forwarded Request (16 bytes) */ struct hwrm_fwd_req_cmpl { __le16 req_len_type; - #define HWRM_FWD_REQ_CMPL_TYPE_MASK 0x3fUL - #define HWRM_FWD_REQ_CMPL_TYPE_SFT 0 - #define HWRM_FWD_REQ_CMPL_TYPE_HWRM_FWD_REQ 0x22UL - #define HWRM_FWD_REQ_CMPL_REQ_LEN_MASK 0xffc0UL - #define HWRM_FWD_REQ_CMPL_REQ_LEN_SFT 6 + #define FWD_REQ_CMPL_TYPE_MASK 0x3fUL + #define FWD_REQ_CMPL_TYPE_SFT 0 + #define FWD_REQ_CMPL_TYPE_HWRM_FWD_REQ 0x22UL + #define FWD_REQ_CMPL_REQ_LEN_MASK 0xffc0UL + #define FWD_REQ_CMPL_REQ_LEN_SFT 6 __le16 source_id; __le32 unused_0; __le32 req_buf_addr_v[2]; - #define HWRM_FWD_REQ_CMPL_V 0x1UL - #define HWRM_FWD_REQ_CMPL_REQ_BUF_ADDR_MASK 0xfffffffeUL - #define HWRM_FWD_REQ_CMPL_REQ_BUF_ADDR_SFT 1 + #define FWD_REQ_CMPL_V 0x1UL + #define FWD_REQ_CMPL_REQ_BUF_ADDR_MASK 0xfffffffeUL + #define FWD_REQ_CMPL_REQ_BUF_ADDR_SFT 1 }; /* HWRM Forwarded Response (16 bytes) */ struct hwrm_fwd_resp_cmpl { __le16 type; - #define HWRM_FWD_RESP_CMPL_TYPE_MASK 0x3fUL - #define HWRM_FWD_RESP_CMPL_TYPE_SFT 0 - #define HWRM_FWD_RESP_CMPL_TYPE_HWRM_FWD_RESP 0x24UL + #define FWD_RESP_CMPL_TYPE_MASK 0x3fUL + #define FWD_RESP_CMPL_TYPE_SFT 0 + #define FWD_RESP_CMPL_TYPE_HWRM_FWD_RESP 0x24UL __le16 source_id; __le16 resp_len; __le16 unused_1; __le32 resp_buf_addr_v[2]; - #define HWRM_FWD_RESP_CMPL_V 0x1UL - #define HWRM_FWD_RESP_CMPL_RESP_BUF_ADDR_MASK 0xfffffffeUL - #define HWRM_FWD_RESP_CMPL_RESP_BUF_ADDR_SFT 1 + #define FWD_RESP_CMPL_V 0x1UL + #define FWD_RESP_CMPL_RESP_BUF_ADDR_MASK 0xfffffffeUL + #define FWD_RESP_CMPL_RESP_BUF_ADDR_SFT 1 }; /* HWRM Asynchronous Event Completion Record (16 bytes) */ struct hwrm_async_event_cmpl { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE 0x0UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_MTU_CHANGE 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE 0x2UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_DCB_CONFIG_CHANGE 0x3UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED 0x4UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_NOT_ALLOWED 0x5UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE 0x6UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_PHY_CFG_CHANGE 0x7UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_UNLOAD 0x10UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_LOAD 0x11UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_FLR_PROC_CMPLT 0x12UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD 0x20UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD 0x21UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_FLR 0x30UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_MAC_ADDR_CHANGE 0x31UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_VF_COMM_STATUS_CHANGE 0x32UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE 0x33UL - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR 0xffUL + #define ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE 0x0UL + #define ASYNC_EVENT_CMPL_EVENT_ID_LINK_MTU_CHANGE 0x1UL + #define ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE 0x2UL + #define ASYNC_EVENT_CMPL_EVENT_ID_DCB_CONFIG_CHANGE 0x3UL + #define ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED 0x4UL + #define ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_NOT_ALLOWED 0x5UL + #define ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE 0x6UL + #define ASYNC_EVENT_CMPL_EVENT_ID_PORT_PHY_CFG_CHANGE 0x7UL + #define ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_UNLOAD 0x10UL + #define ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_LOAD 0x11UL + #define ASYNC_EVENT_CMPL_EVENT_ID_FUNC_FLR_PROC_CMPLT 0x12UL + #define ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD 0x20UL + #define ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD 0x21UL + #define ASYNC_EVENT_CMPL_EVENT_ID_VF_FLR 0x30UL + #define ASYNC_EVENT_CMPL_EVENT_ID_VF_MAC_ADDR_CHANGE 0x31UL + #define ASYNC_EVENT_CMPL_EVENT_ID_PF_VF_COMM_STATUS_CHANGE 0x32UL + #define ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE 0x33UL + #define ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR 0xffUL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_V 0x1UL + #define ASYNC_EVENT_CMPL_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; @@ -129,774 +123,479 @@ struct hwrm_async_event_cmpl { /* HWRM Asynchronous Event Completion Record for link status change (16 bytes) */ struct hwrm_async_event_cmpl_link_status_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_ID_LINK_STATUS_CHANGE 0x0UL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_ID_LINK_STATUS_CHANGE 0x0UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_DOWN (0x0UL << 0) - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_UP (0x1UL << 0) - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_LAST HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_UP - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_MASK 0xeUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_SFT 1 - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_SFT 4 + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE 0x1UL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_DOWN (0x0UL << 0) + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_UP (0x1UL << 0) + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_LAST ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_UP + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_MASK 0xeUL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_SFT 1 + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0UL + #define ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_SFT 4 }; /* HWRM Asynchronous Event Completion Record for link MTU change (16 bytes) */ struct hwrm_async_event_cmpl_link_mtu_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_ID_LINK_MTU_CHANGE 0x1UL + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_ID_LINK_MTU_CHANGE 0x1UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_SFT 0 }; /* HWRM Asynchronous Event Completion Record for link speed change (16 bytes) */ struct hwrm_async_event_cmpl_link_speed_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_ID_LINK_SPEED_CHANGE 0x2UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_ID_LINK_SPEED_CHANGE 0x2UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_FORCE 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_MASK 0xfffeUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_SFT 1 - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_100MB (0x1UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_1GB (0xaUL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_2GB (0x14UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_2_5GB (0x19UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_10GB (0x64UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_20GB (0xc8UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_25GB (0xfaUL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_40GB (0x190UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_50GB (0x1f4UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_100GB (0x3e8UL << 1) - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_LAST HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_100GB - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0000UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_PORT_ID_SFT 16 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_FORCE 0x1UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_MASK 0xfffeUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_SFT 1 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_100MB (0x1UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_1GB (0xaUL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_2GB (0x14UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_2_5GB (0x19UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_10GB (0x64UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_20GB (0xc8UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_25GB (0xfaUL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_40GB (0x190UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_50GB (0x1f4UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_100GB (0x3e8UL << 1) + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_LAST ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_100GB + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0000UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_PORT_ID_SFT 16 }; /* HWRM Asynchronous Event Completion Record for DCB Config change (16 bytes) */ struct hwrm_async_event_cmpl_dcb_config_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_ID_DCB_CONFIG_CHANGE 0x3UL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_ID_DCB_CONFIG_CHANGE 0x3UL __le32 event_data2; - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_ETS 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_PFC 0x2UL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_APP 0x4UL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_ETS 0x1UL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_PFC 0x2UL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA2_APP 0x4UL u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_MASK 0xff0000UL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_SFT 16 - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_NONE (0xffUL << 16) - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_LAST HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_NONE - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_MASK 0xff000000UL - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_SFT 24 - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_NONE (0xffUL << 24) - #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_LAST HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_NONE + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0 + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_MASK 0xff0000UL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_SFT 16 + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_NONE (0xffUL << 16) + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_LAST ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_ROCE_PRIORITY_NONE + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_MASK 0xff000000UL + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_SFT 24 + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_NONE (0xffUL << 24) + #define ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_LAST ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_RECOMMEND_L2_PRIORITY_NONE }; /* HWRM Asynchronous Event Completion Record for port connection not allowed (16 bytes) */ struct hwrm_async_event_cmpl_port_conn_not_allowed { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_ID_PORT_CONN_NOT_ALLOWED 0x4UL + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_ID_PORT_CONN_NOT_ALLOWED 0x4UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_V 0x1UL + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_MASK 0xff0000UL - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_SFT 16 - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_NONE (0x0UL << 16) - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_DISABLETX (0x1UL << 16) - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_WARNINGMSG (0x2UL << 16) - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_PWRDOWN (0x3UL << 16) - #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_LAST HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_PWRDOWN + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0 + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_MASK 0xff0000UL + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_SFT 16 + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_NONE (0x0UL << 16) + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_DISABLETX (0x1UL << 16) + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_WARNINGMSG (0x2UL << 16) + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_PWRDOWN (0x3UL << 16) + #define ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_LAST ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_PWRDOWN }; /* HWRM Asynchronous Event Completion Record for link speed config not allowed (16 bytes) */ struct hwrm_async_event_cmpl_link_speed_cfg_not_allowed { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_EVENT_ID_LINK_SPEED_CFG_NOT_ALLOWED 0x5UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_EVENT_ID_LINK_SPEED_CFG_NOT_ALLOWED 0x5UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_V 0x1UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0 }; /* HWRM Asynchronous Event Completion Record for link speed configuration change (16 bytes) */ struct hwrm_async_event_cmpl_link_speed_cfg_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_ID_LINK_SPEED_CFG_CHANGE 0x6UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_ID_LINK_SPEED_CFG_CHANGE 0x6UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_SUPPORTED_LINK_SPEEDS_CHANGE 0x10000UL - #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_ILLEGAL_LINK_SPEED_CFG 0x20000UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0 + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_SUPPORTED_LINK_SPEEDS_CHANGE 0x10000UL + #define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_ILLEGAL_LINK_SPEED_CFG 0x20000UL }; /* HWRM Asynchronous Event Completion Record for Function Driver Unload (16 bytes) */ struct hwrm_async_event_cmpl_func_drvr_unload { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_ID_FUNC_DRVR_UNLOAD 0x10UL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_ID_FUNC_DRVR_UNLOAD 0x10UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_V 0x1UL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0 + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0 }; /* HWRM Asynchronous Event Completion Record for Function Driver load (16 bytes) */ struct hwrm_async_event_cmpl_func_drvr_load { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL + __le16 event_id; + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_ID_FUNC_DRVR_LOAD 0x11UL + __le32 event_data2; + u8 opaque_v; + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_V 0x1UL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_SFT 1 + u8 timestamp_lo; + __le16 timestamp_hi; + __le32 event_data1; + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0 +}; + +/* HWRM Asynchronous Event Completion Record to indicate completion of FLR related processing (16 bytes) */ +struct hwrm_async_event_cmpl_func_flr_proc_cmplt { + __le16 type; + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_ID_FUNC_DRVR_LOAD 0x11UL + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_EVENT_ID_FUNC_FLR_PROC_CMPLT 0x12UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_V 0x1UL + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0 + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_EVENT_DATA1_FUNC_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_FUNC_FLR_PROC_CMPLT_EVENT_DATA1_FUNC_ID_SFT 0 }; /* HWRM Asynchronous Event Completion Record for PF Driver Unload (16 bytes) */ struct hwrm_async_event_cmpl_pf_drvr_unload { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_ID_PF_DRVR_UNLOAD 0x20UL + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_ID_PF_DRVR_UNLOAD 0x20UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_V 0x1UL + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_MASK 0x70000UL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_SFT 16 + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0 + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_MASK 0x70000UL + #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_SFT 16 }; /* HWRM Asynchronous Event Completion Record for PF Driver load (16 bytes) */ struct hwrm_async_event_cmpl_pf_drvr_load { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD 0x21UL + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD 0x21UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_V 0x1UL + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_MASK 0x70000UL - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_SFT 16 + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0 + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_MASK 0x70000UL + #define ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_SFT 16 }; /* HWRM Asynchronous Event Completion Record for VF FLR (16 bytes) */ struct hwrm_async_event_cmpl_vf_flr { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_VF_FLR_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_VF_FLR_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_VF_FLR_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_ID_VF_FLR 0x30UL + #define ASYNC_EVENT_CMPL_VF_FLR_EVENT_ID_VF_FLR 0x30UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_VF_FLR_V 0x1UL + #define ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_SFT 0 + #define ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_SFT 0 }; /* HWRM Asynchronous Event Completion Record for VF MAC Addr change (16 bytes) */ struct hwrm_async_event_cmpl_vf_mac_addr_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_ID_VF_MAC_ADDR_CHANGE 0x31UL + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_ID_VF_MAC_ADDR_CHANGE 0x31UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_MASK 0xffffUL - #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_SFT 0 + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_MASK 0xffffUL + #define ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_SFT 0 }; /* HWRM Asynchronous Event Completion Record for PF-VF communication status change (16 bytes) */ struct hwrm_async_event_cmpl_pf_vf_comm_status_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_EVENT_ID_PF_VF_COMM_STATUS_CHANGE 0x32UL + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_EVENT_ID_PF_VF_COMM_STATUS_CHANGE 0x32UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_EVENT_DATA1_COMM_ESTABLISHED 0x1UL + #define ASYNC_EVENT_CMPL_PF_VF_COMM_STATUS_CHANGE_EVENT_DATA1_COMM_ESTABLISHED 0x1UL }; /* HWRM Asynchronous Event Completion Record for VF configuration change (16 bytes) */ struct hwrm_async_event_cmpl_vf_cfg_change { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_ID_VF_CFG_CHANGE 0x33UL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_ID_VF_CFG_CHANGE 0x33UL __le32 event_data2; u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_V 0x1UL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_MTU_CHANGE 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_MRU_CHANGE 0x2UL - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_DFLT_MAC_ADDR_CHANGE 0x4UL - #define HWRM_ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_DFLT_VLAN_CHANGE 0x8UL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_MTU_CHANGE 0x1UL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_MRU_CHANGE 0x2UL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_DFLT_MAC_ADDR_CHANGE 0x4UL + #define ASYNC_EVENT_CMPL_VF_CFG_CHANGE_EVENT_DATA1_DFLT_VLAN_CHANGE 0x8UL }; /* HWRM Asynchronous Event Completion Record for HWRM Error (16 bytes) */ struct hwrm_async_event_cmpl_hwrm_error { __le16 type; - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_MASK 0x3fUL - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_HWRM_ASYNC_EVENT 0x2eUL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_MASK 0x3fUL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_SFT 0 + #define ASYNC_EVENT_CMPL_HWRM_ERROR_TYPE_HWRM_ASYNC_EVENT 0x2eUL __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_ID_HWRM_ERROR 0xffUL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_ID_HWRM_ERROR 0xffUL __le32 event_data2; - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_MASK 0xffUL - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_SFT 0 - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_WARNING 0x0UL - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_NONFATAL 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_FATAL 0x2UL - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_LAST HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_FATAL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_MASK 0xffUL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_SFT 0 + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_WARNING 0x0UL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_NONFATAL 0x1UL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_FATAL 0x2UL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_LAST ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA2_SEVERITY_FATAL u8 opaque_v; - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_V 0x1UL - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_MASK 0xfeUL - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_SFT 1 + #define ASYNC_EVENT_CMPL_HWRM_ERROR_V 0x1UL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_MASK 0xfeUL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_SFT 1 u8 timestamp_lo; __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA1_TIMESTAMP 0x1UL + #define ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA1_TIMESTAMP 0x1UL }; -/* HW Resource Manager Specification 1.5.4 */ -#define HWRM_VERSION_MAJOR 1 -#define HWRM_VERSION_MINOR 5 -#define HWRM_VERSION_UPDATE 4 - -#define HWRM_VERSION_STR "1.5.4" -/* - * Following is the signature for HWRM message field that indicates not - * applicable (All F's). Need to cast it the size of the field if needed. - */ -#define HWRM_NA_SIGNATURE ((__le32)(-1)) -#define HWRM_MAX_REQ_LEN (128) /* hwrm_func_buf_rgtr */ -#define HWRM_MAX_RESP_LEN (176) /* hwrm_func_qstats */ -#define HW_HASH_INDEX_SIZE 0x80 /* 7 bit indirection table index. */ -#define HW_HASH_KEY_SIZE 40 -#define HWRM_RESP_VALID_KEY 1 /* valid key for HWRM response */ -/* Input (16 bytes) */ -struct input { +/* hwrm_ver_get */ +/* Input (24 bytes) */ +struct hwrm_ver_get_input { __le16 req_type; __le16 cmpl_ring; __le16 seq_id; __le16 target_id; __le64 resp_addr; + u8 hwrm_intf_maj; + u8 hwrm_intf_min; + u8 hwrm_intf_upd; + u8 unused_0[5]; }; -/* Output (8 bytes) */ -struct output { +/* Output (128 bytes) */ +struct hwrm_ver_get_output { __le16 error_code; __le16 req_type; __le16 seq_id; __le16 resp_len; + u8 hwrm_intf_maj; + u8 hwrm_intf_min; + u8 hwrm_intf_upd; + u8 hwrm_intf_rsvd; + u8 hwrm_fw_maj; + u8 hwrm_fw_min; + u8 hwrm_fw_bld; + u8 hwrm_fw_rsvd; + u8 mgmt_fw_maj; + u8 mgmt_fw_min; + u8 mgmt_fw_bld; + u8 mgmt_fw_rsvd; + u8 netctrl_fw_maj; + u8 netctrl_fw_min; + u8 netctrl_fw_bld; + u8 netctrl_fw_rsvd; + __le32 dev_caps_cfg; + #define VER_GET_RESP_DEV_CAPS_CFG_SECURE_FW_UPD_SUPPORTED 0x1UL + #define VER_GET_RESP_DEV_CAPS_CFG_FW_DCBX_AGENT_SUPPORTED 0x2UL + u8 roce_fw_maj; + u8 roce_fw_min; + u8 roce_fw_bld; + u8 roce_fw_rsvd; + char hwrm_fw_name[16]; + char mgmt_fw_name[16]; + char netctrl_fw_name[16]; + __le32 reserved2[4]; + char roce_fw_name[16]; + __le16 chip_num; + u8 chip_rev; + u8 chip_metal; + u8 chip_bond_id; + u8 chip_platform_type; + #define VER_GET_RESP_CHIP_PLATFORM_TYPE_ASIC 0x0UL + #define VER_GET_RESP_CHIP_PLATFORM_TYPE_FPGA 0x1UL + #define VER_GET_RESP_CHIP_PLATFORM_TYPE_PALLADIUM 0x2UL + __le16 max_req_win_len; + __le16 max_resp_len; + __le16 def_req_timeout; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 valid; }; -/* Command numbering (8 bytes) */ -struct cmd_nums { +/* hwrm_func_reset */ +/* Input (24 bytes) */ +struct hwrm_func_reset_input { __le16 req_type; - #define HWRM_VER_GET (0x0UL) - #define HWRM_FUNC_BUF_UNRGTR (0xeUL) - #define HWRM_FUNC_VF_CFG (0xfUL) - #define RESERVED1 (0x10UL) - #define HWRM_FUNC_RESET (0x11UL) - #define HWRM_FUNC_GETFID (0x12UL) - #define HWRM_FUNC_VF_ALLOC (0x13UL) - #define HWRM_FUNC_VF_FREE (0x14UL) - #define HWRM_FUNC_QCAPS (0x15UL) - #define HWRM_FUNC_QCFG (0x16UL) - #define HWRM_FUNC_CFG (0x17UL) - #define HWRM_FUNC_QSTATS (0x18UL) - #define HWRM_FUNC_CLR_STATS (0x19UL) - #define HWRM_FUNC_DRV_UNRGTR (0x1aUL) - #define HWRM_FUNC_VF_RESC_FREE (0x1bUL) - #define HWRM_FUNC_VF_VNIC_IDS_QUERY (0x1cUL) - #define HWRM_FUNC_DRV_RGTR (0x1dUL) - #define HWRM_FUNC_DRV_QVER (0x1eUL) - #define HWRM_FUNC_BUF_RGTR (0x1fUL) - #define HWRM_PORT_PHY_CFG (0x20UL) - #define HWRM_PORT_MAC_CFG (0x21UL) - #define HWRM_PORT_TS_QUERY (0x22UL) - #define HWRM_PORT_QSTATS (0x23UL) - #define HWRM_PORT_LPBK_QSTATS (0x24UL) - #define HWRM_PORT_CLR_STATS (0x25UL) - #define HWRM_PORT_LPBK_CLR_STATS (0x26UL) - #define HWRM_PORT_PHY_QCFG (0x27UL) - #define HWRM_PORT_MAC_QCFG (0x28UL) - #define HWRM_PORT_BLINK_LED (0x29UL) - #define HWRM_PORT_PHY_QCAPS (0x2aUL) - #define HWRM_PORT_PHY_I2C_WRITE (0x2bUL) - #define HWRM_PORT_PHY_I2C_READ (0x2cUL) - #define HWRM_QUEUE_QPORTCFG (0x30UL) - #define HWRM_QUEUE_QCFG (0x31UL) - #define HWRM_QUEUE_CFG (0x32UL) - #define RESERVED2 (0x33UL) - #define RESERVED3 (0x34UL) - #define HWRM_QUEUE_PFCENABLE_QCFG (0x35UL) - #define HWRM_QUEUE_PFCENABLE_CFG (0x36UL) - #define HWRM_QUEUE_PRI2COS_QCFG (0x37UL) - #define HWRM_QUEUE_PRI2COS_CFG (0x38UL) - #define HWRM_QUEUE_COS2BW_QCFG (0x39UL) - #define HWRM_QUEUE_COS2BW_CFG (0x3aUL) - #define HWRM_VNIC_ALLOC (0x40UL) - #define HWRM_VNIC_FREE (0x41UL) - #define HWRM_VNIC_CFG (0x42UL) - #define HWRM_VNIC_QCFG (0x43UL) - #define HWRM_VNIC_TPA_CFG (0x44UL) - #define HWRM_VNIC_TPA_QCFG (0x45UL) - #define HWRM_VNIC_RSS_CFG (0x46UL) - #define HWRM_VNIC_RSS_QCFG (0x47UL) - #define HWRM_VNIC_PLCMODES_CFG (0x48UL) - #define HWRM_VNIC_PLCMODES_QCFG (0x49UL) - #define HWRM_VNIC_QCAPS (0x4aUL) - #define HWRM_RING_ALLOC (0x50UL) - #define HWRM_RING_FREE (0x51UL) - #define HWRM_RING_CMPL_RING_QAGGINT_PARAMS (0x52UL) - #define HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS (0x53UL) - #define HWRM_RING_RESET (0x5eUL) - #define HWRM_RING_GRP_ALLOC (0x60UL) - #define HWRM_RING_GRP_FREE (0x61UL) - #define RESERVED5 (0x64UL) - #define RESERVED6 (0x65UL) - #define HWRM_VNIC_RSS_COS_LB_CTX_ALLOC (0x70UL) - #define HWRM_VNIC_RSS_COS_LB_CTX_FREE (0x71UL) - #define HWRM_CFA_L2_FILTER_ALLOC (0x90UL) - #define HWRM_CFA_L2_FILTER_FREE (0x91UL) - #define HWRM_CFA_L2_FILTER_CFG (0x92UL) - #define HWRM_CFA_L2_SET_RX_MASK (0x93UL) - #define RESERVED4 (0x94UL) - #define HWRM_CFA_TUNNEL_FILTER_ALLOC (0x95UL) - #define HWRM_CFA_TUNNEL_FILTER_FREE (0x96UL) - #define HWRM_CFA_ENCAP_RECORD_ALLOC (0x97UL) - #define HWRM_CFA_ENCAP_RECORD_FREE (0x98UL) - #define HWRM_CFA_NTUPLE_FILTER_ALLOC (0x99UL) - #define HWRM_CFA_NTUPLE_FILTER_FREE (0x9aUL) - #define HWRM_CFA_NTUPLE_FILTER_CFG (0x9bUL) - #define HWRM_CFA_EM_FLOW_ALLOC (0x9cUL) - #define HWRM_CFA_EM_FLOW_FREE (0x9dUL) - #define HWRM_CFA_EM_FLOW_CFG (0x9eUL) - #define HWRM_TUNNEL_DST_PORT_QUERY (0xa0UL) - #define HWRM_TUNNEL_DST_PORT_ALLOC (0xa1UL) - #define HWRM_TUNNEL_DST_PORT_FREE (0xa2UL) - #define HWRM_STAT_CTX_ALLOC (0xb0UL) - #define HWRM_STAT_CTX_FREE (0xb1UL) - #define HWRM_STAT_CTX_QUERY (0xb2UL) - #define HWRM_STAT_CTX_CLR_STATS (0xb3UL) - #define HWRM_FW_RESET (0xc0UL) - #define HWRM_FW_QSTATUS (0xc1UL) - #define HWRM_FW_SET_TIME (0xc8UL) - #define HWRM_FW_GET_TIME (0xc9UL) - #define HWRM_FW_SET_STRUCTURED_DATA (0xcaUL) - #define HWRM_FW_GET_STRUCTURED_DATA (0xcbUL) - #define HWRM_FW_IPC_MAILBOX (0xccUL) - #define HWRM_EXEC_FWD_RESP (0xd0UL) - #define HWRM_REJECT_FWD_RESP (0xd1UL) - #define HWRM_FWD_RESP (0xd2UL) - #define HWRM_FWD_ASYNC_EVENT_CMPL (0xd3UL) - #define HWRM_TEMP_MONITOR_QUERY (0xe0UL) - #define HWRM_WOL_FILTER_ALLOC (0xf0UL) - #define HWRM_WOL_FILTER_FREE (0xf1UL) - #define HWRM_WOL_FILTER_QCFG (0xf2UL) - #define HWRM_WOL_REASON_QCFG (0xf3UL) - #define HWRM_DBG_READ_DIRECT (0xff10UL) - #define HWRM_DBG_READ_INDIRECT (0xff11UL) - #define HWRM_DBG_WRITE_DIRECT (0xff12UL) - #define HWRM_DBG_WRITE_INDIRECT (0xff13UL) - #define HWRM_DBG_DUMP (0xff14UL) - #define HWRM_NVM_GET_VARIABLE (0xfff1UL) - #define HWRM_NVM_SET_VARIABLE (0xfff2UL) - #define HWRM_NVM_INSTALL_UPDATE (0xfff3UL) - #define HWRM_NVM_MODIFY (0xfff4UL) - #define HWRM_NVM_VERIFY_UPDATE (0xfff5UL) - #define HWRM_NVM_GET_DEV_INFO (0xfff6UL) - #define HWRM_NVM_ERASE_DIR_ENTRY (0xfff7UL) - #define HWRM_NVM_MOD_DIR_ENTRY (0xfff8UL) - #define HWRM_NVM_FIND_DIR_ENTRY (0xfff9UL) - #define HWRM_NVM_GET_DIR_ENTRIES (0xfffaUL) - #define HWRM_NVM_GET_DIR_INFO (0xfffbUL) - #define HWRM_NVM_RAW_DUMP (0xfffcUL) - #define HWRM_NVM_READ (0xfffdUL) - #define HWRM_NVM_WRITE (0xfffeUL) - #define HWRM_NVM_RAW_WRITE_BLK (0xffffUL) - __le16 unused_0[3]; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 enables; + #define FUNC_RESET_REQ_ENABLES_VF_ID_VALID 0x1UL + __le16 vf_id; + u8 func_reset_level; + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETALL 0x0UL + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETME 0x1UL + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETCHILDREN 0x2UL + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETVF 0x3UL + u8 unused_0; }; -/* Return Codes (8 bytes) */ -struct ret_codes { - __le16 error_code; - #define HWRM_ERR_CODE_SUCCESS (0x0UL) - #define HWRM_ERR_CODE_FAIL (0x1UL) - #define HWRM_ERR_CODE_INVALID_PARAMS (0x2UL) - #define HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED (0x3UL) - #define HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR (0x4UL) - #define HWRM_ERR_CODE_INVALID_FLAGS (0x5UL) - #define HWRM_ERR_CODE_INVALID_ENABLES (0x6UL) - #define HWRM_ERR_CODE_HWRM_ERROR (0xfUL) - #define HWRM_ERR_CODE_UNKNOWN_ERR (0xfffeUL) - #define HWRM_ERR_CODE_CMD_NOT_SUPPORTED (0xffffUL) - __le16 unused_0[3]; -}; - -/* Output (16 bytes) */ -struct hwrm_err_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 opaque_0; - __le16 opaque_1; - u8 cmd_err; - u8 valid; -}; - -/* Port Tx Statistics Formats (408 bytes) */ -struct tx_port_stats { - __le64 tx_64b_frames; - __le64 tx_65b_127b_frames; - __le64 tx_128b_255b_frames; - __le64 tx_256b_511b_frames; - __le64 tx_512b_1023b_frames; - __le64 tx_1024b_1518_frames; - __le64 tx_good_vlan_frames; - __le64 tx_1519b_2047_frames; - __le64 tx_2048b_4095b_frames; - __le64 tx_4096b_9216b_frames; - __le64 tx_9217b_16383b_frames; - __le64 tx_good_frames; - __le64 tx_total_frames; - __le64 tx_ucast_frames; - __le64 tx_mcast_frames; - __le64 tx_bcast_frames; - __le64 tx_pause_frames; - __le64 tx_pfc_frames; - __le64 tx_jabber_frames; - __le64 tx_fcs_err_frames; - __le64 tx_control_frames; - __le64 tx_oversz_frames; - __le64 tx_single_dfrl_frames; - __le64 tx_multi_dfrl_frames; - __le64 tx_single_coll_frames; - __le64 tx_multi_coll_frames; - __le64 tx_late_coll_frames; - __le64 tx_excessive_coll_frames; - __le64 tx_frag_frames; - __le64 tx_err; - __le64 tx_tagged_frames; - __le64 tx_dbl_tagged_frames; - __le64 tx_runt_frames; - __le64 tx_fifo_underruns; - __le64 tx_pfc_ena_frames_pri0; - __le64 tx_pfc_ena_frames_pri1; - __le64 tx_pfc_ena_frames_pri2; - __le64 tx_pfc_ena_frames_pri3; - __le64 tx_pfc_ena_frames_pri4; - __le64 tx_pfc_ena_frames_pri5; - __le64 tx_pfc_ena_frames_pri6; - __le64 tx_pfc_ena_frames_pri7; - __le64 tx_eee_lpi_events; - __le64 tx_eee_lpi_duration; - __le64 tx_llfc_logical_msgs; - __le64 tx_hcfc_msgs; - __le64 tx_total_collisions; - __le64 tx_bytes; - __le64 tx_xthol_frames; - __le64 tx_stat_discard; - __le64 tx_stat_error; -}; - -/* Port Rx Statistics Formats (528 bytes) */ -struct rx_port_stats { - __le64 rx_64b_frames; - __le64 rx_65b_127b_frames; - __le64 rx_128b_255b_frames; - __le64 rx_256b_511b_frames; - __le64 rx_512b_1023b_frames; - __le64 rx_1024b_1518_frames; - __le64 rx_good_vlan_frames; - __le64 rx_1519b_2047b_frames; - __le64 rx_2048b_4095b_frames; - __le64 rx_4096b_9216b_frames; - __le64 rx_9217b_16383b_frames; - __le64 rx_total_frames; - __le64 rx_ucast_frames; - __le64 rx_mcast_frames; - __le64 rx_bcast_frames; - __le64 rx_fcs_err_frames; - __le64 rx_ctrl_frames; - __le64 rx_pause_frames; - __le64 rx_pfc_frames; - __le64 rx_unsupported_opcode_frames; - __le64 rx_unsupported_da_pausepfc_frames; - __le64 rx_wrong_sa_frames; - __le64 rx_align_err_frames; - __le64 rx_oor_len_frames; - __le64 rx_code_err_frames; - __le64 rx_false_carrier_frames; - __le64 rx_ovrsz_frames; - __le64 rx_jbr_frames; - __le64 rx_mtu_err_frames; - __le64 rx_match_crc_frames; - __le64 rx_promiscuous_frames; - __le64 rx_tagged_frames; - __le64 rx_double_tagged_frames; - __le64 rx_trunc_frames; - __le64 rx_good_frames; - __le64 rx_pfc_xon2xoff_frames_pri0; - __le64 rx_pfc_xon2xoff_frames_pri1; - __le64 rx_pfc_xon2xoff_frames_pri2; - __le64 rx_pfc_xon2xoff_frames_pri3; - __le64 rx_pfc_xon2xoff_frames_pri4; - __le64 rx_pfc_xon2xoff_frames_pri5; - __le64 rx_pfc_xon2xoff_frames_pri6; - __le64 rx_pfc_xon2xoff_frames_pri7; - __le64 rx_pfc_ena_frames_pri0; - __le64 rx_pfc_ena_frames_pri1; - __le64 rx_pfc_ena_frames_pri2; - __le64 rx_pfc_ena_frames_pri3; - __le64 rx_pfc_ena_frames_pri4; - __le64 rx_pfc_ena_frames_pri5; - __le64 rx_pfc_ena_frames_pri6; - __le64 rx_pfc_ena_frames_pri7; - __le64 rx_sch_crc_err_frames; - __le64 rx_undrsz_frames; - __le64 rx_frag_frames; - __le64 rx_eee_lpi_events; - __le64 rx_eee_lpi_duration; - __le64 rx_llfc_physical_msgs; - __le64 rx_llfc_logical_msgs; - __le64 rx_llfc_msgs_with_crc_err; - __le64 rx_hcfc_msgs; - __le64 rx_hcfc_msgs_with_crc_err; - __le64 rx_bytes; - __le64 rx_runt_bytes; - __le64 rx_runt_frames; - __le64 rx_stat_discard; - __le64 rx_stat_err; -}; - -/* hwrm_ver_get */ -/* Input (24 bytes) */ -struct hwrm_ver_get_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - u8 hwrm_intf_maj; - u8 hwrm_intf_min; - u8 hwrm_intf_upd; - u8 unused_0[5]; -}; - -/* Output (128 bytes) */ -struct hwrm_ver_get_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - u8 hwrm_intf_maj; - u8 hwrm_intf_min; - u8 hwrm_intf_upd; - u8 hwrm_intf_rsvd; - u8 hwrm_fw_maj; - u8 hwrm_fw_min; - u8 hwrm_fw_bld; - u8 hwrm_fw_rsvd; - u8 mgmt_fw_maj; - u8 mgmt_fw_min; - u8 mgmt_fw_bld; - u8 mgmt_fw_rsvd; - u8 netctrl_fw_maj; - u8 netctrl_fw_min; - u8 netctrl_fw_bld; - u8 netctrl_fw_rsvd; - __le32 dev_caps_cfg; - #define VER_GET_RESP_DEV_CAPS_CFG_SECURE_FW_UPD_SUPPORTED 0x1UL - #define VER_GET_RESP_DEV_CAPS_CFG_FW_DCBX_AGENT_SUPPORTED 0x2UL - u8 roce_fw_maj; - u8 roce_fw_min; - u8 roce_fw_bld; - u8 roce_fw_rsvd; - char hwrm_fw_name[16]; - char mgmt_fw_name[16]; - char netctrl_fw_name[16]; - __le32 reserved2[4]; - char roce_fw_name[16]; - __le16 chip_num; - u8 chip_rev; - u8 chip_metal; - u8 chip_bond_id; - u8 chip_platform_type; - #define VER_GET_RESP_CHIP_PLATFORM_TYPE_ASIC 0x0UL - #define VER_GET_RESP_CHIP_PLATFORM_TYPE_FPGA 0x1UL - #define VER_GET_RESP_CHIP_PLATFORM_TYPE_PALLADIUM 0x2UL - __le16 max_req_win_len; - __le16 max_resp_len; - __le16 def_req_timeout; - u8 unused_0; - u8 unused_1; - u8 unused_2; - u8 valid; -}; - -/* hwrm_func_reset */ -/* Input (24 bytes) */ -struct hwrm_func_reset_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le32 enables; - #define FUNC_RESET_REQ_ENABLES_VF_ID_VALID 0x1UL - __le16 vf_id; - u8 func_reset_level; - #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETALL 0x0UL - #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETME 0x1UL - #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETCHILDREN 0x2UL - #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETVF 0x3UL - u8 unused_0; -}; - -/* Output (16 bytes) */ -struct hwrm_func_reset_output { +/* Output (16 bytes) */ +struct hwrm_func_reset_output { __le16 error_code; __le16 req_type; __le16 seq_id; @@ -1057,6 +756,7 @@ struct hwrm_func_qcaps_output { #define FUNC_QCAPS_RESP_FLAGS_TX_BW_CFG_SUPPORTED 0x100UL #define FUNC_QCAPS_RESP_FLAGS_VF_TX_RING_RL_SUPPORTED 0x200UL #define FUNC_QCAPS_RESP_FLAGS_VF_BW_CFG_SUPPORTED 0x400UL + #define FUNC_QCAPS_RESP_FLAGS_STD_TX_RING_MODE_SUPPORTED 0x800UL u8 mac_address[6]; __le16 max_rsscos_ctx; __le16 max_cmpl_rings; @@ -1106,6 +806,7 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_FLAGS_OOB_WOL_MAGICPKT_ENABLED 0x1UL #define FUNC_QCFG_RESP_FLAGS_OOB_WOL_BMP_ENABLED 0x2UL #define FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED 0x4UL + #define FUNC_QCFG_RESP_FLAGS_STD_TX_RING_MODE_ENABLED 0x8UL u8 mac_address[6]; __le16 pci_id; __le16 alloc_rsscos_ctx; @@ -1182,6 +883,7 @@ struct hwrm_func_cfg_input { #define FUNC_CFG_REQ_FLAGS_DISABLE_STP 0x40UL #define FUNC_CFG_REQ_FLAGS_DISABLE_LLDP 0x80UL #define FUNC_CFG_REQ_FLAGS_DISABLE_PTPV2 0x100UL + #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE 0x200UL __le32 enables; #define FUNC_CFG_REQ_ENABLES_MTU 0x1UL #define FUNC_CFG_REQ_ENABLES_MRU 0x2UL @@ -1548,6 +1250,7 @@ struct hwrm_func_drv_qver_output { #define FUNC_DRV_QVER_RESP_OS_TYPE_ESXI 0x68UL #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN864 0x73UL #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN2012R2 0x74UL + #define FUNC_DRV_QVER_RESP_OS_TYPE_UEFI 0x8000UL u8 ver_maj; u8 ver_min; u8 ver_upd; @@ -2109,45 +1812,20 @@ struct hwrm_port_lpbk_clr_stats_output { u8 valid; }; -/* hwrm_port_blink_led */ +/* hwrm_port_phy_qcaps */ /* Input (24 bytes) */ -struct hwrm_port_blink_led_input { +struct hwrm_port_phy_qcaps_input { __le16 req_type; __le16 cmpl_ring; __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le32 num_blinks; - __le32 unused_0; + __le16 port_id; + __le16 unused_0[3]; }; -/* Output (16 bytes) */ -struct hwrm_port_blink_led_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - -/* hwrm_port_phy_qcaps */ -/* Input (24 bytes) */ -struct hwrm_port_phy_qcaps_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le16 port_id; - __le16 unused_0[3]; -}; - -/* Output (24 bytes) */ -struct hwrm_port_phy_qcaps_output { +/* Output (24 bytes) */ +struct hwrm_port_phy_qcaps_output { __le16 error_code; __le16 req_type; __le16 seq_id; @@ -2355,6 +2033,39 @@ struct hwrm_queue_cfg_output { u8 valid; }; +/* hwrm_queue_pfcenable_qcfg */ +/* Input (24 bytes) */ +struct hwrm_queue_pfcenable_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 port_id; + __le16 unused_0[3]; +}; + +/* Output (16 bytes) */ +struct hwrm_queue_pfcenable_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 flags; + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI0_PFC_ENABLED 0x1UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI1_PFC_ENABLED 0x2UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI2_PFC_ENABLED 0x4UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI3_PFC_ENABLED 0x8UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI4_PFC_ENABLED 0x10UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI5_PFC_ENABLED 0x20UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI6_PFC_ENABLED 0x40UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI7_PFC_ENABLED 0x80UL + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 valid; +}; + /* hwrm_queue_pfcenable_cfg */ /* Input (24 bytes) */ struct hwrm_queue_pfcenable_cfg_input { @@ -2389,6 +2100,48 @@ struct hwrm_queue_pfcenable_cfg_output { u8 valid; }; +/* hwrm_queue_pri2cos_qcfg */ +/* Input (24 bytes) */ +struct hwrm_queue_pri2cos_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; + #define QUEUE_PRI2COS_QCFG_REQ_FLAGS_PATH 0x1UL + #define QUEUE_PRI2COS_QCFG_REQ_FLAGS_PATH_TX (0x0UL << 0) + #define QUEUE_PRI2COS_QCFG_REQ_FLAGS_PATH_RX (0x1UL << 0) + #define QUEUE_PRI2COS_QCFG_REQ_FLAGS_PATH_LAST QUEUE_PRI2COS_QCFG_REQ_FLAGS_PATH_RX + #define QUEUE_PRI2COS_QCFG_REQ_FLAGS_IVLAN 0x2UL + u8 port_id; + u8 unused_0[3]; +}; + +/* Output (24 bytes) */ +struct hwrm_queue_pri2cos_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 pri0_cos_queue_id; + u8 pri1_cos_queue_id; + u8 pri2_cos_queue_id; + u8 pri3_cos_queue_id; + u8 pri4_cos_queue_id; + u8 pri5_cos_queue_id; + u8 pri6_cos_queue_id; + u8 pri7_cos_queue_id; + u8 queue_cfg_info; + #define QUEUE_PRI2COS_QCFG_RESP_QUEUE_CFG_INFO_ASYM_CFG 0x1UL + u8 unused_0; + __le16 unused_1; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 valid; +}; + /* hwrm_queue_pri2cos_cfg */ /* Input (40 bytes) */ struct hwrm_queue_pri2cos_cfg_input { @@ -2439,6 +2192,257 @@ struct hwrm_queue_pri2cos_cfg_output { u8 valid; }; +/* hwrm_queue_cos2bw_qcfg */ +/* Input (24 bytes) */ +struct hwrm_queue_cos2bw_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 port_id; + __le16 unused_0[3]; +}; + +/* Output (112 bytes) */ +struct hwrm_queue_cos2bw_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 queue_id0; + u8 unused_0; + __le16 unused_1; + __le32 queue_id0_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id0_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id0_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id0_pri_lvl; + u8 queue_id0_bw_weight; + u8 queue_id1; + __le32 queue_id1_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id1_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id1_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID1_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id1_pri_lvl; + u8 queue_id1_bw_weight; + u8 queue_id2; + __le32 queue_id2_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id2_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id2_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID2_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id2_pri_lvl; + u8 queue_id2_bw_weight; + u8 queue_id3; + __le32 queue_id3_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id3_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id3_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID3_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id3_pri_lvl; + u8 queue_id3_bw_weight; + u8 queue_id4; + __le32 queue_id4_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id4_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id4_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID4_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id4_pri_lvl; + u8 queue_id4_bw_weight; + u8 queue_id5; + __le32 queue_id5_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id5_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id5_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID5_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id5_pri_lvl; + u8 queue_id5_bw_weight; + u8 queue_id6; + __le32 queue_id6_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id6_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id6_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID6_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id6_pri_lvl; + u8 queue_id6_bw_weight; + u8 queue_id7; + __le32 queue_id7_min_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MIN_BW_BW_VALUE_UNIT_INVALID + __le32 queue_id7_max_bw; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_MASK 0xfffffffUL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_SFT 0 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_RSVD 0x10000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_UNIT_MASK 0xe0000000UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_UNIT_SFT 29 + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_UNIT_MBPS (0x0UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_UNIT_INVALID (0x7UL << 29) + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_UNIT_LAST QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_MAX_BW_BW_VALUE_UNIT_INVALID + u8 queue_id7_tsa_assign; + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_TSA_ASSIGN_SP 0x0UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_TSA_ASSIGN_ETS 0x1UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_TSA_ASSIGN_RESERVED_FIRST 0x2UL + #define QUEUE_COS2BW_QCFG_RESP_QUEUE_ID7_TSA_ASSIGN_RESERVED_LAST 0xffUL + u8 queue_id7_pri_lvl; + u8 queue_id7_bw_weight; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 unused_5; + u8 valid; +}; + /* hwrm_queue_cos2bw_cfg */ /* Input (128 bytes) */ struct hwrm_queue_cos2bw_cfg_input { @@ -3820,7 +3824,9 @@ struct hwrm_stat_ctx_alloc_input { __le64 resp_addr; __le64 stats_dma_addr; __le32 update_period_ms; - __le32 unused_0; + u8 stat_ctx_flags; + #define STAT_CTX_ALLOC_REQ_STAT_CTX_FLAGS_ROCE 0x1UL + u8 unused_0[3]; }; /* Output (16 bytes) */ @@ -4052,7 +4058,9 @@ struct hwrm_fw_set_structured_data_input { __le64 src_data_addr; __le16 data_len; u8 hdr_cnt; - u8 unused_0[5]; + u8 unused_0; + __le16 port_id; + __le16 unused_1; }; /* Output (16 bytes) */ @@ -4069,7 +4077,7 @@ struct hwrm_fw_set_structured_data_output { }; /* hwrm_fw_get_structured_data */ -/* Input (32 bytes) */ +/* Input (40 bytes) */ struct hwrm_fw_get_structured_data_input { __le16 req_type; __le16 cmpl_ring; @@ -4089,6 +4097,8 @@ struct hwrm_fw_get_structured_data_input { #define FW_GET_STRUCTURED_DATA_REQ_SUBTYPE_NON_TPMR_OPERATIONAL 0x202UL u8 count; u8 unused_0; + __le16 port_id; + __le16 unused_1[3]; }; /* Output (16 bytes) */ @@ -4598,4 +4608,363 @@ struct hwrm_nvm_install_update_output { u8 valid; }; +/* Hardware Resource Manager Specification */ +/* Input (16 bytes) */ +struct input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* Output (8 bytes) */ +struct output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; +}; + +/* Command numbering (8 bytes) */ +struct cmd_nums { + __le16 req_type; + #define HWRM_VER_GET (0x0UL) + #define HWRM_FUNC_BUF_UNRGTR (0xeUL) + #define HWRM_FUNC_VF_CFG (0xfUL) + #define RESERVED1 (0x10UL) + #define HWRM_FUNC_RESET (0x11UL) + #define HWRM_FUNC_GETFID (0x12UL) + #define HWRM_FUNC_VF_ALLOC (0x13UL) + #define HWRM_FUNC_VF_FREE (0x14UL) + #define HWRM_FUNC_QCAPS (0x15UL) + #define HWRM_FUNC_QCFG (0x16UL) + #define HWRM_FUNC_CFG (0x17UL) + #define HWRM_FUNC_QSTATS (0x18UL) + #define HWRM_FUNC_CLR_STATS (0x19UL) + #define HWRM_FUNC_DRV_UNRGTR (0x1aUL) + #define HWRM_FUNC_VF_RESC_FREE (0x1bUL) + #define HWRM_FUNC_VF_VNIC_IDS_QUERY (0x1cUL) + #define HWRM_FUNC_DRV_RGTR (0x1dUL) + #define HWRM_FUNC_DRV_QVER (0x1eUL) + #define HWRM_FUNC_BUF_RGTR (0x1fUL) + #define HWRM_PORT_PHY_CFG (0x20UL) + #define HWRM_PORT_MAC_CFG (0x21UL) + #define HWRM_PORT_TS_QUERY (0x22UL) + #define HWRM_PORT_QSTATS (0x23UL) + #define HWRM_PORT_LPBK_QSTATS (0x24UL) + #define HWRM_PORT_CLR_STATS (0x25UL) + #define HWRM_PORT_LPBK_CLR_STATS (0x26UL) + #define HWRM_PORT_PHY_QCFG (0x27UL) + #define HWRM_PORT_MAC_QCFG (0x28UL) + #define RESERVED7 (0x29UL) + #define HWRM_PORT_PHY_QCAPS (0x2aUL) + #define HWRM_PORT_PHY_I2C_WRITE (0x2bUL) + #define HWRM_PORT_PHY_I2C_READ (0x2cUL) + #define HWRM_PORT_LED_CFG (0x2dUL) + #define HWRM_PORT_LED_QCFG (0x2eUL) + #define HWRM_PORT_LED_QCAPS (0x2fUL) + #define HWRM_QUEUE_QPORTCFG (0x30UL) + #define HWRM_QUEUE_QCFG (0x31UL) + #define HWRM_QUEUE_CFG (0x32UL) + #define RESERVED2 (0x33UL) + #define RESERVED3 (0x34UL) + #define HWRM_QUEUE_PFCENABLE_QCFG (0x35UL) + #define HWRM_QUEUE_PFCENABLE_CFG (0x36UL) + #define HWRM_QUEUE_PRI2COS_QCFG (0x37UL) + #define HWRM_QUEUE_PRI2COS_CFG (0x38UL) + #define HWRM_QUEUE_COS2BW_QCFG (0x39UL) + #define HWRM_QUEUE_COS2BW_CFG (0x3aUL) + #define HWRM_VNIC_ALLOC (0x40UL) + #define HWRM_VNIC_FREE (0x41UL) + #define HWRM_VNIC_CFG (0x42UL) + #define HWRM_VNIC_QCFG (0x43UL) + #define HWRM_VNIC_TPA_CFG (0x44UL) + #define HWRM_VNIC_TPA_QCFG (0x45UL) + #define HWRM_VNIC_RSS_CFG (0x46UL) + #define HWRM_VNIC_RSS_QCFG (0x47UL) + #define HWRM_VNIC_PLCMODES_CFG (0x48UL) + #define HWRM_VNIC_PLCMODES_QCFG (0x49UL) + #define HWRM_VNIC_QCAPS (0x4aUL) + #define HWRM_RING_ALLOC (0x50UL) + #define HWRM_RING_FREE (0x51UL) + #define HWRM_RING_CMPL_RING_QAGGINT_PARAMS (0x52UL) + #define HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS (0x53UL) + #define HWRM_RING_RESET (0x5eUL) + #define HWRM_RING_GRP_ALLOC (0x60UL) + #define HWRM_RING_GRP_FREE (0x61UL) + #define RESERVED5 (0x64UL) + #define RESERVED6 (0x65UL) + #define HWRM_VNIC_RSS_COS_LB_CTX_ALLOC (0x70UL) + #define HWRM_VNIC_RSS_COS_LB_CTX_FREE (0x71UL) + #define HWRM_CFA_L2_FILTER_ALLOC (0x90UL) + #define HWRM_CFA_L2_FILTER_FREE (0x91UL) + #define HWRM_CFA_L2_FILTER_CFG (0x92UL) + #define HWRM_CFA_L2_SET_RX_MASK (0x93UL) + #define RESERVED4 (0x94UL) + #define HWRM_CFA_TUNNEL_FILTER_ALLOC (0x95UL) + #define HWRM_CFA_TUNNEL_FILTER_FREE (0x96UL) + #define HWRM_CFA_ENCAP_RECORD_ALLOC (0x97UL) + #define HWRM_CFA_ENCAP_RECORD_FREE (0x98UL) + #define HWRM_CFA_NTUPLE_FILTER_ALLOC (0x99UL) + #define HWRM_CFA_NTUPLE_FILTER_FREE (0x9aUL) + #define HWRM_CFA_NTUPLE_FILTER_CFG (0x9bUL) + #define HWRM_CFA_EM_FLOW_ALLOC (0x9cUL) + #define HWRM_CFA_EM_FLOW_FREE (0x9dUL) + #define HWRM_CFA_EM_FLOW_CFG (0x9eUL) + #define HWRM_TUNNEL_DST_PORT_QUERY (0xa0UL) + #define HWRM_TUNNEL_DST_PORT_ALLOC (0xa1UL) + #define HWRM_TUNNEL_DST_PORT_FREE (0xa2UL) + #define HWRM_STAT_CTX_ALLOC (0xb0UL) + #define HWRM_STAT_CTX_FREE (0xb1UL) + #define HWRM_STAT_CTX_QUERY (0xb2UL) + #define HWRM_STAT_CTX_CLR_STATS (0xb3UL) + #define HWRM_FW_RESET (0xc0UL) + #define HWRM_FW_QSTATUS (0xc1UL) + #define HWRM_FW_SET_TIME (0xc8UL) + #define HWRM_FW_GET_TIME (0xc9UL) + #define HWRM_FW_SET_STRUCTURED_DATA (0xcaUL) + #define HWRM_FW_GET_STRUCTURED_DATA (0xcbUL) + #define HWRM_FW_IPC_MAILBOX (0xccUL) + #define HWRM_EXEC_FWD_RESP (0xd0UL) + #define HWRM_REJECT_FWD_RESP (0xd1UL) + #define HWRM_FWD_RESP (0xd2UL) + #define HWRM_FWD_ASYNC_EVENT_CMPL (0xd3UL) + #define HWRM_TEMP_MONITOR_QUERY (0xe0UL) + #define HWRM_WOL_FILTER_ALLOC (0xf0UL) + #define HWRM_WOL_FILTER_FREE (0xf1UL) + #define HWRM_WOL_FILTER_QCFG (0xf2UL) + #define HWRM_WOL_REASON_QCFG (0xf3UL) + #define HWRM_DBG_READ_DIRECT (0xff10UL) + #define HWRM_DBG_READ_INDIRECT (0xff11UL) + #define HWRM_DBG_WRITE_DIRECT (0xff12UL) + #define HWRM_DBG_WRITE_INDIRECT (0xff13UL) + #define HWRM_DBG_DUMP (0xff14UL) + #define HWRM_NVM_GET_VARIABLE (0xfff1UL) + #define HWRM_NVM_SET_VARIABLE (0xfff2UL) + #define HWRM_NVM_INSTALL_UPDATE (0xfff3UL) + #define HWRM_NVM_MODIFY (0xfff4UL) + #define HWRM_NVM_VERIFY_UPDATE (0xfff5UL) + #define HWRM_NVM_GET_DEV_INFO (0xfff6UL) + #define HWRM_NVM_ERASE_DIR_ENTRY (0xfff7UL) + #define HWRM_NVM_MOD_DIR_ENTRY (0xfff8UL) + #define HWRM_NVM_FIND_DIR_ENTRY (0xfff9UL) + #define HWRM_NVM_GET_DIR_ENTRIES (0xfffaUL) + #define HWRM_NVM_GET_DIR_INFO (0xfffbUL) + #define HWRM_NVM_RAW_DUMP (0xfffcUL) + #define HWRM_NVM_READ (0xfffdUL) + #define HWRM_NVM_WRITE (0xfffeUL) + #define HWRM_NVM_RAW_WRITE_BLK (0xffffUL) + __le16 unused_0[3]; +}; + +/* Return Codes (8 bytes) */ +struct ret_codes { + __le16 error_code; + #define HWRM_ERR_CODE_SUCCESS (0x0UL) + #define HWRM_ERR_CODE_FAIL (0x1UL) + #define HWRM_ERR_CODE_INVALID_PARAMS (0x2UL) + #define HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED (0x3UL) + #define HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR (0x4UL) + #define HWRM_ERR_CODE_INVALID_FLAGS (0x5UL) + #define HWRM_ERR_CODE_INVALID_ENABLES (0x6UL) + #define HWRM_ERR_CODE_HWRM_ERROR (0xfUL) + #define HWRM_ERR_CODE_UNKNOWN_ERR (0xfffeUL) + #define HWRM_ERR_CODE_CMD_NOT_SUPPORTED (0xffffUL) + __le16 unused_0[3]; +}; + +/* Output (16 bytes) */ +struct hwrm_err_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 opaque_0; + __le16 opaque_1; + u8 cmd_err; + u8 valid; +}; + +/* Port Tx Statistics Formats (408 bytes) */ +struct tx_port_stats { + __le64 tx_64b_frames; + __le64 tx_65b_127b_frames; + __le64 tx_128b_255b_frames; + __le64 tx_256b_511b_frames; + __le64 tx_512b_1023b_frames; + __le64 tx_1024b_1518_frames; + __le64 tx_good_vlan_frames; + __le64 tx_1519b_2047_frames; + __le64 tx_2048b_4095b_frames; + __le64 tx_4096b_9216b_frames; + __le64 tx_9217b_16383b_frames; + __le64 tx_good_frames; + __le64 tx_total_frames; + __le64 tx_ucast_frames; + __le64 tx_mcast_frames; + __le64 tx_bcast_frames; + __le64 tx_pause_frames; + __le64 tx_pfc_frames; + __le64 tx_jabber_frames; + __le64 tx_fcs_err_frames; + __le64 tx_control_frames; + __le64 tx_oversz_frames; + __le64 tx_single_dfrl_frames; + __le64 tx_multi_dfrl_frames; + __le64 tx_single_coll_frames; + __le64 tx_multi_coll_frames; + __le64 tx_late_coll_frames; + __le64 tx_excessive_coll_frames; + __le64 tx_frag_frames; + __le64 tx_err; + __le64 tx_tagged_frames; + __le64 tx_dbl_tagged_frames; + __le64 tx_runt_frames; + __le64 tx_fifo_underruns; + __le64 tx_pfc_ena_frames_pri0; + __le64 tx_pfc_ena_frames_pri1; + __le64 tx_pfc_ena_frames_pri2; + __le64 tx_pfc_ena_frames_pri3; + __le64 tx_pfc_ena_frames_pri4; + __le64 tx_pfc_ena_frames_pri5; + __le64 tx_pfc_ena_frames_pri6; + __le64 tx_pfc_ena_frames_pri7; + __le64 tx_eee_lpi_events; + __le64 tx_eee_lpi_duration; + __le64 tx_llfc_logical_msgs; + __le64 tx_hcfc_msgs; + __le64 tx_total_collisions; + __le64 tx_bytes; + __le64 tx_xthol_frames; + __le64 tx_stat_discard; + __le64 tx_stat_error; +}; + +/* Port Rx Statistics Formats (528 bytes) */ +struct rx_port_stats { + __le64 rx_64b_frames; + __le64 rx_65b_127b_frames; + __le64 rx_128b_255b_frames; + __le64 rx_256b_511b_frames; + __le64 rx_512b_1023b_frames; + __le64 rx_1024b_1518_frames; + __le64 rx_good_vlan_frames; + __le64 rx_1519b_2047b_frames; + __le64 rx_2048b_4095b_frames; + __le64 rx_4096b_9216b_frames; + __le64 rx_9217b_16383b_frames; + __le64 rx_total_frames; + __le64 rx_ucast_frames; + __le64 rx_mcast_frames; + __le64 rx_bcast_frames; + __le64 rx_fcs_err_frames; + __le64 rx_ctrl_frames; + __le64 rx_pause_frames; + __le64 rx_pfc_frames; + __le64 rx_unsupported_opcode_frames; + __le64 rx_unsupported_da_pausepfc_frames; + __le64 rx_wrong_sa_frames; + __le64 rx_align_err_frames; + __le64 rx_oor_len_frames; + __le64 rx_code_err_frames; + __le64 rx_false_carrier_frames; + __le64 rx_ovrsz_frames; + __le64 rx_jbr_frames; + __le64 rx_mtu_err_frames; + __le64 rx_match_crc_frames; + __le64 rx_promiscuous_frames; + __le64 rx_tagged_frames; + __le64 rx_double_tagged_frames; + __le64 rx_trunc_frames; + __le64 rx_good_frames; + __le64 rx_pfc_xon2xoff_frames_pri0; + __le64 rx_pfc_xon2xoff_frames_pri1; + __le64 rx_pfc_xon2xoff_frames_pri2; + __le64 rx_pfc_xon2xoff_frames_pri3; + __le64 rx_pfc_xon2xoff_frames_pri4; + __le64 rx_pfc_xon2xoff_frames_pri5; + __le64 rx_pfc_xon2xoff_frames_pri6; + __le64 rx_pfc_xon2xoff_frames_pri7; + __le64 rx_pfc_ena_frames_pri0; + __le64 rx_pfc_ena_frames_pri1; + __le64 rx_pfc_ena_frames_pri2; + __le64 rx_pfc_ena_frames_pri3; + __le64 rx_pfc_ena_frames_pri4; + __le64 rx_pfc_ena_frames_pri5; + __le64 rx_pfc_ena_frames_pri6; + __le64 rx_pfc_ena_frames_pri7; + __le64 rx_sch_crc_err_frames; + __le64 rx_undrsz_frames; + __le64 rx_frag_frames; + __le64 rx_eee_lpi_events; + __le64 rx_eee_lpi_duration; + __le64 rx_llfc_physical_msgs; + __le64 rx_llfc_logical_msgs; + __le64 rx_llfc_msgs_with_crc_err; + __le64 rx_hcfc_msgs; + __le64 rx_hcfc_msgs_with_crc_err; + __le64 rx_bytes; + __le64 rx_runt_bytes; + __le64 rx_runt_frames; + __le64 rx_stat_discard; + __le64 rx_stat_err; +}; + +/* Periodic Statistics Context DMA to host (160 bytes) */ +struct ctx_hw_stats { + __le64 rx_ucast_pkts; + __le64 rx_mcast_pkts; + __le64 rx_bcast_pkts; + __le64 rx_discard_pkts; + __le64 rx_drop_pkts; + __le64 rx_ucast_bytes; + __le64 rx_mcast_bytes; + __le64 rx_bcast_bytes; + __le64 tx_ucast_pkts; + __le64 tx_mcast_pkts; + __le64 tx_bcast_pkts; + __le64 tx_discard_pkts; + __le64 tx_drop_pkts; + __le64 tx_ucast_bytes; + __le64 tx_mcast_bytes; + __le64 tx_bcast_bytes; + __le64 tpa_pkts; + __le64 tpa_bytes; + __le64 tpa_events; + __le64 tpa_aborts; +}; + +/* Structure data header (16 bytes) */ +struct hwrm_struct_hdr { + __le16 struct_id; + #define STRUCT_HDR_STRUCT_ID_LLDP_CFG 0x41bUL + #define STRUCT_HDR_STRUCT_ID_DCBX_ETS_CFG 0x41dUL + #define STRUCT_HDR_STRUCT_ID_DCBX_PFC_CFG 0x41fUL + #define STRUCT_HDR_STRUCT_ID_DCBX_APP_CFG 0x421UL + #define STRUCT_HDR_STRUCT_ID_DCBX_STATE_CFG 0x422UL + #define STRUCT_HDR_STRUCT_ID_LLDP_GENERIC_CFG 0x424UL + #define STRUCT_HDR_STRUCT_ID_LLDP_DEVICE_CFG 0x426UL + __le16 len; + u8 version; + u8 count; + __le16 subtype; + __le16 next_offset; + #define STRUCT_HDR_NEXT_OFFSET_LAST 0x0UL + __le16 unused_0[3]; +}; + +/* DCBX Application configuration structure (8 bytes) */ +struct hwrm_struct_data_dcbx_app_cfg { + __le16 protocol_id; + u8 protocol_selector; + #define STRUCT_DATA_DCBX_APP_CFG_PROTOCOL_SELECTOR_ETHER_TYPE 0x1UL + #define STRUCT_DATA_DCBX_APP_CFG_PROTOCOL_SELECTOR_TCP_PORT 0x2UL + #define STRUCT_DATA_DCBX_APP_CFG_PROTOCOL_SELECTOR_UDP_PORT 0x3UL + #define STRUCT_DATA_DCBX_APP_CFG_PROTOCOL_SELECTOR_TCP_UDP_PORT 0x4UL + u8 priority; + u8 valid; + u8 unused_0[3]; +}; + #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index 60e2af8..bff626a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -34,8 +34,7 @@ static int bnxt_hwrm_fwd_async_event_cmpl(struct bnxt *bp, /* broadcast this async event to all VFs */ req.encap_async_event_target_id = cpu_to_le16(0xffff); async_cmpl = (struct hwrm_async_event_cmpl *)req.encap_async_event_cmpl; - async_cmpl->type = - cpu_to_le16(HWRM_ASYNC_EVENT_CMPL_TYPE_HWRM_ASYNC_EVENT); + async_cmpl->type = cpu_to_le16(ASYNC_EVENT_CMPL_TYPE_HWRM_ASYNC_EVENT); async_cmpl->event_id = cpu_to_le16(event_id); mutex_lock(&bp->hwrm_cmd_lock); @@ -288,7 +287,7 @@ int bnxt_set_vf_link_state(struct net_device *dev, int vf_id, int link) } if (vf->flags & (BNXT_VF_LINK_UP | BNXT_VF_LINK_FORCED)) rc = bnxt_hwrm_fwd_async_event_cmpl(bp, vf, - HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE); + ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE); return rc; } @@ -578,8 +577,7 @@ void bnxt_sriov_disable(struct bnxt *bp) if (pci_vfs_assigned(bp->pdev)) { bnxt_hwrm_fwd_async_event_cmpl( - bp, NULL, - HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD); + bp, NULL, ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD); netdev_warn(bp->dev, "Unable to free %d VFs because some are assigned to VMs.\n", num_vfs); } else { -- cgit v1.1 From 7df4ae9fe85567a1710048da8229bd85e0da9df7 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 2 Dec 2016 21:17:17 -0500 Subject: bnxt_en: Implement DCBNL to support host-based DCBX. Support only IEEE DCBX initially. Add IEEE DCBNL ops and functions to get and set the hardware DCBX parameters. The DCB code is conditional on Kconfig CONFIG_BNXT_DCB. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/Kconfig | 10 + drivers/net/ethernet/broadcom/bnxt/Makefile | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 +- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 9 + drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c | 490 ++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h | 41 +++ 6 files changed, 557 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index bd8c80c..404c020 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -203,4 +203,14 @@ config BNXT_SRIOV Virtualization support in the NetXtreme-C/E products. This allows for virtual function acceleration in virtual environments. +config BNXT_DCB + bool "Data Center Bridging (DCB) Support" + default n + depends on BNXT && DCB + ---help--- + Say Y here if you want to use Data Center Bridging (DCB) in the + driver. + + If unsure, say N. + endif # NET_VENDOR_BROADCOM diff --git a/drivers/net/ethernet/broadcom/bnxt/Makefile b/drivers/net/ethernet/broadcom/bnxt/Makefile index 97e78e2..b233a86 100644 --- a/drivers/net/ethernet/broadcom/bnxt/Makefile +++ b/drivers/net/ethernet/broadcom/bnxt/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_BNXT) += bnxt_en.o -bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o +bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7ba5a99..e8ab5fd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -54,6 +54,7 @@ #include "bnxt.h" #include "bnxt_sriov.h" #include "bnxt_ethtool.h" +#include "bnxt_dcb.h" #define BNXT_TX_TIMEOUT (5 * HZ) @@ -4997,7 +4998,7 @@ static void bnxt_enable_napi(struct bnxt *bp) } } -static void bnxt_tx_disable(struct bnxt *bp) +void bnxt_tx_disable(struct bnxt *bp) { int i; struct bnxt_tx_ring_info *txr; @@ -5015,7 +5016,7 @@ static void bnxt_tx_disable(struct bnxt *bp) netif_carrier_off(bp->dev); } -static void bnxt_tx_enable(struct bnxt *bp) +void bnxt_tx_enable(struct bnxt *bp) { int i; struct bnxt_tx_ring_info *txr; @@ -6686,6 +6687,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) bnxt_hwrm_func_drv_unrgtr(bp); bnxt_free_hwrm_resources(bp); + bnxt_dcb_free(bp); pci_iounmap(pdev, bp->bar2); pci_iounmap(pdev, bp->bar1); pci_iounmap(pdev, bp->bar0); @@ -6913,6 +6915,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->min_mtu = ETH_ZLEN; dev->max_mtu = 9500; + bnxt_dcb_init(bp); + #ifdef CONFIG_BNXT_SRIOV init_waitqueue_head(&bp->sriov_cfg_wait); #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 1f3d852..2a714cf 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1026,6 +1026,13 @@ struct bnxt { struct bnxt_irq *irq_tbl; u8 mac_addr[ETH_ALEN]; +#ifdef CONFIG_BNXT_DCB + struct ieee_pfc *ieee_pfc; + struct ieee_ets *ieee_ets; + u8 dcbx_cap; + u8 default_pri; +#endif /* CONFIG_BNXT_DCB */ + u32 msg_enable; u32 hwrm_spec_code; @@ -1221,6 +1228,8 @@ int hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message_silent(struct bnxt *, void *, u32, int); int bnxt_hwrm_set_coal(struct bnxt *); int bnxt_hwrm_func_qcaps(struct bnxt *); +void bnxt_tx_disable(struct bnxt *bp); +void bnxt_tx_enable(struct bnxt *bp); int bnxt_hwrm_set_pause(struct bnxt *); int bnxt_hwrm_set_link_setting(struct bnxt *, bool, bool); int bnxt_hwrm_fw_set_time(struct bnxt *); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c new file mode 100644 index 0000000..f391b47 --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -0,0 +1,490 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2014-2016 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "bnxt_hsi.h" +#include "bnxt.h" +#include "bnxt_dcb.h" + +#ifdef CONFIG_BNXT_DCB +static int bnxt_hwrm_queue_pri2cos_cfg(struct bnxt *bp, struct ieee_ets *ets) +{ + struct hwrm_queue_pri2cos_cfg_input req = {0}; + int rc = 0, i; + u8 *pri2cos; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_PRI2COS_CFG, -1, -1); + req.flags = cpu_to_le32(QUEUE_PRI2COS_CFG_REQ_FLAGS_PATH_BIDIR | + QUEUE_PRI2COS_CFG_REQ_FLAGS_IVLAN); + + pri2cos = &req.pri0_cos_queue_id; + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + req.enables |= cpu_to_le32( + QUEUE_PRI2COS_CFG_REQ_ENABLES_PRI0_COS_QUEUE_ID << i); + + pri2cos[i] = bp->q_info[ets->prio_tc[i]].queue_id; + } + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + return rc; +} + +static int bnxt_hwrm_queue_pri2cos_qcfg(struct bnxt *bp, struct ieee_ets *ets) +{ + struct hwrm_queue_pri2cos_qcfg_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_queue_pri2cos_qcfg_input req = {0}; + int rc = 0; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_PRI2COS_QCFG, -1, -1); + req.flags = cpu_to_le32(QUEUE_PRI2COS_QCFG_REQ_FLAGS_IVLAN); + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + u8 *pri2cos = &resp->pri0_cos_queue_id; + int i, j; + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + u8 queue_id = pri2cos[i]; + + for (j = 0; j < bp->max_tc; j++) { + if (bp->q_info[j].queue_id == queue_id) { + ets->prio_tc[i] = j; + break; + } + } + } + } + return rc; +} + +static int bnxt_hwrm_queue_cos2bw_cfg(struct bnxt *bp, struct ieee_ets *ets, + u8 max_tc) +{ + struct hwrm_queue_cos2bw_cfg_input req = {0}; + struct bnxt_cos2bw_cfg cos2bw; + int rc = 0, i; + void *data; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_COS2BW_CFG, -1, -1); + data = &req.unused_0; + for (i = 0; i < max_tc; i++, data += sizeof(cos2bw) - 4) { + req.enables |= cpu_to_le32( + QUEUE_COS2BW_CFG_REQ_ENABLES_COS_QUEUE_ID0_VALID << i); + + memset(&cos2bw, 0, sizeof(cos2bw)); + cos2bw.queue_id = bp->q_info[i].queue_id; + if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_STRICT) { + cos2bw.tsa = + QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_SP; + cos2bw.pri_lvl = i; + } else { + cos2bw.tsa = + QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_ETS; + cos2bw.bw_weight = ets->tc_tx_bw[i]; + } + memcpy(data, &cos2bw.queue_id, sizeof(cos2bw) - 4); + if (i == 0) { + req.queue_id0 = cos2bw.queue_id; + req.unused_0 = 0; + } + } + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + return rc; +} + +static int bnxt_hwrm_queue_cos2bw_qcfg(struct bnxt *bp, struct ieee_ets *ets) +{ + struct hwrm_queue_cos2bw_qcfg_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_queue_cos2bw_qcfg_input req = {0}; + struct bnxt_cos2bw_cfg cos2bw; + void *data; + int rc, i; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_COS2BW_QCFG, -1, -1); + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + return rc; + + data = &resp->queue_id0 + offsetof(struct bnxt_cos2bw_cfg, queue_id); + for (i = 0; i < bp->max_tc; i++, data += sizeof(cos2bw) - 4) { + int j; + + memcpy(&cos2bw.queue_id, data, sizeof(cos2bw) - 4); + if (i == 0) + cos2bw.queue_id = resp->queue_id0; + + for (j = 0; j < bp->max_tc; j++) { + if (bp->q_info[j].queue_id != cos2bw.queue_id) + continue; + if (cos2bw.tsa == + QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_SP) { + ets->tc_tsa[j] = IEEE_8021QAZ_TSA_STRICT; + } else { + ets->tc_tsa[j] = IEEE_8021QAZ_TSA_ETS; + ets->tc_tx_bw[j] = cos2bw.bw_weight; + } + } + } + return 0; +} + +static int bnxt_hwrm_queue_cfg(struct bnxt *bp, unsigned int lltc_mask) +{ + struct hwrm_queue_cfg_input req = {0}; + int i; + + if (netif_running(bp->dev)) + bnxt_tx_disable(bp); + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_CFG, -1, -1); + req.flags = cpu_to_le32(QUEUE_CFG_REQ_FLAGS_PATH_BIDIR); + req.enables = cpu_to_le32(QUEUE_CFG_REQ_ENABLES_SERVICE_PROFILE); + + /* Configure lossless queues to lossy first */ + req.service_profile = QUEUE_CFG_REQ_SERVICE_PROFILE_LOSSY; + for (i = 0; i < bp->max_tc; i++) { + if (BNXT_LLQ(bp->q_info[i].queue_profile)) { + req.queue_id = cpu_to_le32(bp->q_info[i].queue_id); + hwrm_send_message(bp, &req, sizeof(req), + HWRM_CMD_TIMEOUT); + bp->q_info[i].queue_profile = + QUEUE_CFG_REQ_SERVICE_PROFILE_LOSSY; + } + } + + /* Now configure desired queues to lossless */ + req.service_profile = QUEUE_CFG_REQ_SERVICE_PROFILE_LOSSLESS; + for (i = 0; i < bp->max_tc; i++) { + if (lltc_mask & (1 << i)) { + req.queue_id = cpu_to_le32(bp->q_info[i].queue_id); + hwrm_send_message(bp, &req, sizeof(req), + HWRM_CMD_TIMEOUT); + bp->q_info[i].queue_profile = + QUEUE_CFG_REQ_SERVICE_PROFILE_LOSSLESS; + } + } + if (netif_running(bp->dev)) + bnxt_tx_enable(bp); + + return 0; +} + +static int bnxt_hwrm_queue_pfc_cfg(struct bnxt *bp, struct ieee_pfc *pfc) +{ + struct hwrm_queue_pfcenable_cfg_input req = {0}; + struct ieee_ets *my_ets = bp->ieee_ets; + unsigned int tc_mask = 0, pri_mask = 0; + u8 i, pri, lltc_count = 0; + bool need_q_recfg = false; + int rc; + + if (!my_ets) + return -EINVAL; + + for (i = 0; i < bp->max_tc; i++) { + for (pri = 0; pri < IEEE_8021QAZ_MAX_TCS; pri++) { + if ((pfc->pfc_en & (1 << pri)) && + (my_ets->prio_tc[pri] == i)) { + pri_mask |= 1 << pri; + tc_mask |= 1 << i; + } + } + if (tc_mask & (1 << i)) + lltc_count++; + } + if (lltc_count > bp->max_lltc) + return -EINVAL; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_PFCENABLE_CFG, -1, -1); + req.flags = cpu_to_le32(pri_mask); + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + return rc; + + for (i = 0; i < bp->max_tc; i++) { + if (tc_mask & (1 << i)) { + if (!BNXT_LLQ(bp->q_info[i].queue_profile)) + need_q_recfg = true; + } + } + + if (need_q_recfg) + rc = bnxt_hwrm_queue_cfg(bp, tc_mask); + + return rc; +} + +static int bnxt_hwrm_queue_pfc_qcfg(struct bnxt *bp, struct ieee_pfc *pfc) +{ + struct hwrm_queue_pfcenable_qcfg_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_queue_pfcenable_qcfg_input req = {0}; + u8 pri_mask; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_PFCENABLE_QCFG, -1, -1); + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + return rc; + + pri_mask = le32_to_cpu(resp->flags); + pfc->pfc_en = pri_mask; + return 0; +} + +static int bnxt_ets_validate(struct bnxt *bp, struct ieee_ets *ets, u8 *tc) +{ + int total_ets_bw = 0; + u8 max_tc = 0; + int i; + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + if (ets->prio_tc[i] > bp->max_tc) { + netdev_err(bp->dev, "priority to TC mapping exceeds TC count %d\n", + ets->prio_tc[i]); + return -EINVAL; + } + if (ets->prio_tc[i] > max_tc) + max_tc = ets->prio_tc[i]; + + if ((ets->tc_tx_bw[i] || ets->tc_tsa[i]) && i > bp->max_tc) + return -EINVAL; + + switch (ets->tc_tsa[i]) { + case IEEE_8021QAZ_TSA_STRICT: + break; + case IEEE_8021QAZ_TSA_ETS: + total_ets_bw += ets->tc_tx_bw[i]; + break; + default: + return -ENOTSUPP; + } + } + if (total_ets_bw > 100) + return -EINVAL; + + *tc = max_tc + 1; + return 0; +} + +static int bnxt_dcbnl_ieee_getets(struct net_device *dev, struct ieee_ets *ets) +{ + struct bnxt *bp = netdev_priv(dev); + struct ieee_ets *my_ets = bp->ieee_ets; + + ets->ets_cap = bp->max_tc; + + if (!my_ets) { + int rc; + + if (bp->dcbx_cap & DCB_CAP_DCBX_HOST) + return 0; + + my_ets = kzalloc(sizeof(*my_ets), GFP_KERNEL); + if (!my_ets) + return 0; + rc = bnxt_hwrm_queue_cos2bw_qcfg(bp, my_ets); + if (rc) + return 0; + rc = bnxt_hwrm_queue_pri2cos_qcfg(bp, my_ets); + if (rc) + return 0; + } + + ets->cbs = my_ets->cbs; + memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw)); + memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw)); + memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa)); + memcpy(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc)); + return 0; +} + +static int bnxt_dcbnl_ieee_setets(struct net_device *dev, struct ieee_ets *ets) +{ + struct bnxt *bp = netdev_priv(dev); + struct ieee_ets *my_ets = bp->ieee_ets; + u8 max_tc = 0; + int rc, i; + + if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) || + !(bp->dcbx_cap & DCB_CAP_DCBX_HOST)) + return -EINVAL; + + rc = bnxt_ets_validate(bp, ets, &max_tc); + if (!rc) { + if (!my_ets) { + my_ets = kzalloc(sizeof(*my_ets), GFP_KERNEL); + if (!my_ets) + return -ENOMEM; + /* initialize PRI2TC mappings to invalid value */ + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) + my_ets->prio_tc[i] = IEEE_8021QAZ_MAX_TCS; + bp->ieee_ets = my_ets; + } + rc = bnxt_setup_mq_tc(dev, max_tc); + if (rc) + return rc; + rc = bnxt_hwrm_queue_cos2bw_cfg(bp, ets, max_tc); + if (rc) + return rc; + rc = bnxt_hwrm_queue_pri2cos_cfg(bp, ets); + if (rc) + return rc; + memcpy(my_ets, ets, sizeof(*my_ets)); + } + return rc; +} + +static int bnxt_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc) +{ + struct bnxt *bp = netdev_priv(dev); + struct ieee_pfc *my_pfc = bp->ieee_pfc; + int rc; + + pfc->pfc_cap = bp->max_lltc; + + if (!my_pfc) { + if (bp->dcbx_cap & DCB_CAP_DCBX_HOST) + return 0; + + my_pfc = kzalloc(sizeof(*my_pfc), GFP_KERNEL); + if (!my_pfc) + return 0; + bp->ieee_pfc = my_pfc; + rc = bnxt_hwrm_queue_pfc_qcfg(bp, my_pfc); + if (rc) + return 0; + } + + pfc->pfc_en = my_pfc->pfc_en; + pfc->mbc = my_pfc->mbc; + pfc->delay = my_pfc->delay; + + return 0; +} + +static int bnxt_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) +{ + struct bnxt *bp = netdev_priv(dev); + struct ieee_pfc *my_pfc = bp->ieee_pfc; + int rc; + + if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) || + !(bp->dcbx_cap & DCB_CAP_DCBX_HOST)) + return -EINVAL; + + if (!my_pfc) { + my_pfc = kzalloc(sizeof(*my_pfc), GFP_KERNEL); + if (!my_pfc) + return -ENOMEM; + bp->ieee_pfc = my_pfc; + } + rc = bnxt_hwrm_queue_pfc_cfg(bp, pfc); + if (!rc) + memcpy(my_pfc, pfc, sizeof(*my_pfc)); + + return rc; +} + +static int bnxt_dcbnl_ieee_setapp(struct net_device *dev, struct dcb_app *app) +{ + struct bnxt *bp = netdev_priv(dev); + int rc = -EINVAL; + + if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) || + !(bp->dcbx_cap & DCB_CAP_DCBX_HOST)) + return -EINVAL; + + rc = dcb_ieee_setapp(dev, app); + return rc; +} + +static int bnxt_dcbnl_ieee_delapp(struct net_device *dev, struct dcb_app *app) +{ + struct bnxt *bp = netdev_priv(dev); + int rc; + + if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) + return -EINVAL; + + rc = dcb_ieee_delapp(dev, app); + return rc; +} + +static u8 bnxt_dcbnl_getdcbx(struct net_device *dev) +{ + struct bnxt *bp = netdev_priv(dev); + + return bp->dcbx_cap; +} + +static u8 bnxt_dcbnl_setdcbx(struct net_device *dev, u8 mode) +{ + struct bnxt *bp = netdev_priv(dev); + + /* only support IEEE */ + if ((mode & DCB_CAP_DCBX_VER_CEE) || !(mode & DCB_CAP_DCBX_VER_IEEE)) + return 1; + + if ((mode & DCB_CAP_DCBX_HOST) && BNXT_VF(bp)) + return 1; + + if (mode == bp->dcbx_cap) + return 0; + + bp->dcbx_cap = mode; + return 0; +} + +static const struct dcbnl_rtnl_ops dcbnl_ops = { + .ieee_getets = bnxt_dcbnl_ieee_getets, + .ieee_setets = bnxt_dcbnl_ieee_setets, + .ieee_getpfc = bnxt_dcbnl_ieee_getpfc, + .ieee_setpfc = bnxt_dcbnl_ieee_setpfc, + .ieee_setapp = bnxt_dcbnl_ieee_setapp, + .ieee_delapp = bnxt_dcbnl_ieee_delapp, + .getdcbx = bnxt_dcbnl_getdcbx, + .setdcbx = bnxt_dcbnl_setdcbx, +}; + +void bnxt_dcb_init(struct bnxt *bp) +{ + if (bp->hwrm_spec_code < 0x10501) + return; + + bp->dcbx_cap = DCB_CAP_DCBX_VER_IEEE; + if (BNXT_PF(bp)) + bp->dcbx_cap |= DCB_CAP_DCBX_HOST; + else + bp->dcbx_cap |= DCB_CAP_DCBX_LLD_MANAGED; + bp->dev->dcbnl_ops = &dcbnl_ops; +} + +void bnxt_dcb_free(struct bnxt *bp) +{ + kfree(bp->ieee_pfc); + kfree(bp->ieee_ets); + bp->ieee_pfc = NULL; + bp->ieee_ets = NULL; +} + +#else + +void bnxt_dcb_init(struct bnxt *bp) +{ +} + +void bnxt_dcb_free(struct bnxt *bp) +{ +} + +#endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h new file mode 100644 index 0000000..35a0d28 --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h @@ -0,0 +1,41 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2014-2016 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef BNXT_DCB_H +#define BNXT_DCB_H + +#include + +struct bnxt_dcb { + u8 max_tc; + struct ieee_pfc *ieee_pfc; + struct ieee_ets *ieee_ets; + u8 dcbx_cap; + u8 default_pri; +}; + +struct bnxt_cos2bw_cfg { + u8 pad[3]; + u8 queue_id; + __le32 min_bw; + __le32 max_bw; + u8 tsa; + u8 pri_lvl; + u8 bw_weight; + u8 unused; +}; + +#define BNXT_LLQ(q_profile) \ + ((q_profile) == QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS) + +#define HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL 0x0300 + +void bnxt_dcb_init(struct bnxt *bp); +void bnxt_dcb_free(struct bnxt *bp); +#endif -- cgit v1.1 From c77192f2042537b1e0e5f520db91e4d28778195f Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 2 Dec 2016 21:17:18 -0500 Subject: bnxt_en: Add PFC statistics. Report PFC statistics to ethtool -S and DCBNL. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 7 +++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c | 14 +++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 23 ++++++++++++++++------- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 2a714cf..b4abc1b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1124,6 +1124,13 @@ struct bnxt { u32 lpi_tmr_hi; }; +#define BNXT_RX_STATS_OFFSET(counter) \ + (offsetof(struct rx_port_stats, counter) / 8) + +#define BNXT_TX_STATS_OFFSET(counter) \ + ((offsetof(struct tx_port_stats, counter) + \ + sizeof(struct rx_port_stats) + 512) / 8) + #ifdef CONFIG_NET_RX_BUSY_POLL static inline void bnxt_enable_poll(struct bnxt_napi *bnapi) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index f391b47..fdf2d8c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -347,8 +347,10 @@ static int bnxt_dcbnl_ieee_setets(struct net_device *dev, struct ieee_ets *ets) static int bnxt_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc) { struct bnxt *bp = netdev_priv(dev); + __le64 *stats = (__le64 *)bp->hw_rx_port_stats; struct ieee_pfc *my_pfc = bp->ieee_pfc; - int rc; + long rx_off, tx_off; + int i, rc; pfc->pfc_cap = bp->max_lltc; @@ -369,6 +371,16 @@ static int bnxt_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc) pfc->mbc = my_pfc->mbc; pfc->delay = my_pfc->delay; + if (!stats) + return 0; + + rx_off = BNXT_RX_STATS_OFFSET(rx_pfc_ena_frames_pri0); + tx_off = BNXT_TX_STATS_OFFSET(tx_pfc_ena_frames_pri0); + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++, rx_off++, tx_off++) { + pfc->requests[i] = le64_to_cpu(*(stats + tx_off)); + pfc->indications[i] = le64_to_cpu(*(stats + rx_off)); + } + return 0; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index fa6125e..784aa77 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -107,16 +107,9 @@ static int bnxt_set_coalesce(struct net_device *dev, #define BNXT_NUM_STATS 21 -#define BNXT_RX_STATS_OFFSET(counter) \ - (offsetof(struct rx_port_stats, counter) / 8) - #define BNXT_RX_STATS_ENTRY(counter) \ { BNXT_RX_STATS_OFFSET(counter), __stringify(counter) } -#define BNXT_TX_STATS_OFFSET(counter) \ - ((offsetof(struct tx_port_stats, counter) + \ - sizeof(struct rx_port_stats) + 512) / 8) - #define BNXT_TX_STATS_ENTRY(counter) \ { BNXT_TX_STATS_OFFSET(counter), __stringify(counter) } @@ -150,6 +143,14 @@ static const struct { BNXT_RX_STATS_ENTRY(rx_tagged_frames), BNXT_RX_STATS_ENTRY(rx_double_tagged_frames), BNXT_RX_STATS_ENTRY(rx_good_frames), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri0), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri1), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri2), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri3), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri4), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri5), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri6), + BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri7), BNXT_RX_STATS_ENTRY(rx_undrsz_frames), BNXT_RX_STATS_ENTRY(rx_eee_lpi_events), BNXT_RX_STATS_ENTRY(rx_eee_lpi_duration), @@ -179,6 +180,14 @@ static const struct { BNXT_TX_STATS_ENTRY(tx_fcs_err_frames), BNXT_TX_STATS_ENTRY(tx_err), BNXT_TX_STATS_ENTRY(tx_fifo_underruns), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri0), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri1), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri2), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri3), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri4), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri5), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri6), + BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri7), BNXT_TX_STATS_ENTRY(tx_eee_lpi_events), BNXT_TX_STATS_ENTRY(tx_eee_lpi_duration), BNXT_TX_STATS_ENTRY(tx_total_collisions), -- cgit v1.1 From 40fc3423b983b864bf70b03199191260ae9b2ea6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:50 -0800 Subject: tcp: tsq: add tsq_flags / tsq_enum This is a cleanup, to ease code review of following patches. Old 'enum tsq_flags' is renamed, and a new enumeration is added with the flags used in cmpxchg() operations as opposed to single bit operations. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 11 ++++++++++- net/ipv4/tcp_output.c | 16 ++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 734bab4..d8be083 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -364,7 +364,7 @@ struct tcp_sock { u32 *saved_syn; }; -enum tsq_flags { +enum tsq_enum { TSQ_THROTTLED, TSQ_QUEUED, TCP_TSQ_DEFERRED, /* tcp_tasklet_func() found socket was owned */ @@ -375,6 +375,15 @@ enum tsq_flags { */ }; +enum tsq_flags { + TSQF_THROTTLED = (1UL << TSQ_THROTTLED), + TSQF_QUEUED = (1UL << TSQ_QUEUED), + TCPF_TSQ_DEFERRED = (1UL << TCP_TSQ_DEFERRED), + TCPF_WRITE_TIMER_DEFERRED = (1UL << TCP_WRITE_TIMER_DEFERRED), + TCPF_DELACK_TIMER_DEFERRED = (1UL << TCP_DELACK_TIMER_DEFERRED), + TCPF_MTU_REDUCED_DEFERRED = (1UL << TCP_MTU_REDUCED_DEFERRED), +}; + static inline struct tcp_sock *tcp_sk(const struct sock *sk) { return (struct tcp_sock *)sk; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c7adcb5..8f0289b 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -784,10 +784,10 @@ static void tcp_tasklet_func(unsigned long data) } } -#define TCP_DEFERRED_ALL ((1UL << TCP_TSQ_DEFERRED) | \ - (1UL << TCP_WRITE_TIMER_DEFERRED) | \ - (1UL << TCP_DELACK_TIMER_DEFERRED) | \ - (1UL << TCP_MTU_REDUCED_DEFERRED)) +#define TCP_DEFERRED_ALL (TCPF_TSQ_DEFERRED | \ + TCPF_WRITE_TIMER_DEFERRED | \ + TCPF_DELACK_TIMER_DEFERRED | \ + TCPF_MTU_REDUCED_DEFERRED) /** * tcp_release_cb - tcp release_sock() callback * @sk: socket @@ -808,7 +808,7 @@ void tcp_release_cb(struct sock *sk) nflags = flags & ~TCP_DEFERRED_ALL; } while (cmpxchg(&tp->tsq_flags, flags, nflags) != flags); - if (flags & (1UL << TCP_TSQ_DEFERRED)) + if (flags & TCPF_TSQ_DEFERRED) tcp_tsq_handler(sk); /* Here begins the tricky part : @@ -822,15 +822,15 @@ void tcp_release_cb(struct sock *sk) */ sock_release_ownership(sk); - if (flags & (1UL << TCP_WRITE_TIMER_DEFERRED)) { + if (flags & TCPF_WRITE_TIMER_DEFERRED) { tcp_write_timer_handler(sk); __sock_put(sk); } - if (flags & (1UL << TCP_DELACK_TIMER_DEFERRED)) { + if (flags & TCPF_DELACK_TIMER_DEFERRED) { tcp_delack_timer_handler(sk); __sock_put(sk); } - if (flags & (1UL << TCP_MTU_REDUCED_DEFERRED)) { + if (flags & TCPF_MTU_REDUCED_DEFERRED) { inet_csk(sk)->icsk_af_ops->mtu_reduced(sk); __sock_put(sk); } -- cgit v1.1 From 408f0a6c21e124cc4f6c7aa370b38aa47e55428d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:51 -0800 Subject: tcp: tsq: remove one locked operation in tcp_wfree() Instead of atomically clear TSQ_THROTTLED and atomically set TSQ_QUEUED bits, use one cmpxchg() to perform a single locked operation. Since the following patch will also set TCP_TSQ_DEFERRED here, this cmpxchg() will make this addition free. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8f0289b..4adaf8e 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -860,6 +860,7 @@ void tcp_wfree(struct sk_buff *skb) { struct sock *sk = skb->sk; struct tcp_sock *tp = tcp_sk(sk); + unsigned long flags, nval, oval; int wmem; /* Keep one reference on sk_wmem_alloc. @@ -877,11 +878,17 @@ void tcp_wfree(struct sk_buff *skb) if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) goto out; - if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) && - !test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) { - unsigned long flags; + for (oval = READ_ONCE(tp->tsq_flags);; oval = nval) { struct tsq_tasklet *tsq; + if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED)) + goto out; + + nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED; + nval = cmpxchg(&tp->tsq_flags, oval, nval); + if (nval != oval) + continue; + /* queue this socket to tasklet queue */ local_irq_save(flags); tsq = this_cpu_ptr(&tsq_tasklet); -- cgit v1.1 From b223feb9de2a65c533ff95c08e834fa732906ea5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:52 -0800 Subject: tcp: tsq: add shortcut in tcp_tasklet_func() Under high stress, I've seen tcp_tasklet_func() consuming ~700 usec, handling ~150 tcp sockets. By setting TCP_TSQ_DEFERRED in tcp_wfree(), we give a chance for other cpus/threads entering tcp_write_xmit() to grab it, allowing tcp_tasklet_func() to skip sockets that already did an xmit cycle. In the future, we might give to ACK processing an increased budget to reduce even more tcp_tasklet_func() amount of work. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 4adaf8e..fa23b68 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -767,19 +767,19 @@ static void tcp_tasklet_func(unsigned long data) list_for_each_safe(q, n, &list) { tp = list_entry(q, struct tcp_sock, tsq_node); list_del(&tp->tsq_node); + clear_bit(TSQ_QUEUED, &tp->tsq_flags); sk = (struct sock *)tp; - bh_lock_sock(sk); - - if (!sock_owned_by_user(sk)) { - tcp_tsq_handler(sk); - } else { - /* defer the work to tcp_release_cb() */ - set_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); + if (!sk->sk_lock.owned && + test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) { + bh_lock_sock(sk); + if (!sock_owned_by_user(sk)) { + clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); + tcp_tsq_handler(sk); + } + bh_unlock_sock(sk); } - bh_unlock_sock(sk); - clear_bit(TSQ_QUEUED, &tp->tsq_flags); sk_free(sk); } } @@ -884,7 +884,7 @@ void tcp_wfree(struct sk_buff *skb) if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED)) goto out; - nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED; + nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED | TCPF_TSQ_DEFERRED; nval = cmpxchg(&tp->tsq_flags, oval, nval); if (nval != oval) continue; @@ -2229,6 +2229,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) break; + if (test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) + clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); if (tcp_small_queue_check(sk, skb, 0)) break; -- cgit v1.1 From a9b204d1564702b704ad6fe74f10a102c7b87ba3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:53 -0800 Subject: tcp: tsq: avoid one atomic in tcp_wfree() Under high load, tcp_wfree() has an atomic operation trying to schedule a tasklet over and over. We can schedule it only if our per cpu list was empty. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index fa23b68..0db63ef 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -880,6 +880,7 @@ void tcp_wfree(struct sk_buff *skb) for (oval = READ_ONCE(tp->tsq_flags);; oval = nval) { struct tsq_tasklet *tsq; + bool empty; if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED)) goto out; @@ -892,8 +893,10 @@ void tcp_wfree(struct sk_buff *skb) /* queue this socket to tasklet queue */ local_irq_save(flags); tsq = this_cpu_ptr(&tsq_tasklet); + empty = list_empty(&tsq->head); list_add(&tp->tsq_node, &tsq->head); - tasklet_schedule(&tsq->tasklet); + if (empty) + tasklet_schedule(&tsq->tasklet); local_irq_restore(flags); return; } -- cgit v1.1 From 75eefc6c59fd2c5f1ab95a3a113c217237d12a31 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:54 -0800 Subject: tcp: tsq: add a shortcut in tcp_small_queue_check() Always allow the two first skbs in write queue to be sent, regardless of sk_wmem_alloc/sk_pacing_rate values. This helps a lot in situations where TX completions are delayed either because of driver latencies or softirq latencies. Test is done with no cache line misses. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 0db63ef..d5c4674 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2091,6 +2091,15 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, limit <<= factor; if (atomic_read(&sk->sk_wmem_alloc) > limit) { + /* Always send the 1st or 2nd skb in write queue. + * No need to wait for TX completion to call us back, + * after softirq/tasklet schedule. + * This helps when TX completions are delayed too much. + */ + if (skb == sk->sk_write_queue.next || + skb->prev == sk->sk_write_queue.next) + return false; + set_bit(TSQ_THROTTLED, &tcp_sk(sk)->tsq_flags); /* It is possible TX completion already happened * before we set TSQ_THROTTLED, so we must -- cgit v1.1 From 12a59abc22d6664f7d3944f625ceefee92de8820 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:55 -0800 Subject: tcp: tcp_mtu_probe() is likely to exit early Adding a likely() in tcp_mtu_probe() moves its code which used to be inlined in front of tcp_write_xmit() We still have a cache line miss to access icsk->icsk_mtup.enabled, we will probably have to reorganize fields to help data locality. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d5c4674..5f04bee 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1932,26 +1932,26 @@ static inline void tcp_mtu_check_reprobe(struct sock *sk) */ static int tcp_mtu_probe(struct sock *sk) { - struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb, *nskb, *next; struct net *net = sock_net(sk); - int len; int probe_size; int size_needed; - int copy; + int copy, len; int mss_now; int interval; /* Not currently probing/verifying, * not in recovery, * have enough cwnd, and - * not SACKing (the variable headers throw things off) */ - if (!icsk->icsk_mtup.enabled || - icsk->icsk_mtup.probe_size || - inet_csk(sk)->icsk_ca_state != TCP_CA_Open || - tp->snd_cwnd < 11 || - tp->rx_opt.num_sacks || tp->rx_opt.dsack) + * not SACKing (the variable headers throw things off) + */ + if (likely(!icsk->icsk_mtup.enabled || + icsk->icsk_mtup.probe_size || + inet_csk(sk)->icsk_ca_state != TCP_CA_Open || + tp->snd_cwnd < 11 || + tp->rx_opt.num_sacks || tp->rx_opt.dsack)) return -1; /* Use binary search for probe_size between tcp_mss_base, -- cgit v1.1 From 9115e8cd2a0c6eaaa900c462721f12e1d45f326c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:56 -0800 Subject: net: reorganize struct sock for better data locality Group fields used in TX path, and keep some cache lines mostly read to permit sharing among cpus. Gained two 4 bytes holes on 64bit arches. Added a place holder for tcp tsq_flags, next to sk_wmem_alloc to speed up tcp_wfree() in the following patch. I have not added ____cacheline_aligned_in_smp, this might be done later. I prefer doing this once inet and tcp/udp sockets reorg is also done. Tested with both TCP and UDP. UDP receiver performance under flood increased by ~20 % : Accessing sk_filter/sk_wq/sk_napi_id no longer stalls because sk_drops was moved away from a critical cache line, now mostly read and shared. /* --- cacheline 4 boundary (256 bytes) --- */ unsigned int sk_napi_id; /* 0x100 0x4 */ int sk_rcvbuf; /* 0x104 0x4 */ struct sk_filter * sk_filter; /* 0x108 0x8 */ union { struct socket_wq * sk_wq; /* 0x8 */ struct socket_wq * sk_wq_raw; /* 0x8 */ }; /* 0x110 0x8 */ struct xfrm_policy * sk_policy[2]; /* 0x118 0x10 */ struct dst_entry * sk_rx_dst; /* 0x128 0x8 */ struct dst_entry * sk_dst_cache; /* 0x130 0x8 */ atomic_t sk_omem_alloc; /* 0x138 0x4 */ int sk_sndbuf; /* 0x13c 0x4 */ /* --- cacheline 5 boundary (320 bytes) --- */ int sk_wmem_queued; /* 0x140 0x4 */ atomic_t sk_wmem_alloc; /* 0x144 0x4 */ long unsigned int sk_tsq_flags; /* 0x148 0x8 */ struct sk_buff * sk_send_head; /* 0x150 0x8 */ struct sk_buff_head sk_write_queue; /* 0x158 0x18 */ __s32 sk_peek_off; /* 0x170 0x4 */ int sk_write_pending; /* 0x174 0x4 */ long int sk_sndtimeo; /* 0x178 0x8 */ Signed-off-by: Eric Dumazet Tested-by: Paolo Abeni Signed-off-by: David S. Miller --- include/net/sock.h | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 69afda6..6dfe3aa 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -343,6 +343,9 @@ struct sock { #define sk_rxhash __sk_common.skc_rxhash socket_lock_t sk_lock; + atomic_t sk_drops; + int sk_rcvlowat; + struct sk_buff_head sk_error_queue; struct sk_buff_head sk_receive_queue; /* * The backlog queue is special, it is always used with @@ -359,14 +362,13 @@ struct sock { struct sk_buff *tail; } sk_backlog; #define sk_rmem_alloc sk_backlog.rmem_alloc - int sk_forward_alloc; - __u32 sk_txhash; + int sk_forward_alloc; #ifdef CONFIG_NET_RX_BUSY_POLL - unsigned int sk_napi_id; unsigned int sk_ll_usec; + /* ===== mostly read cache line ===== */ + unsigned int sk_napi_id; #endif - atomic_t sk_drops; int sk_rcvbuf; struct sk_filter __rcu *sk_filter; @@ -379,11 +381,30 @@ struct sock { #endif struct dst_entry *sk_rx_dst; struct dst_entry __rcu *sk_dst_cache; - /* Note: 32bit hole on 64bit arches */ - atomic_t sk_wmem_alloc; atomic_t sk_omem_alloc; int sk_sndbuf; + + /* ===== cache line for TX ===== */ + int sk_wmem_queued; + atomic_t sk_wmem_alloc; + unsigned long sk_tsq_flags; + struct sk_buff *sk_send_head; struct sk_buff_head sk_write_queue; + __s32 sk_peek_off; + int sk_write_pending; + long sk_sndtimeo; + struct timer_list sk_timer; + __u32 sk_priority; + __u32 sk_mark; + u32 sk_pacing_rate; /* bytes per second */ + u32 sk_max_pacing_rate; + struct page_frag sk_frag; + netdev_features_t sk_route_caps; + netdev_features_t sk_route_nocaps; + int sk_gso_type; + unsigned int sk_gso_max_size; + gfp_t sk_allocation; + __u32 sk_txhash; /* * Because of non atomicity rules, all @@ -414,42 +435,24 @@ struct sock { #define SK_PROTOCOL_MAX U8_MAX kmemcheck_bitfield_end(flags); - int sk_wmem_queued; - gfp_t sk_allocation; - u32 sk_pacing_rate; /* bytes per second */ - u32 sk_max_pacing_rate; - netdev_features_t sk_route_caps; - netdev_features_t sk_route_nocaps; - int sk_gso_type; - unsigned int sk_gso_max_size; u16 sk_gso_max_segs; - int sk_rcvlowat; unsigned long sk_lingertime; - struct sk_buff_head sk_error_queue; struct proto *sk_prot_creator; rwlock_t sk_callback_lock; int sk_err, sk_err_soft; u32 sk_ack_backlog; u32 sk_max_ack_backlog; - __u32 sk_priority; - __u32 sk_mark; kuid_t sk_uid; struct pid *sk_peer_pid; const struct cred *sk_peer_cred; long sk_rcvtimeo; - long sk_sndtimeo; - struct timer_list sk_timer; ktime_t sk_stamp; u16 sk_tsflags; u8 sk_shutdown; u32 sk_tskey; struct socket *sk_socket; void *sk_user_data; - struct page_frag sk_frag; - struct sk_buff *sk_send_head; - __s32 sk_peek_off; - int sk_write_pending; #ifdef CONFIG_SECURITY void *sk_security; #endif -- cgit v1.1 From 7aa5470c2c09265902b5e4289afa82e4e7c2987e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Dec 2016 11:14:57 -0800 Subject: tcp: tsq: move tsq_flags close to sk_wmem_alloc tsq_flags being in the same cache line than sk_wmem_alloc makes a lot of sense. Both fields are changed from tcp_wfree() and more generally by various TSQ related functions. Prior patch made room in struct sock and added sk_tsq_flags, this patch deletes tsq_flags from struct tcp_sock. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - net/ipv4/tcp.c | 4 ++-- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_output.c | 24 +++++++++++------------- net/ipv4/tcp_timer.c | 4 ++-- net/ipv6/tcp_ipv6.c | 2 +- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index d8be083..fc5848d 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -186,7 +186,6 @@ struct tcp_sock { u32 tsoffset; /* timestamp offset */ struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ - unsigned long tsq_flags; /* Data for direct copy to user */ struct { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1149b48..1ef3165 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -663,9 +663,9 @@ static void tcp_push(struct sock *sk, int flags, int mss_now, if (tcp_should_autocork(sk, skb, size_goal)) { /* avoid atomic op if TSQ_THROTTLED bit is already set */ - if (!test_bit(TSQ_THROTTLED, &tp->tsq_flags)) { + if (!test_bit(TSQ_THROTTLED, &sk->sk_tsq_flags)) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTOCORKING); - set_bit(TSQ_THROTTLED, &tp->tsq_flags); + set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags); } /* It is possible TX completion already happened * before we set TSQ_THROTTLED. diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b50f059..30d81f5 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -443,7 +443,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) if (!sock_owned_by_user(sk)) { tcp_v4_mtu_reduced(sk); } else { - if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags)) + if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &sk->sk_tsq_flags)) sock_hold(sk); } goto out; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5f04bee..b45101f 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -767,14 +767,15 @@ static void tcp_tasklet_func(unsigned long data) list_for_each_safe(q, n, &list) { tp = list_entry(q, struct tcp_sock, tsq_node); list_del(&tp->tsq_node); - clear_bit(TSQ_QUEUED, &tp->tsq_flags); sk = (struct sock *)tp; + clear_bit(TSQ_QUEUED, &sk->sk_tsq_flags); + if (!sk->sk_lock.owned && - test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) { + test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags)) { bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { - clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); + clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags); tcp_tsq_handler(sk); } bh_unlock_sock(sk); @@ -797,16 +798,15 @@ static void tcp_tasklet_func(unsigned long data) */ void tcp_release_cb(struct sock *sk) { - struct tcp_sock *tp = tcp_sk(sk); unsigned long flags, nflags; /* perform an atomic operation only if at least one flag is set */ do { - flags = tp->tsq_flags; + flags = sk->sk_tsq_flags; if (!(flags & TCP_DEFERRED_ALL)) return; nflags = flags & ~TCP_DEFERRED_ALL; - } while (cmpxchg(&tp->tsq_flags, flags, nflags) != flags); + } while (cmpxchg(&sk->sk_tsq_flags, flags, nflags) != flags); if (flags & TCPF_TSQ_DEFERRED) tcp_tsq_handler(sk); @@ -878,7 +878,7 @@ void tcp_wfree(struct sk_buff *skb) if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) goto out; - for (oval = READ_ONCE(tp->tsq_flags);; oval = nval) { + for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) { struct tsq_tasklet *tsq; bool empty; @@ -886,7 +886,7 @@ void tcp_wfree(struct sk_buff *skb) goto out; nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED | TCPF_TSQ_DEFERRED; - nval = cmpxchg(&tp->tsq_flags, oval, nval); + nval = cmpxchg(&sk->sk_tsq_flags, oval, nval); if (nval != oval) continue; @@ -2100,7 +2100,7 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, skb->prev == sk->sk_write_queue.next) return false; - set_bit(TSQ_THROTTLED, &tcp_sk(sk)->tsq_flags); + set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags); /* It is possible TX completion already happened * before we set TSQ_THROTTLED, so we must * test again the condition. @@ -2241,8 +2241,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) break; - if (test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) - clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); + if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags)) + clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags); if (tcp_small_queue_check(sk, skb, 0)) break; @@ -3545,8 +3545,6 @@ void tcp_send_ack(struct sock *sk) /* We do not want pure acks influencing TCP Small Queues or fq/pacing * too much. * SKB_TRUESIZE(max(1 .. 66, MAX_TCP_HEADER)) is unfortunately ~784 - * We also avoid tcp_wfree() overhead (cache line miss accessing - * tp->tsq_flags) by using regular sock_wfree() */ skb_set_tcp_pure_ack(buff); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 3ea1cf8..3705075 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -310,7 +310,7 @@ static void tcp_delack_timer(unsigned long data) inet_csk(sk)->icsk_ack.blocked = 1; __NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED); /* deleguate our work to tcp_release_cb() */ - if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) + if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &sk->sk_tsq_flags)) sock_hold(sk); } bh_unlock_sock(sk); @@ -592,7 +592,7 @@ static void tcp_write_timer(unsigned long data) tcp_write_timer_handler(sk); } else { /* delegate our work to tcp_release_cb() */ - if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) + if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &sk->sk_tsq_flags)) sock_hold(sk); } bh_unlock_sock(sk); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index a2185a2..73bc8fc6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -399,7 +399,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!sock_owned_by_user(sk)) tcp_v6_mtu_reduced(sk); else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, - &tp->tsq_flags)) + &sk->sk_tsq_flags)) sock_hold(sk); goto out; } -- cgit v1.1 From f0903ea371f56c4977d1c07c2f8d4a55846c2801 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 3 Dec 2016 12:01:19 -0800 Subject: r8169: Add support for restarting auto-negotiation Implement ethtooll::nway_restart by utilizing mii_nway_restart. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 2830190..f9b97f5 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -2344,6 +2344,13 @@ static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data) } } +static int rtl8169_nway_reset(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return mii_nway_restart(&tp->mii); +} + static const struct ethtool_ops rtl8169_ethtool_ops = { .get_drvinfo = rtl8169_get_drvinfo, .get_regs_len = rtl8169_get_regs_len, @@ -2359,6 +2366,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_sset_count = rtl8169_get_sset_count, .get_ethtool_stats = rtl8169_get_ethtool_stats, .get_ts_info = ethtool_op_get_ts_info, + .nway_reset = rtl8169_nway_reset, }; static void rtl8169_get_mac_version(struct rtl8169_private *tp, -- cgit v1.1 From 3c839744b33782b930c5c61df35511ede5e5a574 Mon Sep 17 00:00:00 2001 From: Gianluca Borello Date: Sat, 3 Dec 2016 12:31:33 -0800 Subject: bpf: Preserve const register type on const OR alu ops Occasionally, clang (e.g. version 3.8.1) translates a sum between two constant operands using a BPF_OR instead of a BPF_ADD. The verifier is currently not handling this scenario, and the destination register type becomes UNKNOWN_VALUE even if it's still storing a constant. As a result, the destination register cannot be used as argument to a helper function expecting a ARG_CONST_STACK_*, limiting some use cases. Modify the verifier to handle this case, and add a few tests to make sure all combinations are supported, and stack boundaries are still verified even with BPF_OR. Signed-off-by: Gianluca Borello Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/verifier.c | 9 ++++- tools/testing/selftests/bpf/.gitignore | 1 + tools/testing/selftests/bpf/test_verifier.c | 60 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 0e74221..38d05da 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1481,14 +1481,19 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg = ®s[insn->src_reg]; u8 opcode = BPF_OP(insn->code); - /* dst_reg->type == CONST_IMM here, simulate execution of 'add' insn. - * Don't care about overflow or negative values, just add them + /* dst_reg->type == CONST_IMM here, simulate execution of 'add'/'or' + * insn. Don't care about overflow or negative values, just add them */ if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K) dst_reg->imm += insn->imm; else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X && src_reg->type == CONST_IMM) dst_reg->imm += src_reg->imm; + else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_K) + dst_reg->imm |= insn->imm; + else if (opcode == BPF_OR && BPF_SRC(insn->code) == BPF_X && + src_reg->type == CONST_IMM) + dst_reg->imm |= src_reg->imm; else mark_reg_unknown_value(regs, insn->dst_reg); return 0; diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 3c59f96e..071431b 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -1,2 +1,3 @@ test_verifier test_maps +test_lru_map diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 5da2e9d..8d71e44 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -2683,6 +2683,66 @@ static struct bpf_test tests[] = { .errstr_unpriv = "R0 pointer arithmetic prohibited", .result_unpriv = REJECT, }, + { + "constant register |= constant should keep constant type", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48), + BPF_MOV64_IMM(BPF_REG_2, 34), + BPF_ALU64_IMM(BPF_OR, BPF_REG_2, 13), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "constant register |= constant should not bypass stack boundary checks", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48), + BPF_MOV64_IMM(BPF_REG_2, 34), + BPF_ALU64_IMM(BPF_OR, BPF_REG_2, 24), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-48 access_size=58", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "constant register |= constant register should keep constant type", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48), + BPF_MOV64_IMM(BPF_REG_2, 34), + BPF_MOV64_IMM(BPF_REG_4, 13), + BPF_ALU64_REG(BPF_OR, BPF_REG_2, BPF_REG_4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "constant register |= constant register should not bypass stack boundary checks", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -48), + BPF_MOV64_IMM(BPF_REG_2, 34), + BPF_MOV64_IMM(BPF_REG_4, 24), + BPF_ALU64_REG(BPF_OR, BPF_REG_2, BPF_REG_4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-48 access_size=58", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, }; static int probe_filter_length(const struct bpf_insn *fp) -- cgit v1.1 From 96d5822c1d812ed30da035795a4408f43fe533f3 Mon Sep 17 00:00:00 2001 From: Erik Nordmark Date: Sat, 3 Dec 2016 20:57:09 -0800 Subject: ipv6: Allow IPv4-mapped address as next-hop Made kernel accept IPv6 routes with IPv4-mapped address as next-hop. It is possible to configure IP interfaces with IPv4-mapped addresses, and one can add IPv6 routes for IPv4-mapped destinations/prefixes, yet prior to this fix the kernel returned an EINVAL when attempting to add an IPv6 route with an IPv4-mapped address as a nexthop/gateway. RFC 4798 (a proposed standard RFC) uses IPv4-mapped addresses as nexthops, thus in order to support that type of address configuration the kernel needs to allow IPv4-mapped addresses as nexthops. Signed-off-by: Erik Nordmark Signed-off-by: Bob Gilligan Signed-off-by: David S. Miller --- net/ipv6/route.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index aac7818..2413a06 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2000,8 +2000,11 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg) It is very good, but in some (rare!) circumstances (SIT, PtP, NBMA NOARP links) it is handy to allow some exceptions. --ANK + We allow IPv4-mapped nexthops to support RFC4798-type + addressing */ - if (!(gwa_type & IPV6_ADDR_UNICAST)) + if (!(gwa_type & (IPV6_ADDR_UNICAST | + IPV6_ADDR_MAPPED))) goto out; if (cfg->fc_table) { -- cgit v1.1 From a6e169312971219a34927e8fdece60046fafb8ba Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Sun, 4 Dec 2016 15:25:19 +0200 Subject: net/sched: cls_flower: Set the filter Hardware device for all use-cases Check if the returned device from tcf_exts_get_dev function supports tc offload and in case the rule can't be offloaded, set the filter hw_dev parameter to the original device given by the user. The filter hw_device parameter should always be set by fl_hw_replace_filter function, since this pointer is used by dump stats and destroy filter for each flower rule (offloaded or not). Fixes: 7091d8c7055d ('net/sched: cls_flower: Add offload support using egress Hardware device') Signed-off-by: Hadar Hen Zion Reported-by: Simon Horman Tested-by: Simon Horman Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index c5cea78..29a9e6d 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -236,8 +236,11 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, int err; if (!tc_can_offload(dev, tp)) { - if (tcf_exts_get_dev(dev, &f->exts, &f->hw_dev)) + if (tcf_exts_get_dev(dev, &f->exts, &f->hw_dev) || + (f->hw_dev && !tc_can_offload(f->hw_dev, tp))) { + f->hw_dev = dev; return tc_skip_sw(f->flags) ? -EINVAL : 0; + } dev = f->hw_dev; tc->egress_dev = true; } else { -- cgit v1.1 From 1c0d32fde5bdf1184bc274f864c09799278a1114 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 4 Dec 2016 09:48:16 -0800 Subject: net_sched: gen_estimator: complete rewrite of rate estimators 1) Old code was hard to maintain, due to complex lock chains. (We probably will be able to remove some kfree_rcu() in callers) 2) Using a single timer to update all estimators does not scale. 3) Code was buggy on 32bit kernel (WRITE_ONCE() on 64bit quantity is not supposed to work well) In this rewrite : - I removed the RB tree that had to be scanned in gen_estimator_active(). qdisc dumps should be much faster. - Each estimator has its own timer. - Estimations are maintained in net_rate_estimator structure, instead of dirtying the qdisc. Minor, but part of the simplification. - Reading the estimator uses RCU and a seqcount to provide proper support for 32bit kernels. - We reduce memory need when estimators are not used, since we store a pointer, instead of the bytes/packets counters. - xt_rateest_mt() no longer has to grab a spinlock. (In the future, xt_rateest_tg() could be switched to per cpu counters) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/act_api.h | 2 +- include/net/gen_stats.h | 17 ++- include/net/netfilter/xt_rateest.h | 10 +- include/net/sch_generic.h | 2 +- net/core/gen_estimator.c | 299 +++++++++++++------------------------ net/core/gen_stats.c | 20 ++- net/netfilter/xt_RATEEST.c | 4 +- net/netfilter/xt_rateest.c | 28 ++-- net/sched/act_api.c | 9 +- net/sched/act_police.c | 21 +-- net/sched/sch_api.c | 2 +- net/sched/sch_cbq.c | 6 +- net/sched/sch_drr.c | 6 +- net/sched/sch_generic.c | 2 +- net/sched/sch_hfsc.c | 6 +- net/sched/sch_htb.c | 6 +- net/sched/sch_qfq.c | 8 +- 17 files changed, 182 insertions(+), 266 deletions(-) diff --git a/include/net/act_api.h b/include/net/act_api.h index 9dddf77..1d71644 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -36,7 +36,7 @@ struct tc_action { struct tcf_t tcfa_tm; struct gnet_stats_basic_packed tcfa_bstats; struct gnet_stats_queue tcfa_qstats; - struct gnet_stats_rate_est64 tcfa_rate_est; + struct net_rate_estimator __rcu *tcfa_rate_est; spinlock_t tcfa_lock; struct rcu_head tcfa_rcu; struct gnet_stats_basic_cpu __percpu *cpu_bstats; diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h index 231e121..8b7aa37 100644 --- a/include/net/gen_stats.h +++ b/include/net/gen_stats.h @@ -11,6 +11,8 @@ struct gnet_stats_basic_cpu { struct u64_stats_sync syncp; }; +struct net_rate_estimator; + struct gnet_dump { spinlock_t * lock; struct sk_buff * skb; @@ -42,8 +44,7 @@ void __gnet_stats_copy_basic(const seqcount_t *running, struct gnet_stats_basic_cpu __percpu *cpu, struct gnet_stats_basic_packed *b); int gnet_stats_copy_rate_est(struct gnet_dump *d, - const struct gnet_stats_basic_packed *b, - struct gnet_stats_rate_est64 *r); + struct net_rate_estimator __rcu **ptr); int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue __percpu *cpu_q, struct gnet_stats_queue *q, __u32 qlen); @@ -53,16 +54,16 @@ int gnet_stats_finish_copy(struct gnet_dump *d); int gen_new_estimator(struct gnet_stats_basic_packed *bstats, struct gnet_stats_basic_cpu __percpu *cpu_bstats, - struct gnet_stats_rate_est64 *rate_est, + struct net_rate_estimator __rcu **rate_est, spinlock_t *stats_lock, seqcount_t *running, struct nlattr *opt); -void gen_kill_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est64 *rate_est); +void gen_kill_estimator(struct net_rate_estimator __rcu **ptr); int gen_replace_estimator(struct gnet_stats_basic_packed *bstats, struct gnet_stats_basic_cpu __percpu *cpu_bstats, - struct gnet_stats_rate_est64 *rate_est, + struct net_rate_estimator __rcu **ptr, spinlock_t *stats_lock, seqcount_t *running, struct nlattr *opt); -bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats, - const struct gnet_stats_rate_est64 *rate_est); +bool gen_estimator_active(struct net_rate_estimator __rcu **ptr); +bool gen_estimator_read(struct net_rate_estimator __rcu **ptr, + struct gnet_stats_rate_est64 *sample); #endif diff --git a/include/net/netfilter/xt_rateest.h b/include/net/netfilter/xt_rateest.h index 79f45e1..130e583 100644 --- a/include/net/netfilter/xt_rateest.h +++ b/include/net/netfilter/xt_rateest.h @@ -1,19 +1,23 @@ #ifndef _XT_RATEEST_H #define _XT_RATEEST_H +#include + struct xt_rateest { /* keep lock and bstats on same cache line to speedup xt_rateest_tg() */ struct gnet_stats_basic_packed bstats; spinlock_t lock; - /* keep rstats and lock on same cache line to speedup xt_rateest_mt() */ - struct gnet_stats_rate_est64 rstats; + /* following fields not accessed in hot path */ + unsigned int refcnt; struct hlist_node list; char name[IFNAMSIZ]; - unsigned int refcnt; struct gnet_estimator params; struct rcu_head rcu; + + /* keep this field far away to speedup xt_rateest_mt() */ + struct net_rate_estimator __rcu *rate_est; }; struct xt_rateest *xt_rateest_lookup(const char *name); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index e6aa0a2..498f81b 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -76,7 +76,7 @@ struct Qdisc { struct netdev_queue *dev_queue; - struct gnet_stats_rate_est64 rate_est; + struct net_rate_estimator __rcu *rate_est; struct gnet_stats_basic_cpu __percpu *cpu_bstats; struct gnet_stats_queue __percpu *cpu_qstats; diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index 0993844..101b5d0 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -7,6 +7,7 @@ * 2 of the License, or (at your option) any later version. * * Authors: Alexey Kuznetsov, + * Eric Dumazet * * Changes: * Jamal Hadi Salim - moved it to net/core and reshulfed @@ -30,171 +31,79 @@ #include #include #include -#include #include +#include #include #include -/* - This code is NOT intended to be used for statistics collection, - its purpose is to provide a base for statistical multiplexing - for controlled load service. - If you need only statistics, run a user level daemon which - periodically reads byte counters. - - Unfortunately, rate estimation is not a very easy task. - F.e. I did not find a simple way to estimate the current peak rate - and even failed to formulate the problem 8)8) - - So I preferred not to built an estimator into the scheduler, - but run this task separately. - Ideally, it should be kernel thread(s), but for now it runs - from timers, which puts apparent top bounds on the number of rated - flows, has minimal overhead on small, but is enough - to handle controlled load service, sets of aggregates. - - We measure rate over A=(1<stats_lock) + spin_lock(e->stats_lock); - rcu_read_lock(); - list_for_each_entry_rcu(e, &elist[idx].list, list) { - struct gnet_stats_basic_packed b = {0}; - unsigned long rate; - u64 brate; - - if (e->stats_lock) - spin_lock(e->stats_lock); - read_lock(&est_lock); - if (e->bstats == NULL) - goto skip; - - __gnet_stats_copy_basic(e->running, &b, e->cpu_bstats, e->bstats); - - brate = (b.bytes - e->last_bytes)<<(7 - idx); - e->last_bytes = b.bytes; - e->avbps += (brate >> e->ewma_log) - (e->avbps >> e->ewma_log); - WRITE_ONCE(e->rate_est->bps, (e->avbps + 0xF) >> 5); - - rate = b.packets - e->last_packets; - rate <<= (7 - idx); - e->last_packets = b.packets; - e->avpps += (rate >> e->ewma_log) - (e->avpps >> e->ewma_log); - WRITE_ONCE(e->rate_est->pps, (e->avpps + 0xF) >> 5); -skip: - read_unlock(&est_lock); - if (e->stats_lock) - spin_unlock(e->stats_lock); - } + __gnet_stats_copy_basic(e->running, b, e->cpu_bstats, e->bstats); - if (!list_empty(&elist[idx].list)) { - elist[idx].next_jiffies += ((HZ/4) << idx); + if (e->stats_lock) + spin_unlock(e->stats_lock); - if (unlikely(time_after_eq(jiffies, elist[idx].next_jiffies))) { - /* Ouch... timer was delayed. */ - elist[idx].next_jiffies = jiffies + 1; - } - mod_timer(&elist[idx].timer, elist[idx].next_jiffies); - } - rcu_read_unlock(); } -static void gen_add_node(struct gen_estimator *est) +static void est_timer(unsigned long arg) { - struct rb_node **p = &est_root.rb_node, *parent = NULL; + struct net_rate_estimator *est = (struct net_rate_estimator *)arg; + struct gnet_stats_basic_packed b; + u64 rate, brate; - while (*p) { - struct gen_estimator *e; + est_fetch_counters(est, &b); + brate = (b.bytes - est->last_bytes) << (8 - est->ewma_log); + brate -= (est->avbps >> est->ewma_log); - parent = *p; - e = rb_entry(parent, struct gen_estimator, node); + rate = (u64)(b.packets - est->last_packets) << (8 - est->ewma_log); + rate -= (est->avpps >> est->ewma_log); - if (est->bstats > e->bstats) - p = &parent->rb_right; - else - p = &parent->rb_left; - } - rb_link_node(&est->node, parent, p); - rb_insert_color(&est->node, &est_root); -} - -static -struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats, - const struct gnet_stats_rate_est64 *rate_est) -{ - struct rb_node *p = est_root.rb_node; + write_seqcount_begin(&est->seq); + est->avbps += brate; + est->avpps += rate; + write_seqcount_end(&est->seq); - while (p) { - struct gen_estimator *e; + est->last_bytes = b.bytes; + est->last_packets = b.packets; - e = rb_entry(p, struct gen_estimator, node); + est->next_jiffies += ((HZ/4) << est->intvl_log); - if (bstats > e->bstats) - p = p->rb_right; - else if (bstats < e->bstats || rate_est != e->rate_est) - p = p->rb_left; - else - return e; + if (unlikely(time_after_eq(jiffies, est->next_jiffies))) { + /* Ouch... timer was delayed. */ + est->next_jiffies = jiffies + 1; } - return NULL; + mod_timer(&est->timer, est->next_jiffies); } /** @@ -217,84 +126,76 @@ struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats */ int gen_new_estimator(struct gnet_stats_basic_packed *bstats, struct gnet_stats_basic_cpu __percpu *cpu_bstats, - struct gnet_stats_rate_est64 *rate_est, + struct net_rate_estimator __rcu **rate_est, spinlock_t *stats_lock, seqcount_t *running, struct nlattr *opt) { - struct gen_estimator *est; struct gnet_estimator *parm = nla_data(opt); - struct gnet_stats_basic_packed b = {0}; - int idx; + struct net_rate_estimator *old, *est; + struct gnet_stats_basic_packed b; + int intvl_log; if (nla_len(opt) < sizeof(*parm)) return -EINVAL; + /* allowed timer periods are : + * -2 : 250ms, -1 : 500ms, 0 : 1 sec + * 1 : 2 sec, 2 : 4 sec, 3 : 8 sec + */ if (parm->interval < -2 || parm->interval > 3) return -EINVAL; est = kzalloc(sizeof(*est), GFP_KERNEL); - if (est == NULL) + if (!est) return -ENOBUFS; - __gnet_stats_copy_basic(running, &b, cpu_bstats, bstats); - - idx = parm->interval + 2; + seqcount_init(&est->seq); + intvl_log = parm->interval + 2; est->bstats = bstats; - est->rate_est = rate_est; est->stats_lock = stats_lock; est->running = running; est->ewma_log = parm->ewma_log; - est->last_bytes = b.bytes; - est->avbps = rate_est->bps<<5; - est->last_packets = b.packets; - est->avpps = rate_est->pps<<10; + est->intvl_log = intvl_log; est->cpu_bstats = cpu_bstats; - spin_lock_bh(&est_tree_lock); - if (!elist[idx].timer.function) { - INIT_LIST_HEAD(&elist[idx].list); - setup_timer(&elist[idx].timer, est_timer, idx); + est_fetch_counters(est, &b); + est->last_bytes = b.bytes; + est->last_packets = b.packets; + old = rcu_dereference_protected(*rate_est, 1); + if (old) { + del_timer_sync(&old->timer); + est->avbps = old->avbps; + est->avpps = old->avpps; } - if (list_empty(&elist[idx].list)) { - elist[idx].next_jiffies = jiffies + ((HZ/4) << idx); - mod_timer(&elist[idx].timer, elist[idx].next_jiffies); - } - list_add_rcu(&est->list, &elist[idx].list); - gen_add_node(est); - spin_unlock_bh(&est_tree_lock); + est->next_jiffies = jiffies + ((HZ/4) << intvl_log); + setup_timer(&est->timer, est_timer, (unsigned long)est); + mod_timer(&est->timer, est->next_jiffies); + rcu_assign_pointer(*rate_est, est); + if (old) + kfree_rcu(old, rcu); return 0; } EXPORT_SYMBOL(gen_new_estimator); /** * gen_kill_estimator - remove a rate estimator - * @bstats: basic statistics - * @rate_est: rate estimator statistics + * @rate_est: rate estimator * - * Removes the rate estimator specified by &bstats and &rate_est. + * Removes the rate estimator. * - * Note : Caller should respect an RCU grace period before freeing stats_lock */ -void gen_kill_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est64 *rate_est) +void gen_kill_estimator(struct net_rate_estimator __rcu **rate_est) { - struct gen_estimator *e; - - spin_lock_bh(&est_tree_lock); - while ((e = gen_find_node(bstats, rate_est))) { - rb_erase(&e->node, &est_root); + struct net_rate_estimator *est; - write_lock(&est_lock); - e->bstats = NULL; - write_unlock(&est_lock); - - list_del_rcu(&e->list); - kfree_rcu(e, e_rcu); + est = xchg((__force struct net_rate_estimator **)rate_est, NULL); + if (est) { + del_timer_sync(&est->timer); + kfree_rcu(est, rcu); } - spin_unlock_bh(&est_tree_lock); } EXPORT_SYMBOL(gen_kill_estimator); @@ -314,33 +215,47 @@ EXPORT_SYMBOL(gen_kill_estimator); */ int gen_replace_estimator(struct gnet_stats_basic_packed *bstats, struct gnet_stats_basic_cpu __percpu *cpu_bstats, - struct gnet_stats_rate_est64 *rate_est, + struct net_rate_estimator __rcu **rate_est, spinlock_t *stats_lock, seqcount_t *running, struct nlattr *opt) { - gen_kill_estimator(bstats, rate_est); - return gen_new_estimator(bstats, cpu_bstats, rate_est, stats_lock, running, opt); + return gen_new_estimator(bstats, cpu_bstats, rate_est, + stats_lock, running, opt); } EXPORT_SYMBOL(gen_replace_estimator); /** * gen_estimator_active - test if estimator is currently in use - * @bstats: basic statistics - * @rate_est: rate estimator statistics + * @rate_est: rate estimator * * Returns true if estimator is active, and false if not. */ -bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats, - const struct gnet_stats_rate_est64 *rate_est) +bool gen_estimator_active(struct net_rate_estimator __rcu **rate_est) { - bool res; + return !!rcu_access_pointer(*rate_est); +} +EXPORT_SYMBOL(gen_estimator_active); - ASSERT_RTNL(); +bool gen_estimator_read(struct net_rate_estimator __rcu **rate_est, + struct gnet_stats_rate_est64 *sample) +{ + struct net_rate_estimator *est; + unsigned seq; + + rcu_read_lock(); + est = rcu_dereference(*rate_est); + if (!est) { + rcu_read_unlock(); + return false; + } - spin_lock_bh(&est_tree_lock); - res = gen_find_node(bstats, rate_est) != NULL; - spin_unlock_bh(&est_tree_lock); + do { + seq = read_seqcount_begin(&est->seq); + sample->bps = est->avbps >> 8; + sample->pps = est->avpps >> 8; + } while (read_seqcount_retry(&est->seq, seq)); - return res; + rcu_read_unlock(); + return true; } -EXPORT_SYMBOL(gen_estimator_active); +EXPORT_SYMBOL(gen_estimator_read); diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index 508e051..87f2855 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -194,8 +194,7 @@ EXPORT_SYMBOL(gnet_stats_copy_basic); /** * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV * @d: dumping handle - * @b: basic statistics - * @r: rate estimator statistics + * @rate_est: rate estimator * * Appends the rate estimator statistics to the top level TLV created by * gnet_stats_start_copy(). @@ -205,18 +204,17 @@ EXPORT_SYMBOL(gnet_stats_copy_basic); */ int gnet_stats_copy_rate_est(struct gnet_dump *d, - const struct gnet_stats_basic_packed *b, - struct gnet_stats_rate_est64 *r) + struct net_rate_estimator __rcu **rate_est) { + struct gnet_stats_rate_est64 sample; struct gnet_stats_rate_est est; int res; - if (b && !gen_estimator_active(b, r)) + if (!gen_estimator_read(rate_est, &sample)) return 0; - - est.bps = min_t(u64, UINT_MAX, r->bps); + est.bps = min_t(u64, UINT_MAX, sample.bps); /* we have some time before reaching 2^32 packets per second */ - est.pps = r->pps; + est.pps = sample.pps; if (d->compat_tc_stats) { d->tc_stats.bps = est.bps; @@ -226,11 +224,11 @@ gnet_stats_copy_rate_est(struct gnet_dump *d, if (d->tail) { res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est), TCA_STATS_PAD); - if (res < 0 || est.bps == r->bps) + if (res < 0 || est.bps == sample.bps) return res; /* emit 64bit stats only if needed */ - return gnet_stats_copy(d, TCA_STATS_RATE_EST64, r, sizeof(*r), - TCA_STATS_PAD); + return gnet_stats_copy(d, TCA_STATS_RATE_EST64, &sample, + sizeof(sample), TCA_STATS_PAD); } return 0; diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index dbd6c4a..91a373a 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -63,7 +63,7 @@ void xt_rateest_put(struct xt_rateest *est) mutex_lock(&xt_rateest_mutex); if (--est->refcnt == 0) { hlist_del(&est->list); - gen_kill_estimator(&est->bstats, &est->rstats); + gen_kill_estimator(&est->rate_est); /* * gen_estimator est_timer() might access est->lock or bstats, * wait a RCU grace period before freeing 'est' @@ -132,7 +132,7 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) cfg.est.interval = info->interval; cfg.est.ewma_log = info->ewma_log; - ret = gen_new_estimator(&est->bstats, NULL, &est->rstats, + ret = gen_new_estimator(&est->bstats, NULL, &est->rate_est, &est->lock, NULL, &cfg.opt); if (ret < 0) goto err2; diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index 7720b03..1db02f6 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -18,35 +18,33 @@ static bool xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; - struct gnet_stats_rate_est64 *r; + struct gnet_stats_rate_est64 sample = {0}; u_int32_t bps1, bps2, pps1, pps2; bool ret = true; - spin_lock_bh(&info->est1->lock); - r = &info->est1->rstats; + gen_estimator_read(&info->est1->rate_est, &sample); + if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps1 = info->bps1 >= r->bps ? info->bps1 - r->bps : 0; - pps1 = info->pps1 >= r->pps ? info->pps1 - r->pps : 0; + bps1 = info->bps1 >= sample.bps ? info->bps1 - sample.bps : 0; + pps1 = info->pps1 >= sample.pps ? info->pps1 - sample.pps : 0; } else { - bps1 = r->bps; - pps1 = r->pps; + bps1 = sample.bps; + pps1 = sample.pps; } - spin_unlock_bh(&info->est1->lock); if (info->flags & XT_RATEEST_MATCH_ABS) { bps2 = info->bps2; pps2 = info->pps2; } else { - spin_lock_bh(&info->est2->lock); - r = &info->est2->rstats; + gen_estimator_read(&info->est2->rate_est, &sample); + if (info->flags & XT_RATEEST_MATCH_DELTA) { - bps2 = info->bps2 >= r->bps ? info->bps2 - r->bps : 0; - pps2 = info->pps2 >= r->pps ? info->pps2 - r->pps : 0; + bps2 = info->bps2 >= sample.bps ? info->bps2 - sample.bps : 0; + pps2 = info->pps2 >= sample.pps ? info->pps2 - sample.pps : 0; } else { - bps2 = r->bps; - pps2 = r->pps; + bps2 = sample.bps; + pps2 = sample.pps; } - spin_unlock_bh(&info->est2->lock); } switch (info->mode) { diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f893d18..2095c83 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -41,8 +41,7 @@ static void tcf_hash_destroy(struct tcf_hashinfo *hinfo, struct tc_action *p) spin_lock_bh(&hinfo->lock); hlist_del(&p->tcfa_head); spin_unlock_bh(&hinfo->lock); - gen_kill_estimator(&p->tcfa_bstats, - &p->tcfa_rate_est); + gen_kill_estimator(&p->tcfa_rate_est); /* * gen_estimator est_timer() might access p->tcfa_lock * or bstats, wait a RCU grace period before freeing p @@ -237,8 +236,7 @@ EXPORT_SYMBOL(tcf_hash_check); void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est) { if (est) - gen_kill_estimator(&a->tcfa_bstats, - &a->tcfa_rate_est); + gen_kill_estimator(&a->tcfa_rate_est); call_rcu(&a->tcfa_rcu, free_tcf); } EXPORT_SYMBOL(tcf_hash_cleanup); @@ -670,8 +668,7 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *p, goto errout; if (gnet_stats_copy_basic(NULL, &d, p->cpu_bstats, &p->tcfa_bstats) < 0 || - gnet_stats_copy_rate_est(&d, &p->tcfa_bstats, - &p->tcfa_rate_est) < 0 || + gnet_stats_copy_rate_est(&d, &p->tcfa_rate_est) < 0 || gnet_stats_copy_queue(&d, p->cpu_qstats, &p->tcfa_qstats, p->tcfa_qstats.qlen) < 0) diff --git a/net/sched/act_police.c b/net/sched/act_police.c index c990b73..0ba91d1 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -142,8 +142,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, goto failure_unlock; } else if (tb[TCA_POLICE_AVRATE] && (ret == ACT_P_CREATED || - !gen_estimator_active(&police->tcf_bstats, - &police->tcf_rate_est))) { + !gen_estimator_active(&police->tcf_rate_est))) { err = -EINVAL; goto failure_unlock; } @@ -216,13 +215,17 @@ static int tcf_act_police(struct sk_buff *skb, const struct tc_action *a, bstats_update(&police->tcf_bstats, skb); tcf_lastuse_update(&police->tcf_tm); - 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; + if (police->tcfp_ewma_rate) { + struct gnet_stats_rate_est64 sample; + + if (!gen_estimator_read(&police->tcf_rate_est, &sample) || + sample.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; + } } if (qdisc_pkt_len(skb) <= police->tcfp_mtu) { diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index f337f1b..d7b9342 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1395,7 +1395,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, if (gnet_stats_copy_basic(qdisc_root_sleeping_running(q), &d, cpu_bstats, &q->bstats) < 0 || - gnet_stats_copy_rate_est(&d, &q->bstats, &q->rate_est) < 0 || + gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 || gnet_stats_copy_queue(&d, cpu_qstats, &q->qstats, qlen) < 0) goto nla_put_failure; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index beb554a..9ffe1c2 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -122,7 +122,7 @@ struct cbq_class { psched_time_t penalized; struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est64 rate_est; + struct net_rate_estimator __rcu *rate_est; struct tc_cbq_xstats xstats; struct tcf_proto __rcu *filter_list; @@ -1346,7 +1346,7 @@ cbq_dump_class_stats(struct Qdisc *sch, unsigned long arg, if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d, NULL, &cl->bstats) < 0 || - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 || + gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, NULL, &cl->qstats, cl->q->q.qlen) < 0) return -1; @@ -1405,7 +1405,7 @@ static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl) tcf_destroy_chain(&cl->filter_list); qdisc_destroy(cl->q); qdisc_put_rtab(cl->R_tab); - gen_kill_estimator(&cl->bstats, &cl->rate_est); + gen_kill_estimator(&cl->rate_est); if (cl != &q->link) kfree(cl); } diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 8af5c59..bb4cbdf 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -25,7 +25,7 @@ struct drr_class { struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est64 rate_est; + struct net_rate_estimator __rcu *rate_est; struct list_head alist; struct Qdisc *qdisc; @@ -142,7 +142,7 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl) { - gen_kill_estimator(&cl->bstats, &cl->rate_est); + gen_kill_estimator(&cl->rate_est); qdisc_destroy(cl->qdisc); kfree(cl); } @@ -283,7 +283,7 @@ static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d, NULL, &cl->bstats) < 0 || - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 || + gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, NULL, &cl->qdisc->qstats, qlen) < 0) return -1; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6cfb6e9..6eb9c8e 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -709,7 +709,7 @@ void qdisc_destroy(struct Qdisc *qdisc) qdisc_put_stab(rtnl_dereference(qdisc->stab)); #endif - gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est); + gen_kill_estimator(&qdisc->rate_est); if (ops->reset) ops->reset(qdisc); if (ops->destroy) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 000f1d3..3ffaa6f 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -114,7 +114,7 @@ struct hfsc_class { struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est64 rate_est; + struct net_rate_estimator __rcu *rate_est; struct tcf_proto __rcu *filter_list; /* filter list */ unsigned int filter_cnt; /* filter count */ unsigned int level; /* class level in hierarchy */ @@ -1091,7 +1091,7 @@ hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl) tcf_destroy_chain(&cl->filter_list); qdisc_destroy(cl->qdisc); - gen_kill_estimator(&cl->bstats, &cl->rate_est); + gen_kill_estimator(&cl->rate_est); if (cl != &q->root) kfree(cl); } @@ -1348,7 +1348,7 @@ hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg, xstats.rtwork = cl->cl_cumul; if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d, NULL, &cl->bstats) < 0 || - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 || + gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, NULL, &cl->qstats, cl->qdisc->q.qlen) < 0) return -1; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 9926fe4..760f39e 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -111,7 +111,7 @@ struct htb_class { unsigned int children; struct htb_class *parent; /* parent class */ - struct gnet_stats_rate_est64 rate_est; + struct net_rate_estimator __rcu *rate_est; /* * Written often fields @@ -1145,7 +1145,7 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d) if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d, NULL, &cl->bstats) < 0 || - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 || + gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, NULL, &qs, qlen) < 0) return -1; @@ -1228,7 +1228,7 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl) WARN_ON(!cl->un.leaf.q); qdisc_destroy(cl->un.leaf.q); } - gen_kill_estimator(&cl->bstats, &cl->rate_est); + gen_kill_estimator(&cl->rate_est); tcf_destroy_chain(&cl->filter_list); kfree(cl); } diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index ca0516e..f9e712c 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -137,7 +137,7 @@ struct qfq_class { struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est64 rate_est; + struct net_rate_estimator __rcu *rate_est; struct Qdisc *qdisc; struct list_head alist; /* Link for active-classes list. */ struct qfq_aggregate *agg; /* Parent aggregate. */ @@ -508,7 +508,7 @@ set_change_agg: new_agg = kzalloc(sizeof(*new_agg), GFP_KERNEL); if (new_agg == NULL) { err = -ENOBUFS; - gen_kill_estimator(&cl->bstats, &cl->rate_est); + gen_kill_estimator(&cl->rate_est); goto destroy_class; } sch_tree_lock(sch); @@ -533,7 +533,7 @@ static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl) struct qfq_sched *q = qdisc_priv(sch); qfq_rm_from_agg(q, cl); - gen_kill_estimator(&cl->bstats, &cl->rate_est); + gen_kill_estimator(&cl->rate_est); qdisc_destroy(cl->qdisc); kfree(cl); } @@ -667,7 +667,7 @@ static int qfq_dump_class_stats(struct Qdisc *sch, unsigned long arg, if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d, NULL, &cl->bstats) < 0 || - gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 || + gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, NULL, &cl->qdisc->qstats, cl->qdisc->q.qlen) < 0) return -1; -- cgit v1.1 From abf7e53e9e6bbd2aa23a6d46bd74cb5dc1b7f564 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 4 Dec 2016 12:40:28 -0800 Subject: net: ethoc: Account for duplex changes ethoc_mdio_poll() which is our PHYLIB adjust_link callback does nothing, we should at least react to duplex changes and change MODER accordingly. Speed changes is not a problem, since the OpenCores Ethernet core seems to be reacting okay without us telling it. Signed-off-by: Florian Fainelli Reviewed-by: Tobias Klauser Acked-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/ethoc.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 6456c18..877c02a 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -221,6 +221,9 @@ struct ethoc { struct mii_bus *mdio; struct clk *clk; s8 phy_id; + + int old_link; + int old_duplex; }; /** @@ -667,6 +670,32 @@ static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) static void ethoc_mdio_poll(struct net_device *dev) { + struct ethoc *priv = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; + bool changed = false; + u32 mode; + + if (priv->old_link != phydev->link) { + changed = true; + priv->old_link = phydev->link; + } + + if (priv->old_duplex != phydev->duplex) { + changed = true; + priv->old_duplex = phydev->duplex; + } + + if (!changed) + return; + + mode = ethoc_read(priv, MODER); + if (phydev->duplex == DUPLEX_FULL) + mode |= MODER_FULLD; + else + mode &= ~MODER_FULLD; + ethoc_write(priv, MODER, mode); + + phy_print_status(phydev); } static int ethoc_mdio_probe(struct net_device *dev) @@ -685,6 +714,9 @@ static int ethoc_mdio_probe(struct net_device *dev) return -ENXIO; } + priv->old_duplex = -1; + priv->old_link = -1; + err = phy_connect_direct(dev, phy, ethoc_mdio_poll, PHY_INTERFACE_MODE_GMII); if (err) { @@ -721,6 +753,9 @@ static int ethoc_open(struct net_device *dev) netif_start_queue(dev); } + priv->old_link = -1; + priv->old_duplex = -1; + phy_start(dev->phydev); napi_enable(&priv->napi); -- cgit v1.1 From b34296a9c047504e2020fb4ce2f66f57bd5454a8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 4 Dec 2016 12:40:29 -0800 Subject: net: ethoc: Utilize of_get_mac_address() Do not open code getting the MAC address exclusively from the "local-mac-address" property, but instead use of_get_mac_address() which looks up the MAC address using the 3 typical property names. Signed-off-by: Florian Fainelli Reviewed-by: Tobias Klauser Acked-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/ethoc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 877c02a..8d0cb5c 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -1158,11 +1159,9 @@ static int ethoc_probe(struct platform_device *pdev) memcpy(netdev->dev_addr, pdata->hwaddr, IFHWADDRLEN); priv->phy_id = pdata->phy_id; } else { - const uint8_t *mac; + const void *mac; - mac = of_get_property(pdev->dev.of_node, - "local-mac-address", - NULL); + mac = of_get_mac_address(pdev->dev.of_node); if (mac) memcpy(netdev->dev_addr, mac, IFHWADDRLEN); priv->phy_id = -1; -- cgit v1.1 From 38b4bc207795339084ab41a2d9fa1add5cfd97d0 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 4 Dec 2016 12:40:30 -0800 Subject: net: ethoc: Demote packet dropped error message to debug Spamming the console with: net eth1: packet dropped can happen fairly frequently if the adapter is busy transmitting, demote the message to a debug print. Signed-off-by: Florian Fainelli Reviewed-by: Tobias Klauser Acked-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/ethoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 8d0cb5c..45abc81 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -576,7 +576,7 @@ static irqreturn_t ethoc_interrupt(int irq, void *dev_id) /* We always handle the dropped packet interrupt */ if (pending & INT_MASK_BUSY) { - dev_err(&dev->dev, "packet dropped\n"); + dev_dbg(&dev->dev, "packet dropped\n"); dev->stats.rx_dropped++; } -- cgit v1.1 From 1afaf661b20a075a22eef3332db187001a334b81 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 4 Dec 2016 23:19:39 +0100 Subject: bpf: remove type arg from __is_valid_{,xdp_}access Commit d691f9e8d440 ("bpf: allow programs to write to certain skb fields") pushed access type check outside of __is_valid_access() to have different restrictions for socket filters and tc programs. type is thus not used anymore within __is_valid_access() and should be removed as a function argument. Same for __is_valid_xdp_access() introduced by 6a773a15a1e8 ("bpf: add XDP prog type for early driver filter"). Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/core/filter.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index 56b4358..b751202 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2748,7 +2748,7 @@ lwt_xmit_func_proto(enum bpf_func_id func_id) } } -static bool __is_valid_access(int off, int size, enum bpf_access_type type) +static bool __is_valid_access(int off, int size) { if (off < 0 || off >= sizeof(struct __sk_buff)) return false; @@ -2782,7 +2782,7 @@ static bool sk_filter_is_valid_access(int off, int size, } } - return __is_valid_access(off, size, type); + return __is_valid_access(off, size); } static bool lwt_is_valid_access(int off, int size, @@ -2815,7 +2815,7 @@ static bool lwt_is_valid_access(int off, int size, break; } - return __is_valid_access(off, size, type); + return __is_valid_access(off, size); } static bool sock_filter_is_valid_access(int off, int size, @@ -2833,11 +2833,9 @@ static bool sock_filter_is_valid_access(int off, int size, if (off < 0 || off + size > sizeof(struct bpf_sock)) return false; - /* The verifier guarantees that size > 0. */ if (off % size != 0) return false; - if (size != sizeof(__u32)) return false; @@ -2910,11 +2908,10 @@ static bool tc_cls_act_is_valid_access(int off, int size, break; } - return __is_valid_access(off, size, type); + return __is_valid_access(off, size); } -static bool __is_valid_xdp_access(int off, int size, - enum bpf_access_type type) +static bool __is_valid_xdp_access(int off, int size) { if (off < 0 || off >= sizeof(struct xdp_md)) return false; @@ -2942,7 +2939,7 @@ static bool xdp_is_valid_access(int off, int size, break; } - return __is_valid_xdp_access(off, size, type); + return __is_valid_xdp_access(off, size); } void bpf_warn_invalid_xdp_action(u32 act) -- cgit v1.1 From 8d829bdb97dc3a0c9c8090b9b168ca46ea99c8d8 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 4 Dec 2016 23:19:40 +0100 Subject: bpf, cls: consolidate prog deletion path Commit 18cdb37ebf4c ("net: sched: do not use tcf_proto 'tp' argument from call_rcu") removed the last usage of tp from cls_bpf_delete_prog(), so also remove it from the function as argument to not give a wrong impression. tp is illegal to access from this callback, since it could already have been freed. Refactor the deletion code a bit, so that cls_bpf_destroy() can call into the same code for prog deletion as cls_bpf_delete() op, instead of having it unnecessarily duplicated. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/sched/cls_bpf.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index c37aa8b..f70e03d 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -241,7 +241,7 @@ static int cls_bpf_init(struct tcf_proto *tp) return 0; } -static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog) +static void __cls_bpf_delete_prog(struct cls_bpf_prog *prog) { tcf_exts_destroy(&prog->exts); @@ -255,22 +255,22 @@ static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog) kfree(prog); } -static void __cls_bpf_delete_prog(struct rcu_head *rcu) +static void cls_bpf_delete_prog_rcu(struct rcu_head *rcu) { - struct cls_bpf_prog *prog = container_of(rcu, struct cls_bpf_prog, rcu); - - cls_bpf_delete_prog(prog->tp, prog); + __cls_bpf_delete_prog(container_of(rcu, struct cls_bpf_prog, rcu)); } -static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) +static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog) { - struct cls_bpf_prog *prog = (struct cls_bpf_prog *) arg; - cls_bpf_stop_offload(tp, prog); list_del_rcu(&prog->link); tcf_unbind_filter(tp, &prog->res); - call_rcu(&prog->rcu, __cls_bpf_delete_prog); + call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu); +} +static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) +{ + __cls_bpf_delete(tp, (struct cls_bpf_prog *) arg); return 0; } @@ -282,12 +282,8 @@ static bool cls_bpf_destroy(struct tcf_proto *tp, bool force) if (!force && !list_empty(&head->plist)) return false; - list_for_each_entry_safe(prog, tmp, &head->plist, link) { - cls_bpf_stop_offload(tp, prog); - list_del_rcu(&prog->link); - tcf_unbind_filter(tp, &prog->res); - call_rcu(&prog->rcu, __cls_bpf_delete_prog); - } + list_for_each_entry_safe(prog, tmp, &head->plist, link) + __cls_bpf_delete(tp, prog); kfree_rcu(head, rcu); return true; @@ -511,14 +507,14 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, ret = cls_bpf_offload(tp, prog, oldprog); if (ret) { - cls_bpf_delete_prog(tp, prog); + __cls_bpf_delete_prog(prog); return ret; } if (oldprog) { list_replace_rcu(&oldprog->link, &prog->link); tcf_unbind_filter(tp, &oldprog->res); - call_rcu(&oldprog->rcu, __cls_bpf_delete_prog); + call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu); } else { list_add_rcu(&prog->link, &head->plist); } -- cgit v1.1 From 7bd509e311f408f7a5132fcdde2069af65fa05ae Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 4 Dec 2016 23:19:41 +0100 Subject: bpf: add prog_digest and expose it via fdinfo/netlink When loading a BPF program via bpf(2), calculate the digest over the program's instruction stream and store it in struct bpf_prog's digest member. This is done at a point in time before any instructions are rewritten by the verifier. Any unstable map file descriptor number part of the imm field will be zeroed for the hash. fdinfo example output for progs: # cat /proc/1590/fdinfo/5 pos: 0 flags: 02000002 mnt_id: 11 prog_type: 1 prog_jited: 1 prog_digest: b27e8b06da22707513aa97363dfb11c7c3675d28 memlock: 4096 When programs are pinned and retrieved by an ELF loader, the loader can check the program's digest through fdinfo and compare it against one that was generated over the ELF file's program section to see if the program needs to be reloaded. Furthermore, this can also be exposed through other means such as netlink in case of a tc cls/act dump (or xdp in future), but also through tracepoints or other facilities to identify the program. Other than that, the digest can also serve as a base name for the work in progress kallsyms support of programs. The digest doesn't depend/select the crypto layer, since we need to keep dependencies to a minimum. iproute2 will get support for this facility. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/linux/filter.h | 7 +++- include/uapi/linux/pkt_cls.h | 1 + include/uapi/linux/tc_act/tc_bpf.h | 1 + kernel/bpf/core.c | 65 ++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 24 +++++++++++++- kernel/bpf/verifier.c | 2 ++ net/sched/act_bpf.c | 9 ++++++ net/sched/cls_bpf.c | 8 +++++ 9 files changed, 116 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 69d0a7f..8796ff0 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -216,6 +216,7 @@ u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5); u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); bool bpf_prog_array_compatible(struct bpf_array *array, const struct bpf_prog *fp); +void bpf_prog_calc_digest(struct bpf_prog *fp); const struct bpf_func_proto *bpf_get_trace_printk_proto(void); diff --git a/include/linux/filter.h b/include/linux/filter.h index 9733813..f078d2b 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -56,6 +57,9 @@ struct bpf_prog_aux; /* BPF program can access up to 512 bytes of stack space. */ #define MAX_BPF_STACK 512 +/* Maximum BPF program size in bytes. */ +#define MAX_BPF_SIZE (BPF_MAXINSNS * sizeof(struct bpf_insn)) + /* Helper macros for filter block array initializers. */ /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ @@ -404,8 +408,9 @@ struct bpf_prog { cb_access:1, /* Is control block accessed? */ dst_needed:1; /* Do we need dst entry? */ kmemcheck_bitfield_end(meta); - u32 len; /* Number of filter blocks */ enum bpf_prog_type type; /* Type of BPF program */ + u32 len; /* Number of filter blocks */ + u32 digest[SHA_DIGEST_WORDS]; /* Program digest */ struct bpf_prog_aux *aux; /* Auxiliary fields */ struct sock_fprog_kern *orig_prog; /* Original BPF program */ unsigned int (*bpf_func)(const void *ctx, diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 86786d4..1adc0b6 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -397,6 +397,7 @@ enum { TCA_BPF_NAME, TCA_BPF_FLAGS, TCA_BPF_FLAGS_GEN, + TCA_BPF_DIGEST, __TCA_BPF_MAX, }; diff --git a/include/uapi/linux/tc_act/tc_bpf.h b/include/uapi/linux/tc_act/tc_bpf.h index 063d9d4..a6b88a6 100644 --- a/include/uapi/linux/tc_act/tc_bpf.h +++ b/include/uapi/linux/tc_act/tc_bpf.h @@ -27,6 +27,7 @@ enum { TCA_ACT_BPF_FD, TCA_ACT_BPF_NAME, TCA_ACT_BPF_PAD, + TCA_ACT_BPF_DIGEST, __TCA_ACT_BPF_MAX, }; #define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 82a0414..bdcc9f4 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -136,6 +136,71 @@ void __bpf_prog_free(struct bpf_prog *fp) vfree(fp); } +#define SHA_BPF_RAW_SIZE \ + round_up(MAX_BPF_SIZE + sizeof(__be64) + 1, SHA_MESSAGE_BYTES) + +/* Called under verifier mutex. */ +void bpf_prog_calc_digest(struct bpf_prog *fp) +{ + const u32 bits_offset = SHA_MESSAGE_BYTES - sizeof(__be64); + static u32 ws[SHA_WORKSPACE_WORDS]; + static u8 raw[SHA_BPF_RAW_SIZE]; + struct bpf_insn *dst = (void *)raw; + u32 i, bsize, psize, blocks; + bool was_ld_map; + u8 *todo = raw; + __be32 *result; + __be64 *bits; + + sha_init(fp->digest); + memset(ws, 0, sizeof(ws)); + + /* We need to take out the map fd for the digest calculation + * since they are unstable from user space side. + */ + for (i = 0, was_ld_map = false; i < fp->len; i++) { + dst[i] = fp->insnsi[i]; + if (!was_ld_map && + dst[i].code == (BPF_LD | BPF_IMM | BPF_DW) && + dst[i].src_reg == BPF_PSEUDO_MAP_FD) { + was_ld_map = true; + dst[i].imm = 0; + } else if (was_ld_map && + dst[i].code == 0 && + dst[i].dst_reg == 0 && + dst[i].src_reg == 0 && + dst[i].off == 0) { + was_ld_map = false; + dst[i].imm = 0; + } else { + was_ld_map = false; + } + } + + psize = fp->len * sizeof(struct bpf_insn); + memset(&raw[psize], 0, sizeof(raw) - psize); + raw[psize++] = 0x80; + + bsize = round_up(psize, SHA_MESSAGE_BYTES); + blocks = bsize / SHA_MESSAGE_BYTES; + if (bsize - psize >= sizeof(__be64)) { + bits = (__be64 *)(todo + bsize - sizeof(__be64)); + } else { + bits = (__be64 *)(todo + bsize + bits_offset); + blocks++; + } + *bits = cpu_to_be64((psize - 1) << 3); + + while (blocks--) { + sha_transform(fp->digest, todo, ws); + todo += SHA_MESSAGE_BYTES; + } + + result = (__force __be32 *)fp->digest; + for (i = 0; i < SHA_DIGEST_WORDS; i++) + result[i] = cpu_to_be32(fp->digest[i]); +} + static bool bpf_is_jmp_and_has_target(const struct bpf_insn *insn) { return BPF_CLASS(insn->code) == BPF_JMP && diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 85af86c..c0d2b42 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -662,8 +662,30 @@ static int bpf_prog_release(struct inode *inode, struct file *filp) return 0; } +#ifdef CONFIG_PROC_FS +static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp) +{ + const struct bpf_prog *prog = filp->private_data; + char prog_digest[sizeof(prog->digest) * 2 + 1] = { }; + + bin2hex(prog_digest, prog->digest, sizeof(prog->digest)); + seq_printf(m, + "prog_type:\t%u\n" + "prog_jited:\t%u\n" + "prog_digest:\t%s\n" + "memlock:\t%llu\n", + prog->type, + prog->jited, + prog_digest, + prog->pages * 1ULL << PAGE_SHIFT); +} +#endif + static const struct file_operations bpf_prog_fops = { - .release = bpf_prog_release, +#ifdef CONFIG_PROC_FS + .show_fdinfo = bpf_prog_show_fdinfo, +#endif + .release = bpf_prog_release, }; int bpf_prog_new_fd(struct bpf_prog *prog) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 38d05da..cb37339 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3176,6 +3176,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) log_level = 0; } + bpf_prog_calc_digest(env->prog); + ret = replace_map_fd_with_map_ptr(env); if (ret < 0) goto skip_full_check; diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 84c1d2d..1c60317 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -117,10 +117,19 @@ static int tcf_bpf_dump_bpf_info(const struct tcf_bpf *prog, static int tcf_bpf_dump_ebpf_info(const struct tcf_bpf *prog, struct sk_buff *skb) { + struct nlattr *nla; + if (prog->bpf_name && nla_put_string(skb, TCA_ACT_BPF_NAME, prog->bpf_name)) return -EMSGSIZE; + nla = nla_reserve(skb, TCA_ACT_BPF_DIGEST, + sizeof(prog->filter->digest)); + if (nla == NULL) + return -EMSGSIZE; + + memcpy(nla_data(nla), prog->filter->digest, nla_len(nla)); + return 0; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index f70e03d..adc7760 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -549,10 +549,18 @@ static int cls_bpf_dump_bpf_info(const struct cls_bpf_prog *prog, static int cls_bpf_dump_ebpf_info(const struct cls_bpf_prog *prog, struct sk_buff *skb) { + struct nlattr *nla; + if (prog->bpf_name && nla_put_string(skb, TCA_BPF_NAME, prog->bpf_name)) return -EMSGSIZE; + nla = nla_reserve(skb, TCA_BPF_DIGEST, sizeof(prog->filter->digest)); + if (nla == NULL) + return -EMSGSIZE; + + memcpy(nla_data(nla), prog->filter->digest, nla_len(nla)); + return 0; } -- cgit v1.1 From b0da4f743db522e006de5c8a1956b600872e6979 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 4 Dec 2016 23:37:53 +0100 Subject: net: calxeda: xgmac: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/calxeda/xgmac.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 6e72366..ce7de6f 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1530,15 +1530,14 @@ static const struct net_device_ops xgmac_netdev_ops = { .ndo_set_features = xgmac_set_features, }; -static int xgmac_ethtool_getsettings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int xgmac_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { - cmd->autoneg = 0; - cmd->duplex = DUPLEX_FULL; - ethtool_cmd_speed_set(cmd, 10000); - cmd->supported = 0; - cmd->advertising = 0; - cmd->transceiver = XCVR_INTERNAL; + cmd->base.autoneg = 0; + cmd->base.duplex = DUPLEX_FULL; + cmd->base.speed = 10000; + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 0); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 0); return 0; } @@ -1681,7 +1680,6 @@ static int xgmac_set_wol(struct net_device *dev, } static const struct ethtool_ops xgmac_ethtool_ops = { - .get_settings = xgmac_ethtool_getsettings, .get_link = ethtool_op_get_link, .get_pauseparam = xgmac_get_pauseparam, .set_pauseparam = xgmac_set_pauseparam, @@ -1690,6 +1688,7 @@ static const struct ethtool_ops xgmac_ethtool_ops = { .get_wol = xgmac_get_wol, .set_wol = xgmac_set_wol, .get_sset_count = xgmac_get_sset_count, + .get_link_ksettings = xgmac_ethtool_get_link_ksettings, }; /** -- cgit v1.1 From fd7aabb062fa1a8331a786d617744de220eaf002 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 2 Dec 2016 15:55:38 -0800 Subject: tools: hv: Enable network manager for bonding scripts on RHEL We found network manager is necessary on RHEL to make the synthetic NIC, VF NIC bonding operations handled automatically. So, enabling network manager here. Signed-off-by: Haiyang Zhang Reviewed-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- tools/hv/bondvf.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/hv/bondvf.sh b/tools/hv/bondvf.sh index 8e96023..4aa5369 100755 --- a/tools/hv/bondvf.sh +++ b/tools/hv/bondvf.sh @@ -74,8 +74,8 @@ function create_eth_cfg_redhat { echo DEVICE=$1 >>$fn echo TYPE=Ethernet >>$fn echo BOOTPROTO=none >>$fn + echo UUID=`uuidgen` >>$fn echo ONBOOT=yes >>$fn - echo NM_CONTROLLED=no >>$fn echo PEERDNS=yes >>$fn echo IPV6INIT=yes >>$fn echo MASTER=$2 >>$fn @@ -93,8 +93,8 @@ function create_bond_cfg_redhat { echo DEVICE=$1 >>$fn echo TYPE=Bond >>$fn echo BOOTPROTO=dhcp >>$fn + echo UUID=`uuidgen` >>$fn echo ONBOOT=yes >>$fn - echo NM_CONTROLLED=no >>$fn echo PEERDNS=yes >>$fn echo IPV6INIT=yes >>$fn echo BONDING_MASTER=yes >>$fn -- cgit v1.1 From 3f731d89e4071086b6d4764ef6ee80c1099c639d Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Mon, 5 Dec 2016 10:30:52 +0100 Subject: bpf: add additional verifier tests for BPF_PROG_TYPE_LWT_* - direct packet read is allowed for LWT_* - direct packet write for LWT_IN/LWT_OUT is prohibited - direct packet write for LWT_XMIT is allowed - access to skb->tc_classid is prohibited for LWT_* Signed-off-by: Thomas Graf Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_verifier.c | 134 ++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 8d71e44..0103bf2 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -2743,6 +2743,140 @@ static struct bpf_test tests[] = { .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, + { + "invalid direct packet write for LWT_IN", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "cannot write into packet", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_LWT_IN, + }, + { + "invalid direct packet write for LWT_OUT", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "cannot write into packet", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_LWT_OUT, + }, + { + "direct packet write for LWT_XMIT", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_LWT_XMIT, + }, + { + "direct packet read for LWT_IN", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_LWT_IN, + }, + { + "direct packet read for LWT_OUT", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_LWT_OUT, + }, + { + "direct packet read for LWT_XMIT", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_LWT_XMIT, + }, + { + "invalid access of tc_classid for LWT_IN", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_classid)), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid bpf_context access", + }, + { + "invalid access of tc_classid for LWT_OUT", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_classid)), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid bpf_context access", + }, + { + "invalid access of tc_classid for LWT_XMIT", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_classid)), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid bpf_context access", + }, }; static int probe_filter_length(const struct bpf_insn *fp) -- cgit v1.1 From 2bfcfcd3f3ea5d16d15e0382540683994a7a72a0 Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Mon, 5 Dec 2016 14:12:42 +0100 Subject: net: dsa: mv88e6xxx: Use EDSA on mv88e6097 Use DSA_TAG_PROTO_EDSA as tag_protocol for the mv88e6097. The initialisation was missing before. Fixes: a1f482aa8c33 ("net: dsa: mv88e6xxx: Move the tagging protocol into info") Signed-off-by: Stefan Eichenberger Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ca453f3..7a6c587 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3861,6 +3861,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6097_ops, }, -- cgit v1.1 From d0fb6ba75dc0dfa604d1579cb747eff2438248f8 Mon Sep 17 00:00:00 2001 From: Dongpo Li Date: Mon, 5 Dec 2016 21:27:58 +0800 Subject: net: hix5hd2_gmac: add generic compatible string The "hix5hd2" is SoC name, add the generic ethernet driver name. The "hisi-gemac-v1" is the basic version and "hisi-gemac-v2" adds the SG/TXCSUM/TSO/UFO features. Signed-off-by: Dongpo Li Signed-off-by: David S. Miller --- .../devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt | 9 +++++++-- drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 15 +++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt b/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt index 75d398b..75920f0 100644 --- a/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt +++ b/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt @@ -1,7 +1,12 @@ Hisilicon hix5hd2 gmac controller Required properties: -- compatible: should be "hisilicon,hix5hd2-gmac". +- compatible: should contain one of the following SoC strings: + * "hisilicon,hix5hd2-gemac" + * "hisilicon,hi3798cv200-gemac" + and one of the following version string: + * "hisilicon,hisi-gemac-v1" + * "hisilicon,hisi-gemac-v2" - reg: specifies base physical address(s) and size of the device registers. The first region is the MAC register base and size. The second region is external interface control register. @@ -20,7 +25,7 @@ Required properties: Example: gmac0: ethernet@f9840000 { - compatible = "hisilicon,hix5hd2-gmac"; + compatible = "hisilicon,hix5hd2-gemac", "hisilicon,hisi-gemac-v1"; reg = <0xf9840000 0x1000>,<0xf984300c 0x4>; interrupts = <0 71 4>; #address-cells = <1>; diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index e69a6be..27cb2e6 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -189,6 +189,10 @@ #define dma_cnt(n) ((n) >> 5) #define dma_byte(n) ((n) << 5) +#define HW_CAP_TSO BIT(0) +#define GEMAC_V1 0 +#define GEMAC_V2 (GEMAC_V1 | HW_CAP_TSO) + struct hix5hd2_desc { __le32 buff_addr; __le32 cmd; @@ -1021,7 +1025,10 @@ static int hix5hd2_dev_remove(struct platform_device *pdev) } static const struct of_device_id hix5hd2_of_match[] = { - {.compatible = "hisilicon,hix5hd2-gmac",}, + { .compatible = "hisilicon,hisi-gemac-v1", .data = (void *)GEMAC_V1 }, + { .compatible = "hisilicon,hisi-gemac-v2", .data = (void *)GEMAC_V2 }, + { .compatible = "hisilicon,hix5hd2-gemac", .data = (void *)GEMAC_V1 }, + { .compatible = "hisilicon,hi3798cv200-gemac", .data = (void *)GEMAC_V2 }, {}, }; @@ -1029,7 +1036,7 @@ MODULE_DEVICE_TABLE(of, hix5hd2_of_match); static struct platform_driver hix5hd2_dev_driver = { .driver = { - .name = "hix5hd2-gmac", + .name = "hisi-gemac", .of_match_table = hix5hd2_of_match, }, .probe = hix5hd2_dev_probe, @@ -1038,6 +1045,6 @@ static struct platform_driver hix5hd2_dev_driver = { module_platform_driver(hix5hd2_dev_driver); -MODULE_DESCRIPTION("HISILICON HIX5HD2 Ethernet driver"); +MODULE_DESCRIPTION("HISILICON Gigabit Ethernet MAC driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:hix5hd2-gmac"); +MODULE_ALIAS("platform:hisi-gemac"); -- cgit v1.1 From e5222b1c3f2143a88e660b4faec97d10520d92b3 Mon Sep 17 00:00:00 2001 From: Dongpo Li Date: Mon, 5 Dec 2016 21:27:59 +0800 Subject: net: hix5hd2_gmac: add tx scatter-gather feature "hisi-gemac-v2" adds the SG/TXCSUM/TSO/UFO features. This patch only adds the SG(scatter-gather) driver for transmitting, the drivers of other features will be submitted later. Signed-off-by: Dongpo Li Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 198 ++++++++++++++++++++++++-- 1 file changed, 187 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index 27cb2e6..18af55b 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,8 @@ #define DESC_DATA_LEN_OFF 16 #define DESC_BUFF_LEN_OFF 0 #define DESC_DATA_MASK 0x7ff +#define DESC_SG BIT(30) +#define DESC_FRAGS_NUM_OFF 11 /* DMA descriptor ring helpers */ #define dma_ring_incr(n, s) (((n) + 1) & ((s) - 1)) @@ -192,6 +195,7 @@ #define HW_CAP_TSO BIT(0) #define GEMAC_V1 0 #define GEMAC_V2 (GEMAC_V1 | HW_CAP_TSO) +#define HAS_CAP_TSO(hw_cap) ((hw_cap) & HW_CAP_TSO) struct hix5hd2_desc { __le32 buff_addr; @@ -205,6 +209,27 @@ struct hix5hd2_desc_sw { unsigned int size; }; +struct hix5hd2_sg_desc_ring { + struct sg_desc *desc; + dma_addr_t phys_addr; +}; + +struct frags_info { + __le32 addr; + __le32 size; +}; + +/* hardware supported max skb frags num */ +#define SG_MAX_SKB_FRAGS 17 +struct sg_desc { + __le32 total_len; + __le32 resvd0; + __le32 linear_addr; + __le32 linear_len; + /* reserve one more frags for memory alignment */ + struct frags_info frags[SG_MAX_SKB_FRAGS + 1]; +}; + #define QUEUE_NUMS 4 struct hix5hd2_priv { struct hix5hd2_desc_sw pool[QUEUE_NUMS]; @@ -212,6 +237,7 @@ struct hix5hd2_priv { #define rx_bq pool[1] #define tx_bq pool[2] #define tx_rq pool[3] + struct hix5hd2_sg_desc_ring tx_ring; void __iomem *base; void __iomem *ctrl_base; @@ -225,6 +251,7 @@ struct hix5hd2_priv { struct device_node *phy_node; phy_interface_t phy_mode; + unsigned long hw_cap; unsigned int speed; unsigned int duplex; @@ -515,6 +542,27 @@ next: return num; } +static void hix5hd2_clean_sg_desc(struct hix5hd2_priv *priv, + struct sk_buff *skb, u32 pos) +{ + struct sg_desc *desc; + dma_addr_t addr; + u32 len; + int i; + + desc = priv->tx_ring.desc + pos; + + addr = le32_to_cpu(desc->linear_addr); + len = le32_to_cpu(desc->linear_len); + dma_unmap_single(priv->dev, addr, len, DMA_TO_DEVICE); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + addr = le32_to_cpu(desc->frags[i].addr); + len = le32_to_cpu(desc->frags[i].size); + dma_unmap_page(priv->dev, addr, len, DMA_TO_DEVICE); + } +} + static void hix5hd2_xmit_reclaim(struct net_device *dev) { struct sk_buff *skb; @@ -542,8 +590,15 @@ static void hix5hd2_xmit_reclaim(struct net_device *dev) pkts_compl++; bytes_compl += skb->len; desc = priv->tx_rq.desc + pos; - addr = le32_to_cpu(desc->buff_addr); - dma_unmap_single(priv->dev, addr, skb->len, DMA_TO_DEVICE); + + if (skb_shinfo(skb)->nr_frags) { + hix5hd2_clean_sg_desc(priv, skb, pos); + } else { + addr = le32_to_cpu(desc->buff_addr); + dma_unmap_single(priv->dev, addr, skb->len, + DMA_TO_DEVICE); + } + priv->tx_skb[pos] = NULL; dev_consume_skb_any(skb); pos = dma_ring_incr(pos, TX_DESC_NUM); @@ -604,12 +659,66 @@ static irqreturn_t hix5hd2_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static u32 hix5hd2_get_desc_cmd(struct sk_buff *skb, unsigned long hw_cap) +{ + u32 cmd = 0; + + if (HAS_CAP_TSO(hw_cap)) { + if (skb_shinfo(skb)->nr_frags) + cmd |= DESC_SG; + cmd |= skb_shinfo(skb)->nr_frags << DESC_FRAGS_NUM_OFF; + } else { + cmd |= DESC_FL_FULL | + ((skb->len & DESC_DATA_MASK) << DESC_BUFF_LEN_OFF); + } + + cmd |= (skb->len & DESC_DATA_MASK) << DESC_DATA_LEN_OFF; + cmd |= DESC_VLD_BUSY; + + return cmd; +} + +static int hix5hd2_fill_sg_desc(struct hix5hd2_priv *priv, + struct sk_buff *skb, u32 pos) +{ + struct sg_desc *desc; + dma_addr_t addr; + int ret; + int i; + + desc = priv->tx_ring.desc + pos; + + desc->total_len = cpu_to_le32(skb->len); + addr = dma_map_single(priv->dev, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, addr))) + return -EINVAL; + desc->linear_addr = cpu_to_le32(addr); + desc->linear_len = cpu_to_le32(skb_headlen(skb)); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + int len = frag->size; + + addr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE); + ret = dma_mapping_error(priv->dev, addr); + if (unlikely(ret)) + return -EINVAL; + desc->frags[i].addr = cpu_to_le32(addr); + desc->frags[i].size = cpu_to_le32(len); + } + + return 0; +} + static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev) { struct hix5hd2_priv *priv = netdev_priv(dev); struct hix5hd2_desc *desc; dma_addr_t addr; u32 pos; + u32 cmd; + int ret; /* software write pointer */ pos = dma_cnt(readl_relaxed(priv->base + TX_BQ_WR_ADDR)); @@ -620,18 +729,31 @@ static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - addr = dma_map_single(priv->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (dma_mapping_error(priv->dev, addr)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - desc = priv->tx_bq.desc + pos; + + cmd = hix5hd2_get_desc_cmd(skb, priv->hw_cap); + desc->cmd = cpu_to_le32(cmd); + + if (skb_shinfo(skb)->nr_frags) { + ret = hix5hd2_fill_sg_desc(priv, skb, pos); + if (unlikely(ret)) { + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + addr = priv->tx_ring.phys_addr + pos * sizeof(struct sg_desc); + } else { + addr = dma_map_single(priv->dev, skb->data, skb->len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, addr))) { + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + } desc->buff_addr = cpu_to_le32(addr); + priv->tx_skb[pos] = skb; - desc->cmd = cpu_to_le32(DESC_VLD_BUSY | DESC_FL_FULL | - (skb->len & DESC_DATA_MASK) << DESC_DATA_LEN_OFF | - (skb->len & DESC_DATA_MASK) << DESC_BUFF_LEN_OFF); /* ensure desc updated */ wmb(); @@ -866,10 +988,40 @@ error_free_pool: return -ENOMEM; } +static int hix5hd2_init_sg_desc_queue(struct hix5hd2_priv *priv) +{ + struct sg_desc *desc; + dma_addr_t phys_addr; + + desc = (struct sg_desc *)dma_alloc_coherent(priv->dev, + TX_DESC_NUM * sizeof(struct sg_desc), + &phys_addr, GFP_KERNEL); + if (!desc) + return -ENOMEM; + + priv->tx_ring.desc = desc; + priv->tx_ring.phys_addr = phys_addr; + + return 0; +} + +static void hix5hd2_destroy_sg_desc_queue(struct hix5hd2_priv *priv) +{ + if (priv->tx_ring.desc) { + dma_free_coherent(priv->dev, + TX_DESC_NUM * sizeof(struct sg_desc), + priv->tx_ring.desc, priv->tx_ring.phys_addr); + priv->tx_ring.desc = NULL; + } +} + +static const struct of_device_id hix5hd2_of_match[]; + static int hix5hd2_dev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; + const struct of_device_id *of_id = NULL; struct net_device *ndev; struct hix5hd2_priv *priv; struct resource *res; @@ -887,6 +1039,13 @@ static int hix5hd2_dev_probe(struct platform_device *pdev) priv->dev = dev; priv->netdev = ndev; + of_id = of_match_device(hix5hd2_of_match, dev); + if (!of_id) { + ret = -EINVAL; + goto out_free_netdev; + } + priv->hw_cap = (unsigned long)of_id->data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(dev, res); if (IS_ERR(priv->base)) { @@ -976,11 +1135,24 @@ static int hix5hd2_dev_probe(struct platform_device *pdev) ndev->ethtool_ops = &hix5hd2_ethtools_ops; SET_NETDEV_DEV(ndev, dev); + if (HAS_CAP_TSO(priv->hw_cap)) + ndev->hw_features |= NETIF_F_SG; + + ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA; + ndev->vlan_features |= ndev->features; + ret = hix5hd2_init_hw_desc_queue(priv); if (ret) goto out_phy_node; netif_napi_add(ndev, &priv->napi, hix5hd2_poll, NAPI_POLL_WEIGHT); + + if (HAS_CAP_TSO(priv->hw_cap)) { + ret = hix5hd2_init_sg_desc_queue(priv); + if (ret) + goto out_destroy_queue; + } + ret = register_netdev(priv->netdev); if (ret) { netdev_err(ndev, "register_netdev failed!"); @@ -992,6 +1164,8 @@ static int hix5hd2_dev_probe(struct platform_device *pdev) return ret; out_destroy_queue: + if (HAS_CAP_TSO(priv->hw_cap)) + hix5hd2_destroy_sg_desc_queue(priv); netif_napi_del(&priv->napi); hix5hd2_destroy_hw_desc_queue(priv); out_phy_node: @@ -1016,6 +1190,8 @@ static int hix5hd2_dev_remove(struct platform_device *pdev) mdiobus_unregister(priv->bus); mdiobus_free(priv->bus); + if (HAS_CAP_TSO(priv->hw_cap)) + hix5hd2_destroy_sg_desc_queue(priv); hix5hd2_destroy_hw_desc_queue(priv); of_node_put(priv->phy_node); cancel_work_sync(&priv->tx_timeout_task); -- cgit v1.1 From 7087140d50fb65b62cb7c691d959a99cb726d2fd Mon Sep 17 00:00:00 2001 From: Dongpo Li Date: Mon, 5 Dec 2016 21:28:00 +0800 Subject: net: hix5hd2_gmac: add reset control and clock signals Add three reset control signals, "mac_core_rst", "mac_ifc_rst" and "phy_rst". The following diagram explained how the reset signals work. SoC |----------------------------------------------------- | ------ | | | cpu | | | ------ | | | | | ------------ AMBA bus | | GMAC | | | ---------------------- | | ------------- mac_core_rst | -------------- | | | |clock and |-------------->| mac core | | | | |reset | | -------------- | | | |generator |---- | | | | | ------------- | | ---------------- | | | | ---------->| mac interface | | | | | mac_ifc_rst | ---------------- | | | | | | | | | | | ------------------ | | | |phy_rst | | RGMII interface | | | | | | ------------------ | | | | ---------------------- | |----------|------------------------------------------| | | | ---------- |--------------------- |PHY chip | ---------- The "mac_core_rst" represents "mac core reset signal", it resets the mac core including packet processing unit, descriptor processing unit, tx engine, rx engine, control unit. The "mac_ifc_rst" represents "mac interface reset signal", it resets the mac interface. The mac interface unit connects mac core and data interface like MII/RMII/RGMII. After we set a new value of interface mode, we must reset mac interface to reload the new mode value. The "mac_core_rst" and "mac_ifc_rst" are both optional to be backward compatible with the hix5hd2 SoC. The "phy_rst" represents "phy reset signal", it does a hardware reset on the PHY chip. This reset signal is optional if the PHY can work well without the hardware reset. Add one more clock signal, the existing is MAC core clock, and the new one is MAC interface clock. The MAC interface clock is optional to be backward compatible with the hix5hd2 SoC. Signed-off-by: Dongpo Li Signed-off-by: David S. Miller --- .../bindings/net/hisilicon-hix5hd2-gmac.txt | 20 ++- drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 139 +++++++++++++++++++-- 2 files changed, 144 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt b/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt index 75920f0..063c02d 100644 --- a/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt +++ b/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt @@ -17,6 +17,16 @@ Required properties: - phy-handle: see ethernet.txt [1]. - mac-address: see ethernet.txt [1]. - clocks: clock phandle and specifier pair. +- clock-names: contain the clock name "mac_core"(required) and "mac_ifc"(optional). +- resets: should contain the phandle to the MAC core reset signal(optional), + the MAC interface reset signal(optional) + and the PHY reset signal(optional). +- reset-names: contain the reset signal name "mac_core"(optional), + "mac_ifc"(optional) and "phy"(optional). +- hisilicon,phy-reset-delays-us: triplet of delays if PHY reset signal given. + The 1st cell is reset pre-delay in micro seconds. + The 2nd cell is reset pulse in micro seconds. + The 3rd cell is reset post-delay in micro seconds. - PHY subnode: inherits from phy binding [2] @@ -25,15 +35,19 @@ Required properties: Example: gmac0: ethernet@f9840000 { - compatible = "hisilicon,hix5hd2-gemac", "hisilicon,hisi-gemac-v1"; + compatible = "hisilicon,hi3798cv200-gemac", "hisilicon,hisi-gemac-v2"; reg = <0xf9840000 0x1000>,<0xf984300c 0x4>; interrupts = <0 71 4>; #address-cells = <1>; #size-cells = <0>; - phy-mode = "mii"; + phy-mode = "rgmii"; phy-handle = <&phy2>; mac-address = [00 00 00 00 00 00]; - clocks = <&clock HIX5HD2_MAC0_CLK>; + clocks = <&crg HISTB_ETH0_MAC_CLK>, <&crg HISTB_ETH0_MACIF_CLK>; + clock-names = "mac_core", "mac_ifc"; + resets = <&crg 0xcc 8>, <&crg 0xcc 10>, <&crg 0xcc 12>; + reset-names = "mac_core", "mac_ifc", "phy"; + hisilicon,phy-reset-delays-us = <10000 10000 30000>; phy2: ethernet-phy@2 { reg = <2>; diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index 18af55b..ee7e9ce 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -197,6 +198,15 @@ #define GEMAC_V2 (GEMAC_V1 | HW_CAP_TSO) #define HAS_CAP_TSO(hw_cap) ((hw_cap) & HW_CAP_TSO) +#define PHY_RESET_DELAYS_PROPERTY "hisilicon,phy-reset-delays-us" + +enum phy_reset_delays { + PRE_DELAY, + PULSE, + POST_DELAY, + DELAYS_NUM, +}; + struct hix5hd2_desc { __le32 buff_addr; __le32 cmd; @@ -255,12 +265,26 @@ struct hix5hd2_priv { unsigned int speed; unsigned int duplex; - struct clk *clk; + struct clk *mac_core_clk; + struct clk *mac_ifc_clk; + struct reset_control *mac_core_rst; + struct reset_control *mac_ifc_rst; + struct reset_control *phy_rst; + u32 phy_reset_delays[DELAYS_NUM]; struct mii_bus *bus; struct napi_struct napi; struct work_struct tx_timeout_task; }; +static inline void hix5hd2_mac_interface_reset(struct hix5hd2_priv *priv) +{ + if (!priv->mac_ifc_rst) + return; + + reset_control_assert(priv->mac_ifc_rst); + reset_control_deassert(priv->mac_ifc_rst); +} + static void hix5hd2_config_port(struct net_device *dev, u32 speed, u32 duplex) { struct hix5hd2_priv *priv = netdev_priv(dev); @@ -293,6 +317,7 @@ static void hix5hd2_config_port(struct net_device *dev, u32 speed, u32 duplex) if (duplex) val |= GMAC_FULL_DUPLEX; writel_relaxed(val, priv->ctrl_base); + hix5hd2_mac_interface_reset(priv); writel_relaxed(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN); if (speed == SPEED_1000) @@ -807,16 +832,26 @@ static int hix5hd2_net_open(struct net_device *dev) struct phy_device *phy; int ret; - ret = clk_prepare_enable(priv->clk); + ret = clk_prepare_enable(priv->mac_core_clk); + if (ret < 0) { + netdev_err(dev, "failed to enable mac core clk %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(priv->mac_ifc_clk); if (ret < 0) { - netdev_err(dev, "failed to enable clk %d\n", ret); + clk_disable_unprepare(priv->mac_core_clk); + netdev_err(dev, "failed to enable mac ifc clk %d\n", ret); return ret; } phy = of_phy_connect(dev, priv->phy_node, &hix5hd2_adjust_link, 0, priv->phy_mode); - if (!phy) + if (!phy) { + clk_disable_unprepare(priv->mac_ifc_clk); + clk_disable_unprepare(priv->mac_core_clk); return -ENODEV; + } phy_start(phy); hix5hd2_hw_init(priv); @@ -847,7 +882,8 @@ static int hix5hd2_net_close(struct net_device *dev) phy_disconnect(dev->phydev); } - clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->mac_ifc_clk); + clk_disable_unprepare(priv->mac_core_clk); return 0; } @@ -1015,6 +1051,48 @@ static void hix5hd2_destroy_sg_desc_queue(struct hix5hd2_priv *priv) } } +static inline void hix5hd2_mac_core_reset(struct hix5hd2_priv *priv) +{ + if (!priv->mac_core_rst) + return; + + reset_control_assert(priv->mac_core_rst); + reset_control_deassert(priv->mac_core_rst); +} + +static void hix5hd2_sleep_us(u32 time_us) +{ + u32 time_ms; + + if (!time_us) + return; + + time_ms = DIV_ROUND_UP(time_us, 1000); + if (time_ms < 20) + usleep_range(time_us, time_us + 500); + else + msleep(time_ms); +} + +static void hix5hd2_phy_reset(struct hix5hd2_priv *priv) +{ + /* To make sure PHY hardware reset success, + * we must keep PHY in deassert state first and + * then complete the hardware reset operation + */ + reset_control_deassert(priv->phy_rst); + hix5hd2_sleep_us(priv->phy_reset_delays[PRE_DELAY]); + + reset_control_assert(priv->phy_rst); + /* delay some time to ensure reset ok, + * this depends on PHY hardware feature + */ + hix5hd2_sleep_us(priv->phy_reset_delays[PULSE]); + reset_control_deassert(priv->phy_rst); + /* delay some time to ensure later MDIO access */ + hix5hd2_sleep_us(priv->phy_reset_delays[POST_DELAY]); +} + static const struct of_device_id hix5hd2_of_match[]; static int hix5hd2_dev_probe(struct platform_device *pdev) @@ -1060,23 +1138,55 @@ static int hix5hd2_dev_probe(struct platform_device *pdev) goto out_free_netdev; } - priv->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(priv->clk)) { - netdev_err(ndev, "failed to get clk\n"); + priv->mac_core_clk = devm_clk_get(&pdev->dev, "mac_core"); + if (IS_ERR(priv->mac_core_clk)) { + netdev_err(ndev, "failed to get mac core clk\n"); ret = -ENODEV; goto out_free_netdev; } - ret = clk_prepare_enable(priv->clk); + ret = clk_prepare_enable(priv->mac_core_clk); if (ret < 0) { - netdev_err(ndev, "failed to enable clk %d\n", ret); + netdev_err(ndev, "failed to enable mac core clk %d\n", ret); goto out_free_netdev; } + priv->mac_ifc_clk = devm_clk_get(&pdev->dev, "mac_ifc"); + if (IS_ERR(priv->mac_ifc_clk)) + priv->mac_ifc_clk = NULL; + + ret = clk_prepare_enable(priv->mac_ifc_clk); + if (ret < 0) { + netdev_err(ndev, "failed to enable mac ifc clk %d\n", ret); + goto out_disable_mac_core_clk; + } + + priv->mac_core_rst = devm_reset_control_get(dev, "mac_core"); + if (IS_ERR(priv->mac_core_rst)) + priv->mac_core_rst = NULL; + hix5hd2_mac_core_reset(priv); + + priv->mac_ifc_rst = devm_reset_control_get(dev, "mac_ifc"); + if (IS_ERR(priv->mac_ifc_rst)) + priv->mac_ifc_rst = NULL; + + priv->phy_rst = devm_reset_control_get(dev, "phy"); + if (IS_ERR(priv->phy_rst)) { + priv->phy_rst = NULL; + } else { + ret = of_property_read_u32_array(node, + PHY_RESET_DELAYS_PROPERTY, + priv->phy_reset_delays, + DELAYS_NUM); + if (ret) + goto out_disable_clk; + hix5hd2_phy_reset(priv); + } + bus = mdiobus_alloc(); if (bus == NULL) { ret = -ENOMEM; - goto out_free_netdev; + goto out_disable_clk; } bus->priv = priv; @@ -1159,7 +1269,8 @@ static int hix5hd2_dev_probe(struct platform_device *pdev) goto out_destroy_queue; } - clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->mac_ifc_clk); + clk_disable_unprepare(priv->mac_core_clk); return ret; @@ -1174,6 +1285,10 @@ err_mdiobus: mdiobus_unregister(bus); err_free_mdio: mdiobus_free(bus); +out_disable_clk: + clk_disable_unprepare(priv->mac_ifc_clk); +out_disable_mac_core_clk: + clk_disable_unprepare(priv->mac_core_clk); out_free_netdev: free_netdev(ndev); -- cgit v1.1 From 0855950ba580c89da91939bc5a6e0f8b950c9232 Mon Sep 17 00:00:00 2001 From: Dongpo Li Date: Mon, 5 Dec 2016 21:28:01 +0800 Subject: ARM: dts: hix5hd2: add gmac generic compatible and clock names Add gmac generic compatible and clock names. Signed-off-by: Dongpo Li Signed-off-by: David S. Miller --- arch/arm/boot/dts/hisi-x5hd2.dtsi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/hisi-x5hd2.dtsi b/arch/arm/boot/dts/hisi-x5hd2.dtsi index fdcc23d..0da76c5 100644 --- a/arch/arm/boot/dts/hisi-x5hd2.dtsi +++ b/arch/arm/boot/dts/hisi-x5hd2.dtsi @@ -436,18 +436,20 @@ }; gmac0: ethernet@1840000 { - compatible = "hisilicon,hix5hd2-gmac"; + compatible = "hisilicon,hix5hd2-gemac", "hisilicon,hisi-gemac-v1"; reg = <0x1840000 0x1000>,<0x184300c 0x4>; interrupts = <0 71 4>; clocks = <&clock HIX5HD2_MAC0_CLK>; + clock-names = "mac_core"; status = "disabled"; }; gmac1: ethernet@1841000 { - compatible = "hisilicon,hix5hd2-gmac"; + compatible = "hisilicon,hix5hd2-gemac", "hisilicon,hisi-gemac-v1"; reg = <0x1841000 0x1000>,<0x1843010 0x4>; interrupts = <0 72 4>; clocks = <&clock HIX5HD2_MAC1_CLK>; + clock-names = "mac_core"; status = "disabled"; }; -- cgit v1.1 From a297569fe00a8fae18547061d355c45ef191b483 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 5 Dec 2016 09:57:19 -0800 Subject: net/udp: do not touch skb->peeked unless really needed In UDP recvmsg() path we currently access 3 cache lines from an skb while holding receive queue lock, plus another one if packet is dequeued, since we need to change skb->next->prev 1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08) 2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e) 3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4) skb->peeked is only needed to make sure 0-length packets are properly handled while MSG_PEEK is operated. I had first the intent to remove skb->peeked but the "MSG_PEEK at non-zero offset" support added by Sam Kumar makes this not possible. This patch avoids one cache line miss during the locked section, when skb->len and skb->peeked do not have to be read. It also avoids the skb_set_peeked() cost for non empty UDP datagrams. Signed-off-by: Eric Dumazet Acked-by: Paolo Abeni Signed-off-by: David S. Miller --- net/core/datagram.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/net/core/datagram.c b/net/core/datagram.c index 49816af..9482037 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -214,6 +214,7 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags, if (error) goto no_packet; + *peeked = 0; do { /* Again only user level code calls this function, so nothing * interrupt level will suddenly eat the receive_queue. @@ -227,22 +228,22 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags, spin_lock_irqsave(&queue->lock, cpu_flags); skb_queue_walk(queue, skb) { *last = skb; - *peeked = skb->peeked; if (flags & MSG_PEEK) { if (_off >= skb->len && (skb->len || _off || skb->peeked)) { _off -= skb->len; continue; } - - skb = skb_set_peeked(skb); - error = PTR_ERR(skb); - if (IS_ERR(skb)) { - spin_unlock_irqrestore(&queue->lock, - cpu_flags); - goto no_packet; + if (!skb->len) { + skb = skb_set_peeked(skb); + if (IS_ERR(skb)) { + error = PTR_ERR(skb); + spin_unlock_irqrestore(&queue->lock, + cpu_flags); + goto no_packet; + } } - + *peeked = 1; atomic_inc(&skb->users); } else { __skb_unlink(skb, queue); -- cgit v1.1 From 60c140df15dbc34e2aabe210f82c37b978185b40 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Mon, 5 Dec 2016 23:07:16 +0100 Subject: net: ethernet: slicoss: add slicoss gigabit ethernet driver Add driver for Alacritech gigabit ethernet cards with SLIC (session-layer interface control) technology. The driver provides basic support without SLIC for the following devices: - Mojave cards (single port PCI Gigabit) both copper and fiber - Oasis cards (single and dual port PCI-x Gigabit) copper and fiber - Kalahari cards (dual and quad port PCI-e Gigabit) copper and fiber Signed-off-by: Lino Sanfilippo Signed-off-by: David S. Miller --- drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/alacritech/Kconfig | 28 + drivers/net/ethernet/alacritech/Makefile | 4 + drivers/net/ethernet/alacritech/slic.h | 575 +++++++++ drivers/net/ethernet/alacritech/slicoss.c | 1882 +++++++++++++++++++++++++++++ 6 files changed, 2491 insertions(+) create mode 100644 drivers/net/ethernet/alacritech/Kconfig create mode 100644 drivers/net/ethernet/alacritech/Makefile create mode 100644 drivers/net/ethernet/alacritech/slic.h create mode 100644 drivers/net/ethernet/alacritech/slicoss.c diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 6738fbb..6e16e44 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -21,6 +21,7 @@ source "drivers/net/ethernet/3com/Kconfig" source "drivers/net/ethernet/adaptec/Kconfig" source "drivers/net/ethernet/aeroflex/Kconfig" source "drivers/net/ethernet/agere/Kconfig" +source "drivers/net/ethernet/alacritech/Kconfig" source "drivers/net/ethernet/allwinner/Kconfig" source "drivers/net/ethernet/alteon/Kconfig" source "drivers/net/ethernet/altera/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index e762445..24330f4 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_NET_VENDOR_8390) += 8390/ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/ obj-$(CONFIG_GRETH) += aeroflex/ obj-$(CONFIG_NET_VENDOR_AGERE) += agere/ +obj-$(CONFIG_NET_VENDOR_ALACRITECH) += alacritech/ obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/ obj-$(CONFIG_ALTERA_TSE) += altera/ diff --git a/drivers/net/ethernet/alacritech/Kconfig b/drivers/net/ethernet/alacritech/Kconfig new file mode 100644 index 0000000..09496e1 --- /dev/null +++ b/drivers/net/ethernet/alacritech/Kconfig @@ -0,0 +1,28 @@ +config NET_VENDOR_ALACRITECH + bool "Alacritech devices" + default y + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + questions about Alacritech devices. If you say Y, you will be asked + for your specific device in the following questions. + +if NET_VENDOR_ALACRITECH + +config SLICOSS + tristate "Alacritech Slicoss support" + depends on PCI + select CRC32 + ---help--- + This driver supports Gigabit Ethernet adapters based on the + Session Layer Interface (SLIC) technology by Alacritech. + + Supported are Mojave (1 port) and Oasis (1, 2 and 4 port) cards, + both copper and fiber. + + To compile this driver as a module, choose M here: the module + will be called slicoss. This is recommended. + +endif # NET_VENDOR_ALACRITECH diff --git a/drivers/net/ethernet/alacritech/Makefile b/drivers/net/ethernet/alacritech/Makefile new file mode 100644 index 0000000..8790e9e --- /dev/null +++ b/drivers/net/ethernet/alacritech/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the Alacritech Slicoss driver +# +obj-$(CONFIG_SLICOSS) += slicoss.o diff --git a/drivers/net/ethernet/alacritech/slic.h b/drivers/net/ethernet/alacritech/slic.h new file mode 100644 index 0000000..08931b4 --- /dev/null +++ b/drivers/net/ethernet/alacritech/slic.h @@ -0,0 +1,575 @@ + +#ifndef _SLIC_H +#define _SLIC_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SLIC_VGBSTAT_XPERR 0x40000000 +#define SLIC_VGBSTAT_XERRSHFT 25 +#define SLIC_VGBSTAT_XCSERR 0x23 +#define SLIC_VGBSTAT_XUFLOW 0x22 +#define SLIC_VGBSTAT_XHLEN 0x20 +#define SLIC_VGBSTAT_NETERR 0x01000000 +#define SLIC_VGBSTAT_NERRSHFT 16 +#define SLIC_VGBSTAT_NERRMSK 0x1ff +#define SLIC_VGBSTAT_NCSERR 0x103 +#define SLIC_VGBSTAT_NUFLOW 0x102 +#define SLIC_VGBSTAT_NHLEN 0x100 +#define SLIC_VGBSTAT_LNKERR 0x00000080 +#define SLIC_VGBSTAT_LERRMSK 0xff +#define SLIC_VGBSTAT_LDEARLY 0x86 +#define SLIC_VGBSTAT_LBOFLO 0x85 +#define SLIC_VGBSTAT_LCODERR 0x84 +#define SLIC_VGBSTAT_LDBLNBL 0x83 +#define SLIC_VGBSTAT_LCRCERR 0x82 +#define SLIC_VGBSTAT_LOFLO 0x81 +#define SLIC_VGBSTAT_LUFLO 0x80 + +#define SLIC_IRHDDR_FLEN_MSK 0x0000ffff +#define SLIC_IRHDDR_SVALID 0x80000000 +#define SLIC_IRHDDR_ERR 0x10000000 + +#define SLIC_VRHSTAT_802OE 0x80000000 +#define SLIC_VRHSTAT_TPOFLO 0x10000000 +#define SLIC_VRHSTATB_802UE 0x80000000 +#define SLIC_VRHSTATB_RCVE 0x40000000 +#define SLIC_VRHSTATB_BUFF 0x20000000 +#define SLIC_VRHSTATB_CARRE 0x08000000 +#define SLIC_VRHSTATB_LONGE 0x02000000 +#define SLIC_VRHSTATB_PREA 0x01000000 +#define SLIC_VRHSTATB_CRC 0x00800000 +#define SLIC_VRHSTATB_DRBL 0x00400000 +#define SLIC_VRHSTATB_CODE 0x00200000 +#define SLIC_VRHSTATB_TPCSUM 0x00100000 +#define SLIC_VRHSTATB_TPHLEN 0x00080000 +#define SLIC_VRHSTATB_IPCSUM 0x00040000 +#define SLIC_VRHSTATB_IPLERR 0x00020000 +#define SLIC_VRHSTATB_IPHERR 0x00010000 + +#define SLIC_CMD_XMT_REQ 0x01 +#define SLIC_CMD_TYPE_DUMB 3 + +#define SLIC_RESET_MAGIC 0xDEAD +#define SLIC_ICR_INT_OFF 0 +#define SLIC_ICR_INT_ON 1 +#define SLIC_ICR_INT_MASK 2 + +#define SLIC_ISR_ERR 0x80000000 +#define SLIC_ISR_RCV 0x40000000 +#define SLIC_ISR_CMD 0x20000000 +#define SLIC_ISR_IO 0x60000000 +#define SLIC_ISR_UPC 0x10000000 +#define SLIC_ISR_LEVENT 0x08000000 +#define SLIC_ISR_RMISS 0x02000000 +#define SLIC_ISR_UPCERR 0x01000000 +#define SLIC_ISR_XDROP 0x00800000 +#define SLIC_ISR_UPCBSY 0x00020000 + +#define SLIC_ISR_PING_MASK 0x00700000 +#define SLIC_ISR_UPCERR_MASK (SLIC_ISR_UPCERR | SLIC_ISR_UPCBSY) +#define SLIC_ISR_UPC_MASK (SLIC_ISR_UPC | SLIC_ISR_UPCERR_MASK) +#define SLIC_WCS_START 0x80000000 +#define SLIC_WCS_COMPARE 0x40000000 +#define SLIC_RCVWCS_BEGIN 0x40000000 +#define SLIC_RCVWCS_FINISH 0x80000000 + +#define SLIC_MIICR_REG_16 0x00100000 +#define SLIC_MRV_REG16_XOVERON 0x0068 + +#define SLIC_GIG_LINKUP 0x0001 +#define SLIC_GIG_FULLDUPLEX 0x0002 +#define SLIC_GIG_SPEED_MASK 0x000C +#define SLIC_GIG_SPEED_1000 0x0008 +#define SLIC_GIG_SPEED_100 0x0004 +#define SLIC_GIG_SPEED_10 0x0000 + +#define SLIC_GMCR_RESET 0x80000000 +#define SLIC_GMCR_GBIT 0x20000000 +#define SLIC_GMCR_FULLD 0x10000000 +#define SLIC_GMCR_GAPBB_SHIFT 14 +#define SLIC_GMCR_GAPR1_SHIFT 7 +#define SLIC_GMCR_GAPR2_SHIFT 0 +#define SLIC_GMCR_GAPBB_1000 0x60 +#define SLIC_GMCR_GAPR1_1000 0x2C +#define SLIC_GMCR_GAPR2_1000 0x40 +#define SLIC_GMCR_GAPBB_100 0x70 +#define SLIC_GMCR_GAPR1_100 0x2C +#define SLIC_GMCR_GAPR2_100 0x40 + +#define SLIC_XCR_RESET 0x80000000 +#define SLIC_XCR_XMTEN 0x40000000 +#define SLIC_XCR_PAUSEEN 0x20000000 +#define SLIC_XCR_LOADRNG 0x10000000 + +#define SLIC_GXCR_RESET 0x80000000 +#define SLIC_GXCR_XMTEN 0x40000000 +#define SLIC_GXCR_PAUSEEN 0x20000000 + +#define SLIC_GRCR_RESET 0x80000000 +#define SLIC_GRCR_RCVEN 0x40000000 +#define SLIC_GRCR_RCVALL 0x20000000 +#define SLIC_GRCR_RCVBAD 0x10000000 +#define SLIC_GRCR_CTLEN 0x08000000 +#define SLIC_GRCR_ADDRAEN 0x02000000 +#define SLIC_GRCR_HASHSIZE_SHIFT 17 +#define SLIC_GRCR_HASHSIZE 14 + +/* Reset Register */ +#define SLIC_REG_RESET 0x0000 +/* Interrupt Control Register */ +#define SLIC_REG_ICR 0x0008 +/* Interrupt status pointer */ +#define SLIC_REG_ISP 0x0010 +/* Interrupt status */ +#define SLIC_REG_ISR 0x0018 +/* Header buffer address reg + * 31-8 - phy addr of set of contiguous hdr buffers + * 7-0 - number of buffers passed + * Buffers are 256 bytes long on 256-byte boundaries. + */ +#define SLIC_REG_HBAR 0x0020 +/* Data buffer handle & address reg + * 4 sets of registers; Buffers are 2K bytes long 2 per 4K page. + */ +#define SLIC_REG_DBAR 0x0028 +/* Xmt Cmd buf addr regs. + * 1 per XMT interface + * 31-5 - phy addr of host command buffer + * 4-0 - length of cmd in multiples of 32 bytes + * Buffers are 32 bytes up to 512 bytes long + */ +#define SLIC_REG_CBAR 0x0030 +/* Write control store */ +#define SLIC_REG_WCS 0x0034 +/*Response buffer address reg. + * 31-8 - phy addr of set of contiguous response buffers + * 7-0 - number of buffers passed + * Buffers are 32 bytes long on 32-byte boundaries. + */ +#define SLIC_REG_RBAR 0x0038 +/* Read statistics (UPR) */ +#define SLIC_REG_RSTAT 0x0040 +/* Read link status */ +#define SLIC_REG_LSTAT 0x0048 +/* Write Mac Config */ +#define SLIC_REG_WMCFG 0x0050 +/* Write phy register */ +#define SLIC_REG_WPHY 0x0058 +/* Rcv Cmd buf addr reg */ +#define SLIC_REG_RCBAR 0x0060 +/* Read SLIC Config*/ +#define SLIC_REG_RCONFIG 0x0068 +/* Interrupt aggregation time */ +#define SLIC_REG_INTAGG 0x0070 +/* Write XMIT config reg */ +#define SLIC_REG_WXCFG 0x0078 +/* Write RCV config reg */ +#define SLIC_REG_WRCFG 0x0080 +/* Write rcv addr a low */ +#define SLIC_REG_WRADDRAL 0x0088 +/* Write rcv addr a high */ +#define SLIC_REG_WRADDRAH 0x0090 +/* Write rcv addr b low */ +#define SLIC_REG_WRADDRBL 0x0098 +/* Write rcv addr b high */ +#define SLIC_REG_WRADDRBH 0x00a0 +/* Low bits of mcast mask */ +#define SLIC_REG_MCASTLOW 0x00a8 +/* High bits of mcast mask */ +#define SLIC_REG_MCASTHIGH 0x00b0 +/* Ping the card */ +#define SLIC_REG_PING 0x00b8 +/* Dump command */ +#define SLIC_REG_DUMP_CMD 0x00c0 +/* Dump data pointer */ +#define SLIC_REG_DUMP_DATA 0x00c8 +/* Read card's pci_status register */ +#define SLIC_REG_PCISTATUS 0x00d0 +/* Write hostid field */ +#define SLIC_REG_WRHOSTID 0x00d8 +/* Put card in a low power state */ +#define SLIC_REG_LOW_POWER 0x00e0 +/* Force slic into quiescent state before soft reset */ +#define SLIC_REG_QUIESCE 0x00e8 +/* Reset interface queues */ +#define SLIC_REG_RESET_IFACE 0x00f0 +/* Register is only written when it has changed. + * Bits 63-32 for host i/f addrs. + */ +#define SLIC_REG_ADDR_UPPER 0x00f8 +/* 64 bit Header buffer address reg */ +#define SLIC_REG_HBAR64 0x0100 +/* 64 bit Data buffer handle & address reg */ +#define SLIC_REG_DBAR64 0x0108 +/* 64 bit Xmt Cmd buf addr regs. */ +#define SLIC_REG_CBAR64 0x0110 +/* 64 bit Response buffer address reg.*/ +#define SLIC_REG_RBAR64 0x0118 +/* 64 bit Rcv Cmd buf addr reg*/ +#define SLIC_REG_RCBAR64 0x0120 +/* Read statistics (64 bit UPR) */ +#define SLIC_REG_RSTAT64 0x0128 +/* Download Gigabit RCV sequencer ucode */ +#define SLIC_REG_RCV_WCS 0x0130 +/* Write VlanId field */ +#define SLIC_REG_WRVLANID 0x0138 +/* Read Transformer info */ +#define SLIC_REG_READ_XF_INFO 0x0140 +/* Write Transformer info */ +#define SLIC_REG_WRITE_XF_INFO 0x0148 +/* Write card ticks per second */ +#define SLIC_REG_TICKS_PER_SEC 0x0170 +#define SLIC_REG_HOSTID 0x1554 + +#define PCI_VENDOR_ID_ALACRITECH 0x139A +#define PCI_DEVICE_ID_ALACRITECH_MOJAVE 0x0005 +#define PCI_SUBDEVICE_ID_ALACRITECH_1000X1 0x0005 +#define PCI_SUBDEVICE_ID_ALACRITECH_1000X1_2 0x0006 +#define PCI_SUBDEVICE_ID_ALACRITECH_1000X1F 0x0007 +#define PCI_SUBDEVICE_ID_ALACRITECH_CICADA 0x0008 +#define PCI_SUBDEVICE_ID_ALACRITECH_SES1001T 0x2006 +#define PCI_SUBDEVICE_ID_ALACRITECH_SES1001F 0x2007 +#define PCI_DEVICE_ID_ALACRITECH_OASIS 0x0007 +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XT 0x000B +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XF 0x000C +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XT 0x000D +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XF 0x000E +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2104EF 0x000F +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2104ET 0x0010 +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2102EF 0x0011 +#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2102ET 0x0012 + +/* Note: power of two required for number descriptors */ +#define SLIC_NUM_RX_LES 256 +#define SLIC_RX_BUFF_SIZE 2048 +#define SLIC_RX_BUFF_ALIGN 256 +#define SLIC_RX_BUFF_HDR_SIZE 34 +#define SLIC_MAX_REQ_RX_DESCS 1 + +#define SLIC_NUM_TX_DESCS 256 +#define SLIC_TX_DESC_ALIGN 32 +#define SLIC_MIN_TX_WAKEUP_DESCS 10 +#define SLIC_MAX_REQ_TX_DESCS 1 +#define SLIC_MAX_TX_COMPLETIONS 100 + +#define SLIC_NUM_STAT_DESCS 128 +#define SLIC_STATS_DESC_ALIGN 256 + +#define SLIC_NUM_STAT_DESC_ARRAYS 4 +#define SLIC_INVALID_STAT_DESC_IDX 0xffffffff + +#define SLIC_NAPI_WEIGHT 64 + +#define SLIC_UPR_LSTAT 0 +#define SLIC_UPR_CONFIG 1 + +#define SLIC_EEPROM_SIZE 128 +#define SLIC_EEPROM_MAGIC 0xa5a5 + +#define SLIC_FIRMWARE_MOJAVE "slicoss/gbdownload.sys" +#define SLIC_FIRMWARE_OASIS "slicoss/oasisdownload.sys" +#define SLIC_RCV_FIRMWARE_MOJAVE "slicoss/gbrcvucode.sys" +#define SLIC_RCV_FIRMWARE_OASIS "slicoss/oasisrcvucode.sys" +#define SLIC_FIRMWARE_MIN_SIZE 64 +#define SLIC_FIRMWARE_MAX_SECTIONS 3 + +#define SLIC_MODEL_MOJAVE 0 +#define SLIC_MODEL_OASIS 1 + +#define SLIC_INC_STATS_COUNTER(st, counter) \ +do { \ + u64_stats_update_begin(&(st)->syncp); \ + (st)->counter++; \ + u64_stats_update_end(&(st)->syncp); \ +} while (0) + +#define SLIC_GET_STATS_COUNTER(newst, st, counter) \ +{ \ + unsigned int start; \ + do { \ + start = u64_stats_fetch_begin_irq(&(st)->syncp); \ + newst = (st)->counter; \ + } while (u64_stats_fetch_retry_irq(&(st)->syncp, start)); \ +} + +struct slic_upr { + dma_addr_t paddr; + unsigned int type; + struct list_head list; +}; + +struct slic_upr_list { + bool pending; + struct list_head list; + /* upr list lock */ + spinlock_t lock; +}; + +/* SLIC EEPROM structure for Mojave */ +struct slic_mojave_eeprom { + __le16 id; /* 00 EEPROM/FLASH Magic code 'A5A5'*/ + __le16 eeprom_code_size;/* 01 Size of EEPROM Codes (bytes * 4)*/ + __le16 flash_size; /* 02 Flash size */ + __le16 eeprom_size; /* 03 EEPROM Size */ + __le16 vendor_id; /* 04 Vendor ID */ + __le16 dev_id; /* 05 Device ID */ + u8 rev_id; /* 06 Revision ID */ + u8 class_code[3]; /* 07 Class Code */ + u8 irqpin_dbg; /* 08 Debug Interrupt pin */ + u8 irqpin; /* Network Interrupt Pin */ + u8 min_grant; /* 09 Minimum grant */ + u8 max_lat; /* Maximum Latency */ + __le16 pci_stat; /* 10 PCI Status */ + __le16 sub_vendor_id; /* 11 Subsystem Vendor Id */ + __le16 sub_id; /* 12 Subsystem ID */ + __le16 dev_id_dbg; /* 13 Debug Device Id */ + __le16 ramrom; /* 14 Dram/Rom function */ + __le16 dram_size2pci; /* 15 DRAM size to PCI (bytes * 64K) */ + __le16 rom_size2pci; /* 16 ROM extension size to PCI (bytes * 4k) */ + u8 pad[2]; /* 17 Padding */ + u8 freetime; /* 18 FreeTime setting */ + u8 ifctrl; /* 10-bit interface control (Mojave only) */ + __le16 dram_size; /* 19 DRAM size (bytes * 64k) */ + u8 mac[ETH_ALEN]; /* 20 MAC addresses */ + u8 mac2[ETH_ALEN]; + u8 pad2[6]; + u16 dev_id2; /* Device ID for 2nd PCI function */ + u8 irqpin2; /* Interrupt pin for 2nd PCI function */ + u8 class_code2[3]; /* Class Code for 2nd PCI function */ + u16 cfg_byte6; /* Config Byte 6 */ + u16 pme_cap; /* Power Mgment capabilities */ + u16 nwclk_ctrl; /* NetworkClockControls */ + u8 fru_format; /* Alacritech FRU format type */ + u8 fru_assembly[6]; /* Alacritech FRU information */ + u8 fru_rev[2]; + u8 fru_serial[14]; + u8 fru_pad[3]; + u8 oem_fru[28]; /* optional OEM FRU format type */ + u8 pad3[4]; /* Pad to 128 bytes - includes 2 cksum bytes + * (if OEM FRU info exists) and two unusable + * bytes at the end + */ +}; + +/* SLIC EEPROM structure for Oasis */ +struct slic_oasis_eeprom { + __le16 id; /* 00 EEPROM/FLASH Magic code 'A5A5' */ + __le16 eeprom_code_size;/* 01 Size of EEPROM Codes (bytes * 4)*/ + __le16 spidev0_cfg; /* 02 Flash Config for SPI device 0 */ + __le16 spidev1_cfg; /* 03 Flash Config for SPI device 1 */ + __le16 vendor_id; /* 04 Vendor ID */ + __le16 dev_id; /* 05 Device ID (function 0) */ + u8 rev_id; /* 06 Revision ID */ + u8 class_code0[3]; /* 07 Class Code for PCI function 0 */ + u8 irqpin1; /* 08 Interrupt pin for PCI function 1*/ + u8 class_code1[3]; /* 09 Class Code for PCI function 1 */ + u8 irqpin2; /* 10 Interrupt pin for PCI function 2*/ + u8 irqpin0; /* Interrupt pin for PCI function 0*/ + u8 min_grant; /* 11 Minimum grant */ + u8 max_lat; /* Maximum Latency */ + __le16 sub_vendor_id; /* 12 Subsystem Vendor Id */ + __le16 sub_id; /* 13 Subsystem ID */ + __le16 flash_size; /* 14 Flash size (bytes / 4K) */ + __le16 dram_size2pci; /* 15 DRAM size to PCI (bytes / 64K) */ + __le16 rom_size2pci; /* 16 Flash (ROM extension) size to PCI + * (bytes / 4K) + */ + __le16 dev_id1; /* 17 Device Id (function 1) */ + __le16 dev_id2; /* 18 Device Id (function 2) */ + __le16 dev_stat_cfg; /* 19 Device Status Config Bytes 6-7 */ + __le16 pme_cap; /* 20 Power Mgment capabilities */ + u8 msi_cap; /* 21 MSI capabilities */ + u8 clock_div; /* Clock divider */ + __le16 pci_stat_lo; /* 22 PCI Status bits 15:0 */ + __le16 pci_stat_hi; /* 23 PCI Status bits 31:16 */ + __le16 dram_cfg_lo; /* 24 DRAM Configuration bits 15:0 */ + __le16 dram_cfg_hi; /* 25 DRAM Configuration bits 31:16 */ + __le16 dram_size; /* 26 DRAM size (bytes / 64K) */ + __le16 gpio_tbi_ctrl; /* 27 GPIO/TBI controls for functions 1/0 */ + __le16 eeprom_size; /* 28 EEPROM Size */ + u8 mac[ETH_ALEN]; /* 29 MAC addresses (2 ports) */ + u8 mac2[ETH_ALEN]; + u8 fru_format; /* 35 Alacritech FRU format type */ + u8 fru_assembly[6]; /* Alacritech FRU information */ + u8 fru_rev[2]; + u8 fru_serial[14]; + u8 fru_pad[3]; + u8 oem_fru[28]; /* optional OEM FRU information */ + u8 pad[4]; /* Pad to 128 bytes - includes 2 checksum bytes + * (if OEM FRU info exists) and two unusable + * bytes at the end + */ +}; + +struct slic_stats { + u64 rx_packets; + u64 rx_bytes; + u64 rx_mcasts; + u64 rx_errors; + u64 tx_packets; + u64 tx_bytes; + /* HW STATS */ + u64 rx_buff_miss; + u64 tx_dropped; + u64 irq_errs; + /* transport layer */ + u64 rx_tpcsum; + u64 rx_tpoflow; + u64 rx_tphlen; + /* ip layer */ + u64 rx_ipcsum; + u64 rx_iplen; + u64 rx_iphlen; + /* link layer */ + u64 rx_early; + u64 rx_buffoflow; + u64 rx_lcode; + u64 rx_drbl; + u64 rx_crc; + u64 rx_oflow802; + u64 rx_uflow802; + /* oasis only */ + u64 tx_carrier; + struct u64_stats_sync syncp; +}; + +struct slic_shmem_data { + __le32 isr; + __le32 link; +}; + +struct slic_shmem { + dma_addr_t isr_paddr; + dma_addr_t link_paddr; + struct slic_shmem_data *shmem_data; +}; + +struct slic_rx_info_oasis { + __le32 frame_status; + __le32 frame_status_b; + __le32 time_stamp; + __le32 checksum; +}; + +struct slic_rx_info_mojave { + __le32 frame_status; + __le16 byte_cnt; + __le16 tp_chksum; + __le16 ctx_hash; + __le16 mac_hash; + __le16 buff_lnk; +}; + +struct slic_stat_desc { + __le32 hnd; + __u8 pad[8]; + __le32 status; + __u8 pad2[16]; +}; + +struct slic_stat_queue { + struct slic_stat_desc *descs[SLIC_NUM_STAT_DESC_ARRAYS]; + dma_addr_t paddr[SLIC_NUM_STAT_DESC_ARRAYS]; + unsigned int addr_offset[SLIC_NUM_STAT_DESC_ARRAYS]; + unsigned int active_array; + unsigned int len; + unsigned int done_idx; + size_t mem_size; +}; + +struct slic_tx_desc { + __le32 hnd; + __le32 rsvd; + u8 cmd; + u8 flags; + __le16 rsvd2; + __le32 totlen; + __le32 paddrl; + __le32 paddrh; + __le32 len; + __le32 type; +}; + +struct slic_tx_buffer { + struct sk_buff *skb; + DEFINE_DMA_UNMAP_ADDR(map_addr); + DEFINE_DMA_UNMAP_LEN(map_len); + struct slic_tx_desc *desc; + dma_addr_t desc_paddr; +}; + +struct slic_tx_queue { + struct dma_pool *dma_pool; + struct slic_tx_buffer *txbuffs; + unsigned int len; + unsigned int put_idx; + unsigned int done_idx; +}; + +struct slic_rx_desc { + u8 pad[16]; + __le32 buffer; + __le32 length; + __le32 status; +}; + +struct slic_rx_buffer { + struct sk_buff *skb; + DEFINE_DMA_UNMAP_ADDR(map_addr); + DEFINE_DMA_UNMAP_LEN(map_len); + unsigned int addr_offset; +}; + +struct slic_rx_queue { + struct slic_rx_buffer *rxbuffs; + unsigned int len; + unsigned int done_idx; + unsigned int put_idx; +}; + +struct slic_device { + struct pci_dev *pdev; + struct net_device *netdev; + void __iomem *regs; + /* upper address setting lock */ + spinlock_t upper_lock; + struct slic_shmem shmem; + struct napi_struct napi; + struct slic_rx_queue rxq; + struct slic_tx_queue txq; + struct slic_stat_queue stq; + struct slic_stats stats; + struct slic_upr_list upr_list; + /* link configuration lock */ + spinlock_t link_lock; + bool promisc; + int speed; + unsigned int duplex; + bool is_fiber; + unsigned char model; +}; + +static inline u32 slic_read(struct slic_device *sdev, unsigned int reg) +{ + return ioread32(sdev->regs + reg); +} + +static inline void slic_write(struct slic_device *sdev, unsigned int reg, + u32 val) +{ + iowrite32(val, sdev->regs + reg); +} + +static inline void slic_flush_write(struct slic_device *sdev) +{ + (void)ioread32(sdev->regs + SLIC_REG_HOSTID); +} + +#endif /* _SLIC_H */ diff --git a/drivers/net/ethernet/alacritech/slicoss.c b/drivers/net/ethernet/alacritech/slicoss.c new file mode 100644 index 0000000..e77ecd5 --- /dev/null +++ b/drivers/net/ethernet/alacritech/slicoss.c @@ -0,0 +1,1882 @@ +/* + * Driver for Gigabit Ethernet adapters based on the Session Layer + * Interface (SLIC) technology by Alacritech. The driver does not + * support the hardware acceleration features provided by these cards. + * + * Copyright (C) 2016 Lino Sanfilippo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "slic.h" + +#define DRV_NAME "slicoss" +#define DRV_VERSION "1.0" + +static const struct pci_device_id slic_id_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH, + PCI_DEVICE_ID_ALACRITECH_MOJAVE) }, + { PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH, + PCI_DEVICE_ID_ALACRITECH_OASIS) }, + { 0 } +}; + +static const char slic_stats_strings[][ETH_GSTRING_LEN] = { + "rx_packets", + "rx_bytes", + "rx_multicasts", + "rx_errors", + "rx_buff_miss", + "rx_tp_csum", + "rx_tp_oflow", + "rx_tp_hlen", + "rx_ip_csum", + "rx_ip_len", + "rx_ip_hdr_len", + "rx_early", + "rx_buff_oflow", + "rx_lcode", + "rx_drbl", + "rx_crc", + "rx_oflow_802", + "rx_uflow_802", + "tx_packets", + "tx_bytes", + "tx_carrier", + "tx_dropped", + "irq_errs", +}; + +static inline int slic_next_queue_idx(unsigned int idx, unsigned int qlen) +{ + return (idx + 1) & (qlen - 1); +} + +static inline int slic_get_free_queue_descs(unsigned int put_idx, + unsigned int done_idx, + unsigned int qlen) +{ + if (put_idx >= done_idx) + return (qlen - (put_idx - done_idx) - 1); + return (done_idx - put_idx - 1); +} + +static unsigned int slic_next_compl_idx(struct slic_device *sdev) +{ + struct slic_stat_queue *stq = &sdev->stq; + unsigned int active = stq->active_array; + struct slic_stat_desc *descs; + struct slic_stat_desc *stat; + unsigned int idx; + + descs = stq->descs[active]; + stat = &descs[stq->done_idx]; + + if (!stat->status) + return SLIC_INVALID_STAT_DESC_IDX; + + idx = (le32_to_cpu(stat->hnd) & 0xffff) - 1; + /* reset desc */ + stat->hnd = 0; + stat->status = 0; + + stq->done_idx = slic_next_queue_idx(stq->done_idx, stq->len); + /* check for wraparound */ + if (!stq->done_idx) { + dma_addr_t paddr = stq->paddr[active]; + + slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) | + stq->len); + /* make sure new status descriptors are immediately available */ + slic_flush_write(sdev); + active++; + active &= (SLIC_NUM_STAT_DESC_ARRAYS - 1); + stq->active_array = active; + } + return idx; +} + +static unsigned int slic_get_free_tx_descs(struct slic_tx_queue *txq) +{ + /* ensure tail idx is updated */ + smp_mb(); + return slic_get_free_queue_descs(txq->put_idx, txq->done_idx, txq->len); +} + +static unsigned int slic_get_free_rx_descs(struct slic_rx_queue *rxq) +{ + return slic_get_free_queue_descs(rxq->put_idx, rxq->done_idx, rxq->len); +} + +static void slic_clear_upr_list(struct slic_upr_list *upr_list) +{ + struct slic_upr *upr; + struct slic_upr *tmp; + + spin_lock_bh(&upr_list->lock); + list_for_each_entry_safe(upr, tmp, &upr_list->list, list) { + list_del(&upr->list); + kfree(upr); + } + upr_list->pending = false; + spin_unlock_bh(&upr_list->lock); +} + +static void slic_start_upr(struct slic_device *sdev, struct slic_upr *upr) +{ + u32 reg; + + reg = (upr->type == SLIC_UPR_CONFIG) ? SLIC_REG_RCONFIG : + SLIC_REG_LSTAT; + slic_write(sdev, reg, lower_32_bits(upr->paddr)); + slic_flush_write(sdev); +} + +static void slic_queue_upr(struct slic_device *sdev, struct slic_upr *upr) +{ + struct slic_upr_list *upr_list = &sdev->upr_list; + bool pending; + + spin_lock_bh(&upr_list->lock); + pending = upr_list->pending; + INIT_LIST_HEAD(&upr->list); + list_add_tail(&upr->list, &upr_list->list); + upr_list->pending = true; + spin_unlock_bh(&upr_list->lock); + + if (!pending) + slic_start_upr(sdev, upr); +} + +static struct slic_upr *slic_dequeue_upr(struct slic_device *sdev) +{ + struct slic_upr_list *upr_list = &sdev->upr_list; + struct slic_upr *next_upr = NULL; + struct slic_upr *upr = NULL; + + spin_lock_bh(&upr_list->lock); + if (!list_empty(&upr_list->list)) { + upr = list_first_entry(&upr_list->list, struct slic_upr, list); + list_del(&upr->list); + + if (list_empty(&upr_list->list)) + upr_list->pending = false; + else + next_upr = list_first_entry(&upr_list->list, + struct slic_upr, list); + } + spin_unlock_bh(&upr_list->lock); + /* trigger processing of the next upr in list */ + if (next_upr) + slic_start_upr(sdev, next_upr); + + return upr; +} + +static int slic_new_upr(struct slic_device *sdev, unsigned int type, + dma_addr_t paddr) +{ + struct slic_upr *upr; + + upr = kmalloc(sizeof(*upr), GFP_ATOMIC); + if (!upr) + return -ENOMEM; + upr->type = type; + upr->paddr = paddr; + + slic_queue_upr(sdev, upr); + + return 0; +} + +static void slic_set_mcast_bit(u64 *mcmask, unsigned char const *addr) +{ + u64 mask = *mcmask; + u8 crc; + /* Get the CRC polynomial for the mac address: we use bits 1-8 (lsb), + * bitwise reversed, msb (= lsb bit 0 before bitrev) is automatically + * discarded. + */ + crc = ether_crc(ETH_ALEN, addr) >> 23; + /* we only have space on the SLIC for 64 entries */ + crc &= 0x3F; + mask |= (u64)1 << crc; + *mcmask = mask; +} + +/* must be called with link_lock held */ +static void slic_configure_rcv(struct slic_device *sdev) +{ + u32 val; + + val = SLIC_GRCR_RESET | SLIC_GRCR_ADDRAEN | SLIC_GRCR_RCVEN | + SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT | SLIC_GRCR_RCVBAD; + + if (sdev->duplex == DUPLEX_FULL) + val |= SLIC_GRCR_CTLEN; + + if (sdev->promisc) + val |= SLIC_GRCR_RCVALL; + + slic_write(sdev, SLIC_REG_WRCFG, val); +} + +/* must be called with link_lock held */ +static void slic_configure_xmt(struct slic_device *sdev) +{ + u32 val; + + val = SLIC_GXCR_RESET | SLIC_GXCR_XMTEN; + + if (sdev->duplex == DUPLEX_FULL) + val |= SLIC_GXCR_PAUSEEN; + + slic_write(sdev, SLIC_REG_WXCFG, val); +} + +/* must be called with link_lock held */ +static void slic_configure_mac(struct slic_device *sdev) +{ + u32 val; + + if (sdev->speed == SPEED_1000) { + val = SLIC_GMCR_GAPBB_1000 << SLIC_GMCR_GAPBB_SHIFT | + SLIC_GMCR_GAPR1_1000 << SLIC_GMCR_GAPR1_SHIFT | + SLIC_GMCR_GAPR2_1000 << SLIC_GMCR_GAPR2_SHIFT | + SLIC_GMCR_GBIT; /* enable GMII */ + } else { + val = SLIC_GMCR_GAPBB_100 << SLIC_GMCR_GAPBB_SHIFT | + SLIC_GMCR_GAPR1_100 << SLIC_GMCR_GAPR1_SHIFT | + SLIC_GMCR_GAPR2_100 << SLIC_GMCR_GAPR2_SHIFT; + } + + if (sdev->duplex == DUPLEX_FULL) + val |= SLIC_GMCR_FULLD; + + slic_write(sdev, SLIC_REG_WMCFG, val); +} + +static void slic_configure_link_locked(struct slic_device *sdev, int speed, + unsigned int duplex) +{ + struct net_device *dev = sdev->netdev; + + if (sdev->speed == speed && sdev->duplex == duplex) + return; + + sdev->speed = speed; + sdev->duplex = duplex; + + if (sdev->speed == SPEED_UNKNOWN) { + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); + } else { + /* (re)configure link settings */ + slic_configure_mac(sdev); + slic_configure_xmt(sdev); + slic_configure_rcv(sdev); + slic_flush_write(sdev); + + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + } +} + +static void slic_configure_link(struct slic_device *sdev, int speed, + unsigned int duplex) +{ + spin_lock_bh(&sdev->link_lock); + slic_configure_link_locked(sdev, speed, duplex); + spin_unlock_bh(&sdev->link_lock); +} + +static void slic_set_rx_mode(struct net_device *dev) +{ + struct slic_device *sdev = netdev_priv(dev); + struct netdev_hw_addr *hwaddr; + bool set_promisc; + u64 mcmask; + + if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { + /* Turn on all multicast addresses. We have to do this for + * promiscuous mode as well as ALLMCAST mode (it saves the + * microcode from having to keep state about the MAC + * configuration). + */ + mcmask = ~(u64)0; + } else { + mcmask = 0; + + netdev_for_each_mc_addr(hwaddr, dev) { + slic_set_mcast_bit(&mcmask, hwaddr->addr); + } + } + + slic_write(sdev, SLIC_REG_MCASTLOW, lower_32_bits(mcmask)); + slic_write(sdev, SLIC_REG_MCASTHIGH, upper_32_bits(mcmask)); + + set_promisc = !!(dev->flags & IFF_PROMISC); + + spin_lock_bh(&sdev->link_lock); + if (sdev->promisc != set_promisc) { + sdev->promisc = set_promisc; + slic_configure_rcv(sdev); + /* make sure writes to receiver cant leak out of the lock */ + mmiowb(); + } + spin_unlock_bh(&sdev->link_lock); +} + +static void slic_xmit_complete(struct slic_device *sdev) +{ + struct slic_tx_queue *txq = &sdev->txq; + struct net_device *dev = sdev->netdev; + unsigned int idx = txq->done_idx; + struct slic_tx_buffer *buff; + unsigned int frames = 0; + unsigned int bytes = 0; + + /* Limit processing to SLIC_MAX_TX_COMPLETIONS frames to avoid that new + * completions during processing keeps the loop running endlessly. + */ + do { + idx = slic_next_compl_idx(sdev); + if (idx == SLIC_INVALID_STAT_DESC_IDX) + break; + + txq->done_idx = idx; + buff = &txq->txbuffs[idx]; + + if (unlikely(!buff->skb)) { + netdev_warn(dev, + "no skb found for desc idx %i\n", idx); + continue; + } + dma_unmap_single(&sdev->pdev->dev, + dma_unmap_addr(buff, map_addr), + dma_unmap_len(buff, map_len), DMA_TO_DEVICE); + + bytes += buff->skb->len; + frames++; + + dev_kfree_skb_any(buff->skb); + buff->skb = NULL; + } while (frames < SLIC_MAX_TX_COMPLETIONS); + /* make sure xmit sees the new value for done_idx */ + smp_wmb(); + + u64_stats_update_begin(&sdev->stats.syncp); + sdev->stats.tx_bytes += bytes; + sdev->stats.tx_packets += frames; + u64_stats_update_end(&sdev->stats.syncp); + + netif_tx_lock(dev); + if (netif_queue_stopped(dev) && + (slic_get_free_tx_descs(txq) >= SLIC_MIN_TX_WAKEUP_DESCS)) + netif_wake_queue(dev); + netif_tx_unlock(dev); +} + +static void slic_refill_rx_queue(struct slic_device *sdev, gfp_t gfp) +{ + const unsigned int ALIGN_MASK = SLIC_RX_BUFF_ALIGN - 1; + unsigned int maplen = SLIC_RX_BUFF_SIZE; + struct slic_rx_queue *rxq = &sdev->rxq; + struct net_device *dev = sdev->netdev; + struct slic_rx_buffer *buff; + struct slic_rx_desc *desc; + unsigned int misalign; + unsigned int offset; + struct sk_buff *skb; + dma_addr_t paddr; + + while (slic_get_free_rx_descs(rxq) > SLIC_MAX_REQ_RX_DESCS) { + skb = alloc_skb(maplen + ALIGN_MASK, gfp); + if (!skb) + break; + + paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen, + DMA_FROM_DEVICE); + if (dma_mapping_error(&sdev->pdev->dev, paddr)) { + netdev_err(dev, "mapping rx packet failed\n"); + /* drop skb */ + dev_kfree_skb_any(skb); + break; + } + /* ensure head buffer descriptors are 256 byte aligned */ + offset = 0; + misalign = paddr & ALIGN_MASK; + if (misalign) { + offset = SLIC_RX_BUFF_ALIGN - misalign; + skb_reserve(skb, offset); + } + /* the HW expects dma chunks for descriptor + frame data */ + desc = (struct slic_rx_desc *)skb->data; + /* temporarily sync descriptor for CPU to clear status */ + dma_sync_single_for_cpu(&sdev->pdev->dev, paddr, + offset + sizeof(*desc), + DMA_FROM_DEVICE); + desc->status = 0; + /* return it to HW again */ + dma_sync_single_for_device(&sdev->pdev->dev, paddr, + offset + sizeof(*desc), + DMA_FROM_DEVICE); + + buff = &rxq->rxbuffs[rxq->put_idx]; + buff->skb = skb; + dma_unmap_addr_set(buff, map_addr, paddr); + dma_unmap_len_set(buff, map_len, maplen); + buff->addr_offset = offset; + /* complete write to descriptor before it is handed to HW */ + wmb(); + /* head buffer descriptors are placed immediately before skb */ + slic_write(sdev, SLIC_REG_HBAR, lower_32_bits(paddr) + offset); + rxq->put_idx = slic_next_queue_idx(rxq->put_idx, rxq->len); + } +} + +static void slic_handle_frame_error(struct slic_device *sdev, + struct sk_buff *skb) +{ + struct slic_stats *stats = &sdev->stats; + + if (sdev->model == SLIC_MODEL_OASIS) { + struct slic_rx_info_oasis *info; + u32 status_b; + u32 status; + + info = (struct slic_rx_info_oasis *)skb->data; + status = le32_to_cpu(info->frame_status); + status_b = le32_to_cpu(info->frame_status_b); + /* transport layer */ + if (status_b & SLIC_VRHSTATB_TPCSUM) + SLIC_INC_STATS_COUNTER(stats, rx_tpcsum); + if (status & SLIC_VRHSTAT_TPOFLO) + SLIC_INC_STATS_COUNTER(stats, rx_tpoflow); + if (status_b & SLIC_VRHSTATB_TPHLEN) + SLIC_INC_STATS_COUNTER(stats, rx_tphlen); + /* ip layer */ + if (status_b & SLIC_VRHSTATB_IPCSUM) + SLIC_INC_STATS_COUNTER(stats, rx_ipcsum); + if (status_b & SLIC_VRHSTATB_IPLERR) + SLIC_INC_STATS_COUNTER(stats, rx_iplen); + if (status_b & SLIC_VRHSTATB_IPHERR) + SLIC_INC_STATS_COUNTER(stats, rx_iphlen); + /* link layer */ + if (status_b & SLIC_VRHSTATB_RCVE) + SLIC_INC_STATS_COUNTER(stats, rx_early); + if (status_b & SLIC_VRHSTATB_BUFF) + SLIC_INC_STATS_COUNTER(stats, rx_buffoflow); + if (status_b & SLIC_VRHSTATB_CODE) + SLIC_INC_STATS_COUNTER(stats, rx_lcode); + if (status_b & SLIC_VRHSTATB_DRBL) + SLIC_INC_STATS_COUNTER(stats, rx_drbl); + if (status_b & SLIC_VRHSTATB_CRC) + SLIC_INC_STATS_COUNTER(stats, rx_crc); + if (status & SLIC_VRHSTAT_802OE) + SLIC_INC_STATS_COUNTER(stats, rx_oflow802); + if (status_b & SLIC_VRHSTATB_802UE) + SLIC_INC_STATS_COUNTER(stats, rx_uflow802); + if (status_b & SLIC_VRHSTATB_CARRE) + SLIC_INC_STATS_COUNTER(stats, tx_carrier); + } else { /* mojave */ + struct slic_rx_info_mojave *info; + u32 status; + + info = (struct slic_rx_info_mojave *)skb->data; + status = le32_to_cpu(info->frame_status); + /* transport layer */ + if (status & SLIC_VGBSTAT_XPERR) { + u32 xerr = status >> SLIC_VGBSTAT_XERRSHFT; + + if (xerr == SLIC_VGBSTAT_XCSERR) + SLIC_INC_STATS_COUNTER(stats, rx_tpcsum); + if (xerr == SLIC_VGBSTAT_XUFLOW) + SLIC_INC_STATS_COUNTER(stats, rx_tpoflow); + if (xerr == SLIC_VGBSTAT_XHLEN) + SLIC_INC_STATS_COUNTER(stats, rx_tphlen); + } + /* ip layer */ + if (status & SLIC_VGBSTAT_NETERR) { + u32 nerr = status >> SLIC_VGBSTAT_NERRSHFT & + SLIC_VGBSTAT_NERRMSK; + + if (nerr == SLIC_VGBSTAT_NCSERR) + SLIC_INC_STATS_COUNTER(stats, rx_ipcsum); + if (nerr == SLIC_VGBSTAT_NUFLOW) + SLIC_INC_STATS_COUNTER(stats, rx_iplen); + if (nerr == SLIC_VGBSTAT_NHLEN) + SLIC_INC_STATS_COUNTER(stats, rx_iphlen); + } + /* link layer */ + if (status & SLIC_VGBSTAT_LNKERR) { + u32 lerr = status & SLIC_VGBSTAT_LERRMSK; + + if (lerr == SLIC_VGBSTAT_LDEARLY) + SLIC_INC_STATS_COUNTER(stats, rx_early); + if (lerr == SLIC_VGBSTAT_LBOFLO) + SLIC_INC_STATS_COUNTER(stats, rx_buffoflow); + if (lerr == SLIC_VGBSTAT_LCODERR) + SLIC_INC_STATS_COUNTER(stats, rx_lcode); + if (lerr == SLIC_VGBSTAT_LDBLNBL) + SLIC_INC_STATS_COUNTER(stats, rx_drbl); + if (lerr == SLIC_VGBSTAT_LCRCERR) + SLIC_INC_STATS_COUNTER(stats, rx_crc); + if (lerr == SLIC_VGBSTAT_LOFLO) + SLIC_INC_STATS_COUNTER(stats, rx_oflow802); + if (lerr == SLIC_VGBSTAT_LUFLO) + SLIC_INC_STATS_COUNTER(stats, rx_uflow802); + } + } + SLIC_INC_STATS_COUNTER(stats, rx_errors); +} + +static void slic_handle_receive(struct slic_device *sdev, unsigned int todo, + unsigned int *done) +{ + struct slic_rx_queue *rxq = &sdev->rxq; + struct net_device *dev = sdev->netdev; + struct slic_rx_buffer *buff; + struct slic_rx_desc *desc; + unsigned int frames = 0; + unsigned int bytes = 0; + struct sk_buff *skb; + u32 status; + u32 len; + + while (todo && (rxq->done_idx != rxq->put_idx)) { + buff = &rxq->rxbuffs[rxq->done_idx]; + + skb = buff->skb; + if (!skb) + break; + + desc = (struct slic_rx_desc *)skb->data; + + dma_sync_single_for_cpu(&sdev->pdev->dev, + dma_unmap_addr(buff, map_addr), + buff->addr_offset + sizeof(*desc), + DMA_FROM_DEVICE); + + status = le32_to_cpu(desc->status); + if (!(status & SLIC_IRHDDR_SVALID)) { + dma_sync_single_for_device(&sdev->pdev->dev, + dma_unmap_addr(buff, + map_addr), + buff->addr_offset + + sizeof(*desc), + DMA_FROM_DEVICE); + break; + } + + buff->skb = NULL; + + dma_unmap_single(&sdev->pdev->dev, + dma_unmap_addr(buff, map_addr), + dma_unmap_len(buff, map_len), + DMA_FROM_DEVICE); + + /* skip rx descriptor that is placed before the frame data */ + skb_reserve(skb, SLIC_RX_BUFF_HDR_SIZE); + + if (unlikely(status & SLIC_IRHDDR_ERR)) { + slic_handle_frame_error(sdev, skb); + dev_kfree_skb_any(skb); + } else { + struct ethhdr *eh = (struct ethhdr *)skb->data; + + if (is_multicast_ether_addr(eh->h_dest)) + SLIC_INC_STATS_COUNTER(&sdev->stats, rx_mcasts); + + len = le32_to_cpu(desc->length) & SLIC_IRHDDR_FLEN_MSK; + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + napi_gro_receive(&sdev->napi, skb); + + bytes += len; + frames++; + } + rxq->done_idx = slic_next_queue_idx(rxq->done_idx, rxq->len); + todo--; + } + + u64_stats_update_begin(&sdev->stats.syncp); + sdev->stats.rx_bytes += bytes; + sdev->stats.rx_packets += frames; + u64_stats_update_end(&sdev->stats.syncp); + + slic_refill_rx_queue(sdev, GFP_ATOMIC); +} + +static void slic_handle_link_irq(struct slic_device *sdev) +{ + struct slic_shmem *sm = &sdev->shmem; + struct slic_shmem_data *sm_data = sm->shmem_data; + unsigned int duplex; + int speed; + u32 link; + + link = le32_to_cpu(sm_data->link); + + if (link & SLIC_GIG_LINKUP) { + if (link & SLIC_GIG_SPEED_1000) + speed = SPEED_1000; + else if (link & SLIC_GIG_SPEED_100) + speed = SPEED_100; + else + speed = SPEED_10; + + duplex = (link & SLIC_GIG_FULLDUPLEX) ? DUPLEX_FULL : + DUPLEX_HALF; + } else { + duplex = DUPLEX_UNKNOWN; + speed = SPEED_UNKNOWN; + } + slic_configure_link(sdev, speed, duplex); +} + +static void slic_handle_upr_irq(struct slic_device *sdev, u32 irqs) +{ + struct slic_upr *upr; + + /* remove upr that caused this irq (always the first entry in list) */ + upr = slic_dequeue_upr(sdev); + if (!upr) { + netdev_warn(sdev->netdev, "no upr found on list\n"); + return; + } + + if (upr->type == SLIC_UPR_LSTAT) { + if (unlikely(irqs & SLIC_ISR_UPCERR_MASK)) { + /* try again */ + slic_queue_upr(sdev, upr); + return; + } + slic_handle_link_irq(sdev); + } + kfree(upr); +} + +static int slic_handle_link_change(struct slic_device *sdev) +{ + return slic_new_upr(sdev, SLIC_UPR_LSTAT, sdev->shmem.link_paddr); +} + +static void slic_handle_err_irq(struct slic_device *sdev, u32 isr) +{ + struct slic_stats *stats = &sdev->stats; + + if (isr & SLIC_ISR_RMISS) + SLIC_INC_STATS_COUNTER(stats, rx_buff_miss); + if (isr & SLIC_ISR_XDROP) + SLIC_INC_STATS_COUNTER(stats, tx_dropped); + if (!(isr & (SLIC_ISR_RMISS | SLIC_ISR_XDROP))) + SLIC_INC_STATS_COUNTER(stats, irq_errs); +} + +static void slic_handle_irq(struct slic_device *sdev, u32 isr, + unsigned int todo, unsigned int *done) +{ + if (isr & SLIC_ISR_ERR) + slic_handle_err_irq(sdev, isr); + + if (isr & SLIC_ISR_LEVENT) + slic_handle_link_change(sdev); + + if (isr & SLIC_ISR_UPC_MASK) + slic_handle_upr_irq(sdev, isr); + + if (isr & SLIC_ISR_RCV) + slic_handle_receive(sdev, todo, done); + + if (isr & SLIC_ISR_CMD) + slic_xmit_complete(sdev); +} + +static int slic_poll(struct napi_struct *napi, int todo) +{ + struct slic_device *sdev = container_of(napi, struct slic_device, napi); + struct slic_shmem *sm = &sdev->shmem; + struct slic_shmem_data *sm_data = sm->shmem_data; + u32 isr = le32_to_cpu(sm_data->isr); + int done = 0; + + slic_handle_irq(sdev, isr, todo, &done); + + if (done < todo) { + napi_complete_done(napi, done); + /* reenable irqs */ + sm_data->isr = 0; + /* make sure sm_data->isr is cleard before irqs are reenabled */ + wmb(); + slic_write(sdev, SLIC_REG_ISR, 0); + slic_flush_write(sdev); + } + + return done; +} + +static irqreturn_t slic_irq(int irq, void *dev_id) +{ + struct slic_device *sdev = dev_id; + struct slic_shmem *sm = &sdev->shmem; + struct slic_shmem_data *sm_data = sm->shmem_data; + + slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_MASK); + slic_flush_write(sdev); + /* make sure sm_data->isr is read after ICR_INT_MASK is set */ + wmb(); + + if (!sm_data->isr) { + dma_rmb(); + /* spurious interrupt */ + slic_write(sdev, SLIC_REG_ISR, 0); + slic_flush_write(sdev); + return IRQ_NONE; + } + + napi_schedule_irqoff(&sdev->napi); + + return IRQ_HANDLED; +} + +static void slic_card_reset(struct slic_device *sdev) +{ + u16 cmd; + + slic_write(sdev, SLIC_REG_RESET, SLIC_RESET_MAGIC); + /* flush write by means of config space */ + pci_read_config_word(sdev->pdev, PCI_COMMAND, &cmd); + mdelay(1); +} + +static int slic_init_stat_queue(struct slic_device *sdev) +{ + const unsigned int DESC_ALIGN_MASK = SLIC_STATS_DESC_ALIGN - 1; + struct slic_stat_queue *stq = &sdev->stq; + struct slic_stat_desc *descs; + unsigned int misalign; + unsigned int offset; + dma_addr_t paddr; + size_t size; + int err; + int i; + + stq->len = SLIC_NUM_STAT_DESCS; + stq->active_array = 0; + stq->done_idx = 0; + + size = stq->len * sizeof(*descs) + DESC_ALIGN_MASK; + + for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) { + descs = dma_zalloc_coherent(&sdev->pdev->dev, size, &paddr, + GFP_KERNEL); + if (!descs) { + netdev_err(sdev->netdev, + "failed to allocate status descriptors\n"); + err = -ENOMEM; + goto free_descs; + } + /* ensure correct alignment */ + offset = 0; + misalign = paddr & DESC_ALIGN_MASK; + if (misalign) { + offset = SLIC_STATS_DESC_ALIGN - misalign; + descs += offset; + paddr += offset; + } + + slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) | + stq->len); + stq->descs[i] = descs; + stq->paddr[i] = paddr; + stq->addr_offset[i] = offset; + } + + stq->mem_size = size; + + return 0; + +free_descs: + while (i--) { + dma_free_coherent(&sdev->pdev->dev, stq->mem_size, + stq->descs[i] - stq->addr_offset[i], + stq->paddr[i] - stq->addr_offset[i]); + } + + return err; +} + +static void slic_free_stat_queue(struct slic_device *sdev) +{ + struct slic_stat_queue *stq = &sdev->stq; + int i; + + for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) { + dma_free_coherent(&sdev->pdev->dev, stq->mem_size, + stq->descs[i] - stq->addr_offset[i], + stq->paddr[i] - stq->addr_offset[i]); + } +} + +static int slic_init_tx_queue(struct slic_device *sdev) +{ + struct slic_tx_queue *txq = &sdev->txq; + struct slic_tx_buffer *buff; + struct slic_tx_desc *desc; + unsigned int i; + int err; + + txq->len = SLIC_NUM_TX_DESCS; + txq->put_idx = 0; + txq->done_idx = 0; + + txq->txbuffs = kcalloc(txq->len, sizeof(*buff), GFP_KERNEL); + if (!txq->txbuffs) + return -ENOMEM; + + txq->dma_pool = dma_pool_create("slic_pool", &sdev->pdev->dev, + sizeof(*desc), SLIC_TX_DESC_ALIGN, + 4096); + if (!txq->dma_pool) { + err = -ENOMEM; + netdev_err(sdev->netdev, "failed to create dma pool\n"); + goto free_buffs; + } + + for (i = 0; i < txq->len; i++) { + buff = &txq->txbuffs[i]; + desc = dma_pool_zalloc(txq->dma_pool, GFP_KERNEL, + &buff->desc_paddr); + if (!desc) { + netdev_err(sdev->netdev, + "failed to alloc pool chunk (%i)\n", i); + err = -ENOMEM; + goto free_descs; + } + + desc->hnd = cpu_to_le32((u32)(i + 1)); + desc->cmd = SLIC_CMD_XMT_REQ; + desc->flags = 0; + desc->type = cpu_to_le32(SLIC_CMD_TYPE_DUMB); + buff->desc = desc; + } + + return 0; + +free_descs: + while (i--) { + buff = &txq->txbuffs[i]; + dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr); + } + dma_pool_destroy(txq->dma_pool); + +free_buffs: + kfree(txq->txbuffs); + + return err; +} + +static void slic_free_tx_queue(struct slic_device *sdev) +{ + struct slic_tx_queue *txq = &sdev->txq; + struct slic_tx_buffer *buff; + unsigned int i; + + for (i = 0; i < txq->len; i++) { + buff = &txq->txbuffs[i]; + dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr); + if (!buff->skb) + continue; + + dma_unmap_single(&sdev->pdev->dev, + dma_unmap_addr(buff, map_addr), + dma_unmap_len(buff, map_len), DMA_TO_DEVICE); + consume_skb(buff->skb); + } + dma_pool_destroy(txq->dma_pool); + + kfree(txq->txbuffs); +} + +static int slic_init_rx_queue(struct slic_device *sdev) +{ + struct slic_rx_queue *rxq = &sdev->rxq; + struct slic_rx_buffer *buff; + + rxq->len = SLIC_NUM_RX_LES; + rxq->done_idx = 0; + rxq->put_idx = 0; + + buff = kcalloc(rxq->len, sizeof(*buff), GFP_KERNEL); + if (!buff) + return -ENOMEM; + + rxq->rxbuffs = buff; + slic_refill_rx_queue(sdev, GFP_KERNEL); + + return 0; +} + +static void slic_free_rx_queue(struct slic_device *sdev) +{ + struct slic_rx_queue *rxq = &sdev->rxq; + struct slic_rx_buffer *buff; + unsigned int i; + + /* free rx buffers */ + for (i = 0; i < rxq->len; i++) { + buff = &rxq->rxbuffs[i]; + + if (!buff->skb) + continue; + + dma_unmap_single(&sdev->pdev->dev, + dma_unmap_addr(buff, map_addr), + dma_unmap_len(buff, map_len), + DMA_FROM_DEVICE); + consume_skb(buff->skb); + } + kfree(rxq->rxbuffs); +} + +static void slic_set_link_autoneg(struct slic_device *sdev) +{ + unsigned int subid = sdev->pdev->subsystem_device; + u32 val; + + if (sdev->is_fiber) { + /* We've got a fiber gigabit interface, and register 4 is + * different in fiber mode than in copper mode. + */ + /* advertise FD only @1000 Mb */ + val = MII_ADVERTISE << 16 | ADVERTISE_1000XFULL | + ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; + /* enable PAUSE frames */ + slic_write(sdev, SLIC_REG_WPHY, val); + /* reset phy, enable auto-neg */ + val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE | + BMCR_ANRESTART; + slic_write(sdev, SLIC_REG_WPHY, val); + } else { /* copper gigabit */ + /* We've got a copper gigabit interface, and register 4 is + * different in copper mode than in fiber mode. + */ + /* advertise 10/100 Mb modes */ + val = MII_ADVERTISE << 16 | ADVERTISE_100FULL | + ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF; + /* enable PAUSE frames */ + val |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + /* required by the Cicada PHY */ + val |= ADVERTISE_CSMA; + slic_write(sdev, SLIC_REG_WPHY, val); + + /* advertise FD only @1000 Mb */ + val = MII_CTRL1000 << 16 | ADVERTISE_1000FULL; + slic_write(sdev, SLIC_REG_WPHY, val); + + if (subid != PCI_SUBDEVICE_ID_ALACRITECH_CICADA) { + /* if a Marvell PHY enable auto crossover */ + val = SLIC_MIICR_REG_16 | SLIC_MRV_REG16_XOVERON; + slic_write(sdev, SLIC_REG_WPHY, val); + + /* reset phy, enable auto-neg */ + val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE | + BMCR_ANRESTART; + slic_write(sdev, SLIC_REG_WPHY, val); + } else { + /* enable and restart auto-neg (don't reset) */ + val = MII_BMCR << 16 | BMCR_ANENABLE | BMCR_ANRESTART; + slic_write(sdev, SLIC_REG_WPHY, val); + } + } +} + +static void slic_set_mac_address(struct slic_device *sdev) +{ + u8 *addr = sdev->netdev->dev_addr; + u32 val; + + val = addr[5] | addr[4] << 8 | addr[3] << 16 | addr[2] << 24; + + slic_write(sdev, SLIC_REG_WRADDRAL, val); + slic_write(sdev, SLIC_REG_WRADDRBL, val); + + val = addr[0] << 8 | addr[1]; + + slic_write(sdev, SLIC_REG_WRADDRAH, val); + slic_write(sdev, SLIC_REG_WRADDRBH, val); + slic_flush_write(sdev); +} + +static u32 slic_read_dword_from_firmware(const struct firmware *fw, int *offset) +{ + int idx = *offset; + __le32 val; + + memcpy(&val, fw->data + *offset, sizeof(val)); + idx += 4; + *offset = idx; + + return le32_to_cpu(val); +} + +MODULE_FIRMWARE(SLIC_RCV_FIRMWARE_MOJAVE); +MODULE_FIRMWARE(SLIC_RCV_FIRMWARE_OASIS); + +static int slic_load_rcvseq_firmware(struct slic_device *sdev) +{ + const struct firmware *fw; + const char *file; + u32 codelen; + int idx = 0; + u32 instr; + u32 addr; + int err; + + file = (sdev->model == SLIC_MODEL_OASIS) ? SLIC_RCV_FIRMWARE_OASIS : + SLIC_RCV_FIRMWARE_MOJAVE; + err = request_firmware(&fw, file, &sdev->pdev->dev); + if (err) { + dev_err(&sdev->pdev->dev, + "failed to load receive sequencer firmware %s\n", file); + return err; + } + /* Do an initial sanity check concerning firmware size now. A further + * check follows below. + */ + if (fw->size < SLIC_FIRMWARE_MIN_SIZE) { + dev_err(&sdev->pdev->dev, + "invalid firmware size %zu (min %u expected)\n", + fw->size, SLIC_FIRMWARE_MIN_SIZE); + err = -EINVAL; + goto release; + } + + codelen = slic_read_dword_from_firmware(fw, &idx); + + /* do another sanity check against firmware size */ + if ((codelen + 4) > fw->size) { + dev_err(&sdev->pdev->dev, + "invalid rcv-sequencer firmware size %zu\n", fw->size); + err = -EINVAL; + goto release; + } + + /* download sequencer code to card */ + slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_BEGIN); + for (addr = 0; addr < codelen; addr++) { + __le32 val; + /* write out instruction address */ + slic_write(sdev, SLIC_REG_RCV_WCS, addr); + + instr = slic_read_dword_from_firmware(fw, &idx); + /* write out the instruction data low addr */ + slic_write(sdev, SLIC_REG_RCV_WCS, instr); + + val = (__le32)fw->data[idx]; + instr = le32_to_cpu(val); + idx++; + /* write out the instruction data high addr */ + slic_write(sdev, SLIC_REG_RCV_WCS, instr); + } + /* finish download */ + slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_FINISH); + slic_flush_write(sdev); +release: + release_firmware(fw); + + return err; +} + +MODULE_FIRMWARE(SLIC_FIRMWARE_MOJAVE); +MODULE_FIRMWARE(SLIC_FIRMWARE_OASIS); + +static int slic_load_firmware(struct slic_device *sdev) +{ + u32 sectstart[SLIC_FIRMWARE_MAX_SECTIONS]; + u32 sectsize[SLIC_FIRMWARE_MAX_SECTIONS]; + const struct firmware *fw; + unsigned int datalen; + const char *file; + int code_start; + unsigned int i; + u32 numsects; + int idx = 0; + u32 sect; + u32 instr; + u32 addr; + u32 base; + int err; + + file = (sdev->model == SLIC_MODEL_OASIS) ? SLIC_FIRMWARE_OASIS : + SLIC_FIRMWARE_MOJAVE; + err = request_firmware(&fw, file, &sdev->pdev->dev); + if (err) { + dev_err(&sdev->pdev->dev, "failed to load firmware %s\n", file); + return err; + } + /* Do an initial sanity check concerning firmware size now. A further + * check follows below. + */ + if (fw->size < SLIC_FIRMWARE_MIN_SIZE) { + dev_err(&sdev->pdev->dev, + "invalid firmware size %zu (min is %u)\n", fw->size, + SLIC_FIRMWARE_MIN_SIZE); + err = -EINVAL; + goto release; + } + + numsects = slic_read_dword_from_firmware(fw, &idx); + if (numsects == 0 || numsects > SLIC_FIRMWARE_MAX_SECTIONS) { + dev_err(&sdev->pdev->dev, + "invalid number of sections in firmware: %u", numsects); + err = -EINVAL; + goto release; + } + + datalen = numsects * 8 + 4; + for (i = 0; i < numsects; i++) { + sectsize[i] = slic_read_dword_from_firmware(fw, &idx); + datalen += sectsize[i]; + } + + /* do another sanity check against firmware size */ + if (datalen > fw->size) { + dev_err(&sdev->pdev->dev, + "invalid firmware size %zu (expected >= %u)\n", + fw->size, datalen); + err = -EINVAL; + goto release; + } + /* get sections */ + for (i = 0; i < numsects; i++) + sectstart[i] = slic_read_dword_from_firmware(fw, &idx); + + code_start = idx; + instr = slic_read_dword_from_firmware(fw, &idx); + + for (sect = 0; sect < numsects; sect++) { + unsigned int ssize = sectsize[sect] >> 3; + + base = sectstart[sect]; + + for (addr = 0; addr < ssize; addr++) { + /* write out instruction address */ + slic_write(sdev, SLIC_REG_WCS, base + addr); + /* write out instruction to low addr */ + slic_write(sdev, SLIC_REG_WCS, instr); + instr = slic_read_dword_from_firmware(fw, &idx); + /* write out instruction to high addr */ + slic_write(sdev, SLIC_REG_WCS, instr); + instr = slic_read_dword_from_firmware(fw, &idx); + } + } + + idx = code_start; + + for (sect = 0; sect < numsects; sect++) { + unsigned int ssize = sectsize[sect] >> 3; + + instr = slic_read_dword_from_firmware(fw, &idx); + base = sectstart[sect]; + if (base < 0x8000) + continue; + + for (addr = 0; addr < ssize; addr++) { + /* write out instruction address */ + slic_write(sdev, SLIC_REG_WCS, + SLIC_WCS_COMPARE | (base + addr)); + /* write out instruction to low addr */ + slic_write(sdev, SLIC_REG_WCS, instr); + instr = slic_read_dword_from_firmware(fw, &idx); + /* write out instruction to high addr */ + slic_write(sdev, SLIC_REG_WCS, instr); + instr = slic_read_dword_from_firmware(fw, &idx); + } + } + slic_flush_write(sdev); + mdelay(10); + /* everything OK, kick off the card */ + slic_write(sdev, SLIC_REG_WCS, SLIC_WCS_START); + slic_flush_write(sdev); + /* wait long enough for ucode to init card and reach the mainloop */ + mdelay(20); +release: + release_firmware(fw); + + return err; +} + +static int slic_init_shmem(struct slic_device *sdev) +{ + struct slic_shmem *sm = &sdev->shmem; + struct slic_shmem_data *sm_data; + dma_addr_t paddr; + + sm_data = dma_zalloc_coherent(&sdev->pdev->dev, sizeof(*sm_data), + &paddr, GFP_KERNEL); + if (!sm_data) { + dev_err(&sdev->pdev->dev, "failed to allocate shared memory\n"); + return -ENOMEM; + } + + sm->shmem_data = sm_data; + sm->isr_paddr = paddr; + sm->link_paddr = paddr + offsetof(struct slic_shmem_data, link); + + return 0; +} + +static void slic_free_shmem(struct slic_device *sdev) +{ + struct slic_shmem *sm = &sdev->shmem; + struct slic_shmem_data *sm_data = sm->shmem_data; + + dma_free_coherent(&sdev->pdev->dev, sizeof(*sm_data), sm_data, + sm->isr_paddr); +} + +static int slic_init_iface(struct slic_device *sdev) +{ + struct slic_shmem *sm = &sdev->shmem; + int err; + + sdev->upr_list.pending = false; + + err = slic_init_shmem(sdev); + if (err) { + netdev_err(sdev->netdev, "failed to init shared memory\n"); + return err; + } + + err = slic_load_firmware(sdev); + if (err) { + netdev_err(sdev->netdev, "failed to load firmware\n"); + goto free_sm; + } + + err = slic_load_rcvseq_firmware(sdev); + if (err) { + netdev_err(sdev->netdev, + "failed to load firmware for receive sequencer\n"); + goto free_sm; + } + + slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF); + slic_flush_write(sdev); + mdelay(1); + + err = slic_init_rx_queue(sdev); + if (err) { + netdev_err(sdev->netdev, "failed to init rx queue: %u\n", err); + goto free_sm; + } + + err = slic_init_tx_queue(sdev); + if (err) { + netdev_err(sdev->netdev, "failed to init tx queue: %u\n", err); + goto free_rxq; + } + + err = slic_init_stat_queue(sdev); + if (err) { + netdev_err(sdev->netdev, "failed to init status queue: %u\n", + err); + goto free_txq; + } + + slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr)); + napi_enable(&sdev->napi); + /* disable irq mitigation */ + slic_write(sdev, SLIC_REG_INTAGG, 0); + slic_write(sdev, SLIC_REG_ISR, 0); + slic_flush_write(sdev); + + slic_set_mac_address(sdev); + + spin_lock_bh(&sdev->link_lock); + sdev->duplex = DUPLEX_UNKNOWN; + sdev->speed = SPEED_UNKNOWN; + spin_unlock_bh(&sdev->link_lock); + + slic_set_link_autoneg(sdev); + + err = request_irq(sdev->pdev->irq, slic_irq, IRQF_SHARED, DRV_NAME, + sdev); + if (err) { + netdev_err(sdev->netdev, "failed to request irq: %u\n", err); + goto disable_napi; + } + + slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_ON); + slic_flush_write(sdev); + /* request initial link status */ + err = slic_handle_link_change(sdev); + if (err) + netdev_warn(sdev->netdev, + "failed to set initial link state: %u\n", err); + return 0; + +disable_napi: + napi_disable(&sdev->napi); + slic_free_stat_queue(sdev); +free_txq: + slic_free_tx_queue(sdev); +free_rxq: + slic_free_rx_queue(sdev); +free_sm: + slic_free_shmem(sdev); + slic_card_reset(sdev); + + return err; +} + +static int slic_open(struct net_device *dev) +{ + struct slic_device *sdev = netdev_priv(dev); + int err; + + netif_carrier_off(dev); + + err = slic_init_iface(sdev); + if (err) { + netdev_err(dev, "failed to initialize interface: %i\n", err); + return err; + } + + netif_start_queue(dev); + + return 0; +} + +static int slic_close(struct net_device *dev) +{ + struct slic_device *sdev = netdev_priv(dev); + u32 val; + + netif_stop_queue(dev); + + /* stop irq handling */ + napi_disable(&sdev->napi); + slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF); + slic_write(sdev, SLIC_REG_ISR, 0); + slic_flush_write(sdev); + + free_irq(sdev->pdev->irq, sdev); + /* turn off RCV and XMT and power down PHY */ + val = SLIC_GXCR_RESET | SLIC_GXCR_PAUSEEN; + slic_write(sdev, SLIC_REG_WXCFG, val); + + val = SLIC_GRCR_RESET | SLIC_GRCR_CTLEN | SLIC_GRCR_ADDRAEN | + SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT; + slic_write(sdev, SLIC_REG_WRCFG, val); + + val = MII_BMCR << 16 | BMCR_PDOWN; + slic_write(sdev, SLIC_REG_WPHY, val); + slic_flush_write(sdev); + + slic_clear_upr_list(&sdev->upr_list); + slic_write(sdev, SLIC_REG_QUIESCE, 0); + + slic_free_stat_queue(sdev); + slic_free_tx_queue(sdev); + slic_free_rx_queue(sdev); + slic_free_shmem(sdev); + + slic_card_reset(sdev); + netif_carrier_off(dev); + + return 0; +} + +static netdev_tx_t slic_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct slic_device *sdev = netdev_priv(dev); + struct slic_tx_queue *txq = &sdev->txq; + struct slic_tx_buffer *buff; + struct slic_tx_desc *desc; + dma_addr_t paddr; + u32 cbar_val; + u32 maplen; + + if (unlikely(slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)) { + netdev_err(dev, "BUG! not enough tx LEs left: %u\n", + slic_get_free_tx_descs(txq)); + return NETDEV_TX_BUSY; + } + + maplen = skb_headlen(skb); + paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen, + DMA_TO_DEVICE); + if (dma_mapping_error(&sdev->pdev->dev, paddr)) { + netdev_err(dev, "failed to map tx buffer\n"); + goto drop_skb; + } + + buff = &txq->txbuffs[txq->put_idx]; + buff->skb = skb; + dma_unmap_addr_set(buff, map_addr, paddr); + dma_unmap_len_set(buff, map_len, maplen); + + desc = buff->desc; + desc->totlen = cpu_to_le32(maplen); + desc->paddrl = cpu_to_le32(lower_32_bits(paddr)); + desc->paddrh = cpu_to_le32(upper_32_bits(paddr)); + desc->len = cpu_to_le32(maplen); + + txq->put_idx = slic_next_queue_idx(txq->put_idx, txq->len); + + cbar_val = lower_32_bits(buff->desc_paddr) | 1; + /* complete writes to RAM and DMA before hardware is informed */ + wmb(); + + slic_write(sdev, SLIC_REG_CBAR, cbar_val); + + if (slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS) + netif_stop_queue(dev); + /* make sure writes to io-memory cant leak out of tx queue lock */ + mmiowb(); + + return NETDEV_TX_OK; +drop_skb: + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; +} + +static struct rtnl_link_stats64 *slic_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *lst) +{ + struct slic_device *sdev = netdev_priv(dev); + struct slic_stats *stats = &sdev->stats; + + SLIC_GET_STATS_COUNTER(lst->rx_packets, stats, rx_packets); + SLIC_GET_STATS_COUNTER(lst->tx_packets, stats, tx_packets); + SLIC_GET_STATS_COUNTER(lst->rx_bytes, stats, rx_bytes); + SLIC_GET_STATS_COUNTER(lst->tx_bytes, stats, tx_bytes); + SLIC_GET_STATS_COUNTER(lst->rx_errors, stats, rx_errors); + SLIC_GET_STATS_COUNTER(lst->rx_dropped, stats, rx_buff_miss); + SLIC_GET_STATS_COUNTER(lst->tx_dropped, stats, tx_dropped); + SLIC_GET_STATS_COUNTER(lst->multicast, stats, rx_mcasts); + SLIC_GET_STATS_COUNTER(lst->rx_over_errors, stats, rx_buffoflow); + SLIC_GET_STATS_COUNTER(lst->rx_crc_errors, stats, rx_crc); + SLIC_GET_STATS_COUNTER(lst->rx_fifo_errors, stats, rx_oflow802); + SLIC_GET_STATS_COUNTER(lst->tx_carrier_errors, stats, tx_carrier); + + return lst; +} + +static int slic_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(slic_stats_strings); + default: + return -EOPNOTSUPP; + } +} + +static void slic_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *eth_stats, u64 *data) +{ + struct slic_device *sdev = netdev_priv(dev); + struct slic_stats *stats = &sdev->stats; + + SLIC_GET_STATS_COUNTER(data[0], stats, rx_packets); + SLIC_GET_STATS_COUNTER(data[1], stats, rx_bytes); + SLIC_GET_STATS_COUNTER(data[2], stats, rx_mcasts); + SLIC_GET_STATS_COUNTER(data[3], stats, rx_errors); + SLIC_GET_STATS_COUNTER(data[4], stats, rx_buff_miss); + SLIC_GET_STATS_COUNTER(data[5], stats, rx_tpcsum); + SLIC_GET_STATS_COUNTER(data[6], stats, rx_tpoflow); + SLIC_GET_STATS_COUNTER(data[7], stats, rx_tphlen); + SLIC_GET_STATS_COUNTER(data[8], stats, rx_ipcsum); + SLIC_GET_STATS_COUNTER(data[9], stats, rx_iplen); + SLIC_GET_STATS_COUNTER(data[10], stats, rx_iphlen); + SLIC_GET_STATS_COUNTER(data[11], stats, rx_early); + SLIC_GET_STATS_COUNTER(data[12], stats, rx_buffoflow); + SLIC_GET_STATS_COUNTER(data[13], stats, rx_lcode); + SLIC_GET_STATS_COUNTER(data[14], stats, rx_drbl); + SLIC_GET_STATS_COUNTER(data[15], stats, rx_crc); + SLIC_GET_STATS_COUNTER(data[16], stats, rx_oflow802); + SLIC_GET_STATS_COUNTER(data[17], stats, rx_uflow802); + SLIC_GET_STATS_COUNTER(data[18], stats, tx_packets); + SLIC_GET_STATS_COUNTER(data[19], stats, tx_bytes); + SLIC_GET_STATS_COUNTER(data[20], stats, tx_carrier); + SLIC_GET_STATS_COUNTER(data[21], stats, tx_dropped); + SLIC_GET_STATS_COUNTER(data[22], stats, irq_errs); +} + +static void slic_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + if (stringset == ETH_SS_STATS) { + memcpy(data, slic_stats_strings, sizeof(slic_stats_strings)); + data += sizeof(slic_stats_strings); + } +} + +static void slic_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct slic_device *sdev = netdev_priv(dev); + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(sdev->pdev), sizeof(info->bus_info)); +} + +static const struct ethtool_ops slic_ethtool_ops = { + .get_drvinfo = slic_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = slic_get_strings, + .get_ethtool_stats = slic_get_ethtool_stats, + .get_sset_count = slic_get_sset_count, +}; + +static const struct net_device_ops slic_netdev_ops = { + .ndo_open = slic_open, + .ndo_stop = slic_close, + .ndo_start_xmit = slic_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_get_stats64 = slic_get_stats, + .ndo_set_rx_mode = slic_set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +static u16 slic_eeprom_csum(unsigned char *eeprom, unsigned int len) +{ + unsigned char *ptr = eeprom; + u32 csum = 0; + __le16 data; + + while (len > 1) { + memcpy(&data, ptr, sizeof(data)); + csum += le16_to_cpu(data); + ptr += 2; + len -= 2; + } + if (len > 0) + csum += *(u8 *)ptr; + while (csum >> 16) + csum = (csum & 0xFFFF) + ((csum >> 16) & 0xFFFF); + return ~csum; +} + +/* check eeprom size, magic and checksum */ +static bool slic_eeprom_valid(unsigned char *eeprom, unsigned int size) +{ + const unsigned int MAX_SIZE = 128; + const unsigned int MIN_SIZE = 98; + __le16 magic; + __le16 csum; + + if (size < MIN_SIZE || size > MAX_SIZE) + return false; + memcpy(&magic, eeprom, sizeof(magic)); + if (le16_to_cpu(magic) != SLIC_EEPROM_MAGIC) + return false; + /* cut checksum bytes */ + size -= 2; + memcpy(&csum, eeprom + size, sizeof(csum)); + + return (le16_to_cpu(csum) == slic_eeprom_csum(eeprom, size)); +} + +static int slic_read_eeprom(struct slic_device *sdev) +{ + unsigned int devfn = PCI_FUNC(sdev->pdev->devfn); + struct slic_shmem *sm = &sdev->shmem; + struct slic_shmem_data *sm_data = sm->shmem_data; + const unsigned int MAX_LOOPS = 5000; + unsigned int codesize; + unsigned char *eeprom; + struct slic_upr *upr; + unsigned int i = 0; + dma_addr_t paddr; + int err = 0; + u8 *mac[2]; + + eeprom = dma_zalloc_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE, + &paddr, GFP_KERNEL); + if (!eeprom) + return -ENOMEM; + + slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF); + /* setup ISP temporarily */ + slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr)); + + err = slic_new_upr(sdev, SLIC_UPR_CONFIG, paddr); + if (!err) { + for (i = 0; i < MAX_LOOPS; i++) { + if (le32_to_cpu(sm_data->isr) & SLIC_ISR_UPC) + break; + mdelay(1); + } + if (i == MAX_LOOPS) { + dev_err(&sdev->pdev->dev, + "timed out while waiting for eeprom data\n"); + err = -ETIMEDOUT; + } + upr = slic_dequeue_upr(sdev); + kfree(upr); + } + + slic_write(sdev, SLIC_REG_ISP, 0); + slic_write(sdev, SLIC_REG_ISR, 0); + slic_flush_write(sdev); + + if (err) + goto free_eeprom; + + if (sdev->model == SLIC_MODEL_OASIS) { + struct slic_oasis_eeprom *oee; + + oee = (struct slic_oasis_eeprom *)eeprom; + mac[0] = oee->mac; + mac[1] = oee->mac2; + codesize = le16_to_cpu(oee->eeprom_code_size); + } else { + struct slic_mojave_eeprom *mee; + + mee = (struct slic_mojave_eeprom *)eeprom; + mac[0] = mee->mac; + mac[1] = mee->mac2; + codesize = le16_to_cpu(mee->eeprom_code_size); + } + + if (!slic_eeprom_valid(eeprom, codesize)) { + dev_err(&sdev->pdev->dev, "invalid checksum in eeprom\n"); + err = -EINVAL; + goto free_eeprom; + } + /* set mac address */ + ether_addr_copy(sdev->netdev->dev_addr, mac[devfn]); +free_eeprom: + dma_free_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE, eeprom, paddr); + + return err; +} + +static int slic_init(struct slic_device *sdev) +{ + int err; + + spin_lock_init(&sdev->upper_lock); + spin_lock_init(&sdev->link_lock); + INIT_LIST_HEAD(&sdev->upr_list.list); + spin_lock_init(&sdev->upr_list.lock); + u64_stats_init(&sdev->stats.syncp); + + slic_card_reset(sdev); + + err = slic_load_firmware(sdev); + if (err) { + dev_err(&sdev->pdev->dev, "failed to load firmware\n"); + return err; + } + + /* we need the shared memory to read EEPROM so set it up temporarily */ + err = slic_init_shmem(sdev); + if (err) { + dev_err(&sdev->pdev->dev, "failed to init shared memory\n"); + return err; + } + + err = slic_read_eeprom(sdev); + if (err) { + dev_err(&sdev->pdev->dev, "failed to read eeprom\n"); + goto free_sm; + } + + slic_card_reset(sdev); + slic_free_shmem(sdev); + + return 0; +free_sm: + slic_free_shmem(sdev); + + return err; +} + +static bool slic_is_fiber(unsigned short subdev) +{ + switch (subdev) { + /* Mojave */ + case PCI_SUBDEVICE_ID_ALACRITECH_1000X1F: /* fallthrough */ + case PCI_SUBDEVICE_ID_ALACRITECH_SES1001F: /* fallthrough */ + /* Oasis */ + case PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XF: /* fallthrough */ + case PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XF: /* fallthrough */ + case PCI_SUBDEVICE_ID_ALACRITECH_SEN2104EF: /* fallthrough */ + case PCI_SUBDEVICE_ID_ALACRITECH_SEN2102EF: /* fallthrough */ + return true; + } + return false; +} + +static void slic_configure_pci(struct pci_dev *pdev) +{ + u16 old; + u16 cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &old); + + cmd = old | PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + if (old != cmd) + pci_write_config_word(pdev, PCI_COMMAND, cmd); +} + +static int slic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct slic_device *sdev; + struct net_device *dev; + int err; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "failed to enable PCI device\n"); + return err; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + slic_configure_pci(pdev); + + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, "failed to setup DMA\n"); + goto disable; + } + + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "failed to obtain PCI regions\n"); + goto disable; + } + + dev = alloc_etherdev(sizeof(*sdev)); + if (!dev) { + dev_err(&pdev->dev, "failed to alloc ethernet device\n"); + err = -ENOMEM; + goto free_regions; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + pci_set_drvdata(pdev, dev); + dev->irq = pdev->irq; + dev->netdev_ops = &slic_netdev_ops; + dev->hw_features = NETIF_F_RXCSUM; + dev->features |= dev->hw_features; + + dev->ethtool_ops = &slic_ethtool_ops; + + sdev = netdev_priv(dev); + sdev->model = (pdev->device == PCI_DEVICE_ID_ALACRITECH_OASIS) ? + SLIC_MODEL_OASIS : SLIC_MODEL_MOJAVE; + sdev->is_fiber = slic_is_fiber(pdev->subsystem_device); + sdev->pdev = pdev; + sdev->netdev = dev; + sdev->regs = ioremap_nocache(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (!sdev->regs) { + dev_err(&pdev->dev, "failed to map registers\n"); + err = -ENOMEM; + goto free_netdev; + } + + err = slic_init(sdev); + if (err) { + dev_err(&pdev->dev, "failed to initialize driver\n"); + goto unmap; + } + + netif_napi_add(dev, &sdev->napi, slic_poll, SLIC_NAPI_WEIGHT); + netif_carrier_off(dev); + + err = register_netdev(dev); + if (err) { + dev_err(&pdev->dev, "failed to register net device: %i\n", err); + goto unmap; + } + + return 0; + +unmap: + iounmap(sdev->regs); +free_netdev: + free_netdev(dev); +free_regions: + pci_release_regions(pdev); +disable: + pci_disable_device(pdev); + + return err; +} + +static void slic_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct slic_device *sdev = netdev_priv(dev); + + unregister_netdev(dev); + iounmap(sdev->regs); + free_netdev(dev); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct pci_driver slic_driver = { + .name = DRV_NAME, + .id_table = slic_id_tbl, + .probe = slic_probe, + .remove = slic_remove, +}; + +static int __init slic_init_module(void) +{ + return pci_register_driver(&slic_driver); +} + +static void __exit slic_cleanup_module(void) +{ + pci_unregister_driver(&slic_driver); +} + +module_init(slic_init_module); +module_exit(slic_cleanup_module); + +MODULE_DESCRIPTION("Alacritech non-accelerated SLIC driver"); +MODULE_AUTHOR("Lino Sanfilippo "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); -- cgit v1.1 From b9567027385a7abb33ae54ffa189be9c6c9cf730 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Mon, 5 Dec 2016 23:07:17 +0100 Subject: MAINTAINERS: add entry for slicoss ethernet driver Add myself as maintainer for the slicoss ethernet driver. Signed-off-by: Lino Sanfilippo Signed-off-by: David S. Miller --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c04f619..331f6af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -577,6 +577,11 @@ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained F: drivers/media/usb/airspy/ +ALACRITECH GIGABIT ETHERNET DRIVER +M: Lino Sanfilippo +S: Maintained +F: drivers/net/ethernet/alacritech/* + ALCATEL SPEEDTOUCH USB DRIVER M: Duncan Sands L: linux-usb@vger.kernel.org -- cgit v1.1 From 4ac4b5a623f808e64d4d17b14bc23e934eaed008 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 5 Dec 2016 17:30:25 -0500 Subject: net: dsa: mv88e6xxx: add helper to disable ports Before resetting a switch, the ports should be set to the Disabled state and the transmit queues should be drained. Add an helper to explicit that. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 7a6c587..7cbaff7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2356,17 +2356,11 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) mutex_unlock(&chip->reg_lock); } -static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) +static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip) { - bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE); - u16 is_reset = (ppu_active ? 0x8800 : 0xc800); - struct gpio_desc *gpiod = chip->reset; - unsigned long timeout; - u16 reg; - int err; - int i; + int i, err; - /* Set all ports to the disabled state. */ + /* Set all ports to the Disabled state */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { err = mv88e6xxx_port_set_state(chip, i, PORT_CONTROL_STATE_DISABLED); @@ -2374,9 +2368,27 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) return err; } - /* Wait for transmit queues to drain. */ + /* Wait for transmit queues to drain, + * i.e. 2ms for a maximum frame to be transmitted at 10 Mbps. + */ usleep_range(2000, 4000); + return 0; +} + +static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) +{ + bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE); + u16 is_reset = (ppu_active ? 0x8800 : 0xc800); + struct gpio_desc *gpiod = chip->reset; + unsigned long timeout; + u16 reg; + int err; + + err = mv88e6xxx_disable_ports(chip); + if (err) + return err; + /* If there is a gpio connected to the reset pin, toggle it */ if (gpiod) { gpiod_set_value_cansleep(gpiod, 1); -- cgit v1.1 From 309eca6db96ad69470220fa12c0bdc1ee64da8af Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 5 Dec 2016 17:30:26 -0500 Subject: net: dsa: mv88e6xxx: add helper to hardware reset Add an helper to toggle the eventual GPIO connected to the reset pin. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 7cbaff7..18605b9 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2356,6 +2356,19 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) mutex_unlock(&chip->reg_lock); } +static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) +{ + struct gpio_desc *gpiod = chip->reset; + + /* If there is a GPIO connected to the reset pin, toggle it */ + if (gpiod) { + gpiod_set_value_cansleep(gpiod, 1); + usleep_range(10000, 20000); + gpiod_set_value_cansleep(gpiod, 0); + usleep_range(10000, 20000); + } +} + static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip) { int i, err; @@ -2380,7 +2393,6 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) { bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE); u16 is_reset = (ppu_active ? 0x8800 : 0xc800); - struct gpio_desc *gpiod = chip->reset; unsigned long timeout; u16 reg; int err; @@ -2389,13 +2401,7 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) if (err) return err; - /* If there is a gpio connected to the reset pin, toggle it */ - if (gpiod) { - gpiod_set_value_cansleep(gpiod, 1); - usleep_range(10000, 20000); - gpiod_set_value_cansleep(gpiod, 0); - usleep_range(10000, 20000); - } + mv88e6xxx_hardware_reset(chip); /* Reset the switch. Keep the PPU active if requested. The PPU * needs to be active to support indirect phy register access -- cgit v1.1 From 17e708baf7f24192cb1c7c8dd6b32d1941dfb0e8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 5 Dec 2016 17:30:27 -0500 Subject: net: dsa: mv88e6xxx: add a soft reset operation Marvell chips have different way to issue a software reset. Old chips (such as 88E6060) have a reset bit in an ATU control register. Newer chips moved this bit in a Global control register. Chips with controllable PPU should reset the PPU when resetting the switch. Add a new reset operation to implement these differences and introduce a mv88e6xxx_software_reset() helper to wrap it conveniently. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 72 ++++++++++---------- drivers/net/dsa/mv88e6xxx/global1.c | 121 ++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 4 ++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 15 +++-- 4 files changed, 172 insertions(+), 40 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 18605b9..953c677 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -545,7 +545,8 @@ static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip) return err; usleep_range(1000, 2000); - if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING) + val &= GLOBAL_STATUS_PPU_STATE_MASK; + if (val != GLOBAL_STATUS_PPU_STATE_POLLING) return 0; } @@ -572,7 +573,8 @@ static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip) return err; usleep_range(1000, 2000); - if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING) + val &= GLOBAL_STATUS_PPU_STATE_MASK; + if (val == GLOBAL_STATUS_PPU_STATE_POLLING) return 0; } @@ -2356,6 +2358,14 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) mutex_unlock(&chip->reg_lock); } +static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip) +{ + if (chip->info->ops->reset) + return chip->info->ops->reset(chip); + + return 0; +} + static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) { struct gpio_desc *gpiod = chip->reset; @@ -2391,10 +2401,6 @@ static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip) static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) { - bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE); - u16 is_reset = (ppu_active ? 0x8800 : 0xc800); - unsigned long timeout; - u16 reg; int err; err = mv88e6xxx_disable_ports(chip); @@ -2403,34 +2409,7 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) mv88e6xxx_hardware_reset(chip); - /* Reset the switch. Keep the PPU active if requested. The PPU - * needs to be active to support indirect phy register access - * through global registers 0x18 and 0x19. - */ - if (ppu_active) - err = mv88e6xxx_g1_write(chip, 0x04, 0xc000); - else - err = mv88e6xxx_g1_write(chip, 0x04, 0xc400); - if (err) - return err; - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - err = mv88e6xxx_g1_read(chip, 0x00, ®); - if (err) - return err; - - if ((reg & is_reset) == is_reset) - break; - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - err = -ETIMEDOUT; - else - err = 0; - - return err; + return mv88e6xxx_software_reset(chip); } static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip) @@ -3244,6 +3223,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6185_g1_reset, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -3261,6 +3241,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6185_g1_reset, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -3285,6 +3266,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -3304,6 +3286,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -3328,6 +3311,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6185_g1_reset, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3352,6 +3336,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3369,6 +3354,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3394,6 +3380,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3421,6 +3408,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -3446,6 +3434,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3473,6 +3462,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3493,6 +3483,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6185_g1_reset, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3517,6 +3508,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3541,6 +3533,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3565,6 +3558,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3592,6 +3586,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3616,6 +3611,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3642,6 +3638,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3667,6 +3664,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .stats_get_stats = mv88e6320_stats_get_stats, .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3692,6 +3690,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3717,6 +3716,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3744,6 +3744,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -3770,6 +3771,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3796,6 +3798,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static const struct mv88e6xxx_ops mv88e6391_ops = { @@ -3820,6 +3823,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, .g1_set_egress_port = mv88e6390_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, }; static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 44136ee..c868eb0 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -33,6 +33,127 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask); } +/* Offset 0x00: Switch Global Status Register */ + +static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip) +{ + u16 state; + int i, err; + + for (i = 0; i < 16; ++i) { + err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state); + if (err) + return err; + + /* Check the value of the PPUState bits 15:14 */ + state &= GLOBAL_STATUS_PPU_STATE_MASK; + if (state == GLOBAL_STATUS_PPU_STATE_POLLING) + return 0; + + usleep_range(1000, 2000); + } + + return -ETIMEDOUT; +} + +static int mv88e6352_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip) +{ + u16 state; + int i, err; + + for (i = 0; i < 16; ++i) { + err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state); + if (err) + return err; + + /* Check the value of the PPUState (or InitState) bit 15 */ + if (state & GLOBAL_STATUS_PPU_STATE) + return 0; + + usleep_range(1000, 2000); + } + + return -ETIMEDOUT; +} + +static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip) +{ + const unsigned long timeout = jiffies + 1 * HZ; + u16 val; + int err; + + /* Wait up to 1 second for the switch to be ready. The InitReady bit 11 + * is set to a one when all units inside the device (ATU, VTU, etc.) + * have finished their initialization and are ready to accept frames. + */ + while (time_before(jiffies, timeout)) { + err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); + if (err) + return err; + + if (val & GLOBAL_STATUS_INIT_READY) + break; + + usleep_range(1000, 2000); + } + + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + return 0; +} + +/* Offset 0x04: Switch Global Control Register */ + +int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + /* Set the SWReset bit 15 along with the PPUEn bit 14, to also restart + * the PPU, including re-doing PHY detection and initialization + */ + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); + if (err) + return err; + + val |= GLOBAL_CONTROL_SW_RESET; + val |= GLOBAL_CONTROL_PPU_ENABLE; + + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); + if (err) + return err; + + err = mv88e6xxx_g1_wait_init_ready(chip); + if (err) + return err; + + return mv88e6185_g1_wait_ppu_polling(chip); +} + +int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + /* Set the SWReset bit 15 */ + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); + if (err) + return err; + + val |= GLOBAL_CONTROL_SW_RESET; + + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); + if (err) + return err; + + err = mv88e6xxx_g1_wait_init_ready(chip); + if (err) + return err; + + return mv88e6352_g1_wait_ppu_polling(chip); +} + /* Offset 0x1a: Monitor Control */ /* Offset 0x1a: Monitor & MGMT Control on some devices */ diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index cb61378..9fca215 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -19,6 +19,10 @@ int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val); int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); + +int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip); +int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip); + int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 13c7cc4..f201d13 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -193,12 +193,12 @@ #define GLOBAL_STATUS 0x00 #define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */ -/* Two bits for 6165, 6185 etc */ -#define GLOBAL_STATUS_PPU_MASK (0x3 << 14) -#define GLOBAL_STATUS_PPU_DISABLED_RST (0x0 << 14) -#define GLOBAL_STATUS_PPU_INITIALIZING (0x1 << 14) -#define GLOBAL_STATUS_PPU_DISABLED (0x2 << 14) -#define GLOBAL_STATUS_PPU_POLLING (0x3 << 14) +#define GLOBAL_STATUS_PPU_STATE_MASK (0x3 << 14) /* 6165 6185 */ +#define GLOBAL_STATUS_PPU_STATE_DISABLED_RST (0x0 << 14) +#define GLOBAL_STATUS_PPU_STATE_INITIALIZING (0x1 << 14) +#define GLOBAL_STATUS_PPU_STATE_DISABLED (0x2 << 14) +#define GLOBAL_STATUS_PPU_STATE_POLLING (0x3 << 14) +#define GLOBAL_STATUS_INIT_READY BIT(11) #define GLOBAL_STATUS_IRQ_AVB 8 #define GLOBAL_STATUS_IRQ_DEVICE 7 #define GLOBAL_STATUS_IRQ_STATS 6 @@ -792,6 +792,9 @@ struct mv88e6xxx_ops { int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); + /* Switch Software Reset */ + int (*reset)(struct mv88e6xxx_chip *chip); + /* RGMII Receive/Transmit Timing Control * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise. */ -- cgit v1.1 From a199d8b6956f8973d09d684fc71300889c26e8e4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 5 Dec 2016 17:30:28 -0500 Subject: net: dsa: mv88e6xxx: add PPU operations Some Marvell chips can enable/disable the PPU on demand. This is needed to access the PHY registers when there is no indirection mechanism. Add two new ppu_enable and ppu_disable ops to describe this and finally get rid of the MV88E6XXX_FLAG_PPU* flags. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 76 ++++++++--------------------------- drivers/net/dsa/mv88e6xxx/global1.c | 57 ++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global1.h | 3 ++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 19 ++------- 4 files changed, 81 insertions(+), 74 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 953c677..4da379f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -527,58 +527,18 @@ int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update) static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip) { - u16 val; - int i, err; - - err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); - if (err) - return err; - - err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, - val & ~GLOBAL_CONTROL_PPU_ENABLE); - if (err) - return err; - - for (i = 0; i < 16; i++) { - err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); - if (err) - return err; - - usleep_range(1000, 2000); - val &= GLOBAL_STATUS_PPU_STATE_MASK; - if (val != GLOBAL_STATUS_PPU_STATE_POLLING) - return 0; - } + if (!chip->info->ops->ppu_disable) + return 0; - return -ETIMEDOUT; + return chip->info->ops->ppu_disable(chip); } static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip) { - u16 val; - int i, err; - - err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); - if (err) - return err; - - err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, - val | GLOBAL_CONTROL_PPU_ENABLE); - if (err) - return err; - - for (i = 0; i < 16; i++) { - err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); - if (err) - return err; - - usleep_range(1000, 2000); - val &= GLOBAL_STATUS_PPU_STATE_MASK; - if (val == GLOBAL_STATUS_PPU_STATE_POLLING) - return 0; - } + if (!chip->info->ops->ppu_enable) + return 0; - return -ETIMEDOUT; + return chip->info->ops->ppu_enable(chip); } static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) @@ -2746,22 +2706,12 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) { struct dsa_switch *ds = chip->ds; u32 upstream_port = dsa_upstream_port(ds); - u16 reg; int err; /* Enable the PHY Polling Unit if present, don't discard any packets, * and mask all interrupt sources. */ - err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); - if (err < 0) - return err; - - reg &= ~GLOBAL_CONTROL_PPU_ENABLE; - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) || - mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE)) - reg |= GLOBAL_CONTROL_PPU_ENABLE; - - err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); + err = mv88e6xxx_ppu_enable(chip); if (err) return err; @@ -3223,6 +3173,8 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .ppu_enable = mv88e6185_g1_ppu_enable, + .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, }; @@ -3241,6 +3193,8 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .ppu_enable = mv88e6185_g1_ppu_enable, + .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, }; @@ -3311,6 +3265,8 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .ppu_enable = mv88e6185_g1_ppu_enable, + .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, }; @@ -3483,6 +3439,8 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, + .ppu_enable = mv88e6185_g1_ppu_enable, + .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, }; @@ -4263,13 +4221,13 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip) { - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) + if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable) mv88e6xxx_ppu_state_init(chip); } static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip) { - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) + if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable) mv88e6xxx_ppu_state_destroy(chip); } diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index c868eb0..75af86a 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -35,6 +35,27 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) /* Offset 0x00: Switch Global Status Register */ +static int mv88e6185_g1_wait_ppu_disabled(struct mv88e6xxx_chip *chip) +{ + u16 state; + int i, err; + + for (i = 0; i < 16; i++) { + err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state); + if (err) + return err; + + /* Check the value of the PPUState bits 15:14 */ + state &= GLOBAL_STATUS_PPU_STATE_MASK; + if (state != GLOBAL_STATUS_PPU_STATE_POLLING) + return 0; + + usleep_range(1000, 2000); + } + + return -ETIMEDOUT; +} + static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip) { u16 state; @@ -154,6 +175,42 @@ int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip) return mv88e6352_g1_wait_ppu_polling(chip); } +int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); + if (err) + return err; + + val |= GLOBAL_CONTROL_PPU_ENABLE; + + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); + if (err) + return err; + + return mv88e6185_g1_wait_ppu_polling(chip); +} + +int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); + if (err) + return err; + + val &= ~GLOBAL_CONTROL_PPU_ENABLE; + + err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); + if (err) + return err; + + return mv88e6185_g1_wait_ppu_disabled(chip); +} + /* Offset 0x1a: Monitor Control */ /* Offset 0x1a: Monitor & MGMT Control on some devices */ diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 9fca215..1aec738 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -23,6 +23,9 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip); +int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip); +int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip); + int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index f201d13..af54bae 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -490,12 +490,6 @@ enum mv88e6xxx_cap { MV88E6XXX_CAP_G2_PVT_DATA, /* (0x0c) Cross Chip Port VLAN Data */ MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ - /* PHY Polling Unit. - * See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING. - */ - MV88E6XXX_CAP_PPU, - MV88E6XXX_CAP_PPU_ACTIVE, - /* Per VLAN Spanning Tree Unit (STU). * The Port State database, if present, is accessed through VTU * operations and dedicated SID registers. See GLOBAL_VTU_SID. @@ -537,8 +531,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_G2_PVT_DATA BIT_ULL(MV88E6XXX_CAP_G2_PVT_DATA) #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) -#define MV88E6XXX_FLAG_PPU BIT_ULL(MV88E6XXX_CAP_PPU) -#define MV88E6XXX_FLAG_PPU_ACTIVE BIT_ULL(MV88E6XXX_CAP_PPU_ACTIVE) #define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU) #define MV88E6XXX_FLAG_TEMP BIT_ULL(MV88E6XXX_CAP_TEMP) #define MV88E6XXX_FLAG_TEMP_LIMIT BIT_ULL(MV88E6XXX_CAP_TEMP_LIMIT) @@ -567,7 +559,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6095 \ (MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ - MV88E6XXX_FLAG_PPU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -578,7 +569,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_PPU | \ MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ @@ -605,7 +595,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAG_PPU | \ MV88E6XXX_FLAG_VTU) #define MV88E6XXX_FLAGS_FAMILY_6320 \ @@ -614,7 +603,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_PPU_ACTIVE | \ MV88E6XXX_FLAG_TEMP | \ MV88E6XXX_FLAG_TEMP_LIMIT | \ MV88E6XXX_FLAG_VTU | \ @@ -630,7 +618,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_PPU_ACTIVE | \ MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_TEMP | \ MV88E6XXX_FLAG_VTU | \ @@ -647,7 +634,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_PPU_ACTIVE | \ MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_TEMP | \ MV88E6XXX_FLAG_TEMP_LIMIT | \ @@ -662,7 +648,6 @@ struct mv88e6xxx_ops; #define MV88E6XXX_FLAGS_FAMILY_6390 \ (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ - MV88E6XXX_FLAG_PPU_ACTIVE | \ MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_TEMP | \ MV88E6XXX_FLAG_TEMP_LIMIT | \ @@ -792,6 +777,10 @@ struct mv88e6xxx_ops { int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); + /* PHY Polling Unit (PPU) operations */ + int (*ppu_enable)(struct mv88e6xxx_chip *chip); + int (*ppu_disable)(struct mv88e6xxx_chip *chip); + /* Switch Software Reset */ int (*reset)(struct mv88e6xxx_chip *chip); -- cgit v1.1 From 343dfaa198e98e8195a962c53206ad88076a6066 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 6 Dec 2016 00:23:00 +0100 Subject: Revert "dctcp: update cwnd on congestion event" Neal Cardwell says: If I am reading the code correctly, then I would have two concerns: 1) Has that been tested? That seems like an extremely dramatic decrease in cwnd. For example, if the cwnd is 80, and there are 40 ACKs, and half the ACKs are ECE marked, then my back-of-the-envelope calculations seem to suggest that after just 11 ACKs the cwnd would be down to a minimal value of 2 [..] 2) That seems to contradict another passage in the draft [..] where it sazs: Just as specified in [RFC3168], DCTCP does not react to congestion indications more than once for every window of data. Neal is right. Fortunately we don't have to complicate this by testing vs. current rtt estimate, we can just revert the patch. Normal stack already handles this for us: receiving ACKs with ECE set causes a call to tcp_enter_cwr(), from there on the ssthresh gets adjusted and prr will take care of cwnd adjustment. Fixes: 4780566784b396 ("dctcp: update cwnd on congestion event") Cc: Neal Cardwell Signed-off-by: Florian Westphal Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_dctcp.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c index bde22eb..5f5e593 100644 --- a/net/ipv4/tcp_dctcp.c +++ b/net/ipv4/tcp_dctcp.c @@ -188,8 +188,8 @@ static void dctcp_ce_state_1_to_0(struct sock *sk) static void dctcp_update_alpha(struct sock *sk, u32 flags) { + const struct tcp_sock *tp = tcp_sk(sk); struct dctcp *ca = inet_csk_ca(sk); - struct tcp_sock *tp = tcp_sk(sk); u32 acked_bytes = tp->snd_una - ca->prior_snd_una; /* If ack did not advance snd_una, count dupack as MSS size. @@ -229,13 +229,6 @@ static void dctcp_update_alpha(struct sock *sk, u32 flags) WRITE_ONCE(ca->dctcp_alpha, alpha); dctcp_reset(tp, ca); } - - if (flags & CA_ACK_ECE) { - unsigned int cwnd = dctcp_ssthresh(sk); - - if (cwnd != tp->snd_cwnd) - tp->snd_cwnd = cwnd; - } } static void dctcp_state(struct sock *sk, u8 new_state) -- cgit v1.1 From 48e0a83ece7803a62afd624ba70523ca66631c35 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Tue, 6 Dec 2016 03:45:00 +0200 Subject: net: ethernet: ti: cpsw: fix early budget split The budget split function requires the phy speed to be known. While ndo open a phy speed identification is postponed till the moment link is up. Hence, move it to appropriate callback, when link is up. Reported-by: Grygorii Strashko Fixes: 8feb0a196507 ("net: ethernet: ti: cpsw: split tx budget according between channels") Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 154 ++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 3f96c57..f373a4b 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -753,6 +753,82 @@ requeue: dev_kfree_skb_any(new_skb); } +/* split budget depending on channel rates */ +static void cpsw_split_budget(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; + struct cpsw_vector *txv = cpsw->txv; + u32 consumed_rate, bigest_rate = 0; + int budget, bigest_rate_ch = 0; + struct cpsw_slave *slave; + int i, rlim_ch_num = 0; + u32 ch_rate, max_rate; + int ch_budget = 0; + + if (cpsw->data.dual_emac) + slave = &cpsw->slaves[priv->emac_port]; + else + slave = &cpsw->slaves[cpsw->data.active_slave]; + + max_rate = slave->phy->speed * 1000; + + consumed_rate = 0; + for (i = 0; i < cpsw->tx_ch_num; i++) { + ch_rate = cpdma_chan_get_rate(txv[i].ch); + if (!ch_rate) + continue; + + rlim_ch_num++; + consumed_rate += ch_rate; + } + + if (cpsw->tx_ch_num == rlim_ch_num) { + max_rate = consumed_rate; + } else { + ch_budget = (consumed_rate * CPSW_POLL_WEIGHT) / max_rate; + ch_budget = (CPSW_POLL_WEIGHT - ch_budget) / + (cpsw->tx_ch_num - rlim_ch_num); + bigest_rate = (max_rate - consumed_rate) / + (cpsw->tx_ch_num - rlim_ch_num); + } + + /* split tx budget */ + budget = CPSW_POLL_WEIGHT; + for (i = 0; i < cpsw->tx_ch_num; i++) { + ch_rate = cpdma_chan_get_rate(txv[i].ch); + if (ch_rate) { + txv[i].budget = (ch_rate * CPSW_POLL_WEIGHT) / max_rate; + if (!txv[i].budget) + txv[i].budget = 1; + if (ch_rate > bigest_rate) { + bigest_rate_ch = i; + bigest_rate = ch_rate; + } + } else { + txv[i].budget = ch_budget; + if (!bigest_rate_ch) + bigest_rate_ch = i; + } + + budget -= txv[i].budget; + } + + if (budget) + txv[bigest_rate_ch].budget += budget; + + /* split rx budget */ + budget = CPSW_POLL_WEIGHT; + ch_budget = budget / cpsw->rx_ch_num; + for (i = 0; i < cpsw->rx_ch_num; i++) { + cpsw->rxv[i].budget = ch_budget; + budget -= ch_budget; + } + + if (budget) + cpsw->rxv[0].budget += budget; +} + static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) { struct cpsw_common *cpsw = dev_id; @@ -941,6 +1017,7 @@ static void cpsw_adjust_link(struct net_device *ndev) for_each_slave(priv, _cpsw_adjust_link, priv, &link); if (link) { + cpsw_split_budget(priv->ndev); netif_carrier_on(ndev); if (netif_running(ndev)) netif_tx_wake_all_queues(ndev); @@ -1280,82 +1357,6 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) } } -/* split budget depending on channel rates */ -static void cpsw_split_budget(struct net_device *ndev) -{ - struct cpsw_priv *priv = netdev_priv(ndev); - struct cpsw_common *cpsw = priv->cpsw; - struct cpsw_vector *txv = cpsw->txv; - u32 consumed_rate, bigest_rate = 0; - int budget, bigest_rate_ch = 0; - struct cpsw_slave *slave; - int i, rlim_ch_num = 0; - u32 ch_rate, max_rate; - int ch_budget = 0; - - if (cpsw->data.dual_emac) - slave = &cpsw->slaves[priv->emac_port]; - else - slave = &cpsw->slaves[cpsw->data.active_slave]; - - max_rate = slave->phy->speed * 1000; - - consumed_rate = 0; - for (i = 0; i < cpsw->tx_ch_num; i++) { - ch_rate = cpdma_chan_get_rate(txv[i].ch); - if (!ch_rate) - continue; - - rlim_ch_num++; - consumed_rate += ch_rate; - } - - if (cpsw->tx_ch_num == rlim_ch_num) { - max_rate = consumed_rate; - } else { - ch_budget = (consumed_rate * CPSW_POLL_WEIGHT) / max_rate; - ch_budget = (CPSW_POLL_WEIGHT - ch_budget) / - (cpsw->tx_ch_num - rlim_ch_num); - bigest_rate = (max_rate - consumed_rate) / - (cpsw->tx_ch_num - rlim_ch_num); - } - - /* split tx budget */ - budget = CPSW_POLL_WEIGHT; - for (i = 0; i < cpsw->tx_ch_num; i++) { - ch_rate = cpdma_chan_get_rate(txv[i].ch); - if (ch_rate) { - txv[i].budget = (ch_rate * CPSW_POLL_WEIGHT) / max_rate; - if (!txv[i].budget) - txv[i].budget = 1; - if (ch_rate > bigest_rate) { - bigest_rate_ch = i; - bigest_rate = ch_rate; - } - } else { - txv[i].budget = ch_budget; - if (!bigest_rate_ch) - bigest_rate_ch = i; - } - - budget -= txv[i].budget; - } - - if (budget) - txv[bigest_rate_ch].budget += budget; - - /* split rx budget */ - budget = CPSW_POLL_WEIGHT; - ch_budget = budget / cpsw->rx_ch_num; - for (i = 0; i < cpsw->rx_ch_num; i++) { - cpsw->rxv[i].budget = ch_budget; - budget -= ch_budget; - } - - if (budget) - cpsw->rxv[0].budget += budget; -} - static int cpsw_fill_rx_channels(struct cpsw_priv *priv) { struct cpsw_common *cpsw = priv->cpsw; @@ -1501,7 +1502,6 @@ static int cpsw_ndo_open(struct net_device *ndev) cpsw_set_coalesce(ndev, &coal); } - cpsw_split_budget(ndev); cpdma_ctlr_start(cpsw->dma); cpsw_intr_enable(cpsw); -- cgit v1.1 From 862b3d2090ae3d8b10bb4ee9275fd932bc4d0d44 Mon Sep 17 00:00:00 2001 From: Salil Date: Tue, 6 Dec 2016 11:09:46 +0000 Subject: net: hns: Fix to conditionally convey RX checksum flag to stack This patch introduces the RX checksum function to check the status of the hardware calculated checksum and its error and appropriately convey status to the upper stack in skb->ip_summed field. In hardware, we only support checksum for the following protocols: 1) IPv4, 2) TCP(over IPv4 or IPv6), 3) UDP(over IPv4 or IPv6), 4) SCTP(over IPv4 or IPv6) but we support many L3(IPv4, IPv6, MPLS, PPPoE etc) and L4(TCP, UDP, GRE, SCTP, IGMP, ICMP etc.) protocols. Hardware limitation: Our present hardware RX Descriptor lacks L3/L4 checksum "Status & Error" bit (which usually can be used to indicate whether checksum was calculated by the hardware and if there was any error encountered during checksum calculation). Software workaround: We do get info within the RX descriptor about the kind of L3/L4 protocol coming in the packet and the error status. These errors might not just be checksum errors but could be related to version, length of IPv4, UDP, TCP etc. Because there is no-way of knowing if it is a L3/L4 error due to bad checksum or any other L3/L4 error, we will not (cannot) convey hardware checksum status(CHECKSUM_UNNECESSARY) for such cases to upper stack and will not maintain the RX L3/L4 checksum counters as well. Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 2 + drivers/net/ethernet/hisilicon/hns/hns_enet.c | 76 ++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 09602f1..8016854 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -99,6 +99,8 @@ enum hnae_led_state { #define HNS_RX_FLAG_L3ID_IPV6 0x1 #define HNS_RX_FLAG_L4ID_UDP 0x0 #define HNS_RX_FLAG_L4ID_TCP 0x1 +#define HNS_RX_FLAG_L4ID_SCTP 0x3 + #define HNS_TXD_ASID_S 0 #define HNS_TXD_ASID_M (0xff << HNS_TXD_ASID_S) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 776d81e..672b646 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -566,6 +566,71 @@ static void get_rx_desc_bnum(u32 bnum_flag, int *out_bnum) HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S); } +static void hns_nic_rx_checksum(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb, u32 flag) +{ + struct net_device *netdev = ring_data->napi.dev; + u32 l3id; + u32 l4id; + + /* check if RX checksum offload is enabled */ + if (unlikely(!(netdev->features & NETIF_F_RXCSUM))) + return; + + /* In hardware, we only support checksum for the following protocols: + * 1) IPv4, + * 2) TCP(over IPv4 or IPv6), + * 3) UDP(over IPv4 or IPv6), + * 4) SCTP(over IPv4 or IPv6) + * but we support many L3(IPv4, IPv6, MPLS, PPPoE etc) and L4(TCP, + * UDP, GRE, SCTP, IGMP, ICMP etc.) protocols. + * + * Hardware limitation: + * Our present hardware RX Descriptor lacks L3/L4 checksum "Status & + * Error" bit (which usually can be used to indicate whether checksum + * was calculated by the hardware and if there was any error encountered + * during checksum calculation). + * + * Software workaround: + * We do get info within the RX descriptor about the kind of L3/L4 + * protocol coming in the packet and the error status. These errors + * might not just be checksum errors but could be related to version, + * length of IPv4, UDP, TCP etc. + * Because there is no-way of knowing if it is a L3/L4 error due to bad + * checksum or any other L3/L4 error, we will not (cannot) convey + * checksum status for such cases to upper stack and will not maintain + * the RX L3/L4 checksum counters as well. + */ + + l3id = hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S); + l4id = hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S); + + /* check L3 protocol for which checksum is supported */ + if ((l3id != HNS_RX_FLAG_L3ID_IPV4) && (l3id != HNS_RX_FLAG_L3ID_IPV6)) + return; + + /* check for any(not just checksum)flagged L3 protocol errors */ + if (unlikely(hnae_get_bit(flag, HNS_RXD_L3E_B))) + return; + + /* we do not support checksum of fragmented packets */ + if (unlikely(hnae_get_bit(flag, HNS_RXD_FRAG_B))) + return; + + /* check L4 protocol for which checksum is supported */ + if ((l4id != HNS_RX_FLAG_L4ID_TCP) && + (l4id != HNS_RX_FLAG_L4ID_UDP) && + (l4id != HNS_RX_FLAG_L4ID_SCTP)) + return; + + /* check for any(not just checksum)flagged L4 protocol errors */ + if (unlikely(hnae_get_bit(flag, HNS_RXD_L4E_B))) + return; + + /* now, this has to be a packet with valid RX checksum */ + skb->ip_summed = CHECKSUM_UNNECESSARY; +} + static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, struct sk_buff **out_skb, int *out_bnum) { @@ -684,13 +749,10 @@ out_bnum_err: ring->stats.rx_pkts++; ring->stats.rx_bytes += skb->len; - if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L3E_B) || - hnae_get_bit(bnum_flag, HNS_RXD_L4E_B))) { - ring->stats.l3l4_csum_err++; - return 0; - } - - skb->ip_summed = CHECKSUM_UNNECESSARY; + /* indicate to upper stack if our hardware has already calculated + * the RX checksum + */ + hns_nic_rx_checksum(ring_data, skb, bnum_flag); return 0; } -- cgit v1.1 From 834184b1f3a4635efbdfdae5fb437f109f6605fa Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Nov 2016 21:36:45 +0100 Subject: netfilter: defrag: only register defrag functionality if needed nf_defrag modules for ipv4 and ipv6 export an empty stub function. Any module that needs the defragmentation hooks registered simply 'calls' this empty function to create a phony module dependency -- modprobe will then load the defrag module too. This extends netfilter ipv4/ipv6 defragmentation modules to delay the hook registration until the functionality is requested within a network namespace instead of module load time for all namespaces. Hooks are only un-registered on module unload or when a namespace that used such defrag functionality exits. We have to use struct net for this as the register hooks can be called before netns initialization here from the ipv4/ipv6 conntrack module init path. There is no unregister functionality support, defrag will always be active once it was requested inside a net namespace. The reason is that defrag has impact on nft and iptables rulesets (without defrag we might see framents). Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_defrag_ipv4.h | 3 +- include/net/netfilter/ipv6/nf_defrag_ipv6.h | 3 +- include/net/netns/netfilter.h | 6 ++++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 7 ++++- net/ipv4/netfilter/nf_defrag_ipv4.c | 41 +++++++++++++++++++++++-- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 7 ++++- net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 42 +++++++++++++++++++++++--- net/netfilter/xt_TPROXY.c | 15 ++++++--- net/netfilter/xt_socket.c | 33 +++++++++++++++++--- 9 files changed, 136 insertions(+), 21 deletions(-) diff --git a/include/net/netfilter/ipv4/nf_defrag_ipv4.h b/include/net/netfilter/ipv4/nf_defrag_ipv4.h index f01ef20..db405f7 100644 --- a/include/net/netfilter/ipv4/nf_defrag_ipv4.h +++ b/include/net/netfilter/ipv4/nf_defrag_ipv4.h @@ -1,6 +1,7 @@ #ifndef _NF_DEFRAG_IPV4_H #define _NF_DEFRAG_IPV4_H -void nf_defrag_ipv4_enable(void); +struct net; +int nf_defrag_ipv4_enable(struct net *); #endif /* _NF_DEFRAG_IPV4_H */ diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h index ddf162f..7664efe 100644 --- a/include/net/netfilter/ipv6/nf_defrag_ipv6.h +++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h @@ -1,7 +1,8 @@ #ifndef _NF_DEFRAG_IPV6_H #define _NF_DEFRAG_IPV6_H -void nf_defrag_ipv6_enable(void); +struct net; +int nf_defrag_ipv6_enable(struct net *); int nf_ct_frag6_init(void); void nf_ct_frag6_cleanup(void); diff --git a/include/net/netns/netfilter.h b/include/net/netns/netfilter.h index 58487b1..cea396b 100644 --- a/include/net/netns/netfilter.h +++ b/include/net/netns/netfilter.h @@ -17,5 +17,11 @@ struct netns_nf { struct ctl_table_header *nf_log_dir_header; #endif struct nf_hook_entry __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; +#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) + bool defrag_ipv4; +#endif +#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) + bool defrag_ipv6; +#endif }; #endif diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 6f37544..fcfd071 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -325,6 +325,12 @@ static int ipv4_hooks_register(struct net *net) if (cnet->users > 1) goto out_unlock; + err = nf_defrag_ipv4_enable(net); + if (err) { + cnet->users = 0; + goto out_unlock; + } + err = nf_register_net_hooks(net, ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); @@ -422,7 +428,6 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) int ret = 0; need_conntrack(); - nf_defrag_ipv4_enable(); ret = nf_register_sockopt(&so_getorigdst); if (ret < 0) { diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index d88da36b..49bd6a5 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,8 @@ #endif #include +static DEFINE_MUTEX(defrag4_mutex); + static int nf_ct_ipv4_gather_frags(struct net *net, struct sk_buff *skb, u_int32_t user) { @@ -102,18 +105,50 @@ static struct nf_hook_ops ipv4_defrag_ops[] = { }, }; +static void __net_exit defrag4_net_exit(struct net *net) +{ + if (net->nf.defrag_ipv4) { + nf_unregister_net_hooks(net, ipv4_defrag_ops, + ARRAY_SIZE(ipv4_defrag_ops)); + net->nf.defrag_ipv4 = false; + } +} + +static struct pernet_operations defrag4_net_ops = { + .exit = defrag4_net_exit, +}; + static int __init nf_defrag_init(void) { - return nf_register_hooks(ipv4_defrag_ops, ARRAY_SIZE(ipv4_defrag_ops)); + return register_pernet_subsys(&defrag4_net_ops); } static void __exit nf_defrag_fini(void) { - nf_unregister_hooks(ipv4_defrag_ops, ARRAY_SIZE(ipv4_defrag_ops)); + unregister_pernet_subsys(&defrag4_net_ops); } -void nf_defrag_ipv4_enable(void) +int nf_defrag_ipv4_enable(struct net *net) { + int err = 0; + + might_sleep(); + + if (net->nf.defrag_ipv4) + return 0; + + mutex_lock(&defrag4_mutex); + if (net->nf.defrag_ipv4) + goto out_unlock; + + err = nf_register_net_hooks(net, ipv4_defrag_ops, + ARRAY_SIZE(ipv4_defrag_ops)); + if (err == 0) + net->nf.defrag_ipv4 = true; + + out_unlock: + mutex_unlock(&defrag4_mutex); + return err; } EXPORT_SYMBOL_GPL(nf_defrag_ipv4_enable); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 72fe480..4e34024 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -325,6 +325,12 @@ static int ipv6_hooks_register(struct net *net) if (cnet->users > 1) goto out_unlock; + err = nf_defrag_ipv6_enable(net); + if (err < 0) { + cnet->users = 0; + goto out_unlock; + } + err = nf_register_net_hooks(net, ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); if (err) @@ -427,7 +433,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) int ret = 0; need_conntrack(); - nf_defrag_ipv6_enable(); ret = nf_register_sockopt(&so_getorigdst6); if (ret < 0) { diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index f06b047..8e0bdd0 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -30,6 +30,8 @@ #include #include +static DEFINE_MUTEX(defrag6_mutex); + static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, struct sk_buff *skb) { @@ -87,6 +89,19 @@ static struct nf_hook_ops ipv6_defrag_ops[] = { }, }; +static void __net_exit defrag6_net_exit(struct net *net) +{ + if (net->nf.defrag_ipv6) { + nf_unregister_net_hooks(net, ipv6_defrag_ops, + ARRAY_SIZE(ipv6_defrag_ops)); + net->nf.defrag_ipv6 = false; + } +} + +static struct pernet_operations defrag6_net_ops = { + .exit = defrag6_net_exit, +}; + static int __init nf_defrag_init(void) { int ret = 0; @@ -96,9 +111,9 @@ static int __init nf_defrag_init(void) pr_err("nf_defrag_ipv6: can't initialize frag6.\n"); return ret; } - ret = nf_register_hooks(ipv6_defrag_ops, ARRAY_SIZE(ipv6_defrag_ops)); + ret = register_pernet_subsys(&defrag6_net_ops); if (ret < 0) { - pr_err("nf_defrag_ipv6: can't register hooks\n"); + pr_err("nf_defrag_ipv6: can't register pernet ops\n"); goto cleanup_frag6; } return ret; @@ -111,12 +126,31 @@ cleanup_frag6: static void __exit nf_defrag_fini(void) { - nf_unregister_hooks(ipv6_defrag_ops, ARRAY_SIZE(ipv6_defrag_ops)); + unregister_pernet_subsys(&defrag6_net_ops); nf_ct_frag6_cleanup(); } -void nf_defrag_ipv6_enable(void) +int nf_defrag_ipv6_enable(struct net *net) { + int err = 0; + + might_sleep(); + + if (net->nf.defrag_ipv6) + return 0; + + mutex_lock(&defrag6_mutex); + if (net->nf.defrag_ipv6) + goto out_unlock; + + err = nf_register_net_hooks(net, ipv6_defrag_ops, + ARRAY_SIZE(ipv6_defrag_ops)); + if (err == 0) + net->nf.defrag_ipv6 = true; + + out_unlock: + mutex_unlock(&defrag6_mutex); + return err; } EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable); diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index dbd72cc..80cb7ba 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -531,6 +531,11 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) static int tproxy_tg6_check(const struct xt_tgchk_param *par) { const struct ip6t_ip6 *i = par->entryinfo; + int err; + + err = nf_defrag_ipv6_enable(par->net); + if (err) + return err; if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) && !(i->invflags & IP6T_INV_PROTO)) @@ -545,6 +550,11 @@ static int tproxy_tg6_check(const struct xt_tgchk_param *par) static int tproxy_tg4_check(const struct xt_tgchk_param *par) { const struct ipt_ip *i = par->entryinfo; + int err; + + err = nf_defrag_ipv4_enable(par->net); + if (err) + return err; if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) && !(i->invflags & IPT_INV_PROTO)) @@ -596,11 +606,6 @@ static struct xt_target tproxy_tg_reg[] __read_mostly = { static int __init tproxy_tg_init(void) { - nf_defrag_ipv4_enable(); -#ifdef XT_TPROXY_HAVE_IPV6 - nf_defrag_ipv6_enable(); -#endif - return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); } diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 2198914..770bbec 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -147,9 +147,28 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) } #endif +static int socket_mt_enable_defrag(struct net *net, int family) +{ + switch (family) { + case NFPROTO_IPV4: + return nf_defrag_ipv4_enable(net); +#ifdef XT_SOCKET_HAVE_IPV6 + case NFPROTO_IPV6: + return nf_defrag_ipv6_enable(net); +#endif + } + WARN_ONCE(1, "Unknown family %d\n", family); + return 0; +} + static int socket_mt_v1_check(const struct xt_mtchk_param *par) { const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; + int err; + + err = socket_mt_enable_defrag(par->net, par->family); + if (err) + return err; if (info->flags & ~XT_SOCKET_FLAGS_V1) { pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V1); @@ -161,6 +180,11 @@ static int socket_mt_v1_check(const struct xt_mtchk_param *par) static int socket_mt_v2_check(const struct xt_mtchk_param *par) { const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo; + int err; + + err = socket_mt_enable_defrag(par->net, par->family); + if (err) + return err; if (info->flags & ~XT_SOCKET_FLAGS_V2) { pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V2); @@ -173,7 +197,11 @@ static int socket_mt_v3_check(const struct xt_mtchk_param *par) { const struct xt_socket_mtinfo3 *info = (struct xt_socket_mtinfo3 *)par->matchinfo; + int err; + err = socket_mt_enable_defrag(par->net, par->family); + if (err) + return err; if (info->flags & ~XT_SOCKET_FLAGS_V3) { pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V3); @@ -268,11 +296,6 @@ static struct xt_match socket_mt_reg[] __read_mostly = { static int __init socket_mt_init(void) { - nf_defrag_ipv4_enable(); -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - nf_defrag_ipv6_enable(); -#endif - return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); } -- cgit v1.1 From 0aa8c57a04907a5d02068ff9f917629be97ea78d Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Tue, 15 Nov 2016 17:48:44 -0500 Subject: netfilter: introduce accessor functions for hook entries This allows easier future refactoring. Signed-off-by: Aaron Conole Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 27 +++++++++++++++++++++++++++ net/bridge/br_netfilter_hooks.c | 2 +- net/netfilter/core.c | 10 ++++------ net/netfilter/nf_queue.c | 5 ++--- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 6923014..575aa19 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -79,6 +79,33 @@ struct nf_hook_entry { const struct nf_hook_ops *orig_ops; }; +static inline void +nf_hook_entry_init(struct nf_hook_entry *entry, const struct nf_hook_ops *ops) +{ + entry->next = NULL; + entry->ops = *ops; + entry->orig_ops = ops; +} + +static inline int +nf_hook_entry_priority(const struct nf_hook_entry *entry) +{ + return entry->ops.priority; +} + +static inline int +nf_hook_entry_hookfn(const struct nf_hook_entry *entry, struct sk_buff *skb, + struct nf_hook_state *state) +{ + return entry->ops.hook(entry->ops.priv, skb, state); +} + +static inline const struct nf_hook_ops * +nf_hook_entry_ops(const struct nf_hook_entry *entry) +{ + return entry->orig_ops; +} + static inline void nf_hook_state_init(struct nf_hook_state *p, unsigned int hook, u_int8_t pf, diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 83d937f..adad2ee 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -1010,7 +1010,7 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, elem = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]); - while (elem && (elem->ops.priority <= NF_BR_PRI_BRNF)) + while (elem && (nf_hook_entry_priority(elem) <= NF_BR_PRI_BRNF)) elem = rcu_dereference(elem->next); if (!elem) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index de30e08..2bb46e2 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -102,15 +102,13 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) if (!entry) return -ENOMEM; - entry->orig_ops = reg; - entry->ops = *reg; - entry->next = NULL; + nf_hook_entry_init(entry, reg); mutex_lock(&nf_hook_mutex); /* Find the spot in the list */ while ((p = nf_entry_dereference(*pp)) != NULL) { - if (reg->priority < p->orig_ops->priority) + if (reg->priority < nf_hook_entry_priority(p)) break; pp = &p->next; } @@ -140,7 +138,7 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) mutex_lock(&nf_hook_mutex); while ((p = nf_entry_dereference(*pp)) != NULL) { - if (p->orig_ops == reg) { + if (nf_hook_entry_ops(p) == reg) { rcu_assign_pointer(*pp, p->next); break; } @@ -311,7 +309,7 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, int ret; do { - verdict = entry->ops.hook(entry->ops.priv, skb, state); + verdict = nf_hook_entry_hookfn(entry, skb, state); switch (verdict & NF_VERDICT_MASK) { case NF_ACCEPT: entry = rcu_dereference(entry->next); diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 77cba9f..4a76624 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -185,7 +185,7 @@ static unsigned int nf_iterate(struct sk_buff *skb, do { repeat: - verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state); + verdict = nf_hook_entry_hookfn((*entryp), skb, state); if (verdict != NF_ACCEPT) { if (verdict != NF_REPEAT) return verdict; @@ -200,7 +200,6 @@ repeat: void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) { struct nf_hook_entry *hook_entry = entry->hook; - struct nf_hook_ops *elem = &hook_entry->ops; struct sk_buff *skb = entry->skb; const struct nf_afinfo *afinfo; int err; @@ -209,7 +208,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) /* Continue traversal iff userspace said ok... */ if (verdict == NF_REPEAT) - verdict = elem->hook(elem->priv, skb, &entry->state); + verdict = nf_hook_entry_hookfn(hook_entry, skb, &entry->state); if (verdict == NF_ACCEPT) { afinfo = nf_get_afinfo(entry->state.pf); -- cgit v1.1 From d415b9eb76fc55c03ef5451691170aa5771dcea3 Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Tue, 15 Nov 2016 17:48:45 -0500 Subject: netfilter: decouple nf_hook_entry and nf_hook_ops During nfhook traversal we only need a very small subset of nf_hook_ops members. We need: - next element - hook function to call - hook function priv argument Bridge netfilter also needs 'thresh'; can be obtained via ->orig_ops. nf_hook_entry struct is now 32 bytes on x86_64. A followup patch will turn the run-time list into an array that only stores hook functions plus their priv arguments, eliminating the ->next element. Suggested-by: Florian Westphal Signed-off-by: Aaron Conole Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 575aa19..a4b97be 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -75,7 +75,8 @@ struct nf_hook_ops { struct nf_hook_entry { struct nf_hook_entry __rcu *next; - struct nf_hook_ops ops; + nf_hookfn *hook; + void *priv; const struct nf_hook_ops *orig_ops; }; @@ -83,21 +84,22 @@ static inline void nf_hook_entry_init(struct nf_hook_entry *entry, const struct nf_hook_ops *ops) { entry->next = NULL; - entry->ops = *ops; + entry->hook = ops->hook; + entry->priv = ops->priv; entry->orig_ops = ops; } static inline int nf_hook_entry_priority(const struct nf_hook_entry *entry) { - return entry->ops.priority; + return entry->orig_ops->priority; } static inline int nf_hook_entry_hookfn(const struct nf_hook_entry *entry, struct sk_buff *skb, struct nf_hook_state *state) { - return entry->ops.hook(entry->ops.priv, skb, state); + return entry->hook(entry->priv, skb, state); } static inline const struct nf_hook_ops * -- cgit v1.1 From 679972f3bef8df7aa04cc99b46f71392ebf0b5f8 Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Tue, 15 Nov 2016 17:48:46 -0500 Subject: netfilter: convert while loops to for loops This is to facilitate converting from a singly-linked list to an array of elements. Signed-off-by: Aaron Conole Signed-off-by: Pablo Neira Ayuso --- net/bridge/br_netfilter_hooks.c | 8 ++++---- net/netfilter/core.c | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index adad2ee..b12501a 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -1008,10 +1008,10 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, struct nf_hook_state state; int ret; - elem = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]); - - while (elem && (nf_hook_entry_priority(elem) <= NF_BR_PRI_BRNF)) - elem = rcu_dereference(elem->next); + for (elem = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]); + elem && nf_hook_entry_priority(elem) <= NF_BR_PRI_BRNF; + elem = rcu_dereference(elem->next)) + ; if (!elem) return okfn(net, sk, skb); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 2bb46e2..ce6adfa 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -107,10 +107,9 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) mutex_lock(&nf_hook_mutex); /* Find the spot in the list */ - while ((p = nf_entry_dereference(*pp)) != NULL) { + for (; (p = nf_entry_dereference(*pp)) != NULL; pp = &p->next) { if (reg->priority < nf_hook_entry_priority(p)) break; - pp = &p->next; } rcu_assign_pointer(entry->next, p); rcu_assign_pointer(*pp, entry); @@ -137,12 +136,11 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) return; mutex_lock(&nf_hook_mutex); - while ((p = nf_entry_dereference(*pp)) != NULL) { + for (; (p = nf_entry_dereference(*pp)) != NULL; pp = &p->next) { if (nf_hook_entry_ops(p) == reg) { rcu_assign_pointer(*pp, p->next); break; } - pp = &p->next; } mutex_unlock(&nf_hook_mutex); if (!p) { -- cgit v1.1 From 4d31eef5176df06f218201bc9c0ce40babb41660 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 22 Nov 2016 14:44:17 +0100 Subject: netfilter: x_tables: pass xt_counters struct instead of packet counter On SMP we overload the packet counter (unsigned long) to contain percpu offset. Hide this from callers and pass xt_counters address instead. Preparation patch to allocate the percpu counters in page-sized batch chunks. Signed-off-by: Florian Westphal Acked-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/x_tables.h | 6 +----- net/ipv4/netfilter/arp_tables.c | 4 ++-- net/ipv4/netfilter/ip_tables.c | 4 ++-- net/ipv6/netfilter/ip6_tables.c | 5 ++--- net/netfilter/x_tables.c | 9 +++++++++ 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index cd4eaf8..6e61ede 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -430,11 +430,7 @@ static inline unsigned long xt_percpu_counter_alloc(void) return 0; } -static inline void xt_percpu_counter_free(u64 pcnt) -{ - if (nr_cpu_ids > 1) - free_percpu((void __percpu *) (unsigned long) pcnt); -} +void xt_percpu_counter_free(struct xt_counters *cnt); static inline struct xt_counters * xt_get_this_cpu_counter(struct xt_counters *cnt) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 848a070..019f8e8 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -439,7 +439,7 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size) err: module_put(t->u.kernel.target->me); out: - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); return ret; } @@ -519,7 +519,7 @@ static inline void cleanup_entry(struct arpt_entry *e) if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); } /* Checks and translates the user-supplied table segment (held in diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 46815c8..acc9a0c 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -582,7 +582,7 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name, cleanup_match(ematch, net); } - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); return ret; } @@ -670,7 +670,7 @@ cleanup_entry(struct ipt_entry *e, struct net *net) if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); } /* Checks and translates the user-supplied table segment (held in diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 6ff42b8..88b56a9 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -612,7 +612,7 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name, cleanup_match(ematch, net); } - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); return ret; } @@ -699,8 +699,7 @@ static void cleanup_entry(struct ip6t_entry *e, struct net *net) if (par.target->destroy != NULL) par.target->destroy(&par); module_put(par.target->me); - - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); } /* Checks and translates the user-supplied table segment (held in diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index ad818e5..0580029 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -1615,6 +1615,15 @@ void xt_proto_fini(struct net *net, u_int8_t af) } EXPORT_SYMBOL_GPL(xt_proto_fini); +void xt_percpu_counter_free(struct xt_counters *counters) +{ + unsigned long pcnt = counters->pcnt; + + if (nr_cpu_ids > 1) + free_percpu((void __percpu *)pcnt); +} +EXPORT_SYMBOL_GPL(xt_percpu_counter_free); + static int __net_init xt_net_init(struct net *net) { int i; -- cgit v1.1 From f28e15bacedd444608e25421c72eb2cf4527c9ca Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 22 Nov 2016 14:44:18 +0100 Subject: netfilter: x_tables: pass xt_counters struct to counter allocator Keeps some noise away from a followup patch. Signed-off-by: Florian Westphal Acked-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/x_tables.h | 27 +-------------------------- net/ipv4/netfilter/arp_tables.c | 5 +---- net/ipv4/netfilter/ip_tables.c | 5 +---- net/ipv6/netfilter/ip6_tables.c | 5 +---- net/netfilter/x_tables.c | 30 ++++++++++++++++++++++++++++++ 5 files changed, 34 insertions(+), 38 deletions(-) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 6e61ede..05a94bd 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -404,32 +404,7 @@ static inline unsigned long ifname_compare_aligned(const char *_a, } -/* On SMP, ip(6)t_entry->counters.pcnt holds address of the - * real (percpu) counter. On !SMP, its just the packet count, - * so nothing needs to be done there. - * - * xt_percpu_counter_alloc returns the address of the percpu - * counter, or 0 on !SMP. We force an alignment of 16 bytes - * so that bytes/packets share a common cache line. - * - * Hence caller must use IS_ERR_VALUE to check for error, this - * allows us to return 0 for single core systems without forcing - * callers to deal with SMP vs. NONSMP issues. - */ -static inline unsigned long xt_percpu_counter_alloc(void) -{ - if (nr_cpu_ids > 1) { - void __percpu *res = __alloc_percpu(sizeof(struct xt_counters), - sizeof(struct xt_counters)); - - if (res == NULL) - return -ENOMEM; - - return (__force unsigned long) res; - } - - return 0; -} +bool xt_percpu_counter_alloc(struct xt_counters *counters); void xt_percpu_counter_free(struct xt_counters *cnt); static inline struct xt_counters * diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 019f8e8..808deb2 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -415,13 +415,10 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size) { struct xt_entry_target *t; struct xt_target *target; - unsigned long pcnt; int ret; - pcnt = xt_percpu_counter_alloc(); - if (IS_ERR_VALUE(pcnt)) + if (!xt_percpu_counter_alloc(&e->counters)) return -ENOMEM; - e->counters.pcnt = pcnt; t = arpt_get_target(e); target = xt_request_find_target(NFPROTO_ARP, t->u.user.name, diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index acc9a0c..a48430d 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -539,12 +539,9 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name, unsigned int j; struct xt_mtchk_param mtpar; struct xt_entry_match *ematch; - unsigned long pcnt; - pcnt = xt_percpu_counter_alloc(); - if (IS_ERR_VALUE(pcnt)) + if (!xt_percpu_counter_alloc(&e->counters)) return -ENOMEM; - e->counters.pcnt = pcnt; j = 0; mtpar.net = net; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 88b56a9..a5a9208 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -570,12 +570,9 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name, unsigned int j; struct xt_mtchk_param mtpar; struct xt_entry_match *ematch; - unsigned long pcnt; - pcnt = xt_percpu_counter_alloc(); - if (IS_ERR_VALUE(pcnt)) + if (!xt_percpu_counter_alloc(&e->counters)) return -ENOMEM; - e->counters.pcnt = pcnt; j = 0; mtpar.net = net; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 0580029..be5e830 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -1615,6 +1615,36 @@ void xt_proto_fini(struct net *net, u_int8_t af) } EXPORT_SYMBOL_GPL(xt_proto_fini); +/** + * xt_percpu_counter_alloc - allocate x_tables rule counter + * + * @counter: pointer to counter struct inside the ip(6)/arpt_entry struct + * + * On SMP, the packet counter [ ip(6)t_entry->counters.pcnt ] will then + * contain the address of the real (percpu) counter. + * + * Rule evaluation needs to use xt_get_this_cpu_counter() helper + * to fetch the real percpu counter. + * + * returns false on error. + */ +bool xt_percpu_counter_alloc(struct xt_counters *counter) +{ + void __percpu *res; + + if (nr_cpu_ids <= 1) + return true; + + res = __alloc_percpu(sizeof(struct xt_counters), + sizeof(struct xt_counters)); + if (!res) + return false; + + counter->pcnt = (__force unsigned long)res; + return true; +} +EXPORT_SYMBOL_GPL(xt_percpu_counter_alloc); + void xt_percpu_counter_free(struct xt_counters *counters) { unsigned long pcnt = counters->pcnt; -- cgit v1.1 From ae0ac0ed6fcf5af3be0f63eb935f483f44a402d2 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 22 Nov 2016 14:44:19 +0100 Subject: netfilter: x_tables: pack percpu counter allocations instead of allocating each xt_counter individually, allocate 4k chunks and then use these for counter allocation requests. This should speed up rule evaluation by increasing data locality, also speeds up ruleset loading because we reduce calls to the percpu allocator. As Eric points out we can't use PAGE_SIZE, page_allocator would fail on arches with 64k page size. Suggested-by: Eric Dumazet Signed-off-by: Florian Westphal Acked-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/x_tables.h | 7 ++++++- net/ipv4/netfilter/arp_tables.c | 9 ++++++--- net/ipv4/netfilter/ip_tables.c | 9 ++++++--- net/ipv6/netfilter/ip6_tables.c | 9 ++++++--- net/netfilter/x_tables.c | 33 ++++++++++++++++++++++++--------- 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 05a94bd..5117e4d 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -403,8 +403,13 @@ static inline unsigned long ifname_compare_aligned(const char *_a, return ret; } +struct xt_percpu_counter_alloc_state { + unsigned int off; + const char __percpu *mem; +}; -bool xt_percpu_counter_alloc(struct xt_counters *counters); +bool xt_percpu_counter_alloc(struct xt_percpu_counter_alloc_state *state, + struct xt_counters *counter); void xt_percpu_counter_free(struct xt_counters *cnt); static inline struct xt_counters * diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 808deb2..1258a9a 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -411,13 +411,14 @@ static inline int check_target(struct arpt_entry *e, const char *name) } static inline int -find_check_entry(struct arpt_entry *e, const char *name, unsigned int size) +find_check_entry(struct arpt_entry *e, const char *name, unsigned int size, + struct xt_percpu_counter_alloc_state *alloc_state) { struct xt_entry_target *t; struct xt_target *target; int ret; - if (!xt_percpu_counter_alloc(&e->counters)) + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) return -ENOMEM; t = arpt_get_target(e); @@ -525,6 +526,7 @@ static inline void cleanup_entry(struct arpt_entry *e) static int translate_table(struct xt_table_info *newinfo, void *entry0, const struct arpt_replace *repl) { + struct xt_percpu_counter_alloc_state alloc_state = { 0 }; struct arpt_entry *iter; unsigned int *offsets; unsigned int i; @@ -587,7 +589,8 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, /* Finally, each sanity check must pass */ i = 0; xt_entry_foreach(iter, entry0, newinfo->size) { - ret = find_check_entry(iter, repl->name, repl->size); + ret = find_check_entry(iter, repl->name, repl->size, + &alloc_state); if (ret != 0) break; ++i; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index a48430d..308b456 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -531,7 +531,8 @@ static int check_target(struct ipt_entry *e, struct net *net, const char *name) static int find_check_entry(struct ipt_entry *e, struct net *net, const char *name, - unsigned int size) + unsigned int size, + struct xt_percpu_counter_alloc_state *alloc_state) { struct xt_entry_target *t; struct xt_target *target; @@ -540,7 +541,7 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name, struct xt_mtchk_param mtpar; struct xt_entry_match *ematch; - if (!xt_percpu_counter_alloc(&e->counters)) + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) return -ENOMEM; j = 0; @@ -676,6 +677,7 @@ static int translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, const struct ipt_replace *repl) { + struct xt_percpu_counter_alloc_state alloc_state = { 0 }; struct ipt_entry *iter; unsigned int *offsets; unsigned int i; @@ -735,7 +737,8 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, /* Finally, each sanity check must pass */ i = 0; xt_entry_foreach(iter, entry0, newinfo->size) { - ret = find_check_entry(iter, net, repl->name, repl->size); + ret = find_check_entry(iter, net, repl->name, repl->size, + &alloc_state); if (ret != 0) break; ++i; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index a5a9208..d56d8ac 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -562,7 +562,8 @@ static int check_target(struct ip6t_entry *e, struct net *net, const char *name) static int find_check_entry(struct ip6t_entry *e, struct net *net, const char *name, - unsigned int size) + unsigned int size, + struct xt_percpu_counter_alloc_state *alloc_state) { struct xt_entry_target *t; struct xt_target *target; @@ -571,7 +572,7 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name, struct xt_mtchk_param mtpar; struct xt_entry_match *ematch; - if (!xt_percpu_counter_alloc(&e->counters)) + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) return -ENOMEM; j = 0; @@ -705,6 +706,7 @@ static int translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, const struct ip6t_replace *repl) { + struct xt_percpu_counter_alloc_state alloc_state = { 0 }; struct ip6t_entry *iter; unsigned int *offsets; unsigned int i; @@ -764,7 +766,8 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, /* Finally, each sanity check must pass */ i = 0; xt_entry_foreach(iter, entry0, newinfo->size) { - ret = find_check_entry(iter, net, repl->name, repl->size); + ret = find_check_entry(iter, net, repl->name, repl->size, + &alloc_state); if (ret != 0) break; ++i; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index be5e830..f6ce4a7 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -40,6 +40,7 @@ MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module"); #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) +#define XT_PCPU_BLOCK_SIZE 4096 struct compat_delta { unsigned int offset; /* offset in kernel */ @@ -1618,6 +1619,7 @@ EXPORT_SYMBOL_GPL(xt_proto_fini); /** * xt_percpu_counter_alloc - allocate x_tables rule counter * + * @state: pointer to xt_percpu allocation state * @counter: pointer to counter struct inside the ip(6)/arpt_entry struct * * On SMP, the packet counter [ ip(6)t_entry->counters.pcnt ] will then @@ -1626,21 +1628,34 @@ EXPORT_SYMBOL_GPL(xt_proto_fini); * Rule evaluation needs to use xt_get_this_cpu_counter() helper * to fetch the real percpu counter. * + * To speed up allocation and improve data locality, a 4kb block is + * allocated. + * + * xt_percpu_counter_alloc_state contains the base address of the + * allocated page and the current sub-offset. + * * returns false on error. */ -bool xt_percpu_counter_alloc(struct xt_counters *counter) +bool xt_percpu_counter_alloc(struct xt_percpu_counter_alloc_state *state, + struct xt_counters *counter) { - void __percpu *res; + BUILD_BUG_ON(XT_PCPU_BLOCK_SIZE < (sizeof(*counter) * 2)); if (nr_cpu_ids <= 1) return true; - res = __alloc_percpu(sizeof(struct xt_counters), - sizeof(struct xt_counters)); - if (!res) - return false; - - counter->pcnt = (__force unsigned long)res; + if (!state->mem) { + state->mem = __alloc_percpu(XT_PCPU_BLOCK_SIZE, + XT_PCPU_BLOCK_SIZE); + if (!state->mem) + return false; + } + counter->pcnt = (__force unsigned long)(state->mem + state->off); + state->off += sizeof(*counter); + if (state->off > (XT_PCPU_BLOCK_SIZE - sizeof(*counter))) { + state->mem = NULL; + state->off = 0; + } return true; } EXPORT_SYMBOL_GPL(xt_percpu_counter_alloc); @@ -1649,7 +1664,7 @@ void xt_percpu_counter_free(struct xt_counters *counters) { unsigned long pcnt = counters->pcnt; - if (nr_cpu_ids > 1) + if (nr_cpu_ids > 1 && (pcnt & (XT_PCPU_BLOCK_SIZE - 1)) == 0) free_percpu((void __percpu *)pcnt); } EXPORT_SYMBOL_GPL(xt_percpu_counter_free); -- cgit v1.1 From 11583438b73fbc9117ff8afcbde8c934d0d63713 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Wed, 23 Nov 2016 22:12:20 +0800 Subject: netfilter: nft_fib: convert htonl to ntohl properly Acctually ntohl and htonl are identical, so this doesn't affect anything, but it is conceptually wrong. Signed-off-by: Liping Zhang Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nft_fib_ipv4.c | 2 +- net/ipv6/netfilter/nft_fib_ipv6.c | 2 +- net/netfilter/nft_fib.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index 1b49966..bfffa74 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -198,7 +198,7 @@ nft_fib4_select_ops(const struct nft_ctx *ctx, if (!tb[NFTA_FIB_RESULT]) return ERR_PTR(-EINVAL); - result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT])); + result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT])); switch (result) { case NFT_FIB_RESULT_OIF: diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index d526bb5..c947aad 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -235,7 +235,7 @@ nft_fib6_select_ops(const struct nft_ctx *ctx, if (!tb[NFTA_FIB_RESULT]) return ERR_PTR(-EINVAL); - result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT])); + result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT])); switch (result) { case NFT_FIB_RESULT_OIF: diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index 249c9b8..29a4906 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -86,7 +86,7 @@ int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 0) return -EINVAL; - priv->result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT])); + priv->result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT])); priv->dreg = nft_parse_register(tb[NFTA_FIB_DREG]); switch (priv->result) { -- cgit v1.1 From e0ffdbc78d84e1da090f03ab62da3def0e65159e Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Wed, 23 Nov 2016 22:12:21 +0800 Subject: netfilter: nft_fib_ipv4: initialize *dest to zero Otherwise, if fib lookup fail, *dest will be filled with garbage value, so reverse path filtering will not work properly: # nft add rule x prerouting fib saddr oif eq 0 drop Fixes: f6d0cbcf09c5 ("netfilter: nf_tables: add fib expression") Signed-off-by: Liping Zhang Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nft_fib_ipv4.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index bfffa74..2581363 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -122,6 +122,8 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, fl4.saddr = get_saddr(iph->daddr); } + *dest = 0; + if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) return; -- cgit v1.1 From 1814096980bbe546c4384b7b064126cbe7d40d30 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 24 Nov 2016 12:04:55 +0100 Subject: netfilter: nft_payload: layer 4 checksum adjustment for pseudoheader fields This patch adds a new flag that signals the kernel to update layer 4 checksum if the packet field belongs to the layer 4 pseudoheader. This implicitly provides stateless NAT 1:1 that is useful under very specific usecases. Since rules mangling layer 3 fields that are part of the pseudoheader may potentially convey any layer 4 packet, we have to deal with the layer 4 checksum adjustment using protocol specific code. This patch adds support for TCP, UDP and ICMPv6, since they include the pseudoheader in the layer 4 checksum calculation. ICMP doesn't, so we can skip it. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables_core.h | 1 + include/uapi/linux/netfilter/nf_tables.h | 6 ++ net/netfilter/nft_payload.c | 107 +++++++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 862373d..8f690ef 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -45,6 +45,7 @@ struct nft_payload_set { enum nft_registers sreg:8; u8 csum_type; u8 csum_offset; + u8 csum_flags; }; extern const struct nft_expr_ops nft_payload_fast_ops; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 14e5f61..f030e59 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -659,6 +659,10 @@ enum nft_payload_csum_types { NFT_PAYLOAD_CSUM_INET, }; +enum nft_payload_csum_flags { + NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0), +}; + /** * enum nft_payload_attributes - nf_tables payload expression netlink attributes * @@ -669,6 +673,7 @@ enum nft_payload_csum_types { * @NFTA_PAYLOAD_SREG: source register to load data from (NLA_U32: nft_registers) * @NFTA_PAYLOAD_CSUM_TYPE: checksum type (NLA_U32) * @NFTA_PAYLOAD_CSUM_OFFSET: checksum offset relative to base (NLA_U32) + * @NFTA_PAYLOAD_CSUM_FLAGS: checksum flags (NLA_U32) */ enum nft_payload_attributes { NFTA_PAYLOAD_UNSPEC, @@ -679,6 +684,7 @@ enum nft_payload_attributes { NFTA_PAYLOAD_SREG, NFTA_PAYLOAD_CSUM_TYPE, NFTA_PAYLOAD_CSUM_OFFSET, + NFTA_PAYLOAD_CSUM_FLAGS, __NFTA_PAYLOAD_MAX }; #define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 98fb5d7..36d2b10 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2008-2009 Patrick McHardy + * Copyright (c) 2016 Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,6 +18,10 @@ #include #include #include +/* For layer 4 checksum field offset. */ +#include +#include +#include /* add vlan header into the user buffer for if tag was removed by offloads */ static bool @@ -164,6 +169,87 @@ const struct nft_expr_ops nft_payload_fast_ops = { .dump = nft_payload_dump, }; +static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum) +{ + *sum = csum_fold(csum_add(csum_sub(~csum_unfold(*sum), fsum), tsum)); + if (*sum == 0) + *sum = CSUM_MANGLED_0; +} + +static bool nft_payload_udp_checksum(struct sk_buff *skb, unsigned int thoff) +{ + struct udphdr *uh, _uh; + + uh = skb_header_pointer(skb, thoff, sizeof(_uh), &_uh); + if (!uh) + return false; + + return uh->check; +} + +static int nft_payload_l4csum_offset(const struct nft_pktinfo *pkt, + struct sk_buff *skb, + unsigned int *l4csum_offset) +{ + switch (pkt->tprot) { + case IPPROTO_TCP: + *l4csum_offset = offsetof(struct tcphdr, check); + break; + case IPPROTO_UDP: + if (!nft_payload_udp_checksum(skb, pkt->xt.thoff)) + return -1; + /* Fall through. */ + case IPPROTO_UDPLITE: + *l4csum_offset = offsetof(struct udphdr, check); + break; + case IPPROTO_ICMPV6: + *l4csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); + break; + default: + return -1; + } + + *l4csum_offset += pkt->xt.thoff; + return 0; +} + +static int nft_payload_l4csum_update(const struct nft_pktinfo *pkt, + struct sk_buff *skb, + __wsum fsum, __wsum tsum) +{ + int l4csum_offset; + __sum16 sum; + + /* If we cannot determine layer 4 checksum offset or this packet doesn't + * require layer 4 checksum recalculation, skip this packet. + */ + if (nft_payload_l4csum_offset(pkt, skb, &l4csum_offset) < 0) + return 0; + + if (skb_copy_bits(skb, l4csum_offset, &sum, sizeof(sum)) < 0) + return -1; + + /* Checksum mangling for an arbitrary amount of bytes, based on + * inet_proto_csum_replace*() functions. + */ + if (skb->ip_summed != CHECKSUM_PARTIAL) { + nft_csum_replace(&sum, fsum, tsum); + if (skb->ip_summed == CHECKSUM_COMPLETE) { + skb->csum = ~csum_add(csum_sub(~(skb->csum), fsum), + tsum); + } + } else { + sum = ~csum_fold(csum_add(csum_sub(csum_unfold(sum), fsum), + tsum)); + } + + if (!skb_make_writable(skb, l4csum_offset + sizeof(sum)) || + skb_store_bits(skb, l4csum_offset, &sum, sizeof(sum)) < 0) + return -1; + + return 0; +} + static void nft_payload_set_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -204,14 +290,15 @@ static void nft_payload_set_eval(const struct nft_expr *expr, fsum = skb_checksum(skb, offset, priv->len, 0); tsum = csum_partial(src, priv->len, 0); - sum = csum_fold(csum_add(csum_sub(~csum_unfold(sum), fsum), - tsum)); - if (sum == 0) - sum = CSUM_MANGLED_0; + nft_csum_replace(&sum, fsum, tsum); if (!skb_make_writable(skb, csum_offset + sizeof(sum)) || skb_store_bits(skb, csum_offset, &sum, sizeof(sum)) < 0) goto err; + + if (priv->csum_flags && + nft_payload_l4csum_update(pkt, skb, fsum, tsum) < 0) + goto err; } if (!skb_make_writable(skb, max(offset + priv->len, 0)) || @@ -240,6 +327,15 @@ static int nft_payload_set_init(const struct nft_ctx *ctx, if (tb[NFTA_PAYLOAD_CSUM_OFFSET]) priv->csum_offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_CSUM_OFFSET])); + if (tb[NFTA_PAYLOAD_CSUM_FLAGS]) { + u32 flags; + + flags = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_CSUM_FLAGS])); + if (flags & ~NFT_PAYLOAD_L4CSUM_PSEUDOHDR) + return -EINVAL; + + priv->csum_flags = flags; + } switch (priv->csum_type) { case NFT_PAYLOAD_CSUM_NONE: @@ -262,7 +358,8 @@ static int nft_payload_set_dump(struct sk_buff *skb, const struct nft_expr *expr nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len)) || nla_put_be32(skb, NFTA_PAYLOAD_CSUM_TYPE, htonl(priv->csum_type)) || nla_put_be32(skb, NFTA_PAYLOAD_CSUM_OFFSET, - htonl(priv->csum_offset))) + htonl(priv->csum_offset)) || + nla_put_be32(skb, NFTA_PAYLOAD_CSUM_FLAGS, htonl(priv->csum_flags))) goto nla_put_failure; return 0; -- cgit v1.1 From 1ed9887ee31a38f265124c963f8e5b8a1ffb02e3 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Fri, 25 Nov 2016 12:32:07 +0800 Subject: netfilter: xt_multiport: Fix wrong unmatch result with multiple ports I lost one test case in the last commit for xt_multiport. For example, the rule is "-m multiport --dports 22,80,443". When first port is unmatched and the second is matched, the curent codes could not return the right result. It would return false directly when the first port is unmatched. Fixes: dd2602d00f80 ("netfilter: xt_multiport: Use switch case instead of multiple condition checks") Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_multiport.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index ec06fb1..1cde0e4 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -44,12 +44,18 @@ ports_match_v1(const struct xt_multiport_v1 *minfo, switch (minfo->flags) { case XT_MULTIPORT_SOURCE: - return (src >= s && src <= e) ^ minfo->invert; + if (src >= s && src <= e) + return true ^ minfo->invert; + break; case XT_MULTIPORT_DESTINATION: - return (dst >= s && dst <= e) ^ minfo->invert; + if (dst >= s && dst <= e) + return true ^ minfo->invert; + break; case XT_MULTIPORT_EITHER: - return ((dst >= s && dst <= e) || - (src >= s && src <= e)) ^ minfo->invert; + if ((dst >= s && dst <= e) || + (src >= s && src <= e)) + return true ^ minfo->invert; + break; default: break; } @@ -59,11 +65,17 @@ ports_match_v1(const struct xt_multiport_v1 *minfo, switch (minfo->flags) { case XT_MULTIPORT_SOURCE: - return (src == s) ^ minfo->invert; + if (src == s) + return true ^ minfo->invert; + break; case XT_MULTIPORT_DESTINATION: - return (dst == s) ^ minfo->invert; + if (dst == s) + return true ^ minfo->invert; + break; case XT_MULTIPORT_EITHER: - return (src == s || dst == s) ^ minfo->invert; + if (src == s || dst == s) + return true ^ minfo->invert; + break; default: break; } -- cgit v1.1 From df122f58b834b24c27d7e2ac02a4910d3e56f6ae Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 28 Nov 2016 11:40:05 +0100 Subject: netfilter: ingress: translate 0 nf_hook_slow retval to -1 The caller assumes that < 0 means that skb was stolen (or free'd). All other return values continue skb processing. nf_hook_slow returns 3 different return value types: A) a (negative) errno value: the skb was dropped (NF_DROP, e.g. by iptables '-j DROP' rule). B) 0. The skb was stolen by the hook or queued to userspace. C) 1. all hooks returned NF_ACCEPT so the caller should invoke the okfn so packet processing can continue. nft ingress facility currently doesn't have the 'okfn' that the NF_HOOK() macros use; there is no nfqueue support either. So 1 means that nf_hook_ingress() caller should go on processing the skb. In order to allow use of NF_STOLEN from ingress we need to translate this to an errno number, else we'd crash because we continue with already-free'd (or about to be free-d) skb. The errno value isn't checked, its just important that its less than 0, so return -1. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_ingress.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/netfilter_ingress.h b/include/linux/netfilter_ingress.h index 2dc3b49..5947606 100644 --- a/include/linux/netfilter_ingress.h +++ b/include/linux/netfilter_ingress.h @@ -19,6 +19,7 @@ static inline int nf_hook_ingress(struct sk_buff *skb) { struct nf_hook_entry *e = rcu_dereference(skb->dev->nf_hooks_ingress); struct nf_hook_state state; + int ret; /* Must recheck the ingress hook head, in the event it became NULL * after the check in nf_hook_ingress_active evaluated to true. @@ -29,7 +30,11 @@ static inline int nf_hook_ingress(struct sk_buff *skb) nf_hook_state_init(&state, NF_NETDEV_INGRESS, NFPROTO_NETDEV, skb->dev, NULL, NULL, dev_net(skb->dev), NULL); - return nf_hook_slow(skb, &state, e); + ret = nf_hook_slow(skb, &state, e); + if (ret == 0) + return -1; + + return ret; } static inline void nf_hook_ingress_init(struct net_device *dev) -- cgit v1.1 From 3bf3276119455bd0fc7a7e31be2823118e613842 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 28 Nov 2016 11:40:06 +0100 Subject: netfilter: add and use nf_fwd_netdev_egress ... so we can use current skb instead of working with a clone. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_dup_netdev.h | 1 + net/netfilter/nf_dup_netdev.c | 33 +++++++++++++++++++++++++-------- net/netfilter/nft_fwd_netdev.c | 4 ++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h index 397dcae..3e91935 100644 --- a/include/net/netfilter/nf_dup_netdev.h +++ b/include/net/netfilter/nf_dup_netdev.h @@ -2,5 +2,6 @@ #define _NF_DUP_NETDEV_H_ void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif); +void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif); #endif diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index 44ae986..c9d7f95 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -14,6 +14,29 @@ #include #include +static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev) +{ + if (skb_mac_header_was_set(skb)) + skb_push(skb, skb->mac_len); + + skb->dev = dev; + dev_queue_xmit(skb); +} + +void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif) +{ + struct net_device *dev; + + dev = dev_get_by_index_rcu(nft_net(pkt), oif); + if (!dev) { + kfree_skb(pkt->skb); + return; + } + + nf_do_netdev_egress(pkt->skb, dev); +} +EXPORT_SYMBOL_GPL(nf_fwd_netdev_egress); + void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif) { struct net_device *dev; @@ -24,14 +47,8 @@ void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif) return; skb = skb_clone(pkt->skb, GFP_ATOMIC); - if (skb == NULL) - return; - - if (skb_mac_header_was_set(skb)) - skb_push(skb, skb->mac_len); - - skb->dev = dev; - dev_queue_xmit(skb); + if (skb) + nf_do_netdev_egress(skb, dev); } EXPORT_SYMBOL_GPL(nf_dup_netdev_egress); diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index 763ebc3..ce13a50 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -26,8 +26,8 @@ static void nft_fwd_netdev_eval(const struct nft_expr *expr, struct nft_fwd_netdev *priv = nft_expr_priv(expr); int oif = regs->data[priv->sreg_dev]; - nf_dup_netdev_egress(pkt, oif); - regs->verdict.code = NF_DROP; + nf_fwd_netdev_egress(pkt, oif); + regs->verdict.code = NF_STOLEN; } static const struct nla_policy nft_fwd_netdev_policy[NFTA_FWD_MAX + 1] = { -- cgit v1.1 From e50092404c1bc7aaeb0a0f4077fa6f07b073a20f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:04:32 +0100 Subject: netfilter: nf_tables: add stateful objects This patch augments nf_tables to support stateful objects. This new infrastructure allows you to create, dump and delete stateful objects, that are identified by a user-defined name. This patch adds the generic infrastructure, follow up patches add support for two stateful objects: counters and quotas. This patch provides a native infrastructure for nf_tables to replace nfacct, the extended accounting infrastructure for iptables. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 79 +++++ include/uapi/linux/netfilter/nf_tables.h | 29 ++ net/netfilter/nf_tables_api.c | 516 +++++++++++++++++++++++++++++++ 3 files changed, 624 insertions(+) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 32970cb..903cd618f 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -875,6 +875,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv); * @list: used internally * @chains: chains in the table * @sets: sets in the table + * @objects: stateful objects in the table * @hgenerator: handle generator state * @use: number of chain references to this table * @flags: table flag (see enum nft_table_flags) @@ -885,6 +886,7 @@ struct nft_table { struct list_head list; struct list_head chains; struct list_head sets; + struct list_head objects; u64 hgenerator; u32 use; u16 flags:14, @@ -935,6 +937,73 @@ int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v); /** + * struct nft_object - nf_tables stateful object + * + * @list: table stateful object list node + * @type: pointer to object type + * @data: pointer to object data + * @name: name of this stateful object + * @genmask: generation mask + * @use: number of references to this stateful object + * @data: object data, layout depends on type + */ +struct nft_object { + struct list_head list; + char name[NFT_OBJ_MAXNAMELEN]; + u32 genmask:2, + use:30; + /* runtime data below here */ + const struct nft_object_type *type ____cacheline_aligned; + unsigned char data[] + __attribute__((aligned(__alignof__(u64)))); +}; + +static inline void *nft_obj_data(const struct nft_object *obj) +{ + return (void *)obj->data; +} + +#define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr)) + +struct nft_object *nf_tables_obj_lookup(const struct nft_table *table, + const struct nlattr *nla, u32 objtype, + u8 genmask); + +/** + * struct nft_object_type - stateful object type + * + * @eval: stateful object evaluation function + * @list: list node in list of object types + * @type: stateful object numeric type + * @size: stateful object size + * @owner: module owner + * @maxattr: maximum netlink attribute + * @policy: netlink attribute policy + * @init: initialize object from netlink attributes + * @destroy: release existing stateful object + * @dump: netlink dump stateful object + */ +struct nft_object_type { + void (*eval)(struct nft_object *obj, + struct nft_regs *regs, + const struct nft_pktinfo *pkt); + struct list_head list; + u32 type; + unsigned int size; + unsigned int maxattr; + struct module *owner; + const struct nla_policy *policy; + int (*init)(const struct nlattr * const tb[], + struct nft_object *obj); + void (*destroy)(struct nft_object *obj); + int (*dump)(struct sk_buff *skb, + const struct nft_object *obj); +}; + +int nft_register_obj(struct nft_object_type *obj_type); +void nft_unregister_obj(struct nft_object_type *obj_type); + +/** * struct nft_traceinfo - nft tracing information and state * * @pkt: pktinfo currently processed @@ -981,6 +1050,9 @@ void nft_trace_notify(struct nft_traceinfo *info); #define MODULE_ALIAS_NFT_SET() \ MODULE_ALIAS("nft-set") +#define MODULE_ALIAS_NFT_OBJ(type) \ + MODULE_ALIAS("nft-obj-" __stringify(type)) + /* * The gencursor defines two generations, the currently active and the * next one. Objects contain a bitmask of 2 bits specifying the generations @@ -1157,4 +1229,11 @@ struct nft_trans_elem { #define nft_trans_elem(trans) \ (((struct nft_trans_elem *)trans->data)->elem) +struct nft_trans_obj { + struct nft_object *obj; +}; + +#define nft_trans_obj(trans) \ + (((struct nft_trans_obj *)trans->data)->obj) + #endif /* _NET_NF_TABLES_H */ diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index f030e59..18e30db 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -4,6 +4,7 @@ #define NFT_TABLE_MAXNAMELEN 32 #define NFT_CHAIN_MAXNAMELEN 32 #define NFT_SET_MAXNAMELEN 32 +#define NFT_OBJ_MAXNAMELEN 32 #define NFT_USERDATA_MAXLEN 256 /** @@ -85,6 +86,9 @@ enum nft_verdicts { * @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes) * @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes) * @NFT_MSG_TRACE: trace event (enum nft_trace_attributes) + * @NFT_MSG_NEWOBJ: create a stateful object (enum nft_obj_attributes) + * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) + * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -105,6 +109,9 @@ enum nf_tables_msg_types { NFT_MSG_NEWGEN, NFT_MSG_GETGEN, NFT_MSG_TRACE, + NFT_MSG_NEWOBJ, + NFT_MSG_GETOBJ, + NFT_MSG_DELOBJ, NFT_MSG_MAX, }; @@ -1178,6 +1185,28 @@ enum nft_fib_flags { NFTA_FIB_F_OIF = 1 << 4, /* restrict to oif */ }; +#define NFT_OBJECT_UNSPEC 0 + +/** + * enum nft_object_attributes - nf_tables stateful object netlink attributes + * + * @NFTA_OBJ_TABLE: name of the table containing the expression (NLA_STRING) + * @NFTA_OBJ_NAME: name of this expression type (NLA_STRING) + * @NFTA_OBJ_TYPE: stateful object type (NLA_U32) + * @NFTA_OBJ_DATA: stateful object data (NLA_NESTED) + * @NFTA_OBJ_USE: number of references to this expression (NLA_U32) + */ +enum nft_object_attributes { + NFTA_OBJ_UNSPEC, + NFTA_OBJ_TABLE, + NFTA_OBJ_NAME, + NFTA_OBJ_TYPE, + NFTA_OBJ_DATA, + NFTA_OBJ_USE, + __NFTA_OBJ_MAX +}; +#define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1) + /** * enum nft_trace_attributes - nf_tables trace netlink attributes * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index e5194f6f..2ae717c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -22,6 +22,7 @@ #include static LIST_HEAD(nf_tables_expressions); +static LIST_HEAD(nf_tables_objects); /** * nft_register_afinfo - register nf_tables address family info @@ -304,6 +305,38 @@ static int nft_delset(struct nft_ctx *ctx, struct nft_set *set) return err; } +static int nft_trans_obj_add(struct nft_ctx *ctx, int msg_type, + struct nft_object *obj) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_obj)); + if (trans == NULL) + return -ENOMEM; + + if (msg_type == NFT_MSG_NEWOBJ) + nft_activate_next(ctx->net, obj); + + nft_trans_obj(trans) = obj; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + + return 0; +} + +static int nft_delobj(struct nft_ctx *ctx, struct nft_object *obj) +{ + int err; + + err = nft_trans_obj_add(ctx, NFT_MSG_DELOBJ, obj); + if (err < 0) + return err; + + nft_deactivate_next(ctx->net, obj); + ctx->table->use--; + + return err; +} + /* * Tables */ @@ -688,6 +721,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk, nla_strlcpy(table->name, name, NFT_TABLE_MAXNAMELEN); INIT_LIST_HEAD(&table->chains); INIT_LIST_HEAD(&table->sets); + INIT_LIST_HEAD(&table->objects); table->flags = flags; nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); @@ -709,6 +743,7 @@ static int nft_flush_table(struct nft_ctx *ctx) { int err; struct nft_chain *chain, *nc; + struct nft_object *obj, *ne; struct nft_set *set, *ns; list_for_each_entry(chain, &ctx->table->chains, list) { @@ -735,6 +770,12 @@ static int nft_flush_table(struct nft_ctx *ctx) goto out; } + list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) { + err = nft_delobj(ctx, obj); + if (err < 0) + goto out; + } + list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) { if (!nft_is_active_next(ctx->net, chain)) continue; @@ -3838,6 +3879,434 @@ struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set, } EXPORT_SYMBOL_GPL(nft_set_gc_batch_alloc); +/* + * Stateful objects + */ + +/** + * nft_register_obj- register nf_tables stateful object type + * @obj: object type + * + * Registers the object type for use with nf_tables. Returns zero on + * success or a negative errno code otherwise. + */ +int nft_register_obj(struct nft_object_type *obj_type) +{ + if (obj_type->type == NFT_OBJECT_UNSPEC) + return -EINVAL; + + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_add_rcu(&obj_type->list, &nf_tables_objects); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + return 0; +} +EXPORT_SYMBOL_GPL(nft_register_obj); + +/** + * nft_unregister_obj - unregister nf_tables object type + * @obj: object type + * + * Unregisters the object type for use with nf_tables. + */ +void nft_unregister_obj(struct nft_object_type *obj_type) +{ + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_del_rcu(&obj_type->list); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); +} +EXPORT_SYMBOL_GPL(nft_unregister_obj); + +struct nft_object *nf_tables_obj_lookup(const struct nft_table *table, + const struct nlattr *nla, + u32 objtype, u8 genmask) +{ + struct nft_object *obj; + + list_for_each_entry(obj, &table->objects, list) { + if (!nla_strcmp(nla, obj->name) && + objtype == obj->type->type && + nft_active_genmask(obj, genmask)) + return obj; + } + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL_GPL(nf_tables_obj_lookup); + +static const struct nla_policy nft_obj_policy[NFTA_OBJ_MAX + 1] = { + [NFTA_OBJ_TABLE] = { .type = NLA_STRING }, + [NFTA_OBJ_NAME] = { .type = NLA_STRING }, + [NFTA_OBJ_TYPE] = { .type = NLA_U32 }, + [NFTA_OBJ_DATA] = { .type = NLA_NESTED }, +}; + +static struct nft_object *nft_obj_init(const struct nft_object_type *type, + const struct nlattr *attr) +{ + struct nlattr *tb[type->maxattr + 1]; + struct nft_object *obj; + int err; + + if (attr) { + err = nla_parse_nested(tb, type->maxattr, attr, type->policy); + if (err < 0) + goto err1; + } else { + memset(tb, 0, sizeof(tb[0]) * (type->maxattr + 1)); + } + + err = -ENOMEM; + obj = kzalloc(sizeof(struct nft_object) + type->size, GFP_KERNEL); + if (obj == NULL) + goto err1; + + err = type->init((const struct nlattr * const *)tb, obj); + if (err < 0) + goto err2; + + obj->type = type; + return obj; +err2: + kfree(obj); +err1: + return ERR_PTR(err); +} + +static int nft_object_dump(struct sk_buff *skb, unsigned int attr, + const struct nft_object *obj) +{ + struct nlattr *nest; + + nest = nla_nest_start(skb, attr); + if (!nest) + goto nla_put_failure; + if (obj->type->dump(skb, obj) < 0) + goto nla_put_failure; + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + return -1; +} + +static const struct nft_object_type *__nft_obj_type_get(u32 objtype) +{ + const struct nft_object_type *type; + + list_for_each_entry(type, &nf_tables_objects, list) { + if (objtype == type->type) + return type; + } + return NULL; +} + +static const struct nft_object_type *nft_obj_type_get(u32 objtype) +{ + const struct nft_object_type *type; + + type = __nft_obj_type_get(objtype); + if (type != NULL && try_module_get(type->owner)) + return type; + +#ifdef CONFIG_MODULES + if (type == NULL) { + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + request_module("nft-obj-%u", objtype); + nfnl_lock(NFNL_SUBSYS_NFTABLES); + if (__nft_obj_type_get(objtype)) + return ERR_PTR(-EAGAIN); + } +#endif + return ERR_PTR(-ENOENT); +} + +static int nf_tables_newobj(struct net *net, struct sock *nlsk, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_object_type *type; + u8 genmask = nft_genmask_next(net); + int family = nfmsg->nfgen_family; + struct nft_af_info *afi; + struct nft_table *table; + struct nft_object *obj; + struct nft_ctx ctx; + u32 objtype; + int err; + + if (!nla[NFTA_OBJ_TYPE] || + !nla[NFTA_OBJ_NAME] || + !nla[NFTA_OBJ_DATA]) + return -EINVAL; + + afi = nf_tables_afinfo_lookup(net, family, true); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_OBJ_TABLE], genmask); + if (IS_ERR(table)) + return PTR_ERR(table); + + objtype = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE])); + obj = nf_tables_obj_lookup(table, nla[NFTA_OBJ_NAME], objtype, genmask); + if (IS_ERR(obj)) { + err = PTR_ERR(obj); + if (err != -ENOENT) + return err; + + obj = NULL; + } + + if (obj != NULL) { + if (nlh->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; + + return 0; + } + + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); + + type = nft_obj_type_get(objtype); + if (IS_ERR(type)) + return PTR_ERR(type); + + obj = nft_obj_init(type, nla[NFTA_OBJ_DATA]); + if (IS_ERR(obj)) { + err = PTR_ERR(obj); + goto err1; + } + nla_strlcpy(obj->name, nla[NFTA_OBJ_NAME], NFT_OBJ_MAXNAMELEN); + + err = nft_trans_obj_add(&ctx, NFT_MSG_NEWOBJ, obj); + if (err < 0) + goto err2; + + list_add_tail_rcu(&obj->list, &table->objects); + table->use++; + return 0; +err2: + if (obj->type->destroy) + obj->type->destroy(obj); + kfree(obj); +err1: + module_put(type->owner); + return err; +} + +static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net, + u32 portid, u32 seq, int event, u32 flags, + int family, const struct nft_table *table, + const struct nft_object *obj) +{ + struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + + event |= NFNL_SUBSYS_NFTABLES << 8; + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); + if (nlh == NULL) + goto nla_put_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = htons(net->nft.base_seq & 0xffff); + + if (nla_put_string(skb, NFTA_OBJ_TABLE, table->name) || + nla_put_string(skb, NFTA_OBJ_NAME, obj->name) || + nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->type->type)) || + nla_put_be32(skb, NFTA_OBJ_USE, htonl(obj->use)) || + nft_object_dump(skb, NFTA_OBJ_DATA, obj)) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return 0; + +nla_put_failure: + nlmsg_trim(skb, nlh); + return -1; +} + +static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + const struct nft_object *obj; + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + rcu_read_lock(); + cb->seq = net->nft.base_seq; + + list_for_each_entry_rcu(afi, &net->nft.af_info, list) { + if (family != NFPROTO_UNSPEC && family != afi->family) + continue; + + list_for_each_entry_rcu(table, &afi->tables, list) { + list_for_each_entry_rcu(obj, &table->objects, list) { + if (!nft_is_active(net, obj)) + goto cont; + if (idx < s_idx) + goto cont; + if (idx > s_idx) + memset(&cb->args[1], 0, + sizeof(cb->args) - sizeof(cb->args[0])); + if (nf_tables_fill_obj_info(skb, net, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NFT_MSG_NEWOBJ, + NLM_F_MULTI | NLM_F_APPEND, + afi->family, table, obj) < 0) + goto done; + + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); +cont: + idx++; + } + } + } +done: + rcu_read_unlock(); + + cb->args[0] = idx; + return skb->len; +} + +static int nf_tables_getobj(struct net *net, struct sock *nlsk, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_cur(net); + int family = nfmsg->nfgen_family; + const struct nft_af_info *afi; + const struct nft_table *table; + struct nft_object *obj; + struct sk_buff *skb2; + u32 objtype; + int err; + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nf_tables_dump_obj, + }; + return netlink_dump_start(nlsk, skb, nlh, &c); + } + + if (!nla[NFTA_OBJ_NAME] || + !nla[NFTA_OBJ_TYPE]) + return -EINVAL; + + afi = nf_tables_afinfo_lookup(net, family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_OBJ_TABLE], genmask); + if (IS_ERR(table)) + return PTR_ERR(table); + + objtype = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE])); + obj = nf_tables_obj_lookup(table, nla[NFTA_OBJ_NAME], objtype, genmask); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb2) + return -ENOMEM; + + err = nf_tables_fill_obj_info(skb2, net, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, NFT_MSG_NEWOBJ, 0, + family, table, obj); + if (err < 0) + goto err; + + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); +err: + kfree_skb(skb2); + return err; + + return 0; +} + +static void nft_obj_destroy(struct nft_object *obj) +{ + if (obj->type->destroy) + obj->type->destroy(obj); + + module_put(obj->type->owner); + kfree(obj); +} + +static int nf_tables_delobj(struct net *net, struct sock *nlsk, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + u8 genmask = nft_genmask_next(net); + int family = nfmsg->nfgen_family; + struct nft_af_info *afi; + struct nft_table *table; + struct nft_object *obj; + struct nft_ctx ctx; + u32 objtype; + + if (!nla[NFTA_OBJ_TYPE] || + !nla[NFTA_OBJ_NAME]) + return -EINVAL; + + afi = nf_tables_afinfo_lookup(net, family, true); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_OBJ_TABLE], genmask); + if (IS_ERR(table)) + return PTR_ERR(table); + + objtype = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE])); + obj = nf_tables_obj_lookup(table, nla[NFTA_OBJ_NAME], objtype, genmask); + if (IS_ERR(obj)) + return PTR_ERR(obj); + if (obj->use > 0) + return -EBUSY; + + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); + + return nft_delobj(&ctx, obj); +} + +static int nf_tables_obj_notify(const struct nft_ctx *ctx, + struct nft_object *obj, int event) +{ + struct sk_buff *skb; + int err; + + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) + return 0; + + err = -ENOBUFS; + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + goto err; + + err = nf_tables_fill_obj_info(skb, ctx->net, ctx->portid, ctx->seq, + event, 0, ctx->afi->family, ctx->table, + obj); + if (err < 0) { + kfree_skb(skb); + goto err; + } + + err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); +err: + if (err < 0) { + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, + err); + } + return err; +} + static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, u32 portid, u32 seq) { @@ -3998,6 +4467,21 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { [NFT_MSG_GETGEN] = { .call = nf_tables_getgen, }, + [NFT_MSG_NEWOBJ] = { + .call_batch = nf_tables_newobj, + .attr_count = NFTA_OBJ_MAX, + .policy = nft_obj_policy, + }, + [NFT_MSG_GETOBJ] = { + .call = nf_tables_getobj, + .attr_count = NFTA_OBJ_MAX, + .policy = nft_obj_policy, + }, + [NFT_MSG_DELOBJ] = { + .call_batch = nf_tables_delobj, + .attr_count = NFTA_OBJ_MAX, + .policy = nft_obj_policy, + }, }; static void nft_chain_commit_update(struct nft_trans *trans) @@ -4040,6 +4524,9 @@ static void nf_tables_commit_release(struct nft_trans *trans) nft_set_elem_destroy(nft_trans_elem_set(trans), nft_trans_elem(trans).priv, true); break; + case NFT_MSG_DELOBJ: + nft_obj_destroy(nft_trans_obj(trans)); + break; } kfree(trans); } @@ -4147,6 +4634,17 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) atomic_dec(&te->set->nelems); te->set->ndeact--; break; + case NFT_MSG_NEWOBJ: + nft_clear(net, nft_trans_obj(trans)); + nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), + NFT_MSG_NEWOBJ); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELOBJ: + list_del_rcu(&nft_trans_obj(trans)->list); + nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), + NFT_MSG_DELOBJ); + break; } } @@ -4181,6 +4679,9 @@ static void nf_tables_abort_release(struct nft_trans *trans) nft_set_elem_destroy(nft_trans_elem_set(trans), nft_trans_elem(trans).priv, true); break; + case NFT_MSG_NEWOBJ: + nft_obj_destroy(nft_trans_obj(trans)); + break; } kfree(trans); } @@ -4261,6 +4762,15 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb) nft_trans_destroy(trans); break; + case NFT_MSG_NEWOBJ: + trans->ctx.table->use--; + list_del_rcu(&nft_trans_obj(trans)->list); + break; + case NFT_MSG_DELOBJ: + trans->ctx.table->use++; + nft_clear(trans->ctx.net, nft_trans_obj(trans)); + nft_trans_destroy(trans); + break; } } @@ -4807,6 +5317,7 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) { struct nft_table *table, *nt; struct nft_chain *chain, *nc; + struct nft_object *obj, *ne; struct nft_rule *rule, *nr; struct nft_set *set, *ns; struct nft_ctx ctx = { @@ -4833,6 +5344,11 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) table->use--; nft_set_destroy(set); } + list_for_each_entry_safe(obj, ne, &table->objects, list) { + list_del(&obj->list); + table->use--; + nft_obj_destroy(obj); + } list_for_each_entry_safe(chain, nc, &table->chains, list) { list_del(&chain->list); table->use--; -- cgit v1.1 From b1ce0ced101ee134c5d0bbb378b2c3cadc617f20 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:04:36 +0100 Subject: netfilter: nft_counter: add stateful object type Register a new percpu counter stateful object type into the stateful object infrastructure. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 1 + net/netfilter/nft_counter.c | 140 +++++++++++++++++++++++++------ 2 files changed, 114 insertions(+), 27 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 18e30db..e352ef6 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1186,6 +1186,7 @@ enum nft_fib_flags { }; #define NFT_OBJECT_UNSPEC 0 +#define NFT_OBJECT_COUNTER 1 /** * enum nft_object_attributes - nf_tables stateful object netlink attributes diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index 77db835..6f3dd42 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -31,11 +31,10 @@ struct nft_counter_percpu_priv { struct nft_counter_percpu __percpu *counter; }; -static void nft_counter_eval(const struct nft_expr *expr, - struct nft_regs *regs, - const struct nft_pktinfo *pkt) +static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) { - struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); struct nft_counter_percpu *this_cpu; local_bh_disable(); @@ -47,6 +46,60 @@ static void nft_counter_eval(const struct nft_expr *expr, local_bh_enable(); } +static inline void nft_counter_obj_eval(struct nft_object *obj, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_counter_percpu_priv *priv = nft_obj_data(obj); + + nft_counter_do_eval(priv, regs, pkt); +} + +static int nft_counter_do_init(const struct nlattr * const tb[], + struct nft_counter_percpu_priv *priv) +{ + struct nft_counter_percpu __percpu *cpu_stats; + struct nft_counter_percpu *this_cpu; + + cpu_stats = netdev_alloc_pcpu_stats(struct nft_counter_percpu); + if (cpu_stats == NULL) + return -ENOMEM; + + preempt_disable(); + this_cpu = this_cpu_ptr(cpu_stats); + if (tb[NFTA_COUNTER_PACKETS]) { + this_cpu->counter.packets = + be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); + } + if (tb[NFTA_COUNTER_BYTES]) { + this_cpu->counter.bytes = + be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); + } + preempt_enable(); + priv->counter = cpu_stats; + return 0; +} + +static int nft_counter_obj_init(const struct nlattr * const tb[], + struct nft_object *obj) +{ + struct nft_counter_percpu_priv *priv = nft_obj_data(obj); + + return nft_counter_do_init(tb, priv); +} + +static void nft_counter_do_destroy(struct nft_counter_percpu_priv *priv) +{ + free_percpu(priv->counter); +} + +static void nft_counter_obj_destroy(struct nft_object *obj) +{ + struct nft_counter_percpu_priv *priv = nft_obj_data(obj); + + nft_counter_do_destroy(priv); +} + static void nft_counter_fetch(const struct nft_counter_percpu __percpu *counter, struct nft_counter *total) { @@ -69,9 +122,9 @@ static void nft_counter_fetch(const struct nft_counter_percpu __percpu *counter, } } -static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_counter_do_dump(struct sk_buff *skb, + const struct nft_counter_percpu_priv *priv) { - struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); struct nft_counter total; nft_counter_fetch(priv->counter, &total); @@ -87,36 +140,54 @@ nla_put_failure: return -1; } +static int nft_counter_obj_dump(struct sk_buff *skb, + const struct nft_object *obj) +{ + const struct nft_counter_percpu_priv *priv = nft_obj_data(obj); + + return nft_counter_do_dump(skb, priv); +} + static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, }; +static struct nft_object_type nft_counter_obj __read_mostly = { + .type = NFT_OBJECT_COUNTER, + .size = sizeof(struct nft_counter_percpu_priv), + .maxattr = NFTA_COUNTER_MAX, + .policy = nft_counter_policy, + .eval = nft_counter_obj_eval, + .init = nft_counter_obj_init, + .destroy = nft_counter_obj_destroy, + .dump = nft_counter_obj_dump, + .owner = THIS_MODULE, +}; + +static void nft_counter_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); + + nft_counter_do_eval(priv, regs, pkt); +} + +static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); + + return nft_counter_do_dump(skb, priv); +} + static int nft_counter_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); - struct nft_counter_percpu __percpu *cpu_stats; - struct nft_counter_percpu *this_cpu; - cpu_stats = netdev_alloc_pcpu_stats(struct nft_counter_percpu); - if (cpu_stats == NULL) - return -ENOMEM; - - preempt_disable(); - this_cpu = this_cpu_ptr(cpu_stats); - if (tb[NFTA_COUNTER_PACKETS]) { - this_cpu->counter.packets = - be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); - } - if (tb[NFTA_COUNTER_BYTES]) { - this_cpu->counter.bytes = - be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); - } - preempt_enable(); - priv->counter = cpu_stats; - return 0; + return nft_counter_do_init(tb, priv); } static void nft_counter_destroy(const struct nft_ctx *ctx, @@ -124,7 +195,7 @@ static void nft_counter_destroy(const struct nft_ctx *ctx, { struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); - free_percpu(priv->counter); + nft_counter_do_destroy(priv); } static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src) @@ -174,12 +245,26 @@ static struct nft_expr_type nft_counter_type __read_mostly = { static int __init nft_counter_module_init(void) { - return nft_register_expr(&nft_counter_type); + int err; + + err = nft_register_obj(&nft_counter_obj); + if (err < 0) + return err; + + err = nft_register_expr(&nft_counter_type); + if (err < 0) + goto err1; + + return 0; +err1: + nft_unregister_obj(&nft_counter_obj); + return err; } static void __exit nft_counter_module_exit(void) { nft_unregister_expr(&nft_counter_type); + nft_unregister_obj(&nft_counter_obj); } module_init(nft_counter_module_init); @@ -188,3 +273,4 @@ module_exit(nft_counter_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Patrick McHardy "); MODULE_ALIAS_NFT_EXPR("counter"); +MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_COUNTER); -- cgit v1.1 From 173705d9a2df1490478bf0d39f1b517bd489c8fa Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:04:43 +0100 Subject: netfilter: nft_quota: add stateful object type Register a new quota stateful object type into the new stateful object infrastructure. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 1 + net/netfilter/nft_quota.c | 96 +++++++++++++++++++++++++++----- 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index e352ef6..ad0577b 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1187,6 +1187,7 @@ enum nft_fib_flags { #define NFT_OBJECT_UNSPEC 0 #define NFT_OBJECT_COUNTER 1 +#define NFT_OBJECT_QUOTA 2 /** * enum nft_object_attributes - nf_tables stateful object netlink attributes diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index c00104c..09ce72b 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -27,12 +27,10 @@ static inline bool nft_overquota(struct nft_quota *priv, return atomic64_sub_return(pkt->skb->len, &priv->remain) < 0; } -static void nft_quota_eval(const struct nft_expr *expr, - struct nft_regs *regs, - const struct nft_pktinfo *pkt) +static inline void nft_quota_do_eval(struct nft_quota *priv, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) { - struct nft_quota *priv = nft_expr_priv(expr); - if (nft_overquota(priv, pkt) ^ priv->invert) regs->verdict.code = NFT_BREAK; } @@ -42,11 +40,18 @@ static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = { [NFTA_QUOTA_FLAGS] = { .type = NLA_U32 }, }; -static int nft_quota_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +static void nft_quota_obj_eval(struct nft_object *obj, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_quota *priv = nft_obj_data(obj); + + nft_quota_do_eval(priv, regs, pkt); +} + +static int nft_quota_do_init(const struct nlattr * const tb[], + struct nft_quota *priv) { - struct nft_quota *priv = nft_expr_priv(expr); u32 flags = 0; u64 quota; @@ -70,9 +75,16 @@ static int nft_quota_init(const struct nft_ctx *ctx, return 0; } -static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_quota_obj_init(const struct nlattr * const tb[], + struct nft_object *obj) +{ + struct nft_quota *priv = nft_obj_data(obj); + + return nft_quota_do_init(tb, priv); +} + +static int nft_quota_do_dump(struct sk_buff *skb, const struct nft_quota *priv) { - const struct nft_quota *priv = nft_expr_priv(expr); u32 flags = priv->invert ? NFT_QUOTA_F_INV : 0; if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(priv->quota), @@ -85,6 +97,49 @@ nla_put_failure: return -1; } +static int nft_quota_obj_dump(struct sk_buff *skb, const struct nft_object *obj) +{ + struct nft_quota *priv = nft_obj_data(obj); + + return nft_quota_do_dump(skb, priv); +} + +static struct nft_object_type nft_quota_obj __read_mostly = { + .type = NFT_OBJECT_QUOTA, + .size = sizeof(struct nft_quota), + .maxattr = NFTA_QUOTA_MAX, + .policy = nft_quota_policy, + .init = nft_quota_obj_init, + .eval = nft_quota_obj_eval, + .dump = nft_quota_obj_dump, + .owner = THIS_MODULE, +}; + +static void nft_quota_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_quota *priv = nft_expr_priv(expr); + + nft_quota_do_eval(priv, regs, pkt); +} + +static int nft_quota_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_quota *priv = nft_expr_priv(expr); + + return nft_quota_do_init(tb, priv); +} + +static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_quota *priv = nft_expr_priv(expr); + + return nft_quota_do_dump(skb, priv); +} + static struct nft_expr_type nft_quota_type; static const struct nft_expr_ops nft_quota_ops = { .type = &nft_quota_type, @@ -105,12 +160,26 @@ static struct nft_expr_type nft_quota_type __read_mostly = { static int __init nft_quota_module_init(void) { - return nft_register_expr(&nft_quota_type); + int err; + + err = nft_register_obj(&nft_quota_obj); + if (err < 0) + return err; + + err = nft_register_expr(&nft_quota_type); + if (err < 0) + goto err1; + + return 0; +err1: + nft_unregister_obj(&nft_quota_obj); + return err; } static void __exit nft_quota_module_exit(void) { - nft_unregister_expr(&nft_quota_type); + nft_unregister_expr(&nft_quota_type); + nft_unregister_obj(&nft_quota_obj); } module_init(nft_quota_module_init); @@ -119,3 +188,4 @@ module_exit(nft_quota_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Pablo Neira Ayuso "); MODULE_ALIAS_NFT_EXPR("quota"); +MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_QUOTA); -- cgit v1.1 From c97d22e68bfedfacb9e752dee536c69916ae0933 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:05:38 +0100 Subject: netfilter: nf_tables: add stateful object reference expression This new expression allows us to refer to existing stateful objects from rules. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 14 ++++ net/netfilter/Kconfig | 6 ++ net/netfilter/Makefile | 1 + net/netfilter/nft_objref.c | 112 +++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 net/netfilter/nft_objref.c diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index ad0577b..1043ce4 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1138,6 +1138,20 @@ enum nft_fwd_attributes { #define NFTA_FWD_MAX (__NFTA_FWD_MAX - 1) /** + * enum nft_objref_attributes - nf_tables stateful object expression netlink attributes + * + * @NFTA_OBJREF_IMM_TYPE: object type for immediate reference (NLA_U32: nft_register) + * @NFTA_OBJREF_IMM_NAME: object name for immediate reference (NLA_STRING) + */ +enum nft_objref_attributes { + NFTA_OBJREF_UNSPEC, + NFTA_OBJREF_IMM_TYPE, + NFTA_OBJREF_IMM_NAME, + __NFTA_OBJREF_MAX +}; +#define NFTA_OBJREF_MAX (__NFTA_OBJREF_MAX - 1) + +/** * enum nft_gen_attributes - nf_tables ruleset generation attributes * * @NFTA_GEN_ID: Ruleset generation ID (NLA_U32) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index def4be0..63729b4 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -551,6 +551,12 @@ config NFT_NAT This option adds the "nat" expression that you can use to perform typical Network Address Translation (NAT) packet transformations. +config NFT_OBJREF + tristate "Netfilter nf_tables stateful object reference module" + help + This option adds the "objref" expression that allows you to refer to + stateful objects, such as counters and quotas. + config NFT_QUEUE depends on NETFILTER_NETLINK_QUEUE tristate "Netfilter nf_tables queue module" diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index e4c8c1d..ca30d19 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_NFT_NUMGEN) += nft_numgen.o obj-$(CONFIG_NFT_CT) += nft_ct.o obj-$(CONFIG_NFT_LIMIT) += nft_limit.o obj-$(CONFIG_NFT_NAT) += nft_nat.o +obj-$(CONFIG_NFT_OBJREF) += nft_objref.o obj-$(CONFIG_NFT_QUEUE) += nft_queue.o obj-$(CONFIG_NFT_QUOTA) += nft_quota.o obj-$(CONFIG_NFT_REJECT) += nft_reject.o diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c new file mode 100644 index 0000000..23820f7 --- /dev/null +++ b/net/netfilter/nft_objref.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012-2016 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define nft_objref_priv(expr) *((struct nft_object **)nft_expr_priv(expr)) + +static void nft_objref_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_object *obj = nft_objref_priv(expr); + + obj->type->eval(obj, regs, pkt); +} + +static int nft_objref_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_object *obj = nft_objref_priv(expr); + u8 genmask = nft_genmask_next(ctx->net); + u32 objtype; + + if (!tb[NFTA_OBJREF_IMM_NAME] || + !tb[NFTA_OBJREF_IMM_TYPE]) + return -EINVAL; + + objtype = ntohl(nla_get_be32(tb[NFTA_OBJREF_IMM_TYPE])); + obj = nf_tables_obj_lookup(ctx->table, tb[NFTA_OBJREF_IMM_NAME], objtype, + genmask); + if (IS_ERR(obj)) + return -ENOENT; + + nft_objref_priv(expr) = obj; + obj->use++; + + return 0; +} + +static int nft_objref_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_object *obj = nft_objref_priv(expr); + + if (nla_put_string(skb, NFTA_OBJREF_IMM_NAME, obj->name) || + nla_put_be32(skb, NFTA_OBJREF_IMM_TYPE, htonl(obj->type->type))) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +static void nft_objref_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_object *obj = nft_objref_priv(expr); + + obj->use--; +} + +static struct nft_expr_type nft_objref_type; +static const struct nft_expr_ops nft_objref_ops = { + .type = &nft_objref_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_object *)), + .eval = nft_objref_eval, + .init = nft_objref_init, + .destroy = nft_objref_destroy, + .dump = nft_objref_dump, +}; + +static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { + [NFTA_OBJREF_IMM_NAME] = { .type = NLA_STRING }, + [NFTA_OBJREF_IMM_TYPE] = { .type = NLA_U32 }, +}; + +static struct nft_expr_type nft_objref_type __read_mostly = { + .name = "objref", + .ops = &nft_objref_ops, + .policy = nft_objref_policy, + .maxattr = NFTA_OBJREF_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_objref_module_init(void) +{ + return nft_register_expr(&nft_objref_type); +} + +static void __exit nft_objref_module_exit(void) +{ + nft_unregister_expr(&nft_objref_type); +} + +module_init(nft_objref_module_init); +module_exit(nft_objref_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pablo Neira Ayuso "); +MODULE_ALIAS_NFT_EXPR("objref"); -- cgit v1.1 From 352019c858c27e32bf50a6885850ec20571f6e9e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Dec 2016 10:16:00 -0800 Subject: irda: w83977af_ir: Whitespace neatening Remove leading and trailing whitespace. git diff -w shows no differences. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 9674588..d4b1d74 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -263,7 +263,7 @@ static int w83977af_close(struct w83977af_ir *self) { int iobase; - iobase = self->io.fir_base; + iobase = self->io.fir_base; #ifdef CONFIG_USE_W977_PNP /* enter PnP configuration mode */ -- cgit v1.1 From 8a3fb40d6bb46a04fba1bd085bc062e93e3a8d06 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Dec 2016 10:16:01 -0800 Subject: irda: w83977af_ir: More whitespace neatening Add spaces around operators. git diff -w shows no differences. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 230 ++++++++++++++++++++--------------------- 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index d4b1d74..166dd4e 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -110,7 +110,7 @@ static int __init w83977af_init(void) { int i; - for (i=0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) { + for (i = 0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) { if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) return 0; } @@ -127,7 +127,7 @@ static void __exit w83977af_cleanup(void) { int i; - for (i=0; i < ARRAY_SIZE(dev_self); i++) { + for (i = 0; i < ARRAY_SIZE(dev_self); i++) { if (dev_self[i]) w83977af_close(dev_self[i]); } @@ -156,7 +156,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, /* Lock the port that we need */ if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) { pr_debug("%s(), can't get iobase of 0x%03x\n", - __func__ , iobase); + __func__, iobase); return -ENODEV; } @@ -169,7 +169,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, */ dev = alloc_irdadev(sizeof(struct w83977af_ir)); if (dev == NULL) { - printk( KERN_ERR "IrDA: Can't allocate memory for " + printk(KERN_ERR "IrDA: Can't allocate memory for " "IrDA control block!\n"); err = -ENOMEM; goto err_out; @@ -192,8 +192,8 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, /* The only value we must override it the baudrate */ /* FIXME: The HP HDLS-1100 does not support 1152000! */ - self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); + self->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | IR_57600 | + IR_115200 | IR_576000 | IR_1152000 | (IR_4000000 << 8); /* The HP HDLS-1100 needs 1 ms according to the specs */ self->qos.min_turn_time.bits = qos_mtt_bits; @@ -282,7 +282,7 @@ static int w83977af_close(struct w83977af_ir *self) /* Release the PORT that this driver is using */ pr_debug("%s(), Releasing Region %03x\n", - __func__ , self->io.fir_base); + __func__, self->io.fir_base); release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) @@ -303,7 +303,7 @@ static int w83977af_probe(int iobase, int irq, int dma) int version; int i; - for (i=0; i < 2; i++) { + for (i = 0; i < 2; i++) { #ifdef CONFIG_USE_W977_PNP /* Enter PnP configuration mode */ w977_efm_enter(efbase[i]); @@ -317,7 +317,7 @@ static int w83977af_probe(int iobase, int irq, int dma) w977_write_reg(0x70, irq, efbase[i]); #ifdef CONFIG_ARCH_NETWINDER /* Netwinder uses 1 higher than Linux */ - w977_write_reg(0x74, dma+1, efbase[i]); + w977_write_reg(0x74, dma + 1, efbase[i]); #else w977_write_reg(0x74, dma, efbase[i]); #endif /* CONFIG_ARCH_NETWINDER */ @@ -333,23 +333,23 @@ static int w83977af_probe(int iobase, int irq, int dma) #endif /* CONFIG_USE_W977_PNP */ /* Disable Advanced mode */ switch_bank(iobase, SET2); - outb(iobase+2, 0x00); + outb(iobase + 2, 0x00); /* Turn on UART (global) interrupts */ switch_bank(iobase, SET0); - outb(HCR_EN_IRQ, iobase+HCR); + outb(HCR_EN_IRQ, iobase + HCR); /* Switch to advanced mode */ switch_bank(iobase, SET2); - outb(inb(iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1); + outb(inb(iobase + ADCR1) | ADCR1_ADV_SL, iobase + ADCR1); /* Set default IR-mode */ switch_bank(iobase, SET0); - outb(HCR_SIR, iobase+HCR); + outb(HCR_SIR, iobase + HCR); /* Read the Advanced IR ID */ switch_bank(iobase, SET3); - version = inb(iobase+AUID); + version = inb(iobase + AUID); /* Should be 0x1? */ if (0x10 == (version & 0xf0)) { @@ -357,17 +357,17 @@ static int w83977af_probe(int iobase, int irq, int dma) /* Set FIFO size to 32 */ switch_bank(iobase, SET2); - outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); /* Set FIFO threshold to TX17, RX16 */ switch_bank(iobase, SET0); - outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST| - UFR_EN_FIFO,iobase+UFR); + outb(UFR_RXTL | UFR_TXTL | UFR_TXF_RST | UFR_RXF_RST | + UFR_EN_FIFO, iobase + UFR); /* Receiver frame length */ switch_bank(iobase, SET4); - outb(2048 & 0xff, iobase+6); - outb((2048 >> 8) & 0x1f, iobase+7); + outb(2048 & 0xff, iobase + 6); + outb((2048 >> 8) & 0x1f, iobase + 7); /* * Init HP HSDL-1100 transceiver. @@ -382,7 +382,7 @@ static int w83977af_probe(int iobase, int irq, int dma) * CIRRX pin 40 connected to pin 37 */ switch_bank(iobase, SET7); - outb(0x40, iobase+7); + outb(0x40, iobase + 7); net_info_ratelimited("W83977AF (IR) driver loaded. Version: 0x%02x\n", version); @@ -408,22 +408,22 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) self->io.speed = speed; /* Save current bank */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* Disable interrupts */ switch_bank(iobase, SET0); - outb(0, iobase+ICR); + outb(0, iobase + ICR); /* Select Set 2 */ switch_bank(iobase, SET2); - outb(0x00, iobase+ABHL); + outb(0x00, iobase + ABHL); switch (speed) { - case 9600: outb(0x0c, iobase+ABLL); break; - case 19200: outb(0x06, iobase+ABLL); break; - case 38400: outb(0x03, iobase+ABLL); break; - case 57600: outb(0x02, iobase+ABLL); break; - case 115200: outb(0x01, iobase+ABLL); break; + case 9600: outb(0x0c, iobase + ABLL); break; + case 19200: outb(0x06, iobase + ABLL); break; + case 38400: outb(0x03, iobase + ABLL); break; + case 57600: outb(0x02, iobase + ABLL); break; + case 115200: outb(0x01, iobase + ABLL); break; case 576000: ir_mode = HCR_MIR_576; pr_debug("%s(), handling baud of 576000\n", __func__); @@ -438,36 +438,36 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) break; default: ir_mode = HCR_FIR; - pr_debug("%s(), unknown baud rate of %d\n", __func__ , speed); + pr_debug("%s(), unknown baud rate of %d\n", __func__, speed); break; } /* Set speed mode */ switch_bank(iobase, SET0); - outb(ir_mode, iobase+HCR); + outb(ir_mode, iobase + HCR); /* set FIFO size to 32 */ switch_bank(iobase, SET2); - outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2); /* set FIFO threshold to TX17, RX16 */ switch_bank(iobase, SET0); - outb(0x00, iobase+UFR); /* Reset */ - outb(UFR_EN_FIFO, iobase+UFR); /* First we must enable FIFO */ - outb(0xa7, iobase+UFR); + outb(0x00, iobase + UFR); /* Reset */ + outb(UFR_EN_FIFO, iobase + UFR); /* First we must enable FIFO */ + outb(0xa7, iobase + UFR); netif_wake_queue(self->netdev); /* Enable some interrupts so we can receive frames */ switch_bank(iobase, SET0); if (speed > PIO_MAX_SPEED) { - outb(ICR_EFSFI, iobase+ICR); + outb(ICR_EFSFI, iobase + ICR); w83977af_dma_receive(self); } else - outb(ICR_ERBRI, iobase+ICR); + outb(ICR_ERBRI, iobase + ICR); /* Restore SSR */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); } /* @@ -489,7 +489,7 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, iobase = self->io.fir_base; - pr_debug("%s(%ld), skb->len=%d\n", __func__ , jiffies, + pr_debug("%s(%ld), skb->len=%d\n", __func__, jiffies, (int)skb->len); /* Lock transmit buffer */ @@ -508,7 +508,7 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, } /* Save current set */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* Decide if we should use PIO or DMA transfer */ if (self->io.speed > PIO_MAX_SPEED) { @@ -517,15 +517,15 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, self->tx_buff.len = skb->len; mtt = irda_get_mtt(skb); - pr_debug("%s(%ld), mtt=%d\n", __func__ , jiffies, mtt); + pr_debug("%s(%ld), mtt=%d\n", __func__, jiffies, mtt); if (mtt > 1000) - mdelay(mtt/1000); + mdelay(mtt / 1000); else if (mtt) udelay(mtt); /* Enable DMA interrupt */ switch_bank(iobase, SET0); - outb(ICR_EDMAI, iobase+ICR); + outb(ICR_EDMAI, iobase + ICR); w83977af_dma_write(self, iobase); } else { self->tx_buff.data = self->tx_buff.head; @@ -534,12 +534,12 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, /* Add interrupt on tx low level (will fire immediately) */ switch_bank(iobase, SET0); - outb(ICR_ETXTHI, iobase+ICR); + outb(ICR_ETXTHI, iobase + ICR); } dev_kfree_skb(skb); /* Restore set register */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); return NETDEV_TX_OK; } @@ -553,28 +553,28 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, static void w83977af_dma_write(struct w83977af_ir *self, int iobase) { __u8 set; - pr_debug("%s(), len=%d\n", __func__ , self->tx_buff.len); + pr_debug("%s(), len=%d\n", __func__, self->tx_buff.len); /* Save current set */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* Disable DMA */ switch_bank(iobase, SET0); - outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); /* Choose transmit DMA channel */ switch_bank(iobase, SET2); - outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1); + outb(ADCR1_D_CHSW | /*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase + ADCR1); irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, DMA_MODE_WRITE); self->io.direction = IO_XMIT; /* Enable DMA */ switch_bank(iobase, SET0); - outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR); + outb(inb(iobase + HCR) | HCR_EN_DMA | HCR_TX_WT, iobase + HCR); /* Restore set register */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); } /* @@ -589,28 +589,28 @@ static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) __u8 set; /* Save current bank */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); switch_bank(iobase, SET0); - if (!(inb_p(iobase+USR) & USR_TSRE)) { + if (!(inb_p(iobase + USR) & USR_TSRE)) { pr_debug("%s(), warning, FIFO not empty yet!\n", __func__); fifo_size -= 17; pr_debug("%s(), %d bytes left in tx fifo\n", - __func__ , fifo_size); + __func__, fifo_size); } /* Fill FIFO with current frame */ while ((fifo_size-- > 0) && (actual < len)) { /* Transmit next byte */ - outb(buf[actual++], iobase+TBR); + outb(buf[actual++], iobase + TBR); } pr_debug("%s(), fifo_size %d ; %d sent of %d\n", - __func__ , fifo_size, actual, len); + __func__, fifo_size, actual, len); /* Restore bank */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); return actual; } @@ -627,28 +627,28 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) int iobase; __u8 set; - pr_debug("%s(%ld)\n", __func__ , jiffies); + pr_debug("%s(%ld)\n", __func__, jiffies); IRDA_ASSERT(self != NULL, return;); iobase = self->io.fir_base; /* Save current set */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* Disable DMA */ switch_bank(iobase, SET0); - outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); /* Check for underrun! */ - if (inb(iobase+AUDR) & AUDR_UNDR) { + if (inb(iobase + AUDR) & AUDR_UNDR) { pr_debug("%s(), Transmit underrun!\n", __func__); self->netdev->stats.tx_errors++; self->netdev->stats.tx_fifo_errors++; /* Clear bit, by writing 1 to it */ - outb(AUDR_UNDR, iobase+AUDR); + outb(AUDR_UNDR, iobase + AUDR); } else self->netdev->stats.tx_packets++; @@ -663,7 +663,7 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) netif_wake_queue(self->netdev); /* Restore set */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); } /* @@ -685,19 +685,19 @@ static int w83977af_dma_receive(struct w83977af_ir *self) pr_debug("%s\n", __func__); - iobase= self->io.fir_base; + iobase = self->io.fir_base; /* Save current set */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* Disable DMA */ switch_bank(iobase, SET0); - outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR); /* Choose DMA Rx, DMA Fairness, and Advanced mode */ switch_bank(iobase, SET2); - outb((inb(iobase+ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/|ADCR1_ADV_SL, - iobase+ADCR1); + outb((inb(iobase + ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/ | ADCR1_ADV_SL, + iobase + ADCR1); self->io.direction = IO_RECV; self->rx_buff.data = self->rx_buff.head; @@ -720,21 +720,21 @@ static int w83977af_dma_receive(struct w83977af_ir *self) * be finished transmitting yet */ switch_bank(iobase, SET0); - outb(UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR); + outb(UFR_RXTL | UFR_TXTL | UFR_RXF_RST | UFR_EN_FIFO, iobase + UFR); self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; /* Enable DMA */ switch_bank(iobase, SET0); #ifdef CONFIG_ARCH_NETWINDER - hcr = inb(iobase+HCR); - outb(hcr | HCR_EN_DMA, iobase+HCR); + hcr = inb(iobase + HCR); + outb(hcr | HCR_EN_DMA, iobase + HCR); enable_dma(self->io.dma); spin_unlock_irqrestore(&self->lock, flags); #else - outb(inb(iobase+HCR) | HCR_EN_DMA, iobase+HCR); + outb(inb(iobase + HCR) | HCR_EN_DMA, iobase + HCR); #endif /* Restore set */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); return 0; } @@ -761,17 +761,17 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) iobase = self->io.fir_base; /* Save current set */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); iobase = self->io.fir_base; /* Read status FIFO */ switch_bank(iobase, SET5); - while ((status = inb(iobase+FS_FO)) & FS_FO_FSFDR) { + while ((status = inb(iobase + FS_FO)) & FS_FO_FSFDR) { st_fifo->entries[st_fifo->tail].status = status; - st_fifo->entries[st_fifo->tail].len = inb(iobase+RFLFL); - st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8; + st_fifo->entries[st_fifo->tail].len = inb(iobase + RFLFL); + st_fifo->entries[st_fifo->tail].len |= inb(iobase + RFLFH) << 8; st_fifo->tail++; st_fifo->len++; @@ -814,16 +814,16 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) } else { /* Check if we have transferred all data to memory */ switch_bank(iobase, SET0); - if (inb(iobase+USR) & USR_RDR) { + if (inb(iobase + USR) & USR_RDR) { udelay(80); /* Should be enough!? */ } - skb = dev_alloc_skb(len+1); + skb = dev_alloc_skb(len + 1); if (skb == NULL) { printk(KERN_INFO "%s(), memory squeeze, dropping frame.\n", __func__); /* Restore set register */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); return FALSE; } @@ -833,12 +833,12 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) /* Copy frame without CRC */ if (self->io.speed < 4000000) { - skb_put(skb, len-2); + skb_put(skb, len - 2); skb_copy_to_linear_data(skb, self->rx_buff.data, len - 2); } else { - skb_put(skb, len-4); + skb_put(skb, len - 4); skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); @@ -855,7 +855,7 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) } } /* Restore set register */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); return TRUE; } @@ -877,10 +877,10 @@ static void w83977af_pio_receive(struct w83977af_ir *self) /* Receive all characters in Rx FIFO */ do { - byte = inb(iobase+RBR); + byte = inb(iobase + RBR); async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, byte); - } while (inb(iobase+USR) & USR_RDR); /* Data available */ + } while (inb(iobase + USR) & USR_RDR); /* Data available */ } /* @@ -896,7 +896,7 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) __u8 set; int iobase; - pr_debug("%s(), isr=%#x\n", __func__ , isr); + pr_debug("%s(), isr=%#x\n", __func__, isr); iobase = self->io.fir_base; /* Transmit FIFO low on data */ @@ -916,10 +916,10 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) if (self->tx_buff.len > 0) { new_icr |= ICR_ETXTHI; } else { - set = inb(iobase+SSR); + set = inb(iobase + SSR); switch_bank(iobase, SET0); - outb(AUDR_SFEND, iobase+AUDR); - outb(set, iobase+SSR); + outb(AUDR_SFEND, iobase + AUDR); + outb(set, iobase + SSR); self->netdev->stats.tx_packets++; @@ -965,10 +965,10 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) int iobase; iobase = self->io.fir_base; - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* End of frame detected in FIFO */ - if (isr & (ISR_FEND_I|ISR_FSF_I)) { + if (isr & (ISR_FEND_I | ISR_FSF_I)) { if (w83977af_dma_receive_complete(self)) { /* Wait for next status FIFO interrupt */ @@ -978,11 +978,11 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) /* Set timer value, resolution 1 ms */ switch_bank(iobase, SET4); - outb(0x01, iobase+TMRL); /* 1 ms */ - outb(0x00, iobase+TMRH); + outb(0x01, iobase + TMRL); /* 1 ms */ + outb(0x00, iobase + TMRH); /* Start timer */ - outb(IR_MSL_EN_TMR, iobase+IR_MSL); + outb(IR_MSL_EN_TMR, iobase + IR_MSL); new_icr |= ICR_ETMRI; } @@ -991,7 +991,7 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) if (isr & ISR_TMR_I) { /* Disable timer */ switch_bank(iobase, SET4); - outb(0, iobase+IR_MSL); + outb(0, iobase + IR_MSL); /* Clear timer event */ /* switch_bank(iobase, SET0); */ @@ -1026,7 +1026,7 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) } /* Restore set */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); return new_icr; } @@ -1049,24 +1049,24 @@ static irqreturn_t w83977af_interrupt(int irq, void *dev_id) iobase = self->io.fir_base; /* Save current bank */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); switch_bank(iobase, SET0); - icr = inb(iobase+ICR); - isr = inb(iobase+ISR) & icr; /* Mask out the interesting ones */ + icr = inb(iobase + ICR); + isr = inb(iobase + ISR) & icr; /* Mask out the interesting ones */ - outb(0, iobase+ICR); /* Disable interrupts */ + outb(0, iobase + ICR); /* Disable interrupts */ if (isr) { /* Dispatch interrupt handler for the current speed */ - if (self->io.speed > PIO_MAX_SPEED ) + if (self->io.speed > PIO_MAX_SPEED) icr = w83977af_fir_interrupt(self, isr); else icr = w83977af_sir_interrupt(self, isr); } - outb(icr, iobase+ICR); /* Restore (new) interrupts */ - outb(set, iobase+SSR); /* Restore bank register */ + outb(icr, iobase + ICR); /* Restore (new) interrupts */ + outb(set, iobase + SSR); /* Restore bank register */ return IRQ_RETVAL(isr); } @@ -1088,13 +1088,13 @@ static int w83977af_is_receiving(struct w83977af_ir *self) iobase = self->io.fir_base; /* Check if rx FIFO is not empty */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); switch_bank(iobase, SET2); - if ((inb(iobase+RXFDTH) & 0x3f) != 0) { + if ((inb(iobase + RXFDTH) & 0x3f) != 0) { /* We are receiving something */ status = TRUE; } - outb(set, iobase+SSR); + outb(set, iobase + SSR); } else status = (self->rx_buff.state != OUTSIDE_FRAME); @@ -1123,7 +1123,7 @@ static int w83977af_net_open(struct net_device *dev) iobase = self->io.fir_base; if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, - (void *) dev)) { + (void *)dev)) { return -EAGAIN; } /* @@ -1136,18 +1136,18 @@ static int w83977af_net_open(struct net_device *dev) } /* Save current set */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* Enable some interrupts so we can receive frames again */ switch_bank(iobase, SET0); if (self->io.speed > 115200) { - outb(ICR_EFSFI, iobase+ICR); + outb(ICR_EFSFI, iobase + ICR); w83977af_dma_receive(self); } else - outb(ICR_ERBRI, iobase+ICR); + outb(ICR_ERBRI, iobase + ICR); /* Restore bank register */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); /* Ready to play! */ netif_start_queue(dev); @@ -1195,17 +1195,17 @@ static int w83977af_net_close(struct net_device *dev) disable_dma(self->io.dma); /* Save current set */ - set = inb(iobase+SSR); + set = inb(iobase + SSR); /* Disable interrupts */ switch_bank(iobase, SET0); - outb(0, iobase+ICR); + outb(0, iobase + ICR); free_irq(self->io.irq, dev); free_dma(self->io.dma); /* Restore bank register */ - outb(set, iobase+SSR); + outb(set, iobase + SSR); return 0; } @@ -1218,7 +1218,7 @@ static int w83977af_net_close(struct net_device *dev) */ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct if_irda_req *irq = (struct if_irda_req *) rq; + struct if_irda_req *irq = (struct if_irda_req *)rq; struct w83977af_ir *self; unsigned long flags; int ret = 0; @@ -1229,7 +1229,7 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) IRDA_ASSERT(self != NULL, return -1;); - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__ , dev->name, cmd); + pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); spin_lock_irqsave(&self->lock, flags); -- cgit v1.1 From 47fda1a7669969c2a43d5918eb5e355973820363 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Dec 2016 10:16:02 -0800 Subject: irda: w83977af_ir: Remove and add blank lines Use a more typical vertical spacing style. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 166dd4e..e30cbf0 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -178,7 +178,6 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, self = netdev_priv(dev); spin_lock_init(&self->lock); - /* Initialize IO */ self->io.fir_base = iobase; self->io.irq = irq; @@ -553,6 +552,7 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, static void w83977af_dma_write(struct w83977af_ir *self, int iobase) { __u8 set; + pr_debug("%s(), len=%d\n", __func__, self->tx_buff.len); /* Save current set */ @@ -652,7 +652,6 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) } else self->netdev->stats.tx_packets++; - if (self->new_speed) { w83977af_change_speed(self, self->new_speed); self->new_speed = 0; @@ -1114,7 +1113,6 @@ static int w83977af_net_open(struct net_device *dev) char hwname[32]; __u8 set; - IRDA_ASSERT(dev != NULL, return -1;); self = netdev_priv(dev); @@ -1263,7 +1261,6 @@ MODULE_AUTHOR("Dag Brattli "); MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver"); MODULE_LICENSE("GPL"); - module_param(qos_mtt_bits, int, 0); MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time"); module_param_array(io, int, NULL, 0); -- cgit v1.1 From c947164698cd425103d4e332e67f1dc5d85e826d Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Dec 2016 10:16:03 -0800 Subject: irda: w83977af_ir: Neaten pointer comparisons Convert pointer comparisons to NULL. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index e30cbf0..92f1525 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -168,7 +168,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, * Allocate new instance of the driver */ dev = alloc_irdadev(sizeof(struct w83977af_ir)); - if (dev == NULL) { + if (!dev) { printk(KERN_ERR "IrDA: Can't allocate memory for " "IrDA control block!\n"); err = -ENOMEM; @@ -206,7 +206,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, self->rx_buff.head = dma_zalloc_coherent(NULL, self->rx_buff.truesize, &self->rx_buff_dma, GFP_KERNEL); - if (self->rx_buff.head == NULL) { + if (!self->rx_buff.head) { err = -ENOMEM; goto err_out1; } @@ -214,7 +214,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, self->tx_buff.head = dma_zalloc_coherent(NULL, self->tx_buff.truesize, &self->tx_buff_dma, GFP_KERNEL); - if (self->tx_buff.head == NULL) { + if (!self->tx_buff.head) { err = -ENOMEM; goto err_out2; } @@ -629,7 +629,7 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) pr_debug("%s(%ld)\n", __func__, jiffies); - IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self, return;); iobase = self->io.fir_base; @@ -680,7 +680,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self) unsigned long flags; __u8 hcr; #endif - IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self, return -1;); pr_debug("%s\n", __func__); @@ -818,7 +818,7 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) } skb = dev_alloc_skb(len + 1); - if (skb == NULL) { + if (!skb) { printk(KERN_INFO "%s(), memory squeeze, dropping frame.\n", __func__); /* Restore set register */ @@ -870,7 +870,7 @@ static void w83977af_pio_receive(struct w83977af_ir *self) __u8 byte = 0x00; int iobase; - IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self, return;); iobase = self->io.fir_base; @@ -1081,7 +1081,7 @@ static int w83977af_is_receiving(struct w83977af_ir *self) int iobase; __u8 set; - IRDA_ASSERT(self != NULL, return FALSE;); + IRDA_ASSERT(self, return FALSE;); if (self->io.speed > 115200) { iobase = self->io.fir_base; @@ -1113,10 +1113,10 @@ static int w83977af_net_open(struct net_device *dev) char hwname[32]; __u8 set; - IRDA_ASSERT(dev != NULL, return -1;); + IRDA_ASSERT(dev, return -1;); self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); + IRDA_ASSERT(self, return 0;); iobase = self->io.fir_base; @@ -1174,11 +1174,11 @@ static int w83977af_net_close(struct net_device *dev) int iobase; __u8 set; - IRDA_ASSERT(dev != NULL, return -1;); + IRDA_ASSERT(dev, return -1;); self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return 0;); + IRDA_ASSERT(self, return 0;); iobase = self->io.fir_base; @@ -1221,11 +1221,11 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) unsigned long flags; int ret = 0; - IRDA_ASSERT(dev != NULL, return -1;); + IRDA_ASSERT(dev, return -1;); self = netdev_priv(dev); - IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self, return -1;); pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); -- cgit v1.1 From ae9e736b32a92d9a6f95ba0d25fb2666e3367953 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Dec 2016 10:16:04 -0800 Subject: irda: w83977af_ir: Use the common brace style Add braces where appropriate and remove an unnecessary else. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 92f1525..5d5ec89 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -462,8 +462,9 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) if (speed > PIO_MAX_SPEED) { outb(ICR_EFSFI, iobase + ICR); w83977af_dma_receive(self); - } else + } else { outb(ICR_ERBRI, iobase + ICR); + } /* Restore SSR */ outb(set, iobase + SSR); @@ -502,8 +503,8 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, w83977af_change_speed(self, speed); dev_kfree_skb(skb); return NETDEV_TX_OK; - } else - self->new_speed = speed; + } + self->new_speed = speed; } /* Save current set */ @@ -649,8 +650,9 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) /* Clear bit, by writing 1 to it */ outb(AUDR_UNDR, iobase + AUDR); - } else + } else { self->netdev->stats.tx_packets++; + } if (self->new_speed) { w83977af_change_speed(self, self->new_speed); @@ -813,9 +815,8 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) } else { /* Check if we have transferred all data to memory */ switch_bank(iobase, SET0); - if (inb(iobase + USR) & USR_RDR) { + if (inb(iobase + USR) & USR_RDR) udelay(80); /* Should be enough!? */ - } skb = dev_alloc_skb(len + 1); if (!skb) { @@ -969,7 +970,6 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) /* End of frame detected in FIFO */ if (isr & (ISR_FEND_I | ISR_FSF_I)) { if (w83977af_dma_receive_complete(self)) { - /* Wait for next status FIFO interrupt */ new_icr |= ICR_EFSFI; } else { @@ -1094,8 +1094,9 @@ static int w83977af_is_receiving(struct w83977af_ir *self) status = TRUE; } outb(set, iobase + SSR); - } else + } else { status = (self->rx_buff.state != OUTSIDE_FRAME); + } return status; } @@ -1141,8 +1142,9 @@ static int w83977af_net_open(struct net_device *dev) if (self->io.speed > 115200) { outb(ICR_EFSFI, iobase + ICR); w83977af_dma_receive(self); - } else + } else { outb(ICR_ERBRI, iobase + ICR); + } /* Restore bank register */ outb(set, iobase + SSR); -- cgit v1.1 From 646bf0926d8e9a465abada2ae7c43cd8e1bafdaf Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Dec 2016 10:16:05 -0800 Subject: irda: w83977af_ir: Parenthesis alignment Neaten function declaration and definition arguments. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 5d5ec89..2f95ee2 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -84,13 +84,13 @@ static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL}; /* Some prototypes */ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, - unsigned int dma); + unsigned int dma); static int w83977af_close(struct w83977af_ir *self); static int w83977af_probe(int iobase, int irq, int dma); static int w83977af_dma_receive(struct w83977af_ir *self); static int w83977af_dma_receive_complete(struct w83977af_ir *self); static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, - struct net_device *dev); + struct net_device *dev); static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size); static void w83977af_dma_write(struct w83977af_ir *self, int iobase); static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed); @@ -477,7 +477,7 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) * */ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, - struct net_device *dev) + struct net_device *dev) { struct w83977af_ir *self; __s32 speed; -- cgit v1.1 From 99d8d2159d7c8ecefe1ac37c661cfe8863151ded Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 6 Dec 2016 10:16:06 -0800 Subject: irda: w83977af_ir: Neaten logging Use more common logging style, standardize function output logging use. Miscellanea: o Add and use pr_fmt o Convert printks to pr_ Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/irda/w83977af_ir.c | 56 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 2f95ee2..f293d33 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -39,6 +39,8 @@ * ********************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -155,7 +157,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, /* Lock the port that we need */ if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) { - pr_debug("%s(), can't get iobase of 0x%03x\n", + pr_debug("%s: can't get iobase of 0x%03x\n", __func__, iobase); return -ENODEV; } @@ -169,8 +171,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, */ dev = alloc_irdadev(sizeof(struct w83977af_ir)); if (!dev) { - printk(KERN_ERR "IrDA: Can't allocate memory for " - "IrDA control block!\n"); + pr_err("IrDA: Can't allocate memory for IrDA control block!\n"); err = -ENOMEM; goto err_out; } @@ -229,7 +230,7 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, err = register_netdev(dev); if (err) { - net_err_ratelimited("%s(), register_netdevice() failed!\n", + net_err_ratelimited("%s:, register_netdevice() failed!\n", __func__); goto err_out3; } @@ -280,8 +281,7 @@ static int w83977af_close(struct w83977af_ir *self) unregister_netdev(self->netdev); /* Release the PORT that this driver is using */ - pr_debug("%s(), Releasing Region %03x\n", - __func__, self->io.fir_base); + pr_debug("%s: Releasing Region %03x\n", __func__, self->io.fir_base); release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) @@ -389,7 +389,7 @@ static int w83977af_probe(int iobase, int irq, int dma) return 0; } else { /* Try next extented function register address */ - pr_debug("%s(), Wrong chip version", __func__); + pr_debug("%s: Wrong chip version\n", __func__); } } return -1; @@ -425,19 +425,19 @@ static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) case 115200: outb(0x01, iobase + ABLL); break; case 576000: ir_mode = HCR_MIR_576; - pr_debug("%s(), handling baud of 576000\n", __func__); + pr_debug("%s: handling baud of 576000\n", __func__); break; case 1152000: ir_mode = HCR_MIR_1152; - pr_debug("%s(), handling baud of 1152000\n", __func__); + pr_debug("%s: handling baud of 1152000\n", __func__); break; case 4000000: ir_mode = HCR_FIR; - pr_debug("%s(), handling baud of 4000000\n", __func__); + pr_debug("%s: handling baud of 4000000\n", __func__); break; default: ir_mode = HCR_FIR; - pr_debug("%s(), unknown baud rate of %d\n", __func__, speed); + pr_debug("%s: unknown baud rate of %d\n", __func__, speed); break; } @@ -489,8 +489,7 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, iobase = self->io.fir_base; - pr_debug("%s(%ld), skb->len=%d\n", __func__, jiffies, - (int)skb->len); + pr_debug("%s: %ld, skb->len=%d\n", __func__, jiffies, (int)skb->len); /* Lock transmit buffer */ netif_stop_queue(dev); @@ -517,10 +516,10 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, self->tx_buff.len = skb->len; mtt = irda_get_mtt(skb); - pr_debug("%s(%ld), mtt=%d\n", __func__, jiffies, mtt); - if (mtt > 1000) - mdelay(mtt / 1000); - else if (mtt) + pr_debug("%s: %ld, mtt=%d\n", __func__, jiffies, mtt); + if (mtt > 1000) + mdelay(mtt / 1000); + else if (mtt) udelay(mtt); /* Enable DMA interrupt */ @@ -554,7 +553,7 @@ static void w83977af_dma_write(struct w83977af_ir *self, int iobase) { __u8 set; - pr_debug("%s(), len=%d\n", __func__, self->tx_buff.len); + pr_debug("%s: len=%d\n", __func__, self->tx_buff.len); /* Save current set */ set = inb(iobase + SSR); @@ -594,11 +593,10 @@ static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) switch_bank(iobase, SET0); if (!(inb_p(iobase + USR) & USR_TSRE)) { - pr_debug("%s(), warning, FIFO not empty yet!\n", __func__); + pr_debug("%s: warning, FIFO not empty yet!\n", __func__); fifo_size -= 17; - pr_debug("%s(), %d bytes left in tx fifo\n", - __func__, fifo_size); + pr_debug("%s: %d bytes left in tx fifo\n", __func__, fifo_size); } /* Fill FIFO with current frame */ @@ -607,7 +605,7 @@ static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) outb(buf[actual++], iobase + TBR); } - pr_debug("%s(), fifo_size %d ; %d sent of %d\n", + pr_debug("%s: fifo_size %d ; %d sent of %d\n", __func__, fifo_size, actual, len); /* Restore bank */ @@ -628,7 +626,7 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) int iobase; __u8 set; - pr_debug("%s(%ld)\n", __func__, jiffies); + pr_debug("%s: %ld\n", __func__, jiffies); IRDA_ASSERT(self, return;); @@ -643,7 +641,7 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) /* Check for underrun! */ if (inb(iobase + AUDR) & AUDR_UNDR) { - pr_debug("%s(), Transmit underrun!\n", __func__); + pr_debug("%s: Transmit underrun!\n", __func__); self->netdev->stats.tx_errors++; self->netdev->stats.tx_fifo_errors++; @@ -820,8 +818,8 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) skb = dev_alloc_skb(len + 1); if (!skb) { - printk(KERN_INFO - "%s(), memory squeeze, dropping frame.\n", __func__); + pr_info("%s: memory squeeze, dropping frame\n", + __func__); /* Restore set register */ outb(set, iobase + SSR); @@ -896,7 +894,7 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) __u8 set; int iobase; - pr_debug("%s(), isr=%#x\n", __func__, isr); + pr_debug("%s: isr=%#x\n", __func__, isr); iobase = self->io.fir_base; /* Transmit FIFO low on data */ @@ -932,7 +930,7 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) if (isr & ISR_TXEMP_I) { /* Check if we need to change the speed? */ if (self->new_speed) { - pr_debug("%s(), Changing speed!\n", __func__); + pr_debug("%s: Changing speed!\n", __func__); w83977af_change_speed(self, self->new_speed); self->new_speed = 0; } @@ -1229,7 +1227,7 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) IRDA_ASSERT(self, return -1;); - pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd); + pr_debug("%s: %s, (cmd=0x%X)\n", __func__, dev->name, cmd); spin_lock_irqsave(&self->lock, flags); -- cgit v1.1 From d4aea20d889e05575bb331a3dadf176176f7d631 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 1 Dec 2016 10:34:40 +0100 Subject: tun: Use netif_receive_skb instead of netif_rx This patch changes tun.c to call netif_receive_skb instead of netif_rx when a packet is received (if CONFIG_4KSTACKS is not enabled to avoid stack exhaustion). The difference between the two is that netif_rx queues the packet into the backlog, and netif_receive_skb proccesses the packet in the current context. This patch is required for syzkaller [1] to collect coverage from packet receive paths, when a packet being received through tun (syzkaller collects coverage per process in the process context). As mentioned by Eric this change also speeds up tun/tap. As measured by Peter it speeds up his closed-loop single-stream tap/OVS benchmark by about 23%, from 700k packets/second to 867k packets/second. A similar patch was introduced back in 2010 [2, 3], but the author found out that the patch doesn't help with the task he had in mind (for cgroups to shape network traffic based on the original process) and decided not to go further with it. The main concern back then was about possible stack exhaustion with 4K stacks. [1] https://github.com/google/syzkaller [2] https://www.spinics.net/lists/netdev/thrd440.html#130570 [3] https://www.spinics.net/lists/netdev/msg130570.html Signed-off-by: Andrey Konovalov Acked-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/tun.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a3ac863..a569e61b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1286,7 +1286,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb_probe_transport_header(skb, 0); rxhash = skb_get_hash(skb); +#ifndef CONFIG_4KSTACKS + local_bh_disable(); + netif_receive_skb(skb); + local_bh_enable(); +#else netif_rx_ni(skb); +#endif stats = get_cpu_ptr(tun->pcpu_stats); u64_stats_update_begin(&stats->syncp); -- cgit v1.1 From 7ec9ba11b046b4b7fd768c366870ada60d409295 Mon Sep 17 00:00:00 2001 From: Filip Sadowski Date: Tue, 8 Nov 2016 13:05:13 -0800 Subject: i40e: Driver prints log message on link speed change This patch makes the driver log link speed change. Before applying the patch link messages were printed only on state change. Now message is printed when link is brought up or down and when speed changes. Change-ID: Ifbee14b4b16c24967450b3cecac6e8351dcc8f74 Signed-off-by: Filip Sadowski Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 4cb8fb3..06e3c23 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -596,6 +596,7 @@ struct i40e_vsi { u16 veb_idx; /* index of VEB parent */ struct kobject *kobj; /* sysfs object */ bool current_isup; /* Sync 'link up' logging */ + enum i40e_aq_link_speed current_speed; /* Sync link speed logging */ void *priv; /* client driver data reference. */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5777e49..4534d41 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5225,12 +5225,16 @@ out: */ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) { + enum i40e_aq_link_speed new_speed; char *speed = "Unknown"; char *fc = "Unknown"; - if (vsi->current_isup == isup) + new_speed = vsi->back->hw.phy.link_info.link_speed; + + if ((vsi->current_isup == isup) && (vsi->current_speed == new_speed)) return; vsi->current_isup = isup; + vsi->current_speed = new_speed; if (!isup) { netdev_info(vsi->netdev, "NIC Link is Down\n"); return; -- cgit v1.1 From 4293d5f528b5a0a1b0c1b0c6eb522366822a965a Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 8 Nov 2016 13:05:14 -0800 Subject: i40e: simplify txd use count calculation The i40e_txd_use_count function was fast but confusing. In the comments, it even admits that it's ugly. So replace it with a new function that is (very) slightly faster and has extensive commenting to help the thicker among us (including the author, who will forget in a week) understand how it works. Change-ID: Ifb533f13786a0bf39cb29f77969a5be2c83d9a87 Signed-off-by: Mitch Williams Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 45 +++++++++++++++++---------- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 45 +++++++++++++++++---------- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index de8550f..e065321 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -173,26 +173,37 @@ static inline bool i40e_test_staterr(union i40e_rx_desc *rx_desc, #define I40E_MAX_DATA_PER_TXD_ALIGNED \ (I40E_MAX_DATA_PER_TXD & ~(I40E_MAX_READ_REQ_SIZE - 1)) -/* This ugly bit of math is equivalent to DIV_ROUNDUP(size, X) where X is - * the value I40E_MAX_DATA_PER_TXD_ALIGNED. It is needed due to the fact - * that 12K is not a power of 2 and division is expensive. It is used to - * approximate the number of descriptors used per linear buffer. Note - * that this will overestimate in some cases as it doesn't account for the - * fact that we will add up to 4K - 1 in aligning the 12K buffer, however - * the error should not impact things much as large buffers usually mean - * we will use fewer descriptors then there are frags in an skb. +/** + * i40e_txd_use_count - estimate the number of descriptors needed for Tx + * @size: transmit request size in bytes + * + * Due to hardware alignment restrictions (4K alignment), we need to + * assume that we can have no more than 12K of data per descriptor, even + * though each descriptor can take up to 16K - 1 bytes of aligned memory. + * Thus, we need to divide by 12K. But division is slow! Instead, + * we decompose the operation into shifts and one relatively cheap + * multiply operation. + * + * To divide by 12K, we first divide by 4K, then divide by 3: + * To divide by 4K, shift right by 12 bits + * To divide by 3, multiply by 85, then divide by 256 + * (Divide by 256 is done by shifting right by 8 bits) + * Finally, we add one to round up. Because 256 isn't an exact multiple of + * 3, we'll underestimate near each multiple of 12K. This is actually more + * accurate as we have 4K - 1 of wiggle room that we can fit into the last + * segment. For our purposes this is accurate out to 1M which is orders of + * magnitude greater than our largest possible GSO size. + * + * This would then be implemented as: + * return (((size >> 12) * 85) >> 8) + 1; + * + * Since multiplication and division are commutative, we can reorder + * operations into: + * return ((size * 85) >> 20) + 1; */ static inline unsigned int i40e_txd_use_count(unsigned int size) { - const unsigned int max = I40E_MAX_DATA_PER_TXD_ALIGNED; - const unsigned int reciprocal = ((1ull << 32) - 1 + (max / 2)) / max; - unsigned int adjust = ~(u32)0; - - /* if we rounded up on the reciprocal pull down the adjustment */ - if ((max * reciprocal) > adjust) - adjust = ~(u32)(reciprocal - 1); - - return (u32)((((u64)size * reciprocal) + adjust) >> 32); + return ((size * 85) >> 20) + 1; } /* Tx Descriptors needed, worst case */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index a586e19..a5fc789 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -173,26 +173,37 @@ static inline bool i40e_test_staterr(union i40e_rx_desc *rx_desc, #define I40E_MAX_DATA_PER_TXD_ALIGNED \ (I40E_MAX_DATA_PER_TXD & ~(I40E_MAX_READ_REQ_SIZE - 1)) -/* This ugly bit of math is equivalent to DIV_ROUNDUP(size, X) where X is - * the value I40E_MAX_DATA_PER_TXD_ALIGNED. It is needed due to the fact - * that 12K is not a power of 2 and division is expensive. It is used to - * approximate the number of descriptors used per linear buffer. Note - * that this will overestimate in some cases as it doesn't account for the - * fact that we will add up to 4K - 1 in aligning the 12K buffer, however - * the error should not impact things much as large buffers usually mean - * we will use fewer descriptors then there are frags in an skb. +/** + * i40e_txd_use_count - estimate the number of descriptors needed for Tx + * @size: transmit request size in bytes + * + * Due to hardware alignment restrictions (4K alignment), we need to + * assume that we can have no more than 12K of data per descriptor, even + * though each descriptor can take up to 16K - 1 bytes of aligned memory. + * Thus, we need to divide by 12K. But division is slow! Instead, + * we decompose the operation into shifts and one relatively cheap + * multiply operation. + * + * To divide by 12K, we first divide by 4K, then divide by 3: + * To divide by 4K, shift right by 12 bits + * To divide by 3, multiply by 85, then divide by 256 + * (Divide by 256 is done by shifting right by 8 bits) + * Finally, we add one to round up. Because 256 isn't an exact multiple of + * 3, we'll underestimate near each multiple of 12K. This is actually more + * accurate as we have 4K - 1 of wiggle room that we can fit into the last + * segment. For our purposes this is accurate out to 1M which is orders of + * magnitude greater than our largest possible GSO size. + * + * This would then be implemented as: + * return (((size >> 12) * 85) >> 8) + 1; + * + * Since multiplication and division are commutative, we can reorder + * operations into: + * return ((size * 85) >> 20) + 1; */ static inline unsigned int i40e_txd_use_count(unsigned int size) { - const unsigned int max = I40E_MAX_DATA_PER_TXD_ALIGNED; - const unsigned int reciprocal = ((1ull << 32) - 1 + (max / 2)) / max; - unsigned int adjust = ~(u32)0; - - /* if we rounded up on the reciprocal pull down the adjustment */ - if ((max * reciprocal) > adjust) - adjust = ~(u32)(reciprocal - 1); - - return (u32)((((u64)size * reciprocal) + adjust) >> 32); + return ((size * 85) >> 20) + 1; } /* Tx Descriptors needed, worst case */ -- cgit v1.1 From 1596b5ddbf25c3dd05aed208ea0bb57eac1c5524 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 8 Nov 2016 13:05:15 -0800 Subject: i40e: restore workaround for removing default MAC filter A previous commit 53cb6e9e8949 ("i40e: Removal of workaround for simple MAC address filter deletion") removed a workaround for some firmware versions which was reported to not be necessary in production NICs. Unfortunately this workaround is necessary in some configurations, specifically the Ethernet Controller XL710 for 40GbE QSFP+ (8086:1583). Without this patch, the mentioned NICs with current firmware exhibit issues when adding VLANs, as outlined by the following reproduction: $modprobe i40e $ip link set up $ip link add link vlan100 type vlan id 100 $dmesg | tail kernel: i40e 0000:82:00.0: Error I40E_AQ_RC_EINVAL adding RX filters on PF, promiscuous mode forced on This results in filters being marked as FAILED and setting the device in promiscuous mode. The root cause of receiving the -EINVAL error response appears to be due to a conflict with the default MAC filter which still exists on the default firmware for this device. Attempting to add a new VLAN filter on the default MAC address conflicts with the IGNORE_VLAN setting on the default rule. Change-ID: I4d8f6d48ac5f60cfe981b3baad30eb4d7c170d61 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4534d41..c467cc4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1226,6 +1226,39 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) } /** + * i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM + * @vsi: the PF Main VSI - inappropriate for any other VSI + * @macaddr: the MAC address + * + * Remove whatever filter the firmware set up so the driver can manage + * its own filtering intelligently. + **/ +static void i40e_rm_default_mac_filter(struct i40e_vsi *vsi, u8 *macaddr) +{ + struct i40e_aqc_remove_macvlan_element_data element; + struct i40e_pf *pf = vsi->back; + + /* Only appropriate for the PF main VSI */ + if (vsi->type != I40E_VSI_MAIN) + return; + + memset(&element, 0, sizeof(element)); + ether_addr_copy(element.mac_addr, macaddr); + element.vlan_tag = 0; + /* Ignore error returns, some firmware does it this way... */ + element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; + i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL); + + memset(&element, 0, sizeof(element)); + ether_addr_copy(element.mac_addr, macaddr); + element.vlan_tag = 0; + /* ...and some firmware does it this way. */ + element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH | + I40E_AQC_MACVLAN_DEL_IGNORE_VLAN; + i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL); +} + +/** * i40e_add_filter - Add a mac/vlan filter to the VSI * @vsi: the VSI to be searched * @macaddr: the MAC address @@ -9295,6 +9328,12 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) if (vsi->type == I40E_VSI_MAIN) { SET_NETDEV_DEV(netdev, &pf->pdev->dev); ether_addr_copy(mac_addr, hw->mac.perm_addr); + /* The following steps are necessary to prevent reception + * of tagged packets - some older NVM configurations load a + * default a MAC-VLAN filter that accepts any tagged packet + * which must be replaced by a normal filter. + */ + i40e_rm_default_mac_filter(vsi, mac_addr); spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY); spin_unlock_bh(&vsi->mac_filter_hash_lock); @@ -9828,6 +9867,8 @@ static struct i40e_vsi *i40e_vsi_reinit_setup(struct i40e_vsi *vsi) pf->vsi[pf->lan_vsi]->tc_config.enabled_tc = 0; pf->vsi[pf->lan_vsi]->seid = pf->main_vsi_seid; i40e_vsi_config_tc(pf->vsi[pf->lan_vsi], enabled_tc); + if (vsi->type == I40E_VSI_MAIN) + i40e_rm_default_mac_filter(vsi, pf->hw.mac.perm_addr); /* assign it some queues */ ret = i40e_alloc_rings(vsi); -- cgit v1.1 From 6beb84a73ec5dbd051005068c130195f77e78cfc Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 8 Nov 2016 13:05:16 -0800 Subject: i40e/i40evf: napi_poll must return the work done Currently the function i40e_napi-poll() returns 0 when it clean completely the Rx rings, but this foul budget accounting in core code. Fix this by returning the actual work done, capped to budget - 1, since the core doesn't allow to return the full budget when the driver modifies the NAPI status This is based on a similar change that was made for the ixgbe driver by Paolo Abeni. Change-ID: Ic3d93ad2fa2fc8ce3164bc461e69367da0f9173b Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 2 +- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 5544b50..352cf7c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2027,7 +2027,7 @@ tx_only: else i40e_update_enable_itr(vsi, q_vector); - return 0; + return min(work_done, budget - 1); } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index c4b174af..df67ef3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1490,7 +1490,7 @@ tx_only: else i40e_update_enable_itr(vsi, q_vector); - return 0; + return min(work_done, budget - 1); } /** -- cgit v1.1 From 3c7cbd45bca8ad34c66327c919fc76e0dfee90fe Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 8 Nov 2016 13:05:17 -0800 Subject: i40e: remove code to handle dev_addr specially The netdev->dev_addr MAC filter already exists in the MAC/VLAN hash table, as it is added when we configure the netdev in i40e_configure_netdev. Because we already know that this address will be updated in the hash_for_each loops, we do not need to handle it specially. This removes duplicate code and simplifies the i40e_vsi_add_vlan and i40e_vsi_kill_vlan functions. Because we know these filters must be part of the MAC/VLAN hash table, this should not have any functional impact on what filters are included and is merely a code simplification. Change-ID: I5e648302dbdd7cc29efc6d203b7019c11f0b5705 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 43 +++++------------------------ 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c467cc4..ae4a2b2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2515,17 +2515,6 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) /* Locked once because all functions invoked below iterates list*/ spin_lock_bh(&vsi->mac_filter_hash_lock); - if (vsi->netdev) { - add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, vid); - if (!add_f) { - dev_info(&vsi->back->pdev->dev, - "Could not add vlan filter %d for %pM\n", - vid, vsi->netdev->dev_addr); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - return -ENOMEM; - } - } - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->state == I40E_FILTER_REMOVE) continue; @@ -2539,28 +2528,14 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) } } - /* Now if we add a vlan tag, make sure to check if it is the first - * tag (i.e. a "tag" -1 does exist) and if so replace the -1 "tag" - * with 0, so we now accept untagged and specified tagged traffic - * (and not all tags along with untagged) + /* When we add a new VLAN filter, we need to make sure that all existing + * filters which are marked as vid=-1 (I40E_VLAN_ANY) are converted to + * vid=0. The simplest way is just search for all filters marked as + * vid=-1 and replace them with vid=0. This converts all filters that + * were marked to receive all traffic (tagged or untagged) into + * filters to receive only untagged traffic, so that we don't receive + * tagged traffic for VLANs which we have not configured. */ - if (vid > 0 && vsi->netdev) { - del_f = i40e_find_filter(vsi, vsi->netdev->dev_addr, - I40E_VLAN_ANY); - if (del_f) { - __i40e_del_filter(vsi, del_f); - add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, 0); - if (!add_f) { - dev_info(&vsi->back->pdev->dev, - "Could not add filter 0 for %pM\n", - vsi->netdev->dev_addr); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - return -ENOMEM; - } - } - } - - /* Do not assume that I40E_VLAN_ANY should be reset to VLAN 0 */ if (vid > 0 && !vsi->info.pvid) { hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->state == I40E_FILTER_REMOVE) @@ -2597,7 +2572,6 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) **/ void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) { - struct net_device *netdev = vsi->netdev; struct i40e_mac_filter *f; struct hlist_node *h; int bkt; @@ -2605,9 +2579,6 @@ void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) /* Locked once because all functions invoked below iterates list */ spin_lock_bh(&vsi->mac_filter_hash_lock); - if (vsi->netdev) - i40e_del_filter(vsi, netdev->dev_addr, vid); - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->vlan == vid) __i40e_del_filter(vsi, f); -- cgit v1.1 From 4f9b4307543567b69d03806932c61dcda7f62ca0 Mon Sep 17 00:00:00 2001 From: Henry Tieman Date: Tue, 8 Nov 2016 13:05:18 -0800 Subject: i40e: Blink LED on 1G BaseT boards Before this patch "ethtool -p" was not blinking the LEDs on boards with 1G BaseT PHYs. This commit identifies 1G BaseT boards as having the LEDs connected to the MAC. Also, renamed the flag to be more descriptive of usage. The flag is now I40E_FLAG_PHY_CONTROLS_LEDS. Change-ID: I4eb741da9780da7849ddf2dc4c0cb27ffa42a801 Signed-off-by: Henry Tieman Signed-off-by: Harshitha Ramamurthy Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 10 +++++----- drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 06e3c23..b8f2978 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -356,7 +356,7 @@ struct i40e_pf { #define I40E_FLAG_NO_DCB_SUPPORT BIT_ULL(45) #define I40E_FLAG_USE_SET_LLDP_MIB BIT_ULL(46) #define I40E_FLAG_STOP_FW_LLDP BIT_ULL(47) -#define I40E_FLAG_HAVE_10GBASET_PHY BIT_ULL(48) +#define I40E_FLAG_PHY_CONTROLS_LEDS BIT_ULL(48) #define I40E_FLAG_PF_MAC BIT_ULL(50) #define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) #define I40E_FLAG_HAVE_CRT_RETIMER BIT_ULL(52) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 76753e1..6ba0035 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1890,7 +1890,7 @@ static int i40e_set_phys_id(struct net_device *netdev, switch (state) { case ETHTOOL_ID_ACTIVE: - if (!(pf->flags & I40E_FLAG_HAVE_10GBASET_PHY)) { + if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) { pf->led_status = i40e_led_get(hw); } else { i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL, NULL); @@ -1900,20 +1900,20 @@ static int i40e_set_phys_id(struct net_device *netdev, } return blink_freq; case ETHTOOL_ID_ON: - if (!(pf->flags & I40E_FLAG_HAVE_10GBASET_PHY)) + if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) i40e_led_set(hw, 0xf, false); else ret = i40e_led_set_phy(hw, true, pf->led_status, 0); break; case ETHTOOL_ID_OFF: - if (!(pf->flags & I40E_FLAG_HAVE_10GBASET_PHY)) + if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) i40e_led_set(hw, 0x0, false); else ret = i40e_led_set_phy(hw, false, pf->led_status, 0); break; case ETHTOOL_ID_INACTIVE: - if (!(pf->flags & I40E_FLAG_HAVE_10GBASET_PHY)) { - i40e_led_set(hw, false, pf->led_status); + if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) { + i40e_led_set(hw, pf->led_status, false); } else { ret = i40e_led_set_phy(hw, false, pf->led_status, (pf->phy_led_val | diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index ae4a2b2..dbb854b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -11380,8 +11380,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->main_vsi_seid); if ((pf->hw.device_id == I40E_DEV_ID_10G_BASE_T) || - (pf->hw.device_id == I40E_DEV_ID_10G_BASE_T4)) - pf->flags |= I40E_FLAG_HAVE_10GBASET_PHY; + (pf->hw.device_id == I40E_DEV_ID_10G_BASE_T4)) + pf->flags |= I40E_FLAG_PHY_CONTROLS_LEDS; if (pf->hw.device_id == I40E_DEV_ID_SFP_I_X722) pf->flags |= I40E_FLAG_HAVE_CRT_RETIMER; /* print a string summarizing features */ -- cgit v1.1 From 360238693d2bc93fb6ff73146ed358e84fe67961 Mon Sep 17 00:00:00 2001 From: Bimmy Pujari Date: Tue, 8 Nov 2016 13:05:19 -0800 Subject: Changed version from 1.6.21 to 1.6.25 Signed-off-by: Bimmy Pujari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index dbb854b..aecf63b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -41,7 +41,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 21 +#define DRV_VERSION_BUILD 25 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index ca85021..c0fc533 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -38,7 +38,7 @@ static const char i40evf_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 21 +#define DRV_VERSION_BUILD 25 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) \ -- cgit v1.1 From 5951cf9495bcd2d6beee8c9e5b08671b7e540e4f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 21 Nov 2016 13:03:47 -0800 Subject: i40e: use unsigned printf format specifier for active_filters count Replace the %d specifier used for printing vsi->active_filters and vsi->promisc_threshold with an unsigned %u format specifier. While it is unlikely in practice that these values will ever reach such a large number they are unsigned values and thus should not be interpreted as negative numbers. Change-ID: Iff050fad5a1c8537c4c57fcd527441cd95cfc0d4 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index b8a03a0..f1f41f1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -172,7 +172,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) f->macaddr, f->vlan, i40e_filter_state_string[f->state]); } - dev_info(&pf->pdev->dev, " active_filters %d, promisc_threshold %d, overflow promisc %s\n", + dev_info(&pf->pdev->dev, " active_filters %u, promisc_threshold %u, overflow promisc %s\n", vsi->active_filters, vsi->promisc_threshold, (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state) ? "ON" : "OFF")); -- cgit v1.1 From 3123237af59e91a416380c4871464e94794c072c Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Mon, 21 Nov 2016 13:03:48 -0800 Subject: i40e: Add support for 25G devices Add support for 25G devices - defines and data structures. One tricky part here is that the firmware support for these Devices introduces a mismatch between the PHY type enum and the bitfields for the phy types. This change creates a macro and uses it to increment the 25G PHY values when creating 25G bitfields. Change-ID: I69b24d837d44cf9220bf5cb8dd46c5be89ce490b Signed-off-by: Carolyn Wyborny Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 30 +++++++- drivers/net/ethernet/intel/i40e/i40e_common.c | 11 ++- drivers/net/ethernet/intel/i40e/i40e_devids.h | 2 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 26 ++++++- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +- drivers/net/ethernet/intel/i40e/i40e_type.h | 82 +++++++++++++--------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 3 + .../net/ethernet/intel/i40evf/i40e_adminq_cmd.h | 30 +++++++- drivers/net/ethernet/intel/i40evf/i40e_common.c | 2 + drivers/net/ethernet/intel/i40evf/i40e_devids.h | 2 + drivers/net/ethernet/intel/i40evf/i40e_type.h | 82 +++++++++++++--------- drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 8 +++ .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 3 + 13 files changed, 208 insertions(+), 79 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 67e396b..c9d1f91 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -1642,6 +1642,10 @@ enum i40e_aq_phy_type { I40E_PHY_TYPE_1000BASE_LX = 0x1C, I40E_PHY_TYPE_1000BASE_T_OPTICAL = 0x1D, I40E_PHY_TYPE_20GBASE_KR2 = 0x1E, + I40E_PHY_TYPE_25GBASE_KR = 0x1F, + I40E_PHY_TYPE_25GBASE_CR = 0x20, + I40E_PHY_TYPE_25GBASE_SR = 0x21, + I40E_PHY_TYPE_25GBASE_LR = 0x22, I40E_PHY_TYPE_MAX }; @@ -1650,6 +1654,7 @@ enum i40e_aq_phy_type { #define I40E_LINK_SPEED_10GB_SHIFT 0x3 #define I40E_LINK_SPEED_40GB_SHIFT 0x4 #define I40E_LINK_SPEED_20GB_SHIFT 0x5 +#define I40E_LINK_SPEED_25GB_SHIFT 0x6 enum i40e_aq_link_speed { I40E_LINK_SPEED_UNKNOWN = 0, @@ -1657,7 +1662,8 @@ enum i40e_aq_link_speed { I40E_LINK_SPEED_1GB = BIT(I40E_LINK_SPEED_1000MB_SHIFT), I40E_LINK_SPEED_10GB = BIT(I40E_LINK_SPEED_10GB_SHIFT), I40E_LINK_SPEED_40GB = BIT(I40E_LINK_SPEED_40GB_SHIFT), - I40E_LINK_SPEED_20GB = BIT(I40E_LINK_SPEED_20GB_SHIFT) + I40E_LINK_SPEED_20GB = BIT(I40E_LINK_SPEED_20GB_SHIFT), + I40E_LINK_SPEED_25GB = BIT(I40E_LINK_SPEED_25GB_SHIFT), }; struct i40e_aqc_module_desc { @@ -1690,7 +1696,13 @@ struct i40e_aq_get_phy_abilities_resp { __le32 eeer_val; u8 d3_lpan; #define I40E_AQ_SET_PHY_D3_LPAN_ENA 0x01 - u8 reserved[3]; + u8 phy_type_ext; +#define I40E_AQ_PHY_TYPE_EXT_25G_KR 0X01 +#define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 +#define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 +#define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 + u8 mod_type_ext; + u8 ext_comp_code; u8 phy_id[4]; u8 module_type[3]; u8 qualified_module_count; @@ -1712,7 +1724,12 @@ struct i40e_aq_set_phy_config { /* same bits as above in all */ __le16 eee_capability; __le32 eeer; u8 low_power_ctrl; - u8 reserved[3]; + u8 phy_type_ext; +#define I40E_AQ_PHY_TYPE_EXT_25G_KR 0X01 +#define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 +#define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 +#define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 + u8 reserved[2]; }; I40E_CHECK_CMD_LENGTH(i40e_aq_set_phy_config); @@ -1792,6 +1809,13 @@ struct i40e_aqc_get_link_status { #define I40E_AQ_LINK_TX_DRAINED 0x01 #define I40E_AQ_LINK_TX_FLUSHED 0x03 #define I40E_AQ_LINK_FORCED_40G 0x10 +/* 25G Error Codes */ +#define I40E_AQ_25G_NO_ERR 0X00 +#define I40E_AQ_25G_NOT_PRESENT 0X01 +#define I40E_AQ_25G_NVM_CRC_ERR 0X02 +#define I40E_AQ_25G_SBUS_UCODE_ERR 0X03 +#define I40E_AQ_25G_SERDES_UCODE_ERR 0X04 +#define I40E_AQ_25G_NIMB_UCODE_ERR 0X05 u8 loopback; /* use defines from i40e_aqc_set_lb_mode */ __le16 max_frame_size; u8 config; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index eb392d6..1318c7d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -53,6 +53,8 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: case I40E_DEV_ID_20G_KR2_A: + case I40E_DEV_ID_25G_B: + case I40E_DEV_ID_25G_SFP28: hw->mac.type = I40E_MAC_XL710; break; case I40E_DEV_ID_KX_X722: @@ -1183,6 +1185,8 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw) case I40E_PHY_TYPE_1000BASE_LX: case I40E_PHY_TYPE_40GBASE_SR4: case I40E_PHY_TYPE_40GBASE_LR4: + case I40E_PHY_TYPE_25GBASE_LR: + case I40E_PHY_TYPE_25GBASE_SR: media = I40E_MEDIA_TYPE_FIBER; break; case I40E_PHY_TYPE_100BASE_TX: @@ -1197,6 +1201,7 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw) case I40E_PHY_TYPE_10GBASE_SFPP_CU: case I40E_PHY_TYPE_40GBASE_AOC: case I40E_PHY_TYPE_10GBASE_AOC: + case I40E_PHY_TYPE_25GBASE_CR: media = I40E_MEDIA_TYPE_DA; break; case I40E_PHY_TYPE_1000BASE_KX: @@ -1204,6 +1209,7 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw) case I40E_PHY_TYPE_10GBASE_KR: case I40E_PHY_TYPE_40GBASE_KR4: case I40E_PHY_TYPE_20GBASE_KR2: + case I40E_PHY_TYPE_25GBASE_KR: media = I40E_MEDIA_TYPE_BACKPLANE; break; case I40E_PHY_TYPE_SGMII: @@ -1608,8 +1614,10 @@ i40e_status i40e_aq_get_phy_capabilities(struct i40e_hw *hw, if (hw->aq.asq_last_status == I40E_AQ_RC_EIO) status = I40E_ERR_UNKNOWN_PHY; - if (report_init) + if (report_init) { hw->phy.phy_types = le32_to_cpu(abilities->phy_type); + hw->phy.phy_types |= ((u64)abilities->phy_type_ext << 32); + } return status; } @@ -1701,6 +1709,7 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK; /* Copy over all the old settings */ config.phy_type = abilities.phy_type; + config.phy_type_ext = abilities.phy_type_ext; config.link_speed = abilities.link_speed; config.eee_capability = abilities.eee_capability; config.eeer = abilities.eeer_val; diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h index dd4457d..8e46098 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_devids.h +++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h @@ -39,6 +39,8 @@ #define I40E_DEV_ID_20G_KR2 0x1587 #define I40E_DEV_ID_20G_KR2_A 0x1588 #define I40E_DEV_ID_10G_BASE_T4 0x1589 +#define I40E_DEV_ID_25G_B 0x158A +#define I40E_DEV_ID_25G_SFP28 0x158B #define I40E_DEV_ID_KX_X722 0x37CE #define I40E_DEV_ID_QSFP_X722 0x37CF #define I40E_DEV_ID_SFP_X722 0x37D0 diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 6ba0035..935160a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -265,8 +265,9 @@ static void i40e_partition_setting_complaint(struct i40e_pf *pf) static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, u32 *advertising) { - enum i40e_aq_capabilities_phy_type phy_types = pf->hw.phy.phy_types; struct i40e_link_status *hw_link_info = &pf->hw.phy.link_info; + u64 phy_types = pf->hw.phy.phy_types; + *supported = 0x0; *advertising = 0x0; @@ -369,6 +370,13 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) *advertising |= ADVERTISED_1000baseKX_Full; } + if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_CR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR) { + *supported |= SUPPORTED_Autoneg; + *advertising |= ADVERTISED_Autoneg; + } } /** @@ -491,6 +499,14 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, ADVERTISED_1000baseKX_Full | ADVERTISED_Autoneg; break; + case I40E_PHY_TYPE_25GBASE_KR: + case I40E_PHY_TYPE_25GBASE_CR: + case I40E_PHY_TYPE_25GBASE_SR: + case I40E_PHY_TYPE_25GBASE_LR: + ecmd->supported = SUPPORTED_Autoneg; + ecmd->advertising = ADVERTISED_Autoneg; + /* TODO: add speeds when ethtool is ready to support*/ + break; default: /* if we got here and link is up something bad is afoot */ netdev_info(netdev, "WARNING: Link is up but PHY type 0x%x is not recognized.\n", @@ -512,6 +528,14 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, case I40E_LINK_SPEED_40GB: ethtool_cmd_speed_set(ecmd, SPEED_40000); break; + case I40E_LINK_SPEED_25GB: +#ifdef SPEED_25000 + ethtool_cmd_speed_set(ecmd, SPEED_25000); +#else + netdev_info(netdev, + "Speed is 25G, display not supported by this version of ethtool.\n"); +#endif + break; case I40E_LINK_SPEED_20GB: ethtool_cmd_speed_set(ecmd, SPEED_20000); break; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index aecf63b..b0486c9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -86,6 +86,8 @@ static const struct pci_device_id i40e_pci_tbl[] = { {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_I_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_B), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_SFP28), 0}, /* required last entry */ {0, } }; @@ -5260,6 +5262,9 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) case I40E_LINK_SPEED_20GB: speed = "20 G"; break; + case I40E_LINK_SPEED_25GB: + speed = "25 G"; + break; case I40E_LINK_SPEED_10GB: speed = "10 G"; break; @@ -11368,7 +11373,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_dbg(&pf->pdev->dev, "get supported phy types ret = %s last_status = %s\n", i40e_stat_str(&pf->hw, err), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - pf->hw.phy.phy_types = le32_to_cpu(abilities.phy_type); /* Add a filter to drop all Flow control frames from any VSI from being * transmitted. By doing so we stop a malicious VF from sending out diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index bd1ffae..edc0abd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -213,47 +213,59 @@ struct i40e_link_status { #define I40E_MODULE_TYPE_1000BASE_T 0x08 }; -enum i40e_aq_capabilities_phy_type { - I40E_CAP_PHY_TYPE_SGMII = BIT(I40E_PHY_TYPE_SGMII), - I40E_CAP_PHY_TYPE_1000BASE_KX = BIT(I40E_PHY_TYPE_1000BASE_KX), - I40E_CAP_PHY_TYPE_10GBASE_KX4 = BIT(I40E_PHY_TYPE_10GBASE_KX4), - I40E_CAP_PHY_TYPE_10GBASE_KR = BIT(I40E_PHY_TYPE_10GBASE_KR), - I40E_CAP_PHY_TYPE_40GBASE_KR4 = BIT(I40E_PHY_TYPE_40GBASE_KR4), - I40E_CAP_PHY_TYPE_XAUI = BIT(I40E_PHY_TYPE_XAUI), - I40E_CAP_PHY_TYPE_XFI = BIT(I40E_PHY_TYPE_XFI), - I40E_CAP_PHY_TYPE_SFI = BIT(I40E_PHY_TYPE_SFI), - I40E_CAP_PHY_TYPE_XLAUI = BIT(I40E_PHY_TYPE_XLAUI), - I40E_CAP_PHY_TYPE_XLPPI = BIT(I40E_PHY_TYPE_XLPPI), - I40E_CAP_PHY_TYPE_40GBASE_CR4_CU = BIT(I40E_PHY_TYPE_40GBASE_CR4_CU), - I40E_CAP_PHY_TYPE_10GBASE_CR1_CU = BIT(I40E_PHY_TYPE_10GBASE_CR1_CU), - I40E_CAP_PHY_TYPE_10GBASE_AOC = BIT(I40E_PHY_TYPE_10GBASE_AOC), - I40E_CAP_PHY_TYPE_40GBASE_AOC = BIT(I40E_PHY_TYPE_40GBASE_AOC), - I40E_CAP_PHY_TYPE_100BASE_TX = BIT(I40E_PHY_TYPE_100BASE_TX), - I40E_CAP_PHY_TYPE_1000BASE_T = BIT(I40E_PHY_TYPE_1000BASE_T), - I40E_CAP_PHY_TYPE_10GBASE_T = BIT(I40E_PHY_TYPE_10GBASE_T), - I40E_CAP_PHY_TYPE_10GBASE_SR = BIT(I40E_PHY_TYPE_10GBASE_SR), - I40E_CAP_PHY_TYPE_10GBASE_LR = BIT(I40E_PHY_TYPE_10GBASE_LR), - I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU = BIT(I40E_PHY_TYPE_10GBASE_SFPP_CU), - I40E_CAP_PHY_TYPE_10GBASE_CR1 = BIT(I40E_PHY_TYPE_10GBASE_CR1), - I40E_CAP_PHY_TYPE_40GBASE_CR4 = BIT(I40E_PHY_TYPE_40GBASE_CR4), - I40E_CAP_PHY_TYPE_40GBASE_SR4 = BIT(I40E_PHY_TYPE_40GBASE_SR4), - I40E_CAP_PHY_TYPE_40GBASE_LR4 = BIT(I40E_PHY_TYPE_40GBASE_LR4), - I40E_CAP_PHY_TYPE_1000BASE_SX = BIT(I40E_PHY_TYPE_1000BASE_SX), - I40E_CAP_PHY_TYPE_1000BASE_LX = BIT(I40E_PHY_TYPE_1000BASE_LX), - I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL = - BIT(I40E_PHY_TYPE_1000BASE_T_OPTICAL), - I40E_CAP_PHY_TYPE_20GBASE_KR2 = BIT(I40E_PHY_TYPE_20GBASE_KR2) -}; - struct i40e_phy_info { struct i40e_link_status link_info; struct i40e_link_status link_info_old; bool get_link_info; enum i40e_media_type media_type; /* all the phy types the NVM is capable of */ - enum i40e_aq_capabilities_phy_type phy_types; -}; - + u64 phy_types; +}; + +#define I40E_CAP_PHY_TYPE_SGMII BIT_ULL(I40E_PHY_TYPE_SGMII) +#define I40E_CAP_PHY_TYPE_1000BASE_KX BIT_ULL(I40E_PHY_TYPE_1000BASE_KX) +#define I40E_CAP_PHY_TYPE_10GBASE_KX4 BIT_ULL(I40E_PHY_TYPE_10GBASE_KX4) +#define I40E_CAP_PHY_TYPE_10GBASE_KR BIT_ULL(I40E_PHY_TYPE_10GBASE_KR) +#define I40E_CAP_PHY_TYPE_40GBASE_KR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_KR4) +#define I40E_CAP_PHY_TYPE_XAUI BIT_ULL(I40E_PHY_TYPE_XAUI) +#define I40E_CAP_PHY_TYPE_XFI BIT_ULL(I40E_PHY_TYPE_XFI) +#define I40E_CAP_PHY_TYPE_SFI BIT_ULL(I40E_PHY_TYPE_SFI) +#define I40E_CAP_PHY_TYPE_XLAUI BIT_ULL(I40E_PHY_TYPE_XLAUI) +#define I40E_CAP_PHY_TYPE_XLPPI BIT_ULL(I40E_PHY_TYPE_XLPPI) +#define I40E_CAP_PHY_TYPE_40GBASE_CR4_CU BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4_CU) +#define I40E_CAP_PHY_TYPE_10GBASE_CR1_CU BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1_CU) +#define I40E_CAP_PHY_TYPE_10GBASE_AOC BIT_ULL(I40E_PHY_TYPE_10GBASE_AOC) +#define I40E_CAP_PHY_TYPE_40GBASE_AOC BIT_ULL(I40E_PHY_TYPE_40GBASE_AOC) +#define I40E_CAP_PHY_TYPE_100BASE_TX BIT_ULL(I40E_PHY_TYPE_100BASE_TX) +#define I40E_CAP_PHY_TYPE_1000BASE_T BIT_ULL(I40E_PHY_TYPE_1000BASE_T) +#define I40E_CAP_PHY_TYPE_10GBASE_T BIT_ULL(I40E_PHY_TYPE_10GBASE_T) +#define I40E_CAP_PHY_TYPE_10GBASE_SR BIT_ULL(I40E_PHY_TYPE_10GBASE_SR) +#define I40E_CAP_PHY_TYPE_10GBASE_LR BIT_ULL(I40E_PHY_TYPE_10GBASE_LR) +#define I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU BIT_ULL(I40E_PHY_TYPE_10GBASE_SFPP_CU) +#define I40E_CAP_PHY_TYPE_10GBASE_CR1 BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1) +#define I40E_CAP_PHY_TYPE_40GBASE_CR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4) +#define I40E_CAP_PHY_TYPE_40GBASE_SR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_SR4) +#define I40E_CAP_PHY_TYPE_40GBASE_LR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_LR4) +#define I40E_CAP_PHY_TYPE_1000BASE_SX BIT_ULL(I40E_PHY_TYPE_1000BASE_SX) +#define I40E_CAP_PHY_TYPE_1000BASE_LX BIT_ULL(I40E_PHY_TYPE_1000BASE_LX) +#define I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL \ + BIT_ULL(I40E_PHY_TYPE_1000BASE_T_OPTICAL) +#define I40E_CAP_PHY_TYPE_20GBASE_KR2 BIT_ULL(I40E_PHY_TYPE_20GBASE_KR2) +/* Defining the macro I40E_TYPE_OFFSET to implement a bit shift for some + * PHY types. There is an unused bit (31) in the I40E_CAP_PHY_TYPE_* bit + * fields but no corresponding gap in the i40e_aq_phy_type enumeration. So, + * a shift is needed to adjust for this with values larger than 31. The + * only affected values are I40E_PHY_TYPE_25GBASE_*. + */ +#define I40E_PHY_TYPE_OFFSET 1 +#define I40E_CAP_PHY_TYPE_25GBASE_KR BIT_ULL(I40E_PHY_TYPE_25GBASE_KR + \ + I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_CR BIT_ULL(I40E_PHY_TYPE_25GBASE_CR + \ + I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_SR BIT_ULL(I40E_PHY_TYPE_25GBASE_SR + \ + I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_LR BIT_ULL(I40E_PHY_TYPE_25GBASE_LR + \ + I40E_PHY_TYPE_OFFSET) #define I40E_HW_CAP_MAX_GPIO 30 /* Capabilities of a PF or a VF or the whole device */ struct i40e_hw_capabilities { diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 05ed49b..d28b684 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2921,6 +2921,9 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, case I40E_LINK_SPEED_40GB: speed = 40000; break; + case I40E_LINK_SPEED_25GB: + speed = 25000; + break; case I40E_LINK_SPEED_20GB: speed = 20000; break; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index 40b0eaf..f8d7d95 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -1639,6 +1639,10 @@ enum i40e_aq_phy_type { I40E_PHY_TYPE_1000BASE_LX = 0x1C, I40E_PHY_TYPE_1000BASE_T_OPTICAL = 0x1D, I40E_PHY_TYPE_20GBASE_KR2 = 0x1E, + I40E_PHY_TYPE_25GBASE_KR = 0x1F, + I40E_PHY_TYPE_25GBASE_CR = 0x20, + I40E_PHY_TYPE_25GBASE_SR = 0x21, + I40E_PHY_TYPE_25GBASE_LR = 0x22, I40E_PHY_TYPE_MAX }; @@ -1647,6 +1651,7 @@ enum i40e_aq_phy_type { #define I40E_LINK_SPEED_10GB_SHIFT 0x3 #define I40E_LINK_SPEED_40GB_SHIFT 0x4 #define I40E_LINK_SPEED_20GB_SHIFT 0x5 +#define I40E_LINK_SPEED_25GB_SHIFT 0x6 enum i40e_aq_link_speed { I40E_LINK_SPEED_UNKNOWN = 0, @@ -1654,7 +1659,8 @@ enum i40e_aq_link_speed { I40E_LINK_SPEED_1GB = BIT(I40E_LINK_SPEED_1000MB_SHIFT), I40E_LINK_SPEED_10GB = BIT(I40E_LINK_SPEED_10GB_SHIFT), I40E_LINK_SPEED_40GB = BIT(I40E_LINK_SPEED_40GB_SHIFT), - I40E_LINK_SPEED_20GB = BIT(I40E_LINK_SPEED_20GB_SHIFT) + I40E_LINK_SPEED_20GB = BIT(I40E_LINK_SPEED_20GB_SHIFT), + I40E_LINK_SPEED_25GB = BIT(I40E_LINK_SPEED_25GB_SHIFT), }; struct i40e_aqc_module_desc { @@ -1687,7 +1693,13 @@ struct i40e_aq_get_phy_abilities_resp { __le32 eeer_val; u8 d3_lpan; #define I40E_AQ_SET_PHY_D3_LPAN_ENA 0x01 - u8 reserved[3]; + u8 phy_type_ext; +#define I40E_AQ_PHY_TYPE_EXT_25G_KR 0X01 +#define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 +#define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 +#define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 + u8 mod_type_ext; + u8 ext_comp_code; u8 phy_id[4]; u8 module_type[3]; u8 qualified_module_count; @@ -1709,7 +1721,12 @@ struct i40e_aq_set_phy_config { /* same bits as above in all */ __le16 eee_capability; __le32 eeer; u8 low_power_ctrl; - u8 reserved[3]; + u8 phy_type_ext; +#define I40E_AQ_PHY_TYPE_EXT_25G_KR 0X01 +#define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 +#define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 +#define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 + u8 reserved[2]; }; I40E_CHECK_CMD_LENGTH(i40e_aq_set_phy_config); @@ -1789,6 +1806,13 @@ struct i40e_aqc_get_link_status { #define I40E_AQ_LINK_TX_DRAINED 0x01 #define I40E_AQ_LINK_TX_FLUSHED 0x03 #define I40E_AQ_LINK_FORCED_40G 0x10 +/* 25G Error Codes */ +#define I40E_AQ_25G_NO_ERR 0X00 +#define I40E_AQ_25G_NOT_PRESENT 0X01 +#define I40E_AQ_25G_NVM_CRC_ERR 0X02 +#define I40E_AQ_25G_SBUS_UCODE_ERR 0X03 +#define I40E_AQ_25G_SERDES_UCODE_ERR 0X04 +#define I40E_AQ_25G_NIMB_UCODE_ERR 0X05 u8 loopback; /* use defines from i40e_aqc_set_lb_mode */ __le16 max_frame_size; u8 config; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 7953c13..aa63b7f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -53,6 +53,8 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: case I40E_DEV_ID_20G_KR2_A: + case I40E_DEV_ID_25G_B: + case I40E_DEV_ID_25G_SFP28: hw->mac.type = I40E_MAC_XL710; break; case I40E_DEV_ID_SFP_X722: diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h index 7023570..21dcaee 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_devids.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_devids.h @@ -39,6 +39,8 @@ #define I40E_DEV_ID_20G_KR2 0x1587 #define I40E_DEV_ID_20G_KR2_A 0x1588 #define I40E_DEV_ID_10G_BASE_T4 0x1589 +#define I40E_DEV_ID_25G_B 0x158A +#define I40E_DEV_ID_25G_SFP28 0x158B #define I40E_DEV_ID_VF 0x154C #define I40E_DEV_ID_VF_HV 0x1571 #define I40E_DEV_ID_SFP_X722 0x37D0 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 515484c..c85e8a3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -187,47 +187,59 @@ struct i40e_link_status { #define I40E_MODULE_TYPE_1000BASE_T 0x08 }; -enum i40e_aq_capabilities_phy_type { - I40E_CAP_PHY_TYPE_SGMII = BIT(I40E_PHY_TYPE_SGMII), - I40E_CAP_PHY_TYPE_1000BASE_KX = BIT(I40E_PHY_TYPE_1000BASE_KX), - I40E_CAP_PHY_TYPE_10GBASE_KX4 = BIT(I40E_PHY_TYPE_10GBASE_KX4), - I40E_CAP_PHY_TYPE_10GBASE_KR = BIT(I40E_PHY_TYPE_10GBASE_KR), - I40E_CAP_PHY_TYPE_40GBASE_KR4 = BIT(I40E_PHY_TYPE_40GBASE_KR4), - I40E_CAP_PHY_TYPE_XAUI = BIT(I40E_PHY_TYPE_XAUI), - I40E_CAP_PHY_TYPE_XFI = BIT(I40E_PHY_TYPE_XFI), - I40E_CAP_PHY_TYPE_SFI = BIT(I40E_PHY_TYPE_SFI), - I40E_CAP_PHY_TYPE_XLAUI = BIT(I40E_PHY_TYPE_XLAUI), - I40E_CAP_PHY_TYPE_XLPPI = BIT(I40E_PHY_TYPE_XLPPI), - I40E_CAP_PHY_TYPE_40GBASE_CR4_CU = BIT(I40E_PHY_TYPE_40GBASE_CR4_CU), - I40E_CAP_PHY_TYPE_10GBASE_CR1_CU = BIT(I40E_PHY_TYPE_10GBASE_CR1_CU), - I40E_CAP_PHY_TYPE_10GBASE_AOC = BIT(I40E_PHY_TYPE_10GBASE_AOC), - I40E_CAP_PHY_TYPE_40GBASE_AOC = BIT(I40E_PHY_TYPE_40GBASE_AOC), - I40E_CAP_PHY_TYPE_100BASE_TX = BIT(I40E_PHY_TYPE_100BASE_TX), - I40E_CAP_PHY_TYPE_1000BASE_T = BIT(I40E_PHY_TYPE_1000BASE_T), - I40E_CAP_PHY_TYPE_10GBASE_T = BIT(I40E_PHY_TYPE_10GBASE_T), - I40E_CAP_PHY_TYPE_10GBASE_SR = BIT(I40E_PHY_TYPE_10GBASE_SR), - I40E_CAP_PHY_TYPE_10GBASE_LR = BIT(I40E_PHY_TYPE_10GBASE_LR), - I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU = BIT(I40E_PHY_TYPE_10GBASE_SFPP_CU), - I40E_CAP_PHY_TYPE_10GBASE_CR1 = BIT(I40E_PHY_TYPE_10GBASE_CR1), - I40E_CAP_PHY_TYPE_40GBASE_CR4 = BIT(I40E_PHY_TYPE_40GBASE_CR4), - I40E_CAP_PHY_TYPE_40GBASE_SR4 = BIT(I40E_PHY_TYPE_40GBASE_SR4), - I40E_CAP_PHY_TYPE_40GBASE_LR4 = BIT(I40E_PHY_TYPE_40GBASE_LR4), - I40E_CAP_PHY_TYPE_1000BASE_SX = BIT(I40E_PHY_TYPE_1000BASE_SX), - I40E_CAP_PHY_TYPE_1000BASE_LX = BIT(I40E_PHY_TYPE_1000BASE_LX), - I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL = - BIT(I40E_PHY_TYPE_1000BASE_T_OPTICAL), - I40E_CAP_PHY_TYPE_20GBASE_KR2 = BIT(I40E_PHY_TYPE_20GBASE_KR2) -}; - struct i40e_phy_info { struct i40e_link_status link_info; struct i40e_link_status link_info_old; bool get_link_info; enum i40e_media_type media_type; /* all the phy types the NVM is capable of */ - enum i40e_aq_capabilities_phy_type phy_types; -}; - + u64 phy_types; +}; + +#define I40E_CAP_PHY_TYPE_SGMII BIT_ULL(I40E_PHY_TYPE_SGMII) +#define I40E_CAP_PHY_TYPE_1000BASE_KX BIT_ULL(I40E_PHY_TYPE_1000BASE_KX) +#define I40E_CAP_PHY_TYPE_10GBASE_KX4 BIT_ULL(I40E_PHY_TYPE_10GBASE_KX4) +#define I40E_CAP_PHY_TYPE_10GBASE_KR BIT_ULL(I40E_PHY_TYPE_10GBASE_KR) +#define I40E_CAP_PHY_TYPE_40GBASE_KR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_KR4) +#define I40E_CAP_PHY_TYPE_XAUI BIT_ULL(I40E_PHY_TYPE_XAUI) +#define I40E_CAP_PHY_TYPE_XFI BIT_ULL(I40E_PHY_TYPE_XFI) +#define I40E_CAP_PHY_TYPE_SFI BIT_ULL(I40E_PHY_TYPE_SFI) +#define I40E_CAP_PHY_TYPE_XLAUI BIT_ULL(I40E_PHY_TYPE_XLAUI) +#define I40E_CAP_PHY_TYPE_XLPPI BIT_ULL(I40E_PHY_TYPE_XLPPI) +#define I40E_CAP_PHY_TYPE_40GBASE_CR4_CU BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4_CU) +#define I40E_CAP_PHY_TYPE_10GBASE_CR1_CU BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1_CU) +#define I40E_CAP_PHY_TYPE_10GBASE_AOC BIT_ULL(I40E_PHY_TYPE_10GBASE_AOC) +#define I40E_CAP_PHY_TYPE_40GBASE_AOC BIT_ULL(I40E_PHY_TYPE_40GBASE_AOC) +#define I40E_CAP_PHY_TYPE_100BASE_TX BIT_ULL(I40E_PHY_TYPE_100BASE_TX) +#define I40E_CAP_PHY_TYPE_1000BASE_T BIT_ULL(I40E_PHY_TYPE_1000BASE_T) +#define I40E_CAP_PHY_TYPE_10GBASE_T BIT_ULL(I40E_PHY_TYPE_10GBASE_T) +#define I40E_CAP_PHY_TYPE_10GBASE_SR BIT_ULL(I40E_PHY_TYPE_10GBASE_SR) +#define I40E_CAP_PHY_TYPE_10GBASE_LR BIT_ULL(I40E_PHY_TYPE_10GBASE_LR) +#define I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU BIT_ULL(I40E_PHY_TYPE_10GBASE_SFPP_CU) +#define I40E_CAP_PHY_TYPE_10GBASE_CR1 BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1) +#define I40E_CAP_PHY_TYPE_40GBASE_CR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4) +#define I40E_CAP_PHY_TYPE_40GBASE_SR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_SR4) +#define I40E_CAP_PHY_TYPE_40GBASE_LR4 BIT_ULL(I40E_PHY_TYPE_40GBASE_LR4) +#define I40E_CAP_PHY_TYPE_1000BASE_SX BIT_ULL(I40E_PHY_TYPE_1000BASE_SX) +#define I40E_CAP_PHY_TYPE_1000BASE_LX BIT_ULL(I40E_PHY_TYPE_1000BASE_LX) +#define I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL \ + BIT_ULL(I40E_PHY_TYPE_1000BASE_T_OPTICAL) +#define I40E_CAP_PHY_TYPE_20GBASE_KR2 BIT_ULL(I40E_PHY_TYPE_20GBASE_KR2) +/* Defining the macro I40E_TYPE_OFFSET to implement a bit shift for some + * PHY types. There is an unused bit (31) in the I40E_CAP_PHY_TYPE_* bit + * fields but no corresponding gap in the i40e_aq_phy_type enumeration. So, + * a shift is needed to adjust for this with values larger than 31. The + * only affected values are I40E_PHY_TYPE_25GBASE_*. + */ +#define I40E_PHY_TYPE_OFFSET 1 +#define I40E_CAP_PHY_TYPE_25GBASE_KR BIT_ULL(I40E_PHY_TYPE_25GBASE_KR + \ + I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_CR BIT_ULL(I40E_PHY_TYPE_25GBASE_CR + \ + I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_SR BIT_ULL(I40E_PHY_TYPE_25GBASE_SR + \ + I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_LR BIT_ULL(I40E_PHY_TYPE_25GBASE_LR + \ + I40E_PHY_TYPE_OFFSET) #define I40E_HW_CAP_MAX_GPIO 30 /* Capabilities of a PF or a VF or the whole device */ struct i40e_hw_capabilities { diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index a994015..272d600 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -85,6 +85,14 @@ static int i40evf_get_settings(struct net_device *netdev, case I40E_LINK_SPEED_40GB: ethtool_cmd_speed_set(ecmd, SPEED_40000); break; + case I40E_LINK_SPEED_25GB: +#ifdef SPEED_25000 + ethtool_cmd_speed_set(ecmd, SPEED_25000); +#else + netdev_info(netdev, + "Speed is 25G, display not supported by this version of ethtool.\n"); +#endif + break; case I40E_LINK_SPEED_20GB: ethtool_cmd_speed_set(ecmd, SPEED_20000); break; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index ddf478d..2059a8e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -836,6 +836,9 @@ static void i40evf_print_link_message(struct i40evf_adapter *adapter) case I40E_LINK_SPEED_40GB: speed = "40 G"; break; + case I40E_LINK_SPEED_25GB: + speed = "25 G"; + break; case I40E_LINK_SPEED_20GB: speed = "20 G"; break; -- cgit v1.1 From 60f000a4d839a8f850fb2154aa26dc3f552a4667 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Mon, 21 Nov 2016 13:03:49 -0800 Subject: i40e: Add FEC for 25g This patch adds adminq support for Forward Error Correction ("FEC")for 25g products. Change-ID: Iaff4910737c239d2c730e5c22a313ce9c37d3964 Signed-off-by: Carolyn Wyborny Signed-off-by: Mitch Williams Signed-off-by: Jacek Naczyk Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 25 ++++++++++++++++++++-- drivers/net/ethernet/intel/i40e/i40e_common.c | 2 ++ .../net/ethernet/intel/i40evf/i40e_adminq_cmd.h | 25 ++++++++++++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index c9d1f91..b2101a5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -1686,6 +1686,8 @@ struct i40e_aq_get_phy_abilities_resp { #define I40E_AQ_PHY_LINK_ENABLED 0x08 #define I40E_AQ_PHY_AN_ENABLED 0x10 #define I40E_AQ_PHY_FLAG_MODULE_QUAL 0x20 +#define I40E_AQ_PHY_FEC_ABILITY_KR 0x40 +#define I40E_AQ_PHY_FEC_ABILITY_RS 0x80 __le16 eee_capability; #define I40E_AQ_EEE_100BASE_TX 0x0002 #define I40E_AQ_EEE_1000BASE_T 0x0004 @@ -1701,7 +1703,16 @@ struct i40e_aq_get_phy_abilities_resp { #define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 #define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 #define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 - u8 mod_type_ext; + u8 fec_cfg_curr_mod_ext_info; +#define I40E_AQ_ENABLE_FEC_KR 0x01 +#define I40E_AQ_ENABLE_FEC_RS 0x02 +#define I40E_AQ_REQUEST_FEC_KR 0x04 +#define I40E_AQ_REQUEST_FEC_RS 0x08 +#define I40E_AQ_ENABLE_FEC_AUTO 0x10 +#define I40E_AQ_FEC +#define I40E_AQ_MODULE_TYPE_EXT_MASK 0xE0 +#define I40E_AQ_MODULE_TYPE_EXT_SHIFT 5 + u8 ext_comp_code; u8 phy_id[4]; u8 module_type[3]; @@ -1729,7 +1740,15 @@ struct i40e_aq_set_phy_config { /* same bits as above in all */ #define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 #define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 #define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 - u8 reserved[2]; + u8 fec_config; +#define I40E_AQ_SET_FEC_ABILITY_KR BIT(0) +#define I40E_AQ_SET_FEC_ABILITY_RS BIT(1) +#define I40E_AQ_SET_FEC_REQUEST_KR BIT(2) +#define I40E_AQ_SET_FEC_REQUEST_RS BIT(3) +#define I40E_AQ_SET_FEC_AUTO BIT(4) +#define I40E_AQ_PHY_FEC_CONFIG_SHIFT 0x0 +#define I40E_AQ_PHY_FEC_CONFIG_MASK (0x1F << I40E_AQ_PHY_FEC_CONFIG_SHIFT) + u8 reserved; }; I40E_CHECK_CMD_LENGTH(i40e_aq_set_phy_config); @@ -1819,6 +1838,8 @@ struct i40e_aqc_get_link_status { u8 loopback; /* use defines from i40e_aqc_set_lb_mode */ __le16 max_frame_size; u8 config; +#define I40E_AQ_CONFIG_FEC_KR_ENA 0x01 +#define I40E_AQ_CONFIG_FEC_RS_ENA 0x02 #define I40E_AQ_CONFIG_CRC_ENA 0x04 #define I40E_AQ_CONFIG_PACING_MASK 0x78 u8 external_power_ability; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 1318c7d..f8c4c145 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1714,6 +1714,8 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, config.eee_capability = abilities.eee_capability; config.eeer = abilities.eeer_val; config.low_power_ctrl = abilities.d3_lpan; + config.fec_config = abilities.fec_cfg_curr_mod_ext_info & + I40E_AQ_PHY_FEC_CONFIG_MASK; status = i40e_aq_set_phy_config(hw, &config, NULL); if (status) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index f8d7d95..eeb9864 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -1683,6 +1683,8 @@ struct i40e_aq_get_phy_abilities_resp { #define I40E_AQ_PHY_LINK_ENABLED 0x08 #define I40E_AQ_PHY_AN_ENABLED 0x10 #define I40E_AQ_PHY_FLAG_MODULE_QUAL 0x20 +#define I40E_AQ_PHY_FEC_ABILITY_KR 0x40 +#define I40E_AQ_PHY_FEC_ABILITY_RS 0x80 __le16 eee_capability; #define I40E_AQ_EEE_100BASE_TX 0x0002 #define I40E_AQ_EEE_1000BASE_T 0x0004 @@ -1698,7 +1700,16 @@ struct i40e_aq_get_phy_abilities_resp { #define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 #define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 #define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 - u8 mod_type_ext; + u8 fec_cfg_curr_mod_ext_info; +#define I40E_AQ_ENABLE_FEC_KR 0x01 +#define I40E_AQ_ENABLE_FEC_RS 0x02 +#define I40E_AQ_REQUEST_FEC_KR 0x04 +#define I40E_AQ_REQUEST_FEC_RS 0x08 +#define I40E_AQ_ENABLE_FEC_AUTO 0x10 +#define I40E_AQ_FEC +#define I40E_AQ_MODULE_TYPE_EXT_MASK 0xE0 +#define I40E_AQ_MODULE_TYPE_EXT_SHIFT 5 + u8 ext_comp_code; u8 phy_id[4]; u8 module_type[3]; @@ -1726,7 +1737,15 @@ struct i40e_aq_set_phy_config { /* same bits as above in all */ #define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 #define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 #define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 - u8 reserved[2]; + u8 fec_config; +#define I40E_AQ_SET_FEC_ABILITY_KR BIT(0) +#define I40E_AQ_SET_FEC_ABILITY_RS BIT(1) +#define I40E_AQ_SET_FEC_REQUEST_KR BIT(2) +#define I40E_AQ_SET_FEC_REQUEST_RS BIT(3) +#define I40E_AQ_SET_FEC_AUTO BIT(4) +#define I40E_AQ_PHY_FEC_CONFIG_SHIFT 0x0 +#define I40E_AQ_PHY_FEC_CONFIG_MASK (0x1F << I40E_AQ_PHY_FEC_CONFIG_SHIFT) + u8 reserved; }; I40E_CHECK_CMD_LENGTH(i40e_aq_set_phy_config); @@ -1816,6 +1835,8 @@ struct i40e_aqc_get_link_status { u8 loopback; /* use defines from i40e_aqc_set_lb_mode */ __le16 max_frame_size; u8 config; +#define I40E_AQ_CONFIG_FEC_KR_ENA 0x01 +#define I40E_AQ_CONFIG_FEC_RS_ENA 0x02 #define I40E_AQ_CONFIG_CRC_ENA 0x04 #define I40E_AQ_CONFIG_PACING_MASK 0x78 u8 external_power_ability; -- cgit v1.1 From f62ba91458b508c957f3e0f98f5c2a72633badcb Mon Sep 17 00:00:00 2001 From: Michal Kosiarz Date: Mon, 21 Nov 2016 13:03:50 -0800 Subject: i40e: Add functions which apply correct PHY access method for read and write operation Depending on external PHY type, register access method should be different. Clause22 or Clause45 can be chosen for different PHYs. Implemented functions apply correct access method for used device. Change-ID: If39d5f0da9c0b905a8cbdc1ab89885535e7d0426 Signed-off-by: Michal Kosiarz Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 72 ++++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_prototype.h | 4 ++ drivers/net/ethernet/intel/i40evf/i40e_prototype.h | 4 ++ 3 files changed, 80 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index f8c4c145..1287359 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -4676,6 +4676,78 @@ phy_write_end: } /** + * i40e_write_phy_register + * @hw: pointer to the HW structure + * @page: registers page number + * @reg: register address in the page + * @phy_adr: PHY address on MDIO interface + * @value: PHY register value + * + * Writes value to specified PHY register + **/ +i40e_status i40e_write_phy_register(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 value) +{ + i40e_status status; + + switch (hw->device_id) { + case I40E_DEV_ID_1G_BASE_T_X722: + status = i40e_write_phy_register_clause22(hw, reg, phy_addr, + value); + break; + case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: + case I40E_DEV_ID_10G_BASE_T_X722: + case I40E_DEV_ID_25G_B: + case I40E_DEV_ID_25G_SFP28: + status = i40e_write_phy_register_clause45(hw, page, reg, + phy_addr, value); + break; + default: + status = I40E_ERR_UNKNOWN_PHY; + break; + } + + return status; +} + +/** + * i40e_read_phy_register + * @hw: pointer to the HW structure + * @page: registers page number + * @reg: register address in the page + * @phy_adr: PHY address on MDIO interface + * @value: PHY register value + * + * Reads specified PHY register value + **/ +i40e_status i40e_read_phy_register(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 *value) +{ + i40e_status status; + + switch (hw->device_id) { + case I40E_DEV_ID_1G_BASE_T_X722: + status = i40e_read_phy_register_clause22(hw, reg, phy_addr, + value); + break; + case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: + case I40E_DEV_ID_10G_BASE_T_X722: + case I40E_DEV_ID_25G_B: + case I40E_DEV_ID_25G_SFP28: + status = i40e_read_phy_register_clause45(hw, page, reg, + phy_addr, value); + break; + default: + status = I40E_ERR_UNKNOWN_PHY; + break; + } + + return status; +} + +/** * i40e_get_phy_address * @hw: pointer to the HW structure * @dev_num: PHY port num that address we want diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 37d67e7..2551fc8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -373,6 +373,10 @@ i40e_status i40e_read_phy_register_clause45(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 *value); i40e_status i40e_write_phy_register_clause45(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 value); +i40e_status i40e_read_phy_register(struct i40e_hw *hw, u8 page, u16 reg, + u8 phy_addr, u16 *value); +i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page, u16 reg, + u8 phy_addr, u16 value); u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num); i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, u32 time, u32 interval); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h index d89d521..ba6c6bd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h @@ -115,6 +115,10 @@ i40e_status i40e_read_phy_register(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 *value); i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 value); +i40e_status i40e_read_phy_register(struct i40e_hw *hw, u8 page, u16 reg, + u8 phy_addr, u16 *value); +i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page, u16 reg, + u8 phy_addr, u16 value); u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num); i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, u32 time, u32 interval); -- cgit v1.1 From 910890338399258af210685210760bc0cc82a5dd Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 21 Nov 2016 13:03:51 -0800 Subject: i40e: lock service task correctly The service task lock was being set in the scheduling function, not the actual service task. This would potentially leave the bit set for a long time before the task actually ran. Furthermore, if the service task takes too long, it calls the schedule function to reschedule itself - which would fail to take the lock and do nothing. Instead, set and clear the lock bit in the service task itself. In the process, get rid of the i40e_service_event_complete() function, which is really just two lines of code that can be put right in the service task itself. Change-ID: I83155e682b686121e2897f4429eb7d3f7c669168 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b0486c9..c47e9c5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -288,8 +288,7 @@ struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id) void i40e_service_event_schedule(struct i40e_pf *pf) { if (!test_bit(__I40E_DOWN, &pf->state) && - !test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state) && - !test_and_set_bit(__I40E_SERVICE_SCHED, &pf->state)) + !test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) queue_work(i40e_wq, &pf->service_task); } @@ -5955,19 +5954,6 @@ static void i40e_handle_lan_overflow_event(struct i40e_pf *pf, } /** - * i40e_service_event_complete - Finish up the service event - * @pf: board private structure - **/ -static void i40e_service_event_complete(struct i40e_pf *pf) -{ - WARN_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state)); - - /* flush memory to make sure state is correct before next watchog */ - smp_mb__before_atomic(); - clear_bit(__I40E_SERVICE_SCHED, &pf->state); -} - -/** * i40e_get_cur_guaranteed_fd_count - Get the consumed guaranteed FD filters * @pf: board private structure **/ @@ -7276,10 +7262,12 @@ static void i40e_service_task(struct work_struct *work) /* don't bother with service tasks if a reset is in progress */ if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) { - i40e_service_event_complete(pf); return; } + if (test_and_set_bit(__I40E_SERVICE_SCHED, &pf->state)) + return; + i40e_detect_recover_hung(pf); i40e_sync_filters_subtask(pf); i40e_reset_subtask(pf); @@ -7292,7 +7280,9 @@ static void i40e_service_task(struct work_struct *work) i40e_sync_udp_filters_subtask(pf); i40e_clean_adminq_subtask(pf); - i40e_service_event_complete(pf); + /* flush memory to make sure state is correct before next watchdog */ + smp_mb__before_atomic(); + clear_bit(__I40E_SERVICE_SCHED, &pf->state); /* If the tasks have taken longer than one timer cycle or there * is more work to be done, reschedule the service task now -- cgit v1.1 From 1e28e861c0c5ed33a9d9cd2745f3980912e1890f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 11 Nov 2016 12:39:25 -0800 Subject: i40e: defeature support for PTP L4 frame detection on XL710 A product decision has been made to defeature detection of PTP frames over L4 (UDP) on the XL710 MAC. Do not advertise support for L4 timestamping. Change-ID: I41fbb0f84ebb27c43e23098c08156f2625c6ee06 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 15 +++++++++++++-- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 ++- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 21 +++++++++++++++------ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index b8f2978..f1d838f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -360,6 +360,7 @@ struct i40e_pf { #define I40E_FLAG_PF_MAC BIT_ULL(50) #define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) #define I40E_FLAG_HAVE_CRT_RETIMER BIT_ULL(52) +#define I40E_FLAG_PTP_L4_CAPABLE BIT_ULL(53) /* tracks features that get auto disabled by errors */ u64 auto_disable_flags; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 935160a..cc1465a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1681,8 +1681,19 @@ static int i40e_get_ts_info(struct net_device *dev, info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | - BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | - BIT(HWTSTAMP_FILTER_PTP_V2_EVENT); + BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ); + + if (pf->flags & I40E_FLAG_PTP_L4_CAPABLE) + info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ); return 0; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c47e9c5..806fd56 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8699,7 +8699,8 @@ static int i40e_sw_init(struct i40e_pf *pf) I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE | I40E_FLAG_NO_PCI_LINK_CHECK | I40E_FLAG_USE_SET_LLDP_MIB | - I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; + I40E_FLAG_GENEVE_OFFLOAD_CAPABLE | + I40E_FLAG_PTP_L4_CAPABLE; } else if ((pf->hw.aq.api_maj_ver > 1) || ((pf->hw.aq.api_maj_ver == 1) && (pf->hw.aq.api_min_ver > 4))) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 5e2272c..9e49ffa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -521,6 +521,8 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + if (!(pf->flags & I40E_FLAG_PTP_L4_CAPABLE)) + return -ERANGE; pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V1MESSTYPE0_MASK | I40E_PRTTSYN_CTL1_TSYNTYPE_V1 | @@ -528,19 +530,26 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: - case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + if (!(pf->flags & I40E_FLAG_PTP_L4_CAPABLE)) + return -ERANGE; + /* fall through */ + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V2MESSTYPE0_MASK | - I40E_PRTTSYN_CTL1_TSYNTYPE_V2 | - I40E_PRTTSYN_CTL1_UDP_ENA_MASK; - config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + I40E_PRTTSYN_CTL1_TSYNTYPE_V2; + if (pf->flags & I40E_FLAG_PTP_L4_CAPABLE) { + tsyntype |= I40E_PRTTSYN_CTL1_UDP_ENA_MASK; + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + } else { + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + } break; case HWTSTAMP_FILTER_ALL: default: -- cgit v1.1 From 38326218acce336d99cd128a11ecc69f6512f8e4 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 11 Nov 2016 12:39:26 -0800 Subject: i40e: recalculate vsi->active_filters from hash contents Previous code refactors have accidentally caused issues with the counting of active_filters. Avoid similar issues in the future by simply re-counting the active filters every time after we handle add and delete of all the filters. Additionally this allows us to simplify the check for when we exit promiscuous mode since we can combine the check for failed filters at the same time. Additionally since we recount filters at the end we need to set vsi->promisc_threshold as well. The resulting code takes a bit longer since we do have to loop over filters again. However, the result is more readable and less likely to become incorrect due to failed accounting of filters in the future. Finally, this ensures that it is not possible for vsi->active_filters to ever underflow since we never decrement it. Change-ID: Ib4f3a377e60eb1fa6c91ea86cc02238c08edd102 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 52 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 806fd56..2ccf376 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1870,12 +1870,10 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, aq_ret = i40e_aq_add_macvlan(hw, vsi->seid, list, num_add, NULL); aq_err = hw->aq.asq_last_status; fcnt = i40e_update_filter_state(num_add, list, add_head, aq_ret); - vsi->active_filters += fcnt; if (fcnt != num_add) { *promisc_changed = true; set_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); - vsi->promisc_threshold = (vsi->active_filters * 3) / 4; dev_warn(&vsi->back->pdev->dev, "Error %s adding RX filters on %s, promiscuous mode forced on\n", i40e_aq_str(hw, aq_err), @@ -1939,6 +1937,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) struct i40e_hw *hw = &vsi->back->hw; unsigned int vlan_any_filters = 0; unsigned int non_vlan_filters = 0; + unsigned int failed_filters = 0; unsigned int vlan_filters = 0; bool promisc_changed = false; char vsi_name[16] = "PF"; @@ -1985,7 +1984,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* Move the element into temporary del_list */ hash_del(&f->hlist); hlist_add_head(&f->hlist, &tmp_del_list); - vsi->active_filters--; /* Avoid counting removed filters */ continue; @@ -2046,7 +2044,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) f->state = I40E_FILTER_REMOVE; hash_del(&f->hlist); hlist_add_head(&f->hlist, &tmp_del_list); - vsi->active_filters--; } /* Also update any filters on the tmp_add list */ @@ -2203,27 +2200,36 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) add_list = NULL; } - /* Check to see if we can drop out of overflow promiscuous mode. */ + /* Determine the number of active and failed filters. */ + spin_lock_bh(&vsi->mac_filter_hash_lock); + vsi->active_filters = 0; + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { + if (f->state == I40E_FILTER_ACTIVE) + vsi->active_filters++; + else if (f->state == I40E_FILTER_FAILED) + failed_filters++; + } + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + /* If promiscuous mode has changed, we need to calculate a new + * threshold for when we are safe to exit + */ + if (promisc_changed) + vsi->promisc_threshold = (vsi->active_filters * 3) / 4; + + /* Check if we are able to exit overflow promiscuous mode. We can + * safely exit if we didn't just enter, we no longer have any failed + * filters, and we have reduced filters below the threshold value. + */ if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state) && + !promisc_changed && !failed_filters && (vsi->active_filters < vsi->promisc_threshold)) { - int failed_count = 0; - /* See if we have any failed filters. We can't drop out of - * promiscuous until these have all been deleted. - */ - spin_lock_bh(&vsi->mac_filter_hash_lock); - hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - if (f->state == I40E_FILTER_FAILED) - failed_count++; - } - spin_unlock_bh(&vsi->mac_filter_hash_lock); - if (!failed_count) { - dev_info(&pf->pdev->dev, - "filter logjam cleared on %s, leaving overflow promiscuous mode\n", - vsi_name); - clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); - promisc_changed = true; - vsi->promisc_threshold = 0; - } + dev_info(&pf->pdev->dev, + "filter logjam cleared on %s, leaving overflow promiscuous mode\n", + vsi_name); + clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); + promisc_changed = true; + vsi->promisc_threshold = 0; } /* if the VF is not trusted do not do promisc */ -- cgit v1.1 From ac9e239014413e483abadba7722cfc1672302738 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 11 Nov 2016 12:39:27 -0800 Subject: i40e: refactor i40e_update_filter_state to avoid passing aq_err The current caller of i40e_update_filter_state incorrectly passes aq_ret, an i40e_status variable, instead of the expected aq_err. This happens to work because i40e_status is actually just a typedef integer, and 0 is still the successful return. However i40e_update_filter_state has special handling for ENOSPC which is currently being ignored. Also notice that firmware does not update the per-filter response for many types of errors, such as EINVAL. Thus, modify the filter setup so that the firmware response memory is pre-set with I40E_AQC_MM_ERR_NO_RES. This enables us to refactor i40e_update_filter_state, removing the need to pass aq_err and avoiding a need for having 3 different flows for checking the filter state. The resulting code for i40e_update_filter_state is much simpler, only a single loop and we always check each filter response value every time. Since we pre-set the response value to match our expected error this correctly works for all success and error flows. Change-ID: Ie292c9511f34ee18c6ef40f955ad13e28b7aea7d Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 58 +++++++++++------------------ 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2ccf376..8e65972 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1757,7 +1757,6 @@ static void i40e_undo_filter_entries(struct i40e_vsi *vsi, * @count: Number of filters added * @add_list: return data from fw * @head: pointer to first filter in current batch - * @aq_err: status from fw * * MAC filter entries from list were slated to be added to device. Returns * number of successful filters. Note that 0 does NOT mean success! @@ -1765,47 +1764,30 @@ static void i40e_undo_filter_entries(struct i40e_vsi *vsi, static int i40e_update_filter_state(int count, struct i40e_aqc_add_macvlan_element_data *add_list, - struct i40e_mac_filter *add_head, int aq_err) + struct i40e_mac_filter *add_head) { int retval = 0; int i; - - if (!aq_err) { - retval = count; - /* Everything's good, mark all filters active. */ - for (i = 0; i < count ; i++) { - add_head->state = I40E_FILTER_ACTIVE; - add_head = hlist_entry(add_head->hlist.next, - typeof(struct i40e_mac_filter), - hlist); - } - } else if (aq_err == I40E_AQ_RC_ENOSPC) { - /* Device ran out of filter space. Check the return value - * for each filter to see which ones are active. + for (i = 0; i < count; i++) { + /* Always check status of each filter. We don't need to check + * the firmware return status because we pre-set the filter + * status to I40E_AQC_MM_ERR_NO_RES when sending the filter + * request to the adminq. Thus, if it no longer matches then + * we know the filter is active. */ - for (i = 0; i < count ; i++) { - if (add_list[i].match_method == - I40E_AQC_MM_ERR_NO_RES) { - add_head->state = I40E_FILTER_FAILED; - } else { - add_head->state = I40E_FILTER_ACTIVE; - retval++; - } - add_head = hlist_entry(add_head->hlist.next, - typeof(struct i40e_mac_filter), - hlist); - } - } else { - /* Some other horrible thing happened, fail all filters */ - retval = 0; - for (i = 0; i < count ; i++) { + if (add_list[i].match_method == I40E_AQC_MM_ERR_NO_RES) { add_head->state = I40E_FILTER_FAILED; - add_head = hlist_entry(add_head->hlist.next, - typeof(struct i40e_mac_filter), - hlist); + } else { + add_head->state = I40E_FILTER_ACTIVE; + retval++; } + + add_head = hlist_entry(add_head->hlist.next, + typeof(struct i40e_mac_filter), + hlist); } + return retval; } @@ -1864,12 +1846,11 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, int num_add, bool *promisc_changed) { struct i40e_hw *hw = &vsi->back->hw; - i40e_status aq_ret; int aq_err, fcnt; - aq_ret = i40e_aq_add_macvlan(hw, vsi->seid, list, num_add, NULL); + i40e_aq_add_macvlan(hw, vsi->seid, list, num_add, NULL); aq_err = hw->aq.asq_last_status; - fcnt = i40e_update_filter_state(num_add, list, add_head, aq_ret); + fcnt = i40e_update_filter_state(num_add, list, add_head); if (fcnt != num_add) { *promisc_changed = true; @@ -2168,6 +2149,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) cpu_to_le16((u16)(f->vlan)); } add_list[num_add].queue_number = 0; + /* set invalid match method for later detection */ + add_list[num_add].match_method = + cpu_to_le16((u16)I40E_AQC_MM_ERR_NO_RES); cmd_flags |= I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; add_list[num_add].flags = cpu_to_le16(cmd_flags); num_add++; -- cgit v1.1 From 756970255a58c0f5ff9f9c5bba6fb47c70804077 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 11 Nov 2016 12:39:28 -0800 Subject: i40e: delete filter after adding its replacement when converting Fix a subtle issue with the code for converting VID=-1 filters into VID=0 filters when adding a new VLAN. Previously the code deleted the VID=-1 filter, and then added a new VID=0 filter. In the rare case that the addition fails due to -ENOMEM, we end up completely deleting the filter which prevents recovery if memory pressure subsides. While it is not strictly an issue because it is likely that memory issues would result in many other problems, we shouldn't delete the filter until after the addition succeeds. Change-ID: Icba07ddd04ecc6a3b27c2e29f2c1c8673d266826 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 8e65972..f9e9c90 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2535,7 +2535,6 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) I40E_VLAN_ANY); if (!del_f) continue; - __i40e_del_filter(vsi, del_f); add_f = i40e_add_filter(vsi, f->macaddr, 0); if (!add_f) { dev_info(&vsi->back->pdev->dev, @@ -2544,6 +2543,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } + __i40e_del_filter(vsi, del_f); } } -- cgit v1.1 From 490a4ad3a7c5d0943e648f97815e6cb677f1f8eb Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 11 Nov 2016 12:39:29 -0800 Subject: i40e: factor out addition/deletion of VLAN per each MAC address A future refactor of how the PF assigns a PVID to a VF will want to be able to add and remove a block of filters by VLAN without worrying about accidentally triggering the accounting for I40E_VLAN_ANY. Additionally the PVID assignment would like to be able to batch several changes under one use of the mac_filter_hash_lock. Factor out the addition and deletion of a VLAN on all MACs into their own function which i40e_vsi_(add|kill)_vlan can use. These new functions expect the caller to take the hash lock, as well as perform any necessary accounting for updating I40E_VLAN_ANY filters if we are now operating under VLAN mode. Change-ID: If79e5b60b770433275350a74b3f1880333a185d5 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 68 +++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index f9e9c90..8aedfb7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2493,19 +2493,24 @@ static void i40e_vlan_rx_register(struct net_device *netdev, u32 features) } /** - * i40e_vsi_add_vlan - Add vsi membership for given vlan + * i40e_add_vlan_all_mac - Add a MAC/VLAN filter for each existing MAC address * @vsi: the vsi being configured * @vid: vlan id to be added (0 = untagged only , -1 = any) + * + * This is a helper function for adding a new MAC/VLAN filter with the + * specified VLAN for each existing MAC address already in the hash table. + * This function does *not* perform any accounting to update filters based on + * VLAN mode. + * + * NOTE: this function expects to be called while under the + * mac_filter_hash_lock **/ -int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) +static int i40e_add_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) { - struct i40e_mac_filter *f, *add_f, *del_f; + struct i40e_mac_filter *f, *add_f; struct hlist_node *h; int bkt; - /* Locked once because all functions invoked below iterates list*/ - spin_lock_bh(&vsi->mac_filter_hash_lock); - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->state == I40E_FILTER_REMOVE) continue; @@ -2514,11 +2519,33 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) dev_info(&vsi->back->pdev->dev, "Could not add vlan filter %d for %pM\n", vid, f->macaddr); - spin_unlock_bh(&vsi->mac_filter_hash_lock); return -ENOMEM; } } + return 0; +} + +/** + * i40e_vsi_add_vlan - Add VSI membership for given VLAN + * @vsi: the VSI being configured + * @vid: VLAN id to be added (0 = untagged only , -1 = any) + **/ +int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) +{ + struct i40e_mac_filter *f, *add_f, *del_f; + struct hlist_node *h; + int bkt, err; + + /* Locked once because all functions invoked below iterates list*/ + spin_lock_bh(&vsi->mac_filter_hash_lock); + + err = i40e_add_vlan_all_mac(vsi, vid); + if (err) { + spin_unlock_bh(&vsi->mac_filter_hash_lock); + return err; + } + /* When we add a new VLAN filter, we need to make sure that all existing * filters which are marked as vid=-1 (I40E_VLAN_ANY) are converted to * vid=0. The simplest way is just search for all filters marked as @@ -2557,24 +2584,39 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) } /** - * i40e_vsi_kill_vlan - Remove vsi membership for given vlan + * i40e_rm_vlan_all_mac - Remove MAC/VLAN pair for all MAC with the given VLAN * @vsi: the vsi being configured * @vid: vlan id to be removed (0 = untagged only , -1 = any) - **/ -void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) + * + * This function should be used to remove all VLAN filters which match the + * given VID. It does not schedule the service event and does not take the + * mac_filter_hash_lock so it may be combined with other operations under + * a single invocation of the mac_filter_hash_lock. + * + * NOTE: this function expects to be called while under the + * mac_filter_hash_lock + */ +static void i40e_rm_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) { struct i40e_mac_filter *f; struct hlist_node *h; int bkt; - /* Locked once because all functions invoked below iterates list */ - spin_lock_bh(&vsi->mac_filter_hash_lock); - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (f->vlan == vid) __i40e_del_filter(vsi, f); } +} +/** + * i40e_vsi_kill_vlan - Remove VSI membership for given VLAN + * @vsi: the VSI being configured + * @vid: VLAN id to be removed (0 = untagged only , -1 = any) + **/ +void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) +{ + spin_lock_bh(&vsi->mac_filter_hash_lock); + i40e_rm_vlan_all_mac(vsi, vid); spin_unlock_bh(&vsi->mac_filter_hash_lock); /* schedule our worker thread which will take care of -- cgit v1.1 From 9af52f60b2d9918af6a0b7c18b08cfcdd0574a24 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 11 Nov 2016 12:39:30 -0800 Subject: i40e: use (add|rm)_vlan_all_mac helper functions when changing PVID The current flow for adding or updating the PVID for a VF uses i40e_vsi_add_vlan and i40e_vsi_kill_vlan which each take, then release the hash lock. In addition the two functions also must take special care that they do not perform VLAN mode changes as this will make the code in i40e_ndo_set_vf_port_vlan behave incorrectly. Fix these issues by using the new helper functions i40e_add_vlan_all_mac and i40e_rm_vlan_all_mac which expect the hash lock to already be taken. Additionally these functions do not perform any state updates in regards to VLAN mode, so they are safe to use in the PVID update flow. It should be noted that we don't need the VLAN mode update code here, because there are only a few flows here. (a) we're adding a new PVID In this case, if we already had VLAN filters the VSI is knocked offline so we don't need to worry about pre-existing VLAN filters (b) we're replacing an existing PVID In this case, we can't have any VLAN filters except those with the old PVID which we already take care of manually. (c) we're removing an existing PVID Similarly to above, we can't have any existing VLAN filters except those with the old PVID which we already take care of correctly. Because of this, we do not need (or even want) the special accounting done in i40e_vsi_add_vlan, so use of the helpers is a saner alternative. It also opens the door for a future patch which will refactor the flow of i40e_vsi_add_vlan now that it is not needed in this function. Change-ID: Ia841f63da94e12b106f41cf7d28ce8ce92f2ad99 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 + drivers/net/ethernet/intel/i40e/i40e_main.c | 4 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 43 ++++++++++++++-------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index f1d838f..ba8d309 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -852,7 +852,9 @@ int i40e_open(struct net_device *netdev); int i40e_close(struct net_device *netdev); int i40e_vsi_open(struct i40e_vsi *vsi); void i40e_vlan_stripping_disable(struct i40e_vsi *vsi); +int i40e_add_vlan_all_mac(struct i40e_vsi *vsi, s16 vid); int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid); +void i40e_rm_vlan_all_mac(struct i40e_vsi *vsi, s16 vid); void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid); struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, const u8 *macaddr); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 8aedfb7..49261cc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2505,7 +2505,7 @@ static void i40e_vlan_rx_register(struct net_device *netdev, u32 features) * NOTE: this function expects to be called while under the * mac_filter_hash_lock **/ -static int i40e_add_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) +int i40e_add_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) { struct i40e_mac_filter *f, *add_f; struct hlist_node *h; @@ -2596,7 +2596,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) * NOTE: this function expects to be called while under the * mac_filter_hash_lock */ -static void i40e_rm_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) +void i40e_rm_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) { struct i40e_mac_filter *f; struct hlist_node *h; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index d28b684..a6198b7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2766,7 +2766,6 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlanprio = vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT); struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; - bool is_vsi_in_vlan = false; struct i40e_vsi *vsi; struct i40e_vf *vf; int ret = 0; @@ -2803,11 +2802,10 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, /* duplicate request, so just return success */ goto error_pvid; + /* Locked once because multiple functions below iterate list */ spin_lock_bh(&vsi->mac_filter_hash_lock); - is_vsi_in_vlan = i40e_is_vsi_in_vlan(vsi); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - if (le16_to_cpu(vsi->info.pvid) == 0 && is_vsi_in_vlan) { + if (le16_to_cpu(vsi->info.pvid) == 0 && i40e_is_vsi_in_vlan(vsi)) { dev_err(&pf->pdev->dev, "VF %d has already configured VLAN filters and the administrator is requesting a port VLAN override.\nPlease unload and reload the VF driver for this change to take effect.\n", vf_id); @@ -2830,14 +2828,23 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, */ if ((!(vlan_id || qos) || vlanprio != le16_to_cpu(vsi->info.pvid)) && - vsi->info.pvid) - ret = i40e_vsi_add_vlan(vsi, I40E_VLAN_ANY); + vsi->info.pvid) { + ret = i40e_add_vlan_all_mac(vsi, I40E_VLAN_ANY); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "add VF VLAN failed, ret=%d aq_err=%d\n", ret, + vsi->back->hw.aq.asq_last_status); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_pvid; + } + } if (vsi->info.pvid) { - /* kill old VLAN */ - i40e_vsi_kill_vlan(vsi, (le16_to_cpu(vsi->info.pvid) & - VLAN_VID_MASK)); + /* remove all filters on the old VLAN */ + i40e_rm_vlan_all_mac(vsi, (le16_to_cpu(vsi->info.pvid) & + VLAN_VID_MASK)); } + if (vlan_id || qos) ret = i40e_vsi_add_pvid(vsi, vlanprio); else @@ -2847,24 +2854,30 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, dev_info(&pf->pdev->dev, "Setting VLAN %d, QOS 0x%x on VF %d\n", vlan_id, qos, vf_id); - /* add new VLAN filter */ - ret = i40e_vsi_add_vlan(vsi, vlan_id); + /* add new VLAN filter for each MAC */ + ret = i40e_add_vlan_all_mac(vsi, vlan_id); if (ret) { dev_info(&vsi->back->pdev->dev, "add VF VLAN failed, ret=%d aq_err=%d\n", ret, vsi->back->hw.aq.asq_last_status); + spin_unlock_bh(&vsi->mac_filter_hash_lock); goto error_pvid; } - /* Kill non-vlan MAC filters - ignore error return since - * there might not be any non-vlan MAC filters. - */ - i40e_vsi_kill_vlan(vsi, I40E_VLAN_ANY); + + /* remove the previously added non-VLAN MAC filters */ + i40e_rm_vlan_all_mac(vsi, I40E_VLAN_ANY); } + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + /* Schedule the worker thread to take care of applying changes */ + i40e_service_event_schedule(vsi->back); + if (ret) { dev_err(&pf->pdev->dev, "Unable to update VF vsi context\n"); goto error_pvid; } + /* The Port VLAN needs to be saved across resets the same as the * default LAN MAC address. */ -- cgit v1.1 From 489a32650721b0e1e25283257cacb3a337014794 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 11 Nov 2016 12:39:31 -0800 Subject: i40e: move all updates for VLAN mode into i40e_sync_vsi_filters In a similar fashion to how we handled exiting VLAN mode, move the logic in i40e_vsi_add_vlan into i40e_sync_vsi_filters. Extract this logic into its own function for ease of understanding as it will become quite complex. The new function, i40e_correct_mac_vlan_filters() correctly updates all filters for when we need to enter VLAN mode, exit VLAN mode, and also enforces the PVID when assigned. Call i40e_correct_mac_vlan_filters from i40e_sync_vsi_filters passing it the number of active VLAN filters, and the two temporary lists. Remove the function for updating VLAN=0 filters from i40e_vsi_add_vlan. The end result is that the logic for entering and exiting VLAN mode is in one location which has the most knowledge about all filters. This ensures that we always correctly have the non-VLAN filters assigned to VID=0 or VID=-1 regardless of how we ended up getting to this result. Additionally this enforces the PVID at sync time so that we know for certain that an assigned PVID results in only filters with that PVID will be added to the firmware. Change-ID: I895cee81e9c92d0a16baee38bd0ca51bbb14e372 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 214 +++++++++++++++------------- 1 file changed, 113 insertions(+), 101 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 49261cc..da4cbe3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1227,6 +1227,107 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) } /** + * i40e_correct_mac_vlan_filters - Correct non-VLAN filters if necessary + * @vsi: the VSI to configure + * @tmp_add_list: list of filters ready to be added + * @tmp_del_list: list of filters ready to be deleted + * @vlan_filters: the number of active VLAN filters + * + * Update VLAN=0 and VLAN=-1 (I40E_VLAN_ANY) filters properly so that they + * behave as expected. If we have any active VLAN filters remaining or about + * to be added then we need to update non-VLAN filters to be marked as VLAN=0 + * so that they only match against untagged traffic. If we no longer have any + * active VLAN filters, we need to make all non-VLAN filters marked as VLAN=-1 + * so that they match against both tagged and untagged traffic. In this way, + * we ensure that we correctly receive the desired traffic. This ensures that + * when we have an active VLAN we will receive only untagged traffic and + * traffic matching active VLANs. If we have no active VLANs then we will + * operate in non-VLAN mode and receive all traffic, tagged or untagged. + * + * Finally, in a similar fashion, this function also corrects filters when + * there is an active PVID assigned to this VSI. + * + * In case of memory allocation failure return -ENOMEM. Otherwise, return 0. + * + * This function is only expected to be called from within + * i40e_sync_vsi_filters. + * + * NOTE: This function expects to be called while under the + * mac_filter_hash_lock + */ +static int i40e_correct_mac_vlan_filters(struct i40e_vsi *vsi, + struct hlist_head *tmp_add_list, + struct hlist_head *tmp_del_list, + int vlan_filters) +{ + struct i40e_mac_filter *f, *add_head; + struct hlist_node *h; + int bkt, new_vlan; + + /* To determine if a particular filter needs to be replaced we + * have the three following conditions: + * + * a) if we have a PVID assigned, then all filters which are + * not marked as VLAN=PVID must be replaced with filters that + * are. + * b) otherwise, if we have any active VLANS, all filters + * which are marked as VLAN=-1 must be replaced with + * filters marked as VLAN=0 + * c) finally, if we do not have any active VLANS, all filters + * which are marked as VLAN=0 must be replaced with filters + * marked as VLAN=-1 + */ + + /* Update the filters about to be added in place */ + hlist_for_each_entry(f, tmp_add_list, hlist) { + if (vsi->info.pvid && f->vlan != vsi->info.pvid) + f->vlan = vsi->info.pvid; + else if (vlan_filters && f->vlan == I40E_VLAN_ANY) + f->vlan = 0; + else if (!vlan_filters && f->vlan == 0) + f->vlan = I40E_VLAN_ANY; + } + + /* Update the remaining active filters */ + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { + /* Combine the checks for whether a filter needs to be changed + * and then determine the new VLAN inside the if block, in + * order to avoid duplicating code for adding the new filter + * then deleting the old filter. + */ + if ((vsi->info.pvid && f->vlan != vsi->info.pvid) || + (vlan_filters && f->vlan == I40E_VLAN_ANY) || + (!vlan_filters && f->vlan == 0)) { + /* Determine the new vlan we will be adding */ + if (vsi->info.pvid) + new_vlan = vsi->info.pvid; + else if (vlan_filters) + new_vlan = 0; + else + new_vlan = I40E_VLAN_ANY; + + /* Create the new filter */ + add_head = i40e_add_filter(vsi, f->macaddr, new_vlan); + if (!add_head) + return -ENOMEM; + + /* Put the replacement filter into the add list */ + hash_del(&add_head->hlist); + hlist_add_head(&add_head->hlist, tmp_add_list); + + /* Put the original filter into the delete list */ + f->state = I40E_FILTER_REMOVE; + hash_del(&f->hlist); + hlist_add_head(&f->hlist, tmp_del_list); + } + } + + vsi->has_vlan_filter = !!vlan_filters; + + return 0; +} + +/** * i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM * @vsi: the PF Main VSI - inappropriate for any other VSI * @macaddr: the MAC address @@ -1916,8 +2017,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) struct hlist_head tmp_add_list, tmp_del_list; struct i40e_mac_filter *f, *add_head = NULL; struct i40e_hw *hw = &vsi->back->hw; - unsigned int vlan_any_filters = 0; - unsigned int non_vlan_filters = 0; unsigned int failed_filters = 0; unsigned int vlan_filters = 0; bool promisc_changed = false; @@ -1974,66 +2073,21 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) hlist_add_head(&f->hlist, &tmp_add_list); } - /* Count the number of each type of filter we have - * remaining, ignoring any filters we're about to - * delete. + /* Count the number of active (current and new) VLAN + * filters we have now. Does not count filters which + * are marked for deletion. */ if (f->vlan > 0) vlan_filters++; - else if (!f->vlan) - non_vlan_filters++; - else - vlan_any_filters++; } - /* We should never have VLAN=-1 filters at the same time as we - * have either VLAN=0 or VLAN>0 filters, so warn about this - * case here to help catch any issues. - */ - WARN_ON(vlan_any_filters && (vlan_filters + non_vlan_filters)); - - /* If we only have VLAN=0 filters remaining, and don't have - * any other VLAN filters, we need to convert these VLAN=0 - * filters into VLAN=-1 (I40E_VLAN_ANY) so that we operate - * correctly in non-VLAN mode and receive all traffic tagged - * or untagged. - */ - if (non_vlan_filters && !vlan_filters) { - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, - hlist) { - /* Only replace VLAN=0 filters */ - if (f->vlan) - continue; - - /* Allocate a replacement element */ - add_head = kzalloc(sizeof(*add_head), - GFP_KERNEL); - if (!add_head) - goto err_no_memory_locked; + retval = i40e_correct_mac_vlan_filters(vsi, + &tmp_add_list, + &tmp_del_list, + vlan_filters); + if (retval) + goto err_no_memory_locked; - /* Copy the filter, with new state and VLAN */ - *add_head = *f; - add_head->state = I40E_FILTER_NEW; - add_head->vlan = I40E_VLAN_ANY; - - /* Move the replacement to the add list */ - INIT_HLIST_NODE(&add_head->hlist); - hlist_add_head(&add_head->hlist, - &tmp_add_list); - - /* Move the original to the delete list */ - f->state = I40E_FILTER_REMOVE; - hash_del(&f->hlist); - hlist_add_head(&f->hlist, &tmp_del_list); - } - - /* Also update any filters on the tmp_add list */ - hlist_for_each_entry(f, &tmp_add_list, hlist) { - if (!f->vlan) - f->vlan = I40E_VLAN_ANY; - } - add_head = NULL; - } spin_unlock_bh(&vsi->mac_filter_hash_lock); } @@ -2098,14 +2152,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) del_list = NULL; } - /* After finishing notifying firmware of the deleted filters, update - * the cached value of vsi->has_vlan_filter. Note that we are safe to - * use just !!vlan_filters here because if we only have VLAN=0 (that - * is, non_vlan_filters) these will all be converted to VLAN=-1 in the - * logic above already so this value would still be correct. - */ - vsi->has_vlan_filter = !!vlan_filters; - if (!hlist_empty(&tmp_add_list)) { /* Do all the adds now. */ filter_list_len = hw->aq.asq_buf_size / @@ -2533,48 +2579,14 @@ int i40e_add_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) **/ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) { - struct i40e_mac_filter *f, *add_f, *del_f; - struct hlist_node *h; - int bkt, err; + int err; /* Locked once because all functions invoked below iterates list*/ spin_lock_bh(&vsi->mac_filter_hash_lock); - err = i40e_add_vlan_all_mac(vsi, vid); - if (err) { - spin_unlock_bh(&vsi->mac_filter_hash_lock); - return err; - } - - /* When we add a new VLAN filter, we need to make sure that all existing - * filters which are marked as vid=-1 (I40E_VLAN_ANY) are converted to - * vid=0. The simplest way is just search for all filters marked as - * vid=-1 and replace them with vid=0. This converts all filters that - * were marked to receive all traffic (tagged or untagged) into - * filters to receive only untagged traffic, so that we don't receive - * tagged traffic for VLANs which we have not configured. - */ - if (vid > 0 && !vsi->info.pvid) { - hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { - if (f->state == I40E_FILTER_REMOVE) - continue; - del_f = i40e_find_filter(vsi, f->macaddr, - I40E_VLAN_ANY); - if (!del_f) - continue; - add_f = i40e_add_filter(vsi, f->macaddr, 0); - if (!add_f) { - dev_info(&vsi->back->pdev->dev, - "Could not add filter 0 for %pM\n", - f->macaddr); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - return -ENOMEM; - } - __i40e_del_filter(vsi, del_f); - } - } - spin_unlock_bh(&vsi->mac_filter_hash_lock); + if (err) + return err; /* schedule our worker thread which will take care of * applying the new filter changes -- cgit v1.1 From 795595f68d6c787028345804bb06f5a633af24a2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:05:52 +0100 Subject: netfilter: nft_quota: dump consumed quota Add a new attribute NFTA_QUOTA_CONSUMED that displays the amount of quota that has been already consumed. This allows us to restore the internal state of the quota object between reboots as well as to monitor how wasted it is. This patch changes the logic to account for the consumed bytes, instead of the bytes that remain to be consumed. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 2 ++ net/netfilter/nft_quota.c | 21 ++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 1043ce4..3d47582c 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -988,12 +988,14 @@ enum nft_quota_flags { * * @NFTA_QUOTA_BYTES: quota in bytes (NLA_U16) * @NFTA_QUOTA_FLAGS: flags (NLA_U32) + * @NFTA_QUOTA_CONSUMED: quota already consumed in bytes (NLA_U64) */ enum nft_quota_attributes { NFTA_QUOTA_UNSPEC, NFTA_QUOTA_BYTES, NFTA_QUOTA_FLAGS, NFTA_QUOTA_PAD, + NFTA_QUOTA_CONSUMED, __NFTA_QUOTA_MAX }; #define NFTA_QUOTA_MAX (__NFTA_QUOTA_MAX - 1) diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index 09ce72b..0d34420 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -18,20 +18,20 @@ struct nft_quota { u64 quota; bool invert; - atomic64_t remain; + atomic64_t consumed; }; static inline bool nft_overquota(struct nft_quota *priv, - const struct nft_pktinfo *pkt) + const struct sk_buff *skb) { - return atomic64_sub_return(pkt->skb->len, &priv->remain) < 0; + return atomic64_add_return(skb->len, &priv->consumed) >= priv->quota; } static inline void nft_quota_do_eval(struct nft_quota *priv, struct nft_regs *regs, const struct nft_pktinfo *pkt) { - if (nft_overquota(priv, pkt) ^ priv->invert) + if (nft_overquota(priv, pkt->skb) ^ priv->invert) regs->verdict.code = NFT_BREAK; } @@ -70,7 +70,7 @@ static int nft_quota_do_init(const struct nlattr * const tb[], priv->quota = quota; priv->invert = (flags & NFT_QUOTA_F_INV) ? true : false; - atomic64_set(&priv->remain, quota); + atomic64_set(&priv->consumed, 0); return 0; } @@ -86,9 +86,20 @@ static int nft_quota_obj_init(const struct nlattr * const tb[], static int nft_quota_do_dump(struct sk_buff *skb, const struct nft_quota *priv) { u32 flags = priv->invert ? NFT_QUOTA_F_INV : 0; + u64 consumed; + + consumed = atomic64_read(&priv->consumed); + /* Since we inconditionally increment consumed quota for each packet + * that we see, don't go over the quota boundary in what we send to + * userspace. + */ + if (consumed > priv->quota) + consumed = priv->quota; if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(priv->quota), NFTA_QUOTA_PAD) || + nla_put_be64(skb, NFTA_QUOTA_CONSUMED, cpu_to_be64(consumed), + NFTA_QUOTA_PAD) || nla_put_be32(skb, NFTA_QUOTA_FLAGS, htonl(flags))) goto nla_put_failure; return 0; -- cgit v1.1 From 43da04a593d8b2626f1cf4b56efe9402f6b53652 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:05:44 +0100 Subject: netfilter: nf_tables: atomic dump and reset for stateful objects This patch adds a new NFT_MSG_GETOBJ_RESET command perform an atomic dump-and-reset of the stateful object. This also comes with add support for atomic dump and reset for counter and quota objects. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 3 +- include/uapi/linux/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_tables_api.c | 29 ++++++++++++----- net/netfilter/nft_counter.c | 56 +++++++++++++++++++++++++++----- net/netfilter/nft_quota.c | 18 ++++++---- 5 files changed, 85 insertions(+), 23 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 903cd618f..6f7d6a1 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -997,7 +997,8 @@ struct nft_object_type { struct nft_object *obj); void (*destroy)(struct nft_object *obj); int (*dump)(struct sk_buff *skb, - const struct nft_object *obj); + struct nft_object *obj, + bool reset); }; int nft_register_obj(struct nft_object_type *obj_type); diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 3d47582c..399eac1 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -89,6 +89,7 @@ enum nft_verdicts { * @NFT_MSG_NEWOBJ: create a stateful object (enum nft_obj_attributes) * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) + * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes) */ enum nf_tables_msg_types { NFT_MSG_NEWTABLE, @@ -112,6 +113,7 @@ enum nf_tables_msg_types { NFT_MSG_NEWOBJ, NFT_MSG_GETOBJ, NFT_MSG_DELOBJ, + NFT_MSG_GETOBJ_RESET, NFT_MSG_MAX, }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 2ae717c..bfc015a 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3972,14 +3972,14 @@ err1: } static int nft_object_dump(struct sk_buff *skb, unsigned int attr, - const struct nft_object *obj) + struct nft_object *obj, bool reset) { struct nlattr *nest; nest = nla_nest_start(skb, attr); if (!nest) goto nla_put_failure; - if (obj->type->dump(skb, obj) < 0) + if (obj->type->dump(skb, obj, reset) < 0) goto nla_put_failure; nla_nest_end(skb, nest); return 0; @@ -4096,7 +4096,7 @@ err1: static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net, u32 portid, u32 seq, int event, u32 flags, int family, const struct nft_table *table, - const struct nft_object *obj) + struct nft_object *obj, bool reset) { struct nfgenmsg *nfmsg; struct nlmsghdr *nlh; @@ -4115,7 +4115,7 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net, nla_put_string(skb, NFTA_OBJ_NAME, obj->name) || nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->type->type)) || nla_put_be32(skb, NFTA_OBJ_USE, htonl(obj->use)) || - nft_object_dump(skb, NFTA_OBJ_DATA, obj)) + nft_object_dump(skb, NFTA_OBJ_DATA, obj, reset)) goto nla_put_failure; nlmsg_end(skb, nlh); @@ -4131,10 +4131,14 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); const struct nft_af_info *afi; const struct nft_table *table; - const struct nft_object *obj; unsigned int idx = 0, s_idx = cb->args[0]; struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; + struct nft_object *obj; + bool reset = false; + + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET) + reset = true; rcu_read_lock(); cb->seq = net->nft.base_seq; @@ -4156,7 +4160,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) cb->nlh->nlmsg_seq, NFT_MSG_NEWOBJ, NLM_F_MULTI | NLM_F_APPEND, - afi->family, table, obj) < 0) + afi->family, table, obj, reset) < 0) goto done; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); @@ -4183,6 +4187,7 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk, const struct nft_table *table; struct nft_object *obj; struct sk_buff *skb2; + bool reset = false; u32 objtype; int err; @@ -4214,9 +4219,12 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk, if (!skb2) return -ENOMEM; + if (NFNL_MSG_TYPE(nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET) + reset = true; + err = nf_tables_fill_obj_info(skb2, net, NETLINK_CB(skb).portid, nlh->nlmsg_seq, NFT_MSG_NEWOBJ, 0, - family, table, obj); + family, table, obj, reset); if (err < 0) goto err; @@ -4291,7 +4299,7 @@ static int nf_tables_obj_notify(const struct nft_ctx *ctx, err = nf_tables_fill_obj_info(skb, ctx->net, ctx->portid, ctx->seq, event, 0, ctx->afi->family, ctx->table, - obj); + obj, false); if (err < 0) { kfree_skb(skb); goto err; @@ -4482,6 +4490,11 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .attr_count = NFTA_OBJ_MAX, .policy = nft_obj_policy, }, + [NFT_MSG_GETOBJ_RESET] = { + .call = nf_tables_getobj, + .attr_count = NFTA_OBJ_MAX, + .policy = nft_obj_policy, + }, }; static void nft_chain_commit_update(struct nft_trans *trans) diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index 6f3dd42..f6a02c5 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -100,10 +100,10 @@ static void nft_counter_obj_destroy(struct nft_object *obj) nft_counter_do_destroy(priv); } -static void nft_counter_fetch(const struct nft_counter_percpu __percpu *counter, +static void nft_counter_fetch(struct nft_counter_percpu __percpu *counter, struct nft_counter *total) { - const struct nft_counter_percpu *cpu_stats; + struct nft_counter_percpu *cpu_stats; u64 bytes, packets; unsigned int seq; int cpu; @@ -122,12 +122,52 @@ static void nft_counter_fetch(const struct nft_counter_percpu __percpu *counter, } } +static u64 __nft_counter_reset(u64 *counter) +{ + u64 ret, old; + + do { + old = *counter; + ret = cmpxchg64(counter, old, 0); + } while (ret != old); + + return ret; +} + +static void nft_counter_reset(struct nft_counter_percpu __percpu *counter, + struct nft_counter *total) +{ + struct nft_counter_percpu *cpu_stats; + u64 bytes, packets; + unsigned int seq; + int cpu; + + memset(total, 0, sizeof(*total)); + for_each_possible_cpu(cpu) { + bytes = packets = 0; + + cpu_stats = per_cpu_ptr(counter, cpu); + do { + seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + packets += __nft_counter_reset(&cpu_stats->counter.packets); + bytes += __nft_counter_reset(&cpu_stats->counter.bytes); + } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq)); + + total->packets += packets; + total->bytes += bytes; + } +} + static int nft_counter_do_dump(struct sk_buff *skb, - const struct nft_counter_percpu_priv *priv) + const struct nft_counter_percpu_priv *priv, + bool reset) { struct nft_counter total; - nft_counter_fetch(priv->counter, &total); + if (reset) + nft_counter_reset(priv->counter, &total); + else + nft_counter_fetch(priv->counter, &total); if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes), NFTA_COUNTER_PAD) || @@ -141,11 +181,11 @@ nla_put_failure: } static int nft_counter_obj_dump(struct sk_buff *skb, - const struct nft_object *obj) + struct nft_object *obj, bool reset) { - const struct nft_counter_percpu_priv *priv = nft_obj_data(obj); + struct nft_counter_percpu_priv *priv = nft_obj_data(obj); - return nft_counter_do_dump(skb, priv); + return nft_counter_do_dump(skb, priv, reset); } static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { @@ -178,7 +218,7 @@ static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) { const struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); - return nft_counter_do_dump(skb, priv); + return nft_counter_do_dump(skb, priv, false); } static int nft_counter_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index 0d34420..5d25f57 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -83,12 +83,17 @@ static int nft_quota_obj_init(const struct nlattr * const tb[], return nft_quota_do_init(tb, priv); } -static int nft_quota_do_dump(struct sk_buff *skb, const struct nft_quota *priv) +static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv, + bool reset) { u32 flags = priv->invert ? NFT_QUOTA_F_INV : 0; u64 consumed; - consumed = atomic64_read(&priv->consumed); + if (reset) + consumed = atomic64_xchg(&priv->consumed, 0); + else + consumed = atomic64_read(&priv->consumed); + /* Since we inconditionally increment consumed quota for each packet * that we see, don't go over the quota boundary in what we send to * userspace. @@ -108,11 +113,12 @@ nla_put_failure: return -1; } -static int nft_quota_obj_dump(struct sk_buff *skb, const struct nft_object *obj) +static int nft_quota_obj_dump(struct sk_buff *skb, struct nft_object *obj, + bool reset) { struct nft_quota *priv = nft_obj_data(obj); - return nft_quota_do_dump(skb, priv); + return nft_quota_do_dump(skb, priv, reset); } static struct nft_object_type nft_quota_obj __read_mostly = { @@ -146,9 +152,9 @@ static int nft_quota_init(const struct nft_ctx *ctx, static int nft_quota_dump(struct sk_buff *skb, const struct nft_expr *expr) { - const struct nft_quota *priv = nft_expr_priv(expr); + struct nft_quota *priv = nft_expr_priv(expr); - return nft_quota_do_dump(skb, priv); + return nft_quota_do_dump(skb, priv, false); } static struct nft_expr_type nft_quota_type; -- cgit v1.1 From 2599e98934c5ad166ad184b3682e38aadcb63fb3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:05:48 +0100 Subject: netfilter: nf_tables: notify internal updates of stateful objects Introduce nf_tables_obj_notify() to notify internal state changes in stateful objects. This is used by the quota object to report depletion in a follow up patch. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 ++++ net/netfilter/nf_tables_api.c | 31 +++++++++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 6f7d6a1..339e374 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -969,6 +969,10 @@ struct nft_object *nf_tables_obj_lookup(const struct nft_table *table, const struct nlattr *nla, u32 objtype, u8 genmask); +int nft_obj_notify(struct net *net, struct nft_table *table, + struct nft_object *obj, u32 portid, u32 seq, + int event, int family, int report, gfp_t gfp); + /** * struct nft_object_type - stateful object type * diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index bfc015a..9d2ed3f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4282,38 +4282,45 @@ static int nf_tables_delobj(struct net *net, struct sock *nlsk, return nft_delobj(&ctx, obj); } -static int nf_tables_obj_notify(const struct nft_ctx *ctx, - struct nft_object *obj, int event) +int nft_obj_notify(struct net *net, struct nft_table *table, + struct nft_object *obj, u32 portid, u32 seq, int event, + int family, int report, gfp_t gfp) { struct sk_buff *skb; int err; - if (!ctx->report && - !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) + if (!report && + !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + skb = nlmsg_new(NLMSG_GOODSIZE, gfp); if (skb == NULL) goto err; - err = nf_tables_fill_obj_info(skb, ctx->net, ctx->portid, ctx->seq, - event, 0, ctx->afi->family, ctx->table, - obj, false); + err = nf_tables_fill_obj_info(skb, net, portid, seq, event, 0, family, + table, obj, false); if (err < 0) { kfree_skb(skb); goto err; } - err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, - ctx->report, GFP_KERNEL); + err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, gfp); err: if (err < 0) { - nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, - err); + nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); } return err; } +EXPORT_SYMBOL_GPL(nft_obj_notify); + +static int nf_tables_obj_notify(const struct nft_ctx *ctx, + struct nft_object *obj, int event) +{ + return nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, + ctx->seq, event, ctx->afi->family, ctx->report, + GFP_KERNEL); +} static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, u32 portid, u32 seq) -- cgit v1.1 From 1896531710abcd9a961a17d0c5c6a9f537d479b6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:05:56 +0100 Subject: netfilter: nft_quota: add depleted flag for objects Notify on depleted quota objects. The NFT_QUOTA_F_DEPLETED flag indicates we have reached overquota. Add pointer to table from nft_object, so we can use it when sending the depletion notification to userspace. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 ++ include/uapi/linux/netfilter/nf_tables.h | 1 + net/netfilter/nf_tables_api.c | 1 + net/netfilter/nft_quota.c | 36 +++++++++++++++++++++++++------- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 339e374..ce6fb6e 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -940,6 +940,7 @@ int nft_verdict_dump(struct sk_buff *skb, int type, * struct nft_object - nf_tables stateful object * * @list: table stateful object list node + * @table: table this object belongs to * @type: pointer to object type * @data: pointer to object data * @name: name of this stateful object @@ -950,6 +951,7 @@ int nft_verdict_dump(struct sk_buff *skb, int type, struct nft_object { struct list_head list; char name[NFT_OBJ_MAXNAMELEN]; + struct nft_table *table; u32 genmask:2, use:30; /* runtime data below here */ diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 399eac1..4864cac 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -983,6 +983,7 @@ enum nft_queue_attributes { enum nft_quota_flags { NFT_QUOTA_F_INV = (1 << 0), + NFT_QUOTA_F_DEPLETED = (1 << 1), }; /** diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9d2ed3f..c541970 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4075,6 +4075,7 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk, err = PTR_ERR(obj); goto err1; } + obj->table = table; nla_strlcpy(obj->name, nla[NFTA_OBJ_NAME], NFT_OBJ_MAXNAMELEN); err = nft_trans_obj_add(&ctx, NFT_MSG_NEWOBJ, obj); diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index 5d25f57..7f27ebd 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -17,7 +17,7 @@ struct nft_quota { u64 quota; - bool invert; + unsigned long flags; atomic64_t consumed; }; @@ -27,11 +27,16 @@ static inline bool nft_overquota(struct nft_quota *priv, return atomic64_add_return(skb->len, &priv->consumed) >= priv->quota; } +static inline bool nft_quota_invert(struct nft_quota *priv) +{ + return priv->flags & NFT_QUOTA_F_INV; +} + static inline void nft_quota_do_eval(struct nft_quota *priv, struct nft_regs *regs, const struct nft_pktinfo *pkt) { - if (nft_overquota(priv, pkt->skb) ^ priv->invert) + if (nft_overquota(priv, pkt->skb) ^ nft_quota_invert(priv)) regs->verdict.code = NFT_BREAK; } @@ -40,19 +45,29 @@ static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = { [NFTA_QUOTA_FLAGS] = { .type = NLA_U32 }, }; +#define NFT_QUOTA_DEPLETED_BIT 1 /* From NFT_QUOTA_F_DEPLETED. */ + static void nft_quota_obj_eval(struct nft_object *obj, struct nft_regs *regs, const struct nft_pktinfo *pkt) { struct nft_quota *priv = nft_obj_data(obj); + bool overquota; - nft_quota_do_eval(priv, regs, pkt); + overquota = nft_overquota(priv, pkt->skb); + if (overquota ^ nft_quota_invert(priv)) + regs->verdict.code = NFT_BREAK; + + if (overquota && + !test_and_set_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags)) + nft_obj_notify(nft_net(pkt), obj->table, obj, 0, 0, + NFT_MSG_NEWOBJ, nft_pf(pkt), 0, GFP_ATOMIC); } static int nft_quota_do_init(const struct nlattr * const tb[], struct nft_quota *priv) { - u32 flags = 0; + unsigned long flags = 0; u64 quota; if (!tb[NFTA_QUOTA_BYTES]) @@ -66,10 +81,12 @@ static int nft_quota_do_init(const struct nlattr * const tb[], flags = ntohl(nla_get_be32(tb[NFTA_QUOTA_FLAGS])); if (flags & ~NFT_QUOTA_F_INV) return -EINVAL; + if (flags & NFT_QUOTA_F_DEPLETED) + return -EOPNOTSUPP; } priv->quota = quota; - priv->invert = (flags & NFT_QUOTA_F_INV) ? true : false; + priv->flags = flags; atomic64_set(&priv->consumed, 0); return 0; @@ -86,13 +103,16 @@ static int nft_quota_obj_init(const struct nlattr * const tb[], static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv, bool reset) { - u32 flags = priv->invert ? NFT_QUOTA_F_INV : 0; + u32 flags = priv->flags; u64 consumed; - if (reset) + if (reset) { consumed = atomic64_xchg(&priv->consumed, 0); - else + if (test_and_clear_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags)) + flags |= NFT_QUOTA_F_DEPLETED; + } else { consumed = atomic64_read(&priv->consumed); + } /* Since we inconditionally increment consumed quota for each packet * that we see, don't go over the quota boundary in what we send to -- cgit v1.1 From 8aeff920dcc9b3f8cf43042a76428582634d9208 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:06:00 +0100 Subject: netfilter: nf_tables: add stateful object reference to set elements This patch allows you to refer to stateful objects from set elements. This provides the infrastructure to create maps where the right hand side of the mapping is a stateful object. This allows us to build dictionaries of stateful objects, that you can use to perform fast lookups using any arbitrary key combination. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 9 ++++ include/uapi/linux/netfilter/nf_tables.h | 8 ++++ net/netfilter/nf_tables_api.c | 72 +++++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index ce6fb6e..85f0f03 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -326,6 +326,7 @@ void nft_unregister_set(struct nft_set_ops *ops); * @name: name of the set * @ktype: key type (numeric type defined by userspace, not used in the kernel) * @dtype: data type (verdict or numeric type defined by userspace) + * @objtype: object type (see NFT_OBJECT_* definitions) * @size: maximum set size * @nelems: number of elements * @ndeact: number of deactivated elements queued for removal @@ -347,6 +348,7 @@ struct nft_set { char name[NFT_SET_MAXNAMELEN]; u32 ktype; u32 dtype; + u32 objtype; u32 size; atomic_t nelems; u32 ndeact; @@ -416,6 +418,7 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, * @NFT_SET_EXT_EXPIRATION: element expiration time * @NFT_SET_EXT_USERDATA: user data associated with the element * @NFT_SET_EXT_EXPR: expression assiociated with the element + * @NFT_SET_EXT_OBJREF: stateful object reference associated with element * @NFT_SET_EXT_NUM: number of extension types */ enum nft_set_extensions { @@ -426,6 +429,7 @@ enum nft_set_extensions { NFT_SET_EXT_EXPIRATION, NFT_SET_EXT_USERDATA, NFT_SET_EXT_EXPR, + NFT_SET_EXT_OBJREF, NFT_SET_EXT_NUM }; @@ -554,6 +558,11 @@ static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, return elem + set->ops->elemsize; } +static inline struct nft_object **nft_set_ext_obj(const struct nft_set_ext *ext) +{ + return nft_set_ext(ext, NFT_SET_EXT_OBJREF); +} + void *nft_set_elem_init(const struct nft_set *set, const struct nft_set_ext_tmpl *tmpl, const u32 *key, const u32 *data, diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 4864cac..a6b52db 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -255,6 +255,7 @@ enum nft_rule_compat_attributes { * @NFT_SET_MAP: set is used as a dictionary * @NFT_SET_TIMEOUT: set uses timeouts * @NFT_SET_EVAL: set contains expressions for evaluation + * @NFT_SET_OBJECT: set contains stateful objects */ enum nft_set_flags { NFT_SET_ANONYMOUS = 0x1, @@ -263,6 +264,7 @@ enum nft_set_flags { NFT_SET_MAP = 0x8, NFT_SET_TIMEOUT = 0x10, NFT_SET_EVAL = 0x20, + NFT_SET_OBJECT = 0x40, }; /** @@ -304,6 +306,7 @@ enum nft_set_desc_attributes { * @NFTA_SET_TIMEOUT: default timeout value (NLA_U64) * @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32) * @NFTA_SET_USERDATA: user data (NLA_BINARY) + * @NFTA_SET_OBJ_TYPE: stateful object type (NLA_U32: NFT_OBJECT_*) */ enum nft_set_attributes { NFTA_SET_UNSPEC, @@ -321,6 +324,7 @@ enum nft_set_attributes { NFTA_SET_GC_INTERVAL, NFTA_SET_USERDATA, NFTA_SET_PAD, + NFTA_SET_OBJ_TYPE, __NFTA_SET_MAX }; #define NFTA_SET_MAX (__NFTA_SET_MAX - 1) @@ -344,6 +348,7 @@ enum nft_set_elem_flags { * @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64) * @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY) * @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes) + * @NFTA_SET_ELEM_OBJREF: stateful object reference (NLA_STRING) */ enum nft_set_elem_attributes { NFTA_SET_ELEM_UNSPEC, @@ -355,6 +360,7 @@ enum nft_set_elem_attributes { NFTA_SET_ELEM_USERDATA, NFTA_SET_ELEM_EXPR, NFTA_SET_ELEM_PAD, + NFTA_SET_ELEM_OBJREF, __NFTA_SET_ELEM_MAX }; #define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1) @@ -1207,6 +1213,8 @@ enum nft_fib_flags { #define NFT_OBJECT_UNSPEC 0 #define NFT_OBJECT_COUNTER 1 #define NFT_OBJECT_QUOTA 2 +#define __NFT_OBJECT_MAX 3 +#define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) /** * enum nft_object_attributes - nf_tables stateful object netlink attributes diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index c541970..8228714 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2452,6 +2452,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_GC_INTERVAL] = { .type = NLA_U32 }, [NFTA_SET_USERDATA] = { .type = NLA_BINARY, .len = NFT_USERDATA_MAXLEN }, + [NFTA_SET_OBJ_TYPE] = { .type = NLA_U32 }, }; static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { @@ -2609,6 +2610,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen))) goto nla_put_failure; } + if (set->flags & NFT_SET_OBJECT && + nla_put_be32(skb, NFTA_SET_OBJ_TYPE, htonl(set->objtype))) + goto nla_put_failure; if (set->timeout && nla_put_be64(skb, NFTA_SET_TIMEOUT, @@ -2838,7 +2842,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, unsigned int size; bool create; u64 timeout; - u32 ktype, dtype, flags, policy, gc_int; + u32 ktype, dtype, flags, policy, gc_int, objtype; struct nft_set_desc desc; unsigned char *udata; u16 udlen; @@ -2868,11 +2872,12 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | NFT_SET_INTERVAL | NFT_SET_TIMEOUT | - NFT_SET_MAP | NFT_SET_EVAL)) + NFT_SET_MAP | NFT_SET_EVAL | + NFT_SET_OBJECT)) return -EINVAL; - /* Only one of both operations is supported */ - if ((flags & (NFT_SET_MAP | NFT_SET_EVAL)) == - (NFT_SET_MAP | NFT_SET_EVAL)) + /* Only one of these operations is supported */ + if ((flags & (NFT_SET_MAP | NFT_SET_EVAL | NFT_SET_OBJECT)) == + (NFT_SET_MAP | NFT_SET_EVAL | NFT_SET_OBJECT)) return -EOPNOTSUPP; } @@ -2897,6 +2902,19 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, } else if (flags & NFT_SET_MAP) return -EINVAL; + if (nla[NFTA_SET_OBJ_TYPE] != NULL) { + if (!(flags & NFT_SET_OBJECT)) + return -EINVAL; + + objtype = ntohl(nla_get_be32(nla[NFTA_SET_OBJ_TYPE])); + if (objtype == NFT_OBJECT_UNSPEC || + objtype > NFT_OBJECT_MAX) + return -EINVAL; + } else if (flags & NFT_SET_OBJECT) + return -EINVAL; + else + objtype = NFT_OBJECT_UNSPEC; + timeout = 0; if (nla[NFTA_SET_TIMEOUT] != NULL) { if (!(flags & NFT_SET_TIMEOUT)) @@ -2984,6 +3002,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, set->ktype = ktype; set->klen = desc.klen; set->dtype = dtype; + set->objtype = objtype; set->dlen = desc.dlen; set->flags = flags; set->size = desc.size; @@ -3126,6 +3145,10 @@ const struct nft_set_ext_type nft_set_ext_types[] = { [NFT_SET_EXT_EXPR] = { .align = __alignof__(struct nft_expr), }, + [NFT_SET_EXT_OBJREF] = { + .len = sizeof(struct nft_object *), + .align = __alignof__(struct nft_object *), + }, [NFT_SET_EXT_FLAGS] = { .len = sizeof(u8), .align = __alignof__(u8), @@ -3214,6 +3237,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0) goto nla_put_failure; + if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) && + nla_put_string(skb, NFTA_SET_ELEM_OBJREF, + (*nft_set_ext_obj(ext))->name) < 0) + goto nla_put_failure; + if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(*nft_set_ext_flags(ext)))) @@ -3508,7 +3536,8 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem, nft_data_uninit(nft_set_ext_data(ext), set->dtype); if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext)); - + if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF)) + (*nft_set_ext_obj(ext))->use--; kfree(elem); } EXPORT_SYMBOL_GPL(nft_set_elem_destroy); @@ -3533,11 +3562,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr, u32 nlmsg_flags) { struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; + u8 genmask = nft_genmask_next(ctx->net); struct nft_data_desc d1, d2; struct nft_set_ext_tmpl tmpl; struct nft_set_ext *ext, *ext2; struct nft_set_elem elem; struct nft_set_binding *binding; + struct nft_object *obj = NULL; struct nft_userdata *udata; struct nft_data data; enum nft_registers dreg; @@ -3600,6 +3631,20 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT); } + if (nla[NFTA_SET_ELEM_OBJREF] != NULL) { + if (!(set->flags & NFT_SET_OBJECT)) { + err = -EINVAL; + goto err2; + } + obj = nf_tables_obj_lookup(ctx->table, nla[NFTA_SET_ELEM_OBJREF], + set->objtype, genmask); + if (IS_ERR(obj)) { + err = PTR_ERR(obj); + goto err2; + } + nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF); + } + if (nla[NFTA_SET_ELEM_DATA] != NULL) { err = nft_data_init(ctx, &data, sizeof(data), &d2, nla[NFTA_SET_ELEM_DATA]); @@ -3658,6 +3703,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, udata->len = ulen - 1; nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); } + if (obj) { + *nft_set_ext_obj(ext) = obj; + obj->use++; + } trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); if (trans == NULL) @@ -3667,10 +3716,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, err = set->ops->insert(ctx->net, set, &elem, &ext2); if (err) { if (err == -EEXIST) { - if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && - nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) && - memcmp(nft_set_ext_data(ext), - nft_set_ext_data(ext2), set->dlen) != 0) + if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && + nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) && + memcmp(nft_set_ext_data(ext), + nft_set_ext_data(ext2), set->dlen) != 0) || + (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) && + nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF) && + *nft_set_ext_obj(ext) != *nft_set_ext_obj(ext2))) err = -EBUSY; else if (!(nlmsg_flags & NLM_F_EXCL)) err = 0; -- cgit v1.1 From 63aea29060025fd2732680aa48a6b97687b93af8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:06:03 +0100 Subject: netfilter: nft_objref: support for stateful object maps This patch allows us to refer to stateful object dictionaries, the source register indicates the key data to be used to look up for the corresponding state object. We can refer to these maps through names or, alternatively, the map transaction id. This allows us to refer to both anonymous and named maps. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 6 ++ net/netfilter/nf_tables_api.c | 4 ++ net/netfilter/nft_objref.c | 116 ++++++++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index a6b52db..881d49e 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1153,11 +1153,17 @@ enum nft_fwd_attributes { * * @NFTA_OBJREF_IMM_TYPE: object type for immediate reference (NLA_U32: nft_register) * @NFTA_OBJREF_IMM_NAME: object name for immediate reference (NLA_STRING) + * @NFTA_OBJREF_SET_SREG: source register of the data to look for (NLA_U32: nft_registers) + * @NFTA_OBJREF_SET_NAME: name of the set where to look for (NLA_STRING) + * @NFTA_OBJREF_SET_ID: id of the set where to look for in this transaction (NLA_U32) */ enum nft_objref_attributes { NFTA_OBJREF_UNSPEC, NFTA_OBJREF_IMM_TYPE, NFTA_OBJREF_IMM_NAME, + NFTA_OBJREF_SET_SREG, + NFTA_OBJREF_SET_NAME, + NFTA_OBJREF_SET_ID, __NFTA_OBJREF_MAX }; #define NFTA_OBJREF_MAX (__NFTA_OBJREF_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 8228714..b4db5bf 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2504,6 +2504,7 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table, } return ERR_PTR(-ENOENT); } +EXPORT_SYMBOL_GPL(nf_tables_set_lookup); struct nft_set *nf_tables_set_lookup_byid(const struct net *net, const struct nlattr *nla, @@ -2522,6 +2523,7 @@ struct nft_set *nf_tables_set_lookup_byid(const struct net *net, } return ERR_PTR(-ENOENT); } +EXPORT_SYMBOL_GPL(nf_tables_set_lookup_byid); static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, const char *name) @@ -3124,6 +3126,7 @@ bind: list_add_tail_rcu(&binding->list, &set->bindings); return 0; } +EXPORT_SYMBOL_GPL(nf_tables_bind_set); void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_binding *binding) @@ -3134,6 +3137,7 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, nft_is_active(ctx->net, set)) nf_tables_set_destroy(ctx, set); } +EXPORT_SYMBOL_GPL(nf_tables_unbind_set); const struct nft_set_ext_type nft_set_ext_types[] = { [NFT_SET_EXT_KEY] = { diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 23820f7..415a65b 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -81,14 +81,128 @@ static const struct nft_expr_ops nft_objref_ops = { .dump = nft_objref_dump, }; +struct nft_objref_map { + struct nft_set *set; + enum nft_registers sreg:8; + struct nft_set_binding binding; +}; + +static void nft_objref_map_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_objref_map *priv = nft_expr_priv(expr); + const struct nft_set *set = priv->set; + const struct nft_set_ext *ext; + struct nft_object *obj; + bool found; + + found = set->ops->lookup(nft_net(pkt), set, ®s->data[priv->sreg], + &ext); + if (!found) { + regs->verdict.code = NFT_BREAK; + return; + } + obj = *nft_set_ext_obj(ext); + obj->type->eval(obj, regs, pkt); +} + +static int nft_objref_map_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_objref_map *priv = nft_expr_priv(expr); + u8 genmask = nft_genmask_next(ctx->net); + struct nft_set *set; + int err; + + set = nf_tables_set_lookup(ctx->table, tb[NFTA_OBJREF_SET_NAME], genmask); + if (IS_ERR(set)) { + if (tb[NFTA_OBJREF_SET_ID]) { + set = nf_tables_set_lookup_byid(ctx->net, + tb[NFTA_OBJREF_SET_ID], + genmask); + } + if (IS_ERR(set)) + return PTR_ERR(set); + } + + if (!(set->flags & NFT_SET_OBJECT)) + return -EINVAL; + + priv->sreg = nft_parse_register(tb[NFTA_OBJREF_SET_SREG]); + err = nft_validate_register_load(priv->sreg, set->klen); + if (err < 0) + return err; + + priv->binding.flags = set->flags & NFT_SET_OBJECT; + + err = nf_tables_bind_set(ctx, set, &priv->binding); + if (err < 0) + return err; + + priv->set = set; + return 0; +} + +static int nft_objref_map_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_objref_map *priv = nft_expr_priv(expr); + + if (nft_dump_register(skb, NFTA_OBJREF_SET_SREG, priv->sreg) || + nla_put_string(skb, NFTA_OBJREF_SET_NAME, priv->set->name)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +static void nft_objref_map_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_objref_map *priv = nft_expr_priv(expr); + + nf_tables_unbind_set(ctx, priv->set, &priv->binding); +} + +static struct nft_expr_type nft_objref_type; +static const struct nft_expr_ops nft_objref_map_ops = { + .type = &nft_objref_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)), + .eval = nft_objref_map_eval, + .init = nft_objref_map_init, + .destroy = nft_objref_map_destroy, + .dump = nft_objref_map_dump, +}; + +static const struct nft_expr_ops * +nft_objref_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + if (tb[NFTA_OBJREF_SET_SREG] && + (tb[NFTA_OBJREF_SET_NAME] || + tb[NFTA_OBJREF_SET_ID])) + return &nft_objref_map_ops; + else if (tb[NFTA_OBJREF_IMM_NAME] && + tb[NFTA_OBJREF_IMM_TYPE]) + return &nft_objref_ops; + + return ERR_PTR(-EOPNOTSUPP); +} + static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { [NFTA_OBJREF_IMM_NAME] = { .type = NLA_STRING }, [NFTA_OBJREF_IMM_TYPE] = { .type = NLA_U32 }, + [NFTA_OBJREF_SET_SREG] = { .type = NLA_U32 }, + [NFTA_OBJREF_SET_NAME] = { .type = NLA_STRING }, + [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, }; static struct nft_expr_type nft_objref_type __read_mostly = { .name = "objref", - .ops = &nft_objref_ops, + .select_ops = nft_objref_select_ops, .policy = nft_objref_policy, .maxattr = NFTA_OBJREF_MAX, .owner = THIS_MODULE, -- cgit v1.1 From a9fea2a3c3cf7a09b7f4302489f535477ead67d2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 Nov 2016 00:06:11 +0100 Subject: netfilter: nf_tables: allow to filter stateful object dumps by type This patch adds the netlink code to filter out dump of stateful objects, through the NFTA_OBJ_TYPE netlink attribute. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index b4db5bf..b04d4ee 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4183,12 +4183,18 @@ nla_put_failure: return -1; } +struct nft_obj_filter { + char table[NFT_OBJ_MAXNAMELEN]; + u32 type; +}; + static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) { const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); const struct nft_af_info *afi; const struct nft_table *table; unsigned int idx = 0, s_idx = cb->args[0]; + struct nft_obj_filter *filter = cb->data; struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; struct nft_object *obj; @@ -4213,6 +4219,13 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) if (idx > s_idx) memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(cb->args[0])); + if (filter->table[0] && + strcmp(filter->table, table->name)) + goto cont; + if (filter->type != NFT_OBJECT_UNSPEC && + obj->type->type != filter->type) + goto cont; + if (nf_tables_fill_obj_info(skb, net, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NFT_MSG_NEWOBJ, @@ -4233,6 +4246,31 @@ done: return skb->len; } +static int nf_tables_dump_obj_done(struct netlink_callback *cb) +{ + kfree(cb->data); + + return 0; +} + +static struct nft_obj_filter * +nft_obj_filter_alloc(const struct nlattr * const nla[]) +{ + struct nft_obj_filter *filter; + + filter = kzalloc(sizeof(*filter), GFP_KERNEL); + if (!filter) + return ERR_PTR(-ENOMEM); + + if (nla[NFTA_OBJ_TABLE]) + nla_strlcpy(filter->table, nla[NFTA_OBJ_TABLE], + NFT_TABLE_MAXNAMELEN); + if (nla[NFTA_OBJ_TYPE]) + filter->type = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE])); + + return filter; +} + static int nf_tables_getobj(struct net *net, struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -4251,7 +4289,19 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk, if (nlh->nlmsg_flags & NLM_F_DUMP) { struct netlink_dump_control c = { .dump = nf_tables_dump_obj, + .done = nf_tables_dump_obj_done, }; + + if (nla[NFTA_OBJ_TABLE] || + nla[NFTA_OBJ_TYPE]) { + struct nft_obj_filter *filter; + + filter = nft_obj_filter_alloc(nla); + if (IS_ERR(filter)) + return -ENOMEM; + + c.data = filter; + } return netlink_dump_start(nlsk, skb, nlh, &c); } -- cgit v1.1 From 3b760dcb0fd304873dfde9ff072a49f893c6f5c4 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sat, 3 Dec 2016 21:25:08 +0800 Subject: netfilter: rpfilter: bypass ipv4 lbcast packets with zeronet source Otherwise, DHCP Discover packets(0.0.0.0->255.255.255.255) may be dropped incorrectly. Signed-off-by: Liping Zhang Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/ipt_rpfilter.c | 8 +++++--- net/ipv4/netfilter/nft_fib_ipv4.c | 13 +++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/net/ipv4/netfilter/ipt_rpfilter.c b/net/ipv4/netfilter/ipt_rpfilter.c index 59b4994..f273098 100644 --- a/net/ipv4/netfilter/ipt_rpfilter.c +++ b/net/ipv4/netfilter/ipt_rpfilter.c @@ -83,10 +83,12 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) return true ^ invert; iph = ip_hdr(skb); - if (ipv4_is_multicast(iph->daddr)) { - if (ipv4_is_zeronet(iph->saddr)) - return ipv4_is_local_multicast(iph->daddr) ^ invert; + if (ipv4_is_zeronet(iph->saddr)) { + if (ipv4_is_lbcast(iph->daddr) || + ipv4_is_local_multicast(iph->daddr)) + return true ^ invert; } + flow.flowi4_iif = LOOPBACK_IFINDEX; flow.daddr = iph->saddr; flow.saddr = rpfilter_get_saddr(iph->daddr); diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index 2581363..965b1a1 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -101,12 +101,13 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, } iph = ip_hdr(pkt->skb); - if (ipv4_is_multicast(iph->daddr) && - ipv4_is_zeronet(iph->saddr) && - ipv4_is_local_multicast(iph->daddr)) { - nft_fib_store_result(dest, priv->result, pkt, - get_ifindex(pkt->skb->dev)); - return; + if (ipv4_is_zeronet(iph->saddr)) { + if (ipv4_is_lbcast(iph->daddr) || + ipv4_is_local_multicast(iph->daddr)) { + nft_fib_store_result(dest, priv->result, pkt, + get_ifindex(pkt->skb->dev)); + return; + } } if (priv->flags & NFTA_FIB_F_MARK) -- cgit v1.1 From 3189a290f98d3d3b76536ab61e9ff95751ed8124 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Mon, 5 Dec 2016 15:33:57 +0100 Subject: netfilter: nat: skip checksum on offload SCTP packets SCTP GSO and hardware can do CRC32c computation after netfilter processing, so we can avoid calling sctp_compute_checksum() on skb if skb->ip_summed is equal to CHECKSUM_PARTIAL. Moreover, set skb->ip_summed to CHECKSUM_NONE when the NAT code computes the CRC, to prevent offloaders from computing it again (on ixgbe this resulted in a transmission with wrong L4 checksum). Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_nat_proto_sctp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_nat_proto_sctp.c b/net/netfilter/nf_nat_proto_sctp.c index 2e14108..31d3586 100644 --- a/net/netfilter/nf_nat_proto_sctp.c +++ b/net/netfilter/nf_nat_proto_sctp.c @@ -47,7 +47,10 @@ sctp_manip_pkt(struct sk_buff *skb, hdr->dest = tuple->dst.u.sctp.port; } - hdr->checksum = sctp_compute_cksum(skb, hdroff); + if (skb->ip_summed != CHECKSUM_PARTIAL) { + hdr->checksum = sctp_compute_cksum(skb, hdroff); + skb->ip_summed = CHECKSUM_NONE; + } return true; } -- cgit v1.1 From 1a37ef769d68d2719a9a418bd80fc42d19259b24 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 5 Dec 2016 23:35:48 +0100 Subject: netfilter: nf_tables: constify struct nft_ctx * parameter in nft_trans_alloc() Context is not modified by nft_trans_alloc(), so constify it. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index b04d4ee..b420597 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -111,8 +111,8 @@ static void nft_ctx_init(struct nft_ctx *ctx, ctx->seq = nlh->nlmsg_seq; } -static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type, - u32 size) +static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx, + int msg_type, u32 size) { struct nft_trans *trans; -- cgit v1.1 From 37df5301a3ae903c5b1aa90cae37c6c669dfc386 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 5 Dec 2016 23:35:49 +0100 Subject: netfilter: nft_set: introduce nft_{hash, rbtree}_deactivate_one() This new function allows us to deactivate one single element, this is required by the set flush command that comes in a follow up patch. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_set_hash.c | 24 +++++++++++++++++------- net/netfilter/nft_set_rbtree.c | 11 ++++++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index a3dface..73f7687 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -167,6 +167,19 @@ static void nft_hash_activate(const struct net *net, const struct nft_set *set, nft_set_elem_clear_busy(&he->ext); } +static bool nft_hash_deactivate_one(const struct net *net, + const struct nft_set *set, void *priv) +{ + struct nft_hash_elem *he = priv; + + if (!nft_set_elem_mark_busy(&he->ext) || + !nft_is_active(net, &he->ext)) { + nft_set_elem_change_active(net, set, &he->ext); + return true; + } + return false; +} + static void *nft_hash_deactivate(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem) @@ -181,13 +194,10 @@ static void *nft_hash_deactivate(const struct net *net, rcu_read_lock(); he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); - if (he != NULL) { - if (!nft_set_elem_mark_busy(&he->ext) || - !nft_is_active(net, &he->ext)) - nft_set_elem_change_active(net, set, &he->ext); - else - he = NULL; - } + if (he != NULL && + !nft_hash_deactivate_one(net, set, he)) + he = NULL; + rcu_read_unlock(); return he; diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 36493a7..5580bb6 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -171,6 +171,15 @@ static void nft_rbtree_activate(const struct net *net, nft_set_elem_change_active(net, set, &rbe->ext); } +static bool nft_rbtree_deactivate_one(const struct net *net, + const struct nft_set *set, void *priv) +{ + struct nft_rbtree_elem *rbe = priv; + + nft_set_elem_change_active(net, set, &rbe->ext); + return true; +} + static void *nft_rbtree_deactivate(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem) @@ -204,7 +213,7 @@ static void *nft_rbtree_deactivate(const struct net *net, parent = parent->rb_right; continue; } - nft_set_elem_change_active(net, set, &rbe->ext); + nft_rbtree_deactivate_one(net, set, rbe); return rbe; } } -- cgit v1.1 From 8411b6442e59810fe0750a2f321b9dcb7d0a3d17 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 5 Dec 2016 23:35:50 +0100 Subject: netfilter: nf_tables: support for set flushing This patch adds support for set flushing, that consists of walking over the set elements if the NFTA_SET_ELEM_LIST_ELEMENTS attribute is set. This patch requires the following changes: 1) Add set->ops->deactivate_one() operation: This allows us to deactivate an element from the set element walk path, given we can skip the lookup that happens in ->deactivate(). 2) Add a new nft_trans_alloc_gfp() function since we need to allocate transactions using GFP_ATOMIC given the set walk path happens with held rcu_read_lock. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 6 ++++- net/netfilter/nf_tables_api.c | 55 ++++++++++++++++++++++++++++++++++----- net/netfilter/nft_set_hash.c | 1 + net/netfilter/nft_set_rbtree.c | 1 + 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 85f0f03..924325c 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -259,7 +259,8 @@ struct nft_expr; * @lookup: look up an element within the set * @insert: insert new element into set * @activate: activate new element in the next generation - * @deactivate: deactivate element in the next generation + * @deactivate: lookup for element and deactivate it in the next generation + * @deactivate_one: deactivate element in the next generation * @remove: remove element from set * @walk: iterate over all set elemeennts * @privsize: function to return size of set private data @@ -294,6 +295,9 @@ struct nft_set_ops { void * (*deactivate)(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem); + bool (*deactivate_one)(const struct net *net, + const struct nft_set *set, + void *priv); void (*remove)(const struct nft_set *set, const struct nft_set_elem *elem); void (*walk)(const struct nft_ctx *ctx, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index b420597..a019a87 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -111,12 +111,12 @@ static void nft_ctx_init(struct nft_ctx *ctx, ctx->seq = nlh->nlmsg_seq; } -static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx, - int msg_type, u32 size) +static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx, + int msg_type, u32 size, gfp_t gfp) { struct nft_trans *trans; - trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL); + trans = kzalloc(sizeof(struct nft_trans) + size, gfp); if (trans == NULL) return NULL; @@ -126,6 +126,12 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx, return trans; } +static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx, + int msg_type, u32 size) +{ + return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL); +} + static void nft_trans_destroy(struct nft_trans *trans) { list_del(&trans->list); @@ -3876,6 +3882,34 @@ err1: return err; } +static int nft_flush_set(const struct nft_ctx *ctx, + const struct nft_set *set, + const struct nft_set_iter *iter, + const struct nft_set_elem *elem) +{ + struct nft_trans *trans; + int err; + + trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM, + sizeof(struct nft_trans_elem), GFP_ATOMIC); + if (!trans) + return -ENOMEM; + + if (!set->ops->deactivate_one(ctx->net, set, elem->priv)) { + err = -ENOENT; + goto err1; + } + + nft_trans_elem_set(trans) = (struct nft_set *)set; + nft_trans_elem(trans) = *((struct nft_set_elem *)elem); + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + + return 0; +err1: + kfree(trans); + return err; +} + static int nf_tables_delsetelem(struct net *net, struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -3886,9 +3920,6 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk, struct nft_ctx ctx; int rem, err = 0; - if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) - return -EINVAL; - err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask); if (err < 0) return err; @@ -3900,6 +3931,18 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk, if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) return -EBUSY; + if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) { + struct nft_set_dump_args args = { + .iter = { + .genmask = genmask, + .fn = nft_flush_set, + }, + }; + set->ops->walk(&ctx, set, &args.iter); + + return args.iter.err; + } + nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { err = nft_del_setelem(&ctx, set, attr); if (err < 0) diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 73f7687..1e20e2b 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -397,6 +397,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = { .insert = nft_hash_insert, .activate = nft_hash_activate, .deactivate = nft_hash_deactivate, + .deactivate_one = nft_hash_deactivate_one, .remove = nft_hash_remove, .lookup = nft_hash_lookup, .update = nft_hash_update, diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 5580bb6..08376e5 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -304,6 +304,7 @@ static struct nft_set_ops nft_rbtree_ops __read_mostly = { .insert = nft_rbtree_insert, .remove = nft_rbtree_remove, .deactivate = nft_rbtree_deactivate, + .deactivate_one = nft_rbtree_deactivate_one, .activate = nft_rbtree_activate, .lookup = nft_rbtree_lookup, .walk = nft_rbtree_walk, -- cgit v1.1 From 5bad87348c70bb4d36e876523aade31e9dedd2fc Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Fri, 2 Dec 2016 07:46:38 -0200 Subject: netfilter: x_tables: avoid warn and OOM killer on vmalloc call Andrey Konovalov reported that this vmalloc call is based on an userspace request and that it's spewing traces, which may flood the logs and cause DoS if abused. Florian Westphal also mentioned that this call should not trigger OOM killer. This patch brings the vmalloc call in sync to kmalloc and disables the warn trace on allocation failure and also disable OOM killer invocation. Note, however, that under such stress situation, other places may trigger OOM killer invocation. Reported-by: Andrey Konovalov Cc: Florian Westphal Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: Pablo Neira Ayuso --- net/netfilter/x_tables.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index f6ce4a7..2ff4996 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -959,7 +959,9 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size) if (sz <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) info = kmalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); if (!info) { - info = vmalloc(sz); + info = __vmalloc(sz, GFP_KERNEL | __GFP_NOWARN | + __GFP_NORETRY | __GFP_HIGHMEM, + PAGE_KERNEL); if (!info) return NULL; } -- cgit v1.1 From 2c16d60332643e90d4fa244f4a706c454b8c7569 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 6 Dec 2016 16:25:02 -0500 Subject: netfilter: xt_bpf: support ebpf Add support for attaching an eBPF object by file descriptor. The iptables binary can be called with a path to an elf object or a pinned bpf object. Also pass the mode and path to the kernel to be able to return it later for iptables dump and save. Signed-off-by: Willem de Bruijn Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/xt_bpf.h | 21 ++++++++ net/netfilter/xt_bpf.c | 96 +++++++++++++++++++++++++++++------ 2 files changed, 101 insertions(+), 16 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h index 1fad2c2..b97725a 100644 --- a/include/uapi/linux/netfilter/xt_bpf.h +++ b/include/uapi/linux/netfilter/xt_bpf.h @@ -2,9 +2,11 @@ #define _XT_BPF_H #include +#include #include #define XT_BPF_MAX_NUM_INSTR 64 +#define XT_BPF_PATH_MAX (XT_BPF_MAX_NUM_INSTR * sizeof(struct sock_filter)) struct bpf_prog; @@ -16,4 +18,23 @@ struct xt_bpf_info { struct bpf_prog *filter __attribute__((aligned(8))); }; +enum xt_bpf_modes { + XT_BPF_MODE_BYTECODE, + XT_BPF_MODE_FD_PINNED, + XT_BPF_MODE_FD_ELF, +}; + +struct xt_bpf_info_v1 { + __u16 mode; + __u16 bpf_program_num_elem; + __s32 fd; + union { + struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR]; + char path[XT_BPF_PATH_MAX]; + }; + + /* only used in the kernel */ + struct bpf_prog *filter __attribute__((aligned(8))); +}; + #endif /*_XT_BPF_H */ diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c index dffee9d47..2dedaa2 100644 --- a/net/netfilter/xt_bpf.c +++ b/net/netfilter/xt_bpf.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -20,15 +21,15 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_bpf"); MODULE_ALIAS("ip6t_bpf"); -static int bpf_mt_check(const struct xt_mtchk_param *par) +static int __bpf_mt_check_bytecode(struct sock_filter *insns, __u16 len, + struct bpf_prog **ret) { - struct xt_bpf_info *info = par->matchinfo; struct sock_fprog_kern program; - program.len = info->bpf_program_num_elem; - program.filter = info->bpf_program; + program.len = len; + program.filter = insns; - if (bpf_prog_create(&info->filter, &program)) { + if (bpf_prog_create(ret, &program)) { pr_info("bpf: check failed: parse error\n"); return -EINVAL; } @@ -36,6 +37,42 @@ static int bpf_mt_check(const struct xt_mtchk_param *par) return 0; } +static int __bpf_mt_check_fd(int fd, struct bpf_prog **ret) +{ + struct bpf_prog *prog; + + prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_SOCKET_FILTER); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + *ret = prog; + return 0; +} + +static int bpf_mt_check(const struct xt_mtchk_param *par) +{ + struct xt_bpf_info *info = par->matchinfo; + + return __bpf_mt_check_bytecode(info->bpf_program, + info->bpf_program_num_elem, + &info->filter); +} + +static int bpf_mt_check_v1(const struct xt_mtchk_param *par) +{ + struct xt_bpf_info_v1 *info = par->matchinfo; + + if (info->mode == XT_BPF_MODE_BYTECODE) + return __bpf_mt_check_bytecode(info->bpf_program, + info->bpf_program_num_elem, + &info->filter); + else if (info->mode == XT_BPF_MODE_FD_PINNED || + info->mode == XT_BPF_MODE_FD_ELF) + return __bpf_mt_check_fd(info->fd, &info->filter); + else + return -EINVAL; +} + static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_bpf_info *info = par->matchinfo; @@ -43,31 +80,58 @@ static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par) return BPF_PROG_RUN(info->filter, skb); } +static bool bpf_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_bpf_info_v1 *info = par->matchinfo; + + return !!bpf_prog_run_save_cb(info->filter, (struct sk_buff *) skb); +} + static void bpf_mt_destroy(const struct xt_mtdtor_param *par) { const struct xt_bpf_info *info = par->matchinfo; + + bpf_prog_destroy(info->filter); +} + +static void bpf_mt_destroy_v1(const struct xt_mtdtor_param *par) +{ + const struct xt_bpf_info_v1 *info = par->matchinfo; + bpf_prog_destroy(info->filter); } -static struct xt_match bpf_mt_reg __read_mostly = { - .name = "bpf", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = bpf_mt_check, - .match = bpf_mt, - .destroy = bpf_mt_destroy, - .matchsize = sizeof(struct xt_bpf_info), - .me = THIS_MODULE, +static struct xt_match bpf_mt_reg[] __read_mostly = { + { + .name = "bpf", + .revision = 0, + .family = NFPROTO_UNSPEC, + .checkentry = bpf_mt_check, + .match = bpf_mt, + .destroy = bpf_mt_destroy, + .matchsize = sizeof(struct xt_bpf_info), + .me = THIS_MODULE, + }, + { + .name = "bpf", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = bpf_mt_check_v1, + .match = bpf_mt_v1, + .destroy = bpf_mt_destroy_v1, + .matchsize = sizeof(struct xt_bpf_info_v1), + .me = THIS_MODULE, + }, }; static int __init bpf_mt_init(void) { - return xt_register_match(&bpf_mt_reg); + return xt_register_matches(bpf_mt_reg, ARRAY_SIZE(bpf_mt_reg)); } static void __exit bpf_mt_exit(void) { - xt_unregister_match(&bpf_mt_reg); + xt_unregister_matches(bpf_mt_reg, ARRAY_SIZE(bpf_mt_reg)); } module_init(bpf_mt_init); -- cgit v1.1 From 73c25fb139337ac4fe1695ae3c056961855594db Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 7 Dec 2016 14:40:53 +0100 Subject: netfilter: nft_quota: allow to restore consumed quota Allow to restore consumed quota, this is useful to restore the quota state across reboots. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_quota.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index 7f27ebd..bd6efc5 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -43,6 +43,7 @@ static inline void nft_quota_do_eval(struct nft_quota *priv, static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = { [NFTA_QUOTA_BYTES] = { .type = NLA_U64 }, [NFTA_QUOTA_FLAGS] = { .type = NLA_U32 }, + [NFTA_QUOTA_CONSUMED] = { .type = NLA_U64 }, }; #define NFT_QUOTA_DEPLETED_BIT 1 /* From NFT_QUOTA_F_DEPLETED. */ @@ -68,7 +69,7 @@ static int nft_quota_do_init(const struct nlattr * const tb[], struct nft_quota *priv) { unsigned long flags = 0; - u64 quota; + u64 quota, consumed = 0; if (!tb[NFTA_QUOTA_BYTES]) return -EINVAL; @@ -77,6 +78,12 @@ static int nft_quota_do_init(const struct nlattr * const tb[], if (quota > S64_MAX) return -EOVERFLOW; + if (tb[NFTA_QUOTA_CONSUMED]) { + consumed = be64_to_cpu(nla_get_be64(tb[NFTA_QUOTA_CONSUMED])); + if (consumed > quota) + return -EINVAL; + } + if (tb[NFTA_QUOTA_FLAGS]) { flags = ntohl(nla_get_be32(tb[NFTA_QUOTA_FLAGS])); if (flags & ~NFT_QUOTA_F_INV) @@ -87,7 +94,7 @@ static int nft_quota_do_init(const struct nlattr * const tb[], priv->quota = quota; priv->flags = flags; - atomic64_set(&priv->consumed, 0); + atomic64_set(&priv->consumed, consumed); return 0; } -- cgit v1.1 From 5b8e2f61b9df529ca4af057daf7bfb1de348bdf1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Dec 2016 19:32:50 -0800 Subject: net: sock_rps_record_flow() is for connected sockets Paolo noticed a cache line miss in UDP recvmsg() to access sk_rxhash, sharing a cache line with sk_drops. sk_drops might be heavily incremented by cpus handling a flood targeting this socket. We might place sk_drops on a separate cache line, but lets try to avoid wasting 64 bytes per socket just for this, since we have other bottlenecks to take care of. sock_rps_record_flow() should only access sk_rxhash for connected flows. Testing sk_state for TCP_ESTABLISHED covers most of the cases for connected sockets, for a zero cost, since system calls using sock_rps_record_flow() also access sk->sk_prot which is on the same cache line. A follow up patch will provide a static_key (Jump Label) since most hosts do not even use RFS. Signed-off-by: Eric Dumazet Reported-by: Paolo Abeni Acked-by: Paolo Abeni Signed-off-by: David S. Miller --- include/net/sock.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/net/sock.h b/include/net/sock.h index 6dfe3aa..1749e38 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -913,7 +913,17 @@ static inline void sock_rps_record_flow_hash(__u32 hash) static inline void sock_rps_record_flow(const struct sock *sk) { #ifdef CONFIG_RPS - sock_rps_record_flow_hash(sk->sk_rxhash); + /* Reading sk->sk_rxhash might incur an expensive cache line miss. + * + * TCP_ESTABLISHED does cover almost all states where RFS + * might be useful, and is cheaper [1] than testing : + * IPv4: inet_sk(sk)->inet_daddr + * IPv6: ipv6_addr_any(&sk->sk_v6_daddr) + * OR an additional socket flag + * [1] : sk_state and sk_prot are in the same cache line. + */ + if (sk->sk_state == TCP_ESTABLISHED) + sock_rps_record_flow_hash(sk->sk_rxhash); #endif } -- cgit v1.1 From 33c2657eb688a063ab9cbe11fd4d18c93c7945e1 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 7 Dec 2016 00:26:15 -0500 Subject: bnxt_en: Add bnxt_set_max_func_irqs(). By refactoring existing code into this new function. The new function will be used in subsequent patches. v2: Fixed compile warning when CONFIG_BNXT_SRIOV is not set. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 17 +++++++++++------ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index e84613a..269e757 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4743,6 +4743,16 @@ static int bnxt_trim_rings(struct bnxt *bp, int *rx, int *tx, int max, return 0; } +void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max_irqs) +{ +#if defined(CONFIG_BNXT_SRIOV) + if (BNXT_VF(bp)) + bp->vf.max_irqs = max_irqs; + else +#endif + bp->pf.max_irqs = max_irqs; +} + static int bnxt_setup_msix(struct bnxt *bp) { struct msix_entry *msix_ent; @@ -6949,12 +6959,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bnxt_set_tpa_flags(bp); bnxt_set_ring_params(bp); - if (BNXT_PF(bp)) - bp->pf.max_irqs = max_irqs; -#if defined(CONFIG_BNXT_SRIOV) - else - bp->vf.max_irqs = max_irqs; -#endif + bnxt_set_max_func_irqs(bp, max_irqs); bnxt_set_dflt_rings(bp); /* Default RSS hash cfg. */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index b4abc1b..8327d0d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1235,6 +1235,7 @@ int hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message_silent(struct bnxt *, void *, u32, int); int bnxt_hwrm_set_coal(struct bnxt *); int bnxt_hwrm_func_qcaps(struct bnxt *); +void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max); void bnxt_tx_disable(struct bnxt *bp); void bnxt_tx_enable(struct bnxt *bp); int bnxt_hwrm_set_pause(struct bnxt *); -- cgit v1.1 From 7809592d3e2ec79cd1feab0cc96169d22f6ffee1 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 7 Dec 2016 00:26:16 -0500 Subject: bnxt_en: Enable MSIX early in bnxt_init_one(). To better support the new RDMA driver, we need to move pci_enable_msix() from bnxt_open() to bnxt_init_one(). This way, MSIX vectors are available to the RDMA driver whether the network device is up or down. Part of the existing bnxt_setup_int_mode() function is now refactored into a new bnxt_init_int_mode(). bnxt_init_int_mode() is called during bnxt_init_one() to enable MSIX. The remaining logic in bnxt_setup_int_mode() to map the IRQs to the completion rings is called during bnxt_open(). v2: Fixed compile warning when CONFIG_BNXT_SRIOV is not set. Signed-off-by: Somnath Kotur Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 183 +++++++++++++++++++----------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 115 insertions(+), 69 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 269e757..da302eb 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4743,6 +4743,80 @@ static int bnxt_trim_rings(struct bnxt *bp, int *rx, int *tx, int max, return 0; } +static void bnxt_setup_msix(struct bnxt *bp) +{ + const int len = sizeof(bp->irq_tbl[0].name); + struct net_device *dev = bp->dev; + int tcs, i; + + tcs = netdev_get_num_tc(dev); + if (tcs > 1) { + bp->tx_nr_rings_per_tc = bp->tx_nr_rings / tcs; + if (bp->tx_nr_rings_per_tc == 0) { + netdev_reset_tc(dev); + bp->tx_nr_rings_per_tc = bp->tx_nr_rings; + } else { + int i, off, count; + + bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs; + for (i = 0; i < tcs; i++) { + count = bp->tx_nr_rings_per_tc; + off = i * count; + netdev_set_tc_queue(dev, i, count, off); + } + } + } + + for (i = 0; i < bp->cp_nr_rings; i++) { + char *attr; + + if (bp->flags & BNXT_FLAG_SHARED_RINGS) + attr = "TxRx"; + else if (i < bp->rx_nr_rings) + attr = "rx"; + else + attr = "tx"; + + snprintf(bp->irq_tbl[i].name, len, "%s-%s-%d", dev->name, attr, + i); + bp->irq_tbl[i].handler = bnxt_msix; + } +} + +static void bnxt_setup_inta(struct bnxt *bp) +{ + const int len = sizeof(bp->irq_tbl[0].name); + + if (netdev_get_num_tc(bp->dev)) + netdev_reset_tc(bp->dev); + + snprintf(bp->irq_tbl[0].name, len, "%s-%s-%d", bp->dev->name, "TxRx", + 0); + bp->irq_tbl[0].handler = bnxt_inta; +} + +static int bnxt_setup_int_mode(struct bnxt *bp) +{ + int rc; + + if (bp->flags & BNXT_FLAG_USING_MSIX) + bnxt_setup_msix(bp); + else + bnxt_setup_inta(bp); + + rc = bnxt_set_real_num_queues(bp); + return rc; +} + +static unsigned int bnxt_get_max_func_irqs(struct bnxt *bp) +{ +#if defined(CONFIG_BNXT_SRIOV) + if (BNXT_VF(bp)) + return bp->vf.max_irqs; +#endif + return bp->pf.max_irqs; +} + void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max_irqs) { #if defined(CONFIG_BNXT_SRIOV) @@ -4753,16 +4827,12 @@ void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max_irqs) bp->pf.max_irqs = max_irqs; } -static int bnxt_setup_msix(struct bnxt *bp) +static int bnxt_init_msix(struct bnxt *bp) { - struct msix_entry *msix_ent; - struct net_device *dev = bp->dev; int i, total_vecs, rc = 0, min = 1; - const int len = sizeof(bp->irq_tbl[0].name); - - bp->flags &= ~BNXT_FLAG_USING_MSIX; - total_vecs = bp->cp_nr_rings; + struct msix_entry *msix_ent; + total_vecs = bnxt_get_max_func_irqs(bp); msix_ent = kcalloc(total_vecs, sizeof(struct msix_entry), GFP_KERNEL); if (!msix_ent) return -ENOMEM; @@ -4783,8 +4853,10 @@ static int bnxt_setup_msix(struct bnxt *bp) bp->irq_tbl = kcalloc(total_vecs, sizeof(struct bnxt_irq), GFP_KERNEL); if (bp->irq_tbl) { - int tcs; + for (i = 0; i < total_vecs; i++) + bp->irq_tbl[i].vector = msix_ent[i].vector; + bp->total_irqs = total_vecs; /* Trim rings based upon num of vectors allocated */ rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings, total_vecs, min == 1); @@ -4792,43 +4864,10 @@ static int bnxt_setup_msix(struct bnxt *bp) goto msix_setup_exit; bp->tx_nr_rings_per_tc = bp->tx_nr_rings; - tcs = netdev_get_num_tc(dev); - if (tcs > 1) { - bp->tx_nr_rings_per_tc = bp->tx_nr_rings / tcs; - if (bp->tx_nr_rings_per_tc == 0) { - netdev_reset_tc(dev); - bp->tx_nr_rings_per_tc = bp->tx_nr_rings; - } else { - int i, off, count; + bp->cp_nr_rings = (min == 1) ? + max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) : + bp->tx_nr_rings + bp->rx_nr_rings; - bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs; - for (i = 0; i < tcs; i++) { - count = bp->tx_nr_rings_per_tc; - off = i * count; - netdev_set_tc_queue(dev, i, count, off); - } - } - } - bp->cp_nr_rings = total_vecs; - - for (i = 0; i < bp->cp_nr_rings; i++) { - char *attr; - - bp->irq_tbl[i].vector = msix_ent[i].vector; - if (bp->flags & BNXT_FLAG_SHARED_RINGS) - attr = "TxRx"; - else if (i < bp->rx_nr_rings) - attr = "rx"; - else - attr = "tx"; - - snprintf(bp->irq_tbl[i].name, len, - "%s-%s-%d", dev->name, attr, i); - bp->irq_tbl[i].handler = bnxt_msix; - } - rc = bnxt_set_real_num_queues(bp); - if (rc) - goto msix_setup_exit; } else { rc = -ENOMEM; goto msix_setup_exit; @@ -4838,52 +4877,54 @@ static int bnxt_setup_msix(struct bnxt *bp) return 0; msix_setup_exit: - netdev_err(bp->dev, "bnxt_setup_msix err: %x\n", rc); + netdev_err(bp->dev, "bnxt_init_msix err: %x\n", rc); + kfree(bp->irq_tbl); + bp->irq_tbl = NULL; pci_disable_msix(bp->pdev); kfree(msix_ent); return rc; } -static int bnxt_setup_inta(struct bnxt *bp) +static int bnxt_init_inta(struct bnxt *bp) { - int rc; - const int len = sizeof(bp->irq_tbl[0].name); - - if (netdev_get_num_tc(bp->dev)) - netdev_reset_tc(bp->dev); - bp->irq_tbl = kcalloc(1, sizeof(struct bnxt_irq), GFP_KERNEL); - if (!bp->irq_tbl) { - rc = -ENOMEM; - return rc; - } + if (!bp->irq_tbl) + return -ENOMEM; + + bp->total_irqs = 1; bp->rx_nr_rings = 1; bp->tx_nr_rings = 1; bp->cp_nr_rings = 1; bp->tx_nr_rings_per_tc = bp->tx_nr_rings; bp->flags |= BNXT_FLAG_SHARED_RINGS; bp->irq_tbl[0].vector = bp->pdev->irq; - snprintf(bp->irq_tbl[0].name, len, - "%s-%s-%d", bp->dev->name, "TxRx", 0); - bp->irq_tbl[0].handler = bnxt_inta; - rc = bnxt_set_real_num_queues(bp); - return rc; + return 0; } -static int bnxt_setup_int_mode(struct bnxt *bp) +static int bnxt_init_int_mode(struct bnxt *bp) { int rc = 0; if (bp->flags & BNXT_FLAG_MSIX_CAP) - rc = bnxt_setup_msix(bp); + rc = bnxt_init_msix(bp); if (!(bp->flags & BNXT_FLAG_USING_MSIX) && BNXT_PF(bp)) { /* fallback to INTA */ - rc = bnxt_setup_inta(bp); + rc = bnxt_init_inta(bp); } return rc; } +static void bnxt_clear_int_mode(struct bnxt *bp) +{ + if (bp->flags & BNXT_FLAG_USING_MSIX) + pci_disable_msix(bp->pdev); + + kfree(bp->irq_tbl); + bp->irq_tbl = NULL; + bp->flags &= ~BNXT_FLAG_USING_MSIX; +} + static void bnxt_free_irq(struct bnxt *bp) { struct bnxt_irq *irq; @@ -4902,10 +4943,6 @@ static void bnxt_free_irq(struct bnxt *bp) free_irq(irq->vector, bp->bnapi[i]); irq->requested = 0; } - if (bp->flags & BNXT_FLAG_USING_MSIX) - pci_disable_msix(bp->pdev); - kfree(bp->irq_tbl); - bp->irq_tbl = NULL; } static int bnxt_request_irq(struct bnxt *bp) @@ -6695,6 +6732,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) cancel_work_sync(&bp->sp_task); bp->sp_event = 0; + bnxt_clear_int_mode(bp); bnxt_hwrm_func_drv_unrgtr(bp); bnxt_free_hwrm_resources(bp); bnxt_dcb_free(bp); @@ -6990,10 +7028,14 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err; - rc = register_netdev(dev); + rc = bnxt_init_int_mode(bp); if (rc) goto init_err; + rc = register_netdev(dev); + if (rc) + goto init_err_clr_int; + netdev_info(dev, "%s found at mem %lx, node addr %pM\n", board_info[ent->driver_data].name, (long)pci_resource_start(pdev, 0), dev->dev_addr); @@ -7002,6 +7044,9 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +init_err_clr_int: + bnxt_clear_int_mode(bp); + init_err: pci_iounmap(pdev, bp->bar0); pci_release_regions(pdev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 8327d0d..1461355 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1024,6 +1024,7 @@ struct bnxt { #define BNXT_STATE_FN_RST_DONE 2 struct bnxt_irq *irq_tbl; + int total_irqs; u8 mac_addr[ETH_ALEN]; #ifdef CONFIG_BNXT_DCB -- cgit v1.1 From aa8ed021ab515a93f2a052e9cc80320882889698 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 7 Dec 2016 00:26:17 -0500 Subject: bnxt_en: Move function reset to bnxt_init_one(). Now that MSIX is enabled in bnxt_init_one(), resources may be allocated by the RDMA driver before the network device is opened. So we cannot do function reset in bnxt_open() which will clear all the resources. The proper place to do function reset now is in bnxt_init_one(). If we get AER, we'll do function reset as well. Signed-off-by: Somnath Kotur Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 25 ++++++------------------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 - 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index da302eb..30b482b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5613,22 +5613,7 @@ int bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) static int bnxt_open(struct net_device *dev) { struct bnxt *bp = netdev_priv(dev); - int rc = 0; - if (!test_bit(BNXT_STATE_FN_RST_DONE, &bp->state)) { - rc = bnxt_hwrm_func_reset(bp); - if (rc) { - netdev_err(bp->dev, "hwrm chip reset failure rc: %x\n", - rc); - rc = -EBUSY; - return rc; - } - /* Do func_reset during the 1st PF open only to prevent killing - * the VFs when the PF is brought down and up. - */ - if (BNXT_PF(bp)) - set_bit(BNXT_STATE_FN_RST_DONE, &bp->state); - } return __bnxt_open_nic(bp, true, true); } @@ -7028,6 +7013,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err; + rc = bnxt_hwrm_func_reset(bp); + if (rc) + goto init_err; + rc = bnxt_init_int_mode(bp); if (rc) goto init_err; @@ -7069,7 +7058,6 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct net_device *netdev = pci_get_drvdata(pdev); - struct bnxt *bp = netdev_priv(netdev); netdev_info(netdev, "PCI I/O error detected\n"); @@ -7084,8 +7072,6 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev, if (netif_running(netdev)) bnxt_close(netdev); - /* So that func_reset will be done during slot_reset */ - clear_bit(BNXT_STATE_FN_RST_DONE, &bp->state); pci_disable_device(pdev); rtnl_unlock(); @@ -7119,7 +7105,8 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) } else { pci_set_master(pdev); - if (netif_running(netdev)) + err = bnxt_hwrm_func_reset(bp); + if (!err && netif_running(netdev)) err = bnxt_open(netdev); if (!err) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 1461355..0ee2cc4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1021,7 +1021,6 @@ struct bnxt { unsigned long state; #define BNXT_STATE_OPEN 0 #define BNXT_STATE_IN_SP_TASK 1 -#define BNXT_STATE_FN_RST_DONE 2 struct bnxt_irq *irq_tbl; int total_irqs; -- cgit v1.1 From 7b08f661ab80e87dcdba7ab9a460fe2c9d08bf5b Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 7 Dec 2016 00:26:18 -0500 Subject: bnxt_en: Improve completion ring allocation for VFs. All available remaining completion rings not used by the PF should be made available for the VFs so that there are enough rings in the VF to support RDMA. The earlier workaround code of capping the rings by the statistics context is removed. When SRIOV is disabled, call a new function bnxt_restore_pf_fw_resources() to restore FW resources. Later on we need to add some logic to account for RDMA resources. Signed-off-by: Somnath Kotur Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 +++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 14 ++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 30b482b..52b8ad4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4152,7 +4152,7 @@ func_qcfg_exit: return rc; } -int bnxt_hwrm_func_qcaps(struct bnxt *bp) +static int bnxt_hwrm_func_qcaps(struct bnxt *bp) { int rc = 0; struct hwrm_func_qcaps_input req = {0}; @@ -6856,6 +6856,12 @@ static int bnxt_set_dflt_rings(struct bnxt *bp) return rc; } +void bnxt_restore_pf_fw_resources(struct bnxt *bp) +{ + ASSERT_RTNL(); + bnxt_hwrm_func_qcaps(bp); +} + static void bnxt_parse_log_pcie_link(struct bnxt *bp) { enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 0ee2cc4..43a4b17 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1234,7 +1234,6 @@ int _hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message_silent(struct bnxt *, void *, u32, int); int bnxt_hwrm_set_coal(struct bnxt *); -int bnxt_hwrm_func_qcaps(struct bnxt *); void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max); void bnxt_tx_disable(struct bnxt *bp); void bnxt_tx_enable(struct bnxt *bp); @@ -1245,4 +1244,5 @@ int bnxt_open_nic(struct bnxt *, bool, bool); int bnxt_close_nic(struct bnxt *, bool, bool); int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); int bnxt_get_max_rings(struct bnxt *, int *, int *, bool); +void bnxt_restore_pf_fw_resources(struct bnxt *bp); #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index bff626a..c696025 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -420,15 +420,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); /* Remaining rings are distributed equally amongs VF's for now */ - /* TODO: the following workaroud is needed to restrict total number - * of vf_cp_rings not exceed number of HW ring groups. This WA should - * be removed once new HWRM provides HW ring groups capability in - * hwrm_func_qcap. - */ - vf_cp_rings = min_t(u16, pf->max_cp_rings, pf->max_stat_ctxs); - vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / num_vfs; - /* TODO: restore this logic below once the WA above is removed */ - /* vf_cp_rings = (pf->max_cp_rings - bp->cp_nr_rings) / num_vfs; */ + vf_cp_rings = (pf->max_cp_rings - bp->cp_nr_rings) / num_vfs; vf_stat_ctx = (pf->max_stat_ctxs - bp->num_stat_ctxs) / num_vfs; if (bp->flags & BNXT_FLAG_AGG_RINGS) vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings * 2) / @@ -590,7 +582,9 @@ void bnxt_sriov_disable(struct bnxt *bp) bp->pf.active_vfs = 0; /* Reclaim all resources for the PF. */ - bnxt_hwrm_func_qcaps(bp); + rtnl_lock(); + bnxt_restore_pf_fw_resources(bp); + rtnl_unlock(); } int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs) -- cgit v1.1 From e4060d306b5196966d74e05dee48e6c3a52aaad4 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 7 Dec 2016 00:26:19 -0500 Subject: bnxt_en: Reserve RDMA resources by default. If the device supports RDMA, we'll setup network default rings so that there are enough minimum resources for RDMA, if possible. However, the user can still increase network rings to the max if he wants. The actual RDMA resources won't be reserved until the RDMA driver registers. v2: Fix compile warning when BNXT_CONFIG_SRIOV is not set. Signed-off-by: Somnath Kotur Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 58 ++++++++++++++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 9 +++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 52b8ad4..57285bd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4166,6 +4166,11 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp) if (rc) goto hwrm_func_qcaps_exit; + if (resp->flags & cpu_to_le32(FUNC_QCAPS_RESP_FLAGS_ROCE_V1_SUPPORTED)) + bp->flags |= BNXT_FLAG_ROCEV1_CAP; + if (resp->flags & cpu_to_le32(FUNC_QCAPS_RESP_FLAGS_ROCE_V2_SUPPORTED)) + bp->flags |= BNXT_FLAG_ROCEV2_CAP; + bp->tx_push_thresh = 0; if (resp->flags & cpu_to_le32(FUNC_QCAPS_RESP_FLAGS_PUSH_MODE_SUPPORTED)) @@ -4808,6 +4813,24 @@ static int bnxt_setup_int_mode(struct bnxt *bp) return rc; } +unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp) +{ +#if defined(CONFIG_BNXT_SRIOV) + if (BNXT_VF(bp)) + return bp->vf.max_stat_ctxs; +#endif + return bp->pf.max_stat_ctxs; +} + +unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp) +{ +#if defined(CONFIG_BNXT_SRIOV) + if (BNXT_VF(bp)) + return bp->vf.max_cp_rings; +#endif + return bp->pf.max_cp_rings; +} + static unsigned int bnxt_get_max_func_irqs(struct bnxt *bp) { #if defined(CONFIG_BNXT_SRIOV) @@ -6832,6 +6855,39 @@ int bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx, bool shared) return bnxt_trim_rings(bp, max_rx, max_tx, cp, shared); } +static int bnxt_get_dflt_rings(struct bnxt *bp, int *max_rx, int *max_tx, + bool shared) +{ + int rc; + + rc = bnxt_get_max_rings(bp, max_rx, max_tx, shared); + if (rc) + return rc; + + if (bp->flags & BNXT_FLAG_ROCE_CAP) { + int max_cp, max_stat, max_irq; + + /* Reserve minimum resources for RoCE */ + max_cp = bnxt_get_max_func_cp_rings(bp); + max_stat = bnxt_get_max_func_stat_ctxs(bp); + max_irq = bnxt_get_max_func_irqs(bp); + if (max_cp <= BNXT_MIN_ROCE_CP_RINGS || + max_irq <= BNXT_MIN_ROCE_CP_RINGS || + max_stat <= BNXT_MIN_ROCE_STAT_CTXS) + return 0; + + max_cp -= BNXT_MIN_ROCE_CP_RINGS; + max_irq -= BNXT_MIN_ROCE_CP_RINGS; + max_stat -= BNXT_MIN_ROCE_STAT_CTXS; + max_cp = min_t(int, max_cp, max_irq); + max_cp = min_t(int, max_cp, max_stat); + rc = bnxt_trim_rings(bp, max_rx, max_tx, max_cp, shared); + if (rc) + rc = 0; + } + return rc; +} + static int bnxt_set_dflt_rings(struct bnxt *bp) { int dflt_rings, max_rx_rings, max_tx_rings, rc; @@ -6840,7 +6896,7 @@ static int bnxt_set_dflt_rings(struct bnxt *bp) if (sh) bp->flags |= BNXT_FLAG_SHARED_RINGS; dflt_rings = netif_get_num_default_rss_queues(); - rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh); + rc = bnxt_get_dflt_rings(bp, &max_rx_rings, &max_tx_rings, sh); if (rc) return rc; bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 43a4b17..d796836 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -387,6 +387,9 @@ struct rx_tpa_end_cmp_ext { #define DB_KEY_TX_PUSH (0x4 << 28) #define DB_LONG_TX_PUSH (0x2 << 24) +#define BNXT_MIN_ROCE_CP_RINGS 2 +#define BNXT_MIN_ROCE_STAT_CTXS 1 + #define INVALID_HW_RING_ID ((u16)-1) /* The hardware supports certain page sizes. Use the supported page sizes @@ -953,6 +956,10 @@ struct bnxt { #define BNXT_FLAG_PORT_STATS 0x400 #define BNXT_FLAG_UDP_RSS_CAP 0x800 #define BNXT_FLAG_EEE_CAP 0x1000 + #define BNXT_FLAG_ROCEV1_CAP 0x8000 + #define BNXT_FLAG_ROCEV2_CAP 0x10000 + #define BNXT_FLAG_ROCE_CAP (BNXT_FLAG_ROCEV1_CAP | \ + BNXT_FLAG_ROCEV2_CAP) #define BNXT_FLAG_CHIP_NITRO_A0 0x1000000 #define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \ @@ -1234,6 +1241,8 @@ int _hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message_silent(struct bnxt *, void *, u32, int); int bnxt_hwrm_set_coal(struct bnxt *); +unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp); +unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp); void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max); void bnxt_tx_disable(struct bnxt *bp); void bnxt_tx_enable(struct bnxt *bp); -- cgit v1.1 From a1653b13f14c714f9bfd5e10c603a37c3bcba7b6 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 7 Dec 2016 00:26:20 -0500 Subject: bnxt_en: Refactor the driver registration function with firmware. The driver register function with firmware consists of passing version information and registering for async events. To support the RDMA driver, the async events that we need to register may change. Separate the driver register function into 2 parts so that we can just update the async events for the RDMA driver. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 34 ++++++++++++++++++++++++++----- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 ++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 57285bd..c782942 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -3117,27 +3117,46 @@ int hwrm_send_message_silent(struct bnxt *bp, void *msg, u32 msg_len, return rc; } -static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp) +int bnxt_hwrm_func_rgtr_async_events(struct bnxt *bp, unsigned long *bmap, + int bmap_size) { struct hwrm_func_drv_rgtr_input req = {0}; - int i; DECLARE_BITMAP(async_events_bmap, 256); u32 *events = (u32 *)async_events_bmap; + int i; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_DRV_RGTR, -1, -1); req.enables = - cpu_to_le32(FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE | - FUNC_DRV_RGTR_REQ_ENABLES_VER | - FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD); + cpu_to_le32(FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD); memset(async_events_bmap, 0, sizeof(async_events_bmap)); for (i = 0; i < ARRAY_SIZE(bnxt_async_events_arr); i++) __set_bit(bnxt_async_events_arr[i], async_events_bmap); + if (bmap && bmap_size) { + for (i = 0; i < bmap_size; i++) { + if (test_bit(i, bmap)) + __set_bit(i, async_events_bmap); + } + } + for (i = 0; i < 8; i++) req.async_event_fwd[i] |= cpu_to_le32(events[i]); + return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); +} + +static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp) +{ + struct hwrm_func_drv_rgtr_input req = {0}; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_DRV_RGTR, -1, -1); + + req.enables = + cpu_to_le32(FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE | + FUNC_DRV_RGTR_REQ_ENABLES_VER); + req.os_type = cpu_to_le16(FUNC_DRV_RGTR_REQ_OS_TYPE_LINUX); req.ver_maj = DRV_VER_MAJ; req.ver_min = DRV_VER_MIN; @@ -3146,6 +3165,7 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp) if (BNXT_PF(bp)) { DECLARE_BITMAP(vf_req_snif_bmap, 256); u32 *data = (u32 *)vf_req_snif_bmap; + int i; memset(vf_req_snif_bmap, 0, sizeof(vf_req_snif_bmap)); for (i = 0; i < ARRAY_SIZE(bnxt_vf_req_snif); i++) @@ -7023,6 +7043,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err; + rc = bnxt_hwrm_func_rgtr_async_events(bp, NULL, 0); + if (rc) + goto init_err; + /* Get the MAX capabilities for this function */ rc = bnxt_hwrm_func_qcaps(bp); if (rc) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index d796836..eec2415 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1240,6 +1240,8 @@ void bnxt_hwrm_cmd_hdr_init(struct bnxt *, void *, u16, u16, u16); int _hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message_silent(struct bnxt *, void *, u32, int); +int bnxt_hwrm_func_rgtr_async_events(struct bnxt *bp, unsigned long *bmap, + int bmap_size); int bnxt_hwrm_set_coal(struct bnxt *); unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp); unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp); -- cgit v1.1 From a588e4580a7ecb715dab8bf09725b97aa0e0e3a0 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 7 Dec 2016 00:26:21 -0500 Subject: bnxt_en: Add interface to support RDMA driver. Since the network driver and RDMA driver operate on the same PCI function, we need to create an interface to allow the RDMA driver to share resources with the network driver. 1. Create a new bnxt_en_dev struct which will be returned by bnxt_ulp_probe() upon success. After that, all calls from the RDMA driver to bnxt_en will pass a pointer to this struct. 2. This struct contains additional function pointers to register, request msix, send fw messages, register for async events. 3. If the RDMA driver wants to enable RDMA on the function, it needs to call the function pointer bnxt_register_device(). A ulp_ops structure is passed for RCU protected upcalls from bnxt_en to the RDMA driver. 4. The RDMA driver can call firmware APIs using the bnxt_send_fw_msg() function pointer. 5. 1 stats context is reserved when the RDMA driver registers. MSIX and completion rings are reserved when the RDMA driver calls bnxt_request_msix() function pointer. 6. When the RDMA driver calls bnxt_unregister_device(), all RDMA resources will be cleaned up. v2: Fixed 2 uninitialized variable warnings. Signed-off-by: Somnath Kotur Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/Makefile | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 41 ++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 6 + drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c | 346 ++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h | 93 +++++++ 5 files changed, 483 insertions(+), 5 deletions(-) create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h diff --git a/drivers/net/ethernet/broadcom/bnxt/Makefile b/drivers/net/ethernet/broadcom/bnxt/Makefile index b233a86..6082ed1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/Makefile +++ b/drivers/net/ethernet/broadcom/bnxt/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_BNXT) += bnxt_en.o -bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o +bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index c782942..9608cb4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -52,6 +52,7 @@ #include "bnxt_hsi.h" #include "bnxt.h" +#include "bnxt_ulp.h" #include "bnxt_sriov.h" #include "bnxt_ethtool.h" #include "bnxt_dcb.h" @@ -1528,12 +1529,11 @@ static int bnxt_async_event_process(struct bnxt *bp, set_bit(BNXT_RESET_TASK_SILENT_SP_EVENT, &bp->sp_event); break; default: - netdev_err(bp->dev, "unhandled ASYNC event (id 0x%x)\n", - event_id); goto async_event_process_exit; } schedule_work(&bp->sp_task); async_event_process_exit: + bnxt_ulp_async_events(bp, cmpl); return 0; } @@ -3547,7 +3547,7 @@ static int bnxt_hwrm_vnic_ctx_alloc(struct bnxt *bp, u16 vnic_id, u16 ctx_idx) return rc; } -static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id) +int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id) { unsigned int ring = 0, grp_idx; struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id]; @@ -3595,6 +3595,9 @@ static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id) #endif if ((bp->flags & BNXT_FLAG_STRIP_VLAN) || def_vlan) req.flags |= cpu_to_le32(VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE); + if (!vnic_id && bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP)) + req.flags |= + cpu_to_le32(VNIC_CFG_REQ_FLAGS_ROCE_DUAL_VNIC_MODE); return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); } @@ -4842,6 +4845,16 @@ unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp) return bp->pf.max_stat_ctxs; } +void bnxt_set_max_func_stat_ctxs(struct bnxt *bp, unsigned int max) +{ +#if defined(CONFIG_BNXT_SRIOV) + if (BNXT_VF(bp)) + bp->vf.max_stat_ctxs = max; + else +#endif + bp->pf.max_stat_ctxs = max; +} + unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp) { #if defined(CONFIG_BNXT_SRIOV) @@ -4851,6 +4864,16 @@ unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp) return bp->pf.max_cp_rings; } +void bnxt_set_max_func_cp_rings(struct bnxt *bp, unsigned int max) +{ +#if defined(CONFIG_BNXT_SRIOV) + if (BNXT_VF(bp)) + bp->vf.max_cp_rings = max; + else +#endif + bp->pf.max_cp_rings = max; +} + static unsigned int bnxt_get_max_func_irqs(struct bnxt *bp) { #if defined(CONFIG_BNXT_SRIOV) @@ -6767,6 +6790,8 @@ static void bnxt_remove_one(struct pci_dev *pdev) pci_iounmap(pdev, bp->bar2); pci_iounmap(pdev, bp->bar1); pci_iounmap(pdev, bp->bar0); + kfree(bp->edev); + bp->edev = NULL; free_netdev(dev); pci_release_regions(pdev); @@ -6936,6 +6961,7 @@ void bnxt_restore_pf_fw_resources(struct bnxt *bp) { ASSERT_RTNL(); bnxt_hwrm_func_qcaps(bp); + bnxt_subtract_ulp_resources(bp, BNXT_ROCE_ULP); } static void bnxt_parse_log_pcie_link(struct bnxt *bp) @@ -7047,6 +7073,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err; + bp->ulp_probe = bnxt_ulp_probe; + /* Get the MAX capabilities for this function */ rc = bnxt_hwrm_func_qcaps(bp); if (rc) { @@ -7144,12 +7172,15 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct net_device *netdev = pci_get_drvdata(pdev); + struct bnxt *bp = netdev_priv(netdev); netdev_info(netdev, "PCI I/O error detected\n"); rtnl_lock(); netif_device_detach(netdev); + bnxt_ulp_stop(bp); + if (state == pci_channel_io_perm_failure) { rtnl_unlock(); return PCI_ERS_RESULT_DISCONNECT; @@ -7195,8 +7226,10 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) if (!err && netif_running(netdev)) err = bnxt_open(netdev); - if (!err) + if (!err) { result = PCI_ERS_RESULT_RECOVERED; + bnxt_ulp_start(bp); + } } if (result != PCI_ERS_RESULT_RECOVERED && netif_running(netdev)) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index eec2415..16defe9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -972,6 +972,9 @@ struct bnxt { #define BNXT_SINGLE_PF(bp) (BNXT_PF(bp) && !BNXT_NPAR(bp)) #define BNXT_CHIP_TYPE_NITRO_A0(bp) ((bp)->flags & BNXT_FLAG_CHIP_NITRO_A0) + struct bnxt_en_dev *edev; + struct bnxt_en_dev * (*ulp_probe)(struct net_device *); + struct bnxt_napi **bnapi; struct bnxt_rx_ring_info *rx_ring; @@ -1242,9 +1245,12 @@ int hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message_silent(struct bnxt *, void *, u32, int); int bnxt_hwrm_func_rgtr_async_events(struct bnxt *bp, unsigned long *bmap, int bmap_size); +int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id); int bnxt_hwrm_set_coal(struct bnxt *); unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp); +void bnxt_set_max_func_stat_ctxs(struct bnxt *bp, unsigned int max); unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp); +void bnxt_set_max_func_cp_rings(struct bnxt *bp, unsigned int max); void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max); void bnxt_tx_disable(struct bnxt *bp); void bnxt_tx_enable(struct bnxt *bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c new file mode 100644 index 0000000..8b7464b --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -0,0 +1,346 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2016 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bnxt_hsi.h" +#include "bnxt.h" +#include "bnxt_ulp.h" + +static int bnxt_register_dev(struct bnxt_en_dev *edev, int ulp_id, + struct bnxt_ulp_ops *ulp_ops, void *handle) +{ + struct net_device *dev = edev->net; + struct bnxt *bp = netdev_priv(dev); + struct bnxt_ulp *ulp; + + ASSERT_RTNL(); + if (ulp_id >= BNXT_MAX_ULP) + return -EINVAL; + + ulp = &edev->ulp_tbl[ulp_id]; + if (rcu_access_pointer(ulp->ulp_ops)) { + netdev_err(bp->dev, "ulp id %d already registered\n", ulp_id); + return -EBUSY; + } + if (ulp_id == BNXT_ROCE_ULP) { + unsigned int max_stat_ctxs; + + max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp); + if (max_stat_ctxs <= BNXT_MIN_ROCE_STAT_CTXS || + bp->num_stat_ctxs == max_stat_ctxs) + return -ENOMEM; + bnxt_set_max_func_stat_ctxs(bp, max_stat_ctxs - + BNXT_MIN_ROCE_STAT_CTXS); + } + + atomic_set(&ulp->ref_count, 0); + ulp->handle = handle; + rcu_assign_pointer(ulp->ulp_ops, ulp_ops); + + if (ulp_id == BNXT_ROCE_ULP) { + if (test_bit(BNXT_STATE_OPEN, &bp->state)) + bnxt_hwrm_vnic_cfg(bp, 0); + } + + return 0; +} + +static int bnxt_unregister_dev(struct bnxt_en_dev *edev, int ulp_id) +{ + struct net_device *dev = edev->net; + struct bnxt *bp = netdev_priv(dev); + struct bnxt_ulp *ulp; + int i = 0; + + ASSERT_RTNL(); + if (ulp_id >= BNXT_MAX_ULP) + return -EINVAL; + + ulp = &edev->ulp_tbl[ulp_id]; + if (!rcu_access_pointer(ulp->ulp_ops)) { + netdev_err(bp->dev, "ulp id %d not registered\n", ulp_id); + return -EINVAL; + } + if (ulp_id == BNXT_ROCE_ULP) { + unsigned int max_stat_ctxs; + + max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp); + bnxt_set_max_func_stat_ctxs(bp, max_stat_ctxs + 1); + } + if (ulp->max_async_event_id) + bnxt_hwrm_func_rgtr_async_events(bp, NULL, 0); + + RCU_INIT_POINTER(ulp->ulp_ops, NULL); + synchronize_rcu(); + ulp->max_async_event_id = 0; + ulp->async_events_bmap = NULL; + while (atomic_read(&ulp->ref_count) != 0 && i < 10) { + msleep(100); + i++; + } + return 0; +} + +static int bnxt_req_msix_vecs(struct bnxt_en_dev *edev, int ulp_id, + struct bnxt_msix_entry *ent, int num_msix) +{ + struct net_device *dev = edev->net; + struct bnxt *bp = netdev_priv(dev); + int max_idx, max_cp_rings; + int avail_msix, i, idx; + + ASSERT_RTNL(); + if (ulp_id != BNXT_ROCE_ULP) + return -EINVAL; + + if (!(bp->flags & BNXT_FLAG_USING_MSIX)) + return -ENODEV; + + max_cp_rings = bnxt_get_max_func_cp_rings(bp); + max_idx = min_t(int, bp->total_irqs, max_cp_rings); + avail_msix = max_idx - bp->cp_nr_rings; + if (!avail_msix) + return -ENOMEM; + if (avail_msix > num_msix) + avail_msix = num_msix; + + idx = max_idx - avail_msix; + for (i = 0; i < avail_msix; i++) { + ent[i].vector = bp->irq_tbl[idx + i].vector; + ent[i].ring_idx = idx + i; + ent[i].db_offset = (idx + i) * 0x80; + } + bnxt_set_max_func_irqs(bp, max_idx - avail_msix); + bnxt_set_max_func_cp_rings(bp, max_cp_rings - avail_msix); + edev->ulp_tbl[ulp_id].msix_requested = avail_msix; + return avail_msix; +} + +static int bnxt_free_msix_vecs(struct bnxt_en_dev *edev, int ulp_id) +{ + struct net_device *dev = edev->net; + struct bnxt *bp = netdev_priv(dev); + int max_cp_rings, msix_requested; + + ASSERT_RTNL(); + if (ulp_id != BNXT_ROCE_ULP) + return -EINVAL; + + max_cp_rings = bnxt_get_max_func_cp_rings(bp); + msix_requested = edev->ulp_tbl[ulp_id].msix_requested; + bnxt_set_max_func_cp_rings(bp, max_cp_rings + msix_requested); + edev->ulp_tbl[ulp_id].msix_requested = 0; + bnxt_set_max_func_irqs(bp, bp->total_irqs); + return 0; +} + +void bnxt_subtract_ulp_resources(struct bnxt *bp, int ulp_id) +{ + ASSERT_RTNL(); + if (bnxt_ulp_registered(bp->edev, ulp_id)) { + struct bnxt_en_dev *edev = bp->edev; + unsigned int msix_req, max; + + msix_req = edev->ulp_tbl[ulp_id].msix_requested; + max = bnxt_get_max_func_cp_rings(bp); + bnxt_set_max_func_cp_rings(bp, max - msix_req); + max = bnxt_get_max_func_stat_ctxs(bp); + bnxt_set_max_func_stat_ctxs(bp, max - 1); + } +} + +static int bnxt_send_msg(struct bnxt_en_dev *edev, int ulp_id, + struct bnxt_fw_msg *fw_msg) +{ + struct net_device *dev = edev->net; + struct bnxt *bp = netdev_priv(dev); + struct input *req; + int rc; + + mutex_lock(&bp->hwrm_cmd_lock); + req = fw_msg->msg; + req->resp_addr = cpu_to_le64(bp->hwrm_cmd_resp_dma_addr); + rc = _hwrm_send_message(bp, fw_msg->msg, fw_msg->msg_len, + fw_msg->timeout); + if (!rc) { + struct output *resp = bp->hwrm_cmd_resp_addr; + u32 len = le16_to_cpu(resp->resp_len); + + if (fw_msg->resp_max_len < len) + len = fw_msg->resp_max_len; + + memcpy(fw_msg->resp, resp, len); + } + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + +static void bnxt_ulp_get(struct bnxt_ulp *ulp) +{ + atomic_inc(&ulp->ref_count); +} + +static void bnxt_ulp_put(struct bnxt_ulp *ulp) +{ + atomic_dec(&ulp->ref_count); +} + +void bnxt_ulp_stop(struct bnxt *bp) +{ + struct bnxt_en_dev *edev = bp->edev; + struct bnxt_ulp_ops *ops; + int i; + + if (!edev) + return; + + for (i = 0; i < BNXT_MAX_ULP; i++) { + struct bnxt_ulp *ulp = &edev->ulp_tbl[i]; + + ops = rtnl_dereference(ulp->ulp_ops); + if (!ops || !ops->ulp_stop) + continue; + ops->ulp_stop(ulp->handle); + } +} + +void bnxt_ulp_start(struct bnxt *bp) +{ + struct bnxt_en_dev *edev = bp->edev; + struct bnxt_ulp_ops *ops; + int i; + + if (!edev) + return; + + for (i = 0; i < BNXT_MAX_ULP; i++) { + struct bnxt_ulp *ulp = &edev->ulp_tbl[i]; + + ops = rtnl_dereference(ulp->ulp_ops); + if (!ops || !ops->ulp_start) + continue; + ops->ulp_start(ulp->handle); + } +} + +void bnxt_ulp_sriov_cfg(struct bnxt *bp, int num_vfs) +{ + struct bnxt_en_dev *edev = bp->edev; + struct bnxt_ulp_ops *ops; + int i; + + if (!edev) + return; + + for (i = 0; i < BNXT_MAX_ULP; i++) { + struct bnxt_ulp *ulp = &edev->ulp_tbl[i]; + + rcu_read_lock(); + ops = rcu_dereference(ulp->ulp_ops); + if (!ops || !ops->ulp_sriov_config) { + rcu_read_unlock(); + continue; + } + bnxt_ulp_get(ulp); + rcu_read_unlock(); + ops->ulp_sriov_config(ulp->handle, num_vfs); + bnxt_ulp_put(ulp); + } +} + +void bnxt_ulp_async_events(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl) +{ + u16 event_id = le16_to_cpu(cmpl->event_id); + struct bnxt_en_dev *edev = bp->edev; + struct bnxt_ulp_ops *ops; + int i; + + if (!edev) + return; + + rcu_read_lock(); + for (i = 0; i < BNXT_MAX_ULP; i++) { + struct bnxt_ulp *ulp = &edev->ulp_tbl[i]; + + ops = rcu_dereference(ulp->ulp_ops); + if (!ops || !ops->ulp_async_notifier) + continue; + if (!ulp->async_events_bmap || + event_id > ulp->max_async_event_id) + continue; + + /* Read max_async_event_id first before testing the bitmap. */ + smp_rmb(); + if (test_bit(event_id, ulp->async_events_bmap)) + ops->ulp_async_notifier(ulp->handle, cmpl); + } + rcu_read_unlock(); +} + +static int bnxt_register_async_events(struct bnxt_en_dev *edev, int ulp_id, + unsigned long *events_bmap, u16 max_id) +{ + struct net_device *dev = edev->net; + struct bnxt *bp = netdev_priv(dev); + struct bnxt_ulp *ulp; + + if (ulp_id >= BNXT_MAX_ULP) + return -EINVAL; + + ulp = &edev->ulp_tbl[ulp_id]; + ulp->async_events_bmap = events_bmap; + /* Make sure bnxt_ulp_async_events() sees this order */ + smp_wmb(); + ulp->max_async_event_id = max_id; + bnxt_hwrm_func_rgtr_async_events(bp, events_bmap, max_id + 1); + return 0; +} + +static const struct bnxt_en_ops bnxt_en_ops_tbl = { + .bnxt_register_device = bnxt_register_dev, + .bnxt_unregister_device = bnxt_unregister_dev, + .bnxt_request_msix = bnxt_req_msix_vecs, + .bnxt_free_msix = bnxt_free_msix_vecs, + .bnxt_send_fw_msg = bnxt_send_msg, + .bnxt_register_fw_async_events = bnxt_register_async_events, +}; + +struct bnxt_en_dev *bnxt_ulp_probe(struct net_device *dev) +{ + struct bnxt *bp = netdev_priv(dev); + struct bnxt_en_dev *edev; + + edev = bp->edev; + if (!edev) { + edev = kzalloc(sizeof(*edev), GFP_KERNEL); + if (!edev) + return ERR_PTR(-ENOMEM); + edev->en_ops = &bnxt_en_ops_tbl; + if (bp->flags & BNXT_FLAG_ROCEV1_CAP) + edev->flags |= BNXT_EN_FLAG_ROCEV1_CAP; + if (bp->flags & BNXT_FLAG_ROCEV2_CAP) + edev->flags |= BNXT_EN_FLAG_ROCEV2_CAP; + edev->net = dev; + edev->pdev = bp->pdev; + bp->edev = edev; + } + return bp->edev; +} diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h new file mode 100644 index 0000000..74f816e --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h @@ -0,0 +1,93 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2016 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef BNXT_ULP_H +#define BNXT_ULP_H + +#define BNXT_ROCE_ULP 0 +#define BNXT_OTHER_ULP 1 +#define BNXT_MAX_ULP 2 + +#define BNXT_MIN_ROCE_CP_RINGS 2 +#define BNXT_MIN_ROCE_STAT_CTXS 1 + +struct hwrm_async_event_cmpl; +struct bnxt; + +struct bnxt_ulp_ops { + /* async_notifier() cannot sleep (in BH context) */ + void (*ulp_async_notifier)(void *, struct hwrm_async_event_cmpl *); + void (*ulp_stop)(void *); + void (*ulp_start)(void *); + void (*ulp_sriov_config)(void *, int); +}; + +struct bnxt_msix_entry { + u32 vector; + u32 ring_idx; + u32 db_offset; +}; + +struct bnxt_fw_msg { + void *msg; + int msg_len; + void *resp; + int resp_max_len; + int timeout; +}; + +struct bnxt_ulp { + void *handle; + struct bnxt_ulp_ops __rcu *ulp_ops; + unsigned long *async_events_bmap; + u16 max_async_event_id; + u16 msix_requested; + atomic_t ref_count; +}; + +struct bnxt_en_dev { + struct net_device *net; + struct pci_dev *pdev; + u32 flags; + #define BNXT_EN_FLAG_ROCEV1_CAP 0x1 + #define BNXT_EN_FLAG_ROCEV2_CAP 0x2 + #define BNXT_EN_FLAG_ROCE_CAP (BNXT_EN_FLAG_ROCEV1_CAP | \ + BNXT_EN_FLAG_ROCEV2_CAP) + const struct bnxt_en_ops *en_ops; + struct bnxt_ulp ulp_tbl[BNXT_MAX_ULP]; +}; + +struct bnxt_en_ops { + int (*bnxt_register_device)(struct bnxt_en_dev *, int, + struct bnxt_ulp_ops *, void *); + int (*bnxt_unregister_device)(struct bnxt_en_dev *, int); + int (*bnxt_request_msix)(struct bnxt_en_dev *, int, + struct bnxt_msix_entry *, int); + int (*bnxt_free_msix)(struct bnxt_en_dev *, int); + int (*bnxt_send_fw_msg)(struct bnxt_en_dev *, int, + struct bnxt_fw_msg *); + int (*bnxt_register_fw_async_events)(struct bnxt_en_dev *, int, + unsigned long *, u16); +}; + +static inline bool bnxt_ulp_registered(struct bnxt_en_dev *edev, int ulp_id) +{ + if (edev && rcu_access_pointer(edev->ulp_tbl[ulp_id].ulp_ops)) + return true; + return false; +} + +void bnxt_subtract_ulp_resources(struct bnxt *bp, int ulp_id); +void bnxt_ulp_stop(struct bnxt *bp); +void bnxt_ulp_start(struct bnxt *bp); +void bnxt_ulp_sriov_cfg(struct bnxt *bp, int num_vfs); +void bnxt_ulp_async_events(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl); +struct bnxt_en_dev *bnxt_ulp_probe(struct net_device *dev); + +#endif -- cgit v1.1 From 391fd6caf5cfc74a15a79c1237de0a2d84dfd398 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:33 -0600 Subject: net: ethernet: ti: cpts: switch to readl/writel_relaxed() Switch to readl/writel_relaxed() APIs, because this is recommended API and the CPTS IP is reused on Keystone 2 SoCs where LE/BE modes are supported. Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 85a55b4..a42c449 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -33,8 +33,8 @@ #ifdef CONFIG_TI_CPTS -#define cpts_read32(c, r) __raw_readl(&c->reg->r) -#define cpts_write32(c, v, r) __raw_writel(v, &c->reg->r) +#define cpts_read32(c, r) readl_relaxed(&c->reg->r) +#define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) static int event_expired(struct cpts_event *event) { -- cgit v1.1 From c8395d4e1d4ffbc9d8aa61f534c82e8deed72cfd Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:34 -0600 Subject: net: ethernet: ti: allow cpts to be built separately TI CPTS IP is used as part of TI OMAP CPSW driver, but it's also present as part of NETCP on TI Keystone 2 SoCs. So, It's required to enable build of CPTS for both this drivers and this can be achieved by allowing CPTS to be built separately. Hence, allow cpts to be built separately and convert it to be a module as both CPSW and NETCP drives can be built as modules. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/Kconfig | 2 +- drivers/net/ethernet/ti/Makefile | 3 ++- drivers/net/ethernet/ti/cpsw.c | 22 +++++++++++++++++----- drivers/net/ethernet/ti/cpts.c | 16 ++++++++-------- drivers/net/ethernet/ti/cpts.h | 18 ++++++++++++++---- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index 9904d74..ff7f518 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -74,7 +74,7 @@ config TI_CPSW will be called cpsw. config TI_CPTS - bool "TI Common Platform Time Sync (CPTS) Support" + tristate "TI Common Platform Time Sync (CPTS) Support" depends on TI_CPSW select PTP_1588_CLOCK ---help--- diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index d420d94..1e7c10b 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -12,8 +12,9 @@ obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o obj-$(CONFIG_TI_CPSW_ALE) += cpsw_ale.o +obj-$(CONFIG_TI_CPTS) += cpts.o obj-$(CONFIG_TI_CPSW) += ti_cpsw.o -ti_cpsw-y := cpsw.o cpts.o +ti_cpsw-y := cpsw.o obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o keystone_netcp-y := netcp_core.o diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index f373a4b..8fdb274 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1594,7 +1594,7 @@ fail: return NETDEV_TX_BUSY; } -#ifdef CONFIG_TI_CPTS +#if IS_ENABLED(CONFIG_TI_CPTS) static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) { @@ -1742,7 +1742,16 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } +#else +static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) +{ + return -EOPNOTSUPP; +} +static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) +{ + return -EOPNOTSUPP; +} #endif /*CONFIG_TI_CPTS*/ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) @@ -1755,12 +1764,10 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) return -EINVAL; switch (cmd) { -#ifdef CONFIG_TI_CPTS case SIOCSHWTSTAMP: return cpsw_hwtstamp_set(dev, req); case SIOCGHWTSTAMP: return cpsw_hwtstamp_get(dev, req); -#endif } if (!cpsw->slaves[slave_no].phy) @@ -2100,10 +2107,10 @@ static void cpsw_set_msglevel(struct net_device *ndev, u32 value) priv->msg_enable = value; } +#if IS_ENABLED(CONFIG_TI_CPTS) static int cpsw_get_ts_info(struct net_device *ndev, struct ethtool_ts_info *info) { -#ifdef CONFIG_TI_CPTS struct cpsw_common *cpsw = ndev_to_cpsw(ndev); info->so_timestamping = @@ -2120,7 +2127,12 @@ static int cpsw_get_ts_info(struct net_device *ndev, info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + return 0; +} #else +static int cpsw_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | @@ -2128,9 +2140,9 @@ static int cpsw_get_ts_info(struct net_device *ndev, info->phc_index = -1; info->tx_types = 0; info->rx_filters = 0; -#endif return 0; } +#endif static int cpsw_get_link_ksettings(struct net_device *ndev, struct ethtool_link_ksettings *ecmd) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index a42c449..8cb0369 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -31,8 +31,6 @@ #include "cpts.h" -#ifdef CONFIG_TI_CPTS - #define cpts_read32(c, r) readl_relaxed(&c->reg->r) #define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) @@ -334,6 +332,7 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) memset(ssh, 0, sizeof(*ssh)); ssh->hwtstamp = ns_to_ktime(ns); } +EXPORT_SYMBOL_GPL(cpts_rx_timestamp); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) { @@ -349,13 +348,11 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) ssh.hwtstamp = ns_to_ktime(ns); skb_tstamp_tx(skb, &ssh); } - -#endif /*CONFIG_TI_CPTS*/ +EXPORT_SYMBOL_GPL(cpts_tx_timestamp); int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) { -#ifdef CONFIG_TI_CPTS int err, i; unsigned long flags; @@ -391,18 +388,21 @@ int cpts_register(struct device *dev, struct cpts *cpts, schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); cpts->phc_index = ptp_clock_index(cpts->clock); -#endif return 0; } +EXPORT_SYMBOL_GPL(cpts_register); void cpts_unregister(struct cpts *cpts) { -#ifdef CONFIG_TI_CPTS if (cpts->clock) { ptp_clock_unregister(cpts->clock); cancel_delayed_work_sync(&cpts->overflow_work); } if (cpts->refclk) cpts_clk_release(cpts); -#endif } +EXPORT_SYMBOL_GPL(cpts_unregister); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI CPTS driver"); +MODULE_AUTHOR("Richard Cochran "); diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 69a46b9..416ba2c 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -111,7 +111,7 @@ struct cpts { struct cpsw_cpts __iomem *reg; int tx_enable; int rx_enable; -#ifdef CONFIG_TI_CPTS +#if IS_ENABLED(CONFIG_TI_CPTS) struct ptp_clock_info info; struct ptp_clock *clock; spinlock_t lock; /* protects time registers */ @@ -127,9 +127,11 @@ struct cpts { #endif }; -#ifdef CONFIG_TI_CPTS +#if IS_ENABLED(CONFIG_TI_CPTS) void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); +int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); +void cpts_unregister(struct cpts *cpts); #else static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) { @@ -137,9 +139,17 @@ static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) { } + +static inline int +cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) +{ + return 0; +} + +static inline void cpts_unregister(struct cpts *cpts) +{ +} #endif -int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); -void cpts_unregister(struct cpts *cpts); #endif -- cgit v1.1 From b63ba58ee9957261b39209f3031ff06714cf13cb Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:35 -0600 Subject: net: ethernet: ti: cpsw: minimize direct access to struct cpts This will provide more flexibility in changing CPTS internals and also required for further changes. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 28 +++++++++++++++------------- drivers/net/ethernet/ti/cpts.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 8fdb274..7599895 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1562,7 +1562,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, } if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && - cpsw->cpts->tx_enable) + cpts_is_tx_enabled(cpsw->cpts)) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; skb_tx_timestamp(skb); @@ -1601,7 +1601,8 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave]; u32 ts_en, seq_id; - if (!cpsw->cpts->tx_enable && !cpsw->cpts->rx_enable) { + if (!cpts_is_tx_enabled(cpsw->cpts) && + !cpts_is_rx_enabled(cpsw->cpts)) { slave_write(slave, 0, CPSW1_TS_CTL); return; } @@ -1609,10 +1610,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588; ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS; - if (cpsw->cpts->tx_enable) + if (cpts_is_tx_enabled(cpsw->cpts)) ts_en |= CPSW_V1_TS_TX_EN; - if (cpsw->cpts->rx_enable) + if (cpts_is_rx_enabled(cpsw->cpts)) ts_en |= CPSW_V1_TS_RX_EN; slave_write(slave, ts_en, CPSW1_TS_CTL); @@ -1635,20 +1636,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) case CPSW_VERSION_2: ctrl &= ~CTRL_V2_ALL_TS_MASK; - if (cpsw->cpts->tx_enable) + if (cpts_is_tx_enabled(cpsw->cpts)) ctrl |= CTRL_V2_TX_TS_BITS; - if (cpsw->cpts->rx_enable) + if (cpts_is_rx_enabled(cpsw->cpts)) ctrl |= CTRL_V2_RX_TS_BITS; break; case CPSW_VERSION_3: default: ctrl &= ~CTRL_V3_ALL_TS_MASK; - if (cpsw->cpts->tx_enable) + if (cpts_is_tx_enabled(cpsw->cpts)) ctrl |= CTRL_V3_TX_TS_BITS; - if (cpsw->cpts->rx_enable) + if (cpts_is_rx_enabled(cpsw->cpts)) ctrl |= CTRL_V3_RX_TS_BITS; break; } @@ -1684,7 +1685,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) switch (cfg.rx_filter) { case HWTSTAMP_FILTER_NONE: - cpts->rx_enable = 0; + cpts_rx_enable(cpts, 0); break; case HWTSTAMP_FILTER_ALL: case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: @@ -1700,14 +1701,14 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - cpts->rx_enable = 1; + cpts_rx_enable(cpts, 1); cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; break; default: return -ERANGE; } - cpts->tx_enable = cfg.tx_type == HWTSTAMP_TX_ON; + cpts_tx_enable(cpts, cfg.tx_type == HWTSTAMP_TX_ON); switch (cpsw->version) { case CPSW_VERSION_1: @@ -1736,8 +1737,9 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) return -EOPNOTSUPP; cfg.flags = 0; - cfg.tx_type = cpts->tx_enable ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; - cfg.rx_filter = (cpts->rx_enable ? + cfg.tx_type = cpts_is_tx_enabled(cpts) ? + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + cfg.rx_filter = (cpts_is_rx_enabled(cpts) ? HWTSTAMP_FILTER_PTP_V2_EVENT : HWTSTAMP_FILTER_NONE); return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 416ba2c..29a1e80c 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -132,6 +132,27 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); void cpts_unregister(struct cpts *cpts); + +static inline void cpts_rx_enable(struct cpts *cpts, int enable) +{ + cpts->rx_enable = enable; +} + +static inline bool cpts_is_rx_enabled(struct cpts *cpts) +{ + return !!cpts->rx_enable; +} + +static inline void cpts_tx_enable(struct cpts *cpts, int enable) +{ + cpts->tx_enable = enable; +} + +static inline bool cpts_is_tx_enabled(struct cpts *cpts) +{ + return !!cpts->tx_enable; +} + #else static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) { @@ -149,6 +170,24 @@ cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) static inline void cpts_unregister(struct cpts *cpts) { } + +static inline void cpts_rx_enable(struct cpts *cpts, int enable) +{ +} + +static inline bool cpts_is_rx_enabled(struct cpts *cpts) +{ + return false; +} + +static inline void cpts_tx_enable(struct cpts *cpts, int enable) +{ +} + +static inline bool cpts_is_tx_enabled(struct cpts *cpts) +{ + return false; +} #endif -- cgit v1.1 From fd123a9414766e73fa704547e7f03595741b7de1 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:36 -0600 Subject: net: ethernet: ti: cpts: fix unbalanced clk api usage in cpts_register/unregister There are two issues with TI CPTS code which are reproducible when TI CPSW ethX device passes few up/down iterations: - cpts refclk prepare counter continuously incremented after each up/down iteration; - devm_clk_get(dev, "cpts") is called many times. Hence, fix these issues by using clk_disable_unprepare() in cpts_clk_release() and skipping devm_clk_get() if cpts refclk has been acquired already. Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 8cb0369..61198f1 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -230,18 +230,20 @@ static void cpts_overflow_check(struct work_struct *work) static void cpts_clk_init(struct device *dev, struct cpts *cpts) { - cpts->refclk = devm_clk_get(dev, "cpts"); - if (IS_ERR(cpts->refclk)) { - dev_err(dev, "Failed to get cpts refclk\n"); - cpts->refclk = NULL; - return; + if (!cpts->refclk) { + cpts->refclk = devm_clk_get(dev, "cpts"); + if (IS_ERR(cpts->refclk)) { + dev_err(dev, "Failed to get cpts refclk\n"); + cpts->refclk = NULL; + return; + } } clk_prepare_enable(cpts->refclk); } static void cpts_clk_release(struct cpts *cpts) { - clk_disable(cpts->refclk); + clk_disable_unprepare(cpts->refclk); } static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, -- cgit v1.1 From 6c691405bcaf8f1a7413416cc757756cfb060875 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:37 -0600 Subject: net: ethernet: ti: cpts: fix registration order The ptp clock registered before spinlock, which is protecting it, and before timecounter and cyclecounter initialization in cpts_register(). So, ensure that ptp clock is registered the last, after everything else is done. Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 61198f1..3dda6d5 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -356,15 +356,8 @@ int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) { int err, i; - unsigned long flags; cpts->info = cpts_info; - cpts->clock = ptp_clock_register(&cpts->info, dev); - if (IS_ERR(cpts->clock)) { - err = PTR_ERR(cpts->clock); - cpts->clock = NULL; - return err; - } spin_lock_init(&cpts->lock); cpts->cc.read = cpts_systim_read; @@ -382,15 +375,26 @@ int cpts_register(struct device *dev, struct cpts *cpts, cpts_write32(cpts, CPTS_EN, control); cpts_write32(cpts, TS_PEND_EN, int_enable); - spin_lock_irqsave(&cpts->lock, flags); timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real())); - spin_unlock_irqrestore(&cpts->lock, flags); INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); - schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); + cpts->clock = ptp_clock_register(&cpts->info, dev); + if (IS_ERR(cpts->clock)) { + err = PTR_ERR(cpts->clock); + cpts->clock = NULL; + goto err_ptp; + } cpts->phc_index = ptp_clock_index(cpts->clock); + + schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); + return 0; + +err_ptp: + if (cpts->refclk) + cpts_clk_release(cpts); + return err; } EXPORT_SYMBOL_GPL(cpts_register); -- cgit v1.1 From 8fcd68914e447af006f9597a2688754517fabfe9 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:38 -0600 Subject: net: ethernet: ti: cpts: disable cpts when unregistered The cpts now is left enabled after unregistration. Hence, disable it in cpts_unregister(). Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 3dda6d5..d3c1ac5 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -404,6 +404,10 @@ void cpts_unregister(struct cpts *cpts) ptp_clock_unregister(cpts->clock); cancel_delayed_work_sync(&cpts->overflow_work); } + + cpts_write32(cpts, 0, int_enable); + cpts_write32(cpts, 0, control); + if (cpts->refclk) cpts_clk_release(cpts); } -- cgit v1.1 From e4439fa838eb78b20f82665110b478018268833e Mon Sep 17 00:00:00 2001 From: WingMan Kwok Date: Tue, 6 Dec 2016 18:00:39 -0600 Subject: net: ethernet: ti: cpts: clean up event list if event pool is empty When a CPTS user does not exit gracefully by disabling cpts timestamping and leaving a joined multicast group, the system continues to receive and timestamps the ptp packets which eventually occupy all the event list entries. When this happns, the added code tries to remove some list entries which are expired. Signed-off-by: WingMan Kwok Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index d3c1ac5..7ab1fa7 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -57,6 +57,26 @@ static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) return -1; } +static int cpts_purge_events(struct cpts *cpts) +{ + struct list_head *this, *next; + struct cpts_event *event; + int removed = 0; + + list_for_each_safe(this, next, &cpts->events) { + event = list_entry(this, struct cpts_event, list); + if (event_expired(event)) { + list_del_init(&event->list); + list_add(&event->list, &cpts->pool); + ++removed; + } + } + + if (removed) + pr_debug("cpts: event pool cleaned up %d\n", removed); + return removed ? 0 : -1; +} + /* * Returns zero if matching event type was found. */ @@ -69,10 +89,12 @@ static int cpts_fifo_read(struct cpts *cpts, int match) for (i = 0; i < CPTS_FIFO_DEPTH; i++) { if (cpts_fifo_pop(cpts, &hi, &lo)) break; - if (list_empty(&cpts->pool)) { - pr_err("cpts: event pool is empty\n"); + + if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) { + pr_err("cpts: event pool empty\n"); return -1; } + event = list_first_entry(&cpts->pool, struct cpts_event, list); event->tmo = jiffies + 2; event->high = hi; -- cgit v1.1 From 2a79df3ee9679dd1433ef9baa87ec395355eb0b4 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:40 -0600 Subject: net: ethernet: ti: cpts: drop excessive writes to CTRL and INT_EN regs CPTS module and IRQs are always enabled when CPTS is registered, before starting overflow check work, and disabled during deregistration, when overflow check work has been canceled already. So, It doesn't require to (re)enable CPTS module and IRQs in cpts_overflow_check(). Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 7ab1fa7..fe1bb7f 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -243,8 +243,6 @@ static void cpts_overflow_check(struct work_struct *work) struct timespec64 ts; struct cpts *cpts = container_of(work, struct cpts, overflow_work.work); - cpts_write32(cpts, CPTS_EN, control); - cpts_write32(cpts, TS_PEND_EN, int_enable); cpts_ptp_gettime(&cpts->info, &ts); pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); -- cgit v1.1 From 8a2c9a5ab4b933efd611af0a1313ec7892f5df1a Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:41 -0600 Subject: net: ethernet: ti: cpts: rework initialization/deinitialization The current implementation CPTS initialization and deinitialization (represented by cpts_register/unregister()) does too many static initialization from .ndo_open(), which is reasonable to do once at probe time instead, and also require caller to allocate memory for struct cpts, which is internal for CPTS driver in general. This patch splits CPTS initialization and deinitialization on two parts: - static initializtion cpts_create()/cpts_release() which expected to be executed when parent driver is probed/removed; - dynamic part cpts_register/unregister() which expected to be executed when network device is opened/closed. As result, current code of CPTS parent driver - CPSW - will be simplified (and it also will allow simplify adding support for Keystone 2 devices in the future), plus more initialization errors will be catched earlier. In addition, this change allows to clean up cpts.h for the case when CPTS is disabled. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 24 +++++----- drivers/net/ethernet/ti/cpts.c | 99 +++++++++++++++++++++++++----------------- drivers/net/ethernet/ti/cpts.h | 26 ++++++++--- 3 files changed, 92 insertions(+), 57 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 7599895..a9a8354 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1487,9 +1487,7 @@ static int cpsw_ndo_open(struct net_device *ndev) if (ret < 0) goto err_cleanup; - if (cpts_register(cpsw->dev, cpsw->cpts, - cpsw->data.cpts_clock_mult, - cpsw->data.cpts_clock_shift)) + if (cpts_register(cpsw->cpts)) dev_err(priv->dev, "error registering cpts device\n"); } @@ -2796,6 +2794,7 @@ static int cpsw_probe(struct platform_device *pdev) struct cpdma_params dma_params; struct cpsw_ale_params ale_params; void __iomem *ss_regs; + void __iomem *cpts_regs; struct resource *res, *ss_res; const struct of_device_id *of_id; struct gpio_descs *mode; @@ -2823,12 +2822,6 @@ static int cpsw_probe(struct platform_device *pdev) priv->dev = &ndev->dev; priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); cpsw->rx_packet_max = max(rx_packet_max, 128); - cpsw->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL); - if (!cpsw->cpts) { - dev_err(&pdev->dev, "error allocating cpts\n"); - ret = -ENOMEM; - goto clean_ndev_ret; - } mode = devm_gpiod_get_array_optional(&pdev->dev, "mode", GPIOD_OUT_LOW); if (IS_ERR(mode)) { @@ -2916,7 +2909,7 @@ static int cpsw_probe(struct platform_device *pdev) switch (cpsw->version) { case CPSW_VERSION_1: cpsw->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET; - cpsw->cpts->reg = ss_regs + CPSW1_CPTS_OFFSET; + cpts_regs = ss_regs + CPSW1_CPTS_OFFSET; cpsw->hw_stats = ss_regs + CPSW1_HW_STATS; dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET; @@ -2930,7 +2923,7 @@ static int cpsw_probe(struct platform_device *pdev) case CPSW_VERSION_3: case CPSW_VERSION_4: cpsw->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET; - cpsw->cpts->reg = ss_regs + CPSW2_CPTS_OFFSET; + cpts_regs = ss_regs + CPSW2_CPTS_OFFSET; cpsw->hw_stats = ss_regs + CPSW2_HW_STATS; dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET; @@ -2997,6 +2990,14 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_dma_ret; } + cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, + cpsw->data.cpts_clock_mult, + cpsw->data.cpts_clock_shift); + if (IS_ERR(cpsw->cpts)) { + ret = PTR_ERR(cpsw->cpts); + goto clean_ale_ret; + } + ndev->irq = platform_get_irq(pdev, 1); if (ndev->irq < 0) { dev_err(priv->dev, "error getting irq resource\n"); @@ -3112,6 +3113,7 @@ static int cpsw_remove(struct platform_device *pdev) unregister_netdev(cpsw->slaves[1].ndev); unregister_netdev(ndev); + cpts_release(cpsw->cpts); cpsw_ale_destroy(cpsw->ale); cpdma_ctlr_destroy(cpsw->dma); cpsw_remove_dt(pdev); diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index fe1bb7f..31cd83f 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -248,24 +248,6 @@ static void cpts_overflow_check(struct work_struct *work) schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); } -static void cpts_clk_init(struct device *dev, struct cpts *cpts) -{ - if (!cpts->refclk) { - cpts->refclk = devm_clk_get(dev, "cpts"); - if (IS_ERR(cpts->refclk)) { - dev_err(dev, "Failed to get cpts refclk\n"); - cpts->refclk = NULL; - return; - } - } - clk_prepare_enable(cpts->refclk); -} - -static void cpts_clk_release(struct cpts *cpts) -{ - clk_disable_unprepare(cpts->refclk); -} - static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, u16 ts_seqid, u8 ts_msgtype) { @@ -372,34 +354,23 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(cpts_tx_timestamp); -int cpts_register(struct device *dev, struct cpts *cpts, - u32 mult, u32 shift) +int cpts_register(struct cpts *cpts) { int err, i; - cpts->info = cpts_info; - spin_lock_init(&cpts->lock); - - cpts->cc.read = cpts_systim_read; - cpts->cc.mask = CLOCKSOURCE_MASK(32); - cpts->cc_mult = mult; - cpts->cc.mult = mult; - cpts->cc.shift = shift; - INIT_LIST_HEAD(&cpts->events); INIT_LIST_HEAD(&cpts->pool); for (i = 0; i < CPTS_MAX_EVENTS; i++) list_add(&cpts->pool_data[i].list, &cpts->pool); - cpts_clk_init(dev, cpts); + clk_enable(cpts->refclk); + cpts_write32(cpts, CPTS_EN, control); cpts_write32(cpts, TS_PEND_EN, int_enable); timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real())); - INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); - - cpts->clock = ptp_clock_register(&cpts->info, dev); + cpts->clock = ptp_clock_register(&cpts->info, cpts->dev); if (IS_ERR(cpts->clock)) { err = PTR_ERR(cpts->clock); cpts->clock = NULL; @@ -412,27 +383,73 @@ int cpts_register(struct device *dev, struct cpts *cpts, return 0; err_ptp: - if (cpts->refclk) - cpts_clk_release(cpts); + clk_disable(cpts->refclk); return err; } EXPORT_SYMBOL_GPL(cpts_register); void cpts_unregister(struct cpts *cpts) { - if (cpts->clock) { - ptp_clock_unregister(cpts->clock); - cancel_delayed_work_sync(&cpts->overflow_work); - } + if (WARN_ON(!cpts->clock)) + return; + + cancel_delayed_work_sync(&cpts->overflow_work); + + ptp_clock_unregister(cpts->clock); + cpts->clock = NULL; cpts_write32(cpts, 0, int_enable); cpts_write32(cpts, 0, control); - if (cpts->refclk) - cpts_clk_release(cpts); + clk_disable(cpts->refclk); } EXPORT_SYMBOL_GPL(cpts_unregister); +struct cpts *cpts_create(struct device *dev, void __iomem *regs, + u32 mult, u32 shift) +{ + struct cpts *cpts; + + cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); + if (!cpts) + return ERR_PTR(-ENOMEM); + + cpts->dev = dev; + cpts->reg = (struct cpsw_cpts __iomem *)regs; + spin_lock_init(&cpts->lock); + INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); + + cpts->refclk = devm_clk_get(dev, "cpts"); + if (IS_ERR(cpts->refclk)) { + dev_err(dev, "Failed to get cpts refclk\n"); + return ERR_PTR(PTR_ERR(cpts->refclk)); + } + + clk_prepare(cpts->refclk); + + cpts->cc.read = cpts_systim_read; + cpts->cc.mask = CLOCKSOURCE_MASK(32); + cpts->cc.shift = shift; + cpts->cc_mult = mult; + cpts->cc.mult = mult; + cpts->info = cpts_info; + + return cpts; +} +EXPORT_SYMBOL_GPL(cpts_create); + +void cpts_release(struct cpts *cpts) +{ + if (!cpts) + return; + + if (WARN_ON(!cpts->refclk)) + return; + + clk_unprepare(cpts->refclk); +} +EXPORT_SYMBOL_GPL(cpts_release); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("TI CPTS driver"); MODULE_AUTHOR("Richard Cochran "); diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 29a1e80c..e7d857c 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -20,6 +20,8 @@ #ifndef _TI_CPTS_H_ #define _TI_CPTS_H_ +#if IS_ENABLED(CONFIG_TI_CPTS) + #include #include #include @@ -108,10 +110,10 @@ struct cpts_event { }; struct cpts { + struct device *dev; struct cpsw_cpts __iomem *reg; int tx_enable; int rx_enable; -#if IS_ENABLED(CONFIG_TI_CPTS) struct ptp_clock_info info; struct ptp_clock *clock; spinlock_t lock; /* protects time registers */ @@ -124,14 +126,15 @@ struct cpts { struct list_head events; struct list_head pool; struct cpts_event pool_data[CPTS_MAX_EVENTS]; -#endif }; -#if IS_ENABLED(CONFIG_TI_CPTS) void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); -int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); +int cpts_register(struct cpts *cpts); void cpts_unregister(struct cpts *cpts); +struct cpts *cpts_create(struct device *dev, void __iomem *regs, + u32 mult, u32 shift); +void cpts_release(struct cpts *cpts); static inline void cpts_rx_enable(struct cpts *cpts, int enable) { @@ -154,6 +157,8 @@ static inline bool cpts_is_tx_enabled(struct cpts *cpts) } #else +struct cpts; + static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) { } @@ -161,8 +166,19 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) { } +static inline +struct cpts *cpts_create(struct device *dev, void __iomem *regs, + u32 mult, u32 shift) +{ + return NULL; +} + +static inline void cpts_release(struct cpts *cpts) +{ +} + static inline int -cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) +cpts_register(struct cpts *cpts) { return 0; } -- cgit v1.1 From 4a88fb9565d418cf1c2bf41be7cfe156717b9c85 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:42 -0600 Subject: net: ethernet: ti: cpts: move dt props parsing to cpts driver Move DT properties parsing into CPTS driver to simplify CPSW code and CPTS driver porting on other SoC in the future (like Keystone 2) - with this change it will not be required to add the same DT parsing code in Keystone 2 NETCP driver. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 16 +--------------- drivers/net/ethernet/ti/cpsw.h | 2 -- drivers/net/ethernet/ti/cpts.c | 34 ++++++++++++++++++++++++++++++---- drivers/net/ethernet/ti/cpts.h | 5 +++-- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a9a8354..b62d958 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2524,18 +2524,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->active_slave = prop; - if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { - dev_err(&pdev->dev, "Missing cpts_clock_mult property in the DT.\n"); - return -EINVAL; - } - data->cpts_clock_mult = prop; - - if (of_property_read_u32(node, "cpts_clock_shift", &prop)) { - dev_err(&pdev->dev, "Missing cpts_clock_shift property in the DT.\n"); - return -EINVAL; - } - data->cpts_clock_shift = prop; - data->slave_data = devm_kzalloc(&pdev->dev, data->slaves * sizeof(struct cpsw_slave_data), GFP_KERNEL); @@ -2990,9 +2978,7 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_dma_ret; } - cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, - cpsw->data.cpts_clock_mult, - cpsw->data.cpts_clock_shift); + cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpsw->dev->of_node); if (IS_ERR(cpsw->cpts)) { ret = PTR_ERR(cpsw->cpts); goto clean_ale_ret; diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index 16b54c6..6c3037a 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -31,8 +31,6 @@ struct cpsw_platform_data { u32 channels; /* number of cpdma channels (symmetric) */ u32 slaves; /* number of slave cpgmac ports */ u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ - u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ - u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ u32 ale_entries; /* ale table size */ u32 bd_ram_size; /*buffer descriptor ram size */ u32 mac_control; /* Mac control register */ diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 31cd83f..cb844ed 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -405,10 +405,31 @@ void cpts_unregister(struct cpts *cpts) } EXPORT_SYMBOL_GPL(cpts_unregister); +static int cpts_of_parse(struct cpts *cpts, struct device_node *node) +{ + int ret = -EINVAL; + u32 prop; + + if (of_property_read_u32(node, "cpts_clock_mult", &prop)) + goto of_error; + cpts->cc.mult = prop; + + if (of_property_read_u32(node, "cpts_clock_shift", &prop)) + goto of_error; + cpts->cc.shift = prop; + + return 0; + +of_error: + dev_err(cpts->dev, "CPTS: Missing property in the DT.\n"); + return ret; +} + struct cpts *cpts_create(struct device *dev, void __iomem *regs, - u32 mult, u32 shift) + struct device_node *node) { struct cpts *cpts; + int ret; cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); if (!cpts) @@ -419,6 +440,10 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, spin_lock_init(&cpts->lock); INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); + ret = cpts_of_parse(cpts, node); + if (ret) + return ERR_PTR(ret); + cpts->refclk = devm_clk_get(dev, "cpts"); if (IS_ERR(cpts->refclk)) { dev_err(dev, "Failed to get cpts refclk\n"); @@ -429,9 +454,10 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, cpts->cc.read = cpts_systim_read; cpts->cc.mask = CLOCKSOURCE_MASK(32); - cpts->cc.shift = shift; - cpts->cc_mult = mult; - cpts->cc.mult = mult; + /* save cc.mult original value as it can be modified + * by cpts_ptp_adjfreq(). + */ + cpts->cc_mult = cpts->cc.mult; cpts->info = cpts_info; return cpts; diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index e7d857c..5da23af 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -133,7 +134,7 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); int cpts_register(struct cpts *cpts); void cpts_unregister(struct cpts *cpts); struct cpts *cpts_create(struct device *dev, void __iomem *regs, - u32 mult, u32 shift); + struct device_node *node); void cpts_release(struct cpts *cpts); static inline void cpts_rx_enable(struct cpts *cpts, int enable) @@ -168,7 +169,7 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) static inline struct cpts *cpts_create(struct device *dev, void __iomem *regs, - u32 mult, u32 shift) + struct device_node *node) { return NULL; } -- cgit v1.1 From 5304121adae9fc59f4b640f82200868112edd0bd Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 6 Dec 2016 18:00:43 -0600 Subject: clocksource: export the clocks_calc_mult_shift to use by timestamp code The CPSW CPTS driver is capable of doing timestamping on tx/rx packets and requires to know mult and shift factors for timestamp conversion from raw value to nanoseconds (ptp clock). Now these mult and shift factors are calculated manually and provided through DT, which makes very hard to support of a lot number of platforms, especially if CPTS refclk is not the same for some kind of boards and depends on efuse settings (Keystone 2 platforms). Hence, export clocks_calc_mult_shift() to allow drivers like CPSW CPTS (and other ptp drivesr) to benefit from automaitc calculation of mult and shift factors. Cc: John Stultz Signed-off-by: Murali Karicheri Signed-off-by: Grygorii Strashko Acked-by: Thomas Gleixner Signed-off-by: David S. Miller --- kernel/time/clocksource.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 7e4fad7..150242c 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -89,6 +89,7 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec) *mult = tmp; *shift = sft; } +EXPORT_SYMBOL_GPL(clocks_calc_mult_shift); /*[Clocksource internal variables]--------- * curr_clocksource: -- cgit v1.1 From 88f0f0b0bebfbe086dda853f591ae3a014b19e40 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:44 -0600 Subject: net: ethernet: ti: cpts: calc mult and shift from refclk freq The cyclecounter mult and shift values can be calculated based on the CPTS rfclk frequency and timekeepnig framework provides required algos and API's. Hence, calc mult and shift basing on CPTS rfclk frequency if both cpts_clock_shift and cpts_clock_mult properties are not provided in DT (the basis of calculation algorithm is borrowed from __clocksource_update_freq_scale() commit 7d2f944a2b83 ("clocksource: Provide a generic mult/shift factor calculation")). After this change cpts_clock_shift and cpts_clock_mult DT properties will become optional. Cc: John Stultz Cc: Thomas Gleixner Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 8 +++-- drivers/net/ethernet/ti/cpts.c | 50 ++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 5ad439f..ebda7c9 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -20,8 +20,6 @@ Required properties: - slaves : Specifies number for slaves - active_slave : Specifies the slave to use for time stamping, ethtool and SIOCGMIIPHY -- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds -- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds Optional properties: - ti,hwmods : Must be "cpgmac0" @@ -35,7 +33,11 @@ Optional properties: For example in dra72x-evm, pcf gpio has to be driven low so that cpsw slave 0 and phy data lines are connected via mux. - +- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds +- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds + Mult and shift will be calculated basing on CPTS + rftclk frequency if both cpts_clock_shift and + cpts_clock_mult properties are not provided. Slave Properties: Required properties: diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index cb844ed..ebd413a 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -405,18 +405,52 @@ void cpts_unregister(struct cpts *cpts) } EXPORT_SYMBOL_GPL(cpts_unregister); +static void cpts_calc_mult_shift(struct cpts *cpts) +{ + u64 frac, maxsec, ns; + u32 freq; + + freq = clk_get_rate(cpts->refclk); + + /* Calc the maximum number of seconds which we can run before + * wrapping around. + */ + maxsec = cpts->cc.mask; + do_div(maxsec, freq); + /* limit conversation rate to 10 sec as higher values will produce + * too small mult factors and so reduce the conversion accuracy + */ + if (maxsec > 10) + maxsec = 10; + + if (cpts->cc.mult || cpts->cc.shift) + return; + + clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift, + freq, NSEC_PER_SEC, maxsec); + + frac = 0; + ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac); + + dev_info(cpts->dev, + "CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n", + freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); +} + static int cpts_of_parse(struct cpts *cpts, struct device_node *node) { int ret = -EINVAL; u32 prop; - if (of_property_read_u32(node, "cpts_clock_mult", &prop)) - goto of_error; - cpts->cc.mult = prop; + if (!of_property_read_u32(node, "cpts_clock_mult", &prop)) + cpts->cc.mult = prop; - if (of_property_read_u32(node, "cpts_clock_shift", &prop)) - goto of_error; - cpts->cc.shift = prop; + if (!of_property_read_u32(node, "cpts_clock_shift", &prop)) + cpts->cc.shift = prop; + + if ((cpts->cc.mult && !cpts->cc.shift) || + (!cpts->cc.mult && cpts->cc.shift)) + goto of_error; return 0; @@ -454,11 +488,13 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, cpts->cc.read = cpts_systim_read; cpts->cc.mask = CLOCKSOURCE_MASK(32); + cpts->info = cpts_info; + + cpts_calc_mult_shift(cpts); /* save cc.mult original value as it can be modified * by cpts_ptp_adjfreq(). */ cpts->cc_mult = cpts->cc.mult; - cpts->info = cpts_info; return cpts; } -- cgit v1.1 From 20138cf9efc1a761a17839581b0130d8e4209882 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:45 -0600 Subject: net: ethernet: ti: cpts: fix overflow check period The CPTS drivers uses 8sec period for overflow checking with assumption that CPTS retclk will not exceed 500MHz. But that's not true on some TI platforms (Kesytone 2). As result, it is possible that CPTS counter will overflow more than once between two readings. Hence, fix it by selecting overflow check period dynamically as max_sec_before_overflow/2, where max_sec_before_overflow = max_counter_val / rftclk_freq. Cc: John Stultz Cc: Thomas Gleixner Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 10 +++++++--- drivers/net/ethernet/ti/cpts.h | 4 +--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index ebd413a..0c0d48e 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -245,7 +245,7 @@ static void cpts_overflow_check(struct work_struct *work) cpts_ptp_gettime(&cpts->info, &ts); pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); - schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); + schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period); } static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, @@ -378,8 +378,7 @@ int cpts_register(struct cpts *cpts) } cpts->phc_index = ptp_clock_index(cpts->clock); - schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); - + schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period); return 0; err_ptp: @@ -423,6 +422,11 @@ static void cpts_calc_mult_shift(struct cpts *cpts) if (maxsec > 10) maxsec = 10; + /* Calc overflow check period (maxsec / 2) */ + cpts->ov_check_period = (HZ * maxsec) / 2; + dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n", + cpts->ov_check_period); + if (cpts->cc.mult || cpts->cc.shift) return; diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 5da23af..c96eca2 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -97,9 +97,6 @@ enum { CPTS_EV_TX, /* Ethernet Transmit Event */ }; -/* This covers any input clock up to about 500 MHz. */ -#define CPTS_OVERFLOW_PERIOD (HZ * 8) - #define CPTS_FIFO_DEPTH 16 #define CPTS_MAX_EVENTS 32 @@ -127,6 +124,7 @@ struct cpts { struct list_head events; struct list_head pool; struct cpts_event pool_data[CPTS_MAX_EVENTS]; + unsigned long ov_check_period; }; void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); -- cgit v1.1 From 90364feaf70f251533feff3f228f82df9a9f52b6 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 6 Dec 2016 14:47:15 +0100 Subject: net: stmmac: do not call phy_ethtool_ksettings_set from atomic context >From what I can tell, spin_lock(&priv->lock) is not needed, since the phy_ethtool_ksettings_set call is not given the priv struct. phy_start_aneg takes the phydev->lock. Calls to phy_adjust_link from phy_state_machine also takes the phydev->lock. [ 13.718319] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:97 [ 13.726717] in_atomic(): 1, irqs_disabled(): 0, pid: 1307, name: ethtool [ 13.742115] Hardware name: Axis ARTPEC-6 Platform [ 13.746829] [<80110568>] (unwind_backtrace) from [<8010c2bc>] (show_stack+0x18/0x1c) [ 13.754575] [<8010c2bc>] (show_stack) from [<80433484>] (dump_stack+0x80/0xa0) [ 13.761801] [<80433484>] (dump_stack) from [<80145428>] (___might_sleep+0x108/0x170) [ 13.769554] [<80145428>] (___might_sleep) from [<806c9b50>] (mutex_lock+0x24/0x44) [ 13.777128] [<806c9b50>] (mutex_lock) from [<8050cbc0>] (phy_start_aneg+0x1c/0x13c) [ 13.784783] [<8050cbc0>] (phy_start_aneg) from [<8050d338>] (phy_ethtool_ksettings_set+0x98/0xd0) [ 13.793656] [<8050d338>] (phy_ethtool_ksettings_set) from [<80517adc>] (stmmac_ethtool_set_link_ksettings+0xa0/0xb4) [ 13.804184] [<80517adc>] (stmmac_ethtool_set_link_ksettings) from [<805c5138>] (ethtool_set_settings+0xd4/0x13c) [ 13.814358] [<805c5138>] (ethtool_set_settings) from [<805c9718>] (dev_ethtool+0x13c4/0x211c) [ 13.822882] [<805c9718>] (dev_ethtool) from [<805dc7c0>] (dev_ioctl+0x480/0x8e0) [ 13.830291] [<805dc7c0>] (dev_ioctl) from [<80260e34>] (do_vfs_ioctl+0x94/0xa00) [ 13.837699] [<80260e34>] (do_vfs_ioctl) from [<802617dc>] (SyS_ioctl+0x3c/0x60) [ 13.845011] [<802617dc>] (SyS_ioctl) from [<801088bc>] (__sys_trace_return+0x0/0x10) Signed-off-by: Niklas Cassel Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index d5a8122..6ab7e2b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -406,9 +406,7 @@ stmmac_ethtool_set_link_ksettings(struct net_device *dev, return 0; } - spin_lock(&priv->lock); rc = phy_ethtool_ksettings_set(phy, cmd); - spin_unlock(&priv->lock); return rc; } -- cgit v1.1 From ef0915cacd04c9e35be5f9d62a4e4b5b4b9bcfd1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 7 Dec 2016 01:15:44 +0100 Subject: bpf: fix loading of BPF_MAXINSNS sized programs General assumption is that single program can hold up to BPF_MAXINSNS, that is, 4096 number of instructions. It is the case with cBPF and that limit was carried over to eBPF. When recently testing digest, I noticed that it's actually not possible to feed 4096 instructions via bpf(2). The check for > BPF_MAXINSNS was added back then to bpf_check() in cbd357008604 ("bpf: verifier (add ability to receive verification log)"). However, 09756af46893 ("bpf: expand BPF syscall with program load/unload") added yet another check that comes before that into bpf_prog_load(), but this time bails out already in case of >= BPF_MAXINSNS. Fix it up and perform the check early in bpf_prog_load(), so we can drop the second one in bpf_check(). It makes sense, because also a 0 insn program is useless and we don't want to waste any resources doing work up to bpf_check() point. The existing bpf(2) man page documents E2BIG as the official error for such cases, so just stick with it as well. Fixes: 09756af46893 ("bpf: expand BPF syscall with program load/unload") Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/syscall.c | 4 ++-- kernel/bpf/verifier.c | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index c0d2b42..88f609f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -786,8 +786,8 @@ static int bpf_prog_load(union bpf_attr *attr) /* eBPF programs must be GPL compatible to use GPL-ed functions */ is_gpl = license_is_gpl_compatible(license); - if (attr->insn_cnt >= BPF_MAXINSNS) - return -EINVAL; + if (attr->insn_cnt == 0 || attr->insn_cnt > BPF_MAXINSNS) + return -E2BIG; if (type == BPF_PROG_TYPE_KPROBE && attr->kern_version != LINUX_VERSION_CODE) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index cb37339..da9fb2a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3133,9 +3133,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) struct bpf_verifier_env *env; int ret = -EINVAL; - if ((*prog)->len <= 0 || (*prog)->len > BPF_MAXINSNS) - return -E2BIG; - /* 'struct bpf_verifier_env' can be global, but since it's not small, * allocate/free it every time bpf_check() is called */ -- cgit v1.1 From 48140a210b450db229cc9dd927480f65537dc7eb Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 7 Dec 2016 08:44:47 +0800 Subject: driver: ipvlan: Free ipvl_port directly with kfree instead of kfree_rcu There are two functions which would free the ipvl_port now. The first is ipvlan_port_create. It frees the ipvl_port in the error handler, so it could kfree it directly. The second is ipvlan_port_destroy. It invokes netdev_rx_handler_unregister which enforces one grace period by synchronize_net firstly, so it also could kfree the ipvl_port directly and safely. So it is unnecessary to use kfree_rcu to free ipvl_port. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan.h | 1 - drivers/net/ipvlan/ipvlan_main.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 05a62d2..031093e 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -97,7 +97,6 @@ struct ipvl_port { struct work_struct wq; struct sk_buff_head backlog; int count; - struct rcu_head rcu; }; static inline struct ipvl_port *ipvlan_port_get_rcu(const struct net_device *d) diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 5430460..ffe8994 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -128,7 +128,7 @@ static int ipvlan_port_create(struct net_device *dev) return 0; err: - kfree_rcu(port, rcu); + kfree(port); return err; } @@ -145,7 +145,7 @@ static void ipvlan_port_destroy(struct net_device *dev) netdev_rx_handler_unregister(dev); cancel_work_sync(&port->wq); __skb_queue_purge(&port->backlog); - kfree_rcu(port, rcu); + kfree(port); } #define IPVLAN_FEATURES \ -- cgit v1.1 From a1f5315ce4e1700e65255c617561aa7d8f09a729 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 7 Dec 2016 12:23:18 +0800 Subject: driver: macvlan: Remove the rcu member of macvlan_port When free macvlan_port in macvlan_port_destroy, it is safe to free directly because netdev_rx_handler_unregister could enforce one grace period. So it is unnecessary to use kfree_rcu for macvlan_port. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 3c0a171..20b3fdf2 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -43,7 +43,6 @@ struct macvlan_port { struct net_device *dev; struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; struct list_head vlans; - struct rcu_head rcu; struct sk_buff_head bc_queue; struct work_struct bc_work; bool passthru; @@ -1151,7 +1150,7 @@ static void macvlan_port_destroy(struct net_device *dev) cancel_work_sync(&port->bc_work); __skb_queue_purge(&port->bc_queue); - kfree_rcu(port, rcu); + kfree(port); } static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) -- cgit v1.1 From e82621e329b61ab8d6f0c987376512cf420ac9fe Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 7 Dec 2016 17:26:05 +0800 Subject: nlmon: use core MTU range checking in nlmon driver Since commit 61e84623ace3 ("net: centralize net_device min/max MTU checking"), mtu range is checked at dev_set_mtu(). This patch adds min_mtu for nlmon device and remove unnecessary ndo_change_mtu() function. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- drivers/net/nlmon.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c index 7b7c70e..2de7fae 100644 --- a/drivers/net/nlmon.c +++ b/drivers/net/nlmon.c @@ -27,24 +27,6 @@ static netdev_tx_t nlmon_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static int nlmon_is_valid_mtu(int new_mtu) -{ - /* Note that in netlink we do not really have an upper limit. On - * default, we use NLMSG_GOODSIZE. Here at least we should make - * sure that it's at least the header size. - */ - return new_mtu >= (int) sizeof(struct nlmsghdr); -} - -static int nlmon_change_mtu(struct net_device *dev, int new_mtu) -{ - if (!nlmon_is_valid_mtu(new_mtu)) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - static int nlmon_dev_init(struct net_device *dev) { dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); @@ -124,7 +106,6 @@ static const struct net_device_ops nlmon_ops = { .ndo_stop = nlmon_close, .ndo_start_xmit = nlmon_xmit, .ndo_get_stats64 = nlmon_get_stats64, - .ndo_change_mtu = nlmon_change_mtu, }; static void nlmon_setup(struct net_device *dev) @@ -145,6 +126,7 @@ static void nlmon_setup(struct net_device *dev) * expected in most cases. */ dev->mtu = NLMSG_GOODSIZE; + dev->min_mtu = sizeof(struct nlmsghdr); } static int nlmon_validate(struct nlattr *tb[], struct nlattr *data[]) -- cgit v1.1 From 25e3e84b183a2c25b1e8303f815a6aa5bf78358a Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 7 Dec 2016 17:41:33 +0800 Subject: dummy: expend mtu range for dummy device After commit 61e84623ace3 ("net: centralize net_device min/max MTU checking"), the mtu range for dummy device becomes [68, 1500]. This patch extends it to [0, 65535]. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- drivers/net/dummy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index 69fc840..6421835 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -154,6 +154,9 @@ static void dummy_setup(struct net_device *dev) dev->hw_features |= dev->features; dev->hw_enc_features |= dev->features; eth_hw_addr_random(dev); + + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; } static int dummy_validate(struct nlattr *tb[], struct nlattr *data[]) -- cgit v1.1 From c84d59e72830ce7a7ce67cf7509d37342aa235db Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Mon, 5 Dec 2016 14:47:17 +0100 Subject: ieee802154: atusb: sync header file from firmware for new features This file is shared between the atusb firmware and the kernel driver. In this update it brings the new interfaces from version 0.3 of the firmware. Signed-off-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/atusb.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ieee802154/atusb.h b/drivers/net/ieee802154/atusb.h index 0690edc..b22bbaa 100644 --- a/drivers/net/ieee802154/atusb.h +++ b/drivers/net/ieee802154/atusb.h @@ -13,8 +13,8 @@ * Firmware: ben-wpan/atusb/fw/include/atusb/atusb.h */ -#ifndef _ATUSB_H -#define _ATUSB_H +#ifndef _ATUSB_H +#define _ATUSB_H #define ATUSB_VENDOR_ID 0x20b7 /* Qi Hardware*/ #define ATUSB_PRODUCT_ID 0x1540 /* 802.15.4, device 0 */ @@ -46,9 +46,12 @@ enum atusb_requests { ATUSB_SPI_WRITE2_SYNC, ATUSB_RX_MODE = 0x40, /* HardMAC group */ ATUSB_TX, + ATUSB_EUI64_WRITE = 0x50, /* Parameter in EEPROM grp */ + ATUSB_EUI64_READ, }; -/* Direction bRequest wValue wIndex wLength +/* + * Direction bRequest wValue wIndex wLength * * ->host ATUSB_ID - - 3 * ->host ATUSB_BUILD - - #bytes @@ -76,6 +79,8 @@ enum atusb_requests { * * host-> ATUSB_RX_MODE on - 0 * host-> ATUSB_TX flags ack_seq #bytes + * host-> ATUSB_EUI64_WRITE - - #bytes (8) + * ->host ATUSB_EUI64_READ - - #bytes (8) */ #define ATUSB_REQ_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN) -- cgit v1.1 From 46551564a28e07325140057876f92881d58a219a Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Mon, 5 Dec 2016 14:47:18 +0100 Subject: ieee802154: atusb: store firmware version after retrieval for later use The firmware versions will be used to enable selective features based on the available firmware on the device. Make sure we do not need to fetch it for every check but store it after the initial retrieval. Signed-off-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/atusb.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 322864a..9fdea64 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -58,6 +58,11 @@ struct atusb { struct urb *tx_urb; struct sk_buff *tx_skb; uint8_t tx_ack_seq; /* current TX ACK sequence number */ + + /* Firmware variable */ + unsigned char fw_ver_maj; /* Firmware major version number */ + unsigned char fw_ver_min; /* Firmware minor version number */ + unsigned char fw_hw_type; /* Firmware hardware type */ }; /* ----- USB commands without data ----------------------------------------- */ @@ -594,14 +599,19 @@ static int atusb_get_and_show_revision(struct atusb *atusb) ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0, buffer, 3, 1000); - if (ret >= 0) + if (ret >= 0) { + atusb->fw_ver_maj = buffer[0]; + atusb->fw_ver_min = buffer[1]; + atusb->fw_hw_type = buffer[2]; + dev_info(&usb_dev->dev, "Firmware: major: %u, minor: %u, hardware type: %u\n", - buffer[0], buffer[1], buffer[2]); - if (buffer[0] == 0 && buffer[1] < 2) { + atusb->fw_ver_maj, atusb->fw_ver_min, atusb->fw_hw_type); + } + if (atusb->fw_ver_maj == 0 && atusb->fw_ver_min < 2) { dev_info(&usb_dev->dev, - "Firmware version (%u.%u) is predates our first public release.", - buffer[0], buffer[1]); + "Firmware version (%u.%u) predates our first public release.", + atusb->fw_ver_maj, atusb->fw_ver_min); dev_info(&usb_dev->dev, "Please update to version 0.2 or newer"); } -- cgit v1.1 From 6cc33eba232cebc9918c558b21b1e3b5fcab27b7 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Mon, 5 Dec 2016 14:47:19 +0100 Subject: ieee802154: atusb: try to read permanent extended address from device With version 0.3 the atusb firmware offers an interface to read a permanent EUI64 address from the devices EEPROM. This patch checks if the firmware is new enough and tries to read out and use the address. If this does not work we fall back to the original randomly generated address. Signed-off-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/atusb.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 9fdea64..3ed34cc 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -679,6 +679,43 @@ fail: return -ENODEV; } +static int atusb_set_extended_addr(struct atusb *atusb) +{ + struct usb_device *usb_dev = atusb->usb_dev; + unsigned char buffer[IEEE802154_EXTENDED_ADDR_LEN]; + __le64 extended_addr; + u64 addr; + int ret; + + /* Firmware versions before 0.3 do not support the EUI64_READ command. + * Just use a random address and be done */ + if (atusb->fw_ver_maj == 0 && atusb->fw_ver_min < 3) { + ieee802154_random_extended_addr(&atusb->hw->phy->perm_extended_addr); + return 0; + } + + /* Firmware is new enough so we fetch the address from EEPROM */ + ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), + ATUSB_EUI64_READ, ATUSB_REQ_FROM_DEV, 0, 0, + buffer, IEEE802154_EXTENDED_ADDR_LEN, 1000); + if (ret < 0) + dev_err(&usb_dev->dev, "failed to fetch extended address\n"); + + memcpy(&extended_addr, buffer, IEEE802154_EXTENDED_ADDR_LEN); + /* Check if read address is not empty and the unicast bit is set correctly */ + if (!ieee802154_is_valid_extended_unicast_addr(extended_addr)) { + dev_info(&usb_dev->dev, "no permanent extended address found, random address set\n"); + ieee802154_random_extended_addr(&atusb->hw->phy->perm_extended_addr); + } else { + atusb->hw->phy->perm_extended_addr = extended_addr; + addr = swab64((__force u64)atusb->hw->phy->perm_extended_addr); + dev_info(&usb_dev->dev, "Read permanent extended address %8phC from device\n", + &addr); + } + + return ret; +} + /* ----- Setup ------------------------------------------------------------- */ static int atusb_probe(struct usb_interface *interface, @@ -738,13 +775,14 @@ static int atusb_probe(struct usb_interface *interface, hw->phy->supported.tx_powers = atusb_powers; hw->phy->supported.tx_powers_size = ARRAY_SIZE(atusb_powers); hw->phy->transmit_power = hw->phy->supported.tx_powers[0]; - ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); hw->phy->cca_ed_level = hw->phy->supported.cca_ed_levels[7]; atusb_command(atusb, ATUSB_RF_RESET, 0); atusb_get_and_show_chip(atusb); atusb_get_and_show_revision(atusb); atusb_get_and_show_build(atusb); + atusb_set_extended_addr(atusb); + ret = atusb_get_and_clear_error(atusb); if (ret) { dev_err(&atusb->usb_dev->dev, -- cgit v1.1 From 5d82288b93db3bc7e943464f89079f1360ac3c50 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Mon, 5 Dec 2016 14:47:20 +0100 Subject: ieee802154: atusb: implement .set_frame_retries ops callback From firmware version 0.3 onwards we use the TX_ARET mode allowing for automatic frame retransmissions. To actually make use of this feature we need to implement the callback for setting the frame retries. If the firmware version is to old print a warning and return with invalid value. Signed-off-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/atusb.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 3ed34cc..1253f86 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -546,6 +546,21 @@ atusb_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, u8 retries } static int +atusb_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + struct atusb *atusb = hw->priv; + struct device *dev = &atusb->usb_dev->dev; + + if (atusb->fw_ver_maj == 0 && atusb->fw_ver_min < 3) { + dev_info(dev, "Automatic frame retransmission is only available from " + "firmware version 0.3. Please update if you want this feature."); + return -EINVAL; + } + + return atusb_write_subreg(atusb, SR_MAX_FRAME_RETRIES, retries); +} + +static int atusb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) { struct atusb *atusb = hw->priv; @@ -584,6 +599,7 @@ static const struct ieee802154_ops atusb_ops = { .set_cca_mode = atusb_set_cca_mode, .set_cca_ed_level = atusb_set_cca_ed_level, .set_csma_params = atusb_set_csma_params, + .set_frame_retries = atusb_set_frame_retries, .set_promiscuous_mode = atusb_set_promiscuous_mode, }; @@ -754,7 +770,8 @@ static int atusb_probe(struct usb_interface *interface, hw->parent = &usb_dev->dev; hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | - IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_CSMA_PARAMS; + IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_CSMA_PARAMS | + IEEE802154_HW_FRAME_RETRIES; hw->phy->flags = WPAN_PHY_FLAG_TXPOWER | WPAN_PHY_FLAG_CCA_ED_LEVEL | WPAN_PHY_FLAG_CCA_MODE; -- cgit v1.1 From 0705f7b12b57f94032bc1b6cde05d12a0bafc7fa Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 23 Nov 2016 23:14:01 +0800 Subject: Bluetooth: btmrvl: drop duplicate header slab.h Drop duplicate header slab.h from btmrvl_drv.h. Signed-off-by: Geliang Tang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_drv.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index f742384..fc3caf4 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -32,7 +32,6 @@ #include #include #include -#include #include #define BTM_HEADER_LEN 4 -- cgit v1.1 From a62da6f14db79bd7ea435ab095e998b31b3dbb22 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 8 Dec 2016 08:32:54 +0200 Subject: Bluetooth: SMP: Add support for H7 crypto function and CT2 auth flag Bluetooth 5.0 introduces a new H7 key generation function that's used when both sides of the pairing set the CT2 authentication flag to 1. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 85 ++++++++++++++++++++++++++++++++++++++++++----------- net/bluetooth/smp.h | 1 + 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 43faf2a..fae391f 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -57,7 +57,7 @@ #define SMP_TIMEOUT msecs_to_jiffies(30000) #define AUTH_REQ_MASK(dev) (hci_dev_test_flag(dev, HCI_SC_ENABLED) ? \ - 0x1f : 0x07) + 0x3f : 0x07) #define KEY_DIST_MASK 0x07 /* Maximum message length that can be passed to aes_cmac */ @@ -76,6 +76,7 @@ enum { SMP_FLAG_DHKEY_PENDING, SMP_FLAG_REMOTE_OOB, SMP_FLAG_LOCAL_OOB, + SMP_FLAG_CT2, }; struct smp_dev { @@ -357,6 +358,22 @@ static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16], return err; } +static int smp_h7(struct crypto_shash *tfm_cmac, const u8 w[16], + const u8 salt[16], u8 res[16]) +{ + int err; + + SMP_DBG("w %16phN salt %16phN", w, salt); + + err = aes_cmac(tfm_cmac, salt, w, 16, res); + if (err) + return err; + + SMP_DBG("res %16phN", res); + + return err; +} + /* The following functions map to the legacy SMP crypto functions e, c1, * s1 and ah. */ @@ -1130,20 +1147,31 @@ static void sc_add_ltk(struct smp_chan *smp) static void sc_generate_link_key(struct smp_chan *smp) { - /* These constants are as specified in the core specification. - * In ASCII they spell out to 'tmp1' and 'lebr'. - */ - const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 }; + /* From core spec. Spells out in ASCII as 'lebr'. */ const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c }; smp->link_key = kzalloc(16, GFP_KERNEL); if (!smp->link_key) return; - if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) { - kzfree(smp->link_key); - smp->link_key = NULL; - return; + if (test_bit(SMP_FLAG_CT2, &smp->flags)) { + /* SALT = 0x00000000000000000000000000000000746D7031 */ + const u8 salt[16] = { 0x31, 0x70, 0x6d, 0x74 }; + + if (smp_h7(smp->tfm_cmac, smp->tk, salt, smp->link_key)) { + kzfree(smp->link_key); + smp->link_key = NULL; + return; + } + } else { + /* From core spec. Spells out in ASCII as 'tmp1'. */ + const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 }; + + if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) { + kzfree(smp->link_key); + smp->link_key = NULL; + return; + } } if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) { @@ -1169,10 +1197,7 @@ static void smp_allow_key_dist(struct smp_chan *smp) static void sc_generate_ltk(struct smp_chan *smp) { - /* These constants are as specified in the core specification. - * In ASCII they spell out to 'tmp2' and 'brle'. - */ - const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 }; + /* From core spec. Spells out in ASCII as 'brle'. */ const u8 brle[4] = { 0x65, 0x6c, 0x72, 0x62 }; struct hci_conn *hcon = smp->conn->hcon; struct hci_dev *hdev = hcon->hdev; @@ -1187,8 +1212,19 @@ static void sc_generate_ltk(struct smp_chan *smp) if (key->type == HCI_LK_DEBUG_COMBINATION) set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); - if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk)) - return; + if (test_bit(SMP_FLAG_CT2, &smp->flags)) { + /* SALT = 0x00000000000000000000000000000000746D7032 */ + const u8 salt[16] = { 0x32, 0x70, 0x6d, 0x74 }; + + if (smp_h7(smp->tfm_cmac, key->val, salt, smp->tk)) + return; + } else { + /* From core spec. Spells out in ASCII as 'tmp2'. */ + const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 }; + + if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk)) + return; + } if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk)) return; @@ -1669,6 +1705,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp, if (!rsp) { memset(req, 0, sizeof(*req)); + req->auth_req = SMP_AUTH_CT2; req->init_key_dist = local_dist; req->resp_key_dist = remote_dist; req->max_key_size = conn->hcon->enc_key_size; @@ -1680,6 +1717,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp, memset(rsp, 0, sizeof(*rsp)); + rsp->auth_req = SMP_AUTH_CT2; rsp->max_key_size = conn->hcon->enc_key_size; rsp->init_key_dist = req->init_key_dist & remote_dist; rsp->resp_key_dist = req->resp_key_dist & local_dist; @@ -1744,6 +1782,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) build_bredr_pairing_cmd(smp, req, &rsp); + if (req->auth_req & SMP_AUTH_CT2) + set_bit(SMP_FLAG_CT2, &smp->flags); + key_size = min(req->max_key_size, rsp.max_key_size); if (check_enc_key_size(conn, key_size)) return SMP_ENC_KEY_SIZE; @@ -1761,9 +1802,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) build_pairing_cmd(conn, req, &rsp, auth); - if (rsp.auth_req & SMP_AUTH_SC) + if (rsp.auth_req & SMP_AUTH_SC) { set_bit(SMP_FLAG_SC, &smp->flags); + if (rsp.auth_req & SMP_AUTH_CT2) + set_bit(SMP_FLAG_CT2, &smp->flags); + } + if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) sec_level = BT_SECURITY_MEDIUM; else @@ -1917,6 +1962,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) */ smp->remote_key_dist &= rsp->resp_key_dist; + if ((req->auth_req & SMP_AUTH_CT2) && (auth & SMP_AUTH_CT2)) + set_bit(SMP_FLAG_CT2, &smp->flags); + /* For BR/EDR this means we're done and can start phase 3 */ if (conn->hcon->type == ACL_LINK) { /* Clear bits which are generated but not distributed */ @@ -2312,8 +2360,11 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) authreq = seclevel_to_authreq(sec_level); - if (hci_dev_test_flag(hcon->hdev, HCI_SC_ENABLED)) + if (hci_dev_test_flag(hcon->hdev, HCI_SC_ENABLED)) { authreq |= SMP_AUTH_SC; + if (hci_dev_test_flag(hcon->hdev, HCI_SSP_ENABLED)) + authreq |= SMP_AUTH_CT2; + } /* Require MITM if IO Capability allows or the security level * requires it. diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index ffcc70b..0ff6247 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -57,6 +57,7 @@ struct smp_cmd_pairing { #define SMP_AUTH_MITM 0x04 #define SMP_AUTH_SC 0x08 #define SMP_AUTH_KEYPRESS 0x10 +#define SMP_AUTH_CT2 0x20 #define SMP_CMD_PAIRING_CONFIRM 0x03 struct smp_cmd_pairing_confirm { -- cgit v1.1 From f91c58d68b8638530e0eb2fec76c5b9e429ca39a Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 7 Dec 2016 14:52:53 +0800 Subject: icmp: correct return value of icmp_rcv() Currently, icmp_rcv() always return zero on a packet delivery upcall. To make its behavior more compliant with the way this API should be used, this patch changes this to let it return NET_RX_SUCCESS when the packet is proper handled, and NET_RX_DROP otherwise. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- net/ipv4/icmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 691146a..f79d7a8 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -1047,12 +1047,12 @@ int icmp_rcv(struct sk_buff *skb) if (success) { consume_skb(skb); - return 0; + return NET_RX_SUCCESS; } drop: kfree_skb(skb); - return 0; + return NET_RX_DROP; csum_error: __ICMP_INC_STATS(net, ICMP_MIB_CSUMERRORS); error: -- cgit v1.1 From 1440a3a12ba0a00f9fec7e6a62e49b11e079d381 Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Wed, 7 Dec 2016 17:52:18 +0800 Subject: vhost-vsock: remove unused vq variable Signed-off-by: Peng Tao Reviewed-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- drivers/vhost/vsock.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index e3b30ea..9c3c68b 100644 --- a/drivers/vhost/vsock.c +++ b/drivers/vhost/vsock.c @@ -195,7 +195,6 @@ static int vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt) { struct vhost_vsock *vsock; - struct vhost_virtqueue *vq; int len = pkt->len; /* Find the vhost_vsock according to guest context id */ @@ -205,8 +204,6 @@ vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt) return -ENODEV; } - vq = &vsock->vqs[VSOCK_VQ_RX]; - if (pkt->reply) atomic_inc(&vsock->queued_replies); -- cgit v1.1 From 635abf01918157e61dc9cfdfd70c40bd5957b6a3 Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Wed, 7 Dec 2016 17:52:19 +0800 Subject: vhost: remove unnecessary smp_mb from vhost_work_queue test_and_set_bit() already implies a memory barrier. Signed-off-by: Peng Tao Reviewed-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- drivers/vhost/vhost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index c6f2d89..2663543 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -261,8 +261,8 @@ void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work) if (!test_and_set_bit(VHOST_WORK_QUEUED, &work->flags)) { /* We can only add the work to the list after we're * sure it was not in the list. + * test_and_set_bit() implies a memory barrier. */ - smp_mb(); llist_add(&work->node, &dev->work_list); wake_up_process(dev->worker); } -- cgit v1.1 From 0b801290ea9468a3ed9577281afdb458001ce0d1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 7 Dec 2016 14:14:24 +0300 Subject: drivers: net: xgene: uninitialized variable in xgene_enet_free_pagepool() We never set "slots" in this function. Fixes: a9380b0f7be8 ("drivers: net: xgene: Add support for Jumbo frame") Signed-off-by: Dan Carpenter Acked-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 6c7eea8..884a334 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -635,6 +635,7 @@ static void xgene_enet_free_pagepool(struct xgene_enet_desc_ring *buf_pool, return; dev = ndev_to_dev(buf_pool->ndev); + slots = buf_pool->slots - 1; head = buf_pool->head; for (i = 0; i < 4; i++) { -- cgit v1.1 From d26aac2d8783ab7ec94e07d412d785b3ac183606 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 7 Dec 2016 14:32:17 +0300 Subject: net: mvneta: Indent some statements These two statements were not indented correctly so it's sort of confusing. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 5e5b259..e05e227 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3578,9 +3578,10 @@ static int mvneta_stop(struct net_device *dev) mvneta_stop_dev(pp); mvneta_mdio_remove(pp); - cpuhp_state_remove_instance_nocalls(online_hpstate, &pp->node_online); - cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, - &pp->node_dead); + cpuhp_state_remove_instance_nocalls(online_hpstate, + &pp->node_online); + cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, + &pp->node_dead); on_each_cpu(mvneta_percpu_disable, pp, true); free_percpu_irq(dev->irq, pp->ports); } else { -- cgit v1.1 From faa3ffce78298b2b782297765cffd05f52fed9d4 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 7 Dec 2016 14:03:10 +0200 Subject: net/sched: cls_flower: Add support for matching on flags Add UAPI to provide set of flags for matching, where the flags provided from user-space are mapped to flow-dissector flags. The 1st flag allows to match on whether the packet is an IP fragment and corresponds to the FLOW_DIS_IS_FRAGMENT flag. Signed-off-by: Or Gerlitz Reviewed-by: Paul Blakey Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 7 ++++ net/sched/cls_flower.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 1adc0b6..0ad9f0b 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -458,11 +458,18 @@ enum { TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, /* be16 */ TCA_FLOWER_KEY_ENC_UDP_DST_PORT, /* be16 */ TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, /* be16 */ + + TCA_FLOWER_KEY_FLAGS, /* be32 */ + TCA_FLOWER_KEY_FLAGS_MASK, /* be32 */ __TCA_FLOWER_MAX, }; #define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1) +enum { + TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0), +}; + /* Match-all classifier */ enum { diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 29a9e6d..6c7e1f5 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -386,6 +386,8 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_FLAGS] = { .type = NLA_U32 }, + [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NLA_U32 }, }; static void fl_set_key_val(struct nlattr **tb, @@ -420,6 +422,39 @@ static void fl_set_key_vlan(struct nlattr **tb, } } +static void fl_set_key_flag(u32 flower_key, u32 flower_mask, + u32 *dissector_key, u32 *dissector_mask, + u32 flower_flag_bit, u32 dissector_flag_bit) +{ + if (flower_mask & flower_flag_bit) { + *dissector_mask |= dissector_flag_bit; + if (flower_key & flower_flag_bit) + *dissector_key |= dissector_flag_bit; + } +} + +static void fl_set_key_flags(struct nlattr **tb, + u32 *flags_key, u32 *flags_mask) +{ + u32 key, mask; + + if (!tb[TCA_FLOWER_KEY_FLAGS]) + return; + + key = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS])); + + if (!tb[TCA_FLOWER_KEY_FLAGS_MASK]) + mask = ~0; + else + mask = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS_MASK])); + + *flags_key = 0; + *flags_mask = 0; + + fl_set_key_flag(key, mask, flags_key, flags_mask, + TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); +} + static int fl_set_key(struct net *net, struct nlattr **tb, struct fl_flow_key *key, struct fl_flow_key *mask) { @@ -546,6 +581,8 @@ static int fl_set_key(struct net *net, struct nlattr **tb, &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, sizeof(key->enc_tp.dst)); + fl_set_key_flags(tb, &key->control.flags, &mask->control.flags); + return 0; } @@ -880,6 +917,42 @@ static int fl_dump_key_vlan(struct sk_buff *skb, return 0; } +static void fl_get_key_flag(u32 dissector_key, u32 dissector_mask, + u32 *flower_key, u32 *flower_mask, + u32 flower_flag_bit, u32 dissector_flag_bit) +{ + if (dissector_mask & dissector_flag_bit) { + *flower_mask |= flower_flag_bit; + if (dissector_key & dissector_flag_bit) + *flower_key |= flower_flag_bit; + } +} + +static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) +{ + u32 key, mask; + __be32 _key, _mask; + int err; + + if (!memchr_inv(&flags_mask, 0, sizeof(flags_mask))) + return 0; + + key = 0; + mask = 0; + + fl_get_key_flag(flags_key, flags_mask, &key, &mask, + TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); + + _key = cpu_to_be32(key); + _mask = cpu_to_be32(mask); + + err = nla_put(skb, TCA_FLOWER_KEY_FLAGS, 4, &_key); + if (err) + return err; + + return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask); +} + static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { @@ -1015,6 +1088,9 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, sizeof(key->enc_tp.dst))) goto nla_put_failure; + if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags)) + goto nla_put_failure; + nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags); if (tcf_exts_dump(skb, &f->exts)) -- cgit v1.1 From 3f7d0eb42d593a3326617dac0a247d981b3356d9 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 7 Dec 2016 14:03:11 +0200 Subject: net/mlx5e: Offload TC matching on packets being IP fragments Enable offloading of matching on packets being fragments. Signed-off-by: Or Gerlitz Reviewed-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index f07ef8c..f8829b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -31,6 +31,7 @@ */ #include +#include #include #include #include @@ -363,7 +364,18 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, skb_flow_dissector_target(f->dissector, FLOW_DISSECTOR_KEY_CONTROL, f->key); + + struct flow_dissector_key_control *mask = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_CONTROL, + f->mask); addr_type = key->addr_type; + + if (mask->flags & FLOW_DIS_IS_FRAGMENT) { + MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag, + key->flags & FLOW_DIS_IS_FRAGMENT); + } } if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) { -- cgit v1.1 From da90e38003e2f052d5d6a28df63ac42c9ea87323 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 7 Dec 2016 13:24:44 +0100 Subject: net: axienet: Utilize of_get_mac_address() Do not open code getting the MAC address exclusively from the "local-mac-address" property, but instead use of_get_mac_address() which looks up the MAC address using the 3 typical property names. Also avoid casting away the const qualifier of the return value by making axienet_set_mac_address() take a const void* address. Follows commit b34296a9c047 ("net: ethoc: Utilize of_get_mac_address()"). Cc: Florian Fainelli Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index c9c8a3b..b96e969 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -292,7 +293,8 @@ out: * This function is called to initialize the MAC address of the Axi Ethernet * core. It writes to the UAW0 and UAW1 registers of the core. */ -static void axienet_set_mac_address(struct net_device *ndev, void *address) +static void axienet_set_mac_address(struct net_device *ndev, + const void *address) { struct axienet_local *lp = netdev_priv(ndev); @@ -1456,7 +1458,7 @@ static int axienet_probe(struct platform_device *pdev) struct device_node *np; struct axienet_local *lp; struct net_device *ndev; - u8 mac_addr[6]; + const void *mac_addr; struct resource *ethres, dmares; u32 value; @@ -1567,13 +1569,12 @@ static int axienet_probe(struct platform_device *pdev) } /* Retrieve the MAC address */ - ret = of_property_read_u8_array(pdev->dev.of_node, - "local-mac-address", mac_addr, 6); - if (ret) { + mac_addr = of_get_mac_address(pdev->dev.of_node); + if (!mac_addr) { dev_err(&pdev->dev, "could not find MAC address\n"); goto free_netdev; } - axienet_set_mac_address(ndev, (void *)mac_addr); + axienet_set_mac_address(ndev, mac_addr); lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD; lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD; -- cgit v1.1 From 06205472765437fc3a6cae7c67052f40e08e120e Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 7 Dec 2016 13:25:29 +0100 Subject: net: ll_temac: Utilize of_get_mac_address() Do not open code getting the MAC address exclusively from the "local-mac-address" property, but instead use of_get_mac_address() which looks up the MAC address using the 3 typical property names. Also avoid casting away the const qualifier of the return value by making temac_init_mac_address() take a const void* address. Follows commit b34296a9c047 ("net: ethoc: Utilize of_get_mac_address()"). Cc: Florian Fainelli Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/ll_temac_main.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index bcd7b76..d73da8a 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -332,7 +333,7 @@ static void temac_do_set_mac_address(struct net_device *ndev) mutex_unlock(&lp->indirect_mutex); } -static int temac_init_mac_address(struct net_device *ndev, void *address) +static int temac_init_mac_address(struct net_device *ndev, const void *address) { memcpy(ndev->dev_addr, address, ETH_ALEN); if (!is_valid_ether_addr(ndev->dev_addr)) @@ -982,7 +983,7 @@ static int temac_of_probe(struct platform_device *op) struct net_device *ndev; const void *addr; __be32 *p; - int size, rc = 0; + int rc = 0; /* Init network device structure */ ndev = alloc_etherdev(sizeof(*lp)); @@ -1074,13 +1075,13 @@ static int temac_of_probe(struct platform_device *op) /* Retrieve the MAC address */ - addr = of_get_property(op->dev.of_node, "local-mac-address", &size); - if ((!addr) || (size != 6)) { + addr = of_get_mac_address(op->dev.of_node); + if (!addr) { dev_err(&op->dev, "could not find MAC address\n"); rc = -ENODEV; goto err_iounmap_2; } - temac_init_mac_address(ndev, (void *)addr); + temac_init_mac_address(ndev, addr); rc = temac_mdio_setup(lp, op->dev.of_node); if (rc) -- cgit v1.1 From 6357d15e48dfad6d8cec187bf4f032d1deb715d9 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 13:41:06 +0100 Subject: bindings: net: stmmac: correct note about TSO snps,tso was previously placed under AXI BUS Mode parameters, suggesting that the property should be in the stmmac-axi-config node. TSO (TCP Segmentation Offloading) has nothing to do with AXI BUS Mode parameters, and the parser actually expects it to be in the root node, not in the stmmac-axi-config. Also added a note about snps,tso only being available on GMAC4 and newer. Signed-off-by: Niklas Cassel Acked-by: Alexandre TORGUE Acked-by: Rob Herring Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index 41b49e6..b95ff99 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -1,7 +1,7 @@ * STMicroelectronics 10/100/1000 Ethernet driver (GMAC) Required properties: -- compatible: Should be "snps,dwmac-" "snps,dwmac" +- compatible: Should be "snps,dwmac-", "snps,dwmac" For backwards compatibility: "st,spear600-gmac" is also supported. - reg: Address and length of the register set for the device - interrupt-parent: Should be the phandle for the interrupt controller @@ -50,6 +50,8 @@ Optional properties: - snps,ps-speed: port selection speed that can be passed to the core when PCS is supported. For example, this is used in case of SGMII and MAC2MAC connection. +- snps,tso: this enables the TSO feature otherwise it will be managed by + MAC HW capability register. Only for GMAC4 and newer. - AXI BUS Mode parameters: below the list of all the parameters to program the AXI register inside the DMA module: - snps,lpi_en: enable Low Power Interface @@ -62,8 +64,6 @@ Optional properties: - snps,fb: fixed-burst - snps,mb: mixed-burst - snps,rb: rebuild INCRx Burst - - snps,tso: this enables the TSO feature otherwise it will be managed by - MAC HW capability register. - mdio: with compatible = "snps,dwmac-mdio", create and register mdio bus. Examples: -- cgit v1.1 From 4966a692d055c97f83a17da6035e93806b407473 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 13:41:07 +0100 Subject: net: stmmac: dwmac-generic: add missing compatible strings devicetree binding for stmmac states: - compatible: Should be "snps,dwmac-", "snps,dwmac" For backwards compatibility: "st,spear600-gmac" is also supported. Since dwmac-generic.c calls stmmac_probe_config_dt explicitly, another alternative would have been to remove all compatible strings other than "snps,dwmac" and "st,spear600-gmac" from dwmac-generic.c. However, that would probably do more good than harm, since when trying to figure out what hardware a certain driver supports, you usually look at the compatible strings in the struct of_device_id, and not in some function defined in a completely different file. No functional change intended. Signed-off-by: Niklas Cassel Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c index e6e6c2f..3304095 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c @@ -71,9 +71,12 @@ err_remove_config_dt: static const struct of_device_id dwmac_generic_match[] = { { .compatible = "st,spear600-gmac"}, + { .compatible = "snps,dwmac-3.50a"}, { .compatible = "snps,dwmac-3.610"}, { .compatible = "snps,dwmac-3.70a"}, { .compatible = "snps,dwmac-3.710"}, + { .compatible = "snps,dwmac-4.00"}, + { .compatible = "snps,dwmac-4.10a"}, { .compatible = "snps,dwmac"}, { } }; -- cgit v1.1 From 7cc99fd29b982964e63afa92a7e29db49b69c90e Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 13:41:08 +0100 Subject: net: stmmac: stmmac_platform: use correct setup function for gmac4 devicetree binding for stmmac states: - compatible: Should be "snps,dwmac-", "snps,dwmac" For backwards compatibility: "st,spear600-gmac" is also supported. Previously, when specifying "snps,dwmac-4.10a", "snps,dwmac" as your compatible string, plat_stmmacenet_data would have both has_gmac and has_gmac4 set. This would lead to stmmac_hw_init calling dwmac1000_setup rather than dwmac4_setup, resulting in a non-functional driver. This happened since the check for has_gmac is done before the check for has_gmac4. However, the order should not matter, so it does not make sense to have both set. If something is valid for both, you should do as the stmmac_interrupt does: if (priv->plat->has_gmac || priv->plat->has_gmac4) ... The places where it was obvious that the author actually meant if (has_gmac || has_gmac4) rather than if (has_gmac) has been updated. Signed-off-by: Niklas Cassel Acked-by: Alexandre TORGUE Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 4 ++-- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 6ab7e2b..699ee1d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -263,7 +263,7 @@ static void stmmac_ethtool_getdrvinfo(struct net_device *dev, { struct stmmac_priv *priv = netdev_priv(dev); - if (priv->plat->has_gmac) + if (priv->plat->has_gmac || priv->plat->has_gmac4) strlcpy(info->driver, GMAC_ETHTOOL_NAME, sizeof(info->driver)); else strlcpy(info->driver, MAC100_ETHTOOL_NAME, @@ -446,7 +446,7 @@ static void stmmac_ethtool_gregs(struct net_device *dev, memset(reg_space, 0x0, REG_SPACE_SIZE); - if (!priv->plat->has_gmac) { + if (!(priv->plat->has_gmac || priv->plat->has_gmac4)) { /* MAC registers */ for (i = 0; i < 12; i++) reg_space[i] = readl(priv->ioaddr + (i * 4)); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index e528e71..d3b6f92 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -292,6 +292,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) if (of_device_is_compatible(np, "snps,dwmac-4.00") || of_device_is_compatible(np, "snps,dwmac-4.10a")) { plat->has_gmac4 = 1; + plat->has_gmac = 0; plat->pmt = 1; plat->tso_en = of_property_read_bool(np, "snps,tso"); } -- cgit v1.1 From 972d3876faa8a9195122b2d2bcd3155f904fff37 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 7 Dec 2016 13:48:27 +0100 Subject: flow dissector: ICMP support Allow dissection of ICMP(V6) type and code. This should only occur if a packet is ICMP(V6) and the dissector has FLOW_DISSECTOR_KEY_ICMP set. There are currently no users of FLOW_DISSECTOR_KEY_ICMP. A follow-up patch will allow FLOW_DISSECTOR_KEY_ICMP to be used by the flower classifier. Signed-off-by: Simon Horman Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/flow_dissector.h | 17 +++++++++++++++++ net/core/flow_dissector.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h index c4f3166..d896a33 100644 --- a/include/net/flow_dissector.h +++ b/include/net/flow_dissector.h @@ -104,6 +104,22 @@ struct flow_dissector_key_ports { }; }; +/** + * flow_dissector_key_icmp: + * @ports: type and code of ICMP header + * icmp: ICMP type (high) and code (low) + * type: ICMP type + * code: ICMP code + */ +struct flow_dissector_key_icmp { + union { + __be16 icmp; + struct { + u8 type; + u8 code; + }; + }; +}; /** * struct flow_dissector_key_eth_addrs: @@ -122,6 +138,7 @@ enum flow_dissector_key_id { FLOW_DISSECTOR_KEY_IPV4_ADDRS, /* struct flow_dissector_key_ipv4_addrs */ FLOW_DISSECTOR_KEY_IPV6_ADDRS, /* struct flow_dissector_key_ipv6_addrs */ FLOW_DISSECTOR_KEY_PORTS, /* struct flow_dissector_key_ports */ + FLOW_DISSECTOR_KEY_ICMP, /* struct flow_dissector_key_icmp */ FLOW_DISSECTOR_KEY_ETH_ADDRS, /* struct flow_dissector_key_eth_addrs */ FLOW_DISSECTOR_KEY_TIPC_ADDRS, /* struct flow_dissector_key_tipc_addrs */ FLOW_DISSECTOR_KEY_VLAN, /* struct flow_dissector_key_flow_vlan */ diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 1eb6f94..d6447dc 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -58,6 +58,28 @@ void skb_flow_dissector_init(struct flow_dissector *flow_dissector, EXPORT_SYMBOL(skb_flow_dissector_init); /** + * skb_flow_get_be16 - extract be16 entity + * @skb: sk_buff to extract from + * @poff: offset to extract at + * @data: raw buffer pointer to the packet + * @hlen: packet header length + * + * The function will try to retrieve a be32 entity at + * offset poff + */ +__be16 skb_flow_get_be16(const struct sk_buff *skb, int poff, void *data, + int hlen) +{ + __be16 *u, _u; + + u = __skb_header_pointer(skb, poff, sizeof(_u), data, hlen, &_u); + if (u) + return *u; + + return 0; +} + +/** * __skb_flow_get_ports - extract the upper layer ports and return them * @skb: sk_buff to extract the ports from * @thoff: transport header offset @@ -117,6 +139,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_dissector_key_basic *key_basic; struct flow_dissector_key_addrs *key_addrs; struct flow_dissector_key_ports *key_ports; + struct flow_dissector_key_icmp *key_icmp; struct flow_dissector_key_tags *key_tags; struct flow_dissector_key_vlan *key_vlan; struct flow_dissector_key_keyid *key_keyid; @@ -546,6 +569,14 @@ ip_proto_again: data, hlen); } + if (dissector_uses_key(flow_dissector, + FLOW_DISSECTOR_KEY_ICMP)) { + key_icmp = skb_flow_dissector_target(flow_dissector, + FLOW_DISSECTOR_KEY_ICMP, + target_container); + key_icmp->icmp = skb_flow_get_be16(skb, nhoff, data, hlen); + } + out_good: ret = true; -- cgit v1.1 From 7b684884fbfab33251115fa5054fb821c34b93be Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 7 Dec 2016 13:48:28 +0100 Subject: net/sched: cls_flower: Support matching on ICMP type and code Support matching on ICMP type and code. Example usage: tc qdisc add dev eth0 ingress tc filter add dev eth0 protocol ip parent ffff: flower \ indev eth0 ip_proto icmp type 8 code 0 action drop tc filter add dev eth0 protocol ipv6 parent ffff: flower \ indev eth0 ip_proto icmpv6 type 128 code 0 action drop Signed-off-by: Simon Horman Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 10 +++++++++ net/sched/cls_flower.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 0ad9f0b..cb4bcdc 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -461,6 +461,16 @@ enum { TCA_FLOWER_KEY_FLAGS, /* be32 */ TCA_FLOWER_KEY_FLAGS_MASK, /* be32 */ + + TCA_FLOWER_KEY_ICMPV4_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */ + __TCA_FLOWER_MAX, }; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 6c7e1f5..e040c51 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -39,6 +39,7 @@ struct fl_flow_key { struct flow_dissector_key_ipv6_addrs ipv6; }; struct flow_dissector_key_ports tp; + struct flow_dissector_key_icmp icmp; struct flow_dissector_key_keyid enc_key_id; union { struct flow_dissector_key_ipv4_addrs enc_ipv4; @@ -388,6 +389,14 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_FLAGS] = { .type = NLA_U32 }, [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NLA_U32 }, + [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NLA_U8 }, }; static void fl_set_key_val(struct nlattr **tb, @@ -537,6 +546,26 @@ static int fl_set_key(struct net *net, struct nlattr **tb, fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, sizeof(key->tp.dst)); + } else if (key->basic.n_proto == htons(ETH_P_IP) && + key->basic.ip_proto == IPPROTO_ICMP) { + fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV4_TYPE, + &mask->icmp.type, + TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, + sizeof(key->icmp.type)); + fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE, + &mask->icmp.code, + TCA_FLOWER_KEY_ICMPV4_CODE_MASK, + sizeof(key->icmp.code)); + } else if (key->basic.n_proto == htons(ETH_P_IPV6) && + key->basic.ip_proto == IPPROTO_ICMPV6) { + fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV6_TYPE, + &mask->icmp.type, + TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, + sizeof(key->icmp.type)); + fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE, + &mask->icmp.code, + TCA_FLOWER_KEY_ICMPV4_CODE_MASK, + sizeof(key->icmp.code)); } if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || @@ -649,6 +678,8 @@ static void fl_init_dissector(struct cls_fl_head *head, FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp); FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, + FLOW_DISSECTOR_KEY_ICMP, icmp); + FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, FLOW_DISSECTOR_KEY_VLAN, vlan); FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); @@ -1050,6 +1081,28 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, sizeof(key->tp.dst)))) goto nla_put_failure; + else if (key->basic.n_proto == htons(ETH_P_IP) && + key->basic.ip_proto == IPPROTO_ICMP && + (fl_dump_key_val(skb, &key->icmp.type, + TCA_FLOWER_KEY_ICMPV4_TYPE, &mask->icmp.type, + TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, + sizeof(key->icmp.type)) || + fl_dump_key_val(skb, &key->icmp.code, + TCA_FLOWER_KEY_ICMPV4_CODE, &mask->icmp.code, + TCA_FLOWER_KEY_ICMPV4_CODE_MASK, + sizeof(key->icmp.code)))) + goto nla_put_failure; + else if (key->basic.n_proto == htons(ETH_P_IPV6) && + key->basic.ip_proto == IPPROTO_ICMPV6 && + (fl_dump_key_val(skb, &key->icmp.type, + TCA_FLOWER_KEY_ICMPV6_TYPE, &mask->icmp.type, + TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, + sizeof(key->icmp.type)) || + fl_dump_key_val(skb, &key->icmp.code, + TCA_FLOWER_KEY_ICMPV6_CODE, &mask->icmp.code, + TCA_FLOWER_KEY_ICMPV6_CODE_MASK, + sizeof(key->icmp.code)))) + goto nla_put_failure; if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && (fl_dump_key_val(skb, &key->enc_ipv4.src, -- cgit v1.1 From 3f0dd6b79c014231e671f754a499aa017c2b955c Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 7 Dec 2016 14:43:30 +0100 Subject: net: ethernet: slicoss: use module_pci_driver() Use module_pci_driver() to get rid of some boilerplate code. Signed-off-by: Tobias Klauser Acked-by: Lino Sanfilippo Signed-off-by: David S. Miller --- drivers/net/ethernet/alacritech/slicoss.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/ethernet/alacritech/slicoss.c b/drivers/net/ethernet/alacritech/slicoss.c index e77ecd5..b9fbd01 100644 --- a/drivers/net/ethernet/alacritech/slicoss.c +++ b/drivers/net/ethernet/alacritech/slicoss.c @@ -1863,18 +1863,7 @@ static struct pci_driver slic_driver = { .remove = slic_remove, }; -static int __init slic_init_module(void) -{ - return pci_register_driver(&slic_driver); -} - -static void __exit slic_cleanup_module(void) -{ - pci_unregister_driver(&slic_driver); -} - -module_init(slic_init_module); -module_exit(slic_cleanup_module); +module_pci_driver(slic_driver); MODULE_DESCRIPTION("Alacritech non-accelerated SLIC driver"); MODULE_AUTHOR("Lino Sanfilippo "); -- cgit v1.1 From 89ab75bf7253271c0ca805931105a298bc22bfdf Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 15:20:03 +0100 Subject: net: stmmac: return error if no DMA configuration is found All drivers except pci glue layer calls stmmac_probe_config_dt. stmmac_probe_config_dt does a kzalloc dma_cfg. pci glue layer does kzalloc dma_cfg explicitly, so all current drivers does a kzalloc dma_cfg. Return an error if no DMA configuration is found, that way we can assume that the DMA configuration always exists. Signed-off-by: Niklas Cassel Acked-by: Alexandre Torgue Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 982c952..1436680 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1578,16 +1578,12 @@ static void stmmac_check_ether_addr(struct stmmac_priv *priv) */ static int stmmac_init_dma_engine(struct stmmac_priv *priv) { - int pbl = DEFAULT_DMA_PBL, fixed_burst = 0, aal = 0; - int mixed_burst = 0; int atds = 0; int ret = 0; - if (priv->plat->dma_cfg) { - pbl = priv->plat->dma_cfg->pbl; - fixed_burst = priv->plat->dma_cfg->fixed_burst; - mixed_burst = priv->plat->dma_cfg->mixed_burst; - aal = priv->plat->dma_cfg->aal; + if (!priv->plat->dma_cfg) { + dev_err(priv->device, "DMA configuration not found\n"); + return -EINVAL; } if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE)) @@ -1599,8 +1595,12 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) return ret; } - priv->hw->dma->init(priv->ioaddr, pbl, fixed_burst, mixed_burst, - aal, priv->dma_tx_phy, priv->dma_rx_phy, atds); + priv->hw->dma->init(priv->ioaddr, + priv->plat->dma_cfg->pbl, + priv->plat->dma_cfg->fixed_burst, + priv->plat->dma_cfg->mixed_burst, + priv->plat->dma_cfg->aal, + priv->dma_tx_phy, priv->dma_rx_phy, atds); if (priv->synopsys_id >= DWMAC_CORE_4_00) { priv->rx_tail_addr = priv->dma_rx_phy + -- cgit v1.1 From 50ca903afc2a4ccf7efd25744577fecb0ce6fb49 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 15:20:04 +0100 Subject: net: stmmac: simplify the common DMA init API Use struct stmmac_dma_cfg *dma_cfg as an argument rather than using all the struct members as individual arguments. Signed-off-by: Niklas Cassel Acked-by: Alexandre Torgue Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++-- drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 13 +++++++------ drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 7 ++++--- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 14 ++++++++------ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +----- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 3ced2e1..b13a144 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -412,8 +412,8 @@ extern const struct stmmac_desc_ops ndesc_ops; struct stmmac_dma_ops { /* DMA core initialization */ int (*reset)(void __iomem *ioaddr); - void (*init)(void __iomem *ioaddr, int pbl, int fb, int mb, - int aal, u32 dma_tx, u32 dma_rx, int atds); + void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, + u32 dma_tx, u32 dma_rx, int atds); /* Configure the AXI Bus Mode Register */ void (*axi)(void __iomem *ioaddr, struct stmmac_axi *axi); /* Dump DMA registers */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index f353852..318ae9f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -84,8 +84,9 @@ static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi) writel(value, ioaddr + DMA_AXI_BUS_MODE); } -static void dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, - int aal, u32 dma_tx, u32 dma_rx, int atds) +static void dwmac1000_dma_init(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 dma_tx, u32 dma_rx, int atds) { u32 value = readl(ioaddr + DMA_BUS_MODE); @@ -101,20 +102,20 @@ static void dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, */ value |= DMA_BUS_MODE_MAXPBL; value &= ~DMA_BUS_MODE_PBL_MASK; - value |= (pbl << DMA_BUS_MODE_PBL_SHIFT); + value |= (dma_cfg->pbl << DMA_BUS_MODE_PBL_SHIFT); /* Set the Fixed burst mode */ - if (fb) + if (dma_cfg->fixed_burst) value |= DMA_BUS_MODE_FB; /* Mixed Burst has no effect when fb is set */ - if (mb) + if (dma_cfg->mixed_burst) value |= DMA_BUS_MODE_MB; if (atds) value |= DMA_BUS_MODE_ATDS; - if (aal) + if (dma_cfg->aal) value |= DMA_BUS_MODE_AAL; writel(value, ioaddr + DMA_BUS_MODE); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index 61f54c9..e5664da 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -32,11 +32,12 @@ #include "dwmac100.h" #include "dwmac_dma.h" -static void dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, - int aal, u32 dma_tx, u32 dma_rx, int atds) +static void dwmac100_dma_init(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 dma_tx, u32 dma_rx, int atds) { /* Enable Application Access by writing to DMA CSR0 */ - writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT), + writel(DMA_BUS_MODE_DEFAULT | (dma_cfg->pbl << DMA_BUS_MODE_PBL_SHIFT), ioaddr + DMA_BUS_MODE); /* Mask interrupts by writing to CSR7 */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index e81b6e5..7d82a34 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -99,27 +99,29 @@ static void dwmac4_dma_init_channel(void __iomem *ioaddr, int pbl, writel(dma_rx_phy, ioaddr + DMA_CHAN_RX_BASE_ADDR(channel)); } -static void dwmac4_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb, - int aal, u32 dma_tx, u32 dma_rx, int atds) +static void dwmac4_dma_init(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 dma_tx, u32 dma_rx, int atds) { u32 value = readl(ioaddr + DMA_SYS_BUS_MODE); int i; /* Set the Fixed burst mode */ - if (fb) + if (dma_cfg->fixed_burst) value |= DMA_SYS_BUS_FB; /* Mixed Burst has no effect when fb is set */ - if (mb) + if (dma_cfg->mixed_burst) value |= DMA_SYS_BUS_MB; - if (aal) + if (dma_cfg->aal) value |= DMA_SYS_BUS_AAL; writel(value, ioaddr + DMA_SYS_BUS_MODE); for (i = 0; i < DMA_CHANNEL_NB_MAX; i++) - dwmac4_dma_init_channel(ioaddr, pbl, dma_tx, dma_rx, i); + dwmac4_dma_init_channel(ioaddr, dma_cfg->pbl, + dma_tx, dma_rx, i); } static void _dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 channel) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 1436680..b1e42dd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1595,11 +1595,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) return ret; } - priv->hw->dma->init(priv->ioaddr, - priv->plat->dma_cfg->pbl, - priv->plat->dma_cfg->fixed_burst, - priv->plat->dma_cfg->mixed_burst, - priv->plat->dma_cfg->aal, + priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, priv->dma_tx_phy, priv->dma_rx_phy, atds); if (priv->synopsys_id >= DWMAC_CORE_4_00) { -- cgit v1.1 From a332e2fa56343ca5f11f05f90e53d0d471384e0e Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 15:20:05 +0100 Subject: net: stmmac: stmmac_platform: fix parsing of DT binding commit 64c3b252e9fc ("net: stmmac: fixed the pbl setting with DT") changed the parsing of the DT binding. Before 64c3b252e9fc, snps,fixed-burst and snps,mixed-burst were parsed regardless if the property snps,pbl existed or not. After the commit, fixed burst and mixed burst are only parsed if snps,pbl exists. Now when snps,aal has been added, it too is only parsed if snps,pbl exists. Since the DT binding does not specify that fixed burst, mixed burst or aal depend on snps,pbl being specified, undo changes introduced by 64c3b252e9fc. The issue commit 64c3b252e9fc ("net: stmmac: fixed the pbl setting with DT") tries to address is solved in another way: The databook specifies that all values other than 1, 2, 4, 8, 16, or 32 results in undefined behavior, so snps,pbl = <0> is invalid. If pbl is 0 after parsing, set pbl to DEFAULT_DMA_PBL. This handles the case where the property is omitted, and also handles the case where the property is specified without any data. Signed-off-by: Niklas Cassel Acked-by: Alexandre Torgue Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +-- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 29 +++++++++++----------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b1e42dd..b518812 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1581,8 +1581,8 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) int atds = 0; int ret = 0; - if (!priv->plat->dma_cfg) { - dev_err(priv->device, "DMA configuration not found\n"); + if (!priv->plat->dma_cfg || !priv->plat->dma_cfg->pbl) { + dev_err(priv->device, "Invalid DMA configuration\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index d3b6f92..81800f2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -304,21 +304,22 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) plat->force_sf_dma_mode = 1; } - if (of_find_property(np, "snps,pbl", NULL)) { - dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), - GFP_KERNEL); - if (!dma_cfg) { - stmmac_remove_config_dt(pdev, plat); - return ERR_PTR(-ENOMEM); - } - plat->dma_cfg = dma_cfg; - of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); - dma_cfg->aal = of_property_read_bool(np, "snps,aal"); - dma_cfg->fixed_burst = - of_property_read_bool(np, "snps,fixed-burst"); - dma_cfg->mixed_burst = - of_property_read_bool(np, "snps,mixed-burst"); + dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), + GFP_KERNEL); + if (!dma_cfg) { + stmmac_remove_config_dt(pdev, plat); + return ERR_PTR(-ENOMEM); } + plat->dma_cfg = dma_cfg; + + of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); + if (!dma_cfg->pbl) + dma_cfg->pbl = DEFAULT_DMA_PBL; + + dma_cfg->aal = of_property_read_bool(np, "snps,aal"); + dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); + dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst"); + plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode"); if (plat->force_thresh_dma_mode) { plat->force_sf_dma_mode = 0; -- cgit v1.1 From 825658a27361c67c14c10bfa0359fc23d76a42b8 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 15:20:06 +0100 Subject: net: stmmac: dwmac1000: fix define DMA_BUS_MODE_RPBL_MASK DMA_BUS_MODE_RPBL_MASK is really 6 bits, just like DMA_BUS_MODE_PBL_MASK. Signed-off-by: Niklas Cassel Acked-by: Alexandre Torgue Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index ff3e5ab..52b9407 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -225,7 +225,7 @@ enum rx_tx_priority_ratio { #define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */ #define DMA_BUS_MODE_MB 0x04000000 /* Mixed burst */ -#define DMA_BUS_MODE_RPBL_MASK 0x003e0000 /* Rx-Programmable Burst Len */ +#define DMA_BUS_MODE_RPBL_MASK 0x007e0000 /* Rx-Programmable Burst Len */ #define DMA_BUS_MODE_RPBL_SHIFT 17 #define DMA_BUS_MODE_USP 0x00800000 #define DMA_BUS_MODE_MAXPBL 0x01000000 -- cgit v1.1 From 89caaa2d80b7bf9bd8632cd3137254f8c685e5db Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 15:20:07 +0100 Subject: net: stmmac: add support for independent DMA pbl for tx/rx GMAC and newer supports independent programmable burst lengths for DMA tx/rx. Add new optional devicetree properties representing this. To be backwards compatible, snps,pbl will still be valid, but snps,txpbl/snps,rxpbl will override the value in snps,pbl if set. If the IP is synthesized to use the AXI interface, there is a register and a matching DT property inside the optional stmmac-axi-config DT node for controlling burst lengths, named snps,blen. However, using this register, it is not possible to control tx and rx independently. Also, this register is not available if the IP was synthesized with, e.g., the AHB interface. Signed-off-by: Niklas Cassel Acked-by: Alexandre Torgue Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 6 +++++- Documentation/networking/stmmac.txt | 19 +++++++++++++------ drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 12 ++++++------ drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 12 +++++++----- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 ++ include/linux/stmmac.h | 2 ++ 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index b95ff99..8080038 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -34,7 +34,11 @@ Optional properties: platforms. - tx-fifo-depth: See ethernet.txt file in the same directory - rx-fifo-depth: See ethernet.txt file in the same directory -- snps,pbl Programmable Burst Length +- snps,pbl Programmable Burst Length (tx and rx) +- snps,txpbl Tx Programmable Burst Length. Only for GMAC and newer. + If set, DMA tx will use this value rather than snps,pbl. +- snps,rxpbl Rx Programmable Burst Length. Only for GMAC and newer. + If set, DMA rx will use this value rather than snps,pbl. - snps,aal Address-Aligned Beats - snps,fixed-burst Program the DMA to use the fixed burst mode - snps,mixed-burst Program the DMA to use the mixed burst mode diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index 014f4f7..6add573 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -153,7 +153,8 @@ Where: o pbl: the Programmable Burst Length is maximum number of beats to be transferred in one DMA transaction. GMAC also enables the 4xPBL by default. - o fixed_burst/mixed_burst/burst_len + o txpbl/rxpbl: GMAC and newer supports independent DMA pbl for tx/rx. + o fixed_burst/mixed_burst/aal o clk_csr: fixed CSR Clock range selection. o has_gmac: uses the GMAC core. o enh_desc: if sets the MAC will use the enhanced descriptor structure. @@ -205,16 +206,22 @@ tuned according to the HW capabilities. struct stmmac_dma_cfg { int pbl; + int txpbl; + int rxpbl; int fixed_burst; - int burst_len_supported; + int mixed_burst; + bool aal; }; Where: - o pbl: Programmable Burst Length + o pbl: Programmable Burst Length (tx and rx) + o txpbl: Transmit Programmable Burst Length. Only for GMAC and newer. + If set, DMA tx will use this value rather than pbl. + o rxpbl: Receive Programmable Burst Length. Only for GMAC and newer. + If set, DMA rx will use this value rather than pbl. o fixed_burst: program the DMA to use the fixed burst mode - o burst_len: this is the value we put in the register - supported values are provided as macros in - linux/stmmac.h header file. + o mixed_burst: program the DMA to use the mixed burst mode + o aal: Address-Aligned Beats --- diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 318ae9f..99b8040 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -89,20 +89,20 @@ static void dwmac1000_dma_init(void __iomem *ioaddr, u32 dma_tx, u32 dma_rx, int atds) { u32 value = readl(ioaddr + DMA_BUS_MODE); + int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; + int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; /* * Set the DMA PBL (Programmable Burst Length) mode. * * Note: before stmmac core 3.50 this mode bit was 4xPBL, and * post 3.5 mode bit acts as 8*PBL. - * - * This configuration doesn't take care about the Separate PBL - * so only the bits: 13-8 are programmed with the PBL passed from the - * platform. */ value |= DMA_BUS_MODE_MAXPBL; - value &= ~DMA_BUS_MODE_PBL_MASK; - value |= (dma_cfg->pbl << DMA_BUS_MODE_PBL_SHIFT); + value |= DMA_BUS_MODE_USP; + value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK); + value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT); + value |= (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); /* Set the Fixed burst mode */ if (dma_cfg->fixed_burst) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 7d82a34..2c3b209 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -71,11 +71,14 @@ static void dwmac4_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi) writel(value, ioaddr + DMA_SYS_BUS_MODE); } -static void dwmac4_dma_init_channel(void __iomem *ioaddr, int pbl, +static void dwmac4_dma_init_channel(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, u32 dma_tx_phy, u32 dma_rx_phy, u32 channel) { u32 value; + int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; + int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; /* set PBL for each channels. Currently we affect same configuration * on each channel @@ -85,11 +88,11 @@ static void dwmac4_dma_init_channel(void __iomem *ioaddr, int pbl, writel(value, ioaddr + DMA_CHAN_CONTROL(channel)); value = readl(ioaddr + DMA_CHAN_TX_CONTROL(channel)); - value = value | (pbl << DMA_BUS_MODE_PBL_SHIFT); + value = value | (txpbl << DMA_BUS_MODE_PBL_SHIFT); writel(value, ioaddr + DMA_CHAN_TX_CONTROL(channel)); value = readl(ioaddr + DMA_CHAN_RX_CONTROL(channel)); - value = value | (pbl << DMA_BUS_MODE_RPBL_SHIFT); + value = value | (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); writel(value, ioaddr + DMA_CHAN_RX_CONTROL(channel)); /* Mask interrupts by writing to CSR7 */ @@ -120,8 +123,7 @@ static void dwmac4_dma_init(void __iomem *ioaddr, writel(value, ioaddr + DMA_SYS_BUS_MODE); for (i = 0; i < DMA_CHANNEL_NB_MAX; i++) - dwmac4_dma_init_channel(ioaddr, dma_cfg->pbl, - dma_tx, dma_rx, i); + dwmac4_dma_init_channel(ioaddr, dma_cfg, dma_tx, dma_rx, i); } static void _dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 channel) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 81800f2..96afe05 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -315,6 +315,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); if (!dma_cfg->pbl) dma_cfg->pbl = DEFAULT_DMA_PBL; + of_property_read_u32(np, "snps,txpbl", &dma_cfg->txpbl); + of_property_read_u32(np, "snps,rxpbl", &dma_cfg->rxpbl); dma_cfg->aal = of_property_read_bool(np, "snps,aal"); dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 3537fb3..e6d7a59 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -88,6 +88,8 @@ struct stmmac_mdio_bus_data { struct stmmac_dma_cfg { int pbl; + int txpbl; + int rxpbl; int fixed_burst; int mixed_burst; bool aal; -- cgit v1.1 From 4022d039a315951e59d95d22e79198d861ce4490 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 7 Dec 2016 15:20:08 +0100 Subject: net: smmac: allow configuring lower pbl values The driver currently always sets the PBLx8/PBLx4 bit, which means that the pbl values configured via the pbl/txpbl/rxpbl DT properties are always multiplied by 8/4 in the hardware. In order to allow the DT to configure lower pbl values, while at the same time not changing behavior of any existing device trees using the pbl/txpbl/rxpbl settings, add a property to disable the multiplication of the pbl by 8/4 in the hardware. Suggested-by: Rabin Vincent Signed-off-by: Niklas Cassel Acked-by: Alexandre Torgue Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 2 ++ Documentation/networking/stmmac.txt | 5 ++++- drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 2 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 1 + include/linux/stmmac.h | 1 + 7 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index 8080038..128da75 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -39,6 +39,8 @@ Optional properties: If set, DMA tx will use this value rather than snps,pbl. - snps,rxpbl Rx Programmable Burst Length. Only for GMAC and newer. If set, DMA rx will use this value rather than snps,pbl. +- snps,no-pbl-x8 Don't multiply the pbl/txpbl/rxpbl values by 8. + For core rev < 3.50, don't multiply the values by 4. - snps,aal Address-Aligned Beats - snps,fixed-burst Program the DMA to use the fixed burst mode - snps,mixed-burst Program the DMA to use the mixed burst mode diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index 6add573..2bb0707 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -152,8 +152,9 @@ Where: o dma_cfg: internal DMA parameters o pbl: the Programmable Burst Length is maximum number of beats to be transferred in one DMA transaction. - GMAC also enables the 4xPBL by default. + GMAC also enables the 4xPBL by default. (8xPBL for GMAC 3.50 and newer) o txpbl/rxpbl: GMAC and newer supports independent DMA pbl for tx/rx. + o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default. o fixed_burst/mixed_burst/aal o clk_csr: fixed CSR Clock range selection. o has_gmac: uses the GMAC core. @@ -208,6 +209,7 @@ struct stmmac_dma_cfg { int pbl; int txpbl; int rxpbl; + bool pblx8; int fixed_burst; int mixed_burst; bool aal; @@ -219,6 +221,7 @@ Where: If set, DMA tx will use this value rather than pbl. o rxpbl: Receive Programmable Burst Length. Only for GMAC and newer. If set, DMA rx will use this value rather than pbl. + o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default. o fixed_burst: program the DMA to use the fixed burst mode o mixed_burst: program the DMA to use the mixed burst mode o aal: Address-Aligned Beats diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 99b8040..612d3aa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -98,7 +98,8 @@ static void dwmac1000_dma_init(void __iomem *ioaddr, * Note: before stmmac core 3.50 this mode bit was 4xPBL, and * post 3.5 mode bit acts as 8*PBL. */ - value |= DMA_BUS_MODE_MAXPBL; + if (dma_cfg->pblx8) + value |= DMA_BUS_MODE_MAXPBL; value |= DMA_BUS_MODE_USP; value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK); value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 2c3b209..8196ab5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -84,7 +84,8 @@ static void dwmac4_dma_init_channel(void __iomem *ioaddr, * on each channel */ value = readl(ioaddr + DMA_CHAN_CONTROL(channel)); - value = value | DMA_BUS_MODE_PBL; + if (dma_cfg->pblx8) + value = value | DMA_BUS_MODE_PBL; writel(value, ioaddr + DMA_CHAN_CONTROL(channel)); value = readl(ioaddr + DMA_CHAN_TX_CONTROL(channel)); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index 56c8a23..a283177 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -81,6 +81,7 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat) plat->mdio_bus_data->phy_mask = 0; plat->dma_cfg->pbl = 32; + plat->dma_cfg->pblx8 = true; /* TODO: AXI */ /* Set default value for multicast hash bins */ @@ -115,6 +116,7 @@ static int quark_default_data(struct plat_stmmacenet_data *plat, plat->mdio_bus_data->phy_mask = 0; plat->dma_cfg->pbl = 16; + plat->dma_cfg->pblx8 = true; plat->dma_cfg->fixed_burst = 1; /* AXI (TODO) */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 96afe05..082cd48 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -317,6 +317,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) dma_cfg->pbl = DEFAULT_DMA_PBL; of_property_read_u32(np, "snps,txpbl", &dma_cfg->txpbl); of_property_read_u32(np, "snps,rxpbl", &dma_cfg->rxpbl); + dma_cfg->pblx8 = !of_property_read_bool(np, "snps,no-pbl-x8"); dma_cfg->aal = of_property_read_bool(np, "snps,aal"); dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index e6d7a59..266dab9 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -90,6 +90,7 @@ struct stmmac_dma_cfg { int pbl; int txpbl; int rxpbl; + bool pblx8; int fixed_burst; int mixed_burst; bool aal; -- cgit v1.1 From c9fba3ed3a43709789a7a2a8b15b6595d3faf887 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 7 Dec 2016 23:02:09 +0800 Subject: macsec: remove first zero and add attribute name in comments Remove first zero for add, and use full attribute name in comments. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- drivers/net/macsec.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index cc00eb0..f83cf66 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3346,19 +3346,18 @@ static struct net *macsec_get_link_net(const struct net_device *dev) static size_t macsec_get_size(const struct net_device *dev) { - return 0 + - nla_total_size_64bit(8) + /* SCI */ - nla_total_size(1) + /* ICV_LEN */ - nla_total_size_64bit(8) + /* CIPHER_SUITE */ - nla_total_size(4) + /* WINDOW */ - nla_total_size(1) + /* ENCODING_SA */ - nla_total_size(1) + /* ENCRYPT */ - nla_total_size(1) + /* PROTECT */ - nla_total_size(1) + /* INC_SCI */ - nla_total_size(1) + /* ES */ - nla_total_size(1) + /* SCB */ - nla_total_size(1) + /* REPLAY_PROTECT */ - nla_total_size(1) + /* VALIDATION */ + return nla_total_size_64bit(8) + /* IFLA_MACSEC_SCI */ + nla_total_size(1) + /* IFLA_MACSEC_ICV_LEN */ + nla_total_size_64bit(8) + /* IFLA_MACSEC_CIPHER_SUITE */ + nla_total_size(4) + /* IFLA_MACSEC_WINDOW */ + nla_total_size(1) + /* IFLA_MACSEC_ENCODING_SA */ + nla_total_size(1) + /* IFLA_MACSEC_ENCRYPT */ + nla_total_size(1) + /* IFLA_MACSEC_PROTECT */ + nla_total_size(1) + /* IFLA_MACSEC_INC_SCI */ + nla_total_size(1) + /* IFLA_MACSEC_ES */ + nla_total_size(1) + /* IFLA_MACSEC_SCB */ + nla_total_size(1) + /* IFLA_MACSEC_REPLAY_PROTECT */ + nla_total_size(1) + /* IFLA_MACSEC_VALIDATION */ 0; } -- cgit v1.1 From 13bfff25c081f4e060af761c4082b5a96f756810 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Dec 2016 08:29:10 -0800 Subject: net: rfs: add a jump label RFS is not commonly used, so add a jump label to avoid some conditionals in fast path. Signed-off-by: Eric Dumazet Cc: Paolo Abeni Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/sock.h | 25 ++++++++++++++----------- net/core/dev.c | 2 ++ net/core/sysctl_net_core.c | 5 ++++- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1ff5ea6..994f742 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -192,6 +192,7 @@ struct net_device_stats { #ifdef CONFIG_RPS #include extern struct static_key rps_needed; +extern struct static_key rfs_needed; #endif struct neighbour; diff --git a/include/net/sock.h b/include/net/sock.h index 1749e38..2729e77 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -913,17 +913,20 @@ static inline void sock_rps_record_flow_hash(__u32 hash) static inline void sock_rps_record_flow(const struct sock *sk) { #ifdef CONFIG_RPS - /* Reading sk->sk_rxhash might incur an expensive cache line miss. - * - * TCP_ESTABLISHED does cover almost all states where RFS - * might be useful, and is cheaper [1] than testing : - * IPv4: inet_sk(sk)->inet_daddr - * IPv6: ipv6_addr_any(&sk->sk_v6_daddr) - * OR an additional socket flag - * [1] : sk_state and sk_prot are in the same cache line. - */ - if (sk->sk_state == TCP_ESTABLISHED) - sock_rps_record_flow_hash(sk->sk_rxhash); + if (static_key_false(&rfs_needed)) { + /* Reading sk->sk_rxhash might incur an expensive cache line + * miss. + * + * TCP_ESTABLISHED does cover almost all states where RFS + * might be useful, and is cheaper [1] than testing : + * IPv4: inet_sk(sk)->inet_daddr + * IPv6: ipv6_addr_any(&sk->sk_v6_daddr) + * OR an additional socket flag + * [1] : sk_state and sk_prot are in the same cache line. + */ + if (sk->sk_state == TCP_ESTABLISHED) + sock_rps_record_flow_hash(sk->sk_rxhash); + } #endif } diff --git a/net/core/dev.c b/net/core/dev.c index bffb525..1d33ce0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3447,6 +3447,8 @@ EXPORT_SYMBOL(rps_cpu_mask); struct static_key rps_needed __read_mostly; EXPORT_SYMBOL(rps_needed); +struct static_key rfs_needed __read_mostly; +EXPORT_SYMBOL(rfs_needed); static struct rps_dev_flow * set_rps_cpu(struct net_device *dev, struct sk_buff *skb, diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 0df2aa6..2a46e40 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -79,10 +79,13 @@ static int rps_sock_flow_sysctl(struct ctl_table *table, int write, if (sock_table != orig_sock_table) { rcu_assign_pointer(rps_sock_flow_table, sock_table); - if (sock_table) + if (sock_table) { static_key_slow_inc(&rps_needed); + static_key_slow_inc(&rfs_needed); + } if (orig_sock_table) { static_key_slow_dec(&rps_needed); + static_key_slow_dec(&rfs_needed); synchronize_rcu(); vfree(orig_sock_table); } -- cgit v1.1 From 846b46873eeb3baf40f7e6d8fe8f98aec95e7727 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Wed, 7 Dec 2016 08:54:31 -0800 Subject: liquidio CN23XX: VF offload features Adds support for VF link initialization and offload features. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 562 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/octeon_device.c | 3 + 2 files changed, 565 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index e6321f3..f05a7c3 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -22,7 +22,9 @@ #include "octeon_iq.h" #include "response_manager.h" #include "octeon_device.h" +#include "octeon_nic.h" #include "octeon_main.h" +#include "octeon_network.h" #include "cn23xx_vf_device.h" MODULE_AUTHOR("Cavium Networks, "); @@ -30,6 +32,33 @@ MODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Virtual Function MODULE_LICENSE("GPL"); MODULE_VERSION(LIQUIDIO_VERSION); +static int debug = -1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "NETIF_MSG debug bits"); + +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) + +#define LIO_IFSTATE_REGISTERED 0x02 +#define LIO_IFSTATE_RUNNING 0x04 + +struct liquidio_if_cfg_context { + int octeon_id; + + wait_queue_head_t wc; + + int cond; +}; + +struct liquidio_if_cfg_resp { + u64 rh; + struct liquidio_if_cfg_info cfg_info; + u64 status; +}; + +#define OCTNIC_GSO_MAX_HEADER_SIZE 128 +#define OCTNIC_GSO_MAX_SIZE \ + (CN23XX_DEFAULT_INPUT_JABBER - OCTNIC_GSO_MAX_HEADER_SIZE) + struct octeon_device_priv { /* Tasklet structures for this device. */ struct tasklet_struct droq_tasklet; @@ -40,6 +69,7 @@ static int liquidio_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void liquidio_vf_remove(struct pci_dev *pdev); static int octeon_device_init(struct octeon_device *oct); +static int liquidio_stop(struct net_device *netdev); static int lio_wait_for_oq_pkts(struct octeon_device *oct) { @@ -113,6 +143,26 @@ static struct pci_driver liquidio_vf_pci_driver = { .remove = liquidio_vf_remove, }; +/** + * \brief set interface state + * @param lio per-network private data + * @param state_flag flag state to set + */ +static void ifstate_set(struct lio *lio, int state_flag) +{ + atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) | state_flag)); +} + +/** + * \brief clear interface state + * @param lio per-network private data + * @param state_flag flag state to clear + */ +static void ifstate_reset(struct lio *lio, int state_flag) +{ + atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag))); +} + static int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) { @@ -316,6 +366,7 @@ static void octeon_destroy_resources(struct octeon_device *oct) /* No more instructions will be forwarded. */ atomic_set(&oct->status, OCT_DEV_IN_RESET); + oct->app_mode = CVM_DRV_INVALID_APP; dev_dbg(&oct->pci_dev->dev, "Device state is now %s\n", lio_get_state_string(&oct->status)); @@ -420,6 +471,63 @@ static void octeon_destroy_resources(struct octeon_device *oct) } /** + * \brief Destroy NIC device interface + * @param oct octeon device + * @param ifidx which interface to destroy + * + * Cleanup associated with each interface for an Octeon device when NIC + * module is being unloaded or if initialization fails during load. + */ +static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) +{ + struct net_device *netdev = oct->props[ifidx].netdev; + struct lio *lio; + + if (!netdev) { + dev_err(&oct->pci_dev->dev, "%s No netdevice ptr for index %d\n", + __func__, ifidx); + return; + } + + lio = GET_LIO(netdev); + + dev_dbg(&oct->pci_dev->dev, "NIC device cleanup\n"); + + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) + liquidio_stop(netdev); + + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) + unregister_netdev(netdev); + + free_netdev(netdev); + + oct->props[ifidx].gmxport = -1; + + oct->props[ifidx].netdev = NULL; +} + +/** + * \brief Stop complete NIC functionality + * @param oct octeon device + */ +static int liquidio_stop_nic_module(struct octeon_device *oct) +{ + int i; + + dev_dbg(&oct->pci_dev->dev, "Stopping network interfaces\n"); + if (!oct->ifcount) { + dev_err(&oct->pci_dev->dev, "Init for Octeon was not completed\n"); + return 1; + } + + for (i = 0; i < oct->ifcount; i++) + liquidio_destroy_nic_device(oct, i); + + dev_dbg(&oct->pci_dev->dev, "Network interfaces stopped\n"); + return 0; +} + +/** * \brief Cleans up resources at unload time * @param pdev PCI device structure */ @@ -429,6 +537,9 @@ static void liquidio_vf_remove(struct pci_dev *pdev) dev_dbg(&oct_dev->pci_dev->dev, "Stopping device\n"); + if (oct_dev->app_mode == CVM_DRV_NIC_APP) + liquidio_stop_nic_module(oct_dev); + /* Reset the octeon device and cleanup all memory allocated for * the octeon device by driver. */ @@ -472,6 +583,452 @@ static int octeon_pci_os_setup(struct octeon_device *oct) } /** + * \brief Callback for getting interface configuration + * @param status status of request + * @param buf pointer to resp structure + */ +static void if_cfg_callback(struct octeon_device *oct, + u32 status __attribute__((unused)), void *buf) +{ + struct octeon_soft_command *sc = (struct octeon_soft_command *)buf; + struct liquidio_if_cfg_context *ctx; + struct liquidio_if_cfg_resp *resp; + + resp = (struct liquidio_if_cfg_resp *)sc->virtrptr; + ctx = (struct liquidio_if_cfg_context *)sc->ctxptr; + + oct = lio_get_device(ctx->octeon_id); + if (resp->status) + dev_err(&oct->pci_dev->dev, "nic if cfg instruction failed. Status: %llx\n", + CVM_CAST64(resp->status)); + WRITE_ONCE(ctx->cond, 1); + + snprintf(oct->fw_info.liquidio_firmware_version, 32, "%s", + resp->cfg_info.liquidio_firmware_version); + + /* This barrier is required to be sure that the response has been + * written fully before waking up the handler + */ + wmb(); + + wake_up_interruptible(&ctx->wc); +} + +/** + * \brief Select queue based on hash + * @param dev Net device + * @param skb sk_buff structure + * @returns selected queue number + */ +static u16 select_q(struct net_device *dev, struct sk_buff *skb, + void *accel_priv __attribute__((unused)), + select_queue_fallback_t fallback __attribute__((unused))) +{ + struct lio *lio; + u32 qindex; + + lio = GET_LIO(dev); + + qindex = skb_tx_hash(dev, skb); + + return (u16)(qindex % (lio->linfo.num_txpciq)); +} + +/** + * \brief Net device stop for LiquidIO + * @param netdev network device + */ +static int liquidio_stop(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + + netif_info(lio, ifdown, lio->netdev, "Stopping interface!\n"); + /* Inform that netif carrier is down */ + lio->intf_open = 0; + lio->linfo.link.s.link_up = 0; + + netif_carrier_off(netdev); + lio->link_changes++; + + ifstate_reset(lio, LIO_IFSTATE_RUNNING); + + dev_info(&oct->pci_dev->dev, "%s interface is stopped\n", netdev->name); + + return 0; +} + +/** Sending command to enable/disable RX checksum offload + * @param netdev pointer to network device + * @param command OCTNET_CMD_TNL_RX_CSUM_CTL + * @param rx_cmd_bit OCTNET_CMD_RXCSUM_ENABLE/ + * OCTNET_CMD_RXCSUM_DISABLE + * @returns SUCCESS or FAILURE + */ +static int liquidio_set_rxcsum_command(struct net_device *netdev, int command, + u8 rx_cmd) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + int ret = 0; + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = command; + nctrl.ncmd.s.param1 = rx_cmd; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = 100; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "DEVFLAGS RXCSUM change failed in core (ret:0x%x)\n", + ret); + } + return ret; +} + +/** \brief Net device fix features + * @param netdev pointer to network device + * @param request features requested + * @returns updated features list + */ +static netdev_features_t liquidio_fix_features(struct net_device *netdev, + netdev_features_t request) +{ + struct lio *lio = netdev_priv(netdev); + + if ((request & NETIF_F_RXCSUM) && + !(lio->dev_capability & NETIF_F_RXCSUM)) + request &= ~NETIF_F_RXCSUM; + + if ((request & NETIF_F_HW_CSUM) && + !(lio->dev_capability & NETIF_F_HW_CSUM)) + request &= ~NETIF_F_HW_CSUM; + + if ((request & NETIF_F_TSO) && !(lio->dev_capability & NETIF_F_TSO)) + request &= ~NETIF_F_TSO; + + if ((request & NETIF_F_TSO6) && !(lio->dev_capability & NETIF_F_TSO6)) + request &= ~NETIF_F_TSO6; + + if ((request & NETIF_F_LRO) && !(lio->dev_capability & NETIF_F_LRO)) + request &= ~NETIF_F_LRO; + + /* Disable LRO if RXCSUM is off */ + if (!(request & NETIF_F_RXCSUM) && (netdev->features & NETIF_F_LRO) && + (lio->dev_capability & NETIF_F_LRO)) + request &= ~NETIF_F_LRO; + + return request; +} + +/** \brief Net device set features + * @param netdev pointer to network device + * @param features features to enable/disable + */ +static int liquidio_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct lio *lio = netdev_priv(netdev); + + if (!((netdev->features ^ features) & NETIF_F_LRO)) + return 0; + + if ((features & NETIF_F_LRO) && (lio->dev_capability & NETIF_F_LRO)) + liquidio_set_feature(netdev, OCTNET_CMD_LRO_ENABLE, + OCTNIC_LROIPV4 | OCTNIC_LROIPV6); + else if (!(features & NETIF_F_LRO) && + (lio->dev_capability & NETIF_F_LRO)) + liquidio_set_feature(netdev, OCTNET_CMD_LRO_DISABLE, + OCTNIC_LROIPV4 | OCTNIC_LROIPV6); + if (!(netdev->features & NETIF_F_RXCSUM) && + (lio->enc_dev_capability & NETIF_F_RXCSUM) && + (features & NETIF_F_RXCSUM)) + liquidio_set_rxcsum_command(netdev, OCTNET_CMD_TNL_RX_CSUM_CTL, + OCTNET_CMD_RXCSUM_ENABLE); + else if ((netdev->features & NETIF_F_RXCSUM) && + (lio->enc_dev_capability & NETIF_F_RXCSUM) && + !(features & NETIF_F_RXCSUM)) + liquidio_set_rxcsum_command(netdev, OCTNET_CMD_TNL_RX_CSUM_CTL, + OCTNET_CMD_RXCSUM_DISABLE); + + return 0; +} + +static const struct net_device_ops lionetdevops = { + .ndo_fix_features = liquidio_fix_features, + .ndo_set_features = liquidio_set_features, + .ndo_select_queue = select_q, +}; + +/** + * \brief Setup network interfaces + * @param octeon_dev octeon device + * + * Called during init time for each device. It assumes the NIC + * is already up and running. The link information for each + * interface is passed in link_info. + */ +static int setup_nic_devices(struct octeon_device *octeon_dev) +{ + int retval, num_iqueues, num_oqueues; + struct liquidio_if_cfg_context *ctx; + u32 resp_size, ctx_size, data_size; + struct liquidio_if_cfg_resp *resp; + struct octeon_soft_command *sc; + union oct_nic_if_cfg if_cfg; + struct octdev_props *props; + struct net_device *netdev; + struct lio_version *vdata; + struct lio *lio = NULL; + u8 mac[ETH_ALEN], i, j; + u32 ifidx_or_pfnum; + + ifidx_or_pfnum = octeon_dev->pf_num; + + for (i = 0; i < octeon_dev->ifcount; i++) { + resp_size = sizeof(struct liquidio_if_cfg_resp); + ctx_size = sizeof(struct liquidio_if_cfg_context); + data_size = sizeof(struct lio_version); + sc = (struct octeon_soft_command *) + octeon_alloc_soft_command(octeon_dev, data_size, + resp_size, ctx_size); + resp = (struct liquidio_if_cfg_resp *)sc->virtrptr; + ctx = (struct liquidio_if_cfg_context *)sc->ctxptr; + vdata = (struct lio_version *)sc->virtdptr; + + *((u64 *)vdata) = 0; + vdata->major = cpu_to_be16(LIQUIDIO_BASE_MAJOR_VERSION); + vdata->minor = cpu_to_be16(LIQUIDIO_BASE_MINOR_VERSION); + vdata->micro = cpu_to_be16(LIQUIDIO_BASE_MICRO_VERSION); + + WRITE_ONCE(ctx->cond, 0); + ctx->octeon_id = lio_get_device_id(octeon_dev); + init_waitqueue_head(&ctx->wc); + + if_cfg.u64 = 0; + + if_cfg.s.num_iqueues = octeon_dev->sriov_info.rings_per_vf; + if_cfg.s.num_oqueues = octeon_dev->sriov_info.rings_per_vf; + if_cfg.s.base_queue = 0; + + sc->iq_no = 0; + + octeon_prepare_soft_command(octeon_dev, sc, OPCODE_NIC, + OPCODE_NIC_IF_CFG, 0, if_cfg.u64, + 0); + + sc->callback = if_cfg_callback; + sc->callback_arg = sc; + sc->wait_time = 5000; + + retval = octeon_send_soft_command(octeon_dev, sc); + if (retval == IQ_SEND_FAILED) { + dev_err(&octeon_dev->pci_dev->dev, + "iq/oq config failed status: %x\n", retval); + /* Soft instr is freed by driver in case of failure. */ + goto setup_nic_dev_fail; + } + + /* Sleep on a wait queue till the cond flag indicates that the + * response arrived or timed-out. + */ + if (sleep_cond(&ctx->wc, &ctx->cond) == -EINTR) { + dev_err(&octeon_dev->pci_dev->dev, "Wait interrupted\n"); + goto setup_nic_wait_intr; + } + + retval = resp->status; + if (retval) { + dev_err(&octeon_dev->pci_dev->dev, "iq/oq config failed\n"); + goto setup_nic_dev_fail; + } + + octeon_swap_8B_data((u64 *)(&resp->cfg_info), + (sizeof(struct liquidio_if_cfg_info)) >> 3); + + num_iqueues = hweight64(resp->cfg_info.iqmask); + num_oqueues = hweight64(resp->cfg_info.oqmask); + + if (!(num_iqueues) || !(num_oqueues)) { + dev_err(&octeon_dev->pci_dev->dev, + "Got bad iqueues (%016llx) or oqueues (%016llx) from firmware.\n", + resp->cfg_info.iqmask, resp->cfg_info.oqmask); + goto setup_nic_dev_fail; + } + dev_dbg(&octeon_dev->pci_dev->dev, + "interface %d, iqmask %016llx, oqmask %016llx, numiqueues %d, numoqueues %d\n", + i, resp->cfg_info.iqmask, resp->cfg_info.oqmask, + num_iqueues, num_oqueues); + + netdev = alloc_etherdev_mq(LIO_SIZE, num_iqueues); + + if (!netdev) { + dev_err(&octeon_dev->pci_dev->dev, "Device allocation failed\n"); + goto setup_nic_dev_fail; + } + + SET_NETDEV_DEV(netdev, &octeon_dev->pci_dev->dev); + + /* Associate the routines that will handle different + * netdev tasks. + */ + netdev->netdev_ops = &lionetdevops; + + lio = GET_LIO(netdev); + + memset(lio, 0, sizeof(struct lio)); + + lio->ifidx = ifidx_or_pfnum; + + props = &octeon_dev->props[i]; + props->gmxport = resp->cfg_info.linfo.gmxport; + props->netdev = netdev; + + lio->linfo.num_rxpciq = num_oqueues; + lio->linfo.num_txpciq = num_iqueues; + + for (j = 0; j < num_oqueues; j++) { + lio->linfo.rxpciq[j].u64 = + resp->cfg_info.linfo.rxpciq[j].u64; + } + for (j = 0; j < num_iqueues; j++) { + lio->linfo.txpciq[j].u64 = + resp->cfg_info.linfo.txpciq[j].u64; + } + + lio->linfo.hw_addr = resp->cfg_info.linfo.hw_addr; + lio->linfo.gmxport = resp->cfg_info.linfo.gmxport; + lio->linfo.link.u64 = resp->cfg_info.linfo.link.u64; + lio->linfo.macaddr_is_admin_asgnd = + resp->cfg_info.linfo.macaddr_is_admin_asgnd; + + lio->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); + + lio->dev_capability = NETIF_F_HIGHDMA + | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM + | NETIF_F_SG | NETIF_F_RXCSUM + | NETIF_F_TSO | NETIF_F_TSO6 + | NETIF_F_GRO + | NETIF_F_LRO; + netif_set_gso_max_size(netdev, OCTNIC_GSO_MAX_SIZE); + + netdev->features = (lio->dev_capability & ~NETIF_F_LRO); + + netdev->hw_features = lio->dev_capability; + + /* Point to the properties for octeon device to which this + * interface belongs. + */ + lio->oct_dev = octeon_dev; + lio->octprops = props; + lio->netdev = netdev; + + dev_dbg(&octeon_dev->pci_dev->dev, + "if%d gmx: %d hw_addr: 0x%llx\n", i, + lio->linfo.gmxport, CVM_CAST64(lio->linfo.hw_addr)); + + /* 64-bit swap required on LE machines */ + octeon_swap_8B_data(&lio->linfo.hw_addr, 1); + for (j = 0; j < ETH_ALEN; j++) + mac[j] = *((u8 *)(((u8 *)&lio->linfo.hw_addr) + 2 + j)); + + /* Copy MAC Address to OS network device structure */ + ether_addr_copy(netdev->dev_addr, mac); + + if (netdev->features & NETIF_F_LRO) + liquidio_set_feature(netdev, OCTNET_CMD_LRO_ENABLE, + OCTNIC_LROIPV4 | OCTNIC_LROIPV6); + + if ((debug != -1) && (debug & NETIF_MSG_HW)) + liquidio_set_feature(netdev, OCTNET_CMD_VERBOSE_ENABLE, + 0); + + /* Register the network device with the OS */ + if (register_netdev(netdev)) { + dev_err(&octeon_dev->pci_dev->dev, "Device registration failed\n"); + goto setup_nic_dev_fail; + } + + dev_dbg(&octeon_dev->pci_dev->dev, + "Setup NIC ifidx:%d mac:%02x%02x%02x%02x%02x%02x\n", + i, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + netif_carrier_off(netdev); + lio->link_changes++; + + ifstate_set(lio, LIO_IFSTATE_REGISTERED); + + /* Sending command to firmware to enable Rx checksum offload + * by default at the time of setup of Liquidio driver for + * this device + */ + liquidio_set_rxcsum_command(netdev, OCTNET_CMD_TNL_RX_CSUM_CTL, + OCTNET_CMD_RXCSUM_ENABLE); + liquidio_set_feature(netdev, OCTNET_CMD_TNL_TX_CSUM_CTL, + OCTNET_CMD_TXCSUM_ENABLE); + + dev_dbg(&octeon_dev->pci_dev->dev, + "NIC ifidx:%d Setup successful\n", i); + + octeon_free_soft_command(octeon_dev, sc); + } + + return 0; + +setup_nic_dev_fail: + + octeon_free_soft_command(octeon_dev, sc); + +setup_nic_wait_intr: + + while (i--) { + dev_err(&octeon_dev->pci_dev->dev, + "NIC ifidx:%d Setup failed\n", i); + liquidio_destroy_nic_device(octeon_dev, i); + } + return -ENODEV; +} + +/** + * \brief initialize the NIC + * @param oct octeon device + * + * This initialization routine is called once the Octeon device application is + * up and running + */ +static int liquidio_init_nic_module(struct octeon_device *oct) +{ + int num_nic_ports = 1; + int i, retval = 0; + + dev_dbg(&oct->pci_dev->dev, "Initializing network interfaces\n"); + + /* only default iq and oq were initialized + * initialize the rest as well run port_config command for each port + */ + oct->ifcount = num_nic_ports; + memset(oct->props, 0, + sizeof(struct octdev_props) * num_nic_ports); + + for (i = 0; i < MAX_OCTEON_LINKS; i++) + oct->props[i].gmxport = -1; + + retval = setup_nic_devices(oct); + if (retval) { + dev_err(&oct->pci_dev->dev, "Setup NIC devices failed\n"); + goto octnet_init_failure; + } + +octnet_init_failure: + + oct->ifcount = 0; + + return retval; +} + +/** * \brief Device initialization for each Octeon device that is probed * @param octeon_dev octeon device */ @@ -498,6 +1055,8 @@ static int octeon_device_init(struct octeon_device *oct) atomic_set(&oct->status, OCT_DEV_PCI_MAP_DONE); + oct->app_mode = CVM_DRV_NIC_APP; + /* Initialize the dispatch mechanism used to push packets arriving on * Octeon Output queues. */ @@ -594,6 +1153,9 @@ static int octeon_device_init(struct octeon_device *oct) atomic_set(&oct->status, OCT_DEV_RUNNING); + if (liquidio_init_nic_module(oct)) + return 1; + return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 6d54032..583818e 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -1221,6 +1221,9 @@ struct octeon_config *octeon_get_conf(struct octeon_device *oct) } else if (OCTEON_CN23XX_PF(oct)) { default_oct_conf = (struct octeon_config *) (CHIP_CONF(oct, cn23xx_pf)); + } else if (OCTEON_CN23XX_VF(oct)) { + default_oct_conf = (struct octeon_config *) + (CHIP_CONF(oct, cn23xx_vf)); } return default_oct_conf; } -- cgit v1.1 From 3cd25e48215ff0b2968d00856fcfb0664ad1c495 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Wed, 7 Dec 2016 08:54:32 -0800 Subject: liquidio CN23XX: VF link status Adds support for VF link status related changes. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 249 +++++++++++++++++++++ 1 file changed, 249 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index f05a7c3..07e4864 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -163,6 +163,186 @@ static void ifstate_reset(struct lio *lio, int state_flag) atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag))); } +/** + * \brief Stop Tx queues + * @param netdev network device + */ +static void txqs_stop(struct net_device *netdev) +{ + if (netif_is_multiqueue(netdev)) { + int i; + + for (i = 0; i < netdev->num_tx_queues; i++) + netif_stop_subqueue(netdev, i); + } else { + netif_stop_queue(netdev); + } +} + +/** + * \brief Start Tx queues + * @param netdev network device + */ +static void txqs_start(struct net_device *netdev) +{ + if (netif_is_multiqueue(netdev)) { + int i; + + for (i = 0; i < netdev->num_tx_queues; i++) + netif_start_subqueue(netdev, i); + } else { + netif_start_queue(netdev); + } +} + +/** + * \brief Wake Tx queues + * @param netdev network device + */ +static void txqs_wake(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + + if (netif_is_multiqueue(netdev)) { + int i; + + for (i = 0; i < netdev->num_tx_queues; i++) { + int qno = lio->linfo.txpciq[i % (lio->linfo.num_txpciq)] + .s.q_no; + if (__netif_subqueue_stopped(netdev, i)) { + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno, + tx_restart, 1); + netif_wake_subqueue(netdev, i); + } + } + } else { + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq, + tx_restart, 1); + netif_wake_queue(netdev); + } +} + +/** + * \brief Start Tx queue + * @param netdev network device + */ +static void start_txq(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + + if (lio->linfo.link.s.link_up) { + txqs_start(netdev); + return; + } +} + +/** + * \brief Print link information + * @param netdev network device + */ +static void print_link_info(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) { + struct oct_link_info *linfo = &lio->linfo; + + if (linfo->link.s.link_up) { + netif_info(lio, link, lio->netdev, "%d Mbps %s Duplex UP\n", + linfo->link.s.speed, + (linfo->link.s.duplex) ? "Full" : "Half"); + } else { + netif_info(lio, link, lio->netdev, "Link Down\n"); + } + } +} + +/** + * \brief Routine to notify MTU change + * @param work work_struct data structure + */ +static void octnet_link_status_change(struct work_struct *work) +{ + struct cavium_wk *wk = (struct cavium_wk *)work; + struct lio *lio = (struct lio *)wk->ctxptr; + + rtnl_lock(); + call_netdevice_notifiers(NETDEV_CHANGEMTU, lio->netdev); + rtnl_unlock(); +} + +/** + * \brief Sets up the mtu status change work + * @param netdev network device + */ +static int setup_link_status_change_wq(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + + lio->link_status_wq.wq = alloc_workqueue("link-status", + WQ_MEM_RECLAIM, 0); + if (!lio->link_status_wq.wq) { + dev_err(&oct->pci_dev->dev, "unable to create cavium link status wq\n"); + return -1; + } + INIT_DELAYED_WORK(&lio->link_status_wq.wk.work, + octnet_link_status_change); + lio->link_status_wq.wk.ctxptr = lio; + + return 0; +} + +static void cleanup_link_status_change_wq(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + + if (lio->link_status_wq.wq) { + cancel_delayed_work_sync(&lio->link_status_wq.wk.work); + destroy_workqueue(lio->link_status_wq.wq); + } +} + +/** + * \brief Update link status + * @param netdev network device + * @param ls link status structure + * + * Called on receipt of a link status response from the core application to + * update each interface's link status. + */ +static void update_link_status(struct net_device *netdev, + union oct_link_status *ls) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + + if ((lio->intf_open) && (lio->linfo.link.u64 != ls->u64)) { + lio->linfo.link.u64 = ls->u64; + + print_link_info(netdev); + lio->link_changes++; + + if (lio->linfo.link.s.link_up) { + netif_carrier_on(netdev); + txqs_wake(netdev); + } else { + netif_carrier_off(netdev); + txqs_stop(netdev); + } + + if (lio->linfo.link.s.mtu < netdev->mtu) { + dev_warn(&oct->pci_dev->dev, + "PF has changed the MTU for gmx port. Reducing the mtu from %d to %d\n", + netdev->mtu, lio->linfo.link.s.mtu); + lio->mtu = lio->linfo.link.s.mtu; + netdev->mtu = lio->linfo.link.s.mtu; + queue_delayed_work(lio->link_status_wq.wq, + &lio->link_status_wq.wk.work, 0); + } + } +} + static int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) { @@ -499,6 +679,8 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) unregister_netdev(netdev); + cleanup_link_status_change_wq(netdev); + free_netdev(netdev); oct->props[ifidx].gmxport = -1; @@ -635,6 +817,28 @@ static u16 select_q(struct net_device *dev, struct sk_buff *skb, } /** + * \brief Net device open for LiquidIO + * @param netdev network device + */ +static int liquidio_open(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + + ifstate_set(lio, LIO_IFSTATE_RUNNING); + + /* Ready for link status updates */ + lio->intf_open = 1; + + netif_info(lio, ifup, lio->netdev, "Interface Open, ready for traffic\n"); + start_txq(netdev); + + dev_info(&oct->pci_dev->dev, "%s interface is opened\n", netdev->name); + + return 0; +} + +/** * \brief Net device stop for LiquidIO * @param netdev network device */ @@ -653,6 +857,8 @@ static int liquidio_stop(struct net_device *netdev) ifstate_reset(lio, LIO_IFSTATE_RUNNING); + txqs_stop(netdev); + dev_info(&oct->pci_dev->dev, "%s interface is stopped\n", netdev->name); return 0; @@ -758,11 +964,47 @@ static int liquidio_set_features(struct net_device *netdev, } static const struct net_device_ops lionetdevops = { + .ndo_open = liquidio_open, + .ndo_stop = liquidio_stop, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, .ndo_select_queue = select_q, }; +static int lio_nic_info(struct octeon_recv_info *recv_info, void *buf) +{ + struct octeon_device *oct = (struct octeon_device *)buf; + struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt; + union oct_link_status *ls; + int gmxport = 0; + int i; + + if (recv_pkt->buffer_size[0] != sizeof(*ls)) { + dev_err(&oct->pci_dev->dev, "Malformed NIC_INFO, len=%d, ifidx=%d\n", + recv_pkt->buffer_size[0], + recv_pkt->rh.r_nic_info.gmxport); + goto nic_info_err; + } + + gmxport = recv_pkt->rh.r_nic_info.gmxport; + ls = (union oct_link_status *)get_rbd(recv_pkt->buffer_ptr[0]); + + octeon_swap_8B_data((u64 *)ls, (sizeof(union oct_link_status)) >> 3); + + for (i = 0; i < oct->ifcount; i++) { + if (oct->props[i].gmxport == gmxport) { + update_link_status(oct->props[i].netdev, ls); + break; + } + } + +nic_info_err: + for (i = 0; i < recv_pkt->buffer_count; i++) + recv_buffer_free(recv_pkt->buffer_ptr[i]); + octeon_free_recv_info(recv_info); + return 0; +} + /** * \brief Setup network interfaces * @param octeon_dev octeon device @@ -788,6 +1030,10 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) ifidx_or_pfnum = octeon_dev->pf_num; + /* This is to handle link status changes */ + octeon_register_dispatch_fn(octeon_dev, OPCODE_NIC, OPCODE_NIC_INFO, + lio_nic_info, octeon_dev); + for (i = 0; i < octeon_dev->ifcount; i++) { resp_size = sizeof(struct liquidio_if_cfg_resp); ctx_size = sizeof(struct liquidio_if_cfg_context); @@ -946,6 +1192,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) liquidio_set_feature(netdev, OCTNET_CMD_VERBOSE_ENABLE, 0); + if (setup_link_status_change_wq(netdev)) + goto setup_nic_dev_fail; + /* Register the network device with the OS */ if (register_netdev(netdev)) { dev_err(&octeon_dev->pci_dev->dev, "Device registration failed\n"); -- cgit v1.1 From 50f7f94b96a9b9dad1996953b86ece3417cd0983 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Wed, 7 Dec 2016 08:54:33 -0800 Subject: liquidio CN23XX: VF mac address Adds support for configuring mtu, multicast and mac address. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 195 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/liquidio_common.h | 1 + .../net/ethernet/cavium/liquidio/octeon_network.h | 1 + 3 files changed, 197 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 07e4864..6f23944 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -864,6 +864,194 @@ static int liquidio_stop(struct net_device *netdev) return 0; } +/** + * \brief Converts a mask based on net device flags + * @param netdev network device + * + * This routine generates a octnet_ifflags mask from the net device flags + * received from the OS. + */ +static enum octnet_ifflags get_new_flags(struct net_device *netdev) +{ + enum octnet_ifflags f = OCTNET_IFFLAG_UNICAST; + + if (netdev->flags & IFF_PROMISC) + f |= OCTNET_IFFLAG_PROMISC; + + if (netdev->flags & IFF_ALLMULTI) + f |= OCTNET_IFFLAG_ALLMULTI; + + if (netdev->flags & IFF_MULTICAST) { + f |= OCTNET_IFFLAG_MULTICAST; + + /* Accept all multicast addresses if there are more than we + * can handle + */ + if (netdev_mc_count(netdev) > MAX_OCTEON_MULTICAST_ADDR) + f |= OCTNET_IFFLAG_ALLMULTI; + } + + if (netdev->flags & IFF_BROADCAST) + f |= OCTNET_IFFLAG_BROADCAST; + + return f; +} + +static void liquidio_set_uc_list(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + struct netdev_hw_addr *ha; + u64 *mac; + + if (lio->netdev_uc_count == netdev_uc_count(netdev)) + return; + + if (netdev_uc_count(netdev) > MAX_NCTRL_UDD) { + dev_err(&oct->pci_dev->dev, "too many MAC addresses in netdev uc list\n"); + return; + } + + lio->netdev_uc_count = netdev_uc_count(netdev); + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + nctrl.ncmd.s.cmd = OCTNET_CMD_SET_UC_LIST; + nctrl.ncmd.s.more = lio->netdev_uc_count; + nctrl.ncmd.s.param1 = oct->vf_num; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + /* copy all the addresses into the udd */ + mac = &nctrl.udd[0]; + netdev_for_each_uc_addr(ha, netdev) { + ether_addr_copy(((u8 *)mac) + 2, ha->addr); + mac++; + } + + octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); +} + +/** + * \brief Net device set_multicast_list + * @param netdev network device + */ +static void liquidio_set_mcast_list(struct net_device *netdev) +{ + int mc_count = min(netdev_mc_count(netdev), MAX_OCTEON_MULTICAST_ADDR); + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + struct netdev_hw_addr *ha; + u64 *mc; + int ret; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + /* Create a ctrl pkt command to be sent to core app. */ + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_SET_MULTI_LIST; + nctrl.ncmd.s.param1 = get_new_flags(netdev); + nctrl.ncmd.s.param2 = mc_count; + nctrl.ncmd.s.more = mc_count; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + /* copy all the addresses into the udd */ + mc = &nctrl.udd[0]; + netdev_for_each_mc_addr(ha, netdev) { + *mc = 0; + ether_addr_copy(((u8 *)mc) + 2, ha->addr); + /* no need to swap bytes */ + if (++mc > &nctrl.udd[mc_count]) + break; + } + + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + + /* Apparently, any activity in this call from the kernel has to + * be atomic. So we won't wait for response. + */ + nctrl.wait_time = 0; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "DEVFLAGS change failed in core (ret: 0x%x)\n", + ret); + } + + liquidio_set_uc_list(netdev); +} + +/** + * \brief Net device set_mac_address + * @param netdev network device + */ +static int liquidio_set_mac(struct net_device *netdev, void *p) +{ + struct sockaddr *addr = (struct sockaddr *)p; + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + int ret = 0; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (ether_addr_equal(addr->sa_data, netdev->dev_addr)) + return 0; + + if (lio->linfo.macaddr_is_admin_asgnd) + return -EPERM; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MACADDR; + nctrl.ncmd.s.param1 = 0; + nctrl.ncmd.s.more = 1; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + nctrl.wait_time = 100; + + nctrl.udd[0] = 0; + /* The MAC Address is presented in network byte order. */ + ether_addr_copy((u8 *)&nctrl.udd[0] + 2, addr->sa_data); + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "MAC Address change failed\n"); + return -ENOMEM; + } + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + ether_addr_copy(((u8 *)&lio->linfo.hw_addr) + 2, addr->sa_data); + + return 0; +} + +/** + * \brief Net device change_mtu + * @param netdev network device + */ +static int liquidio_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + + lio->mtu = new_mtu; + + netif_info(lio, probe, lio->netdev, "MTU Changed from %d to %d\n", + netdev->mtu, new_mtu); + dev_info(&oct->pci_dev->dev, "%s MTU Changed from %d to %d\n", + netdev->name, netdev->mtu, new_mtu); + + netdev->mtu = new_mtu; + + return 0; +} + /** Sending command to enable/disable RX checksum offload * @param netdev pointer to network device * @param command OCTNET_CMD_TNL_RX_CSUM_CTL @@ -966,6 +1154,9 @@ static int liquidio_set_features(struct net_device *netdev, static const struct net_device_ops lionetdevops = { .ndo_open = liquidio_open, .ndo_stop = liquidio_stop, + .ndo_set_mac_address = liquidio_set_mac, + .ndo_set_rx_mode = liquidio_set_mcast_list, + .ndo_change_mtu = liquidio_change_mtu, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, .ndo_select_queue = select_q, @@ -1165,6 +1356,10 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) netdev->hw_features = lio->dev_capability; + /* MTU range: 68 - 16000 */ + netdev->min_mtu = LIO_MIN_MTU_SIZE; + netdev->max_mtu = LIO_MAX_MTU_SIZE; + /* Point to the properties for octeon device to which this * interface belongs. */ diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index f308ee4..ba329f6 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -212,6 +212,7 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_ID_ACTIVE 0x1a +#define OCTNET_CMD_SET_UC_LIST 0x1b #define OCTNET_CMD_SET_VF_LINKSTATE 0x1c #define OCTNET_CMD_VXLAN_PORT_ADD 0x0 #define OCTNET_CMD_VXLAN_PORT_DEL 0x1 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index e94edc8..6bb8941 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -123,6 +123,7 @@ struct lio { /* work queue for link status */ struct cavium_wq link_status_wq; + int netdev_uc_count; }; #define LIO_SIZE (sizeof(struct lio)) -- cgit v1.1 From beea50a5d56bfff2c3589d1ebff715e46dceffbd Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Wed, 7 Dec 2016 08:54:34 -0800 Subject: liquidio CN23XX: VF scatter gather lists Adds support for VF scatter gather lists. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 6f23944..f861a9b 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -55,10 +55,28 @@ struct liquidio_if_cfg_resp { u64 status; }; +#define OCTNIC_MAX_SG (MAX_SKB_FRAGS) + #define OCTNIC_GSO_MAX_HEADER_SIZE 128 #define OCTNIC_GSO_MAX_SIZE \ (CN23XX_DEFAULT_INPUT_JABBER - OCTNIC_GSO_MAX_HEADER_SIZE) +struct octnic_gather { + /* List manipulation. Next and prev pointers. */ + struct list_head list; + + /* Size of the gather component at sg in bytes. */ + int sg_size; + + /* Number of bytes that sg was adjusted to make it 8B-aligned. */ + int adjust; + + /* Gather component that can accommodate max sized fragment list + * received from the IP layer. + */ + struct octeon_sg_entry *sg; +}; + struct octeon_device_priv { /* Tasklet structures for this device. */ struct tasklet_struct droq_tasklet; @@ -237,6 +255,114 @@ static void start_txq(struct net_device *netdev) } /** + * Remove the node at the head of the list. The list would be empty at + * the end of this call if there are no more nodes in the list. + */ +static struct list_head *list_delete_head(struct list_head *root) +{ + struct list_head *node; + + if ((root->prev == root) && (root->next == root)) + node = NULL; + else + node = root->next; + + if (node) + list_del(node); + + return node; +} + +/** + * \brief Delete gather lists + * @param lio per-network private data + */ +static void delete_glists(struct lio *lio) +{ + struct octnic_gather *g; + int i; + + if (!lio->glist) + return; + + for (i = 0; i < lio->linfo.num_txpciq; i++) { + do { + g = (struct octnic_gather *) + list_delete_head(&lio->glist[i]); + if (g) { + if (g->sg) + kfree((void *)((unsigned long)g->sg - + g->adjust)); + kfree(g); + } + } while (g); + } + + kfree(lio->glist); + kfree(lio->glist_lock); +} + +/** + * \brief Setup gather lists + * @param lio per-network private data + */ +static int setup_glists(struct lio *lio, int num_iqs) +{ + struct octnic_gather *g; + int i, j; + + lio->glist_lock = + kzalloc(sizeof(*lio->glist_lock) * num_iqs, GFP_KERNEL); + if (!lio->glist_lock) + return 1; + + lio->glist = + kzalloc(sizeof(*lio->glist) * num_iqs, GFP_KERNEL); + if (!lio->glist) { + kfree(lio->glist_lock); + return 1; + } + + for (i = 0; i < num_iqs; i++) { + spin_lock_init(&lio->glist_lock[i]); + + INIT_LIST_HEAD(&lio->glist[i]); + + for (j = 0; j < lio->tx_qsize; j++) { + g = kzalloc(sizeof(*g), GFP_KERNEL); + if (!g) + break; + + g->sg_size = ((ROUNDUP4(OCTNIC_MAX_SG) >> 2) * + OCT_SG_ENTRY_SIZE); + + g->sg = kmalloc(g->sg_size + 8, GFP_KERNEL); + if (!g->sg) { + kfree(g); + break; + } + + /* The gather component should be aligned on 64-bit + * boundary + */ + if (((unsigned long)g->sg) & 7) { + g->adjust = 8 - (((unsigned long)g->sg) & 7); + g->sg = (struct octeon_sg_entry *) + ((unsigned long)g->sg + g->adjust); + } + list_add_tail(&g->list, &lio->glist[i]); + } + + if (j != lio->tx_qsize) { + delete_glists(lio); + return 1; + } + } + + return 0; +} + +/** * \brief Print link information * @param netdev network device */ @@ -681,6 +807,8 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) cleanup_link_status_change_wq(netdev); + delete_glists(lio); + free_netdev(netdev); oct->props[ifidx].gmxport = -1; @@ -1379,6 +1507,12 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) /* Copy MAC Address to OS network device structure */ ether_addr_copy(netdev->dev_addr, mac); + if (setup_glists(lio, num_iqueues)) { + dev_err(&octeon_dev->pci_dev->dev, + "Gather list allocation failed\n"); + goto setup_nic_dev_fail; + } + if (netdev->features & NETIF_F_LRO) liquidio_set_feature(netdev, OCTNET_CMD_LRO_ENABLE, OCTNIC_LROIPV4 | OCTNIC_LROIPV6); -- cgit v1.1 From 9981328a8722f0649190fdd7d027ee745fb01189 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Wed, 7 Dec 2016 08:54:35 -0800 Subject: liquidio CN23XX: VF xmit Adds support for transmit functionality in VF. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.c | 21 ++ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 340 +++++++++++++++++++++ .../net/ethernet/cavium/liquidio/request_manager.c | 6 +- 3 files changed, 364 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c index 108e487..b6117b6 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c @@ -529,6 +529,26 @@ static u64 cn23xx_vf_msix_interrupt_handler(void *dev) return ret; } +static u32 cn23xx_update_read_index(struct octeon_instr_queue *iq) +{ + u32 pkt_in_done = readl(iq->inst_cnt_reg); + u32 last_done; + u32 new_idx; + + last_done = pkt_in_done - iq->pkt_in_done; + iq->pkt_in_done = pkt_in_done; + + /* Modulo of the new index with the IQ size will give us + * the new index. The iq->reset_instr_cnt is always zero for + * cn23xx, so no extra adjustments are needed. + */ + new_idx = (iq->octeon_read_index + + (u32)(last_done & CN23XX_PKT_IN_DONE_CNT_MASK)) % + iq->max_count; + + return new_idx; +} + static void cn23xx_enable_vf_interrupt(struct octeon_device *oct, u8 intr_flag) { struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip; @@ -660,6 +680,7 @@ int cn23xx_setup_octeon_vf_device(struct octeon_device *oct) oct->fn_list.msix_interrupt_handler = cn23xx_vf_msix_interrupt_handler; oct->fn_list.setup_device_regs = cn23xx_setup_vf_device_regs; + oct->fn_list.update_iq_read_idx = cn23xx_update_read_index; oct->fn_list.enable_interrupt = cn23xx_enable_vf_interrupt; oct->fn_list.disable_interrupt = cn23xx_disable_vf_interrupt; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index f861a9b..bcc8888 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -55,6 +55,21 @@ struct liquidio_if_cfg_resp { u64 status; }; +union tx_info { + u64 u64; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + u16 gso_size; + u16 gso_segs; + u32 reserved; +#else + u32 reserved; + u16 gso_segs; + u16 gso_size; +#endif + } s; +}; + #define OCTNIC_MAX_SG (MAX_SKB_FRAGS) #define OCTNIC_GSO_MAX_HEADER_SIZE 128 @@ -255,6 +270,19 @@ static void start_txq(struct net_device *netdev) } /** + * \brief Stop a queue + * @param netdev network device + * @param q which queue to stop + */ +static void stop_q(struct net_device *netdev, int q) +{ + if (netif_is_multiqueue(netdev)) + netif_stop_subqueue(netdev, q); + else + netif_stop_queue(netdev); +} + +/** * Remove the node at the head of the list. The list would be empty at * the end of this call if there are no more nodes in the list. */ @@ -945,6 +973,45 @@ static u16 select_q(struct net_device *dev, struct sk_buff *skb, } /** + * \brief Setup input and output queues + * @param octeon_dev octeon device + * @param ifidx Interface index + * + * Note: Queues are with respect to the octeon device. Thus + * an input queue is for egress packets, and output queues + * are for ingress packets. + */ +static int setup_io_queues(struct octeon_device *octeon_dev, int ifidx) +{ + struct net_device *netdev; + int num_tx_descs; + struct lio *lio; + int retval = 0; + int q; + + netdev = octeon_dev->props[ifidx].netdev; + + lio = GET_LIO(netdev); + + /* set up IQs. */ + for (q = 0; q < lio->linfo.num_txpciq; q++) { + num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( + octeon_get_conf(octeon_dev), lio->ifidx); + retval = octeon_setup_iq(octeon_dev, ifidx, q, + lio->linfo.txpciq[q], num_tx_descs, + netdev_get_tx_queue(netdev, q)); + if (retval) { + dev_err(&octeon_dev->pci_dev->dev, + " %s : Runtime IQ(TxQ) creation failed.\n", + __func__); + return 1; + } + } + + return 0; +} + +/** * \brief Net device open for LiquidIO * @param netdev network device */ @@ -1180,6 +1247,259 @@ static int liquidio_change_mtu(struct net_device *netdev, int new_mtu) return 0; } +/** \brief Transmit networks packets to the Octeon interface + * @param skbuff skbuff struct to be passed to network layer. + * @param netdev pointer to network device + * @returns whether the packet was transmitted to the device okay or not + * (NETDEV_TX_OK or NETDEV_TX_BUSY) + */ +static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct octnet_buf_free_info *finfo; + union octnic_cmd_setup cmdsetup; + struct octnic_data_pkt ndata; + struct octeon_instr_irh *irh; + struct oct_iq_stats *stats; + struct octeon_device *oct; + int q_idx = 0, iq_no = 0; + union tx_info *tx_info; + struct lio *lio; + int status = 0; + u64 dptr = 0; + u32 tag = 0; + int j; + + lio = GET_LIO(netdev); + oct = lio->oct_dev; + + if (netif_is_multiqueue(netdev)) { + q_idx = skb->queue_mapping; + q_idx = (q_idx % (lio->linfo.num_txpciq)); + tag = q_idx; + iq_no = lio->linfo.txpciq[q_idx].s.q_no; + } else { + iq_no = lio->txq; + } + + stats = &oct->instr_queue[iq_no]->stats; + + /* Check for all conditions in which the current packet cannot be + * transmitted. + */ + if (!(atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) || + (!lio->linfo.link.s.link_up) || (skb->len <= 0)) { + netif_info(lio, tx_err, lio->netdev, "Transmit failed link_status : %d\n", + lio->linfo.link.s.link_up); + goto lio_xmit_failed; + } + + /* Use space in skb->cb to store info used to unmap and + * free the buffers. + */ + finfo = (struct octnet_buf_free_info *)skb->cb; + finfo->lio = lio; + finfo->skb = skb; + finfo->sc = NULL; + + /* Prepare the attributes for the data to be passed to OSI. */ + memset(&ndata, 0, sizeof(struct octnic_data_pkt)); + + ndata.buf = finfo; + + ndata.q_no = iq_no; + + if (netif_is_multiqueue(netdev)) { + if (octnet_iq_is_full(oct, ndata.q_no)) { + /* defer sending if queue is full */ + netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n", + ndata.q_no); + stats->tx_iq_busy++; + return NETDEV_TX_BUSY; + } + } else { + if (octnet_iq_is_full(oct, lio->txq)) { + /* defer sending if queue is full */ + stats->tx_iq_busy++; + netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n", + ndata.q_no); + return NETDEV_TX_BUSY; + } + } + + ndata.datasize = skb->len; + + cmdsetup.u64 = 0; + cmdsetup.s.iq_no = iq_no; + + if (skb->ip_summed == CHECKSUM_PARTIAL) + cmdsetup.s.transport_csum = 1; + + if (!skb_shinfo(skb)->nr_frags) { + cmdsetup.s.u.datasize = skb->len; + octnet_prepare_pci_cmd(oct, &ndata.cmd, &cmdsetup, tag); + /* Offload checksum calculation for TCP/UDP packets */ + dptr = dma_map_single(&oct->pci_dev->dev, + skb->data, + skb->len, + DMA_TO_DEVICE); + if (dma_mapping_error(&oct->pci_dev->dev, dptr)) { + dev_err(&oct->pci_dev->dev, "%s DMA mapping error 1\n", + __func__); + return NETDEV_TX_BUSY; + } + + ndata.cmd.cmd3.dptr = dptr; + finfo->dptr = dptr; + ndata.reqtype = REQTYPE_NORESP_NET; + + } else { + struct skb_frag_struct *frag; + struct octnic_gather *g; + int i, frags; + + spin_lock(&lio->glist_lock[q_idx]); + g = (struct octnic_gather *)list_delete_head( + &lio->glist[q_idx]); + spin_unlock(&lio->glist_lock[q_idx]); + + if (!g) { + netif_info(lio, tx_err, lio->netdev, + "Transmit scatter gather: glist null!\n"); + goto lio_xmit_failed; + } + + cmdsetup.s.gather = 1; + cmdsetup.s.u.gatherptrs = (skb_shinfo(skb)->nr_frags + 1); + octnet_prepare_pci_cmd(oct, &ndata.cmd, &cmdsetup, tag); + + memset(g->sg, 0, g->sg_size); + + g->sg[0].ptr[0] = dma_map_single(&oct->pci_dev->dev, + skb->data, + (skb->len - skb->data_len), + DMA_TO_DEVICE); + if (dma_mapping_error(&oct->pci_dev->dev, g->sg[0].ptr[0])) { + dev_err(&oct->pci_dev->dev, "%s DMA mapping error 2\n", + __func__); + return NETDEV_TX_BUSY; + } + add_sg_size(&g->sg[0], (skb->len - skb->data_len), 0); + + frags = skb_shinfo(skb)->nr_frags; + i = 1; + while (frags--) { + frag = &skb_shinfo(skb)->frags[i - 1]; + + g->sg[(i >> 2)].ptr[(i & 3)] = + dma_map_page(&oct->pci_dev->dev, + frag->page.p, + frag->page_offset, + frag->size, + DMA_TO_DEVICE); + if (dma_mapping_error(&oct->pci_dev->dev, + g->sg[i >> 2].ptr[i & 3])) { + dma_unmap_single(&oct->pci_dev->dev, + g->sg[0].ptr[0], + skb->len - skb->data_len, + DMA_TO_DEVICE); + for (j = 1; j < i; j++) { + frag = &skb_shinfo(skb)->frags[j - 1]; + dma_unmap_page(&oct->pci_dev->dev, + g->sg[j >> 2].ptr[j & 3], + frag->size, + DMA_TO_DEVICE); + } + dev_err(&oct->pci_dev->dev, "%s DMA mapping error 3\n", + __func__); + return NETDEV_TX_BUSY; + } + + add_sg_size(&g->sg[(i >> 2)], frag->size, (i & 3)); + i++; + } + + dptr = dma_map_single(&oct->pci_dev->dev, + g->sg, g->sg_size, + DMA_TO_DEVICE); + if (dma_mapping_error(&oct->pci_dev->dev, dptr)) { + dev_err(&oct->pci_dev->dev, "%s DMA mapping error 4\n", + __func__); + dma_unmap_single(&oct->pci_dev->dev, g->sg[0].ptr[0], + skb->len - skb->data_len, + DMA_TO_DEVICE); + for (j = 1; j <= frags; j++) { + frag = &skb_shinfo(skb)->frags[j - 1]; + dma_unmap_page(&oct->pci_dev->dev, + g->sg[j >> 2].ptr[j & 3], + frag->size, DMA_TO_DEVICE); + } + return NETDEV_TX_BUSY; + } + + ndata.cmd.cmd3.dptr = dptr; + finfo->dptr = dptr; + finfo->g = g; + + ndata.reqtype = REQTYPE_NORESP_NET_SG; + } + + irh = (struct octeon_instr_irh *)&ndata.cmd.cmd3.irh; + tx_info = (union tx_info *)&ndata.cmd.cmd3.ossp[0]; + + if (skb_shinfo(skb)->gso_size) { + tx_info->s.gso_size = skb_shinfo(skb)->gso_size; + tx_info->s.gso_segs = skb_shinfo(skb)->gso_segs; + } + + status = octnet_send_nic_data_pkt(oct, &ndata); + if (status == IQ_SEND_FAILED) + goto lio_xmit_failed; + + netif_info(lio, tx_queued, lio->netdev, "Transmit queued successfully\n"); + + if (status == IQ_SEND_STOP) { + dev_err(&oct->pci_dev->dev, "Rcvd IQ_SEND_STOP signal; stopping IQ-%d\n", + iq_no); + stop_q(lio->netdev, q_idx); + } + + netif_trans_update(netdev); + + if (skb_shinfo(skb)->gso_size) + stats->tx_done += skb_shinfo(skb)->gso_segs; + else + stats->tx_done++; + stats->tx_tot_bytes += skb->len; + + return NETDEV_TX_OK; + +lio_xmit_failed: + stats->tx_dropped++; + netif_info(lio, tx_err, lio->netdev, "IQ%d Transmit dropped:%llu\n", + iq_no, stats->tx_dropped); + if (dptr) + dma_unmap_single(&oct->pci_dev->dev, dptr, + ndata.datasize, DMA_TO_DEVICE); + tx_buffer_free(skb); + return NETDEV_TX_OK; +} + +/** \brief Network device Tx timeout + * @param netdev pointer to network device + */ +static void liquidio_tx_timeout(struct net_device *netdev) +{ + struct lio *lio; + + lio = GET_LIO(netdev); + + netif_info(lio, tx_err, lio->netdev, + "Transmit timeout tx_dropped:%ld, waking up queues now!!\n", + netdev->stats.tx_dropped); + netif_trans_update(netdev); + txqs_wake(netdev); +} + /** Sending command to enable/disable RX checksum offload * @param netdev pointer to network device * @param command OCTNET_CMD_TNL_RX_CSUM_CTL @@ -1282,8 +1602,10 @@ static int liquidio_set_features(struct net_device *netdev, static const struct net_device_ops lionetdevops = { .ndo_open = liquidio_open, .ndo_stop = liquidio_stop, + .ndo_start_xmit = liquidio_xmit, .ndo_set_mac_address = liquidio_set_mac, .ndo_set_rx_mode = liquidio_set_mcast_list, + .ndo_tx_timeout = liquidio_tx_timeout, .ndo_change_mtu = liquidio_change_mtu, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, @@ -1507,6 +1829,24 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) /* Copy MAC Address to OS network device structure */ ether_addr_copy(netdev->dev_addr, mac); + if (setup_io_queues(octeon_dev, i)) { + dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n"); + goto setup_nic_dev_fail; + } + + /* For VFs, enable Octeon device interrupts here, + * as this is contingent upon IO queue setup + */ + octeon_dev->fn_list.enable_interrupt(octeon_dev, + OCTEON_ALL_INTR); + + /* By default all interfaces on a single Octeon uses the same + * tx and rx queues + */ + lio->txq = lio->linfo.txpciq[0].s.q_no; + + lio->tx_qsize = octeon_get_tx_qsize(octeon_dev, lio->txq); + if (setup_glists(lio, num_iqueues)) { dev_err(&octeon_dev->pci_dev->dev, "Gather list allocation failed\n"); diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index ea2b7e4..3ce6675 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -394,7 +394,7 @@ lio_process_iq_request_list(struct octeon_device *oct, case REQTYPE_SOFT_COMMAND: sc = buf; - if (OCTEON_CN23XX_PF(oct)) + if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) irh = (struct octeon_instr_irh *) &sc->cmd.cmd3.irh; else @@ -607,7 +607,7 @@ octeon_prepare_soft_command(struct octeon_device *oct, oct_cfg = octeon_get_conf(oct); - if (OCTEON_CN23XX_PF(oct)) { + if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) { ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3; ih3->pkind = oct->instr_queue[sc->iq_no]->txpciq.s.pkind; @@ -700,7 +700,7 @@ int octeon_send_soft_command(struct octeon_device *oct, struct octeon_instr_irh *irh; u32 len; - if (OCTEON_CN23XX_PF(oct)) { + if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) { ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3; if (ih3->dlengsz) { WARN_ON(!sc->dmadptr); -- cgit v1.1 From 9bdca5c66b40e10589b96e70343e9ff11d69531e Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Wed, 7 Dec 2016 08:54:36 -0800 Subject: liquidio CN23XX: VF TX buffers Adds support for freeing VF xmit buffers. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 182 +++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index bcc8888..e4297f8 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -270,6 +270,19 @@ static void start_txq(struct net_device *netdev) } /** + * \brief Wake a queue + * @param netdev network device + * @param q which queue to wake + */ +static void wake_q(struct net_device *netdev, int q) +{ + if (netif_is_multiqueue(netdev)) + netif_wake_subqueue(netdev, q); + else + netif_wake_queue(netdev); +} + +/** * \brief Stop a queue * @param netdev network device * @param q which queue to stop @@ -920,6 +933,163 @@ static int octeon_pci_os_setup(struct octeon_device *oct) return 0; } +static int skb_iq(struct lio *lio, struct sk_buff *skb) +{ + int q = 0; + + if (netif_is_multiqueue(lio->netdev)) + q = skb->queue_mapping % lio->linfo.num_txpciq; + + return q; +} + +/** + * \brief Check Tx queue state for a given network buffer + * @param lio per-network private data + * @param skb network buffer + */ +static int check_txq_state(struct lio *lio, struct sk_buff *skb) +{ + int q = 0, iq = 0; + + if (netif_is_multiqueue(lio->netdev)) { + q = skb->queue_mapping; + iq = lio->linfo.txpciq[(q % (lio->linfo.num_txpciq))].s.q_no; + } else { + iq = lio->txq; + q = iq; + } + + if (octnet_iq_is_full(lio->oct_dev, iq)) + return 0; + + if (__netif_subqueue_stopped(lio->netdev, q)) { + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq, tx_restart, 1); + wake_q(lio->netdev, q); + } + + return 1; +} + +/** + * \brief Unmap and free network buffer + * @param buf buffer + */ +static void free_netbuf(void *buf) +{ + struct octnet_buf_free_info *finfo; + struct sk_buff *skb; + struct lio *lio; + + finfo = (struct octnet_buf_free_info *)buf; + skb = finfo->skb; + lio = finfo->lio; + + dma_unmap_single(&lio->oct_dev->pci_dev->dev, finfo->dptr, skb->len, + DMA_TO_DEVICE); + + check_txq_state(lio, skb); + + tx_buffer_free(skb); +} + +/** + * \brief Unmap and free gather buffer + * @param buf buffer + */ +static void free_netsgbuf(void *buf) +{ + struct octnet_buf_free_info *finfo; + struct octnic_gather *g; + struct sk_buff *skb; + int i, frags, iq; + struct lio *lio; + + finfo = (struct octnet_buf_free_info *)buf; + skb = finfo->skb; + lio = finfo->lio; + g = finfo->g; + frags = skb_shinfo(skb)->nr_frags; + + dma_unmap_single(&lio->oct_dev->pci_dev->dev, + g->sg[0].ptr[0], (skb->len - skb->data_len), + DMA_TO_DEVICE); + + i = 1; + while (frags--) { + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; + + pci_unmap_page((lio->oct_dev)->pci_dev, + g->sg[(i >> 2)].ptr[(i & 3)], + frag->size, DMA_TO_DEVICE); + i++; + } + + dma_unmap_single(&lio->oct_dev->pci_dev->dev, + finfo->dptr, g->sg_size, + DMA_TO_DEVICE); + + iq = skb_iq(lio, skb); + + spin_lock(&lio->glist_lock[iq]); + list_add_tail(&g->list, &lio->glist[iq]); + spin_unlock(&lio->glist_lock[iq]); + + check_txq_state(lio, skb); /* mq support: sub-queue state check */ + + tx_buffer_free(skb); +} + +/** + * \brief Unmap and free gather buffer with response + * @param buf buffer + */ +static void free_netsgbuf_with_resp(void *buf) +{ + struct octnet_buf_free_info *finfo; + struct octeon_soft_command *sc; + struct octnic_gather *g; + struct sk_buff *skb; + int i, frags, iq; + struct lio *lio; + + sc = (struct octeon_soft_command *)buf; + skb = (struct sk_buff *)sc->callback_arg; + finfo = (struct octnet_buf_free_info *)&skb->cb; + + lio = finfo->lio; + g = finfo->g; + frags = skb_shinfo(skb)->nr_frags; + + dma_unmap_single(&lio->oct_dev->pci_dev->dev, + g->sg[0].ptr[0], (skb->len - skb->data_len), + DMA_TO_DEVICE); + + i = 1; + while (frags--) { + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; + + pci_unmap_page((lio->oct_dev)->pci_dev, + g->sg[(i >> 2)].ptr[(i & 3)], + frag->size, DMA_TO_DEVICE); + i++; + } + + dma_unmap_single(&lio->oct_dev->pci_dev->dev, + finfo->dptr, g->sg_size, + DMA_TO_DEVICE); + + iq = skb_iq(lio, skb); + + spin_lock(&lio->glist_lock[iq]); + list_add_tail(&g->list, &lio->glist[iq]); + spin_unlock(&lio->glist_lock[iq]); + + /* Don't free the skb yet */ + + check_txq_state(lio, skb); +} + /** * \brief Callback for getting interface configuration * @param status status of request @@ -1675,6 +1845,18 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) octeon_register_dispatch_fn(octeon_dev, OPCODE_NIC, OPCODE_NIC_INFO, lio_nic_info, octeon_dev); + /* REQTYPE_RESP_NET and REQTYPE_SOFT_COMMAND do not have free functions. + * They are handled directly. + */ + octeon_register_reqtype_free_fn(octeon_dev, REQTYPE_NORESP_NET, + free_netbuf); + + octeon_register_reqtype_free_fn(octeon_dev, REQTYPE_NORESP_NET_SG, + free_netsgbuf); + + octeon_register_reqtype_free_fn(octeon_dev, REQTYPE_RESP_NET_SG, + free_netsgbuf_with_resp); + for (i = 0; i < octeon_dev->ifcount; i++) { resp_size = sizeof(struct liquidio_if_cfg_resp); ctx_size = sizeof(struct liquidio_if_cfg_context); -- cgit v1.1 From 9217c3cf84d736bffe674a5fc3722cd9a8ef8898 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Wed, 7 Dec 2016 08:54:37 -0800 Subject: liquidio VF rx data and ctl path Adds support for VF receive data control path. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 412 ++++++++++++++++++++- .../net/ethernet/cavium/liquidio/octeon_device.c | 2 +- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 10 + .../ethernet/cavium/liquidio/response_manager.c | 3 +- 4 files changed, 423 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index e4297f8..9989ac3 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -38,6 +38,8 @@ MODULE_PARM_DESC(debug, "NETIF_MSG debug bits"); #define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +/* Bit mask values for lio->ifstate */ +#define LIO_IFSTATE_DROQ_OPS 0x01 #define LIO_IFSTATE_REGISTERED 0x02 #define LIO_IFSTATE_RUNNING 0x04 @@ -55,6 +57,14 @@ struct liquidio_if_cfg_resp { u64 status; }; +struct liquidio_rx_ctl_context { + int octeon_id; + + wait_queue_head_t wc; + + int cond; +}; + union tx_info { u64 u64; struct { @@ -177,6 +187,16 @@ static struct pci_driver liquidio_vf_pci_driver = { }; /** + * \brief check interface state + * @param lio per-network private data + * @param state_flag flag state to check + */ +static int ifstate_check(struct lio *lio, int state_flag) +{ + return atomic_read(&lio->ifstate) & state_flag; +} + +/** * \brief set interface state * @param lio per-network private data * @param state_flag flag state to set @@ -510,6 +530,31 @@ static void update_link_status(struct net_device *netdev, } } +static void update_txq_status(struct octeon_device *oct, int iq_num) +{ + struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; + struct net_device *netdev; + struct lio *lio; + + netdev = oct->props[iq->ifidx].netdev; + lio = GET_LIO(netdev); + if (netif_is_multiqueue(netdev)) { + if (__netif_subqueue_stopped(netdev, iq->q_index) && + lio->linfo.link.s.link_up && + (!octnet_iq_is_full(oct, iq_num))) { + netif_wake_subqueue(netdev, iq->q_index); + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, + tx_restart, 1); + } else { + if (!octnet_iq_is_full(oct, lio->txq)) { + INCR_INSTRQUEUE_PKT_COUNT( + lio->oct_dev, lio->txq, tx_restart, 1); + wake_q(netdev, lio->txq); + } + } + } +} + static int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) { @@ -818,6 +863,91 @@ static void octeon_destroy_resources(struct octeon_device *oct) } /** + * \brief Callback for rx ctrl + * @param status status of request + * @param buf pointer to resp structure + */ +static void rx_ctl_callback(struct octeon_device *oct, + u32 status, void *buf) +{ + struct octeon_soft_command *sc = (struct octeon_soft_command *)buf; + struct liquidio_rx_ctl_context *ctx; + + ctx = (struct liquidio_rx_ctl_context *)sc->ctxptr; + + oct = lio_get_device(ctx->octeon_id); + if (status) + dev_err(&oct->pci_dev->dev, "rx ctl instruction failed. Status: %llx\n", + CVM_CAST64(status)); + WRITE_ONCE(ctx->cond, 1); + + /* This barrier is required to be sure that the response has been + * written fully before waking up the handler + */ + wmb(); + + wake_up_interruptible(&ctx->wc); +} + +/** + * \brief Send Rx control command + * @param lio per-network private data + * @param start_stop whether to start or stop + */ +static void send_rx_ctrl_cmd(struct lio *lio, int start_stop) +{ + struct octeon_device *oct = (struct octeon_device *)lio->oct_dev; + int ctx_size = sizeof(struct liquidio_rx_ctl_context); + struct liquidio_rx_ctl_context *ctx; + struct octeon_soft_command *sc; + union octnet_cmd *ncmd; + int retval; + + if (oct->props[lio->ifidx].rx_on == start_stop) + return; + + sc = (struct octeon_soft_command *) + octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, + 16, ctx_size); + + ncmd = (union octnet_cmd *)sc->virtdptr; + ctx = (struct liquidio_rx_ctl_context *)sc->ctxptr; + + WRITE_ONCE(ctx->cond, 0); + ctx->octeon_id = lio_get_device_id(oct); + init_waitqueue_head(&ctx->wc); + + ncmd->u64 = 0; + ncmd->s.cmd = OCTNET_CMD_RX_CTL; + ncmd->s.param1 = start_stop; + + octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3)); + + sc->iq_no = lio->linfo.txpciq[0].s.q_no; + + octeon_prepare_soft_command(oct, sc, OPCODE_NIC, + OPCODE_NIC_CMD, 0, 0, 0); + + sc->callback = rx_ctl_callback; + sc->callback_arg = sc; + sc->wait_time = 5000; + + retval = octeon_send_soft_command(oct, sc); + if (retval == IQ_SEND_FAILED) { + netif_info(lio, rx_err, lio->netdev, "Failed to send RX Control message\n"); + } else { + /* Sleep on a wait queue till the cond flag indicates that the + * response arrived or timed-out. + */ + if (sleep_cond(&ctx->wc, &ctx->cond) == -EINTR) + return; + oct->props[lio->ifidx].rx_on = start_stop; + } + + octeon_free_soft_command(oct, sc); +} + +/** * \brief Destroy NIC device interface * @param oct octeon device * @param ifidx which interface to destroy @@ -828,6 +958,7 @@ static void octeon_destroy_resources(struct octeon_device *oct) static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) { struct net_device *netdev = oct->props[ifidx].netdev; + struct napi_struct *napi, *n; struct lio *lio; if (!netdev) { @@ -843,6 +974,15 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) if (atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) liquidio_stop(netdev); + if (oct->props[lio->ifidx].napi_enabled == 1) { + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + napi_disable(napi); + + oct->props[lio->ifidx].napi_enabled = 0; + + oct->droq[0]->ops.poll_mode = 0; + } + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) unregister_netdev(netdev); @@ -863,7 +1003,8 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) */ static int liquidio_stop_nic_module(struct octeon_device *oct) { - int i; + struct lio *lio; + int i, j; dev_dbg(&oct->pci_dev->dev, "Stopping network interfaces\n"); if (!oct->ifcount) { @@ -871,6 +1012,17 @@ static int liquidio_stop_nic_module(struct octeon_device *oct) return 1; } + spin_lock_bh(&oct->cmd_resp_wqlock); + oct->cmd_resp_state = OCT_DRV_OFFLINE; + spin_unlock_bh(&oct->cmd_resp_wqlock); + + for (i = 0; i < oct->ifcount; i++) { + lio = GET_LIO(oct->props[i].netdev); + for (j = 0; j < lio->linfo.num_rxpciq; j++) + octeon_unregister_droq_ops(oct, + lio->linfo.rxpciq[j].s.q_no); + } + for (i = 0; i < oct->ifcount; i++) liquidio_destroy_nic_device(oct, i); @@ -1091,6 +1243,41 @@ static void free_netsgbuf_with_resp(void *buf) } /** + * \brief Setup output queue + * @param oct octeon device + * @param q_no which queue + * @param num_descs how many descriptors + * @param desc_size size of each descriptor + * @param app_ctx application context + */ +static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, + int desc_size, void *app_ctx) +{ + int ret_val; + + dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); + /* droq creation and local register settings. */ + ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); + if (ret_val < 0) + return ret_val; + + if (ret_val == 1) { + dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); + return 0; + } + + /* Enable the droq queues */ + octeon_set_droq_pkt_op(oct, q_no, 1); + + /* Send Credit for Octeon Output queues. Credits are always + * sent after the output queue is enabled. + */ + writel(oct->droq[q_no]->max_count, oct->droq[q_no]->pkts_credit_reg); + + return ret_val; +} + +/** * \brief Callback for getting interface configuration * @param status status of request * @param buf pointer to resp structure @@ -1142,6 +1329,155 @@ static u16 select_q(struct net_device *dev, struct sk_buff *skb, return (u16)(qindex % (lio->linfo.num_txpciq)); } +/** Routine to push packets arriving on Octeon interface upto network layer. + * @param oct_id - octeon device id. + * @param skbuff - skbuff struct to be passed to network layer. + * @param len - size of total data received. + * @param rh - Control header associated with the packet + * @param param - additional control data with the packet + * @param arg - farg registered in droq_ops + */ +static void +liquidio_push_packet(u32 octeon_id __attribute__((unused)), + void *skbuff, + u32 len, + union octeon_rh *rh, + void *param, + void *arg) +{ + struct napi_struct *napi = param; + struct octeon_droq *droq = + container_of(param, struct octeon_droq, napi); + struct net_device *netdev = (struct net_device *)arg; + struct sk_buff *skb = (struct sk_buff *)skbuff; + + if (netdev) { + struct lio *lio = GET_LIO(netdev); + int packet_was_received; + + /* Do not proceed if the interface is not in RUNNING state. */ + if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { + recv_buffer_free(skb); + droq->stats.rx_dropped++; + return; + } + + skb->dev = netdev; + + skb_record_rx_queue(skb, droq->q_no); + if (likely(len > MIN_SKB_SIZE)) { + struct octeon_skb_page_info *pg_info; + unsigned char *va; + + pg_info = ((struct octeon_skb_page_info *)(skb->cb)); + if (pg_info->page) { + /* For Paged allocation use the frags */ + va = page_address(pg_info->page) + + pg_info->page_offset; + memcpy(skb->data, va, MIN_SKB_SIZE); + skb_put(skb, MIN_SKB_SIZE); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + pg_info->page, + pg_info->page_offset + + MIN_SKB_SIZE, + len - MIN_SKB_SIZE, + LIO_RXBUFFER_SZ); + } + } else { + struct octeon_skb_page_info *pg_info = + ((struct octeon_skb_page_info *)(skb->cb)); + skb_copy_to_linear_data(skb, + page_address(pg_info->page) + + pg_info->page_offset, len); + skb_put(skb, len); + put_page(pg_info->page); + } + + skb_pull(skb, rh->r_dh.len * 8); + skb->protocol = eth_type_trans(skb, skb->dev); + + if ((netdev->features & NETIF_F_RXCSUM) && + (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)) + /* checksum has already been verified */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + + packet_was_received = (napi_gro_receive(napi, skb) != GRO_DROP); + + if (packet_was_received) { + droq->stats.rx_bytes_received += len; + droq->stats.rx_pkts_received++; + netdev->last_rx = jiffies; + } else { + droq->stats.rx_dropped++; + netif_info(lio, rx_err, lio->netdev, + "droq:%d error rx_dropped:%llu\n", + droq->q_no, droq->stats.rx_dropped); + } + + } else { + recv_buffer_free(skb); + } +} + +/** + * \brief callback when receive interrupt occurs and we are in NAPI mode + * @param arg pointer to octeon output queue + */ +static void liquidio_vf_napi_drv_callback(void *arg) +{ + struct octeon_droq *droq = arg; + + napi_schedule_irqoff(&droq->napi); +} + +/** + * \brief Entry point for NAPI polling + * @param napi NAPI structure + * @param budget maximum number of items to process + */ +static int liquidio_napi_poll(struct napi_struct *napi, int budget) +{ + struct octeon_instr_queue *iq; + struct octeon_device *oct; + struct octeon_droq *droq; + int tx_done = 0, iq_no; + int work_done; + + droq = container_of(napi, struct octeon_droq, napi); + oct = droq->oct_dev; + iq_no = droq->q_no; + + /* Handle Droq descriptors */ + work_done = octeon_process_droq_poll_cmd(oct, droq->q_no, + POLL_EVENT_PROCESS_PKTS, + budget); + + /* Flush the instruction queue */ + iq = oct->instr_queue[iq_no]; + if (iq) { + /* Process iq buffers with in the budget limits */ + tx_done = octeon_flush_iq(oct, iq, 1, budget); + /* Update iq read-index rather than waiting for next interrupt. + * Return back if tx_done is false. + */ + update_txq_status(oct, iq_no); + } else { + dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", + __func__, iq_no); + } + + if ((work_done < budget) && (tx_done)) { + napi_complete(napi); + octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, + POLL_EVENT_ENABLE_INTR, 0); + return 0; + } + + return (!tx_done) ? (budget) : (work_done); +} + /** * \brief Setup input and output queues * @param octeon_dev octeon device @@ -1153,16 +1489,68 @@ static u16 select_q(struct net_device *dev, struct sk_buff *skb, */ static int setup_io_queues(struct octeon_device *octeon_dev, int ifidx) { + struct octeon_droq_ops droq_ops; struct net_device *netdev; + static int cpu_id_modulus; + struct octeon_droq *droq; + struct napi_struct *napi; + static int cpu_id; int num_tx_descs; struct lio *lio; int retval = 0; - int q; + int q, q_no; netdev = octeon_dev->props[ifidx].netdev; lio = GET_LIO(netdev); + memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); + + droq_ops.fptr = liquidio_push_packet; + droq_ops.farg = netdev; + + droq_ops.poll_mode = 1; + droq_ops.napi_fn = liquidio_vf_napi_drv_callback; + cpu_id = 0; + cpu_id_modulus = num_present_cpus(); + + /* set up DROQs. */ + for (q = 0; q < lio->linfo.num_rxpciq; q++) { + q_no = lio->linfo.rxpciq[q].s.q_no; + + retval = octeon_setup_droq( + octeon_dev, q_no, + CFG_GET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(octeon_dev), + lio->ifidx), + CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(octeon_get_conf(octeon_dev), + lio->ifidx), + NULL); + if (retval) { + dev_err(&octeon_dev->pci_dev->dev, + "%s : Runtime DROQ(RxQ) creation failed.\n", + __func__); + return 1; + } + + droq = octeon_dev->droq[q_no]; + napi = &droq->napi; + netif_napi_add(netdev, napi, liquidio_napi_poll, 64); + + /* designate a CPU for this droq */ + droq->cpu_id = cpu_id; + cpu_id++; + if (cpu_id >= cpu_id_modulus) + cpu_id = 0; + + octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); + } + + /* 23XX VF can send/recv control messages (via the first VF-owned + * droq) from the firmware even if the ethX interface is down, + * so that's why poll_mode must be off for the first droq. + */ + octeon_dev->droq[0]->ops.poll_mode = 0; + /* set up IQs. */ for (q = 0; q < lio->linfo.num_txpciq; q++) { num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( @@ -1189,6 +1577,16 @@ static int liquidio_open(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; + struct napi_struct *napi, *n; + + if (!oct->props[lio->ifidx].napi_enabled) { + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + napi_enable(napi); + + oct->props[lio->ifidx].napi_enabled = 1; + + oct->droq[0]->ops.poll_mode = 1; + } ifstate_set(lio, LIO_IFSTATE_RUNNING); @@ -1198,6 +1596,9 @@ static int liquidio_open(struct net_device *netdev) netif_info(lio, ifup, lio->netdev, "Interface Open, ready for traffic\n"); start_txq(netdev); + /* tell Octeon to start forwarding packets to host */ + send_rx_ctrl_cmd(lio, 1); + dev_info(&oct->pci_dev->dev, "%s interface is opened\n", netdev->name); return 0; @@ -1220,6 +1621,9 @@ static int liquidio_stop(struct net_device *netdev) netif_carrier_off(netdev); lio->link_changes++; + /* tell Octeon to stop forwarding packets to host */ + send_rx_ctrl_cmd(lio, 0); + ifstate_reset(lio, LIO_IFSTATE_RUNNING); txqs_stop(netdev); @@ -2016,6 +2420,8 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) goto setup_nic_dev_fail; } + ifstate_set(lio, LIO_IFSTATE_DROQ_OPS); + /* For VFs, enable Octeon device interrupts here, * as this is contingent upon IO queue setup */ @@ -2026,8 +2432,10 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) * tx and rx queues */ lio->txq = lio->linfo.txpciq[0].s.q_no; + lio->rxq = lio->linfo.rxpciq[0].s.q_no; lio->tx_qsize = octeon_get_tx_qsize(octeon_dev, lio->txq); + lio->rx_qsize = octeon_get_rx_qsize(octeon_dev, lio->rxq); if (setup_glists(lio, num_iqueues)) { dev_err(&octeon_dev->pci_dev->dev, diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 583818e..a8df493 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -1374,7 +1374,7 @@ void lio_enable_irq(struct octeon_droq *droq, struct octeon_instr_queue *iq) /*write resend. Writing RESEND in SLI_PKTX_CNTS should be enough *to trigger tx interrupts as well, if they are pending. */ - if (oct && OCTEON_CN23XX_PF(oct)) { + if (oct && (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct))) { if (droq) writeq(CN23XX_INTR_RESEND, droq->pkts_sent_reg); /*we race with firmrware here. read and write the IN_DONE_CNTS*/ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 8bf1ac76..0be87d1 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -28,6 +28,7 @@ #include "cn66xx_regs.h" #include "cn66xx_device.h" #include "cn23xx_pf_device.h" +#include "cn23xx_vf_device.h" struct niclist { struct list_head list; @@ -261,6 +262,11 @@ int octeon_init_droq(struct octeon_device *oct, c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf23); c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf23); + } else if (OCTEON_CN23XX_VF(oct)) { + struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_vf); + + c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf23); + c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf23); } else { return 1; } @@ -889,6 +895,10 @@ octeon_process_droq_poll_cmd(struct octeon_device *oct, u32 q_no, int cmd, lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]); } break; + + case OCTEON_CN23XX_VF_VID: + lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]); + break; } return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c index fdaf742..2fbaae9 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c @@ -84,7 +84,8 @@ int lio_process_ordered_list(struct octeon_device *octeon_dev, sc = (struct octeon_soft_command *)ordered_sc_list-> head.next; - if (OCTEON_CN23XX_PF(octeon_dev)) { + if (OCTEON_CN23XX_PF(octeon_dev) || + OCTEON_CN23XX_VF(octeon_dev)) { rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd3.rdp; rptr = sc->cmd.cmd3.rptr; } else { -- cgit v1.1 From c8c8b127091b758f5768f906bcdeeb88bc9951ca Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Dec 2016 09:19:33 -0800 Subject: udp: under rx pressure, try to condense skbs Under UDP flood, many softirq producers try to add packets to UDP receive queue, and one user thread is burning one cpu trying to dequeue packets as fast as possible. Two parts of the per packet cost are : - copying payload from kernel space to user space, - freeing memory pieces associated with skb. If socket is under pressure, softirq handler(s) can try to pull in skb->head the payload of the packet if it fits. Meaning the softirq handler(s) can free/reuse the page fragment immediately, instead of letting udp_recvmsg() do this hundreds of usec later, possibly from another node. Additional gains : - We reduce skb->truesize and thus can store more packets per SO_RCVBUF - We avoid cache line misses at copyout() time and consume_skb() time, and avoid one put_page() with potential alien freeing on NUMA hosts. This comes at the cost of a copy, bounded to available tail room, which is usually small. (We might have to fix GRO_MAX_HEAD which looks bigger than necessary) This patch gave me about 5 % increase in throughput in my tests. skb_condense() helper could probably used in other contexts. Signed-off-by: Eric Dumazet Cc: Paolo Abeni Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 ++ net/core/skbuff.c | 28 ++++++++++++++++++++++++++++ net/ipv4/udp.c | 12 +++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 9c535fb..0cd92b0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1966,6 +1966,8 @@ static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len) return __pskb_pull_tail(skb, len - skb_headlen(skb)) != NULL; } +void skb_condense(struct sk_buff *skb); + /** * skb_headroom - bytes at buffer head * @skb: buffer to check diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b45cd14..84151cf 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4931,3 +4931,31 @@ struct sk_buff *pskb_extract(struct sk_buff *skb, int off, return clone; } EXPORT_SYMBOL(pskb_extract); + +/** + * skb_condense - try to get rid of fragments/frag_list if possible + * @skb: buffer + * + * Can be used to save memory before skb is added to a busy queue. + * If packet has bytes in frags and enough tail room in skb->head, + * pull all of them, so that we can free the frags right now and adjust + * truesize. + * Notes: + * We do not reallocate skb->head thus can not fail. + * Caller must re-evaluate skb->truesize if needed. + */ +void skb_condense(struct sk_buff *skb) +{ + if (!skb->data_len || + skb->data_len > skb->end - skb->tail || + skb_cloned(skb)) + return; + + /* Nice, we can free page frag(s) right now */ + __pskb_pull_tail(skb, skb->data_len); + + /* Now adjust skb->truesize, since __pskb_pull_tail() does + * not do this. + */ + skb->truesize = SKB_TRUESIZE(skb_end_offset(skb)); +} diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 16d88ba..f5628ad 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1199,7 +1199,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) { struct sk_buff_head *list = &sk->sk_receive_queue; int rmem, delta, amt, err = -ENOMEM; - int size = skb->truesize; + int size; /* try to avoid the costly atomic add/sub pair when the receive * queue is full; always allow at least a packet @@ -1208,6 +1208,16 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) if (rmem > sk->sk_rcvbuf) goto drop; + /* Under mem pressure, it might be helpful to help udp_recvmsg() + * having linear skbs : + * - Reduce memory overhead and thus increase receive queue capacity + * - Less cache line misses at copyout() time + * - Less work at consume_skb() (less alien page frag freeing) + */ + if (rmem > (sk->sk_rcvbuf >> 1)) + skb_condense(skb); + size = skb->truesize; + /* we drop only if the receive buf is full and the receive * queue contains some other skb */ -- cgit v1.1 From 3665f3817cd354ab7a811b3a4f282c4f5cb1a0d0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Dec 2016 10:05:36 -0800 Subject: net: do not read sk_drops if application does not care sk_drops can be an often written field, do not read it unless application showed interest. Note that sk_drops can be read via inet_diag, so applications can avoid getting this info from every received packet. In the future, 'reading' sk_drops might require folding per node or per cpu fields, and thus become even more expensive than today. Signed-off-by: Eric Dumazet Cc: Paolo Abeni Signed-off-by: David S. Miller --- include/net/sock.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/sock.h b/include/net/sock.h index 2729e77..e17aa3d 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2163,7 +2163,8 @@ struct sock_skb_cb { static inline void sock_skb_set_dropcount(const struct sock *sk, struct sk_buff *skb) { - SOCK_SKB_CB(skb)->dropcount = atomic_read(&sk->sk_drops); + SOCK_SKB_CB(skb)->dropcount = sock_flag(sk, SOCK_RXQ_OVFL) ? + atomic_read(&sk->sk_drops) : 0; } static inline void sk_drops_add(struct sock *sk, const struct sk_buff *skb) -- cgit v1.1 From d2a4dd37f6b41fbcad76efbf63124eb3126c66fe Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 7 Dec 2016 10:57:59 -0800 Subject: bpf: fix state equivalence Commmits 57a09bf0a416 ("bpf: Detect identical PTR_TO_MAP_VALUE_OR_NULL registers") and 484611357c19 ("bpf: allow access into map value arrays") by themselves are correct, but in combination they make state equivalence ignore 'id' field of the register state which can lead to accepting invalid program. Fixes: 57a09bf0a416 ("bpf: Detect identical PTR_TO_MAP_VALUE_OR_NULL registers") Fixes: 484611357c19 ("bpf: allow access into map value arrays") Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/bpf_verifier.h | 14 +++++++------- kernel/bpf/verifier.c | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 7453c12..a13b031 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -18,13 +18,6 @@ struct bpf_reg_state { enum bpf_reg_type type; - /* - * Used to determine if any memory access using this register will - * result in a bad access. - */ - s64 min_value; - u64 max_value; - u32 id; union { /* valid when type == CONST_IMM | PTR_TO_STACK | UNKNOWN_VALUE */ s64 imm; @@ -40,6 +33,13 @@ struct bpf_reg_state { */ struct bpf_map *map_ptr; }; + u32 id; + /* Used to determine if any memory access using this register will + * result in a bad access. These two fields must be last. + * See states_equal() + */ + s64 min_value; + u64 max_value; }; enum bpf_stack_slot_type { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index da9fb2a..5b14f85 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2528,7 +2528,7 @@ static bool states_equal(struct bpf_verifier_env *env, * we didn't do a variable access into a map then we are a-ok. */ if (!varlen_map_access && - rold->type == rcur->type && rold->imm == rcur->imm) + memcmp(rold, rcur, offsetofend(struct bpf_reg_state, id)) == 0) continue; /* If we didn't map access then again we don't care about the -- cgit v1.1 From f38e7a32ee4fc9c8aeeac59e6e0462cd281586e3 Mon Sep 17 00:00:00 2001 From: "Woojung.Huh@microchip.com" Date: Wed, 7 Dec 2016 20:26:07 +0000 Subject: phy: add phy fixup unregister functions >From : Woojung Huh Add functions to unregister phy fixup for modules. int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask) Unregister phy fixup from phy_fixup_list per bus_id, phy_uid & phy_uid_mask int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask) Unregister phy fixup from phy_fixup_list. Use it for fixup registered by phy_register_fixup_for_uid() int phy_unregister_fixup_for_id(const char *bus_id) Unregister phy fixup from phy_fixup_list. Use it for fixup registered by phy_register_fixup_for_id() Signed-off-by: Woojung Huh Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 9 ++++++++ drivers/net/phy/phy_device.c | 47 ++++++++++++++++++++++++++++++++++++++++ include/linux/phy.h | 4 ++++ 3 files changed, 60 insertions(+) diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index e017d93..16f90d8 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -407,6 +407,15 @@ Board Fixups The stubs set one of the two matching criteria, and set the other one to match anything. + When phy_register_fixup() or *_for_uid()/*_for_id() is called at module, + unregister fixup and free allocate memory are required. + + Call one of following function before unloading module. + + int phy_unregister_fixup(const char *phy_id, u32 phy_uid, u32 phy_uid_mask); + int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask); + int phy_register_fixup_for_id(const char *phy_id); + Standards IEEE Standard 802.3: CSMA/CD Access Method and Physical Layer Specifications, Section Two: diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index aeaf1bc..32fa7c7 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -235,6 +235,53 @@ int phy_register_fixup_for_id(const char *bus_id, } EXPORT_SYMBOL(phy_register_fixup_for_id); +/** + * phy_unregister_fixup - remove a phy_fixup from the list + * @bus_id: A string matches fixup->bus_id (or PHY_ANY_ID) in phy_fixup_list + * @phy_uid: A phy id matches fixup->phy_id (or PHY_ANY_UID) in phy_fixup_list + * @phy_uid_mask: Applied to phy_uid and fixup->phy_uid before comparison + */ +int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask) +{ + struct list_head *pos, *n; + struct phy_fixup *fixup; + int ret; + + ret = -ENODEV; + + mutex_lock(&phy_fixup_lock); + list_for_each_safe(pos, n, &phy_fixup_list) { + fixup = list_entry(pos, struct phy_fixup, list); + + if ((!strcmp(fixup->bus_id, bus_id)) && + ((fixup->phy_uid & phy_uid_mask) == + (phy_uid & phy_uid_mask))) { + list_del(&fixup->list); + kfree(fixup); + ret = 0; + break; + } + } + mutex_unlock(&phy_fixup_lock); + + return ret; +} +EXPORT_SYMBOL(phy_unregister_fixup); + +/* Unregisters a fixup of any PHY with the UID in phy_uid */ +int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask) +{ + return phy_unregister_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask); +} +EXPORT_SYMBOL(phy_unregister_fixup_for_uid); + +/* Unregisters a fixup of the PHY with id string bus_id */ +int phy_unregister_fixup_for_id(const char *bus_id) +{ + return phy_unregister_fixup(bus_id, PHY_ANY_UID, 0xffffffff); +} +EXPORT_SYMBOL(phy_unregister_fixup_for_id); + /* Returns 1 if fixup matches phydev in bus_id and phy_uid. * Fixups can be set to match any in one or more fields. */ diff --git a/include/linux/phy.h b/include/linux/phy.h index feb8a98..f7d95f6 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -860,6 +860,10 @@ int phy_register_fixup_for_id(const char *bus_id, int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, int (*run)(struct phy_device *)); +int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask); +int phy_unregister_fixup_for_id(const char *bus_id); +int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask); + int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable); int phy_get_eee_err(struct phy_device *phydev); int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data); -- cgit v1.1 From 02dc1f3d613d5a859513d7416c9aca370425a7e0 Mon Sep 17 00:00:00 2001 From: Woojung Huh Date: Wed, 7 Dec 2016 20:26:25 +0000 Subject: lan78xx: add LAN7801 MAC only support Add LAN7801 MAC only support with phy fixup functions. Signed-off-by: Woojung Huh Signed-off-by: David S. Miller --- drivers/net/usb/Kconfig | 5 +++ drivers/net/usb/lan78xx.c | 109 +++++++++++++++++++++++++++++++++++++++++++++- drivers/net/usb/lan78xx.h | 14 ++++++ 3 files changed, 126 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index cdde590..3dd490f5 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -114,6 +114,11 @@ config USB_LAN78XX help This option adds support for Microchip LAN78XX based USB 2 & USB 3 10/100/1000 Ethernet adapters. + LAN7800 : USB 3 to 10/100/1000 Ethernet adapter + LAN7850 : USB 2 to 10/100/1000 Ethernet adapter + LAN7801 : USB 3 to 10/100/1000 Ethernet adapter (MAC only) + + Proper PHY driver is required for LAN7801. To compile this driver as a module, choose M here: the module will be called lan78xx. diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 019f758..08f8703 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -40,7 +40,7 @@ #define DRIVER_AUTHOR "WOOJUNG HUH " #define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices" #define DRIVER_NAME "lan78xx" -#define DRIVER_VERSION "1.0.5" +#define DRIVER_VERSION "1.0.6" #define TX_TIMEOUT_JIFFIES (5 * HZ) #define THROTTLE_JIFFIES (HZ / 8) @@ -67,6 +67,7 @@ #define LAN78XX_USB_VENDOR_ID (0x0424) #define LAN7800_USB_PRODUCT_ID (0x7800) #define LAN7850_USB_PRODUCT_ID (0x7850) +#define LAN7801_USB_PRODUCT_ID (0x7801) #define LAN78XX_EEPROM_MAGIC (0x78A5) #define LAN78XX_OTP_MAGIC (0x78F3) @@ -390,6 +391,7 @@ struct lan78xx_net { u32 chipid; u32 chiprev; struct mii_bus *mdiobus; + phy_interface_t interface; int fc_autoneg; u8 fc_request_control; @@ -400,6 +402,10 @@ struct lan78xx_net { struct irq_domain_data domain_data; }; +/* define external phy id */ +#define PHY_LAN8835 (0x0007C130) +#define PHY_KSZ9031RNX (0x00221620) + /* use ethtool to change the level for any given device */ static int msg_level = -1; module_param(msg_level, int, 0); @@ -1697,6 +1703,7 @@ static int lan78xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx) done: mutex_unlock(&dev->phy_mutex); usb_autopm_put_interface(dev->intf); + return ret; } @@ -1759,6 +1766,10 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev) /* set to internal PHY id */ dev->mdiobus->phy_mask = ~(1 << 1); break; + case ID_REV_CHIP_ID_7801_: + /* scan thru PHYAD[2..0] */ + dev->mdiobus->phy_mask = ~(0xFF); + break; } ret = mdiobus_register(dev->mdiobus); @@ -1933,6 +1944,47 @@ static void lan78xx_remove_irq_domain(struct lan78xx_net *dev) dev->domain_data.irqdomain = NULL; } +static int lan8835_fixup(struct phy_device *phydev) +{ + int buf; + int ret; + struct lan78xx_net *dev = netdev_priv(phydev->attached_dev); + + /* LED2/PME_N/IRQ_N/RGMII_ID pin to IRQ_N mode */ + buf = phy_read_mmd_indirect(phydev, 0x8010, 3); + buf &= ~0x1800; + buf |= 0x0800; + phy_write_mmd_indirect(phydev, 0x8010, 3, buf); + + /* RGMII MAC TXC Delay Enable */ + ret = lan78xx_write_reg(dev, MAC_RGMII_ID, + MAC_RGMII_ID_TXC_DELAY_EN_); + + /* RGMII TX DLL Tune Adjust */ + ret = lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00); + + dev->interface = PHY_INTERFACE_MODE_RGMII_TXID; + + return 1; +} + +static int ksz9031rnx_fixup(struct phy_device *phydev) +{ + struct lan78xx_net *dev = netdev_priv(phydev->attached_dev); + + /* Micrel9301RNX PHY configuration */ + /* RGMII Control Signal Pad Skew */ + phy_write_mmd_indirect(phydev, 4, 2, 0x0077); + /* RGMII RX Data Pad Skew */ + phy_write_mmd_indirect(phydev, 5, 2, 0x7777); + /* RGMII RX Clock Pad Skew */ + phy_write_mmd_indirect(phydev, 8, 2, 0x1FF); + + dev->interface = PHY_INTERFACE_MODE_RGMII_RXID; + + return 1; +} + static int lan78xx_phy_init(struct lan78xx_net *dev) { int ret; @@ -1945,6 +1997,42 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) return -EIO; } + if ((dev->chipid == ID_REV_CHIP_ID_7800_) || + (dev->chipid == ID_REV_CHIP_ID_7850_)) { + phydev->is_internal = true; + dev->interface = PHY_INTERFACE_MODE_GMII; + + } else if (dev->chipid == ID_REV_CHIP_ID_7801_) { + if (!phydev->drv) { + netdev_err(dev->net, "no PHY driver found\n"); + return -EIO; + } + + dev->interface = PHY_INTERFACE_MODE_RGMII; + + /* external PHY fixup for KSZ9031RNX */ + ret = phy_register_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0, + ksz9031rnx_fixup); + if (ret < 0) { + netdev_err(dev->net, "fail to register fixup\n"); + return ret; + } + /* external PHY fixup for LAN8835 */ + ret = phy_register_fixup_for_uid(PHY_LAN8835, 0xfffffff0, + lan8835_fixup); + if (ret < 0) { + netdev_err(dev->net, "fail to register fixup\n"); + return ret; + } + /* add more external PHY fixup here if needed */ + + phydev->is_internal = false; + } else { + netdev_err(dev->net, "unknown ID found\n"); + ret = -EIO; + goto error; + } + /* if phyirq is not set, use polling mode in phylib */ if (dev->domain_data.phyirq > 0) phydev->irq = dev->domain_data.phyirq; @@ -1957,7 +2045,7 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) ret = phy_connect_direct(dev->net, phydev, lan78xx_link_status_change, - PHY_INTERFACE_MODE_GMII); + dev->interface); if (ret) { netdev_err(dev->net, "can't attach PHY to %s\n", dev->mdiobus->id); @@ -1982,6 +2070,12 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); return 0; + +error: + phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0); + phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0); + + return ret; } static int lan78xx_set_rx_max_frame_length(struct lan78xx_net *dev, int size) @@ -2338,6 +2432,9 @@ static int lan78xx_reset(struct lan78xx_net *dev) } while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_)); ret = lan78xx_read_reg(dev, MAC_CR, &buf); + /* LAN7801 only has RGMII mode */ + if (dev->chipid == ID_REV_CHIP_ID_7801_) + buf &= ~MAC_CR_GMII_EN_; buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; ret = lan78xx_write_reg(dev, MAC_CR, buf); @@ -2464,8 +2561,12 @@ static int lan78xx_stop(struct net_device *net) if (timer_pending(&dev->stat_monitor)) del_timer_sync(&dev->stat_monitor); + phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0); + phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0); + phy_stop(net->phydev); phy_disconnect(net->phydev); + net->phydev = NULL; clear_bit(EVENT_DEV_OPEN, &dev->flags); @@ -3888,6 +3989,10 @@ static const struct usb_device_id products[] = { /* LAN7850 USB Gigabit Ethernet Device */ USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7850_USB_PRODUCT_ID), }, + { + /* LAN7801 USB Gigabit Ethernet Device */ + USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7801_USB_PRODUCT_ID), + }, {}, }; MODULE_DEVICE_TABLE(usb, products); diff --git a/drivers/net/usb/lan78xx.h b/drivers/net/usb/lan78xx.h index 4092790..25aa546 100644 --- a/drivers/net/usb/lan78xx.h +++ b/drivers/net/usb/lan78xx.h @@ -108,6 +108,7 @@ #define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) #define ID_REV_CHIP_ID_7800_ (0x7800) #define ID_REV_CHIP_ID_7850_ (0x7850) +#define ID_REV_CHIP_ID_7801_ (0x7801) #define FPGA_REV (0x04) #define FPGA_REV_MINOR_MASK_ (0x0000FF00) @@ -550,6 +551,7 @@ #define LTM_INACTIVE1_TIMER10_ (0x0000FFFF) #define MAC_CR (0x100) +#define MAC_CR_GMII_EN_ (0x00080000) #define MAC_CR_EEE_TX_CLK_STOP_EN_ (0x00040000) #define MAC_CR_EEE_EN_ (0x00020000) #define MAC_CR_EEE_TLAR_EN_ (0x00010000) @@ -787,6 +789,18 @@ #define PHY_DEV_ID_MODEL_MASK_ (0x0FC00000) #define PHY_DEV_ID_OUI_MASK_ (0x003FFFFF) +#define RGMII_TX_BYP_DLL (0x708) +#define RGMII_TX_BYP_DLL_TX_TUNE_ADJ_MASK_ (0x000FC00) +#define RGMII_TX_BYP_DLL_TX_TUNE_SEL_MASK_ (0x00003F0) +#define RGMII_TX_BYP_DLL_TX_DLL_RESET_ (0x0000002) +#define RGMII_TX_BYP_DLL_TX_DLL_BYPASS_ (0x0000001) + +#define RGMII_RX_BYP_DLL (0x70C) +#define RGMII_RX_BYP_DLL_RX_TUNE_ADJ_MASK_ (0x000FC00) +#define RGMII_RX_BYP_DLL_RX_TUNE_SEL_MASK_ (0x00003F0) +#define RGMII_RX_BYP_DLL_RX_DLL_RESET_ (0x0000002) +#define RGMII_RX_BYP_DLL_RX_DLL_BYPASS_ (0x0000001) + #define OTP_BASE_ADDR (0x00001000) #define OTP_ADDR_RANGE_ (0x1FF) -- cgit v1.1 From 17bedab2723145d17b14084430743549e6943d03 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 7 Dec 2016 15:53:11 -0800 Subject: bpf: xdp: Allow head adjustment in XDP prog This patch allows XDP prog to extend/remove the packet data at the head (like adding or removing header). It is done by adding a new XDP helper bpf_xdp_adjust_head(). It also renames bpf_helper_changes_skb_data() to bpf_helper_changes_pkt_data() to better reflect that XDP prog does not work on skb. This patch adds one "xdp_adjust_head" bit to bpf_prog for the XDP-capable driver to check if the XDP prog requires bpf_xdp_adjust_head() support. The driver can then decide to error out during XDP_SETUP_PROG. Signed-off-by: Martin KaFai Lau Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Acked-by: John Fastabend Signed-off-by: David S. Miller --- arch/powerpc/net/bpf_jit_comp64.c | 4 ++-- arch/s390/net/bpf_jit_comp.c | 2 +- arch/x86/net/bpf_jit_comp.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 5 ++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 5 ++++ .../net/ethernet/netronome/nfp/nfp_net_common.c | 4 ++++ drivers/net/ethernet/qlogic/qede/qede_main.c | 5 ++++ include/linux/filter.h | 6 +++-- include/uapi/linux/bpf.h | 11 ++++++++- kernel/bpf/core.c | 2 +- kernel/bpf/syscall.c | 2 ++ kernel/bpf/verifier.c | 2 +- net/core/filter.c | 28 ++++++++++++++++++++-- 13 files changed, 67 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index 0fe98a5..73a5cf1 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -766,7 +766,7 @@ emit_clear: func = (u8 *) __bpf_call_base + imm; /* Save skb pointer if we need to re-cache skb data */ - if (bpf_helper_changes_skb_data(func)) + if (bpf_helper_changes_pkt_data(func)) PPC_BPF_STL(3, 1, bpf_jit_stack_local(ctx)); bpf_jit_emit_func_call(image, ctx, (u64)func); @@ -775,7 +775,7 @@ emit_clear: PPC_MR(b2p[BPF_REG_0], 3); /* refresh skb cache */ - if (bpf_helper_changes_skb_data(func)) { + if (bpf_helper_changes_pkt_data(func)) { /* reload skb pointer to r3 */ PPC_BPF_LL(3, 1, bpf_jit_stack_local(ctx)); bpf_jit_emit_skb_loads(image, ctx); diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index bee281f..167b31b 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -981,7 +981,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i EMIT2(0x0d00, REG_14, REG_W1); /* lgr %b0,%r2: load return value into %b0 */ EMIT4(0xb9040000, BPF_REG_0, REG_2); - if (bpf_helper_changes_skb_data((void *)func)) { + if (bpf_helper_changes_pkt_data((void *)func)) { jit->seen |= SEEN_SKB_CHANGE; /* lg %b1,ST_OFF_SKBP(%r15) */ EMIT6_DISP_LH(0xe3000000, 0x0004, BPF_REG_1, REG_0, diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index fe04a04..e76d1af 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -853,7 +853,7 @@ xadd: if (is_imm8(insn->off)) func = (u8 *) __bpf_call_base + imm32; jmp_offset = func - (image + addrs[i]); if (seen_ld_abs) { - reload_skb_data = bpf_helper_changes_skb_data(func); + reload_skb_data = bpf_helper_changes_pkt_data(func); if (reload_skb_data) { EMIT1(0x57); /* push %rdi */ jmp_offset += 22; /* pop, mov, sub, mov */ diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 49a81f1..f441eda 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2686,6 +2686,11 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog) int err; int i; + if (prog && prog->xdp_adjust_head) { + en_err(priv, "Does not support bpf_xdp_adjust_head()\n"); + return -EOPNOTSUPP; + } + xdp_ring_num = prog ? priv->rx_ring_num : 0; /* No need to reconfigure buffers when simply swapping the diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 0702027..cbfa38f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3183,6 +3183,11 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) bool reset, was_opened; int i; + if (prog && prog->xdp_adjust_head) { + netdev_err(netdev, "Does not support bpf_xdp_adjust_head()\n"); + return -EOPNOTSUPP; + } + mutex_lock(&priv->state_lock); if ((netdev->features & NETIF_F_LRO) && prog) { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 00d9a03..e8d4481 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2946,6 +2946,10 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) }; int err; + if (prog && prog->xdp_adjust_head) { + nn_err(nn, "Does not support bpf_xdp_adjust_head()\n"); + return -EOPNOTSUPP; + } if (!prog && !nn->xdp_prog) return 0; if (prog && nn->xdp_prog) { diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index cf1dd14..aecdd1c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -2507,6 +2507,11 @@ static int qede_xdp_set(struct qede_dev *edev, struct bpf_prog *prog) { struct qede_reload_args args; + if (prog && prog->xdp_adjust_head) { + DP_ERR(edev, "Does not support bpf_xdp_adjust_head()\n"); + return -EOPNOTSUPP; + } + /* If we're called, there was already a bpf reference increment */ args.func = &qede_xdp_reload_func; args.u.new_prog = prog; diff --git a/include/linux/filter.h b/include/linux/filter.h index f078d2b..6a16583 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -406,7 +406,8 @@ struct bpf_prog { u16 jited:1, /* Is our filter JIT'ed? */ gpl_compatible:1, /* Is filter GPL compatible? */ cb_access:1, /* Is control block accessed? */ - dst_needed:1; /* Do we need dst entry? */ + dst_needed:1, /* Do we need dst entry? */ + xdp_adjust_head:1; /* Adjusting pkt head? */ kmemcheck_bitfield_end(meta); enum bpf_prog_type type; /* Type of BPF program */ u32 len; /* Number of filter blocks */ @@ -440,6 +441,7 @@ struct bpf_skb_data_end { struct xdp_buff { void *data; void *data_end; + void *data_hard_start; }; /* compute the linear packet data range [data, data_end) which @@ -595,7 +597,7 @@ void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog); -bool bpf_helper_changes_skb_data(void *func); +bool bpf_helper_changes_pkt_data(void *func); struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 6123d9b..0eb0e87 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -424,6 +424,12 @@ union bpf_attr { * @len: length of header to be pushed in front * @flags: Flags (unused for now) * Return: 0 on success or negative error + * + * int bpf_xdp_adjust_head(xdp_md, delta) + * Adjust the xdp_md.data by delta + * @xdp_md: pointer to xdp_md + * @delta: An positive/negative integer to be added to xdp_md.data + * Return: 0 on success or negative on error */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -469,7 +475,8 @@ union bpf_attr { FN(csum_update), \ FN(set_hash_invalid), \ FN(get_numa_node_id), \ - FN(skb_change_head), + FN(skb_change_head), \ + FN(xdp_adjust_head), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -576,6 +583,8 @@ struct bpf_sock { __u32 protocol; }; +#define XDP_PACKET_HEADROOM 256 + /* User return codes for XDP prog type. * A valid XDP program must return one of these defined values. All other * return codes are reserved for future use. Unknown return codes will result diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index bdcc9f4..83e0d15 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1143,7 +1143,7 @@ struct bpf_prog * __weak bpf_int_jit_compile(struct bpf_prog *prog) return prog; } -bool __weak bpf_helper_changes_skb_data(void *func) +bool __weak bpf_helper_changes_pkt_data(void *func) { return false; } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 88f609f..4819ec9 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -579,6 +579,8 @@ static void fixup_bpf_calls(struct bpf_prog *prog) prog->dst_needed = 1; if (insn->imm == BPF_FUNC_get_prandom_u32) bpf_user_rnd_init_once(); + if (insn->imm == BPF_FUNC_xdp_adjust_head) + prog->xdp_adjust_head = 1; if (insn->imm == BPF_FUNC_tail_call) { /* mark bpf_tail_call as different opcode * to avoid conditional branch in diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5b14f85..d28f9a3 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1216,7 +1216,7 @@ static int check_call(struct bpf_verifier_env *env, int func_id) return -EINVAL; } - changes_data = bpf_helper_changes_skb_data(fn->func); + changes_data = bpf_helper_changes_pkt_data(fn->func); memset(&meta, 0, sizeof(meta)); meta.pkt_access = fn->pkt_access; diff --git a/net/core/filter.c b/net/core/filter.c index b751202..b146170 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2234,7 +2234,28 @@ static const struct bpf_func_proto bpf_skb_change_head_proto = { .arg3_type = ARG_ANYTHING, }; -bool bpf_helper_changes_skb_data(void *func) +BPF_CALL_2(bpf_xdp_adjust_head, struct xdp_buff *, xdp, int, offset) +{ + void *data = xdp->data + offset; + + if (unlikely(data < xdp->data_hard_start || + data > xdp->data_end - ETH_HLEN)) + return -EINVAL; + + xdp->data = data; + + return 0; +} + +static const struct bpf_func_proto bpf_xdp_adjust_head_proto = { + .func = bpf_xdp_adjust_head, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + +bool bpf_helper_changes_pkt_data(void *func) { if (func == bpf_skb_vlan_push || func == bpf_skb_vlan_pop || @@ -2244,7 +2265,8 @@ bool bpf_helper_changes_skb_data(void *func) func == bpf_skb_change_tail || func == bpf_skb_pull_data || func == bpf_l3_csum_replace || - func == bpf_l4_csum_replace) + func == bpf_l4_csum_replace || + func == bpf_xdp_adjust_head) return true; return false; @@ -2670,6 +2692,8 @@ xdp_func_proto(enum bpf_func_id func_id) return &bpf_xdp_event_output_proto; case BPF_FUNC_get_smp_processor_id: return &bpf_get_smp_processor_id_proto; + case BPF_FUNC_xdp_adjust_head: + return &bpf_xdp_adjust_head_proto; default: return sk_filter_func_proto(func_id); } -- cgit v1.1 From b45f0674b997ee769907d6092dfa9ee8f59bb51d Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 7 Dec 2016 15:53:12 -0800 Subject: mlx4: xdp: Allow raising MTU up to one page minus eth and vlan hdrs When XDP is active in mlx4, mlx4 is using one page/pkt. At the same time (i.e. when XDP is active), it is currently limiting MTU to be FRAG_SZ0 - ETH_HLEN - (2 * VLAN_HLEN) which is 1514 in x86. AFAICT, we can at least raise the MTU limit up to PAGE_SIZE - ETH_HLEN - (2 * VLAN_HLEN) which this patch is doing. It will be useful in the next patch which allows XDP program to extend the packet by adding new header(s). Note: In the earlier XDP patches, there is already existing guard to ensure the page/pkt scheme only applies when XDP is active in mlx4. Signed-off-by: Martin KaFai Lau Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 28 +++++++++++----- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 46 ++++++++++++++------------ 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index f441eda..c97d25b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -51,6 +51,8 @@ #include "mlx4_en.h" #include "en_port.h" +#define MLX4_EN_MAX_XDP_MTU ((int)(PAGE_SIZE - ETH_HLEN - (2 * VLAN_HLEN))) + int mlx4_en_setup_tc(struct net_device *dev, u8 up) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -2249,6 +2251,19 @@ void mlx4_en_destroy_netdev(struct net_device *dev) free_netdev(dev); } +static bool mlx4_en_check_xdp_mtu(struct net_device *dev, int mtu) +{ + struct mlx4_en_priv *priv = netdev_priv(dev); + + if (mtu > MLX4_EN_MAX_XDP_MTU) { + en_err(priv, "mtu:%d > max:%d when XDP prog is attached\n", + mtu, MLX4_EN_MAX_XDP_MTU); + return false; + } + + return true; +} + static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -2258,11 +2273,10 @@ static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu) en_dbg(DRV, priv, "Change MTU called - current:%d new:%d\n", dev->mtu, new_mtu); - if (priv->tx_ring_num[TX_XDP] && MLX4_EN_EFF_MTU(new_mtu) > FRAG_SZ0) { - en_err(priv, "MTU size:%d requires frags but XDP running\n", - new_mtu); - return -EOPNOTSUPP; - } + if (priv->tx_ring_num[TX_XDP] && + !mlx4_en_check_xdp_mtu(dev, new_mtu)) + return -ENOTSUPP; + dev->mtu = new_mtu; if (netif_running(dev)) { @@ -2715,10 +2729,8 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog) return 0; } - if (priv->num_frags > 1) { - en_err(priv, "Cannot set XDP if MTU requires multiple frags\n"); + if (!mlx4_en_check_xdp_mtu(dev, dev->mtu)) return -EOPNOTSUPP; - } tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 6562f78..23e9d04 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1164,37 +1164,39 @@ static const int frag_sizes[] = { void mlx4_en_calc_rx_buf(struct net_device *dev) { - enum dma_data_direction dma_dir = PCI_DMA_FROMDEVICE; struct mlx4_en_priv *priv = netdev_priv(dev); int eff_mtu = MLX4_EN_EFF_MTU(dev->mtu); - int order = MLX4_EN_ALLOC_PREFER_ORDER; - u32 align = SMP_CACHE_BYTES; - int buf_size = 0; int i = 0; /* bpf requires buffers to be set up as 1 packet per page. * This only works when num_frags == 1. */ if (priv->tx_ring_num[TX_XDP]) { - dma_dir = PCI_DMA_BIDIRECTIONAL; - /* This will gain efficient xdp frame recycling at the expense - * of more costly truesize accounting + priv->frag_info[0].order = 0; + priv->frag_info[0].frag_size = eff_mtu; + priv->frag_info[0].frag_prefix_size = 0; + /* This will gain efficient xdp frame recycling at the + * expense of more costly truesize accounting */ - align = PAGE_SIZE; - order = 0; - } - - while (buf_size < eff_mtu) { - priv->frag_info[i].order = order; - priv->frag_info[i].frag_size = - (eff_mtu > buf_size + frag_sizes[i]) ? - frag_sizes[i] : eff_mtu - buf_size; - priv->frag_info[i].frag_prefix_size = buf_size; - priv->frag_info[i].frag_stride = - ALIGN(priv->frag_info[i].frag_size, align); - priv->frag_info[i].dma_dir = dma_dir; - buf_size += priv->frag_info[i].frag_size; - i++; + priv->frag_info[0].frag_stride = PAGE_SIZE; + priv->frag_info[0].dma_dir = PCI_DMA_BIDIRECTIONAL; + i = 1; + } else { + int buf_size = 0; + + while (buf_size < eff_mtu) { + priv->frag_info[i].order = MLX4_EN_ALLOC_PREFER_ORDER; + priv->frag_info[i].frag_size = + (eff_mtu > buf_size + frag_sizes[i]) ? + frag_sizes[i] : eff_mtu - buf_size; + priv->frag_info[i].frag_prefix_size = buf_size; + priv->frag_info[i].frag_stride = + ALIGN(priv->frag_info[i].frag_size, + SMP_CACHE_BYTES); + priv->frag_info[i].dma_dir = PCI_DMA_FROMDEVICE; + buf_size += priv->frag_info[i].frag_size; + i++; + } } priv->num_frags = i; -- cgit v1.1 From ea3349a03519dcd4f32d949cd80ab995623dc5ac Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 7 Dec 2016 15:53:13 -0800 Subject: mlx4: xdp: Reserve headroom for receiving packet when XDP prog is active Reserve XDP_PACKET_HEADROOM for packet and enable bpf_xdp_adjust_head() support. This patch only affects the code path when XDP is active. After testing, the tx_dropped counter is incremented if the xdp_prog sends more than wire MTU. Signed-off-by: Martin KaFai Lau Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 8 ++------ drivers/net/ethernet/mellanox/mlx4/en_rx.c | 24 ++++++++++++++++++------ drivers/net/ethernet/mellanox/mlx4/en_tx.c | 9 +++++---- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 3 ++- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index c97d25b..bcd9553 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -51,7 +51,8 @@ #include "mlx4_en.h" #include "en_port.h" -#define MLX4_EN_MAX_XDP_MTU ((int)(PAGE_SIZE - ETH_HLEN - (2 * VLAN_HLEN))) +#define MLX4_EN_MAX_XDP_MTU ((int)(PAGE_SIZE - ETH_HLEN - (2 * VLAN_HLEN) - \ + XDP_PACKET_HEADROOM)) int mlx4_en_setup_tc(struct net_device *dev, u8 up) { @@ -2700,11 +2701,6 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog) int err; int i; - if (prog && prog->xdp_adjust_head) { - en_err(priv, "Does not support bpf_xdp_adjust_head()\n"); - return -EOPNOTSUPP; - } - xdp_ring_num = prog ? priv->rx_ring_num : 0; /* No need to reconfigure buffers when simply swapping the diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 23e9d04..3c37e216b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -96,7 +96,6 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_alloc page_alloc[MLX4_EN_MAX_RX_FRAGS]; const struct mlx4_en_frag_info *frag_info; struct page *page; - dma_addr_t dma; int i; for (i = 0; i < priv->num_frags; i++) { @@ -115,9 +114,10 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, for (i = 0; i < priv->num_frags; i++) { frags[i] = ring_alloc[i]; - dma = ring_alloc[i].dma + ring_alloc[i].page_offset; + frags[i].page_offset += priv->frag_info[i].rx_headroom; + rx_desc->data[i].addr = cpu_to_be64(frags[i].dma + + frags[i].page_offset); ring_alloc[i] = page_alloc[i]; - rx_desc->data[i].addr = cpu_to_be64(dma); } return 0; @@ -250,7 +250,8 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, if (ring->page_cache.index > 0) { frags[0] = ring->page_cache.buf[--ring->page_cache.index]; - rx_desc->data[0].addr = cpu_to_be64(frags[0].dma); + rx_desc->data[0].addr = cpu_to_be64(frags[0].dma + + frags[0].page_offset); return 0; } @@ -889,6 +890,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud if (xdp_prog) { struct xdp_buff xdp; dma_addr_t dma; + void *orig_data; u32 act; dma = be64_to_cpu(rx_desc->data[0].addr); @@ -896,11 +898,19 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud priv->frag_info[0].frag_size, DMA_FROM_DEVICE); - xdp.data = page_address(frags[0].page) + - frags[0].page_offset; + xdp.data_hard_start = page_address(frags[0].page); + xdp.data = xdp.data_hard_start + frags[0].page_offset; xdp.data_end = xdp.data + length; + orig_data = xdp.data; act = bpf_prog_run_xdp(xdp_prog, &xdp); + + if (xdp.data != orig_data) { + length = xdp.data_end - xdp.data; + frags[0].page_offset = xdp.data - + xdp.data_hard_start; + } + switch (act) { case XDP_PASS: break; @@ -1180,6 +1190,7 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) */ priv->frag_info[0].frag_stride = PAGE_SIZE; priv->frag_info[0].dma_dir = PCI_DMA_BIDIRECTIONAL; + priv->frag_info[0].rx_headroom = XDP_PACKET_HEADROOM; i = 1; } else { int buf_size = 0; @@ -1194,6 +1205,7 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) ALIGN(priv->frag_info[i].frag_size, SMP_CACHE_BYTES); priv->frag_info[i].dma_dir = PCI_DMA_FROMDEVICE; + priv->frag_info[i].rx_headroom = 0; buf_size += priv->frag_info[i].frag_size; i++; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 4b597dc..5886ad7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -354,7 +354,7 @@ u32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv, struct mlx4_en_rx_alloc frame = { .page = tx_info->page, .dma = tx_info->map0_dma, - .page_offset = 0, + .page_offset = XDP_PACKET_HEADROOM, .page_size = PAGE_SIZE, }; @@ -1132,7 +1132,7 @@ netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_ring *rx_ring, tx_info->page = frame->page; frame->page = NULL; tx_info->map0_dma = dma; - tx_info->map0_byte_count = length; + tx_info->map0_byte_count = PAGE_SIZE; tx_info->nr_txbb = nr_txbb; tx_info->nr_bytes = max_t(unsigned int, length, ETH_ZLEN); tx_info->data_offset = (void *)data - (void *)tx_desc; @@ -1141,9 +1141,10 @@ netdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_ring *rx_ring, tx_info->linear = 1; tx_info->inl = 0; - dma_sync_single_for_device(priv->ddev, dma, length, PCI_DMA_TODEVICE); + dma_sync_single_range_for_device(priv->ddev, dma, frame->page_offset, + length, PCI_DMA_TODEVICE); - data->addr = cpu_to_be64(dma); + data->addr = cpu_to_be64(dma + frame->page_offset); data->lkey = ring->mr_key; dma_wmb(); data->byte_count = cpu_to_be32(length); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 20a9364..ba1c6cd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -475,7 +475,8 @@ struct mlx4_en_frag_info { u16 frag_prefix_size; u32 frag_stride; enum dma_data_direction dma_dir; - int order; + u16 order; + u16 rx_headroom; }; #ifdef CONFIG_MLX4_EN_DCB -- cgit v1.1 From 12d8bb64e3f65f5287ff17c084d076a28daa8096 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 7 Dec 2016 15:53:14 -0800 Subject: bpf: xdp: Add XDP example for head adjustment The XDP prog checks if the incoming packet matches any VIP:PORT combination in the BPF hashmap. If it is, it will encapsulate the packet with a IPv4/v6 header as instructed by the value of the BPF hashmap and then XDP_TX it out. The VIP:PORT -> IP-Encap-Info can be specified by the cmd args of the user prog. Acked-by: Alexei Starovoitov Signed-off-by: Martin KaFai Lau Signed-off-by: David S. Miller --- samples/bpf/Makefile | 4 + samples/bpf/bpf_helpers.h | 2 + samples/bpf/bpf_load.c | 94 +++++++++++++ samples/bpf/bpf_load.h | 1 + samples/bpf/xdp1_user.c | 93 ------------- samples/bpf/xdp_tx_iptunnel_common.h | 37 +++++ samples/bpf/xdp_tx_iptunnel_kern.c | 236 ++++++++++++++++++++++++++++++++ samples/bpf/xdp_tx_iptunnel_user.c | 256 +++++++++++++++++++++++++++++++++++ 8 files changed, 630 insertions(+), 93 deletions(-) create mode 100644 samples/bpf/xdp_tx_iptunnel_common.h create mode 100644 samples/bpf/xdp_tx_iptunnel_kern.c create mode 100644 samples/bpf/xdp_tx_iptunnel_user.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 00cd308..f2219c1 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -33,6 +33,7 @@ hostprogs-y += trace_event hostprogs-y += sampleip hostprogs-y += tc_l2_redirect hostprogs-y += lwt_len_hist +hostprogs-y += xdp_tx_iptunnel test_lru_dist-objs := test_lru_dist.o libbpf.o sock_example-objs := sock_example.o libbpf.o @@ -67,6 +68,7 @@ trace_event-objs := bpf_load.o libbpf.o trace_event_user.o sampleip-objs := bpf_load.o libbpf.o sampleip_user.o tc_l2_redirect-objs := bpf_load.o libbpf.o tc_l2_redirect_user.o lwt_len_hist-objs := bpf_load.o libbpf.o lwt_len_hist_user.o +xdp_tx_iptunnel-objs := bpf_load.o libbpf.o xdp_tx_iptunnel_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -99,6 +101,7 @@ always += test_current_task_under_cgroup_kern.o always += trace_event_kern.o always += sampleip_kern.o always += lwt_len_hist_kern.o +always += xdp_tx_iptunnel_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/testing/selftests/bpf/ @@ -129,6 +132,7 @@ HOSTLOADLIBES_trace_event += -lelf HOSTLOADLIBES_sampleip += -lelf HOSTLOADLIBES_tc_l2_redirect += -l elf HOSTLOADLIBES_lwt_len_hist += -l elf +HOSTLOADLIBES_xdp_tx_iptunnel += -lelf # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index 8370a6e..faaffe2 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h @@ -57,6 +57,8 @@ static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = (void *) BPF_FUNC_skb_set_tunnel_opt; static unsigned long long (*bpf_get_prandom_u32)(void) = (void *) BPF_FUNC_get_prandom_u32; +static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = + (void *) BPF_FUNC_xdp_adjust_head; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index 49b45cc..e30b6de 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -12,6 +12,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -450,3 +454,93 @@ struct ksym *ksym_search(long key) /* out of range. return _stext */ return &syms[0]; } + +int set_link_xdp_fd(int ifindex, int fd) +{ + struct sockaddr_nl sa; + int sock, seq = 0, len, ret = -1; + char buf[4096]; + struct nlattr *nla, *nla_xdp; + struct { + struct nlmsghdr nh; + struct ifinfomsg ifinfo; + char attrbuf[64]; + } req; + struct nlmsghdr *nh; + struct nlmsgerr *err; + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + printf("open netlink socket: %s\n", strerror(errno)); + return -1; + } + + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + printf("bind to netlink: %s\n", strerror(errno)); + goto cleanup; + } + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nh.nlmsg_type = RTM_SETLINK; + req.nh.nlmsg_pid = 0; + req.nh.nlmsg_seq = ++seq; + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_index = ifindex; + nla = (struct nlattr *)(((char *)&req) + + NLMSG_ALIGN(req.nh.nlmsg_len)); + nla->nla_type = NLA_F_NESTED | 43/*IFLA_XDP*/; + + nla_xdp = (struct nlattr *)((char *)nla + NLA_HDRLEN); + nla_xdp->nla_type = 1/*IFLA_XDP_FD*/; + nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); + memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); + nla->nla_len = NLA_HDRLEN + nla_xdp->nla_len; + + req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + printf("send to netlink: %s\n", strerror(errno)); + goto cleanup; + } + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + printf("recv from netlink: %s\n", strerror(errno)); + goto cleanup; + } + + for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); + nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_pid != getpid()) { + printf("Wrong pid %d, expected %d\n", + nh->nlmsg_pid, getpid()); + goto cleanup; + } + if (nh->nlmsg_seq != seq) { + printf("Wrong seq %d, expected %d\n", + nh->nlmsg_seq, seq); + goto cleanup; + } + switch (nh->nlmsg_type) { + case NLMSG_ERROR: + err = (struct nlmsgerr *)NLMSG_DATA(nh); + if (!err->error) + continue; + printf("nlmsg error %s\n", strerror(-err->error)); + goto cleanup; + case NLMSG_DONE: + break; + } + } + + ret = 0; + +cleanup: + close(sock); + return ret; +} diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h index 4adeeef..fb46a42 100644 --- a/samples/bpf/bpf_load.h +++ b/samples/bpf/bpf_load.h @@ -31,4 +31,5 @@ struct ksym { int load_kallsyms(void); struct ksym *ksym_search(long key); +int set_link_xdp_fd(int ifindex, int fd); #endif diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index 2b2150d..5f040a0 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -5,111 +5,18 @@ * License as published by the Free Software Foundation. */ #include -#include -#include #include #include #include #include #include #include -#include #include #include "bpf_load.h" #include "bpf_util.h" #include "libbpf.h" -static int set_link_xdp_fd(int ifindex, int fd) -{ - struct sockaddr_nl sa; - int sock, seq = 0, len, ret = -1; - char buf[4096]; - struct nlattr *nla, *nla_xdp; - struct { - struct nlmsghdr nh; - struct ifinfomsg ifinfo; - char attrbuf[64]; - } req; - struct nlmsghdr *nh; - struct nlmsgerr *err; - - memset(&sa, 0, sizeof(sa)); - sa.nl_family = AF_NETLINK; - - sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (sock < 0) { - printf("open netlink socket: %s\n", strerror(errno)); - return -1; - } - - if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { - printf("bind to netlink: %s\n", strerror(errno)); - goto cleanup; - } - - memset(&req, 0, sizeof(req)); - req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - req.nh.nlmsg_type = RTM_SETLINK; - req.nh.nlmsg_pid = 0; - req.nh.nlmsg_seq = ++seq; - req.ifinfo.ifi_family = AF_UNSPEC; - req.ifinfo.ifi_index = ifindex; - nla = (struct nlattr *)(((char *)&req) - + NLMSG_ALIGN(req.nh.nlmsg_len)); - nla->nla_type = NLA_F_NESTED | 43/*IFLA_XDP*/; - - nla_xdp = (struct nlattr *)((char *)nla + NLA_HDRLEN); - nla_xdp->nla_type = 1/*IFLA_XDP_FD*/; - nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); - memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); - nla->nla_len = NLA_HDRLEN + nla_xdp->nla_len; - - req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); - - if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { - printf("send to netlink: %s\n", strerror(errno)); - goto cleanup; - } - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - printf("recv from netlink: %s\n", strerror(errno)); - goto cleanup; - } - - for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); - nh = NLMSG_NEXT(nh, len)) { - if (nh->nlmsg_pid != getpid()) { - printf("Wrong pid %d, expected %d\n", - nh->nlmsg_pid, getpid()); - goto cleanup; - } - if (nh->nlmsg_seq != seq) { - printf("Wrong seq %d, expected %d\n", - nh->nlmsg_seq, seq); - goto cleanup; - } - switch (nh->nlmsg_type) { - case NLMSG_ERROR: - err = (struct nlmsgerr *)NLMSG_DATA(nh); - if (!err->error) - continue; - printf("nlmsg error %s\n", strerror(-err->error)); - goto cleanup; - case NLMSG_DONE: - break; - } - } - - ret = 0; - -cleanup: - close(sock); - return ret; -} - static int ifindex; static void int_exit(int sig) diff --git a/samples/bpf/xdp_tx_iptunnel_common.h b/samples/bpf/xdp_tx_iptunnel_common.h new file mode 100644 index 0000000..dd12cc3 --- /dev/null +++ b/samples/bpf/xdp_tx_iptunnel_common.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#ifndef _SAMPLES_BPF_XDP_TX_IPTNL_COMMON_H +#define _SAMPLES_BPF_XDP_TX_IPTNL_COMMON_H + +#include + +#define MAX_IPTNL_ENTRIES 256U + +struct vip { + union { + __u32 v6[4]; + __u32 v4; + } daddr; + __u16 dport; + __u16 family; + __u8 protocol; +}; + +struct iptnl_info { + union { + __u32 v6[4]; + __u32 v4; + } saddr; + union { + __u32 v6[4]; + __u32 v4; + } daddr; + __u16 family; + __u8 dmac[6]; +}; + +#endif diff --git a/samples/bpf/xdp_tx_iptunnel_kern.c b/samples/bpf/xdp_tx_iptunnel_kern.c new file mode 100644 index 0000000..85c38ec --- /dev/null +++ b/samples/bpf/xdp_tx_iptunnel_kern.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program shows how to use bpf_xdp_adjust_head() by + * encapsulating the incoming packet in an IPv4/v6 header + * and then XDP_TX it out. + */ +#include +#include +#include +#include +#include +#include +#include +#include "bpf_helpers.h" +#include "xdp_tx_iptunnel_common.h" + +struct bpf_map_def SEC("maps") rxcnt = { + .type = BPF_MAP_TYPE_PERCPU_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u64), + .max_entries = 256, +}; + +struct bpf_map_def SEC("maps") vip2tnl = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(struct vip), + .value_size = sizeof(struct iptnl_info), + .max_entries = MAX_IPTNL_ENTRIES, +}; + +static __always_inline void count_tx(u32 protocol) +{ + u64 *rxcnt_count; + + rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol); + if (rxcnt_count) + *rxcnt_count += 1; +} + +static __always_inline int get_dport(void *trans_data, void *data_end, + u8 protocol) +{ + struct tcphdr *th; + struct udphdr *uh; + + switch (protocol) { + case IPPROTO_TCP: + th = (struct tcphdr *)trans_data; + if (th + 1 > data_end) + return -1; + return th->dest; + case IPPROTO_UDP: + uh = (struct udphdr *)trans_data; + if (uh + 1 > data_end) + return -1; + return uh->dest; + default: + return 0; + } +} + +static __always_inline void set_ethhdr(struct ethhdr *new_eth, + const struct ethhdr *old_eth, + const struct iptnl_info *tnl, + __be16 h_proto) +{ + memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source)); + memcpy(new_eth->h_dest, tnl->dmac, sizeof(new_eth->h_dest)); + new_eth->h_proto = h_proto; +} + +static __always_inline int handle_ipv4(struct xdp_md *xdp) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct iptnl_info *tnl; + struct ethhdr *new_eth; + struct ethhdr *old_eth; + struct iphdr *iph = data + sizeof(struct ethhdr); + u16 *next_iph_u16; + u16 payload_len; + struct vip vip = {}; + int dport; + u32 csum = 0; + int i; + + if (iph + 1 > data_end) + return XDP_DROP; + + dport = get_dport(iph + 1, data_end, iph->protocol); + if (dport == -1) + return XDP_DROP; + + vip.protocol = iph->protocol; + vip.family = AF_INET; + vip.daddr.v4 = iph->daddr; + vip.dport = dport; + payload_len = ntohs(iph->tot_len); + + tnl = bpf_map_lookup_elem(&vip2tnl, &vip); + /* It only does v4-in-v4 */ + if (!tnl || tnl->family != AF_INET) + return XDP_PASS; + + /* The vip key is found. Add an IP header and send it out */ + + if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr))) + return XDP_DROP; + + data = (void *)(long)xdp->data; + data_end = (void *)(long)xdp->data_end; + + new_eth = data; + iph = data + sizeof(*new_eth); + old_eth = data + sizeof(*iph); + + if (new_eth + 1 > data_end || + old_eth + 1 > data_end || + iph + 1 > data_end) + return XDP_DROP; + + set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IP)); + + iph->version = 4; + iph->ihl = sizeof(*iph) >> 2; + iph->frag_off = 0; + iph->protocol = IPPROTO_IPIP; + iph->check = 0; + iph->tos = 0; + iph->tot_len = htons(payload_len + sizeof(*iph)); + iph->daddr = tnl->daddr.v4; + iph->saddr = tnl->saddr.v4; + iph->ttl = 8; + + next_iph_u16 = (u16 *)iph; +#pragma clang loop unroll(full) + for (i = 0; i < sizeof(*iph) >> 1; i++) + csum += *next_iph_u16++; + + iph->check = ~((csum & 0xffff) + (csum >> 16)); + + count_tx(vip.protocol); + + return XDP_TX; +} + +static __always_inline int handle_ipv6(struct xdp_md *xdp) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct iptnl_info *tnl; + struct ethhdr *new_eth; + struct ethhdr *old_eth; + struct ipv6hdr *ip6h = data + sizeof(struct ethhdr); + __u16 payload_len; + struct vip vip = {}; + int dport; + + if (ip6h + 1 > data_end) + return XDP_DROP; + + dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr); + if (dport == -1) + return XDP_DROP; + + vip.protocol = ip6h->nexthdr; + vip.family = AF_INET6; + memcpy(vip.daddr.v6, ip6h->daddr.s6_addr32, sizeof(vip.daddr)); + vip.dport = dport; + payload_len = ip6h->payload_len; + + tnl = bpf_map_lookup_elem(&vip2tnl, &vip); + /* It only does v6-in-v6 */ + if (!tnl || tnl->family != AF_INET6) + return XDP_PASS; + + /* The vip key is found. Add an IP header and send it out */ + + if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr))) + return XDP_DROP; + + data = (void *)(long)xdp->data; + data_end = (void *)(long)xdp->data_end; + + new_eth = data; + ip6h = data + sizeof(*new_eth); + old_eth = data + sizeof(*ip6h); + + if (new_eth + 1 > data_end || + old_eth + 1 > data_end || + ip6h + 1 > data_end) + return XDP_DROP; + + set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IPV6)); + + ip6h->version = 6; + ip6h->priority = 0; + memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl)); + ip6h->payload_len = htons(ntohs(payload_len) + sizeof(*ip6h)); + ip6h->nexthdr = IPPROTO_IPV6; + ip6h->hop_limit = 8; + memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6)); + memcpy(ip6h->daddr.s6_addr32, tnl->daddr.v6, sizeof(tnl->daddr.v6)); + + count_tx(vip.protocol); + + return XDP_TX; +} + +SEC("xdp_tx_iptunnel") +int _xdp_tx_iptunnel(struct xdp_md *xdp) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct ethhdr *eth = data; + __u16 h_proto; + + if (eth + 1 > data_end) + return XDP_DROP; + + h_proto = eth->h_proto; + + if (h_proto == htons(ETH_P_IP)) + return handle_ipv4(xdp); + else if (h_proto == htons(ETH_P_IPV6)) + + return handle_ipv6(xdp); + else + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c new file mode 100644 index 0000000..7a71f5c --- /dev/null +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_load.h" +#include "libbpf.h" +#include "bpf_util.h" +#include "xdp_tx_iptunnel_common.h" + +#define STATS_INTERVAL_S 2U + +static int ifindex = -1; + +static void int_exit(int sig) +{ + if (ifindex > -1) + set_link_xdp_fd(ifindex, -1); + exit(0); +} + +/* simple per-protocol drop counter + */ +static void poll_stats(unsigned int kill_after_s) +{ + const unsigned int nr_protos = 256; + unsigned int nr_cpus = bpf_num_possible_cpus(); + time_t started_at = time(NULL); + __u64 values[nr_cpus], prev[nr_protos][nr_cpus]; + __u32 proto; + int i; + + memset(prev, 0, sizeof(prev)); + + while (!kill_after_s || time(NULL) - started_at <= kill_after_s) { + sleep(STATS_INTERVAL_S); + + for (proto = 0; proto < nr_protos; proto++) { + __u64 sum = 0; + + assert(bpf_lookup_elem(map_fd[0], &proto, values) == 0); + for (i = 0; i < nr_cpus; i++) + sum += (values[i] - prev[proto][i]); + + if (sum) + printf("proto %u: sum:%10llu pkts, rate:%10llu pkts/s\n", + proto, sum, sum / STATS_INTERVAL_S); + memcpy(prev[proto], values, sizeof(values)); + } + } +} + +static void usage(const char *cmd) +{ + printf("Start a XDP prog which encapsulates incoming packets\n" + "in an IPv4/v6 header and XDP_TX it out. The dst \n" + "is used to select packets to encapsulate\n\n"); + printf("Usage: %s [...]\n", cmd); + printf(" -i Interface Index\n"); + printf(" -a IPv4 or IPv6\n"); + printf(" -p A port range (e.g. 433-444) is also allowed\n"); + printf(" -s Used in the IPTunnel header\n"); + printf(" -d Used in the IPTunnel header\n"); + printf(" -m Used in sending the IP Tunneled pkt\n"); + printf(" -T Default: 0 (forever)\n"); + printf(" -P Default is TCP\n"); + printf(" -h Display this help\n"); +} + +static int parse_ipstr(const char *ipstr, unsigned int *addr) +{ + if (inet_pton(AF_INET6, ipstr, addr) == 1) { + return AF_INET6; + } else if (inet_pton(AF_INET, ipstr, addr) == 1) { + addr[1] = addr[2] = addr[3] = 0; + return AF_INET; + } + + fprintf(stderr, "%s is an invalid IP\n", ipstr); + return AF_UNSPEC; +} + +static int parse_ports(const char *port_str, int *min_port, int *max_port) +{ + char *end; + long tmp_min_port; + long tmp_max_port; + + tmp_min_port = strtol(optarg, &end, 10); + if (tmp_min_port < 1 || tmp_min_port > 65535) { + fprintf(stderr, "Invalid port(s):%s\n", optarg); + return 1; + } + + if (*end == '-') { + end++; + tmp_max_port = strtol(end, NULL, 10); + if (tmp_max_port < 1 || tmp_max_port > 65535) { + fprintf(stderr, "Invalid port(s):%s\n", optarg); + return 1; + } + } else { + tmp_max_port = tmp_min_port; + } + + if (tmp_min_port > tmp_max_port) { + fprintf(stderr, "Invalid port(s):%s\n", optarg); + return 1; + } + + if (tmp_max_port - tmp_min_port + 1 > MAX_IPTNL_ENTRIES) { + fprintf(stderr, "Port range (%s) is larger than %u\n", + port_str, MAX_IPTNL_ENTRIES); + return 1; + } + *min_port = tmp_min_port; + *max_port = tmp_max_port; + + return 0; +} + +int main(int argc, char **argv) +{ + unsigned char opt_flags[256] = {}; + unsigned int kill_after_s = 0; + const char *optstr = "i:a:p:s:d:m:T:P:h"; + int min_port = 0, max_port = 0; + struct iptnl_info tnl = {}; + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + struct vip vip = {}; + char filename[256]; + int opt; + int i; + + tnl.family = AF_UNSPEC; + vip.protocol = IPPROTO_TCP; + + for (i = 0; i < strlen(optstr); i++) + if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z') + opt_flags[(unsigned char)optstr[i]] = 1; + + while ((opt = getopt(argc, argv, optstr)) != -1) { + unsigned short family; + unsigned int *v6; + + switch (opt) { + case 'i': + ifindex = atoi(optarg); + break; + case 'a': + vip.family = parse_ipstr(optarg, vip.daddr.v6); + if (vip.family == AF_UNSPEC) + return 1; + break; + case 'p': + if (parse_ports(optarg, &min_port, &max_port)) + return 1; + break; + case 'P': + vip.protocol = atoi(optarg); + break; + case 's': + case 'd': + if (opt == 's') + v6 = tnl.saddr.v6; + else + v6 = tnl.daddr.v6; + + family = parse_ipstr(optarg, v6); + if (family == AF_UNSPEC) + return 1; + if (tnl.family == AF_UNSPEC) { + tnl.family = family; + } else if (tnl.family != family) { + fprintf(stderr, + "The IP version of the src and dst addresses used in the IP encapsulation does not match\n"); + return 1; + } + break; + case 'm': + if (!ether_aton_r(optarg, + (struct ether_addr *)tnl.dmac)) { + fprintf(stderr, "Invalid mac address:%s\n", + optarg); + return 1; + } + break; + case 'T': + kill_after_s = atoi(optarg); + break; + default: + usage(argv[0]); + return 1; + } + opt_flags[opt] = 0; + } + + for (i = 0; i < strlen(optstr); i++) { + if (opt_flags[(unsigned int)optstr[i]]) { + fprintf(stderr, "Missing argument -%c\n", optstr[i]); + usage(argv[0]); + return 1; + } + } + + if (setrlimit(RLIMIT_MEMLOCK, &r)) { + perror("setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY)"); + return 1; + } + + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + + if (load_bpf_file(filename)) { + printf("%s", bpf_log_buf); + return 1; + } + + if (!prog_fd[0]) { + printf("load_bpf_file: %s\n", strerror(errno)); + return 1; + } + + signal(SIGINT, int_exit); + + while (min_port <= max_port) { + vip.dport = htons(min_port++); + if (bpf_update_elem(map_fd[1], &vip, &tnl, BPF_NOEXIST)) { + perror("bpf_update_elem(&vip2tnl)"); + return 1; + } + } + + if (set_link_xdp_fd(ifindex, prog_fd[0]) < 0) { + printf("link set xdp fd failed\n"); + return 1; + } + + poll_stats(kill_after_s); + + set_link_xdp_fd(ifindex, -1); + + return 0; +} -- cgit v1.1 From ea1e76f78a30c7055afd9e94f785743eeda71c52 Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Thu, 8 Dec 2016 13:16:25 +0530 Subject: cxgb4/cxgb4vf: Remove deprecated module parameters Remove deprecated module parameters num_vf, dflt_msg_enable and force_init. Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 41 +--------------------- .../net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 9 +---- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 449884f..48113c6 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -134,24 +134,6 @@ MODULE_FIRMWARE(FW5_FNAME); MODULE_FIRMWARE(FW6_FNAME); /* - * Normally we're willing to become the firmware's Master PF but will be happy - * if another PF has already become the Master and initialized the adapter. - * Setting "force_init" will cause this driver to forcibly establish itself as - * the Master PF and initialize the adapter. - */ -static uint force_init; - -module_param(force_init, uint, 0644); -MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter," - "deprecated parameter"); - -static int dflt_msg_enable = DFLT_MSG_ENABLE; - -module_param(dflt_msg_enable, int, 0644); -MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T4 default message enable bitmap, " - "deprecated parameter"); - -/* * The driver uses the best interrupt scheme available on a platform in the * order MSI-X, MSI, legacy INTx interrupts. This parameter determines which * of these schemes the driver may consider as follows: @@ -179,16 +161,6 @@ MODULE_PARM_DESC(msi, "whether to use INTx (0), MSI (1) or MSI-X (2)"); */ static int rx_dma_offset = 2; -#ifdef CONFIG_PCI_IOV -/* Configure the number of PCI-E Virtual Function which are to be instantiated - * on SR-IOV Capable Physical Functions. - */ -static unsigned int num_vf[NUM_OF_PF_WITH_SRIOV]; - -module_param_array(num_vf, uint, NULL, 0644); -MODULE_PARM_DESC(num_vf, "number of VFs for each of PFs 0-3, deprecated parameter - please use the pci sysfs interface."); -#endif - /* TX Queue select used to determine what algorithm to use for selecting TX * queue. Select between the kernel provided function (select_queue=0) or user * cxgb_select_queue function (select_queue=1) @@ -4729,7 +4701,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->name = pci_name(pdev); adapter->mbox = func; adapter->pf = func; - adapter->msg_enable = dflt_msg_enable; + adapter->msg_enable = DFLT_MSG_ENABLE; memset(adapter->chan_map, 0xff, sizeof(adapter->chan_map)); spin_lock_init(&adapter->stats_lock); @@ -4988,17 +4960,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) sriov: #ifdef CONFIG_PCI_IOV - if (func < ARRAY_SIZE(num_vf) && num_vf[func] > 0) { - dev_warn(&pdev->dev, - "Enabling SR-IOV VFs using the num_vf module " - "parameter is deprecated - please use the pci sysfs " - "interface instead.\n"); - if (pci_enable_sriov(pdev, num_vf[func]) == 0) - dev_info(&pdev->dev, - "instantiated %u virtual functions\n", - num_vf[func]); - } - adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) { err = -ENOMEM; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 5d4da0e..fa43e06d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -70,13 +70,6 @@ NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\ NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) -static int dflt_msg_enable = DFLT_MSG_ENABLE; - -module_param(dflt_msg_enable, int, 0644); -MODULE_PARM_DESC(dflt_msg_enable, - "default adapter ethtool message level bitmap, " - "deprecated parameter"); - /* * The driver uses the best interrupt scheme available on a platform in the * order MSI-X then MSI. This parameter determines which of these schemes the @@ -2891,7 +2884,7 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev, * Initialize adapter level features. */ adapter->name = pci_name(pdev); - adapter->msg_enable = dflt_msg_enable; + adapter->msg_enable = DFLT_MSG_ENABLE; err = adap_init0(adapter); if (err) goto err_unmap_bar; -- cgit v1.1 From 2135f9b112b8d0670f5328486da6047e1b49dd8c Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Thu, 8 Dec 2016 13:00:44 -0800 Subject: liquidio VF vlan support Adds support for VF vlan features. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 9989ac3..bcfc927 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1350,6 +1350,7 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)), container_of(param, struct octeon_droq, napi); struct net_device *netdev = (struct net_device *)arg; struct sk_buff *skb = (struct sk_buff *)skbuff; + u16 vtag = 0; if (netdev) { struct lio *lio = GET_LIO(netdev); @@ -1403,6 +1404,16 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)), else skb->ip_summed = CHECKSUM_NONE; + /* inbound VLAN tag */ + if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + rh->r_dh.vlan) { + u16 priority = rh->r_dh.priority; + u16 vid = rh->r_dh.vlan; + + vtag = (priority << VLAN_PRIO_SHIFT) | vid; + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); + } + packet_was_received = (napi_gro_receive(napi, skb) != GRO_DROP); if (packet_was_received) { @@ -2025,6 +2036,12 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) tx_info->s.gso_segs = skb_shinfo(skb)->gso_segs; } + /* HW insert VLAN tag */ + if (skb_vlan_tag_present(skb)) { + irh->priority = skb_vlan_tag_get(skb) >> VLAN_PRIO_SHIFT; + irh->vlan = skb_vlan_tag_get(skb) & VLAN_VID_MASK; + } + status = octnet_send_nic_data_pkt(oct, &ndata); if (status == IQ_SEND_FAILED) goto lio_xmit_failed; @@ -2074,6 +2091,61 @@ static void liquidio_tx_timeout(struct net_device *netdev) txqs_wake(netdev); } +static int +liquidio_vlan_rx_add_vid(struct net_device *netdev, + __be16 proto __attribute__((unused)), u16 vid) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + int ret = 0; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_ADD_VLAN_FILTER; + nctrl.ncmd.s.param1 = vid; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = 100; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "Add VLAN filter failed in core (ret: 0x%x)\n", + ret); + } + + return ret; +} + +static int +liquidio_vlan_rx_kill_vid(struct net_device *netdev, + __be16 proto __attribute__((unused)), u16 vid) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + int ret = 0; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_DEL_VLAN_FILTER; + nctrl.ncmd.s.param1 = vid; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = 100; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "Add VLAN filter failed in core (ret: 0x%x)\n", + ret); + } + return ret; +} + /** Sending command to enable/disable RX checksum offload * @param netdev pointer to network device * @param command OCTNET_CMD_TNL_RX_CSUM_CTL @@ -2180,6 +2252,8 @@ static const struct net_device_ops lionetdevops = { .ndo_set_mac_address = liquidio_set_mac, .ndo_set_rx_mode = liquidio_set_mcast_list, .ndo_tx_timeout = liquidio_tx_timeout, + .ndo_vlan_rx_add_vid = liquidio_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = liquidio_vlan_rx_kill_vid, .ndo_change_mtu = liquidio_change_mtu, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, @@ -2388,6 +2462,12 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) | NETIF_F_LRO; netif_set_gso_max_size(netdev, OCTNIC_GSO_MAX_SIZE); + netdev->vlan_features = lio->dev_capability; + /* Add any unchangeable hw features */ + lio->dev_capability |= NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_TX; + netdev->features = (lio->dev_capability & ~NETIF_F_LRO); netdev->hw_features = lio->dev_capability; -- cgit v1.1 From c05ebcc8a5136a44dc1c5122372ccf725109a13b Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Thu, 8 Dec 2016 13:00:45 -0800 Subject: liquidio VF vxlan Adds VF vxlan offload support. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 97 +++++++++++++++++++++- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index bcfc927..5d1023b 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1398,12 +1398,24 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)), skb->protocol = eth_type_trans(skb, skb->dev); if ((netdev->features & NETIF_F_RXCSUM) && - (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)) + (((rh->r_dh.encap_on) && + (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || + (!(rh->r_dh.encap_on) && + (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)))) /* checksum has already been verified */ skb->ip_summed = CHECKSUM_UNNECESSARY; else skb->ip_summed = CHECKSUM_NONE; + /* Setting Encapsulation field on basis of status received + * from the firmware + */ + if (rh->r_dh.encap_on) { + skb->encapsulation = 1; + skb->csum_level = 1; + droq->stats.rx_vxlan++; + } + /* inbound VLAN tag */ if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && rh->r_dh.vlan) { @@ -1916,8 +1928,14 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) cmdsetup.u64 = 0; cmdsetup.s.iq_no = iq_no; - if (skb->ip_summed == CHECKSUM_PARTIAL) - cmdsetup.s.transport_csum = 1; + if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (skb->encapsulation) { + cmdsetup.s.tnl_csum = 1; + stats->tx_vxlan++; + } else { + cmdsetup.s.transport_csum = 1; + } + } if (!skb_shinfo(skb)->nr_frags) { cmdsetup.s.u.datasize = skb->len; @@ -2177,6 +2195,40 @@ static int liquidio_set_rxcsum_command(struct net_device *netdev, int command, return ret; } +/** Sending command to add/delete VxLAN UDP port to firmware + * @param netdev pointer to network device + * @param command OCTNET_CMD_VXLAN_PORT_CONFIG + * @param vxlan_port VxLAN port to be added or deleted + * @param vxlan_cmd_bit OCTNET_CMD_VXLAN_PORT_ADD, + * OCTNET_CMD_VXLAN_PORT_DEL + * @returns SUCCESS or FAILURE + */ +static int liquidio_vxlan_port_command(struct net_device *netdev, int command, + u16 vxlan_port, u8 vxlan_cmd_bit) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + int ret = 0; + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = command; + nctrl.ncmd.s.more = vxlan_cmd_bit; + nctrl.ncmd.s.param1 = vxlan_port; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = 100; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, + "DEVFLAGS VxLAN port add/delete failed in core (ret : 0x%x)\n", + ret); + } + return ret; +} + /** \brief Net device fix features * @param netdev pointer to network device * @param request features requested @@ -2245,6 +2297,30 @@ static int liquidio_set_features(struct net_device *netdev, return 0; } +static void liquidio_add_vxlan_port(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + if (ti->type != UDP_TUNNEL_TYPE_VXLAN) + return; + + liquidio_vxlan_port_command(netdev, + OCTNET_CMD_VXLAN_PORT_CONFIG, + htons(ti->port), + OCTNET_CMD_VXLAN_PORT_ADD); +} + +static void liquidio_del_vxlan_port(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + if (ti->type != UDP_TUNNEL_TYPE_VXLAN) + return; + + liquidio_vxlan_port_command(netdev, + OCTNET_CMD_VXLAN_PORT_CONFIG, + htons(ti->port), + OCTNET_CMD_VXLAN_PORT_DEL); +} + static const struct net_device_ops lionetdevops = { .ndo_open = liquidio_open, .ndo_stop = liquidio_stop, @@ -2257,6 +2333,8 @@ static const struct net_device_ops lionetdevops = { .ndo_change_mtu = liquidio_change_mtu, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, + .ndo_udp_tunnel_add = liquidio_add_vxlan_port, + .ndo_udp_tunnel_del = liquidio_del_vxlan_port, .ndo_select_queue = select_q, }; @@ -2462,6 +2540,19 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) | NETIF_F_LRO; netif_set_gso_max_size(netdev, OCTNIC_GSO_MAX_SIZE); + /* Copy of transmit encapsulation capabilities: + * TSO, TSO6, Checksums for this device + */ + lio->enc_dev_capability = NETIF_F_IP_CSUM + | NETIF_F_IPV6_CSUM + | NETIF_F_GSO_UDP_TUNNEL + | NETIF_F_HW_CSUM | NETIF_F_SG + | NETIF_F_RXCSUM + | NETIF_F_TSO | NETIF_F_TSO6 + | NETIF_F_LRO; + + netdev->hw_enc_features = + (lio->enc_dev_capability & ~NETIF_F_LRO); netdev->vlan_features = lio->dev_capability; /* Add any unchangeable hw features */ lio->dev_capability |= NETIF_F_HW_VLAN_CTAG_FILTER | -- cgit v1.1 From d8ab848c6b4fc2efee14fd37afdc4d27fc237f4f Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Thu, 8 Dec 2016 13:00:46 -0800 Subject: liquidio VF ethtool stats Adds support for VF ethtool stats Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_vf_device.h | 2 + drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 512 +++++++++++++++++---- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 79 ++++ 3 files changed, 495 insertions(+), 98 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index 6715df3..3f98c73 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -44,5 +44,7 @@ int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct); int cn23xx_setup_octeon_vf_device(struct octeon_device *oct); +u32 cn23xx_vf_get_oq_ticks(struct octeon_device *oct, u32 time_intr_in_us); + void cn23xx_dump_vf_initialized_regs(struct octeon_device *oct); #endif diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index e233796..b00c300 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -29,6 +29,7 @@ #include "cn66xx_regs.h" #include "cn66xx_device.h" #include "cn23xx_pf_device.h" +#include "cn23xx_vf_device.h" static int octnet_get_link_stats(struct net_device *netdev); @@ -72,6 +73,7 @@ enum { #define OCT_ETHTOOL_REGDUMP_LEN 4096 #define OCT_ETHTOOL_REGDUMP_LEN_23XX (4096 * 11) +#define OCT_ETHTOOL_REGDUMP_LEN_23XX_VF (4096 * 2) #define OCT_ETHTOOL_REGSVER 1 /* statistics of PF */ @@ -147,6 +149,19 @@ static const char oct_stats_strings[][ETH_GSTRING_LEN] = { "link_state_changes", }; +/* statistics of VF */ +static const char oct_vf_stats_strings[][ETH_GSTRING_LEN] = { + "rx_packets", + "tx_packets", + "rx_bytes", + "tx_bytes", + "rx_errors", /* jabber_err + l2_err+frame_err */ + "tx_errors", /* fw_err_pko + fw_err_link+fw_err_drop */ + "rx_dropped", /* total_rcvd - fw_total_rcvd + dmac_drop + fw_err_drop */ + "tx_dropped", + "link_state_changes", +}; + /* statistics of host tx queue */ static const char oct_iq_stats_strings[][ETH_GSTRING_LEN] = { "packets", /*oct->instr_queue[iq_no]->stats.tx_done*/ @@ -192,25 +207,28 @@ static const char oct_priv_flags_strings[][ETH_GSTRING_LEN] = { #define OCTNIC_NCMD_AUTONEG_ON 0x1 #define OCTNIC_NCMD_PHY_ON 0x2 -static int lio_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int lio_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ecmd) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; struct oct_link_info *linfo; + u32 supported, advertising; linfo = &lio->linfo; if (linfo->link.s.if_mode == INTERFACE_MODE_XAUI || linfo->link.s.if_mode == INTERFACE_MODE_RXAUI || linfo->link.s.if_mode == INTERFACE_MODE_XFI) { - ecmd->port = PORT_FIBRE; - ecmd->supported = - (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | - SUPPORTED_Pause); - ecmd->advertising = - (ADVERTISED_10000baseT_Full | ADVERTISED_Pause); - ecmd->transceiver = XCVR_EXTERNAL; - ecmd->autoneg = AUTONEG_DISABLE; + ecmd->base.port = PORT_FIBRE; + supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | + SUPPORTED_Pause); + advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_Pause); + ethtool_convert_legacy_u32_to_link_mode( + ecmd->link_modes.supported, supported); + ethtool_convert_legacy_u32_to_link_mode( + ecmd->link_modes.advertising, advertising); + ecmd->base.autoneg = AUTONEG_DISABLE; } else { dev_err(&oct->pci_dev->dev, "Unknown link interface reported %d\n", @@ -218,11 +236,11 @@ static int lio_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) } if (linfo->link.s.link_up) { - ethtool_cmd_speed_set(ecmd, linfo->link.s.speed); - ecmd->duplex = linfo->link.s.duplex; + ecmd->base.speed = linfo->link.s.speed; + ecmd->base.duplex = linfo->link.s.duplex; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + ecmd->base.speed = SPEED_UNKNOWN; + ecmd->base.duplex = DUPLEX_UNKNOWN; } return 0; @@ -246,6 +264,23 @@ lio_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) } static void +lio_get_vf_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + struct octeon_device *oct; + struct lio *lio; + + lio = GET_LIO(netdev); + oct = lio->oct_dev; + + memset(drvinfo, 0, sizeof(struct ethtool_drvinfo)); + strcpy(drvinfo->driver, "liquidio_vf"); + strcpy(drvinfo->version, LIQUIDIO_VERSION); + strncpy(drvinfo->fw_version, oct->fw_info.liquidio_firmware_version, + ETHTOOL_FWVERS_LEN); + strncpy(drvinfo->bus_info, pci_name(oct->pci_dev), 32); +} + +static void lio_ethtool_get_channels(struct net_device *dev, struct ethtool_channels *channel) { @@ -982,6 +1017,109 @@ lio_get_ethtool_stats(struct net_device *netdev, } } +static void lio_vf_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats + __attribute__((unused)), + u64 *data) +{ + struct net_device_stats *netstats = &netdev->stats; + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct_dev = lio->oct_dev; + int i = 0, j, vj; + + netdev->netdev_ops->ndo_get_stats(netdev); + /* sum of oct->droq[oq_no]->stats->rx_pkts_received */ + data[i++] = CVM_CAST64(netstats->rx_packets); + /* sum of oct->instr_queue[iq_no]->stats.tx_done */ + data[i++] = CVM_CAST64(netstats->tx_packets); + /* sum of oct->droq[oq_no]->stats->rx_bytes_received */ + data[i++] = CVM_CAST64(netstats->rx_bytes); + /* sum of oct->instr_queue[iq_no]->stats.tx_tot_bytes */ + data[i++] = CVM_CAST64(netstats->tx_bytes); + data[i++] = CVM_CAST64(netstats->rx_errors); + data[i++] = CVM_CAST64(netstats->tx_errors); + /* sum of oct->droq[oq_no]->stats->rx_dropped + + * oct->droq[oq_no]->stats->dropped_nodispatch + + * oct->droq[oq_no]->stats->dropped_toomany + + * oct->droq[oq_no]->stats->dropped_nomem + */ + data[i++] = CVM_CAST64(netstats->rx_dropped); + /* sum of oct->instr_queue[iq_no]->stats.tx_dropped */ + data[i++] = CVM_CAST64(netstats->tx_dropped); + /* lio->link_changes */ + data[i++] = CVM_CAST64(lio->link_changes); + + for (vj = 0; vj < lio->linfo.num_txpciq; vj++) { + j = lio->linfo.txpciq[vj].s.q_no; + + /* packets to network port */ + /* # of packets tx to network */ + data[i++] = CVM_CAST64(oct_dev->instr_queue[j]->stats.tx_done); + /* # of bytes tx to network */ + data[i++] = CVM_CAST64( + oct_dev->instr_queue[j]->stats.tx_tot_bytes); + /* # of packets dropped */ + data[i++] = CVM_CAST64( + oct_dev->instr_queue[j]->stats.tx_dropped); + /* # of tx fails due to queue full */ + data[i++] = CVM_CAST64( + oct_dev->instr_queue[j]->stats.tx_iq_busy); + /* XXX gather entries sent */ + data[i++] = CVM_CAST64( + oct_dev->instr_queue[j]->stats.sgentry_sent); + + /* instruction to firmware: data and control */ + /* # of instructions to the queue */ + data[i++] = CVM_CAST64( + oct_dev->instr_queue[j]->stats.instr_posted); + /* # of instructions processed */ + data[i++] = + CVM_CAST64(oct_dev->instr_queue[j]->stats.instr_processed); + /* # of instructions could not be processed */ + data[i++] = + CVM_CAST64(oct_dev->instr_queue[j]->stats.instr_dropped); + /* bytes sent through the queue */ + data[i++] = CVM_CAST64( + oct_dev->instr_queue[j]->stats.bytes_sent); + /* tso request */ + data[i++] = CVM_CAST64(oct_dev->instr_queue[j]->stats.tx_gso); + /* vxlan request */ + data[i++] = CVM_CAST64(oct_dev->instr_queue[j]->stats.tx_vxlan); + /* txq restart */ + data[i++] = CVM_CAST64( + oct_dev->instr_queue[j]->stats.tx_restart); + } + + /* RX */ + for (vj = 0; vj < lio->linfo.num_rxpciq; vj++) { + j = lio->linfo.rxpciq[vj].s.q_no; + + /* packets send to TCP/IP network stack */ + /* # of packets to network stack */ + data[i++] = CVM_CAST64( + oct_dev->droq[j]->stats.rx_pkts_received); + /* # of bytes to network stack */ + data[i++] = CVM_CAST64( + oct_dev->droq[j]->stats.rx_bytes_received); + data[i++] = CVM_CAST64(oct_dev->droq[j]->stats.dropped_nomem + + oct_dev->droq[j]->stats.dropped_toomany + + oct_dev->droq[j]->stats.rx_dropped); + data[i++] = CVM_CAST64(oct_dev->droq[j]->stats.dropped_nomem); + data[i++] = CVM_CAST64(oct_dev->droq[j]->stats.dropped_toomany); + data[i++] = CVM_CAST64(oct_dev->droq[j]->stats.rx_dropped); + + /* control and data path */ + data[i++] = CVM_CAST64(oct_dev->droq[j]->stats.pkts_received); + data[i++] = CVM_CAST64(oct_dev->droq[j]->stats.bytes_received); + data[i++] = + CVM_CAST64(oct_dev->droq[j]->stats.dropped_nodispatch); + + data[i++] = CVM_CAST64(oct_dev->droq[j]->stats.rx_vxlan); + data[i++] = + CVM_CAST64(oct_dev->droq[j]->stats.rx_alloc_failure); + } +} + static void lio_get_priv_flags_strings(struct lio *lio, u8 *data) { struct octeon_device *oct_dev = lio->oct_dev; @@ -989,6 +1127,7 @@ static void lio_get_priv_flags_strings(struct lio *lio, u8 *data) switch (oct_dev->chip_id) { case OCTEON_CN23XX_PF_VID: + case OCTEON_CN23XX_VF_VID: for (i = 0; i < ARRAY_SIZE(oct_priv_flags_strings); i++) { sprintf(data, "%s", oct_priv_flags_strings[i]); data += ETH_GSTRING_LEN; @@ -1050,12 +1189,61 @@ static void lio_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } } +static void lio_vf_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) +{ + int num_iq_stats, num_oq_stats, i, j; + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct_dev = lio->oct_dev; + int num_stats; + + switch (stringset) { + case ETH_SS_STATS: + num_stats = ARRAY_SIZE(oct_vf_stats_strings); + for (j = 0; j < num_stats; j++) { + sprintf(data, "%s", oct_vf_stats_strings[j]); + data += ETH_GSTRING_LEN; + } + + num_iq_stats = ARRAY_SIZE(oct_iq_stats_strings); + for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct_dev); i++) { + if (!(oct_dev->io_qmask.iq & BIT_ULL(i))) + continue; + for (j = 0; j < num_iq_stats; j++) { + sprintf(data, "tx-%d-%s", i, + oct_iq_stats_strings[j]); + data += ETH_GSTRING_LEN; + } + } + + num_oq_stats = ARRAY_SIZE(oct_droq_stats_strings); + for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct_dev); i++) { + if (!(oct_dev->io_qmask.oq & BIT_ULL(i))) + continue; + for (j = 0; j < num_oq_stats; j++) { + sprintf(data, "rx-%d-%s", i, + oct_droq_stats_strings[j]); + data += ETH_GSTRING_LEN; + } + } + break; + + case ETH_SS_PRIV_FLAGS: + lio_get_priv_flags_strings(lio, data); + break; + default: + netif_info(lio, drv, lio->netdev, "Unknown Stringset !!\n"); + break; + } +} + static int lio_get_priv_flags_ss_count(struct lio *lio) { struct octeon_device *oct_dev = lio->oct_dev; switch (oct_dev->chip_id) { case OCTEON_CN23XX_PF_VID: + case OCTEON_CN23XX_VF_VID: return ARRAY_SIZE(oct_priv_flags_strings); case OCTEON_CN68XX: case OCTEON_CN66XX: @@ -1083,6 +1271,23 @@ static int lio_get_sset_count(struct net_device *netdev, int sset) } } +static int lio_vf_get_sset_count(struct net_device *netdev, int sset) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct_dev = lio->oct_dev; + + switch (sset) { + case ETH_SS_STATS: + return (ARRAY_SIZE(oct_vf_stats_strings) + + ARRAY_SIZE(oct_iq_stats_strings) * oct_dev->num_iqs + + ARRAY_SIZE(oct_droq_stats_strings) * oct_dev->num_oqs); + case ETH_SS_PRIV_FLAGS: + return lio_get_priv_flags_ss_count(lio); + default: + return -EOPNOTSUPP; + } +} + static int lio_get_intr_coalesce(struct net_device *netdev, struct ethtool_coalesce *intr_coal) { @@ -1095,6 +1300,7 @@ static int lio_get_intr_coalesce(struct net_device *netdev, switch (oct->chip_id) { case OCTEON_CN23XX_PF_VID: + case OCTEON_CN23XX_VF_VID: if (!intrmod_cfg->rx_enable) { intr_coal->rx_coalesce_usecs = intrmod_cfg->rx_usecs; intr_coal->rx_max_coalesced_frames = @@ -1141,7 +1347,7 @@ static int lio_get_intr_coalesce(struct net_device *netdev, intr_coal->rx_max_coalesced_frames_low = intrmod_cfg->rx_mincnt_trigger; } - if (OCTEON_CN23XX_PF(oct) && + if ((OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) && (intrmod_cfg->tx_enable)) { intr_coal->use_adaptive_tx_coalesce = intrmod_cfg->tx_enable; intr_coal->tx_max_coalesced_frames_high = @@ -1499,6 +1705,26 @@ oct_cfg_rx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal) oct->intrmod.rx_frames = rx_max_coalesced_frames; break; } + case OCTEON_CN23XX_VF_VID: { + int q_no; + + if (!intr_coal->rx_max_coalesced_frames) + rx_max_coalesced_frames = oct->intrmod.rx_frames; + else + rx_max_coalesced_frames = + intr_coal->rx_max_coalesced_frames; + for (q_no = 0; q_no < oct->num_oqs; q_no++) { + octeon_write_csr64( + oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no), + (octeon_read_csr64( + oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no)) & + (0x3fffff00000000UL)) | + rx_max_coalesced_frames); + /* consider writing to resend bit here */ + } + oct->intrmod.rx_frames = rx_max_coalesced_frames; + break; + } default: return -EINVAL; } @@ -1552,6 +1778,27 @@ static int oct_cfg_rx_intrtime(struct lio *lio, oct->intrmod.rx_usecs = rx_coalesce_usecs; break; } + case OCTEON_CN23XX_VF_VID: { + u64 time_threshold; + int q_no; + + if (!intr_coal->rx_coalesce_usecs) + rx_coalesce_usecs = oct->intrmod.rx_usecs; + else + rx_coalesce_usecs = intr_coal->rx_coalesce_usecs; + + time_threshold = + cn23xx_vf_get_oq_ticks(oct, (u32)rx_coalesce_usecs); + for (q_no = 0; q_no < oct->num_oqs; q_no++) { + octeon_write_csr64( + oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no), + (oct->intrmod.rx_frames | + (time_threshold << 32))); + /* consider setting resend bit */ + } + oct->intrmod.rx_usecs = rx_coalesce_usecs; + break; + } default: return -EINVAL; } @@ -1573,6 +1820,7 @@ oct_cfg_tx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal case OCTEON_CN68XX: case OCTEON_CN66XX: break; + case OCTEON_CN23XX_VF_VID: case OCTEON_CN23XX_PF_VID: { int q_no; @@ -1631,6 +1879,7 @@ static int lio_set_intr_coalesce(struct net_device *netdev, } break; case OCTEON_CN23XX_PF_VID: + case OCTEON_CN23XX_VF_VID: break; default: return -EINVAL; @@ -1693,86 +1942,6 @@ static int lio_get_ts_info(struct net_device *netdev, return 0; } -static int lio_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) -{ - struct lio *lio = GET_LIO(netdev); - struct octeon_device *oct = lio->oct_dev; - struct oct_link_info *linfo; - struct octnic_ctrl_pkt nctrl; - int ret = 0; - - /* get the link info */ - linfo = &lio->linfo; - - if (ecmd->autoneg != AUTONEG_ENABLE && ecmd->autoneg != AUTONEG_DISABLE) - return -EINVAL; - - if (ecmd->autoneg == AUTONEG_DISABLE && ((ecmd->speed != SPEED_100 && - ecmd->speed != SPEED_10) || - (ecmd->duplex != DUPLEX_HALF && - ecmd->duplex != DUPLEX_FULL))) - return -EINVAL; - - /* Ethtool Support is not provided for XAUI, RXAUI, and XFI Interfaces - * as they operate at fixed Speed and Duplex settings - */ - if (linfo->link.s.if_mode == INTERFACE_MODE_XAUI || - linfo->link.s.if_mode == INTERFACE_MODE_RXAUI || - linfo->link.s.if_mode == INTERFACE_MODE_XFI) { - dev_info(&oct->pci_dev->dev, - "Autonegotiation, duplex and speed settings cannot be modified.\n"); - return -EINVAL; - } - - memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); - - nctrl.ncmd.u64 = 0; - nctrl.ncmd.s.cmd = OCTNET_CMD_SET_SETTINGS; - nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; - nctrl.wait_time = 1000; - nctrl.netpndev = (u64)netdev; - nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; - - /* Passing the parameters sent by ethtool like Speed, Autoneg & Duplex - * to SE core application using ncmd.s.more & ncmd.s.param - */ - if (ecmd->autoneg == AUTONEG_ENABLE) { - /* Autoneg ON */ - nctrl.ncmd.s.more = OCTNIC_NCMD_PHY_ON | - OCTNIC_NCMD_AUTONEG_ON; - nctrl.ncmd.s.param1 = ecmd->advertising; - } else { - /* Autoneg OFF */ - nctrl.ncmd.s.more = OCTNIC_NCMD_PHY_ON; - - nctrl.ncmd.s.param2 = ecmd->duplex; - - nctrl.ncmd.s.param1 = ecmd->speed; - } - - ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); - if (ret < 0) { - dev_err(&oct->pci_dev->dev, "Failed to set settings\n"); - return -1; - } - - return 0; -} - -static int lio_nway_reset(struct net_device *netdev) -{ - if (netif_running(netdev)) { - struct ethtool_cmd ecmd; - - memset(&ecmd, 0, sizeof(struct ethtool_cmd)); - ecmd.autoneg = 0; - ecmd.speed = 0; - ecmd.duplex = 0; - lio_set_settings(netdev, &ecmd); - } - return 0; -} - /* Return register dump len. */ static int lio_get_regs_len(struct net_device *dev) { @@ -1782,6 +1951,8 @@ static int lio_get_regs_len(struct net_device *dev) switch (oct->chip_id) { case OCTEON_CN23XX_PF_VID: return OCT_ETHTOOL_REGDUMP_LEN_23XX; + case OCTEON_CN23XX_VF_VID: + return OCT_ETHTOOL_REGDUMP_LEN_23XX_VF; default: return OCT_ETHTOOL_REGDUMP_LEN; } @@ -2007,6 +2178,123 @@ static int cn23xx_read_csr_reg(char *s, struct octeon_device *oct) return len; } +static int cn23xx_vf_read_csr_reg(char *s, struct octeon_device *oct) +{ + int len = 0; + u32 reg; + int i; + + /* PCI Window Registers */ + + len += sprintf(s + len, "\n\t Octeon CSR Registers\n\n"); + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_OQ_BUFF_INFO_SIZE(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_OUT_SIZE): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_IQ_INSTR_COUNT64(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT_IN_DONE%d_CNTS): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_OQ_PKTS_CREDIT(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_SLIST_BAOFF_DBELL): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_OQ_SIZE(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_SLIST_FIFO_RSIZE): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_OQ_PKT_CONTROL(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d__OUTPUT_CONTROL): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_OQ_BASE_ADDR64(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_SLIST_BADDR): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_INT_LEVELS): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_OQ_PKTS_SENT(i); + len += sprintf(s + len, "\n[%08x] (SLI_PKT%d_CNTS): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = 0x100c0 + i * CN23XX_VF_OQ_OFFSET; + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_ERROR_INFO): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = 0x100d0 + i * CN23XX_VF_IQ_OFFSET; + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_VF_INT_SUM): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_IQ_PKT_CONTROL64(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_INPUT_CONTROL): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_IQ_BASE_ADDR64(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_INSTR_BADDR): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_IQ_DOORBELL(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_INSTR_BAOFF_DBELL): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_IQ_SIZE(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT%d_INSTR_FIFO_RSIZE): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + for (i = 0; i < (oct->sriov_info.rings_per_vf); i++) { + reg = CN23XX_VF_SLI_IQ_INSTR_COUNT64(i); + len += sprintf(s + len, + "\n[%08x] (SLI_PKT_IN_DONE%d_CNTS): %016llx\n", + reg, i, (u64)octeon_read_csr64(oct, reg)); + } + + return len; +} + static int cn6xxx_read_csr_reg(char *s, struct octeon_device *oct) { u32 reg; @@ -2153,6 +2441,10 @@ static void lio_get_regs(struct net_device *dev, memset(regbuf, 0, OCT_ETHTOOL_REGDUMP_LEN_23XX); len += cn23xx_read_csr_reg(regbuf + len, oct); break; + case OCTEON_CN23XX_VF_VID: + memset(regbuf, 0, OCT_ETHTOOL_REGDUMP_LEN_23XX_VF); + len += cn23xx_vf_read_csr_reg(regbuf + len, oct); + break; case OCTEON_CN68XX: case OCTEON_CN66XX: memset(regbuf, 0, OCT_ETHTOOL_REGDUMP_LEN); @@ -2183,7 +2475,7 @@ static int lio_set_priv_flags(struct net_device *netdev, u32 flags) } static const struct ethtool_ops lio_ethtool_ops = { - .get_settings = lio_get_settings, + .get_link_ksettings = lio_get_link_ksettings, .get_link = ethtool_op_get_link, .get_drvinfo = lio_get_drvinfo, .get_ringparam = lio_ethtool_get_ringparam, @@ -2200,8 +2492,26 @@ static const struct ethtool_ops lio_ethtool_ops = { .get_msglevel = lio_get_msglevel, .set_msglevel = lio_set_msglevel, .get_sset_count = lio_get_sset_count, - .nway_reset = lio_nway_reset, - .set_settings = lio_set_settings, + .get_coalesce = lio_get_intr_coalesce, + .set_coalesce = lio_set_intr_coalesce, + .get_priv_flags = lio_get_priv_flags, + .set_priv_flags = lio_set_priv_flags, + .get_ts_info = lio_get_ts_info, +}; + +static const struct ethtool_ops lio_vf_ethtool_ops = { + .get_link_ksettings = lio_get_link_ksettings, + .get_link = ethtool_op_get_link, + .get_drvinfo = lio_get_vf_drvinfo, + .get_ringparam = lio_ethtool_get_ringparam, + .get_channels = lio_ethtool_get_channels, + .get_strings = lio_vf_get_strings, + .get_ethtool_stats = lio_vf_get_ethtool_stats, + .get_regs_len = lio_get_regs_len, + .get_regs = lio_get_regs, + .get_msglevel = lio_get_msglevel, + .set_msglevel = lio_set_msglevel, + .get_sset_count = lio_vf_get_sset_count, .get_coalesce = lio_get_intr_coalesce, .set_coalesce = lio_set_intr_coalesce, .get_priv_flags = lio_get_priv_flags, @@ -2211,5 +2521,11 @@ static const struct ethtool_ops lio_ethtool_ops = { void liquidio_set_ethtool_ops(struct net_device *netdev) { - netdev->ethtool_ops = &lio_ethtool_ops; + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + + if (OCTEON_CN23XX_VF(oct)) + netdev->ethtool_ops = &lio_vf_ethtool_ops; + else + netdev->ethtool_ops = &lio_ethtool_ops; } diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 5d1023b..97e9b6b 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1824,6 +1824,56 @@ static int liquidio_set_mac(struct net_device *netdev, void *p) } /** + * \brief Net device get_stats + * @param netdev network device + */ +static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + struct net_device_stats *stats = &netdev->stats; + u64 pkts = 0, drop = 0, bytes = 0; + struct oct_droq_stats *oq_stats; + struct oct_iq_stats *iq_stats; + struct octeon_device *oct; + int i, iq_no, oq_no; + + oct = lio->oct_dev; + + for (i = 0; i < lio->linfo.num_txpciq; i++) { + iq_no = lio->linfo.txpciq[i].s.q_no; + iq_stats = &oct->instr_queue[iq_no]->stats; + pkts += iq_stats->tx_done; + drop += iq_stats->tx_dropped; + bytes += iq_stats->tx_tot_bytes; + } + + stats->tx_packets = pkts; + stats->tx_bytes = bytes; + stats->tx_dropped = drop; + + pkts = 0; + drop = 0; + bytes = 0; + + for (i = 0; i < lio->linfo.num_rxpciq; i++) { + oq_no = lio->linfo.rxpciq[i].s.q_no; + oq_stats = &oct->droq[oq_no]->stats; + pkts += oq_stats->rx_pkts_received; + drop += (oq_stats->rx_dropped + + oq_stats->dropped_nodispatch + + oq_stats->dropped_toomany + + oq_stats->dropped_nomem); + bytes += oq_stats->rx_bytes_received; + } + + stats->rx_bytes = bytes; + stats->rx_packets = pkts; + stats->rx_dropped = drop; + + return stats; +} + +/** * \brief Net device change_mtu * @param netdev network device */ @@ -2325,6 +2375,7 @@ static const struct net_device_ops lionetdevops = { .ndo_open = liquidio_open, .ndo_stop = liquidio_stop, .ndo_start_xmit = liquidio_xmit, + .ndo_get_stats = liquidio_get_stats, .ndo_set_mac_address = liquidio_set_mac, .ndo_set_rx_mode = liquidio_set_mcast_list, .ndo_tx_timeout = liquidio_tx_timeout, @@ -2614,6 +2665,13 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) goto setup_nic_dev_fail; } + /* Register ethtool support */ + liquidio_set_ethtool_ops(netdev); + if (lio->oct_dev->chip_id == OCTEON_CN23XX_VF_VID) + octeon_dev->priv_flags = OCT_PRIV_FLAG_DEFAULT; + else + octeon_dev->priv_flags = 0x0; + if (netdev->features & NETIF_F_LRO) liquidio_set_feature(netdev, OCTNET_CMD_LRO_ENABLE, OCTNIC_LROIPV4 | OCTNIC_LROIPV6); @@ -2679,6 +2737,7 @@ setup_nic_wait_intr: */ static int liquidio_init_nic_module(struct octeon_device *oct) { + struct oct_intrmod_cfg *intrmod_cfg; int num_nic_ports = 1; int i, retval = 0; @@ -2700,6 +2759,26 @@ static int liquidio_init_nic_module(struct octeon_device *oct) goto octnet_init_failure; } + /* Initialize interrupt moderation params */ + intrmod_cfg = &((struct octeon_device *)oct)->intrmod; + intrmod_cfg->rx_enable = 1; + intrmod_cfg->check_intrvl = LIO_INTRMOD_CHECK_INTERVAL; + intrmod_cfg->maxpkt_ratethr = LIO_INTRMOD_MAXPKT_RATETHR; + intrmod_cfg->minpkt_ratethr = LIO_INTRMOD_MINPKT_RATETHR; + intrmod_cfg->rx_maxcnt_trigger = LIO_INTRMOD_RXMAXCNT_TRIGGER; + intrmod_cfg->rx_maxtmr_trigger = LIO_INTRMOD_RXMAXTMR_TRIGGER; + intrmod_cfg->rx_mintmr_trigger = LIO_INTRMOD_RXMINTMR_TRIGGER; + intrmod_cfg->rx_mincnt_trigger = LIO_INTRMOD_RXMINCNT_TRIGGER; + intrmod_cfg->tx_enable = 1; + intrmod_cfg->tx_maxcnt_trigger = LIO_INTRMOD_TXMAXCNT_TRIGGER; + intrmod_cfg->tx_mincnt_trigger = LIO_INTRMOD_TXMINCNT_TRIGGER; + intrmod_cfg->rx_frames = CFG_GET_OQ_INTR_PKT(octeon_get_conf(oct)); + intrmod_cfg->rx_usecs = CFG_GET_OQ_INTR_TIME(octeon_get_conf(oct)); + intrmod_cfg->tx_frames = CFG_GET_IQ_INTR_PKT(octeon_get_conf(oct)); + dev_dbg(&oct->pci_dev->dev, "Network interfaces ready\n"); + + return retval; + octnet_init_failure: oct->ifcount = 0; -- cgit v1.1 From f625fe620815403676a012b18c3ddc501c73f03d Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Thu, 8 Dec 2016 13:00:47 -0800 Subject: liquidio VF timestamp Adds support for VF timestamp. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 180 ++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 97e9b6b..dc0e1f6 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -42,6 +42,7 @@ MODULE_PARM_DESC(debug, "NETIF_MSG debug bits"); #define LIO_IFSTATE_DROQ_OPS 0x01 #define LIO_IFSTATE_REGISTERED 0x02 #define LIO_IFSTATE_RUNNING 0x04 +#define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08 struct liquidio_if_cfg_context { int octeon_id; @@ -65,6 +66,12 @@ struct liquidio_rx_ctl_context { int cond; }; +struct oct_timestamp_resp { + u64 rh; + u64 timestamp; + u64 status; +}; + union tx_info { u64 u64; struct { @@ -1894,6 +1901,169 @@ static int liquidio_change_mtu(struct net_device *netdev, int new_mtu) return 0; } +/** + * \brief Handler for SIOCSHWTSTAMP ioctl + * @param netdev network device + * @param ifr interface request + * @param cmd command + */ +static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr) +{ + struct lio *lio = GET_LIO(netdev); + struct hwtstamp_config conf; + + if (copy_from_user(&conf, ifr->ifr_data, sizeof(conf))) + return -EFAULT; + + if (conf.flags) + return -EINVAL; + + switch (conf.tx_type) { + case HWTSTAMP_TX_ON: + case HWTSTAMP_TX_OFF: + break; + default: + return -ERANGE; + } + + switch (conf.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + conf.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + if (conf.rx_filter == HWTSTAMP_FILTER_ALL) + ifstate_set(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED); + + else + ifstate_reset(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED); + + return copy_to_user(ifr->ifr_data, &conf, sizeof(conf)) ? -EFAULT : 0; +} + +/** + * \brief ioctl handler + * @param netdev network device + * @param ifr interface request + * @param cmd command + */ +static int liquidio_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCSHWTSTAMP: + return hwtstamp_ioctl(netdev, ifr); + default: + return -EOPNOTSUPP; + } +} + +static void handle_timestamp(struct octeon_device *oct, u32 status, void *buf) +{ + struct sk_buff *skb = (struct sk_buff *)buf; + struct octnet_buf_free_info *finfo; + struct oct_timestamp_resp *resp; + struct octeon_soft_command *sc; + struct lio *lio; + + finfo = (struct octnet_buf_free_info *)skb->cb; + lio = finfo->lio; + sc = finfo->sc; + oct = lio->oct_dev; + resp = (struct oct_timestamp_resp *)sc->virtrptr; + + if (status != OCTEON_REQUEST_DONE) { + dev_err(&oct->pci_dev->dev, "Tx timestamp instruction failed. Status: %llx\n", + CVM_CAST64(status)); + resp->timestamp = 0; + } + + octeon_swap_8B_data(&resp->timestamp, 1); + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { + struct skb_shared_hwtstamps ts; + u64 ns = resp->timestamp; + + netif_info(lio, tx_done, lio->netdev, + "Got resulting SKBTX_HW_TSTAMP skb=%p ns=%016llu\n", + skb, (unsigned long long)ns); + ts.hwtstamp = ns_to_ktime(ns + lio->ptp_adjust); + skb_tstamp_tx(skb, &ts); + } + + octeon_free_soft_command(oct, sc); + tx_buffer_free(skb); +} + +/* \brief Send a data packet that will be timestamped + * @param oct octeon device + * @param ndata pointer to network data + * @param finfo pointer to private network data + */ +static int send_nic_timestamp_pkt(struct octeon_device *oct, + struct octnic_data_pkt *ndata, + struct octnet_buf_free_info *finfo) +{ + struct octeon_soft_command *sc; + int ring_doorbell; + struct lio *lio; + int retval; + u32 len; + + lio = finfo->lio; + + sc = octeon_alloc_soft_command_resp(oct, &ndata->cmd, + sizeof(struct oct_timestamp_resp)); + finfo->sc = sc; + + if (!sc) { + dev_err(&oct->pci_dev->dev, "No memory for timestamped data packet\n"); + return IQ_SEND_FAILED; + } + + if (ndata->reqtype == REQTYPE_NORESP_NET) + ndata->reqtype = REQTYPE_RESP_NET; + else if (ndata->reqtype == REQTYPE_NORESP_NET_SG) + ndata->reqtype = REQTYPE_RESP_NET_SG; + + sc->callback = handle_timestamp; + sc->callback_arg = finfo->skb; + sc->iq_no = ndata->q_no; + + len = (u32)((struct octeon_instr_ih3 *)(&sc->cmd.cmd3.ih3))->dlengsz; + + ring_doorbell = 1; + + retval = octeon_send_command(oct, sc->iq_no, ring_doorbell, &sc->cmd, + sc, len, ndata->reqtype); + + if (retval == IQ_SEND_FAILED) { + dev_err(&oct->pci_dev->dev, "timestamp data packet failed status: %x\n", + retval); + octeon_free_soft_command(oct, sc); + } else { + netif_info(lio, tx_queued, lio->netdev, "Queued timestamp packet\n"); + } + + return retval; +} + /** \brief Transmit networks packets to the Octeon interface * @param skbuff skbuff struct to be passed to network layer. * @param netdev pointer to network device @@ -1986,6 +2156,10 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) cmdsetup.s.transport_csum = 1; } } + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + cmdsetup.s.timestamp = 1; + } if (!skb_shinfo(skb)->nr_frags) { cmdsetup.s.u.datasize = skb->len; @@ -2110,7 +2284,10 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) irh->vlan = skb_vlan_tag_get(skb) & VLAN_VID_MASK; } - status = octnet_send_nic_data_pkt(oct, &ndata); + if (unlikely(cmdsetup.s.timestamp)) + status = send_nic_timestamp_pkt(oct, &ndata, finfo); + else + status = octnet_send_nic_data_pkt(oct, &ndata); if (status == IQ_SEND_FAILED) goto lio_xmit_failed; @@ -2382,6 +2559,7 @@ static const struct net_device_ops lionetdevops = { .ndo_vlan_rx_add_vid = liquidio_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = liquidio_vlan_rx_kill_vid, .ndo_change_mtu = liquidio_change_mtu, + .ndo_do_ioctl = liquidio_ioctl, .ndo_fix_features = liquidio_fix_features, .ndo_set_features = liquidio_set_features, .ndo_udp_tunnel_add = liquidio_add_vxlan_port, -- cgit v1.1 From b0d66369edcdb35003feceeb05eb39844dab9769 Mon Sep 17 00:00:00 2001 From: Raghu Vatsavayi Date: Thu, 8 Dec 2016 13:00:48 -0800 Subject: liquidio VF error handling Adds support for VF error handling. Signed-off-by: Raghu Vatsavayi Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 139 +++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index dc0e1f6..70d96c1 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -175,6 +175,144 @@ static int wait_for_pending_requests(struct octeon_device *oct) return 0; } +/** + * \brief Cause device to go quiet so it can be safely removed/reset/etc + * @param oct Pointer to Octeon device + */ +static void pcierror_quiesce_device(struct octeon_device *oct) +{ + int i; + + /* Disable the input and output queues now. No more packets will + * arrive from Octeon, but we should wait for all packet processing + * to finish. + */ + + /* To allow for in-flight requests */ + schedule_timeout_uninterruptible(100); + + if (wait_for_pending_requests(oct)) + dev_err(&oct->pci_dev->dev, "There were pending requests\n"); + + /* Force all requests waiting to be fetched by OCTEON to complete. */ + for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { + struct octeon_instr_queue *iq; + + if (!(oct->io_qmask.iq & BIT_ULL(i))) + continue; + iq = oct->instr_queue[i]; + + if (atomic_read(&iq->instr_pending)) { + spin_lock_bh(&iq->lock); + iq->fill_cnt = 0; + iq->octeon_read_index = iq->host_write_index; + iq->stats.instr_processed += + atomic_read(&iq->instr_pending); + lio_process_iq_request_list(oct, iq, 0); + spin_unlock_bh(&iq->lock); + } + } + + /* Force all pending ordered list requests to time out. */ + lio_process_ordered_list(oct, 1); + + /* We do not need to wait for output queue packets to be processed. */ +} + +/** + * \brief Cleanup PCI AER uncorrectable error status + * @param dev Pointer to PCI device + */ +static void cleanup_aer_uncorrect_error_status(struct pci_dev *dev) +{ + u32 status, mask; + int pos = 0x100; + + pr_info("%s :\n", __func__); + + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); + if (dev->error_state == pci_channel_io_normal) + status &= ~mask; /* Clear corresponding nonfatal bits */ + else + status &= mask; /* Clear corresponding fatal bits */ + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); +} + +/** + * \brief Stop all PCI IO to a given device + * @param dev Pointer to Octeon device + */ +static void stop_pci_io(struct octeon_device *oct) +{ + struct msix_entry *msix_entries; + int i; + + /* No more instructions will be forwarded. */ + atomic_set(&oct->status, OCT_DEV_IN_RESET); + + for (i = 0; i < oct->ifcount; i++) + netif_device_detach(oct->props[i].netdev); + + /* Disable interrupts */ + oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); + + pcierror_quiesce_device(oct); + if (oct->msix_on) { + msix_entries = (struct msix_entry *)oct->msix_entries; + for (i = 0; i < oct->num_msix_irqs; i++) { + /* clear the affinity_cpumask */ + irq_set_affinity_hint(msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + } + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + octeon_free_ioq_vector(oct); + } + dev_dbg(&oct->pci_dev->dev, "Device state is now %s\n", + lio_get_state_string(&oct->status)); + + /* making it a common function for all OCTEON models */ + cleanup_aer_uncorrect_error_status(oct->pci_dev); + + pci_disable_device(oct->pci_dev); +} + +/** + * \brief called when PCI error is detected + * @param pdev Pointer to PCI device + * @param state The current pci connection state + * + * This function is called after a PCI bus error affecting + * this device has been detected. + */ +static pci_ers_result_t liquidio_pcie_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct octeon_device *oct = pci_get_drvdata(pdev); + + /* Non-correctable Non-fatal errors */ + if (state == pci_channel_io_normal) { + dev_err(&oct->pci_dev->dev, "Non-correctable non-fatal error reported:\n"); + cleanup_aer_uncorrect_error_status(oct->pci_dev); + return PCI_ERS_RESULT_CAN_RECOVER; + } + + /* Non-correctable Fatal errors */ + dev_err(&oct->pci_dev->dev, "Non-correctable FATAL reported by PCI AER driver\n"); + stop_pci_io(oct); + + return PCI_ERS_RESULT_DISCONNECT; +} + +/* For PCI-E Advanced Error Recovery (AER) Interface */ +static const struct pci_error_handlers liquidio_vf_err_handler = { + .error_detected = liquidio_pcie_error_detected, +}; + static const struct pci_device_id liquidio_vf_pci_tbl[] = { { PCI_VENDOR_ID_CAVIUM, OCTEON_CN23XX_VF_VID, @@ -191,6 +329,7 @@ static struct pci_driver liquidio_vf_pci_driver = { .id_table = liquidio_vf_pci_tbl, .probe = liquidio_vf_probe, .remove = liquidio_vf_remove, + .err_handler = &liquidio_vf_err_handler, /* For AER */ }; /** -- cgit v1.1 From 846cc1231a0a020c6e16017ff37d3000c18bd957 Mon Sep 17 00:00:00 2001 From: Amit Kushwaha Date: Thu, 8 Dec 2016 18:21:53 +0530 Subject: net: socket: preferred __aligned(size) for control buffer This patch cleanup checkpatch.pl warning WARNING: __aligned(size) is preferred over __attribute__((aligned(size))) Signed-off-by: Amit Kushwaha Signed-off-by: David S. Miller --- net/socket.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/socket.c b/net/socket.c index e631894..5835383 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1,3 +1,4 @@ + /* * NET An implementation of the SOCKET network access protocol. * @@ -1918,7 +1919,7 @@ static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg, struct sockaddr_storage address; struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; unsigned char ctl[sizeof(struct cmsghdr) + 20] - __attribute__ ((aligned(sizeof(__kernel_size_t)))); + __aligned(sizeof(__kernel_size_t)); /* 20 is size of ipv6_pktinfo */ unsigned char *ctl_buf = ctl; int ctl_len; -- cgit v1.1 From 9afd895271d91cd6ed743d22fce043f8c28b30ce Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 8 Dec 2016 22:57:03 +0100 Subject: net/mlx5e: use %pad format string for dma_addr_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On 32-bit ARM with 64-bit dma_addr_t I get this warning about an incorrect format string: In file included from /git/arm-soc/drivers/net/ethernet/mellanox/mlx5/core/alloc.c:42:0: drivers/net/ethernet/mellanox/mlx5/core/alloc.c: In function ‘mlx5_frag_buf_alloc_node’: drivers/net/ethernet/mellanox/mlx5/core/alloc.c:134:12: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] We have the special %pad format for printing dma_addr_t, so use that to print the correct address and avoid the warning. Fixes: 1c1b522808a1 ("net/mlx5e: Implement Fragmented Work Queue (WQ)") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 44791de..66bd213 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -130,8 +130,8 @@ int mlx5_frag_buf_alloc_node(struct mlx5_core_dev *dev, int size, if (frag->map & ((1 << buf->page_shift) - 1)) { dma_free_coherent(&dev->pdev->dev, frag_sz, buf->frags[i].buf, buf->frags[i].map); - mlx5_core_warn(dev, "unexpected map alignment: 0x%p, page_shift=%d\n", - (void *)frag->map, buf->page_shift); + mlx5_core_warn(dev, "unexpected map alignment: %pad, page_shift=%d\n", + &frag->map, buf->page_shift); goto err_free_buf; } size -= frag_sz; -- cgit v1.1 From dece303fa3aaaf1dbaf4ddd95e8af1c16ceb7bb1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 8 Dec 2016 22:57:04 +0100 Subject: net: xgene: move xgene_cle_ptree_ewdn data off stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The array for initializing the cle is set up on the stack with almost entirely constant data and then passed to a function that converts it into HW specific bit patterns. With the latest addition, the size of this array has grown to the point that we get a warning about potential stack overflow in allmodconfig builds: xgene_enet_cle.c: In function ‘xgene_enet_cle_init’: xgene_enet_cle.c:836:1: error: the frame size of 1032 bytes is larger than 1024 bytes [-Werror=frame-larger-than=] Looking a bit deeper at the usage, I noticed that the only modification of the data is in dead code, as we don't even use the cle module for phy_mode other than PHY_INTERFACE_MODE_XGMII. This means we can simply mark the structure constant and access it directly rather than passing the pointer down through another structure, making the code more efficient at the same time as avoiding the warning. Fixes: a809701fed15 ("drivers: net: xgene: fix: RSS for non-TCP/UDP") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_cle.c | 768 ++++++++++++------------ drivers/net/ethernet/apm/xgene/xgene_enet_cle.h | 2 - 2 files changed, 381 insertions(+), 389 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c index 1dc6c20..e1a51d8 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c @@ -79,10 +79,10 @@ static void xgene_cle_kn_to_hw(struct xgene_cle_ptree_kn *kn, u32 *buf) } } -static void xgene_cle_dn_to_hw(struct xgene_cle_ptree_ewdn *dn, +static void xgene_cle_dn_to_hw(const struct xgene_cle_ptree_ewdn *dn, u32 *buf, u32 jb) { - struct xgene_cle_ptree_branch *br; + const struct xgene_cle_ptree_branch *br; u32 i, j = 0; u32 npp; @@ -205,17 +205,385 @@ static int xgene_cle_setup_dbptr(struct xgene_enet_pdata *pdata, return 0; } +static const struct xgene_cle_ptree_ewdn xgene_init_ptree_dn[] = { + { + /* PKT_TYPE_NODE */ + .node_type = EWDN, + .last_node = 0, + .hdr_len_store = 1, + .hdr_extn = NO_BYTE, + .byte_store = NO_BYTE, + .search_byte_store = NO_BYTE, + .result_pointer = DB_RES_DROP, + .num_branches = 2, + .branch = { + { + /* IPV4 */ + .valid = 1, + .next_packet_pointer = 22, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = PKT_PROT_NODE, + .next_branch = 0, + .data = 0x8, + .mask = 0x0 + }, + { + .valid = 0, + .next_packet_pointer = 262, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = LAST_NODE, + .next_branch = 0, + .data = 0x0, + .mask = 0xffff + } + }, + }, + { + /* PKT_PROT_NODE */ + .node_type = EWDN, + .last_node = 0, + .hdr_len_store = 1, + .hdr_extn = NO_BYTE, + .byte_store = NO_BYTE, + .search_byte_store = NO_BYTE, + .result_pointer = DB_RES_DROP, + .num_branches = 3, + .branch = { + { + /* TCP */ + .valid = 1, + .next_packet_pointer = 26, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_TCP_NODE, + .next_branch = 0, + .data = 0x0600, + .mask = 0x00ff + }, + { + /* UDP */ + .valid = 1, + .next_packet_pointer = 26, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_UDP_NODE, + .next_branch = 0, + .data = 0x1100, + .mask = 0x00ff + }, + { + .valid = 0, + .next_packet_pointer = 26, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 0, + .data = 0x0, + .mask = 0xffff + } + } + }, + { + /* RSS_IPV4_TCP_NODE */ + .node_type = EWDN, + .last_node = 0, + .hdr_len_store = 1, + .hdr_extn = NO_BYTE, + .byte_store = NO_BYTE, + .search_byte_store = BOTH_BYTES, + .result_pointer = DB_RES_DROP, + .num_branches = 6, + .branch = { + { + /* SRC IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 28, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_TCP_NODE, + .next_branch = 1, + .data = 0x0, + .mask = 0xffff + }, + { + /* SRC IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 30, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_TCP_NODE, + .next_branch = 2, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 32, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_TCP_NODE, + .next_branch = 3, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 34, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_TCP_NODE, + .next_branch = 4, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP SRC Port */ + .valid = 0, + .next_packet_pointer = 36, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_TCP_NODE, + .next_branch = 5, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP DST Port */ + .valid = 0, + .next_packet_pointer = 256, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = LAST_NODE, + .next_branch = 0, + .data = 0x0, + .mask = 0xffff + } + } + }, + { + /* RSS_IPV4_UDP_NODE */ + .node_type = EWDN, + .last_node = 0, + .hdr_len_store = 1, + .hdr_extn = NO_BYTE, + .byte_store = NO_BYTE, + .search_byte_store = BOTH_BYTES, + .result_pointer = DB_RES_DROP, + .num_branches = 6, + .branch = { + { + /* SRC IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 28, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_UDP_NODE, + .next_branch = 1, + .data = 0x0, + .mask = 0xffff + }, + { + /* SRC IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 30, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_UDP_NODE, + .next_branch = 2, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 32, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_UDP_NODE, + .next_branch = 3, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 34, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_UDP_NODE, + .next_branch = 4, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP SRC Port */ + .valid = 0, + .next_packet_pointer = 36, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_UDP_NODE, + .next_branch = 5, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP DST Port */ + .valid = 0, + .next_packet_pointer = 258, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = LAST_NODE, + .next_branch = 0, + .data = 0x0, + .mask = 0xffff + } + } + }, + { + /* RSS_IPV4_OTHERS_NODE */ + .node_type = EWDN, + .last_node = 0, + .hdr_len_store = 1, + .hdr_extn = NO_BYTE, + .byte_store = NO_BYTE, + .search_byte_store = BOTH_BYTES, + .result_pointer = DB_RES_DROP, + .num_branches = 6, + .branch = { + { + /* SRC IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 28, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 1, + .data = 0x0, + .mask = 0xffff + }, + { + /* SRC IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 30, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 2, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B01 */ + .valid = 0, + .next_packet_pointer = 32, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 3, + .data = 0x0, + .mask = 0xffff + }, + { + /* DST IPV4 B23 */ + .valid = 0, + .next_packet_pointer = 34, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 4, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP SRC Port */ + .valid = 0, + .next_packet_pointer = 36, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = RSS_IPV4_OTHERS_NODE, + .next_branch = 5, + .data = 0x0, + .mask = 0xffff + }, + { + /* TCP DST Port */ + .valid = 0, + .next_packet_pointer = 260, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = LAST_NODE, + .next_branch = 0, + .data = 0x0, + .mask = 0xffff + } + } + }, + + { + /* LAST NODE */ + .node_type = EWDN, + .last_node = 1, + .hdr_len_store = 1, + .hdr_extn = NO_BYTE, + .byte_store = NO_BYTE, + .search_byte_store = NO_BYTE, + .result_pointer = DB_RES_DROP, + .num_branches = 1, + .branch = { + { + .valid = 0, + .next_packet_pointer = 0, + .jump_bw = JMP_FW, + .jump_rel = JMP_ABS, + .operation = EQT, + .next_node = MAX_NODES, + .next_branch = 0, + .data = 0, + .mask = 0xffff + } + } + } +}; + static int xgene_cle_setup_node(struct xgene_enet_pdata *pdata, struct xgene_enet_cle *cle) { struct xgene_cle_ptree *ptree = &cle->ptree; - struct xgene_cle_ptree_ewdn *dn = ptree->dn; + const struct xgene_cle_ptree_ewdn *dn = xgene_init_ptree_dn; + int num_dn = ARRAY_SIZE(xgene_init_ptree_dn); struct xgene_cle_ptree_kn *kn = ptree->kn; u32 buf[CLE_DRAM_REGS]; int i, j, ret; memset(buf, 0, sizeof(buf)); - for (i = 0; i < ptree->num_dn; i++) { + for (i = 0; i < num_dn; i++) { xgene_cle_dn_to_hw(&dn[i], buf, cle->jump_bytes); ret = xgene_cle_dram_wr(cle, buf, 17, i + ptree->start_node, PTREE_RAM, CLE_CMD_WR); @@ -225,8 +593,8 @@ static int xgene_cle_setup_node(struct xgene_enet_pdata *pdata, /* continue node index for key node */ memset(buf, 0, sizeof(buf)); - for (j = i; j < (ptree->num_kn + ptree->num_dn); j++) { - xgene_cle_kn_to_hw(&kn[j - ptree->num_dn], buf); + for (j = i; j < (ptree->num_kn + num_dn); j++) { + xgene_cle_kn_to_hw(&kn[j - num_dn], buf); ret = xgene_cle_dram_wr(cle, buf, 17, j + ptree->start_node, PTREE_RAM, CLE_CMD_WR); if (ret) @@ -407,392 +775,20 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) struct xgene_enet_cle *enet_cle = &pdata->cle; u32 def_qid, def_fpsel, def_nxtfpsel, pool_id; struct xgene_cle_dbptr dbptr[DB_MAX_PTRS]; - struct xgene_cle_ptree_branch *br; struct xgene_cle_ptree *ptree; struct xgene_cle_ptree_kn kn; int ret; - struct xgene_cle_ptree_ewdn ptree_dn[] = { - { - /* PKT_TYPE_NODE */ - .node_type = EWDN, - .last_node = 0, - .hdr_len_store = 1, - .hdr_extn = NO_BYTE, - .byte_store = NO_BYTE, - .search_byte_store = NO_BYTE, - .result_pointer = DB_RES_DROP, - .num_branches = 2, - .branch = { - { - /* IPV4 */ - .valid = 1, - .next_packet_pointer = 22, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = PKT_PROT_NODE, - .next_branch = 0, - .data = 0x8, - .mask = 0x0 - }, - { - .valid = 0, - .next_packet_pointer = 262, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = LAST_NODE, - .next_branch = 0, - .data = 0x0, - .mask = 0xffff - } - }, - }, - { - /* PKT_PROT_NODE */ - .node_type = EWDN, - .last_node = 0, - .hdr_len_store = 1, - .hdr_extn = NO_BYTE, - .byte_store = NO_BYTE, - .search_byte_store = NO_BYTE, - .result_pointer = DB_RES_DROP, - .num_branches = 3, - .branch = { - { - /* TCP */ - .valid = 1, - .next_packet_pointer = 26, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_TCP_NODE, - .next_branch = 0, - .data = 0x0600, - .mask = 0x00ff - }, - { - /* UDP */ - .valid = 1, - .next_packet_pointer = 26, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_UDP_NODE, - .next_branch = 0, - .data = 0x1100, - .mask = 0x00ff - }, - { - .valid = 0, - .next_packet_pointer = 26, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_OTHERS_NODE, - .next_branch = 0, - .data = 0x0, - .mask = 0xffff - } - } - }, - { - /* RSS_IPV4_TCP_NODE */ - .node_type = EWDN, - .last_node = 0, - .hdr_len_store = 1, - .hdr_extn = NO_BYTE, - .byte_store = NO_BYTE, - .search_byte_store = BOTH_BYTES, - .result_pointer = DB_RES_DROP, - .num_branches = 6, - .branch = { - { - /* SRC IPV4 B01 */ - .valid = 0, - .next_packet_pointer = 28, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_TCP_NODE, - .next_branch = 1, - .data = 0x0, - .mask = 0xffff - }, - { - /* SRC IPV4 B23 */ - .valid = 0, - .next_packet_pointer = 30, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_TCP_NODE, - .next_branch = 2, - .data = 0x0, - .mask = 0xffff - }, - { - /* DST IPV4 B01 */ - .valid = 0, - .next_packet_pointer = 32, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_TCP_NODE, - .next_branch = 3, - .data = 0x0, - .mask = 0xffff - }, - { - /* DST IPV4 B23 */ - .valid = 0, - .next_packet_pointer = 34, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_TCP_NODE, - .next_branch = 4, - .data = 0x0, - .mask = 0xffff - }, - { - /* TCP SRC Port */ - .valid = 0, - .next_packet_pointer = 36, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_TCP_NODE, - .next_branch = 5, - .data = 0x0, - .mask = 0xffff - }, - { - /* TCP DST Port */ - .valid = 0, - .next_packet_pointer = 256, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = LAST_NODE, - .next_branch = 0, - .data = 0x0, - .mask = 0xffff - } - } - }, - { - /* RSS_IPV4_UDP_NODE */ - .node_type = EWDN, - .last_node = 0, - .hdr_len_store = 1, - .hdr_extn = NO_BYTE, - .byte_store = NO_BYTE, - .search_byte_store = BOTH_BYTES, - .result_pointer = DB_RES_DROP, - .num_branches = 6, - .branch = { - { - /* SRC IPV4 B01 */ - .valid = 0, - .next_packet_pointer = 28, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_UDP_NODE, - .next_branch = 1, - .data = 0x0, - .mask = 0xffff - }, - { - /* SRC IPV4 B23 */ - .valid = 0, - .next_packet_pointer = 30, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_UDP_NODE, - .next_branch = 2, - .data = 0x0, - .mask = 0xffff - }, - { - /* DST IPV4 B01 */ - .valid = 0, - .next_packet_pointer = 32, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_UDP_NODE, - .next_branch = 3, - .data = 0x0, - .mask = 0xffff - }, - { - /* DST IPV4 B23 */ - .valid = 0, - .next_packet_pointer = 34, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_UDP_NODE, - .next_branch = 4, - .data = 0x0, - .mask = 0xffff - }, - { - /* TCP SRC Port */ - .valid = 0, - .next_packet_pointer = 36, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_UDP_NODE, - .next_branch = 5, - .data = 0x0, - .mask = 0xffff - }, - { - /* TCP DST Port */ - .valid = 0, - .next_packet_pointer = 258, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = LAST_NODE, - .next_branch = 0, - .data = 0x0, - .mask = 0xffff - } - } - }, - { - /* RSS_IPV4_OTHERS_NODE */ - .node_type = EWDN, - .last_node = 0, - .hdr_len_store = 1, - .hdr_extn = NO_BYTE, - .byte_store = NO_BYTE, - .search_byte_store = BOTH_BYTES, - .result_pointer = DB_RES_DROP, - .num_branches = 6, - .branch = { - { - /* SRC IPV4 B01 */ - .valid = 0, - .next_packet_pointer = 28, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_OTHERS_NODE, - .next_branch = 1, - .data = 0x0, - .mask = 0xffff - }, - { - /* SRC IPV4 B23 */ - .valid = 0, - .next_packet_pointer = 30, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_OTHERS_NODE, - .next_branch = 2, - .data = 0x0, - .mask = 0xffff - }, - { - /* DST IPV4 B01 */ - .valid = 0, - .next_packet_pointer = 32, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_OTHERS_NODE, - .next_branch = 3, - .data = 0x0, - .mask = 0xffff - }, - { - /* DST IPV4 B23 */ - .valid = 0, - .next_packet_pointer = 34, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_OTHERS_NODE, - .next_branch = 4, - .data = 0x0, - .mask = 0xffff - }, - { - /* TCP SRC Port */ - .valid = 0, - .next_packet_pointer = 36, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = RSS_IPV4_OTHERS_NODE, - .next_branch = 5, - .data = 0x0, - .mask = 0xffff - }, - { - /* TCP DST Port */ - .valid = 0, - .next_packet_pointer = 260, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = LAST_NODE, - .next_branch = 0, - .data = 0x0, - .mask = 0xffff - } - } - }, - { - /* LAST NODE */ - .node_type = EWDN, - .last_node = 1, - .hdr_len_store = 1, - .hdr_extn = NO_BYTE, - .byte_store = NO_BYTE, - .search_byte_store = NO_BYTE, - .result_pointer = DB_RES_DROP, - .num_branches = 1, - .branch = { - { - .valid = 0, - .next_packet_pointer = 0, - .jump_bw = JMP_FW, - .jump_rel = JMP_ABS, - .operation = EQT, - .next_node = MAX_NODES, - .next_branch = 0, - .data = 0, - .mask = 0xffff - } - } - } - }; + if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) + return -EINVAL; ptree = &enet_cle->ptree; ptree->start_pkt = 12; /* Ethertype */ - if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { - ret = xgene_cle_setup_rss(pdata); - if (ret) { - netdev_err(pdata->ndev, "RSS initialization failed\n"); - return ret; - } - } else { - br = &ptree_dn[PKT_PROT_NODE].branch[0]; - br->valid = 0; - br->next_packet_pointer = 260; - br->next_node = LAST_NODE; - br->data = 0x0000; - br->mask = 0xffff; + + ret = xgene_cle_setup_rss(pdata); + if (ret) { + netdev_err(pdata->ndev, "RSS initialization failed\n"); + return ret; } def_qid = xgene_enet_dst_ring_num(pdata->rx_ring[0]); @@ -825,10 +821,8 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) kn.key[0].priority = 0; kn.key[0].result_pointer = DB_RES_ACCEPT; - ptree->dn = ptree_dn; ptree->kn = &kn; ptree->dbptr = dbptr; - ptree->num_dn = MAX_NODES; ptree->num_kn = 1; ptree->num_dbptr = DB_MAX_PTRS; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h index 290d5d1..18fe8d5 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h @@ -278,10 +278,8 @@ struct xgene_cle_dbptr { }; struct xgene_cle_ptree { - struct xgene_cle_ptree_ewdn *dn; struct xgene_cle_ptree_kn *kn; struct xgene_cle_dbptr *dbptr; - u32 num_dn; u32 num_kn; u32 num_dbptr; u32 start_node; -- cgit v1.1 From f006b2c5dff35cb32ee77a0cbcabb52d90118c19 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 8 Dec 2016 22:57:05 +0100 Subject: net: xgene: avoid bogus maybe-uninitialized warning In some configurations, gcc cannot trace the state of variables across a spin_unlock() barrier, leading to a warning about correct code: xgene_enet_main.c: In function 'xgene_enet_start_xmit': ../../../phy/mdio-xgene.h:112:14: error: 'mss_index' may be used uninitialized in this function [-Werror=maybe-uninitialized] Here we can trivially move the assignment before that spin_unlock, which reliably avoids the warning. Fixes: e3978673f514 ("drivers: net: xgene: Fix MSS programming") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 884a334..523b8ef 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -319,11 +319,11 @@ static int xgene_enet_setup_mss(struct net_device *ndev, u32 mss) } } - spin_unlock(&pdata->mss_lock); - /* No slots with ref_count = 0 available, return busy */ if (!mss_index_found) - return -EBUSY; + mss_index = -EBUSY; + + spin_unlock(&pdata->mss_lock); return mss_index; } -- cgit v1.1 From eeb04a9688f4f939f2a0eaec5a2d53e05e75e691 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 21 Nov 2016 13:55:48 +0100 Subject: nl80211: fix logic inversion in start_nan() Arend inadvertently inverted the logic while converting to wdev_running(), fix that. Fixes: 73c7da3dae1e ("cfg80211: add generic helper to check interface is running") Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 24ab199..765bd41 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10628,7 +10628,7 @@ static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info) if (wdev->iftype != NL80211_IFTYPE_NAN) return -EOPNOTSUPP; - if (!wdev_running(wdev)) + if (wdev_running(wdev)) return -EEXIST; if (rfkill_blocked(rdev->rfkill)) -- cgit v1.1 From 2fa436b3a2a7009c11a3bc03fe0ff4c26e80fd87 Mon Sep 17 00:00:00 2001 From: Vamsi Krishna Date: Fri, 2 Dec 2016 23:59:08 +0200 Subject: nl80211: Use different attrs for BSSID and random MAC addr in scan req NL80211_ATTR_MAC was used to set both the specific BSSID to be scanned and the random MAC address to be used when privacy is enabled. When both the features are enabled, both the BSSID and the local MAC address were getting same value causing Probe Request frames to go with unintended DA. Hence, this has been fixed by using a different NL80211_ATTR_BSSID attribute to set the specific BSSID (which was the more recent addition in cfg80211) for a scan. Backwards compatibility with old userspace software is maintained to some extent by allowing NL80211_ATTR_MAC to be used to set the specific BSSID when scanning without enabling random MAC address use. Scanning with random source MAC address was introduced by commit ad2b26abc157 ("cfg80211: allow drivers to support random MAC addresses for scan") and the issue was introduced with the addition of the second user for the same attribute in commit 818965d39177 ("cfg80211: Allow a scan request for a specific BSSID"). Fixes: 818965d39177 ("cfg80211: Allow a scan request for a specific BSSID") Signed-off-by: Vamsi Krishna Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 7 ++++++- net/wireless/nl80211.c | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 259c9c7..6b76e3b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -323,7 +323,7 @@ * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the - * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to + * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to * specify a BSSID to scan for; if not included, the wildcard BSSID will * be used. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to @@ -1977,6 +1977,9 @@ enum nl80211_commands { * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast * packets should be send out as unicast to all stations (flag attribute). * + * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also + * used in various commands/events for specifying the BSSID. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2381,6 +2384,8 @@ enum nl80211_attrs { NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, + NL80211_ATTR_BSSID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 765bd41..3df85a7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -404,6 +404,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { .len = FILS_MAX_KEK_LEN }, [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN }, [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, }, + [NL80211_ATTR_BSSID] = { .len = ETH_ALEN }, }; /* policy for the key attributes */ @@ -6703,7 +6704,20 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - if (info->attrs[NL80211_ATTR_MAC]) + /* Initial implementation used NL80211_ATTR_MAC to set the specific + * BSSID to scan for. This was problematic because that same attribute + * was already used for another purpose (local random MAC address). The + * NL80211_ATTR_BSSID attribute was added to fix this. For backwards + * compatibility with older userspace components, also use the + * NL80211_ATTR_MAC value here if it can be determined to be used for + * the specific BSSID use case instead of the random MAC address + * (NL80211_ATTR_SCAN_FLAGS is used to enable random MAC address use). + */ + if (info->attrs[NL80211_ATTR_BSSID]) + memcpy(request->bssid, + nla_data(info->attrs[NL80211_ATTR_BSSID]), ETH_ALEN); + else if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) && + info->attrs[NL80211_ATTR_MAC]) memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]), ETH_ALEN); else -- cgit v1.1 From e6f462df9acd2a3295e5d34eb29e2823220cf129 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Dec 2016 17:22:09 +0100 Subject: cfg80211/mac80211: fix BSS leaks when abandoning assoc attempts When mac80211 abandons an association attempt, it may free all the data structures, but inform cfg80211 and userspace about it only by sending the deauth frame it received, in which case cfg80211 has no link to the BSS struct that was used and will not cfg80211_unhold_bss() it. Fix this by providing a way to inform cfg80211 of this with the BSS entry passed, so that it can clean up properly, and use this ability in the appropriate places in mac80211. This isn't ideal: some code is more or less duplicated and tracing is missing. However, it's a fairly small change and it's thus easier to backport - cleanups can come later. Cc: stable@vger.kernel.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 11 +++++++++++ net/mac80211/mlme.c | 21 ++++++++++++--------- net/wireless/core.h | 1 + net/wireless/mlme.c | 12 ++++++++++++ net/wireless/sme.c | 14 ++++++++++++++ 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2019310..814be4b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4686,6 +4686,17 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss); /** + * cfg80211_abandon_assoc - notify cfg80211 of abandoned association attempt + * @dev: network device + * @bss: The BSS entry with which association was abandoned. + * + * Call this whenever - for reasons reported through other API, like deauth RX, + * an association attempt was abandoned. + * This function may sleep. The caller must hold the corresponding wdev's mutex. + */ +void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss); + +/** * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame * @dev: network device * @buf: 802.11 frame (header + body) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d157b25..098ce9b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2518,7 +2518,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, } static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, - bool assoc) + bool assoc, bool abandon) { struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; @@ -2541,6 +2541,9 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, mutex_lock(&sdata->local->mtx); ieee80211_vif_release_channel(sdata); mutex_unlock(&sdata->local->mtx); + + if (abandon) + cfg80211_abandon_assoc(sdata->dev, assoc_data->bss); } kfree(assoc_data); @@ -2773,7 +2776,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, bssid, reason_code, ieee80211_get_reason_code_string(reason_code)); - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, true); cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); return; @@ -3182,14 +3185,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (status_code != WLAN_STATUS_SUCCESS) { sdata_info(sdata, "%pM denied association (code=%d)\n", mgmt->sa, status_code); - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); event.u.mlme.status = MLME_DENIED; event.u.mlme.reason = status_code; drv_event_callback(sdata->local, sdata, &event); } else { if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); cfg80211_assoc_timeout(sdata->dev, bss); return; } @@ -3202,7 +3205,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, * recalc after assoc_data is NULL but before associated * is set can cause the interface to go idle */ - ieee80211_destroy_assoc_data(sdata, true); + ieee80211_destroy_assoc_data(sdata, true, false); /* get uapsd queues configuration */ uapsd_queues = 0; @@ -3901,7 +3904,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) .u.mlme.status = MLME_TIMEOUT, }; - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); cfg80211_assoc_timeout(sdata->dev, bss); drv_event_callback(sdata->local, sdata, &event); } @@ -4040,7 +4043,7 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DEAUTH_LEAVING, false, frame_buf); if (ifmgd->assoc_data) - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, true); if (ifmgd->auth_data) ieee80211_destroy_auth_data(sdata, false); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, @@ -4949,7 +4952,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, IEEE80211_STYPE_DEAUTH, req->reason_code, tx, frame_buf); - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, true); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, req->reason_code); @@ -5024,7 +5027,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) sdata_lock(sdata); if (ifmgd->assoc_data) { struct cfg80211_bss *bss = ifmgd->assoc_data->bss; - ieee80211_destroy_assoc_data(sdata, false); + ieee80211_destroy_assoc_data(sdata, false, false); cfg80211_assoc_timeout(sdata->dev, bss); } if (ifmgd->auth_data) diff --git a/net/wireless/core.h b/net/wireless/core.h index fb2fcd5..9820fa2 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -409,6 +409,7 @@ void cfg80211_sme_disassoc(struct wireless_dev *wdev); void cfg80211_sme_deauth(struct wireless_dev *wdev); void cfg80211_sme_auth_timeout(struct wireless_dev *wdev); void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev); +void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev); /* internal helpers */ bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index bd1f7a1..4646cf5 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -149,6 +149,18 @@ void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss) } EXPORT_SYMBOL(cfg80211_assoc_timeout); +void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + + cfg80211_sme_abandon_assoc(wdev); + + cfg80211_unhold_bss(bss_from_pub(bss)); + cfg80211_put_bss(wiphy, bss); +} +EXPORT_SYMBOL(cfg80211_abandon_assoc); + void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 2b5bb38..5e0d193 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -39,6 +39,7 @@ struct cfg80211_conn { CFG80211_CONN_ASSOCIATING, CFG80211_CONN_ASSOC_FAILED, CFG80211_CONN_DEAUTH, + CFG80211_CONN_ABANDON, CFG80211_CONN_CONNECTED, } state; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; @@ -206,6 +207,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, WLAN_REASON_DEAUTH_LEAVING, false); + /* fall through */ + case CFG80211_CONN_ABANDON: /* free directly, disconnected event already sent */ cfg80211_sme_free(wdev); return 0; @@ -423,6 +426,17 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev) schedule_work(&rdev->conn_work); } +void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + + if (!wdev->conn) + return; + + wdev->conn->state = CFG80211_CONN_ABANDON; + schedule_work(&rdev->conn_work); +} + static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev, const u8 *ies, size_t ies_len, const u8 **out_ies, size_t *out_ies_len) -- cgit v1.1 From 1e88ab6fbbaacbd79dd14ab9ceec7a595611248d Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 8 Dec 2016 13:24:20 -0600 Subject: net: qcom/emac: move phy init code to separate files The internal PHY of the EMAC differs on each SOC, and the list will only continue to grow. By separating the code into individual files, we can add support for more SOCs more cleanly. Note: The internal PHY is also sometimes called the SGMII device. We also stop referring to the various PHY variations by version number, so no more "v2", "v3", etc. Instead, the devices are named after the SOC they are, which is in sync with the device tree property names. Future patches will probably rearrange more code among the files. Signed-off-by: Timur Tabi Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/emac/Makefile | 3 +- .../ethernet/qualcomm/emac/emac-sgmii-fsm9900.c | 245 ++++++++++ .../ethernet/qualcomm/emac/emac-sgmii-qdf2432.c | 210 ++++++++ drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 538 +-------------------- drivers/net/ethernet/qualcomm/emac/emac-sgmii.h | 5 +- 5 files changed, 478 insertions(+), 523 deletions(-) create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c diff --git a/drivers/net/ethernet/qualcomm/emac/Makefile b/drivers/net/ethernet/qualcomm/emac/Makefile index 01ee144..204b787 100644 --- a/drivers/net/ethernet/qualcomm/emac/Makefile +++ b/drivers/net/ethernet/qualcomm/emac/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o -qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o +qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o \ + emac-sgmii-fsm9900.o emac-sgmii-qdf2432.o diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c new file mode 100644 index 0000000..af690e1 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c @@ -0,0 +1,245 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Qualcomm Technologies, Inc. FSM9900 EMAC SGMII Controller driver. + */ + +#include +#include "emac.h" + +/* EMAC_QSERDES register offsets */ +#define EMAC_QSERDES_COM_SYS_CLK_CTRL 0x0000 +#define EMAC_QSERDES_COM_PLL_CNTRL 0x0014 +#define EMAC_QSERDES_COM_PLL_IP_SETI 0x0018 +#define EMAC_QSERDES_COM_PLL_CP_SETI 0x0024 +#define EMAC_QSERDES_COM_PLL_IP_SETP 0x0028 +#define EMAC_QSERDES_COM_PLL_CP_SETP 0x002c +#define EMAC_QSERDES_COM_SYSCLK_EN_SEL 0x0038 +#define EMAC_QSERDES_COM_RESETSM_CNTRL 0x0040 +#define EMAC_QSERDES_COM_PLLLOCK_CMP1 0x0044 +#define EMAC_QSERDES_COM_PLLLOCK_CMP2 0x0048 +#define EMAC_QSERDES_COM_PLLLOCK_CMP3 0x004c +#define EMAC_QSERDES_COM_PLLLOCK_CMP_EN 0x0050 +#define EMAC_QSERDES_COM_DEC_START1 0x0064 +#define EMAC_QSERDES_COM_DIV_FRAC_START1 0x0098 +#define EMAC_QSERDES_COM_DIV_FRAC_START2 0x009c +#define EMAC_QSERDES_COM_DIV_FRAC_START3 0x00a0 +#define EMAC_QSERDES_COM_DEC_START2 0x00a4 +#define EMAC_QSERDES_COM_PLL_CRCTRL 0x00ac +#define EMAC_QSERDES_COM_RESET_SM 0x00bc +#define EMAC_QSERDES_TX_BIST_MODE_LANENO 0x0100 +#define EMAC_QSERDES_TX_TX_EMP_POST1_LVL 0x0108 +#define EMAC_QSERDES_TX_TX_DRV_LVL 0x010c +#define EMAC_QSERDES_TX_LANE_MODE 0x0150 +#define EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN 0x0170 +#define EMAC_QSERDES_RX_CDR_CONTROL 0x0200 +#define EMAC_QSERDES_RX_CDR_CONTROL2 0x0210 +#define EMAC_QSERDES_RX_RX_EQ_GAIN12 0x0230 + +/* EMAC_SGMII register offsets */ +#define EMAC_SGMII_PHY_SERDES_START 0x0000 +#define EMAC_SGMII_PHY_CMN_PWR_CTRL 0x0004 +#define EMAC_SGMII_PHY_RX_PWR_CTRL 0x0008 +#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C +#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 +#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 +#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 +#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 + +#define PLL_IPSETI(x) ((x) & 0x3f) + +#define PLL_CPSETI(x) ((x) & 0xff) + +#define PLL_IPSETP(x) ((x) & 0x3f) + +#define PLL_CPSETP(x) ((x) & 0x1f) + +#define PLL_RCTRL(x) (((x) & 0xf) << 4) +#define PLL_CCTRL(x) ((x) & 0xf) + +#define LANE_MODE(x) ((x) & 0x1f) + +#define SYSCLK_CM BIT(4) +#define SYSCLK_AC_COUPLE BIT(3) + +#define OCP_EN BIT(5) +#define PLL_DIV_FFEN BIT(2) +#define PLL_DIV_ORD BIT(1) + +#define SYSCLK_SEL_CMOS BIT(3) + +#define FRQ_TUNE_MODE BIT(4) + +#define PLLLOCK_CMP_EN BIT(0) + +#define DEC_START1_MUX BIT(7) +#define DEC_START1(x) ((x) & 0x7f) + +#define DIV_FRAC_START_MUX BIT(7) +#define DIV_FRAC_START(x) ((x) & 0x7f) + +#define DIV_FRAC_START3_MUX BIT(4) +#define DIV_FRAC_START3(x) ((x) & 0xf) + +#define DEC_START2_MUX BIT(1) +#define DEC_START2 BIT(0) + +#define READY BIT(5) + +#define TX_EMP_POST1_LVL_MUX BIT(5) +#define TX_EMP_POST1_LVL(x) ((x) & 0x1f) + +#define TX_DRV_LVL_MUX BIT(4) +#define TX_DRV_LVL(x) ((x) & 0xf) + +#define EMP_EN_MUX BIT(1) +#define EMP_EN BIT(0) + +#define SECONDORDERENABLE BIT(6) +#define FIRSTORDER_THRESH(x) (((x) & 0x7) << 3) +#define SECONDORDERGAIN(x) ((x) & 0x7) + +#define RX_EQ_GAIN2(x) (((x) & 0xf) << 4) +#define RX_EQ_GAIN1(x) ((x) & 0xf) + +#define SERDES_START BIT(0) + +#define BIAS_EN BIT(6) +#define PLL_EN BIT(5) +#define SYSCLK_EN BIT(4) +#define CLKBUF_L_EN BIT(3) +#define PLL_TXCLK_EN BIT(1) +#define PLL_RXCLK_EN BIT(0) + +#define L0_RX_SIGDET_EN BIT(7) +#define L0_RX_TERM_MODE(x) (((x) & 3) << 4) +#define L0_RX_I_EN BIT(1) + +#define L0_TX_EN BIT(5) +#define L0_CLKBUF_EN BIT(4) +#define L0_TRAN_BIAS_EN BIT(1) + +#define L0_RX_EQUALIZE_ENABLE BIT(6) +#define L0_RESET_TSYNC_EN BIT(4) +#define L0_DRV_LVL(x) ((x) & 0xf) + +#define PWRDN_B BIT(0) +#define CDR_MAX_CNT(x) ((x) & 0xff) + +#define PLLLOCK_CMP(x) ((x) & 0xff) + +#define SERDES_START_WAIT_TIMES 100 + +struct emac_reg_write { + unsigned int offset; + u32 val; +}; + +static void emac_reg_write_all(void __iomem *base, + const struct emac_reg_write *itr, size_t size) +{ + size_t i; + + for (i = 0; i < size; ++itr, ++i) + writel(itr->val, base + itr->offset); +} + +static const struct emac_reg_write physical_coding_sublayer_programming[] = { + {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, + {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, + {EMAC_SGMII_PHY_CMN_PWR_CTRL, + BIAS_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN}, + {EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN | L0_TRAN_BIAS_EN}, + {EMAC_SGMII_PHY_RX_PWR_CTRL, + L0_RX_SIGDET_EN | L0_RX_TERM_MODE(1) | L0_RX_I_EN}, + {EMAC_SGMII_PHY_CMN_PWR_CTRL, + BIAS_EN | PLL_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | + PLL_RXCLK_EN}, + {EMAC_SGMII_PHY_LANE_CTRL1, + L0_RX_EQUALIZE_ENABLE | L0_RESET_TSYNC_EN | L0_DRV_LVL(15)}, +}; + +static const struct emac_reg_write sysclk_refclk_setting[] = { + {EMAC_QSERDES_COM_SYSCLK_EN_SEL, SYSCLK_SEL_CMOS}, + {EMAC_QSERDES_COM_SYS_CLK_CTRL, SYSCLK_CM | SYSCLK_AC_COUPLE}, +}; + +static const struct emac_reg_write pll_setting[] = { + {EMAC_QSERDES_COM_PLL_IP_SETI, PLL_IPSETI(1)}, + {EMAC_QSERDES_COM_PLL_CP_SETI, PLL_CPSETI(59)}, + {EMAC_QSERDES_COM_PLL_IP_SETP, PLL_IPSETP(10)}, + {EMAC_QSERDES_COM_PLL_CP_SETP, PLL_CPSETP(9)}, + {EMAC_QSERDES_COM_PLL_CRCTRL, PLL_RCTRL(15) | PLL_CCTRL(11)}, + {EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN | PLL_DIV_ORD}, + {EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX | DEC_START1(2)}, + {EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX | DEC_START2}, + {EMAC_QSERDES_COM_DIV_FRAC_START1, + DIV_FRAC_START_MUX | DIV_FRAC_START(85)}, + {EMAC_QSERDES_COM_DIV_FRAC_START2, + DIV_FRAC_START_MUX | DIV_FRAC_START(42)}, + {EMAC_QSERDES_COM_DIV_FRAC_START3, + DIV_FRAC_START3_MUX | DIV_FRAC_START3(3)}, + {EMAC_QSERDES_COM_PLLLOCK_CMP1, PLLLOCK_CMP(43)}, + {EMAC_QSERDES_COM_PLLLOCK_CMP2, PLLLOCK_CMP(104)}, + {EMAC_QSERDES_COM_PLLLOCK_CMP3, PLLLOCK_CMP(0)}, + {EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN}, + {EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE}, +}; + +static const struct emac_reg_write cdr_setting[] = { + {EMAC_QSERDES_RX_CDR_CONTROL, + SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(2)}, + {EMAC_QSERDES_RX_CDR_CONTROL2, + SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(4)}, +}; + +static const struct emac_reg_write tx_rx_setting[] = { + {EMAC_QSERDES_TX_BIST_MODE_LANENO, 0}, + {EMAC_QSERDES_TX_TX_DRV_LVL, TX_DRV_LVL_MUX | TX_DRV_LVL(15)}, + {EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN, EMP_EN_MUX | EMP_EN}, + {EMAC_QSERDES_TX_TX_EMP_POST1_LVL, + TX_EMP_POST1_LVL_MUX | TX_EMP_POST1_LVL(1)}, + {EMAC_QSERDES_RX_RX_EQ_GAIN12, RX_EQ_GAIN2(15) | RX_EQ_GAIN1(15)}, + {EMAC_QSERDES_TX_LANE_MODE, LANE_MODE(8)}, +}; + +int emac_sgmii_init_fsm9900(struct emac_adapter *adpt) +{ + struct emac_phy *phy = &adpt->phy; + unsigned int i; + + emac_reg_write_all(phy->base, physical_coding_sublayer_programming, + ARRAY_SIZE(physical_coding_sublayer_programming)); + emac_reg_write_all(phy->base, sysclk_refclk_setting, + ARRAY_SIZE(sysclk_refclk_setting)); + emac_reg_write_all(phy->base, pll_setting, ARRAY_SIZE(pll_setting)); + emac_reg_write_all(phy->base, cdr_setting, ARRAY_SIZE(cdr_setting)); + emac_reg_write_all(phy->base, tx_rx_setting, ARRAY_SIZE(tx_rx_setting)); + + /* Power up the Ser/Des engine */ + writel(SERDES_START, phy->base + EMAC_SGMII_PHY_SERDES_START); + + for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { + if (readl(phy->base + EMAC_QSERDES_COM_RESET_SM) & READY) + break; + usleep_range(100, 200); + } + + if (i == SERDES_START_WAIT_TIMES) { + netdev_err(adpt->netdev, "error: ser/des failed to start\n"); + return -EIO; + } + /* Mask out all the SGMII Interrupt */ + writel(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_MASK); + + return 0; +} diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c new file mode 100644 index 0000000..6170200 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Qualcomm Technologies, Inc. QDF2432 EMAC SGMII Controller driver. + */ + +#include +#include "emac.h" + +/* EMAC_SGMII register offsets */ +#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C +#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 +#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 +#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 +#define EMAC_SGMII_PHY_RESET_CTRL 0x00a8 +#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 + +/* SGMII digital lane registers */ +#define EMAC_SGMII_LN_DRVR_CTRL0 0x000C +#define EMAC_SGMII_LN_DRVR_TAP_EN 0x0018 +#define EMAC_SGMII_LN_TX_MARGINING 0x001C +#define EMAC_SGMII_LN_TX_PRE 0x0020 +#define EMAC_SGMII_LN_TX_POST 0x0024 +#define EMAC_SGMII_LN_TX_BAND_MODE 0x0060 +#define EMAC_SGMII_LN_LANE_MODE 0x0064 +#define EMAC_SGMII_LN_PARALLEL_RATE 0x0078 +#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x00B8 +#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x00D0 +#define EMAC_SGMII_LN_VGA_INITVAL 0x0134 +#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x017C +#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x0188 +#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x0194 +#define EMAC_SGMII_LN_RX_BAND 0x019C +#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x01B8 +#define EMAC_SGMII_LN_RSM_CONFIG 0x01F0 +#define EMAC_SGMII_LN_SIGDET_ENABLES 0x0224 +#define EMAC_SGMII_LN_SIGDET_CNTRL 0x0228 +#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x022C +#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x02A0 +#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x02AC +#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x02BC + +/* SGMII digital lane register values */ +#define UCDR_STEP_BY_TWO_MODE0 BIT(7) +#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) +#define UCDR_ENABLE BIT(6) +#define UCDR_SO_SATURATION(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS4 BIT(7) +#define SIGDET_EN_PS0_TO_PS2 BIT(6) + +#define TXVAL_VALID_INIT BIT(4) +#define KR_PCIGEN3_MODE BIT(0) + +#define MAIN_EN BIT(0) + +#define TX_MARGINING_MUX BIT(6) +#define TX_MARGINING(x) ((x) & 0x3f) + +#define TX_PRE_MUX BIT(6) + +#define TX_POST_MUX BIT(6) + +#define CML_GEAR_MODE(x) (((x) & 7) << 3) +#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) + +#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) +#define MIXER_DATARATE_MODE(x) ((x) & 3) + +#define VGA_THRESH_DFE(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) +#define SIGDET_FLT_BYP BIT(0) + +#define SIGDET_LVL(x) (((x) & 0xf) << 4) + +#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) + +#define DRVR_LOGIC_CLK_EN BIT(4) +#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) + +#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) + +#define BAND_MODE0(x) ((x) & 0x3) + +#define LANE_MODE(x) ((x) & 0x1f) + +#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) +#define BYPASS_RSM_SAMP_CAL BIT(1) +#define BYPASS_RSM_DLL_CAL BIT(0) + +#define L0_RX_EQUALIZE_ENABLE BIT(6) + +#define PWRDN_B BIT(0) + +#define CDR_MAX_CNT(x) ((x) & 0xff) + +#define SERDES_START_WAIT_TIMES 100 + +struct emac_reg_write { + unsigned int offset; + u32 val; +}; + +static void emac_reg_write_all(void __iomem *base, + const struct emac_reg_write *itr, size_t size) +{ + size_t i; + + for (i = 0; i < size; ++itr, ++i) + writel(itr->val, base + itr->offset); +} + +static const struct emac_reg_write sgmii_laned[] = { + /* CDR Settings */ + {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, + UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, + {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, + {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, + + /* TX/RX Settings */ + {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, + + {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, + {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, + {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, + {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, + {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, + + {EMAC_SGMII_LN_CML_CTRL_MODE0, + CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, + {EMAC_SGMII_LN_MIXER_CTRL_MODE0, + MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, + {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, + {EMAC_SGMII_LN_SIGDET_ENABLES, + SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, + {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, + + {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, + {EMAC_SGMII_LN_RX_MISC_CNTRL0, 0}, + {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, + DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, + + {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, + {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(2)}, + {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(3)}, + {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, + {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(3)}, + {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, +}; + +static const struct emac_reg_write physical_coding_sublayer_programming[] = { + {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, + {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, + {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, + {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, +}; + +int emac_sgmii_init_qdf2432(struct emac_adapter *adpt) +{ + struct emac_phy *phy = &adpt->phy; + void __iomem *phy_regs = phy->base; + void __iomem *laned = phy->digital; + unsigned int i; + u32 lnstatus; + + /* PCS lane-x init */ + emac_reg_write_all(phy->base, physical_coding_sublayer_programming, + ARRAY_SIZE(physical_coding_sublayer_programming)); + + /* SGMII lane-x init */ + emac_reg_write_all(phy->digital, sgmii_laned, ARRAY_SIZE(sgmii_laned)); + + /* Power up PCS and start reset lane state machine */ + + writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); + writel(1, laned + SGMII_LN_RSM_START); + + /* Wait for c_ready assertion */ + for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { + lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); + if (lnstatus & BIT(1)) + break; + usleep_range(100, 200); + } + + if (i == SERDES_START_WAIT_TIMES) { + netdev_err(adpt->netdev, "SGMII failed to start\n"); + return -EIO; + } + + /* Disable digital and SERDES loopback */ + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); + writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); + + /* Mask out all the SGMII Interrupt */ + writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); + + return 0; +} diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c index 72fe343..0b25c46 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c @@ -20,448 +20,33 @@ #include "emac-mac.h" #include "emac-sgmii.h" -/* EMAC_QSERDES register offsets */ -#define EMAC_QSERDES_COM_SYS_CLK_CTRL 0x000000 -#define EMAC_QSERDES_COM_PLL_CNTRL 0x000014 -#define EMAC_QSERDES_COM_PLL_IP_SETI 0x000018 -#define EMAC_QSERDES_COM_PLL_CP_SETI 0x000024 -#define EMAC_QSERDES_COM_PLL_IP_SETP 0x000028 -#define EMAC_QSERDES_COM_PLL_CP_SETP 0x00002c -#define EMAC_QSERDES_COM_SYSCLK_EN_SEL 0x000038 -#define EMAC_QSERDES_COM_RESETSM_CNTRL 0x000040 -#define EMAC_QSERDES_COM_PLLLOCK_CMP1 0x000044 -#define EMAC_QSERDES_COM_PLLLOCK_CMP2 0x000048 -#define EMAC_QSERDES_COM_PLLLOCK_CMP3 0x00004c -#define EMAC_QSERDES_COM_PLLLOCK_CMP_EN 0x000050 -#define EMAC_QSERDES_COM_DEC_START1 0x000064 -#define EMAC_QSERDES_COM_DIV_FRAC_START1 0x000098 -#define EMAC_QSERDES_COM_DIV_FRAC_START2 0x00009c -#define EMAC_QSERDES_COM_DIV_FRAC_START3 0x0000a0 -#define EMAC_QSERDES_COM_DEC_START2 0x0000a4 -#define EMAC_QSERDES_COM_PLL_CRCTRL 0x0000ac -#define EMAC_QSERDES_COM_RESET_SM 0x0000bc -#define EMAC_QSERDES_TX_BIST_MODE_LANENO 0x000100 -#define EMAC_QSERDES_TX_TX_EMP_POST1_LVL 0x000108 -#define EMAC_QSERDES_TX_TX_DRV_LVL 0x00010c -#define EMAC_QSERDES_TX_LANE_MODE 0x000150 -#define EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN 0x000170 -#define EMAC_QSERDES_RX_CDR_CONTROL 0x000200 -#define EMAC_QSERDES_RX_CDR_CONTROL2 0x000210 -#define EMAC_QSERDES_RX_RX_EQ_GAIN12 0x000230 - /* EMAC_SGMII register offsets */ -#define EMAC_SGMII_PHY_SERDES_START 0x000000 -#define EMAC_SGMII_PHY_CMN_PWR_CTRL 0x000004 -#define EMAC_SGMII_PHY_RX_PWR_CTRL 0x000008 -#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x00000C -#define EMAC_SGMII_PHY_LANE_CTRL1 0x000018 -#define EMAC_SGMII_PHY_AUTONEG_CFG2 0x000048 -#define EMAC_SGMII_PHY_CDR_CTRL0 0x000058 -#define EMAC_SGMII_PHY_SPEED_CFG1 0x000074 -#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x000080 -#define EMAC_SGMII_PHY_RESET_CTRL 0x0000a8 -#define EMAC_SGMII_PHY_IRQ_CMD 0x0000ac -#define EMAC_SGMII_PHY_INTERRUPT_CLEAR 0x0000b0 -#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x0000b4 -#define EMAC_SGMII_PHY_INTERRUPT_STATUS 0x0000b8 -#define EMAC_SGMII_PHY_RX_CHK_STATUS 0x0000d4 -#define EMAC_SGMII_PHY_AUTONEG0_STATUS 0x0000e0 -#define EMAC_SGMII_PHY_AUTONEG1_STATUS 0x0000e4 - -/* EMAC_QSERDES_COM_PLL_IP_SETI */ -#define PLL_IPSETI(x) ((x) & 0x3f) - -/* EMAC_QSERDES_COM_PLL_CP_SETI */ -#define PLL_CPSETI(x) ((x) & 0xff) - -/* EMAC_QSERDES_COM_PLL_IP_SETP */ -#define PLL_IPSETP(x) ((x) & 0x3f) - -/* EMAC_QSERDES_COM_PLL_CP_SETP */ -#define PLL_CPSETP(x) ((x) & 0x1f) - -/* EMAC_QSERDES_COM_PLL_CRCTRL */ -#define PLL_RCTRL(x) (((x) & 0xf) << 4) -#define PLL_CCTRL(x) ((x) & 0xf) - -/* SGMII v2 PHY registers per lane */ -#define EMAC_SGMII_PHY_LN_OFFSET 0x0400 - -/* SGMII v2 digital lane registers */ -#define EMAC_SGMII_LN_DRVR_CTRL0 0x00C -#define EMAC_SGMII_LN_DRVR_TAP_EN 0x018 -#define EMAC_SGMII_LN_TX_MARGINING 0x01C -#define EMAC_SGMII_LN_TX_PRE 0x020 -#define EMAC_SGMII_LN_TX_POST 0x024 -#define EMAC_SGMII_LN_TX_BAND_MODE 0x060 -#define EMAC_SGMII_LN_LANE_MODE 0x064 -#define EMAC_SGMII_LN_PARALLEL_RATE 0x078 -#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x0B8 -#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x0D0 -#define EMAC_SGMII_LN_VGA_INITVAL 0x134 -#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x17C -#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x188 -#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x194 -#define EMAC_SGMII_LN_RX_BAND 0x19C -#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x1B8 -#define EMAC_SGMII_LN_RSM_CONFIG 0x1F0 -#define EMAC_SGMII_LN_SIGDET_ENABLES 0x224 -#define EMAC_SGMII_LN_SIGDET_CNTRL 0x228 -#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x22C -#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x2A0 -#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x2AC -#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x2BC - -/* SGMII v2 digital lane register values */ -#define UCDR_STEP_BY_TWO_MODE0 BIT(7) -#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) -#define UCDR_ENABLE BIT(6) -#define UCDR_SO_SATURATION(x) ((x) & 0x3f) -#define SIGDET_LP_BYP_PS4 BIT(7) -#define SIGDET_EN_PS0_TO_PS2 BIT(6) -#define EN_ACCOUPLEVCM_SW_MUX BIT(5) -#define EN_ACCOUPLEVCM_SW BIT(4) -#define RX_SYNC_EN BIT(3) -#define RXTERM_HIGHZ_PS5 BIT(2) -#define SIGDET_EN_PS3 BIT(1) -#define EN_ACCOUPLE_VCM_PS3 BIT(0) -#define UFS_MODE BIT(5) -#define TXVAL_VALID_INIT BIT(4) -#define TXVAL_VALID_MUX BIT(3) -#define TXVAL_VALID BIT(2) -#define USB3P1_MODE BIT(1) -#define KR_PCIGEN3_MODE BIT(0) -#define PRE_EN BIT(3) -#define POST_EN BIT(2) -#define MAIN_EN_MUX BIT(1) -#define MAIN_EN BIT(0) -#define TX_MARGINING_MUX BIT(6) -#define TX_MARGINING(x) ((x) & 0x3f) -#define TX_PRE_MUX BIT(6) -#define TX_PRE(x) ((x) & 0x3f) -#define TX_POST_MUX BIT(6) -#define TX_POST(x) ((x) & 0x3f) -#define CML_GEAR_MODE(x) (((x) & 7) << 3) -#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) -#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) -#define MIXER_DATARATE_MODE(x) ((x) & 3) -#define VGA_THRESH_DFE(x) ((x) & 0x3f) -#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) -#define SIGDET_LP_BYP_MUX BIT(4) -#define SIGDET_LP_BYP BIT(3) -#define SIGDET_EN_MUX BIT(2) -#define SIGDET_EN BIT(1) -#define SIGDET_FLT_BYP BIT(0) -#define SIGDET_LVL(x) (((x) & 0xf) << 4) -#define SIGDET_BW_CTRL(x) ((x) & 0xf) -#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) -#define SIGDET_DEGLITCH_BYP BIT(0) -#define INVERT_PCS_RX_CLK BIT(7) -#define PWM_EN BIT(6) -#define RXBIAS_SEL(x) (((x) & 0x3) << 4) -#define EBDAC_SIGN BIT(3) -#define EDAC_SIGN BIT(2) -#define EN_AUXTAP1SIGN_INVERT BIT(1) -#define EN_DAC_CHOPPING BIT(0) -#define DRVR_LOGIC_CLK_EN BIT(4) -#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) -#define PARALLEL_RATE_MODE2(x) (((x) & 0x3) << 4) -#define PARALLEL_RATE_MODE1(x) (((x) & 0x3) << 2) -#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) -#define BAND_MODE2(x) (((x) & 0x3) << 4) -#define BAND_MODE1(x) (((x) & 0x3) << 2) -#define BAND_MODE0(x) ((x) & 0x3) -#define LANE_SYNC_MODE BIT(5) -#define LANE_MODE(x) ((x) & 0x1f) -#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) -#define EN_DLL_MODE0 BIT(4) -#define EN_IQ_DCC_MODE0 BIT(3) -#define EN_IQCAL_MODE0 BIT(2) -#define EN_QPATH_MODE0 BIT(1) -#define EN_EPATH_MODE0 BIT(0) -#define FORCE_TSYNC_ACK BIT(7) -#define FORCE_CMN_ACK BIT(6) -#define FORCE_CMN_READY BIT(5) -#define EN_RCLK_DEGLITCH BIT(4) -#define BYPASS_RSM_CDR_RESET BIT(3) -#define BYPASS_RSM_TSYNC BIT(2) -#define BYPASS_RSM_SAMP_CAL BIT(1) -#define BYPASS_RSM_DLL_CAL BIT(0) - -/* EMAC_QSERDES_COM_SYS_CLK_CTRL */ -#define SYSCLK_CM BIT(4) -#define SYSCLK_AC_COUPLE BIT(3) - -/* EMAC_QSERDES_COM_PLL_CNTRL */ -#define OCP_EN BIT(5) -#define PLL_DIV_FFEN BIT(2) -#define PLL_DIV_ORD BIT(1) - -/* EMAC_QSERDES_COM_SYSCLK_EN_SEL */ -#define SYSCLK_SEL_CMOS BIT(3) - -/* EMAC_QSERDES_COM_RESETSM_CNTRL */ -#define FRQ_TUNE_MODE BIT(4) - -/* EMAC_QSERDES_COM_PLLLOCK_CMP_EN */ -#define PLLLOCK_CMP_EN BIT(0) - -/* EMAC_QSERDES_COM_DEC_START1 */ -#define DEC_START1_MUX BIT(7) -#define DEC_START1(x) ((x) & 0x7f) - -/* EMAC_QSERDES_COM_DIV_FRAC_START1 * EMAC_QSERDES_COM_DIV_FRAC_START2 */ -#define DIV_FRAC_START_MUX BIT(7) -#define DIV_FRAC_START(x) ((x) & 0x7f) - -/* EMAC_QSERDES_COM_DIV_FRAC_START3 */ -#define DIV_FRAC_START3_MUX BIT(4) -#define DIV_FRAC_START3(x) ((x) & 0xf) - -/* EMAC_QSERDES_COM_DEC_START2 */ -#define DEC_START2_MUX BIT(1) -#define DEC_START2 BIT(0) - -/* EMAC_QSERDES_COM_RESET_SM */ -#define READY BIT(5) - -/* EMAC_QSERDES_TX_TX_EMP_POST1_LVL */ -#define TX_EMP_POST1_LVL_MUX BIT(5) -#define TX_EMP_POST1_LVL(x) ((x) & 0x1f) -#define TX_EMP_POST1_LVL_BMSK 0x1f -#define TX_EMP_POST1_LVL_SHFT 0 - -/* EMAC_QSERDES_TX_TX_DRV_LVL */ -#define TX_DRV_LVL_MUX BIT(4) -#define TX_DRV_LVL(x) ((x) & 0xf) - -/* EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN */ -#define EMP_EN_MUX BIT(1) -#define EMP_EN BIT(0) - -/* EMAC_QSERDES_RX_CDR_CONTROL & EMAC_QSERDES_RX_CDR_CONTROL2 */ -#define HBW_PD_EN BIT(7) -#define SECONDORDERENABLE BIT(6) -#define FIRSTORDER_THRESH(x) (((x) & 0x7) << 3) -#define SECONDORDERGAIN(x) ((x) & 0x7) - -/* EMAC_QSERDES_RX_RX_EQ_GAIN12 */ -#define RX_EQ_GAIN2(x) (((x) & 0xf) << 4) -#define RX_EQ_GAIN1(x) ((x) & 0xf) - -/* EMAC_SGMII_PHY_SERDES_START */ -#define SERDES_START BIT(0) - -/* EMAC_SGMII_PHY_CMN_PWR_CTRL */ -#define BIAS_EN BIT(6) -#define PLL_EN BIT(5) -#define SYSCLK_EN BIT(4) -#define CLKBUF_L_EN BIT(3) -#define PLL_TXCLK_EN BIT(1) -#define PLL_RXCLK_EN BIT(0) - -/* EMAC_SGMII_PHY_RX_PWR_CTRL */ -#define L0_RX_SIGDET_EN BIT(7) -#define L0_RX_TERM_MODE(x) (((x) & 3) << 4) -#define L0_RX_I_EN BIT(1) - -/* EMAC_SGMII_PHY_TX_PWR_CTRL */ -#define L0_TX_EN BIT(5) -#define L0_CLKBUF_EN BIT(4) -#define L0_TRAN_BIAS_EN BIT(1) - -/* EMAC_SGMII_PHY_LANE_CTRL1 */ -#define L0_RX_EQUALIZE_ENABLE BIT(6) -#define L0_RESET_TSYNC_EN BIT(4) -#define L0_DRV_LVL(x) ((x) & 0xf) - -/* EMAC_SGMII_PHY_AUTONEG_CFG2 */ +#define EMAC_SGMII_PHY_AUTONEG_CFG2 0x0048 +#define EMAC_SGMII_PHY_SPEED_CFG1 0x0074 +#define EMAC_SGMII_PHY_IRQ_CMD 0x00ac +#define EMAC_SGMII_PHY_INTERRUPT_CLEAR 0x00b0 +#define EMAC_SGMII_PHY_INTERRUPT_STATUS 0x00b8 + #define FORCE_AN_TX_CFG BIT(5) #define FORCE_AN_RX_CFG BIT(4) #define AN_ENABLE BIT(0) -/* EMAC_SGMII_PHY_SPEED_CFG1 */ #define DUPLEX_MODE BIT(4) #define SPDMODE_1000 BIT(1) #define SPDMODE_100 BIT(0) #define SPDMODE_10 0 -#define SPDMODE_BMSK 3 -#define SPDMODE_SHFT 0 - -/* EMAC_SGMII_PHY_POW_DWN_CTRL0 */ -#define PWRDN_B BIT(0) -#define CDR_MAX_CNT(x) ((x) & 0xff) - -/* EMAC_QSERDES_TX_BIST_MODE_LANENO */ -#define BIST_LANE_NUMBER(x) (((x) & 3) << 5) -#define BISTMODE(x) ((x) & 0x1f) - -/* EMAC_QSERDES_COM_PLLLOCK_CMPx */ -#define PLLLOCK_CMP(x) ((x) & 0xff) -/* EMAC_SGMII_PHY_RESET_CTRL */ -#define PHY_SW_RESET BIT(0) - -/* EMAC_SGMII_PHY_IRQ_CMD */ #define IRQ_GLOBAL_CLEAR BIT(0) -/* EMAC_SGMII_PHY_INTERRUPT_MASK */ #define DECODE_CODE_ERR BIT(7) #define DECODE_DISP_ERR BIT(6) -#define PLL_UNLOCK BIT(5) -#define AN_ILLEGAL_TERM BIT(4) -#define SYNC_FAIL BIT(3) -#define AN_START BIT(2) -#define AN_END BIT(1) -#define AN_REQUEST BIT(0) #define SGMII_PHY_IRQ_CLR_WAIT_TIME 10 -#define SGMII_PHY_INTERRUPT_ERR (\ - DECODE_CODE_ERR |\ - DECODE_DISP_ERR) - -#define SGMII_ISR_AN_MASK (\ - AN_REQUEST |\ - AN_START |\ - AN_END |\ - AN_ILLEGAL_TERM |\ - PLL_UNLOCK |\ - SYNC_FAIL) - -#define SGMII_ISR_MASK (\ - SGMII_PHY_INTERRUPT_ERR |\ - SGMII_ISR_AN_MASK) - -/* SGMII TX_CONFIG */ -#define TXCFG_LINK 0x8000 -#define TXCFG_MODE_BMSK 0x1c00 -#define TXCFG_1000_FULL 0x1800 -#define TXCFG_100_FULL 0x1400 -#define TXCFG_100_HALF 0x0400 -#define TXCFG_10_FULL 0x1000 -#define TXCFG_10_HALF 0x0000 +#define SGMII_PHY_INTERRUPT_ERR (DECODE_CODE_ERR | DECODE_DISP_ERR) #define SERDES_START_WAIT_TIMES 100 -struct emac_reg_write { - unsigned int offset; - u32 val; -}; - -static void emac_reg_write_all(void __iomem *base, - const struct emac_reg_write *itr, size_t size) -{ - size_t i; - - for (i = 0; i < size; ++itr, ++i) - writel(itr->val, base + itr->offset); -} - -static const struct emac_reg_write physical_coding_sublayer_programming_v1[] = { - {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, - {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, - {EMAC_SGMII_PHY_CMN_PWR_CTRL, - BIAS_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN}, - {EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN | L0_TRAN_BIAS_EN}, - {EMAC_SGMII_PHY_RX_PWR_CTRL, - L0_RX_SIGDET_EN | L0_RX_TERM_MODE(1) | L0_RX_I_EN}, - {EMAC_SGMII_PHY_CMN_PWR_CTRL, - BIAS_EN | PLL_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | - PLL_RXCLK_EN}, - {EMAC_SGMII_PHY_LANE_CTRL1, - L0_RX_EQUALIZE_ENABLE | L0_RESET_TSYNC_EN | L0_DRV_LVL(15)}, -}; - -static const struct emac_reg_write sysclk_refclk_setting[] = { - {EMAC_QSERDES_COM_SYSCLK_EN_SEL, SYSCLK_SEL_CMOS}, - {EMAC_QSERDES_COM_SYS_CLK_CTRL, SYSCLK_CM | SYSCLK_AC_COUPLE}, -}; - -static const struct emac_reg_write pll_setting[] = { - {EMAC_QSERDES_COM_PLL_IP_SETI, PLL_IPSETI(1)}, - {EMAC_QSERDES_COM_PLL_CP_SETI, PLL_CPSETI(59)}, - {EMAC_QSERDES_COM_PLL_IP_SETP, PLL_IPSETP(10)}, - {EMAC_QSERDES_COM_PLL_CP_SETP, PLL_CPSETP(9)}, - {EMAC_QSERDES_COM_PLL_CRCTRL, PLL_RCTRL(15) | PLL_CCTRL(11)}, - {EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN | PLL_DIV_ORD}, - {EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX | DEC_START1(2)}, - {EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX | DEC_START2}, - {EMAC_QSERDES_COM_DIV_FRAC_START1, - DIV_FRAC_START_MUX | DIV_FRAC_START(85)}, - {EMAC_QSERDES_COM_DIV_FRAC_START2, - DIV_FRAC_START_MUX | DIV_FRAC_START(42)}, - {EMAC_QSERDES_COM_DIV_FRAC_START3, - DIV_FRAC_START3_MUX | DIV_FRAC_START3(3)}, - {EMAC_QSERDES_COM_PLLLOCK_CMP1, PLLLOCK_CMP(43)}, - {EMAC_QSERDES_COM_PLLLOCK_CMP2, PLLLOCK_CMP(104)}, - {EMAC_QSERDES_COM_PLLLOCK_CMP3, PLLLOCK_CMP(0)}, - {EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN}, - {EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE}, -}; - -static const struct emac_reg_write cdr_setting[] = { - {EMAC_QSERDES_RX_CDR_CONTROL, - SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(2)}, - {EMAC_QSERDES_RX_CDR_CONTROL2, - SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(4)}, -}; - -static const struct emac_reg_write tx_rx_setting[] = { - {EMAC_QSERDES_TX_BIST_MODE_LANENO, 0}, - {EMAC_QSERDES_TX_TX_DRV_LVL, TX_DRV_LVL_MUX | TX_DRV_LVL(15)}, - {EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN, EMP_EN_MUX | EMP_EN}, - {EMAC_QSERDES_TX_TX_EMP_POST1_LVL, - TX_EMP_POST1_LVL_MUX | TX_EMP_POST1_LVL(1)}, - {EMAC_QSERDES_RX_RX_EQ_GAIN12, RX_EQ_GAIN2(15) | RX_EQ_GAIN1(15)}, - {EMAC_QSERDES_TX_LANE_MODE, LANE_MODE(8)}, -}; - -static const struct emac_reg_write sgmii_v2_laned[] = { - /* CDR Settings */ - {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, - UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, - {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, - {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, - - /* TX/RX Settings */ - {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, - - {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, - {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, - {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, - {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, - {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, - - {EMAC_SGMII_LN_CML_CTRL_MODE0, - CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, - {EMAC_SGMII_LN_MIXER_CTRL_MODE0, - MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, - {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, - {EMAC_SGMII_LN_SIGDET_ENABLES, - SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, - {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, - - {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, - {EMAC_SGMII_LN_RX_MISC_CNTRL0, 0}, - {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, - DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, - - {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, - {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(2)}, - {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(3)}, - {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, - {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(3)}, - {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, -}; - -static const struct emac_reg_write physical_coding_sublayer_programming_v2[] = { - {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, - {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, - {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, - {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, -}; - static int emac_sgmii_link_init(struct emac_adapter *adpt) { struct phy_device *phydev = adpt->phydev; @@ -536,98 +121,6 @@ static int emac_sgmii_irq_clear(struct emac_adapter *adpt, u32 irq_bits) return 0; } -int emac_sgmii_init_v1(struct emac_adapter *adpt) -{ - struct emac_phy *phy = &adpt->phy; - unsigned int i; - int ret; - - ret = emac_sgmii_link_init(adpt); - if (ret) - return ret; - - emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v1, - ARRAY_SIZE(physical_coding_sublayer_programming_v1)); - emac_reg_write_all(phy->base, sysclk_refclk_setting, - ARRAY_SIZE(sysclk_refclk_setting)); - emac_reg_write_all(phy->base, pll_setting, ARRAY_SIZE(pll_setting)); - emac_reg_write_all(phy->base, cdr_setting, ARRAY_SIZE(cdr_setting)); - emac_reg_write_all(phy->base, tx_rx_setting, - ARRAY_SIZE(tx_rx_setting)); - - /* Power up the Ser/Des engine */ - writel(SERDES_START, phy->base + EMAC_SGMII_PHY_SERDES_START); - - for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { - if (readl(phy->base + EMAC_QSERDES_COM_RESET_SM) & READY) - break; - usleep_range(100, 200); - } - - if (i == SERDES_START_WAIT_TIMES) { - netdev_err(adpt->netdev, "error: ser/des failed to start\n"); - return -EIO; - } - /* Mask out all the SGMII Interrupt */ - writel(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_MASK); - - emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); - - return 0; -} - -int emac_sgmii_init_v2(struct emac_adapter *adpt) -{ - struct emac_phy *phy = &adpt->phy; - void __iomem *phy_regs = phy->base; - void __iomem *laned = phy->digital; - unsigned int i; - u32 lnstatus; - int ret; - - ret = emac_sgmii_link_init(adpt); - if (ret) - return ret; - - /* PCS lane-x init */ - emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v2, - ARRAY_SIZE(physical_coding_sublayer_programming_v2)); - - /* SGMII lane-x init */ - emac_reg_write_all(phy->digital, - sgmii_v2_laned, ARRAY_SIZE(sgmii_v2_laned)); - - /* Power up PCS and start reset lane state machine */ - - writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); - writel(1, laned + SGMII_LN_RSM_START); - - /* Wait for c_ready assertion */ - for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { - lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); - if (lnstatus & BIT(1)) - break; - usleep_range(100, 200); - } - - if (i == SERDES_START_WAIT_TIMES) { - netdev_err(adpt->netdev, "SGMII failed to start\n"); - return -EIO; - } - - /* Disable digital and SERDES loopback */ - writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); - writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); - writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); - - /* Mask out all the SGMII Interrupt */ - writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); - - emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); - - return 0; -} - static void emac_sgmii_reset_prepare(struct emac_adapter *adpt) { struct emac_phy *phy = &adpt->phy; @@ -651,16 +144,19 @@ void emac_sgmii_reset(struct emac_adapter *adpt) { int ret; - clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000); emac_sgmii_reset_prepare(adpt); + ret = emac_sgmii_link_init(adpt); + if (ret) { + netdev_err(adpt->netdev, "unsupported link speed\n"); + return; + } + ret = adpt->phy.initialize(adpt); if (ret) netdev_err(adpt->netdev, "could not reinitialize internal PHY (error=%i)\n", ret); - - clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000); } static int emac_sgmii_acpi_match(struct device *dev, void *data) @@ -668,7 +164,7 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data) static const struct acpi_device_id match_table[] = { { .id = "QCOM8071", - .driver_data = (kernel_ulong_t)emac_sgmii_init_v2, + .driver_data = (kernel_ulong_t)emac_sgmii_init_qdf2432, }, {} }; @@ -684,11 +180,11 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data) static const struct of_device_id emac_sgmii_dt_match[] = { { .compatible = "qcom,fsm9900-emac-sgmii", - .data = emac_sgmii_init_v1, + .data = emac_sgmii_init_fsm9900, }, { .compatible = "qcom,qdf2432-emac-sgmii", - .data = emac_sgmii_init_v2, + .data = emac_sgmii_init_qdf2432, }, {} }; @@ -765,6 +261,8 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) if (ret) goto error; + emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); + /* We've remapped the addresses, so we don't need the device any * more. of_find_device_by_node() says we should release it. */ diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h index ce79212..e2bef14 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h @@ -16,9 +16,10 @@ struct emac_adapter; struct platform_device; -int emac_sgmii_init_v1(struct emac_adapter *adpt); -int emac_sgmii_init_v2(struct emac_adapter *adpt); int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt); void emac_sgmii_reset(struct emac_adapter *adpt); +int emac_sgmii_init_fsm9900(struct emac_adapter *adpt); +int emac_sgmii_init_qdf2432(struct emac_adapter *adpt); + #endif -- cgit v1.1 From a51f4047232aeb4aee85268b351c7a84b83b36c5 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 8 Dec 2016 13:24:21 -0600 Subject: net: qcom/emac: add support for the Qualcomm Technologies QDF2400 The QDF2432 and the QDF2400 have slightly different internal PHYs, so there are some programming differences. Some of the registers in the QDF2400 have moved, and some registers require different values during initialization. Because of the differences, and because HIDs are a scare resource, the ACPI tables specify the hardware version in an _HRV property. Version 1 is the QDF2432, and version 2 is the QDF2400. Any future SOC that has the same internal PHY but different programming requirements will be assigned the next available version number. Signed-off-by: Timur Tabi Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/emac/Makefile | 3 +- .../ethernet/qualcomm/emac/emac-sgmii-qdf2400.c | 217 +++++++++++++++++++++ drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 33 +++- drivers/net/ethernet/qualcomm/emac/emac-sgmii.h | 1 + 4 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c diff --git a/drivers/net/ethernet/qualcomm/emac/Makefile b/drivers/net/ethernet/qualcomm/emac/Makefile index 204b787..7a66879 100644 --- a/drivers/net/ethernet/qualcomm/emac/Makefile +++ b/drivers/net/ethernet/qualcomm/emac/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o \ - emac-sgmii-fsm9900.o emac-sgmii-qdf2432.o + emac-sgmii-fsm9900.o emac-sgmii-qdf2432.o \ + emac-sgmii-qdf2400.o diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c new file mode 100644 index 0000000..5b84194 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c @@ -0,0 +1,217 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Qualcomm Technologies, Inc. QDF2400 EMAC SGMII Controller driver. + */ + +#include +#include "emac.h" + +/* EMAC_SGMII register offsets */ +#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C +#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 +#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 +#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 +#define EMAC_SGMII_PHY_RESET_CTRL 0x00a8 +#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 + +/* SGMII digital lane registers */ +#define EMAC_SGMII_LN_DRVR_CTRL0 0x000C +#define EMAC_SGMII_LN_DRVR_TAP_EN 0x0018 +#define EMAC_SGMII_LN_TX_MARGINING 0x001C +#define EMAC_SGMII_LN_TX_PRE 0x0020 +#define EMAC_SGMII_LN_TX_POST 0x0024 +#define EMAC_SGMII_LN_TX_BAND_MODE 0x0060 +#define EMAC_SGMII_LN_LANE_MODE 0x0064 +#define EMAC_SGMII_LN_PARALLEL_RATE 0x007C +#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x00C0 +#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x00D8 +#define EMAC_SGMII_LN_VGA_INITVAL 0x013C +#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x0184 +#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x0190 +#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x019C +#define EMAC_SGMII_LN_RX_BAND 0x01A4 +#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x01C0 +#define EMAC_SGMII_LN_RSM_CONFIG 0x01F8 +#define EMAC_SGMII_LN_SIGDET_ENABLES 0x0230 +#define EMAC_SGMII_LN_SIGDET_CNTRL 0x0234 +#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x0238 +#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x02AC +#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x02B8 +#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x02C8 + +/* SGMII digital lane register values */ +#define UCDR_STEP_BY_TWO_MODE0 BIT(7) +#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) +#define UCDR_ENABLE BIT(6) +#define UCDR_SO_SATURATION(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS4 BIT(7) +#define SIGDET_EN_PS0_TO_PS2 BIT(6) + +#define TXVAL_VALID_INIT BIT(4) +#define KR_PCIGEN3_MODE BIT(0) + +#define MAIN_EN BIT(0) + +#define TX_MARGINING_MUX BIT(6) +#define TX_MARGINING(x) ((x) & 0x3f) + +#define TX_PRE_MUX BIT(6) + +#define TX_POST_MUX BIT(6) + +#define CML_GEAR_MODE(x) (((x) & 7) << 3) +#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) + +#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) +#define MIXER_DATARATE_MODE(x) ((x) & 3) + +#define VGA_THRESH_DFE(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) +#define SIGDET_FLT_BYP BIT(0) + +#define SIGDET_LVL(x) (((x) & 0xf) << 4) + +#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) + +#define INVERT_PCS_RX_CLK BIT(7) + +#define DRVR_LOGIC_CLK_EN BIT(4) +#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) + +#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) + +#define BAND_MODE0(x) ((x) & 0x3) + +#define LANE_MODE(x) ((x) & 0x1f) + +#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) +#define EN_DLL_MODE0 BIT(4) +#define EN_IQ_DCC_MODE0 BIT(3) +#define EN_IQCAL_MODE0 BIT(2) + +#define BYPASS_RSM_SAMP_CAL BIT(1) +#define BYPASS_RSM_DLL_CAL BIT(0) + +#define L0_RX_EQUALIZE_ENABLE BIT(6) + +#define PWRDN_B BIT(0) + +#define CDR_MAX_CNT(x) ((x) & 0xff) + +#define SERDES_START_WAIT_TIMES 100 + +struct emac_reg_write { + unsigned int offset; + u32 val; +}; + +static void emac_reg_write_all(void __iomem *base, + const struct emac_reg_write *itr, size_t size) +{ + size_t i; + + for (i = 0; i < size; ++itr, ++i) + writel(itr->val, base + itr->offset); +} + +static const struct emac_reg_write sgmii_laned[] = { + /* CDR Settings */ + {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, + UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, + {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, + {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, + + /* TX/RX Settings */ + {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, + + {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, + {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, + {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, + {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, + {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, + + {EMAC_SGMII_LN_CML_CTRL_MODE0, + CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, + {EMAC_SGMII_LN_MIXER_CTRL_MODE0, + MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, + {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, + {EMAC_SGMII_LN_SIGDET_ENABLES, + SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, + {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, + + {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, + {EMAC_SGMII_LN_RX_MISC_CNTRL0, INVERT_PCS_RX_CLK}, + {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, + DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, + + {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, + {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(1)}, + {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(2)}, + {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, + {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(2) | + EN_DLL_MODE0 | EN_IQ_DCC_MODE0 | EN_IQCAL_MODE0}, + {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, +}; + +static const struct emac_reg_write physical_coding_sublayer_programming[] = { + {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, + {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, + {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, + {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, +}; + +int emac_sgmii_init_qdf2400(struct emac_adapter *adpt) +{ + struct emac_phy *phy = &adpt->phy; + void __iomem *phy_regs = phy->base; + void __iomem *laned = phy->digital; + unsigned int i; + u32 lnstatus; + + /* PCS lane-x init */ + emac_reg_write_all(phy->base, physical_coding_sublayer_programming, + ARRAY_SIZE(physical_coding_sublayer_programming)); + + /* SGMII lane-x init */ + emac_reg_write_all(phy->digital, sgmii_laned, ARRAY_SIZE(sgmii_laned)); + + /* Power up PCS and start reset lane state machine */ + + writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); + writel(1, laned + SGMII_LN_RSM_START); + + /* Wait for c_ready assertion */ + for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { + lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); + if (lnstatus & BIT(1)) + break; + usleep_range(100, 200); + } + + if (i == SERDES_START_WAIT_TIMES) { + netdev_err(adpt->netdev, "SGMII failed to start\n"); + return -EIO; + } + + /* Disable digital and SERDES loopback */ + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); + writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); + + /* Mask out all the SGMII Interrupt */ + writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); + + return 0; +} diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c index 0b25c46..bf722a9 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c @@ -161,20 +161,45 @@ void emac_sgmii_reset(struct emac_adapter *adpt) static int emac_sgmii_acpi_match(struct device *dev, void *data) { +#ifdef CONFIG_ACPI static const struct acpi_device_id match_table[] = { { .id = "QCOM8071", - .driver_data = (kernel_ulong_t)emac_sgmii_init_qdf2432, }, {} }; const struct acpi_device_id *id = acpi_match_device(match_table, dev); emac_sgmii_initialize *initialize = data; - if (id) - *initialize = (emac_sgmii_initialize)id->driver_data; + if (id) { + acpi_handle handle = ACPI_HANDLE(dev); + unsigned long long hrv; + acpi_status status; + + status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); + if (status) { + if (status == AE_NOT_FOUND) + /* Older versions of the QDF2432 ACPI tables do + * not have an _HRV property. + */ + hrv = 1; + else + /* Something is wrong with the tables */ + return 0; + } - return !!id; + switch (hrv) { + case 1: + *initialize = emac_sgmii_init_qdf2432; + return 1; + case 2: + *initialize = emac_sgmii_init_qdf2400; + return 1; + } + } +#endif + + return 0; } static const struct of_device_id emac_sgmii_dt_match[] = { diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h index e2bef14..80ed3dc 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h @@ -21,5 +21,6 @@ void emac_sgmii_reset(struct emac_adapter *adpt); int emac_sgmii_init_fsm9900(struct emac_adapter *adpt); int emac_sgmii_init_qdf2432(struct emac_adapter *adpt); +int emac_sgmii_init_qdf2400(struct emac_adapter *adpt); #endif -- cgit v1.1 From 4b272750dbe6f92a8d39a0ee1c7bd50d6cc1a2c8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Dec 2016 11:41:54 -0800 Subject: udp: add busylocks in RX path Idea of busylocks is to let producers grab an extra spinlock to relieve pressure on the receive_queue spinlock shared by consumer. This behavior is requested only once socket receive queue is above half occupancy. Under flood, this means that only one producer can be in line trying to acquire the receive_queue spinlock. These busylock can be allocated on a per cpu manner, instead of a per socket one (that would consume a cache line per socket) This patch considerably improves UDP behavior under stress, depending on number of NIC RX queues and/or RPS spread. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/udp.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f5628ad..e6a68d6 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1195,10 +1195,36 @@ void udp_skb_destructor(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(udp_skb_destructor); +/* Idea of busylocks is to let producers grab an extra spinlock + * to relieve pressure on the receive_queue spinlock shared by consumer. + * Under flood, this means that only one producer can be in line + * trying to acquire the receive_queue spinlock. + * These busylock can be allocated on a per cpu manner, instead of a + * per socket one (that would consume a cache line per socket) + */ +static int udp_busylocks_log __read_mostly; +static spinlock_t *udp_busylocks __read_mostly; + +static spinlock_t *busylock_acquire(void *ptr) +{ + spinlock_t *busy; + + busy = udp_busylocks + hash_ptr(ptr, udp_busylocks_log); + spin_lock(busy); + return busy; +} + +static void busylock_release(spinlock_t *busy) +{ + if (busy) + spin_unlock(busy); +} + int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) { struct sk_buff_head *list = &sk->sk_receive_queue; int rmem, delta, amt, err = -ENOMEM; + spinlock_t *busy = NULL; int size; /* try to avoid the costly atomic add/sub pair when the receive @@ -1214,8 +1240,11 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) * - Less cache line misses at copyout() time * - Less work at consume_skb() (less alien page frag freeing) */ - if (rmem > (sk->sk_rcvbuf >> 1)) + if (rmem > (sk->sk_rcvbuf >> 1)) { skb_condense(skb); + + busy = busylock_acquire(sk); + } size = skb->truesize; /* we drop only if the receive buf is full and the receive @@ -1252,6 +1281,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) if (!sock_flag(sk, SOCK_DEAD)) sk->sk_data_ready(sk); + busylock_release(busy); return 0; uncharge_drop: @@ -1259,6 +1289,7 @@ uncharge_drop: drop: atomic_inc(&sk->sk_drops); + busylock_release(busy); return err; } EXPORT_SYMBOL_GPL(__udp_enqueue_schedule_skb); @@ -2613,6 +2644,7 @@ EXPORT_SYMBOL(udp_flow_hashrnd); void __init udp_init(void) { unsigned long limit; + unsigned int i; udp_table_init(&udp_table, "UDP"); limit = nr_free_buffer_pages() / 8; @@ -2623,4 +2655,13 @@ void __init udp_init(void) sysctl_udp_rmem_min = SK_MEM_QUANTUM; sysctl_udp_wmem_min = SK_MEM_QUANTUM; + + /* 16 spinlocks per cpu */ + udp_busylocks_log = ilog2(nr_cpu_ids) + 4; + udp_busylocks = kmalloc(sizeof(spinlock_t) << udp_busylocks_log, + GFP_KERNEL); + if (!udp_busylocks) + panic("UDP: failed to alloc udp_busylocks\n"); + for (i = 0; i < (1U << udp_busylocks_log); i++) + spin_lock_init(udp_busylocks + i); } -- cgit v1.1 From c84d949057cab262b4d3110ead9a42a58c2958f7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Dec 2016 11:41:55 -0800 Subject: udp: copy skb->truesize in the first cache line In UDP RX handler, we currently clear skb->dev before skb is added to receive queue, because device pointer is no longer available once we exit from RCU section. Since this first cache line is always hot, lets reuse this space to store skb->truesize and thus avoid a cache line miss at udp_recvmsg()/udp_skb_destructor time while receive queue spinlock is held. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 ++++++++- net/ipv4/udp.c | 13 ++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0cd92b0..332e767 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -645,8 +645,15 @@ struct sk_buff { struct rb_node rbnode; /* used in netem & tcp stack */ }; struct sock *sk; - struct net_device *dev; + union { + struct net_device *dev; + /* Some protocols might use this space to store information, + * while device pointer would be NULL. + * UDP receive path is one user. + */ + unsigned long dev_scratch; + }; /* * This is the control buffer. It is free to use for every * layer. Please put your private variables there. If you diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index e6a68d6..c608334 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1188,10 +1188,14 @@ static void udp_rmem_release(struct sock *sk, int size, int partial) __sk_mem_reduce_allocated(sk, amt >> SK_MEM_QUANTUM_SHIFT); } -/* Note: called with sk_receive_queue.lock held */ +/* Note: called with sk_receive_queue.lock held. + * Instead of using skb->truesize here, find a copy of it in skb->dev_scratch + * This avoids a cache line miss while receive_queue lock is held. + * Look at __udp_enqueue_schedule_skb() to find where this copy is done. + */ void udp_skb_destructor(struct sock *sk, struct sk_buff *skb) { - udp_rmem_release(sk, skb->truesize, 1); + udp_rmem_release(sk, skb->dev_scratch, 1); } EXPORT_SYMBOL(udp_skb_destructor); @@ -1246,6 +1250,10 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) busy = busylock_acquire(sk); } size = skb->truesize; + /* Copy skb->truesize into skb->dev_scratch to avoid a cache line miss + * in udp_skb_destructor() + */ + skb->dev_scratch = size; /* we drop only if the receive buf is full and the receive * queue contains some other skb @@ -1272,7 +1280,6 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) /* no need to setup a destructor, we will explicitly release the * forward allocated memory on dequeue */ - skb->dev = NULL; sock_skb_set_dropcount(sk, skb); __skb_queue_tail(list, skb); -- cgit v1.1 From 6b229cf77d683f634f0edd876c6d1015402303ad Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Dec 2016 11:41:56 -0800 Subject: udp: add batching to udp_rmem_release() If udp_recvmsg() constantly releases sk_rmem_alloc for every read packet, it gives opportunity for producers to immediately grab spinlocks and desperatly try adding another packet, causing false sharing. We can add a simple heuristic to give the signal by batches of ~25 % of the queue capacity. This patch considerably increases performance under flood by about 50 %, since the thread draining the queue is no longer slowed by false sharing. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/udp.h | 3 +++ net/ipv4/udp.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/linux/udp.h b/include/linux/udp.h index d1fd8cd..c0f5308 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -79,6 +79,9 @@ struct udp_sock { int (*gro_complete)(struct sock *sk, struct sk_buff *skb, int nhoff); + + /* This field is dirtied by udp_recvmsg() */ + int forward_deficit; }; static inline struct udp_sock *udp_sk(const struct sock *sk) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c608334..5a38faa 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1177,8 +1177,20 @@ out: /* fully reclaim rmem/fwd memory allocated for skb */ static void udp_rmem_release(struct sock *sk, int size, int partial) { + struct udp_sock *up = udp_sk(sk); int amt; + if (likely(partial)) { + up->forward_deficit += size; + size = up->forward_deficit; + if (size < (sk->sk_rcvbuf >> 2) && + !skb_queue_empty(&sk->sk_receive_queue)) + return; + } else { + size += up->forward_deficit; + } + up->forward_deficit = 0; + atomic_sub(size, &sk->sk_rmem_alloc); sk->sk_forward_alloc += size; amt = (sk->sk_forward_alloc - partial) & ~(SK_MEM_QUANTUM - 1); -- cgit v1.1 From 02ab0d139cff1efc5aa1fb4378c727668334fe97 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Dec 2016 11:41:57 -0800 Subject: udp: udp_rmem_release() should touch sk_rmem_alloc later In flood situations, keeping sk_rmem_alloc at a high value prevents producers from touching the socket. It makes sense to lower sk_rmem_alloc only at the end of udp_rmem_release() after the thread draining receive queue in udp_recvmsg() finished the writes to sk_forward_alloc. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/udp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 5a38faa..9ca279b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1191,13 +1191,14 @@ static void udp_rmem_release(struct sock *sk, int size, int partial) } up->forward_deficit = 0; - atomic_sub(size, &sk->sk_rmem_alloc); sk->sk_forward_alloc += size; amt = (sk->sk_forward_alloc - partial) & ~(SK_MEM_QUANTUM - 1); sk->sk_forward_alloc -= amt; if (amt) __sk_mem_reduce_allocated(sk, amt >> SK_MEM_QUANTUM_SHIFT); + + atomic_sub(size, &sk->sk_rmem_alloc); } /* Note: called with sk_receive_queue.lock held. -- cgit v1.1 From 3174fed9820edc95cff74ad0934c3240c7fb5115 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 9 Dec 2016 08:02:05 -0800 Subject: net: skb_condense() can also deal with empty skbs It seems attackers can also send UDP packets with no payload at all. skb_condense() can still be a win in this case. It will be possible to replace the custom code in tcp_add_backlog() to get full benefit from skb_condense() Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/skbuff.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 84151cf..65a74e1 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4946,16 +4946,20 @@ EXPORT_SYMBOL(pskb_extract); */ void skb_condense(struct sk_buff *skb) { - if (!skb->data_len || - skb->data_len > skb->end - skb->tail || - skb_cloned(skb)) - return; - - /* Nice, we can free page frag(s) right now */ - __pskb_pull_tail(skb, skb->data_len); + if (skb->data_len) { + if (skb->data_len > skb->end - skb->tail || + skb_cloned(skb)) + return; - /* Now adjust skb->truesize, since __pskb_pull_tail() does - * not do this. + /* Nice, we can free page frag(s) right now */ + __pskb_pull_tail(skb, skb->data_len); + } + /* At this point, skb->truesize might be over estimated, + * because skb had a fragment, and fragments do not tell + * their truesize. + * When we pulled its content into skb->head, fragment + * was freed, but __pskb_pull_tail() could not possibly + * adjust skb->truesize, not knowing the frag truesize. */ skb->truesize = SKB_TRUESIZE(skb_end_offset(skb)); } -- cgit v1.1 From efa172f42836477bf1ac3c9a3053140df764699c Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 9 Dec 2016 21:10:59 -0800 Subject: netlink: use blocking notifier netlink_chain is called in ->release(), which is apparently a process context, so we don't have to use an atomic notifier here. Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/netlink/af_netlink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 246f29d..801d474 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -113,7 +113,7 @@ static atomic_t nl_table_users = ATOMIC_INIT(0); #define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock)); -static ATOMIC_NOTIFIER_HEAD(netlink_chain); +static BLOCKING_NOTIFIER_HEAD(netlink_chain); static DEFINE_SPINLOCK(netlink_tap_lock); static struct list_head netlink_tap_all __read_mostly; @@ -711,7 +711,7 @@ static int netlink_release(struct socket *sock) .protocol = sk->sk_protocol, .portid = nlk->portid, }; - atomic_notifier_call_chain(&netlink_chain, + blocking_notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); } @@ -2504,13 +2504,13 @@ static const struct file_operations netlink_seq_fops = { int netlink_register_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_register(&netlink_chain, nb); + return blocking_notifier_chain_register(&netlink_chain, nb); } EXPORT_SYMBOL(netlink_register_notifier); int netlink_unregister_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_unregister(&netlink_chain, nb); + return blocking_notifier_chain_unregister(&netlink_chain, nb); } EXPORT_SYMBOL(netlink_unregister_notifier); -- cgit v1.1 From fa1bd57a63773f56877c7629a7ff69081ce3ca01 Mon Sep 17 00:00:00 2001 From: Amit Kushwaha Date: Sat, 10 Dec 2016 11:14:47 +0530 Subject: net: socket: removed an unnecessary newline This patch removes a newline which was added in socket.c file in net-next Signed-off-by: Amit Kushwaha Signed-off-by: David S. Miller --- net/socket.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/socket.c b/net/socket.c index 5835383..dc01d7b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1,4 +1,3 @@ - /* * NET An implementation of the SOCKET network access protocol. * -- cgit v1.1 From 0e85663b887aa7c87d64965952fbf436c209fbe4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 10 Dec 2016 11:38:32 +0100 Subject: net: mvneta: select GENERIC_ALLOCATOR We previously relied on GENERIC_ALLOCATOR to be selected by CONFIG_ARM, but now we can compile-test the driver on other architectures that don't select it: drivers/net/built-in.o: In function `mvneta_bm_remove': mvneta_bm.c:(.text+0x4ee35): undefined reference to `gen_pool_free' This adds an explicit select for the part of the driver that has the dependency. Fixes: a0627f776a45 ("net: marvell: Allow drivers to be built with COMPILE_TEST") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 3b8f11f..f4b7cf1 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -76,6 +76,7 @@ config MVNETA_BM default y if MVNETA=y && MVNETA_BM_ENABLE!=n default MVNETA_BM_ENABLE select HWBM + select GENERIC_ALLOCATOR help MVNETA_BM must not be 'm' if MVNETA=y, so this symbol ensures that all dependencies are met. -- cgit v1.1 From cb7d78d045579831fbf40112f93bf2064a7ef14f Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Sat, 10 Dec 2016 14:23:46 +0200 Subject: net: ethernet: ti: cpsw: use same macros to get active slave Use the same, more convenient macros, to get active slave. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index b62d958..c45f7d2 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1624,10 +1624,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) struct cpsw_common *cpsw = priv->cpsw; u32 ctrl, mtype; - if (cpsw->data.dual_emac) - slave = &cpsw->slaves[priv->emac_port]; - else - slave = &cpsw->slaves[cpsw->data.active_slave]; + slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)]; ctrl = slave_read(slave, CPSW2_CONTROL); switch (cpsw->version) { -- cgit v1.1 From 32b5f2d1f9bf653491b6c03a55e26177211f8218 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Sat, 10 Dec 2016 14:23:47 +0200 Subject: net: ethernet: ti: cpsw: don't start queue twice No need to start queues after cpsw is started as it will be done while cpsw_adjust_link(), after phy connection. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c45f7d2..23213a3 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1506,8 +1506,6 @@ static int cpsw_ndo_open(struct net_device *ndev) if (cpsw->data.dual_emac) cpsw->slaves[priv->emac_port].open_stat = true; - netif_tx_start_all_queues(ndev); - return 0; err_cleanup: -- cgit v1.1 From 32b78d8563c57fc1d68ae73a5ae15359cb243f89 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Sat, 10 Dec 2016 14:23:48 +0200 Subject: net: ethernet: ti: cpsw: combine budget and weight split and check Re-split weight along with budget. It simplify code a little and update state after every rate change. Also it's necessarily to move arguments checks to this combined function. Replace maximum rate check for an interface on maximum possible rate. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 107 +++++++++++++---------------------------- 1 file changed, 34 insertions(+), 73 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 23213a3..a2c2c06 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -753,27 +753,18 @@ requeue: dev_kfree_skb_any(new_skb); } -/* split budget depending on channel rates */ -static void cpsw_split_budget(struct net_device *ndev) +static void cpsw_split_res(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); + u32 consumed_rate = 0, bigest_rate = 0; struct cpsw_common *cpsw = priv->cpsw; struct cpsw_vector *txv = cpsw->txv; - u32 consumed_rate, bigest_rate = 0; + int i, ch_weight, rlim_ch_num = 0; int budget, bigest_rate_ch = 0; struct cpsw_slave *slave; - int i, rlim_ch_num = 0; u32 ch_rate, max_rate; int ch_budget = 0; - if (cpsw->data.dual_emac) - slave = &cpsw->slaves[priv->emac_port]; - else - slave = &cpsw->slaves[cpsw->data.active_slave]; - - max_rate = slave->phy->speed * 1000; - - consumed_rate = 0; for (i = 0; i < cpsw->tx_ch_num; i++) { ch_rate = cpdma_chan_get_rate(txv[i].ch); if (!ch_rate) @@ -785,7 +776,14 @@ static void cpsw_split_budget(struct net_device *ndev) if (cpsw->tx_ch_num == rlim_ch_num) { max_rate = consumed_rate; + } else if (!rlim_ch_num) { + ch_budget = CPSW_POLL_WEIGHT / cpsw->tx_ch_num; + bigest_rate = 0; + max_rate = consumed_rate; } else { + slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)]; + max_rate = slave->phy->speed * 1000; + ch_budget = (consumed_rate * CPSW_POLL_WEIGHT) / max_rate; ch_budget = (CPSW_POLL_WEIGHT - ch_budget) / (cpsw->tx_ch_num - rlim_ch_num); @@ -793,22 +791,28 @@ static void cpsw_split_budget(struct net_device *ndev) (cpsw->tx_ch_num - rlim_ch_num); } - /* split tx budget */ + /* split tx weight/budget */ budget = CPSW_POLL_WEIGHT; for (i = 0; i < cpsw->tx_ch_num; i++) { ch_rate = cpdma_chan_get_rate(txv[i].ch); if (ch_rate) { txv[i].budget = (ch_rate * CPSW_POLL_WEIGHT) / max_rate; if (!txv[i].budget) - txv[i].budget = 1; + txv[i].budget++; if (ch_rate > bigest_rate) { bigest_rate_ch = i; bigest_rate = ch_rate; } + + ch_weight = (ch_rate * 100) / max_rate; + if (!ch_weight) + ch_weight++; + cpdma_chan_set_weight(cpsw->txv[i].ch, ch_weight); } else { txv[i].budget = ch_budget; if (!bigest_rate_ch) bigest_rate_ch = i; + cpdma_chan_set_weight(cpsw->txv[i].ch, 0); } budget -= txv[i].budget; @@ -1017,7 +1021,7 @@ static void cpsw_adjust_link(struct net_device *ndev) for_each_slave(priv, _cpsw_adjust_link, priv, &link); if (link) { - cpsw_split_budget(priv->ndev); + cpsw_split_res(priv->ndev); netif_carrier_on(ndev); if (netif_running(ndev)) netif_tx_wake_all_queues(ndev); @@ -1962,64 +1966,25 @@ static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev, static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) { struct cpsw_priv *priv = netdev_priv(ndev); - int tx_ch_num = ndev->real_num_tx_queues; - u32 consumed_rate, min_rate, max_rate; struct cpsw_common *cpsw = priv->cpsw; - struct cpsw_slave *slave; - int ret, i, weight; - int rlim_num = 0; + u32 min_rate; u32 ch_rate; + int ret; ch_rate = netdev_get_tx_queue(ndev, queue)->tx_maxrate; if (ch_rate == rate) return 0; - if (cpsw->data.dual_emac) - slave = &cpsw->slaves[priv->emac_port]; - else - slave = &cpsw->slaves[cpsw->data.active_slave]; - max_rate = slave->phy->speed; - - consumed_rate = 0; - for (i = 0; i < tx_ch_num; i++) { - if (i == queue) - ch_rate = rate; - else - ch_rate = netdev_get_tx_queue(ndev, i)->tx_maxrate; - if (!ch_rate) - continue; - - rlim_num++; - consumed_rate += ch_rate; - } - - if (consumed_rate > max_rate) - dev_info(priv->dev, "The common rate shouldn't be more than %dMbps", - max_rate); - - if (consumed_rate > max_rate) { - if (max_rate == 10 && consumed_rate <= 100) { - max_rate = 100; - } else if (max_rate <= 100 && consumed_rate <= 1000) { - max_rate = 1000; - } else { - dev_err(priv->dev, "The common rate cannot be more than %dMbps", - max_rate); - return -EINVAL; - } - } - - if (consumed_rate > max_rate) { - dev_err(priv->dev, "The common rate cannot be more than %dMbps", - max_rate); + ch_rate = rate * 1000; + min_rate = cpdma_chan_get_min_rate(cpsw->dma); + if ((ch_rate < min_rate && ch_rate)) { + dev_err(priv->dev, "The channel rate cannot be less than %dMbps", + min_rate); return -EINVAL; } - rate *= 1000; - min_rate = cpdma_chan_get_min_rate(cpsw->dma); - if ((rate < min_rate && rate)) { - dev_err(priv->dev, "The common rate cannot be less than %dMbps", - min_rate); + if (rate > 2000) { + dev_err(priv->dev, "The channel rate cannot be more than 2Gbps"); return -EINVAL; } @@ -2029,17 +1994,13 @@ static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) return ret; } - if (rlim_num == tx_ch_num) - max_rate = consumed_rate; + ret = cpdma_chan_set_rate(cpsw->txv[queue].ch, ch_rate); + pm_runtime_put(cpsw->dev); - weight = (rate * 100) / (max_rate * 1000); - cpdma_chan_set_weight(cpsw->txv[queue].ch, weight); - ret = cpdma_chan_set_rate(cpsw->txv[queue].ch, rate); + if (ret) + return ret; - /* re-split budget between channels */ - if (!rate) - cpsw_split_budget(ndev); - pm_runtime_put(cpsw->dev); + cpsw_split_res(ndev); return ret; } @@ -2399,7 +2360,7 @@ static int cpsw_set_channels(struct net_device *ndev, if (ret) goto err; - cpsw_split_budget(ndev); + cpsw_split_res(ndev); /* After this receive is started */ cpdma_ctlr_start(cpsw->dma); -- cgit v1.1 From 0be01b8e0a365138e8f94315de13ab3751d199e2 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Sat, 10 Dec 2016 14:23:49 +0200 Subject: net: ethernet: ti: cpsw: re-split res only when speed is changed Don't re-split res in the following cases: - speed of phys is not changed - speed of phys is changed and no rate limited channels - speed of phys is changed and all channels are rate limited - phy is unlinked while dev is open - phy is linked back but speed is not changed The maximum speed is sum of "linked" phys, thus res are split taken in account two interfaces, both for dual emac mode and for switch mode. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 64 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a2c2c06..7ccfa63 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -394,6 +394,7 @@ struct cpsw_common { u32 irqs_table[IRQ_NUM]; struct cpts *cpts; int rx_ch_num, tx_ch_num; + int speed; }; struct cpsw_priv { @@ -761,7 +762,6 @@ static void cpsw_split_res(struct net_device *ndev) struct cpsw_vector *txv = cpsw->txv; int i, ch_weight, rlim_ch_num = 0; int budget, bigest_rate_ch = 0; - struct cpsw_slave *slave; u32 ch_rate, max_rate; int ch_budget = 0; @@ -781,8 +781,16 @@ static void cpsw_split_res(struct net_device *ndev) bigest_rate = 0; max_rate = consumed_rate; } else { - slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)]; - max_rate = slave->phy->speed * 1000; + max_rate = cpsw->speed * 1000; + + /* if max_rate is less then expected due to reduced link speed, + * split proportionally according next potential max speed + */ + if (max_rate < consumed_rate) + max_rate *= 10; + + if (max_rate < consumed_rate) + max_rate *= 10; ch_budget = (consumed_rate * CPSW_POLL_WEIGHT) / max_rate; ch_budget = (CPSW_POLL_WEIGHT - ch_budget) / @@ -1013,15 +1021,56 @@ static void _cpsw_adjust_link(struct cpsw_slave *slave, slave->mac_control = mac_control; } +static int cpsw_get_common_speed(struct cpsw_common *cpsw) +{ + int i, speed; + + for (i = 0, speed = 0; i < cpsw->data.slaves; i++) + if (cpsw->slaves[i].phy && cpsw->slaves[i].phy->link) + speed += cpsw->slaves[i].phy->speed; + + return speed; +} + +static int cpsw_need_resplit(struct cpsw_common *cpsw) +{ + int i, rlim_ch_num; + int speed, ch_rate; + + /* re-split resources only in case speed was changed */ + speed = cpsw_get_common_speed(cpsw); + if (speed == cpsw->speed || !speed) + return 0; + + cpsw->speed = speed; + + for (i = 0, rlim_ch_num = 0; i < cpsw->tx_ch_num; i++) { + ch_rate = cpdma_chan_get_rate(cpsw->txv[i].ch); + if (!ch_rate) + break; + + rlim_ch_num++; + } + + /* cases not dependent on speed */ + if (!rlim_ch_num || rlim_ch_num == cpsw->tx_ch_num) + return 0; + + return 1; +} + static void cpsw_adjust_link(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); + struct cpsw_common *cpsw = priv->cpsw; bool link = false; for_each_slave(priv, _cpsw_adjust_link, priv, &link); if (link) { - cpsw_split_res(priv->ndev); + if (cpsw_need_resplit(cpsw)) + cpsw_split_res(ndev); + netif_carrier_on(ndev); if (netif_running(ndev)) netif_tx_wake_all_queues(ndev); @@ -1538,6 +1587,10 @@ static int cpsw_ndo_stop(struct net_device *ndev) cpsw_ale_stop(cpsw->ale); } for_each_slave(priv, cpsw_slave_stop, cpsw); + + if (cpsw_need_resplit(cpsw)) + cpsw_split_res(ndev); + pm_runtime_put_sync(cpsw->dev); if (cpsw->data.dual_emac) cpsw->slaves[priv->emac_port].open_stat = false; @@ -1983,7 +2036,7 @@ static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) return -EINVAL; } - if (rate > 2000) { + if (rate > cpsw->speed) { dev_err(priv->dev, "The channel rate cannot be more than 2Gbps"); return -EINVAL; } @@ -2998,6 +3051,7 @@ static int cpsw_probe(struct platform_device *pdev) ndev->ethtool_ops = &cpsw_ethtool_ops; netif_napi_add(ndev, &cpsw->napi_rx, cpsw_rx_poll, CPSW_POLL_WEIGHT); netif_tx_napi_add(ndev, &cpsw->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT); + cpsw_split_res(ndev); /* register the network device */ SET_NETDEV_DEV(ndev, &pdev->dev); -- cgit v1.1 From 52986a2f926cc0de544e32005a3436bb6db39f10 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Sat, 10 Dec 2016 14:23:50 +0200 Subject: net: ethernet: ti: cpsw: sync rates for channels in dual emac mode The channels are common for both ndevs in dual emac mode. Hence, keep in sync their rates. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 7ccfa63..b203143 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2020,9 +2020,10 @@ static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) { struct cpsw_priv *priv = netdev_priv(ndev); struct cpsw_common *cpsw = priv->cpsw; + struct cpsw_slave *slave; u32 min_rate; u32 ch_rate; - int ret; + int i, ret; ch_rate = netdev_get_tx_queue(ndev, queue)->tx_maxrate; if (ch_rate == rate) @@ -2053,6 +2054,15 @@ static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate) if (ret) return ret; + /* update rates for slaves tx queues */ + for (i = 0; i < cpsw->data.slaves; i++) { + slave = &cpsw->slaves[i]; + if (!slave->ndev) + continue; + + netdev_get_tx_queue(slave->ndev, queue)->tx_maxrate = rate; + } + cpsw_split_res(ndev); return ret; } -- cgit v1.1 From bfd8d977af2390e646d4a5df8e88c1d7a1ae63d9 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 10 Dec 2016 15:00:48 +0100 Subject: net: nicvf: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- .../net/ethernet/cavium/thunder/nicvf_ethtool.c | 56 ++++++++++++---------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index b048241..2e74bba 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -116,33 +116,34 @@ static const unsigned int nicvf_n_hw_stats = ARRAY_SIZE(nicvf_hw_stats); static const unsigned int nicvf_n_drv_stats = ARRAY_SIZE(nicvf_drv_stats); static const unsigned int nicvf_n_queue_stats = ARRAY_SIZE(nicvf_queue_stats); -static int nicvf_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) +static int nicvf_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct nicvf *nic = netdev_priv(netdev); + u32 supported, advertising; - cmd->supported = 0; - cmd->transceiver = XCVR_EXTERNAL; + supported = 0; + advertising = 0; if (!nic->link_up) { - cmd->duplex = DUPLEX_UNKNOWN; - ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); + cmd->base.duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; return 0; } switch (nic->speed) { case SPEED_1000: - cmd->port = PORT_MII | PORT_TP; - cmd->autoneg = AUTONEG_ENABLE; - cmd->supported |= SUPPORTED_MII | SUPPORTED_TP; - cmd->supported |= SUPPORTED_1000baseT_Full | + cmd->base.port = PORT_MII | PORT_TP; + cmd->base.autoneg = AUTONEG_ENABLE; + supported |= SUPPORTED_MII | SUPPORTED_TP; + supported |= SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_10baseT_Half; - cmd->supported |= SUPPORTED_Autoneg; - cmd->advertising |= ADVERTISED_1000baseT_Full | + supported |= SUPPORTED_Autoneg; + advertising |= ADVERTISED_1000baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_100baseT_Half | @@ -151,24 +152,29 @@ static int nicvf_get_settings(struct net_device *netdev, break; case SPEED_10000: if (nic->mac_type == BGX_MODE_RXAUI) { - cmd->port = PORT_TP; - cmd->supported |= SUPPORTED_TP; + cmd->base.port = PORT_TP; + supported |= SUPPORTED_TP; } else { - cmd->port = PORT_FIBRE; - cmd->supported |= SUPPORTED_FIBRE; + cmd->base.port = PORT_FIBRE; + supported |= SUPPORTED_FIBRE; } - cmd->autoneg = AUTONEG_DISABLE; - cmd->supported |= SUPPORTED_10000baseT_Full; + cmd->base.autoneg = AUTONEG_DISABLE; + supported |= SUPPORTED_10000baseT_Full; break; case SPEED_40000: - cmd->port = PORT_FIBRE; - cmd->autoneg = AUTONEG_DISABLE; - cmd->supported |= SUPPORTED_FIBRE; - cmd->supported |= SUPPORTED_40000baseCR4_Full; + cmd->base.port = PORT_FIBRE; + cmd->base.autoneg = AUTONEG_DISABLE; + supported |= SUPPORTED_FIBRE; + supported |= SUPPORTED_40000baseCR4_Full; break; } - cmd->duplex = nic->duplex; - ethtool_cmd_speed_set(cmd, nic->speed); + cmd->base.duplex = nic->duplex; + cmd->base.speed = nic->speed; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } @@ -770,7 +776,6 @@ static int nicvf_set_pauseparam(struct net_device *dev, } static const struct ethtool_ops nicvf_ethtool_ops = { - .get_settings = nicvf_get_settings, .get_link = nicvf_get_link, .get_drvinfo = nicvf_get_drvinfo, .get_msglevel = nicvf_get_msglevel, @@ -793,6 +798,7 @@ static const struct ethtool_ops nicvf_ethtool_ops = { .get_pauseparam = nicvf_get_pauseparam, .set_pauseparam = nicvf_set_pauseparam, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = nicvf_get_link_ksettings, }; void nicvf_set_ethtool_ops(struct net_device *netdev) -- cgit v1.1 From 82dd4332aa07843f7444c55bc17fee9bd76aa0f7 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 10 Dec 2016 13:44:27 -0500 Subject: net: bridge: add helper to offload ageing time The SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME switchdev attr is actually set when initializing a bridge port, and when configuring the bridge ageing time from ioctl/netlink/sysfs. Add a __set_ageing_time helper to offload the ageing time to physical switches, and add the SWITCHDEV_F_DEFER flag since it can be called under bridge lock. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/bridge/br_private.h | 1 + net/bridge/br_stp.c | 28 ++++++++++++++++++++-------- net/bridge/br_stp_if.c | 12 +++--------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 26aec23..e2cd6d1 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -999,6 +999,7 @@ void __br_set_forward_delay(struct net_bridge *br, unsigned long t); int br_set_forward_delay(struct net_bridge *br, unsigned long x); int br_set_hello_time(struct net_bridge *br, unsigned long x); int br_set_max_age(struct net_bridge *br, unsigned long x); +int __set_ageing_time(struct net_device *dev, unsigned long t); int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 9258b8e..6ebe2a0 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -562,6 +562,24 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) } +/* called under bridge lock */ +int __set_ageing_time(struct net_device *dev, unsigned long t) +{ + struct switchdev_attr attr = { + .orig_dev = dev, + .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, + .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, + .u.ageing_time = jiffies_to_clock_t(t), + }; + int err; + + err = switchdev_port_attr_set(dev, &attr); + if (err && err != -EOPNOTSUPP) + return err; + + return 0; +} + /* Set time interval that dynamic forwarding entries live * For pure software bridge, allow values outside the 802.1 * standard specification for special cases: @@ -572,17 +590,11 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) */ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) { - struct switchdev_attr attr = { - .orig_dev = br->dev, - .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, - .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP, - .u.ageing_time = ageing_time, - }; unsigned long t = clock_t_to_jiffies(ageing_time); int err; - err = switchdev_port_attr_set(br->dev, &attr); - if (err && err != -EOPNOTSUPP) + err = __set_ageing_time(br->dev, t); + if (err) return err; br->ageing_time = t; diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index d8ad73b..2efbba5 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -36,12 +36,6 @@ static inline port_id br_make_port_id(__u8 priority, __u16 port_no) /* called under bridge lock */ void br_init_port(struct net_bridge_port *p) { - struct switchdev_attr attr = { - .orig_dev = p->dev, - .id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, - .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER, - .u.ageing_time = jiffies_to_clock_t(p->br->ageing_time), - }; int err; p->port_id = br_make_port_id(p->priority, p->port_no); @@ -50,9 +44,9 @@ void br_init_port(struct net_bridge_port *p) p->topology_change_ack = 0; p->config_pending = 0; - err = switchdev_port_attr_set(p->dev, &attr); - if (err && err != -EOPNOTSUPP) - netdev_err(p->dev, "failed to set HW ageing time\n"); + err = __set_ageing_time(p->dev, p->br->ageing_time); + if (err) + netdev_err(p->dev, "failed to offload ageing time\n"); } /* NO locks held */ -- cgit v1.1 From 8384b5f5b293389a42cc65bfca2d767d15809aa1 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 10 Dec 2016 13:44:28 -0500 Subject: net: bridge: add helper to set topology change Add a __br_set_topology_change helper to set the topology change value. This can be later extended to add actions when the topology change flag is set or cleared. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/bridge/br_private_stp.h | 1 + net/bridge/br_stp.c | 10 ++++++++-- net/bridge/br_stp_if.c | 2 +- net/bridge/br_stp_timer.c | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index 2fe910c..3f7543a 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -61,6 +61,7 @@ void br_received_tcn_bpdu(struct net_bridge_port *p); void br_transmit_config(struct net_bridge_port *p); void br_transmit_tcn(struct net_bridge *br); void br_topology_change_detection(struct net_bridge *br); +void __br_set_topology_change(struct net_bridge *br, unsigned char val); /* br_stp_bpdu.c */ void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 6ebe2a0..8d7b4c7 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -234,7 +234,7 @@ static void br_record_config_timeout_values(struct net_bridge *br, br->max_age = bpdu->max_age; br->hello_time = bpdu->hello_time; br->forward_delay = bpdu->forward_delay; - br->topology_change = bpdu->topology_change; + __br_set_topology_change(br, bpdu->topology_change); } /* called under bridge lock */ @@ -344,7 +344,7 @@ void br_topology_change_detection(struct net_bridge *br) isroot ? "propagating" : "sending tcn bpdu"); if (isroot) { - br->topology_change = 1; + __br_set_topology_change(br, 1); mod_timer(&br->topology_change_timer, jiffies + br->bridge_forward_delay + br->bridge_max_age); } else if (!br->topology_change_detected) { @@ -603,6 +603,12 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) return 0; } +/* called under bridge lock */ +void __br_set_topology_change(struct net_bridge *br, unsigned char val) +{ + br->topology_change = val; +} + void __br_set_forward_delay(struct net_bridge *br, unsigned long t) { br->bridge_forward_delay = t; diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 2efbba5..6c1e214 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -81,7 +81,7 @@ void br_stp_disable_bridge(struct net_bridge *br) } - br->topology_change = 0; + __br_set_topology_change(br, 0); br->topology_change_detected = 0; spin_unlock_bh(&br->lock); diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index da058b8..7ddb38e 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -125,7 +125,7 @@ static void br_topology_change_timer_expired(unsigned long arg) br_debug(br, "topo change timer expired\n"); spin_lock(&br->lock); br->topology_change_detected = 0; - br->topology_change = 0; + __br_set_topology_change(br, 0); spin_unlock(&br->lock); } -- cgit v1.1 From 34d8acd8aabb6e070b6fb718909db80458ef32ec Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 10 Dec 2016 13:44:29 -0500 Subject: net: bridge: shorten ageing time on topology change 802.1D [1] specifies that the bridges must use a short value to age out dynamic entries in the Filtering Database for a period, once a topology change has been communicated by the root bridge. Add a bridge_ageing_time member in the net_bridge structure to store the bridge ageing time value configured by the user (ioctl/netlink/sysfs). If we are using in-kernel STP, shorten the ageing time value to twice the forward delay used by the topology when the topology change flag is set. When the flag is cleared, restore the configured ageing time. [1] "8.3.5 Notifying topology changes ", http://profesores.elo.utfsm.cl/~agv/elo309/doc/802.1D-1998.pdf Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- net/bridge/br_device.c | 2 +- net/bridge/br_private.h | 3 ++- net/bridge/br_stp.c | 27 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index c08e02b..bca5ead 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -409,7 +409,7 @@ void br_dev_setup(struct net_device *dev) br->bridge_max_age = br->max_age = 20 * HZ; br->bridge_hello_time = br->hello_time = 2 * HZ; br->bridge_forward_delay = br->forward_delay = 15 * HZ; - br->ageing_time = BR_DEFAULT_AGEING_TIME; + br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME; dev->max_mtu = ETH_MAX_MTU; br_netfilter_rtable_init(br); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index e2cd6d1..8ce621e8 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -300,10 +300,11 @@ struct net_bridge unsigned long max_age; unsigned long hello_time; unsigned long forward_delay; - unsigned long bridge_max_age; unsigned long ageing_time; + unsigned long bridge_max_age; unsigned long bridge_hello_time; unsigned long bridge_forward_delay; + unsigned long bridge_ageing_time; u8 group_addr[ETH_ALEN]; bool group_addr_set; diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 8d7b4c7..71fd1a4 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -597,7 +597,11 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) if (err) return err; + spin_lock_bh(&br->lock); + br->bridge_ageing_time = t; br->ageing_time = t; + spin_unlock_bh(&br->lock); + mod_timer(&br->gc_timer, jiffies); return 0; @@ -606,6 +610,29 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time) /* called under bridge lock */ void __br_set_topology_change(struct net_bridge *br, unsigned char val) { + unsigned long t; + int err; + + if (br->stp_enabled == BR_KERNEL_STP && br->topology_change != val) { + /* On topology change, set the bridge ageing time to twice the + * forward delay. Otherwise, restore its default ageing time. + */ + + if (val) { + t = 2 * br->forward_delay; + br_debug(br, "decreasing ageing time to %lu\n", t); + } else { + t = br->bridge_ageing_time; + br_debug(br, "restoring ageing time to %lu\n", t); + } + + err = __set_ageing_time(br->dev, t); + if (err) + br_warn(br, "error offloading ageing time\n"); + else + br->ageing_time = t; + } + br->topology_change = val; } -- cgit v1.1 From 980f140493b6d9d9e1fc67ce594170186be848a1 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Fri, 9 Dec 2016 00:55:42 +0100 Subject: net: ethernet: sxgbe: remove private tx queue lock The driver uses a private lock for synchronization of the xmit function and the xmit completion handler, but since the NETIF_F_LLTX flag is not set, the xmit function is also called with the xmit_lock held. On the other hand the completion handler uses the reverse locking order by first taking the private lock and (in case that the tx queue had been stopped) then the xmit_lock. Improve the locking by removing the private lock and using only the xmit_lock for synchronization instead. Signed-off-by: Lino Sanfilippo Signed-off-by: David S. Miller --- drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h | 1 - drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 27 +++++------------------ 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h index 5cb51b6..c61f260 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h @@ -384,7 +384,6 @@ struct sxgbe_tx_queue { dma_addr_t *tx_skbuff_dma; struct sk_buff **tx_skbuff; struct timer_list txtimer; - spinlock_t tx_lock; /* lock for tx queues */ unsigned int cur_tx; unsigned int dirty_tx; u32 tx_count_frames; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 5dbe406..cddcff5 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -426,9 +426,6 @@ static int init_tx_ring(struct device *dev, u8 queue_no, tx_ring->dirty_tx = 0; tx_ring->cur_tx = 0; - /* initialise TX queue lock */ - spin_lock_init(&tx_ring->tx_lock); - return 0; dmamem_err: @@ -743,7 +740,7 @@ static void sxgbe_tx_queue_clean(struct sxgbe_tx_queue *tqueue) dev_txq = netdev_get_tx_queue(priv->dev, queue_no); - spin_lock(&tqueue->tx_lock); + __netif_tx_lock(dev_txq, smp_processor_id()); priv->xstats.tx_clean++; while (tqueue->dirty_tx != tqueue->cur_tx) { @@ -781,18 +778,13 @@ static void sxgbe_tx_queue_clean(struct sxgbe_tx_queue *tqueue) /* wake up queue */ if (unlikely(netif_tx_queue_stopped(dev_txq) && - sxgbe_tx_avail(tqueue, tx_rsize) > SXGBE_TX_THRESH(priv))) { - netif_tx_lock(priv->dev); - if (netif_tx_queue_stopped(dev_txq) && - sxgbe_tx_avail(tqueue, tx_rsize) > SXGBE_TX_THRESH(priv)) { - if (netif_msg_tx_done(priv)) - pr_debug("%s: restart transmit\n", __func__); - netif_tx_wake_queue(dev_txq); - } - netif_tx_unlock(priv->dev); + sxgbe_tx_avail(tqueue, tx_rsize) > SXGBE_TX_THRESH(priv))) { + if (netif_msg_tx_done(priv)) + pr_debug("%s: restart transmit\n", __func__); + netif_tx_wake_queue(dev_txq); } - spin_unlock(&tqueue->tx_lock); + __netif_tx_unlock(dev_txq); } /** @@ -1304,9 +1296,6 @@ static netdev_tx_t sxgbe_xmit(struct sk_buff *skb, struct net_device *dev) tqueue->hwts_tx_en))) ctxt_desc_req = 1; - /* get the spinlock */ - spin_lock(&tqueue->tx_lock); - if (priv->tx_path_in_lpi_mode) sxgbe_disable_eee_mode(priv); @@ -1316,8 +1305,6 @@ static netdev_tx_t sxgbe_xmit(struct sk_buff *skb, struct net_device *dev) netdev_err(dev, "%s: Tx Ring is full when %d queue is awake\n", __func__, txq_index); } - /* release the spin lock in case of BUSY */ - spin_unlock(&tqueue->tx_lock); return NETDEV_TX_BUSY; } @@ -1436,8 +1423,6 @@ static netdev_tx_t sxgbe_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->dma->enable_dma_transmission(priv->ioaddr, txq_index); - spin_unlock(&tqueue->tx_lock); - return NETDEV_TX_OK; } -- cgit v1.1 From 739c8e149ae40a1eb044edb92a133b93b59369d8 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Fri, 9 Dec 2016 00:55:43 +0100 Subject: net: ethernet: stmmac: remove private tx queue lock The driver uses a private lock for synchronization of the xmit function and the xmit completion handler, but since the NETIF_F_LLTX flag is not set, the xmit function is also called with the xmit_lock held. On the other hand the completion handler uses the reverse locking order by first taking the private lock and (in case that the tx queue had been stopped) then the xmit_lock. Improve the locking by removing the private lock and using only the xmit_lock for synchronization instead. Signed-off-by: Lino Sanfilippo Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 - drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 28 +++++------------------ 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index dbacb80..eab04ae 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -64,7 +64,6 @@ struct stmmac_priv { dma_addr_t dma_tx_phy; int tx_coalesce; int hwts_tx_en; - spinlock_t tx_lock; bool tx_path_in_lpi_mode; struct timer_list txtimer; bool tso; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b518812..3e40578 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1308,7 +1308,7 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) unsigned int bytes_compl = 0, pkts_compl = 0; unsigned int entry = priv->dirty_tx; - spin_lock(&priv->tx_lock); + netif_tx_lock(priv->dev); priv->xstats.tx_clean++; @@ -1379,22 +1379,17 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); if (unlikely(netif_queue_stopped(priv->dev) && - stmmac_tx_avail(priv) > STMMAC_TX_THRESH)) { - netif_tx_lock(priv->dev); - if (netif_queue_stopped(priv->dev) && - stmmac_tx_avail(priv) > STMMAC_TX_THRESH) { - netif_dbg(priv, tx_done, priv->dev, - "%s: restart transmit\n", __func__); - netif_wake_queue(priv->dev); - } - netif_tx_unlock(priv->dev); + stmmac_tx_avail(priv) > STMMAC_TX_THRESH)) { + netif_dbg(priv, tx_done, priv->dev, + "%s: restart transmit\n", __func__); + netif_wake_queue(priv->dev); } if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) { stmmac_enable_eee_mode(priv); mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(eee_timer)); } - spin_unlock(&priv->tx_lock); + netif_tx_unlock(priv->dev); } static inline void stmmac_enable_dma_irq(struct stmmac_priv *priv) @@ -2002,8 +1997,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) u8 proto_hdr_len; int i; - spin_lock(&priv->tx_lock); - /* Compute header lengths */ proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); @@ -2017,7 +2010,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) "%s: Tx Ring full when queue awake\n", __func__); } - spin_unlock(&priv->tx_lock); return NETDEV_TX_BUSY; } @@ -2152,11 +2144,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, STMMAC_CHAN0); - spin_unlock(&priv->tx_lock); return NETDEV_TX_OK; dma_map_err: - spin_unlock(&priv->tx_lock); dev_err(priv->device, "Tx dma map failed\n"); dev_kfree_skb(skb); priv->dev->stats.tx_dropped++; @@ -2188,10 +2178,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) return stmmac_tso_xmit(skb, dev); } - spin_lock(&priv->tx_lock); - if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) { - spin_unlock(&priv->tx_lock); if (!netif_queue_stopped(dev)) { netif_stop_queue(dev); /* This is a hard error, log it. */ @@ -2362,11 +2349,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, STMMAC_CHAN0); - spin_unlock(&priv->tx_lock); return NETDEV_TX_OK; dma_map_err: - spin_unlock(&priv->tx_lock); netdev_err(priv->dev, "Tx DMA map failed\n"); dev_kfree_skb(skb); priv->dev->stats.tx_dropped++; @@ -3353,7 +3338,6 @@ int stmmac_dvr_probe(struct device *device, netif_napi_add(ndev, &priv->napi, stmmac_poll, 64); spin_lock_init(&priv->lock); - spin_lock_init(&priv->tx_lock); ret = register_netdev(ndev); if (ret) { -- cgit v1.1 From 41c43fbee68f4f9a2a9675d83bca91c77862d7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Sun, 11 Dec 2016 00:18:57 +0000 Subject: net: l2tp: export debug flags to UAPI Move the L2TP_MSG_* definitions to UAPI, as it is part of the netlink API. Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- include/uapi/linux/l2tp.h | 17 ++++++++++++++++- net/l2tp/l2tp_core.h | 10 ---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/uapi/linux/l2tp.h b/include/uapi/linux/l2tp.h index 5daa48e..85ddb74 100644 --- a/include/uapi/linux/l2tp.h +++ b/include/uapi/linux/l2tp.h @@ -108,7 +108,7 @@ enum { L2TP_ATTR_VLAN_ID, /* u16 */ L2TP_ATTR_COOKIE, /* 0, 4 or 8 bytes */ L2TP_ATTR_PEER_COOKIE, /* 0, 4 or 8 bytes */ - L2TP_ATTR_DEBUG, /* u32 */ + L2TP_ATTR_DEBUG, /* u32, enum l2tp_debug_flags */ L2TP_ATTR_RECV_SEQ, /* u8 */ L2TP_ATTR_SEND_SEQ, /* u8 */ L2TP_ATTR_LNS_MODE, /* u8 */ @@ -175,6 +175,21 @@ enum l2tp_seqmode { L2TP_SEQ_ALL = 2, }; +/** + * enum l2tp_debug_flags - debug message categories for L2TP tunnels/sessions + * + * @L2TP_MSG_DEBUG: verbose debug (if compiled in) + * @L2TP_MSG_CONTROL: userspace - kernel interface + * @L2TP_MSG_SEQ: sequence numbers + * @L2TP_MSG_DATA: data packets + */ +enum l2tp_debug_flags { + L2TP_MSG_DEBUG = (1 << 0), + L2TP_MSG_CONTROL = (1 << 1), + L2TP_MSG_SEQ = (1 << 2), + L2TP_MSG_DATA = (1 << 3), +}; + /* * NETLINK_GENERIC related info */ diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 2599af6..8f560f7 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -23,16 +23,6 @@ #define L2TP_HASH_BITS_2 8 #define L2TP_HASH_SIZE_2 (1 << L2TP_HASH_BITS_2) -/* Debug message categories for the DEBUG socket option */ -enum { - L2TP_MSG_DEBUG = (1 << 0), /* verbose debug (if - * compiled in) */ - L2TP_MSG_CONTROL = (1 << 1), /* userspace - kernel - * interface */ - L2TP_MSG_SEQ = (1 << 2), /* sequence numbers */ - L2TP_MSG_DATA = (1 << 3), /* data packets */ -}; - struct sk_buff; struct l2tp_stats { -- cgit v1.1 From 47c3e7783be4e142b861d34b5c2e223330b05d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Sun, 11 Dec 2016 00:18:58 +0000 Subject: net: l2tp: deprecate PPPOL2TP_MSG_* in favour of L2TP_MSG_* PPPOL2TP_MSG_* and L2TP_MSG_* are duplicates, and are being used interchangeably in the kernel, so let's standardize on L2TP_MSG_* internally, and keep PPPOL2TP_MSG_* defined in UAPI for compatibility. Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- Documentation/networking/l2tp.txt | 8 ++++---- include/uapi/linux/if_pppol2tp.h | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Documentation/networking/l2tp.txt b/Documentation/networking/l2tp.txt index 4650a00..9bc271c 100644 --- a/Documentation/networking/l2tp.txt +++ b/Documentation/networking/l2tp.txt @@ -177,10 +177,10 @@ setsockopt on the PPPoX socket to set a debug mask. The following debug mask bits are available: -PPPOL2TP_MSG_DEBUG verbose debug (if compiled in) -PPPOL2TP_MSG_CONTROL userspace - kernel interface -PPPOL2TP_MSG_SEQ sequence numbers handling -PPPOL2TP_MSG_DATA data packets +L2TP_MSG_DEBUG verbose debug (if compiled in) +L2TP_MSG_CONTROL userspace - kernel interface +L2TP_MSG_SEQ sequence numbers handling +L2TP_MSG_DATA data packets If enabled, files under a l2tp debugfs directory can be used to dump kernel state about L2TP tunnels and sessions. To access it, the diff --git a/include/uapi/linux/if_pppol2tp.h b/include/uapi/linux/if_pppol2tp.h index 4bd1f55..6418c4d 100644 --- a/include/uapi/linux/if_pppol2tp.h +++ b/include/uapi/linux/if_pppol2tp.h @@ -18,6 +18,7 @@ #include #include #include +#include /* Structure used to connect() the socket to a particular tunnel UDP * socket over IPv4. @@ -90,14 +91,12 @@ enum { PPPOL2TP_SO_REORDERTO = 5, }; -/* Debug message categories for the DEBUG socket option */ +/* Debug message categories for the DEBUG socket option (deprecated) */ enum { - PPPOL2TP_MSG_DEBUG = (1 << 0), /* verbose debug (if - * compiled in) */ - PPPOL2TP_MSG_CONTROL = (1 << 1), /* userspace - kernel - * interface */ - PPPOL2TP_MSG_SEQ = (1 << 2), /* sequence numbers */ - PPPOL2TP_MSG_DATA = (1 << 3), /* data packets */ + PPPOL2TP_MSG_DEBUG = L2TP_MSG_DEBUG, + PPPOL2TP_MSG_CONTROL = L2TP_MSG_CONTROL, + PPPOL2TP_MSG_SEQ = L2TP_MSG_SEQ, + PPPOL2TP_MSG_DATA = L2TP_MSG_DATA, }; -- cgit v1.1 From fba40c632c6473fa89660e870a6042c0fe733f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Sun, 11 Dec 2016 00:18:59 +0000 Subject: net: l2tp: ppp: change PPPOL2TP_MSG_* => L2TP_MSG_* Signed-off-by: Asbjoern Sloth Toennesen Signed-off-by: David S. Miller --- net/l2tp/l2tp_ppp.c | 54 ++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 2ddfec1..36cc56f 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -231,14 +231,14 @@ static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int if (sk->sk_state & PPPOX_BOUND) { struct pppox_sock *po; - l2tp_dbg(session, PPPOL2TP_MSG_DATA, + l2tp_dbg(session, L2TP_MSG_DATA, "%s: recv %d byte data frame, passing to ppp\n", session->name, data_len); po = pppox_sk(sk); ppp_input(&po->chan, skb); } else { - l2tp_dbg(session, PPPOL2TP_MSG_DATA, + l2tp_dbg(session, L2TP_MSG_DATA, "%s: recv %d byte data frame, passing to L2TP socket\n", session->name, data_len); @@ -251,7 +251,7 @@ static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int return; no_sock: - l2tp_info(session, PPPOL2TP_MSG_DATA, "%s: no socket\n", session->name); + l2tp_info(session, L2TP_MSG_DATA, "%s: no socket\n", session->name); kfree_skb(skb); } @@ -773,7 +773,7 @@ out_no_ppp: /* This is how we get the session context from the socket. */ sk->sk_user_data = session; sk->sk_state = PPPOX_CONNECTED; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: created\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n", session->name); end: @@ -827,7 +827,7 @@ static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_i ps = l2tp_session_priv(session); ps->tunnel_sock = tunnel->sock; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: created\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n", session->name); error = 0; @@ -989,7 +989,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, struct l2tp_tunnel *tunnel = session->tunnel; struct pppol2tp_ioc_stats stats; - l2tp_dbg(session, PPPOL2TP_MSG_CONTROL, + l2tp_dbg(session, L2TP_MSG_CONTROL, "%s: pppol2tp_session_ioctl(cmd=%#x, arg=%#lx)\n", session->name, cmd, arg); @@ -1009,7 +1009,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, if (copy_to_user((void __user *) arg, &ifr, sizeof(struct ifreq))) break; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get mtu=%d\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get mtu=%d\n", session->name, session->mtu); err = 0; break; @@ -1025,7 +1025,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, session->mtu = ifr.ifr_mtu; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set mtu=%d\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set mtu=%d\n", session->name, session->mtu); err = 0; break; @@ -1039,7 +1039,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, if (put_user(session->mru, (int __user *) arg)) break; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get mru=%d\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get mru=%d\n", session->name, session->mru); err = 0; break; @@ -1054,7 +1054,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, break; session->mru = val; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set mru=%d\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set mru=%d\n", session->name, session->mru); err = 0; break; @@ -1064,7 +1064,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, if (put_user(ps->flags, (int __user *) arg)) break; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get flags=%d\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get flags=%d\n", session->name, ps->flags); err = 0; break; @@ -1074,7 +1074,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, if (get_user(val, (int __user *) arg)) break; ps->flags = val; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set flags=%d\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set flags=%d\n", session->name, ps->flags); err = 0; break; @@ -1091,7 +1091,7 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, if (copy_to_user((void __user *) arg, &stats, sizeof(stats))) break; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get L2TP stats\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get L2TP stats\n", session->name); err = 0; break; @@ -1119,7 +1119,7 @@ static int pppol2tp_tunnel_ioctl(struct l2tp_tunnel *tunnel, struct sock *sk; struct pppol2tp_ioc_stats stats; - l2tp_dbg(tunnel, PPPOL2TP_MSG_CONTROL, + l2tp_dbg(tunnel, L2TP_MSG_CONTROL, "%s: pppol2tp_tunnel_ioctl(cmd=%#x, arg=%#lx)\n", tunnel->name, cmd, arg); @@ -1155,7 +1155,7 @@ static int pppol2tp_tunnel_ioctl(struct l2tp_tunnel *tunnel, err = -EFAULT; break; } - l2tp_info(tunnel, PPPOL2TP_MSG_CONTROL, "%s: get L2TP stats\n", + l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: get L2TP stats\n", tunnel->name); err = 0; break; @@ -1245,7 +1245,7 @@ static int pppol2tp_tunnel_setsockopt(struct sock *sk, switch (optname) { case PPPOL2TP_SO_DEBUG: tunnel->debug = val; - l2tp_info(tunnel, PPPOL2TP_MSG_CONTROL, "%s: set debug=%x\n", + l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: set debug=%x\n", tunnel->name, tunnel->debug); break; @@ -1273,7 +1273,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk, break; } session->recv_seq = !!val; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set recv_seq=%d\n", session->name, session->recv_seq); break; @@ -1291,7 +1291,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk, PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; } l2tp_session_set_header_len(session, session->tunnel->version); - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set send_seq=%d\n", session->name, session->send_seq); break; @@ -1302,20 +1302,20 @@ static int pppol2tp_session_setsockopt(struct sock *sk, break; } session->lns_mode = !!val; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set lns_mode=%d\n", session->name, session->lns_mode); break; case PPPOL2TP_SO_DEBUG: session->debug = val; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set debug=%x\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set debug=%x\n", session->name, session->debug); break; case PPPOL2TP_SO_REORDERTO: session->reorder_timeout = msecs_to_jiffies(val); - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: set reorder_timeout=%d\n", session->name, session->reorder_timeout); break; @@ -1396,7 +1396,7 @@ static int pppol2tp_tunnel_getsockopt(struct sock *sk, switch (optname) { case PPPOL2TP_SO_DEBUG: *val = tunnel->debug; - l2tp_info(tunnel, PPPOL2TP_MSG_CONTROL, "%s: get debug=%x\n", + l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: get debug=%x\n", tunnel->name, tunnel->debug); break; @@ -1419,31 +1419,31 @@ static int pppol2tp_session_getsockopt(struct sock *sk, switch (optname) { case PPPOL2TP_SO_RECVSEQ: *val = session->recv_seq; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get recv_seq=%d\n", session->name, *val); break; case PPPOL2TP_SO_SENDSEQ: *val = session->send_seq; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get send_seq=%d\n", session->name, *val); break; case PPPOL2TP_SO_LNSMODE: *val = session->lns_mode; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get lns_mode=%d\n", session->name, *val); break; case PPPOL2TP_SO_DEBUG: *val = session->debug; - l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get debug=%d\n", + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get debug=%d\n", session->name, *val); break; case PPPOL2TP_SO_REORDERTO: *val = (int) jiffies_to_msecs(session->reorder_timeout); - l2tp_info(session, PPPOL2TP_MSG_CONTROL, + l2tp_info(session, L2TP_MSG_CONTROL, "%s: get reorder_timeout=%d\n", session->name, *val); break; -- cgit v1.1 From 529ed12752635ba8a35dc78ec70ed6f42570b4ca Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 7 Dec 2016 13:20:51 -0600 Subject: net: phy: phy drivers should not set SUPPORTED_[Asym_]Pause Instead of having individual PHY drivers set the SUPPORTED_Pause and SUPPORTED_Asym_Pause flags, phylib itself should set those flags, unless there is a hardware erratum or other special case. During autonegotiation, the PHYs will determine whether to enable pause frame support. Pause frames are a feature that is supported by the MAC. It is the MAC that generates the frames and that processes them. The PHY can only be configured to allow them to pass through. This commit also effectively reverts the recently applied c7a61319 ("net: phy: dp83848: Support ethernet pause frames"). So the new process is: 1) Unless the PHY driver overrides it, phylib sets the SUPPORTED_Pause and SUPPORTED_AsymPause bits in phydev->supported. This indicates that the PHY supports pause frames. 2) The MAC driver checks phydev->supported before it calls phy_start(). If (SUPPORTED_Pause | SUPPORTED_AsymPause) is set, then the MAC driver sets those bits in phydev->advertising, if it wants to enable pause frame support. 3) When the link state changes, the MAC driver checks phydev->pause and phydev->asym_pause, If the bits are set, then it enables the corresponding features in the MAC. The algorithm is: if (phydev->pause) The MAC should be programmed to receive and honor pause frames it receives, i.e. enable receive flow control. if (phydev->pause != phydev->asym_pause) The MAC should be programmed to transmit pause frames when needed, i.e. enable transmit flow control. Signed-off-by: Timur Tabi Signed-off-by: David S. Miller --- drivers/net/phy/bcm-cygnus.c | 3 +-- drivers/net/phy/bcm7xxx.c | 6 ++---- drivers/net/phy/broadcom.c | 42 ++++++++++++++---------------------------- drivers/net/phy/dp83848.c | 4 +--- drivers/net/phy/icplus.c | 6 ++---- drivers/net/phy/intel-xway.c | 24 ++++++++---------------- drivers/net/phy/micrel.c | 30 ++++++++++++------------------ drivers/net/phy/microchip.c | 3 +-- drivers/net/phy/national.c | 2 +- drivers/net/phy/phy_device.c | 19 +++++++++++++++++++ drivers/net/phy/smsc.c | 18 ++++++------------ 11 files changed, 67 insertions(+), 90 deletions(-) diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c index 196400c..3fe8cc5 100644 --- a/drivers/net/phy/bcm-cygnus.c +++ b/drivers/net/phy/bcm-cygnus.c @@ -134,8 +134,7 @@ static struct phy_driver bcm_cygnus_phy_driver[] = { .phy_id = PHY_ID_BCM_CYGNUS, .phy_id_mask = 0xfffffff0, .name = "Broadcom Cygnus PHY", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .config_init = bcm_cygnus_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index aae00bd..264b085 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -386,8 +386,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .phy_id = (_oui), \ .phy_id_mask = 0xfffffff0, \ .name = _name, \ - .features = PHY_GBIT_FEATURES | \ - SUPPORTED_Pause | SUPPORTED_Asym_Pause, \ + .features = PHY_GBIT_FEATURES, \ .flags = PHY_IS_INTERNAL, \ .config_init = bcm7xxx_28nm_config_init, \ .config_aneg = genphy_config_aneg, \ @@ -406,8 +405,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .phy_id = (_oui), \ .phy_id_mask = 0xfffffff0, \ .name = _name, \ - .features = PHY_BASIC_FEATURES | \ - SUPPORTED_Pause | SUPPORTED_Asym_Pause, \ + .features = PHY_BASIC_FEATURES, \ .flags = PHY_IS_INTERNAL, \ .config_init = bcm7xxx_config_init, \ .config_aneg = genphy_config_aneg, \ diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 409b365..4223e35 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -525,8 +525,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM5411, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5411", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -537,8 +536,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM5421, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5421", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -549,8 +547,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM5461, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5461", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -561,8 +558,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM54612E, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54612E", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = bcm54612e_config_aneg, @@ -573,8 +569,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM54616S, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54616S", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -585,8 +580,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM5464, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5464", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -597,8 +591,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM5481, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5481", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = bcm5481_config_aneg, @@ -609,8 +602,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM54810, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54810", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = bcm5481_config_aneg, @@ -621,8 +613,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM5482, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5482", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm5482_config_init, .config_aneg = genphy_config_aneg, @@ -633,8 +624,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM50610, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM50610", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -645,8 +635,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM50610M, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM50610M", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -657,8 +646,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM57780, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM57780", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, @@ -669,8 +657,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCMAC131, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCMAC131", - .features = PHY_BASIC_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = brcm_fet_config_init, .config_aneg = genphy_config_aneg, @@ -681,8 +668,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id = PHY_ID_BCM5241, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5241", - .features = PHY_BASIC_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = brcm_fet_config_init, .config_aneg = genphy_config_aneg, diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c index 320d0dc..800b39f 100644 --- a/drivers/net/phy/dp83848.c +++ b/drivers/net/phy/dp83848.c @@ -88,9 +88,7 @@ MODULE_DEVICE_TABLE(mdio, dp83848_tbl); .phy_id = _id, \ .phy_id_mask = 0xfffffff0, \ .name = _name, \ - .features = (PHY_BASIC_FEATURES | \ - SUPPORTED_Pause | \ - SUPPORTED_Asym_Pause), \ + .features = PHY_BASIC_FEATURES, \ .flags = PHY_HAS_INTERRUPT, \ \ .soft_reset = genphy_soft_reset, \ diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index e5f251b..22b51f0 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -225,8 +225,7 @@ static struct phy_driver icplus_driver[] = { .phy_id = 0x02430d90, .name = "ICPlus IP1001", .phy_id_mask = 0x0ffffff0, - .features = PHY_GBIT_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .config_init = &ip1001_config_init, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, @@ -236,8 +235,7 @@ static struct phy_driver icplus_driver[] = { .phy_id = 0x02430c54, .name = "ICPlus IP101A/G", .phy_id_mask = 0x0ffffff0, - .features = PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause, + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, .ack_interrupt = ip101a_g_ack_interrupt, .config_init = &ip101a_g_config_init, diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c index c300ab5..b1fd7bb 100644 --- a/drivers/net/phy/intel-xway.c +++ b/drivers/net/phy/intel-xway.c @@ -239,8 +239,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY11G_1_3, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3", - .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = xway_gphy14_config_aneg, @@ -254,8 +253,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY22F_1_3, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.3", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = xway_gphy14_config_aneg, @@ -269,8 +267,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY11G_1_4, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4", - .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = xway_gphy14_config_aneg, @@ -284,8 +281,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY22F_1_4, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.4", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = xway_gphy14_config_aneg, @@ -299,8 +295,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY11G_1_5, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6", - .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = genphy_config_aneg, @@ -314,8 +309,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY22F_1_5, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = genphy_config_aneg, @@ -329,8 +323,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY11G_VR9, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (xRX integrated)", - .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = genphy_config_aneg, @@ -344,8 +337,7 @@ static struct phy_driver xway_gphy[] = { .phy_id = PHY_ID_PHY22F_VR9, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (xRX integrated)", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = xway_gphy_config_init, .config_aneg = genphy_config_aneg, diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index ea92d52..9a77289 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -790,7 +790,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KS8737, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KS8737", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ks8737_type, .config_init = kszphy_config_init, @@ -807,8 +807,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8021, .phy_id_mask = 0x00ffffff, .name = "Micrel KSZ8021 or KSZ8031", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8021_type, .probe = kszphy_probe, @@ -826,8 +825,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8031, .phy_id_mask = 0x00ffffff, .name = "Micrel KSZ8031", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | - SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8021_type, .probe = kszphy_probe, @@ -845,8 +843,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8041, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8041", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8041_type, .probe = kszphy_probe, @@ -864,8 +861,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8041RNLI, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8041RNLI", - .features = PHY_BASIC_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8041_type, .probe = kszphy_probe, @@ -883,8 +879,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8051, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8051", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8051_type, .probe = kszphy_probe, @@ -902,7 +897,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8001, .name = "Micrel KSZ8001 or KS8721", .phy_id_mask = 0x00fffffc, - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8041_type, .probe = kszphy_probe, @@ -920,7 +915,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8081, .name = "Micrel KSZ8081 or KSZ8091", .phy_id_mask = MICREL_PHY_ID_MASK, - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8081_type, .probe = kszphy_probe, @@ -938,7 +933,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8061, .name = "Micrel KSZ8061", .phy_id_mask = MICREL_PHY_ID_MASK, - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, @@ -954,7 +949,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ9021, .phy_id_mask = 0x000ffffe, .name = "Micrel KSZ9021 Gigabit PHY", - .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz9021_type, .config_init = ksz9021_config_init, @@ -973,7 +968,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ9031, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ9031 Gigabit PHY", - .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz9021_type, .config_init = ksz9031_config_init, @@ -990,7 +985,6 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8873MLL Switch", - .features = (SUPPORTED_Pause | SUPPORTED_Asym_Pause), .flags = PHY_HAS_MAGICANEG, .config_init = kszphy_config_init, .config_aneg = ksz8873mll_config_aneg, @@ -1004,7 +998,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ886X, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ886X Switch", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index 12825a5..324fbf6 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -146,8 +146,7 @@ static struct phy_driver microchip_phy_driver[] = { .phy_id_mask = 0xfffffff0, .name = "Microchip LAN88xx", - .features = (PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause), + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, .probe = lan88xx_probe, diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c index 2a1b490..2addf1d 100644 --- a/drivers/net/phy/national.c +++ b/drivers/net/phy/national.c @@ -133,7 +133,7 @@ static struct phy_driver dp83865_driver[] = { { .phy_id = DP83865_PHY_ID, .phy_id_mask = 0xfffffff0, .name = "NatSemi DP83865", - .features = PHY_GBIT_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = ns_config_init, .config_aneg = genphy_config_aneg, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0aadef9..9c06f80 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1719,6 +1719,25 @@ static int phy_probe(struct device *dev) */ of_set_phy_eee_broken(phydev); + /* The Pause Frame bits indicate that the PHY can support passing + * pause frames. During autonegotiation, the PHYs will determine if + * they should allow pause frames to pass. The MAC driver should then + * use that result to determine whether to enable flow control via + * pause frames. + * + * Normally, PHY drivers should not set the Pause bits, and instead + * allow phylib to do that. However, there may be some situations + * (e.g. hardware erratum) where the driver wants to set only one + * of these bits. + */ + if (phydrv->features & (SUPPORTED_Pause | SUPPORTED_Asym_Pause)) { + phydev->supported &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause); + phydev->supported |= phydrv->features & + (SUPPORTED_Pause | SUPPORTED_Asym_Pause); + } else { + phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + } + /* Set the state to READY by default */ phydev->state = PHY_READY; diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index b62c4aa..fb32eaf 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -168,8 +168,7 @@ static struct phy_driver smsc_phy_driver[] = { .phy_id_mask = 0xfffffff0, .name = "SMSC LAN83C185", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, .probe = smsc_phy_probe, @@ -191,8 +190,7 @@ static struct phy_driver smsc_phy_driver[] = { .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8187", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, .probe = smsc_phy_probe, @@ -214,8 +212,7 @@ static struct phy_driver smsc_phy_driver[] = { .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8700", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, .probe = smsc_phy_probe, @@ -237,8 +234,7 @@ static struct phy_driver smsc_phy_driver[] = { .phy_id_mask = 0xfffffff0, .name = "SMSC LAN911x Internal PHY", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, .probe = smsc_phy_probe, @@ -259,8 +255,7 @@ static struct phy_driver smsc_phy_driver[] = { .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8710/LAN8720", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, .probe = smsc_phy_probe, @@ -282,8 +277,7 @@ static struct phy_driver smsc_phy_driver[] = { .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8740", - .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause - | SUPPORTED_Asym_Pause), + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, .probe = smsc_phy_probe, -- cgit v1.1 From 6246168b4a38357b135d07370464f27d4f3a68ce Mon Sep 17 00:00:00 2001 From: WingMan Kwok Date: Thu, 8 Dec 2016 16:21:56 -0600 Subject: net: ethernet: ti: netcp: add support of cpts This patch adds support of the cpts device found in the gbe and 10gbe ethernet switches on the keystone 2 SoCs (66AK2E/L/Hx, 66AK2Gx). Cc: Richard Cochran Signed-off-by: WingMan Kwok Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/Kconfig | 7 +- drivers/net/ethernet/ti/netcp.h | 2 +- drivers/net/ethernet/ti/netcp_core.c | 20 +- drivers/net/ethernet/ti/netcp_ethss.c | 437 +++++++++++++++++++++++++++++++++- 4 files changed, 452 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index ff7f518..dc217fd 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -75,12 +75,13 @@ config TI_CPSW config TI_CPTS tristate "TI Common Platform Time Sync (CPTS) Support" - depends on TI_CPSW + depends on TI_CPSW || TI_KEYSTONE_NETCP select PTP_1588_CLOCK ---help--- This driver supports the Common Platform Time Sync unit of - the CPSW Ethernet Switch. The unit can time stamp PTP UDP/IPv4 - and Layer 2 packets, and the driver offers a PTP Hardware Clock. + the CPSW Ethernet Switch and Keystone 2 1g/10g Switch Subsystem. + The unit can time stamp PTP UDP/IPv4 and Layer 2 packets, and the + driver offers a PTP Hardware Clock. config TI_KEYSTONE_NETCP tristate "TI Keystone NETCP Core Support" diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h index 17a26a4..0f58c58 100644 --- a/drivers/net/ethernet/ti/netcp.h +++ b/drivers/net/ethernet/ti/netcp.h @@ -121,7 +121,7 @@ struct netcp_packet { bool rxtstamp_complete; void *ts_context; - int (*txtstamp_complete)(void *ctx, struct netcp_packet *pkt); + void (*txtstamp)(void *ctx, struct sk_buff *skb); }; static inline u32 *netcp_push_psdata(struct netcp_packet *p_info, diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 7981b99..c243335 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -100,6 +100,11 @@ struct netcp_intf_modpriv { void *module_priv; }; +struct netcp_tx_cb { + void *ts_context; + void (*txtstamp)(void *context, struct sk_buff *skb); +}; + static LIST_HEAD(netcp_devices); static LIST_HEAD(netcp_modules); static DEFINE_MUTEX(netcp_modules_lock); @@ -544,6 +549,7 @@ int netcp_register_rxhook(struct netcp_intf *netcp_priv, int order, return 0; } +EXPORT_SYMBOL_GPL(netcp_register_rxhook); int netcp_unregister_rxhook(struct netcp_intf *netcp_priv, int order, netcp_hook_rtn *hook_rtn, void *hook_data) @@ -566,6 +572,7 @@ int netcp_unregister_rxhook(struct netcp_intf *netcp_priv, int order, return -ENOENT; } +EXPORT_SYMBOL_GPL(netcp_unregister_rxhook); static void netcp_frag_free(bool is_frag, void *ptr) { @@ -730,6 +737,7 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) /* Call each of the RX hooks */ p_info.skb = skb; + skb->dev = netcp->ndev; p_info.rxtstamp_complete = false; list_for_each_entry(rx_hook, &netcp->rxhook_list_head, list) { int ret; @@ -987,6 +995,7 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp, unsigned int budget) { struct knav_dma_desc *desc; + struct netcp_tx_cb *tx_cb; struct sk_buff *skb; unsigned int dma_sz; dma_addr_t dma; @@ -1014,6 +1023,10 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp, continue; } + tx_cb = (struct netcp_tx_cb *)skb->cb; + if (tx_cb->txtstamp) + tx_cb->txtstamp(tx_cb->ts_context, skb); + if (netif_subqueue_stopped(netcp->ndev, skb) && netif_running(netcp->ndev) && (knav_pool_count(netcp->tx_pool) > @@ -1154,6 +1167,7 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp, struct netcp_tx_pipe *tx_pipe = NULL; struct netcp_hook_list *tx_hook; struct netcp_packet p_info; + struct netcp_tx_cb *tx_cb; unsigned int dma_sz; dma_addr_t dma; u32 tmp = 0; @@ -1164,7 +1178,7 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp, p_info.tx_pipe = NULL; p_info.psdata_len = 0; p_info.ts_context = NULL; - p_info.txtstamp_complete = NULL; + p_info.txtstamp = NULL; p_info.epib = desc->epib; p_info.psdata = (u32 __force *)desc->psdata; memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(__le32)); @@ -1189,6 +1203,10 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp, goto out; } + tx_cb = (struct netcp_tx_cb *)skb->cb; + tx_cb->ts_context = p_info.ts_context; + tx_cb->txtstamp = p_info.txtstamp; + /* update descriptor */ if (p_info.psdata_len) { /* psdata points to both native-endian and device-endian data */ diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index 48cb04f..c7e547e 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -23,10 +23,13 @@ #include #include #include +#include +#include #include #include "cpsw_ale.h" #include "netcp.h" +#include "cpts.h" #define NETCP_DRIVER_NAME "TI KeyStone Ethernet Driver" #define NETCP_DRIVER_VERSION "v1.0" @@ -51,6 +54,7 @@ #define GBE13_EMAC_OFFSET 0x100 #define GBE13_SLAVE_PORT2_OFFSET 0x200 #define GBE13_HW_STATS_OFFSET 0x300 +#define GBE13_CPTS_OFFSET 0x500 #define GBE13_ALE_OFFSET 0x600 #define GBE13_HOST_PORT_NUM 0 #define GBE13_NUM_ALE_ENTRIES 1024 @@ -74,6 +78,7 @@ #define GBENU_SLAVE_PORT_OFFSET 0x2000 #define GBENU_EMAC_OFFSET 0x2330 #define GBENU_HW_STATS_OFFSET 0x1a000 +#define GBENU_CPTS_OFFSET 0x1d000 #define GBENU_ALE_OFFSET 0x1e000 #define GBENU_HOST_PORT_NUM 0 #define GBENU_NUM_ALE_ENTRIES 1024 @@ -93,6 +98,7 @@ #define XGBE10_HOST_PORT_OFFSET 0x34 #define XGBE10_SLAVE_PORT_OFFSET 0x64 #define XGBE10_EMAC_OFFSET 0x400 +#define XGBE10_CPTS_OFFSET 0x600 #define XGBE10_ALE_OFFSET 0x700 #define XGBE10_HW_STATS_OFFSET 0x800 #define XGBE10_HOST_PORT_NUM 0 @@ -155,6 +161,7 @@ #define GBE_TX_QUEUE 648 #define GBE_TXHOOK_ORDER 0 +#define GBE_RXHOOK_ORDER 0 #define GBE_DEFAULT_ALE_AGEOUT 30 #define SLAVE_LINK_IS_XGMII(s) ((s)->link_interface >= XGMII_LINK_MAC_PHY) #define NETCP_LINK_STATE_INVALID -1 @@ -169,6 +176,56 @@ #define HOST_TX_PRI_MAP_DEFAULT 0x00000000 +#if IS_ENABLED(CONFIG_TI_CPTS) +/* Px_TS_CTL register fields */ +#define TS_RX_ANX_F_EN BIT(0) +#define TS_RX_VLAN_LT1_EN BIT(1) +#define TS_RX_VLAN_LT2_EN BIT(2) +#define TS_RX_ANX_D_EN BIT(3) +#define TS_TX_ANX_F_EN BIT(4) +#define TS_TX_VLAN_LT1_EN BIT(5) +#define TS_TX_VLAN_LT2_EN BIT(6) +#define TS_TX_ANX_D_EN BIT(7) +#define TS_LT2_EN BIT(8) +#define TS_RX_ANX_E_EN BIT(9) +#define TS_TX_ANX_E_EN BIT(10) +#define TS_MSG_TYPE_EN_SHIFT 16 +#define TS_MSG_TYPE_EN_MASK 0xffff + +/* Px_TS_SEQ_LTYPE register fields */ +#define TS_SEQ_ID_OFS_SHIFT 16 +#define TS_SEQ_ID_OFS_MASK 0x3f + +/* Px_TS_CTL_LTYPE2 register fields */ +#define TS_107 BIT(16) +#define TS_129 BIT(17) +#define TS_130 BIT(18) +#define TS_131 BIT(19) +#define TS_132 BIT(20) +#define TS_319 BIT(21) +#define TS_320 BIT(22) +#define TS_TTL_NONZERO BIT(23) +#define TS_UNI_EN BIT(24) +#define TS_UNI_EN_SHIFT 24 + +#define TS_TX_ANX_ALL_EN \ + (TS_TX_ANX_D_EN | TS_TX_ANX_E_EN | TS_TX_ANX_F_EN) + +#define TS_RX_ANX_ALL_EN \ + (TS_RX_ANX_D_EN | TS_RX_ANX_E_EN | TS_RX_ANX_F_EN) + +#define TS_CTL_DST_PORT TS_319 +#define TS_CTL_DST_PORT_SHIFT 21 + +#define TS_CTL_MADDR_ALL \ + (TS_107 | TS_129 | TS_130 | TS_131 | TS_132) + +#define TS_CTL_MADDR_SHIFT 16 + +/* The PTP event messages - Sync, Delay_Req, Pdelay_Req, and Pdelay_Resp. */ +#define EVENT_MSG_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3)) +#endif /* CONFIG_TI_CPTS */ + struct xgbe_ss_regs { u32 id_ver; u32 synce_count; @@ -616,6 +673,13 @@ struct gbe_hw_stats { #define GBE_MAX_HW_STAT_MODS 9 #define GBE_HW_STATS_REG_MAP_SZ 0x100 +struct ts_ctl { + int uni; + u8 dst_port_map; + u8 maddr_map; + u8 ts_mcast_type; +}; + struct gbe_slave { void __iomem *port_regs; void __iomem *emac_regs; @@ -630,6 +694,7 @@ struct gbe_slave { u32 mac_control; u8 phy_port_t; struct device_node *phy_node; + struct ts_ctl ts_ctl; struct list_head slave_list; }; @@ -655,6 +720,7 @@ struct gbe_priv { void __iomem *switch_regs; void __iomem *host_port_regs; void __iomem *ale_reg; + void __iomem *cpts_reg; void __iomem *sgmii_port_regs; void __iomem *sgmii_port34_regs; void __iomem *xgbe_serdes_regs; @@ -678,6 +744,9 @@ struct gbe_priv { int num_et_stats; /* Lock for updating the hwstats */ spinlock_t hw_stats_lock; + + int cpts_registered; + struct cpts *cpts; }; struct gbe_intf { @@ -1912,6 +1981,49 @@ static int keystone_set_link_ksettings(struct net_device *ndev, return phy_ethtool_ksettings_set(phy, cmd); } +#if IS_ENABLED(CONFIG_TI_CPTS) +static int keystone_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_intf *gbe_intf; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf || !gbe_intf->gbe_dev->cpts) + return -EINVAL; + + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->phc_index = gbe_intf->gbe_dev->cpts->phc_index; + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + return 0; +} +#else +static int keystone_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + info->phc_index = -1; + info->tx_types = 0; + info->rx_filters = 0; + return 0; +} +#endif /* CONFIG_TI_CPTS */ + static const struct ethtool_ops keystone_ethtool_ops = { .get_drvinfo = keystone_get_drvinfo, .get_link = ethtool_op_get_link, @@ -1922,6 +2034,7 @@ static const struct ethtool_ops keystone_ethtool_ops = { .get_ethtool_stats = keystone_get_ethtool_stats, .get_link_ksettings = keystone_get_link_ksettings, .set_link_ksettings = keystone_set_link_ksettings, + .get_ts_info = keystone_get_ts_info, }; #define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ @@ -2365,16 +2478,279 @@ static int gbe_del_vid(void *intf_priv, int vid) return 0; } +#if IS_ENABLED(CONFIG_TI_CPTS) +#define HAS_PHY_TXTSTAMP(p) ((p)->drv && (p)->drv->txtstamp) +#define HAS_PHY_RXTSTAMP(p) ((p)->drv && (p)->drv->rxtstamp) + +static void gbe_txtstamp(void *context, struct sk_buff *skb) +{ + struct gbe_intf *gbe_intf = context; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + cpts_tx_timestamp(gbe_dev->cpts, skb); +} + +static bool gbe_need_txtstamp(struct gbe_intf *gbe_intf, + const struct netcp_packet *p_info) +{ + struct sk_buff *skb = p_info->skb; + unsigned int class = ptp_classify_raw(skb); + + if (class == PTP_CLASS_NONE) + return false; + + switch (class) { + case PTP_CLASS_V1_IPV4: + case PTP_CLASS_V1_IPV6: + case PTP_CLASS_V2_IPV4: + case PTP_CLASS_V2_IPV6: + case PTP_CLASS_V2_L2: + case (PTP_CLASS_V2_VLAN | PTP_CLASS_L2): + case (PTP_CLASS_V2_VLAN | PTP_CLASS_IPV4): + case (PTP_CLASS_V2_VLAN | PTP_CLASS_IPV6): + return true; + } + + return false; +} + +static int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf, + struct netcp_packet *p_info) +{ + struct phy_device *phydev = p_info->skb->dev->phydev; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + if (!(skb_shinfo(p_info->skb)->tx_flags & SKBTX_HW_TSTAMP) || + !cpts_is_tx_enabled(gbe_dev->cpts)) + return 0; + + /* If phy has the txtstamp api, assume it will do it. + * We mark it here because skb_tx_timestamp() is called + * after all the txhooks are called. + */ + if (phydev && HAS_PHY_TXTSTAMP(phydev)) { + skb_shinfo(p_info->skb)->tx_flags |= SKBTX_IN_PROGRESS; + return 0; + } + + if (gbe_need_txtstamp(gbe_intf, p_info)) { + p_info->txtstamp = gbe_txtstamp; + p_info->ts_context = (void *)gbe_intf; + skb_shinfo(p_info->skb)->tx_flags |= SKBTX_IN_PROGRESS; + } + + return 0; +} + +static int gbe_rxtstamp(struct gbe_intf *gbe_intf, struct netcp_packet *p_info) +{ + struct phy_device *phydev = p_info->skb->dev->phydev; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + if (p_info->rxtstamp_complete) + return 0; + + if (phydev && HAS_PHY_RXTSTAMP(phydev)) { + p_info->rxtstamp_complete = true; + return 0; + } + + cpts_rx_timestamp(gbe_dev->cpts, p_info->skb); + p_info->rxtstamp_complete = true; + + return 0; +} + +static int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *ifr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + struct cpts *cpts = gbe_dev->cpts; + struct hwtstamp_config cfg; + + if (!cpts) + return -EOPNOTSUPP; + + cfg.flags = 0; + cfg.tx_type = cpts_is_tx_enabled(cpts) ? + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + cfg.rx_filter = (cpts_is_rx_enabled(cpts) ? + cpts->rx_enable : HWTSTAMP_FILTER_NONE); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static void gbe_hwtstamp(struct gbe_intf *gbe_intf) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + struct gbe_slave *slave = gbe_intf->slave; + u32 ts_en, seq_id, ctl; + + if (!cpts_is_rx_enabled(gbe_dev->cpts) && + !cpts_is_tx_enabled(gbe_dev->cpts)) { + writel(0, GBE_REG_ADDR(slave, port_regs, ts_ctl)); + return; + } + + seq_id = (30 << TS_SEQ_ID_OFS_SHIFT) | ETH_P_1588; + ts_en = EVENT_MSG_BITS << TS_MSG_TYPE_EN_SHIFT; + ctl = ETH_P_1588 | TS_TTL_NONZERO | + (slave->ts_ctl.dst_port_map << TS_CTL_DST_PORT_SHIFT) | + (slave->ts_ctl.uni ? TS_UNI_EN : + slave->ts_ctl.maddr_map << TS_CTL_MADDR_SHIFT); + + if (cpts_is_tx_enabled(gbe_dev->cpts)) + ts_en |= (TS_TX_ANX_ALL_EN | TS_TX_VLAN_LT1_EN); + + if (cpts_is_rx_enabled(gbe_dev->cpts)) + ts_en |= (TS_RX_ANX_ALL_EN | TS_RX_VLAN_LT1_EN); + + writel(ts_en, GBE_REG_ADDR(slave, port_regs, ts_ctl)); + writel(seq_id, GBE_REG_ADDR(slave, port_regs, ts_seq_ltype)); + writel(ctl, GBE_REG_ADDR(slave, port_regs, ts_ctl_ltype2)); +} + +static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + struct cpts *cpts = gbe_dev->cpts; + struct hwtstamp_config cfg; + + if (!cpts) + return -EOPNOTSUPP; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + /* reserved for future extensions */ + if (cfg.flags) + return -EINVAL; + + switch (cfg.tx_type) { + case HWTSTAMP_TX_OFF: + cpts_tx_enable(cpts, 0); + break; + case HWTSTAMP_TX_ON: + cpts_tx_enable(cpts, 1); + break; + default: + return -ERANGE; + } + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + cpts_rx_enable(cpts, 0); + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT); + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT); + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + default: + return -ERANGE; + } + + gbe_hwtstamp(gbe_intf); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static void gbe_register_cpts(struct gbe_priv *gbe_dev) +{ + if (!gbe_dev->cpts) + return; + + if (gbe_dev->cpts_registered > 0) + goto done; + + if (cpts_register(gbe_dev->cpts)) { + dev_err(gbe_dev->dev, "error registering cpts device\n"); + return; + } + +done: + ++gbe_dev->cpts_registered; +} + +static void gbe_unregister_cpts(struct gbe_priv *gbe_dev) +{ + if (!gbe_dev->cpts || (gbe_dev->cpts_registered <= 0)) + return; + + if (--gbe_dev->cpts_registered) + return; + + cpts_unregister(gbe_dev->cpts); +} +#else +static inline int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf, + struct netcp_packet *p_info) +{ + return 0; +} + +static inline int gbe_rxtstamp(struct gbe_intf *gbe_intf, + struct netcp_packet *p_info) +{ + return 0; +} + +static inline int gbe_hwtstamp(struct gbe_intf *gbe_intf, + struct ifreq *ifr, int cmd) +{ + return -EOPNOTSUPP; +} + +static inline void gbe_register_cpts(struct gbe_priv *gbe_dev) +{ +} + +static inline void gbe_unregister_cpts(struct gbe_priv *gbe_dev) +{ +} + +static inline int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *req) +{ + return -EOPNOTSUPP; +} + +static inline int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *req) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_TI_CPTS */ + static int gbe_ioctl(void *intf_priv, struct ifreq *req, int cmd) { struct gbe_intf *gbe_intf = intf_priv; struct phy_device *phy = gbe_intf->slave->phy; - int ret = -EOPNOTSUPP; + + if (!phy || !phy->drv->hwtstamp) { + switch (cmd) { + case SIOCGHWTSTAMP: + return gbe_hwtstamp_get(gbe_intf, req); + case SIOCSHWTSTAMP: + return gbe_hwtstamp_set(gbe_intf, req); + } + } if (phy) - ret = phy_mii_ioctl(phy, req, cmd); + return phy_mii_ioctl(phy, req, cmd); - return ret; + return -EOPNOTSUPP; } static void netcp_ethss_timer(unsigned long arg) @@ -2410,12 +2786,20 @@ static void netcp_ethss_timer(unsigned long arg) add_timer(&gbe_dev->timer); } -static int gbe_tx_hook(int order, void *data, struct netcp_packet *p_info) +static int gbe_txhook(int order, void *data, struct netcp_packet *p_info) { struct gbe_intf *gbe_intf = data; p_info->tx_pipe = &gbe_intf->tx_pipe; - return 0; + + return gbe_txtstamp_mark_pkt(gbe_intf, p_info); +} + +static int gbe_rxhook(int order, void *data, struct netcp_packet *p_info) +{ + struct gbe_intf *gbe_intf = data; + + return gbe_rxtstamp(gbe_intf, p_info); } static int gbe_open(void *intf_priv, struct net_device *ndev) @@ -2465,11 +2849,14 @@ static int gbe_open(void *intf_priv, struct net_device *ndev) if (ret) goto fail; - netcp_register_txhook(netcp, GBE_TXHOOK_ORDER, gbe_tx_hook, - gbe_intf); + netcp_register_txhook(netcp, GBE_TXHOOK_ORDER, gbe_txhook, gbe_intf); + netcp_register_rxhook(netcp, GBE_RXHOOK_ORDER, gbe_rxhook, gbe_intf); slave->open = true; netcp_ethss_update_link_state(gbe_dev, slave, ndev); + + gbe_register_cpts(gbe_dev); + return 0; fail: @@ -2481,16 +2868,36 @@ static int gbe_close(void *intf_priv, struct net_device *ndev) { struct gbe_intf *gbe_intf = intf_priv; struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + gbe_unregister_cpts(gbe_dev); gbe_slave_stop(gbe_intf); - netcp_unregister_txhook(netcp, GBE_TXHOOK_ORDER, gbe_tx_hook, - gbe_intf); + + netcp_unregister_rxhook(netcp, GBE_RXHOOK_ORDER, gbe_rxhook, gbe_intf); + netcp_unregister_txhook(netcp, GBE_TXHOOK_ORDER, gbe_txhook, gbe_intf); gbe_intf->slave->open = false; atomic_set(&gbe_intf->slave->link_state, NETCP_LINK_STATE_INVALID); return 0; } +#if IS_ENABLED(CONFIG_TI_CPTS) +static void init_slave_ts_ctl(struct gbe_slave *slave) +{ + slave->ts_ctl.uni = 1; + slave->ts_ctl.dst_port_map = + (TS_CTL_DST_PORT >> TS_CTL_DST_PORT_SHIFT) & 0x3; + slave->ts_ctl.maddr_map = + (TS_CTL_MADDR_ALL >> TS_CTL_MADDR_SHIFT) & 0x1f; +} + +#else +static void init_slave_ts_ctl(struct gbe_slave *slave) +{ +} +#endif /* CONFIG_TI_CPTS */ + static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave, struct device_node *node) { @@ -2605,6 +3012,8 @@ static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave, } atomic_set(&slave->link_state, NETCP_LINK_STATE_INVALID); + + init_slave_ts_ctl(slave); return 0; } @@ -2795,6 +3204,7 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev, XGBE10_HW_STATS_OFFSET + (GBE_HW_STATS_REG_MAP_SZ * i); gbe_dev->ale_reg = gbe_dev->switch_regs + XGBE10_ALE_OFFSET; + gbe_dev->cpts_reg = gbe_dev->switch_regs + XGBE10_CPTS_OFFSET; gbe_dev->ale_ports = gbe_dev->max_num_ports; gbe_dev->host_port = XGBE10_HOST_PORT_NUM; gbe_dev->ale_entries = XGBE10_NUM_ALE_ENTRIES; @@ -2917,6 +3327,7 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev, (GBE_HW_STATS_REG_MAP_SZ * (i & 0x1)); } + gbe_dev->cpts_reg = gbe_dev->switch_regs + GBE13_CPTS_OFFSET; gbe_dev->ale_reg = gbe_dev->switch_regs + GBE13_ALE_OFFSET; gbe_dev->ale_ports = gbe_dev->max_num_ports; gbe_dev->host_port = GBE13_HOST_PORT_NUM; @@ -3006,6 +3417,7 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev, gbe_dev->hw_stats_regs[i] = gbe_dev->switch_regs + GBENU_HW_STATS_OFFSET + (GBENU_HW_STATS_REG_MAP_SZ * i); + gbe_dev->cpts_reg = gbe_dev->switch_regs + GBENU_CPTS_OFFSET; gbe_dev->ale_reg = gbe_dev->switch_regs + GBENU_ALE_OFFSET; gbe_dev->ale_ports = gbe_dev->max_num_ports; gbe_dev->host_port = GBENU_HOST_PORT_NUM; @@ -3187,6 +3599,12 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev, dev_dbg(gbe_dev->dev, "Created a gbe ale engine\n"); } + gbe_dev->cpts = cpts_create(gbe_dev->dev, gbe_dev->cpts_reg, node); + if (IS_ENABLED(CONFIG_TI_CPTS) && IS_ERR(gbe_dev->cpts)) { + ret = PTR_ERR(gbe_dev->cpts); + goto free_sec_ports; + } + /* initialize host port */ gbe_init_host_port(gbe_dev); @@ -3275,6 +3693,7 @@ static int gbe_remove(struct netcp_device *netcp_device, void *inst_priv) struct gbe_priv *gbe_dev = inst_priv; del_timer_sync(&gbe_dev->timer); + cpts_release(gbe_dev->cpts); cpsw_ale_stop(gbe_dev->ale); cpsw_ale_destroy(gbe_dev->ale); netcp_txpipe_close(&gbe_dev->tx_pipe); -- cgit v1.1 From 0266ac4536cdbf37e0bed918b97b6763aa8b84e2 Mon Sep 17 00:00:00 2001 From: "Keller, Jacob E" Date: Fri, 9 Dec 2016 13:39:21 -0800 Subject: i40e: don't truncate match_method assignment The .match_method field is a u8, so we shouldn't be casting to a u16, and because it is only one byte, we do not need to byte swap anything. Just assign the value directly. This avoids issues on Big Endian architectures which would have byte swapped and then incorrectly truncated the value. Signed-off-by: Jacob Keller Cc: Stephen Rothwell Cc: Bimmy Pujari Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index da4cbe3..ad4cf63 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2196,8 +2196,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } add_list[num_add].queue_number = 0; /* set invalid match method for later detection */ - add_list[num_add].match_method = - cpu_to_le16((u16)I40E_AQC_MM_ERR_NO_RES); + add_list[num_add].match_method = I40E_AQC_MM_ERR_NO_RES; cmd_flags |= I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; add_list[num_add].flags = cpu_to_le16(cmd_flags); num_add++; -- cgit v1.1 From 311191297125156319be8f86d546ea1c569f7e95 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sat, 10 Dec 2016 14:22:42 -0800 Subject: e1000: use disable_hardirq() for e1000_netpoll() In commit 02cea3958664 ("genirq: Provide disable_hardirq()") Peter introduced disable_hardirq() for netpoll, but it is forgotten to use it for e1000. This patch changes disable_irq() to disable_hardirq() for e1000. Reported-by: Dave Jones Suggested-by: Sabrina Dubroca Cc: Peter Zijlstra (Intel) Cc: Jeff Kirsher Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/e1000/e1000_main.c | 4 ++-- drivers/net/ethernet/intel/e1000e/netdev.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 33076fa..93fc6c6 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -5255,8 +5255,8 @@ static void e1000_netpoll(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); - disable_irq(adapter->pdev->irq); - e1000_intr(adapter->pdev->irq, netdev); + if (disable_hardirq(adapter->pdev->irq)) + e1000_intr(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); } #endif diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 8759d92..ffcf35a 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -6755,13 +6755,13 @@ static void e1000_netpoll(struct net_device *netdev) e1000_intr_msix(adapter->pdev->irq, netdev); break; case E1000E_INT_MODE_MSI: - disable_irq(adapter->pdev->irq); - e1000_intr_msi(adapter->pdev->irq, netdev); + if (disable_hardirq(adapter->pdev->irq)) + e1000_intr_msi(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); break; default: /* E1000E_INT_MODE_LEGACY */ - disable_irq(adapter->pdev->irq); - e1000_intr(adapter->pdev->irq, netdev); + if (disable_hardirq(adapter->pdev->irq)) + e1000_intr(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); break; } -- cgit v1.1 From d84701ecbcd6ad63faa7a9c18ad670d1c4d561c0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Date: Sun, 11 Dec 2016 11:43:59 +0100 Subject: netfilter: nft_counter: rework atomic dump and reset Dump and reset doesn't work unless cmpxchg64() is used both from packet and control plane paths. This approach is going to be slow though. Instead, use a percpu seqcount to fetch counters consistently, then subtract bytes and packets in case a reset was requested. The cpu that running over the reset code is guaranteed to own this stats exclusively, we have to turn counters into signed 64bit though so stats update on reset don't get wrong on underflow. This patch is based on original sketch from Eric Dumazet. Fixes: 43da04a593d8 ("netfilter: nf_tables: atomic dump and reset for stateful objects") Suggested-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- net/netfilter/nft_counter.c | 127 +++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 72 deletions(-) diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index f6a02c5..7f84222 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -18,31 +18,33 @@ #include struct nft_counter { - u64 bytes; - u64 packets; -}; - -struct nft_counter_percpu { - struct nft_counter counter; - struct u64_stats_sync syncp; + s64 bytes; + s64 packets; }; struct nft_counter_percpu_priv { - struct nft_counter_percpu __percpu *counter; + struct nft_counter __percpu *counter; }; +static DEFINE_PER_CPU(seqcount_t, nft_counter_seq); + static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv, struct nft_regs *regs, const struct nft_pktinfo *pkt) { - struct nft_counter_percpu *this_cpu; + struct nft_counter *this_cpu; + seqcount_t *myseq; local_bh_disable(); this_cpu = this_cpu_ptr(priv->counter); - u64_stats_update_begin(&this_cpu->syncp); - this_cpu->counter.bytes += pkt->skb->len; - this_cpu->counter.packets++; - u64_stats_update_end(&this_cpu->syncp); + myseq = this_cpu_ptr(&nft_counter_seq); + + write_seqcount_begin(myseq); + + this_cpu->bytes += pkt->skb->len; + this_cpu->packets++; + + write_seqcount_end(myseq); local_bh_enable(); } @@ -58,21 +60,21 @@ static inline void nft_counter_obj_eval(struct nft_object *obj, static int nft_counter_do_init(const struct nlattr * const tb[], struct nft_counter_percpu_priv *priv) { - struct nft_counter_percpu __percpu *cpu_stats; - struct nft_counter_percpu *this_cpu; + struct nft_counter __percpu *cpu_stats; + struct nft_counter *this_cpu; - cpu_stats = netdev_alloc_pcpu_stats(struct nft_counter_percpu); + cpu_stats = alloc_percpu(struct nft_counter); if (cpu_stats == NULL) return -ENOMEM; preempt_disable(); this_cpu = this_cpu_ptr(cpu_stats); if (tb[NFTA_COUNTER_PACKETS]) { - this_cpu->counter.packets = + this_cpu->packets = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); } if (tb[NFTA_COUNTER_BYTES]) { - this_cpu->counter.bytes = + this_cpu->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); } preempt_enable(); @@ -100,80 +102,59 @@ static void nft_counter_obj_destroy(struct nft_object *obj) nft_counter_do_destroy(priv); } -static void nft_counter_fetch(struct nft_counter_percpu __percpu *counter, +static void nft_counter_reset(struct nft_counter_percpu_priv __percpu *priv, struct nft_counter *total) { - struct nft_counter_percpu *cpu_stats; - u64 bytes, packets; - unsigned int seq; - int cpu; + struct nft_counter *this_cpu; - memset(total, 0, sizeof(*total)); - for_each_possible_cpu(cpu) { - cpu_stats = per_cpu_ptr(counter, cpu); - do { - seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp); - bytes = cpu_stats->counter.bytes; - packets = cpu_stats->counter.packets; - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq)); - - total->packets += packets; - total->bytes += bytes; - } -} - -static u64 __nft_counter_reset(u64 *counter) -{ - u64 ret, old; - - do { - old = *counter; - ret = cmpxchg64(counter, old, 0); - } while (ret != old); - - return ret; + local_bh_disable(); + this_cpu = this_cpu_ptr(priv->counter); + this_cpu->packets -= total->packets; + this_cpu->bytes -= total->bytes; + local_bh_enable(); } -static void nft_counter_reset(struct nft_counter_percpu __percpu *counter, +static void nft_counter_fetch(struct nft_counter_percpu_priv *priv, struct nft_counter *total) { - struct nft_counter_percpu *cpu_stats; + struct nft_counter *this_cpu; + const seqcount_t *myseq; u64 bytes, packets; unsigned int seq; int cpu; memset(total, 0, sizeof(*total)); for_each_possible_cpu(cpu) { - bytes = packets = 0; - - cpu_stats = per_cpu_ptr(counter, cpu); + myseq = per_cpu_ptr(&nft_counter_seq, cpu); + this_cpu = per_cpu_ptr(priv->counter, cpu); do { - seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp); - packets += __nft_counter_reset(&cpu_stats->counter.packets); - bytes += __nft_counter_reset(&cpu_stats->counter.bytes); - } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq)); + seq = read_seqcount_begin(myseq); + bytes = this_cpu->bytes; + packets = this_cpu->packets; + } while (read_seqcount_retry(myseq, seq)); - total->packets += packets; - total->bytes += bytes; + total->bytes += bytes; + total->packets += packets; } } static int nft_counter_do_dump(struct sk_buff *skb, - const struct nft_counter_percpu_priv *priv, + struct nft_counter_percpu_priv *priv, bool reset) { struct nft_counter total; - if (reset) - nft_counter_reset(priv->counter, &total); - else - nft_counter_fetch(priv->counter, &total); + nft_counter_fetch(priv, &total); if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes), NFTA_COUNTER_PAD) || nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.packets), NFTA_COUNTER_PAD)) goto nla_put_failure; + + if (reset) + nft_counter_reset(priv, &total); + return 0; nla_put_failure: @@ -216,7 +197,7 @@ static void nft_counter_eval(const struct nft_expr *expr, static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) { - const struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); + struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); return nft_counter_do_dump(skb, priv, false); } @@ -242,21 +223,20 @@ static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src) { struct nft_counter_percpu_priv *priv = nft_expr_priv(src); struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst); - struct nft_counter_percpu __percpu *cpu_stats; - struct nft_counter_percpu *this_cpu; + struct nft_counter __percpu *cpu_stats; + struct nft_counter *this_cpu; struct nft_counter total; - nft_counter_fetch(priv->counter, &total); + nft_counter_fetch(priv, &total); - cpu_stats = __netdev_alloc_pcpu_stats(struct nft_counter_percpu, - GFP_ATOMIC); + cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_ATOMIC); if (cpu_stats == NULL) return -ENOMEM; preempt_disable(); this_cpu = this_cpu_ptr(cpu_stats); - this_cpu->counter.packets = total.packets; - this_cpu->counter.bytes = total.bytes; + this_cpu->packets = total.packets; + this_cpu->bytes = total.bytes; preempt_enable(); priv_clone->counter = cpu_stats; @@ -285,7 +265,10 @@ static struct nft_expr_type nft_counter_type __read_mostly = { static int __init nft_counter_module_init(void) { - int err; + int cpu, err; + + for_each_possible_cpu(cpu) + seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu)); err = nft_register_obj(&nft_counter_obj); if (err < 0) -- cgit v1.1